javadstjava-calendar

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


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.


Solution

  • 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.