Store Java 8 Instant as BSON date using SpringData-MongoDB
Asked Answered
C

2

21

I have the following class that I want to store in MongoDB using Spring Data

@Document()
public class Tuple2<T extends Enum<T>> {

@Id
private String id;

@Indexed
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private final Instant timeCreated;

...
}

DateTimeFormat annotation javadoc states:

Declares that a field should be formatted as a date time. Supports formatting by style pattern, ISO date time pattern, or custom format pattern string. Can be applied to java.util.Date, java.util.Calendar, java.long.Long, Joda-Time value types; and as of Spring 4 and JDK 8, to JSR-310 java.time types too.

I am using Spring 4.1.1 and JDK 8, so I'd expect that it applies to Instant. However, here's what is actually stored:

"timeCreated" : {
    "seconds" : NumberLong(1416757496),
    "nanos" : 503000000
}

If I write and register custom convertor from Instant to Date like explained in this answer then it works, however I'd like to avoid that, as I am sure there must be a better way.

After further digging in Spring source code I've found the following class Jsr310DateTimeFormatAnnotationFormatterFactory which looks promising:

Formats fields annotated with the DateTimeFormat annotation using the JSR-310 java.time package in JDK 8.

Its' source does not reference Instant, but it does reference OffsetTime and LocalTime. Even so, when I change Instant to be OffsetDateTime in my example, it is still stored as a composite object instead of ISODate.

What is missing?

Clingy answered 23/11, 2014 at 16:38 Comment(0)
L
4

I think the problem is what you are trying to use Instant as a time. Conceptually it is a point of the timeline and it does not imply formatting.

As we know, Java 8 time API was developed with an eye at joda-time (and with partisipating of joda-time's developers). Here is comment from joda-time Instant:

An Instant should be used to represent a point in time irrespective of any other factor, such as chronology or time zone.

That's why there is no formatting possibilities for org.joda.time.Instant in JodaDateTimeFormatAnnotationFormatterFactory which appeared in Spring since version 3.0. And also it was not implemented in Jsr310DateTimeFormatAnnotationFormatterFactory

So, you should use custom converter or consider to use more suitable class.

Lamellate answered 30/11, 2014 at 20:12 Comment(2)
Seems reasonable. There is however OffsetDateTime in Jsr310DateTimeFormatAnnotationFormatterFactory, but it is not formatted as wellClingy
@Clingy DateTimeFormat annotation is not responsible for formatting in storage (mongodb). It works at application-level (e.g. formatting on JSP pages). There is converters for converting data to appropripriate type. I found only ZonedDateTimeToCalendarConverter for Java 8 time API. So can you try to use ZonedDateTime?Lamellate
V
0

I use instant for all timestamp type data, just like your timecreated. If it is for the end user, like a calendar entry, than LocalDateTime works better but they can all be created using ISO formatted time strings. At some point, the point of time data (date or instant) will need to be formatted for readability or serialization/portability.

So to answer this question if anyone runs into this issue with current MongoDB versions, you do not need to do anything in your code. I initialized some data in my mongodb container using the init script. I used ISODate formatting.

 "timestamp": ISODate("2020-03-17T13:50:56.618Z")

I also have a Spring Boot 2 and Spring Data application. The documentation has full support for it. https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mapping-conversion

Vociferance answered 15/4, 2020 at 4:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.