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

View file

@ -61,6 +61,8 @@
*/ */
package java.time; 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.NANOS_PER_SECOND;
import static java.time.LocalTime.SECONDS_PER_DAY; import static java.time.LocalTime.SECONDS_PER_DAY;
import static java.time.LocalTime.SECONDS_PER_HOUR; import static java.time.LocalTime.SECONDS_PER_HOUR;
@ -1145,8 +1147,8 @@ public final class Instant
if (unit instanceof ChronoUnit chronoUnit) { if (unit instanceof ChronoUnit chronoUnit) {
return switch (chronoUnit) { return switch (chronoUnit) {
case NANOS -> nanosUntil(end); case NANOS -> nanosUntil(end);
case MICROS -> nanosUntil(end) / 1000; case MICROS -> microsUntil(end);
case MILLIS -> Math.subtractExact(end.toEpochMilli(), toEpochMilli()); case MILLIS -> millisUntil(end);
case SECONDS -> secondsUntil(end); case SECONDS -> secondsUntil(end);
case MINUTES -> secondsUntil(end) / SECONDS_PER_MINUTE; case MINUTES -> secondsUntil(end) / SECONDS_PER_MINUTE;
case HOURS -> secondsUntil(end) / SECONDS_PER_HOUR; case HOURS -> secondsUntil(end) / SECONDS_PER_HOUR;
@ -1164,6 +1166,18 @@ public final class Instant
return Math.addExact(totalNanos, end.nanos - nanos); 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) { private long secondsUntil(Instant end) {
long secsDiff = Math.subtractExact(end.seconds, seconds); long secsDiff = Math.subtractExact(end.seconds, seconds);
long nanosDiff = end.nanos - nanos; long nanosDiff = end.nanos - nanos;

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -182,14 +182,22 @@ public final class LocalTime
* Seconds per day. * Seconds per day.
*/ */
static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_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. * 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. * 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. * Nanos per millisecond.
*/ */

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -60,6 +60,7 @@
package test.java.time; package test.java.time;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import org.testng.annotations.DataProvider; import org.testng.annotations.DataProvider;
@ -67,6 +68,7 @@ import static org.testng.Assert.assertEquals;
/** /**
* Test Instant. * Test Instant.
* @bug 8273369
*/ */
@Test @Test
public class TestInstant extends AbstractTest { public class TestInstant extends AbstractTest {
@ -96,4 +98,26 @@ public class TestInstant extends AbstractTest {
assertEquals(millis, m, name); 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);
}
} }