javadatetimetimezonedatetime-conversion

How to get timezone from local and UTC timestamps java


I have a use case where the data coming as input does not specify the timezone, however we have two separate fields "local" and "utc", which can in practice denote the offset, and thus the timezone.

I would like to get the timezone, so that I am able to convert another time, provided in UTC, to corresponding local time.

Input:

transaction_date_local transaction_date_utc
2023-05-01 07:00:23.021 2023-05-01T14:00:23.021Z

Based on this, I would like to convert another time query_time_utc from UTC to local
E.g. query_time_utc = 2023-06-01 06:59:59 , should be converted to local time (query_time_local)

How can we accomplish this ? I am thinking of getting the diff between local and UTC time, then apply that diff to the available UTC time to get local time. However, wanted to check if there is a higher level API available.

Appreciate support on this (preferably Java / Scala)


Solution

  • Many time zones may share an offset

    denote the offset, and thus the timezone

    No, an offset does not indicate the time zone. Many time zones can share a particular offset.

    For example, an offset of seven hours behind UTC (-07:00) may be used by time zones such as America/Inuvik, America/Boise, America/Yellowknife, America/Cambridge_Bay, America/Ciudad_Juarez, America/Phoenix, America/Dawson, America/Dawson_Creek, America/Edmonton, America/Fort_Nelson, America/Hermosillo, America/Mazatlan, America/Denver, America/Whitehorse, America/Tijuana, America/Los_Angeles, and America/Vancouver.

    Offset versus time zone

    An offset is a merely a number of hours-minutes-seconds ahead or behind a temporal meridian such as UTC.

    A time zone is a named history of the past, present, and future changes to the offset used by the people of a particular region, as decided by their politicians.

    Moment

    A Z on the end of a date-time string is a standard abbreviation of +00:00, an offset of zero hours-minutes-seconds from the temporal meridian of UTC.

    Parse a Z string in standard ISO 8601 format as a Instant.

    Instant instant = Instant.parse( "2023-05-01T14:00:23.021Z" ) ;
    

    To view the moment through the wall-clock/calendar used by the people of a particular region, apply a time zone (ZoneId) to get a ZonedDateTime.

    ZoneId zTokyo = ZoneId .of( "Asia/Tokyo" ) ;
    ZonedDateTime zdtTokyo = instant.atZone( zTokyo ) ;
    
    ZoneId zDawsonCreek = ZoneId .of( "America/Dawson_Creek" ) ;
    ZonedDateTime zdtDawsonCreek = instant.atZone( zDawsonCreek ) ;
    

    These three objects, instant, zdtTokyo, and zdtDawsonCreek all represent the same simultaneous moment, the exact same point on the timeline.

    See that code run at Ideone.com.

    instant.toString() = 2023-05-01T14:00:23.021Z
    zdtTokyo.toString() = 2023-05-01T23:00:23.021+09:00[Asia/Tokyo]
    zdtDawsonCreek.toString() = 2023-05-01T07:00:23.021-07:00[America/Dawson_Creek]
    

    Not a moment

    A date-with-time string lacking an indicator of offset or zone is parsed as a LocalDateTime object.

    Replace the SPACE in the middle with a T to comply with ISO 8601 standard.

    LocalDateTime ldt = LocalDateTime.parse( "2023-05-01 07:00:23.021".replace( " " , "T" ) )
    

    The LocalDateTime class does not represent a moment, is not a point on the timeline. So, a LocalDateTime object is inherently ambiguous. In your example we see 7 AM on the first of the month. But have no way of knowing if that means 7 AM in Tokyo, 7 AM in Toulouse, or 7 AM IN Toledo Ohio US — three different moments, several hours apart.

    If you are certain that such a string lacking in zone/offset is meant to represent a momoet as seen in a particular time zone, you can apply that zone.

    ZonedDateTime zdtTokyo = ldt.atZone( zTokyo );
    

    But the ideal solution is to educate the publisher of your data about including at least the offset per the ISO 8601 standard. Even better, also append the name of the time zone in square brackets per the wise extension to ISO 8601 format as used in the java.time.ZonedDateTime class.

    Converting

    convert another time … from UTC to local

    Yes, you can adjust from a moment as seen in UTC to a boost as seen in a particular time zone. This was shown above. But you will need to know the time zone.

    You cannot assume the offset. People do change their offset, with the change driven by the whims of politicians. Those whims can vary at any moment. So you have no way of knowing the offset that may be in effect in the future.