java - 在 Java 中处理日期和时间的不一致

标签 java date datetime time calendar

我注意到 java 中日期和时间的奇怪行为。我有以下代码:

public class TestDateTime {
    public static void main(String[] args) {
        TimeZone.setDefault(TimeZone.getTimeZone("Europe/Helsinki"));

        Calendar calendar = GregorianCalendar.getInstance();

        assert(calendar.getTimeZone().equals(TimeZone.getDefault()));
        //Set 1899-12-30T23:00:00
        calendar.set(1899,11,30,23,0,0);
        calendar.set(Calendar.MILLISECOND,0);

        long timeInMillis = calendar.getTimeInMillis();
        java.util.Date calendarDateTime = new java.util.Date(timeInMillis);

        LocalDateTime localDateTime = LocalDateTime.ofInstant(ofEpochMilli(timeInMillis), ZoneId.systemDefault());
        System.out.println("Time in millis: " + timeInMillis);
        System.out.println("Date: " + calendarDateTime.toString());
        System.out.println("Local DateTime: " + localDateTime.toString());
    }
}

输出是:

Time in millis: -2209086000000
Date: Sat Dec 30 23:00:00 EET 1899
Local DateTime: 1899-12-30T22:39:49

timeInMillis 必须包含从 1970-01-01T00:00:00Z 传递的毫秒数。 Date 类的实例存储从 1970-01-01T00:00:00Z 传递的毫秒数。 Date.toString() 方法返回默认时区的本地日期和时间。

因此 Date.toString()LocalDateTime.toString() 必须返回相同的日期和时间,但我们看到了差异(超过 20 分钟)。

这是 java 的错误,还是我在 Java 中错误地使用了日期和时间?

最佳答案

这是芬兰时间变化造成的怪异现象,参见Clock Changes in Helsinki, Finland (Helsingfors) in 1921 :

May 1, 1921 - Time Zone Change (HMT → EET)

When local standard time was about to reach Sunday, May 1, 1921, 12:00:00 midnight clocks were turned forward 0:20:11 hours to Sunday, May 1, 1921, 12:20:11 am local standard time instead

那 20 分 11 秒似乎就是您所观察到的。

作为Jim Garrison said in his answer , LocalDateTime 正确处理了它,而 Calendar 则没有。

实际上,似乎旧的 TimeZone 得到的偏移量是错误的,而新的 ZoneId 得到的是正确的,如以下测试代码所示:

public static void main(String[] args) {
    compare(1800, 1, 1,  0, 0, 0);
    compare(1899,12,31, 23,59,59);
    compare(1900, 1, 1,  0, 0, 0);
    compare(1900,12,30, 23, 0, 0);
    compare(1921, 4,30,  0, 0, 0);
    compare(1921, 5, 1,  0, 0, 0);
    compare(1921, 5, 2,  0, 0, 0);
}
private static void compare(int year, int month, int day, int hour, int minute, int second) {
    Calendar calendar = new GregorianCalendar();
    calendar.clear();
    calendar.setTimeZone(TimeZone.getTimeZone("Europe/Helsinki"));
    calendar.set(year, month-1, day, hour, minute, second);
    Date date = calendar.getTime();
    
    ZonedDateTime zdt = ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.of("Europe/Helsinki"));
    
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z XXX");
    sdf.setTimeZone(TimeZone.getTimeZone("Europe/Helsinki"));
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss z XXX");
    
    System.out.printf("%04d-%02d-%02d %02d:%02d:%02d   %s = %d   %s = %d   %d%n",
                      year, month, day, hour, minute, second,
                      sdf.format(date), date.getTime(),
                      dtf.format(zdt), zdt.toInstant().toEpochMilli(),
                      date.getTime() - zdt.toInstant().toEpochMilli());
}

输出

1800-01-01 00:00:00   1800-01-01 00:00:00 EET +02:00 = -5364669600000   1800-01-01 00:00:00 EET +01:39 = -5364668389000   -1211000
1899-12-31 23:59:59   1899-12-31 23:59:59 EET +02:00 = -2208996001000   1899-12-31 23:59:59 EET +01:39 = -2208994790000   -1211000
1900-01-01 00:00:00   1900-01-01 00:00:00 EET +02:00 = -2208996000000   1900-01-01 00:00:00 EET +01:39 = -2208994789000   -1211000
1900-12-30 23:00:00   1900-12-30 23:00:00 EET +01:39 = -2177548789000   1900-12-30 23:00:00 EET +01:39 = -2177548789000   0
1921-04-30 00:00:00   1921-04-30 00:00:00 EET +01:39 = -1536025189000   1921-04-30 00:00:00 EET +01:39 = -1536025189000   0
1921-05-01 00:00:00   1921-05-01 00:20:11 EET +02:00 = -1535938789000   1921-05-01 00:20:11 EET +02:00 = -1535938789000   0
1921-05-02 00:00:00   1921-05-02 00:00:00 EET +02:00 = -1535853600000   1921-05-02 00:00:00 EET +02:00 = -1535853600000   0

关于java - 在 Java 中处理日期和时间的不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42865515/

相关文章:

java - 如何将 Java 流中的 RuntimeExceptions 从无效的流元素映射到 "recover"

javascript - 如何将格式为 "YYYY-MM-DD hh:mm:ss"的日期转换为 UNIX 时间戳

行级别和表级别的 MySQL 日期时间和时间戳

java - Hashtable contains() 不读取 char 类型

java - 尝试在我的世界启动器 VoidWrath 中运行 mod,我收到错误 java.lang.NoClassDefFoundError : javax/servlet/ServletContextListener

java - 对象化查询 : filter by date

python - 将日期时间索引透视到开始列和结束列

javascript - 如何将 GMT 时间转换为本地时间?

java - Temboo Google 初始化 Java 的 OAuth

R 日期格式,如何将日期格式更改为 2011 年 6 月 1 日