Given a ZonedDateTime
, calling toString
then ZonedDateTime.parse
returns a different time in some cases.
Here is a specific example. Code is in Scala but it's no different in Java.
import java.time._
val t = 1193534827725L
val z = ZoneId.of("Europe/Paris")
val a = Instant.ofEpochMilli(t).atZone(z) // 2007-10-28T02:27:07.725+01:00[Europe/Paris]
val b = ZonedDateTime.parse(a.toString) // 2007-10-28T02:27:07.725+02:00[Europe/Paris]
a == b // returns false!
The re-parsed value also has a different epochMilli:
scala> List(a, b).map(_.toInstant.toEpochMilli)
res46: List[Long] = List(1193534827725, 1193531227725)
scala> List(a, b).map(_.toInstant.toEpochMilli == t)
res47: List[Boolean] = List(true, false)
Using .format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
instead of .toString
doesn't work either.
According to the Javadoc for Instant#atZone
which defers to https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html#ofInstant-java.time.Instant-java.time.ZoneId-
Converting an instant to a zoned date-time is simple as there is only one valid offset for each instant.
What's going on?
Edit: It's also worth mentioning that daylight savings occurred around the sample time (ref) such that there were two instances of 02:27:07.725
. As you can see from .toString
the offset is correct but parsing doesn't seem to respect it.
now() method of a ZonedDateTime class used to obtain the current date-time from the system clock in the default time-zone. This method will return ZonedDateTime based on system clock with default time-zone to obtain the current date-time. The zone and offset will be set based on the time-zone in the clock.
ZonedDateTime is an immutable representation of a date-time with a time-zone. This class stores all date and time fields, to a precision of nanoseconds, and a time-zone, with a zone offset used to handle ambiguous local date-times.
ZoneOffset describes a time-zone offset, which is the amount of time (typically in hours) by which a time zone differs from UTC/Greenwich. ZonedDateTime describes a date-time with a time zone in the ISO-8601 calendar system (such as 2007-12-03T10:15:30+01:00 Europe/Paris ).
A LocalDateTime instance represents a point in the local timeline. It cannot represent an instant on the universal timeline without additional information such as an offset or time zone. A ZonedDateTime instance represents an instant in the universal timeline. It is the combination of date, time and zone information.
Fixed in Java 9.
See JDK-8066982: ZonedDateTime.parse() returns wrong ZoneOffset around DST fall transition
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With