diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeFeature.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeFeature.java
index 81dde0b8..ad8a4c88 100644
--- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeFeature.java
+++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeFeature.java
@@ -10,9 +10,14 @@
public enum JavaTimeFeature implements JacksonFeature
{
/**
- * Placeholder
+ * Feature that determines whether {@link java.time.ZoneId} is normalized
+ * (via call to {@code java.time.ZoneId#normalized()}) when deserializing
+ * types like {@link java.time.ZonedDateTime}.
+ *
+ * Default setting is enabled, for backwards-compatibility with
+ * Jackson 2.15.
*/
- BOGUS(false);
+ NORMALIZE_DESERIALID_ZONE_ID(true);
/**
* Whether feature is enabled or disabled by default.
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 99ef65ea..d1566c82 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
@@ -67,7 +67,8 @@ public class InstantDeserializer
a -> Instant.ofEpochMilli(a.value),
a -> Instant.ofEpochSecond(a.integer, a.fraction),
null,
- true // yes, replace zero offset with Z
+ true, // yes, replace zero offset with Z
+ true // default: yes, normalize ZoneId
);
public static final InstantDeserializer OFFSET_DATE_TIME = new InstantDeserializer<>(
@@ -76,7 +77,8 @@ public class InstantDeserializer
a -> OffsetDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId),
a -> OffsetDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId),
(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
+ true, // yes, replace zero offset with Z
+ true // default: yes, normalize ZoneId
);
public static final InstantDeserializer ZONED_DATE_TIME = new InstantDeserializer<>(
@@ -85,7 +87,8 @@ public class InstantDeserializer
a -> ZonedDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId),
a -> ZonedDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId),
ZonedDateTime::withZoneSameInstant,
- false // keep zero offset and Z separate since zones explicitly supported
+ false, // keep zero offset and Z separate since zones explicitly supported
+ true // default: yes, normalize ZoneId
);
protected final Function fromMilliseconds;
@@ -119,13 +122,21 @@ public class InstantDeserializer
*/
protected final Boolean _readTimestampsAsNanosOverride;
+ /**
+ * Flag set from
+ * {@link com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature#NORMALIZE_DESERIALIZED_ZONE_ID} to
+ * determine whether {@link ZoneId} is to be normalized during deserialization.
+ */
+ protected final boolean _normalizeZoneId;
+
protected InstantDeserializer(Class supportedType,
DateTimeFormatter formatter,
Function parsedToValue,
Function fromMilliseconds,
Function fromNanoseconds,
BiFunction adjust,
- boolean replaceZeroOffsetAsZ)
+ boolean replaceZeroOffsetAsZ,
+ boolean normalizeZoneId)
{
super(supportedType, formatter);
this.parsedToValue = parsedToValue;
@@ -135,6 +146,7 @@ protected InstantDeserializer(Class supportedType,
this.replaceZeroOffsetAsZ = replaceZeroOffsetAsZ;
this._adjustToContextTZOverride = null;
this._readTimestampsAsNanosOverride = null;
+ _normalizeZoneId = normalizeZoneId;
}
@SuppressWarnings("unchecked")
@@ -148,6 +160,7 @@ protected InstantDeserializer(InstantDeserializer base, DateTimeFormatter f)
replaceZeroOffsetAsZ = (_formatter == DateTimeFormatter.ISO_INSTANT);
_adjustToContextTZOverride = base._adjustToContextTZOverride;
_readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride;
+ _normalizeZoneId = base._normalizeZoneId;
}
@SuppressWarnings("unchecked")
@@ -161,6 +174,7 @@ protected InstantDeserializer(InstantDeserializer base, Boolean adjustToConte
replaceZeroOffsetAsZ = base.replaceZeroOffsetAsZ;
_adjustToContextTZOverride = adjustToContextTimezoneOverride;
_readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride;
+ _normalizeZoneId = base._normalizeZoneId;
}
@SuppressWarnings("unchecked")
@@ -174,6 +188,7 @@ protected InstantDeserializer(InstantDeserializer base, DateTimeFormatter f,
replaceZeroOffsetAsZ = (_formatter == DateTimeFormatter.ISO_INSTANT);
_adjustToContextTZOverride = base._adjustToContextTZOverride;
_readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride;
+ _normalizeZoneId = base._normalizeZoneId;
}
/**
@@ -194,6 +209,7 @@ protected InstantDeserializer(InstantDeserializer base,
replaceZeroOffsetAsZ = base.replaceZeroOffsetAsZ;
_adjustToContextTZOverride = adjustToContextTimezoneOverride;
_readTimestampsAsNanosOverride = readTimestampsAsNanosOverride;
+ _normalizeZoneId = base._normalizeZoneId;
}
@Override
@@ -364,7 +380,11 @@ private ZoneId getZone(DeserializationContext context)
// Instants are always in UTC, so don't waste compute cycles
// Normalizing the zone to prevent discrepancies.
// See https://github.com/FasterXML/jackson-modules-java8/pull/267 for details
- return (_valueClass == Instant.class) ? null : context.getTimeZone().toZoneId().normalized();
+ if (_valueClass == Instant.class) {
+ return null;
+ }
+ ZoneId zoneId = context.getTimeZone().toZoneId();
+ return _normalizeZoneId ? zoneId.normalized() : zoneId;
}
private String replaceZeroOffsetAsZIfNecessary(String text)