diff --git a/src/java.base/share/classes/java/time/Duration.java b/src/java.base/share/classes/java/time/Duration.java index 23577a8a634..7b3289a1f59 100644 --- a/src/java.base/share/classes/java/time/Duration.java +++ b/src/java.base/share/classes/java/time/Duration.java @@ -138,6 +138,37 @@ public final class Duration * Constant for a duration of zero. */ public static final Duration ZERO = new Duration(0, 0); + /** + * The minimum supported {@code Duration}, which is {@link Long#MIN_VALUE} + * seconds. + * + * @apiNote This constant represents the smallest possible instance of + * {@code Duration}. Since {@code Duration} is directed, the smallest + * possible duration is negative. + * + * The constant is intended to be used as a sentinel value or in tests. + * Care should be taken when performing arithmetic on {@code MIN} as there + * is a high risk that {@link ArithmeticException} or {@link DateTimeException} + * will be thrown. + * + * @since 26 + */ + public static final Duration MIN = new Duration(Long.MIN_VALUE, 0); + /** + * The maximum supported {@code Duration}, which is {@link Long#MAX_VALUE} + * seconds and {@code 999,999,999} nanoseconds. + * + * @apiNote This constant represents the largest possible instance of + * {@code Duration}. + * + * The constant is intended to be used as a sentinel value or in tests. + * Care should be taken when performing arithmetic on {@code MAX} as there + * is a high risk that {@link ArithmeticException} or {@link DateTimeException} + * will be thrown. + * + * @since 26 + */ + public static final Duration MAX = new Duration(Long.MAX_VALUE, 999_999_999); /** * Serialization version. */ diff --git a/src/java.base/share/classes/java/time/temporal/ChronoUnit.java b/src/java.base/share/classes/java/time/temporal/ChronoUnit.java index 8f94e061d4d..6e944b296da 100644 --- a/src/java.base/share/classes/java/time/temporal/ChronoUnit.java +++ b/src/java.base/share/classes/java/time/temporal/ChronoUnit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -184,10 +184,9 @@ public enum ChronoUnit implements TemporalUnit { * Artificial unit that represents the concept of forever. * This is primarily used with {@link TemporalField} to represent unbounded fields * such as the year or era. - * The estimated duration of this unit is artificially defined as the largest duration - * supported by {@link Duration}. + * The estimated duration of this unit is artificially defined as {@link Duration#MAX}. */ - FOREVER("Forever", Duration.ofSeconds(Long.MAX_VALUE, 999_999_999)); + FOREVER("Forever", Duration.MAX); private final String name; private final Duration duration; diff --git a/test/jdk/java/time/tck/java/time/TCKDuration.java b/test/jdk/java/time/tck/java/time/TCKDuration.java index ee3950dec06..2057e8e8939 100644 --- a/test/jdk/java/time/tck/java/time/TCKDuration.java +++ b/test/jdk/java/time/tck/java/time/TCKDuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,6 +71,8 @@ import static java.time.temporal.ChronoUnit.SECONDS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -115,6 +117,44 @@ public class TCKDuration extends AbstractTCKTest { assertEquals(Duration.ZERO.getNano(), 0); } + @Test + public void test_min() { + assertEquals(Duration.MIN.getSeconds(), Long.MIN_VALUE); + assertEquals(Duration.MIN.getNano(), 0); + // no duration minimally less than MIN + assertThrows(ArithmeticException.class, () -> Duration.MIN.minusNanos(1)); + } + + @Test + public void test_max() { + assertEquals(Duration.MAX.getSeconds(), Long.MAX_VALUE); + assertEquals(Duration.MAX.getNano(), 999_999_999); + // no duration minimally greater than MAX + assertThrows(ArithmeticException.class, () -> Duration.MAX.plusNanos(1)); + } + + @Test + public void test_constant_properties() { + assertTrue(Duration.MIN.compareTo(Duration.MIN) == 0); + assertEquals(Duration.MIN, Duration.MIN); + assertTrue(Duration.ZERO.compareTo(Duration.ZERO) == 0); + assertEquals(Duration.ZERO, Duration.ZERO); + assertTrue(Duration.MAX.compareTo(Duration.MAX) == 0); + assertEquals(Duration.MAX, Duration.MAX); + + assertTrue(Duration.MIN.compareTo(Duration.ZERO) < 0); + assertTrue(Duration.ZERO.compareTo(Duration.MIN) > 0); + assertNotEquals(Duration.ZERO, Duration.MIN); + + assertTrue(Duration.ZERO.compareTo(Duration.MAX) < 0); + assertTrue(Duration.MAX.compareTo(Duration.ZERO) > 0); + assertNotEquals(Duration.ZERO, Duration.MAX); + + assertTrue(Duration.MIN.compareTo(Duration.MAX) < 0); + assertTrue(Duration.MAX.compareTo(Duration.MIN) > 0); + assertNotEquals(Duration.MIN, Duration.MAX); + } + //----------------------------------------------------------------------- // ofSeconds(long) //-----------------------------------------------------------------------