moment.js 时区不一致

moment.js timezone inconsistency

我正在使用 momentjs 格式化给定的日期。以下行为在不同时区不同:

moment(new Date("2016" + "-" + "06" + "-01").toISOString()).format('MMMM YYYY')

它让我在 America/Denver 的时区 May 2016 和 Asia/Karachi 的 June 2016 时区。我通过将浏览器时区更改为不同时区来进行测试。两者都应该是June 2016

当我将 new Date() 中的格式更改为使用斜杠而不是像下面这样的连字符时,它在两个时区都给出了正确的结果,即 May 2016.

moment(new Date("2016" + "/" + "06" + "/01").toISOString()).format('MMMM YYYY')

两者似乎都是有效的 ISO 字符串,什么会导致这种不一致?

对您的问题的简短回答是 javascript 日期的解析器无法以对任何人都有意义的方式工作。相反,您应该只使用 Moment 的解析器来获得您想要的结果。以有意义的方式解析日期约占该时刻存在原因的 50%。 如果您消除日期调用并使用 Moment 来解析您的日期,您将观察到以下代码在任何浏览器中都会产生 2016 年 6 月,因为如果您使用 Moment 的默认构造函数,您的字符串将被解释为当地时间:

moment('2016-06-01').format()

如果您想改用斜杠,可以是:

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.

长答案是,当您将仅作为日期的 ISO8601 格式的字符串传递给 JavaScript 日期构造函数时,它将将该字符串解释为 UTC。因为丹佛在夏令时是 UTC -6,而卡拉奇一直是 UTC +5,所以当时刻将时间戳显示为本地时间时,您会看到您所做的结果。您可以观察到以下内容:

var a = new Date('2016-06-01'); 
a.toISOString();
"2016-06-01T00:00:00.000Z"

注意上面时间戳中的'Z'表示是UTC,至于toISOString总是returns一个UTC时间戳。该时间戳在卡拉奇是 6 月,因为卡拉奇早于 UTC,而在丹佛是 5 月,因为丹佛落后于 UTC。

也请注意:

var a = new Date('2016-06-01T00:00'); 
a.toISOString();
"2016-06-01T05:00:00.000Z"

如果我在字符串上输入时间,它会被解释为当地时间。因为我的时区是 1 月 1 日的 UTC-5,所以全球时间线上的时间点比我传递的字符串早了五个小时。

您看到的行为 - 将 2016-06-01 解释为 UTC,但将 2016-06-01T00:00 解释为本地时间,实际上是为了适应跨浏览器的技术债务。 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.

或者,当您使用斜杠 (2016/06/01) 时,您使用的 JS 实现选择将该格式解释为本地时间,因为它不符合 ECMA 标准中的任何格式.这不是有效的 ISO8601 格式。请务必注意,此行为是特定于实现的,并且会因 browsers/environments 而异。 The ECMA standard does not define a behavior for parsing that date format. 其他浏览器可能会以其他方式解析此字符串。

作为一般建议,不要使用 JavaScript 日期解析器。它不能正常工作。您可以使用 moment 的几个竞争对手之一的 Moment.js,或者自己手动解析字符串。这些都是更好的选择。