Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么减去这两次(1927年)会得到一个奇怪的结果?_Java_Date_Timezone - Fatal编程技术网

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

Java 为什么减去这两次(1927年)会得到一个奇怪的结果?,java,date,timezone,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";

如果我运行以下程序,它将解析两个日期字符串,两个日期字符串的引用时间间隔为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版本:

353
java版本“1.6.0_22”
Java(TM)SE运行时环境(build 1.6.0_22-b04)
动态代码演化客户端VM(构建0.2-b02-internal、19.0-b04-internal、混合模式)
时区(`Timezone.getDefault()`): sun.util.calendar.ZoneInfo[id=“亚洲/上海”, 偏移量=2880000,节省量=0, useDaylight=false, 转换=19, lastRule=null] 语言环境(Locale.getDefault()):zh\u CN
12月31日是上海的时区变更

有关1927年在上海的详细信息,请参见。基本上在1927年底的午夜,时钟倒转了5分52秒。所以“1927-12-3123:54:08”实际上发生了两次,看起来Java正在解析它,作为本地日期/时间的晚一个可能的瞬间-因此不同

这不过是时区这个通常怪异而奇妙的世界中的又一个插曲

编辑:停止按!历史变迁

如果使用的2013a版本重新构建,原始问题将不再表现出完全相同的行为。在2013a,结果是358秒,转换时间是23:54:03,而不是23:54:08

我之所以注意到这一点,是因为我在野田佳彦时代收集了这样的问题,形式是。。。测试现在已经改变了,但它只是去显示-甚至历史数据都不是安全的

编辑:历史记录再次更改

在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
假定的有效概念),因此其他库不需要引入这种人工转换。

您遇到了一个:

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


这并不特别奇怪,因为时区因政治或行政行为而被切换或改变,时不时发生在几乎所有地方。

这种奇怪现象的寓意是:

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

增加时间时,应将时间转换回UTC,然后加或减。仅将本地时间用于显示

通过这种方式,你将能够走过任何时间段,其中小时或分钟发生两次


如果转换为UTC,请添加每秒,并转换为本地时间显示。您将通过LMT的11:54:08 p.m.-11:59:59 p.m.和CST的11:54:08 p.m.-11:59:59 p.m.

而不是转换每个日期,您可以使用以下代码:

long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);
然后看到结果是:

1

正如其他人所解释的,这里有一个时间间断。
1927-12-31 23:54:08在
Asia/Shanghai
有两个可能的时区偏移,但只有一个偏移用于
1927-12-31 23:54:07
。因此,根据使用的偏移量,可能有1秒的差异,也可能有5分53秒的差异

这种偏移量的轻微变化,而不是我们习惯的一小时夏令时(夏季),稍微掩盖了这个问题

请注意,2013年时区数据库的更新将这种不连续性提前了几秒钟,但其影响仍然可以观察到

Java8上新的
java.time
包让我们更清楚地看到这一点,并提供处理它的工具。鉴于:

DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");

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

ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);

Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());

Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());
然后,
durationAtLaterOffset
将为1秒,而
durationAtLaterOffset
将为5分53秒

此外,这两个偏移相同:

// Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();
但这两个是不同的:

// +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();

// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();
1927-12-31 23:59:59
1928-01-01 00:00:00
进行比较,您可以看到相同的问题,不过,在这种情况下,较早的偏移量产生较长的发散,较早的日期有两个可能的偏移量

另一种方法是检查是否正在进行转换。我们可以这样做:

// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);
通过使用
zot4
上的
isOverlap()
isGap()
方法,可以检查转换是否为重叠,其中该日期/时间有多个有效偏移,或者该日期/时间对该区域id无效


我希望,一旦Java 8被广泛使用,或者对于那些使用Java 7并采用JSR 310后端口的人来说,这有助于人们处理此类问题。

我很抱歉地说,时间的不连续性已经有所改变

两年前,就在最近

要吸取的教训:尽一切代价避免非UTC时间,但可能用于显示的时间除外