logoNavigate back to the homepage
ARCHETYPER

Moment

HPM

·

 
August 7th, 2020 · 6 min read

Moment

Overview

Time management is always a hot topic for discussion and it’s no different in case of Javascript applications! Managing date and time, especially when different formats and timezones are to be supported, requires a mature library in most applications.

Moment.js is a lightweight JavaScript library for parsing, validating, manipulating and formatting date and time values in browser and Node.js environments. Available as a JS package for most of the bundlers and as a JS script that can be imported directly from the CDN, moment.js seems to be a solid solution for all date-time needs.

Highlights

Getting started with moment.js is pretty easy. It can be installed by running the command npm install moment.

1/* Babel or ES6 */
2import moment from 'moment';
3
4/* Node or requireJs */
5
6var moment = require('moment');

Note: In 2.4.0, the globally exported moment object was deprecated. It will be removed in next major release.

Moment operates by creating a wrapper over the JS’s native Date.prototype object and the object returned is mutable.

Parsing and Extract

Getting the current date-time is as simple as instantiating a moment object. The returned value will be a Moment object, from which a simple date and time string can be extracted using toString() method.

const current = moment(); // current.toString() = "Wed Jul 29 2020 10:44:07 GMT+0530"

Other methods in the Moment object can be used to extract the values of specific parts or to set them. Some of the commonly used ones are listed below:

MethodDescriptionExample for GetExample for Set
second([Number]), seconds([Number])Manipulate seconds from 0 to 59. If range is beyond limits, manipulates the minutemoment().second() - 49moment().second(10) - 10
minute([Number]), minutes([Number])Manipulate minutes from 0 to 59. If range is beyond limits, manipulates the hourmoment().minute() - 15moment().minute(10) - 10
hour([Number]), hours([Number])Manipulate hours from 0 to 23. If range is beyond limits, manipulates the daymoment().hour() - 16moment().hour(25) - Sets hour value of 1 for next day
date([Number]), dates([Number])Manipulate date from 1 to 31. If range is beyond limits, manipulates the monthmoment().date() - 12moment(“2020-08-03”).date(-5) - Sets date 5 days behind, to July 26 2020
day([Number or String]), days([Number or String])Manipulate day from 0 to 6. If range is beyond limits, manipulates the weekmoment().day() - 1moment().day(5) - 5 (Saturday)
month([Number or String]), months([Number or String])Manipulate month from 0 to 11. If range is beyond limits, manipulates the yearmoment().month() - 8moment().month(10) - 10
year([Number]), years([Number])Manipulate year from -270,000 to 270,000moment().year() - 2020moment().year(1970) - 1970
weekday([Number])Same as the day method mentioned above but locale aware. For example, if Monday is start day for the current locale, 0 relates to Monday.moment().weekday() - 1moment().weekday(1) - 1 (Tuesday for locale having first day as Monday)
get(String), set(String, Int), set(Object(String, Int))Get or Set values based on parameter valuemoment().get(‘year’) - 2020moment().set(‘millisecond’, 100) - 100, moment().set({ year: 2020 }) - 2020
min(Moment[,Moment…]), min(Moment[])Get the minimum (most distant past) of the moment instances givenmoment.min(moment(‘1996-12-13’), moment(‘1996-12-15’)).toString() - Fri Dec 13 1996 00:00:00 GMT+0530
max(Moment[,Moment…]), max(Moment[])Get the maximum (most distant future) of the moment instances givenmoment.max(moment(‘1996-12-13’), moment(‘1996-12-15’)).toString() Sun Dec 15 1996 00:00:00 GMT+0530

Moment provides powerful date-time parsing and supports all ISO 8601 strings. Formats not defined under this ISO specification are also supported but it depends on the browser’s ability to parse that format via the native Date object.

Couple of ways to parse a date-time string or object are as below. Do note that by default, Moment provides timestamps in local mode. If you need it in UTC, it has be explicitly specified.

Without format specifier

Some examples for simple parsing would be:

