Struct Span
struct Span { ... }
A span of time represented via a mixture of calendar and clock units.
A span represents a duration of time in units of years, months, weeks,
days, hours, minutes, seconds, milliseconds, microseconds and nanoseconds.
Spans are used to as inputs to routines like
Zoned::checked_add and Date::saturating_sub,
and are also outputs from routines like
Timestamp::since and DateTime::until.
Range of spans
Except for nanoseconds, each unit can represent the full span of time expressible via any combination of datetime supported by Jiff. For example:
use ;
let options = new.largest;
assert_eq!;
let options = options.largest;
assert_eq!;
let options = options.largest;
assert_eq!;
let options = options.largest;
// Span is too big, overflow!
assert!;
# Ok::
Building spans
A default or empty span corresponds to a duration of zero time:
use Span;
assert!;
assert!;
Spans are Copy types that have mutator methods on them for creating new
spans:
use Span;
let span = new.days.hours.minutes;
assert_eq!;
But Jiff provides a ToSpan trait that defines extension methods on
primitive signed integers to make span creation terser:
use ToSpan;
let span = 5.days.hours.minutes;
assert_eq!;
// singular units on integers can be used too:
let span = 1.day.hours.minutes;
assert_eq!;
Negative spans
A span may be negative. All of these are equivalent:
use ;
let span = -new.days;
assert_eq!;
let span = new.days.negate;
assert_eq!;
let span = new.days;
assert_eq!;
let span = -new.days.negate;
assert_eq!;
let span = -5.days;
assert_eq!;
let span = .days;
assert_eq!;
let span = -;
assert_eq!;
The sign of a span applies to the entire span. When a span is negative, then all of its units are negative:
use ToSpan;
let span = -5.days.hours.minutes;
assert_eq!;
assert_eq!;
assert_eq!;
And if any of a span's units are negative, then the entire span is regarded as negative:
use ToSpan;
// It's the same thing.
let span = .days.hours.minutes;
assert_eq!;
assert_eq!;
assert_eq!;
// Still the same. All negative.
let span = 5.days.hours.minutes;
assert_eq!;
assert_eq!;
assert_eq!;
// But this is not! The negation in front applies
// to the entire span, which was already negative
// by virtue of at least one of its units being
// negative. So the negation operator in front turns
// the span positive.
let span = -5.days.hours.minutes;
assert_eq!;
assert_eq!;
assert_eq!;
You can also ask for the absolute value of a span:
use Span;
let span = new.days.hours.minutes.negate.abs;
assert_eq!;
assert_eq!;
assert_eq!;
Parsing and printing
The Span type provides convenient trait implementations of
std::str::FromStr and [std::fmt::Display]:
use ;
let span: Span = "P2m10dT2h30m".parse?;
// By default, capital unit designator labels are used.
// This can be changed with `jiff::fmt::temporal::SpanPrinter::lowercase`.
assert_eq!;
// Or use the "friendly" format by invoking the `Display` alternate:
assert_eq!;
// Parsing automatically supports both the ISO 8601 and "friendly"
// formats. Note that we use `Span::fieldwise` to create a `Span` that
// compares based on each field. To compare based on total duration, use
// `Span::compare` or `Span::total`.
let span: Span = "2mo 10d 2h 30m".parse?;
assert_eq!;
let span: Span = "2 months, 10 days, 2 hours, 30 minutes".parse?;
assert_eq!;
# Ok::
The format supported is a variation (nearly a subset) of the duration format specified in ISO 8601 and a Jiff-specific "friendly" format. Here are more examples:
use ;
let spans = ;
for in spans
# Ok::
For more details, see the fmt::temporal and
fmt::friendly modules.
Comparisons
A Span does not implement the PartialEq or Eq traits. These traits
were implemented in an earlier version of Jiff, but they made it too
easy to introduce bugs. For example, 120.minutes() and 2.hours()
always correspond to the same total duration, but they have different
representations in memory and so didn't compare equivalent.
The reason why the PartialEq and Eq trait implementations do not do
comparisons with total duration is because it is fundamentally impossible
to do such comparisons without a reference date in all cases.
However, it is undeniably occasionally useful to do comparisons based
on the component fields, so long as such use cases can tolerate two
different spans comparing unequal even when their total durations are
equivalent. For example, many of the tests in Jiff (including the tests in
the documentation) work by comparing a Span to an expected result. This
is a good demonstration of when fieldwise comparisons are appropriate.
To do fieldwise comparisons with a span, use the Span::fieldwise
method. This method creates a SpanFieldwise, which is just a Span
that implements PartialEq and Eq in a fieldwise manner. In other words,
it's a speed bump to ensure this is the kind of comparison you actually
want. For example:
use ToSpan;
assert_ne!;
// These also work since you only need one fieldwise span to do a compare:
assert_ne!;
assert_ne!;
This is because doing true comparisons requires arithmetic and a relative
datetime in the general case, and which can fail due to overflow. This
operation is provided via [Span::compare]:
use ;
// This doesn't need a reference date since it's only using time units.
assert_eq!;
// But if you have calendar units, then you need a
// reference date at minimum:
assert!;
assert_eq!;
// A month can be a differing number of days!
assert_eq!;
# Ok::
Arithmetic
Spans can be added or subtracted via Span::checked_add and
[Span::checked_sub]:
use ;
let span1 = 2.hours.minutes;
let span2: Span = "PT89400s".parse?;
assert_eq!;
# Ok::
When your spans involve calendar units, a relative datetime must be provided. (Because, for example, 1 month from March 1 is 31 days, but 1 month from April 1 is 30 days.)
use ;
let span1 = 2.years.months.days;
let span2 = 400.days;
assert_eq!;
// The span changes when a leap year isn't included!
assert_eq!;
# Ok::
Rounding and balancing
Unlike datetimes, multiple distinct Span values can actually correspond
to the same duration of time. For example, all of the following correspond
to the same duration:
- 2 hours, 30 minutes
- 150 minutes
- 1 hour, 90 minutes
The first is said to be balanced. That is, its biggest non-zero unit cannot
be expressed in an integer number of units bigger than hours. But the
second is unbalanced because 150 minutes can be split up into hours and
minutes. We call this sort of span a "top-heavy" unbalanced span. The third
span is also unbalanced, but it's "bottom-heavy" and rarely used. Jiff
will generally only produce spans of the first two types. In particular,
most Span producing APIs accept a "largest" Unit parameter, and the
result can be said to be a span "balanced up to the largest unit provided."
Balanced and unbalanced spans can be switched between as needed via
the Span::round API by providing a rounding configuration with
SpanRound::largest` set:
use ;
let span = 2.hours.minutes;
let unbalanced = span.round?;
assert_eq!;
let balanced = unbalanced.round?;
assert_eq!;
# Ok::
Balancing can also be done as part of computing spans from two datetimes:
use ;
let zdt1 = date.at.in_tz?;
let zdt2 = date.at.in_tz?;
// To make arithmetic reversible, the default largest unit for spans of
// time computed from zoned datetimes is hours:
assert_eq!;
// But we can ask for the span to be balanced up to years:
assert_eq!;
# Ok::
While the Span::round API does balancing, it also, of course, does
rounding as well. Rounding occurs when the smallest unit is set to
something bigger than [Unit::Nanosecond]:
use ;
let span = 2.hours.minutes;
assert_eq!;
# Ok::
When rounding spans with calendar units (years, months or weeks), then a relative datetime is required:
use ;
let span = 10.years.months;
let options = new
.smallest
.relative;
assert_eq!;
# Ok::
Days are not always 24 hours!
That is, a Span is made up of uniform and non-uniform units.
A uniform unit is a unit whose elapsed duration is always the same. A non-uniform unit is a unit whose elapsed duration is not always the same. There are two things that can impact the length of a non-uniform unit: the calendar date and the time zone.
Years and months are always considered non-uniform units. For example,
1 month from 2024-04-01 is 30 days, while 1 month from 2024-05-01 is
31 days. Similarly for years because of leap years.
Hours, minutes, seconds, milliseconds, microseconds and nanoseconds are always considered uniform units.
Days are only considered non-uniform when in the presence of a zone aware datetime. A day can be more or less than 24 hours, and it can be balanced up and down, but only when a relative zoned datetime is given. This typically happens because of DST (daylight saving time), but can also occur because of other time zone transitions too.
use ;
// 2024-03-10 in New York was 23 hours long,
// because of a jump to DST at 2am.
let zdt = date.at.in_tz?;
// Goes from days to hours:
assert_eq!;
// Goes from hours to days:
assert_eq!;
// 24 hours is more than 1 day starting at this time:
assert_eq!;
# Ok::
And similarly, days can be longer than 24 hours:
use ;
// 2024-11-03 in New York was 25 hours long,
// because of a repetition of the 1 o'clock AM hour.
let zdt = date.at.in_tz?;
// Goes from days to hours:
assert_eq!;
// Goes from hours to days:
assert_eq!;
// 24 hours is less than 1 day starting at this time,
// so it stays in units of hours even though we ask
// for days (because 24 isn't enough hours to make
// 1 day):
assert_eq!;
# Ok::
The APIs on Span will otherwise treat days as non-uniform unless a
relative civil date is given, or there is an explicit opt-in to invariant
24-hour days. For example:
use ;
let span = 1.day;
// An error because days aren't always 24 hours:
assert_eq!;
// Opt into invariant 24 hour days without a relative date:
let marker = days_are_24_hours;
let hours = span.total?;
// Or use a relative civil date, and all days are 24 hours:
let date = date;
let hours = span.total?;
assert_eq!;
# Ok::
In Jiff, all weeks are 7 days. And generally speaking, weeks only appear in
a Span if they were explicitly put there by the caller or if they were
explicitly requested by the caller in an API. For example:
use ;
let dt1 = date.at;
let dt2 = date.at;
// Default units go up to days.
assert_eq!;
// No weeks, even though we requested up to year.
assert_eq!;
// We get weeks only when we ask for them.
assert_eq!;
# Ok::
Integration with std::time::Duration and SignedDuration
While Jiff primarily uses a Span for doing arithmetic on datetimes,
one can convert between a Span and a std::time::Duration or a
SignedDuration. The main difference between them is that a Span
always keeps tracks of its individual units, and a Span can represent
non-uniform units like months. In contrast, Duration and SignedDuration
are always an exact elapsed amount of time. They don't distinguish between
120 seconds and 2 minutes. And they can't represent the concept of
"months" because a month doesn't have a single fixed amount of time.
However, an exact duration is still useful in certain contexts. Beyond
that, it serves as an interoperability point due to the presence of an
unsigned exact duration type in the standard library. Because of that,
Jiff provides TryFrom trait implementations for converting to and from a
std::time::Duration (and, of course, a SignedDuration). For example, to
convert from a std::time::Duration to a Span:
use Duration;
use ;
let duration = new;
let span = try_from?;
// A duration-to-span conversion always results in a span with
// non-zero units no bigger than seconds.
assert_eq!;
// Note that the conversion is fallible! For example:
assert!;
// At present, a Jiff `Span` can only represent a range of time equal to
// the range of time expressible via minimum and maximum Jiff timestamps.
// Which is roughly -9999-01-01 to 9999-12-31, or ~20,000 years.
assert!;
# Ok::
And to convert from a Span to a std::time::Duration:
use Duration;
use ;
let span = 86_400.seconds
.milliseconds
.microseconds
.nanoseconds;
let duration = try_from?;
assert_eq!;
# Ok::
Note that an error will occur when converting a Span to a
std::time::Duration using the TryFrom trait implementation with units
bigger than hours:
use Duration;
use ToSpan;
let span = 2.days.hours;
assert_eq!;
# Ok::
Similar code can be written for SignedDuration as well.
If you need to convert such spans, then as the error suggests, you'll need
to use Span::to_duration with a relative date.
And note that since a Span is signed and a std::time::Duration is unsigned,
converting a negative Span to std::time::Duration will always fail. One can use
Span::signum to get the sign of the span and Span::abs to make the
span positive before converting it to a Duration:
use Duration;
use ;
let span = -86_400.seconds.nanoseconds;
let = ;
assert_eq!;
# Ok::
Or, consider using Jiff's own SignedDuration instead:
# // See: https://github.com/rust-lang/rust/pull/121364
#
use ;
let span = -86_400.seconds.nanoseconds;
let duration = try_from?;
assert_eq!;
# Ok::
Implementations
impl Span
fn try_years<I: Into<i64>>(self: Self, years: I) -> Result<Span, Error>Set the number of years on this span. The value may be negative.
The panicking version of this method is
Span::years.Errors
This returns an error when the number of years is too small or too big. The minimum value is
-19,998. The maximum value is19,998.fn try_months<I: Into<i64>>(self: Self, months: I) -> Result<Span, Error>Set the number of months on this span. The value may be negative.
The panicking version of this method is
Span::months.Errors
This returns an error when the number of months is too small or too big. The minimum value is
-239,976. The maximum value is239,976.fn try_weeks<I: Into<i64>>(self: Self, weeks: I) -> Result<Span, Error>Set the number of weeks on this span. The value may be negative.
The panicking version of this method is
Span::weeks.Errors
This returns an error when the number of weeks is too small or too big. The minimum value is
-1,043,497. The maximum value is1_043_497.fn try_days<I: Into<i64>>(self: Self, days: I) -> Result<Span, Error>Set the number of days on this span. The value may be negative.
The panicking version of this method is
Span::days.Errors
This returns an error when the number of days is too small or too big. The minimum value is
-7,304,484. The maximum value is7,304,484.fn try_hours<I: Into<i64>>(self: Self, hours: I) -> Result<Span, Error>Set the number of hours on this span. The value may be negative.
The panicking version of this method is
Span::hours.Errors
This returns an error when the number of hours is too small or too big. The minimum value is
-175,307,616. The maximum value is175,307,616.fn try_minutes<I: Into<i64>>(self: Self, minutes: I) -> Result<Span, Error>Set the number of minutes on this span. The value may be negative.
The panicking version of this method is
Span::minutes.Errors
This returns an error when the number of minutes is too small or too big. The minimum value is
-10,518,456,960. The maximum value is10,518,456,960.fn try_seconds<I: Into<i64>>(self: Self, seconds: I) -> Result<Span, Error>Set the number of seconds on this span. The value may be negative.
The panicking version of this method is
Span::seconds.Errors
This returns an error when the number of seconds is too small or too big. The minimum value is
-631,107,417,600. The maximum value is631,107,417,600.fn try_milliseconds<I: Into<i64>>(self: Self, milliseconds: I) -> Result<Span, Error>Set the number of milliseconds on this span. The value may be negative.
The panicking version of this method is
Span::milliseconds.Errors
This returns an error when the number of milliseconds is too small or too big. The minimum value is
-631,107,417,600,000. The maximum value is631,107,417,600,000.fn try_microseconds<I: Into<i64>>(self: Self, microseconds: I) -> Result<Span, Error>Set the number of microseconds on this span. The value may be negative.
The panicking version of this method is
Span::microseconds.Errors
This returns an error when the number of microseconds is too small or too big. The minimum value is
-631,107,417,600,000,000. The maximum value is631,107,417,600,000,000.fn try_nanoseconds<I: Into<i64>>(self: Self, nanoseconds: I) -> Result<Span, Error>Set the number of nanoseconds on this span. The value may be negative.
Note that unlike all other units, a 64-bit integer number of nanoseconds is not big enough to represent all possible spans between all possible datetimes supported by Jiff. This means, for example, that computing a span between two datetimes that are far enough apart and requesting a largest unit of
Unit::Nanosecond, might return an error due to lack of precision.The panicking version of this method is
Span::nanoseconds.Errors
This returns an error when the number of nanoseconds is too small or too big. The minimum value is
-9,223,372,036,854,775,807. The maximum value is9,223,372,036,854,775,807.
impl Span
fn get_years(self: &Self) -> i16Returns the number of year units in this span.
Note that this is not the same as the total number of years in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 3.years.months; assert_eq!; assert_eq!; # Ok::fn get_months(self: &Self) -> i32Returns the number of month units in this span.
Note that this is not the same as the total number of months in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 7.months.days; assert_eq!; assert_eq!; # Ok::fn get_weeks(self: &Self) -> i32Returns the number of week units in this span.
Note that this is not the same as the total number of weeks in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 3.weeks.days; assert_eq!; assert_eq!; # Ok::fn get_days(self: &Self) -> i32Returns the number of day units in this span.
Note that this is not the same as the total number of days in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 3.days.hours; assert_eq!; let zdt: Zoned = "2024-03-07[America/New_York]".parse?; assert_eq!; # Ok::fn get_hours(self: &Self) -> i32Returns the number of hour units in this span.
Note that this is not the same as the total number of hours in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 3.hours.minutes; assert_eq!; assert_eq!; # Ok::fn get_minutes(self: &Self) -> i64Returns the number of minute units in this span.
Note that this is not the same as the total number of minutes in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 3.minutes.seconds; assert_eq!; assert_eq!; # Ok::fn get_seconds(self: &Self) -> i64Returns the number of second units in this span.
Note that this is not the same as the total number of seconds in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 3.seconds.milliseconds; assert_eq!; assert_eq!; # Ok::fn get_milliseconds(self: &Self) -> i64Returns the number of millisecond units in this span.
Note that this is not the same as the total number of milliseconds in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 3.milliseconds.microseconds; assert_eq!; assert_eq!; # Ok::fn get_microseconds(self: &Self) -> i64Returns the number of microsecond units in this span.
Note that this is not the same as the total number of microseconds in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 3.microseconds.nanoseconds; assert_eq!; assert_eq!; # Ok::fn get_nanoseconds(self: &Self) -> i64Returns the number of nanosecond units in this span.
Note that this is not the same as the total number of nanoseconds in the span. To get that, you'll need to use either
Span::roundorSpan::total.Example
use ; let span = 3.microseconds.nanoseconds; assert_eq!; assert_eq!; # Ok::
impl Span
fn new() -> SpanCreates a new span representing a zero duration. That is, a duration in which no time has passed.
fn years<I: Into<i64>>(self: Self, years: I) -> SpanSet the number of years on this span. The value may be negative.
The fallible version of this method is
Span::try_years.Panics
This panics when the number of years is too small or too big. The minimum value is
-19,998. The maximum value is19,998.fn months<I: Into<i64>>(self: Self, months: I) -> SpanSet the number of months on this span. The value may be negative.
The fallible version of this method is
Span::try_months.Panics
This panics when the number of months is too small or too big. The minimum value is
-239,976. The maximum value is239,976.fn weeks<I: Into<i64>>(self: Self, weeks: I) -> SpanSet the number of weeks on this span. The value may be negative.
The fallible version of this method is
Span::try_weeks.Panics
This panics when the number of weeks is too small or too big. The minimum value is
-1,043,497. The maximum value is1_043_497.fn days<I: Into<i64>>(self: Self, days: I) -> SpanSet the number of days on this span. The value may be negative.
The fallible version of this method is
Span::try_days.Panics
This panics when the number of days is too small or too big. The minimum value is
-7,304,484. The maximum value is7,304,484.fn hours<I: Into<i64>>(self: Self, hours: I) -> SpanSet the number of hours on this span. The value may be negative.
The fallible version of this method is
Span::try_hours.Panics
This panics when the number of hours is too small or too big. The minimum value is
-175,307,616. The maximum value is175,307,616.fn minutes<I: Into<i64>>(self: Self, minutes: I) -> SpanSet the number of minutes on this span. The value may be negative.
The fallible version of this method is
Span::try_minutes.Panics
This panics when the number of minutes is too small or too big. The minimum value is
-10,518,456,960. The maximum value is10,518,456,960.fn seconds<I: Into<i64>>(self: Self, seconds: I) -> SpanSet the number of seconds on this span. The value may be negative.
The fallible version of this method is
Span::try_seconds.Panics
This panics when the number of seconds is too small or too big. The minimum value is
-631,107,417,600. The maximum value is631,107,417,600.fn milliseconds<I: Into<i64>>(self: Self, milliseconds: I) -> SpanSet the number of milliseconds on this span. The value may be negative.
The fallible version of this method is
Span::try_milliseconds.Panics
This panics when the number of milliseconds is too small or too big. The minimum value is
-631,107,417,600,000. The maximum value is631,107,417,600,000.fn microseconds<I: Into<i64>>(self: Self, microseconds: I) -> SpanSet the number of microseconds on this span. The value may be negative.
The fallible version of this method is
Span::try_microseconds.Panics
This panics when the number of microseconds is too small or too big. The minimum value is
-631,107,417,600,000,000. The maximum value is631,107,417,600,000,000.fn nanoseconds<I: Into<i64>>(self: Self, nanoseconds: I) -> SpanSet the number of nanoseconds on this span. The value may be negative.
Note that unlike all other units, a 64-bit integer number of nanoseconds is not big enough to represent all possible spans between all possible datetimes supported by Jiff. This means, for example, that computing a span between two datetimes that are far enough apart and requesting a largest unit of
Unit::Nanosecond, might return an error due to lack of precision.The fallible version of this method is
Span::try_nanoseconds.Panics
This panics when the number of nanoseconds is too small or too big. The minimum value is
-9,223,372,036,854,775,807. The maximum value is9,223,372,036,854,775,807.
impl Span
fn abs(self: Self) -> SpanReturns a new span that is the absolute value of this span.
If this span is zero or positive, then this is a no-op.
Example
use ToSpan; let span = -100.seconds; assert_eq!; let span = span.abs; assert_eq!;fn negate(self: Self) -> SpanReturns a new span that negates this span.
If this span is zero, then this is a no-op. If this span is negative, then the returned span is positive. If this span is positive, then the returned span is negative.
Example
use ToSpan; let span = 100.days; assert_eq!; let span = span.negate; assert_eq!;Example: available via the negation operator
This routine can also be used via
-:use ToSpan; let span = 100.days; assert_eq!; let span = -span; assert_eq!;fn signum(self: Self) -> i8Returns the "sign number" or "signum" of this span.
The number returned is
-1when this span is negative,0when this span is zero and1when this span is positive.fn is_positive(self: Self) -> boolReturns true if and only if this span is positive.
This returns false when the span is zero or negative.
Example
use ToSpan; assert!; assert!;fn is_negative(self: Self) -> boolReturns true if and only if this span is negative.
This returns false when the span is zero or positive.
Example
use ToSpan; assert!; assert!;fn is_zero(self: Self) -> boolReturns true if and only if every field in this span is set to
0.Example
use ; assert!; assert!; assert!; assert!; assert!;fn fieldwise(self: Self) -> SpanFieldwiseReturns this
Spanas a value with a type that implements theHash,EqandPartialEqtraits in a fieldwise fashion.A
SpanFieldwiseis meant to make it easy to compare two spans in a "dumb" way based purely on its unit values. This is distinct from something likeSpan::comparethat performs a comparison on the actual elapsed time of two spans.It is generally discouraged to use
SpanFieldwisesince spans that represent an equivalent elapsed amount of time may compare unequal. However, in some cases, it is useful to be able to assert precise field values. For example, Jiff itself makes heavy use of fieldwise comparisons for tests.Example: the difference between
SpanFieldwiseandSpan::compareIn short,
SpanFieldwiseconsiders2 hoursand120 minutesto be distinct values, butSpan::compareconsiders them to be equivalent:use Ordering; use ToSpan; assert_ne!; assert_eq!; # Ok::fn checked_mul(self: Self, rhs: i64) -> Result<Span, Error>Multiplies each field in this span by a given integer.
If this would cause any individual field in this span to overflow, then this returns an error.
Example
use ToSpan; let span = 4.days.seconds; assert_eq!; assert_eq!; // Notice that no re-balancing is done. It's "just" multiplication. assert_eq!; let span = 10_000.years; // too big! assert!; # Ok::Example: available via the multiplication operator
This method can be used via the
*operator. Note though that a panic happens on overflow.use ToSpan; let span = 4.days.seconds; assert_eq!; assert_eq!; assert_eq!; assert_eq!; # Ok::fn checked_add<'a, A: Into<SpanArithmetic<'a>>>(self: &Self, options: A) -> Result<Span, Error>Adds a span to this one and returns the sum as a new span.
When adding a span with units greater than hours, callers must provide a relative datetime to anchor the spans.
Arithmetic proceeds as specified in RFC 5545. Bigger units are added together before smaller units.
This routine accepts anything that implements
Into<SpanArithmetic>. There are some trait implementations that make using this routine ergonomic:From<Span> for SpanArithmeticadds the given span to this one.From<(Span, civil::Date)> for SpanArithmeticadds the given span to this one relative to the given date. There are alsoFromimplementations forcivil::DateTimeandZoned.
This also works with different duration types, such as
SignedDurationandstd::time::Duration, via additional trait implementations:From<SignedDuration> for SpanArithmeticadds the given duration to this one.From<(SignedDuration, civil::Date)> for SpanArithmeticadds the given duration to this one relative to the given date. There are alsoFromimplementations forcivil::DateTimeandZoned.
And similarly for
std::time::Duration.Adding a negative span is equivalent to subtracting its absolute value.
The largest non-zero unit in the span returned is at most the largest non-zero unit among the two spans being added. For an absolute duration, its "largest" unit is considered to be nanoseconds.
The sum returned is automatically re-balanced so that the span is not "bottom heavy."
Errors
This returns an error when adding the two spans would overflow any individual field of a span. This will also return an error if either of the spans have non-zero units of days or greater and no relative reference time is provided.
Callers may use
SpanArithmetic::days_are_24_hoursas a special marker instead of providing a relative civil date to indicate that all days should be 24 hours long. This also results in treating all weeks as seven 24 hour days (168 hours).Example
use ToSpan; assert_eq!; # Ok::Example: re-balancing
This example shows how units are automatically rebalanced into bigger units when appropriate.
use ToSpan; let span1 = 2.hours.minutes; let span2 = 2.minutes; assert_eq!; # Ok::Example: days are not assumed to be 24 hours by default
When dealing with units involving days or weeks, one must either provide a relative datetime (shown in the following examples) or opt into invariant 24 hour days:
use ; let span1 = 2.days.hours; let span2 = 2.hours; assert_eq!; # Ok::Example: adding spans with calendar units
If you try to add two spans with calendar units without specifying a relative datetime, you'll get an error:
use ToSpan; let span1 = 1.month.days; let span2 = 15.days; assert!;A relative datetime is needed because calendar spans may correspond to different actual durations depending on where the span begins:
use ; let span1 = 1.month.days; let span2 = 15.days; // 1 month from March 1 is 31 days... assert_eq!; // ... but 1 month from April 1 is 30 days! assert_eq!; # Ok::Example: error on overflow
Adding two spans can overflow, and this will result in an error:
use ToSpan; assert!;Example: adding an absolute duration to a span
This shows how one isn't limited to just adding two spans together. One can also add absolute durations to a span.
use Duration; use ; assert_eq!; assert_eq!; # Ok::Note that even when adding an absolute duration, if the span contains non-uniform units, you still need to provide a relative datetime:
use ; // Might be 1 month or less than 1 month! let dur = from_hours; // No relative datetime provided even when the span // contains non-uniform units results in an error. assert!; // In this case, 30 days is one month (April). assert_eq!; // In this case, 30 days is less than one month (May). assert_eq!; # Ok::fn checked_sub<'a, A: Into<SpanArithmetic<'a>>>(self: &Self, options: A) -> Result<Span, Error>This routine is identical to
Span::checked_addwith the given duration negated.Errors
This has the same error conditions as
Span::checked_add.Example
use Duration; use ; assert_eq!; assert_eq!; assert_eq!; # Ok::fn compare<'a, C: Into<SpanCompare<'a>>>(self: &Self, options: C) -> Result<Ordering, Error>Compares two spans in terms of how long they are. Negative spans are considered shorter than the zero span.
Two spans compare equal when they correspond to the same duration of time, even if their individual fields are different. This is in contrast to the
Eqtrait implementation ofSpan, which performs exact field-wise comparisons. This split exists because the comparison provided by this routine is "heavy" in that it may need to do datetime arithmetic to return an answer. In contrast, theEqtrait implementation is "cheap."This routine accepts anything that implements
Into<SpanCompare>. There are some trait implementations that make using this routine ergonomic:From<Span> for SpanComparecompares the given span to this one.From<(Span, civil::Date)> for SpanArithmeticcompares the given span to this one relative to the given date. There are alsoFromimplementations forcivil::DateTimeandZoned.
Errors
If either of the spans being compared have a non-zero calendar unit (units bigger than hours), then this routine requires a relative datetime. If one is not provided, then an error is returned.
An error can also occur when adding either span to the relative datetime given results in overflow.
Callers may use
SpanArithmetic::days_are_24_hoursas a special marker instead of providing a relative civil date to indicate that all days should be 24 hours long. This also results in treating all weeks as seven 24 hour days (168 hours).Example
use ToSpan; let span1 = 3.hours; let span2 = 180.minutes; assert_eq!; // But notice that the two spans are not equal via `Eq`: assert_ne!; # Ok::Example: negative spans are less than zero
use ToSpan; let span1 = -1.second; let span2 = 0.seconds; assert_eq!; # Ok::Example: comparisons take DST into account
When a relative datetime is time zone aware, then DST is taken into account when comparing spans:
use ; let span1 = 79.hours.minutes; let span2 = 3.days.hours.seconds; let span3 = 3.days.hours.minutes; let relative: Zoned = "2020-11-01T00-07[America/Los_Angeles]".parse?; let mut spans = ; spans.sort_by; assert_eq!; // Compare with the result of sorting without taking DST into account. // We can that by providing a relative civil date: let relative = date; spans.sort_by; assert_eq!; # Ok::See the examples for
Span::totalif you want to sort spans without anunwrap()call.fn total<'a, T: Into<SpanTotal<'a>>>(self: &Self, options: T) -> Result<f64, Error>Returns a floating point number representing the total number of a specific unit (as given) in this span. If the span is not evenly divisible by the requested units, then the number returned may have a fractional component.
This routine accepts anything that implements
Into<SpanTotal>. There are some trait implementations that make using this routine ergonomic:From<Unit> for SpanTotalcomputes a total for the given unit in this span.From<(Unit, civil::Date)> for SpanTotalcomputes a total for the given unit in this span, relative to the given date. There are alsoFromimplementations forcivil::DateTimeandZoned.
Errors
If this span has any non-zero calendar unit (units bigger than hours), then this routine requires a relative datetime. If one is not provided, then an error is returned.
An error can also occur when adding the span to the relative datetime given results in overflow.
Callers may use
SpanArithmetic::days_are_24_hoursas a special marker instead of providing a relative civil date to indicate that all days should be 24 hours long. This also results in treating all weeks as seven 24 hour days (168 hours).Example
This example shows how to find the number of seconds in a particular span:
use ; let span = 3.hours.minutes; assert_eq!; # Ok::Example: 24 hour days
This shows how to find the total number of 24 hour days in
123,456,789seconds.use ; let span = 123_456_789.seconds; assert_eq!; # Ok::Example: DST is taken into account
The month of March 2024 in
America/New_Yorkhad 31 days, but one of those days was 23 hours long due a transition into daylight saving time:use ; let span = 744.hours; let relative = date.in_tz?; // Because of the short day, 744 hours is actually a little *more* than // 1 month starting from 2024-03-01. assert_eq!; # Ok::Now compare what happens when the relative datetime is civil and not time zone aware:
use ; let span = 744.hours; let relative = date; assert_eq!; # Ok::Example: infallible sorting
The sorting example in
Span::comparehas to useunwrap()in itssort_by(..)call becauseSpan::comparemay fail and there is no "fallible" sorting routine in Rust's standard library (as of 2024-07-07). While the ways in whichSpan::comparecan fail for a valid configuration are limited to overflow for "extreme" values, it is possible to sort spans infallibly by computing floating point representations for each span up-front:use ; let span1 = 79.hours.minutes; let span2 = 3.days.hours.seconds; let span3 = 3.days.hours.minutes; let relative: Zoned = "2020-11-01T00-07[America/Los_Angeles]".parse?; let mut spans = ; spans.sort_by; assert_eq!; // Compare with the result of sorting without taking DST into account. // We do that here by providing a relative civil date. let relative: Date = "2020-11-01".parse?; let mut spans = ; spans.sort_by; assert_eq!; # Ok::fn round<'a, R: Into<SpanRound<'a>>>(self: Self, options: R) -> Result<Span, Error>Returns a new span that is balanced and rounded.
Rounding a span has a number of parameters, all of which are optional. When no parameters are given, then no rounding or balancing is done, and the span as given is returned. That is, it's a no-op.
The parameters are, in brief:
SpanRound::largestsets the largestUnitthat is allowed to be non-zero in the span returned. When only the largest unit is set, rounding itself doesn't occur and instead the span is merely balanced.SpanRound::smallestsets the smallestUnitthat is allowed to be non-zero in the span returned. By default, it is set toUnit::Nanosecond, i.e., no rounding occurs. When the smallest unit is set to something bigger than nanoseconds, then the non-zero units in the span smaller than the smallest unit are used to determine how the span should be rounded. For example, rounding1 hour 59 minutesto the nearest hour using the default rounding mode would produce2 hours.SpanRound::modedetermines how to handle the remainder when rounding. The default isRoundMode::HalfExpand, which corresponds to how you were taught to round in school. Alternative modes, likeRoundMode::Trunc, exist too. For example, a truncating rounding of1 hour 59 minutesto the nearest hour would produce1 hour.SpanRound::incrementsets the rounding granularity to use for the configured smallest unit. For example, if the smallest unit is minutes and the increment is 5, then the span returned will always have its minute units set to a multiple of5.SpanRound::relativesets the datetime from which to interpret the span. This is required when rounding spans with calendar units (years, months or weeks). When a relative datetime is time zone aware, then rounding accounts for the fact that not all days are 24 hours long. When a relative datetime is omitted or is civil (not time zone aware), then days are always 24 hours long.
Constructing a
SpanRoundThis routine accepts anything that implements
Into<SpanRound>. There are a few key trait implementations that make this convenient:From<Unit> for SpanRoundwill construct a rounding configuration where the smallest unit is set to the one given.From<(Unit, i64)> for SpanRoundwill construct a rounding configuration where the smallest unit and the rounding increment are set to the ones given.
To set other options (like the largest unit, the rounding mode and the relative datetime), one must explicitly create a
SpanRoundand pass it to this routine.Errors
In general, there are two main ways for rounding to fail: an improper configuration like trying to round a span with calendar units but without a relative datetime, or when overflow occurs. Overflow can occur when the span, added to the relative datetime if given, would exceed the minimum or maximum datetime values. Overflow can also occur if the span is too big to fit into the requested unit configuration. For example, a span like
19_998.years()cannot be represented with a 64-bit integer number of nanoseconds.Callers may use
SpanArithmetic::days_are_24_hoursas a special marker instead of providing a relative civil date to indicate that all days should be 24 hours long. This also results in treating all weeks as seven 24 hour days (168 hours).Example: balancing
This example demonstrates balancing, not rounding. And in particular, this example shows how to balance a span as much as possible (i.e., with units of hours or smaller) without needing to specify a relative datetime:
use ; let span = 123_456_789_123_456_789i64.nanoseconds; assert_eq!; # Ok::Or you can opt into invariant 24-hour days (and 7-day weeks) without a relative date with [
SpanRound::days_are_24_hours]:use ; let span = 123_456_789_123_456_789i64.nanoseconds; assert_eq!; # Ok::Example: balancing and rounding
This example is like the one before it, but where we round to the nearest second:
use ; let span = 123_456_789_123_456_789i64.nanoseconds; assert_eq!; # Ok::Or, just rounding to the nearest hour can make use of the
From<Unit> for SpanRoundtrait implementation:use ; let span = 123_456_789_123_456_789i64.nanoseconds; assert_eq!; # Ok::Example: balancing with a relative datetime
Even with calendar units, so long as a relative datetime is provided, it's easy to turn days into bigger units:
use ; let span = 1_000.days; let relative = date; let options = new.largest.relative; assert_eq!; # Ok::Example: round to the nearest half-hour
use ; let span: Span = "PT23h50m3.123s".parse?; assert_eq!; # Ok::Example: yearly quarters in a span
This example shows how to find how many full 3 month quarters are in a particular span of time.
use ; let span1 = 10.months.days; let round = new .smallest .increment .mode // A relative datetime must be provided when // rounding involves calendar units. .relative; let span2 = span1.round?; assert_eq!; # Ok::fn to_duration<'a, R: Into<SpanRelativeTo<'a>>>(self: &Self, relative: R) -> Result<SignedDuration, Error>Converts a
Spanto aSignedDurationrelative to the date given.In most cases, it is unlikely that you'll need to use this routine to convert a
Spanto aSignedDuration. Namely, by default:Zoned::untilguarantees that the biggest non-zero unit is hours.Timestamp::untilguarantees that the biggest non-zero unit is seconds.DateTime::untilguarantees that the biggest non-zero unit is days.Date::untilguarantees that the biggest non-zero unit is days.Time::untilguarantees that the biggest non-zero unit is hours.
In the above, only
DateTime::untilandDate::untilreturn calendar units by default. In which case, one may passSpanRelativeTo::days_are_24_hoursor an actual relative date to resolve the length of a day.Of course, any of the above can be changed by asking, for example,
Zoned::untilto return units up to years.Errors
This returns an error if adding this span to the date given results in overflow. This can also return an error if one uses
SpanRelativeTo::days_are_24_hourswith aSpanthat has non-zero units greater than weeks.Example: converting a span with calendar units to a
SignedDurationThis compares the number of seconds in a non-leap year with a leap year:
use ; let span = 1.year; let duration = span.to_duration?; assert_eq!; let duration = span.to_duration?; assert_eq!; # Ok::Example: converting a span without a relative datetime
If for some reason it doesn't make sense to include a relative datetime, you can use this routine to convert a
Spanwith units up to weeks to aSignedDurationvia theSpanRelativeTo::days_are_24_hoursmarker:use ; let span = 1.week.days; let duration = span.to_duration?; assert_eq!; # Ok::
impl Clone for Span
fn clone(self: &Self) -> Span
impl Copy for Span
impl Debug for Span
fn fmt(self: &Self, f: &mut Formatter<'_>) -> Result
impl Default for Span
fn default() -> Span
impl Display for Span
fn fmt(self: &Self, f: &mut Formatter<'_>) -> Result
impl Freeze for Span
impl From for Span
fn from(span: SpanFieldwise) -> Span
impl FromStr for Span
fn from_str(string: &str) -> Result<Span, Error>
impl Mul for Span
fn mul(self: Self, rhs: i64) -> Span
impl Neg for Span
fn neg(self: Self) -> Span
impl PartialEq for Span
fn eq(self: &Self, rhs: &SpanFieldwise) -> bool
impl RefUnwindSafe for Span
impl Send for Span
impl Serialize for Span
fn serialize<S: serde::Serializer>(self: &Self, serializer: S) -> Result<<S as >::Ok, <S as >::Error>
impl Sync for Span
impl TryFrom for Span
fn try_from(d: SignedDuration) -> Result<Span, Error>
impl TryFrom for Span
fn try_from(d: UnsignedDuration) -> Result<Span, Error>
impl Unpin for Span
impl UnsafeUnpin for Span
impl UnwindSafe for Span
impl<'de> Deserialize for Span
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Span, <D as >::Error>
impl<T> Any for Span
fn type_id(self: &Self) -> TypeId
impl<T> Borrow for Span
fn borrow(self: &Self) -> &T
impl<T> BorrowMut for Span
fn borrow_mut(self: &mut Self) -> &mut T
impl<T> CloneToUninit for Span
unsafe fn clone_to_uninit(self: &Self, dest: *mut u8)
impl<T> DeserializeOwned for Span
impl<T> From for Span
fn from(t: T) -> TReturns the argument unchanged.
impl<T> ToOwned for Span
fn to_owned(self: &Self) -> Tfn clone_into(self: &Self, target: &mut T)
impl<T> ToString for Span
fn to_string(self: &Self) -> String
impl<T, U> Into for Span
fn into(self: Self) -> UCalls
U::from(self).That is, this conversion is whatever the implementation of
[From]<T> for Uchooses to do.
impl<T, U> TryFrom for Span
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
impl<T, U> TryInto for Span
fn try_into(self: Self) -> Result<U, <U as TryFrom<T>>::Error>