问题
I am formatting a given date using momentjs. The following behaves differently in different timezones:
moment(new Date("2016" + "-" + "06" + "-01").toISOString()).format('MMMM YYYY')
It gives me May 2016
in timezone of America/Denver and June 2016
in Asia/Karachi. I tested by changing the browser timezone to different timezones. It should be June 2016
in both.
When i change the format in new Date()
to use slashes instead of hyphens like below, it gives me correct result in both timezones i.e. May 2016
.
moment(new Date("2016" + "/" + "06" + "/01").toISOString()).format('MMMM YYYY')
Both seem to be valid ISO strings, what would cause this inconsistency?
回答1:
The short answer to your question is that parser for javascript date doesn't work in a way that makes sense to anybody. Instead, you should just use Moment's parser to get the result you want. Parsing dates in a way that makes sense is about 50% of the reason that moment exists. If you eliminate the date call and use Moment to parse your date, you will observe that the following code will result in June 2016 in any browser, because your string will be interpreted as local time if you are using Moment's default constructor:
moment('2016-06-01').format()
If you wanted to use slashes instead, it would be:
moment('2016/06/01', 'YYYY/MM/DD').format()
See moment's parsing guide for more information about how moment interprets times with it's different constructor methods.
The long answer is that when you pass a string in ISO8601 format that is date only to the JavaScript date constructor, it will interpret that string as UTC. Because Denver is UTC -6 on daylight time, and Karachi is UTC +5 all the time, when moment then displays that timestamp as local time you see the result that you do. You can observe the following:
var a = new Date('2016-06-01');
a.toISOString();
"2016-06-01T00:00:00.000Z"
Note that the 'Z' in the above timestamp indicates that it is UTC, as toISOString always returns a UTC timestamp. That timestamp is June in Karachi because Karachi is ahead of UTC, while May in Denver because Denver is behind UTC.
Observe this as well:
var a = new Date('2016-06-01T00:00');
a.toISOString();
"2016-06-01T05:00:00.000Z"
If I put a time on my string, it is interpreted as local time. Because my timezone was UTC-5 on January 1, the point on the global timeline is appropriately five hours ahead of the string I passed.
The behavior you are seeing - interpreting 2016-06-01 as UTC, but 2016-06-01T00:00 as local, is actually an effort to accommodate technical debt across browsers. It has been made the standard behavior in the 7th edition of the ECMA 262 specification, so expect for that not to change. See this link as well.
Alternately, when you use the slashes (2016/06/01) the JS implementation that you are using is choosing to interpret that format as local time, as it does not conform to any of the formats in the ECMA standard. This is NOT a valid ISO8601 format. It is very important to note that this behavior is implementation specific, and will vary across browsers/environments. The ECMA standard does not define a behavior for parsing that date format. Other browsers may parse this string in other ways.
As general advice, don't use the JavaScript date parser. It doesn't work right. You can use Moment.js, one of moment's several competitors, or manually parse strings yourself. All of these are better options.
来源:https://stackoverflow.com/questions/37874178/moment-js-timezone-inconsistency