Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8199095

java.time.Instant.until(x, ChronoUnit.MICROS) throws "long overflow" ArithmeticException, even when result fits into long

    Details

    • Subcomponent:
    • CPU:
      x86_64
    • OS:
      linux

      Description

      FULL PRODUCT VERSION :


      A DESCRIPTION OF THE PROBLEM :
      When calling instantA.until(instantB, ChronoUnit.MICROS) for time intervals where instantA.until(instantB, ChronoUnit.NANOS) would (correctly) overflow and throw an ArithmeticException, the MICROS variant throws, too, even when a 64-bit long integer is large enough to hold the result.

      Reason: until(x, MICROS) first calculates nanoseconds and then divides by 1000 to get microseconds.

      To wit:

          public long until(Temporal endExclusive, TemporalUnit unit) {
              Instant end = Instant.from(endExclusive);
              if (unit instanceof ChronoUnit) {
                  ChronoUnit f = (ChronoUnit) unit;
                  switch (f) {
                      case NANOS: return nanosUntil(end);
                      case MICROS: return nanosUntil(end) / 1000;


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      See provided source code.


      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      java.lang.ArithmeticException: long overflow
      at java.lang.Math.multiplyExact(Math.java:892)
      at java.time.Instant.nanosUntil(Instant.java:1164)
      at java.time.Instant.until(Instant.java:1149)


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      Instant start = ZonedDateTime.of(1, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC).toInstant();
      Instant end = ZonedDateTime.of(2018, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC).toInstant();
      long millis = start.until(end, ChronoUnit.MILLIS);
      long calculatedMicros = millis * 1000;
      System.out.println(millis);
      System.out.println(calculatedMicros);
      try {
          if (calculatedMicros > millis /* no overflow in multiplication above -> example start/end are fine */
                  && calculatedMicros != start.until(end, ChronoUnit.MICROS))
          {
              // we never get here
          }
      } catch (ArithmeticException ae)
      {
          System.out.println("Bug in Instant.until(x, ChronoUnit.MICROS: " + ae);
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      roll your own until()

          static final long MICROS_PER_SECOND = 1_000_000;
          static long microsUntil(Instant begin, Instant end) {
              long secsDiff = Math.subtractExact(end.getEpochSecond(), begin.getEpochSecond());
              long totalMicros = Math.multiplyExact(secsDiff, MICROS_PER_SECOND);
              return Math.addExact(totalMicros, (end.getNano() - begin.getNano()) / 1000);
          }


        Attachments

          Activity

            People

            • Assignee:
              naoto Naoto Sato
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated: