Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson OffsetDateTime serialization Z instead of +00:00 timezone?

I'm using Spring Boot with the following ObjectMapper:

@Bean
public ObjectMapper objectMapper()
{
    final ObjectMapper mapper = new ObjectMapper();

    mapper.enable(SerializationFeature.INDENT_OUTPUT);

    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);  
    mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true)); // Makes no difference to output

    mapper.findAndRegisterModules();

    return mapper;
}

When OffsetDateTimes are serialized and returned in responses, they have a format like this:

"2020-02-28T12:28:29.01Z"
"2020-02-28T12:36:21.885Z"

I would have expected the timezone information at the end to look like this instead:

"2020-02-28T10:41:25.287+00:00"

Is there something I'm missing or doing wrong here, or anyway I can get the timezone information serialized as the +00:00 format instead of 885Z format?

Many thanks!

like image 780
rmf Avatar asked Feb 28 '20 12:02

rmf


1 Answers

The new Java 8 Time API provides a DateTimeFormatter where you can set the end of the format to one or more x or X. As per the api description:

Offset X and x: This formats the offset based on the number of pattern letters. One letter outputs just the hour, such as '+01', unless the minute is non-zero in which case the minute is also output, such as '+0130'. Two letters outputs the hour and minute, without a colon, such as '+0130'. Three letters outputs the hour and minute, with a colon, such as '+01:30'. Four letters outputs the hour and minute and optional second, without a colon, such as '+013015'. Five letters outputs the hour and minute and optional second, with a colon, such as '+01:30:15'. Six or more letters throws IllegalArgumentException. Pattern letter 'X' (upper case) will output 'Z' when the offset to be output would be zero, whereas pattern letter 'x' (lower case) will output '+00', '+0000', or '+00:00'.

So, in your case your formatting string should end in xxx for +1:30, e.g. "yyyy-MM-dd'T'HH:mm:ss.SSSxxx" with the SSS always giving the milliseconds.

To use this DateTimeFormatter with Jackson, you either need to define a custom serializer

public class DefaultZonedDateTimeSerializer extends JsonSerializer<ZonedDateTime> {


  private static final DateTimeFormatter ISO_8601_FORMATTER = DateTimeFormatter
        .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
        .withZone(ZoneId.of("UTC"));

  @Override
  public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    if (value == null) {
        throw new IOException("ZonedDateTime argument is null.");
    }

    gen.writeString(ISO_8601_FORMATTER.format(value));
}

and annotate the respective fields in your beans with

@JsonSerialize(using = DefaultZonedDateTimeSerializer.class)
private ZonedDateTime someTimeProperty;

or you need to convert from DateTimeFormatter to DateFormat (older, but used by Jackson) as described here: Using DateTimeFormatter with ObjectMapper

like image 175
Ralf Wagner Avatar answered Nov 04 '22 06:11

Ralf Wagner