java zoneinfo有什么问题?

时间:2014-10-31 12:00:36

标签: java timezone

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

像这样的代码

System.out.println(new java.util.Date());
System.out.println(System.getProperty("user.timezone"));

返回

Fri Oct 24 13:43:22 GMT+03:00 2014
GMT+03:00

如果我在24.10.2014设置系统日期

并且该代码返回

Sun Oct 26 14:44:26 GMT+03:00 2014
GMT+03:00

如果我在26.10.2014设置系统日期

在我看来,这是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());

然后返回

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]

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]

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

3 个答案:

答案 0 :(得分:2)

通过添加正确时区的定义来解决问题。

TimeZone.setDefault(TimeZone.getTimeZone("Europe/Moscow"));

答案 1 :(得分:2)

tl; dr

  • 在表示时区(+03:00)时不要使用偏移量(Europe/Moscow
  • 从不依赖JVM当前的默认时区。
  • 请勿使用java.util.Date

使用UTC暂时使用java.time.Instant

Instant.now()

请在某个时区中使用java.time.ZonedDateTime

ZonedDateTime.now(
    ZoneId.of( "Europe/Moscow" ) 
)

偏移量与时区

正如Jon Skeet的评论所指出的那样,您的JVM的初始默认时区不是time zone,而仅仅是offset-from-UTC

有什么区别?偏移量只是小时(分钟)和秒(正数(在UTC之前)或负数(在UTC之后))的数量。时区多了 个。时区是特定区域的人们过去,现在和将来对偏移量的更改的历史记录。每当政界人士认为如此时,区域的偏移量就会改变。例如,许多政客都接受Daylight Saving Time (DST)的疯癫,并且每年两次更改抵消额。

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

java.time

您使用的是可怕的日期时间类,而这些类在几年前已被JSR 310中定义的 java.time 类取代。

使用TimeZone代替ZoneId

ZoneId z = ZoneId.of( "Europe/Moscow" ) ;
ZonedDateTime 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).

避免设置默认时区

您应该只将JVM的默认时区设置为绝望的最后一幕。

设置默认时区(顺便说一下,默认语言环境)会立即影响该JVM中运行的所有应用程序的所有线程中的所有代码。您将无礼地更改其他程序员背后的区域。您甚至可能会发现,他们的代码在运行期间 期间更改了后面的区域。

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

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

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

java.util.Date::toString说谎

请注意,您正在调用的toString对象上的Date方法具有在生成表示该时刻的文本时动态应用JVM当前默认时区的功能。尽管这是好主意,但不幸的设计决定使无数试图混淆Java中日期时间值的程序员感到困惑。 java.util.Date实际上在UTC中,以毫秒为单位,自1970年UTC的第一刻起算。字符串中显示的时区实际上不在Date对象中。

但这是没有根据的,因为这是许多完全避免此类的原因之一。请改用java.util.Instant。使用GregorianCalendar代替ZonedDateTime

table of date-time classes in Java, both modern and legacy.


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

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

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:0)

您的第二次测试(2014年10月26日)是在冬季变化之后所以您可能需要更正时间-1小时