java - 为什么减去两个时间( 以1927 ) 会给出一个奇怪的结果?

  显示原文与译文双语对照的内容
0 0

如果我运行下面的程序,它将分析两个日期字符串的引用时间间隔一秒,并比较它们:

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(ld3); 
 System.out.println(ld4); 
 System.out.println(ld4-ld3);
}

输出结果为:

-1325491905
-1325491552
353

为什么 ld4-ld3 不是 1 ( 就像我在one-second中的差异所期望的那样),而是 353

如果我将日期更改为一秒钟:

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
时间: 原作者:

0 0

这是在上海 31年十二月的时区变化。

有关详细信息,请参阅本页 1927的上海。 基本上在 1927的午夜时分,时钟返回 5分钟和 52秒。 "1927-12-31 23:54:08"na actually_________________________________________________________________前,parsing looks likeit爪哇islater华南地区的地方,可能瞬间fortime-日期/因而区别。

只是在时间区域的奇妙奇妙世界中又一个插曲。

收费: 停止媒体 ! 历史更改。。

原始问题将不再显示同样的行为,如果用 2013版本的TZDB 重新生成。 在 2013,结果将是 358秒,过渡时间的23: 54: 03代替 23: 54: 08.

我只注意到这是因为我在Noda时间内收集这样的问题,以单元测试的形式 。 测试现在已经改变了,但它只是为了显示- 即使历史数据是安全的。

编辑: 历史再次更改。。

在 TZDB 2014中,更改时间已经移动到 1900-12-31,现在是一个 343秒的更改( 所以 tt+1 之间的时间是 344秒,如果你明白我的意思) 。

编辑:回答肯问题的亲戚过渡 1900. 看起来java时区实现对待所有时区就好比是在标准时间任何即时 1900utc开始前:

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 认为是一个有效的概念)的任何概念,所以其他库不需要引入这种人工转换。

原作者:
0 0

我运行了你的代码,输出是:

-1325466353
-1325466352
1

还有:

-1325466352
-1325466351
1

打开:

$ java -version
java version"1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.7) (fedora-52.1.9.7.fc14-i386)
OpenJDK Client VM (build 19.0-b09, mixed mode)
原作者:
0 0

当递增时,你应该转换回 UTC,然后添加或者减去。 仅使用本地时间显示。

这样你就可以在任何时间或者时间发生两次。

如果转换为 UTC,则添加每秒,并转换为本地时间以显示。 你会经过 11: 54: 08 p.m. 以前 - 11: 59: 59 p.m. 以前然后 11: 54: 08 p.m. 中科 - 11 0: 11 1: 11 2 p.m. 春秋国旅。

原作者:
0 0

使用以下代码代替每个日期

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

结果是:

1
原作者:
0 0

我很抱歉,但是时间不连续已经移动了一点

两年前 JDK 6, JDK 7最近在更新 25

学习的教训:避免non-UTC时间,除了可能的显示。

原作者:
0 0

就像其他人所解释的,有一个时间不连续。 1927-12-31 23:54:08Asia/Shanghai 处有两个可能的时区偏移量,但 1927-12-31 23:54:07 只有一个偏移量。 所以,根据使用的偏移量,有一个第二个差或者 5分钟和 53秒差。

这种偏移的轻微偏移,而不是通常的one-hour日光节约( 夏季时间),使问题有点模糊。

注意,2013更新时区数据库在几秒钟之前移动了这个不连续,但效果仍然可以观察到。

java.time 包java 8让使用更清楚地看到这个,并提供工具来处理它。 给出:

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());

然后 durationAtEarlierOffset 将是一秒钟,而 durationAtLaterOffset 将是五分钟和 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 相比,同样的问题,但在这种情况下,它是产生较长散度的早期偏移量,它是有两个可能偏移的早期日期。

另一种方法是检查是否发生了转换。 我们可以这样做:

//Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);
//An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

你可以检查是否过渡是一个重叠——在这种情况下有多个有效抵消,日期/时间——或者一个缺口——在这种情况下,日期/时间无效区域id-通过使用 isOverlap()zot4isGap() 方法。

我希望这能帮助人们处理这种问题一旦java 8越来越广泛,或使用java 7那些采用jsr 310 backport 。

原作者:
...