JavaScript 时区对于过去的夏令时转换规则是错误的

IT技术 javascript node.js google-chrome firefox timezone
2021-01-25 02:48:58

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

如果您运行以下脚本,它将报告 240 分钟的偏移量。那是错误的,它应该报告 300 分钟。IE10 正确执行此操作。有谁知道修复?

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

更新:

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

我最终只是将 Date 的 getTimezoneOffset 原型替换为我自己的原型,该原型基于硬编码表进行计算。这对我们有用,因为我们只在美国做生意,但这是我能想象的最糟糕的解决方案......

<!DOCTYPE html>
<html>
    <head>
        <title>Moment Test</title>
        <script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.0.0/moment.min.js"></script>
        <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
        <script>
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);
}

        </script>
    </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>
2个回答

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

“ECMAScript 的实现不应该试图确定确切的时间是否受夏令时的约束,而应该确定如果当时使用了当前的夏令时算法,夏令时是否会生效。这避免了诸如此类的并发症考虑到当地全年遵守夏令时的年份。”

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

有可能——甚至可能——这种行为会在 ECMAScript 的未来版本中改变,要求在所有时间点使用实际的 DST 规则。最初不需要这样做,因为它给实现者带来了传送 tzdata 的负担。然而,语言已经变得足够重要,从长远来看,可能每个人都不得不接受它。但据我所知,这种变化可能需要数年时间,所以不要屏住呼吸。

看起来ES6 的最新草案已经改变了这一点。>>> “ECMAScript 的实现预计将尽最大努力确定本地夏令时调整。一种依赖于实现的算法,使用时区的最佳可用信息来确定本地夏令时调整 DaylightSavingTA(t),以毫秒。”
2021-03-20 02:48:58
圣烟!好吧,这就回答了。但仍然......神圣的烟雾!
2021-03-23 02:48:58
哇哦哇哦哇哦。我想知道编写 ECMAScript 规范的人是否知道这句话会产生什么可怕的影响。为什么它不只依赖于操作系统?它从操作系统中获取当前规则但忽略操作系统具有所有其他规则是没有意义的。Windows 有自己的,其他人都有 TZDB,所以我不认为这是一个很大的负担。有没有办法在某个地方对此进行投票?
2021-04-01 02:48:58
我为由 IANA 时区数据库驱动的 JavaScript 实现了一个完整但很小的时区感知日期库。它执行时区感知日期数学和时区感知日期格式,并具有完整的strftime格式实现目前没有其他针对时区感知日期的纯 JavaScript 解决方案。github.com/bigeasy/时区
2021-04-06 02:48:58
2021-04-11 02:48:58

我已经确认这是 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 日期很糟糕,但我认为我们至少可以依赖于此。您甚至不必查看偏移量或涉及解析。只需检查以下值:

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

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

> 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 日。这是目前生效的“11 月的第一个星期日”规则的后续规则,但在 2004 年,该规则应该是“10 月的最后一个星期日”的旧规则。

更新 1

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

Node.js 截图

为了证明 IE 没问题,这里是 IE10 的输出:

IE10 截图

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

更新 2

值得一提的是,在最新的 Firefox 21 中,这个问题确实发生了,但它的表现方式有所不同,因为另一个问题使标准名称切换日光,即使使用了正确的偏移量也是如此。换句话说,在 Firefox 上,输出是这样的:

火狐截图

我将把它留在这里供后代使用,但从技术上讲,这不是答案。有关解释,请参阅 Jeff 的回答。
2021-04-01 02:48:58