Skip to content

Commit

Permalink
Optimize InstantDeserializer addInColonToOffsetIfMissing (#336)
Browse files Browse the repository at this point in the history
  • Loading branch information
schlosna authored Dec 17, 2024
1 parent fa6a27a commit 29aa2b8
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -519,14 +519,42 @@ private static String replaceZeroOffsetAsZ(String text)
}

// @since 2.13
private String addInColonToOffsetIfMissing(String text)
private static String addInColonToOffsetIfMissing(String text)
{
final Matcher matcher = ISO8601_COLONLESS_OFFSET_REGEX.matcher(text);
if (matcher.find()){
StringBuilder sb = new StringBuilder(matcher.group(0));
sb.insert(3, ":");
int timeIndex = text.indexOf('T');
if (timeIndex < 0 || timeIndex > text.length() - 1) {
return text;
}

int offsetIndex = text.indexOf('+', timeIndex + 1);
if (offsetIndex < 0) {
offsetIndex = text.indexOf('-', timeIndex + 1);
}

if (offsetIndex < 0 || offsetIndex > text.length() - 5) {
return text;
}

int colonIndex = text.indexOf(':', offsetIndex);
if (colonIndex == offsetIndex + 3) {
return text;
}

return matcher.replaceFirst(sb.toString());
if (Character.isDigit(text.charAt(offsetIndex + 1))
&& Character.isDigit(text.charAt(offsetIndex + 2))
&& Character.isDigit(text.charAt(offsetIndex + 3))
&& Character.isDigit(text.charAt(offsetIndex + 4))) {
String match = text.substring(offsetIndex, offsetIndex + 5);
return text.substring(0, offsetIndex)
+ match.substring(0, 3) + ':' + match.substring(3)
+ text.substring(offsetIndex + match.length());
}

// fallback to slow regex path, should be fully handled by the above
final Matcher matcher = ISO8601_COLONLESS_OFFSET_REGEX.matcher(text);
if (matcher.find()) {
String match = matcher.group(0);
return matcher.replaceFirst(match.substring(0, 3) + ':' + match.substring(3));
}
return text;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.util.Arrays;
import java.util.Map;
import java.util.TimeZone;

Expand Down Expand Up @@ -811,6 +812,40 @@ public void testOffsetDateTimeMinOrMax() throws Exception
_testOffsetDateTimeMinOrMax(OffsetDateTime.MAX);
}

@Test
public void OffsetDateTime_with_offset_can_be_deserialized() throws Exception {
ObjectReader r = MAPPER.readerFor(OffsetDateTime.class)
.without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

String base = "2015-07-24T12:23:34.184";
for (String offset : Arrays.asList("+00", "-00")) {
String time = base + offset;
if (!System.getProperty("java.version").startsWith("1.8")) {
// JDK 8 cannot parse hour offsets without minutes
assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + '"'));
}
assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + "00" + '"'));
assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + ":00" + '"'));
assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184" + offset + ":30"), r.readValue('"' + time + "30" + '"'));
assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184" + offset + ":30"), r.readValue('"' + time + ":30" + '"'));
}

for (String prefix : Arrays.asList("-", "+")) {
for (String hours : Arrays.asList("00", "01", "02", "03", "11", "12")) {
String time = base + prefix + hours;
OffsetDateTime expectedHour = OffsetDateTime.parse(time + ":00");
if (!System.getProperty("java.version").startsWith("1.8")) {
// JDK 8 cannot parse hour offsets without minutes
assertIsEqual(expectedHour, r.readValue('"' + time + '"'));
}
assertIsEqual(expectedHour, r.readValue('"' + time + "00" + '"'));
assertIsEqual(expectedHour, r.readValue('"' + time + ":00" + '"'));
assertIsEqual(OffsetDateTime.parse(time + ":30"), r.readValue('"' + time + "30" + '"'));
assertIsEqual(OffsetDateTime.parse(time + ":30"), r.readValue('"' + time + ":30" + '"'));
}
}
}

private void _testOffsetDateTimeMinOrMax(OffsetDateTime offsetDateTime)
throws Exception
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Map;
import java.util.TimeZone;

Expand Down Expand Up @@ -282,6 +284,39 @@ public void testDeserializationWithoutColonInTimeZoneWithTZDB() throws Throwable
wrapper.value);
}

@Test
public void ZonedDateTime_with_offset_can_be_deserialized() throws Exception {
ObjectReader r = newMapper().readerFor(ZonedDateTime.class)
.without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

String base = "2015-07-24T12:23:34.184";
for (String offset : Arrays.asList("+00", "-00")) {
String time = base + offset;
if (!System.getProperty("java.version").startsWith("1.8")) {
// JDK 8 cannot parse hour offsets without minutes
assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + '"'));
}
assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + "00" + '"'));
assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + ":00" + '"'));
assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184" + offset + ":30" ), r.readValue('"' + time + "30" + '"'));
assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184" + offset + ":30" ), r.readValue('"' + time + ":30" + '"'));
}

for (String prefix : Arrays.asList("-", "+")) {
for (String hours : Arrays.asList("00", "01", "02", "03", "11", "12")) {
String time = base + prefix + hours;
ZonedDateTime expectedHour = ZonedDateTime.parse(time + ":00");
if (!System.getProperty("java.version").startsWith("1.8")) {
// JDK 8 cannot parse hour offsets without minutes
assertEquals(expectedHour, r.readValue('"' + time + '"'));
}
assertEquals(expectedHour, r.readValue('"' + time + "00" + '"'));
assertEquals(expectedHour, r.readValue('"' + time + ":00" + '"'));
assertEquals(ZonedDateTime.parse(time + ":30"), r.readValue('"' + time + "30" + '"'));
assertEquals(ZonedDateTime.parse(time + ":30"), r.readValue('"' + time + ":30" + '"'));
}
}
}

private void expectFailure(String json) throws Throwable {
try {
Expand Down

0 comments on commit 29aa2b8

Please sign in to comment.