question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[2.12.0-rc1] Can no longer serialize using different timezones on independent OffsetDateTime fields

See original GitHub issue

Hi it would seem like #185 (PR for #175) removes the possibility of producing different offset formats with the same ObjectMapper.

In the example below you will see 3 cases:

  • One using an ObjectMapper without setting it’s time zone.
  • One using an ObjectMapper with it’s time zone set to the value appropriate for the the offset field.
  • One using an ObjectMapper with it’s time zone set to the value appropriate for the the offset2 field.

None of these options seem to work. I think it might just be necessary to honor the given object’s timezone when the timezone of the ObjectMapper is not set, I would love to hear your opinion on this or if there’s an alternate way to make this work I would also love to hear about it.

I’ve added a fourth test case which shows that deserialization is possible when using DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE. This makes me think that at a higher level the change introduces asymmetry in the behavior, and I think being able to go back and forth preserving the data is important.

Please let me know if I can improve this report or assist in any way.

import static org.junit.Assert.assertEquals;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.time.*;
import java.util.TimeZone;
import org.junit.Test;

public class ObjectMapperTest {

    private static final LocalDateTime LOCAL = LocalDateTime.of(2018, 11, 26, 17, 53, 22);
    private static final OffsetDateTime OFFSET = OffsetDateTime.of(LOCAL.plusSeconds(1), ZoneOffset.of("-05:00"));
    private static final OffsetDateTime OFFSET_2 = OffsetDateTime.of(LOCAL.plusSeconds(3), ZoneOffset.of("Z"));
    private static final TestPojo TEST_POJO = new TestPojo(OFFSET, OFFSET_2);
    private static final String EXPECTED_JSON = "{"
            + "\"offset\":\"2018-11-26T17:53:23-05:00\","
            + "\"offset2\":\"11/26/2018 17:53:25Z\""
            + "}";

    @Test
    public void testSerialization() throws Exception {
        ObjectMapper subject = newObjectMapper();
        String json = subject.writeValueAsString(TEST_POJO);
        assertEquals("Json should include timezone", EXPECTED_JSON, json);
    }

    @Test
    public void testSerializationWithUTCTimezoneInMapper() throws Exception {
        ObjectMapper subject = newObjectMapper();
        subject.setTimeZone(TimeZone.getTimeZone(ZoneId.of("UTC")));
        String json = subject.writeValueAsString(TEST_POJO);
        assertEquals("Json should include timezone", EXPECTED_JSON, json);
    }

    @Test
    public void testSerializationWithMinus5TimezoneInMapper() throws Exception {
        ObjectMapper subject = newObjectMapper();
        subject.setTimeZone(TimeZone.getTimeZone(ZoneId.of("-05:00")));
        String json = subject.writeValueAsString(TEST_POJO);
        assertEquals("Json should include timezone", EXPECTED_JSON, json);
    }

    @Test
    public void testDeserialization() throws Exception {
        ObjectMapper subject = newObjectMapper();
        subject.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
        String json = ("{"
                + "  'offset': '2018-11-26T17:53:23-05:00',"
                + "  'offset2': '11/26/2018 17:53:25Z'"
                + "}").replaceAll("'", "\"");
        TestPojo pojo = subject.readValue(json, TestPojo.class);
        assertEquals("The time is equivalent for offset", OFFSET, pojo.getOffset());
        assertEquals("The time is equivalent for offset2", OFFSET_2, pojo.getOffset2());
    }

    private ObjectMapper newObjectMapper() {
      ObjectMapper mapper = new ObjectMapper();
      mapper.registerModule(new JavaTimeModule());
      mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
      return mapper;
    }

    @JsonPropertyOrder({"offset", "offset2"})
    static class TestPojo {
        private static final String UTC_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX";
        private static final String ANOTHER_DATETIME_FORMAT = "MM/dd/yyyy' 'HH:mm:ssXXX";

        private final OffsetDateTime offset;
        private final OffsetDateTime offset2;

        @JsonCreator(mode = Mode.PROPERTIES)
        public TestPojo(
                @JsonProperty("offset") @JsonFormat(shape = JsonFormat.Shape.STRING,
                        pattern = UTC_DATETIME_FORMAT) OffsetDateTime offset,
                @JsonProperty("offset2") @JsonFormat(shape = JsonFormat.Shape.STRING,
                        pattern = ANOTHER_DATETIME_FORMAT) OffsetDateTime offset2) {
            this.offset = offset;
            this.offset2 = offset2;
        }

        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = UTC_DATETIME_FORMAT)
        public OffsetDateTime getOffset() { return offset; }
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ANOTHER_DATETIME_FORMAT)
        public OffsetDateTime getOffset2() { return offset2; }
    }
}

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:10 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
cowtowncodercommented, Oct 19, 2020

I added MapperConfig.hasExplicitTimeZone() (base class of SerializationConfig and DeserializationConfig), so this is now accessible for 2.12.

0reactions
BluYouscommented, Oct 8, 2021
Read more comments on GitHub >

github_iconTop Results From Across the Web

Can't deserialize datetime with timezone offset 'unparsed text ...
The input format matches an OffsetDateTime , which can be parsed with the respective built-in formatter DateTimeFormatter.
Read more >
Differences Between ZonedDateTime and OffsetDateTime
stores all date and time fields, to a precision of nanoseconds, and a timezone, with a zone offset used to handle ambiguous local...
Read more >
Handling Timezones in a Spring Boot Application - Reflectoring
As the Date API does not have time zone information, we have to use the Calendar class. However, it cannot be formatted, so...
Read more >
Compare DateTime, DateTimeOffset, TimeSpan, and ...
Learn about differences between the DateTime, DateTimeOffset, TimeSpan, and TimeZoneInfo types to represent date and time information in .
Read more >
Working with Time Zones - W3C
Date and time values can be complex and the relationship between computer and human timekeeping systems can lead to problems. This document aims ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found