1

I am using spring boot with angularJs UI, my application passes java.time.LocalDateTime to the anular view.

I am trying to use angularjs's date filter, as below:

{{ localDateTime | date : 'yyyy/MM/dd hh:mm:ss' }}

But it does not work. The output is shown as below:

{"dayOfMonth":8,"dayOfWeek":"FRIDAY","dayOfYear":39,"month":"FEBRUARY","year":2019,"monthValue":2,"hour":14,"minute":48,"nano":87000000,"second":24,"chronology":{"id":"ISO","calendarType":"iso8601"}}

I even tried adding application.property as suggested here as well. Still it appears as above JSON. Can someone please help.

Anil Bhaskar
  • 3,718
  • 4
  • 33
  • 51

3 Answers3

3

The issue is that by default Jackson doesn't know how to properly serialize the Java 8 date structures (JSR 310) to JSON. As a result, it returns an entire object structure containing dayOfMonth, dayOfWeek, ... as you observed.

A solution would be to serialize your dates to a well-known format, like an ISO-8601 string. To do this, you first have to add support for these Java 8 types by adding the following dependency:

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

After that, you should notice that your date is being serialized as an array, for example:

[2019, 2, 8, 11, 0, 0, 0, 0]

This is an array containing the year, month day of the month and so on. However, the AngularJS date filter cannot use these results yet. The proper solution would be to format them as an ISO-8601 string without timezone (considering it's a LocalDateTime). You can do this by disabling the WRITE_DATES_AS_TIMESTAMPS serialization feature within Jackson, by configuring the following property within application.properties:

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

Now your date will beformatted as a string, for example:

"2019-02-08T11:00:00.000"

If you take a look at the documentation of the AngularJS date filter, you can see that this is properly supported:

Date to format either as Date object, milliseconds (string or number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ).

Be aware, since you're using a LocalDateTime, no timezone info is given, and thus, the AngularJS date filter considers this to be the local browser time, as mentioned in the same documentation:

If no timezone is specified in the string input, the time is considered to be in the local timezone.

This means that if the server timezone is different from the browser timezone, there will be issues with the given time.

g00glen00b
  • 41,995
  • 13
  • 95
  • 133
  • 1
    JSR-310's `LocalDateTime` is **not** a timestamp, unless server time zone is static and known to all parties. Thus, it's not correct to serialize the `LocalDateTime` to a number. – M. Prokhorov Feb 08 '19 at 09:56
  • @M.Prokhorov You're right, I didn't pay enough attention to the actual type being used. I rephrased my answer to properly address the issue you mentioned. – g00glen00b Feb 08 '19 at 10:29
1

Actually Jackson (at least if the version is not too outdated) does know how to deal with it. You just need to annotate your property correctly:

@JsonFormat(shape= JsonFormat.Shape.STRING, pattern="EEE MMM dd HH:mm:ss Z yyyy")
@JsonProperty("created_at") 
ZonedDateTime created_at; 

See this question: Jackson deserialize date from Twitter to `ZonedDateTime`

Michael Gantman
  • 7,315
  • 2
  • 19
  • 36
0

You can create a custom date filter that wraps the angular date filter to support the way Jackson is serializing your object. After you add the jackson-datatype-jsr310 dependency this should do the trick:

Angular Filter

angular.module('YourModule')
    .filter('localDateTime', localDateTime);

localDateTime.$inject = ['$filter'];
function localDateTime($filter) {
    return function (input, format) {
        if(!input){
            return '';
        }


        const year   = input[0] || 0;
        const month  = input[1] || 0;
        const day    = input[2] || 0;
        const hour   = input[3] || 0;
        const minute = input[4] || 0;
        const second = input[5] || 0;
        const nano   = input[6] || 0;

        const date = new Date(year, month-1, day, hour, minute, second, nano);
        return $filter('date')(date, format);
    };
}

HTML

{{yourTimeField | localDateTime: 'yyyy/MM/dd hh:mm:ss'}}

If you don't want to add the jackson-datatype-jsr310 depenency, you can modify the const date values to use the property on the object instead.

Note: I have previously had issues POST'ing data to the server that in in the object format and was forced to update the dependency

Michael Lynch
  • 1,743
  • 15
  • 27