操作系统无关
您的操作系统配置无关紧要。默认情况下,大多数 Java 实现会在启动时从主机操作系统中获取其初始默认时区。但是时区的定义存储在内 Java 实现中。
Java 时区更新程序
因此,您需要在 Java 实现中更新时区定义。大多数实现使用tz database,也称为tzdata。
对于 Oracle 品牌的 Java 实施,Oracle 提供了Timezone Updater Tool。该着陆页的截止日期为 2018-08,因此您的时区更改可能已包含在内。但我建议您更仔细地调查以验证。
对于其他实现,请咨询供应商。他们可能已经提供了 JVM 的更新版本以包含新的 tzdata。或者他们也提供了一个更新工具。或者您可以手动替换 tzdata 文件。
避免使用代码破坏区域
我强烈建议您避免尝试自己在代码中对偏移量进行人为调整。 你可能会弄错。日期时间工作非常棘手和混乱。
但如果你坚持,首先要避免可怕的旧日期时间类,例如GregorianCalendar & Calendar & Date。这些在几年前被 JSR 310 取代。如果您必须与尚未更新到 java.time 的旧代码进行互操作,请在现代类中完成您的工作,然后最后通过添加到旧课程。
使用现代的 java.time 类,具体来说:
-
Instant(暂时以 UTC 为准)
-
OffsetDateTime(暂时与 UTC 的偏移量为小时-分钟-秒,但没有时区)
-
ZonedDateTime(特定时区的片刻)
您可以使用这些类在 Stack Overflow 上搜索许多现有示例和说明。你应该关注OffsetDateTime、ZoneOffset(而不是ZoneId)和Instant,因为如果你知道你的tzdata文件已经过时,你必须避免ZonedDateTime。
同一时刻,不同的挂钟时间
OffsetDateTime::withOffsetSameInstant
OffsetDateTime odt = OffsetDateTime.parse( "2018-10-21T01:00:00.000-02:00" ) ;
ZoneOffset offset = ZoneOffset.ofHours( -3 ) ;
OffsetDateTime odt2 = odt.withOffsetSameInstant( offset ) ; // Same moment, same point on the timeline, different wall-clock time.
odt.toString(): 2018-10-21T01:00-02:00
odt2.toString(): 2018-10-21T00:00-03:00
在该示例中,odt 和 odt2 都表示同一时刻,时间轴上的同一点。如果您提取Instant(UTC 值),您的结果将是同一时刻。只是他们的挂钟时间不同。
Instant instant1 = odt.toInstant() ; // Adjust to UTC.
Instant instant2 = odt2.toInstant() ;
boolean sameMoment = instant1.equals( instant2 ) ;
instant1.toString(): 2018-10-21T03:00:00Z
instant2.toString(): 2018-10-21T03:00:00Z
sameMoment = true
末尾的 Z 表示 UTC,与 UTC 的偏移量为零,+00:00。 Z 发音为“祖鲁语”。由ISO 8601 标准定义。
不同的时刻,相同的挂钟时间
OffsetDateTime::withOffsetSameLocal
相比之下,您可能想要强制使用时间,从而代表一个不同的时刻。为此,请使用withOffsetSameLocal 方法。请注意,您正在改变数据的含义,您正在移动到时间轴上的另一个点。
OffsetDateTime differentMomentButSameTimeOfDay = odt. withOffsetSameLocal( offset ) ;
differentMomentButSameTimeOfDay.toString(): 2018-10-21T01:00-03:00
提取瞬间,看看我们有不同的瞬间。
Instant differentInstant = differentMomentButSameTimeOfDay.toInstant() ;
不同的Instant.toString(): 2018-10-21T04:00:00Z
请注意上面看到的 4 AM UTC 与 3 AM UTC。这里的这一时刻发生在上述时刻之后的一个小时。时间线上的两个不同点。
在您完全理解时间轴上的点的概念之前不要尝试这项工作,并且点之间的变化与调整偏移完全不同。在做实际工作之前广泛练习。三心二意的猜测会让你陷入痛苦和头痛的世界。
而且,正如我上面所建议的,您最好花时间安装更新的 tzdata 文件,而不是破解这些偏移量。
实时代码
查看以上所有代码run live at IdeOne.com。
到处更新tzdata
为了获得最佳效果,您应该在所有这些不同的地方更新 tzdata(或等效的):
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 & JPA 2.2 支持 java.time。
从哪里获取 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。