Daniel
Daniel

Reputation: 383

Why aren't milliseconds excluded from JSON when using dateFormat in Jackson's ObjectMapper with Java 8 dates?

I am trying to figure out why Jackson (2.9.5) formats dates from Java 8 incorrectly.

data class Test(
        val zonedDateTim: ZonedDateTime = ZonedDateTime.now(),
        val offsetDateTim: OffsetDateTime = OffsetDateTime.now(),
        val date: Date = Date(),
        val localDateTime: LocalDateTime = LocalDateTime.now()
)

val mapper = ObjectMapper().apply {
    registerModule(KotlinModule())
    registerModule(JavaTimeModule())
    dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
    enable(SerializationFeature.INDENT_OUTPUT)
}

println(mapper.writeValueAsString(Test()))

From the date format I provided I would expect to get dates formatted without milliseconds but instead the result looks like this:

{
  "zonedDateTim" : "2018-07-27T13:18:26.452+02:00",
  "offsetDateTim" : "2018-07-27T13:18:26.452+02:00",
  "date" : "2018-07-27T13:18:26",
  "localDateTime" : "2018-07-27T13:18:26.452"
}

Why are milliseconds being included in the formatted dates?

Upvotes: 5

Views: 5650

Answers (2)

assylias
assylias

Reputation: 328608

dateFormat only applies to Date objects - the 3 other objects are handled by the JavaTimeModule, which uses ISO formatting by default.

If you want a different format, you can use:

val format = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

val javaTimeModule = JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, LocalDateTimeSerializer(format));
javaTimeModule.addSerializer(ZonedDateTime.class, ZonedDateTimeSerializer(format));
mapper.registerModule(javaTimeModule);

You may also need to add mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); but I'm not 100% sure that it's necessary.

Also note that with that format, you will lose time zone and offset information. So you may want a different format for Offset/Zoned-DateTimes.

Upvotes: 10

Basil Bourque
Basil Bourque

Reputation: 338564

The Answer by assylias is correct. Here are some further thoughts.

Truncate

If you really do not want the fractional second at all, truncate to whole seconds.

Instant instant = Instant.now().truncatedTo( ChronoUnit.SECONDS ) ;
OffsetDateTime odt = OffsetDateTime.now().truncatedTo( ChronoUnit.SECONDS ) ;
ZonedDateTime zdt = ZonedDateTime.now().truncatedTo( ChronoUnit.SECONDS ) ;

The formatters used by the various toString methods by default omit any text representing the fractional second if the value is zero.

So the value of:

2018-07-27T13:18:26.452+02:00

…becomes:

2018-07-27T13:18:26.000+02:00

…and its String representation will be generated as seconds without a fraction:

2018-07-27T13:18:26+02:00

Try it.

OffsetDateTime odt = OffsetDateTime.parse( "2018-07-27T13:18:26.452+02:00" ) ;
OffsetDateTime odtTrunc = odt.truncatedTo( ChronoUnit.SECONDS ) ;

System.out.println( "odt.toString(): " + odt ) ;
System.out.println( "odtTrunc.toString(): " + odtTrunc ) ;

Try that code live at IdeOne.com.

odt.toString(): 2018-07-27T13:18:26.452+02:00

odtTrunc.toString(): 2018-07-27T13:18:26+02:00

Avoid legacy classes

The code in your Question confuses me. Do not mix the troublesome old legacy date-time classes such as Date & SimpleDateFormat with the modern java.time classes. The legacy classes are entirely supplanted by the modern ones.

Timeline

Be clear that LocalDateTime serves a very different purpose than Instant, OffsetDateTime, and ZonedDateTime. A LocalDateTime object purposely lacks any concept of time zone or offset-from-UTC. So it cannot represent a moment, is not a point on the timeline.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Upvotes: 3

Related Questions