I was privileged to represent the JS Foundation at TC39 in January of 2017. Check out this report on the JS foundation blog.
It was awesome to see everybody at That Conference today. I was amazed at how much enthusiasm I got for the open spaces discussion along with the talk. These are the slides from today:
I’m super excited to highlight this awesome RFC from Lucas proposing to bring his Frozen Moment immutability plugin into the Moment.js core package as an optional add-on.
This RFC proposes a new Moment namespace,
moment.frozen that wrappers all Moment functions with code that will automatically clone the Moment before performing mutations, creating an immutable API.
Moving forward, it is our desire to make the Frozen Moment immutable API the preferred way of interacting with Moment.js, effectively causing the library to become immutable. By making a second API, we allow legacy products to continue to function as before.
This all started when I wrote a blog post about why Moment isn’t immutable yet about a month ago. With the amount of enthusiasm we saw from the community, we decided to move forward with making Lucas’ plugin official.
As of right now, I am reworking our build process to allow us to better package this plugin.
In the meantime, we need community feedback on a few things:
- Should this be packaged as a separate plugin, or just live in the core library as a second API?
- What should this API be named? Frozen is a working name that probably will not be adopted. We have discussed “m“ and “mom“ as possible options.
- Should the immutable API be a second global instead of being attached to the Moment global?
- Should we simply make the “moment“ namespace immutable, and have a “momentLegacy“ namespace that users can use to overwrite the definition of moment for back-comparability if desired?
- How should plugins and other libraries that depend on Moment interact with this API?
- If we do overwrite the “moment“ namespace with an immutable one, should we release a compatibility build under a 2.0 version number? This build would have the same underlying code, but a mutable moment namespace.
Moving forward, we will be looking for contributors in a few key areas:
- Documentation rework (needs to handle multiple APIs)
- Package management
In particular, someone with a UX background who wanted to help us improve the design of the docs page to support multiple APIs would be greatly appreciated.
Yesterday I wrote a post about how Moment.js isn’t immutable, and how it really isn’t in the cards for development right now.
I think as a team, we are sometimes not great about communicating things to our community that we are doing, and that are moving the library forward. As such, I wanted to write a short post about stuff we do have going on.
Next Major Release
The next major release of Moment.js and Moment TimeZone will have a complete rework of how the system handles time zones internally. This pull request is the completed work on the moment side. Tim has yet to get to the Moment TimeZone side, but it’s coming.
From a functionality standpoint for the library, this doesn’t really add much, though we might get in some syntactic niceties we didn’t have before. Internally though, we see a drastic improvement in our code base. This will allow us a lot more flexibility with new features down the road.
We also hope to have all of Moment changed over to Babel and Rollup by next major release. Again, this doesn’t add much for functionality, but it set us up to start using more ES2015 features. As a team, we have to decide what features we want to bring into the code and when. This is still in discussion.
There is no date for this, but all of this code is well in progress.
Other Stuff on the Table
- Modularized features
- I personally am very attached to getting this one done
- In particular, I would like to see a stand-alone parser that didn’t require the rest of the library
- Duration Formatting
- Non-Gregorian calendars
If any of these are particularly important to you, let me know!
I have been answering some Stack Overflow questions lately, and it seems like quite a lot of them all boil down to someone saying that Moment.JS is showing the wrong date.
Here are some of the most common reasons why Moment might not be showing what you expect, and the ways to get it to do what you want:
The console shows the wrong date when I dump a Moment object
I might run code that looks something like this:
As you can see, I parsed January 1, but am seeing December 31 in the console.
When you dump a Moment object to the console, you see
Object.prototype.toString() being called on the
This is easy to work around. Ignore the value of
_d – it is not useful information. Instead, rely on
.format() to get the correct result:
.format() Shows a Date I Didn’t Parse
Most often, when
.format() isn’t giving the expected result, it is because the date was parsed with an offset, and the wrong Moment constructor function was used. This is a little bit of a complicated statement, but let’s break it down.
First, the date format causing confusion is usually ISO 8601 format with offset. That format looks like this:
This indicates that the date in question is May 14 2016 at 8:33 AM in local time, and that the local time of this date is offset three hours behind UTC.
You may also see an ISO8601 formatted date that looks like this:
Note that the
z in the date above indicates that it is in UTC, or has an offset of
This is a very helpful format in that it reflects both local time, and global time in the same time stamp. It can get people into trouble when making moments though.
Moment offers four ways to construct a moment object, and all of them will use this offset information to achieve different results.
The moment constructor functions are as follows:
moment.tz()(this only works with the Moment TimeZone add on)
The moment() Function
When you use the default moment constructor, it will convert the date from the provided offset to the environment’s local time. Right now my local time is
-05:00. As such, if I take my
-03:00 date from above, and parse it with moment, I get the following:
As you can see, the time has been shifted from
06:33:14 to reflect the offset change.
It is important to note that the default moment constructor will use information from the browser to keep the moment in the user’s local time, even when offsets change due to DST. If I add six months to this moment, my local offset will be
-06:00. This will be properly handled:
moment('2016-05-14T08:33:14-03:00').add(6, 'months').format() 2016-11-14T06:33:14-06:00
The moment.utc() Function
When you use the moment UTC constructor, it will convert the date from the offset provided to UTC. Using the same date string:
As you can see, this time the date has been shifted forward three hours, from
11:33:14 to be in line with UTC.
The moment.parseZone() Function
.parseZone() function will parse a moment with the offset fixed to the provided offset.
It is very important to remember that this function is mis-named. When you use
.parseZone() you are parsing the moment to have a fixed offset. A fixed offset is NOT a time zone. Most time zones will have more than one offset at various points in the year due to DST. Also, most time zones have changed their offset over time. Any math performed with a moment in fixed offset mode may yield unexpected results. The use cases for a fixed offset moment are limited. See the Stack Overflow timezone tag for more information on time zones vs offsets.
Using my same time, I get the following result with
As you can see, the time and offset are unchanged.
The moment.tz() Function
moment.tz() function will parse a given time with offset and convert it to the time zone provided. It is useful when you need to interpret a time in a time zone other than UTC, or the user’s local time.
Note that this functionality does not work without the Moment TimeZone add on library being included in your project. The Moment TimeZone library uses the IANA time zones to provide time zone support. A list of IANA time zone identifiers and offsets can be found here.
If you only need to work in UTC, and the user’s local time, do NOT bring in the Moment TimeZone library. It only causes extra code to be sent to the browser. Of course if you are in Node, feel free to bring it in everywhere just in case.
Using the same date and time as before, I will specify the time be parsed to Europe/Berlin. This time zone is UTC +2 at the time of year specified:
moment.tz('2016-05-14T08:33:14-03:00', 'Europe/Berlin').format() 2016-05-14T13:33:14+02:00
As you can see, the time has been shifted five hours forward, to move from
+02:00. The resultant time is
13:33. This moment will be time zone aware, so if you were to add six months to it, at which point Berlin will have an offset of
-01:00, the moment will correctly change offsets:
moment.tz('2016-05-14T08:33:14-03:00', 'Europe/Berlin').add(6, 'months').format() 2016-11-14T13:33:14+01:00
But I Don’t WANT the Offset
A common complaint about these functions is that people just don’t want the offset to be used. They wish for the date to be interpreted as local time.
If you are thinking this, please use caution. When you get a date with an offset, you are getting valuable information about both local time, and the point on the global timeline. Throwing this information away may not be helpful down the road.
If you are sure that you do not want the offset information, the best practice would be to have the system that you are getting the date from stop sending the offset. Any other choice may be misinterpreted later.
As developers though, we live in reality. We can’t always change the data that is being sent to us. If you need to ignore the offset, you have a few options.
If the offset is
z, so all dates are being sent in UTC, just parse in UTC mode. No change will be made to the time.
moment.utc('2016-05-14T08:33:14Z').format('LLL') May 14, 2016 8:33 AM
If the offset is some other number, and you wish to ignore it, you can use
.parseZone(), and simply not display the offset information:
moment.parseZone('2016-05-14T08:33:14-03:00').format('LLL') May 14, 2016 8:33 AM
Remember though, both of the above methods will not handle DST transitions in the user’s local time. Thus, mathematical operations can have unexpected results.
If you want to interpret the date as the user’s local time, and be able to safely do math with it, you can specify a format that has no
Z token, and thus ignores the offset:
moment('2016-05-14T08:33:14-03:00', 'YYYY-MM-DDTHH:mm:ss').format() 2016-05-14T08:33:14-05:00
As you can see, the time has been interpreted as local and has my offset. Note that this will not work in strict parsing mode, as there will be left over training characters. It will result in an invalid date.
Slides are here:
Slides about date and time!
Big thanks to everyone who showed up and asked awesome questions.