As any experienced programmer knows, dates and times are incredibly common in most application-level code. You might use dates for tracking the creation of an object, to track the time since an event occurred, or to save the date of an upcoming event. However, dates aren't easy to work with, so it's important to have a library that is both accurate and has a simple interface. The standard JavaScript Date
object isn't too bad, but it lacks some important features and isn't always simple to work with.
In this article you'll see how Moment makes dates and times easy to parse, format, and manipulate.
Parsing Dates
Strings
By default, Moment attempts to parse date strings using the ISO 8601 format, which has a wide range of valid dates. You can specify as little, or as much, time precision as you want in your date-time using this format. This is great for us since dates can take on many different forms, depending on the level of detail you want to specify.
Considering trying to parse all of these different formats on your own:
- 20160628
- 2016-06-28T09
- 20160628T080910,123
- 2016-06-28 09:30:26.123
- 2016-06-28 09:30:26.123+07:00
As you can see, not only does the time precision change, but the format in which it is specified can widely vary, which is why it's so important to have a capable time parser.
First off, the simplest way to create a moment
object is to call the constructor with no arguments:
> const moment = require('moment');
> let m = moment();
This will instantiate a date object with the current time.
To parse a date-time string with Moment, just pass it to the constructor:
> let date = moment('2016-06-28 09:30:26.123');
If for some reason Moment isn't able to parse the string you gave it, then it will fall back to using the built-in new Date()
object for parsing.
To check if your date was parsed and valid, use the .isValid()
method:
> moment('2016-06-28 09:30:26.123').isValid();
true
> moment('derp').isValid();
false
For all date objects created with Moment, no matter how you parse or create them, the time zone in the object will default to the current time zone, unless specified directly. To get UTC times back, use moment.utc()
instead. For more information on time zones, check out the section Moment Time Zones.
Specifying Formats
One of my favorite parsing features in Moment is the string/format parser. It is basically like a reverse string formatter. You provide the date-time string to be parsed and another string that specifies the format that it's in. This way you can use strings of any format you want and still comfortably use them with Moment.
For example, in the US (for some reason) we like to format our dates as "Month/Day/Year", whereas much of the rest of the world formats theirs as "Day/Month/Year". This leaves a lot of room for confusion. For example, is the date "11/06/2016" supposed to be November 6th, or June 11th?
Image: John Harding/Mona Chalabi via The Guardian
So how will we know if your dates are being parsed correctly? Using format specifiers like this ensures there are no ambiguities in your dates, assuming you know beforehand which format they're in. In the following example we're still able to parse the correct dates despite the different, and potentially confusing, formats.
> let d1 = moment('11.06.2016', 'DD-MM-YYYY');
> let d2 = moment('06/11/2016', 'MM-DD-YYYY');
> d1.format(); // '2016-06-11T00:00:00-05:00'
> d2.format(); // '2016-06-11T00:00:00-05:00'
Notice that we also use different delimiters in our date strings, "." and "/". Moment actually ignores all non-alphanumeric characters when using these formats, so you don't always need to worry about matching the formats perfectly.
For a complete set of available formatting tokens, check out this section of the Moment.js docs.
Unix Timestamps
As you'd expect, Moment is also capable of parsing integer dates (Unix time) as well, either in the seconds or milliseconds format:
> moment.unix(1467128085); // Date in seconds from 1970
> moment(1467128085747); // Date in milliseconds from 1970
The only difference in the resulting times is the precision. millisecondDate
will have a non-zero value for the millisecond field.
Printing Dates
In my opinion, this is one of the more useful parts of Moment, mostly because JavaScript's built-in Date
object doesn't have very good support for it. Surprisingly, the only built-in way to do formatting with Date
is to use the Date.toLocaleDateString()
method, which feels pretty clunky and not as flexible:
> let d = new Date(1467128085747);
> let options = {
... weekday: 'long', year: 'numeric', month: 'short',
... day: 'numeric', hour: '2-digit', minute: '2-digit'
... };
> date.toLocaleTimeString('en-us', options);
'Tuesday, Jun 28, 2016, 10:34 AM'
With Moment, we can easily achieve this same formatting with only one line of code (which I'll show in the next section).
We'll break this down into a few subsections. First, we'll go over the traditional formatting with tokens, then we'll show the relative date formats available (like "18 minutes ago"), and finally we'll show how to format the dates as different kinds of structured data, like arrays, JSON, or a plain JavaScript Object
.
Formatting
Use the .format()
method to display the date as a string. Without any arguments, it prints the string in the ISO 8601 representation:
> let date = moment.unix(1467128085);
> date.format();
'2016-06-28T10:34:45-05:00'
Otherwise, you can provide your own format and customize it to your liking using tokens.
> date.format('dddd, MMMM Do YYYY, h:mm a');
'Tuesday, June 28th 2016, 10:34 am'
You might notice that this is the same representation as the Date.toLocaleTimeString()
example from above, but in one line. And all it took was the string dddd, MMMM Do YYYY, h:mm a
.
Again, the full list of format tokens can be found at Moment's very thorough documentation website.
Relative Formats
Oftentimes in web apps, for example, it's helpful to show the user how much time has elapsed since an event occurred. Instead of calculating this yourself, Moment offers some utility functions to handle this formatting for you.
In all cases, you can use any date from the past or future and the returned string will reflect the correct tense.
Out of the box, you get a few different options:
Time from now
Assuming today's date is July 1st, 2016, you'd get the following relative formatting:
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
> moment({year: 2016, month: 3, day: 13, hour: 10}).fromNow();
'3 months ago'
> moment({year: 2016, month: 9, day: 23, hour: 10}).fromNow();
'in 4 months'
You can optionally pass a Boolean
to .fromNow()
telling it whether or not to include the "ago" (or "in") string in the formatting. This way you can still easily customize the relative string if needed.
> moment({year: 2016, month: 3, day: 13, hour: 10}).fromNow(true);
'3 months'
Time from Date
> let may = moment({year: 2016, month: 5, day: 3});
> let october = moment({year: 2016, month: 10, day: 9});
>
> may.from(october);
'5 months ago'
> october.from(may);
'in 5 months'
Time to now
> moment({year: 2016, month: 3, day: 13, hour: 10}).toNow();
'in 3 months'
> moment({year: 2016, month: 9, day: 23, hour: 10}).toNow();
'4 months ago'
Time to Date
> let may = moment({year: 2016, month: 5, day: 3});
> let october = moment({year: 2016, month: 10, day: 9});
> may.to(october)
'in 5 months'
>
> october.to(may)
'5 months ago'
You might have noticed that both the "from" and "to" methods can be interchanged, depending on which dates are passed in the argument. It is all relative.
Structured Date-times
In some cases, it may be more convenient to have your date data in a structured format, possibly for use in an algorithm or serialization. Moment offers a few different ways to format the data in to data structures:
- toDate(): Returns the Moment date as a JavaScript
Date
- toArray(): Returns date data as an array -
[ 2016, 5, 28, 10, 34, 45, 747 ]
- toJSON(): Returns ISO date string adjusted to UTC - "2016-06-28T15:34:45.747Z"
- toISOString(): Returns ISO date string adjusted to UTC - "2016-06-28T15:34:45.747Z"
- toObject(): Returns a plain JavaScript
Object
with date data -{years: 2016, months: 5, date: 28, hours: 10, minutes: 34, seconds: 45, milliseconds: 747}
- toString(): Returns a formatted string similar to
Date.toString()
- "Tue Jun 28 2016 10:34:45 GMT-0500"
Manipulating Dates
The ability to manipulate dates is also pretty important for many applications. And this isn't as simple as your normal arithmetic either - manipulating dates is hard. Can you easily figure out these date/time additions/subtractions? It isn't an easy task to program.
- February 21st + 13 weeks
- 3:14am + 424 minutes
- July 1st - 1899400140 milliseconds
Now, what if it's a leap year? Or a year with a leap second? Lucky for you, you don't need to figure this out yourself. Moment already has for you.
There are quite a few time manipulation methods, so we'll only go over the more commonly used ones:
Adding/Subtracting
Use a number/string or an object to manipulate the date:
> moment().add(7, 'days');
> moment().subtract({days:13, months:3});
Chaining also works well:
> moment().add({hours: 7}).subtract(13, 'minutes');
Start/End of Time
These convenience methods set the date/time to the end of the given unit of time. For example, if you have a date with a time of 2:15, but you need it to be the start of the day, you'd use:
> moment().startOf('day');
This will set the time to 12:00am of the same day. The same works for year, month, hour, and many more.
> moment().endOf('year'); // sets date to 12-31-2016 23:59:59.999
I've found this to be very useful in reporting applications where users can select time frames for reports, like Google Analytics. In order to retrieve the correct data, you need to have the correct range.
Moment Time Zones
Moment supports setting time zone offsets out of the box, but if you need better time zone support, then you should consider using moment-timezone
.
This library lets you specify time zones by city, region, or other identifiers, which can make things much simpler for user-facing applications.
To use it, install with npm and require()
this in place of moment
:
> const moment = require('moment-timezone');
With over 550 time zone identifiers, you can split up your time zone specifiers by various regional categories and names:
- Time zone name: US/Central, US/Eastern, US/Mountain, etc
- City: America/Chicago, America/Los_Angeles, Asia/Dubai, Australia/Sydney, etc
- GMT Offset: Etc/GMT+6, Etc/GMT-2, Etc/GMT0, etc
For a full list of time zone identifiers, you can see a full list of names by executing:
> const moment = require('moment-timezone');
> moment.tz.names()
To use these identifiers to set the time and time zone with the .tz()
method:
> moment.tz({year: 2016, month: 6, day: 30, hour: 11}, 'America/Los_Angeles').format();
'2016-07-30T11:00:00-07:00'
> moment.tz({year: 2016, month: 6, day: 30, hour: 11}, 'America/Chicago').format();
'2016-07-30T11:00:00-05:00'
Conclusion
Programmatically working with dates and times is hard, but it doesn't have to be the hardest thing you do. Moment is a great example of a library making a difficult topic much simpler with a clean and easy-to-use API.
In addition to the parsing, formatting, and manipulation that Moment provides, there is also add-on support for time zones via the moment-timezone
package. Make life easier for yourself and your users by adding better support for time zones.
What other features of Moment do you use often? Let us know in the comments!