java timezone用法 date设置时区 - 为什么减去这两次(在1927年)给出一个奇怪的结果?




4 Answers

这是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的形式收集这样的问题......测试现在已经改变了,但它只是显示 - 甚至历史数据都不安全。

编辑:历史再次发生变化......

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

timezone北京时间 java获取当前时区

如果我运行以下程序,它解析引用时间间隔为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 (正如我所预期的那样,时间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



这种陌生感的道德是:

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



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

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

并看到结果是:

1



正如其他人所解释的那样,那里有一段时间不连续。 Asia/Shanghai 1927-12-31 23:54:08有两种可能的时区偏移,但1927-12-31 23:54:07只有一次偏移。 因此,根据使用的偏移量,存在一秒差异或5分53秒差异。

这种轻微的偏移转移,而不是我们习惯的通常的一小时夏令时(夏令时),可以稍微模糊一下这个问题。

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

Java 8上的新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());

然后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:591928-01-01 00:00:00相同的问题,但在这种情况下,它是产生较长分歧的早期偏移,它是较早的日期,有两个可能的抵消。

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

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

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

您可以检查转换是否是重叠 - 在这种情况下,该日期/时间有多个有效偏移量 - 或间隙 - 在这种情况下,日期/时间对于该区域ID无效 - 通过使用isOverlap()isGap()方法。

我希望这有助于人们在Java 8广泛使用后处理这类问题,或者使用采用JSR 310反向端口的Java 7的人。




Related