8273369: Computing micros between two instants unexpectedly overflows for some cases

Reviewed-by: lancea, rriggs, joehw
This commit is contained in:
Naoto Sato 2021-09-10 16:36:57 +00:00
parent efe3ed1e70
commit 81d2acee57
3 changed files with 52 additions and 6 deletions
src/java.base/share/classes/java/time
test/jdk/java/time/test/java/time

@ -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;
@ -1145,8 +1147,8 @@ public final class Instant
if (unit instanceof ChronoUnit chronoUnit) {
return switch (chronoUnit) {
case NANOS -> nanosUntil(end);
case MICROS -> nanosUntil(end) / 1000;
case MILLIS -> Math.subtractExact(end.toEpochMilli(), toEpochMilli());
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;
@ -1164,6 +1166,18 @@ public final class Instant
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;

@ -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.
*/

@ -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 @@ import static org.testng.Assert.assertEquals;
/**
* Test Instant.
* @bug 8273369
*/
@Test
public class TestInstant extends AbstractTest {
@ -96,4 +98,26 @@ public class TestInstant extends AbstractTest {
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);
}
}