Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manually set the Daylight Saving (DST) shift date in Java

Tags:

java

dst

My country changed the the Daylight Saving shift date from "October 21" to "November 4" and we need to apply this in our back-end.

The appropriate solution is to update the Operating System configuration, but we have restrictions to do so (legacy dependencies). We are looking for a workaround.

Is it possible to use code and change the DST shift date programmatically?

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());

Output must be -03:00:

XML Date: 2018-10-21T01:00:00.000-02:00
like image 727
Evandro Pomatti Avatar asked Oct 02 '18 18:10

Evandro Pomatti


People also ask

How do I change the time on my DST?

Daylight Saving Time begins on Sunday, March 13, 2022 at 2:00 A.M. On Saturday night, clocks are set forward one hour (i.e., losing one hour) to “spring forward.” Daylight Saving Time ends on Sunday, November 6, 2022, at 2:00 A.M. On Saturday night, clocks are set back one hour (i.e., gaining one hour) to “fall back.”

How does Java determine Daylight Savings?

The observesDaylightTime() method of TimeZone class in Java is used to check and verify whether the given date is in daylight saving time or if any transition from Standard Time to Daylight Saving Time will occur at any future time.

What is the function to offset the date for daylight saving?

IsDaylightSavingTime(DateTimeOffset) Indicates whether a specified date and time falls in the range of daylight saving time for the time zone of the current TimeZoneInfo object.


1 Answers

OS irrelevent

Your operating system configuration is irrelevant. Most Java implementations by default pick up their initial default time zone from the host OS upon launch. But the definition of the time zones is stored within the Java implementation.

Java time zone updater

So you need to update the time zone definitions within your Java implementation. Most implementations use the tz database also known as tzdata.

For the Oracle-branded Java implementation, Oracle provides the Timezone Updater Tool. That landing page has an as-of date of 2018-08, so perhaps your time zone’s changes have been included. But I suggest you investigate more closely to verify.

For other implementations, check with the vendor. They may have provided an updated version of the JVM to include the fresh tzdata. Or perhaps they too provide an updater tool. Or perhaps you can replace the tzdata file manually.

Avoid mangling zone with code

I strongly suggest you avoid trying to make artificial adjustments to the offset yourself in code. You will likely get it wrong. Date-time work in surprisingly tricky and confusing.

But if you insist, firstly avoid the terrible old legacy date-time classes such as GregorianCalendar & Calendar & Date. These were supplanted years ago by JSR 310. If you must interoperate with old code not yet updated to java.time, do your work in the modern classes and then at the end convert via new methods added to the old classes.

Use the modern the java.time classes, specifically:

  • Instant (for a moment in UTC)
  • OffsetDateTime (for a moment with an offset-from-UTC of hours-minutes-seconds but no time zone)
  • ZonedDateTime (for a moment in a particular time zone)

You can search Stack Overflow for many existing examples and explanations using these classes. You should focus on OffsetDateTime, ZoneOffset (rather than ZoneId), and Instant since you must avoid ZonedDateTime if your know your tzdata file to be outdated.

Same moment, different wall-clock time

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

In that example, both odt and odt2 represent the same simultaneous moment, the same point on the timeline. If you extract an Instant (a value in UTC), your results will be the same moment. Only their wall-clock time is different.

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

The Z on the end means UTC, an offset-from-UTC of zero, +00:00. The Z is pronounced “Zulu”. Defined by the ISO 8601 standard.

Different moment, same wall-clock time

enter image description here

OffsetDateTime::withOffsetSameLocal​

In contrast, you may want to force the time-of-day thereby representing a different moment. For that, use withOffsetSameLocal method. Be very aware that you are changing the meaning of data, you are moving to another point on the timeline.

OffsetDateTime differentMomentButSameTimeOfDay = odt. withOffsetSameLocal( offset ) ;

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

Extract the instant to see we have a different moment.

Instant differentInstant = differentMomentButSameTimeOfDay.toInstant() ;

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

Notice the 4 AM UTC versus 3 AM UTC seen above. This moment here occurs an hour after the moment above. Two different points on the timeline.

Do not attempt this work until you fully comprehend the concept of points on the timeline, and changing between points being entirely different than adjusting offsets. Practice extensively before doing real work. Half-hearted guessing will land you in a world of hurt and headache.

And, as I suggested above, your time would be much better spent installing updated tzdata files rather than hacking these offsets.

Live code

See all the code above run live at IdeOne.com.

Update tzdata everywhere

For best results, you should be updating the tzdata (or equivalent) in all these various places:

  • Your operating systems
  • Your JVMs
  • Your database engines, such as Postgres
  • Any libraries bundling their own time zone info (ex: Joda-Time)

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

Where to obtain the java.time classes?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
    • Java 9 brought some minor features and fixes.
  • Java SE 6 and Java SE 7
    • Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
    • Later versions of Android (26+) bundle implementations of the java.time classes.
    • For earlier Android (<26), the process of API desugaring brings a subset of the java.time functionality not originally built into Android.
      • If the desugaring does not offer what you need, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above) to Android. See How to use ThreeTenABP….

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

like image 129
Basil Bourque Avatar answered Nov 15 '22 00:11

Basil Bourque