On Tue, 30 Sep 2025 23:06:39 GMT, Pavel Rappo <[email protected]> wrote:

>> We recently [discussed] the possibility of introducing saturating arithmetic 
>> for deadline computation. Consider this PR as a starting point. Once we 
>> agree on the implementation, I'll file a CSR.
>> 
>> I created a method in `Instant` to add `Duration`. One could argue that the 
>> proper way would be to go all the way and create a method in `Temporal` to 
>> add `TemporalAmount`. Or maybe even expand the functionality, and create an 
>> additional method in `Temporal` to subtract `TemporalAmount`.
>> 
>> My current thinking is that if we were to do that, there would be a lot of 
>> expensive, unused code. Saturating logic seems to be only useful for 
>> `Instant` and `Duration`.
>> 
>> Even if we decide to extend `Temporal` to add/subtract `TemporalAmount`, it 
>> could always be done later. From the perspective of `Instant`, 
>> `plus(TemporalAmount)` will be just an overload of `plus(Duration)`.
>> 
>> [discussed]: 
>> https://mail.openjdk.org/pipermail/core-libs-dev/2025-September/151098.html
>
> Pavel Rappo has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Slightly improve corner case

In 1fe394b I added a test case to exercise behaviour similar to the one 
discussed previously for `minus`. The test case ultimately does this:

    Instant.MAX.plus(Duration.between(Instant.MAX, Instant.MIN))

It looks like the result is `Instant.MIN`, but it isn't. It would be 
`Instant.MIN` if `plus` didn't overflow intermediately.

---

Let me again explain what happens, if only for future reference for myself. 
`Instant.MAX` is 31556889864403199.999,999,999 seconds. 
`Duration.between(Instant.MAX, Instant.MIN)` is -63113904031622399.999,999,999 
seconds. The sum of those is -31557014167219200 seconds, which is exactly 
`Instant.MIN`.

However, the internal representation of `Duration` and `Instant` are not what 
one might think. While `seconds` can be of either sign, `nanos` are always 
non-negative, within this range 0 <= nanos <= 999,999,999.

So, negative `Duration` -63113904031622399.999,999,999 is represented as 
-63113904031622200 seconds plus 1 nanosecond. That is, it's a value that is 1 
nanosecond closer to 0 than -63113904031622200 is.

When `Instant.plus(Duration)` is invoked, it first creates an `Instant` whose 
seconds are the sum of the seconds and whose nanoseconds are those of the 
original `Instant`. Any nanoseconds from `Duration` are then added to that 
`Instant` to produce yet another `Instant`, which holds the final result.

The overflow happens in the first step of `plus`, because 31556889864403199 - 
63113904031622200 is -31557014167219201, which is less than the 
-31556889864403200 seconds of `Instant.MIN`. If `plus` proceeded to the second 
step, which is nanos adjustment, it would get 999,999,999 + 1, which is equal 
to a whole positive second. It would then add it to -31557014167219201 and get 
-31557014167219200, which is `Instant.MIN`.

-------------

PR Comment: https://git.openjdk.org/jdk/pull/27549#issuecomment-3355846946

Reply via email to