Spring Data JPA and ZonedDateTime

My database had a couple of timestamp columns that I wanted to display in ISO8601 format, which is a very popular format for date/time, and in particular it is handled very easily in Javascript. This is especially interesting when considering REST APIs or, as in our case, GraphQL servers that must support an array of frameworks and clients.

First of all, I have to say that manipulating java.sql.Timestamps is by far the easiest way to work with timestamp type columns and spring-data-jpa Specifications. Operations such as CriteriaBuilder greatThanOrEqualTo can be performed right on the Hibernate entity attributes, as JPA knows to perform the proper conversions and produce proper SQL. If this stuff gets confusing I highly recommend this guide.

Otherwise, even using simple native queries (using JdbcTemplate or just PreparedStatement), using Timestamp.from is just as easy.

Then, converting to ZonedDateTime is easy. Let’s print one while we’re at it:

1
2
ZonedDateTime t = timestamp.toInstant().atZone(ZonedId.systemDefault());
System.out.println(t.toString);

2018-04-16T08:12:59.626+03:00[Asia/Jerusalem]

While this may be compliant with ISO8601, the presence of the timezone name (ZoneId) may be redundant, even though the documentation states that the offset prevails in case of discrepancy.

Thankfully, there is another class, OffsetDateTime, which, as its name indicates, represents a date and time with an offset, but without a ZoneId. If we wanted to reproduce the former output without the ZoneId we could do:

1
2
ZonedDateTime t = timestamp.toInstant().atZone(ZonedId.systemDefault());
System.out.println(t.toOffsetDateTime().toString());

Or directly:

1
2
OffsetDateTime t = OffsetDateTime.ofInstant(timestamp.toInstant(), ZonedId.systemDefault());
System.out.println(t.toString());

Note that you may lose the beauty of using Instant.atZone, but you get to use the object that you need.