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 ac4ba34a..0daf2f04 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 @@ -54,13 +54,6 @@ public class InstantDeserializer { private static final long serialVersionUID = 1L; - /** - * Constants used to check if the time offset is zero. See [jackson-modules-java8#18] - * - * @since 2.9.0 - */ - private static final Pattern ISO8601_UTC_ZERO_OFFSET_SUFFIX_REGEX = Pattern.compile("\\+00:?(00)?$"); - /** * Constants used to check if ISO 8601 time string is colonless. See [jackson-modules-java8#131] * @@ -226,7 +219,7 @@ public T deserialize(JsonParser parser, DeserializationContext context) throws I return (T) parser.getEmbeddedObject(); case JsonTokenId.ID_START_ARRAY: - return _deserializeFromArray(parser, context); + return _deserializeFromArray(parser, context); } return _handleUnexpectedToken(context, parser, JsonToken.VALUE_STRING, JsonToken.VALUE_NUMBER_INT, JsonToken.VALUE_NUMBER_FLOAT); @@ -337,12 +330,37 @@ private ZoneId getZone(DeserializationContext context) private String replaceZeroOffsetAsZIfNecessary(String text) { if (replaceZeroOffsetAsZ) { - return ISO8601_UTC_ZERO_OFFSET_SUFFIX_REGEX.matcher(text).replaceFirst("Z"); + return replaceZeroOffsetAsZ(text); } return text; } + private static String replaceZeroOffsetAsZ(String text) + { + int plusIndex = text.lastIndexOf('+'); + if (plusIndex < 0) { + return text; + } + int maybeOffsetIndex = plusIndex + 1; + int remaining = text.length() - maybeOffsetIndex; + switch (remaining) { + case 2: + return text.regionMatches(maybeOffsetIndex, "00", 0, remaining) + ? text.substring(0, plusIndex) + 'Z' + : text; + case 4: + return text.regionMatches(maybeOffsetIndex, "0000", 0, remaining) + ? text.substring(0, plusIndex) + 'Z' + : text; + case 5: + return text.regionMatches(maybeOffsetIndex, "00:00", 0, remaining) + ? text.substring(0, plusIndex) + 'Z' + : text; + } + return text; + } + // @since 2.13 private String addInColonToOffsetIfMissing(String text) { diff --git a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserTest.java b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserTest.java index 070d89f5..9382c393 100644 --- a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserTest.java +++ b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserTest.java @@ -23,6 +23,7 @@ import static com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.ISO8601_COLONLESS_OFFSET_REGEX; import static org.junit.Assert.*; import static org.junit.Assert.assertNull; +import static org.junit.Assume.assumeTrue; public class InstantDeserTest extends ModuleTestBase { @@ -434,10 +435,43 @@ public void testDeserializationFromStringWithZeroZoneOffset03() throws Exception assertEquals("The value is not correct.", date, result); } + @Test + public void testDeserializationFromStringWithZeroZoneOffset04() throws Exception { + assumeInstantCanParseOffsets(); + Instant date = Instant.now(); + String json = formatWithZeroZoneOffset(date, "+00:30"); + Instant result = READER.readValue(json); + assertNotEquals("The value is not correct.", date, result); + } + + @Test + public void testDeserializationFromStringWithZeroZoneOffset05() throws Exception { + assumeInstantCanParseOffsets(); + Instant date = Instant.now(); + String json = formatWithZeroZoneOffset(date, "+01:30"); + Instant result = READER.readValue(json); + assertNotEquals("The value is not correct.", date, result); + } + + @Test + public void testDeserializationFromStringWithZeroZoneOffset06() throws Exception { + assumeInstantCanParseOffsets(); + Instant date = Instant.now(); + String json = formatWithZeroZoneOffset(date, "-00:00"); + Instant result = READER.readValue(json); + assertEquals("The value is not correct.", date, result); + } + private String formatWithZeroZoneOffset(Instant date, String offset){ return '"' + FORMATTER.format(date).replaceFirst("Z$", offset) + '"'; } + private static void assumeInstantCanParseOffsets() { + // DateTimeFormatter.ISO_INSTANT didn't handle offsets until JDK 12+. + // This was added by https://bugs.openjdk.org/browse/JDK-8166138 + assumeTrue(System.getProperty("java.specification.version").compareTo("12") > 0); + } + /* /********************************************************************** /* Deserialization, misc other