I'm having a hard time understanding java.time between ZoneDateTime - Instant - LocalDateTime , so far, the only thing I know of is:
So I have this conversion below
val seoul = "Asia/Seoul"
val zoneId = ZoneId.of(seoul)
val now = ZonedDateTime.now()
val convertedZoneDateTIme = ZonedDateTime.of(now.toLocalDateTime(), zoneId).withZoneSameInstant(ZoneOffset.UTC)
val convertedInstant = now.toInstant().atZone(zoneId)
// expected output
println(convertedInstant.format(DateTimeFormatter.ofPattern(format)))
// not expected output
println(converted.format(DateTimeFormatter.ofPattern(format)))
Output
2021-05-02 03:15:13
2021-05-02 09:15:13
I'm trying to convert a given time to another Time Zone, a use-case where a user moved to a different timezone and I need to update any information about a stored date.
Why am I getting an incorrect value on the second one..? Why do I have to convert it to Instant first and proceed with conversion?
Thank you in advance
Most of your bullets are fully correct. Only you should not use Instant
for working between LocalDateTime
and ZonedDateTime
as you said in your first bullet. Converting between Instant
and LocalDateTime
requires a time zone (or at least an offset from UTC), so should go through a ZonedDateTime
. So ZonedDateTime
is the one to use between the two others. As I said, the rest is correct.
You are not being perfectly clear about what you had expected from your code nor how more specifically observed result differs. Assuming you wanted to use the same point in time throughout, this line is where your surprise arises:
val convertedZoneDateTIme = ZonedDateTime.of(now.toLocalDateTime(), zoneId).withZoneSameInstant(ZoneOffset.UTC)
now
is a ZonedDateTime
in your own time zone (the default time zone of your JVM to be precise). By taking only the date and time of day from it and combining them with a different time zone you are keeping the time of day but in that way (probably) changing the point in the flow of time. Next you are converting to UTC keeping the point in time (the instant), thereby (probably) changing the time of day and possibly the date. You have got nothing left from the ZonedDateTime
that was your starting point, and I can’t see that the operation makes sense. To convert now
to UTC keeping the point on the timeline use the simpler:
val convertedZoneDateTIme = now.withZoneSameInstant(ZoneOffset.UTC)
With this change your two outputs agree about the point in time. Example output:
2021-05-07 02:30:16 +09:00 Korean Standard Time 2021-05-06 17:30:16 +00:00 Z
I used a format
of uuuu-MM-dd HH:mm:ss xxx zzzz
.
Also for you other conversion I would prefer to use withZoneSameInstant()
. Then we don’t need to go through an Instant
.
val convertedInstant = now.withZoneSameInstant(zoneId)
It gives the same result as your code.
A short overview of what is in each of the classes discussed:
Class | Date and time of day | Point in time | Time zone |
---|---|---|---|
ZonedDateTime |
Yes | Yes | Yes |
Instant |
- | Yes | - |
LocalDateTime |
Yes | - | - |
Basically you don’t have any use for LocalDateTime
for your purpose, and also Instant
, while useable, isn’t necessary. ZonedDateTime
alone fulfils your needs.