1

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

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

它给了我May 2016在美国/丹佛和June 2016亚洲/卡拉奇的时区。我通过将浏览器时区更改为不同的时区进行了测试。它应该June 2016在两者中。

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

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

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

4

1 回答 1

4

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

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

如果你想改用斜杠,它会是:

moment('2016/06/01', 'YYYY/MM/DD').format()

有关 moment 如何使用不同的构造函数方法解释时间的更多信息,请参阅 moment 的解析指南。

长答案是,当您将 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 总是返回一个 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,所以全球时间线上的点比我通过的字符串提前了 5 小时。

您所看到的行为 - 将 2016-06-01 解释为 UTC,但将 2016-06-01T00:00 解释为本地,实际上是为了适应跨浏览器的技术债务。它已成为 ECMA 262 规范第 7 版中的标准行为,因此希望它不会改变。请参阅此链接。

或者,当您使用斜杠 (2016/06/01) 时,您正在使用的 JS 实现选择将该格式解释为本地时间,因为它不符合 ECMA 标准中的任何格式。这不是有效的 ISO8601 格式。请务必注意,此行为是特定于实现的,并且会因浏览器/环境而异。ECMA 标准没有定义解析该日期格式的行为。其他浏览器可能会以其他方式解析此字符串。

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

于 2016-06-17T08:48:18.447 回答