Handling Java 8 Dates and Time with Jackson (JSR-310)

When you are starting a new Spring Boot application today, you will probably want to use Java 8. However, it still can be tricky with certain frameworks when using new features like the Date and Time API (known as JSR-310).

Jackson is one of them. By Default, it does not handle the new Java 8 date and time classes correctly as dates but rather serializes them as objects.

But, let’s take a closer look at the problem and apply an easy fix.

Problem

So, for example, if we return a Instant like in the following example

@RestController
public class DateProblemExampleController {

    @RequestMapping("/now")
    public Instant getNow() {
        return Instant.now();
    }
}

We would get this as the JSON response.

{
    "nano":24000000,
    "epoch_second":1493887688
}

When we use a LocalDateTime, it will be even more complex.

    {
    "day_of_year": 124,
    "month_value": 5,
    "year": 2017,
    "month": "MAY",
    "day_of_month": 4,
    "day_of_week": "THURSDAY",
    "hour": 10,
    "minute": 49,
    "second": 7,
    "nano": 10000000,
    "chronology": {
        "id": "ISO",
        "calendar_type": "iso8601"
    }
}

Solution

Luckily, the solution is pretty simple and straight forward. The Jackson framework provides an extension for correctly handling Java 8 dates and times. Just add it to your pom

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

The correct version is set in the dependency Managment of the Spring Boot parent. If you are using it outside of Spring Boot, you need to add the version number.

If you are using the default ObjectMapper the handling of Java 8 date is picked up automatically by Spring Boot. However, if you have defined your own ObjectMapper you might need to register the module.

registerModule(new JavaTimeModule());

Now, we can call our API again.

[2017, 5, 4, 11, 20, 49, 44000000]

Looks better, but still not useable. So, let’s adjust the ObjectMapper to handle dates not as timestamps using the SerializationFeature.WRITE_DATES_AS_TIMESTAMPS property.

@Bean
@Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper objectMapper = builder.build();
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    return objectMapper;
}

When you use Spring Boot, you could also set this adding the following line to the application.properties file:

spring.jackson.serialization.write-dates-as-timestamps=false

Spring Boot enables the configuration of the default ObjectMapper with properties under the spring.jackson prefix. Read the jackson section in the Spring Boot docs for more info on this.

Now we receive the values in a more useable format.

For Instant

"2017-05-04T09:24:20.985Z"

For LocalDateTime

"2017-05-04T11:28:56.816"

Conclusion

Still a nuisance with the support of Java 8 features by default. However, in the case of Jackson, it is straight forward to fix it and make it useable in Spring Boot.