Duration.MAX_VALUE

Pavel Rappo pavel.rappo at gmail.com
Mon Oct 13 20:44:21 UTC 2025


Thanks! I think that your experiment confirms that we do _not_ need
subtraction, at least initially. A single, focused method for
saturated addition should be enough. I'll update PR (
https://github.com/openjdk/jdk/pull/27549 ), which you are welcome to
review.

Stephen, are you okay with this?

-Pavel

On Mon, Oct 13, 2025 at 6:56 PM Éamonn McManus <emcmanus at google.com> wrote:
>
> On Thu, 9 Oct 2025 at 07:25, Pavel Rappo <pavel.rappo at gmail.com> wrote:
>>
>> Out of curiosity, how is Instants.saturatedSubtract(Instant, Duration) used?
>
>
> That is an excellent question, and it prompted me to look into saturated addition and subtraction in a bit more detail. As an experiment, I tried running all of Google's internal tests against a modified Instants class where saturatedAdd and saturatedSubtract behaved identically to regular Instant.add/subtract(Duration). That led to hundreds of test failures, although I think there might have been fewer than 10 distinct root causes. Then I retried the failing tests with only saturatedSubtract being changed. All but one of the previously-failing tests now passed. That shows that saturatedAdd has a much wider applicability than saturatedSubtract.
>
> I get the impression from looking at some samples that people are often using saturatedSubtract out of an abundance of caution rather than having a real reason to think the subtraction might overflow. Many of the examples I saw were using fixed-length durations, which could only overflow if being subtracted from Instant.MIN or thereabouts.
>
> The one test that failed with saturatedSubtract was testing code that does something like this:
>
> Instant finalizationTime = ...;
> boolean recentlyFinalized =
>     finalizationTime.isAfter(
>         Instants.saturatedSubtract(now, minAgeForDeletion));
>
> The idea is that something can only be deleted if it was not "recently finalized". If it was finalized less than minAgeForDeletion time ago then it can't be deleted. In the failing test, minAgeForDeletion is Durations.MAX, which signals that something can never be deleted (it will always be recently finalized).
> The code could just as easily do this:
>
> boolean recentlyFinalized =
>     now.isBefore(
>         Instants.saturatedAdd(finalizationTime, minAgeForDeletion));
>
> So I don't think this is a particularly convincing use case for saturatedSubtract. The main reason for having it would be that it might be surprising to have add but not subtract.
>
> Éamonn
>
> On Thu, 9 Oct 2025 at 07:25, Pavel Rappo <pavel.rappo at gmail.com> wrote:
>>
>> Out of curiosity, how is Instants.saturatedSubtract(Instant, Duration) used?
>>
>> On Wed, Oct 8, 2025 at 2:13 AM Éamonn McManus <emcmanus at google.com> wrote:
>> >
>> > Yes, we have utility classes Instants and Durations, and Instants includes methods Instants.saturatedAdd(Instant, Duration) and Instants.saturatedSubtract(Instant, Duration). Each has a modest number of uses in the codebase, about three orders of magnitude less than the number of classes that reference Instant or Duration.
>> >
>> > On Tue, 7 Oct 2025 at 14:18, Pavel Rappo <pavel.rappo at gmail.com> wrote:
>> >>
>> >> Éamonn, Kurt,
>> >>
>> >> Is there any saturating arithmetic for instant + duration in your code base?
>> >>
>> >>
>> >>
>> >> On Fri, Sep 5, 2025 at 8:58 PM Éamonn McManus <emcmanus at google.com> wrote:
>> >> >
>> >> > As promised, Kurt and I have examined some of the uses of our Durations.MAX constant. This is a summary of what we see in a random sample of 30 out of about 700 usages in Google's (giant) codebase.
>> >> >
>> >> > First, about half of the usages specifically concern deadlines. Many of them involve a method that sets an RPC deadline and where it is explicitly documented that you should use Durations.MAX to mean no deadline (or equivalently, infinite deadline).
>> >> >
>> >> > Several other usages concern deadline-adjacent concepts such as time-to-live or cache expiration delay.
>> >> >
>> >> > A number of usages specifically compare a Duration against Durations.MAX to recognize the "infinite duration" value.
>> >> >
>> >> > One usage uses our internal Sleeper interface to do sleeper.sleep(Durations.MAX), for an indefinite sleep (until interrupted).
>> >> >
>> >> > In a couple of places, there is a maximum allowed Duration for some operation, and a user-supplied Duration value is capped by this maximum. When no maximum is needed, the cap is Durations.MAX. This is similar to the "sentinel" use case I mentioned earlier.
>> >> >
>> >> > One case is using Durations.MAX in a test, to ensure that a function works correctly for all Duration values including the largest one. It is testing a Kotlin extension function that allows you to write e.g. 5.minutes:
>> >> >
>> >> >     val n = Durations.MAX.toMinutes()
>> >> >
>> >> >     assertThat(n.minutes).isEqualTo(Duration.ofMinutes(n))
>> >> >
>> >> >     assertFailsWith<ArithmeticException> { (n + 1).minutes }
>> >> >
>> >> >
>> >> > In summary, I think the most interesting use cases fall into these categories:
>> >> >
>> >> > to express an effectively-infinite Duration, possibly accompanied by special-case logic to optimize the exact value Durations.MAX;
>> >> >
>> >> > to express the absence of an optional cap on a user-supplied Duration value;
>> >> >
>> >> > to test that code works correctly even with extreme Duration values.
>> >> >
>> >> >
>> >> > Of these, the first is potentially fragile because of the overflow problems we discussed. The other two seem unproblematic, though.
>> >> >
>> >> >
>> >> >
>> >> > On Thu, 4 Sept 2025 at 15:02, Éamonn McManus <emcmanus at google.com> wrote:
>> >> >>
>> >> >> Two typical use cases:
>> >> >>
>> >> >> // 1. Sentinel
>> >> >> Duration min = Duration.MAX;
>> >> >> for (var foo : something()) {
>> >> >>   if (foo.duration().compareTo(min) < 0) {
>> >> >>     min = foo.duration();
>> >> >>   }
>> >> >> }
>> >> >>
>> >> >> // 2. "Forever"
>> >> >> void frob(Optional<Duration> optionalTimeout) {
>> >> >>   Duration timeout = optionalTimeout.orElse(Duration.MAX);
>> >> >>   Instant start = Instant.now();
>> >> >>   boolean done = false;
>> >> >>   while (!done && startTime.until(Instant.now()).compareTo(timeout) < 0) {...}
>> >> >> }
>> >> >>
>> >> >> The second case illustrates why this is potentially a bit delicate. You better not write this:
>> >> >>
>> >> >> void frob(Optional<Duration> optionalTimeout) {
>> >> >>   Duration timeout = optionalTimeout.orElse(Duration.MAX);
>> >> >>   Instant deadline = Instant.now().plus(timeout); // oops
>> >> >>   boolean done = false;
>> >> >>   while (!done && Instant.now().isBefore(deadline)) {...}
>> >> >> }
>> >> >>
>> >> >> Like Kevin, I am skeptical about Duration.MIN. If it means the most negative Duration, that is just Duration.MAX.negated(); and if it means the smallest positive Duration, that is just Duration.ofNanos(1).
>> >> >>
>> >> >> On Wed, 3 Sept 2025 at 18:32, Roger Riggs <roger.riggs at oracle.com> wrote:
>> >> >>>
>> >> >>> Hi,
>> >> >>>
>> >> >>> I'd be interested in the range of use cases for Duration.MAX or MIN.
>> >> >>>
>> >> >>> But for deadlines, I think the code should compute the deadline from a Duration of its choice based on the use.
>> >> >>> Maybe there is a use for Duration.REALLY_BIG or _SMALL, but that ignores information about the particular use that is relevant. Its just sloppy code that doesn't bother to express how long is long enough to meet operational parameters.
>> >> >>>
>> >> >>> YMMV, Roger
>> >> >>>
>> >> >>> On 9/3/25 8:21 PM, Kurt Alfred Kluever wrote:
>> >> >>>
>> >> >>> Duration.MIN is a whole 'nother bag of worms, because Durations are signed (they can be positive or negative...or zero). Internally we also have Durations.MIN, but it's not public ... and along with it, I left myself a helpful note about naming:
>> >> >>>
>> >> >>>   /** The minimum supported {@code Duration}, approximately -292 billion years. */
>> >> >>>   // Note: before making this constant public, consider that "MIN" might not be a great name (not
>> >> >>>   //       everyone knows that Durations can be negative!).
>> >> >>>   static final Duration MIN = Duration.ofSeconds(Long.MIN_VALUE);
>> >> >>>
>> >> >>> This reminds me of Double.MIN_VALUE (which is the smallest _positive_ double value) --- we've seen Double.MIN_VALUE misused so much that we introduced Doubles.MIN_POSITIVE_VALUE as a more descriptive alias. A large percent of Double.MIN_VALUE users actually want the smallest possible negative value, aka -Double.MAX_VALUE.
>> >> >>>
>> >> >>> If we introduce Duration.MIN, I hope it would not be Duration.ofNanos(1), but rather Duration.ofSeconds(Long.MIN_VALUE).
>> >> >>>
>> >> >>> On Wed, Sep 3, 2025 at 7:59 PM ecki <ecki at zusammenkunft.net> wrote:
>> >> >>>>
>> >> >>>> If you ask me, I don’t find it very useful, It won’t work for arithmetrics, even the APIs would have a hard time using it (how do you express the deadline) and APIs with a timeout parameter do have a good reason for it, better pick “possible” values for better self healing and unstuck of systems. In fact I would err on the smaller side in combination with expecting spurious wakeups.
>> >> >>>>
>> >> >>>> BTW, when you introduce MIN as well, maybe also think about min precision, min delta or such. Will it always be 1 nano?
>> >> >>>>
>> >> >>>> Gruß,
>> >> >>>> Bernd
>> >> >>>> --
>> >> >>>> https://bernd.eckenfels.net
>> >> >>>> ________________________________
>> >> >>>> Von: core-libs-dev <core-libs-dev-retn at openjdk.org> im Auftrag von Pavel Rappo <pavel.rappo at gmail.com>
>> >> >>>> Gesendet: Donnerstag, September 4, 2025 12:41 AM
>> >> >>>> An: Kurt Alfred Kluever <kak at google.com>
>> >> >>>> Cc: Stephen Colebourne <scolebourne at joda.org>; core-libs-dev <core-libs-dev at openjdk.org>
>> >> >>>> Betreff: Re: Duration.MAX_VALUE
>> >> >>>>
>> >> >>>> This is useful; thanks. It would be good to see more of your data.
>> >> >>>>
>> >> >>>> My use case is also duration which practically means **forever**. I
>> >> >>>> pass it to methods that accept timeouts, and expect these methods to
>> >> >>>> correctly interpret it.
>> >> >>>>
>> >> >>>> One example of a practical interpretation is
>> >> >>>> java.util.concurrent.TimeUnit.convert(Duration). This method never
>> >> >>>> overflows; instead, it caps at Long.MAX_VALUE nanoseconds, which is
>> >> >>>> roughly 292 years.
>> >> >>>>
>> >> >>>> Would I be okay, if the proposed duration didn't reflect **forever**
>> >> >>>> but instead reflected **long enough**? I think so. But it still
>> >> >>>> somehow feels wrong to make it less than maximum representable value.
>> >> >>>>
>> >> >>>> Personally, I'm not interested in calendar arithmetic, that is, in
>> >> >>>> adding or subtracting durations. Others might be, and that's okay and
>> >> >>>> needs to be factored in. For better or worse, java.time made a choice
>> >> >>>> to be unforgiving in regard to overflow and is very upfront about it.
>> >> >>>> It's not only proposed Duration.MAX. The same thing happens if you try
>> >> >>>> this
>> >> >>>>
>> >> >>>> Instant.MAX.toEpochMilli()
>> >> >>>>
>> >> >>>> I guess my point is that doing calendar arithmetic on an unknown value
>> >> >>>> is probably wrong. Doing it on a known huge/edge-case value is surely
>> >> >>>> wrong. So back to your data. I would be interested to see what
>> >> >>>> triggers overflows for your Durations.MAX.
>> >> >>>>
>> >> >>>> On Wed, Sep 3, 2025 at 8:45 PM Kurt Alfred Kluever <kak at google.com> wrote:
>> >> >>>> >
>> >> >>>> > Hi all,
>> >> >>>> >
>> >> >>>> > Internally at Google, we've had a Durations.MAX constant exposed for the past 7 years. It now has about 700 usages across our depot, which I can try to categorize (at a future date).
>> >> >>>> >
>> >> >>>> > While I haven't performed that analysis yet, I think exposing this constant was a bit of a mistake. People seem to want to use MAX to mean "forever" (often in regards to an RPC deadline). This works fine as long as every single layer that touches the deadline is very careful about overflow. The only reasonable thing you can do with MAX is compareTo() and equals(). Attempting to do any simple math operation (e.g., now+deadline) is going to explode. Additionally, decomposing Duration.MAX explodes for any sub-second precision (e.g., toMillis()).
>> >> >>>> >
>> >> >>>> > As we dug into this, another proposal came up which was something like Durations.VERY_LONG. This duration would be longer than any reasonable finite duration but not long enough to cause an overflow when added to any reasonable time. E.g., a million years would probably satisfy both criteria. This would mean math operations and decompositions won't explode (well, microseconds and nanoseconds still would), and it could safely be used as a relative timeout.
>> >> >>>> >
>> >> >>>> > As I mentioned above, I'd be happy to try to categorize a sample of our 700 existing usages if folks think that would be useful for this proposal.
>> >> >>>> >
>> >> >>>> > Thanks,
>> >> >>>> >
>> >> >>>> > -Kurt Alfred Kluever (on behalf of Google's Java and Kotlin Ecosystem team)
>> >> >>>> >
>> >> >>>> > On Wed, Sep 3, 2025 at 1:53 PM Pavel Rappo <pavel.rappo at gmail.com> wrote:
>> >> >>>> >>
>> >> >>>> >> If I understood you correctly, you think we should also add
>> >> >>>> >> Duration.MIN. If so, what use case do you envision for it? Or we add
>> >> >>>> >> if purely for symmetry with Instant?
>> >> >>>> >>
>> >> >>>> >> On Wed, Sep 3, 2025 at 6:43 PM Pavel Rappo <pavel.rappo at gmail.com> wrote:
>> >> >>>> >> >
>> >> >>>> >> > On Wed, Sep 3, 2025 at 6:06 PM Stephen Colebourne <scolebourne at joda.org> wrote:
>> >> >>>> >> > >
>> >> >>>> >> > > Hmm, yes. Not sure why that didn't get added in Java 8!
>> >> >>>> >> > > The constants would be MAX/MIN as per classes like Instant.
>> >> >>>> >> > > Stephen
>> >> >>>> >> >
>> >> >>>> >> > I thought that naming could be tricky :) The public constant
>> >> >>>> >> > Duration.ZERO and the public method isZero() are already there.
>> >> >>>> >> > However, it does not preclude us from naming a new constant MAX.
>> >> >>>> >
>> >> >>>> >
>> >> >>>> >
>> >> >>>> > --
>> >> >>>> > kak
>> >> >>>>
>> >> >>>
>> >> >>>
>> >> >>> --
>> >> >>> kak
>> >> >>>
>> >> >>>


More information about the core-libs-dev mailing list