【问题标题】:Java Calendar returns wrong hour in MS Windows for `America/Santiago` zoneJava 日历在 MS Windows 中为“美国/圣地亚哥”区域返回错误的时间
【发布时间】:2016-09-28 18:19:27
【问题描述】:

我有一个桌面应用程序(必须在 Windows 中运行),它需要在执行特定操作时记录时间戳,但我在调用日历时得到的时间带有错误的时间和正确的时区。 运行一些测试,我发现如果我取消选中 Windows 时区设置上的“自动调整夏令时时钟”复选框,日历会提供正确的时间。 Linux 上不存在此问题。不幸的是,我不允许在 Windows 机器上进行任何更改。

更多信息:

  • Windows 时钟中设置的时区:“美国/圣地亚哥”(-4:00)。
  • 测试代码:

    public class Test2 {
    
        public static void main(String[] args) {
            SimpleDateFormat sdfBase = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
            long milisBase = Calendar.getInstance().getTimeInMillis();
            Calendar calenBase = Calendar.getInstance();
            Date fechaBase = calenBase.getTime();
            String fechaStringBase = sdfBase.format(fechaBase);
            TimeZone tzBase = Calendar.getInstance().getTimeZone();
    
    
            System.out.println("Time (in mill): " + milisBase);
            System.out.println("TimeZone: " + tzBase.getDisplayName() + " - " + tzBase.getID() + " -Offset: " + tzBase.getRawOffset());
            System.out.println("Date Calendar: " + fechaBase);
            System.out.println("Fecha StringBase: " + fechaStringBase);
        }
    }
    

得到以下结果:

选中“自动调整夏令时时钟”(Windows 时钟:17:50:07)

Time (in mill): 1464645007120
TimeZone: Chile Time - America/Santiago -Offset: -10800000
Date Calendar: Mon May 30 18:50:07 CLT 2016
Fecha StringBase: 30-05-2016 18:50:07

取消选中“自动调整夏令时时钟”(Windows 时钟:17:50:37)

Time (in mill): 1464645037914
TimeZone: GMT-04:00 - GMT-04:00 -Offset: -14400000
Date Calendar: Mon May 30 17:50:37 GMT-04:00 2016
Fecha StringBase: 30-05-2016 17:50:37

示例:当 windows 时钟读取 13:00:00 (America/Santiago -4:00) 时执行该操作,但时间戳标记时间为 14:00:00(America/Santiago -3:00) .我取消选中窗口“自动调整夏令时时钟”,由于它不是 DST,Windows 时钟保持不变,我再次执行该操作,时间戳现在读取与 Windows 时钟相同的小时。

我发现了关于时区和该时区 DST 更改的先前问题,但其中大多数是指 2015 年智利没有更改为 DST 的问题。

有什么方法可以在不干预 Windows 机器的情况下解决这个问题?

【问题讨论】:

  • 提供您的代码。
  • 第一个实例被命名为“calen”,而日期是从“calendar”中读取的。两个实例都可以使用不同的配置。或者您需要更新到真实代码。
  • getInstance 是一个函数调用,我看不到这段代码是如何通过编译器的,忘记了其中固有的运行时错误。
  • 我将很快提供测试用例的代码和结果
  • 您可以检查“timezone.useDaylightTime()”是否使用 DST。有和没有夏令时的时差是一小时。您可以在 12 月 1 日进行复查。我认为无论有没有夏令时,这两个日期都应该相同。

标签: java date time calendar timezone


【解决方案1】:

总结

智利当局在最后一刻更改了今年(2016 年)的时区规则。 Java 中的tz 数据库尚未更新。

详情如下。

java.time

您正在使用与早期 Java 捆绑在一起的旧日期时间类,这些类已被证明很麻烦。在 Java 8 及更高版本中,这些类被 java.time 框架(受 Joda-Time 库的启发)取代。避免使用旧课程。

关注世界标准时间

