blob: 52dc928671766a6b2e79198fb7a8da9a23202a8e [file]
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of "dart:core";
/// A difference between points in time.
///
/// A `Duration` is a signed number of microseconds, which represents
/// an offset from a point in time to another.
/// You can add a `Duration` to a [DateTime], which gives the
/// point in time that is offset by that duration from the original.
///
/// Most durations are _positive_ and represent an offset
/// to a later time. A positive duration is useful for representing
/// a delay, a point in the future relative to now (as used by
/// [Future.delayed]),
/// or as a measure of time that has passed from an earlier
/// time until now (as reported by [Stopwatch.elapsed]).
///
/// A negative duration is the difference from a later time to an
/// earlier, and adding it to a time gives an earlier time.
/// You cannot wait for a negative amount of time using
/// [Future.delayed].
///
/// Durations are independent of calendars. For example, a duration of 2 days
/// is always 48 hours, even when it is added to a `DateTime` just when the
/// time zone is about to make a daylight-savings switch. (See [DateTime.add]).
///
/// A duration is a (positive or negative) number of microseconds.
/// Durations are ordered by this number of microseconds when compared
/// using [operator <] or [compareTo].
///
/// Despite the same name, a `Duration` object does not implement "Durations"
/// as specified by ISO 8601. In particular, a duration object does not keep
/// track of the individually provided members (such as "days" or "hours"), but
/// only uses these arguments to compute the length of the corresponding time
/// interval.
///
/// To create a new `Duration` object, use this class's single constructor
/// giving the appropriate arguments:
/// ```dart
/// const fastestMarathon = Duration(hours: 2, minutes: 3, seconds: 2);
/// ```
/// The created [Duration] is defined by the number of microseconds
/// that is the sum of all the individual arguments to the constructor.
///
/// Properties can access that single number in different ways.
/// The [inMicroseconds] is the entire value, and, for example,
/// [inMinutes] gives the number of whole minutes in the total duration,
/// which includes any minutes that were provided as "hours" to the constructor
/// or provided as "seconds" with a value of 60 or above.
///
/// ```dart
/// const fastestMarathon = Duration(hours: 2, minutes: 0, seconds: 35);
/// print(fastestMarathon.inDays); // 0
/// print(fastestMarathon.inHours); // 2
/// print(fastestMarathon.inMinutes); // 120
/// print(fastestMarathon.inSeconds); // 7235
/// print(fastestMarathon.inMilliseconds); // 7235000
/// ```
/// If a duration is negative, all the properties derived from the duration are
/// non-positive (negative unless they are zero).
/// ```dart
/// const overDayAgo = Duration(days: -1, hours: -10);
/// print(overDayAgo.inDays); // -1
/// print(overDayAgo.inHours); // -34
/// print(overDayAgo.inMinutes); // -2040
/// ```
///
/// Use one of the properties, such as [inDays],
/// to retrieve the integer value of the `Duration` in the specified time unit.
/// Note that the returned value is rounded towards zero.
/// For example,
/// ```dart
/// const aLongWeekend = Duration(hours: 88);
/// print(aLongWeekend.inDays); // 3
/// const aLongWeekendAgo = Duration(hours: -88);
/// print(aLongWeekendAgo.inDays); // -3
/// ```
///
/// A duration also provides arithmetic and comparison operators,
/// all based on the microsecond offset of the duration.
/// and the class provides a set of constants useful for converting
/// between time units.
/// ```dart
/// const firstHalf = Duration(minutes: 45); // 00:45:00.000000
/// const secondHalf = Duration(minutes: 45); // 00:45:00.000000
/// const overTime = Duration(minutes: 30); // 00:30:00.000000
/// final maxGameTime = firstHalf + secondHalf + overTime;
/// print(maxGameTime.inMinutes); // 120
///
/// // The duration of the firstHalf and secondHalf is the same, returns 0.
/// var result = firstHalf.compareTo(secondHalf);
/// print(result); // 0
///
/// // Duration of overTime is shorter than firstHalf, returns < 0.
/// result = overTime.compareTo(firstHalf);
/// print(result); // < 0
///
/// // Duration of secondHalf is longer than overTime, returns > 0.
/// result = secondHalf.compareTo(overTime);
/// print(result); // > 0
/// ```
///
/// **See also:**
/// * [DateTime] to represent a point in time.
/// * [Stopwatch] to measure time-spans.
class Duration implements Comparable<Duration> {
/// The number of microseconds per millisecond.
static const int microsecondsPerMillisecond = 1000;
/// The number of milliseconds per second.
static const int millisecondsPerSecond = 1000;
/// The number of seconds per minute.
///
/// Notice that some minutes of official clock time might
/// differ in length because of leap seconds.
/// The [Duration] and [DateTime] classes ignore leap seconds
/// and consider all minutes to have 60 seconds.
static const int secondsPerMinute = 60;
/// The number of minutes per hour.
static const int minutesPerHour = 60;
/// The number of hours per day.
///
/// Notice that some calendar days may differ in length because
/// of time zone changes due to daylight saving.
/// The [Duration] class is calendar and time zone agnostic and
/// considers all days to have 24 hours.
static const int hoursPerDay = 24;
/// The number of microseconds per second.
static const int microsecondsPerSecond =
microsecondsPerMillisecond * millisecondsPerSecond;
/// The number of microseconds per minute.
static const int microsecondsPerMinute =
microsecondsPerSecond * secondsPerMinute;
/// The number of microseconds per hour.
static const int microsecondsPerHour = microsecondsPerMinute * minutesPerHour;
/// The number of microseconds per day.
static const int microsecondsPerDay = microsecondsPerHour * hoursPerDay;
/// The number of milliseconds per minute.
static const int millisecondsPerMinute =
millisecondsPerSecond * secondsPerMinute;
/// The number of milliseconds per hour.
static const int millisecondsPerHour = millisecondsPerMinute * minutesPerHour;
/// The number of milliseconds per day.
static const int millisecondsPerDay = millisecondsPerHour * hoursPerDay;
/// The number of seconds per hour.
static const int secondsPerHour = secondsPerMinute * minutesPerHour;
/// The number of seconds per day.
static const int secondsPerDay = secondsPerHour * hoursPerDay;
/// The number of minutes per day.
static const int minutesPerDay = minutesPerHour * hoursPerDay;
/// An empty duration, representing zero time.
static const Duration zero = Duration(seconds: 0);
/// The time offset of this duration in microseconds.
///
/// The value can be greater than 999999.
/// For example, a duration of nine seconds, 123 milliseconds and
/// 678 microseconds has 9123678 microseconds.
/// ```dart
/// const duration = Duration(seconds: 9, milliseconds: 123,
/// microseconds: 567);
/// print(duration.inMicroseconds); // 9123678
/// ```
final int inMicroseconds;
/// Creates a new [Duration] object whose value
/// is the sum of all individual parts.
///
/// Individual parts can be larger than the number of those
/// parts in the next larger unit.
/// For example, [hours] can be greater than 23.
/// If this happens, the value overflows into the next larger
/// unit, so 26 [hours] is the same as 2 [hours] and
/// one more [days].
/// Likewise, values can be negative, in which case they
/// underflow and subtract from the next larger unit.
///
/// If the total number of microseconds cannot be represented
/// as an integer value, the number of microseconds might overflow
/// and be truncated to a smaller number of bits,
/// or it might lose precision.
///
/// All arguments are 0 by default.
/// ```dart
/// const duration = Duration(days: 1, hours: 8, minutes: 56, seconds: 59,
/// milliseconds: 30, microseconds: 10);
/// print(duration); // 32:56:59.030010
/// ```
const Duration({
int days = 0,
int hours = 0,
int minutes = 0,
int seconds = 0,
int milliseconds = 0,
int microseconds = 0,
}) : this._microseconds(
microseconds +
microsecondsPerMillisecond * milliseconds +
microsecondsPerSecond * seconds +
microsecondsPerMinute * minutes +
microsecondsPerHour * hours +
microsecondsPerDay * days,
);
// Fast path internal direct constructor to avoids the optional arguments
// and [_microseconds] recomputation.
// The `+ 0` prevents -0.0 on the web, if the incoming duration happens to be -0.0.
const Duration._microseconds(int duration) : inMicroseconds = duration + 0;
/// Adds this `Duration` and [other] and
/// returns the sum as a new `Duration` object.
Duration operator +(Duration other) {
return Duration._microseconds(inMicroseconds + other.inMicroseconds);
}
/// The offset of this duration minus the offset of [other].
Duration operator -(Duration other) {
return Duration._microseconds(inMicroseconds - other.inMicroseconds);
}
/// This [Duration] scaled by [factor].
///
/// Note that when [factor] is a double, and the duration is greater than
/// 53 bits, precision is lost because of double-precision arithmetic.
Duration operator *(num factor) {
return Duration._microseconds((inMicroseconds * factor).round());
}
/// One-[quotient]th of this duration.
///
/// Divides the offset in microseconds of this duration by [quotient]
/// rounding towards zero, and provides a new `Duration` with that
/// as its [inMicroseconds].
///
/// The [quotient] must not be `0`. If it is negative, the result
/// is equivalent to dividing by the absolute value and then negating.
Duration operator ~/(int quotient) {
// By doing the check here instead of relying on "~/" below we get the
// exception even with dart2js.
if (quotient == 0) throw IntegerDivisionByZeroException();
return Duration._microseconds(inMicroseconds ~/ quotient);
}
/// Whether this duration's offset is smaller than that of [other].
///
/// A negative duration's offset is always smaller than a positive one's.
/// Equivalent to comparing [inMicroseconds]
bool operator <(Duration other) => this.inMicroseconds < other.inMicroseconds;
/// Whether this duration's offset is greater than that of [other].
///
/// A negative duration's offset is always smaller than a positive one's.
/// Equivalent to comparing [inMicroseconds]
bool operator >(Duration other) => this.inMicroseconds > other.inMicroseconds;
/// Whether this duration's offset is not greater than that of [other].
///
/// A negative duration's offset is always smaller than a positive one's.
/// Equivalent to comparing [inMicroseconds]
bool operator <=(Duration other) =>
this.inMicroseconds <= other.inMicroseconds;
/// Whether this duration's offset is not smaller than that of [other].
///
/// A negative duration's offset is always smaller than a positive one's.
/// Equivalent to comparing [inMicroseconds]
bool operator >=(Duration other) =>
this.inMicroseconds >= other.inMicroseconds;
/// The number of entire days spanned by this [Duration].
///
/// For example, a duration of four days and three hours
/// has four entire days.
/// ```dart
/// const duration = Duration(days: 4, hours: 3);
/// print(duration.inDays); // 4
/// ```
/// A negative duration has a negative number of whole days,
/// so that `(-duration).inDays` = `-(duration.inDays)`.
int get inDays => inMicroseconds ~/ Duration.microsecondsPerDay;
/// The number of entire hours spanned by this [Duration].
///
/// The returned value can be greater than 23.
/// For example, a duration of four days and three hours
/// has 99 entire hours.
/// ```dart
/// const duration = Duration(days: 4, hours: 3);
/// print(duration.inHours); // 99 = 4 * 24 + 3
/// ```
/// A negative duration has a negative number of whole hours,
/// so that `(-duration).inHours` = `-(duration.inHours)`.
int get inHours => inMicroseconds ~/ Duration.microsecondsPerHour;
/// The number of whole minutes spanned by this [Duration].
///
/// The returned value can be greater than 59.
/// For example, a duration of three hours and 12 minutes
/// has 192 minutes.
/// ```dart
/// const duration = Duration(hours: 3, minutes: 12);
/// print(duration.inMinutes); // 192 = 3 * 60 + 12
/// ```
/// A negative duration has a negative number of whole minutes,
/// so that `(-duration).inMinutes` = `-(duration.inMinutes)`.
int get inMinutes => inMicroseconds ~/ Duration.microsecondsPerMinute;
/// The number of whole seconds spanned by this [Duration].
///
/// The returned value can be greater than 59.
/// For example, a duration of three minutes and 12 seconds
/// has 192 seconds.
/// ```dart
/// const duration = Duration(minutes: 3, seconds: 12);
/// print(duration.inSeconds); // 192 = 3 * 60 + 12
/// ```
/// A negative duration has a negative number of whole seconds,
/// so that `(-duration).inSeconds` = `-(duration.inSeconds)`.
int get inSeconds => inMicroseconds ~/ Duration.microsecondsPerSecond;
/// The number of whole milliseconds spanned by this [Duration].
///
/// The returned value can be greater than 999.
/// For example, a duration of nine seconds and 123 milliseconds
/// has 9125 milliseconds.
/// ```dart
/// const duration = Duration(seconds: 9, milliseconds: 123);
/// print(duration.inMilliseconds); // 9123 = 9 * 1000 + 123
/// ```
int get inMilliseconds =>
inMicroseconds ~/ Duration.microsecondsPerMillisecond;
/// Whether this [Duration] is equivalent to [other].
///
/// Durations are equivalent if they have the same number
/// of microseconds, as reported by [inMicroseconds].
bool operator ==(Object other) =>
other is Duration && inMicroseconds == other.inMicroseconds;
/// The hash code of a duration is the hash code of its [inMicroseconds].
int get hashCode => inMicroseconds.hashCode;
/// Compares this [Duration] to [other].
///
/// Returns a negative integer if this [Duration]'s offset is smaller
/// than that of [other], a positive integer if this duration's offset
/// is greater than that of [other], and 0 if they are equivalent.
///
/// A longer positive duration ends after a shorter positive duration,
/// a positive duration ends after a negative duration,
/// and a shorter negative duration ends after a longer negative duration.
/// Equivalent to comparing the [inMicroseconds] of the duration).
///
/// It is always the case that `duration1.compareTo(duration2) < 0`
/// if and only if
/// `(someDate + duration1).compareTo(someDate + duration2) < 0`.
int compareTo(Duration other) =>
inMicroseconds.compareTo(other.inMicroseconds);
/// A string representation of this [Duration].
///
/// A string with hours, minutes, seconds, and microseconds, in the
/// following format: `H:MM:SS.mmmmmm`. For example,
/// ```dart
/// var d = const Duration(days: 1, hours: 1, minutes: 33, microseconds: 500);
/// print(d.toString()); // 25:33:00.000500
///
/// d = const Duration(hours: 1, minutes: 10, microseconds: 500);
/// print(d.toString()); // 1:10:00.000500
/// ```
String toString() {
var microseconds = inMicroseconds;
var sign = "";
var negative = microseconds < 0;
var hours = microseconds ~/ microsecondsPerHour;
microseconds = microseconds.remainder(microsecondsPerHour);
// Correcting for being negative after first division, instead of before,
// to avoid negating min-int, -(2^31-1), of a native int64.
if (negative) {
hours = 0 - hours; // Not using `-hours` to avoid creating -0.0 on web.
microseconds = 0 - microseconds;
sign = "-";
}
var minutes = microseconds ~/ microsecondsPerMinute;
microseconds = microseconds.remainder(microsecondsPerMinute);
var minutesPadding = minutes < 10 ? "0" : "";
var seconds = microseconds ~/ microsecondsPerSecond;
microseconds = microseconds.remainder(microsecondsPerSecond);
var secondsPadding = seconds < 10 ? "0" : "";
// Padding up to six digits for microseconds.
var microsecondsText = microseconds.toString().padLeft(6, "0");
return "$sign$hours:"
"$minutesPadding$minutes:"
"$secondsPadding$seconds."
"$microsecondsText";
}
/// Whether this [Duration] is negative.
///
/// A negative [Duration] represents the difference from a later time to an
/// earlier time.
bool get isNegative => inMicroseconds < 0;
/// A positive [Duration] with the same absolute length as this [Duration].
///
/// The returned [Duration] has the same length as this one, but is always
/// positive where possible.
Duration abs() =>
inMicroseconds >= 0 ? this : Duration._microseconds(0 - inMicroseconds);
/// Creates a new [Duration] with the opposite sign of this [Duration].
///
/// The returned [Duration] has the absolute length as this one, but has the
/// opposite sign (as reported by [isNegative]) where possible.
// Using subtraction helps dart2js avoid negative zeros.
Duration operator -() => Duration._microseconds(0 - inMicroseconds);
}