为什么减去这两次(1927年)会得到一个奇怪的结果?

java date timezone


如果我运行下面的程序,解析两个日期字符串,引用相隔1秒的时间,并对它们进行比较。

public static void main(String[] args) throws ParseException {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    Date sDt3 = sf.parse(str3);  
    Date sDt4 = sf.parse(str4);  
    long ld3 = sDt3.getTime() /1000;  
    long ld4 = sDt4.getTime() /1000;
    System.out.println(ld4-ld3);
}

输出为:

353

为什么 ld4-ld3 不是 1 (就像我从一秒的时间差中期望的那样),而是 353

如果我把日期改成1秒后的时间。

String str3 = "1927-12-31 23:54:08";  
String str4 = "1927-12-31 23:54:09";  

然后 ld4-ld3 将为 1


Java版本:

java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)
Timezone(`TimeZone.getDefault()`):

sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
offset=28800000,dstSavings=0,
useDaylight=false,
transitions=19,
lastRule=null]

Locale(Locale.getDefault()): zh_CN



Answer 1 Jon Skeet


12月31日是上海的时区变化。

有关1927年上海的详细信息,请参见此页。基本上在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秒(因此,如果您明白我的意思,那么 tt+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 认为是有效的概念)的概念,因此其他库不需要引入这种人为的转换。




Answer 2 Michael Borgwardt


您遇到了当地时间不连续性

1928年1月1日,当当地标准时间即将达到星期日时,00:00:00时钟倒转0:05:52小时,到1927年12月31日星期六,当地标准时间23:54:08。1927年12月23日,当地标准时间23:54:08,而不是

这种情况并不特别奇怪,由于政治或行政行为导致时区的切换或改变,这种情况在各地都曾发生过。




Answer 3 Raedwald


这种陌生感的寓意是。

  • 尽可能使用UTC的日期和时间。
  • 如果您不能以UTC显示日期或时间,请始终指明时区。
  • 如果您不能要求输入UTC的日期/时间,则要求明确指出时区。



Answer 4 PatrickO


在递增时间时,您应该将时间转换回UTC,然后再进行加减。仅在显示时使用当地时间。

这样一来,你就可以走过任何小时或分钟发生两次的时期。

如果您转换为UTC,则每秒添加一次,然后转换为本地时间进行显示。您将经历LMT的 11:54:08 pm -LMT的11:59:59 pm,然后经历CST的 11:54:08 pm- CST的 11:59:59 pm 。




Answer 5 Rajshri


你可以使用以下代码来代替转换每个日期。

long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);

再看结果是。

1