程序员应该首先考虑UTC。始终verify the current time in UTC 以确保计算机甚至具有正确的时间。时区颠倒和Daylight Saving Time (DST) 的变化会让你的大脑受伤,而UTC 就是你的阿司匹林。

在 Java 8 及更高版本中,Instant 类以 UTC 表示时间轴上的时刻,分辨率以纳秒为单位。

Instant now = Instant.now();
System.out.println( "Instant.now(): " + now );

始终在此问题等帖子中包含 UTC 值。

主机操作系统的时区无关紧要

JVM 拥有自己的当前默认时区表示。默认情况下,在大多数实现中,该区域是从主机操作系统的时区设置中选取的。但是 Java 启动配置可以覆盖该默认值。 JVM 内任何应用程序的任何线程中的任何 Java 代码都可以通过调用 TimeZone.setDefault 来更改 JVM 的当前默认时区在运行时

因此主机操作系统的当前时区设置是无关紧要的。重要的是 JVM 中的当前设置。

您的主机操作系统中确实重要的部分是它的硬件时钟是否正确地知道当前时刻。在大多数 JVM 实现中,当前时刻由主机硬件时钟确定,然后 JVM 应用自己的当前默认时区。

2016 年初为智利重新定义时区

政客们喜欢摆弄时区和Daylight Saving Time (DST)。智利也不例外。请参阅维基百科文章,Time in Chile

据我所知……去年,2015 年,智利在-03:00 永久停留在夏令时,并决定永远不会恢复到-04:00 的标准时间。然后他们再次改变主意,announcing in March 2016 他们将重新引入冬季的标准时间(南半球,现在是冬天,五月下旬,六月,七月)。在 2016 年 5 月 15 日午夜(几周前),-04:00 的旧标准偏移量开始生效。标准/冬季时间将持续到 2016 年 8 月 14 日(或直到他们再次改变主意)。 [带着一粒盐把这一切都拿走。请确认我得到的事实是正确的。]

这些频繁的更改对需要更新其tz time zone database 文件以反映最新改动的计算机造成严重破坏。

您的操作系统有自己的时区定义副本。 Java 有自己的副本。您的数据库引擎等其他软件可能有自己的副本。

当前的 Java 8 Update 92 缺少智利最近的更改

所以让我们在 Java 8 Update 92 中运行一个测试,看看 tz 的 Java 副本是否是最新的。

long millis = 1464645007120L;
Instant instant = Instant.ofEpochMilli ( millis );  // UTC, always.

ZoneId zoneId = ZoneId.of ( "America/Santiago" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );

转储到控制台。

System.out.println ( "millis: " + millis + "  | instant: " + instant + " | zoneId: " + zoneId + " | zdt: " + zdt );

毫:1464645007120 |瞬间:2016-05-30T21:50:07.120Z | zoneId:美国/圣地亚哥 | zdt: 2016-05-30T18:50:07.120-03:00[美国/圣地亚哥]

你可以看到最后一部分的偏移量是-03:00。因此,在我看来,current version of Java released mid-April 不幸地与 3 月份宣布的智利变化不同步。

从我的读数看来,America/Santiago 今天 (2016-05-30) 的正确偏移量应该是 -04:00,如在 time.is/Santiago 上看到的那样。

通常我会建议使用Java tzupdater tool。但是current download 标有2015 这个词所以我认为它也不是最新的。

我建议向 Oracle 的 Java 团队提交错误报告。

解决方法

您可能需要一段时间才能获得tz 数据库的更正版本(尽管也许您可以破解自己的更新——我不知道)。在此之前,作为一种解决方法,也许您可​​以使用ZoneOffset 类在Java 代码中指定您自己的偏移量,ZoneId 的子类仅表示与UTC 的偏移量,而没有任何调整异常(例如DST)的规则。我还没有考虑过其中的含义。小心点。

ZoneOffset zoneOffset = ZoneOffset.of( -4 ); // Negative four hours for '-04:00'.

【讨论】:

    猜你喜欢
    • 2017-11-09
    • 1970-01-01
    • 2014-12-27
    • 2017-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多