All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.threeten.bp.Duration.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  * Neither the name of JSR-310 nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.threeten.bp

import java.io.Serializable
import java.math.BigDecimal
import java.math.BigInteger
import java.math.RoundingMode
import java.util.{ Arrays, Collections, Objects }
import java.util.regex.Matcher
import java.util.regex.Pattern

import org.threeten.bp.LocalTime.SECONDS_PER_DAY
import org.threeten.bp.LocalTime.SECONDS_PER_HOUR
import org.threeten.bp.LocalTime.SECONDS_PER_MINUTE
import org.threeten.bp.format.DateTimeParseException
import org.threeten.bp.temporal.ChronoField.NANO_OF_SECOND
import org.threeten.bp.temporal.ChronoUnit
import org.threeten.bp.temporal.ChronoUnit.NANOS
import org.threeten.bp.temporal.ChronoUnit.MICROS
import org.threeten.bp.temporal.ChronoUnit.MILLIS
import org.threeten.bp.temporal.ChronoUnit.SECONDS
import org.threeten.bp.temporal.ChronoUnit.DAYS
import org.threeten.bp.temporal.Temporal
import org.threeten.bp.temporal.TemporalAmount
import org.threeten.bp.temporal.TemporalUnit
import org.threeten.bp.temporal.UnsupportedTemporalTypeException

object Duration {

  /** Constant for a duration of zero. */
  lazy val ZERO: Duration = new Duration(0, 0)

  /** Constant for nanos per second. */
  private def NANOS_PER_SECOND: Int = 1000000000

  /** Constant for nanos per milli. */
  private def NANOS_PER_MILLI: Int = 1000000

  /** Constant for nanos per second. */
  private def BI_NANOS_PER_SECOND: BigInteger = BigInteger.valueOf(NANOS_PER_SECOND.toLong)

