示例#1
0
  public static Duration parse(CharSequence text) {
    Objects.requireNonNull(text, "text");
    Matcher matcher = PATTERN.matcher(text);
    if (matcher.matches()) {

      if ("T".equals(matcher.group(3)) == false) {
        boolean negate = "-".equals(matcher.group(1));
        String dayMatch = matcher.group(2);
        String hourMatch = matcher.group(4);
        String minuteMatch = matcher.group(5);
        String secondMatch = matcher.group(6);
        String fractionMatch = matcher.group(7);
        if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) {
          long daysAsSecs = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days");
          long hoursAsSecs = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours");
          long minsAsSecs = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes");
          long seconds = parseNumber(text, secondMatch, 1, "seconds");
          int nanos = parseFraction(text, fractionMatch, seconds < 0 ? -1 : 1);
          try {
            return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos);
          } catch (ArithmeticException ex) {
            throw (DateTimeParseException)
                new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0)
                    .initCause(ex);
          }
        }
      }
    }
    throw new DateTimeParseException("Text cannot be parsed to a Duration", text, 0);
  }
示例#2
0
public final class Duration implements TemporalAmount, Comparable<Duration>, Serializable {

  public static final Duration ZERO = new Duration(0, 0);

  private static final long serialVersionUID = 3078945930695997490L;

  private static final BigInteger BI_NANOS_PER_SECOND = BigInteger.valueOf(NANOS_PER_SECOND);

  private static final Pattern PATTERN =
      Pattern.compile(
          "([-+]?)P(?:([-+]?[0-9]+)D)?"
              + "(T(?:([-+]?[0-9]+)H)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)(?:[.,]([0-9]{0,9}))?S)?)?",
          Pattern.CASE_INSENSITIVE);

  private final long seconds;

  private final int nanos;

  private Duration(long seconds, int nanos) {
    super();
    this.seconds = seconds;
    this.nanos = nanos;
  }

  public static Duration ofDays(long days) {
    return create(Math.multiplyExact(days, SECONDS_PER_DAY), 0);
  }

  public static Duration ofHours(long hours) {
    return create(Math.multiplyExact(hours, SECONDS_PER_HOUR), 0);
  }

  public static Duration ofMinutes(long minutes) {
    return create(Math.multiplyExact(minutes, SECONDS_PER_MINUTE), 0);
  }

  public static Duration ofSeconds(long seconds) {
    return create(seconds, 0);
  }

