From 67e06eec44ed94e6eeaf84e7eaca334120136141 Mon Sep 17 00:00:00 2001 From: Michael O'Keeffe <454889+kupci@users.noreply.github.com> Date: Tue, 9 Jun 2020 22:05:07 -0500 Subject: [PATCH] Fix #166: deserialize OffsetDateTime.MIN or OffsetDateTime.MAX with ADJUST_DATES_TO_CONTEXT_TIME_ZONE enabled (#176) * Do not adjust Time Zone if value is OffsetDateTime.MIN or OffsetDateTime.MAX with ADJUST_DATES_TO_CONTEXT_TIME_ZONE enabled. * deleted private e-mail * Fixing e-mail. --- datetime/README.md | 2 +- .../jsr310/deser/InstantDeserializer.java | 2 +- .../jsr310/deser/OffsetDateTimeDeserTest.java | 39 +++++++++++++++++++ release-notes/CREDITS-2.x | 4 ++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/datetime/README.md b/datetime/README.md index 9405d98f..4c8bc5ad 100644 --- a/datetime/README.md +++ b/datetime/README.md @@ -23,7 +23,7 @@ more ambiguous integer types are read as fractional seconds without a decimal po For TimeZone handling, `ADJUST_DATES_TO_CONTEXT_TIME_ZONE` (default: true) specifies whether the context provided by `java.time.TimeZone` 'SerializedProvider#getTimeZone()' should be used to adjust Date/Time values on deserialization, even if the value itself -contains timezone information. If disabled, it will only be used if the value itself does not contain any TimeZone information. +contains timezone information. If the value is `OffsetDateTime.MIN` or `OffsetDateTime.MAX`, the Date/Time value will not be adjusted. If disabled, it will only be used if the value itself does not contain any TimeZone information. Finally, there are two features that apply to array handling. `UNWRAP_SINGLE_VALUE_ARRAYS` (default: false) allows auto-conversion from single-element arrays to non-JSON-array values. If the JSON value contains more than one element in the array, deserialization will still fail. `ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT` (default: false) determines whether empty Array value ("[ ]" in JSON) is accepted diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java index 11db736a..7e3ad2e9 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java @@ -73,7 +73,7 @@ public class InstantDeserializer OffsetDateTime::from, a -> OffsetDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId), a -> OffsetDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId), - (d, z) -> d.withOffsetSameInstant(z.getRules().getOffset(d.toLocalDateTime())), + (d, z) -> (d.isEqual(OffsetDateTime.MIN) || d.isEqual(OffsetDateTime.MAX) ? d : d.withOffsetSameInstant(z.getRules().getOffset(d.toLocalDateTime()))), true // yes, replace zero offset with Z ); diff --git a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/OffsetDateTimeDeserTest.java b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/OffsetDateTimeDeserTest.java index 04bef24e..2e7c492c 100644 --- a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/OffsetDateTimeDeserTest.java +++ b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/OffsetDateTimeDeserTest.java @@ -656,6 +656,45 @@ public void testStrictDeserializeFromEmptyString() throws Exception { objectReader.readValue(valueFromEmptyStr); } + // [module-java8#166] + @Test + public void testDeserializationNoAdjustIfMIN() throws Exception + { + OffsetDateTime date = OffsetDateTime.MIN; + ObjectMapper m = newMapper() + .configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, true) + .setTimeZone(TimeZone.getTimeZone(Z1)) + .addMixIn(Temporal.class, MockObjectConfiguration.class); + Temporal value = m.readValue( + "[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", Temporal.class + ); + + assertNotNull("The value should not be null.", value); + assertTrue("The value should be an OffsetDateTime.", value instanceof OffsetDateTime); + OffsetDateTime actualValue = (OffsetDateTime) value; + assertIsEqual(date, actualValue); + assertEquals(date.getOffset(),actualValue.getOffset()); + } + + @Test + public void testDeserializationNoAdjustIfMAX() throws Exception + { + OffsetDateTime date = OffsetDateTime.MAX; + ObjectMapper m = newMapper() + .configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, true) + .setTimeZone(TimeZone.getTimeZone(Z1)) + .addMixIn(Temporal.class, MockObjectConfiguration.class); + Temporal value = m.readValue( + "[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", Temporal.class + ); + + assertNotNull("The value should not be null.", value); + assertTrue("The value should be an OffsetDateTime.", value instanceof OffsetDateTime); + OffsetDateTime actualValue = (OffsetDateTime) value; + assertIsEqual(date, actualValue); + assertEquals(date.getOffset(),actualValue.getOffset()); + } + private static void assertIsEqual(OffsetDateTime expected, OffsetDateTime actual) { assertTrue("The value is not correct. Expected timezone-adjusted <" + expected + ">, actual <" + actual + ">.", diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 812efb21..a218fe5b 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -103,3 +103,7 @@ Arturas Gusevas (agusevas@github) Samantha Williamson (samwill@github) * Contributed fix to #148: Allow strict `LocalDate` parsing (2.11.0) + +Moritz Orth (morth@github.com) + * Reported and suggested fix for #166: Cannot deserialize OffsetDateTime.MIN and + OffsetDateTime.MAX with ADJUST_DATES_TO_CONTEXT_TIME_ZONE enabled (2.12)