Skip to content

Commit 236faaf

Browse files
authored
Fix #359 (#378)
1 parent 6ba6fa5 commit 236faaf

File tree

5 files changed

+20
-5
lines changed

5 files changed

+20
-5
lines changed

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/DecimalUtils.java

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

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,9 @@ protected T _fromDecimal(DeserializationContext context, BigDecimal value)
480480
{
481481
FromDecimalArguments args =
482482
DecimalUtils.extractSecondsAndNanos(value, (s, ns) -> new FromDecimalArguments(s, ns, getZone(context)),
483-
// [modules-java8#337] since 2.19, only Instant needs negative adjustment
484-
true);
483+
// [modules-java8#359] since 2.21, Instant.ofEpochSecond() correctly handles
484+
// negative nanoseconds, so no adjustment needed
485+
false);
485486
return fromNanoseconds.apply(args);
486487
}
487488

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
package com.fasterxml.jackson.datatype.jsr310.tofix;
1+
package com.fasterxml.jackson.datatype.jsr310.deser;
22

33
import java.time.Instant;
44

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

77
import com.fasterxml.jackson.databind.ObjectReader;
88
import com.fasterxml.jackson.datatype.jsr310.ModuleTestBase;
9-
import com.fasterxml.jackson.datatype.jsr310.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

release-notes/CREDITS-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ Kevin Mahon (@Strongbeard)
213213
* Fixed #291: `InstantDeserializer` fails to parse negative numeric timestamp strings
214214
for pre-1970 values
215215
(2.18.4)
216+
* Reported #359: `InstantDeserializer` deserializes the nanosecond portion of fractional
217+
negative timestamps incorrectly
218+
(2.21.0)
216219

217220
Joey Muia (@jmuia)
218221
* Reported #337: Negative `Duration` does not round-trip properly with

release-notes/VERSION-2.x

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ Modules:
1010

1111
2.21.0 (not yet released)
1212

13+
#359: `InstantDeserializer` deserializes the nanosecond portion of fractional
14+
negative timestamps incorrectly
15+
(reported by Kevin M)
16+
(fix by @cowtowncoder, w/ Claude code)
17+
1318
No changes since 2.20
1419

1520
2.20.1 (30-Oct-2025)

0 commit comments

Comments
 (0)