在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的Date
( yyyy-mm-dd hh :mi:ss ),其中
UTC( t )= t - LocalTZA - DaylightSavingTA( t - LocalTZA)
LocalTZA是标准时间的本地时区调整(例如,太平洋时区的-08:00),而DaylightSavingTA( t )是 t的夏令时调整(例如,夏令时期间01:00,否则为00:00)。
但我不清楚,DaylightSavingTA应该在太平洋时区的 t = 2013-03-10 10:34:56或 t = 2013-10-20 03:34:56在巴西利亚时区。
答案 0 :(得分:3)
根据ECMAScript规范,预期的行为是什么?
好问题!你是正确的,这取决于严格未定义的DaylightSavingTA
,但是我们可以从反函数中使用的方式得出DaylightSavingTA
的含义:
LocalTime(t)= t + LocalTZA + DaylightSavingTA(t)
为了正确地将UTC t转换为本地时间,DaylightSavingTA(hour_at_beginning_of_dst)必须为1小时,DaylightSavingTA(hour_after_end_of_dst)必须为0.
使用t
在UTC()函数中调用相同的函数,该函数表示DST调整的时间。因此,在DST开始时不存在的当地时间,DaylightSavingTA(-dst-adjusted-time-is-one-hour-ahead-ahead)是1小时。因此:
新日期(2013年,9月20日)可以合法地返回10月19日吗?
是
我问,因为我(可能还有其他人)编写了像新日期(年,月,日)这样的代码来构建一个没有时间的日期,显然这并不总是有效!
事实上!使用UTC日期进行此类计算 - new Date(Date.UTC(2013, 9, 20))
和getUTC*
方法。一般来说,除了最终面向用户的时间表示之外,最好坚持使用UTC。
答案 1 :(得分:1)
你的观察是正确的,我已经做了类似的观察。有关其他详细信息,请阅读this post和the blog article I wrote。
要点是ECMAScript 5在第15.9.1.8节中定义了DaylightSavingTA算法的 part ,但它确实很糟糕。 ECMAScript 6的效果看起来更好。
对于无效输入向前或向后跳过,规范不要求它以任何方式掉落。实际上,除了规范之外,只做下列其中一项是有道理的:
按DST金额向前跳过。这是有道理的,因为如果有人忘记在输入中调整DST,您可能会遇到无效值。
抛出错误或异常,因为该日历时间不存在。 JavaScript不会这样做,但其他语言/库也可以。例如,如果你传递一个无效的本地时间,.NET的TimeZoneInfo.ConvertTimeToUtc
方法将抛出异常。
我无法想象任何真实世界的用例,就像Chrome那样向后移动是有意义的。它符合ES5规范,但没有任何意义。
Firefox只有一个错误,其标签搞砸了,also described here。
另请注意,虽然DST的大多数区域都会过渡一个小时,但至少有一个只有30分钟(Australia/Lord_Howe
),而且一些南极洲区域甚至更加陌生。
另一个相关问题是后退转型的含糊不清。由于DST,存在重叠,其中有效的本地时间可以表示两个不同的UTC时刻。同样,ES5规范没有说明在这种情况下应该发生什么。实际上,我们再次发现每个浏览器的行为都不同。有些人需要白天时间,因为这是两个可能时刻中的第一个。其他人需要标准时间,因为它是“标准”。
在其他框架中,Python(通过pytz)将抛出异常,而.NET的TimeZoneInfo
方法将采用“标准”时间。像Noda Time这样的图书馆为您提供了选择。但在JavaScript中,行为是特定于实现的。
对于模糊时间或无效时间,您可以做的最好的事情是测试您的输入。如果它无效,请提示您的用户输入有效时间。如果它不明确,请提示您的用户选择两种可能之一。
考虑LocalTime(t)
函数的图表,因为它适用于美国太平洋时间(America/Los_Angeles
)。
这是一个有效的函数,但如果你翻转X轴和Y轴,那么你将看到UTC(t)
的效果,它没有在弹簧前进过渡范围内定义,并且根本不是一个函数在后退过渡范围内。