  public static Duration ofSeconds(long seconds, long nanoAdjustment) {
    long secs = Math.addExact(seconds, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND));
    int nos = (int) Math.floorMod(nanoAdjustment, NANOS_PER_SECOND);
    return create(secs, nos);
  }

  public static Duration ofMillis(long millis) {
    long secs = millis / 1000;
    int mos = (int) (millis % 1000);
    if (mos < 0) {
      mos += 1000;
      secs--;
    }
    return create(secs, mos * 1000_000);
  }

  public static Duration ofNanos(long nanos) {
    long secs = nanos / NANOS_PER_SECOND;
    int nos = (int) (nanos % NANOS_PER_SECOND);
    if (nos < 0) {
      nos += NANOS_PER_SECOND;
      secs--;
    }
    return create(secs, nos);
  }

  public static Duration of(long amount, TemporalUnit unit) {
    return ZERO.plus(amount, unit);
  }

  public static Duration from(TemporalAmount amount) {
    Objects.requireNonNull(amount, "amount");
    Duration duration = ZERO;
    for (TemporalUnit unit : amount.getUnits()) {
      duration = duration.plus(amount.get(unit), unit);
    }
    return duration;
  }

  public static Duration parse(CharSequence text) {
    Objects.requireNonNull(text, "text");
    Matcher matcher = PATTERN.matcher(text);
    if (matcher.matches()) {

      if ("T".equals(matcher.group(3)) == false) {
        boolean negate = "-".equals(matcher.group(1));
        String dayMatch = matcher.group(2);
        String hourMatch = matcher.group(4);
        String minuteMatch = matcher.group(5);
        String secondMatch = matcher.group(6);
        String fractionMatch = matcher.group(7);
        if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) {
          long daysAsSecs = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days");
          long hoursAsSecs = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours");
          long minsAsSecs = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes");
          long seconds = parseNumber(text, secondMatch, 1, "seconds");
          int nanos = parseFraction(text, fractionMatch, seconds < 0 ? -1 : 1);
          try {
            return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos);
          } catch (ArithmeticException ex) {
            throw (DateTimeParseException)
                new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0)
                    .initCause(ex);
          }
        }
      }
    }
    throw new DateTimeParseException("Text cannot be parsed to a Duration", text, 0);
  }

  private static long parseNumber(
      CharSequence text, String parsed, int multiplier, String errorText) {

    if (parsed == null) {
      return 0;
    }
    try {
      long val = Long.parseLong(parsed);
      return Math.multiplyExact(val, multiplier);
    } catch (NumberFormatException | ArithmeticException ex) {
      throw (DateTimeParseException)
          new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0)
              .initCause(ex);
    }
  }

  private static int parseFraction(CharSequence text, String parsed, int negate) {

    if (parsed == null || parsed.length() == 0) {
      return 0;
    }
    try {
      parsed = (parsed + "000000000").substring(0, 9);
      return Integer.parseInt(parsed) * negate;
    } catch (NumberFormatException | ArithmeticException ex) {
      throw (DateTimeParseException)
          new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0)
              .initCause(ex);
    }
  }

  private static Duration create(
      boolean negate, long daysAsSecs, long hoursAsSecs, long minsAsSecs, long secs, int nanos) {
    long seconds =
        Math.addExact(daysAsSecs, Math.addExact(hoursAsSecs, Math.addExact(minsAsSecs, secs)));
    if (negate) {
      return ofSeconds(seconds, nanos).negated();
    }
    return ofSeconds(seconds, nanos);
  }

  public static Duration between(Temporal startInclusive, Temporal endExclusive) {
    try {
      return ofNanos(startInclusive.until(endExclusive, NANOS));
    } catch (DateTimeException | ArithmeticException ex) {
      long secs = startInclusive.until(endExclusive, SECONDS);
      long nanos;
      try {
        nanos = endExclusive.getLong(NANO_OF_SECOND) - startInclusive.getLong(NANO_OF_SECOND);
        if (secs > 0 && nanos < 0) {
          secs++;
        } else if (secs < 0 && nanos > 0) {
          secs--;
        }
      } catch (DateTimeException ex2) {
        nanos = 0;
      }
      return ofSeconds(secs, nanos);
    }
  }

  private static Duration create(long seconds, int nanoAdjustment) {
    if ((seconds | nanoAdjustment) == 0) {
      return ZERO;
    }
    return new Duration(seconds, nanoAdjustment);
  }

  private static Duration create(BigDecimal seconds) {
    BigInteger nanos = seconds.movePointRight(9).toBigIntegerExact();
    BigInteger[] divRem = nanos.divideAndRemainder(BI_NANOS_PER_SECOND);
    if (divRem[0].bitLength() > 63) {
      throw new ArithmeticException("Exceeds capacity of Duration: " + nanos);
    }
    return ofSeconds(divRem[0].longValue(), divRem[1].intValue());
  }

  static Duration readExternal(DataInput in) throws IOException {
    long seconds = in.readLong();
    int nanos = in.readInt();
    return Duration.ofSeconds(seconds, nanos);
  }

  @Override
  public long get(TemporalUnit unit) {
    if (unit == SECONDS) {
      return seconds;
    } else if (unit == NANOS) {
      return nanos;
    } else {
      throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
    }
  }

  @Override
  public List<TemporalUnit> getUnits() {
    return DurationUnits.UNITS;
  }

  public boolean isZero() {
    return (seconds | nanos) == 0;
  }

  public boolean isNegative() {
    return seconds < 0;
  }

  public long getSeconds() {
    return seconds;
  }

  public int getNano() {
    return nanos;
  }

  public Duration withSeconds(long seconds) {
    return create(seconds, nanos);
  }

  public Duration withNanos(int nanoOfSecond) {
    NANO_OF_SECOND.checkValidIntValue(nanoOfSecond);
    return create(seconds, nanoOfSecond);
  }

  public Duration plus(Duration duration) {
    return plus(duration.getSeconds(), duration.getNano());
  }

  public Duration plus(long amountToAdd, TemporalUnit unit) {
    Objects.requireNonNull(unit, "unit");
    if (unit == DAYS) {
      return plus(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY), 0);
    }
    if (unit.isDurationEstimated()) {
      throw new UnsupportedTemporalTypeException("Unit must not have an estimated duration");
    }
    if (amountToAdd == 0) {
      return this;
    }
    if (unit instanceof ChronoUnit) {
      switch ((ChronoUnit) unit) {
        case NANOS:
          return plusNanos(amountToAdd);
        case MICROS:
          return plusSeconds((amountToAdd / (1000_000L * 1000)) * 1000)
              .plusNanos((amountToAdd % (1000_000L * 1000)) * 1000);
        case MILLIS:
          return plusMillis(amountToAdd);
        case SECONDS:
          return plusSeconds(amountToAdd);
      }
      return plusSeconds(Math.multiplyExact(unit.getDuration().seconds, amountToAdd));
    }
    Duration duration = unit.getDuration().multipliedBy(amountToAdd);
    return plusSeconds(duration.getSeconds()).plusNanos(duration.getNano());
  }

  public Duration plusDays(long daysToAdd) {
    return plus(Math.multiplyExact(daysToAdd, SECONDS_PER_DAY), 0);
  }

  public Duration plusHours(long hoursToAdd) {
    return plus(Math.multiplyExact(hoursToAdd, SECONDS_PER_HOUR), 0);
  }

  public Duration plusMinutes(long minutesToAdd) {
    return plus(Math.multiplyExact(minutesToAdd, SECONDS_PER_MINUTE), 0);
  }

  public Duration plusSeconds(long secondsToAdd) {
    return plus(secondsToAdd, 0);
  }

  public Duration plusMillis(long millisToAdd) {
    return plus(millisToAdd / 1000, (millisToAdd % 1000) * 1000_000);
  }

  public Duration plusNanos(long nanosToAdd) {
    return plus(0, nanosToAdd);
  }

  private Duration plus(long secondsToAdd, long nanosToAdd) {
    if ((secondsToAdd | nanosToAdd) == 0) {
      return this;
    }
    long epochSec = Math.addExact(seconds, secondsToAdd);
    epochSec = Math.addExact(epochSec, nanosToAdd / NANOS_PER_SECOND);
    nanosToAdd = nanosToAdd % NANOS_PER_SECOND;
    long nanoAdjustment = nanos + nanosToAdd;

    return ofSeconds(epochSec, nanoAdjustment);
  }

  public Duration minus(Duration duration) {
    long secsToSubtract = duration.getSeconds();
    int nanosToSubtract = duration.getNano();
    if (secsToSubtract == Long.MIN_VALUE) {
      return plus(Long.MAX_VALUE, -nanosToSubtract).plus(1, 0);
    }
    return plus(-secsToSubtract, -nanosToSubtract);
  }

  public Duration minus(long amountToSubtract, TemporalUnit unit) {
    return (amountToSubtract == Long.MIN_VALUE
        ? plus(Long.MAX_VALUE, unit).plus(1, unit)
        : plus(-amountToSubtract, unit));
  }

  public Duration minusDays(long daysToSubtract) {
    return (daysToSubtract == Long.MIN_VALUE
        ? plusDays(Long.MAX_VALUE).plusDays(1)
        : plusDays(-daysToSubtract));
  }

  public Duration minusHours(long hoursToSubtract) {
    return (hoursToSubtract == Long.MIN_VALUE
        ? plusHours(Long.MAX_VALUE).plusHours(1)
        : plusHours(-hoursToSubtract));
  }

  public Duration minusMinutes(long minutesToSubtract) {
    return (minutesToSubtract == Long.MIN_VALUE
        ? plusMinutes(Long.MAX_VALUE).plusMinutes(1)
        : plusMinutes(-minutesToSubtract));
  }

  public Duration minusSeconds(long secondsToSubtract) {
    return (secondsToSubtract == Long.MIN_VALUE
        ? plusSeconds(Long.MAX_VALUE).plusSeconds(1)
        : plusSeconds(-secondsToSubtract));
  }

  public Duration minusMillis(long millisToSubtract) {
    return (millisToSubtract == Long.MIN_VALUE
        ? plusMillis(Long.MAX_VALUE).plusMillis(1)
        : plusMillis(-millisToSubtract));
  }

  public Duration minusNanos(long nanosToSubtract) {
    return (nanosToSubtract == Long.MIN_VALUE
        ? plusNanos(Long.MAX_VALUE).plusNanos(1)
        : plusNanos(-nanosToSubtract));
  }

  public Duration multipliedBy(long multiplicand) {
    if (multiplicand == 0) {
      return ZERO;
    }
    if (multiplicand == 1) {
      return this;
    }
    return create(toSeconds().multiply(BigDecimal.valueOf(multiplicand)));
  }

  public Duration dividedBy(long divisor) {
    if (divisor == 0) {
      throw new ArithmeticException("Cannot divide by zero");
    }
    if (divisor == 1) {
      return this;
    }
    return create(toSeconds().divide(BigDecimal.valueOf(divisor), RoundingMode.DOWN));
  }

  private BigDecimal toSeconds() {
    return BigDecimal.valueOf(seconds).add(BigDecimal.valueOf(nanos, 9));
  }

  public Duration negated() {
    return multipliedBy(-1);
  }

  public Duration abs() {
    return isNegative() ? negated() : this;
  }

  @Override
  public Temporal addTo(Temporal temporal) {
    if (seconds != 0) {
      temporal = temporal.plus(seconds, SECONDS);
    }
    if (nanos != 0) {
      temporal = temporal.plus(nanos, NANOS);
    }
    return temporal;
  }

  @Override
  public Temporal subtractFrom(Temporal temporal) {
    if (seconds != 0) {
      temporal = temporal.minus(seconds, SECONDS);
    }
    if (nanos != 0) {
      temporal = temporal.minus(nanos, NANOS);
    }
    return temporal;
  }

  public long toDays() {
    return seconds / SECONDS_PER_DAY;
  }

  public long toHours() {
    return seconds / SECONDS_PER_HOUR;
  }

  public long toMinutes() {
    return seconds / SECONDS_PER_MINUTE;
  }

  public long toMillis() {
    long millis = Math.multiplyExact(seconds, 1000);
    millis = Math.addExact(millis, nanos / 1000_000);
    return millis;
  }

  public long toNanos() {
    long totalNanos = Math.multiplyExact(seconds, NANOS_PER_SECOND);
    totalNanos = Math.addExact(totalNanos, nanos);
    return totalNanos;
  }

  @Override
  public int compareTo(Duration otherDuration) {
    int cmp = Long.compare(seconds, otherDuration.seconds);
    if (cmp != 0) {
      return cmp;
    }
    return nanos - otherDuration.nanos;
  }

  @Override
  public boolean equals(Object otherDuration) {
    if (this == otherDuration) {
      return true;
    }
    if (otherDuration instanceof Duration) {
      Duration other = (Duration) otherDuration;
      return this.seconds == other.seconds && this.nanos == other.nanos;
    }
    return false;
  }

  @Override
  public int hashCode() {
    return ((int) (seconds ^ (seconds >>> 32))) + (51 * nanos);
  }

  @Override
  public String toString() {
    if (this == ZERO) {
      return "PT0S";
    }
    long hours = seconds / SECONDS_PER_HOUR;
    int minutes = (int) ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
    int secs = (int) (seconds % SECONDS_PER_MINUTE);
    StringBuilder buf = new StringBuilder(24);
    buf.append("PT");
    if (hours != 0) {
      buf.append(hours).append('H');
    }
    if (minutes != 0) {
      buf.append(minutes).append('M');
    }
    if (secs == 0 && nanos == 0 && buf.length() > 2) {
      return buf.toString();
    }
    if (secs < 0 && nanos > 0) {
      if (secs == -1) {
        buf.append("-0");
      } else {
        buf.append(secs + 1);
      }
    } else {
      buf.append(secs);
    }
    if (nanos > 0) {
      int pos = buf.length();
      if (secs < 0) {
        buf.append(2 * NANOS_PER_SECOND - nanos);
      } else {
        buf.append(nanos + NANOS_PER_SECOND);
      }
      while (buf.charAt(buf.length() - 1) == '0') {
        buf.setLength(buf.length() - 1);
      }
      buf.setCharAt(pos, '.');
    }
    buf.append('S');
    return buf.toString();
  }

  private Object writeReplace() {
    return new Ser(Ser.DURATION_TYPE, this);
  }

  private void readObject(ObjectInputStream s) throws InvalidObjectException {
    throw new InvalidObjectException("Deserialization via serialization delegate");
  }

  void writeExternal(DataOutput out) throws IOException {
    out.writeLong(seconds);
    out.writeInt(nanos);
  }

  private static class DurationUnits {
    static final List<TemporalUnit> UNITS =
        Collections.unmodifiableList(Arrays.<TemporalUnit>asList(SECONDS, NANOS));
  }
}