mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 03:36:59 +00:00
0f0f045128
The `toString` used the sign of `hours` to get a leading `-` when the duration is negative. However, a negative duration of less than one hour would therefore miss out on the sign, because `-0` is not an integer. Now handles sign explicitly. Fixes #48841. Change-Id: I0ab6d451faf1c76b838fc35a692f07c5b035d2a7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241748 Reviewed-by: Nate Bosch <nbosch@google.com> Commit-Queue: Lasse Nielsen <lrn@google.com>
377 lines
14 KiB
Dart
377 lines
14 KiB
Dart
// 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 span of time, such as 27 days, 4 hours, 12 minutes, and 3 seconds.
|
|
///
|
|
/// A `Duration` represents a difference from one point in time to another. The
|
|
/// duration may be "negative" if the difference is from a later time to an
|
|
/// earlier.
|
|
///
|
|
/// Durations are context independent. 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]).
|
|
///
|
|
/// 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 [Duration] represents a single number of microseconds,
|
|
/// which is the sum of all the individual arguments to the constructor.
|
|
///
|
|
/// Properties can access that single number in different ways.
|
|
/// For example the [inMinutes] gives the number of whole minutes
|
|
/// in the total duration, which includes the minutes that were provided
|
|
/// as "hours" to the constructor, and can be larger than 59.
|
|
///
|
|
/// ```dart
|
|
/// const fastestMarathon = Duration(hours: 2, minutes: 3, seconds: 2);
|
|
/// print(fastestMarathon.inDays); // 0
|
|
/// print(fastestMarathon.inHours); // 2
|
|
/// print(fastestMarathon.inMinutes); // 123
|
|
/// print(fastestMarathon.inSeconds); // 7382
|
|
/// print(fastestMarathon.inMilliseconds); // 7382000
|
|
/// ```
|
|
/// The duration can be negative, in which case
|
|
/// all the properties derived from the duration are also non-positive.
|
|
/// ```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 down.
|
|
/// For example,
|
|
/// ```dart
|
|
/// const aLongWeekend = Duration(hours: 88);
|
|
/// print(aLongWeekend.inDays); // 3
|
|
/// ```
|
|
/// This class provides a collection of arithmetic
|
|
/// and comparison operators,
|
|
/// plus a set of constants useful for converting 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 days may differ in length because
|
|
/// of time zone changes due to daylight saving.
|
|
/// The [Duration] class is 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 total microseconds of this [Duration] object.
|
|
final int _duration;
|
|
|
|
/// 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 be truncated
|
|
/// and 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.
|
|
const Duration._microseconds(this._duration);
|
|
|
|
/// Adds this Duration and [other] and
|
|
/// returns the sum as a new Duration object.
|
|
Duration operator +(Duration other) {
|
|
return Duration._microseconds(_duration + other._duration);
|
|
}
|
|
|
|
/// Subtracts [other] from this Duration and
|
|
/// returns the difference as a new Duration object.
|
|
Duration operator -(Duration other) {
|
|
return Duration._microseconds(_duration - other._duration);
|
|
}
|
|
|
|
/// Multiplies this Duration by the given [factor] and returns the result
|
|
/// as a new Duration object.
|
|
///
|
|
/// 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((_duration * factor).round());
|
|
}
|
|
|
|
/// Divides this Duration by the given [quotient] and returns the truncated
|
|
/// result as a new Duration object.
|
|
///
|
|
/// Throws an [IntegerDivisionByZeroException] if [quotient] is `0`.
|
|
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(_duration ~/ quotient);
|
|
}
|
|
|
|
/// Whether this [Duration] is shorter than [other].
|
|
bool operator <(Duration other) => this._duration < other._duration;
|
|
|
|
/// Whether this [Duration] is longer than [other].
|
|
bool operator >(Duration other) => this._duration > other._duration;
|
|
|
|
/// Whether this [Duration] is shorter than or equal to [other].
|
|
bool operator <=(Duration other) => this._duration <= other._duration;
|
|
|
|
/// Whether this [Duration] is longer than or equal to [other].
|
|
bool operator >=(Duration other) => this._duration >= other._duration;
|
|
|
|
/// 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
|
|
/// ```
|
|
int get inDays => _duration ~/ 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
|
|
/// ```
|
|
int get inHours => _duration ~/ 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
|
|
/// ```
|
|
int get inMinutes => _duration ~/ 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
|
|
/// ```
|
|
int get inSeconds => _duration ~/ Duration.microsecondsPerSecond;
|
|
|
|
/// The number of whole milliseconds spanned by this [Duration].
|
|
///
|
|
/// The returned value can be greater than 999.
|
|
/// For example, a duration of three seconds and 125 milliseconds
|
|
/// has 3125 milliseconds.
|
|
/// ```dart
|
|
/// const duration = Duration(seconds: 3, milliseconds: 125);
|
|
/// print(duration.inMilliseconds); // 3125
|
|
/// ```
|
|
int get inMilliseconds => _duration ~/ Duration.microsecondsPerMillisecond;
|
|
|
|
/// The number of whole microseconds spanned by this [Duration].
|
|
///
|
|
/// The returned value can be greater than 999999.
|
|
/// For example, a duration of three seconds, 125 milliseconds and
|
|
/// 369 microseconds has 3125369 microseconds.
|
|
/// ```dart
|
|
/// const duration = Duration(seconds: 3, milliseconds: 125,
|
|
/// microseconds: 369);
|
|
/// print(duration.inMicroseconds); // 3125369
|
|
/// ```
|
|
int get inMicroseconds => _duration;
|
|
|
|
/// Whether this [Duration] has the same length as [other].
|
|
///
|
|
/// Durations have the same length if they have the same number
|
|
/// of microseconds, as reported by [inMicroseconds].
|
|
bool operator ==(Object other) =>
|
|
other is Duration && _duration == other.inMicroseconds;
|
|
|
|
int get hashCode => _duration.hashCode;
|
|
|
|
/// Compares this [Duration] to [other], returning zero if the values are equal.
|
|
///
|
|
/// Returns a negative integer if this [Duration] is shorter than
|
|
/// [other], or a positive integer if it is longer.
|
|
///
|
|
/// A negative [Duration] is always considered shorter than a positive one.
|
|
///
|
|
/// It is always the case that `duration1.compareTo(duration2) < 0` iff
|
|
/// `(someDate + duration1).compareTo(someDate + duration2) < 0`.
|
|
int compareTo(Duration other) => _duration.compareTo(other._duration);
|
|
|
|
/// Returns a string representation of this [Duration].
|
|
///
|
|
/// Returns 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 = (microseconds < 0) ? "-" : "";
|
|
|
|
var hours = microseconds ~/ microsecondsPerHour;
|
|
microseconds = microseconds.remainder(microsecondsPerHour);
|
|
|
|
if (microseconds < 0) microseconds = -microseconds;
|
|
|
|
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" : "";
|
|
|
|
var paddedMicroseconds = microseconds.toString().padLeft(6, "0");
|
|
return "$sign${hours.abs()}:"
|
|
"$minutesPadding$minutes:"
|
|
"$secondsPadding$seconds.$paddedMicroseconds";
|
|
}
|
|
|
|
/// Whether this [Duration] is negative.
|
|
///
|
|
/// A negative [Duration] represents the difference from a later time to an
|
|
/// earlier time.
|
|
bool get isNegative => _duration < 0;
|
|
|
|
/// Creates a new [Duration] representing the absolute length of this
|
|
/// [Duration].
|
|
///
|
|
/// The returned [Duration] has the same length as this one, but is always
|
|
/// positive where possible.
|
|
Duration abs() => Duration._microseconds(_duration.abs());
|
|
|
|
/// Creates a new [Duration] with the opposite direction of this [Duration].
|
|
///
|
|
/// The returned [Duration] has the same length as this one, but will have the
|
|
/// opposite sign (as reported by [isNegative]) as this one where possible.
|
|
// Using subtraction helps dart2js avoid negative zeros.
|
|
Duration operator -() => Duration._microseconds(0 - _duration);
|
|
}
|