JavaScript时区在过去的夏令时转换规则上存在错误。

29

在2007年,我们转换夏令时的日期发生了改变。在该更改之前,任何落在DST延长范围内的日期在Chrome和Firefox中报告的时区偏移量是不正确的。就像Firefox和Chrome没有注意到夏令时曾经有不同的日期。

如果运行以下脚本,它将报告240分钟的偏移量。这是不正确的,应该报告300分钟。IE10可以正确地执行此操作。有人知道解决方法吗?

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

更新:

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

最终我只是将Date的原型替换为自己的代码,用硬编码表格计算时区偏移量。 这对我们来说有效,因为我们只在美国从事业务。但这是我能想象到的最糟糕的解决方案...

<!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>

可能的解决方案:https://dev59.com/oE7Sa4cB1Zd3GeqP7eW1 - David
你用的是什么操作系统?你的计算机时区设置是什么? - Matt Johnson-Pint
这太震撼了,但我已经在Windows 8上使用Chrome 27和美国东部时间进行了确认。 - Matt Johnson-Pint
我刚刚在所有美国时区的Windows 7和8上的Chrome和Firefox(最新版本),以及Mac上的Safari,Chrome和Firefox上进行了确认。这非常令人不安...有人知道根本原因吗?是TZDB更新出了问题吗? - Matt Johnson-Pint
@David - 你提供的链接与此无关。这是FF和Chrome中的真正错误。 - Matt Johnson-Pint
2个回答

37

使用当前的夏令时规则并忽略在特定日期/时间生效的规则是指定行为。请参见ES5 15.9.1.8:

"ECMAScript的实现不应尝试确定确切的时间是否受夏令时影响,而应只确定如果在该时间使用当前的夏令时算法是否会生效。这避免了诸如考虑区域全年观察夏令时的年份之类的复杂性."

规则是:将当前的夏令时规则应用于指定的任何时间。这导致明显荒谬的行为,但这就是ECMAScript所要求的。

未来ECMAScript版本中可能会更改此行为,以要求在所有时间点使用实际的夏令时规则。最初不要求这样做是因为它会给实现者带来tzdata的负担。然而,这门语言已变得足够重要,可能每个人最终都必须接受这种负担。但我不知道这种变化可能需要多少年,所以不要抱有期待。


6
天啊!好的,这回答了问题。但是...天啊! - Brian
5
哇哦哇哦哇。我在想撰写 ECMAScript 规范的人是否意识到这句话会造成多么可怕的影响。为什么它不依赖于操作系统?它从操作系统中获取当前规则,却忽略了操作系统拥有的所有其他规则,这是毫无意义的。Windows 有自己的规则,其他所有人都使用 TZDB,所以我认为这并不是一个负担。有没有地方可以投票表决呢? - Matt Johnson-Pint
2
我已经实现了一个完整而微小的时区感知日期库,用于由IANA时区数据库驱动的JavaScript。它执行时区感知日期数学和时区感知日期格式化,并具有对strftime格式的完全实现。目前没有其他纯JavaScript解决方案可用于时区感知日期。https://github.com/bigeasy/timezone - Alan Gutierrez
2
看起来ES6的最新草案已经为此做出了改进。实现ECMAScript的代码应该尽力确定本地的夏令时调整。使用基于可用时区信息的实现依赖算法,以确定本地夏令时调整DaylightSavingTA(t),单位为毫秒。 - Matt Johnson-Pint
1
博客文章:JavaScript的日期类型非常糟糕 - Matt Johnson-Pint
显示剩余2条评论

4

我确认这是JavaScript中一个真正的bug。

  • 测试了遵循夏令时的常见美国时区
    • Eastern,Central,Mountain,Pacific
  • 在Chrome,Firefox,Safari中测试并失败(最新版本)
  • 在IE 6、7、8和9中测试并失败。
  • 在IE 10上测试并通过(未受影响)。
  • 在Windows 7、8和Mac OSX上进行测试。

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

我认为这可能是一个WebKit bug,但Firefox使用Gecko。

我检查了各种问题列表,没有发现这个特定的问题。也许我错过了什么。我不确定应该在哪里提交错误报告,因为它影响多个地方。

也许这是一个核心JavaScript bug?我真的很难相信一个基本的功能被单元测试忽视了。

我曾以为这只影响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点结束,时钟倒退至凌晨1点。因此到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 Screenshot

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

IE10 Screenshot

有趣的是IE和Firefox把1:00的歧义解析为夏令时,而Chrome则将其解析为标准时间,但这是一个单独的问题。它确实选择了正确的转换日期。
更新2
值得一提的是,在最新的Firefox 21中,这个问题确实存在,但它呈现出不同的形式,因为它被另一个问题所复合,该问题会在使用正确的偏移量时切换到夏令时和标准名称。换句话说,在Firefox上,输出会像这样:

Firefox Screenshot


1
我把这个留在这里作为纪念,但从技术上讲,它并不是一个答案。请参考Jeff的回答来了解原因。 - Matt Johnson-Pint

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接