From 52b1391b3a79344d8accfd6c059bc1bdd2cc705b Mon Sep 17 00:00:00 2001 From: Roland Mesde Date: Thu, 13 Nov 2025 09:32:41 -0800 Subject: [PATCH 1/2] Backport 81d2acee57188a4507c798b46b0bd129dc302fec --- .../share/classes/java/time/Instant.java | 36 +++++++++++++------ .../share/classes/java/time/LocalTime.java | 14 ++++++-- .../java/time/test/java/time/TestInstant.java | 26 +++++++++++++- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/src/java.base/share/classes/java/time/Instant.java b/src/java.base/share/classes/java/time/Instant.java index 0f52edc9ffa..68a7bb73a3b 100644 --- a/src/java.base/share/classes/java/time/Instant.java +++ b/src/java.base/share/classes/java/time/Instant.java @@ -61,6 +61,8 @@ */ package java.time; +import static java.time.LocalTime.MICROS_PER_SECOND; +import static java.time.LocalTime.MILLIS_PER_SECOND; import static java.time.LocalTime.NANOS_PER_SECOND; import static java.time.LocalTime.SECONDS_PER_DAY; import static java.time.LocalTime.SECONDS_PER_HOUR; @@ -1143,17 +1145,17 @@ public Temporal adjustInto(Temporal temporal) { public long until(Temporal endExclusive, TemporalUnit unit) { Instant end = Instant.from(endExclusive); if (unit instanceof ChronoUnit chronoUnit) { - switch (chronoUnit) { - case NANOS: return nanosUntil(end); - case MICROS: return nanosUntil(end) / 1000; - case MILLIS: return Math.subtractExact(end.toEpochMilli(), toEpochMilli()); - case SECONDS: return secondsUntil(end); - case MINUTES: return secondsUntil(end) / SECONDS_PER_MINUTE; - case HOURS: return secondsUntil(end) / SECONDS_PER_HOUR; - case HALF_DAYS: return secondsUntil(end) / (12 * SECONDS_PER_HOUR); - case DAYS: return secondsUntil(end) / (SECONDS_PER_DAY); - } - throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); + return switch (chronoUnit) { + case NANOS -> nanosUntil(end); + case MICROS -> microsUntil(end); + case MILLIS -> millisUntil(end); + case SECONDS -> secondsUntil(end); + case MINUTES -> secondsUntil(end) / SECONDS_PER_MINUTE; + case HOURS -> secondsUntil(end) / SECONDS_PER_HOUR; + case HALF_DAYS -> secondsUntil(end) / (12 * SECONDS_PER_HOUR); + case DAYS -> secondsUntil(end) / (SECONDS_PER_DAY); + default -> throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); + }; } return unit.between(this, end); } @@ -1164,6 +1166,18 @@ private long nanosUntil(Instant end) { return Math.addExact(totalNanos, end.nanos - nanos); } + private long microsUntil(Instant end) { + long secsDiff = Math.subtractExact(end.seconds, seconds); + long totalMicros = Math.multiplyExact(secsDiff, MICROS_PER_SECOND); + return Math.addExact(totalMicros, (end.nanos - nanos) / 1000); + } + + private long millisUntil(Instant end) { + long secsDiff = Math.subtractExact(end.seconds, seconds); + long totalMillis = Math.multiplyExact(secsDiff, MILLIS_PER_SECOND); + return Math.addExact(totalMillis, (end.nanos - nanos) / 1000_000); + } + private long secondsUntil(Instant end) { long secsDiff = Math.subtractExact(end.seconds, seconds); long nanosDiff = end.nanos - nanos; diff --git a/src/java.base/share/classes/java/time/LocalTime.java b/src/java.base/share/classes/java/time/LocalTime.java index 0c419fe2f60..a6ac2fcf794 100644 --- a/src/java.base/share/classes/java/time/LocalTime.java +++ b/src/java.base/share/classes/java/time/LocalTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021, 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 @@ -182,14 +182,22 @@ public final class LocalTime * Seconds per day. */ static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; + /** + * Milliseconds per second. + */ + static final long MILLIS_PER_SECOND = 1000L; /** * Milliseconds per day. */ - static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L; + static final long MILLIS_PER_DAY = MILLIS_PER_SECOND * SECONDS_PER_DAY; + /** + * Microseconds per second. + */ + static final long MICROS_PER_SECOND = 1000_000L; /** * Microseconds per day. */ - static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L; + static final long MICROS_PER_DAY = MICROS_PER_SECOND * SECONDS_PER_DAY; /** * Nanos per millisecond. */ diff --git a/test/jdk/java/time/test/java/time/TestInstant.java b/test/jdk/java/time/test/java/time/TestInstant.java index 203bb579834..c6a3fb90683 100644 --- a/test/jdk/java/time/test/java/time/TestInstant.java +++ b/test/jdk/java/time/test/java/time/TestInstant.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021, 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 @@ -60,6 +60,7 @@ package test.java.time; import java.time.Instant; +import java.time.temporal.ChronoUnit; import org.testng.annotations.Test; import org.testng.annotations.DataProvider; @@ -67,6 +68,7 @@ /** * Test Instant. + * @bug 8273369 */ @Test public class TestInstant extends AbstractTest { @@ -96,4 +98,26 @@ public void test_epochMillis(String name, long millis) { assertEquals(millis, m, name); } + /** + * Checks whether Instant.until() returning microseconds does not throw + * an ArithmeticException for Instants apart for more than Long.MAX_VALUE + * nanoseconds. + */ + @Test + public void test_microsUntil() { + var nanoMax = Instant.EPOCH.plusNanos(Long.MAX_VALUE); + var totalMicros = Instant.EPOCH.until(nanoMax, ChronoUnit.MICROS); + var plusOneMicro = Instant.EPOCH.until(nanoMax.plusNanos(1000), ChronoUnit.MICROS); + assertEquals(plusOneMicro - totalMicros, 1L); + } + + /** + * Checks whether Instant.until() returning milliseconds does not throw + * an ArithmeticException for very large/small Instants + */ + @Test + public void test_millisUntil() { + assertEquals(Instant.MIN.until(Instant.MIN.plusSeconds(1), ChronoUnit.MILLIS), 1000L); + assertEquals(Instant.MAX.plusSeconds(-1).until(Instant.MAX, ChronoUnit.MILLIS), 1000L); + } } From 658eb7bddd67aba2345951ffaf2e739c8b67613b Mon Sep 17 00:00:00 2001 From: Roland Mesde Date: Tue, 18 Nov 2025 12:45:22 -0800 Subject: [PATCH 2/2] Reverted swtch statement (reviewer requested) --- .../share/classes/java/time/Instant.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/java.base/share/classes/java/time/Instant.java b/src/java.base/share/classes/java/time/Instant.java index 68a7bb73a3b..197ec120ef0 100644 --- a/src/java.base/share/classes/java/time/Instant.java +++ b/src/java.base/share/classes/java/time/Instant.java @@ -1145,17 +1145,17 @@ public Temporal adjustInto(Temporal temporal) { public long until(Temporal endExclusive, TemporalUnit unit) { Instant end = Instant.from(endExclusive); if (unit instanceof ChronoUnit chronoUnit) { - return switch (chronoUnit) { - case NANOS -> nanosUntil(end); - case MICROS -> microsUntil(end); - case MILLIS -> millisUntil(end); - case SECONDS -> secondsUntil(end); - case MINUTES -> secondsUntil(end) / SECONDS_PER_MINUTE; - case HOURS -> secondsUntil(end) / SECONDS_PER_HOUR; - case HALF_DAYS -> secondsUntil(end) / (12 * SECONDS_PER_HOUR); - case DAYS -> secondsUntil(end) / (SECONDS_PER_DAY); - default -> throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); - }; + switch (chronoUnit) { + case NANOS: return nanosUntil(end); + case MICROS: return nanosUntil(end) / 1000; + case MILLIS: return Math.subtractExact(end.toEpochMilli(), toEpochMilli()); + case SECONDS: return secondsUntil(end); + case MINUTES: return secondsUntil(end) / SECONDS_PER_MINUTE; + case HOURS: return secondsUntil(end) / SECONDS_PER_HOUR; + case HALF_DAYS: return secondsUntil(end) / (12 * SECONDS_PER_HOUR); + case DAYS: return secondsUntil(end) / (SECONDS_PER_DAY); + } + throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } return unit.between(this, end); }