Search code examples
javadatetimejodatimedstgmt

Java API to get daylight saving boundaries for a year


I have a date range (start and end date) and require to know whether this falls within a Daylight Saving change over.

Is there any Java API available to check this or any Java code to achieve this?


Solution

  • Daylight Saving changes occur at different dates in each country/region, so the first thing to know is the name of the timezone you're checking.

    I'm writing this answer using both Joda-Time and the new Java Date/Time API and both use the IANA's list of timezone names (in the format Continent/City). Both API's also avoid to use the 3-letter names because they are ambiguous and not standard.

    For the code below I'm gonna use America/Sao_Paulo (the timezone where I live, which has DST changes every year), but you can replace it with the timezone you want.

    The code below shows you how to check if a date is in DST and find the next date when a DST change will occur. So, if you have a start and end dates and want to know if both are in within a DST change, you can check if both are in DST or not and also find the next and previous DST changes (and check if the dates are between those changes - it's not clear to me how your check should be done).


    Also be aware that Joda-Time is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".


    Joda-Time

    You can use the org.joda.time.DateTimeZone class. To know all the available timezones, call DateTimeZone.getAvailableIDs().

    The code below checks if a date is in DST and also finds the next date when a DST change will occur:

    // create timezone object
    DateTimeZone zone = DateTimeZone.forID("America/Sao_Paulo");
    
    // check if a date is in DST
    DateTime inDst = new DateTime(2017, 1, 1, 10, 0, zone);
    // isStandardOffset returns false (it's in DST)
    System.out.println(zone.isStandardOffset(inDst.getMillis()));
    // check when it'll be the next DST change
    DateTime nextDstChange = new DateTime(zone.nextTransition(inDst.getMillis()), zone);
    System.out.println(nextDstChange); // 2017-02-18T23:00:00.000-03:00
    
    // check if a date is in DST
    DateTime noDst = new DateTime(2017, 6, 18, 10, 0, zone);
    // isStandardOffset returns true (it's not in DST)
    System.out.println(zone.isStandardOffset(noDst.getMillis()));
    // check when it'll be the next DST change
    nextDstChange = new DateTime(zone.nextTransition(noDst.getMillis()), zone);
    System.out.println(nextDstChange); // 2017-10-15T01:00:00.000-02:00
    

    If you want to find the previous DST change (instead of the next), call previousTransition() instead of nextTransition().


    Java new Date/Time API

    If you're using Java 8, the new java.time API already comes natively.

    If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).

    The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

    The code is very similar to Joda-Time's version. The main differences:

    • While Joda-Time has isStandardOffset() to check if the date is not in DST, the new API has isDaylightSavings() to check if the date is in DST.
    • Joda-Time provides the methods directly in the DateTimeZone class, but the new API has a dedicated class to its DST rules (java.time.zone.ZoneRules)
    • The methods for next and previous transitions return a java.time.zone.ZoneOffsetTransition instead of directly returning a date (this object provides more information about the DST change, as showed below).

    Despite all those differences, the idea is very similar:

    // create timezone object
    ZoneId zone = ZoneId.of("America/Sao_Paulo");
    // get the timezone's rules
    ZoneRules rules = zone.getRules();
    
    // check if a date is in DST
    ZonedDateTime inDST = ZonedDateTime.of(2017, 1, 1, 10, 0, 0, 0, zone);
    // isDaylightSavings returns true (it's in DST)
    System.out.println(rules.isDaylightSavings(inDST.toInstant()));
    // check when it'll be the next DST change
    ZoneOffsetTransition nextTransition = rules.nextTransition(inDST.toInstant());
    // getInstant() returns the UTC instant; atZone converts to the specified timezone
    System.out.println(nextTransition.getInstant().atZone(zone)); // 2017-02-18T23:00-03:00[America/Sao_Paulo]
    
    // you can also check the date/time and offset before and after the DST change
    // in this case, at 19/02/2017, the clock is moved 1 hour back (from midnight to 11 PM)
    ZonedDateTime beforeDST = ZonedDateTime.of(nextTransition.getDateTimeBefore(), nextTransition.getOffsetBefore());
    System.out.println(beforeDST); // 2017-02-19T00:00-02:00
    ZonedDateTime afterDST = ZonedDateTime.of(nextTransition.getDateTimeAfter(), nextTransition.getOffsetAfter());
    System.out.println(afterDST); // 2017-02-18T23:00-03:00
    
    // check if a date is in DST
    ZonedDateTime noDST = ZonedDateTime.of(2017, 6, 1, 10, 0, 0, 0, zone);
    // isDaylightSavings returns false (it's not in DST)
    System.out.println(rules.isDaylightSavings(noDST.toInstant()));
    // check when it'll be the next DST change
    nextTransition = rules.nextTransition(noDST.toInstant());
    // getInstant() returns the UTC instant; atZone converts to the specified timezone
    System.out.println(nextTransition.getInstant().atZone(zone)); // 2017-10-15T01:00-02:00[America/Sao_Paulo]
    
    // you can also check the date/time and offset before and after the DST change
    // in this case, at 15/10/2017, the clock is moved 1 hour forward (from midnight to 1 AM)
    beforeDST = ZonedDateTime.of(nextTransition.getDateTimeBefore(), nextTransition.getOffsetBefore());
    System.out.println(beforeDST); // 2017-10-15T00:00-03:00
    afterDST = ZonedDateTime.of(nextTransition.getDateTimeAfter(), nextTransition.getOffsetAfter());
    System.out.println(afterDST); // 2017-10-15T01:00-02:00
    

    If you want to find the previous DST change instead of the next, you can call rules.previousTransition() instead of rules.nextTransition().