如何在Java中手动设置夏令时(DST)转换日期

时间:2018-10-02 18:12:59

标签: java dst

我国将夏令时的更改日期从“ 10月21日”更改为“ 11月4日”,我们需要在后端应用此日期。

适当的解决方案是更新操作系统配置,但这样做有一定的限制(旧式依赖性)。我们正在寻找解决方法。

是否可以使用代码并以编程方式更改DST转换日期?

GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(0);
gc.set(2018, Calendar.OCTOBER, 21, 0, 0, 0);
gc.setTimeZone(TimeZone.getTimeZone("Brazil/East"));
XMLGregorianCalendar xml = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
System.out.println("XML Date: " + xml.toString());

输出必须为-03:00

XML Date: 2018-10-21T01:00:00.000-02:00

1 个答案:

答案 0 :(得分:8)

操作系统无关事件

您的操作系统配置与之无关。默认情况下,大多数Java实现在启动时都会从主机OS获取其初始默认时区。但是时区的定义存储在Java实现中。

Java时区更新程序

因此,您需要在Java实现中更新时区定义。大多数实现使用tz database,也称为 tzdata

对于Oracle品牌的Java实现,Oracle提供了Timezone Updater Tool。该目标页面的截止日期为2018-08,因此您的时区更改可能已包括在内。但我建议您更仔细地调查以进行验证。

对于其他实现,请与供应商联系。他们可能提供了JVM的更新版本,以包括新的tzdata。也许他们也提供了更新程序工具。或者,您可以手动替换tzdata文件。

避免使用代码破坏区域

我强烈建议您避免自己在代码中对偏移量进行人为调整。 您可能会弄错它。日期时间工作非常棘手且令人困惑。

但是,如果您坚持要使用,首先要避免使用可怕的旧式传统日期时间类,例如GregorianCalendarCalendarDate。这些在几年前被JSR 310取代。如果您必须与尚未更新为 java.time 的旧代码进行互操作,请在现代类中进行工作,然后最后通过添加到Java的新方法进行转换。旧课程。

使用现代的 java.time 类,具体是:

  • Instant(以UTC为主)
  • OffsetDateTime(暂时偏离UTC的时间为小时-分钟-秒,但没有时区)
  • ZonedDateTime(在特定时区的片刻)

您可以使用这些类在Stack Overflow上搜索许多现有示例和说明。您应该专注于OffsetDateTimeZoneOffset(而不是ZoneId)和Instant,因为如果您知道自己的 tzdata,则必须避免使用ZonedDateTime 文件将过时。

时刻相同,挂钟时间不同

enter image description here

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

在该示例中,odtodt2代表相同的同时时刻,时间轴上的相同点。如果提取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:00Z发音为“ Zulu”。由ISO 8601标准定义。

不同的时刻,相同的时钟时间

enter image description here

OffsetDateTime::withOffsetSameLocal​

相比之下,您可能需要强制设置一天中的时间,从而代表一个不同的时刻。为此,请使用withOffsetSameLocal方法。请注意,您正在更改数据的含义,您正在移至时间轴上的另一点。

OffsetDateTime differentMomentButSameTimeOfDay = odt. withOffsetSameLocal( offset ) ;
  

differentMomentButSameTimeOfDay.toString():2018-10-21T01:00-03:00

提取瞬间,看看我们有不同的瞬间。

Instant differentInstant = differentMomentButSameTimeOfDay.toInstant() ;
  

differentInstant.toString():2018-10-21T04:00:00Z

请注意,上述的凌晨4点UTC与凌晨3点UTC。此刻发生在上述时间的一个小时之后。时间轴上的两个不同点。

在完全理解时间线上的点的概念并且在点之间进行更改与调整偏移完全不同之前,请勿尝试此工作。在进行实际工作之前,应广泛练习。三心二意的猜测会让您陷入痛苦和头痛的世界。

而且,正如我在上面所建议的,花时间安装更新的 tzdata 文件比破解这些偏移量要好得多。

实时代码

查看run live at IdeOne.com以上的所有代码。

随时随地更新 tzdata

为了获得最佳结果,您应该在所有这些不同地方更新 tzdata (或同等功能):

  • 您的操作系统
  • 您的JVM
  • 您的数据库引擎,例如Postgres
  • 任何图书馆都捆绑了自己的时区信息(例如:Joda-Time

关于 java.time

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

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

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

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

在哪里获取java.time类?

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