Skip to content

InstantDeserializer deserializes the nanosecond portion of fractional timestamps incorrectly: -1.000000001 deserializes to 1969-12-31T23:59:59.000000001Z instead of 1969-12-31T23:59:58.999999999Z #359

Open
@Strongbeard

Description

@Strongbeard

Version

2.15.4 - 2.19 and the current master branch (commit c84b1a9 as of this post).
I have not tested anything earlier than what I plan to use.

Description

Please correct me if I am misguided on how negative fractional representations of timestamps should be parsed into java.time.Instant objects. I expected -1.000000001 to deserialize to 1969-12-31T23:59:58.999999999Z, but currently it deserializes to 1969-12-31T23:59:59.000000001Z.

I assume when "splitting" the fraction into its whole seconds and nanoseconds portions, the negative sign should be distributed to both. In other words: -1.000000001 == -1 + -0.000000001, but instead the behavior appears to be -1.000000001 == -1 + 0.000000001.

Strangely, any scenario where the seconds portion is 0 deserializes as I expect it to. For example, -0.000000001 deserializes to 1969-12-31T23:59:59.999999999Z. For some reason, the distribution of the negativity from seconds to nanoseconds is preserved here.

Adding the following unit test to datetime/src/test/java/tools/jackson/datatype/jsr310/deser/InstantDeserTest.java should demonstrate the issue, where test 4 fails and test 5 succeeds:

@Test
public void testDeserializationAsFloat04() throws Exception {
    Instant actual = READER.readValue("-1.000000001");
    Instant expected = Instant.ofEpochSecond(-1L, -1L);
    assertEquals(expected, actual);
}

@Test
public void testDeserializationAsFloat05() throws Exception {
    Instant actual = READER.readValue("-0.000000001");
    Instant expected = Instant.ofEpochSecond(0L, -1L);
    assertEquals(expected, actual);
}

The following error is produced when executing the tests:

[ERROR] tools.jackson.datatype.jsr310.deser.InstantDeserTest.testDeserializationAsFloat04 -- Time elapsed: 0.128 s <<< FAILURE!
org.opentest4j.AssertionFailedError: expected: <1969-12-31T23:59:58.999999999Z> but was: <1969-12-31T23:59:59.000000001Z>
        at [email protected]/org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
        at [email protected]/org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
        at [email protected]/org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
        at [email protected]/org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
        at [email protected]/org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
        at [email protected]/org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1145)
        at tools.jackson.datatype.javatime/tools.jackson.datatype.jsr310.deser.InstantDeserTest.testDeserializationAsFloat04(InstantDeserTest.java:121)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

If my assumptions are correct, then the fix appears more complicated than just changing the negativeAdjustment boolean passed to the call to DecimalUtils.extractSecondsAndNanos(BigDecimal, BiFunction, boolean) within the _fromDecimal(DeserializationContext, BigDecimal) function of InstantDeserializer to false since that causes unit test ZonedDateTimeSerTest#testSerializationAsTimestamp01NegativeSeconds to fail.

Related Issues

I believe this is related to the implementation of the fix for issues #304, #69, and #132.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions