关于node.js:过去的夏令时转换规则的JavaScript时区错误

JavaScript Time Zone is wrong for past Daylight Saving Time transition rules

2007年,我们改用夏令时的日子发生了变化。在此更改之前属于DST扩展范围内的任何日期都会在Chrome和Firefox中报告错误的时区偏移。这就像Firefox和Chrome没有注意到DST曾经有过不同日子的事实。

如果您运行以下脚本,它将报告240分钟的偏移量。这是不正确的,它应该报告300分钟。 IE10正确地做到了这一点。有谁知道修复?

1
alert(new Date('11/04/2004').getTimezoneOffset());

更新:

这是我刚刚攻击的一段有趣的代码(见下文)。令人惊讶的是,除了IE,每个浏览器的大多数日期都有多远。将开始日期和结束日期与此进行比较:http://www.timeanddate.com/worldclock/timezone.html?n = 77& syear = 2000

我最后用我自己的getTimezoneOffset替换了Date的原型,它根据硬编码表计算它。这对我们有用,因为我们只在美国做生意。这是我能想象到的最糟糕的解决方案......

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html>
    <head>
        Moment Test
        <script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.0.0/moment.min.js">
        <script src="http://code.jquery.com/jquery-1.10.1.min.js">
       
var lastOffset = null;
var $tbody = null;
var endDate = new Date('01/01/2021');

function addDate(d) {
    if($tbody === null)
        $tbody = $('#dates');

    var offset = d.getTimezoneOffset();
    var s = '';
    if(lastOffset != offset) {
        if(lastOffset != null)
            s = '<tr style="background-color: red;">';
        lastOffset = offset;
    }
    else {
        s = '<tr>';
    }
    var m = new moment(d);
    s += '<td>' + m.format('YYYY-MM-DD') + '</td><td>' + m.format('YYYY-MM-DDTHH:mm:ssZ') + '</td><td>' + m.format('YYYY-MM-DDTHH:mm:ss') + '</td><td>' + offset + '</td></tr>';
    $tbody.append($(s));
    d.setDate(d.getDate() + 1);

    if(d < endDate)
        window.setTimeout(function(){addDate(d)}, 0);
}

       
    </head>
    <body>
        <button onclick="addDate(new Date('01/01/1980'));">Fill Table</button>
        <table border="1">
            <thead><tr><th>Date</th><th>Date 2</th><th>Date 3</th><th>TZ Offset</th></tr></thead>
            <tbody id='dates'></tbody>
        </table>
    </body>
</html>


它实际上是指定使用当前DST规则的行为,并忽略在检查的特定日期/时间的那些规则。见ES5 15.9.1.8:

"ECMAScript的实施不应该试图确定准确的时间是否适用于夏令时,而只是当时使用当前的夏令时算法时夏令时是否有效。这可以避免诸如此类的复杂化。考虑到该地区全年观察夏令时的年份。"

规则是:将当前的DST规则应用于指定的任何时间。这导致了无意义的废话行为,但这正是ECMAScript所要求的。

有可能 - 甚至 - 可能会在未来版本的ECMAScript中改变这种行为,以便在所有时间点使用实际的DST规则。这不是最初需要的,因为它对实施者施加了tzdata的运输负担。然而,这种语言已经变得非常重要,从长远来看,可能每个人都必须从中吸取它。但是对于我所知道的所有变化可能需要几年的时间,所以不要屏住呼吸。


我已经确认这是JavaScript中的一个真正的错误。

  • 使用夏令时之后的美国时区进行测试

    • 东部,中部,山区,太平洋
  • 在Chrome,Firefox,Safari和失败(最新版本)测试
  • 在IE 6,7,8,9中测试并失败。
  • 在IE 10中测试并通过(未受影响)。
  • 在Windows 7,8和Mac OSX上测试过。

这非常令人不安。有谁知道根本原因?

我认为它可能是一个WebKit错误,但Firefox使用Gecko。

我检查了各种问题列表,无法在任何地方找到这个特定的问题。也许我错过了什么。我不确定在哪里提交错误报告,因为它会影响多个地方。

也许这是一个核心JavaScript错误?我真的很难相信单元测试忽略了这个基本的东西。

我想也许它只是影响Windows系统,因为操作系统有Windows时区而不是TZDB,但事实并非如此,因为它也发生在Mac上。

我们都知道JavaScript日期很棘手,但我认为我们至少可以依赖于此。您甚至不必查看偏移量或涉及解析。只需检查以下值:

1
new Date(2004,10,4)  // recall, js months are 0-11, so this is Nov. 4 2004.

2004年在美国,夏令时于10月31日凌晨2点结束,当时钟回到凌晨1点。所以到11月4日,他们肯定都应该在标准时间,但他们不是!例如,在控制台上的Chrome开发工具中,时钟设置为美国东部时区:

1
2
3
4
5
> new Date(2004,10,7,0,0)
  Sun Nov 07 2004 00:00:00 GMT-0400 (Eastern Daylight Time)

> new Date(2004,10,7,1,0)
  Sun Nov 07 2004 01:00:00 GMT-0500 (Eastern Standard Time)

它将过渡日期定在11月7日。这就是现在生效的"十一月的第一个星期日"规则,但在2004年,该规则应该是"十月的最后一个星期日"的旧规则。

更新1

它似乎不限于浏览器。它在Node.js中也失败了

Node.js Screenshot

只是为了证明IE很好,这是IE10的输出:

IE10 Screenshot

有趣的是,IE和Firefox将1:00的歧义解析为日光时间,而Chrome将其解析为标准时间,但这是一个单独的问题。它确实选择了正确的过渡日期。

更新2

值得一提的是,在最新的Firefox 21中,这个问题确实发生了,但它本身就有所不同,因为它更复杂的是另一个问题,它为标准名称切换日光,即使使用了正确的偏移量。换句话说,在Firefox上,输出如下:

Firefox Screenshot