夏令时开始时间无效
简洁版本
在JavaScript中, new Date(2013, 9, 20)
合法返回10月19日吗?
长版本
夏令时开始时,由于时钟向前调整,因此当地时间有差距。 如果我构造的Date
对象的时间落在这个间隙内,根据ECMAScript规范,预期的行为是什么?
各种浏览器的行为有所不同,如下所示。 (我在Windows 8上运行这些测试。)
示例1:在太平洋时区(UTC-08)中,表达式
new Date(2013, 2, 10, 2, 34, 56).toString() // Sun Mar 10 2013 02:34:56
给出以下结果:
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)表达
new Date(2013, 9, 20, 0, 34, 56).toString() // Sun Oct 20 2013 00:34:56
给出以下结果:
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向后调整时间,Firefox无法下定决心。
规范说:根据我可以收集的内容, 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 )。
但我不清楚,DaylightSavingTA应该在太平洋时区t = 2013-03-10 10:34:56或巴西利亚时区的t = 2013-10-20 03:34:56返回一个参数。
根据ECMAScript规范,预期的行为是什么?
好问题! 您确定这取决于严格未定义的DaylightSavingTA
,但是我们可以从反向函数中使用DaylightSavingTA
的方式推导出有关DaylightSavingTA
含义:
LocalTime(t)= t + LocalTZA + DaylightSavingTA(t)
为了将UTC t正确转换为本地时间,DaylightSavingTA(hour_at_beginning_of_dst)必须为1小时,而DaylightSavingTA(hour_after_end_of_dst)必须为0。
在UTC()函数中使用在该函数中表示DST调整的时间的t
来调用相同的函数。 因此,在夏令时开始时不存在的当地小时,DaylightSavingTA(每小时提前调整时间)为1小时。 所以:
新日期(2013,9,20)可以合法返回10月19日吗?
是。
我在问,因为我(可能还有其他人)已经编写了像新日期(年,月,日)这样的代码来构建一个没有时间的日期,而且显然这并不总是奏效!
确实! 使用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规范,但它没有任何意义。
Firefox只有一个标签混乱的bug,这里也有描述。
另外请记住,虽然大多数地区的DST过渡了一个小时,但至少有一个地区只有30分钟( Australia/Lord_Howe
),而一些南极地区甚至更加陌生。
另一个相关的问题是在回退转换中含糊不清。 由于DST,有效的本地时间可能代表两个不同的UTC时刻。 同样,ES5规范也没有提到在这种情况下会发生什么。 实际上,我们再次发现每个浏览器的行为不同。 有些人需要白天,因为这是两个可能时刻中的第一个。 其他人则需要标准时间,因为这是“标准”。
在其他框架中,Python(通过pytz)将抛出一个异常,而.NET的TimeZoneInfo
方法将假定“标准”时间。 像Noda Time这样的图书馆可以为您提供选择。 但在JavaScript中,行为是特定于实现的。
对于模糊的时间或无效的时间,你可以做的最好的事情是测试你的输入。 如果无效,请提示用户输入有效时间。 如果不明确,请提示用户选择两种可能性中的一种。
考虑LocalTime(t)
函数的图表,因为它适用于美国太平洋时间( America/Los_Angeles
)。
这是一个有效的函数,但是如果翻转X轴和Y轴,那么您将看到UTC(t)
的效果,该效应没有在弹簧向前转换范围内定义,并且在下降的时候并不是一个函数,后退转换范围。