Skip to content

Commit

Permalink
Fix(#291): InstantDeserializer fails to parse negative numeric timest…
Browse files Browse the repository at this point in the history
…amp strings for pre-1970 values (#362)
  • Loading branch information
Strongbeard authored Mar 5, 2025
1 parent ec402a6 commit adf848b
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS;

/**
* Deserializer for Java 8 temporal {@link Instant}s, {@link OffsetDateTime},
* and {@link ZonedDateTime}s.
Expand Down Expand Up @@ -380,11 +382,16 @@ protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext conte
}

// Helper method to find Strings of form "all digits" and "digits-comma-digits"
protected int _countPeriods(String str)
protected int _countPeriods(String str, boolean allowLeadingPlusSign)
{
int commas = 0;
for (int i = 0, end = str.length(); i < end; ++i) {
int ch = str.charAt(i);
int i = 0;
int ch = str.charAt(i);
if (ch == '-' || (ch == '+' && allowLeadingPlusSign)) {
++i;
}
for (int end = str.length(); i < end; ++i) {
ch = str.charAt(i);
if (ch < '0' || ch > '9') {
if (ch == '.') {
++commas;
Expand Down Expand Up @@ -413,7 +420,7 @@ protected T _fromString(JsonParser p, DeserializationContext ctxt,
_formatter == DateTimeFormatter.ISO_ZONED_DATE_TIME
) {
// 22-Jan-2016, [datatype-jsr310#16]: Allow quoted numbers too
int dots = _countPeriods(string);
int dots = _countPeriods(string, p.isEnabled(ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature()));
if (dots >= 0) { // negative if not simple number
try {
if (dots == 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.fasterxml.jackson.datatype.jsr310.deser;

import java.time.Instant;
import java.util.Locale;

import org.junit.Test;

import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.ModuleTestBase;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

// [modules-java8#291] InstantDeserializer fails to parse negative numeric timestamp strings for
// pre-1970 values.
public class InstantDeser291Test
extends ModuleTestBase
{
private final JsonMapper MAPPER = JsonMapper.builder()
.defaultLocale(Locale.ENGLISH)
.addModule(new JavaTimeModule()
.enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS))
.build();
private final ObjectReader READER = MAPPER.readerFor(Instant.class);
private final ObjectReader READER_ALLOW_LEADING_PLUS = READER
.withFeatures(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS);

private static final Instant INSTANT_3_SEC_AFTER_EPOC = Instant.ofEpochSecond(3);
private static final Instant INSTANT_3_SEC_BEFORE_EPOC = Instant.ofEpochSecond(-3);

private static final String STR_3_SEC = "\"3.000000000\"";
private static final String STR_POSITIVE_3 = "\"+3.000000000\"";
private static final String STR_NEGATIVE_3 = "\"-3.000000000\"";

/**
* Baseline that always succeeds, even before resolution of issue 291
* @throws Exception
*/
@Test
public void testNormalNumericalString() throws Exception {
assertEquals(INSTANT_3_SEC_AFTER_EPOC, READER.readValue(STR_3_SEC));
}

/**
* Should succeed after issue 291 is resolved.
* @throws Exception
*/
@Test
public void testNegativeNumericalString() throws Exception {
assertEquals(INSTANT_3_SEC_BEFORE_EPOC, READER.readValue(STR_NEGATIVE_3));
}

/**
* This should always fail since {@link JsonReadFeature#ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS} is
* not enabled
* @throws Exception
*/
@Test
public void testPlusSignNumericalString() throws Exception {
assertThrows(InvalidFormatException.class, () -> READER.readValue(STR_POSITIVE_3));
}

/**
* Should succeed after issue 291 is resolved. Scenario where
* {@link JsonReadFeature#ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS} is enabled
* @throws Exception
*/
@Test
public void testAllowedPlusSignNumericalString() throws Exception {
assertEquals(INSTANT_3_SEC_AFTER_EPOC, READER_ALLOW_LEADING_PLUS.readValue(STR_POSITIVE_3));
}
}

0 comments on commit adf848b

Please sign in to comment.