Java时区设置错误

12

我有一个Java实例,似乎使用了完全错误的时区。它使用的是美国加拉加斯时区,而不是Windows正在使用的澳大利亚/悉尼时区。

我首先通过系统时钟检查了Windows时间,然后检查了 HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/ControlSet001ControlSet002。所有这些都设置为悉尼时区。

是否有人知道这是否是Java的一个bug,或者它是在引用其他地方设置的时间?

Java版本是1.6.0_06


在很久以前,我曾经看到过一个 JVM 认为 OS/2 中的“MET”时区不是中欧时区,而是中东时区。花了我一些时间才弄清楚为什么所有日期都相差3.5小时。 - Thorbjørn Ravn Andersen
@ThorbjørnRavnAndersen 这是因为像 ESTIST 这样的 2-4 个字母的缩写不是真正的时区,没有标准化,甚至不是唯一的!请以 大陆/地区 的格式指定一个正确的时区名称,例如 America/MontrealAfrica/CasablancaPacific/Auckland - Basil Bourque
@BasilBourque 我认为在那之前OS/2已经消失了。由于最初的提议是来自1993年(https://mm.icann.org/pipermail/tz/1993-October/009233.html),而我使用的Warp是在96年发布的,这听起来很有可能。 - Thorbjørn Ravn Andersen
10个回答

14

确保在启动应用程序时为JVM设置时区:

-Duser.timezone="Australia/Sydney"

本来希望得到一个合适的解决方案,但最终选择了这个临时方法。 - Jonathan Maddison
-Duser.timezone="UTC" - Mike Causer

6

6

2

尝试在您的应用程序中获取默认时区,或手动设置时区(已注释行)。

以下是我的一个例子:

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        Locale locale = Locale.getDefault();
        TimeZone localTimeZone = TimeZone.getDefault(); 
        //TimeZone localTimeZone = TimeZone.getTimeZone("Australia/Sydney");
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, locale);
        dateFormat.setTimeZone(localTimeZone);
        Date rightNow = new Date();
        System.out.println(locale.toString() + ": " + dateFormat.format(rightNow));
    }
}

谢谢大家,我在想为什么我需要手动指定时区。难道不应该与操作系统相同吗? - Jonathan Maddison
顺便提一下,上述代码返回了正确的语言环境,但是时区不正确。 - Jonathan Maddison
应该这样做。你可以尝试更新Java到最新版本,以防有错误。 - Robert Balent
有时候部署者无法控制操作系统的设置(比如我自己),因此编程方式进行设置更为可取。 - Fred Haslam
@balent 不正确:时区和语言环境都不需要与主机操作系统匹配。大多数 JVM 在启动时默认使用主机操作系统的设置,但可以通过向启动 JVM 发送标志或在运行时调用 setDefault 的 Java 代码来覆盖这些设置。请注意是在运行时调用 setDefault - Basil Bourque

2
我最近也遇到了同样的问题,这似乎是由于Windows在注册表中表示其时区设置的歧义以及Java无法正确解释它所导致的。
更多细节可以在这篇文章中找到,该文章还描述了受影响计算机的“治疗”方法:
  • 手动更改日期/时间,然后改回原来的正确时间。
  • 更改时区,然后改回原来的时区。
  • 从时间服务器请求自动时间更新。

在升级到Windows Server 2008后,我们使用Java 7遇到了相同的问题。更改系统的时区,然后将其恢复为原来的设置可以为我们解决该问题。 - Didier L

1

这样的问题历史悠久,来来去去,始终没有一个合理的解决方案。在此处查看更多信息:Java在Windows上错误的时区错误

编辑:回答Tom和Francis:简而言之,Java运行时很难正确地找到计算机上的当前时区。

Windows注册表中关于时区的信息不可靠,并且本机Windows API也是如此,它依赖于msvcrt.dll和各种msvcrxx.dll。还有管理的(.NET)API需要安装某个版本的.NET Framework,这与Java的可移植性相矛盾。

因此,Java运行时的开发人员在Windows上的当前时区方面面临困难,这可能会一直持续,直到微软有某些合作的原因。

