Why the seconds between the parsed LocalDateTime variables of timestamp -923130000 and -923130001 is 3599?

huangapple go评论70阅读模式
英文:

Why the seconds between the parsed LocalDateTime variables of timestamp -923130000 and -923130001 is 3599?

问题

Java解析时间戳-923130000和-923130001为LocalDateTime失败。两者之间的秒数差为3599。太奇怪了。

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;

public class TestLocalDateTime {

    public static void main(String[] args) {
        System.out.println("-923130000: " + toLocalDateTime(-923130000L));
        System.out.println("-923130001: " + toLocalDateTime(-923130001L));

        System.out.println(ChronoUnit.SECONDS.between(toLocalDateTime(-923130000L), toLocalDateTime(-923130001L)));
    }

    private static LocalDateTime toLocalDateTime(Long timestamp) {
        return LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
    }

}

输出:

-923130000: 1940-09-30T23:00
-923130001: 1940-09-30T23:59:59
3599

Java版本:

$ java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

此代码在macOS上执行。

英文:

Java parse timestamp -923130000 and -923130001 as LocalDateTime failed. The seconds between the two result is 3599. It's too weired.

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;

public class TestLocalDateTime {

    public static void main(String[] args) {
        System.out.println("-923130000: " + toLocalDateTime(-923130000L));
        System.out.println("-923130001: " + toLocalDateTime(-923130001L));

        System.out.println(ChronoUnit.SECONDS.between(toLocalDateTime(-923130000L), toLocalDateTime(-923130001L)));
    }

    private static LocalDateTime toLocalDateTime(Long timestamp) {
        return LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
    }

}

output:

-923130000: 1940-09-30T23:00
-923130001: 1940-09-30T23:59:59
3599

Java version:

$ java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

The code is executed on macOS .

答案1

得分: 5

显然是时区数据中的一个错误

Java 8 和 9 似乎认为中国时区在 1940 年 9 月 30 日之后的午夜从偏移量 +09:00 更改为 +08:00,因此在 23:59:59 后的一秒将再次变为 23:00。显示此问题的时区包括 Asia/Chungking、Asia/Shanghai、Asia/Chongqing、PRC 和 Asia/Harbin。

看起来 Java 在这一点上是错误的。我在 dateandtime.com 上检查了上海,它对这个夜晚没有任何时区转换的记录。并且我无法在 Java 11 上复现相同的行为。

可以使用以下代码片段来观察这一点:

ZoneId zone = ZoneId.of("Asia/Shanghai");
System.out.println(Instant.ofEpochSecond(-923_130_001L).atZone(zone));
System.out.println(Instant.ofEpochSecond(-923_130_000L).atZone(zone));

在 Java 8 和 9 上的输出:

> 1940-09-30T23:59:59+09:00[Asia/Shanghai]
> 1940-09-30T23:00+08:00[Asia/Shanghai]

在 Java 11 上的输出:

> 1940-09-30T23:59:59+09:00[Asia/Shanghai]
> 1940-10-01T00:00+09:00[Asia/Shanghai]

假设您暂时不打算立即升级到 Java 10 或 11,您可以做的最好的事情可能是更新当前 Java 8 安装中的时区数据库。请参阅时区更新工具

每日提示:LocalDateTime 上始终优先使用 ZonedDateTimeZonedDateTime 知道自己的时区和偏移量。在您的情况下,只需打印 ZonedDateTime 对象或在调试器中检查它们,即可了解发生了什么。将时区作为日期时间对象的一部分可以防止许多愚蠢的错误。ZonedDateTime 可以以与 LocalDateTime 相同(甚至更多)的方式进行格式化,因此向用户呈现的方式不会改变。

链接: 上海多年来的时间变化

英文:

Apparently a bug in the time zone data

Java 8 and 9 seem to believe that China time zone changed from offset +09:00 to +08:00 at midnight after September 30, 1940, so the second after 23:59:59 would again be 23:00. Time zones showing this include Asia/Chungking, Asia/Shanghai, Asia/Chongqing, PRC and Asia/Harbin.

It also seems that Java is wrong on this point. I checked Shanghai on dateandtime.com, and it knows of no time zone transition in this night. Also I cannot reproduce the same behaviour on Java 11.

One way to see this is using the following code snippet:

	ZoneId zone = ZoneId.of("Asia/Shanghai");
	System.out.println(Instant.ofEpochSecond(-923_130_001L).atZone(zone));
	System.out.println(Instant.ofEpochSecond(-923_130_000L).atZone(zone));

Output on Java 8 and 9:

> 1940-09-30T23:59:59+09:00[Asia/Shanghai]
> 1940-09-30T23:00+08:00[Asia/Shanghai]

Output on Java 11:

> 1940-09-30T23:59:59+09:00[Asia/Shanghai]
> 1940-10-01T00:00+09:00[Asia/Shanghai]

Assuming you don’t want to upgrade to java 10 or 11 or later immediately the best thing you can do is probably update the time zone database in your current Java 8 installation/s. See Timezone Updater Tool.

Tip of the day: Always prefer ZonedDateTime over LocalDateTime. A ZonedDateTime knows its own time zone and offset. In your situation just printing the ZonedDateTime objects or inspecting them in the debugger would reveal what’s happening. Having the time zone be part of the date-time object prevents many silly mistakes. A ZonedDateTime can be formatted in any way a LocalDateTime can (and more), so presentation to the user is unchanged.

Link: Time Changes in Shanghai Over the Years

huangapple
  • 本文由 发表于 2020年10月14日 00:31:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/64339380.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定