On Mon, 29 Sep 2025 13:55:35 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 Here's a related issue I discovered while implementing this PR's functionality. It seems that for any `Instant i` (1) and (2) should be equivalent: i.plus(Duration.between(i, Instant.MAX)); // (1) i.minus(Duration.between(Instant.MAX, i)); // (2) Well, they aren't: (1) results in `Instant.MAX` as expected, whereas (2) sometimes throws `DateTimeException`, indicating instant overflow. Mind you, `Duration` can address values larger than `Duration.between(Instant.MIN, Instant.MAX)`, so in (1) and (2) `Duration` never overflows and this property holds for any `i`: Duration.between(i, Instant.MAX) .equals(Duration.between(Instant.MAX, i).negated()) So, why does (2) overflow? What happens is this: * If `Duration.nanos` are non-zero, negative seconds are farther away from 0 than the equivalent positive seconds are. For example, this var pos = Duration.ofSeconds(1, 1); var neg = pos.negated(); System.out.println(pos.getSeconds() + ", " + pos.getNano()); System.out.println(neg.getSeconds() + ", " + neg.getNano()); outputs 1, 1 -2, 999999999 * `minus` first operates on seconds disregarding nanos So (2) overflows on seconds even if its intended, compound result doesn't. However, if you carefully select `i`, then no overflow will happen, and both (1) and (2) will result in `Instant.MAX`. For example, Instant i = Instant.ofEpochSecond(0, 999_999_999); This issue somewhat reminded me of the difference between `Math.sqrt(x*x + y*y)` and [`Math.hypot(x, y)`][]. [`Math.hypot(x, y)`]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Math.html#hypot(double,double) ------------- PR Comment: https://git.openjdk.org/jdk/pull/27549#issuecomment-3347925868
