• Stars
    star
    718
  • Rank 63,070 (Top 2 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created about 6 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Abstraction over common javascript date management libraries

date-io

Stand With Ukraine

Abstraction over common JavaScript date management libraries.

npm package codecov typescript travis lerna code style: prettier

The project exposes an abstraction interface over luxon, date-fns v2, dayjs and moment. It allows you to build any UI date or time components while utilizing the same date management library in use within your user's project.

It simplifies timezone management, allows your code to return the exact same type that your user expects and works with specific calendar systems (e.g. Jalali calendar)

Projects

Library Downloads
@date-io/date-fns npm download
@date-io/moment npm download
@date-io/luxon npm download
@date-io/dayjs npm download
@date-io/js-joda npm download
@date-io/date-fns-jalali npm download
@date-io/jalaali npm download
@date-io/hijri npm download

Usage example

import LuxonAdapter from "@date-io/luxon";
import DateFnsAdapter from "@date-io/date-fns";

const dateFns = new DateFnsAdapter();
const luxon = new LuxonAdapter({ locale: "fr" }); // pass french locale

const initialLuxonDate = luxon.date("2018-10-28T11:44:00.000Z");
const initialDateFnsDate = dateFns.date("2018-10-28T11:44:00.000Z");

const updatedLuxonDate = luxon.addDays(initialLuxonDate, 2);
const updatedDateFnsDate = dateFns.addDays(initialDateFnsDate, 2);

luxon.format(updatedLuxonDate, "fullDateTime24h"); // "2018, octobre 30 11:44"
dateFns.format(updatedLuxonDate, "fullDateTime24h"); // "2018, October 30th 11:44"

Interface

The implemented interface. If you cannot find the method you require, please let us know, and we will add it!

Localized output will of course vary based on the locale and date library used. Inline examples here are based on using moment with the en-US locale.

export interface DateIOFormats<TLibFormatToken = string> {
  /** Localized full date @example "Jan 1, 2019" */
  fullDate: TLibFormatToken;
  /** Partially localized full date with weekday, useful for text-to-speech accessibility @example "Tuesday, January 1, 2019" */
  fullDateWithWeekday: TLibFormatToken;
  /** Date format string with month and day of month @example "1 January" */
  normalDate: TLibFormatToken;
  /** Date format string with weekday, month and day of month @example "Wed, Jan 1" */
  normalDateWithWeekday: TLibFormatToken;
  /** Shorter day format @example "Jan 1" */
  shortDate: TLibFormatToken;
  /** Year format string @example "2019" */
  year: TLibFormatToken;
  /** Month format string @example "January" */
  month: TLibFormatToken;
  /** Short month format string @example "Jan" */
  monthShort: TLibFormatToken;
  /** Month with year format string @example "January 2018" */
  monthAndYear: TLibFormatToken;
  /** Month with date format string @example "January 1" */
  monthAndDate: TLibFormatToken;
  /** Weekday format string @example "Wednesday" */
  weekday: TLibFormatToken;
  /** Short weekday format string @example "Wed" */
  weekdayShort: TLibFormatToken;
  /** Day format string @example "1" */
  dayOfMonth: TLibFormatToken;
  /** Hours format string @example "11" */
  hours12h: TLibFormatToken;
  /** Hours format string @example "23" */
  hours24h: TLibFormatToken;
  /** Minutes format string @example "44" */
  minutes: TLibFormatToken;
  /** Seconds format string @example "00" */
  seconds: TLibFormatToken;
  /** Full time localized format string @example "11:44 PM" for US, "23:44" for Europe */
  fullTime: TLibFormatToken;
  /** Not localized full time format string @example "11:44 PM" */
  fullTime12h: TLibFormatToken;
  /** Not localized full time format string @example "23:44" */
  fullTime24h: TLibFormatToken;
  /** Date & time format string with localized time @example "Jan 1, 2018 11:44 PM" */
  fullDateTime: TLibFormatToken;
  /** Not localized date & Time format 12h @example "Jan 1, 2018 11:44 PM" */
  fullDateTime12h: TLibFormatToken;
  /** Not localized date & Time format 24h @example "Jan 1, 2018 23:44" */
  fullDateTime24h: TLibFormatToken;
  /** Localized keyboard input friendly date format @example "02/13/2020 */
  keyboardDate: TLibFormatToken;
  /** Localized keyboard input friendly date/time format @example "02/13/2020 23:44" */
  keyboardDateTime: TLibFormatToken;
  /** Partially localized keyboard input friendly date/time 12h format @example "02/13/2020 11:44 PM" */
  keyboardDateTime12h: TLibFormatToken;
  /** Partially localized keyboard input friendly date/time 24h format @example "02/13/2020 23:44" */
  keyboardDateTime24h: TLibFormatToken;
}

export type Unit =
  | "years"
  | "quarters"
  | "months"
  | "weeks"
  | "days"
  | "hours"
  | "minutes"
  | "seconds"
  | "milliseconds";

export interface IUtils<TDate> {
  formats: DateIOFormats<any>;
  locale?: any;
  moment?: any;
  dayjs?: any;
  /** Name of the library that is used right now */
  lib: string;

  // constructor (options?: { formats?: DateIOFormats, locale?: any, instance?: any });

  date(value?: any): TDate | null;
  toJsDate(value: TDate): Date;
  parseISO(isString: string): TDate;
  toISO(value: TDate): string;
  parse(value: string, format: string): TDate | null;

  getCurrentLocaleCode(): string;
  is12HourCycleInCurrentLocale(): boolean;
  /** Returns user readable format (taking into account localized format tokens), useful to render helper text for input (e.g. placeholder). For luxon always returns empty string. */
  getFormatHelperText(format: string): string;

  isNull(value: TDate | null): boolean;
  isValid(value: any): boolean;
  getDiff(value: TDate, comparing: TDate | string, unit?: Unit): number;
  isEqual(value: any, comparing: any): boolean;

  isSameDay(value: TDate, comparing: TDate): boolean;
  isSameMonth(value: TDate, comparing: TDate): boolean;
  isSameYear(value: TDate, comparing: TDate): boolean;
  isSameHour(value: TDate, comparing: TDate): boolean;

  isAfter(value: TDate, comparing: TDate): boolean;
  isAfterDay(value: TDate, comparing: TDate): boolean;
  isAfterYear(value: TDate, comparing: TDate): boolean;

  isBeforeDay(value: TDate, comparing: TDate): boolean;
  isBeforeYear(value: TDate, comparing: TDate): boolean;
  isBefore(value: TDate, comparing: TDate): boolean;

  isWithinRange(value: TDate, range: [TDate, TDate]): boolean;

  startOfYear(value: TDate): TDate;
  endOfYear(value: TDate): TDate;
  startOfMonth(value: TDate): TDate;
  endOfMonth(value: TDate): TDate;
  startOfWeek(value: TDate): TDate;
  endOfWeek(value: TDate): TDate;

  addSeconds(value: TDate, count: number): TDate;
  addMinutes(value: TDate, count: number): TDate;
  addHours(value: TDate, count: number): TDate;
  addDays(value: TDate, count: number): TDate;
  addWeeks(value: TDate, count: number): TDate;
  addMonths(value: TDate, count: number): TDate;
  addYears(value: TDate, count: number): TDate;

  startOfDay(value: TDate): TDate;
  endOfDay(value: TDate): TDate;

  format(value: TDate, formatKey: keyof DateIOFormats): string;
  formatByString(value: TDate, formatString: string): string;
  formatNumber(numberToFormat: string): string;

  getHours(value: TDate): number;
  setHours(value: TDate, count: number): TDate;

  getMinutes(value: TDate): number;
  setMinutes(value: TDate, count: number): TDate;

  getSeconds(value: TDate): number;
  setSeconds(value: TDate, count: number): TDate;

  getDate(value: TDate): number;
  setDate(value: TDate, count: number): TDate;

  getMonth(value: TDate): number;
  getDaysInMonth(value: TDate): number;
  setMonth(value: TDate, count: number): TDate;
  getNextMonth(value: TDate): TDate;
  getPreviousMonth(value: TDate): TDate;

  getMonthArray(value: TDate): TDate[];

  getYear(value: TDate): number;
  setYear(value: TDate, count: number): TDate;

  mergeDateAndTime(date: TDate, time: TDate): TDate;

  getWeekdays(): string[];
  getWeekArray(date: TDate): TDate[][];
  getYearRange(start: TDate, end: TDate): TDate[];

  /** Allow to customize displaying "am/pm" strings */
  getMeridiemText(ampm: "am" | "pm"): string;
}

For library authors

If you are a library author that exposes date/time management utils or controls you may want to use date-io to interop with the most popular libraries. Here are some instructions on how to use date-fns as an adapter.

1. Install the bindings

First of all, it is required to provide the adapters for your users. We do not recommend installing the date-io directly by the end users, cause it may be easy to mismatch the version. The better way will be to reexport them.

Firstly install all the adapters you want to support and lock the version.

Yes, you will install all of them as dependencies. But every adapter is 50kb unpacked npm module and relieas to the library as for optional peer dependency. It won't be included in user bundle until user will choose which library he want's to use.

{
  "dependencies": {
    "@date-io/date-fns": "x.x.x",
    "@date-io/dayjs": "x.x.x",
    "@date-io/luxon": "x.x.x",
    "@date-io/date-fns-jalali": "x.x.x",
    "@date-io/jalaali": "x.x.x"
  }
}

2. Reexport the bindings

// you-awesome-lib/adapters/date-fns
export { default } from "@date-io/date-fns";

3. Use it for date-time management

Register it using your library context. It may be react context, dependency injection container or any other tool that allows the user to register the used library 1 time.

// react example
import { createMyAdapter } from "your-awesome-lib/adapters/date-fns";

<DateLibProvider adapter={createMyAdapter({ locale: "fr" })}>
  {/* ... */}
</DateLibProvider>;

And use the interface of date-io (or your custom interface).

import { IUtils } from "@date-io/core/IUtils";

function myFunctionInLibrary<TDate>(date: TDate, adapter: IUtils<TDate>) {
  // ...
  const weekArray = adapter.getWeekArray(Date);
  // ...
}

Overriding behavior

It is possible to change or extend the behavior of any adapter by simply inheriting and overriding the base class of utils while saving the same interface.

Let's say you want to override getYear to always return 2007 and getWeekdays to contain only 2 weekends (let's admit that this is what we all desire) you can do the following:

