diff --git a/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java index ac361b64..fd121e89 100644 --- a/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java @@ -16,11 +16,14 @@ package com.fasterxml.jackson.datatype.jsr310.deser; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonTokenId; +import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.datatype.jsr310.DecimalUtils; import java.io.IOException; @@ -91,6 +94,8 @@ public class InstantDeserializer */ protected final boolean replace0000AsZ; + protected final Boolean adjustToContextTimezoneOverride; + protected InstantDeserializer(Class supportedType, DateTimeFormatter formatter, Function parsedToValue, @@ -105,6 +110,7 @@ protected InstantDeserializer(Class supportedType, this.fromNanoseconds = fromNanoseconds; this.adjust = adjust == null ? ((d, z) -> d) : adjust; this.replace0000AsZ = replace0000AsZ; + this.adjustToContextTimezoneOverride = null; } @SuppressWarnings("unchecked") @@ -116,6 +122,19 @@ protected InstantDeserializer(InstantDeserializer base, DateTimeFormatter f) fromNanoseconds = base.fromNanoseconds; adjust = base.adjust; replace0000AsZ = (_formatter == DateTimeFormatter.ISO_INSTANT); + adjustToContextTimezoneOverride = base.adjustToContextTimezoneOverride; + } + + @SuppressWarnings("unchecked") + protected InstantDeserializer(InstantDeserializer base, Boolean adjustToContextTimezoneOverride) + { + super((Class) base.handledType(), base._formatter); + parsedToValue = base.parsedToValue; + fromMilliseconds = base.fromMilliseconds; + fromNanoseconds = base.fromNanoseconds; + adjust = base.adjust; + replace0000AsZ = base.replace0000AsZ; + this.adjustToContextTimezoneOverride = adjustToContextTimezoneOverride; } @Override @@ -173,7 +192,7 @@ public T deserialize(JsonParser parser, DeserializationContext context) throws I try { TemporalAccessor acc = _formatter.parse(string); value = parsedToValue.apply(acc); - if (context.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)) { + if (isAdjustToContextTimezone(context)) { return adjust.apply(value, this.getZone(context)); } } catch (DateTimeException e) { @@ -190,6 +209,27 @@ public T deserialize(JsonParser parser, DeserializationContext context) throws I throw context.mappingException("Expected type float, integer, or string."); } + @SuppressWarnings("unchecked") + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + InstantDeserializer deserializer = + (InstantDeserializer)super.createContextual(ctxt, property); + if (deserializer != this) { + JsonFormat.Value val = findFormatOverrides(ctxt, property, handledType()); + if (val != null) { + return new InstantDeserializer<>(deserializer, val.getFeature(JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)); + } + } + return this; + } + + private boolean isAdjustToContextTimezone(DeserializationContext context) { + return adjustToContextTimezoneOverride != null ? adjustToContextTimezoneOverride : + context.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + } + /** * Helper method to find Strings of form "all digits" and "digits-comma-digits" */ diff --git a/src/test/java/com/fasterxml/jackson/datatype/jsr310/TestOffsetDateTimeDeserialization.java b/src/test/java/com/fasterxml/jackson/datatype/jsr310/TestOffsetDateTimeDeserialization.java index ad68ab80..98cb0c4e 100644 --- a/src/test/java/com/fasterxml/jackson/datatype/jsr310/TestOffsetDateTimeDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/datatype/jsr310/TestOffsetDateTimeDeserialization.java @@ -1,5 +1,6 @@ package com.fasterxml.jackson.datatype.jsr310; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectReader; @@ -10,6 +11,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeParseException; +import java.util.TimeZone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -44,6 +46,38 @@ public void testDeserializationAsString03() throws Exception expect(OffsetDateTime.of(2000, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(5)), parsed) ; } + public static class WithContextTimezoneDateFieldBean { + @JsonFormat(shape = JsonFormat.Shape.STRING, + pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", with = JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + public OffsetDateTime date; + } + + @Test + public void testDeserializationWithContextTimezoneFeatureOverride() throws Exception + { + String inputStr = "{\"date\":\"2016-05-13T17:24:40.545+03\"}"; + WithContextTimezoneDateFieldBean result = newMapper().setTimeZone(TimeZone.getTimeZone("UTC")). + disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE).readValue(inputStr, WithContextTimezoneDateFieldBean.class); + notNull(result); + expect(OffsetDateTime.of(2016, 5, 13, 14, 24, 40, 545000000, ZoneOffset.UTC), result.date); + } + + public static class WithoutContextTimezoneDateFieldBean { + @JsonFormat(shape = JsonFormat.Shape.STRING, + pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", without = JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + public OffsetDateTime date; + } + + @Test + public void testDeserializationWithoutContextTimezoneFeatureOverride() throws Exception + { + String inputStr = "{\"date\":\"2016-05-13T17:24:40.545+03\"}"; + WithoutContextTimezoneDateFieldBean result = newMapper().setTimeZone(TimeZone.getTimeZone("UTC")). + enable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE).readValue(inputStr, WithoutContextTimezoneDateFieldBean.class); + notNull(result); + expect(OffsetDateTime.of(2016, 5, 13, 17, 24, 40, 545000000, ZoneOffset.ofHours(3)), result.date); + } + @Test public void testBadDeserializationAsString01() throws Throwable {