Moment.js vs Intl object
Tags: javascript
Published: 2020-09-28 15:26:08

Let's continue exploring Moment.js, observing it's most used features, and looking for feasible replacements for them.

In the previous post I briefly shows you how you can use `Intl.DateTimeFormat` to replicate some of the time formatting that MomentJS does, only it's built-in to the language and not a library you have to install.

It turns out that `Intl.DateTimeFormat` has a human readable way to specify formatting codes without having to memorize the %letter for each format code. Instead of using single letters preceded by percentage signs, you pass it a JSON object that has keys for each of month, year, minute etc. corresponding to each date or time unit, and a string value telling Javascript things how many digits to use, whether to use a short or long date/time format, whether to use uppercase or lowercase AM/PM times, and other information. Essentially, you create an object that looks like this:

```js
const options = {
   dateStyle: 'short',
   timeStyle: 'full',
}
```

Or this:

```js
const options = {
   hour12: true,
   day: 'numeric',
   month: 'long',
   year: '2-digit',
   minute: '2-digit',
   second: '2-digit',
};
```

And then you pass this object as the second argument to the `Intl.DateTimeFormat()` constructor. The first argument is a two-character locale that's used for making a language and country specific date string.

While Moment lets you make an arbitrary-looking string that you can plug date and time format units in, `Intl.DateTimeFormat` is more rigid and only lets you make date and time notations that are actually in circulation. If it saves code size, then this trade-off is all the better. So when you create a `DateTimeFormat` object you are not creating the actual date/time string but rather an object with fillers in them that's suitable for substituting them for a `Date` object. It is cleaner than sprinkling `%A, %h` and such all over the string, we now deal precisely with objects. To get a date/time string out of `DateTimeFormat` you must call its `date()` method and pass it a `Date` object.

It could be as simple as `Date()` to give it the current date, or you can specify the year, month, day, hours and others in the `Date()` constructor to pass it numbers for specific time units, left-most time unts being mandatory while right-most time units are optional. What I'm trying to say is, you can make a `Date` object with just month and year, without any day or finer grained time value, but you can't make a `Date` object giving it only a month without a year.

More information about [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) and [DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) at the provided MDN documentation.

---

Check out this momentJS snippet:

```js
moment().subtract(6, 'days').calendar();
```

Depending on the day of week and time of day, it will print something like `Last Friday at 9:35 PM`. We have already seen how to format date and time unit with `strftime`-like format codes, and if you don't remember what those are, now's a good time to consult your nearest `man 3 strftime` manpage for a list of them all.

The tricky part to calculate is the relative calendar day of week. Notice how it reads `Last Friday` not simply `Friday`. There is no format code for printing this sort of date unit. Instead, we have to rely on an elaborate handcrafted piece of code to do it for us, which is pretty much what MomentJS did all this time, but if you *don't* need to make relative calendar days of week, then not having this snippet in your deployed Javascript can save a lot of space.

Instead of trying to come up with a novel function to calculate this (I could even pull it straight out of MomentJS source code for time's worth), I'm going to opt for the opinion that if it becomes this complicated to print a relative calendar date and time, and you need that support, you are better off staying with MomentJS. It's not like it's going to disappear one day. Javascript *can* however print relative dates and times of the form `2 days ago`, `yesterday`, `tomorrow` and `2 days from now`: Just create an `Intl.RelativeTimeFormat` object.

(Credits: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/RelativeTimeFormat)
```js
const rtf = new Intl.RelativeTimeFormat("en", {
    localeMatcher: "best fit",
    numeric: "auto", // change to "always" if you want 1 day ago/from now
                     // instead of yesterday/tomorrow
    style: "long",
});

// Format relative time using negative value (-1).
rtf.format(-1, "day");
// > "1 day ago"

// Format relative time using positive  value (1).
rtf.format(1, "day");
// > "in 1 day"
```

*Important: English is not the only possible locale, any country code can be used.*

---

Next post, I'm going to talk about [Luxon](https://moment.github.io/luxon/index.html), a wrapper around the `Intl` object. We will see if this library can accomplish some of the things `Intl` failed to do, although it comes at a trade-off of bigger deployment code size, but less than MomentJS's, that's for sure.