关于javascript:夏令时开始时的无效时间

Invalid times at the start of Daylight Saving Time

短版

在javascript中,new Date(2013, 9, 20)是否可以合法返回10月19日?

长版

当夏令时开始时,当时钟向前调整时,本地时间有一个间隙。如果我构造一个Date对象的时间在这个时间间隔内,那么根据ecmascript规范,预期的行为是什么?

不同的浏览器行为不同,如下所示。(我在Windows 8上运行了这些测试。)

例1:在太平洋时区(UTC-08),表达式

1
new Date(2013, 2, 10, 2, 34, 56).toString() // Sun Mar 10 2013 02:34:56

给出以下结果:

1
2
3
4
IE 10:      Sun Mar 10 03:34:56 PDT 2013
IE 11:      Sun Mar 10 2013 03:34:56 GMT-0700 (Pacific Daylight Time)
Chrome 29:  Sun Mar 10 2013 01:34:56 GMT-0800 (Pacific Standard Time)
Firefox 23: Sun Mar 10 2013 03:34:56 GMT-0700 (Pacific Standard Time)

例2:在巴西利亚时区(UTC-03),表达式

1
new Date(2013, 9, 20, 0, 34, 56).toString() // Sun Oct 20 2013 00:34:56

给出以下结果:

1
2
3
4
IE 10:      Sun Oct 20 01:34:56 UTC-0200 2013
IE 11:      Sun Oct 20 2013 01:34:56 GMT-0200 (E. South America Daylight Time)
Chrome 29:  Sat Oct 19 2013 23:34:56 GMT-0300 (E. South America Standard Time)
Firefox 23: Sat Oct 19 2013 23:34:56 GMT-0300

基于这两个例子,IE似乎调整了时间向前,Chrome调整了时间向后,而火狐无法下定决心。

规范说明:根据我收集到的信息,new Date(yyyy, mm-1, dd, hh, mi, ss)构造了一个时间值为UTC(yyyy-mm-dd hh:mi:ss)的Date,其中

UTC(t) = t – LocalTZA – DaylightSavingTA(t – LocalTZA)

localtza是标准时间的本地时区调整(例如,太平洋时区为-08:00),daylightsavingta(t)是t的夏令时调整(例如,DST期间为01:00,否则为00:00)。

不过,我不清楚,太平洋时区的t=2013-03-10 10 10:34:56或巴西利亚时区的t=2013-10-20 03:34:56的争论应该返回哪一天。


what is the expected behavior according to the ECMAScript specification?

好问题!您是正确的,这取决于严格未定义的DaylightSavingTA,但是我们可以从用于反函数时的工作方式得出关于DaylightSavingTA的含义:

LocalTime(t) = t + LocalTZA + DaylightSavingTA(t)

为此,要将UTC T正确转换为本地时间,daylightsavingta(在_dst的开始_小时)必须为1小时,daylightsavingta(在_dst的结束_小时之后)必须为0。

在utc()函数中,使用t调用相同的函数,该函数中的t表示DST调整时间。因此,在DST开始时不存在的本地时间内,daylightsavingta(DST调整时间,提前一小时)为1小时。因此:

Can new Date(2013, 9, 20) legitimately return October 19?

对。

I'm asking because I (and probably others) have written code like new Date(year, month, day) to construct a date without a time, and evidently this doesn't always work!

的确!这种计算方法使用UTC日期—new Date(Date.UTC(2013, 9, 20))getUTC*方法。一般来说,除了面向最终用户的时代演示外,最好还是坚持使用UTC。


你的观察是正确的,我也做过类似的观察。阅读这篇文章和我写的博客文章了解更多细节。

要点是ECMAScript 5在第15.9.1.8节中定义了DaylightSavingta算法的一部分,但它确实做了一个很糟糕的工作。ECMAScript 6的情况更好。

对于无效输入的向前或向后跳过,规范不要求它以任何方式下降。但是,在实用性方面,规范除外,只需执行以下操作之一:

  • 按DST金额向前跳。这是有意义的,因为如果有人忘记调整输入中的DST,您可能会遇到无效值。

  • 抛出一个错误或异常,因为日历时间不存在。JavaScript不会这样做,但其他语言/库会这样做。例如,.NET的TimeZoneInfo.ConvertTimeToUtc方法将在传递无效的本地时间时引发异常。

我想不出任何一个现实世界的用例,像Chrome那样向后移动是有意义的。它在ES5规范中有效,但没有任何意义。

火狐只是有一个错误,它的标签是混乱的,这也是这里描述的。

也要记住,虽然大多数区域的DST转换时间为一小时,但至少有一个只有30分钟(Australia/Lord_Howe分钟),而且一些南极洲区域甚至更为陌生。

另一个相关的关注点是回退过渡中的模糊性。由于DST,有效的本地时间可以表示两个不同的UTC时间存在重叠。同样,ES5规范对这种情况下应该发生什么保持沉默。实际上,我们再次发现每个浏览器的行为是不同的。有些人选择白天,因为这是两个可能的时刻中的第一个。其他人则采用标准时间,因为,嗯,这是"标准"。

在其他框架中,python(via-pytz)将抛出一个异常,而.NET的TimeZoneInfo方法将假定为"标准"时间。像noda time这样的库为您提供了选项。但是在JavaScript中,行为是特定于实现的。

对于不明确的时间或无效的时间,最好的方法可能是测试输入。如果无效,提示用户输入有效时间。如果不明确,提示用户选择两种可能性之一。

考虑LocalTime(t)函数的图形,因为它适用于美国太平洋时间(America/Los_Angeles)。

&Spring Forward DST Transition

&Fall Back DST Transition

这是一个有效的函数,但是如果翻转x和y轴,您将看到UTC(t)的效果,它没有在弹簧前向过渡范围内定义,也不是在后向过渡范围内的函数。