class CustomDateTime extends DayjsUtils implements IUtils<Dayjs> {
  getWeekdays = () => {
    const start = this.dayjs().startOf("week");
    return [0, 1].map((diff) => this.formatByString(start.add(diff, "day"), "dd"));
  };
}

Note: that you will need to do this with every adapter you want to support to be in sync.

Extending behavior

It is possible also to add custom functionality to the adapter, in case the author of date-io doesn't want to add it. To do this create your custom interface that extends IUtils and inherit your adapters both from the base adapter and your custom interface.

// your-awesome-lib/adapters/CustomAdapter
import { IUtils } from "@date-io/core/IUtils";
import DateIODateFnsAdapter from "@date-io/date-fns";

interface CustomUtils<TDate> extends  IUtils<TDate> {
  getDayOfYear(day: TDate): number;
}

interface CustomDateFnsUtils extends DateIODateFnsAdapter implements CustomUtils<Date> {
  getDayOfYear(day: Date): number {
    return getDayOfYear(day);
  }
}

Build system

In some build systems (hello babel) it may be impossible to extend the methods because they are defined as class properties. While it should be standardized already there are still some issues with it. In this case you can use the following workaround:

// you-awesome-lib/adapters/date-fns
import { CustomUtils } from "../CustomUtils";
import getDayOfYear from "date-fns/getDayOfYear";
import DateIODateFnsAdapter from "@date-io/date-fns";

