Skip to content

Commit adf848b

Browse files
authored
Fix(#291): InstantDeserializer fails to parse negative numeric timestamp strings for pre-1970 values (#362)
1 parent ec402a6 commit adf848b

File tree

2 files changed

+88
-4
lines changed

2 files changed

+88
-4
lines changed

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import java.util.regex.Matcher;
4545
import java.util.regex.Pattern;
4646

47+
import static com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS;
48+
4749
/**
4850
* Deserializer for Java 8 temporal {@link Instant}s, {@link OffsetDateTime},
4951
* and {@link ZonedDateTime}s.
@@ -380,11 +382,16 @@ protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext conte
380382
}
381383

382384
// Helper method to find Strings of form "all digits" and "digits-comma-digits"
383-
protected int _countPeriods(String str)
385+
protected int _countPeriods(String str, boolean allowLeadingPlusSign)
384386
{
385387
int commas = 0;
386-
for (int i = 0, end = str.length(); i < end; ++i) {
387-
int ch = str.charAt(i);
388+
int i = 0;
389+
int ch = str.charAt(i);
390+
if (ch == '-' || (ch == '+' && allowLeadingPlusSign)) {
391+
++i;
392+
}
393+
for (int end = str.length(); i < end; ++i) {
394+
ch = str.charAt(i);
388395
if (ch < '0' || ch > '9') {
389396
if (ch == '.') {
390397
++commas;
@@ -413,7 +420,7 @@ protected T _fromString(JsonParser p, DeserializationContext ctxt,
413420
_formatter == DateTimeFormatter.ISO_ZONED_DATE_TIME
414421
) {
415422
// 22-Jan-2016, [datatype-jsr310#16]: Allow quoted numbers too
416-
int dots = _countPeriods(string);
423+
int dots = _countPeriods(string, p.isEnabled(ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature()));
417424
if (dots >= 0) { // negative if not simple number
418425
try {
419426
if (dots == 0) {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.fasterxml.jackson.datatype.jsr310.deser;
2+
3+
import java.time.Instant;
4+
import java.util.Locale;
5+
6+
import org.junit.Test;
7+
8+
import com.fasterxml.jackson.core.json.JsonReadFeature;
9+
import com.fasterxml.jackson.databind.ObjectReader;
10+
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
11+
import com.fasterxml.jackson.databind.json.JsonMapper;
12+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature;
13+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
14+
import com.fasterxml.jackson.datatype.jsr310.ModuleTestBase;
15+
16+
import static org.junit.Assert.assertEquals;
17+
import static org.junit.Assert.assertThrows;
18+
19+
// [modules-java8#291] InstantDeserializer fails to parse negative numeric timestamp strings for
20+
// pre-1970 values.
21+
public class InstantDeser291Test
22+
extends ModuleTestBase
23+
{
24+
private final JsonMapper MAPPER = JsonMapper.builder()
25+
.defaultLocale(Locale.ENGLISH)
26+
.addModule(new JavaTimeModule()
27+
.enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS))
28+
.build();
29+
private final ObjectReader READER = MAPPER.readerFor(Instant.class);
30+
private final ObjectReader READER_ALLOW_LEADING_PLUS = READER
31+
.withFeatures(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS);
32+
33+
private static final Instant INSTANT_3_SEC_AFTER_EPOC = Instant.ofEpochSecond(3);
34+
private static final Instant INSTANT_3_SEC_BEFORE_EPOC = Instant.ofEpochSecond(-3);
35+
36+
private static final String STR_3_SEC = "\"3.000000000\"";
37+
private static final String STR_POSITIVE_3 = "\"+3.000000000\"";
38+
private static final String STR_NEGATIVE_3 = "\"-3.000000000\"";
39+
40+
/**
41+
* Baseline that always succeeds, even before resolution of issue 291
42+
* @throws Exception
43+
*/
44+
@Test
45+
public void testNormalNumericalString() throws Exception {
46+
assertEquals(INSTANT_3_SEC_AFTER_EPOC, READER.readValue(STR_3_SEC));
47+
}
48+
49+
/**
50+
* Should succeed after issue 291 is resolved.
51+
* @throws Exception
52+
*/
53+
@Test
54+
public void testNegativeNumericalString() throws Exception {
55+
assertEquals(INSTANT_3_SEC_BEFORE_EPOC, READER.readValue(STR_NEGATIVE_3));
56+
}
57+
58+
/**
59+
* This should always fail since {@link JsonReadFeature#ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS} is
60+
* not enabled
61+
* @throws Exception
62+
*/
63+
@Test
64+
public void testPlusSignNumericalString() throws Exception {
65+
assertThrows(InvalidFormatException.class, () -> READER.readValue(STR_POSITIVE_3));
66+
}
67+
68+
/**
69+
* Should succeed after issue 291 is resolved. Scenario where
70+
* {@link JsonReadFeature#ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS} is enabled
71+
* @throws Exception
72+
*/
73+
@Test
74+
public void testAllowedPlusSignNumericalString() throws Exception {
75+
assertEquals(INSTANT_3_SEC_AFTER_EPOC, READER_ALLOW_LEADING_PLUS.readValue(STR_POSITIVE_3));
76+
}
77+
}

0 commit comments

Comments
 (0)