1moment("2020-12-30"); // Mon Dec 25 1995 00:00:00 GMT+0530
2
3moment("2020W065"); // Basic (short) ISO8061 format with week and weekday - week 6 and 5th day in 2020 - Fri Feb 07 2020 00:00:00 GMT+0530
4
5moment(2318788876406) // Unix Epoch value - Thu Jun 25 2043 01:31:16 GMT+0530
6
7moment({ year:1996, month:11, day:13, hour:23, minute:10, second:30, millisecond:123}) // Fri Dec 13 1996 23:10:30 GMT+0530 - Observe that month starts from 0!
8
9moment(new Date(1996, 11, 13)) // JS Date object - Fri Dec 13 1996 00:00:00 GMT+053
10
11moment([2020, 6, 29]) // Wed Jul 29 2020 00:00:00 GMT+0530 - Format is [year, month, day, hour, minute, second, millisecond]
12
13moment.utc("1996-12-13", "YYYY-MM-DD") // In UTC - Fri Dec 13 1996 00:00:00 GMT+0000
14
15moment.parseZone("2020-01-01T00:00:00+08:00") // With specified timezone - Wed Jan 01 2020 00:00:00 GMT+0800

With format specifier

The format of the value to be parsed can be specified for better parsing and the supported format strings can be found here

1moment('24-12-2020 11:15:00', "DD-MM-YYYY hh:mm:ss", true) // Thu Dec 24 2020 11:15:00 GMT+0530

In normal parsing, the non numeric characters are usually ignored when format is specified! This essentially means moment("13-12-1996", "DD-MM-YYYY) is treated the same as moment("13/12/1996", "DD-MM-YYYY). This might provide inconsistent results when parsing both time and date and hence strict mode is recommended to be used by passing a true boolean as the last parameter as shown in the example above.

Multiple formats can also be provided to the parser, in which case Moment will try to parse the value by picking the parser from the list provided in a sequential manner and use the first valid one it finds from the list

moment("13-12-1996", ["MM-DD-YYYY", "DD-MM", "DD-MM-YYYY"]) // Uses the last format in the list

Locale can also be specified for the parsing by providing a locale string

moment('2020 juillet', 'YYYY MMM', 'fr') // Wed Jul 01 2020 00:00:00 GMT+0530

Explicit special formats can also be specified for parsing like ISO 8061

moment("1996-12-13T05:06:07", ["YYYY", moment.ISO_8601, "YYYY-MM-DD"])

Detecting invalid dates

Moment provides a simple isValid() method to determine if a valid date object is provided. For example

1moment("2020 13", "YYYY MM").isValid() // false (invalid month)
2
3moment("2020 13", "YYYY MM").invalidAt() // 1 - Since the invalid month provided here (13) is the 2nd value, it's position index of 1 is returned
4
5moment("2020 12", "YYYY MM").isValid() // true

Since the parser is not very strict, unexpected behaviors/issues can crop up like:

moment('2020 is a date', 'YYYY-MM-DD').isValid() // true as 2016 matches YYYY

Hence, extra care should be taken while checking validity with Moment and strict mode is advised to be used always.

Manipulations

Moment supports almost all of your date time manipulations used regularly like add, subtract, min and max. Some of the unique ones include start of time and end of time. Examples below showcase some of the ways you can manipulate the values of the moment object you possess!

Add and Subtract

1moment().add(7, 'days') // Add 7 days to current date - Tue Aug 04 2020 to Tue Aug 11 2020
2
3moment().add({ days:7, months: 2}) // Add 7 days and 2 months to current date - Tue Aug 04 2020 to Sun Oct 11 2020
4
5moment().subtract(1.5, 'months') // Subtract one and half months from today's date - Tue Aug 04 2020 to Thu Jun 04 2020

If you pass decimal values to the duration values of days or months above, it will be rounded to the nearest integer. Weeks, quarters and years are converted to days first and then rounded.

Start and End of time

These are interesting methods which mutate the original moment object value to start or end value of the unit specified.

1moment().startOf('year') // Sets to January 1st 12:00 am of the current year - Wed Jan 01 2020 00:00:00
2
3moment().endOf('year') // Sets to December 31st 11:59 pm of the current year - Thu Dec 31 2020 23:59:59

Local and UTC values

The local and UTC values for a moment object and the offsets of the same can be obtained as shown below. The local and utc methods set a flag in the original moment object and all further retrieval from them will give the corresponding values.

1const utcDate = moment.utc("2020-01-13 15:00")
2utcDate.hours() // 15
3utcDate.minutes() // 0
4utcDate.local() // GMT +05:30
5utcDate.hours() // 20
6utcDate.minutes() // 30
7
8const localDate = moment("2020-01-13 15:00") // GMT +05:30
9utcDate.hours() // 15
10utcDate.minutes() // 0
11utcDate.utc()
12utcDate.hours() // 9
13utcDate.minutes() // 30
14
15moment().utcOffset() // Get offset from utc in minutes - 330 for GMT+05:30
16moment().utcOffset(8); // Set offset in hours when value in argument is less than 16 or greater than -16, else in minutes, on the moment object returned.

Formatting and Querying

Formatting and querying is one of the most used features, especially in front-end applications, for fetching and displaying date-time values in the required format. One of the core functions offered by moment is format. It accepts the format string (whose supported values can be found here) and returns the value accordingly. If no string is passed, it returns in ISO8601 format as standard. This standard format can also be overridden by setting the format string to the defaultFormat property of the moment object.

1moment().format() // ISO8601 format - 2020-08-05T10:09:02+05:30
2
3moment().format('dddd, MMMM Do YYYY, h:mm:ss') // Wednesday, August 5th 2020, 10:10:27
4
5moment().format('[The time is ] h:mm:ss a') // The time is 10:10:27 am

Though moment offers many more formatting options, some of the uncommon ones are:

1moment().toArray() // [2020, 7, 5, 10, 18, 36, 699]
2
3moment().toDate() // Returns JS's native Date object
4
5moment().toObject() // { "years": 2020, "months": 7, "date": 5, "hours": 10, "minutes": 21, "seconds": 5 "milliseconds": 108 }
6
7moment().inspect() // Returns the string form of the moment command that can be used to get a value - moment("2020-08-05T10:22:30.574")

Some of the useful query methods offered by moment include:

1moment('2020-10-20').isBefore('2010-10-30') // false
2moment('2015-10-20').isBefore('2020-01-01', 'year') // true
3
4moment('2020-05-20').isSame('2020-05-20') // true
5
6moment('2021-10-20').isAfter('2020-10-15') // true
7
8moment().isDST() // Checks if the current date-time is in daylight saving time
9
10moment().isLeapYear() // Checks if the year in the moment object is a leap year
11
12moment.isDate(new Date()) // true - Checks if the object in the parameter is a native JS Date object

Other Utilities

Moment also provides support of internationalization, plugins to handle date-time in formats of common languages like Java, custom values for locales and operating in terms of durations. Let’s take a sneak peek into the few tricks Moment has to offer!

i18n

Internationalization is natively supported by Moment and global and object specific local settings are allowed. The global locale setting can be done using the locale method. Note that the package comes with en-us locale as default and others have to be manually loaded. Many of the commonly used locales are readily available for usage in the package sources under locale directory and only requires the file to be loaded during runtime.

pro-tip: Getting rid of the locales you don’t need from the package sources helps reduce the bundle size!

moment.locale("fr", { ...configObject }) //setting French locale

The keys to be passed to the configObject above can be found over at moment docs.

The objects loaded before setting the locale globally will not be affected by the locale change. A list locales can also be specified and moment will pick the first available one for use. In case of locale specifier substrings like en-au, Moment will search from most-specific to least-specific and load the most apt one it finds.

moment.locale(['gb', 'fr']) // will use fr when gb has not been already loaded

For changing locales for specific objects, the locale method on the instance can be called:

1var tempLocale = moment()
2tempLocale.locale('fr') // changes locale only for this instance

Loading locale files varies as per the platform. Please check the Moment docs for your platform of choice.

The info of the currently loaded locale can be fetched using the localeData method:

moment.localeData('fr') // returns the config of the fr locale

Locale Customization

Many of the values and attributes of the package can be easily modified by using the locale method and it is recommended that you define your own custom locale and load it if you are gonna end up modifying an existing locale extensively for your use case. You can also use updateLocale method if you are looking into modifying only specific aspects of a locale.

For example, to update the minimal weekday abbreviations (the shorter form of a weekday, like “Sun” for Sunday) for a locale, you can do:

1moment.updateLocale('en', {
2 weekdaysMin : ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
3 }
4);

One of the aspects you might find useful to update would be the message returned for an Invalid date.

1moment.updateLocale("en", {
2 invalidDate: "INVALID_DATE"
3});

Similarly, almost all aspects can be modified for a locale and we highly recommend spending some time in setting up a custom locale extending a base locale or modifying your existing one to suit your use cases at the beginning of your project.

Duration Objects

Moment can be used to create and use duration objects. As their name suggests, they contain time or date durations as the values instead of the typical date-time values. For example:

moment.duration(3, 'months') // returns an object containing 3 months duration value

Like normal moment objects, these can be manipulated with add, subtract and similar other methods. One of the interesting aspects of this is the humanize method. This method returns the durations in humanized or plain english way. For example, a duration of 1 month in a humanized way would look like:

moment.duration(1, "month").humanize() // "a month"

This method also supports suffixes as shown below:

1moment.duration(5, "minutes").humanize(true) // "in 5 minutes"
2
3moment.duration(-5, "minutes").humanize(true) // "5 minutes ago"

Plugins

Though not extensive, Moment does support plugins and you can find quite a few by the community, helping you tackle those niche use cases which plain moment can’t cater for!

Most of the plugins revolve around providing support for different calendars like Jalaali, Hijri etc., language specific date-time formats like Strftime, MSDate, Java DateFormat etc. and catering for very specific use cases like getting Twitter’s time format or checking if a given date is a German holiday.

We recommend checking out the list of plugins in the official documentation along with simply doing a Google search for finding a plugin to cater for your specific needs.

Extension

If you are looking into extending or writing custom parsers for Moment, then some of the methods like normalizeUnits (allows normalizing aliases of unit enums) and invalid (allows creation of invalid Moment objects) are provided. As these are not used extensively for regular use cases, we recommend checking out Moment’s Documentation if you are looking for more info on these.

Conclusion

Moment is a powerful and extensive date-time management library that can more-or-less handle any type of manipulation or querying you may throw at it! Operating as a wrapper on top of the native JS Date object, it follows a OOPS approach to handling the objects. Though it does not compromise on it’s feature set, it does have it’s fair share of drawbacks like very bulky package size, little to no support for tree-shaking and mutability of the objects.

The set of functionalities we have covered in this guide is in no means exhaustive and the package has many more interesting and unique features up its sleeve. Do check it’s docs and guides to get the complete picture!

In conclusion, if you are seeking a specific functionality that exists only in Moment or your project already uses it, then it’s better to stick to it, otherwise we can look into more simpler but performant libraries like date-fns (Our in-depth guide and review for date-fns can be found here) .

Evaluation metrics

CategoryRatingsDeliberations
Ease of useAverageSimple code to get or manipulate date objects, can even be included as a js script from their CDN, lot of deprecations and keywords in method params to be aware of, enabling tree-shaking requires good amount of effort
CommunityGoodWidely used and many active threads on popular forums, frequently updated with latest publish 2 months ago
Active usageGoodMore than 17M weekly and 58M monthly downloads and ~44K dependant packages on NPM
VulnerabilitiesGoodNo vulnerabilities reported by npm audit for v2.27.0 as of publishing
Docs and TrainingsAverageExtensive docs and guides provided by moment, lack of good structuring of content based on topics and lot of cross-referencing between guide and docs makes it tedious to refer

Check out the package and some reading materials

Video review of the package

Video review of the package with interesting use cases and in-depth exploration of the features coming soon! Check out Unpackaged Reviews for more related content.

Disclosures

The content and evaluation scores mentioned in this article/review is subjective and is the personal opinion of authors at Unpackaged Reviews based on everyday usage and research on popular developer forums. They do not represent any company’s views and is not impacted by any sponsorships/collaboration.

Header Photo by NeONBRAND on Unsplash

More articles from Unpackaged Reviews

Tailwind

Build great interfaces without breaking a sweat

August 1st, 2020 · 4 min read

Date-fns

Instantly solve all your date-time issues!

July 28th, 2020 · 4 min read
© 2020-2021 Unpackaged Reviews
Link to $https://twitter.com/unpakgd_reviewsLink to $https://unpackaged-reviews.medium.com