代码之家  ›  专栏  ›  技术社区  ›  Evandro Pomatti

如何在爪哇手动设置日光节约(DST)换档日期

  •  6
  • Evandro Pomatti  · 技术社区  · 6 年前

    我国将夏令时的换班日期从“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 回复  |  直到 6 年前
        1
  •  8
  •   Basil Bourque    6 年前

    操作系统无关

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

    。 < H1> Java时区更新器< /H1> <> P>因此,您需要更新Java实现中的时区定义。大多数实现都使用 tz database 也被称为 tzdata

    < Oracle标记的Java实现,Oracle提供了 EM >时区更新工具
    < /A>。该登录页的日期为2018-08,因此可能包括您的时区更改。但我建议您进行更密切的调查以核实。

    对于其他实现,请与供应商联系。他们可能已经提供了JVM的更新版本,以包含新的TZdata。或者他们也提供了一个更新工具。或者您可以手动替换TZDATA文件。

    使用代码避免损坏区域

    我强烈建议您避免在代码中人为地调整偏移量。 你很可能会弄错。 约会时间工作异常复杂和混乱。

    但是如果你坚持,首先要避免那些可怕的旧的传统日期时间类,例如 gregoriancalendar & calendar & date>。这些在几年前被JSR 310所取代。如果必须与旧代码进行互操作,还没有更新到 java.Time ,请在现代类中完成工作,然后在结束时通过添加到旧类的新方法进行转换。

    使用现代的 java.时间< /EM>类,具体地说:

    • instant (暂时使用UTC)
    • offsetdatetime (暂时从UTC偏移小时分钟秒,但没有时区)
    • ZonedDateTime (在特定时区待一会儿)

    您可以使用这些类在堆栈溢出中搜索许多现有的示例和解释。您应该专注于 offsetdatetime >, zoneoffset >(而不是 代码> ZONID ID/代码> < /A>,和 代码>即时< /COD> < /A>,因为您必须避免<代码> ZONDATEDATIMECT/<代码>如果您知道您的 TZDATA 文件过期。

    0/DOCS

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

    offsetdatetime::withoffsetSameinstant_139;

    offsetdatetime odt=offsetdatetime.parse(“2018-10-21t01:00:00.000-02:00”);
    zoneoffset offset=zoneoffset.ofhours(-3);
    offsetdatetime odt2=odt.withoffsetsameinstant_(offset);//同一时刻、同一时间线上的点、不同的壁钟时间。
    < /代码> 
    
    < Buff行情>
    

    odt.toString():2018-10-21t01:00-02:00

    odt2.toString():2018-10-21t00:00-03:00

    < /块引用>

    在该示例中,odtodt2都表示同一同时时刻,时间线上的同一点。如果您提取一个instant>(a value in utc),您的结果将是相同的时刻。只有他们的挂钟时间不同。

    instant instant1=odt.toInstant();//调整到UTC。
    Instant Instant2=odt2.ToInstant();
    布尔samemotion=instant1.equals(instant2);
    < /代码> 
    
    < Buff行情>
    

    Instant1.ToString():2018-10-21t03:00:00z

    Instant2.ToString():2018-10-21t03:00:00z

    samemomen=真

    < /块引用>

    结束时的z表示UTC,从UTC到零的偏移量,+00:00z读作__zulu__。由iso 8601标准定义。

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

    offsetdatetime::withoffsetSamelocal_139;

    相反,您可能希望强制一天中的时间,从而表示一个不同的时间。为此,使用代码> OffStSAMELOLCAL< <代码> < /A>方法。请注意,您正在更改数据的含义,您将移动到时间线上的另一个点。

    offsetDateTime differentMomentButSametimeOfDay=odt.带偏移量同步(偏移量);
    < /代码> 
    
    < Buff行情>
    

    差分时间间隔:2018-10-21t01:00-03:00

    < /块引用>

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

    instant differentinstant=differentMomentButSametimeofday.toInstant();
    < /代码> 
    
    < Buff行情>
    

    差分状态toString():2018-10-21t04:00:00z

    < /块引用>

    请注意上面看到的4 AM UTC与3 AM UTC。这一时刻发生在上述时刻一小时后。时间线上的两个不同点。

    在完全理解时间线上的点的概念之前,不要尝试这项工作,并且在点之间的变化与调整偏移完全不同。在做真正的工作之前广泛地练习。半心半意的猜测会让你陷入伤痛和头痛的世界。

    而且,正如我上面所建议的,您的时间将更好地花在安装更新的tzdatafiles上,而不是黑客攻击这些偏移量上。

    实时代码

    请参阅上面的所有代码run live at ideone.com.>

    更新所有地方

    为了获得最佳结果,您应该在所有这些不同的位置更新TZdata(或等效工具):。


    关于EM> Java.时间

    > HRFF =“HTTP://DOCS.Oracle .COM/JavaSe/10/DOCS/AIM/Java/Time/PayaGeReal.Simuly.HTML”Re=“NoFoLoLoNeFror”> EM>Java.Time< A/>框架被构建到Java 8和以后。En.WiKiTo.Org/WiKi/LeaCyyl系统“Reave=”NoFoLoReNoFreRe> >日期时间类,如“HyPSE//DOCS.Oracle .COM/JavaS/10/DOCS/API/Java/UTL/DATE.HTML”Re=“NoFoLoLoNeFror”>“代码> Java.UTIL.DATE < /COD> < /A>,< HRFF=”HTTPS:/DOCS.Oracle .COM/JavaSe/10/DOCS/API/Java/这些类取代了麻烦的旧的< HRFF=“HTTPS://”。util/calendar.html“rel=”nofollow noreferrer“>calendar,&simpledateformat

    joda time>a>project,now inmaintenance mode,advises migration to the > java.时间< /a>类。

    要了解更多信息,请参阅oracle tutorial>。以及搜索堆栈溢出以获得许多示例和解释。规范是jsr 310

    你可以交换 java.时间< /e>对象直接与你的数据库。使用ajdbc drivercompliance withjdbc 4.2或更高版本。不需要字符串,不需要<代码> Java.SQL.*/Cux>类。

    在哪里获取java.time类?

      SEE8,“NoroLoLo NorfRever”> JavaSe 8 < /强> < /A>,< HeRF=“http://or.wig/wiki/javayVeluon,历史>αjavaSe9”Re=“NorfOLLO NoreForver”>强> Java SE 9 <强> < /A>,< HRFF=“http://En.org/wiki /javauVeluon历史JavaaSe10”Re= =“nfoLo.n”。
    • =http://e.维基百科. org/wiki/javai版本.OrrForErr>强> > JavaSE 10 <强> > A/>,< HRFF=“http://eN.gor/wik/javayVeluon历史> javaSe11”Re=“NofOLLoNoreForver”>强> Java SE 11 <强> ,后一部分为标准JAVA API,捆绑式实现。
        Java 9添加了一些小的特征和修复。
    • < HiRF=“http:/or.wig/wiki/javayVeluon历史> javaaSe6”Re=“强> JavaSE 6 < /A>和< A HRFF=“http://En.org/wiki /javai版本>历史> JavaSe7”Re=“NofOLLL NoreForver”>强> Java SE 7 <强> < /A>
      • 大部分Java.TimeActudio被归入Java 6和7中的< A HRFF=“HTTP://www-thuteN.Org/StuteTeNbp/”Re=“NoFoLoLoeFror”> <强> e> ThreeTen Backport <强> ./LI>
    • android

    threeten extra.project extends java.time with additional classes.这个项目是将来可能添加到java.time的一个试验场。您可以在这里找到一些有用的类,例如interval,yearweek,yearquarter>,andmore.

    启动时从主机操作系统。但是定义存储的时区数在内部Java实现。

    Java时区更新器

    因此,您需要更新Java实现中的时区定义。大多数实现使用tz database亦称为TZDATA.

    对于Oracle品牌的Java实现,Oracle提供了Timezone Updater Tool. 该登录页的日期为2018-08,因此可能包括您的时区更改。但我建议你更仔细地调查核实。

    对于其他实现,请与供应商核实。他们可能已经提供了JVM的更新版本,以包含新的TZdata。或者他们也提供了一个更新工具。或者您可以手动替换TZDATA文件。

    使用代码避免损坏区域

    我强烈建议您避免在代码中人为地调整偏移量。你很可能弄错了。约会时间的工作令人惊讶的棘手和混乱。

    但是如果你坚持的话,首先要避免像GregorianCalendar和;Calendar和;Date. 这些在几年前被JSR 310所取代。如果必须与尚未更新到的旧代码进行互操作Java.时间在现代类中完成您的工作,然后在最后通过添加到旧类中的新方法进行转换。

    使用现代Java.时间类,具体来说:

    • Instant(暂时使用UTC)
    • OffsetDateTime(暂时与UTC的时差为小时分钟秒,但没有时区)
    • ZonedDateTime(在特定时区的某一时刻)

    您可以使用这些类在堆栈溢出中搜索许多现有的示例和解释。你应该专注于OffsetDateTime,ZoneOffset(而不是ZoneId)Instant因为你必须避免ZoDeDeDeTimeTimes如果你知道你的TZDATA文件过期。

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

    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表示同一同时时刻,时间线上的同一点。如果你提取一个瞬间(以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

    samemomen=真

    这个Z在末尾表示UTC,与UTC的偏移量为零,+00:00. 这个Z发音为__zulu__。定义为ISO 8601标准。

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

    enter image description here

    OffsetDateTime::withOffsetSameLocal​

    相反,您可能希望强制一天中的时间,从而表示不同的时刻。为此,使用withOffsetSameLocal方法。要知道你是更改数据的含义,您将移动到时间线上的另一个点。

    OffsetDateTime differentMomentButSameTimeOfDay = odt. withOffsetSameLocal( offset ) ;
    

    差分时间间隔:2018-10-21t01:00-03:00

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

    Instant differentInstant = differentMomentButSameTimeOfDay.toInstant() ;
    

    差分状态toString():2018-10-21t04:00:00z

    请注意上面看到的4 AM UTC与3 AM UTC。这一时刻发生在上述时刻一小时后。时间线上的两个不同点。

    在完全理解时间线上的点的概念之前,不要尝试这项工作,并且在点之间的变化与调整偏移完全不同。在做真正的工作之前广泛地练习。半心半意的猜测会让你陷入伤痛和头痛的世界。

    而且,正如我上面所建议的,您的时间将更好地用于安装更新TZDATA文件而不是黑客攻击这些偏移。

    实况编码

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

    更新TZDATA到处

    为了获得最佳结果,您应该更新TZDATA在所有这些不同的地方:

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

    关于Java.时间

    这个java.time框架是在Java 8和之后构建的。这些阶级取代了麻烦的旧阶级。legacy日期时间类,如java.util.Date,Calendar和;SimpleDateFormat.

    这个Joda-Time项目,现在maintenance mode,建议迁移到java.time类。

    要了解更多信息,请参阅Oracle Tutorial. 以及搜索堆栈溢出以获得许多示例和解释。规格为JSR 310.

    你可以换Java.时间直接使用数据库的对象。使用AJDBC driver顺从JDBC 4.2或稍后。不需要字符串,不需要java.sql.*类。

    在哪里获取java.time类?

    这个ThreeTen-ExtraProject使用其他类扩展java.time。这个项目是将来可能添加到java.time的一个试验场。您可以在这里找到一些有用的类,例如Interval,YearWeek,YearQuartermore.