FULL PRODUCT VERSION :
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b18, mixed mode)
A DESCRIPTION OF THE PROBLEM :
java.time.Duration.toNanos() and toMillis() throw an ArithmeticException on an intermediate calculation when the duration is very close to, but greater than Long.MIN_VALUE nanoseconds or milliseconds, respectively.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Duration.ofNanos(Long.MIN_VALUE).toNanos();
Duration.ofMillis(Long.MIN_VALUE).toMillis();
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected result from each statement is Long.MIN_VALUE.
ACTUAL -
Both the statements throw ArithmeticException.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public class Bug {
public static void main(String[] args) {
// uncomment following line for toNanos() bug
// Duration.ofNanos(Long.MIN_VALUE).toNanos();
Duration.ofMillis(Long.MIN_VALUE).toMillis();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
The following implementations of toNanos() and toMillis() fix the problem:
public long toMillis() {
long millis;
if (isNegative()) {
millis = Math.multiplyExact(seconds + 1, 1000);
millis = Math.addExact(millis, nanos / 1000_000 - 1000);
}
else {
millis = Math.multiplyExact(seconds, 1000);
millis = Math.addExact(millis, nanos / 1000_000);
}
return millis;
}
public long toNanos() {
long totalNanos;
if (isNegative()) {
totalNanos = Math.multiplyExact(seconds() + 1, NANOS_PER_SECOND);
totalNanos = Math.addExact(totalNanos, nanos - NANOS_PER_SECOND);
}
else {
totalNanos = Math.multiplyExact(seconds, NANOS_PER_SECOND);
totalNanos = Math.addExact(totalNanos, nanos);
}
return totalNanos;
}
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b18, mixed mode)
A DESCRIPTION OF THE PROBLEM :
java.time.Duration.toNanos() and toMillis() throw an ArithmeticException on an intermediate calculation when the duration is very close to, but greater than Long.MIN_VALUE nanoseconds or milliseconds, respectively.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Duration.ofNanos(Long.MIN_VALUE).toNanos();
Duration.ofMillis(Long.MIN_VALUE).toMillis();
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected result from each statement is Long.MIN_VALUE.
ACTUAL -
Both the statements throw ArithmeticException.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public class Bug {
public static void main(String[] args) {
// uncomment following line for toNanos() bug
// Duration.ofNanos(Long.MIN_VALUE).toNanos();
Duration.ofMillis(Long.MIN_VALUE).toMillis();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
The following implementations of toNanos() and toMillis() fix the problem:
public long toMillis() {
long millis;
if (isNegative()) {
millis = Math.multiplyExact(seconds + 1, 1000);
millis = Math.addExact(millis, nanos / 1000_000 - 1000);
}
else {
millis = Math.multiplyExact(seconds, 1000);
millis = Math.addExact(millis, nanos / 1000_000);
}
return millis;
}
public long toNanos() {
long totalNanos;
if (isNegative()) {
totalNanos = Math.multiplyExact(seconds() + 1, NANOS_PER_SECOND);
totalNanos = Math.addExact(totalNanos, nanos - NANOS_PER_SECOND);
}
else {
totalNanos = Math.multiplyExact(seconds, NANOS_PER_SECOND);
totalNanos = Math.addExact(totalNanos, nanos);
}
return totalNanos;
}