I came across this question on StackOverflow:
Is there any reason to use ZoneId.of("UTC") instead of ZoneOffset.UTC?
Since I had to deal with an issue related to this question in production, I wanted to share my answer here because I think it might be useful to anyone serializing ZonedDateTime
with Jackson.
Tl;dr if you use Jackson, you might be better of using ZoneId
instead of ZoneOffset
.
I can think of one situation where ZoneId.of("UTC")
might be preferable over ZoneOffset.UTC
. If you use jackson-datatype-jsr310 to deserialize ZonedDataTime
from JSON, you get dates with ZoneId
.
For example, let's say we have this POJO (getters/setters omitted for brevity):
class Example {
private ZonedDateTime date = ZonedDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.DAYS);
}
If you print an instance of the class you get:
Example example1 = new Example();
System.out.println(example1);
// output:
// Example(date=2022-06-17T00:00Z) - as expected
What happens when you deserialize JSON containing the above string 2022-06-17T00:00Z
?
String json = "{\"date\":\"2022-06-17T00:00Z\"}";
Example example2 = mapper.readValue(json, Example.class);
System.out.println(example2);
// output:
// Example(date=2022-06-17T00:00Z[UTC]) // now with ZoneId(!)
So now we have two variants, one with ZoneOffset
and one with ZoneId
:
2022-06-17T00:00Z
2022-06-17T00:00Z[UTC]
And these are not equal to each other. For example:
Instant instant = Instant.now();
ZonedDateTime z1 = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
ZonedDateTime z2 = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"));
System.out.println(z1.equals(z2)); // => false
As a result example1.equals(example2)
will also be false
. This could be a source of subtle bugs.
Note: you get same result if your serialize new Example()
to a JSON string and deseralize the string back to an object. You will get a date with ZoneId
.
Tested with Jackson 2.13.1
.
Top comments (0)