Java why does Calendar.get(Calendar.DST_OFFSET) give 0 during daylight savings time?

643 Views Asked by At

I have a Calendar object that corresponds to 2021-07-05T18:00:00.000-04:00 (Eastern Daylight Time). Yet Calendar.get(DST_OFFSET) and Calendar.getTimeZone().getDSTSavings() both give 0. It should be 1 hour. What am I missing or what am I going wrong? All the other methods I play with are returning the expected values.

I am creating the Calendar using setTimeInMillis() and TimeZone using offsets. Is that the reason it is not working? The displayed civil times are always right...

As much as I would like to use the new Java time I am using Java for Android. Last I checked only the most recent versions of Android support the new Java time. They may eventually add support to their older versions.

2

There are 2 best solutions below

3
deHaar On BEST ANSWER

One problem is that the input defines an offset from UTC, but not a real time zone with specific rules (like if DST is applied at all and if it is, when will DST be applied).

Calendar is clearly not capable of handling those rules, the class (and probably the entire API) was not designed to be.

That's one of the reasons for java.time having been introduced in Java 8.

Here's some example use of java.time in a situation like yours:

public static void main(String[] args) {
    // example String in ISO format
    String dateString = "2021-07-05T18:00:00.000-04:00";
    // define your time zone
    ZoneId americaNewYork = ZoneId.of("America/New_York");
    // parse the (zone-less) String and add the time zone
    ZonedDateTime odt = OffsetDateTime.parse(dateString)
                                      .atZoneSameInstant(americaNewYork);
    // then get the rules of that zone
    long hours = americaNewYork.getRules()
                               // then get the daylight savings of the datetime
                               .getDaylightSavings(odt.toInstant())
                               // and get the full hours of the dst offset
                               .toHoursPart();
    
    // use a formatter to format the output (nearly) as desired
    System.out.println(odt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
                        + " has a daylight saving offset of "
                        + hours);
}

This prints

2021-07-05T18:00:00-04:00[America/New_York] has a daylight saving offset of 1

EDIT:

Your comment made me provide a similar version that uses a long as input:

public static void main(String[] args) {
    // example String in ISO format
    long input = 1625522400000L;
    // create an Instant from the input
    Instant instant = Instant.ofEpochMilli(input);
    // define your time zone
    ZoneId americaNewYork = ZoneId.of("America/New_York");
    // then get the rules of that zone
    long hours = americaNewYork.getRules()
                               // then get the daylight savings of the Instant
                               .getDaylightSavings(instant)
                               // and get the full hours of the dst offset
                               .toHoursPart();
    
    // use a formatter to format the output (nearly) as desired
    System.out.println(ZonedDateTime.ofInstant(instant, americaNewYork)
                                    .format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
                        + " has a daylight saving offset of "
                        + hours);
}

The output is just the same as in the above example.

2
Negi Rox On

in a java bug tracker you will find your problem.

During the "fall-back" period, Calendar doesn't support disambiguation and the given local time is interpreted as standard time. To avoid the unexpected DST to standard time change, call add() to reset the value.

you can resolve it by replacing set() with

cal.add(Calendar.MINUTE, -cal.get(Calendar.MINUTE));