DEV Community

Cover image for Is there any reason to use ZoneId.of("UTC") instead of ZoneOffset.UTC?
armandino
armandino

Posted on

Is there any reason to use ZoneId.of("UTC") instead of ZoneOffset.UTC?

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);
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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(!)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)