Dealing with Dates and Times: Alternatives to Moment.js
Tags: javascript, momentjs
Published: 2023-03-06 02:56:06

by [Ejiro Thankgod](https://blog.openreplay.com/authors/ejiro-thankgod)

One of the most crucial elements when setting up an application to be utilized in various languages is date formatting. Nobody wants to work with the Date object and end up breaking them; instead, they search for ready-made libraries that allow easy management of dates.

[Moment.Js](https://momentjs.com/) is one of the most popular JavaScript frameworks to format and modify dates, a date library for JavaScript that can parse, validate, manipulate, and format dates. However, some aspects of this library, such as its size or organizational style, can cause you to wonder if any better options are available.

We'll examine five Moment.js substitutes for date internationalization in this article:

- JavaScript Internationalization API
- Day.js
- Luxon
- Date-fns
- Temporal


## JavaScript Internationalization API

The [ECMAScript Internationalization API's](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) namespace is represented by the global object `intl`. This object has the following constructors for dates:

- Date and time formatting is defined by `Intl.DateTimeFormat()`.
- Dates and timestamps can be expressed using the language-sensitive, readable words defined by `Intl.RelativeTimeFormat()`.
- Objects with the `Intl.Locale()` constructor represents a Unicode locale identification.
- The `Intl.NumberFormat()` constructor for objects makes language-sensitive number formatting possible.

These constructors accept two optional inputs: the locale and an object containing options to change the output. For instance:

```javascript
const timeFormat = new Intl.RelativeTimeFormat("de-DE");
const dateFormat = new Intl.DateTimeFormat("da");
```

According to BCP 47, the locale parameter is a string that provides a language tag, which consists of the following components: 

- Language code (ISO 639-1/639-2). 
- Script code (ISO 15924). 
- Country code (ISO 3166).
- Variant (from iana.org), search for "Type: variant"). 
- Extensions (from Unicode, more detail here). 

Among the choices made available by `Intl.DateTimeFormat` is the date's format (`full`, `long`, `medium`, and `short`), whether to use a 12- or 24-hour time, and how to format the representation of various aspects of the day, such as the year, month, weekday, etc.

All the parameters you can use to customize this object are described in further detail on the Intl.DateTimeFormat documentation page.

The object only has the following fields for Intl.RelativeTimeFormat:

- The recommended locale-matching algorithm is `localeMatcher`. `Lookup` (from the most specific to the least specific; if `en-us` is unavailable, `en` is picked) and `best fit` are two possible values (the default value, if `en-us` is not available, something like `en-uk` can be chosen).
- to format the output message as numeric. The only two options are `always` (for instance, `2 hours ago`) or `auto`, which occasionally disallows the output of numeric numbers (for example, `yesterday`).
- `style` to format the output message's length. `Long`, `short`, and `narrow` are potential values.

The methods `format()` and `formatToParts()` can format a date after you have an object of type `Intl.DateTimeFormat` or `Intl.RelativeTimeFormat`. The latter function returns an array containing the output's parts.

When using `Intl.DateTimeFormat`, the formatting methods use the Date object as input:

```javascript
const date = new Date(Date.UTC(2022, 7, 30, 3, 5, 0));
const options = {
  hour12: true,
  day: "numeric",
  month: "long",
  year: "2-digit",
  minute: "2-digit",
  second: "2-digit",
};
// Sample output: 30 Αυγούστου 22, 05:00
console.log(new Intl.DateTimeFormat("el", options).format(date));
// Sample output: 30. August 22, 05:00
console.log(new Intl.DateTimeFormat("de-AT", options).format(date));
```

If you only enter a few date-time elements in the options object, you will see the following in the output:

```javascript
const date = new Date(Date.UTC(2022, 7, 30, 3, 5, 0));
const options = {
  day: "numeric",
  year: "2-digit",
};
// Sample output: 22 30
console.log(new Intl.DateTimeFormat("el", options).format(date));
```

The `format()` function for `Intl.RelativeTimeFormat` requires two arguments: the number to be included in the message and its unit (such as the `year` or `second`, in either the singular or plural form):

```javascript
const options = {
  localeMatcher: "best fit",
  numeric: "auto",
  style: "short",
};
// Output: sem. ant.
console.log(new Intl.RelativeTimeFormat("es-ES", options).format(-1, "week"));
// Output: 上個月
console.log(new Intl.RelativeTimeFormat("zh-TW", options).format(-1, "month"));

```

A further distinction between utilizing the `always` and `auto` values for the numeric property is as follows:
   
```javascript
// Output: in 1 day
console.log(
  new Intl.RelativeTimeFormat("en", { numeric: "always" }).format(1, "day")
);
// Output: tomorrow
console.log(
  new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(1, "day")
);

```

You can try to change all of the examples above; however, you could have some issues with your browser.
    Modern browsers provide good support for most `Intl.DateTimeFormat`, but only Chrome 71 and Firefox 70 completely support `Intl.RelativeTimeFormat` (Neither Safari nor Edge currently supports it).


## Day.js
![--](https://paper-attachments.dropboxusercontent.com/s_7B494AE3CD97BB0665B39878DE60CF9809F6ECC07534CF8A0A12FA409ABD7275_1671545937063_Screenshot+from+2022-12-20+15-18-45.png)


[Day.Js](https://day.js.org/) is a lightweight library substitute for Moment.js. Day.js, by default, uses the American English locale. Other localities must be imported in the following way to be used:

```javascript
 <script src= "path/to/dayjs/locale/de"></script>
    <script>
      dayjs.locale('de') // use locale globally
      dayjs().locale('de').format() // use locale in a specific instance
    </script>
```

The `format()` method in the example above returns a string that contains the formatted date. To format the date in a certain way, a string including the tokens might be used:

```javascript
// Sample output: September 2022, Freitag
console.log(dayjs().locale(localeDe).format("MMMM YYYY, dddd"));
```

The additional functionality of Day.js is mostly provided by plugins, which you can load following your requirements. The `UTC` plugin, for instance, includes the following methods to obtain a date in UTC and local time:

```javascript
let utc = require("dayjs/plugin/utc");
dayjs.extend(utc);

dayjs().format(); //2022-09-03T17:11:55+08:00

dayjs.utc().format(); // 2022-09-03T09:11:55Z
```

The `advancedFormat`, `localizedFormat`, `relativeTime`, and `Calendar` plugins can all be used for internationalization.

The `format()` method now has more formatting choices thanks to the `advancedFormat` and `localizedFormat` plugins (you can view all the options on the plugins documentation page):

```javascript
let advancedFormat = require("dayjs/plugin/advancedFormat");
dayjs.extend(advancedFormat);

dayjs().format("Q Do k kk X x");

let localizedFormat = require("dayjs/plugin/localizedFormat");
dayjs.extend(localizedFormat);

dayjs().format("L LT");

let relativeTime = require("dayjs/plugin/relativeTime");
dayjs.extend(relativeTime);

dayjs().from(dayjs("1999-01-01")); // in 23 years
```


To display calendar time, the `Calendar` plugin introduces the calendar method (within a distance of seven days). The output doesn't appear to be localized:

```javascript
let calendar = require("dayjs/plugin/calendar");
dayjs.extend(calendar);

dayjs().calendar(dayjs("2022-09-03"));
dayjs().calendar(null, {
  sameDay: "[Today at] h:mm A", // The same day ( Today at 2:30 AM )
  nextDay: "[Tomorrow at] h:mm A", // The next day ( Tomorrow at 2:30 AM )
  nextWeek: "dddd [at] h:mm A", // The next week ( Sunday at 2:30 AM )
  lastDay: "[Yesterday at] h:mm A", // The day before ( Yesterday at 2:30 AM )
  lastWeek: "[Last] dddd [at] h:mm A", // Last week ( Last Monday at 2:30 AM )
  sameElse: "DD/MM/YYYY", // Everything else ( 17/10/2025 )
});
```


## Luxon
![--](https://paper-attachments.dropboxusercontent.com/s_7B494AE3CD97BB0665B39878DE60CF9809F6ECC07534CF8A0A12FA409ABD7275_1671545691260_Screenshot+from+2022-12-20+15-14-36.png)


[Luxon](https://moment.github.io/luxon/#/) was developed by one of Moment's maintainers and incorporates many of its principles while making specific improvements. Luxon can be considered an internationalization wrapper for `Intl.DateTimeFormat` and `Intl.RelativeTimeFormat`.

To format dates when adhering to a locale, for instance, use the method `toFormat(fmt:string, opts: Object)` and date-time tokens from this table after first establishing the locale:

```javascript
// Sample output: 2022 августа
console.log(luxon.DateTime.local().setLocale("ru").toFormat("yyyy MMMM"));
```

The options object, which the method accepts as an argument, can also contain the locale:

```javascript
// Output: 2021 октября
console.log(
  luxon.DateTime.local(2021, 10).toFormat("yyyy MMMM", { locale: "ru" })
);
```

Alternatively, you can specify the locale at creation time if you're using methods like Object, ISO, Format, HTTP, or RFC2822:

```javascript
const date = luxon.DateTime.fromISO("2021-10-19", { locale: "it" });
// Output: 2021 ottobre 19
console.log(date.toFormat("yyyy MMMM dd"));
```

However, using the methods `toLocaleString()` and `toLocaleParts()`, which produce an array containing the different components of the string and a localized string representing the date, respectively, is advised.

These methods use the same parameters (along with some presets, like `DateTime.DATE_SHORT`), making them similar to the `Intl.DateTimeFormat` methods `format()` and `formatToParts()` among others).

```javascript
const date = luxon.DateTime.utc(2021, 10, 1, 9, 3, 1);
const options = {
  hour12: true,
  day: "numeric",
  month: "long",
  year: "2-digit",
  minute: "2-digit",
  second: "2-digit",
};
// Output: 1 octobre 21, 03:01
console.log(date.setLocale("fr").toLocaleString(options));
// Output: 1. Oktober 21, 03:01
console.log(date.setLocale("de-AT").toLocaleString(options));
/* Output: [{"type": "day", "value": "1"},{"type": "literal", "value":" "},{"type": "month", "value": "ottobre"},{"type": "literal", "value":" "},{"type": "year", "value": "21"},{"type": "literal", "value": ","},{"type": "minute", "value": "03"},{"type": "literal", "value": ":"},{"type": "second", "value": "01"}] */
console.log(
  JSON.stringify(date.setLocale("it").toLocaleParts(options), null, 3)
);
// Output: 9:03 AM
console.log(date.toLocaleString(luxon.DateTime.TIME_SIMPLE));
// Output: 10/01/2021
console.log(date.toLocaleString({ locale: "pt" }));
```

The only methods that provide functionality similar to `Intl.RelativeTimeFormat` are `toRelative` (which, by default, returns a string representation of a given time relative to now) and `toRelativeCalendar` (which, by default, returns a string representation of a given date relative to today).

```javascript
// Sample output: 0 seconds ago
console.log(luxon.DateTime.local().plus({ days: 0 }).toRelative());
// Sample output: today
console.log(luxon.DateTime.local().plus({ days: 0 }).toRelativeCalendar());
```

The only issue with the methods mentioned above is that they won't be localized, unlike `Intl.RelativeTimeFormat`, If your browser doesn't support the stated API.



<h2>Session Replay for Developers</h2> 
<p><em>
Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data
</em></p>
<img alt="OpenReplay" width="768" height="400" src="https://blog.openreplay.com/assets/overview_Z17Qdyg.png" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em>




## Date-fns
![--](https://paper-attachments.dropboxusercontent.com/s_7B494AE3CD97BB0665B39878DE60CF9809F6ECC07534CF8A0A12FA409ABD7275_1671545874686_Screenshot+from+2022-12-20+15-17-33.png)


[Date-fns](https://date-fns.org/) is another well-liked JavaScript package for date processing and formatting. To utilize the most recent version, v2, directly in a browser, you'll need to use a bundler like Browserify because it is only available as an NPM package now.

This library has about sixty separate locations. To use one or more of them, you must import locales in this manner: 

```javascript
import { es, enCA, it, ptBR } from "date-fns/locale";
```

The following functions accept a locale as a parameter: 

- `format`, which accepts a date, a string specifying the formatting pattern (based on the date field symbols of Unicode Technical Standard #35), and an object containing parameters like the locale and the index of the first day of the week as arguments and returns the formatted date.
 
- The `formatDistance` function returns the distance in words between the supplied dates after accepting as inputs the dates to compare and an object containing options such as the locale or whether to include seconds.


- Similar to `formatDistance`, `formatDistanceStrict` does not employ any aids, such as almost, over, or less than. Features of the available options objects enable one to round fractional units and force a time unit.


- `formatRelative` is a format that displays a date in words in relation to a base date. It can also accept an options object as input to set the position and index of the beginning day of the week.

Here are a few instances:

```javascript
import {
  format,
  formatDistance,
  formatDistanceToNow,
  formatDistanceStrict,
  formatRelative,
  addDays,
} from "date-fns";

// Output: febrero / 23
console.log(format(new Date(), "MMMM '/' yy", { locale: es }));

// The Output: za manje od 10 sekundi
console.log(
  formatDistance(
    new Date(2022, 9, 1, 0, 0, 20),
    new Date(2022, 9, 1, 0, 0, 15),
    { locale: srLatn, includeSeconds: true, addSuffix: true }
  )
);


// Output: aproape 2 ani (assuming now is 15/02/2023 12:00)
 console.log(formatDistanceToNow(new Date(2021, 2, 15), { locale: ro }));


// Output: un minuto
console.log(
    formatDistanceStrict(
      new Date(2022, 9, 1, 0, 1, 20),
      new Date(2022, 9, 1, 0, 0, 15),
      { locale: it, unit: "minute"}
    ):

// Output: morgen om 18:38
console.log(
  formatRelative(addDays(new Date(), 1), new Date(), { locale: nlBE })
);
```

`FormatRelative` is frequently used in conjunction with helpers to add or remove various time units, such as `addWeeks`, `subMonths`, and `addQuarters`, among others.

Also, keep in mind that `formatRelative` will return the date provided as the first argument if the gap between the dates is greater than six days:

```javascript
// If today is September 10, 2022 the output will be 17/09/2022
console.log(
  formatRelative(addDays(new Date(), 7), new Date(), { locale: ptBR })
);
```

## Temporal
![--](https://paper-attachments.dropboxusercontent.com/s_7B494AE3CD97BB0665B39878DE60CF9809F6ECC07534CF8A0A12FA409ABD7275_1671546034747_Screenshot+from+2022-12-20+15-20-24.png)


[Temporal](https://tc39.es/proposal-temporal/docs/now.html) API for JavaScript aims to completely fix dates by adding a new global object named Temporal. Although relatively new, it's said to have provided different ECMAScript classes for scoped use cases like time-only, date-only, and others. Incorrectly assuming 0, UTC, or the local time zone for truly unknown values will result in problems and make code harder to read.

### Temporal API
The majority of Temporal's APIs are divided into  `plain` and  `zoned` versions, which is the first significant difference that will stand out. A `plain` date/time represents a date or time without a timezone indication, which is the only distinction between these two types. On the other hand, a `zoned` date/time represents a certain date and time in a particular timezone. There are a couple of APIs we'd cover, which we'll discuss below.

### PlainDateTime
Given that it just provides a date and time without taking the time zone into account, the `PlainDateTime` object is one of the simplest objects to comprehend. Using the `Temporal.Now.plainDateTimeISO` method is the simplest approach to creating a new `PlainDateTime`.

```javascript
const today = Temporal.Now.plainDateTimeISO();
console.log(today.toString());
// 2022-09-03T14:17:35.306655305
```

### TimeZone
A particular time zone is represented by the `TimeZone` data type. There are two most applied techniques for this, and `Temporal.Now.timeZone` is one of them, then you have the `from` technique. Now, You might use the constructor instead of the from method.


```javascript
const timeZone = Temporal.TimeZone.from("Africa/Cairo");
console.log(timeZone.toString());
// Africa/Cairo

const localTimeZone = Temporal.Now.timeZone();
console.log(localTimeZone.toString());
// America/Chicago
```

### Calendar
The last data type you need to understand, and maybe the least, is the `calendar` data type. A calendar can be created using the constructor or, if you prefer, the `from` method.

```javascript
const calendar = Temporal.Calendar.from("iso8601");
console.log(calendar.toString());
// iso8601
```

In terms of simplicity, the Temporal API fits well to cover dates and times. Although still in the testing phase, its founder urges developers to keep experimenting with its API to fix its bugs as the next generational library for solving internationalization problems with simplicity.

Although there are presently no browsers that support this API in any way, if you want to start utilizing it right away, you can use a polyfill@js-temporal/polyfill. You can use the temporal API as soon as you install this package.

## Conclusion

Moment.js is a strong and established library for handling dates. However, aside from the fact that it's no longer in development, it could be excessive for some projects. In this article, I've analyzed four well-known libraries' approaches to date formatting in the context of internationalization in this article.

The JavaScript Internationalization API's features might be sufficient for straightforward use cases. Still, if you require a higher-level API (for instance, for relative times) and additional features like timezones or helper methods for adding or removing units of time, you might want to take a look at one of the other libraries examined in this article.


## Resources
- [Moment.Js](https://momentjs.com/)
- [Day.Js](https://day.js.org/)
- [Luxon](https://moment.github.io/luxon/#/)
- [Date-fns](https://date-fns.org/)
- [JavaScript Internationalization API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl)
- [Temporal](https://tc39.es/proposal-temporal/docs/now.html)



[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)