7

I have a datetime string "2018-01-15 01:16:00" which is in EST timezone. I want to convert this into another timezone dynamically using the UTC offset. My javascript code passes this UTC offset as a parameter and the servlet has to convert/format this datetime string to the timezone identified by the provided offset.

I have tried many approaches including the one documented in the oracle tutorials but unable to arrive at a solution.

Below is my code that I am trying, any help is greatly appreciated.

private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final String DEFAULT_TIME_ZONE = ZoneId.SHORT_IDS.get("EST");

public static void main(String[] args) throws Exception {
    String dateTime = "2018-01-15 02:35:00";
    //parse the datetime using LocalDateTime
    LocalDateTime defaultDateTime = LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern(DATE_FORMAT));

    //get the datetime in default timezone
    ZoneId defaultZoneId = ZoneId.of(DEFAULT_TIME_ZONE);
    ZonedDateTime defaultZoneDateTime = defaultDateTime.atZone(defaultZoneId);
    System.out.println("EST time: "+defaultZoneDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT)));
    ZonedDateTime utcZonedDateTime = defaultZoneDateTime.withZoneSameInstant(ZoneId.of("UTC"));
    String utcTime = defaultZoneDateTime.withZoneSameInstant(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern(DATE_FORMAT));
    System.out.println("UTC : "+utcTime);

    //IST timezone
    ZoneOffset offset = ZoneOffset.of("+05:30");
    OffsetDateTime offsetDate = OffsetDateTime.of(utcZonedDateTime.toLocalDateTime(), offset);
    String targetTimeZone = offsetDate.format(DateTimeFormatter.ofPattern(DATE_FORMAT));
    System.out.printf("target time : "+targetTimeZone);

}

OUTPUT

EST time: 2018-01-15 02:35:00
UTC : 2018-01-15 07:37:00
target time : 2018-01-15 07:37:00

Expected target time : 2018-01-15 13:05:00

5
  • 2
    First thing to check - what do you really mean by "EST"? Do you mean "Eastern Time" (which is UTC+5 or UTC+6 depending on the time of year) or genuinely "Eastern Standard Time" (which is UTC+5 all year round)? It's pretty much always better to use a full time zone ID e.g. America/New_York. Commented Jan 15, 2018 at 7:31
  • Next: providing a complete example with actual output is great, but it would be a lot more helpful if you'd provide the expected output as well. Commented Jan 15, 2018 at 7:32
  • Thanks, I mean genuinely "Eastern Standard Time" because this datetime string is coming from a 3rd party API and doesn't give accurate information. Commented Jan 15, 2018 at 7:33
  • Thanks @JonSkeet I have added expected output. The code which I have posted is a complete example with hardcoded input datetime string. Commented Jan 15, 2018 at 7:39
  • 1
    (My earlier comments should have mentioned UTC-5 and UTC-4 rather than UTC+5 and UTC+6 btw.) Commented Jan 15, 2018 at 7:46

2 Answers 2

12

The immediate problem is this line:

OffsetDateTime offsetDate = OffsetDateTime.of(utcZonedDateTime.toLocalDateTime(), offset);

That's saying you want the same local date/time, but with the specified offset. That changes which instant in time is being represented.

Instead, you really want to represent the same instant in time, but at a particular offset. So the shortest fix is:

OffsetDateTime offsetDate = utcZonedDateTime.toInstant().atOffset(offset);

However, there are a number of other aspects which could do with changing:

  • Prefer ZoneOffset.UTC to ZoneId.of("UTC")
  • Using EST as a time zone is confusing - it's not clear whether you expect it to mean "Eastern Time" (changing between EST and EDT) or pure standard time of UTC-5. Assuming you actually mean "Eastern Time" it would be better to use America/New_York as a zone ID.
  • It's unclear what you want to happen if the input string represents a skipped or ambiguous value in Eastern time. These happen around DST transitions.

Next, you don't need to convert the ZonedDateTime in Eastern time into a ZonedDateTime in UTC at all. Either convert it directly to an instant:

OffsetDateTime target = defaultZoneDateTime.toInstant().at(offset);

Or create a ZonedDateTime for the target instead:

ZonedDateTime target = defaultZoneDateTime.withZoneSameInstant(offset);

Given that an offset isn't really a time zone, I'd probably go with the first of these.

Sign up to request clarification or add additional context in comments.

Comments

5

You're using

OffsetDateTime.of(utcZonedDateTime.toLocalDateTime(), offset)

to create your target. You're thus constructing an OffsetDateTime in the target offset, having a LocalDateTime equal to the LocalDateTime in the UTC zone.

What you want is the exact same transformation as the one you're using to go from the EST time to UTC: keep the same instant, but go to a different timezone:

defaultZoneDateTime.withZoneSameInstant(offset);

or, if you really want an OffsetDateTime and not a ZonedDateTime:

OffsetDateTime.ofInstant(defaultZoneDateTime.toInstant(), offset);

1 Comment

It’s probably just my taste, but to get an OffsetDateTime I’d go for defaultZoneDateTime.withZoneSameInstant(offset).toOffsetDateTime().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.