export const createMyAdapter(options) {
  const dateFns = new DateFnsUtils(options)

  const adapter = {
    ...dateFns,

    getWeekArray() {
      const startDate = endOfWeek(adapter.endOfMonth(date), { locale: adapter.locale })
      const extraWeek = adapter.getWeekdays().map((day, index) => adapter.addDays(startDate, index + 1))
      return [...dateFns.getWeekArray(date), extraWeek]
    },

    // ...
  }

  return adapter
}

More Repositories

1

odiff

The fastest pixel-by-pixel image visual difference tool in the world.
OCaml
2,000
star
2

cypress-real-events

Fire native system events from Cypress.
HTML
754
star
3

blendr

The hacker's BLE (bluetooth low energy) browser terminal app
Rust
292
star
4

eslint-plugin-pretty-imports

Do you hate chaos in javascript imports?
TypeScript
204
star
5

subtitler

Free on-device web app for audio transcribing and rendering subtitles
ReScript
129
star
6

reason-date-fns

Reason binding for date-fns/fp. Curried by default :3
Reason
45
star
7

workspace-climate-control

Your own open-source CO2 sensor with ready to use TUI
C
37
star
8

material-ui-datetimepicker

[UNMAINTAINED] Wrapper for Material UI Date and Time picker components
JavaScript
31
star
9

hook2hoc

Typesafe converter of React hooks to React hocs πŸ€“
TypeScript
25
star
10

trololors

Not the fastest terminal colors library. Don't even ask about size.
Rust
16
star
11

caps-word.nvim

Much better way to type SCREAMING_SNAKE_CASE words aka constants
Lua
14
star
12

my-nvim-config

My neovim configs (terminal profile neovim 😱)
Lua
12
star
13

CV

The personal CV
TypeScript
8
star
14

ns-react-18next

[Unmaintained] Namespaced i18next localization in react with no tears
TypeScript
8
star
15

bots

TypeScript
2
star
16

date-fns-comparison

JavaScript
2
star
17

reasonml-for-geese-intro

Introduction in ReasonML
JavaScript
2
star
18

bff-workshop

JavaScript
2
star
19

testcon-cypress-demo

Demo project for testcon 2019
JavaScript
2
star
20

Kova-player

The most beautifull music player for Windows
C#
2
star
21

MeetingLiveStream

JavaScript
1
star
22

holyjs-component-testing-workshop

TypeScript
1
star
23

cypress-qa-fwdays-webinar

JavaScript
1
star
24

cypress-4-2020

Repository for the members of https://fwdays.com/en/event/cypress-workshop workshop
TypeScript
1
star
25

test_opam_windows

C
1
star
26

test-date-io-type

Test repo shows how to use @date-io/type
JavaScript
1
star
27

ina3222-pico

Python
1
star
28

odiff-debug

JavaScript
1
star
29

minecraft2024

1
star
30

fframes_ios_demo_project

Rust
1
star
31

chernihiv-it-cypress-demo

TypeScript
1
star
32

meeting-sticker

C
1
star