Skip to content
Permalink
Browse files
8273369: Computing micros between two instants unexpectedly overflows…
… for some cases

Reviewed-by: lancea, rriggs, joehw
  • Loading branch information
naotoj committed Sep 10, 2021
1 parent efe3ed1 commit 81d2acee57188a4507c798b46b0bd129dc302fec
@@ -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 long until(Temporal endExclusive, TemporalUnit unit) {
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 @@ 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;
@@ -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 @@
* 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,13 +60,15 @@
package test.java.time;

import java.time.Instant;
import java.time.temporal.ChronoUnit;

import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import static org.testng.Assert.assertEquals;

/**
* 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);
}
}

1 comment on commit 81d2ace

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on 81d2ace Sep 10, 2021

Please sign in to comment.