Skip to content

Commit e3f7473

Browse files
authored
Fix date-time#359 (in embedded-in-jackson-3 date/time code) (#5399)
1 parent 1fc5272 commit e3f7473

File tree

4 files changed

+14
-5
lines changed

4 files changed

+14
-5
lines changed

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Versions: 3.x (for earlier see VERSION-2.x)
1010
#5350: Add `DeserializationFeature.USE_NULL_FOR_MISSING_REFERENCE_VALUES` for
1111
selecting `null` vs "empty/absent" value when deserializing missing `Optional` value
1212
#5361: Fix Maven SBOM publishing (worked in 3.0.0-rc4 but not in rc5 or later)
13+
(date-time)#359: `InstantDeserializer` deserializes the nanosecond portion of
14+
fractional negative timestamps incorrectly
1315

1416
3.0.2 (07-Nov-2025)
1517

src/main/java/tools/jackson/databind/ext/javatime/deser/InstantDeserializer.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,9 @@ protected T _fromDecimal(DeserializationContext context, BigDecimal value)
395395
{
396396
FromDecimalArguments args =
397397
DecimalUtils.extractSecondsAndNanos(value, (s, ns) -> new FromDecimalArguments(s, ns, getZone(context)),
398-
// [modules-java8#337] since 2.19, only Instant needs negative adjustment
399-
true);
398+
// [modules-java8#359] since 2.21, Instant.ofEpochSecond() correctly handles
399+
// negative nanoseconds, so no adjustment needed
400+
false);
400401
return fromNanoseconds.apply(args);
401402
}
402403

src/main/java/tools/jackson/databind/ext/javatime/util/DecimalUtils.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ public static String toDecimal(long seconds, int nanoseconds)
8181
*/
8282
public static BigDecimal toBigDecimal(long seconds, int nanoseconds)
8383
{
84+
// [modules-java8#359] For negative seconds with positive nanos (times before epoch),
85+
// we need to compute the proper decimal value: seconds + (nanos / 1_000_000_000)
86+
// Example: Instant{epochSecond=-1, nano=999000000} represents -0.001 seconds
87+
if (seconds < 0 && nanoseconds > 0) {
88+
return BigDecimal.valueOf(seconds)
89+
.add(BigDecimal.valueOf(nanoseconds).scaleByPowerOfTen(-9));
90+
}
91+
8492
if (nanoseconds == 0L) {
8593
// 14-Mar-2015, tatu: Let's retain one zero to avoid interpretation
8694
// as integral number

src/test/java/tools/jackson/databind/ext/javatime/tofix/InstantDeserializerNegative359Test.java renamed to src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeserializerNegative359Test.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
package tools.jackson.databind.ext.javatime.tofix;
1+
package tools.jackson.databind.ext.javatime.deser;
22

33
import java.time.Instant;
44

55
import org.junit.jupiter.api.Test;
66

77
import tools.jackson.databind.ObjectReader;
88
import tools.jackson.databind.ext.javatime.DateTimeTestBase;
9-
import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected;
109

1110
import static org.junit.jupiter.api.Assertions.assertEquals;
1211

@@ -18,7 +17,6 @@ public class InstantDeserializerNegative359Test
1817
{
1918
private final ObjectReader READER = newMapper().readerFor(Instant.class);
2019

21-
@JacksonTestFailureExpected
2220
@Test
2321
public void testDeserializationAsFloat04()
2422
throws Exception

0 commit comments

Comments
 (0)