  /** The pattern for parsing. */
  private def 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
    )

  /**
   * Obtains an instance of {@code Duration} from a number of standard 24 hour days.
   *
   * The seconds are calculated based on the standard definition of a day, where each day is 86400
   * seconds which implies a 24 hour day. The nanosecond in second field is set to zero.
   *
   * @param days
   *   the number of days, positive or negative
   * @return
   *   a { @code Duration}, not null
   * @throws ArithmeticException
   *   if the input days exceeds the capacity of { @code Duration}
   */
  def ofDays(days: Long): Duration = create(Math.multiplyExact(days, 86400L), 0)

  /**
   * Obtains an instance of {@code Duration} from a number of standard length hours.
   *
   * The seconds are calculated based on the standard definition of an hour, where each hour is 3600
   * seconds. The nanosecond in second field is set to zero.
   *
   * @param hours
   *   the number of hours, positive or negative
   * @return
   *   a { @code Duration}, not null
   * @throws ArithmeticException
   *   if the input hours exceeds the capacity of { @code Duration}
   */
  def ofHours(hours: Long): Duration = create(Math.multiplyExact(hours, 3600L), 0)

  /**
   * Obtains an instance of {@code Duration} from a number of standard length minutes.
   *
   * The seconds are calculated based on the standard definition of a minute, where each minute is
   * 60 seconds. The nanosecond in second field is set to zero.
   *
   * @param minutes
   *   the number of minutes, positive or negative
   * @return
   *   a { @code Duration}, not null
   * @throws ArithmeticException
   *   if the input minutes exceeds the capacity of { @code Duration}
   */
  def ofMinutes(minutes: Long): Duration = create(Math.multiplyExact(minutes, 60L), 0)

  /**
   * Obtains an instance of {@code Duration} from a number of seconds.
   *
   * The nanosecond in second field is set to zero.
   *
   * @param seconds
   *   the number of seconds, positive or negative
   * @return
   *   a { @code Duration}, not null
   */
  def ofSeconds(seconds: Long): Duration = create(seconds, 0)

  /**
   * Obtains an instance of {@code Duration} from a number of seconds and an adjustment in
   * nanoseconds.
   *
   * This method allows an arbitrary number of nanoseconds to be passed in. The factory will alter
   * the values of the second and nanosecond in order to ensure that the stored nanosecond is in the
   * range 0 to 999,999,999. For example, the following will result in the exactly the same
   * duration: 
 Duration.ofSeconds(3, 1); Duration.ofSeconds(4, -999_999_999);
   * Duration.ofSeconds(2, 1000_000_001); 
* * @param seconds * the number of seconds, positive or negative * @param nanoAdjustment * the nanosecond adjustment to the number of seconds, positive or negative * @return * a { @code Duration}, not null * @throws ArithmeticException * if the adjustment causes the seconds to exceed the capacity of { @code Duration} */ def ofSeconds(seconds: Long, nanoAdjustment: Long): Duration = { val secs: Long = Math.addExact(seconds, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND.toLong)) val nos: Int = Math.floorMod(nanoAdjustment, NANOS_PER_SECOND.toLong).toInt create(secs, nos) } /** * Obtains an instance of {@code Duration} from a number of milliseconds. * * The seconds and nanoseconds are extracted from the specified milliseconds. * * @param millis * the number of milliseconds, positive or negative * @return * a { @code Duration}, not null */ def ofMillis(millis: Long): Duration = { var secs: Long = millis / 1000 var mos: Int = (millis % 1000).toInt if (mos < 0) { mos += 1000 secs -= 1 } create(secs, mos * NANOS_PER_MILLI) } /** * Obtains an instance of {@code Duration} from a number of nanoseconds. * * The seconds and nanoseconds are extracted from the specified nanoseconds. * * @param nanos * the number of nanoseconds, positive or negative * @return * a { @code Duration}, not null */ def ofNanos(nanos: Long): Duration = { var secs: Long = nanos / NANOS_PER_SECOND var nos: Int = (nanos % NANOS_PER_SECOND).toInt if (nos < 0) { nos += NANOS_PER_SECOND secs -= 1 } create(secs, nos) } /** * Obtains an instance of {@code Duration} from a duration in the specified unit. * * The parameters represent the two parts of a phrase like '6 Hours'. For example:
   * Duration.of(3, SECONDS); Duration.of(465, HOURS); 
Only a subset of units are accepted by * this method. The unit must either have an {@link TemporalUnit#isDurationEstimated() exact * duration} or be {@link ChronoUnit#DAYS} which is treated as 24 hours. Other units throw an * exception. * * @param amount * the amount of the duration, measured in terms of the unit, positive or negative * @param unit * the unit that the duration is measured in, must have an exact duration, not null * @return * a { @code Duration}, not null * @throws DateTimeException * if the period unit has an estimated duration * @throws ArithmeticException * if a numeric overflow occurs */ def of(amount: Long, unit: TemporalUnit): Duration = ZERO.plus(amount, unit) /** * Obtains an instance of {@code Duration} from an amount. * * This obtains a duration based on the specified amount. A TemporalAmount represents an amount of * time, which may be date-based or time-based, which this factory extracts to a duration. * * The conversion loops around the set of units from the amount and uses the duration of the unit * to calculate the total Duration. Only a subset of units are accepted by this method. The unit * must either have an exact duration or be ChronoUnit.DAYS which is treated as 24 hours. If any * other units are found then an exception is thrown. * * @param amount * the amount to convert, not null * @return * a { @code Duration}, not null * @throws DateTimeException * if the amount cannot be converted * @throws ArithmeticException * if a numeric overflow occurs */ def from(amount: TemporalAmount): Duration = { Objects.requireNonNull(amount, "amount") var duration: Duration = ZERO val units = amount.getUnits.iterator while (units.hasNext) { val unit = units.next() duration = duration.plus(amount.get(unit), unit) } duration } /** * Obtains an instance of {@code Duration} representing the duration between two instants. * * Obtains a {@code Duration} representing the duration between two instants. This calculates the * duration between two temporal objects of the same type. The difference in seconds is calculated * using {@link Temporal#until(Temporal, TemporalUnit)}. The difference in nanoseconds is * calculated using by querying the {@link ChronoField#NANO_OF_SECOND NANO_OF_SECOND} field. * * The result of this method can be a negative period if the end is before the start. To guarantee * to obtain a positive duration call abs() on the result. * * @param startInclusive * the start instant, inclusive, not null * @param endExclusive * the end instant, exclusive, not null * @return * a { @code Duration}, not null * @throws DateTimeException * if the seconds between the temporals cannot be obtained * @throws ArithmeticException * if the calculation exceeds the capacity of { @code Duration} */ def between(startInclusive: Temporal, endExclusive: Temporal): Duration = { var secs: Long = startInclusive.until(endExclusive, SECONDS) var nanos: Long = 0 if (startInclusive.isSupported(NANO_OF_SECOND) && endExclusive.isSupported(NANO_OF_SECOND)) try { val startNos: Long = startInclusive.getLong(NANO_OF_SECOND) nanos = endExclusive.getLong(NANO_OF_SECOND) - startNos if (secs > 0 && nanos < 0) nanos += NANOS_PER_SECOND else if (secs < 0 && nanos > 0) nanos -= NANOS_PER_SECOND else if (secs == 0 && nanos != 0) { val adjustedEnd: Temporal = endExclusive.`with`(NANO_OF_SECOND, startNos) secs = startInclusive.until(adjustedEnd, SECONDS) } } catch { case _: DateTimeException => case _: ArithmeticException => } ofSeconds(secs, nanos) } /** * Obtains a {@code Duration} from a text string such as {@code PnDTnHnMn.nS}. * * This will parse a textual representation of a duration, including the string produced by {@code * toString()}. The formats accepted are based on the ISO-8601 duration format {@code * PnDTnHnMn.nS} with days considered to be exactly 24 hours. * * The string starts with an optional sign, denoted by the ASCII negative or positive symbol. If * negative, the whole period is negated. The ASCII letter "P" is next in upper or lower case. * There are then four sections, each consisting of a number and a suffix. The sections have * suffixes in ASCII of "D", "H", "M" and "S" for days, hours, minutes and seconds, accepted in * upper or lower case. The suffixes must occur in order. The ASCII letter "T" must occur before * the first occurrence, if any, of an hour, minute or second section. At least one of the four * sections must be present, and if "T" is present there must be at least one section after the * "T". The number part of each section must consist of one or more ASCII digits. The number may * be prefixed by the ASCII negative or positive symbol. The number of days, hours and minutes * must parse to a {@code long}. The number of seconds must parse to a {@code long} with optional * fraction. The decimal point may be either a dot or a comma. The fractional part may have from * zero to 9 digits. * * The leading plus/minus sign, and negative values for other units are not part of the ISO-8601 * standard. * * Examples:
 "PT20.345S" -> parses as "20.345 seconds" "PT15M" -> parses as "15 minutes"
   * (where a minute is 60 seconds) "PT10H" -> parses as "10 hours" (where an hour is 3600 seconds)
   * "P2D" -> parses as "2 days" (where a day is 24 hours or 86400 seconds) "P2DT3H4M" -> parses as
   * "2 days, 3 hours and 4 minutes" "P-6H3M" -> parses as "-6 hours and +3 minutes" "-P6H3M" ->
   * parses as "-6 hours and -3 minutes" "-P-6H+3M" -> parses as "+6 hours and -3 minutes" 
* * @param text * the text to parse, not null * @return * the parsed duration, not null * @throws DateTimeParseException * if the text cannot be parsed to a duration */ def parse(text: CharSequence): Duration = { Objects.requireNonNull(text, "text") val matcher: Matcher = PATTERN.matcher(text) if (matcher.matches) if (!("T" == matcher.group(3))) { val negate: Boolean = "-" == matcher.group(1) val dayMatch: String = matcher.group(2) val hourMatch: String = matcher.group(4) val minuteMatch: String = matcher.group(5) val secondMatch: String = matcher.group(6) val fractionMatch: String = matcher.group(7) if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) { val daysAsSecs: Long = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days") val hoursAsSecs: Long = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours") val minsAsSecs: Long = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes") val seconds: Long = parseNumber(text, secondMatch, 1, "seconds") val negativeSecs: Boolean = secondMatch != null && secondMatch.charAt(0) == '-' val nanos: Int = parseFraction(text, fractionMatch, if (negativeSecs) -1 else 1) try return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos) catch { case ex: ArithmeticException => throw new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0, ex ) } } } throw new DateTimeParseException("Text cannot be parsed to a Duration", text, 0) } private def parseNumber( text: CharSequence, parsed: String, multiplier: Int, errorText: String ): Long = { var _parsed = parsed if (_parsed == null) return 0 try { if (_parsed.startsWith("+")) _parsed = _parsed.substring(1) val `val`: Long = java.lang.Long.parseLong(_parsed) Math.multiplyExact(`val`, multiplier.toLong) } catch { case ex: NumberFormatException => throw new DateTimeParseException(s"Text cannot be parsed to a Duration: $errorText", text, 0, ex ) case ex: ArithmeticException => throw new DateTimeParseException(s"Text cannot be parsed to a Duration: $errorText", text, 0, ex ) } } private def parseFraction(text: CharSequence, parsed: String, negate: Int): Int = { var _parsed = parsed if (_parsed == null || _parsed.length == 0) return 0 try { _parsed = (_parsed + "000000000").substring(0, 9) _parsed.toInt * negate } catch { case ex: NumberFormatException => throw new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0, ex ) case ex: ArithmeticException => throw new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0, ex ) } } private def create( negate: Boolean, daysAsSecs: Long, hoursAsSecs: Long, minsAsSecs: Long, secs: Long, nanos: Int ): Duration = { val seconds: Long = Math.addExact(daysAsSecs, Math.addExact(hoursAsSecs, Math.addExact(minsAsSecs, secs))) if (negate) ofSeconds(seconds, nanos.toLong).negated else ofSeconds(seconds, nanos.toLong) } /** * Obtains an instance of {@code Duration} using seconds and nanoseconds. * * @param seconds * the length of the duration in seconds, positive or negative * @param nanoAdjustment * the nanosecond adjustment within the second, from 0 to 999,999,999 */ private def create(seconds: Long, nanoAdjustment: Int): Duration = if ((seconds | nanoAdjustment) == 0) ZERO else new Duration(seconds, nanoAdjustment) /** * Creates an instance of {@code Duration} from a number of seconds. * * @param seconds * the number of seconds, up to scale 9, positive or negative * @return * a { @code Duration}, not null * @throws ArithmeticException * if numeric overflow occurs */ private def create(seconds: BigDecimal): Duration = { val nanos: BigInteger = seconds.movePointRight(9).toBigIntegerExact val divRem: Array[BigInteger] = nanos.divideAndRemainder(BI_NANOS_PER_SECOND) if (divRem(0).bitLength > 63) throw new ArithmeticException(s"Exceeds capacity of Duration: $nanos") ofSeconds(divRem(0).longValue, divRem(1).longValue) } } /** * A time-based amount of time, such as '34.5 seconds'. * * This class models a quantity or amount of time in terms of seconds and nanoseconds. It can be * accessed using other duration-based units, such as minutes and hours. In addition, the {@link * ChronoUnit#DAYS DAYS} unit can be used and is treated as exactly equal to 24 hours, thus ignoring * daylight savings effects. See {@link Period} for the date-based equivalent to this class. * * A physical duration could be of infinite length. For practicality, the duration is stored with * constraints similar to {@link Instant}. The duration uses nanosecond resolution with a maximum * value of the seconds that can be held in a {@code long}. This is greater than the current * estimated age of the universe. * * The range of a duration requires the storage of a number larger than a {@code long}. To achieve * this, the class stores a {@code long} representing seconds and an {@code int} representing * nanosecond-of-second, which will always be between 0 and 999,999,999. * * The duration is measured in "seconds", but these are not necessarily identical to the scientific * "SI second" definition based on atomic clocks. This difference only impacts durations measured * near a leap-second and should not affect most applications. See {@link Instant} for a discussion * as to the meaning of the second and time-scales. * *

Specification for implementors

This class is immutable and thread-safe. * * Constructs an instance of {@code Duration} using seconds and nanoseconds. * * @param seconds * the length of the duration in seconds, positive or negative * @param nanos * the nanoseconds within the second, from 0 to 999,999,999 */ @SerialVersionUID(3078945930695997490L) final class Duration private (private val seconds: Long, private val nanos: Int) extends TemporalAmount with Ordered[Duration] with Serializable { def getUnits: java.util.List[TemporalUnit] = Collections.unmodifiableList[TemporalUnit](Arrays.asList(SECONDS, NANOS)) def get(unit: TemporalUnit): Long = if (unit eq SECONDS) seconds else if (unit eq NANOS) nanos.toLong else throw new UnsupportedTemporalTypeException(s"Unsupported unit: $unit") /** * Checks if this duration is zero length. * * A {@code Duration} represents a directed distance between two points on the time-line and can * therefore be positive, zero or negative. This method checks whether the length is zero. * * @return * true if this duration has a total length equal to zero */ def isZero: Boolean = (seconds | nanos) == 0 /** * Checks if this duration is negative, excluding zero. * * A {@code Duration} represents a directed distance between two points on the time-line and can * therefore be positive, zero or negative. This method checks whether the length is less than * zero. * * @return * true if this duration has a total length less than zero */ def isNegative: Boolean = seconds < 0 /** * Gets the number of seconds in this duration. * * The length of the duration is stored using two fields - seconds and nanoseconds. The * nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to the length in * seconds. The total duration is defined by calling this method and {@link #getNano()}. * * A {@code Duration} represents a directed distance between two points on the time-line. A * negative duration is expressed by the negative sign of the seconds part. A duration of -1 * nanosecond is stored as -1 seconds plus 999,999,999 nanoseconds. * * @return * the whole seconds part of the length of the duration, positive or negative */ def getSeconds: Long = seconds /** * Gets the number of nanoseconds within the second in this duration. * * The length of the duration is stored using two fields - seconds and nanoseconds. The * nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to the length in * seconds. The total duration is defined by calling this method and {@link #getSeconds()}. * * A {@code Duration} represents a directed distance between two points on the time-line. A * negative duration is expressed by the negative sign of the seconds part. A duration of -1 * nanosecond is stored as -1 seconds plus 999,999,999 nanoseconds. * * @return * the nanoseconds within the second part of the length of the duration, from 0 to 999,999,999 */ def getNano: Int = nanos /** * Returns a copy of this duration with the specified amount of seconds. * * This returns a duration with the specified seconds, retaining the nano-of-second part of this * duration. * * This instance is immutable and unaffected by this method call. * * @param seconds * the seconds to represent, may be negative * @return * a { @code Duration} based on this period with the requested seconds, not null */ def withSeconds(seconds: Long): Duration = Duration.create(seconds, nanos) /** * Returns a copy of this duration with the specified nano-of-second. * * This returns a duration with the specified nano-of-second, retaining the seconds part of this * duration. * * This instance is immutable and unaffected by this method call. * * @param nanoOfSecond * the nano-of-second to represent, from 0 to 999,999,999 * @return * a { @code Duration} based on this period with the requested nano-of-second, not null * @throws DateTimeException * if the nano-of-second is invalid */ def withNanos(nanoOfSecond: Int): Duration = { NANO_OF_SECOND.checkValidIntValue(nanoOfSecond.toLong) Duration.create(seconds, nanoOfSecond) } /** * Returns a copy of this duration with the specified duration added. * * This instance is immutable and unaffected by this method call. * * @param duration * the duration to add, positive or negative, not null * @return * a { @code Duration} based on this duration with the specified duration added, not null * @throws ArithmeticException * if numeric overflow occurs */ def plus(duration: Duration): Duration = plus(duration.getSeconds, duration.getNano.toLong) /** * Returns a copy of this duration with the specified duration added. * * The duration amount is measured in terms of the specified unit. Only a subset of units are * accepted by this method. The unit must either have an {@link TemporalUnit#isDurationEstimated() * exact duration} or be {@link ChronoUnit#DAYS} which is treated as 24 hours. Other units throw * an exception. * * This instance is immutable and unaffected by this method call. * * @param amountToAdd * the amount of the period, measured in terms of the unit, positive or negative * @param unit * the unit that the period is measured in, must have an exact duration, not null * @return * a { @code Duration} based on this duration with the specified duration added, not null * @throws ArithmeticException * if numeric overflow occurs */ def plus(amountToAdd: Long, unit: TemporalUnit): Duration = { Objects.requireNonNull(unit, "unit") if (unit eq DAYS) return plus(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY.toLong), 0) if (unit.isDurationEstimated) throw new DateTimeException("Unit must not have an estimated duration") if (amountToAdd == 0) return this unit match { case u: ChronoUnit => u match { case NANOS => plusNanos(amountToAdd) case MICROS => plusSeconds((amountToAdd / (1000000L * 1000)) * 1000) .plusNanos((amountToAdd % (1000000L * 1000)) * 1000) case MILLIS => plusMillis(amountToAdd) case SECONDS => plusSeconds(amountToAdd) case _ => plusSeconds(Math.multiplyExact(unit.getDuration.seconds, amountToAdd)) } case _ => val duration: Duration = unit.getDuration.multipliedBy(amountToAdd) plusSeconds(duration.getSeconds).plusNanos(duration.getNano.toLong) } } /** * Returns a copy of this duration with the specified duration in 24 hour days added. * * This instance is immutable and unaffected by this method call. * * @param daysToAdd * the days to add, positive or negative * @return * a { @code Duration} based on this duration with the specified days added, not null * @throws ArithmeticException * if numeric overflow occurs */ def plusDays(daysToAdd: Long): Duration = plus(Math.multiplyExact(daysToAdd, SECONDS_PER_DAY.toLong), 0) /** * Returns a copy of this duration with the specified duration in hours added. * * This instance is immutable and unaffected by this method call. * * @param hoursToAdd * the hours to add, positive or negative * @return * a { @code Duration} based on this duration with the specified hours added, not null * @throws ArithmeticException * if numeric overflow occurs */ def plusHours(hoursToAdd: Long): Duration = plus(Math.multiplyExact(hoursToAdd, SECONDS_PER_HOUR.toLong), 0) /** * Returns a copy of this duration with the specified duration in minutes added. * * This instance is immutable and unaffected by this method call. * * @param minutesToAdd * the minutes to add, positive or negative * @return * a { @code Duration} based on this duration with the specified minutes added, not null * @throws ArithmeticException * if numeric overflow occurs */ def plusMinutes(minutesToAdd: Long): Duration = plus(Math.multiplyExact(minutesToAdd, SECONDS_PER_MINUTE.toLong), 0) /** * Returns a copy of this duration with the specified duration in seconds added. * * This instance is immutable and unaffected by this method call. * * @param secondsToAdd * the seconds to add, positive or negative * @return * a { @code Duration} based on this duration with the specified seconds added, not null * @throws ArithmeticException * if numeric overflow occurs */ def plusSeconds(secondsToAdd: Long): Duration = plus(secondsToAdd, 0) /** * Returns a copy of this duration with the specified duration in milliseconds added. * * This instance is immutable and unaffected by this method call. * * @param millisToAdd * the milliseconds to add, positive or negative * @return * a { @code Duration} based on this duration with the specified milliseconds added, not null * @throws ArithmeticException * if numeric overflow occurs */ def plusMillis(millisToAdd: Long): Duration = plus(millisToAdd / 1000, (millisToAdd % 1000) * Duration.NANOS_PER_MILLI) /** * Returns a copy of this duration with the specified duration in nanoseconds added. * * This instance is immutable and unaffected by this method call. * * @param nanosToAdd * the nanoseconds to add, positive or negative * @return * a { @code Duration} based on this duration with the specified nanoseconds added, not null * @throws ArithmeticException * if numeric overflow occurs */ def plusNanos(nanosToAdd: Long): Duration = plus(0, nanosToAdd) /** * Returns a copy of this duration with the specified duration added. * * This instance is immutable and unaffected by this method call. * * @param secondsToAdd * the seconds to add, positive or negative * @param nanosToAdd * the nanos to add, positive or negative * @return * a { @code Duration} based on this duration with the specified seconds added, not null * @throws ArithmeticException * if numeric overflow occurs */ private def plus(secondsToAdd: Long, nanosToAdd: Long): Duration = { var _nanosToAdd = nanosToAdd if ((secondsToAdd | _nanosToAdd) == 0) return this var epochSec: Long = Math.addExact(seconds, secondsToAdd) epochSec = Math.addExact(epochSec, _nanosToAdd / Duration.NANOS_PER_SECOND) _nanosToAdd = _nanosToAdd % Duration.NANOS_PER_SECOND val nanoAdjustment: Long = nanos + _nanosToAdd Duration.ofSeconds(epochSec, nanoAdjustment) } /** * Returns a copy of this duration with the specified duration subtracted. * * This instance is immutable and unaffected by this method call. * * @param duration * the duration to subtract, positive or negative, not null * @return * a { @code Duration} based on this duration with the specified duration subtracted, not null * @throws ArithmeticException * if numeric overflow occurs */ def minus(duration: Duration): Duration = { val secsToSubtract: Long = duration.getSeconds val nanosToSubtract: Int = duration.getNano if (secsToSubtract == Long.MinValue) plus(Long.MaxValue, -nanosToSubtract.toLong).plus(1, 0) else plus(-secsToSubtract, -nanosToSubtract.toLong) } /** * Returns a copy of this duration with the specified duration subtracted. * * The duration amount is measured in terms of the specified unit. Only a subset of units are * accepted by this method. The unit must either have an {@link TemporalUnit#isDurationEstimated() * exact duration} or be {@link ChronoUnit#DAYS} which is treated as 24 hours. Other units throw * an exception. * * This instance is immutable and unaffected by this method call. * * @param amountToSubtract * the amount of the period, measured in terms of the unit, positive or negative * @param unit * the unit that the period is measured in, must have an exact duration, not null * @return * a { @code Duration} based on this duration with the specified duration subtracted, not null * @throws ArithmeticException * if numeric overflow occurs */ def minus(amountToSubtract: Long, unit: TemporalUnit): Duration = if (amountToSubtract == Long.MinValue) plus(Long.MaxValue, unit).plus(1, unit) else plus(-amountToSubtract, unit) /** * Returns a copy of this duration with the specified duration in 24 hour days subtracted. * * This instance is immutable and unaffected by this method call. * * @param daysToSubtract * the days to subtract, positive or negative * @return * a { @code Duration} based on this duration with the specified days subtracted, not null * @throws ArithmeticException * if numeric overflow occurs */ def minusDays(daysToSubtract: Long): Duration = if (daysToSubtract == Long.MinValue) plusDays(Long.MaxValue).plusDays(1) else plusDays(-daysToSubtract) /** * Returns a copy of this duration with the specified duration in hours subtracted. * * This instance is immutable and unaffected by this method call. * * @param hoursToSubtract * the hours to subtract, positive or negative * @return * a { @code Duration} based on this duration with the specified hours subtracted, not null * @throws ArithmeticException * if numeric overflow occurs */ def minusHours(hoursToSubtract: Long): Duration = if (hoursToSubtract == Long.MinValue) plusHours(Long.MaxValue).plusHours(1) else plusHours(-hoursToSubtract) /** * Returns a copy of this duration with the specified duration in minutes subtracted. * * This instance is immutable and unaffected by this method call. * * @param minutesToSubtract * the minutes to subtract, positive or negative * @return * a { @code Duration} based on this duration with the specified minutes subtracted, not null * @throws ArithmeticException * if numeric overflow occurs */ def minusMinutes(minutesToSubtract: Long): Duration = if (minutesToSubtract == Long.MinValue) plusMinutes(Long.MaxValue).plusMinutes(1) else plusMinutes(-minutesToSubtract) /** * Returns a copy of this duration with the specified duration in seconds subtracted. * * This instance is immutable and unaffected by this method call. * * @param secondsToSubtract * the seconds to subtract, positive or negative * @return * a { @code Duration} based on this duration with the specified seconds subtracted, not null * @throws ArithmeticException * if numeric overflow occurs */ def minusSeconds(secondsToSubtract: Long): Duration = if (secondsToSubtract == Long.MinValue) plusSeconds(Long.MaxValue).plusSeconds(1) else plusSeconds(-secondsToSubtract) /** * Returns a copy of this duration with the specified duration in milliseconds subtracted. * * This instance is immutable and unaffected by this method call. * * @param millisToSubtract * the milliseconds to subtract, positive or negative * @return * a { @code Duration} based on this duration with the specified milliseconds subtracted, not * null * @throws ArithmeticException * if numeric overflow occurs */ def minusMillis(millisToSubtract: Long): Duration = if (millisToSubtract == Long.MinValue) plusMillis(Long.MaxValue).plusMillis(1) else plusMillis(-millisToSubtract) /** * Returns a copy of this duration with the specified duration in nanoseconds subtracted. * * This instance is immutable and unaffected by this method call. * * @param nanosToSubtract * the nanoseconds to subtract, positive or negative * @return * a { @code Duration} based on this duration with the specified nanoseconds subtracted, not * null * @throws ArithmeticException * if numeric overflow occurs */ def minusNanos(nanosToSubtract: Long): Duration = if (nanosToSubtract == Long.MinValue) plusNanos(Long.MaxValue).plusNanos(1) else plusNanos(-nanosToSubtract) /** * Returns a copy of this duration multiplied by the scalar. * * This instance is immutable and unaffected by this method call. * * @param multiplicand * the value to multiply the duration by, positive or negative * @return * a { @code Duration} based on this duration multiplied by the specified scalar, not null * @throws ArithmeticException * if numeric overflow occurs */ def multipliedBy(multiplicand: Long): Duration = if (multiplicand == 0) Duration.ZERO else if (multiplicand == 1) this else Duration.create(toSeconds.multiply(BigDecimal.valueOf(multiplicand))) /** * Returns a copy of this duration divided by the specified value. * * This instance is immutable and unaffected by this method call. * * @param divisor * the value to divide the duration by, positive or negative, not zero * @return * a { @code Duration} based on this duration divided by the specified divisor, not null * @throws ArithmeticException * if the divisor is zero * @throws ArithmeticException * if numeric overflow occurs */ def dividedBy(divisor: Long): Duration = if (divisor == 0) throw new ArithmeticException("Cannot divide by zero") else if (divisor == 1) this else Duration.create(toSeconds.divide(BigDecimal.valueOf(divisor), RoundingMode.DOWN)) /** * Converts this duration to the total length in seconds and fractional nanoseconds expressed as a * {@code BigDecimal}. * * @return * the total length of the duration in seconds, with a scale of 9, not null */ private def toSeconds: BigDecimal = BigDecimal.valueOf(seconds).add(BigDecimal.valueOf(nanos.toLong, 9)) /** * Returns a copy of this duration with the length negated. * * This method swaps the sign of the total length of this duration. For example, {@code PT1.3S} * will be returned as {@code PT-1.3S}. * * This instance is immutable and unaffected by this method call. * * @return * a { @code Duration} based on this duration with the amount negated, not null * @throws ArithmeticException * if numeric overflow occurs */ def negated: Duration = multipliedBy(-1) /** * Returns a copy of this duration with a positive length. * * This method returns a positive duration by effectively removing the sign from any negative * total length. For example, {@code PT-1.3S} will be returned as {@code PT1.3S}. * * This instance is immutable and unaffected by this method call. * * @return * a { @code Duration} based on this duration with an absolute length, not null * @throws ArithmeticException * if numeric overflow occurs */ def abs: Duration = if (isNegative) negated else this /** * Adds this duration to the specified temporal object. * * This returns a temporal object of the same observable type as the input with this duration * added. * * In most cases, it is clearer to reverse the calling pattern by using {@link * Temporal#plus(TemporalAmount)}.
 // these two lines are equivalent, but the second
   * approach is recommended dateTime = thisDuration.addTo(dateTime); dateTime =
   * dateTime.plus(thisDuration); 
* * The calculation will add the seconds, then nanos. Only non-zero amounts will be added. * * This instance is immutable and unaffected by this method call. * * @param temporal * the temporal object to adjust, not null * @return * an object of the same type with the adjustment made, not null * @throws DateTimeException * if unable to add * @throws ArithmeticException * if numeric overflow occurs */ def addTo(temporal: Temporal): Temporal = { var _temporal = temporal if (seconds != 0) _temporal = _temporal.plus(seconds, SECONDS) if (nanos != 0) _temporal = _temporal.plus(nanos.toLong, NANOS) _temporal } /** * Subtracts this duration from the specified temporal object. * * This returns a temporal object of the same observable type as the input with this duration * subtracted. * * In most cases, it is clearer to reverse the calling pattern by using {@link * Temporal#minus(TemporalAmount)}.
 // these two lines are equivalent, but the second
   * approach is recommended dateTime = thisDuration.subtractFrom(dateTime); dateTime =
   * dateTime.minus(thisDuration); 
* * The calculation will subtract the seconds, then nanos. Only non-zero amounts will be added. * * This instance is immutable and unaffected by this method call. * * @param temporal * the temporal object to adjust, not null * @return * an object of the same type with the adjustment made, not null * @throws DateTimeException * if unable to subtract * @throws ArithmeticException * if numeric overflow occurs */ def subtractFrom(temporal: Temporal): Temporal = { var _temporal = temporal if (seconds != 0) _temporal = _temporal.minus(seconds, SECONDS) if (nanos != 0) _temporal = _temporal.minus(nanos.toLong, NANOS) _temporal } /** * Gets the number of days in this duration. * * This returns the total number of days in the duration by dividing the number of seconds by * 86400. This is based on the standard definition of a day as 24 hours. * * This instance is immutable and unaffected by this method call. * * @return * the number of days in the duration, may be negative */ def toDays: Long = seconds / SECONDS_PER_DAY /** * Gets the number of hours in this duration. * * This returns the total number of hours in the duration by dividing the number of seconds by * 3600. * * This instance is immutable and unaffected by this method call. * * @return * the number of hours in the duration, may be negative */ def toHours: Long = seconds / SECONDS_PER_HOUR /** * Gets the number of minutes in this duration. * * This returns the total number of minutes in the duration by dividing the number of seconds by * 60. * * This instance is immutable and unaffected by this method call. * * @return * the number of minutes in the duration, may be negative */ def toMinutes: Long = seconds / SECONDS_PER_MINUTE /** * Converts this duration to the total length in milliseconds. * * If this duration is too large to fit in a {@code long} milliseconds, then an exception is * thrown. * * If this duration has greater than millisecond precision, then the conversion will drop any * excess precision information as though the amount in nanoseconds was subject to integer * division by one million. * * @return * the total length of the duration in milliseconds * @throws ArithmeticException * if numeric overflow occurs */ def toMillis: Long = { val result: Long = Math.multiplyExact(seconds, 1000L) Math.addExact(result, nanos.toLong / Duration.NANOS_PER_MILLI) } /** * Converts this duration to the total length in nanoseconds expressed as a {@code long}. * * If this duration is too large to fit in a {@code long} nanoseconds, then an exception is * thrown. * * @return * the total length of the duration in nanoseconds * @throws ArithmeticException * if numeric overflow occurs */ def toNanos: Long = { val result: Long = Math.multiplyExact(seconds, Duration.NANOS_PER_SECOND.toLong) Math.addExact(result, nanos.toLong) } /** * Compares this duration to the specified {@code Duration}. * * The comparison is based on the total length of the durations. It is "consistent with equals", * as defined by {@link Comparable}. * * @param otherDuration * the other duration to compare to, not null * @return * the comparator value, negative if less, positive if greater */ def compare(otherDuration: Duration): Int = { val cmp: Int = java.lang.Long.compare(seconds, otherDuration.seconds) if (cmp != 0) cmp else nanos - otherDuration.nanos } override def compareTo(other: Duration): Int = compare(other) /** * Checks if this duration is equal to the specified {@code Duration}. * * The comparison is based on the total length of the durations. * * @param other * the other duration, null returns false * @return * true if the other duration is equal to this one */ override def equals(other: Any): Boolean = other match { case otherDuration: Duration => (this eq otherDuration) || (this.seconds == otherDuration.seconds && this.nanos == otherDuration.nanos) case _ => false } /** * A hash code for this duration. * * @return * a suitable hash code */ override def hashCode: Int = (seconds ^ (seconds >>> 32)).toInt + (51 * nanos) /** * A string representation of this duration using ISO-8601 seconds based representation, such as * {@code PT8H6M12.345S}. * * The format of the returned string will be {@code PTnHnMnS}, where n is the relevant hours, * minutes or seconds part of the duration. Any fractional seconds are placed after a decimal * point i the seconds section. If a section has a zero value, it is omitted. The hours, minutes * and seconds will all have the same sign. * * Examples:
 "20.345 seconds" -> "PT20.345S "15 minutes" (15 * 60 seconds) -> "PT15M" "10
   * hours" (10 * 3600 seconds) -> "PT10H" "2 days" (2 * 86400 seconds) -> "PT48H" 
Note that * multiples of 24 hours are not output as days to avoid confusion with {@code Period}. * * @return * an ISO-8601 representation of this duration, not null */ override def toString: String = { if (this eq Duration.ZERO) return "PT0S" val hours: Long = seconds / SECONDS_PER_HOUR val minutes: Int = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE).asInstanceOf[Int] val secs: Int = (seconds % SECONDS_PER_MINUTE).asInstanceOf[Int] val buf: StringBuilder = 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) { val pos: Int = buf.length if (secs < 0) buf.append(2 * Duration.NANOS_PER_SECOND - nanos) else buf.append(nanos + Duration.NANOS_PER_SECOND) while (buf.charAt(buf.length - 1) == '0') buf.setLength(buf.length - 1) buf.setCharAt(pos, '.') } buf.append('S') buf.toString } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy