DEV Community

Ali Sherief
Ali Sherief

Posted on

Moment.js vs Intl object

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:

const options = {
   dateStyle: 'short',
   timeStyle: 'full',
}
Enter fullscreen mode Exit fullscreen mode

Or this:

const options = {
   hour12: true,
   day: 'numeric',
   month: 'long',
   year: '2-digit',
   minute: '2-digit',
   second: '2-digit',
};
Enter fullscreen mode Exit fullscreen mode

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 and DateTimeFormat at the provided MDN documentation.


Check out this momentJS snippet:

moment().subtract(6, 'days').calendar();
Enter fullscreen mode Exit fullscreen mode

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)

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"
Enter fullscreen mode Exit fullscreen mode

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


Next post, I'm going to talk about Luxon, 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.

Discussion (0)