Search code examples
javadstjava-time

Java: Local Standard Time to Instant


A service like this has an explicit note that

All times are specified in Local Standard Time (LST). Add 1 hour to adjust for Daylight Saving Time where and when it is observed.

Given a LocalDateTime object ldt, creating an instant is as simple as Instant.of(ldt,zoneId). However, if I supply the ZoneId, then Instant will assume the daylight-savings (DST) adjustment has already been made, and ldt is with respect to DST, which is not the case. What is the most Java-esque way to make the Instant's constructor apply the "standard" offset rather than taking into account the DST?

EDIT: I mean, short of hard-codedly checking, for the given year, the exact dates of DST transitions? Sure, I can get the offset as ZoneOffset offset= ZoneOffset.of(zoneId) and then subtract offset.getTotalSeconds() from the given timestamp, to move to the "reference" time. But giving that reference-adjusted timestamp to Instant still has the same problem of the DST.

EDIT: As has been suggested below, I tried this:

String str = String.format("%04d-%02d-%02dT%s:00", year, month, day, hourMinutes);
LocalDateTime ldt = LocalDateTime.parse(str);
Instant i1= ldt.toInstant(zoneId.getRules().getOffset(ldt));
Instant key= ldt.toInstant(zoneId.getRules().getStandardOffset(i1));

Solution

  • You can get the standard offset of a ZoneId by calling getRules() and then getStandardOffset().

    However, you need an Instant. Because sometimes the standard offset changes too...

    So one way is to first convert the LocalDateTime to Instant, using the offset at that LocalDateTime. Then we get the standard offset at that instant, and use that to get the actual Instant we want:

    LocalDateTime ldt = ...;
    Instant i = ldt.toInstant(someZoneId.getRules().getOffset(ldt));
    Instant i2 = ldt.toInstant(someZoneId.getRules().getStandardOffset(i));
    System.out.println(i2);
    

    I think this wouldn't work for some specific local times if both the standard offset and the DST offset changes at the same time, because the first instant i is calculated with the offset before the transition. You can calculate i with the offset after the transition by doing this:

    Instant i;
    ZoneOffsetTransition transition = someZoneId.getRules().getTransition(ldt)
    if (transition == null) {
        i = ldt.toInstant(someZoneId.getRules().getOffset(ldt));
    } else {
        i = ldt.toInstant(transition.getOffsetAfter());
    }
    

    Update:

    But even still, this will produce the wrong result for some really corner cases. The problem is that java.time does not provide a way of getting the transitions in standard offset.