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?
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).".
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()
.
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:
isStandardOffset()
to check if the date is not in DST, the new API has isDaylightSavings()
to check if the date is in DST.DateTimeZone
class, but the new API has a dedicated class to its DST rules (java.time.zone.ZoneRules
)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()
.