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

Eva*_*tti 6 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());
Run Code Online (Sandbox Code Playgroud)

输出必须是-03:00:

XML Date: 2018-10-21T01:00:00.000-02:00
Run Code Online (Sandbox Code Playgroud)

Bas*_*que 9

操作系统无关紧要

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

Java时区更新程序

因此,您需要更新Java实现中的时区定义.大多数实现使用tz数据库,也称为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因为你必须避免ZonedDateTime,如果你知道你的tzdata是过时的文件.

同一时刻,不同的挂钟时间

在此输入图像描述

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.
Run Code Online (Sandbox Code Playgroud)

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 ) ; 
Run Code Online (Sandbox Code Playgroud)

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 ) ;
Run Code Online (Sandbox Code Playgroud)

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

提取瞬间,看看我们有不同的时刻.

Instant differentInstant = differentMomentButSameTimeOfDay.toInstant() ;
Run Code Online (Sandbox Code Playgroud)

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

请注意上面的4 AM UTC与上午3点的UTC.这一刻发生在上面一刻之后的一个小时.时间轴上的两个不同点.

在完全理解时间轴上的点的概念,并且在点之间进行切换与调整偏移完全不同之前,请不要尝试这项工作.在做实际工作之前要广泛实践.半心半意的猜测会让你陷入痛苦和头痛的世界.

而且,正如我上面所建议的那样,花在安装更新的tzdata文件而不是破解这些偏移上的时间会更好.

实时代码

查看以上在IdeOne.com运行的所有代码.

到处更新tzdata

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

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

关于java.time

java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date,Calendar,和SimpleDateFormat.

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

要了解更多信息,请参阅Oracle教程.并搜索Stack Overflow以获取许多示例和解释.规范是JSR 310.

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

从哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.