java zoneinfo 有什么问题?

wol*_*dec 3 java timezone

我的 Mageia 4 中有欧洲/莫斯科时区。

代码是这样的

System.out.println(new java.util.Date());
System.out.println(System.getProperty("user.timezone"));
Run Code Online (Sandbox Code Playgroud)

回报

Fri Oct 24 13:43:22 GMT+03:00 2014
GMT+03:00
Run Code Online (Sandbox Code Playgroud)

如果我将系统日期设置为2014年10月24日

该代码返回

Sun Oct 26 14:44:26 GMT+03:00 2014
GMT+03:00
Run Code Online (Sandbox Code Playgroud)

如果我将系统日期设置为2014年10月26日

在我看来,这是 java zoneinfo 系统的错误行为。我下载了 tzupdater 并运行它,文件 Europe/Moscow 已更新,现在其大小为 705 kB。

我尝试下面的代码:

TimeZone.setDefault(TimeZone.getTimeZone("Europe/Moscow"));
                System.out.println(new java.util.Date());
                System.out.println(java.util.TimeZone.getDefault());
Run Code Online (Sandbox Code Playgroud)

它返回

Fri Oct 24 15:10:34 MSK 2014
sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,dstSavings=0,useDaylight=false,transitions=79,lastRule=null]
Run Code Online (Sandbox Code Playgroud)

Sun Oct 26 15:32:03 MSK 2014
sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,dstSavings=0,useDaylight=false,transitions=79,lastRule=null]
Run Code Online (Sandbox Code Playgroud)

为什么这样?为什么这两种情况下的偏移量相同?

Bas*_*que 5

太长了;博士

\n\n
    \n
  • +03:00当您表示时区 ( Europe/Moscow)时,请勿使用偏移量 ( )
  • \n
  • 切勿依赖 JVM\xe2\x80\x99s 当前的默认时区。
  • \n
  • 切勿使用java.util.Date.
  • \n
\n\n

对于 UTC 时间,请使用java.time.Instant.

\n\n
Instant.now()\n
Run Code Online (Sandbox Code Playgroud)\n\n

对于某个时区的某个时刻,请使用java.time.ZonedDateTime

\n\n
ZonedDateTime.now(\n    ZoneId.of( "Europe/Moscow" ) \n)\n
Run Code Online (Sandbox Code Playgroud)\n\n

偏移量与时区

\n\n

正如 Jon Skeet 的评论所指出的,您的 JVM\xe2\x80\x99s 初始默认时区不是一个时区,它只是与 UTC 的偏移量

\n\n

有什么不同?偏移量只是小时数、分钟数和秒数,正数(早于 UTC)或负数(晚于 UTC)。时区的意义要大得多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。只要政治家认为,某个地区的抵消额就可以改变。例如,许多政治家相信夏令时 (DST)的疯狂做法,并每年更改两次偏移量。

\n\n

因此,如果您将时区设置为仅偏移量(例如+03:00(比 UTC/GMT 提前三小时))而不是时区(例如 )Europe/Moscow,则您当前的日期时间将始终报告为比 UTC 早三个小时。您所在地区的偏移量变化(例如 DST)将被忽略,因为您是这么说的,您说过“始终比 UTC 早三个小时”。

\n\n

java.time

\n\n

您正在使用糟糕的日期时间类,这些类在几年前就被JSR 310 中定义的java.time类所取代。

\n\n

而不是TimeZone使用ZoneId.

\n\n
ZoneId z = ZoneId.of( "Europe/Moscow" ) ;\nZonedDateTime zdt = ZonedDateTime.now( z ) ;  // Capture the current moment as seen in the wall-clock time used by the people of a particular region (a time zone).\n
Run Code Online (Sandbox Code Playgroud)\n\n

避免设置默认时区

\n\n

您只应将 JVM 设置为默认时区作为最后的绝望之举。

\n\n

设置默认时区(顺便说一句,还有默认区域设置)会立即影响该 JVM 中运行的所有应用程序的所有线程中的所有代码。你会在其他程序员背后粗鲁地改变区域。您甚至可能会发现他们的代码在运行时更改了背后的区域。

\n\n

最好编写所有日期时间处理,不要依赖当前的默认区域(或区域设置)。通过传递可选参数明确指定您想要/预期的时区。就我个人而言,我希望这些时区参数是必需的而不是可选的,以帮助受过教育的程序员解决日期时间问题。

\n\n

我们可以在上面的代码中看到一个例子。请注意我们如何将ZoneId“俄罗斯”传递给该now方法。否则,我们将捕获恰好是 JVM\xe2\x80\x99s 当前默认时区的任何区域的挂钟时间中的当前时刻。

\n\n

提示:如果很重要,请务必与用户确认时区。

\n\n

java.util.Date::toString谎言

\n\n

请注意,您调用的对象toString上的方法Date具有动态应用 JVM\xe2\x80\x99s 当前默认时区的反功能,同时生成表示该时刻的文本。尽管本意是好的,但这种不幸的设计决策却让无数试图在 Java 中争论日期时间值的程序员感到困惑。Ajava.util.Date实际上采用 UTC 格式,表示自 UTC 1970 年第一个时刻以来的毫秒数。字符串中显示的时区实际上并不在Date对象中。

\n\n

但这是没有意义的,因为这是完全避免此类的众多原因之一。java.util.Instant代替使用。而不是GregorianCalendar使用ZonedDateTime.

\n\n

Java 中的日期时间类表,包括现代的和传统的。

\n\n
\n\n

关于java.time

\n\n

java.time框架内置于 Java 8 及更高版本中这些类取代了麻烦的旧遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

\n\n

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

\n\n

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

\n\n

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

\n\n

从哪里获取 java.time 类?

\n\n\n\n

ThreeTen -Extra项目通过附加类扩展了 java.time。该项目是 java.time 未来可能添加的内容的试验场。您可能会在这里找到一些有用的类,例如Interval、、YearWeekYearQuarter

\n