Java 为什么减去这两次(1927年)会得到一个奇怪的结果?
如果我运行以下程序,它将解析两个日期字符串,两个日期字符串的引用时间间隔为1秒,并对它们进行比较: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";
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,请添加每秒,并转换为本地时间显示。您将通过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时间,但可能用于显示的时间除外