为什么减去这两个纪元毫秒值(在1927年)会得到一个奇怪的结果?

发布时间 2023-10-06 12:54:42作者: 小满独家

内容来自 DOC https://q.houxu6.top/?s=为什么减去这两个纪元毫秒值(在1927年)会得到一个奇怪的结果?

在Java中,Date对象的getTime()方法返回的是从1970年1月1日00:00:00 GMT开始的毫秒数。当你解析日期字符串时,这些毫秒数是基于GMT的。然而,你的程序运行的环境时区是Asia/Shanghai,其偏移量为28800000毫秒。

因此,当你计算两个日期之间的差异时,你实际上是在计算它们在GMT和Asia/Shanghai时区的偏移量之差。在你的例子中,"1927-12-31 23:54:07"和"1927-12-31 23:54:08"之间的差异是353秒,转换为毫秒就是353000毫秒。然后,这个差异被从"1927-12-31 23:54:07"的GMT时间(即1927年12月31日23:54:07加上353秒)中减去,得到的结果是1927年12月31日23:54:06.587,这就是你看到的输出。

如果你将日期改为"1927-12-31 23:54:08"和"1927-12-31 23:54:09",那么这两个日期之间的差异就是1秒,转换为毫秒就是1000毫秒。然后,这个差异被从"1927-12-31 23:54:08"的GMT时间(即1927年12月31日23:54:08加上1秒)中减去,得到的结果是1927年12月31日23:54:07.999,这正好是1,这就是为什么你看到的输出是1的原因。


1927年12月31日,上海时区发生了一次变化。

请查看 这个页面 获取1927年12月31日在上海的详细时间信息。基本上,在1927年底午夜时分,时钟倒退了5分钟52秒。因此,“1927-12-31 23:54:08”实际上发生了两次,看起来Java将其解析为该本地日期/时间更晚的可能时刻,因此出现了差异。

这只是经常奇怪和美妙的时区世界中的一个插曲。

如果使用TZDB 2013a版本重新构建,原始问题将不再展示完全相同的行为。在2013a中,结果将是358秒,而过渡时间是23:54:03而不是23:54:08。

我只是在Noda Time中以单元测试的形式收集这样的问题,这使我注意到了这个差异 - 甚至历史数据也不安全。

在TZDB 2014f中,时区变化的时间已经移动到1900年12月31日,现在只是一个只有343秒的变化(所以如果你明白了我的意思,在t和t+1之间的时间是344秒)。


回答一个关于1900年转换的问题,看起来Java时区实现将所有时区都视为在任何1900年UTC开始之前的任何瞬间的标准时间:

import java.util.TimeZone;

public class Test {
    public static void main(String[] args) throws Exception {
        long startOf1900Utc = -2208988800000L;
        for (String id : TimeZone.getAvailableIDs()) {
            TimeZone zone = TimeZone.getTimeZone(id);
            if (zone.getRawOffset() != zone.getOffset(startOf1900Utc - 1)) {
                System.out.println(id);
            }
        }
    }
}

上面的代码在我的Windows机器上没有输出。因此,任何在1900年开始时具有除标准偏移量之外的任何其他偏移量的时区都将将该转换计算为一个过渡。TZDB本身有一些更早的数据,并且不依赖于任何“固定”标准时间的概念(这是getRawOffset假设为有效概念的想法),因此其他库不需要引入这种人为的过渡。