如果您希望Java应用程序在任何时区下正常工作,请为用户提供通过GUI更正时区的可能性。


虽然这个链接或许能够回答问题,但最好包含答案的必要部分并提供该链接作为参考。仅有链接的回答可能会因为链接页面的改变而失效。 - Tom

0

如果时区似乎完全消失了,在我的情况下,Java 5被用在一个传统系统上,请确保%JAVA_HOME%\jre\lib\zi文件夹中存在正确的文件。例如,我无论如何都无法让Europe/Athens工作。结果发现%JAVA_HOME%\jre\lib\zi\Europe\Athens文件丢失了(我使用的JDK中那些指向大陆的目录完全是空的...)- 与Oracle新下载的 JDK 1.5.0_22进行了比较。


0

我通过将以下内容添加到我的数据库URL中解决了该错误:

?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC


0

我曾经遇到过同样的错误,当我将时区设置为马来半岛标准时间时,JVM给了我委内瑞拉时间时区。

以下修复方法适用于我:

在注册表编辑器中,将您的时区更改为另一个时区(我试图输入另一个文本,如“新加坡时间”)。您可以在此处找到注册表:

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/TimeZoneInformation

然后,我使用控制面板、日期和时间设置将其重置回所需的时区。当我再次检查注册表编辑器时,我可以看到它已恢复为马来半岛标准时间。现在我的JVM正确读取它了...


0

简短摘要

Java程序员不应该依赖于JVM的当前默认时区。

  • 始终以洲/地区的格式,例如Africa/Casablanca指定一个正确的时区名称
  • 当重要时,始终与用户确认所需/预期的时区。

正确的时区名称

指定一个洲/地区的格式,例如America/MontrealAfrica/CasablancaPacific/Auckland作为正确的时区名称。永远不要使用2-4个字母的缩写,如CSTESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的!例如,如果您使用IST表示爱尔兰标准时间,则可能会意外地得到印度标准时间。

ZoneId z = ZoneId.of( "America/Montreal" ) ; // Or "Pacific/Auckland", "Africa/Tunis", etc. 

指定时区

永远不要依赖 JVM 的当前默认时区。默认值可能会发生变化。

  • 正如其他答案所解释的那样,大多数 JVM 默认使用启动时主机操作系统的默认时区。
  • 或者启动 JVM 的脚本可能会传递一个标志以在启动时指定默认时区。
  • 在运行时(!),JVM 的当前默认时区可以被 JVM 中任何应用程序中任何线程中的任何代码调用 TimeZone.setDefault 设置。(顺便说一句,TimeZone 已被 ZoneId & ZoneOffset 废弃,除了那个 setter 方法外,应该只在最极端的情况下调用它。)
  • 当然,用户或系统管理员可以更改主机操作系统上的当前默认时区,这在大多数实现中运行的 JVM 中是检测到的。

因此,出于所有这些原因,JVM当前的默认时区完全不受程序员控制,更糟糕的是,它甚至可以在运行时动态更改。因此,使用当前默认值是不可靠的。

仅在最轻松的情况下使用JVM的当前默认时区。对于任何重要的事情,您必须与用户确认所需的时区。这是程序员经常抵制但却是真实的不便之处。


本地化

提示:对于本地化(Locale)也同理。JVM当前的默认Locale可能与主机操作系统不匹配,随时可能更改,并且因此不可靠。始终与用户确认所需/预期的Locale


关于java.time

java.time框架已经内置于Java 8及以后版本中。这些类替代了老旧的、麻烦的日期时间类,如legacy java.util.Date, Calendar, SimpleDateFormat, 和 java.util.TimeZone

Joda-Time 项目现在处于维护模式,建议迁移到 java.time 类。

想要了解更多,请参阅 Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是 JSR 310

您可以直接与数据库交换 java.time 对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。无需字符串,也不需要 java.sql.* 类。

如何获取 java.time 类?

ThreeTen-Extra 项目通过扩展 java.time 的类库提供了一些额外的类。该项目是用于可能未来加入 java.time 的试验场。您可以在这里找到一些有用的类,例如 Interval, YearWeek, YearQuarter, 和更多


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接