jiff/
span.rs

1use core::{cmp::Ordering, time::Duration as UnsignedDuration};
2
3use crate::{
4    civil::{Date, DateTime, Time},
5    duration::{Duration, SDuration},
6    error::{span::Error as E, Error, ErrorContext},
7    fmt::{friendly, temporal},
8    tz::TimeZone,
9    util::{
10        borrow::DumbCow,
11        rangeint::{ri64, ri8, RFrom, RInto, TryRFrom, TryRInto},
12        round::increment,
13        t::{self, Constant, NoUnits, NoUnits128, Sign, C},
14    },
15    RoundMode, SignedDuration, Timestamp, Zoned,
16};
17
18/// A macro helper, only used in tests, for comparing spans for equality.
19#[cfg(test)]
20macro_rules! span_eq {
21    ($span1:expr, $span2:expr $(,)?) => {{
22        assert_eq!($span1.fieldwise(), $span2.fieldwise());
23    }};
24    ($span1:expr, $span2:expr, $($tt:tt)*) => {{
25        assert_eq!($span1.fieldwise(), $span2.fieldwise(), $($tt)*);
26    }};
27}
28
29#[cfg(test)]
30pub(crate) use span_eq;
31
32/// A span of time represented via a mixture of calendar and clock units.
33///
34/// A span represents a duration of time in units of years, months, weeks,
35/// days, hours, minutes, seconds, milliseconds, microseconds and nanoseconds.
36/// Spans are used to as inputs to routines like
37/// [`Zoned::checked_add`] and [`Date::saturating_sub`],
38/// and are also outputs from routines like
39/// [`Timestamp::since`] and [`DateTime::until`].
40///
41/// # Range of spans
42///
43/// Except for nanoseconds, each unit can represent the full span of time
44/// expressible via any combination of datetime supported by Jiff. For example:
45///
46/// ```
47/// use jiff::{civil::{DateTime, DateTimeDifference}, ToSpan, Unit};
48///
49/// let options = DateTimeDifference::new(DateTime::MAX).largest(Unit::Year);
50/// assert_eq!(DateTime::MIN.until(options)?.get_years(), 19_998);
51///
52/// let options = options.largest(Unit::Day);
53/// assert_eq!(DateTime::MIN.until(options)?.get_days(), 7_304_483);
54///
55/// let options = options.largest(Unit::Microsecond);
56/// assert_eq!(
57///     DateTime::MIN.until(options)?.get_microseconds(),
58///     631_107_417_599_999_999i64,
59/// );
60///
61/// let options = options.largest(Unit::Nanosecond);
62/// // Span is too big, overflow!
63/// assert!(DateTime::MIN.until(options).is_err());
64///
65/// # Ok::<(), Box<dyn std::error::Error>>(())
66/// ```
67///
68/// # Building spans
69///
70/// A default or empty span corresponds to a duration of zero time:
71///
72/// ```
73/// use jiff::Span;
74///
75/// assert!(Span::new().is_zero());
76/// assert!(Span::default().is_zero());
77/// ```
78///
79/// Spans are `Copy` types that have mutator methods on them for creating new
80/// spans:
81///
82/// ```
83/// use jiff::Span;
84///
85/// let span = Span::new().days(5).hours(8).minutes(1);
86/// assert_eq!(span.to_string(), "P5DT8H1M");
87/// ```
88///
89/// But Jiff provides a [`ToSpan`] trait that defines extension methods on
90/// primitive signed integers to make span creation terser:
91///
92/// ```
93/// use jiff::ToSpan;
94///
95/// let span = 5.days().hours(8).minutes(1);
96/// assert_eq!(span.to_string(), "P5DT8H1M");
97/// // singular units on integers can be used too:
98/// let span = 1.day().hours(8).minutes(1);
99/// assert_eq!(span.to_string(), "P1DT8H1M");
100/// ```
101///
102/// # Negative spans
103///
104/// A span may be negative. All of these are equivalent:
105///
106/// ```
107/// use jiff::{Span, ToSpan};
108///
109/// let span = -Span::new().days(5);
110/// assert_eq!(span.to_string(), "-P5D");
111///
112/// let span = Span::new().days(5).negate();
113/// assert_eq!(span.to_string(), "-P5D");
114///
115/// let span = Span::new().days(-5);
116/// assert_eq!(span.to_string(), "-P5D");
117///
118/// let span = -Span::new().days(-5).negate();
119/// assert_eq!(span.to_string(), "-P5D");
120///
121/// let span = -5.days();
122/// assert_eq!(span.to_string(), "-P5D");
123///
124/// let span = (-5).days();
125/// assert_eq!(span.to_string(), "-P5D");
126///
127/// let span = -(5.days());
128/// assert_eq!(span.to_string(), "-P5D");
129/// ```
130///
131/// The sign of a span applies to the entire span. When a span is negative,
132/// then all of its units are negative:
133///
134/// ```
135/// use jiff::ToSpan;
136///
137/// let span = -5.days().hours(10).minutes(1);
138/// assert_eq!(span.get_days(), -5);
139/// assert_eq!(span.get_hours(), -10);
140/// assert_eq!(span.get_minutes(), -1);
141/// ```
142///
143/// And if any of a span's units are negative, then the entire span is regarded
144/// as negative:
145///
146/// ```
147/// use jiff::ToSpan;
148///
149/// // It's the same thing.
150/// let span = (-5).days().hours(-10).minutes(-1);
151/// assert_eq!(span.get_days(), -5);
152/// assert_eq!(span.get_hours(), -10);
153/// assert_eq!(span.get_minutes(), -1);
154///
155/// // Still the same. All negative.
156/// let span = 5.days().hours(-10).minutes(1);
157/// assert_eq!(span.get_days(), -5);
158/// assert_eq!(span.get_hours(), -10);
159/// assert_eq!(span.get_minutes(), -1);
160///
161/// // But this is not! The negation in front applies
162/// // to the entire span, which was already negative
163/// // by virtue of at least one of its units being
164/// // negative. So the negation operator in front turns
165/// // the span positive.
166/// let span = -5.days().hours(-10).minutes(-1);
167/// assert_eq!(span.get_days(), 5);
168/// assert_eq!(span.get_hours(), 10);
169/// assert_eq!(span.get_minutes(), 1);
170/// ```
171///
172/// You can also ask for the absolute value of a span:
173///
174/// ```
175/// use jiff::Span;
176///
177/// let span = Span::new().days(5).hours(10).minutes(1).negate().abs();
178/// assert_eq!(span.get_days(), 5);
179/// assert_eq!(span.get_hours(), 10);
180/// assert_eq!(span.get_minutes(), 1);
181/// ```
182///
183/// # Parsing and printing
184///
185/// The `Span` type provides convenient trait implementations of
186/// [`std::str::FromStr`] and [`std::fmt::Display`]:
187///
188/// ```
189/// use jiff::{Span, ToSpan};
190///
191/// let span: Span = "P2m10dT2h30m".parse()?;
192/// // By default, capital unit designator labels are used.
193/// // This can be changed with `jiff::fmt::temporal::SpanPrinter::lowercase`.
194/// assert_eq!(span.to_string(), "P2M10DT2H30M");
195///
196/// // Or use the "friendly" format by invoking the `Display` alternate:
197/// assert_eq!(format!("{span:#}"), "2mo 10d 2h 30m");
198///
199/// // Parsing automatically supports both the ISO 8601 and "friendly"
200/// // formats. Note that we use `Span::fieldwise` to create a `Span` that
201/// // compares based on each field. To compare based on total duration, use
202/// // `Span::compare` or `Span::total`.
203/// let span: Span = "2mo 10d 2h 30m".parse()?;
204/// assert_eq!(span, 2.months().days(10).hours(2).minutes(30).fieldwise());
205/// let span: Span = "2 months, 10 days, 2 hours, 30 minutes".parse()?;
206/// assert_eq!(span, 2.months().days(10).hours(2).minutes(30).fieldwise());
207///
208/// # Ok::<(), Box<dyn std::error::Error>>(())
209/// ```
210///
211/// The format supported is a variation (nearly a subset) of the duration
212/// format specified in [ISO 8601] _and_ a Jiff-specific "friendly" format.
213/// Here are more examples:
214///
215/// ```
216/// use jiff::{Span, ToSpan};
217///
218/// let spans = [
219///     // ISO 8601
220///     ("P40D", 40.days()),
221///     ("P1y1d", 1.year().days(1)),
222///     ("P3dT4h59m", 3.days().hours(4).minutes(59)),
223///     ("PT2H30M", 2.hours().minutes(30)),
224///     ("P1m", 1.month()),
225///     ("P1w", 1.week()),
226///     ("P1w4d", 1.week().days(4)),
227///     ("PT1m", 1.minute()),
228///     ("PT0.0021s", 2.milliseconds().microseconds(100)),
229///     ("PT0s", 0.seconds()),
230///     ("P0d", 0.seconds()),
231///     (
232///         "P1y1m1dT1h1m1.1s",
233///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
234///     ),
235///     // Jiff's "friendly" format
236///     ("40d", 40.days()),
237///     ("40 days", 40.days()),
238///     ("1y1d", 1.year().days(1)),
239///     ("1yr 1d", 1.year().days(1)),
240///     ("3d4h59m", 3.days().hours(4).minutes(59)),
241///     ("3 days, 4 hours, 59 minutes", 3.days().hours(4).minutes(59)),
242///     ("3d 4h 59m", 3.days().hours(4).minutes(59)),
243///     ("2h30m", 2.hours().minutes(30)),
244///     ("2h 30m", 2.hours().minutes(30)),
245///     ("1mo", 1.month()),
246///     ("1w", 1.week()),
247///     ("1 week", 1.week()),
248///     ("1w4d", 1.week().days(4)),
249///     ("1 wk 4 days", 1.week().days(4)),
250///     ("1m", 1.minute()),
251///     ("0.0021s", 2.milliseconds().microseconds(100)),
252///     ("0s", 0.seconds()),
253///     ("0d", 0.seconds()),
254///     ("0 days", 0.seconds()),
255///     (
256///         "1y1mo1d1h1m1.1s",
257///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
258///     ),
259///     (
260///         "1yr 1mo 1day 1hr 1min 1.1sec",
261///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
262///     ),
263///     (
264///         "1 year, 1 month, 1 day, 1 hour, 1 minute 1.1 seconds",
265///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
266///     ),
267///     (
268///         "1 year, 1 month, 1 day, 01:01:01.1",
269///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
270///     ),
271/// ];
272/// for (string, span) in spans {
273///     let parsed: Span = string.parse()?;
274///     assert_eq!(
275///         span.fieldwise(),
276///         parsed.fieldwise(),
277///         "result of parsing {string:?}",
278///     );
279/// }
280///
281/// # Ok::<(), Box<dyn std::error::Error>>(())
282/// ```
283///
284/// For more details, see the [`fmt::temporal`](temporal) and
285/// [`fmt::friendly`](friendly) modules.
286///
287/// [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html
288///
289/// # Comparisons
290///
291/// A `Span` does not implement the `PartialEq` or `Eq` traits. These traits
292/// were implemented in an earlier version of Jiff, but they made it too
293/// easy to introduce bugs. For example, `120.minutes()` and `2.hours()`
294/// always correspond to the same total duration, but they have different
295/// representations in memory and so didn't compare equivalent.
296///
297/// The reason why the `PartialEq` and `Eq` trait implementations do not do
298/// comparisons with total duration is because it is fundamentally impossible
299/// to do such comparisons without a reference date in all cases.
300///
301/// However, it is undeniably occasionally useful to do comparisons based
302/// on the component fields, so long as such use cases can tolerate two
303/// different spans comparing unequal even when their total durations are
304/// equivalent. For example, many of the tests in Jiff (including the tests in
305/// the documentation) work by comparing a `Span` to an expected result. This
306/// is a good demonstration of when fieldwise comparisons are appropriate.
307///
308/// To do fieldwise comparisons with a span, use the [`Span::fieldwise`]
309/// method. This method creates a [`SpanFieldwise`], which is just a `Span`
310/// that implements `PartialEq` and `Eq` in a fieldwise manner. In other words,
311/// it's a speed bump to ensure this is the kind of comparison you actually
312/// want. For example:
313///
314/// ```
315/// use jiff::ToSpan;
316///
317/// assert_ne!(1.hour().fieldwise(), 60.minutes().fieldwise());
318/// // These also work since you only need one fieldwise span to do a compare:
319/// assert_ne!(1.hour(), 60.minutes().fieldwise());
320/// assert_ne!(1.hour().fieldwise(), 60.minutes());
321/// ```
322///
323/// This is because doing true comparisons requires arithmetic and a relative
324/// datetime in the general case, and which can fail due to overflow. This
325/// operation is provided via [`Span::compare`]:
326///
327/// ```
328/// use jiff::{civil::date, ToSpan};
329///
330/// // This doesn't need a reference date since it's only using time units.
331/// assert_eq!(1.hour().compare(60.minutes())?, std::cmp::Ordering::Equal);
332/// // But if you have calendar units, then you need a
333/// // reference date at minimum:
334/// assert!(1.month().compare(30.days()).is_err());
335/// assert_eq!(
336///     1.month().compare((30.days(), date(2025, 6, 1)))?,
337///     std::cmp::Ordering::Equal,
338/// );
339/// // A month can be a differing number of days!
340/// assert_eq!(
341///     1.month().compare((30.days(), date(2025, 7, 1)))?,
342///     std::cmp::Ordering::Greater,
343/// );
344///
345/// # Ok::<(), Box<dyn std::error::Error>>(())
346/// ```
347///
348/// # Arithmetic
349///
350/// Spans can be added or subtracted via [`Span::checked_add`] and
351/// [`Span::checked_sub`]:
352///
353/// ```
354/// use jiff::{Span, ToSpan};
355///
356/// let span1 = 2.hours().minutes(20);
357/// let span2: Span = "PT89400s".parse()?;
358/// assert_eq!(span1.checked_add(span2)?, 27.hours().minutes(10).fieldwise());
359///
360/// # Ok::<(), Box<dyn std::error::Error>>(())
361/// ```
362///
363/// When your spans involve calendar units, a relative datetime must be
364/// provided. (Because, for example, 1 month from March 1 is 31 days, but
365/// 1 month from April 1 is 30 days.)
366///
367/// ```
368/// use jiff::{civil::date, Span, ToSpan};
369///
370/// let span1 = 2.years().months(6).days(20);
371/// let span2 = 400.days();
372/// assert_eq!(
373///     span1.checked_add((span2, date(2023, 1, 1)))?,
374///     3.years().months(7).days(24).fieldwise(),
375/// );
376/// // The span changes when a leap year isn't included!
377/// assert_eq!(
378///     span1.checked_add((span2, date(2025, 1, 1)))?,
379///     3.years().months(7).days(23).fieldwise(),
380/// );
381///
382/// # Ok::<(), Box<dyn std::error::Error>>(())
383/// ```
384///
385/// # Rounding and balancing
386///
387/// Unlike datetimes, multiple distinct `Span` values can actually correspond
388/// to the same duration of time. For example, all of the following correspond
389/// to the same duration:
390///
391/// * 2 hours, 30 minutes
392/// * 150 minutes
393/// * 1 hour, 90 minutes
394///
395/// The first is said to be balanced. That is, its biggest non-zero unit cannot
396/// be expressed in an integer number of units bigger than hours. But the
397/// second is unbalanced because 150 minutes can be split up into hours and
398/// minutes. We call this sort of span a "top-heavy" unbalanced span. The third
399/// span is also unbalanced, but it's "bottom-heavy" and rarely used. Jiff
400/// will generally only produce spans of the first two types. In particular,
401/// most `Span` producing APIs accept a "largest" [`Unit`] parameter, and the
402/// result can be said to be a span "balanced up to the largest unit provided."
403///
404/// Balanced and unbalanced spans can be switched between as needed via
405/// the [`Span::round`] API by providing a rounding configuration with
406/// [`SpanRound::largest`]` set:
407///
408/// ```
409/// use jiff::{SpanRound, ToSpan, Unit};
410///
411/// let span = 2.hours().minutes(30);
412/// let unbalanced = span.round(SpanRound::new().largest(Unit::Minute))?;
413/// assert_eq!(unbalanced, 150.minutes().fieldwise());
414/// let balanced = unbalanced.round(SpanRound::new().largest(Unit::Hour))?;
415/// assert_eq!(balanced, 2.hours().minutes(30).fieldwise());
416///
417/// # Ok::<(), Box<dyn std::error::Error>>(())
418/// ```
419///
420/// Balancing can also be done as part of computing spans from two datetimes:
421///
422/// ```
423/// use jiff::{civil::date, ToSpan, Unit};
424///
425/// let zdt1 = date(2024, 7, 7).at(15, 23, 0, 0).in_tz("America/New_York")?;
426/// let zdt2 = date(2024, 11, 5).at(8, 0, 0, 0).in_tz("America/New_York")?;
427///
428/// // To make arithmetic reversible, the default largest unit for spans of
429/// // time computed from zoned datetimes is hours:
430/// assert_eq!(zdt1.until(&zdt2)?, 2_897.hour().minutes(37).fieldwise());
431/// // But we can ask for the span to be balanced up to years:
432/// assert_eq!(
433///     zdt1.until((Unit::Year, &zdt2))?,
434///     3.months().days(28).hours(16).minutes(37).fieldwise(),
435/// );
436///
437/// # Ok::<(), Box<dyn std::error::Error>>(())
438/// ```
439///
440/// While the [`Span::round`] API does balancing, it also, of course, does
441/// rounding as well. Rounding occurs when the smallest unit is set to
442/// something bigger than [`Unit::Nanosecond`]:
443///
444/// ```
445/// use jiff::{ToSpan, Unit};
446///
447/// let span = 2.hours().minutes(30);
448/// assert_eq!(span.round(Unit::Hour)?, 3.hours().fieldwise());
449///
450/// # Ok::<(), Box<dyn std::error::Error>>(())
451/// ```
452///
453/// When rounding spans with calendar units (years, months or weeks), then a
454/// relative datetime is required:
455///
456/// ```
457/// use jiff::{civil::date, SpanRound, ToSpan, Unit};
458///
459/// let span = 10.years().months(11);
460/// let options = SpanRound::new()
461///     .smallest(Unit::Year)
462///     .relative(date(2024, 1, 1));
463/// assert_eq!(span.round(options)?, 11.years().fieldwise());
464///
465/// # Ok::<(), Box<dyn std::error::Error>>(())
466/// ```
467///
468/// # Days are not always 24 hours!
469///
470/// That is, a `Span` is made up of uniform and non-uniform units.
471///
472/// A uniform unit is a unit whose elapsed duration is always the same.
473/// A non-uniform unit is a unit whose elapsed duration is not always the same.
474/// There are two things that can impact the length of a non-uniform unit:
475/// the calendar date and the time zone.
476///
477/// Years and months are always considered non-uniform units. For example,
478/// 1 month from `2024-04-01` is 30 days, while 1 month from `2024-05-01` is
479/// 31 days. Similarly for years because of leap years.
480///
481/// Hours, minutes, seconds, milliseconds, microseconds and nanoseconds are
482/// always considered uniform units.
483///
484/// Days are only considered non-uniform when in the presence of a zone aware
485/// datetime. A day can be more or less than 24 hours, and it can be balanced
486/// up and down, but only when a relative zoned datetime is given. This
487/// typically happens because of DST (daylight saving time), but can also occur
488/// because of other time zone transitions too.
489///
490/// ```
491/// use jiff::{civil::date, SpanRound, ToSpan, Unit};
492///
493/// // 2024-03-10 in New York was 23 hours long,
494/// // because of a jump to DST at 2am.
495/// let zdt = date(2024, 3, 9).at(21, 0, 0, 0).in_tz("America/New_York")?;
496/// // Goes from days to hours:
497/// assert_eq!(
498///     1.day().round(SpanRound::new().largest(Unit::Hour).relative(&zdt))?,
499///     23.hours().fieldwise(),
500/// );
501/// // Goes from hours to days:
502/// assert_eq!(
503///     23.hours().round(SpanRound::new().largest(Unit::Day).relative(&zdt))?,
504///     1.day().fieldwise(),
505/// );
506/// // 24 hours is more than 1 day starting at this time:
507/// assert_eq!(
508///     24.hours().round(SpanRound::new().largest(Unit::Day).relative(&zdt))?,
509///     1.day().hours(1).fieldwise(),
510/// );
511///
512/// # Ok::<(), Box<dyn std::error::Error>>(())
513/// ```
514///
515/// And similarly, days can be longer than 24 hours:
516///
517/// ```
518/// use jiff::{civil::date, SpanRound, ToSpan, Unit};
519///
520/// // 2024-11-03 in New York was 25 hours long,
521/// // because of a repetition of the 1 o'clock AM hour.
522/// let zdt = date(2024, 11, 2).at(21, 0, 0, 0).in_tz("America/New_York")?;
523/// // Goes from days to hours:
524/// assert_eq!(
525///     1.day().round(SpanRound::new().largest(Unit::Hour).relative(&zdt))?,
526///     25.hours().fieldwise(),
527/// );
528/// // Goes from hours to days:
529/// assert_eq!(
530///     25.hours().round(SpanRound::new().largest(Unit::Day).relative(&zdt))?,
531///     1.day().fieldwise(),
532/// );
533/// // 24 hours is less than 1 day starting at this time,
534/// // so it stays in units of hours even though we ask
535/// // for days (because 24 isn't enough hours to make
536/// // 1 day):
537/// assert_eq!(
538///     24.hours().round(SpanRound::new().largest(Unit::Day).relative(&zdt))?,
539///     24.hours().fieldwise(),
540/// );
541///
542/// # Ok::<(), Box<dyn std::error::Error>>(())
543/// ```
544///
545/// The APIs on `Span` will otherwise treat days as non-uniform unless a
546/// relative civil date is given, or there is an explicit opt-in to invariant
547/// 24-hour days. For example:
548///
549/// ```
550/// use jiff::{civil, SpanRelativeTo, ToSpan, Unit};
551///
552/// let span = 1.day();
553///
554/// // An error because days aren't always 24 hours:
555/// assert_eq!(
556///     span.total(Unit::Hour).unwrap_err().to_string(),
557///     "using unit 'day' in a span or configuration requires that either \
558///      a relative reference time be given or \
559///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
560///      invariant 24-hour days, but neither were provided",
561/// );
562/// // Opt into invariant 24 hour days without a relative date:
563/// let marker = SpanRelativeTo::days_are_24_hours();
564/// let hours = span.total((Unit::Hour, marker))?;
565/// assert_eq!(hours, 24.0);
566/// // Or use a relative civil date, and all days are 24 hours:
567/// let date = civil::date(2020, 1, 1);
568/// let hours = span.total((Unit::Hour, date))?;
569/// assert_eq!(hours, 24.0);
570///
571/// # Ok::<(), Box<dyn std::error::Error>>(())
572/// ```
573///
574/// In Jiff, all weeks are 7 days. And generally speaking, weeks only appear in
575/// a `Span` if they were explicitly put there by the caller or if they were
576/// explicitly requested by the caller in an API. For example:
577///
578/// ```
579/// use jiff::{civil::date, ToSpan, Unit};
580///
581/// let dt1 = date(2024, 1, 1).at(0, 0, 0, 0);
582/// let dt2 = date(2024, 7, 16).at(0, 0, 0, 0);
583/// // Default units go up to days.
584/// assert_eq!(dt1.until(dt2)?, 197.days().fieldwise());
585/// // No weeks, even though we requested up to year.
586/// assert_eq!(dt1.until((Unit::Year, dt2))?, 6.months().days(15).fieldwise());
587/// // We get weeks only when we ask for them.
588/// assert_eq!(dt1.until((Unit::Week, dt2))?, 28.weeks().days(1).fieldwise());
589///
590/// # Ok::<(), Box<dyn std::error::Error>>(())
591/// ```
592///
593/// # Integration with [`std::time::Duration`] and [`SignedDuration`]
594///
595/// While Jiff primarily uses a `Span` for doing arithmetic on datetimes,
596/// one can convert between a `Span` and a [`std::time::Duration`] or a
597/// [`SignedDuration`]. The main difference between them is that a `Span`
598/// always keeps tracks of its individual units, and a `Span` can represent
599/// non-uniform units like months. In contrast, `Duration` and `SignedDuration`
600/// are always an exact elapsed amount of time. They don't distinguish between
601/// `120 seconds` and `2 minutes`. And they can't represent the concept of
602/// "months" because a month doesn't have a single fixed amount of time.
603///
604/// However, an exact duration is still useful in certain contexts. Beyond
605/// that, it serves as an interoperability point due to the presence of an
606/// unsigned exact duration type in the standard library. Because of that,
607/// Jiff provides `TryFrom` trait implementations for converting to and from a
608/// `std::time::Duration` (and, of course, a `SignedDuration`). For example, to
609/// convert from a `std::time::Duration` to a `Span`:
610///
611/// ```
612/// use std::time::Duration;
613///
614/// use jiff::{Span, ToSpan};
615///
616/// let duration = Duration::new(86_400, 123_456_789);
617/// let span = Span::try_from(duration)?;
618/// // A duration-to-span conversion always results in a span with
619/// // non-zero units no bigger than seconds.
620/// assert_eq!(
621///     span.fieldwise(),
622///     86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
623/// );
624///
625/// // Note that the conversion is fallible! For example:
626/// assert!(Span::try_from(Duration::from_secs(u64::MAX)).is_err());
627/// // At present, a Jiff `Span` can only represent a range of time equal to
628/// // the range of time expressible via minimum and maximum Jiff timestamps.
629/// // Which is roughly -9999-01-01 to 9999-12-31, or ~20,000 years.
630/// assert!(Span::try_from(Duration::from_secs(999_999_999_999)).is_err());
631///
632/// # Ok::<(), Box<dyn std::error::Error>>(())
633/// ```
634///
635/// And to convert from a `Span` to a `std::time::Duration`:
636///
637/// ```
638/// use std::time::Duration;
639///
640/// use jiff::{Span, ToSpan};
641///
642/// let span = 86_400.seconds()
643///     .milliseconds(123)
644///     .microseconds(456)
645///     .nanoseconds(789);
646/// let duration = Duration::try_from(span)?;
647/// assert_eq!(duration, Duration::new(86_400, 123_456_789));
648///
649/// # Ok::<(), Box<dyn std::error::Error>>(())
650/// ```
651///
652/// Note that an error will occur when converting a `Span` to a
653/// `std::time::Duration` using the `TryFrom` trait implementation with units
654/// bigger than hours:
655///
656/// ```
657/// use std::time::Duration;
658///
659/// use jiff::ToSpan;
660///
661/// let span = 2.days().hours(10);
662/// assert_eq!(
663///     Duration::try_from(span).unwrap_err().to_string(),
664///     "failed to convert span to duration without relative datetime \
665///      (must use `jiff::Span::to_duration` instead): using unit 'day' \
666///      in a span or configuration requires that either a relative \
667///      reference time be given or \
668///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
669///      invariant 24-hour days, but neither were provided",
670/// );
671///
672/// # Ok::<(), Box<dyn std::error::Error>>(())
673/// ```
674///
675/// Similar code can be written for `SignedDuration` as well.
676///
677/// If you need to convert such spans, then as the error suggests, you'll need
678/// to use [`Span::to_duration`] with a relative date.
679///
680/// And note that since a `Span` is signed and a `std::time::Duration` is unsigned,
681/// converting a negative `Span` to `std::time::Duration` will always fail. One can use
682/// [`Span::signum`] to get the sign of the span and [`Span::abs`] to make the
683/// span positive before converting it to a `Duration`:
684///
685/// ```
686/// use std::time::Duration;
687///
688/// use jiff::{Span, ToSpan};
689///
690/// let span = -86_400.seconds().nanoseconds(1);
691/// let (sign, duration) = (span.signum(), Duration::try_from(span.abs())?);
692/// assert_eq!((sign, duration), (-1, Duration::new(86_400, 1)));
693///
694/// # Ok::<(), Box<dyn std::error::Error>>(())
695/// ```
696///
697/// Or, consider using Jiff's own [`SignedDuration`] instead:
698///
699/// ```
700/// # // See: https://github.com/rust-lang/rust/pull/121364
701/// # #![allow(unknown_lints, ambiguous_negative_literals)]
702/// use jiff::{SignedDuration, Span, ToSpan};
703///
704/// let span = -86_400.seconds().nanoseconds(1);
705/// let duration = SignedDuration::try_from(span)?;
706/// assert_eq!(duration, SignedDuration::new(-86_400, -1));
707///
708/// # Ok::<(), Box<dyn std::error::Error>>(())
709/// ```
710#[derive(Clone, Copy)]
711pub struct Span {
712    sign: Sign,
713    units: UnitSet,
714    years: t::SpanYears,
715    months: t::SpanMonths,
716    weeks: t::SpanWeeks,
717    days: t::SpanDays,
718    hours: t::SpanHours,
719    minutes: t::SpanMinutes,
720    seconds: t::SpanSeconds,
721    milliseconds: t::SpanMilliseconds,
722    microseconds: t::SpanMicroseconds,
723    nanoseconds: t::SpanNanoseconds,
724}
725
726/// Infallible routines for setting units on a `Span`.
727///
728/// These are useful when the units are determined by the programmer or when
729/// they have been validated elsewhere. In general, use these routines when
730/// constructing an invalid `Span` should be considered a bug in the program.
731impl Span {
732    /// Creates a new span representing a zero duration. That is, a duration
733    /// in which no time has passed.
734    pub fn new() -> Span {
735        Span::default()
736    }
737
738    /// Set the number of years on this span. The value may be negative.
739    ///
740    /// The fallible version of this method is [`Span::try_years`].
741    ///
742    /// # Panics
743    ///
744    /// This panics when the number of years is too small or too big.
745    /// The minimum value is `-19,998`.
746    /// The maximum value is `19,998`.
747    #[inline]
748    pub fn years<I: Into<i64>>(self, years: I) -> Span {
749        self.try_years(years).expect("value for years is out of bounds")
750    }
751
752    /// Set the number of months on this span. The value may be negative.
753    ///
754    /// The fallible version of this method is [`Span::try_months`].
755    ///
756    /// # Panics
757    ///
758    /// This panics when the number of months is too small or too big.
759    /// The minimum value is `-239,976`.
760    /// The maximum value is `239,976`.
761    #[inline]
762    pub fn months<I: Into<i64>>(self, months: I) -> Span {
763        self.try_months(months).expect("value for months is out of bounds")
764    }
765
766    /// Set the number of weeks on this span. The value may be negative.
767    ///
768    /// The fallible version of this method is [`Span::try_weeks`].
769    ///
770    /// # Panics
771    ///
772    /// This panics when the number of weeks is too small or too big.
773    /// The minimum value is `-1,043,497`.
774    /// The maximum value is `1_043_497`.
775    #[inline]
776    pub fn weeks<I: Into<i64>>(self, weeks: I) -> Span {
777        self.try_weeks(weeks).expect("value for weeks is out of bounds")
778    }
779
780    /// Set the number of days on this span. The value may be negative.
781    ///
782    /// The fallible version of this method is [`Span::try_days`].
783    ///
784    /// # Panics
785    ///
786    /// This panics when the number of days is too small or too big.
787    /// The minimum value is `-7,304,484`.
788    /// The maximum value is `7,304,484`.
789    #[inline]
790    pub fn days<I: Into<i64>>(self, days: I) -> Span {
791        self.try_days(days).expect("value for days is out of bounds")
792    }
793
794    /// Set the number of hours on this span. The value may be negative.
795    ///
796    /// The fallible version of this method is [`Span::try_hours`].
797    ///
798    /// # Panics
799    ///
800    /// This panics when the number of hours is too small or too big.
801    /// The minimum value is `-175,307,616`.
802    /// The maximum value is `175,307,616`.
803    #[inline]
804    pub fn hours<I: Into<i64>>(self, hours: I) -> Span {
805        self.try_hours(hours).expect("value for hours is out of bounds")
806    }
807
808    /// Set the number of minutes on this span. The value may be negative.
809    ///
810    /// The fallible version of this method is [`Span::try_minutes`].
811    ///
812    /// # Panics
813    ///
814    /// This panics when the number of minutes is too small or too big.
815    /// The minimum value is `-10,518,456,960`.
816    /// The maximum value is `10,518,456,960`.
817    #[inline]
818    pub fn minutes<I: Into<i64>>(self, minutes: I) -> Span {
819        self.try_minutes(minutes).expect("value for minutes is out of bounds")
820    }
821
822    /// Set the number of seconds on this span. The value may be negative.
823    ///
824    /// The fallible version of this method is [`Span::try_seconds`].
825    ///
826    /// # Panics
827    ///
828    /// This panics when the number of seconds is too small or too big.
829    /// The minimum value is `-631,107,417,600`.
830    /// The maximum value is `631,107,417,600`.
831    #[inline]
832    pub fn seconds<I: Into<i64>>(self, seconds: I) -> Span {
833        self.try_seconds(seconds).expect("value for seconds is out of bounds")
834    }
835
836    /// Set the number of milliseconds on this span. The value may be negative.
837    ///
838    /// The fallible version of this method is [`Span::try_milliseconds`].
839    ///
840    /// # Panics
841    ///
842    /// This panics when the number of milliseconds is too small or too big.
843    /// The minimum value is `-631,107,417,600,000`.
844    /// The maximum value is `631,107,417,600,000`.
845    #[inline]
846    pub fn milliseconds<I: Into<i64>>(self, milliseconds: I) -> Span {
847        self.try_milliseconds(milliseconds)
848            .expect("value for milliseconds is out of bounds")
849    }
850
851    /// Set the number of microseconds on this span. The value may be negative.
852    ///
853    /// The fallible version of this method is [`Span::try_microseconds`].
854    ///
855    /// # Panics
856    ///
857    /// This panics when the number of microseconds is too small or too big.
858    /// The minimum value is `-631,107,417,600,000,000`.
859    /// The maximum value is `631,107,417,600,000,000`.
860    #[inline]
861    pub fn microseconds<I: Into<i64>>(self, microseconds: I) -> Span {
862        self.try_microseconds(microseconds)
863            .expect("value for microseconds is out of bounds")
864    }
865
866    /// Set the number of nanoseconds on this span. The value may be negative.
867    ///
868    /// Note that unlike all other units, a 64-bit integer number of
869    /// nanoseconds is not big enough to represent all possible spans between
870    /// all possible datetimes supported by Jiff. This means, for example, that
871    /// computing a span between two datetimes that are far enough apart _and_
872    /// requesting a largest unit of [`Unit::Nanosecond`], might return an
873    /// error due to lack of precision.
874    ///
875    /// The fallible version of this method is [`Span::try_nanoseconds`].
876    ///
877    /// # Panics
878    ///
879    /// This panics when the number of nanoseconds is too small or too big.
880    /// The minimum value is `-9,223,372,036,854,775,807`.
881    /// The maximum value is `9,223,372,036,854,775,807`.
882    #[inline]
883    pub fn nanoseconds<I: Into<i64>>(self, nanoseconds: I) -> Span {
884        self.try_nanoseconds(nanoseconds)
885            .expect("value for nanoseconds is out of bounds")
886    }
887}
888
889/// Fallible methods for setting units on a `Span`.
890///
891/// These methods are useful when the span is made up of user provided values
892/// that may not be in range.
893impl Span {
894    /// Set the number of years on this span. The value may be negative.
895    ///
896    /// The panicking version of this method is [`Span::years`].
897    ///
898    /// # Errors
899    ///
900    /// This returns an error when the number of years is too small or too big.
901    /// The minimum value is `-19,998`.
902    /// The maximum value is `19,998`.
903    #[inline]
904    pub fn try_years<I: Into<i64>>(self, years: I) -> Result<Span, Error> {
905        let years = t::SpanYears::try_new("years", years)?;
906        Ok(self.years_ranged(years))
907    }
908
909    /// Set the number of months on this span. The value may be negative.
910    ///
911    /// The panicking version of this method is [`Span::months`].
912    ///
913    /// # Errors
914    ///
915    /// This returns an error when the number of months is too small or too big.
916    /// The minimum value is `-239,976`.
917    /// The maximum value is `239,976`.
918    #[inline]
919    pub fn try_months<I: Into<i64>>(self, months: I) -> Result<Span, Error> {
920        type Range = ri64<{ t::SpanMonths::MIN }, { t::SpanMonths::MAX }>;
921        let months = Range::try_new("months", months)?;
922        Ok(self.months_ranged(months.rinto()))
923    }
924
925    /// Set the number of weeks on this span. The value may be negative.
926    ///
927    /// The panicking version of this method is [`Span::weeks`].
928    ///
929    /// # Errors
930    ///
931    /// This returns an error when the number of weeks is too small or too big.
932    /// The minimum value is `-1,043,497`.
933    /// The maximum value is `1_043_497`.
934    #[inline]
935    pub fn try_weeks<I: Into<i64>>(self, weeks: I) -> Result<Span, Error> {
936        type Range = ri64<{ t::SpanWeeks::MIN }, { t::SpanWeeks::MAX }>;
937        let weeks = Range::try_new("weeks", weeks)?;
938        Ok(self.weeks_ranged(weeks.rinto()))
939    }
940
941    /// Set the number of days on this span. The value may be negative.
942    ///
943    /// The panicking version of this method is [`Span::days`].
944    ///
945    /// # Errors
946    ///
947    /// This returns an error when the number of days is too small or too big.
948    /// The minimum value is `-7,304,484`.
949    /// The maximum value is `7,304,484`.
950    #[inline]
951    pub fn try_days<I: Into<i64>>(self, days: I) -> Result<Span, Error> {
952        type Range = ri64<{ t::SpanDays::MIN }, { t::SpanDays::MAX }>;
953        let days = Range::try_new("days", days)?;
954        Ok(self.days_ranged(days.rinto()))
955    }
956
957    /// Set the number of hours on this span. The value may be negative.
958    ///
959    /// The panicking version of this method is [`Span::hours`].
960    ///
961    /// # Errors
962    ///
963    /// This returns an error when the number of hours is too small or too big.
964    /// The minimum value is `-175,307,616`.
965    /// The maximum value is `175,307,616`.
966    #[inline]
967    pub fn try_hours<I: Into<i64>>(self, hours: I) -> Result<Span, Error> {
968        type Range = ri64<{ t::SpanHours::MIN }, { t::SpanHours::MAX }>;
969        let hours = Range::try_new("hours", hours)?;
970        Ok(self.hours_ranged(hours.rinto()))
971    }
972
973    /// Set the number of minutes on this span. The value may be negative.
974    ///
975    /// The panicking version of this method is [`Span::minutes`].
976    ///
977    /// # Errors
978    ///
979    /// This returns an error when the number of minutes is too small or too big.
980    /// The minimum value is `-10,518,456,960`.
981    /// The maximum value is `10,518,456,960`.
982    #[inline]
983    pub fn try_minutes<I: Into<i64>>(self, minutes: I) -> Result<Span, Error> {
984        type Range = ri64<{ t::SpanMinutes::MIN }, { t::SpanMinutes::MAX }>;
985        let minutes = Range::try_new("minutes", minutes.into())?;
986        Ok(self.minutes_ranged(minutes))
987    }
988
989    /// Set the number of seconds on this span. The value may be negative.
990    ///
991    /// The panicking version of this method is [`Span::seconds`].
992    ///
993    /// # Errors
994    ///
995    /// This returns an error when the number of seconds is too small or too big.
996    /// The minimum value is `-631,107,417,600`.
997    /// The maximum value is `631,107,417,600`.
998    #[inline]
999    pub fn try_seconds<I: Into<i64>>(self, seconds: I) -> Result<Span, Error> {
1000        type Range = ri64<{ t::SpanSeconds::MIN }, { t::SpanSeconds::MAX }>;
1001        let seconds = Range::try_new("seconds", seconds.into())?;
1002        Ok(self.seconds_ranged(seconds))
1003    }
1004
1005    /// Set the number of milliseconds on this span. The value may be negative.
1006    ///
1007    /// The panicking version of this method is [`Span::milliseconds`].
1008    ///
1009    /// # Errors
1010    ///
1011    /// This returns an error when the number of milliseconds is too small or
1012    /// too big.
1013    /// The minimum value is `-631,107,417,600,000`.
1014    /// The maximum value is `631,107,417,600,000`.
1015    #[inline]
1016    pub fn try_milliseconds<I: Into<i64>>(
1017        self,
1018        milliseconds: I,
1019    ) -> Result<Span, Error> {
1020        type Range =
1021            ri64<{ t::SpanMilliseconds::MIN }, { t::SpanMilliseconds::MAX }>;
1022        let milliseconds =
1023            Range::try_new("milliseconds", milliseconds.into())?;
1024        Ok(self.milliseconds_ranged(milliseconds))
1025    }
1026
1027    /// Set the number of microseconds on this span. The value may be negative.
1028    ///
1029    /// The panicking version of this method is [`Span::microseconds`].
1030    ///
1031    /// # Errors
1032    ///
1033    /// This returns an error when the number of microseconds is too small or
1034    /// too big.
1035    /// The minimum value is `-631,107,417,600,000,000`.
1036    /// The maximum value is `631,107,417,600,000,000`.
1037    #[inline]
1038    pub fn try_microseconds<I: Into<i64>>(
1039        self,
1040        microseconds: I,
1041    ) -> Result<Span, Error> {
1042        type Range =
1043            ri64<{ t::SpanMicroseconds::MIN }, { t::SpanMicroseconds::MAX }>;
1044        let microseconds =
1045            Range::try_new("microseconds", microseconds.into())?;
1046        Ok(self.microseconds_ranged(microseconds))
1047    }
1048
1049    /// Set the number of nanoseconds on this span. The value may be negative.
1050    ///
1051    /// Note that unlike all other units, a 64-bit integer number of
1052    /// nanoseconds is not big enough to represent all possible spans between
1053    /// all possible datetimes supported by Jiff. This means, for example, that
1054    /// computing a span between two datetimes that are far enough apart _and_
1055    /// requesting a largest unit of [`Unit::Nanosecond`], might return an
1056    /// error due to lack of precision.
1057    ///
1058    /// The panicking version of this method is [`Span::nanoseconds`].
1059    ///
1060    /// # Errors
1061    ///
1062    /// This returns an error when the number of nanoseconds is too small or
1063    /// too big.
1064    /// The minimum value is `-9,223,372,036,854,775,807`.
1065    /// The maximum value is `9,223,372,036,854,775,807`.
1066    #[inline]
1067    pub fn try_nanoseconds<I: Into<i64>>(
1068        self,
1069        nanoseconds: I,
1070    ) -> Result<Span, Error> {
1071        type Range =
1072            ri64<{ t::SpanNanoseconds::MIN }, { t::SpanNanoseconds::MAX }>;
1073        let nanoseconds = Range::try_new("nanoseconds", nanoseconds.into())?;
1074        Ok(self.nanoseconds_ranged(nanoseconds))
1075    }
1076}
1077
1078/// Routines for accessing the individual units in a `Span`.
1079impl Span {
1080    /// Returns the number of year units in this span.
1081    ///
1082    /// Note that this is not the same as the total number of years in the
1083    /// span. To get that, you'll need to use either [`Span::round`] or
1084    /// [`Span::total`].
1085    ///
1086    /// # Example
1087    ///
1088    /// ```
1089    /// use jiff::{civil::date, ToSpan, Unit};
1090    ///
1091    /// let span = 3.years().months(24);
1092    /// assert_eq!(3, span.get_years());
1093    /// assert_eq!(5.0, span.total((Unit::Year, date(2024, 1, 1)))?);
1094    ///
1095    /// # Ok::<(), Box<dyn std::error::Error>>(())
1096    /// ```
1097    #[inline]
1098    pub fn get_years(&self) -> i16 {
1099        self.get_years_ranged().get()
1100    }
1101
1102    /// Returns the number of month units in this span.
1103    ///
1104    /// Note that this is not the same as the total number of months in the
1105    /// span. To get that, you'll need to use either [`Span::round`] or
1106    /// [`Span::total`].
1107    ///
1108    /// # Example
1109    ///
1110    /// ```
1111    /// use jiff::{civil::date, ToSpan, Unit};
1112    ///
1113    /// let span = 7.months().days(59);
1114    /// assert_eq!(7, span.get_months());
1115    /// assert_eq!(9.0, span.total((Unit::Month, date(2022, 6, 1)))?);
1116    ///
1117    /// # Ok::<(), Box<dyn std::error::Error>>(())
1118    /// ```
1119    #[inline]
1120    pub fn get_months(&self) -> i32 {
1121        self.get_months_ranged().get()
1122    }
1123
1124    /// Returns the number of week units in this span.
1125    ///
1126    /// Note that this is not the same as the total number of weeks in the
1127    /// span. To get that, you'll need to use either [`Span::round`] or
1128    /// [`Span::total`].
1129    ///
1130    /// # Example
1131    ///
1132    /// ```
1133    /// use jiff::{civil::date, ToSpan, Unit};
1134    ///
1135    /// let span = 3.weeks().days(14);
1136    /// assert_eq!(3, span.get_weeks());
1137    /// assert_eq!(5.0, span.total((Unit::Week, date(2024, 1, 1)))?);
1138    ///
1139    /// # Ok::<(), Box<dyn std::error::Error>>(())
1140    /// ```
1141    #[inline]
1142    pub fn get_weeks(&self) -> i32 {
1143        self.get_weeks_ranged().get()
1144    }
1145
1146    /// Returns the number of day units in this span.
1147    ///
1148    /// Note that this is not the same as the total number of days in the
1149    /// span. To get that, you'll need to use either [`Span::round`] or
1150    /// [`Span::total`].
1151    ///
1152    /// # Example
1153    ///
1154    /// ```
1155    /// use jiff::{ToSpan, Unit, Zoned};
1156    ///
1157    /// let span = 3.days().hours(47);
1158    /// assert_eq!(3, span.get_days());
1159    ///
1160    /// let zdt: Zoned = "2024-03-07[America/New_York]".parse()?;
1161    /// assert_eq!(5.0, span.total((Unit::Day, &zdt))?);
1162    ///
1163    /// # Ok::<(), Box<dyn std::error::Error>>(())
1164    /// ```
1165    #[inline]
1166    pub fn get_days(&self) -> i32 {
1167        self.get_days_ranged().get()
1168    }
1169
1170    /// Returns the number of hour units in this span.
1171    ///
1172    /// Note that this is not the same as the total number of hours in the
1173    /// span. To get that, you'll need to use either [`Span::round`] or
1174    /// [`Span::total`].
1175    ///
1176    /// # Example
1177    ///
1178    /// ```
1179    /// use jiff::{ToSpan, Unit};
1180    ///
1181    /// let span = 3.hours().minutes(120);
1182    /// assert_eq!(3, span.get_hours());
1183    /// assert_eq!(5.0, span.total(Unit::Hour)?);
1184    ///
1185    /// # Ok::<(), Box<dyn std::error::Error>>(())
1186    /// ```
1187    #[inline]
1188    pub fn get_hours(&self) -> i32 {
1189        self.get_hours_ranged().get()
1190    }
1191
1192    /// Returns the number of minute units in this span.
1193    ///
1194    /// Note that this is not the same as the total number of minutes in the
1195    /// span. To get that, you'll need to use either [`Span::round`] or
1196    /// [`Span::total`].
1197    ///
1198    /// # Example
1199    ///
1200    /// ```
1201    /// use jiff::{ToSpan, Unit};
1202    ///
1203    /// let span = 3.minutes().seconds(120);
1204    /// assert_eq!(3, span.get_minutes());
1205    /// assert_eq!(5.0, span.total(Unit::Minute)?);
1206    ///
1207    /// # Ok::<(), Box<dyn std::error::Error>>(())
1208    /// ```
1209    #[inline]
1210    pub fn get_minutes(&self) -> i64 {
1211        self.get_minutes_ranged().get()
1212    }
1213
1214    /// Returns the number of second units in this span.
1215    ///
1216    /// Note that this is not the same as the total number of seconds in the
1217    /// span. To get that, you'll need to use either [`Span::round`] or
1218    /// [`Span::total`].
1219    ///
1220    /// # Example
1221    ///
1222    /// ```
1223    /// use jiff::{ToSpan, Unit};
1224    ///
1225    /// let span = 3.seconds().milliseconds(2_000);
1226    /// assert_eq!(3, span.get_seconds());
1227    /// assert_eq!(5.0, span.total(Unit::Second)?);
1228    ///
1229    /// # Ok::<(), Box<dyn std::error::Error>>(())
1230    /// ```
1231    #[inline]
1232    pub fn get_seconds(&self) -> i64 {
1233        self.get_seconds_ranged().get()
1234    }
1235
1236    /// Returns the number of millisecond units in this span.
1237    ///
1238    /// Note that this is not the same as the total number of milliseconds in
1239    /// the span. To get that, you'll need to use either [`Span::round`] or
1240    /// [`Span::total`].
1241    ///
1242    /// # Example
1243    ///
1244    /// ```
1245    /// use jiff::{ToSpan, Unit};
1246    ///
1247    /// let span = 3.milliseconds().microseconds(2_000);
1248    /// assert_eq!(3, span.get_milliseconds());
1249    /// assert_eq!(5.0, span.total(Unit::Millisecond)?);
1250    ///
1251    /// # Ok::<(), Box<dyn std::error::Error>>(())
1252    /// ```
1253    #[inline]
1254    pub fn get_milliseconds(&self) -> i64 {
1255        self.get_milliseconds_ranged().get()
1256    }
1257
1258    /// Returns the number of microsecond units in this span.
1259    ///
1260    /// Note that this is not the same as the total number of microseconds in
1261    /// the span. To get that, you'll need to use either [`Span::round`] or
1262    /// [`Span::total`].
1263    ///
1264    /// # Example
1265    ///
1266    /// ```
1267    /// use jiff::{ToSpan, Unit};
1268    ///
1269    /// let span = 3.microseconds().nanoseconds(2_000);
1270    /// assert_eq!(3, span.get_microseconds());
1271    /// assert_eq!(5.0, span.total(Unit::Microsecond)?);
1272    ///
1273    /// # Ok::<(), Box<dyn std::error::Error>>(())
1274    /// ```
1275    #[inline]
1276    pub fn get_microseconds(&self) -> i64 {
1277        self.get_microseconds_ranged().get()
1278    }
1279
1280    /// Returns the number of nanosecond units in this span.
1281    ///
1282    /// Note that this is not the same as the total number of nanoseconds in
1283    /// the span. To get that, you'll need to use either [`Span::round`] or
1284    /// [`Span::total`].
1285    ///
1286    /// # Example
1287    ///
1288    /// ```
1289    /// use jiff::{ToSpan, Unit};
1290    ///
1291    /// let span = 3.microseconds().nanoseconds(2_000);
1292    /// assert_eq!(2_000, span.get_nanoseconds());
1293    /// assert_eq!(5_000.0, span.total(Unit::Nanosecond)?);
1294    ///
1295    /// # Ok::<(), Box<dyn std::error::Error>>(())
1296    /// ```
1297    #[inline]
1298    pub fn get_nanoseconds(&self) -> i64 {
1299        self.get_nanoseconds_ranged().get()
1300    }
1301}
1302
1303/// Routines for manipulating, comparing and inspecting `Span` values.
1304impl Span {
1305    /// Returns a new span that is the absolute value of this span.
1306    ///
1307    /// If this span is zero or positive, then this is a no-op.
1308    ///
1309    /// # Example
1310    ///
1311    /// ```
1312    /// use jiff::ToSpan;
1313    ///
1314    /// let span = -100.seconds();
1315    /// assert_eq!(span.to_string(), "-PT100S");
1316    /// let span = span.abs();
1317    /// assert_eq!(span.to_string(), "PT100S");
1318    /// ```
1319    #[inline]
1320    pub fn abs(self) -> Span {
1321        if self.is_zero() {
1322            return self;
1323        }
1324        Span { sign: ri8::N::<1>(), ..self }
1325    }
1326
1327    /// Returns a new span that negates this span.
1328    ///
1329    /// If this span is zero, then this is a no-op. If this span is negative,
1330    /// then the returned span is positive. If this span is positive, then
1331    /// the returned span is negative.
1332    ///
1333    /// # Example
1334    ///
1335    /// ```
1336    /// use jiff::ToSpan;
1337    ///
1338    /// let span = 100.days();
1339    /// assert_eq!(span.to_string(), "P100D");
1340    /// let span = span.negate();
1341    /// assert_eq!(span.to_string(), "-P100D");
1342    /// ```
1343    ///
1344    /// # Example: available via the negation operator
1345    ///
1346    /// This routine can also be used via `-`:
1347    ///
1348    /// ```
1349    /// use jiff::ToSpan;
1350    ///
1351    /// let span = 100.days();
1352    /// assert_eq!(span.to_string(), "P100D");
1353    /// let span = -span;
1354    /// assert_eq!(span.to_string(), "-P100D");
1355    /// ```
1356    #[inline]
1357    pub fn negate(self) -> Span {
1358        Span { sign: -self.sign, ..self }
1359    }
1360
1361    /// Returns the "sign number" or "signum" of this span.
1362    ///
1363    /// The number returned is `-1` when this span is negative,
1364    /// `0` when this span is zero and `1` when this span is positive.
1365    #[inline]
1366    pub fn signum(self) -> i8 {
1367        self.sign.signum().get()
1368    }
1369
1370    /// Returns true if and only if this span is positive.
1371    ///
1372    /// This returns false when the span is zero or negative.
1373    ///
1374    /// # Example
1375    ///
1376    /// ```
1377    /// use jiff::ToSpan;
1378    ///
1379    /// assert!(!2.months().is_negative());
1380    /// assert!((-2.months()).is_negative());
1381    /// ```
1382    #[inline]
1383    pub fn is_positive(self) -> bool {
1384        self.get_sign_ranged() > C(0)
1385    }
1386
1387    /// Returns true if and only if this span is negative.
1388    ///
1389    /// This returns false when the span is zero or positive.
1390    ///
1391    /// # Example
1392    ///
1393    /// ```
1394    /// use jiff::ToSpan;
1395    ///
1396    /// assert!(!2.months().is_negative());
1397    /// assert!((-2.months()).is_negative());
1398    /// ```
1399    #[inline]
1400    pub fn is_negative(self) -> bool {
1401        self.get_sign_ranged() < C(0)
1402    }
1403
1404    /// Returns true if and only if every field in this span is set to `0`.
1405    ///
1406    /// # Example
1407    ///
1408    /// ```
1409    /// use jiff::{Span, ToSpan};
1410    ///
1411    /// assert!(Span::new().is_zero());
1412    /// assert!(Span::default().is_zero());
1413    /// assert!(0.seconds().is_zero());
1414    /// assert!(!0.seconds().seconds(1).is_zero());
1415    /// assert!(0.seconds().seconds(1).seconds(0).is_zero());
1416    /// ```
1417    #[inline]
1418    pub fn is_zero(self) -> bool {
1419        self.sign == C(0)
1420    }
1421
1422    /// Returns this `Span` as a value with a type that implements the
1423    /// `Hash`, `Eq` and `PartialEq` traits in a fieldwise fashion.
1424    ///
1425    /// A `SpanFieldwise` is meant to make it easy to compare two spans in a
1426    /// "dumb" way based purely on its unit values. This is distinct from
1427    /// something like [`Span::compare`] that performs a comparison on the
1428    /// actual elapsed time of two spans.
1429    ///
1430    /// It is generally discouraged to use `SpanFieldwise` since spans that
1431    /// represent an equivalent elapsed amount of time may compare unequal.
1432    /// However, in some cases, it is useful to be able to assert precise
1433    /// field values. For example, Jiff itself makes heavy use of fieldwise
1434    /// comparisons for tests.
1435    ///
1436    /// # Example: the difference between `SpanFieldwise` and `Span::compare`
1437    ///
1438    /// In short, `SpanFieldwise` considers `2 hours` and `120 minutes` to be
1439    /// distinct values, but `Span::compare` considers them to be equivalent:
1440    ///
1441    /// ```
1442    /// use std::cmp::Ordering;
1443    /// use jiff::ToSpan;
1444    ///
1445    /// assert_ne!(120.minutes().fieldwise(), 2.hours().fieldwise());
1446    /// assert_eq!(120.minutes().compare(2.hours())?, Ordering::Equal);
1447    ///
1448    /// # Ok::<(), Box<dyn std::error::Error>>(())
1449    /// ```
1450    #[inline]
1451    pub fn fieldwise(self) -> SpanFieldwise {
1452        SpanFieldwise(self)
1453    }
1454
1455    /// Multiplies each field in this span by a given integer.
1456    ///
1457    /// If this would cause any individual field in this span to overflow, then
1458    /// this returns an error.
1459    ///
1460    /// # Example
1461    ///
1462    /// ```
1463    /// use jiff::ToSpan;
1464    ///
1465    /// let span = 4.days().seconds(8);
1466    /// assert_eq!(span.checked_mul(2)?, 8.days().seconds(16).fieldwise());
1467    /// assert_eq!(span.checked_mul(-3)?, -12.days().seconds(24).fieldwise());
1468    /// // Notice that no re-balancing is done. It's "just" multiplication.
1469    /// assert_eq!(span.checked_mul(10)?, 40.days().seconds(80).fieldwise());
1470    ///
1471    /// let span = 10_000.years();
1472    /// // too big!
1473    /// assert!(span.checked_mul(3).is_err());
1474    ///
1475    /// # Ok::<(), Box<dyn std::error::Error>>(())
1476    /// ```
1477    ///
1478    /// # Example: available via the multiplication operator
1479    ///
1480    /// This method can be used via the `*` operator. Note though that a panic
1481    /// happens on overflow.
1482    ///
1483    /// ```
1484    /// use jiff::ToSpan;
1485    ///
1486    /// let span = 4.days().seconds(8);
1487    /// assert_eq!(span * 2, 8.days().seconds(16).fieldwise());
1488    /// assert_eq!(2 * span, 8.days().seconds(16).fieldwise());
1489    /// assert_eq!(span * -3, -12.days().seconds(24).fieldwise());
1490    /// assert_eq!(-3 * span, -12.days().seconds(24).fieldwise());
1491    ///
1492    /// # Ok::<(), Box<dyn std::error::Error>>(())
1493    /// ```
1494    #[inline]
1495    pub fn checked_mul(mut self, rhs: i64) -> Result<Span, Error> {
1496        if rhs == 0 {
1497            return Ok(Span::default());
1498        } else if rhs == 1 {
1499            return Ok(self);
1500        }
1501        self.sign *= t::Sign::try_new("span factor", rhs.signum())
1502            .expect("signum fits in ri8");
1503        // This is all somewhat odd, but since each of our span fields uses
1504        // a different primitive representation and range of allowed values,
1505        // we only seek to perform multiplications when they will actually
1506        // do something. Otherwise, we risk multiplying the mins/maxs of a
1507        // ranged integer and causing a spurious panic. Basically, the idea
1508        // here is the allowable values for our multiple depend on what we're
1509        // actually going to multiply with it. If our span has non-zero years,
1510        // then our multiple can't exceed the bounds of `SpanYears`, otherwise
1511        // it is guaranteed to overflow.
1512        if self.years != C(0) {
1513            let rhs = t::SpanYears::try_new("years multiple", rhs)?;
1514            self.years = self.years.try_checked_mul("years", rhs.abs())?;
1515        }
1516        if self.months != C(0) {
1517            let rhs = t::SpanMonths::try_new("months multiple", rhs)?;
1518            self.months = self.months.try_checked_mul("months", rhs.abs())?;
1519        }
1520        if self.weeks != C(0) {
1521            let rhs = t::SpanWeeks::try_new("weeks multiple", rhs)?;
1522            self.weeks = self.weeks.try_checked_mul("weeks", rhs.abs())?;
1523        }
1524        if self.days != C(0) {
1525            let rhs = t::SpanDays::try_new("days multiple", rhs)?;
1526            self.days = self.days.try_checked_mul("days", rhs.abs())?;
1527        }
1528        if self.hours != C(0) {
1529            let rhs = t::SpanHours::try_new("hours multiple", rhs)?;
1530            self.hours = self.hours.try_checked_mul("hours", rhs.abs())?;
1531        }
1532        if self.minutes != C(0) {
1533            let rhs = t::SpanMinutes::try_new("minutes multiple", rhs)?;
1534            self.minutes =
1535                self.minutes.try_checked_mul("minutes", rhs.abs())?;
1536        }
1537        if self.seconds != C(0) {
1538            let rhs = t::SpanSeconds::try_new("seconds multiple", rhs)?;
1539            self.seconds =
1540                self.seconds.try_checked_mul("seconds", rhs.abs())?;
1541        }
1542        if self.milliseconds != C(0) {
1543            let rhs =
1544                t::SpanMilliseconds::try_new("milliseconds multiple", rhs)?;
1545            self.milliseconds = self
1546                .milliseconds
1547                .try_checked_mul("milliseconds", rhs.abs())?;
1548        }
1549        if self.microseconds != C(0) {
1550            let rhs =
1551                t::SpanMicroseconds::try_new("microseconds multiple", rhs)?;
1552            self.microseconds = self
1553                .microseconds
1554                .try_checked_mul("microseconds", rhs.abs())?;
1555        }
1556        if self.nanoseconds != C(0) {
1557            let rhs =
1558                t::SpanNanoseconds::try_new("nanoseconds multiple", rhs)?;
1559            self.nanoseconds =
1560                self.nanoseconds.try_checked_mul("nanoseconds", rhs.abs())?;
1561        }
1562        // N.B. We don't need to update `self.units` here since it shouldn't
1563        // change. The only way it could is if a unit goes from zero to
1564        // non-zero (which can't happen, because multiplication by zero is
1565        // always zero), or if a unit goes from non-zero to zero. That also
1566        // can't happen because we handle the case of the factor being zero
1567        // specially above, and it returns a `Span` will all units zero
1568        // correctly.
1569        Ok(self)
1570    }
1571
1572    /// Adds a span to this one and returns the sum as a new span.
1573    ///
1574    /// When adding a span with units greater than hours, callers must provide
1575    /// a relative datetime to anchor the spans.
1576    ///
1577    /// Arithmetic proceeds as specified in [RFC 5545]. Bigger units are
1578    /// added together before smaller units.
1579    ///
1580    /// This routine accepts anything that implements `Into<SpanArithmetic>`.
1581    /// There are some trait implementations that make using this routine
1582    /// ergonomic:
1583    ///
1584    /// * `From<Span> for SpanArithmetic` adds the given span to this one.
1585    /// * `From<(Span, civil::Date)> for SpanArithmetic` adds the given
1586    /// span to this one relative to the given date. There are also `From`
1587    /// implementations for `civil::DateTime` and `Zoned`.
1588    ///
1589    /// This also works with different duration types, such as
1590    /// [`SignedDuration`] and [`std::time::Duration`], via additional trait
1591    /// implementations:
1592    ///
1593    /// * `From<SignedDuration> for SpanArithmetic` adds the given duration to
1594    /// this one.
1595    /// * `From<(SignedDuration, civil::Date)> for SpanArithmetic` adds the
1596    /// given duration to this one relative to the given date. There are also
1597    /// `From` implementations for `civil::DateTime` and `Zoned`.
1598    ///
1599    /// And similarly for `std::time::Duration`.
1600    ///
1601    /// Adding a negative span is equivalent to subtracting its absolute value.
1602    ///
1603    /// The largest non-zero unit in the span returned is at most the largest
1604    /// non-zero unit among the two spans being added. For an absolute
1605    /// duration, its "largest" unit is considered to be nanoseconds.
1606    ///
1607    /// The sum returned is automatically re-balanced so that the span is not
1608    /// "bottom heavy."
1609    ///
1610    /// [RFC 5545]: https://datatracker.ietf.org/doc/html/rfc5545
1611    ///
1612    /// # Errors
1613    ///
1614    /// This returns an error when adding the two spans would overflow any
1615    /// individual field of a span. This will also return an error if either
1616    /// of the spans have non-zero units of days or greater and no relative
1617    /// reference time is provided.
1618    ///
1619    /// Callers may use [`SpanArithmetic::days_are_24_hours`] as a special
1620    /// marker instead of providing a relative civil date to indicate that
1621    /// all days should be 24 hours long. This also results in treating all
1622    /// weeks as seven 24 hour days (168 hours).
1623    ///
1624    /// # Example
1625    ///
1626    /// ```
1627    /// use jiff::ToSpan;
1628    ///
1629    /// assert_eq!(
1630    ///     1.hour().checked_add(30.minutes())?,
1631    ///     1.hour().minutes(30).fieldwise(),
1632    /// );
1633    ///
1634    /// # Ok::<(), Box<dyn std::error::Error>>(())
1635    /// ```
1636    ///
1637    /// # Example: re-balancing
1638    ///
1639    /// This example shows how units are automatically rebalanced into bigger
1640    /// units when appropriate.
1641    ///
1642    /// ```
1643    /// use jiff::ToSpan;
1644    ///
1645    /// let span1 = 2.hours().minutes(59);
1646    /// let span2 = 2.minutes();
1647    /// assert_eq!(span1.checked_add(span2)?, 3.hours().minutes(1).fieldwise());
1648    ///
1649    /// # Ok::<(), Box<dyn std::error::Error>>(())
1650    /// ```
1651    ///
1652    /// # Example: days are not assumed to be 24 hours by default
1653    ///
1654    /// When dealing with units involving days or weeks, one must either
1655    /// provide a relative datetime (shown in the following examples) or opt
1656    /// into invariant 24 hour days:
1657    ///
1658    /// ```
1659    /// use jiff::{SpanRelativeTo, ToSpan};
1660    ///
1661    /// let span1 = 2.days().hours(23);
1662    /// let span2 = 2.hours();
1663    /// assert_eq!(
1664    ///     span1.checked_add((span2, SpanRelativeTo::days_are_24_hours()))?,
1665    ///     3.days().hours(1).fieldwise(),
1666    /// );
1667    ///
1668    /// # Ok::<(), Box<dyn std::error::Error>>(())
1669    /// ```
1670    ///
1671    /// # Example: adding spans with calendar units
1672    ///
1673    /// If you try to add two spans with calendar units without specifying a
1674    /// relative datetime, you'll get an error:
1675    ///
1676    /// ```
1677    /// use jiff::ToSpan;
1678    ///
1679    /// let span1 = 1.month().days(15);
1680    /// let span2 = 15.days();
1681    /// assert!(span1.checked_add(span2).is_err());
1682    /// ```
1683    ///
1684    /// A relative datetime is needed because calendar spans may correspond to
1685    /// different actual durations depending on where the span begins:
1686    ///
1687    /// ```
1688    /// use jiff::{civil::date, ToSpan};
1689    ///
1690    /// let span1 = 1.month().days(15);
1691    /// let span2 = 15.days();
1692    /// // 1 month from March 1 is 31 days...
1693    /// assert_eq!(
1694    ///     span1.checked_add((span2, date(2008, 3, 1)))?,
1695    ///     2.months().fieldwise(),
1696    /// );
1697    /// // ... but 1 month from April 1 is 30 days!
1698    /// assert_eq!(
1699    ///     span1.checked_add((span2, date(2008, 4, 1)))?,
1700    ///     1.month().days(30).fieldwise(),
1701    /// );
1702    ///
1703    /// # Ok::<(), Box<dyn std::error::Error>>(())
1704    /// ```
1705    ///
1706    /// # Example: error on overflow
1707    ///
1708    /// Adding two spans can overflow, and this will result in an error:
1709    ///
1710    /// ```
1711    /// use jiff::ToSpan;
1712    ///
1713    /// assert!(19_998.years().checked_add(1.year()).is_err());
1714    /// ```
1715    ///
1716    /// # Example: adding an absolute duration to a span
1717    ///
1718    /// This shows how one isn't limited to just adding two spans together.
1719    /// One can also add absolute durations to a span.
1720    ///
1721    /// ```
1722    /// use std::time::Duration;
1723    ///
1724    /// use jiff::{SignedDuration, ToSpan};
1725    ///
1726    /// assert_eq!(
1727    ///     1.hour().checked_add(SignedDuration::from_mins(30))?,
1728    ///     1.hour().minutes(30).fieldwise(),
1729    /// );
1730    /// assert_eq!(
1731    ///     1.hour().checked_add(Duration::from_secs(30 * 60))?,
1732    ///     1.hour().minutes(30).fieldwise(),
1733    /// );
1734    ///
1735    /// # Ok::<(), Box<dyn std::error::Error>>(())
1736    /// ```
1737    ///
1738    /// Note that even when adding an absolute duration, if the span contains
1739    /// non-uniform units, you still need to provide a relative datetime:
1740    ///
1741    /// ```
1742    /// use jiff::{civil::date, SignedDuration, ToSpan};
1743    ///
1744    /// // Might be 1 month or less than 1 month!
1745    /// let dur = SignedDuration::from_hours(30 * 24);
1746    /// // No relative datetime provided even when the span
1747    /// // contains non-uniform units results in an error.
1748    /// assert!(1.month().checked_add(dur).is_err());
1749    /// // In this case, 30 days is one month (April).
1750    /// assert_eq!(
1751    ///     1.month().checked_add((dur, date(2024, 3, 1)))?,
1752    ///     2.months().fieldwise(),
1753    /// );
1754    /// // In this case, 30 days is less than one month (May).
1755    /// assert_eq!(
1756    ///     1.month().checked_add((dur, date(2024, 4, 1)))?,
1757    ///     1.month().days(30).fieldwise(),
1758    /// );
1759    ///
1760    /// # Ok::<(), Box<dyn std::error::Error>>(())
1761    /// ```
1762    #[inline]
1763    pub fn checked_add<'a, A: Into<SpanArithmetic<'a>>>(
1764        &self,
1765        options: A,
1766    ) -> Result<Span, Error> {
1767        let options: SpanArithmetic<'_> = options.into();
1768        options.checked_add(*self)
1769    }
1770
1771    #[inline]
1772    fn checked_add_span<'a>(
1773        &self,
1774        relative: Option<SpanRelativeTo<'a>>,
1775        span: &Span,
1776    ) -> Result<Span, Error> {
1777        let (span1, span2) = (*self, *span);
1778        let unit = span1.largest_unit().max(span2.largest_unit());
1779        let start = match relative {
1780            Some(r) => match r.to_relative(unit)? {
1781                None => return span1.checked_add_invariant(unit, &span2),
1782                Some(r) => r,
1783            },
1784            None => {
1785                requires_relative_date_err(unit)?;
1786                return span1.checked_add_invariant(unit, &span2);
1787            }
1788        };
1789        let mid = start.checked_add(span1)?;
1790        let end = mid.checked_add(span2)?;
1791        start.until(unit, &end)
1792    }
1793
1794    #[inline]
1795    fn checked_add_duration<'a>(
1796        &self,
1797        relative: Option<SpanRelativeTo<'a>>,
1798        duration: SignedDuration,
1799    ) -> Result<Span, Error> {
1800        let (span1, dur2) = (*self, duration);
1801        let unit = span1.largest_unit();
1802        let start = match relative {
1803            Some(r) => match r.to_relative(unit)? {
1804                None => {
1805                    return span1.checked_add_invariant_duration(unit, dur2)
1806                }
1807                Some(r) => r,
1808            },
1809            None => {
1810                requires_relative_date_err(unit)?;
1811                return span1.checked_add_invariant_duration(unit, dur2);
1812            }
1813        };
1814        let mid = start.checked_add(span1)?;
1815        let end = mid.checked_add_duration(dur2)?;
1816        start.until(unit, &end)
1817    }
1818
1819    /// Like `checked_add`, but only applies for invariant units. That is,
1820    /// when *both* spans whose non-zero units are all hours or smaller
1821    /// (or weeks or smaller with the "days are 24 hours" marker).
1822    #[inline]
1823    fn checked_add_invariant(
1824        &self,
1825        unit: Unit,
1826        span: &Span,
1827    ) -> Result<Span, Error> {
1828        assert!(unit <= Unit::Week);
1829        let nanos1 = self.to_invariant_nanoseconds();
1830        let nanos2 = span.to_invariant_nanoseconds();
1831        let sum = nanos1 + nanos2;
1832        Span::from_invariant_nanoseconds(unit, sum)
1833    }
1834
1835    /// Like `checked_add_invariant`, but adds an absolute duration.
1836    #[inline]
1837    fn checked_add_invariant_duration(
1838        &self,
1839        unit: Unit,
1840        duration: SignedDuration,
1841    ) -> Result<Span, Error> {
1842        assert!(unit <= Unit::Week);
1843        let nanos1 = self.to_invariant_nanoseconds();
1844        let nanos2 = t::NoUnits96::new_unchecked(duration.as_nanos());
1845        let sum = nanos1 + nanos2;
1846        Span::from_invariant_nanoseconds(unit, sum)
1847    }
1848
1849    /// This routine is identical to [`Span::checked_add`] with the given
1850    /// duration negated.
1851    ///
1852    /// # Errors
1853    ///
1854    /// This has the same error conditions as [`Span::checked_add`].
1855    ///
1856    /// # Example
1857    ///
1858    /// ```
1859    /// use std::time::Duration;
1860    ///
1861    /// use jiff::{SignedDuration, ToSpan};
1862    ///
1863    /// assert_eq!(
1864    ///     1.hour().checked_sub(30.minutes())?,
1865    ///     30.minutes().fieldwise(),
1866    /// );
1867    /// assert_eq!(
1868    ///     1.hour().checked_sub(SignedDuration::from_mins(30))?,
1869    ///     30.minutes().fieldwise(),
1870    /// );
1871    /// assert_eq!(
1872    ///     1.hour().checked_sub(Duration::from_secs(30 * 60))?,
1873    ///     30.minutes().fieldwise(),
1874    /// );
1875    ///
1876    /// # Ok::<(), Box<dyn std::error::Error>>(())
1877    /// ```
1878    #[inline]
1879    pub fn checked_sub<'a, A: Into<SpanArithmetic<'a>>>(
1880        &self,
1881        options: A,
1882    ) -> Result<Span, Error> {
1883        let mut options: SpanArithmetic<'_> = options.into();
1884        options.duration = options.duration.checked_neg()?;
1885        options.checked_add(*self)
1886    }
1887
1888    /// Compares two spans in terms of how long they are. Negative spans are
1889    /// considered shorter than the zero span.
1890    ///
1891    /// Two spans compare equal when they correspond to the same duration
1892    /// of time, even if their individual fields are different. This is in
1893    /// contrast to the `Eq` trait implementation of `SpanFieldwise` (created
1894    /// by [`Span::fieldwise`]), which performs exact field-wise comparisons.
1895    /// This split exists because the comparison provided by this routine is
1896    /// "heavy" in that it may need to do datetime arithmetic to return an
1897    /// answer. In contrast, the `Eq` trait implementation is "cheap."
1898    ///
1899    /// This routine accepts anything that implements `Into<SpanCompare>`.
1900    /// There are some trait implementations that make using this routine
1901    /// ergonomic:
1902    ///
1903    /// * `From<Span> for SpanCompare` compares the given span to this one.
1904    /// * `From<(Span, civil::Date)> for SpanArithmetic` compares the given
1905    /// span to this one relative to the given date. There are also `From`
1906    /// implementations for `civil::DateTime` and `Zoned`.
1907    ///
1908    /// # Errors
1909    ///
1910    /// If either of the spans being compared have a non-zero calendar unit
1911    /// (units bigger than hours), then this routine requires a relative
1912    /// datetime. If one is not provided, then an error is returned.
1913    ///
1914    /// An error can also occur when adding either span to the relative
1915    /// datetime given results in overflow.
1916    ///
1917    /// Callers may use [`SpanArithmetic::days_are_24_hours`] as a special
1918    /// marker instead of providing a relative civil date to indicate that
1919    /// all days should be 24 hours long. This also results in treating all
1920    /// weeks as seven 24 hour days (168 hours).
1921    ///
1922    /// # Example
1923    ///
1924    /// ```
1925    /// use jiff::ToSpan;
1926    ///
1927    /// let span1 = 3.hours();
1928    /// let span2 = 180.minutes();
1929    /// assert_eq!(span1.compare(span2)?, std::cmp::Ordering::Equal);
1930    /// // But notice that the two spans are not equal via `Eq`:
1931    /// assert_ne!(span1.fieldwise(), span2.fieldwise());
1932    ///
1933    /// # Ok::<(), Box<dyn std::error::Error>>(())
1934    /// ```
1935    ///
1936    /// # Example: negative spans are less than zero
1937    ///
1938    /// ```
1939    /// use jiff::ToSpan;
1940    ///
1941    /// let span1 = -1.second();
1942    /// let span2 = 0.seconds();
1943    /// assert_eq!(span1.compare(span2)?, std::cmp::Ordering::Less);
1944    ///
1945    /// # Ok::<(), Box<dyn std::error::Error>>(())
1946    /// ```
1947    ///
1948    /// # Example: comparisons take DST into account
1949    ///
1950    /// When a relative datetime is time zone aware, then DST is taken into
1951    /// account when comparing spans:
1952    ///
1953    /// ```
1954    /// use jiff::{civil, ToSpan, Zoned};
1955    ///
1956    /// let span1 = 79.hours().minutes(10);
1957    /// let span2 = 3.days().hours(7).seconds(630);
1958    /// let span3 = 3.days().hours(6).minutes(50);
1959    ///
1960    /// let relative: Zoned = "2020-11-01T00-07[America/Los_Angeles]".parse()?;
1961    /// let mut spans = [span1, span2, span3];
1962    /// spans.sort_by(|s1, s2| s1.compare((s2, &relative)).unwrap());
1963    /// assert_eq!(
1964    ///     spans.map(|sp| sp.fieldwise()),
1965    ///     [span1.fieldwise(), span3.fieldwise(), span2.fieldwise()],
1966    /// );
1967    ///
1968    /// // Compare with the result of sorting without taking DST into account.
1969    /// // We can that by providing a relative civil date:
1970    /// let relative = civil::date(2020, 11, 1);
1971    /// spans.sort_by(|s1, s2| s1.compare((s2, relative)).unwrap());
1972    /// assert_eq!(
1973    ///     spans.map(|sp| sp.fieldwise()),
1974    ///     [span3.fieldwise(), span1.fieldwise(), span2.fieldwise()],
1975    /// );
1976    ///
1977    /// # Ok::<(), Box<dyn std::error::Error>>(())
1978    /// ```
1979    ///
1980    /// See the examples for [`Span::total`] if you want to sort spans without
1981    /// an `unwrap()` call.
1982    #[inline]
1983    pub fn compare<'a, C: Into<SpanCompare<'a>>>(
1984        &self,
1985        options: C,
1986    ) -> Result<Ordering, Error> {
1987        let options: SpanCompare<'_> = options.into();
1988        options.compare(*self)
1989    }
1990
1991    /// Returns a floating point number representing the total number of a
1992    /// specific unit (as given) in this span. If the span is not evenly
1993    /// divisible by the requested units, then the number returned may have a
1994    /// fractional component.
1995    ///
1996    /// This routine accepts anything that implements `Into<SpanTotal>`. There
1997    /// are some trait implementations that make using this routine ergonomic:
1998    ///
1999    /// * `From<Unit> for SpanTotal` computes a total for the given unit in
2000    /// this span.
2001    /// * `From<(Unit, civil::Date)> for SpanTotal` computes a total for the
2002    /// given unit in this span, relative to the given date. There are also
2003    /// `From` implementations for `civil::DateTime` and `Zoned`.
2004    ///
2005    /// # Errors
2006    ///
2007    /// If this span has any non-zero calendar unit (units bigger than hours),
2008    /// then this routine requires a relative datetime. If one is not provided,
2009    /// then an error is returned.
2010    ///
2011    /// An error can also occur when adding the span to the relative
2012    /// datetime given results in overflow.
2013    ///
2014    /// Callers may use [`SpanArithmetic::days_are_24_hours`] as a special
2015    /// marker instead of providing a relative civil date to indicate that
2016    /// all days should be 24 hours long. This also results in treating all
2017    /// weeks as seven 24 hour days (168 hours).
2018    ///
2019    /// # Example
2020    ///
2021    /// This example shows how to find the number of seconds in a particular
2022    /// span:
2023    ///
2024    /// ```
2025    /// use jiff::{ToSpan, Unit};
2026    ///
2027    /// let span = 3.hours().minutes(10);
2028    /// assert_eq!(span.total(Unit::Second)?, 11_400.0);
2029    ///
2030    /// # Ok::<(), Box<dyn std::error::Error>>(())
2031    /// ```
2032    ///
2033    /// # Example: 24 hour days
2034    ///
2035    /// This shows how to find the total number of 24 hour days in
2036    /// `123,456,789` seconds.
2037    ///
2038    /// ```
2039    /// use jiff::{SpanTotal, ToSpan, Unit};
2040    ///
2041    /// let span = 123_456_789.seconds();
2042    /// assert_eq!(
2043    ///     span.total(SpanTotal::from(Unit::Day).days_are_24_hours())?,
2044    ///     1428.8980208333332,
2045    /// );
2046    ///
2047    /// # Ok::<(), Box<dyn std::error::Error>>(())
2048    /// ```
2049    ///
2050    /// # Example: DST is taken into account
2051    ///
2052    /// The month of March 2024 in `America/New_York` had 31 days, but one of
2053    /// those days was 23 hours long due a transition into daylight saving
2054    /// time:
2055    ///
2056    /// ```
2057    /// use jiff::{civil::date, ToSpan, Unit};
2058    ///
2059    /// let span = 744.hours();
2060    /// let relative = date(2024, 3, 1).in_tz("America/New_York")?;
2061    /// // Because of the short day, 744 hours is actually a little *more* than
2062    /// // 1 month starting from 2024-03-01.
2063    /// assert_eq!(span.total((Unit::Month, &relative))?, 1.0013888888888889);
2064    ///
2065    /// # Ok::<(), Box<dyn std::error::Error>>(())
2066    /// ```
2067    ///
2068    /// Now compare what happens when the relative datetime is civil and not
2069    /// time zone aware:
2070    ///
2071    /// ```
2072    /// use jiff::{civil::date, ToSpan, Unit};
2073    ///
2074    /// let span = 744.hours();
2075    /// let relative = date(2024, 3, 1);
2076    /// assert_eq!(span.total((Unit::Month, relative))?, 1.0);
2077    ///
2078    /// # Ok::<(), Box<dyn std::error::Error>>(())
2079    /// ```
2080    ///
2081    /// # Example: infallible sorting
2082    ///
2083    /// The sorting example in [`Span::compare`] has to use `unwrap()` in
2084    /// its `sort_by(..)` call because `Span::compare` may fail and there
2085    /// is no "fallible" sorting routine in Rust's standard library (as of
2086    /// 2024-07-07). While the ways in which `Span::compare` can fail for
2087    /// a valid configuration are limited to overflow for "extreme" values, it
2088    /// is possible to sort spans infallibly by computing floating point
2089    /// representations for each span up-front:
2090    ///
2091    /// ```
2092    /// use jiff::{civil::Date, ToSpan, Unit, Zoned};
2093    ///
2094    /// let span1 = 79.hours().minutes(10);
2095    /// let span2 = 3.days().hours(7).seconds(630);
2096    /// let span3 = 3.days().hours(6).minutes(50);
2097    ///
2098    /// let relative: Zoned = "2020-11-01T00-07[America/Los_Angeles]".parse()?;
2099    /// let mut spans = [
2100    ///     (span1, span1.total((Unit::Day, &relative))?),
2101    ///     (span2, span2.total((Unit::Day, &relative))?),
2102    ///     (span3, span3.total((Unit::Day, &relative))?),
2103    /// ];
2104    /// spans.sort_by(|&(_, total1), &(_, total2)| total1.total_cmp(&total2));
2105    /// assert_eq!(
2106    ///     spans.map(|(sp, _)| sp.fieldwise()),
2107    ///     [span1.fieldwise(), span3.fieldwise(), span2.fieldwise()],
2108    /// );
2109    ///
2110    /// // Compare with the result of sorting without taking DST into account.
2111    /// // We do that here by providing a relative civil date.
2112    /// let relative: Date = "2020-11-01".parse()?;
2113    /// let mut spans = [
2114    ///     (span1, span1.total((Unit::Day, relative))?),
2115    ///     (span2, span2.total((Unit::Day, relative))?),
2116    ///     (span3, span3.total((Unit::Day, relative))?),
2117    /// ];
2118    /// spans.sort_by(|&(_, total1), &(_, total2)| total1.total_cmp(&total2));
2119    /// assert_eq!(
2120    ///     spans.map(|(sp, _)| sp.fieldwise()),
2121    ///     [span3.fieldwise(), span1.fieldwise(), span2.fieldwise()],
2122    /// );
2123    ///
2124    /// # Ok::<(), Box<dyn std::error::Error>>(())
2125    /// ```
2126    #[inline]
2127    pub fn total<'a, T: Into<SpanTotal<'a>>>(
2128        &self,
2129        options: T,
2130    ) -> Result<f64, Error> {
2131        let options: SpanTotal<'_> = options.into();
2132        options.total(*self)
2133    }
2134
2135    /// Returns a new span that is balanced and rounded.
2136    ///
2137    /// Rounding a span has a number of parameters, all of which are optional.
2138    /// When no parameters are given, then no rounding or balancing is done,
2139    /// and the span as given is returned. That is, it's a no-op.
2140    ///
2141    /// The parameters are, in brief:
2142    ///
2143    /// * [`SpanRound::largest`] sets the largest [`Unit`] that is allowed to
2144    /// be non-zero in the span returned. When _only_ the largest unit is set,
2145    /// rounding itself doesn't occur and instead the span is merely balanced.
2146    /// * [`SpanRound::smallest`] sets the smallest [`Unit`] that is allowed to
2147    /// be non-zero in the span returned. By default, it is set to
2148    /// [`Unit::Nanosecond`], i.e., no rounding occurs. When the smallest unit
2149    /// is set to something bigger than nanoseconds, then the non-zero units
2150    /// in the span smaller than the smallest unit are used to determine how
2151    /// the span should be rounded. For example, rounding `1 hour 59 minutes`
2152    /// to the nearest hour using the default rounding mode would produce
2153    /// `2 hours`.
2154    /// * [`SpanRound::mode`] determines how to handle the remainder when
2155    /// rounding. The default is [`RoundMode::HalfExpand`], which corresponds
2156    /// to how you were taught to round in school. Alternative modes, like
2157    /// [`RoundMode::Trunc`], exist too. For example, a truncating rounding of
2158    /// `1 hour 59 minutes` to the nearest hour would produce `1 hour`.
2159    /// * [`SpanRound::increment`] sets the rounding granularity to use for
2160    /// the configured smallest unit. For example, if the smallest unit is
2161    /// minutes and the increment is 5, then the span returned will always have
2162    /// its minute units set to a multiple of `5`.
2163    /// * [`SpanRound::relative`] sets the datetime from which to interpret the
2164    /// span. This is required when rounding spans with calendar units (years,
2165    /// months or weeks). When a relative datetime is time zone aware, then
2166    /// rounding accounts for the fact that not all days are 24 hours long.
2167    /// When a relative datetime is omitted or is civil (not time zone aware),
2168    /// then days are always 24 hours long.
2169    ///
2170    /// # Constructing a [`SpanRound`]
2171    ///
2172    /// This routine accepts anything that implements `Into<SpanRound>`. There
2173    /// are a few key trait implementations that make this convenient:
2174    ///
2175    /// * `From<Unit> for SpanRound` will construct a rounding configuration
2176    /// where the smallest unit is set to the one given.
2177    /// * `From<(Unit, i64)> for SpanRound` will construct a rounding
2178    /// configuration where the smallest unit and the rounding increment are
2179    /// set to the ones given.
2180    ///
2181    /// To set other options (like the largest unit, the rounding mode and the
2182    /// relative datetime), one must explicitly create a `SpanRound` and pass
2183    /// it to this routine.
2184    ///
2185    /// # Errors
2186    ///
2187    /// In general, there are two main ways for rounding to fail: an improper
2188    /// configuration like trying to round a span with calendar units but
2189    /// without a relative datetime, or when overflow occurs. Overflow can
2190    /// occur when the span, added to the relative datetime if given, would
2191    /// exceed the minimum or maximum datetime values. Overflow can also occur
2192    /// if the span is too big to fit into the requested unit configuration.
2193    /// For example, a span like `19_998.years()` cannot be represented with a
2194    /// 64-bit integer number of nanoseconds.
2195    ///
2196    /// Callers may use [`SpanArithmetic::days_are_24_hours`] as a special
2197    /// marker instead of providing a relative civil date to indicate that
2198    /// all days should be 24 hours long. This also results in treating all
2199    /// weeks as seven 24 hour days (168 hours).
2200    ///
2201    /// # Example: balancing
2202    ///
2203    /// This example demonstrates balancing, not rounding. And in particular,
2204    /// this example shows how to balance a span as much as possible (i.e.,
2205    /// with units of hours or smaller) without needing to specify a relative
2206    /// datetime:
2207    ///
2208    /// ```
2209    /// use jiff::{SpanRound, ToSpan, Unit};
2210    ///
2211    /// let span = 123_456_789_123_456_789i64.nanoseconds();
2212    /// assert_eq!(
2213    ///     span.round(SpanRound::new().largest(Unit::Hour))?.fieldwise(),
2214    ///     34_293.hours().minutes(33).seconds(9)
2215    ///         .milliseconds(123).microseconds(456).nanoseconds(789),
2216    /// );
2217    ///
2218    /// # Ok::<(), Box<dyn std::error::Error>>(())
2219    /// ```
2220    ///
2221    /// Or you can opt into invariant 24-hour days (and 7-day weeks) without a
2222    /// relative date with [`SpanRound::days_are_24_hours`]:
2223    ///
2224    /// ```
2225    /// use jiff::{SpanRound, ToSpan, Unit};
2226    ///
2227    /// let span = 123_456_789_123_456_789i64.nanoseconds();
2228    /// assert_eq!(
2229    ///     span.round(
2230    ///         SpanRound::new().largest(Unit::Day).days_are_24_hours(),
2231    ///     )?.fieldwise(),
2232    ///     1_428.days()
2233    ///         .hours(21).minutes(33).seconds(9)
2234    ///         .milliseconds(123).microseconds(456).nanoseconds(789),
2235    /// );
2236    ///
2237    /// # Ok::<(), Box<dyn std::error::Error>>(())
2238    /// ```
2239    ///
2240    /// # Example: balancing and rounding
2241    ///
2242    /// This example is like the one before it, but where we round to the
2243    /// nearest second:
2244    ///
2245    /// ```
2246    /// use jiff::{SpanRound, ToSpan, Unit};
2247    ///
2248    /// let span = 123_456_789_123_456_789i64.nanoseconds();
2249    /// assert_eq!(
2250    ///     span.round(SpanRound::new().largest(Unit::Hour).smallest(Unit::Second))?,
2251    ///     34_293.hours().minutes(33).seconds(9).fieldwise(),
2252    /// );
2253    ///
2254    /// # Ok::<(), Box<dyn std::error::Error>>(())
2255    /// ```
2256    ///
2257    /// Or, just rounding to the nearest hour can make use of the
2258    /// `From<Unit> for SpanRound` trait implementation:
2259    ///
2260    /// ```
2261    /// use jiff::{ToSpan, Unit};
2262    ///
2263    /// let span = 123_456_789_123_456_789i64.nanoseconds();
2264    /// assert_eq!(span.round(Unit::Hour)?, 34_294.hours().fieldwise());
2265    ///
2266    /// # Ok::<(), Box<dyn std::error::Error>>(())
2267    /// ```
2268    ///
2269    /// # Example: balancing with a relative datetime
2270    ///
2271    /// Even with calendar units, so long as a relative datetime is provided,
2272    /// it's easy to turn days into bigger units:
2273    ///
2274    /// ```
2275    /// use jiff::{civil::date, SpanRound, ToSpan, Unit};
2276    ///
2277    /// let span = 1_000.days();
2278    /// let relative = date(2000, 1, 1);
2279    /// let options = SpanRound::new().largest(Unit::Year).relative(relative);
2280    /// assert_eq!(span.round(options)?, 2.years().months(8).days(26).fieldwise());
2281    ///
2282    /// # Ok::<(), Box<dyn std::error::Error>>(())
2283    /// ```
2284    ///
2285    /// # Example: round to the nearest half-hour
2286    ///
2287    /// ```
2288    /// use jiff::{Span, ToSpan, Unit};
2289    ///
2290    /// let span: Span = "PT23h50m3.123s".parse()?;
2291    /// assert_eq!(span.round((Unit::Minute, 30))?, 24.hours().fieldwise());
2292    ///
2293    /// # Ok::<(), Box<dyn std::error::Error>>(())
2294    /// ```
2295    ///
2296    /// # Example: yearly quarters in a span
2297    ///
2298    /// This example shows how to find how many full 3 month quarters are in a
2299    /// particular span of time.
2300    ///
2301    /// ```
2302    /// use jiff::{civil::date, RoundMode, SpanRound, ToSpan, Unit};
2303    ///
2304    /// let span1 = 10.months().days(15);
2305    /// let round = SpanRound::new()
2306    ///     .smallest(Unit::Month)
2307    ///     .increment(3)
2308    ///     .mode(RoundMode::Trunc)
2309    ///     // A relative datetime must be provided when
2310    ///     // rounding involves calendar units.
2311    ///     .relative(date(2024, 1, 1));
2312    /// let span2 = span1.round(round)?;
2313    /// assert_eq!(span2.get_months() / 3, 3);
2314    ///
2315    /// # Ok::<(), Box<dyn std::error::Error>>(())
2316    /// ```
2317    #[inline]
2318    pub fn round<'a, R: Into<SpanRound<'a>>>(
2319        self,
2320        options: R,
2321    ) -> Result<Span, Error> {
2322        let options: SpanRound<'a> = options.into();
2323        options.round(self)
2324    }
2325
2326    /// Converts a `Span` to a [`SignedDuration`] relative to the date given.
2327    ///
2328    /// In most cases, it is unlikely that you'll need to use this routine to
2329    /// convert a `Span` to a `SignedDuration` and instead will be ably to
2330    /// use `SignedDuration::try_from(span)`. Namely, by default:
2331    ///
2332    /// * [`Zoned::until`] guarantees that the biggest non-zero unit is hours.
2333    /// * [`Timestamp::until`] guarantees that the biggest non-zero unit is
2334    /// seconds.
2335    /// * [`DateTime::until`] guarantees that the biggest non-zero unit is
2336    /// days.
2337    /// * [`Date::until`] guarantees that the biggest non-zero unit is days.
2338    /// * [`Time::until`] guarantees that the biggest non-zero unit is hours.
2339    ///
2340    /// In the above, only [`DateTime::until`] and [`Date::until`] return
2341    /// calendar units by default, and thus would require this routine. (In
2342    /// which case, one may pass [`SpanRelativeTo::days_are_24_hours`] or an
2343    /// actual relative date to resolve the length of a day.)
2344    ///
2345    /// Of course, one may change the defaults. For example, if one
2346    /// uses `Zoned::until` with the largest unit set to `Unit::Year`
2347    /// and the resulting `Span` includes non-zero calendar units, then
2348    /// `SignedDuration::try_from` will fail because there is no relative date.
2349    ///
2350    /// # Errors
2351    ///
2352    /// This returns an error if adding this span to the date given results in
2353    /// overflow. This can also return an error if one uses
2354    /// [`SpanRelativeTo::days_are_24_hours`] with a `Span` that has non-zero
2355    /// units greater than weeks.
2356    ///
2357    /// # Example: converting a span with calendar units to a `SignedDuration`
2358    ///
2359    /// This compares the number of seconds in a non-leap year with a leap
2360    /// year:
2361    ///
2362    /// ```
2363    /// use jiff::{civil::date, SignedDuration, ToSpan};
2364    ///
2365    /// let span = 1.year();
2366    ///
2367    /// let duration = span.to_duration(date(2024, 1, 1))?;
2368    /// assert_eq!(duration, SignedDuration::from_secs(31_622_400));
2369    /// let duration = span.to_duration(date(2023, 1, 1))?;
2370    /// assert_eq!(duration, SignedDuration::from_secs(31_536_000));
2371    ///
2372    /// # Ok::<(), Box<dyn std::error::Error>>(())
2373    /// ```
2374    ///
2375    /// # Example: converting a span without a relative datetime
2376    ///
2377    /// If for some reason it doesn't make sense to include a
2378    /// relative datetime, you can use this routine to convert a
2379    /// `Span` with units up to weeks to a `SignedDuration` via the
2380    /// [`SpanRelativeTo::days_are_24_hours`] marker:
2381    ///
2382    /// ```
2383    /// use jiff::{civil::date, SignedDuration, SpanRelativeTo, ToSpan};
2384    ///
2385    /// let span = 1.week().days(1);
2386    ///
2387    /// let duration = span.to_duration(SpanRelativeTo::days_are_24_hours())?;
2388    /// assert_eq!(duration, SignedDuration::from_hours(192));
2389    ///
2390    /// # Ok::<(), Box<dyn std::error::Error>>(())
2391    /// ```
2392    #[inline]
2393    pub fn to_duration<'a, R: Into<SpanRelativeTo<'a>>>(
2394        &self,
2395        relative: R,
2396    ) -> Result<SignedDuration, Error> {
2397        let max_unit = self.largest_unit();
2398        let relative: SpanRelativeTo<'a> = relative.into();
2399        let Some(result) = relative.to_relative(max_unit).transpose() else {
2400            return Ok(self.to_duration_invariant());
2401        };
2402        let relspan = result
2403            .and_then(|r| r.into_relative_span(Unit::Second, *self))
2404            .with_context(|| match relative.kind {
2405                SpanRelativeToKind::Civil(_) => E::ToDurationCivil,
2406                SpanRelativeToKind::Zoned(_) => E::ToDurationZoned,
2407                SpanRelativeToKind::DaysAre24Hours => {
2408                    E::ToDurationDaysAre24Hours
2409                }
2410            })?;
2411        debug_assert!(relspan.span.largest_unit() <= Unit::Second);
2412        Ok(relspan.span.to_duration_invariant())
2413    }
2414
2415    /// Converts an entirely invariant span to a `SignedDuration`.
2416    ///
2417    /// Callers must ensure that this span has no units greater than weeks.
2418    /// If it does have non-zero units of days or weeks, then every day is
2419    /// considered 24 hours and every week 7 days. Generally speaking, callers
2420    /// should also ensure that if this span does have non-zero day/week units,
2421    /// then callers have either provided a civil relative date or the special
2422    /// `SpanRelativeTo::days_are_24_hours()` marker.
2423    #[inline]
2424    pub(crate) fn to_duration_invariant(&self) -> SignedDuration {
2425        // This guarantees, at compile time, that a maximal invariant Span
2426        // (that is, all units are days or lower and all units are set to their
2427        // maximum values) will still balance out to a number of seconds that
2428        // fits into a `i64`. This in turn implies that a `SignedDuration` can
2429        // represent all possible invariant positive spans.
2430        const _FITS_IN_U64: () = {
2431            debug_assert!(
2432                i64::MAX as i128
2433                    > ((t::SpanWeeks::MAX
2434                        * t::SECONDS_PER_CIVIL_WEEK.bound())
2435                        + (t::SpanDays::MAX
2436                            * t::SECONDS_PER_CIVIL_DAY.bound())
2437                        + (t::SpanHours::MAX * t::SECONDS_PER_HOUR.bound())
2438                        + (t::SpanMinutes::MAX
2439                            * t::SECONDS_PER_MINUTE.bound())
2440                        + t::SpanSeconds::MAX
2441                        + (t::SpanMilliseconds::MAX
2442                            / t::MILLIS_PER_SECOND.bound())
2443                        + (t::SpanMicroseconds::MAX
2444                            / t::MICROS_PER_SECOND.bound())
2445                        + (t::SpanNanoseconds::MAX
2446                            / t::NANOS_PER_SECOND.bound())),
2447            );
2448            ()
2449        };
2450
2451        let nanos = self.to_invariant_nanoseconds();
2452        debug_assert!(
2453            self.largest_unit() <= Unit::Week,
2454            "units must be weeks or lower"
2455        );
2456        // OK because we have a compile time assert above that ensures our
2457        // nanoseconds are in the valid range of a `SignedDuration`.
2458        SignedDuration::from_nanos_i128(nanos.get())
2459    }
2460}
2461
2462/// Crate internal APIs that operate on ranged integer types.
2463impl Span {
2464    #[inline]
2465    pub(crate) fn years_ranged(self, years: t::SpanYears) -> Span {
2466        let mut span = Span { years: years.abs(), ..self };
2467        span.sign = self.resign(years, &span);
2468        span.units = span.units.set(Unit::Year, years == C(0));
2469        span
2470    }
2471
2472    #[inline]
2473    pub(crate) fn months_ranged(self, months: t::SpanMonths) -> Span {
2474        let mut span = Span { months: months.abs(), ..self };
2475        span.sign = self.resign(months, &span);
2476        span.units = span.units.set(Unit::Month, months == C(0));
2477        span
2478    }
2479
2480    #[inline]
2481    pub(crate) fn weeks_ranged(self, weeks: t::SpanWeeks) -> Span {
2482        let mut span = Span { weeks: weeks.abs(), ..self };
2483        span.sign = self.resign(weeks, &span);
2484        span.units = span.units.set(Unit::Week, weeks == C(0));
2485        span
2486    }
2487
2488    #[inline]
2489    pub(crate) fn days_ranged(self, days: t::SpanDays) -> Span {
2490        let mut span = Span { days: days.abs(), ..self };
2491        span.sign = self.resign(days, &span);
2492        span.units = span.units.set(Unit::Day, days == C(0));
2493        span
2494    }
2495
2496    #[inline]
2497    pub(crate) fn hours_ranged(self, hours: t::SpanHours) -> Span {
2498        let mut span = Span { hours: hours.abs(), ..self };
2499        span.sign = self.resign(hours, &span);
2500        span.units = span.units.set(Unit::Hour, hours == C(0));
2501        span
2502    }
2503
2504    #[inline]
2505    pub(crate) fn minutes_ranged(self, minutes: t::SpanMinutes) -> Span {
2506        let mut span = Span { minutes: minutes.abs(), ..self };
2507        span.sign = self.resign(minutes, &span);
2508        span.units = span.units.set(Unit::Minute, minutes == C(0));
2509        span
2510    }
2511
2512    #[inline]
2513    pub(crate) fn seconds_ranged(self, seconds: t::SpanSeconds) -> Span {
2514        let mut span = Span { seconds: seconds.abs(), ..self };
2515        span.sign = self.resign(seconds, &span);
2516        span.units = span.units.set(Unit::Second, seconds == C(0));
2517        span
2518    }
2519
2520    #[inline]
2521    fn milliseconds_ranged(self, milliseconds: t::SpanMilliseconds) -> Span {
2522        let mut span = Span { milliseconds: milliseconds.abs(), ..self };
2523        span.sign = self.resign(milliseconds, &span);
2524        span.units = span.units.set(Unit::Millisecond, milliseconds == C(0));
2525        span
2526    }
2527
2528    #[inline]
2529    fn microseconds_ranged(self, microseconds: t::SpanMicroseconds) -> Span {
2530        let mut span = Span { microseconds: microseconds.abs(), ..self };
2531        span.sign = self.resign(microseconds, &span);
2532        span.units = span.units.set(Unit::Microsecond, microseconds == C(0));
2533        span
2534    }
2535
2536    #[inline]
2537    pub(crate) fn nanoseconds_ranged(
2538        self,
2539        nanoseconds: t::SpanNanoseconds,
2540    ) -> Span {
2541        let mut span = Span { nanoseconds: nanoseconds.abs(), ..self };
2542        span.sign = self.resign(nanoseconds, &span);
2543        span.units = span.units.set(Unit::Nanosecond, nanoseconds == C(0));
2544        span
2545    }
2546
2547    #[inline]
2548    fn try_days_ranged(
2549        self,
2550        days: impl TryRInto<t::SpanDays>,
2551    ) -> Result<Span, Error> {
2552        let days = days.try_rinto("days")?;
2553        Ok(self.days_ranged(days))
2554    }
2555
2556    #[inline]
2557    pub(crate) fn try_hours_ranged(
2558        self,
2559        hours: impl TryRInto<t::SpanHours>,
2560    ) -> Result<Span, Error> {
2561        let hours = hours.try_rinto("hours")?;
2562        Ok(self.hours_ranged(hours))
2563    }
2564
2565    #[inline]
2566    pub(crate) fn try_minutes_ranged(
2567        self,
2568        minutes: impl TryRInto<t::SpanMinutes>,
2569    ) -> Result<Span, Error> {
2570        let minutes = minutes.try_rinto("minutes")?;
2571        Ok(self.minutes_ranged(minutes))
2572    }
2573
2574    #[inline]
2575    pub(crate) fn try_seconds_ranged(
2576        self,
2577        seconds: impl TryRInto<t::SpanSeconds>,
2578    ) -> Result<Span, Error> {
2579        let seconds = seconds.try_rinto("seconds")?;
2580        Ok(self.seconds_ranged(seconds))
2581    }
2582
2583    #[inline]
2584    pub(crate) fn try_milliseconds_ranged(
2585        self,
2586        milliseconds: impl TryRInto<t::SpanMilliseconds>,
2587    ) -> Result<Span, Error> {
2588        let milliseconds = milliseconds.try_rinto("milliseconds")?;
2589        Ok(self.milliseconds_ranged(milliseconds))
2590    }
2591
2592    #[inline]
2593    pub(crate) fn try_microseconds_ranged(
2594        self,
2595        microseconds: impl TryRInto<t::SpanMicroseconds>,
2596    ) -> Result<Span, Error> {
2597        let microseconds = microseconds.try_rinto("microseconds")?;
2598        Ok(self.microseconds_ranged(microseconds))
2599    }
2600
2601    #[inline]
2602    pub(crate) fn try_nanoseconds_ranged(
2603        self,
2604        nanoseconds: impl TryRInto<t::SpanNanoseconds>,
2605    ) -> Result<Span, Error> {
2606        let nanoseconds = nanoseconds.try_rinto("nanoseconds")?;
2607        Ok(self.nanoseconds_ranged(nanoseconds))
2608    }
2609
2610    #[inline]
2611    fn try_units_ranged(
2612        self,
2613        unit: Unit,
2614        value: NoUnits,
2615    ) -> Result<Span, Error> {
2616        Ok(match unit {
2617            Unit::Year => self.years_ranged(value.try_rinto("years")?),
2618            Unit::Month => self.months_ranged(value.try_rinto("months")?),
2619            Unit::Week => self.weeks_ranged(value.try_rinto("weeks")?),
2620            Unit::Day => self.days_ranged(value.try_rinto("days")?),
2621            Unit::Hour => self.hours_ranged(value.try_rinto("hours")?),
2622            Unit::Minute => self.minutes_ranged(value.try_rinto("minutes")?),
2623            Unit::Second => self.seconds_ranged(value.try_rinto("seconds")?),
2624            Unit::Millisecond => {
2625                self.milliseconds_ranged(value.try_rinto("milliseconds")?)
2626            }
2627            Unit::Microsecond => {
2628                self.microseconds_ranged(value.try_rinto("microseconds")?)
2629            }
2630            Unit::Nanosecond => {
2631                self.nanoseconds_ranged(value.try_rinto("nanoseconds")?)
2632            }
2633        })
2634    }
2635
2636    #[inline]
2637    pub(crate) fn get_years_ranged(&self) -> t::SpanYears {
2638        self.years * self.sign
2639    }
2640
2641    #[inline]
2642    pub(crate) fn get_months_ranged(&self) -> t::SpanMonths {
2643        self.months * self.sign
2644    }
2645
2646    #[inline]
2647    pub(crate) fn get_weeks_ranged(&self) -> t::SpanWeeks {
2648        self.weeks * self.sign
2649    }
2650
2651    #[inline]
2652    pub(crate) fn get_days_ranged(&self) -> t::SpanDays {
2653        self.days * self.sign
2654    }
2655
2656    #[inline]
2657    pub(crate) fn get_hours_ranged(&self) -> t::SpanHours {
2658        self.hours * self.sign
2659    }
2660
2661    #[inline]
2662    pub(crate) fn get_minutes_ranged(&self) -> t::SpanMinutes {
2663        self.minutes * self.sign
2664    }
2665
2666    #[inline]
2667    pub(crate) fn get_seconds_ranged(&self) -> t::SpanSeconds {
2668        self.seconds * self.sign
2669    }
2670
2671    #[inline]
2672    pub(crate) fn get_milliseconds_ranged(&self) -> t::SpanMilliseconds {
2673        self.milliseconds * self.sign
2674    }
2675
2676    #[inline]
2677    pub(crate) fn get_microseconds_ranged(&self) -> t::SpanMicroseconds {
2678        self.microseconds * self.sign
2679    }
2680
2681    #[inline]
2682    pub(crate) fn get_nanoseconds_ranged(&self) -> t::SpanNanoseconds {
2683        self.nanoseconds * self.sign
2684    }
2685
2686    #[inline]
2687    pub(crate) fn get_years_unsigned(&self) -> t::SpanYears {
2688        self.years
2689    }
2690
2691    #[inline]
2692    pub(crate) fn get_months_unsigned(&self) -> t::SpanMonths {
2693        self.months
2694    }
2695
2696    #[inline]
2697    pub(crate) fn get_weeks_unsigned(&self) -> t::SpanWeeks {
2698        self.weeks
2699    }
2700
2701    #[inline]
2702    pub(crate) fn get_days_unsigned(&self) -> t::SpanDays {
2703        self.days
2704    }
2705
2706    #[inline]
2707    pub(crate) fn get_hours_unsigned(&self) -> t::SpanHours {
2708        self.hours
2709    }
2710
2711    #[inline]
2712    pub(crate) fn get_minutes_unsigned(&self) -> t::SpanMinutes {
2713        self.minutes
2714    }
2715
2716    #[inline]
2717    pub(crate) fn get_seconds_unsigned(&self) -> t::SpanSeconds {
2718        self.seconds
2719    }
2720
2721    #[inline]
2722    pub(crate) fn get_milliseconds_unsigned(&self) -> t::SpanMilliseconds {
2723        self.milliseconds
2724    }
2725
2726    #[inline]
2727    pub(crate) fn get_microseconds_unsigned(&self) -> t::SpanMicroseconds {
2728        self.microseconds
2729    }
2730
2731    #[inline]
2732    pub(crate) fn get_nanoseconds_unsigned(&self) -> t::SpanNanoseconds {
2733        self.nanoseconds
2734    }
2735
2736    #[inline]
2737    fn get_sign_ranged(&self) -> ri8<-1, 1> {
2738        self.sign
2739    }
2740
2741    #[inline]
2742    fn get_units_ranged(&self, unit: Unit) -> NoUnits {
2743        match unit {
2744            Unit::Year => self.get_years_ranged().rinto(),
2745            Unit::Month => self.get_months_ranged().rinto(),
2746            Unit::Week => self.get_weeks_ranged().rinto(),
2747            Unit::Day => self.get_days_ranged().rinto(),
2748            Unit::Hour => self.get_hours_ranged().rinto(),
2749            Unit::Minute => self.get_minutes_ranged().rinto(),
2750            Unit::Second => self.get_seconds_ranged().rinto(),
2751            Unit::Millisecond => self.get_milliseconds_ranged().rinto(),
2752            Unit::Microsecond => self.get_microseconds_ranged().rinto(),
2753            Unit::Nanosecond => self.get_nanoseconds_ranged().rinto(),
2754        }
2755    }
2756}
2757
2758/// Crate internal APIs that permit setting units without checks.
2759///
2760/// Callers should be very careful when using these. These notably also do
2761/// not handle updating the sign on the `Span` and require the precisely
2762/// correct integer primitive.
2763impl Span {
2764    #[inline]
2765    pub(crate) fn years_unchecked(self, years: i16) -> Span {
2766        let mut span =
2767            Span { years: t::SpanYears::new_unchecked(years), ..self };
2768        span.units = span.units.set(Unit::Year, years == 0);
2769        span
2770    }
2771
2772    #[inline]
2773    pub(crate) fn months_unchecked(self, months: i32) -> Span {
2774        let mut span =
2775            Span { months: t::SpanMonths::new_unchecked(months), ..self };
2776        span.units = span.units.set(Unit::Month, months == 0);
2777        span
2778    }
2779
2780    #[inline]
2781    pub(crate) fn weeks_unchecked(self, weeks: i32) -> Span {
2782        let mut span =
2783            Span { weeks: t::SpanWeeks::new_unchecked(weeks), ..self };
2784        span.units = span.units.set(Unit::Week, weeks == 0);
2785        span
2786    }
2787
2788    #[inline]
2789    pub(crate) fn days_unchecked(self, days: i32) -> Span {
2790        let mut span = Span { days: t::SpanDays::new_unchecked(days), ..self };
2791        span.units = span.units.set(Unit::Day, days == 0);
2792        span
2793    }
2794
2795    #[inline]
2796    pub(crate) fn hours_unchecked(self, hours: i32) -> Span {
2797        let mut span =
2798            Span { hours: t::SpanHours::new_unchecked(hours), ..self };
2799        span.units = span.units.set(Unit::Hour, hours == 0);
2800        span
2801    }
2802
2803    #[inline]
2804    pub(crate) fn minutes_unchecked(self, minutes: i64) -> Span {
2805        let mut span =
2806            Span { minutes: t::SpanMinutes::new_unchecked(minutes), ..self };
2807        span.units = span.units.set(Unit::Minute, minutes == 0);
2808        span
2809    }
2810
2811    #[inline]
2812    pub(crate) fn seconds_unchecked(self, seconds: i64) -> Span {
2813        let mut span =
2814            Span { seconds: t::SpanSeconds::new_unchecked(seconds), ..self };
2815        span.units = span.units.set(Unit::Second, seconds == 0);
2816        span
2817    }
2818
2819    #[inline]
2820    pub(crate) fn milliseconds_unchecked(self, milliseconds: i64) -> Span {
2821        let mut span = Span {
2822            milliseconds: t::SpanMilliseconds::new_unchecked(milliseconds),
2823            ..self
2824        };
2825        span.units = span.units.set(Unit::Millisecond, milliseconds == 0);
2826        span
2827    }
2828
2829    #[inline]
2830    pub(crate) fn microseconds_unchecked(self, microseconds: i64) -> Span {
2831        let mut span = Span {
2832            microseconds: t::SpanMicroseconds::new_unchecked(microseconds),
2833            ..self
2834        };
2835        span.units = span.units.set(Unit::Microsecond, microseconds == 0);
2836        span
2837    }
2838
2839    #[inline]
2840    pub(crate) fn nanoseconds_unchecked(self, nanoseconds: i64) -> Span {
2841        let mut span = Span {
2842            nanoseconds: t::SpanNanoseconds::new_unchecked(nanoseconds),
2843            ..self
2844        };
2845        span.units = span.units.set(Unit::Nanosecond, nanoseconds == 0);
2846        span
2847    }
2848
2849    #[inline]
2850    pub(crate) fn sign_unchecked(self, sign: Sign) -> Span {
2851        Span { sign, ..self }
2852    }
2853}
2854
2855/// Crate internal helper routines.
2856impl Span {
2857    /// Converts the given number of nanoseconds to a `Span` whose units do not
2858    /// exceed `largest`.
2859    ///
2860    /// Note that `largest` is capped at `Unit::Week`. Note though that if
2861    /// any unit greater than `Unit::Week` is given, then it is treated as
2862    /// `Unit::Day`. The only way to get weeks in the `Span` returned is to
2863    /// specifically request `Unit::Week`.
2864    ///
2865    /// And also note that days in this context are civil days. That is, they
2866    /// are always 24 hours long. Callers needing to deal with variable length
2867    /// days should do so outside of this routine and should not provide a
2868    /// `largest` unit bigger than `Unit::Hour`.
2869    pub(crate) fn from_invariant_nanoseconds(
2870        largest: Unit,
2871        nanos: NoUnits128,
2872    ) -> Result<Span, Error> {
2873        let mut span = Span::new();
2874        match largest {
2875            Unit::Week => {
2876                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2877                span = span.try_nanoseconds_ranged(
2878                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2879                )?;
2880                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2881                span = span.try_microseconds_ranged(
2882                    micros.rem_ceil(t::MICROS_PER_MILLI),
2883                )?;
2884                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2885                span = span.try_milliseconds_ranged(
2886                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2887                )?;
2888                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2889                span = span.try_seconds_ranged(
2890                    secs.rem_ceil(t::SECONDS_PER_MINUTE),
2891                )?;
2892                let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2893                span = span
2894                    .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2895                let days = hours.div_ceil(t::HOURS_PER_CIVIL_DAY);
2896                span = span.try_hours_ranged(
2897                    hours.rem_ceil(t::HOURS_PER_CIVIL_DAY),
2898                )?;
2899                let weeks = days.div_ceil(t::DAYS_PER_CIVIL_WEEK);
2900                span = span
2901                    .try_days_ranged(days.rem_ceil(t::DAYS_PER_CIVIL_WEEK))?;
2902                span = span.weeks_ranged(weeks.try_rinto("weeks")?);
2903                Ok(span)
2904            }
2905            Unit::Year | Unit::Month | Unit::Day => {
2906                // Unit::Year | Unit::Month | Unit::Week | Unit::Day => {
2907                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2908                span = span.try_nanoseconds_ranged(
2909                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2910                )?;
2911                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2912                span = span.try_microseconds_ranged(
2913                    micros.rem_ceil(t::MICROS_PER_MILLI),
2914                )?;
2915                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2916                span = span.try_milliseconds_ranged(
2917                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2918                )?;
2919                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2920                span = span.try_seconds_ranged(
2921                    secs.rem_ceil(t::SECONDS_PER_MINUTE),
2922                )?;
2923                let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2924                span = span
2925                    .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2926                let days = hours.div_ceil(t::HOURS_PER_CIVIL_DAY);
2927                span = span.try_hours_ranged(
2928                    hours.rem_ceil(t::HOURS_PER_CIVIL_DAY),
2929                )?;
2930                span = span.try_days_ranged(days)?;
2931                Ok(span)
2932            }
2933            Unit::Hour => {
2934                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2935                span = span.try_nanoseconds_ranged(
2936                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2937                )?;
2938                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2939                span = span.try_microseconds_ranged(
2940                    micros.rem_ceil(t::MICROS_PER_MILLI),
2941                )?;
2942                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2943                span = span.try_milliseconds_ranged(
2944                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2945                )?;
2946                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2947                span = span.try_seconds_ranged(
2948                    secs.rem_ceil(t::SECONDS_PER_MINUTE),
2949                )?;
2950                let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2951                span = span
2952                    .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2953                span = span.try_hours_ranged(hours)?;
2954                Ok(span)
2955            }
2956            Unit::Minute => {
2957                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2958                span = span.try_nanoseconds_ranged(
2959                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2960                )?;
2961                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2962                span = span.try_microseconds_ranged(
2963                    micros.rem_ceil(t::MICROS_PER_MILLI),
2964                )?;
2965                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2966                span = span.try_milliseconds_ranged(
2967                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2968                )?;
2969                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2970                span =
2971                    span.try_seconds(secs.rem_ceil(t::SECONDS_PER_MINUTE))?;
2972                span = span.try_minutes_ranged(mins)?;
2973                Ok(span)
2974            }
2975            Unit::Second => {
2976                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2977                span = span.try_nanoseconds_ranged(
2978                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2979                )?;
2980                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2981                span = span.try_microseconds_ranged(
2982                    micros.rem_ceil(t::MICROS_PER_MILLI),
2983                )?;
2984                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2985                span = span.try_milliseconds_ranged(
2986                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2987                )?;
2988                span = span.try_seconds_ranged(secs)?;
2989                Ok(span)
2990            }
2991            Unit::Millisecond => {
2992                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2993                span = span.try_nanoseconds_ranged(
2994                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2995                )?;
2996                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2997                span = span.try_microseconds_ranged(
2998                    micros.rem_ceil(t::MICROS_PER_MILLI),
2999                )?;
3000                span = span.try_milliseconds_ranged(millis)?;
3001                Ok(span)
3002            }
3003            Unit::Microsecond => {
3004                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
3005                span = span.try_nanoseconds_ranged(
3006                    nanos.rem_ceil(t::NANOS_PER_MICRO),
3007                )?;
3008                span = span.try_microseconds_ranged(micros)?;
3009                Ok(span)
3010            }
3011            Unit::Nanosecond => {
3012                span = span.try_nanoseconds_ranged(nanos)?;
3013                Ok(span)
3014            }
3015        }
3016    }
3017
3018    /// Converts the non-variable units of this `Span` to a total number of
3019    /// nanoseconds.
3020    ///
3021    /// This includes days and weeks, even though they can be of irregular
3022    /// length during time zone transitions. If this applies, then callers
3023    /// should set the days and weeks to `0` before calling this routine.
3024    ///
3025    /// All units above weeks are always ignored.
3026    #[inline]
3027    pub(crate) fn to_invariant_nanoseconds(&self) -> NoUnits128 {
3028        let mut nanos = NoUnits128::rfrom(self.get_nanoseconds_ranged());
3029        nanos += NoUnits128::rfrom(self.get_microseconds_ranged())
3030            * t::NANOS_PER_MICRO;
3031        nanos += NoUnits128::rfrom(self.get_milliseconds_ranged())
3032            * t::NANOS_PER_MILLI;
3033        nanos +=
3034            NoUnits128::rfrom(self.get_seconds_ranged()) * t::NANOS_PER_SECOND;
3035        nanos +=
3036            NoUnits128::rfrom(self.get_minutes_ranged()) * t::NANOS_PER_MINUTE;
3037        nanos +=
3038            NoUnits128::rfrom(self.get_hours_ranged()) * t::NANOS_PER_HOUR;
3039        nanos +=
3040            NoUnits128::rfrom(self.get_days_ranged()) * t::NANOS_PER_CIVIL_DAY;
3041        nanos += NoUnits128::rfrom(self.get_weeks_ranged())
3042            * t::NANOS_PER_CIVIL_WEEK;
3043        nanos
3044    }
3045
3046    /// Converts the non-variable units of this `Span` to a total number of
3047    /// seconds if there is no fractional second component. Otherwise,
3048    /// `None` is returned.
3049    ///
3050    /// This is useful for short-circuiting in arithmetic operations when
3051    /// it's faster to only deal with seconds. And in particular, acknowledges
3052    /// that nanosecond precision durations are somewhat rare.
3053    ///
3054    /// This includes days and weeks, even though they can be of irregular
3055    /// length during time zone transitions. If this applies, then callers
3056    /// should set the days and weeks to `0` before calling this routine.
3057    ///
3058    /// All units above weeks are always ignored.
3059    #[inline]
3060    pub(crate) fn to_invariant_seconds(&self) -> Option<NoUnits> {
3061        if self.has_fractional_seconds() {
3062            return None;
3063        }
3064        let mut seconds = NoUnits::rfrom(self.get_seconds_ranged());
3065        seconds +=
3066            NoUnits::rfrom(self.get_minutes_ranged()) * t::SECONDS_PER_MINUTE;
3067        seconds +=
3068            NoUnits::rfrom(self.get_hours_ranged()) * t::SECONDS_PER_HOUR;
3069        seconds +=
3070            NoUnits::rfrom(self.get_days_ranged()) * t::SECONDS_PER_CIVIL_DAY;
3071        seconds += NoUnits::rfrom(self.get_weeks_ranged())
3072            * t::SECONDS_PER_CIVIL_WEEK;
3073        Some(seconds)
3074    }
3075
3076    /// Rebalances the invariant units (days or lower) on this span so that
3077    /// the largest possible non-zero unit is the one given.
3078    ///
3079    /// Units above day are ignored and dropped.
3080    ///
3081    /// If the given unit is greater than days, then it is treated as-if it
3082    /// were days.
3083    ///
3084    /// # Errors
3085    ///
3086    /// This can return an error in the case of lop-sided units. For example,
3087    /// if this span has maximal values for all units, then rebalancing is
3088    /// not possible because the number of days after balancing would exceed
3089    /// the limit.
3090    #[cfg(test)] // currently only used in zic parser?
3091    #[inline]
3092    pub(crate) fn rebalance(self, unit: Unit) -> Result<Span, Error> {
3093        Span::from_invariant_nanoseconds(unit, self.to_invariant_nanoseconds())
3094    }
3095
3096    /// Returns true if and only if this span has at least one non-zero
3097    /// fractional second unit.
3098    #[inline]
3099    pub(crate) fn has_fractional_seconds(&self) -> bool {
3100        self.milliseconds != C(0)
3101            || self.microseconds != C(0)
3102            || self.nanoseconds != C(0)
3103    }
3104
3105    /// Returns an equivalent span, but with all non-calendar (units below
3106    /// days) set to zero.
3107    #[cfg_attr(feature = "perf-inline", inline(always))]
3108    pub(crate) fn only_calendar(self) -> Span {
3109        let mut span = self;
3110        span.hours = t::SpanHours::N::<0>();
3111        span.minutes = t::SpanMinutes::N::<0>();
3112        span.seconds = t::SpanSeconds::N::<0>();
3113        span.milliseconds = t::SpanMilliseconds::N::<0>();
3114        span.microseconds = t::SpanMicroseconds::N::<0>();
3115        span.nanoseconds = t::SpanNanoseconds::N::<0>();
3116        if span.sign != C(0)
3117            && span.years == C(0)
3118            && span.months == C(0)
3119            && span.weeks == C(0)
3120            && span.days == C(0)
3121        {
3122            span.sign = t::Sign::N::<0>();
3123        }
3124        span.units = span.units.only_calendar();
3125        span
3126    }
3127
3128    /// Returns an equivalent span, but with all calendar (units above
3129    /// hours) set to zero.
3130    #[cfg_attr(feature = "perf-inline", inline(always))]
3131    pub(crate) fn only_time(self) -> Span {
3132        let mut span = self;
3133        span.years = t::SpanYears::N::<0>();
3134        span.months = t::SpanMonths::N::<0>();
3135        span.weeks = t::SpanWeeks::N::<0>();
3136        span.days = t::SpanDays::N::<0>();
3137        if span.sign != C(0)
3138            && span.hours == C(0)
3139            && span.minutes == C(0)
3140            && span.seconds == C(0)
3141            && span.milliseconds == C(0)
3142            && span.microseconds == C(0)
3143            && span.nanoseconds == C(0)
3144        {
3145            span.sign = t::Sign::N::<0>();
3146        }
3147        span.units = span.units.only_time();
3148        span
3149    }
3150
3151    /// Returns an equivalent span, but with all units greater than or equal to
3152    /// the one given set to zero.
3153    #[cfg_attr(feature = "perf-inline", inline(always))]
3154    pub(crate) fn only_lower(self, unit: Unit) -> Span {
3155        let mut span = self;
3156        // Unit::Nanosecond is the minimum, so nothing can be smaller than it.
3157        if unit <= Unit::Microsecond {
3158            span = span.microseconds_ranged(C(0).rinto());
3159        }
3160        if unit <= Unit::Millisecond {
3161            span = span.milliseconds_ranged(C(0).rinto());
3162        }
3163        if unit <= Unit::Second {
3164            span = span.seconds_ranged(C(0).rinto());
3165        }
3166        if unit <= Unit::Minute {
3167            span = span.minutes_ranged(C(0).rinto());
3168        }
3169        if unit <= Unit::Hour {
3170            span = span.hours_ranged(C(0).rinto());
3171        }
3172        if unit <= Unit::Day {
3173            span = span.days_ranged(C(0).rinto());
3174        }
3175        if unit <= Unit::Week {
3176            span = span.weeks_ranged(C(0).rinto());
3177        }
3178        if unit <= Unit::Month {
3179            span = span.months_ranged(C(0).rinto());
3180        }
3181        if unit <= Unit::Year {
3182            span = span.years_ranged(C(0).rinto());
3183        }
3184        span
3185    }
3186
3187    /// Returns an equivalent span, but with all units less than the one given
3188    /// set to zero.
3189    #[cfg_attr(feature = "perf-inline", inline(always))]
3190    pub(crate) fn without_lower(self, unit: Unit) -> Span {
3191        let mut span = self;
3192        if unit > Unit::Nanosecond {
3193            span = span.nanoseconds_ranged(C(0).rinto());
3194        }
3195        if unit > Unit::Microsecond {
3196            span = span.microseconds_ranged(C(0).rinto());
3197        }
3198        if unit > Unit::Millisecond {
3199            span = span.milliseconds_ranged(C(0).rinto());
3200        }
3201        if unit > Unit::Second {
3202            span = span.seconds_ranged(C(0).rinto());
3203        }
3204        if unit > Unit::Minute {
3205            span = span.minutes_ranged(C(0).rinto());
3206        }
3207        if unit > Unit::Hour {
3208            span = span.hours_ranged(C(0).rinto());
3209        }
3210        if unit > Unit::Day {
3211            span = span.days_ranged(C(0).rinto());
3212        }
3213        if unit > Unit::Week {
3214            span = span.weeks_ranged(C(0).rinto());
3215        }
3216        if unit > Unit::Month {
3217            span = span.months_ranged(C(0).rinto());
3218        }
3219        // Unit::Year is the max, so nothing can be bigger than it.
3220        span
3221    }
3222
3223    /// Returns an error corresponding to the smallest non-time non-zero unit.
3224    ///
3225    /// If all non-time units are zero, then this returns `None`.
3226    #[cfg_attr(feature = "perf-inline", inline(always))]
3227    pub(crate) fn smallest_non_time_non_zero_unit_error(
3228        &self,
3229    ) -> Option<Error> {
3230        let non_time_unit = self.largest_calendar_unit()?;
3231        Some(Error::from(E::NotAllowedCalendarUnits { unit: non_time_unit }))
3232    }
3233
3234    /// Returns the largest non-zero calendar unit, or `None` if there are no
3235    /// non-zero calendar units.
3236    #[inline]
3237    pub(crate) fn largest_calendar_unit(&self) -> Option<Unit> {
3238        self.units().only_calendar().largest_unit()
3239    }
3240
3241    /// Returns the largest non-zero unit in this span.
3242    ///
3243    /// If all components of this span are zero, then `Unit::Nanosecond` is
3244    /// returned.
3245    #[inline]
3246    pub(crate) fn largest_unit(&self) -> Unit {
3247        self.units().largest_unit().unwrap_or(Unit::Nanosecond)
3248    }
3249
3250    /// Returns the set of units on this `Span`.
3251    #[inline]
3252    pub(crate) fn units(&self) -> UnitSet {
3253        self.units
3254    }
3255
3256    /// Returns a string containing the value of all non-zero fields.
3257    ///
3258    /// This is useful for debugging. Normally, this would be the "alternate"
3259    /// debug impl (perhaps), but that's what insta uses and I preferred having
3260    /// the friendly format used there since it is much more terse.
3261    #[cfg(feature = "alloc")]
3262    #[allow(dead_code)]
3263    pub(crate) fn debug(&self) -> alloc::string::String {
3264        use core::fmt::Write;
3265
3266        let mut buf = alloc::string::String::new();
3267        write!(buf, "Span {{ sign: {:?}, units: {:?}", self.sign, self.units)
3268            .unwrap();
3269        if self.years != C(0) {
3270            write!(buf, ", years: {:?}", self.years).unwrap();
3271        }
3272        if self.months != C(0) {
3273            write!(buf, ", months: {:?}", self.months).unwrap();
3274        }
3275        if self.weeks != C(0) {
3276            write!(buf, ", weeks: {:?}", self.weeks).unwrap();
3277        }
3278        if self.days != C(0) {
3279            write!(buf, ", days: {:?}", self.days).unwrap();
3280        }
3281        if self.hours != C(0) {
3282            write!(buf, ", hours: {:?}", self.hours).unwrap();
3283        }
3284        if self.minutes != C(0) {
3285            write!(buf, ", minutes: {:?}", self.minutes).unwrap();
3286        }
3287        if self.seconds != C(0) {
3288            write!(buf, ", seconds: {:?}", self.seconds).unwrap();
3289        }
3290        if self.milliseconds != C(0) {
3291            write!(buf, ", milliseconds: {:?}", self.milliseconds).unwrap();
3292        }
3293        if self.microseconds != C(0) {
3294            write!(buf, ", microseconds: {:?}", self.microseconds).unwrap();
3295        }
3296        if self.nanoseconds != C(0) {
3297            write!(buf, ", nanoseconds: {:?}", self.nanoseconds).unwrap();
3298        }
3299        buf.push_str(" }}");
3300        buf
3301    }
3302
3303    /// Given some new units to set on this span and the span updates with the
3304    /// new units, this determines the what the sign of `new` should be.
3305    #[inline]
3306    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3307        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3308            // Negative units anywhere always makes the entire span negative.
3309            if units < C(0) {
3310                return Sign::N::<-1>();
3311            }
3312            let mut new_is_zero = new.sign == C(0) && units == C(0);
3313            // When `units == 0` and it was previously non-zero, then
3314            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3315            // when it should be true. So in this case, we need to re-check all
3316            // the units to set the sign correctly.
3317            if units == C(0) {
3318                new_is_zero = new.years == C(0)
3319                    && new.months == C(0)
3320                    && new.weeks == C(0)
3321                    && new.days == C(0)
3322                    && new.hours == C(0)
3323                    && new.minutes == C(0)
3324                    && new.seconds == C(0)
3325                    && new.milliseconds == C(0)
3326                    && new.microseconds == C(0)
3327                    && new.nanoseconds == C(0);
3328            }
3329            match (span.is_zero(), new_is_zero) {
3330                (_, true) => Sign::N::<0>(),
3331                (true, false) => units.signum().rinto(),
3332                // If the old and new span are both non-zero, and we know our
3333                // new units are not negative, then the sign remains unchanged.
3334                (false, false) => new.sign,
3335            }
3336        }
3337        imp(self, units.rinto(), new)
3338    }
3339}
3340
3341impl Default for Span {
3342    #[inline]
3343    fn default() -> Span {
3344        Span {
3345            sign: ri8::N::<0>(),
3346            units: UnitSet::empty(),
3347            years: C(0).rinto(),
3348            months: C(0).rinto(),
3349            weeks: C(0).rinto(),
3350            days: C(0).rinto(),
3351            hours: C(0).rinto(),
3352            minutes: C(0).rinto(),
3353            seconds: C(0).rinto(),
3354            milliseconds: C(0).rinto(),
3355            microseconds: C(0).rinto(),
3356            nanoseconds: C(0).rinto(),
3357        }
3358    }
3359}
3360
3361impl core::fmt::Debug for Span {
3362    #[inline]
3363    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3364        use crate::fmt::StdFmtWrite;
3365
3366        friendly::DEFAULT_SPAN_PRINTER
3367            .print_span(self, StdFmtWrite(f))
3368            .map_err(|_| core::fmt::Error)
3369    }
3370}
3371
3372impl core::fmt::Display for Span {
3373    #[inline]
3374    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3375        use crate::fmt::StdFmtWrite;
3376
3377        if f.alternate() {
3378            friendly::DEFAULT_SPAN_PRINTER
3379                .print_span(self, StdFmtWrite(f))
3380                .map_err(|_| core::fmt::Error)
3381        } else {
3382            temporal::DEFAULT_SPAN_PRINTER
3383                .print_span(self, StdFmtWrite(f))
3384                .map_err(|_| core::fmt::Error)
3385        }
3386    }
3387}
3388
3389impl core::str::FromStr for Span {
3390    type Err = Error;
3391
3392    #[inline]
3393    fn from_str(string: &str) -> Result<Span, Error> {
3394        parse_iso_or_friendly(string.as_bytes())
3395    }
3396}
3397
3398impl core::ops::Neg for Span {
3399    type Output = Span;
3400
3401    #[inline]
3402    fn neg(self) -> Span {
3403        self.negate()
3404    }
3405}
3406
3407/// This multiplies each unit in a span by an integer.
3408///
3409/// This panics on overflow. For checked arithmetic, use [`Span::checked_mul`].
3410impl core::ops::Mul<i64> for Span {
3411    type Output = Span;
3412
3413    #[inline]
3414    fn mul(self, rhs: i64) -> Span {
3415        self.checked_mul(rhs)
3416            .expect("multiplying `Span` by a scalar overflowed")
3417    }
3418}
3419
3420/// This multiplies each unit in a span by an integer.
3421///
3422/// This panics on overflow. For checked arithmetic, use [`Span::checked_mul`].
3423impl core::ops::Mul<Span> for i64 {
3424    type Output = Span;
3425
3426    #[inline]
3427    fn mul(self, rhs: Span) -> Span {
3428        rhs.checked_mul(self)
3429            .expect("multiplying `Span` by a scalar overflowed")
3430    }
3431}
3432
3433/// Converts a `Span` to a [`std::time::Duration`].
3434///
3435/// # Errors
3436///
3437/// This can fail for only two reasons:
3438///
3439/// * The span is negative. This is an error because a `std::time::Duration` is
3440///   unsigned.)
3441/// * The span has any non-zero units greater than hours. This is an error
3442///   because it's impossible to determine the length of, e.g., a month without
3443///   a reference date.
3444///
3445/// This can never result in overflow because a `Duration` can represent a
3446/// bigger span of time than `Span` when limited to units of hours or lower.
3447///
3448/// If you need to convert a `Span` to a `Duration` that has non-zero
3449/// units bigger than hours, then please use [`Span::to_duration`] with a
3450/// corresponding relative date.
3451///
3452/// # Example: maximal span
3453///
3454/// This example shows the maximum possible span using units of hours or
3455/// smaller, and the corresponding `Duration` value:
3456///
3457/// ```
3458/// use std::time::Duration;
3459///
3460/// use jiff::Span;
3461///
3462/// let sp = Span::new()
3463///     .hours(175_307_616)
3464///     .minutes(10_518_456_960i64)
3465///     .seconds(631_107_417_600i64)
3466///     .milliseconds(631_107_417_600_000i64)
3467///     .microseconds(631_107_417_600_000_000i64)
3468///     .nanoseconds(9_223_372_036_854_775_807i64);
3469/// let duration = Duration::try_from(sp)?;
3470/// assert_eq!(duration, Duration::new(3_164_760_460_036, 854_775_807));
3471///
3472/// # Ok::<(), Box<dyn std::error::Error>>(())
3473/// ```
3474///
3475/// # Example: converting a negative span
3476///
3477/// Since a `Span` is signed and a `Duration` is unsigned, converting
3478/// a negative `Span` to `Duration` will always fail. One can use
3479/// [`Span::signum`] to get the sign of the span and [`Span::abs`] to make the
3480/// span positive before converting it to a `Duration`:
3481///
3482/// ```
3483/// use std::time::Duration;
3484///
3485/// use jiff::{Span, ToSpan};
3486///
3487/// let span = -86_400.seconds().nanoseconds(1);
3488/// let (sign, duration) = (span.signum(), Duration::try_from(span.abs())?);
3489/// assert_eq!((sign, duration), (-1, Duration::new(86_400, 1)));
3490///
3491/// # Ok::<(), Box<dyn std::error::Error>>(())
3492/// ```
3493impl TryFrom<Span> for UnsignedDuration {
3494    type Error = Error;
3495
3496    #[inline]
3497    fn try_from(sp: Span) -> Result<UnsignedDuration, Error> {
3498        // This isn't needed, but improves error messages.
3499        if sp.is_negative() {
3500            return Err(Error::from(E::ConvertNegative));
3501        }
3502        SignedDuration::try_from(sp).and_then(UnsignedDuration::try_from)
3503    }
3504}
3505
3506/// Converts a [`std::time::Duration`] to a `Span`.
3507///
3508/// The span returned from this conversion will only ever have non-zero units
3509/// of seconds or smaller.
3510///
3511/// # Errors
3512///
3513/// This only fails when the given `Duration` overflows the maximum number of
3514/// seconds representable by a `Span`.
3515///
3516/// # Example
3517///
3518/// This shows a basic conversion:
3519///
3520/// ```
3521/// use std::time::Duration;
3522///
3523/// use jiff::{Span, ToSpan};
3524///
3525/// let duration = Duration::new(86_400, 123_456_789);
3526/// let span = Span::try_from(duration)?;
3527/// // A duration-to-span conversion always results in a span with
3528/// // non-zero units no bigger than seconds.
3529/// assert_eq!(
3530///     span.fieldwise(),
3531///     86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
3532/// );
3533///
3534/// # Ok::<(), Box<dyn std::error::Error>>(())
3535/// ```
3536///
3537/// # Example: rounding
3538///
3539/// This example shows how to convert a `Duration` to a `Span`, and then round
3540/// it up to bigger units given a relative date:
3541///
3542/// ```
3543/// use std::time::Duration;
3544///
3545/// use jiff::{civil::date, Span, SpanRound, ToSpan, Unit};
3546///
3547/// let duration = Duration::new(450 * 86_401, 0);
3548/// let span = Span::try_from(duration)?;
3549/// // We get back a simple span of just seconds:
3550/// assert_eq!(span.fieldwise(), Span::new().seconds(450 * 86_401));
3551/// // But we can balance it up to bigger units:
3552/// let options = SpanRound::new()
3553///     .largest(Unit::Year)
3554///     .relative(date(2024, 1, 1));
3555/// assert_eq!(
3556///     span.round(options)?,
3557///     1.year().months(2).days(25).minutes(7).seconds(30).fieldwise(),
3558/// );
3559///
3560/// # Ok::<(), Box<dyn std::error::Error>>(())
3561/// ```
3562impl TryFrom<UnsignedDuration> for Span {
3563    type Error = Error;
3564
3565    #[inline]
3566    fn try_from(d: UnsignedDuration) -> Result<Span, Error> {
3567        let seconds = i64::try_from(d.as_secs())
3568            .map_err(|_| Error::slim_range("unsigned duration seconds"))?;
3569        let nanoseconds = i64::from(d.subsec_nanos());
3570        let milliseconds = nanoseconds / t::NANOS_PER_MILLI.value();
3571        let microseconds = (nanoseconds % t::NANOS_PER_MILLI.value())
3572            / t::NANOS_PER_MICRO.value();
3573        let nanoseconds = nanoseconds % t::NANOS_PER_MICRO.value();
3574
3575        let span = Span::new().try_seconds(seconds)?;
3576        // These are all OK because `Duration::subsec_nanos` is guaranteed to
3577        // return less than 1_000_000_000 nanoseconds. And splitting that up
3578        // into millis, micros and nano components is guaranteed to fit into
3579        // the limits of a `Span`.
3580        Ok(span
3581            .milliseconds(milliseconds)
3582            .microseconds(microseconds)
3583            .nanoseconds(nanoseconds))
3584    }
3585}
3586
3587/// Converts a `Span` to a [`SignedDuration`].
3588///
3589/// # Errors
3590///
3591/// This can fail for only when the span has any non-zero units greater than
3592/// hours. This is an error because it's impossible to determine the length of,
3593/// e.g., a month without a reference date.
3594///
3595/// This can never result in overflow because a `SignedDuration` can represent
3596/// a bigger span of time than `Span` when limited to units of hours or lower.
3597///
3598/// If you need to convert a `Span` to a `SignedDuration` that has non-zero
3599/// units bigger than hours, then please use [`Span::to_duration`] with a
3600/// corresponding relative date.
3601///
3602/// # Example: maximal span
3603///
3604/// This example shows the maximum possible span using units of hours or
3605/// smaller, and the corresponding `SignedDuration` value:
3606///
3607/// ```
3608/// use jiff::{SignedDuration, Span};
3609///
3610/// let sp = Span::new()
3611///     .hours(175_307_616)
3612///     .minutes(10_518_456_960i64)
3613///     .seconds(631_107_417_600i64)
3614///     .milliseconds(631_107_417_600_000i64)
3615///     .microseconds(631_107_417_600_000_000i64)
3616///     .nanoseconds(9_223_372_036_854_775_807i64);
3617/// let duration = SignedDuration::try_from(sp)?;
3618/// assert_eq!(duration, SignedDuration::new(3_164_760_460_036, 854_775_807));
3619///
3620/// # Ok::<(), Box<dyn std::error::Error>>(())
3621/// ```
3622impl TryFrom<Span> for SignedDuration {
3623    type Error = Error;
3624
3625    #[inline]
3626    fn try_from(sp: Span) -> Result<SignedDuration, Error> {
3627        requires_relative_date_err(sp.largest_unit())
3628            .context(E::ConvertSpanToSignedDuration)?;
3629        Ok(sp.to_duration_invariant())
3630    }
3631}
3632
3633/// Converts a [`SignedDuration`] to a `Span`.
3634///
3635/// The span returned from this conversion will only ever have non-zero units
3636/// of seconds or smaller.
3637///
3638/// # Errors
3639///
3640/// This only fails when the given `SignedDuration` overflows the maximum
3641/// number of seconds representable by a `Span`.
3642///
3643/// # Example
3644///
3645/// This shows a basic conversion:
3646///
3647/// ```
3648/// use jiff::{SignedDuration, Span, ToSpan};
3649///
3650/// let duration = SignedDuration::new(86_400, 123_456_789);
3651/// let span = Span::try_from(duration)?;
3652/// // A duration-to-span conversion always results in a span with
3653/// // non-zero units no bigger than seconds.
3654/// assert_eq!(
3655///     span.fieldwise(),
3656///     86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
3657/// );
3658///
3659/// # Ok::<(), Box<dyn std::error::Error>>(())
3660/// ```
3661///
3662/// # Example: rounding
3663///
3664/// This example shows how to convert a `SignedDuration` to a `Span`, and then
3665/// round it up to bigger units given a relative date:
3666///
3667/// ```
3668/// use jiff::{civil::date, SignedDuration, Span, SpanRound, ToSpan, Unit};
3669///
3670/// let duration = SignedDuration::new(450 * 86_401, 0);
3671/// let span = Span::try_from(duration)?;
3672/// // We get back a simple span of just seconds:
3673/// assert_eq!(span.fieldwise(), Span::new().seconds(450 * 86_401));
3674/// // But we can balance it up to bigger units:
3675/// let options = SpanRound::new()
3676///     .largest(Unit::Year)
3677///     .relative(date(2024, 1, 1));
3678/// assert_eq!(
3679///     span.round(options)?,
3680///     1.year().months(2).days(25).minutes(7).seconds(30).fieldwise(),
3681/// );
3682///
3683/// # Ok::<(), Box<dyn std::error::Error>>(())
3684/// ```
3685impl TryFrom<SignedDuration> for Span {
3686    type Error = Error;
3687
3688    #[inline]
3689    fn try_from(d: SignedDuration) -> Result<Span, Error> {
3690        let seconds = d.as_secs();
3691        let nanoseconds = i64::from(d.subsec_nanos());
3692        let milliseconds = nanoseconds / t::NANOS_PER_MILLI.value();
3693        let microseconds = (nanoseconds % t::NANOS_PER_MILLI.value())
3694            / t::NANOS_PER_MICRO.value();
3695        let nanoseconds = nanoseconds % t::NANOS_PER_MICRO.value();
3696
3697        let span = Span::new().try_seconds(seconds)?;
3698        // These are all OK because `|SignedDuration::subsec_nanos|` is
3699        // guaranteed to return less than 1_000_000_000 nanoseconds. And
3700        // splitting that up into millis, micros and nano components is
3701        // guaranteed to fit into the limits of a `Span`.
3702        Ok(span
3703            .milliseconds(milliseconds)
3704            .microseconds(microseconds)
3705            .nanoseconds(nanoseconds))
3706    }
3707}
3708
3709#[cfg(feature = "serde")]
3710impl serde_core::Serialize for Span {
3711    #[inline]
3712    fn serialize<S: serde_core::Serializer>(
3713        &self,
3714        serializer: S,
3715    ) -> Result<S::Ok, S::Error> {
3716        serializer.collect_str(self)
3717    }
3718}
3719
3720#[cfg(feature = "serde")]
3721impl<'de> serde_core::Deserialize<'de> for Span {
3722    #[inline]
3723    fn deserialize<D: serde_core::Deserializer<'de>>(
3724        deserializer: D,
3725    ) -> Result<Span, D::Error> {
3726        use serde_core::de;
3727
3728        struct SpanVisitor;
3729
3730        impl<'de> de::Visitor<'de> for SpanVisitor {
3731            type Value = Span;
3732
3733            fn expecting(
3734                &self,
3735                f: &mut core::fmt::Formatter,
3736            ) -> core::fmt::Result {
3737                f.write_str("a span duration string")
3738            }
3739
3740            #[inline]
3741            fn visit_bytes<E: de::Error>(
3742                self,
3743                value: &[u8],
3744            ) -> Result<Span, E> {
3745                parse_iso_or_friendly(value).map_err(de::Error::custom)
3746            }
3747
3748            #[inline]
3749            fn visit_str<E: de::Error>(self, value: &str) -> Result<Span, E> {
3750                self.visit_bytes(value.as_bytes())
3751            }
3752        }
3753
3754        deserializer.deserialize_str(SpanVisitor)
3755    }
3756}
3757
3758#[cfg(test)]
3759impl quickcheck::Arbitrary for Span {
3760    fn arbitrary(g: &mut quickcheck::Gen) -> Span {
3761        // In order to sample from the full space of possible spans, we need
3762        // to provide a relative datetime. But if we do that, then it's
3763        // possible the span plus the datetime overflows. So we pick one
3764        // datetime and shrink the size of the span we can produce.
3765        type Nanos = ri64<-631_107_417_600_000_000, 631_107_417_600_000_000>;
3766        let nanos = Nanos::arbitrary(g).get();
3767        let relative =
3768            SpanRelativeTo::from(DateTime::constant(0, 1, 1, 0, 0, 0, 0));
3769        let round =
3770            SpanRound::new().largest(Unit::arbitrary(g)).relative(relative);
3771        Span::new().nanoseconds(nanos).round(round).unwrap()
3772    }
3773
3774    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
3775        alloc::boxed::Box::new(
3776            (
3777                (
3778                    self.get_years_ranged(),
3779                    self.get_months_ranged(),
3780                    self.get_weeks_ranged(),
3781                    self.get_days_ranged(),
3782                ),
3783                (
3784                    self.get_hours_ranged(),
3785                    self.get_minutes_ranged(),
3786                    self.get_seconds_ranged(),
3787                    self.get_milliseconds_ranged(),
3788                ),
3789                (
3790                    self.get_microseconds_ranged(),
3791                    self.get_nanoseconds_ranged(),
3792                ),
3793            )
3794                .shrink()
3795                .filter_map(
3796                    |(
3797                        (years, months, weeks, days),
3798                        (hours, minutes, seconds, milliseconds),
3799                        (microseconds, nanoseconds),
3800                    )| {
3801                        let span = Span::new()
3802                            .years_ranged(years)
3803                            .months_ranged(months)
3804                            .weeks_ranged(weeks)
3805                            .days_ranged(days)
3806                            .hours_ranged(hours)
3807                            .minutes_ranged(minutes)
3808                            .seconds_ranged(seconds)
3809                            .milliseconds_ranged(milliseconds)
3810                            .microseconds_ranged(microseconds)
3811                            .nanoseconds_ranged(nanoseconds);
3812                        Some(span)
3813                    },
3814                ),
3815        )
3816    }
3817}
3818
3819/// A wrapper for [`Span`] that implements the `Hash`, `Eq` and `PartialEq`
3820/// traits.
3821///
3822/// A `SpanFieldwise` is meant to make it easy to compare two spans in a "dumb"
3823/// way based purely on its unit values, while still providing a speed bump
3824/// to avoid accidentally doing this comparison on `Span` directly. This is
3825/// distinct from something like [`Span::compare`] that performs a comparison
3826/// on the actual elapsed time of two spans.
3827///
3828/// It is generally discouraged to use `SpanFieldwise` since spans that
3829/// represent an equivalent elapsed amount of time may compare unequal.
3830/// However, in some cases, it is useful to be able to assert precise field
3831/// values. For example, Jiff itself makes heavy use of fieldwise comparisons
3832/// for tests.
3833///
3834/// # Construction
3835///
3836/// While callers may use `SpanFieldwise(span)` (where `span` has type [`Span`])
3837/// to construct a value of this type, callers may find [`Span::fieldwise`]
3838/// more convenient. Namely, `Span::fieldwise` may avoid the need to explicitly
3839/// import `SpanFieldwise`.
3840///
3841/// # Trait implementations
3842///
3843/// In addition to implementing the `Hash`, `Eq` and `PartialEq` traits, this
3844/// type also provides `PartialEq` impls for comparing a `Span` with a
3845/// `SpanFieldwise`. This simplifies comparisons somewhat while still requiring
3846/// that at least one of the values has an explicit fieldwise comparison type.
3847///
3848/// # Safety
3849///
3850/// This type is guaranteed to have the same layout in memory as [`Span`].
3851///
3852/// # Example: the difference between `SpanFieldwise` and [`Span::compare`]
3853///
3854/// In short, `SpanFieldwise` considers `2 hours` and `120 minutes` to be
3855/// distinct values, but `Span::compare` considers them to be equivalent:
3856///
3857/// ```
3858/// use std::cmp::Ordering;
3859/// use jiff::ToSpan;
3860///
3861/// assert_ne!(120.minutes().fieldwise(), 2.hours().fieldwise());
3862/// assert_eq!(120.minutes().compare(2.hours())?, Ordering::Equal);
3863///
3864/// // These comparisons are allowed between a `Span` and a `SpanFieldwise`.
3865/// // Namely, as long as one value is "fieldwise," then the comparison is OK.
3866/// assert_ne!(120.minutes().fieldwise(), 2.hours());
3867/// assert_ne!(120.minutes(), 2.hours().fieldwise());
3868///
3869/// # Ok::<(), Box<dyn std::error::Error>>(())
3870/// ```
3871#[derive(Clone, Copy, Debug, Default)]
3872#[repr(transparent)]
3873pub struct SpanFieldwise(pub Span);
3874
3875// Exists so that things like `-1.day().fieldwise()` works as expected.
3876impl core::ops::Neg for SpanFieldwise {
3877    type Output = SpanFieldwise;
3878
3879    #[inline]
3880    fn neg(self) -> SpanFieldwise {
3881        SpanFieldwise(self.0.negate())
3882    }
3883}
3884
3885impl Eq for SpanFieldwise {}
3886
3887impl PartialEq for SpanFieldwise {
3888    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3889        self.0.sign == rhs.0.sign
3890            && self.0.years == rhs.0.years
3891            && self.0.months == rhs.0.months
3892            && self.0.weeks == rhs.0.weeks
3893            && self.0.days == rhs.0.days
3894            && self.0.hours == rhs.0.hours
3895            && self.0.minutes == rhs.0.minutes
3896            && self.0.seconds == rhs.0.seconds
3897            && self.0.milliseconds == rhs.0.milliseconds
3898            && self.0.microseconds == rhs.0.microseconds
3899            && self.0.nanoseconds == rhs.0.nanoseconds
3900    }
3901}
3902
3903impl<'a> PartialEq<SpanFieldwise> for &'a SpanFieldwise {
3904    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3905        *self == rhs
3906    }
3907}
3908
3909impl PartialEq<Span> for SpanFieldwise {
3910    fn eq(&self, rhs: &Span) -> bool {
3911        self == rhs.fieldwise()
3912    }
3913}
3914
3915impl PartialEq<SpanFieldwise> for Span {
3916    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3917        self.fieldwise() == *rhs
3918    }
3919}
3920
3921impl<'a> PartialEq<SpanFieldwise> for &'a Span {
3922    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3923        self.fieldwise() == *rhs
3924    }
3925}
3926
3927impl core::hash::Hash for SpanFieldwise {
3928    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
3929        self.0.sign.hash(state);
3930        self.0.years.hash(state);
3931        self.0.months.hash(state);
3932        self.0.weeks.hash(state);
3933        self.0.days.hash(state);
3934        self.0.hours.hash(state);
3935        self.0.minutes.hash(state);
3936        self.0.seconds.hash(state);
3937        self.0.milliseconds.hash(state);
3938        self.0.microseconds.hash(state);
3939        self.0.nanoseconds.hash(state);
3940    }
3941}
3942
3943impl From<Span> for SpanFieldwise {
3944    fn from(span: Span) -> SpanFieldwise {
3945        SpanFieldwise(span)
3946    }
3947}
3948
3949impl From<SpanFieldwise> for Span {
3950    fn from(span: SpanFieldwise) -> Span {
3951        span.0
3952    }
3953}
3954
3955/// A trait for enabling concise literals for creating [`Span`] values.
3956///
3957/// In short, this trait lets you write something like `5.seconds()` or
3958/// `1.day()` to create a [`Span`]. Once a `Span` has been created, you can
3959/// use its mutator methods to add more fields. For example,
3960/// `1.day().hours(10)` is equivalent to `Span::new().days(1).hours(10)`.
3961///
3962/// This trait is implemented for the following integer types: `i8`, `i16`,
3963/// `i32` and `i64`.
3964///
3965/// Note that this trait is provided as a convenience and should generally
3966/// only be used for literals in your source code. You should not use this
3967/// trait on numbers provided by end users. Namely, if the number provided
3968/// is not within Jiff's span limits, then these trait methods will panic.
3969/// Instead, use fallible mutator constructors like [`Span::try_days`]
3970/// or [`Span::try_seconds`].
3971///
3972/// # Example
3973///
3974/// ```
3975/// use jiff::ToSpan;
3976///
3977/// assert_eq!(5.days().to_string(), "P5D");
3978/// assert_eq!(5.days().hours(10).to_string(), "P5DT10H");
3979///
3980/// // Negation works and it doesn't matter where the sign goes. It can be
3981/// // applied to the span itself or to the integer.
3982/// assert_eq!((-5.days()).to_string(), "-P5D");
3983/// assert_eq!((-5).days().to_string(), "-P5D");
3984/// ```
3985///
3986/// # Example: alternative via span parsing
3987///
3988/// Another way of tersely building a `Span` value is by parsing a ISO 8601
3989/// duration string:
3990///
3991/// ```
3992/// use jiff::Span;
3993///
3994/// let span = "P5y2m15dT23h30m10s".parse::<Span>()?;
3995/// assert_eq!(
3996///     span.fieldwise(),
3997///     Span::new().years(5).months(2).days(15).hours(23).minutes(30).seconds(10),
3998/// );
3999///
4000/// # Ok::<(), Box<dyn std::error::Error>>(())
4001/// ```
4002pub trait ToSpan: Sized {
4003    /// Create a new span from this integer in units of years.
4004    ///
4005    /// # Panics
4006    ///
4007    /// When `Span::new().years(self)` would panic.
4008    fn years(self) -> Span;
4009
4010    /// Create a new span from this integer in units of months.
4011    ///
4012    /// # Panics
4013    ///
4014    /// When `Span::new().months(self)` would panic.
4015    fn months(self) -> Span;
4016
4017    /// Create a new span from this integer in units of weeks.
4018    ///
4019    /// # Panics
4020    ///
4021    /// When `Span::new().weeks(self)` would panic.
4022    fn weeks(self) -> Span;
4023
4024    /// Create a new span from this integer in units of days.
4025    ///
4026    /// # Panics
4027    ///
4028    /// When `Span::new().days(self)` would panic.
4029    fn days(self) -> Span;
4030
4031    /// Create a new span from this integer in units of hours.
4032    ///
4033    /// # Panics
4034    ///
4035    /// When `Span::new().hours(self)` would panic.
4036    fn hours(self) -> Span;
4037
4038    /// Create a new span from this integer in units of minutes.
4039    ///
4040    /// # Panics
4041    ///
4042    /// When `Span::new().minutes(self)` would panic.
4043    fn minutes(self) -> Span;
4044
4045    /// Create a new span from this integer in units of seconds.
4046    ///
4047    /// # Panics
4048    ///
4049    /// When `Span::new().seconds(self)` would panic.
4050    fn seconds(self) -> Span;
4051
4052    /// Create a new span from this integer in units of milliseconds.
4053    ///
4054    /// # Panics
4055    ///
4056    /// When `Span::new().milliseconds(self)` would panic.
4057    fn milliseconds(self) -> Span;
4058
4059    /// Create a new span from this integer in units of microseconds.
4060    ///
4061    /// # Panics
4062    ///
4063    /// When `Span::new().microseconds(self)` would panic.
4064    fn microseconds(self) -> Span;
4065
4066    /// Create a new span from this integer in units of nanoseconds.
4067    ///
4068    /// # Panics
4069    ///
4070    /// When `Span::new().nanoseconds(self)` would panic.
4071    fn nanoseconds(self) -> Span;
4072
4073    /// Equivalent to `years()`, but reads better for singular units.
4074    #[inline]
4075    fn year(self) -> Span {
4076        self.years()
4077    }
4078
4079    /// Equivalent to `months()`, but reads better for singular units.
4080    #[inline]
4081    fn month(self) -> Span {
4082        self.months()
4083    }
4084
4085    /// Equivalent to `weeks()`, but reads better for singular units.
4086    #[inline]
4087    fn week(self) -> Span {
4088        self.weeks()
4089    }
4090
4091    /// Equivalent to `days()`, but reads better for singular units.
4092    #[inline]
4093    fn day(self) -> Span {
4094        self.days()
4095    }
4096
4097    /// Equivalent to `hours()`, but reads better for singular units.
4098    #[inline]
4099    fn hour(self) -> Span {
4100        self.hours()
4101    }
4102
4103    /// Equivalent to `minutes()`, but reads better for singular units.
4104    #[inline]
4105    fn minute(self) -> Span {
4106        self.minutes()
4107    }
4108
4109    /// Equivalent to `seconds()`, but reads better for singular units.
4110    #[inline]
4111    fn second(self) -> Span {
4112        self.seconds()
4113    }
4114
4115    /// Equivalent to `milliseconds()`, but reads better for singular units.
4116    #[inline]
4117    fn millisecond(self) -> Span {
4118        self.milliseconds()
4119    }
4120
4121    /// Equivalent to `microseconds()`, but reads better for singular units.
4122    #[inline]
4123    fn microsecond(self) -> Span {
4124        self.microseconds()
4125    }
4126
4127    /// Equivalent to `nanoseconds()`, but reads better for singular units.
4128    #[inline]
4129    fn nanosecond(self) -> Span {
4130        self.nanoseconds()
4131    }
4132}
4133
4134macro_rules! impl_to_span {
4135    ($ty:ty) => {
4136        impl ToSpan for $ty {
4137            #[inline]
4138            fn years(self) -> Span {
4139                Span::new().years(self)
4140            }
4141            #[inline]
4142            fn months(self) -> Span {
4143                Span::new().months(self)
4144            }
4145            #[inline]
4146            fn weeks(self) -> Span {
4147                Span::new().weeks(self)
4148            }
4149            #[inline]
4150            fn days(self) -> Span {
4151                Span::new().days(self)
4152            }
4153            #[inline]
4154            fn hours(self) -> Span {
4155                Span::new().hours(self)
4156            }
4157            #[inline]
4158            fn minutes(self) -> Span {
4159                Span::new().minutes(self)
4160            }
4161            #[inline]
4162            fn seconds(self) -> Span {
4163                Span::new().seconds(self)
4164            }
4165            #[inline]
4166            fn milliseconds(self) -> Span {
4167                Span::new().milliseconds(self)
4168            }
4169            #[inline]
4170            fn microseconds(self) -> Span {
4171                Span::new().microseconds(self)
4172            }
4173            #[inline]
4174            fn nanoseconds(self) -> Span {
4175                Span::new().nanoseconds(self)
4176            }
4177        }
4178    };
4179}
4180
4181impl_to_span!(i8);
4182impl_to_span!(i16);
4183impl_to_span!(i32);
4184impl_to_span!(i64);
4185
4186/// A way to refer to a single calendar or clock unit.
4187///
4188/// This type is principally used in APIs involving a [`Span`], which is a
4189/// duration of time. For example, routines like [`Zoned::until`] permit
4190/// specifying the largest unit of the span returned:
4191///
4192/// ```
4193/// use jiff::{Unit, Zoned};
4194///
4195/// let zdt1: Zoned = "2024-07-06 17:40-04[America/New_York]".parse()?;
4196/// let zdt2: Zoned = "2024-11-05 08:00-05[America/New_York]".parse()?;
4197/// let span = zdt1.until((Unit::Year, &zdt2))?;
4198/// assert_eq!(format!("{span:#}"), "3mo 29d 14h 20m");
4199///
4200/// # Ok::<(), Box<dyn std::error::Error>>(())
4201/// ```
4202///
4203/// But a `Unit` is also used in APIs for rounding datetimes themselves:
4204///
4205/// ```
4206/// use jiff::{Unit, Zoned};
4207///
4208/// let zdt: Zoned = "2024-07-06 17:44:22.158-04[America/New_York]".parse()?;
4209/// let nearest_minute = zdt.round(Unit::Minute)?;
4210/// assert_eq!(
4211///     nearest_minute.to_string(),
4212///     "2024-07-06T17:44:00-04:00[America/New_York]",
4213/// );
4214///
4215/// # Ok::<(), Box<dyn std::error::Error>>(())
4216/// ```
4217///
4218/// # Example: ordering
4219///
4220/// This example demonstrates that `Unit` has an ordering defined such that
4221/// bigger units compare greater than smaller units.
4222///
4223/// ```
4224/// use jiff::Unit;
4225///
4226/// assert!(Unit::Year > Unit::Nanosecond);
4227/// assert!(Unit::Day > Unit::Hour);
4228/// assert!(Unit::Hour > Unit::Minute);
4229/// assert!(Unit::Hour > Unit::Minute);
4230/// assert_eq!(Unit::Hour, Unit::Hour);
4231/// ```
4232#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
4233pub enum Unit {
4234    /// A Gregorian calendar year. It usually has 365 days for non-leap years,
4235    /// and 366 days for leap years.
4236    Year = 9,
4237    /// A Gregorian calendar month. It usually has one of 28, 29, 30 or 31
4238    /// days.
4239    Month = 8,
4240    /// A week is 7 days that either begins on Sunday or Monday.
4241    Week = 7,
4242    /// A day is usually 24 hours, but some days may have different lengths
4243    /// due to time zone transitions.
4244    Day = 6,
4245    /// An hour is always 60 minutes.
4246    Hour = 5,
4247    /// A minute is always 60 seconds. (Jiff behaves as if leap seconds do not
4248    /// exist.)
4249    Minute = 4,
4250    /// A second is always 1,000 milliseconds.
4251    Second = 3,
4252    /// A millisecond is always 1,000 microseconds.
4253    Millisecond = 2,
4254    /// A microsecond is always 1,000 nanoseconds.
4255    Microsecond = 1,
4256    /// A nanosecond is the smallest granularity of time supported by Jiff.
4257    Nanosecond = 0,
4258}
4259
4260impl Unit {
4261    /// Returns the next biggest unit, if one exists.
4262    pub(crate) fn next(&self) -> Option<Unit> {
4263        match *self {
4264            Unit::Year => None,
4265            Unit::Month => Some(Unit::Year),
4266            Unit::Week => Some(Unit::Month),
4267            Unit::Day => Some(Unit::Week),
4268            Unit::Hour => Some(Unit::Day),
4269            Unit::Minute => Some(Unit::Hour),
4270            Unit::Second => Some(Unit::Minute),
4271            Unit::Millisecond => Some(Unit::Second),
4272            Unit::Microsecond => Some(Unit::Millisecond),
4273            Unit::Nanosecond => Some(Unit::Microsecond),
4274        }
4275    }
4276
4277    /*
4278    /// Returns the next smallest unit, if one exists.
4279    pub(crate) fn prev(&self) -> Option<Unit> {
4280        match *self {
4281            Unit::Year => Some(Unit::Month),
4282            Unit::Month => Some(Unit::Week),
4283            Unit::Week => Some(Unit::Day),
4284            Unit::Day => Some(Unit::Hour),
4285            Unit::Hour => Some(Unit::Minute),
4286            Unit::Minute => Some(Unit::Second),
4287            Unit::Second => Some(Unit::Millisecond),
4288            Unit::Millisecond => Some(Unit::Microsecond),
4289            Unit::Microsecond => Some(Unit::Nanosecond),
4290            Unit::Nanosecond => None,
4291        }
4292    }
4293    */
4294
4295    /// Returns the number of nanoseconds in this unit as a 128-bit integer.
4296    ///
4297    /// # Panics
4298    ///
4299    /// When this unit is always variable. That is, years or months.
4300    pub(crate) fn nanoseconds(self) -> NoUnits128 {
4301        match self {
4302            Unit::Nanosecond => Constant(1),
4303            Unit::Microsecond => t::NANOS_PER_MICRO,
4304            Unit::Millisecond => t::NANOS_PER_MILLI,
4305            Unit::Second => t::NANOS_PER_SECOND,
4306            Unit::Minute => t::NANOS_PER_MINUTE,
4307            Unit::Hour => t::NANOS_PER_HOUR,
4308            Unit::Day => t::NANOS_PER_CIVIL_DAY,
4309            Unit::Week => t::NANOS_PER_CIVIL_WEEK,
4310            unit => unreachable!("{unit:?} has no definitive time interval"),
4311        }
4312        .rinto()
4313    }
4314
4315    /// Returns true when this unit is definitively variable.
4316    ///
4317    /// In effect, this is any unit bigger than 'day', because any such unit
4318    /// can vary in time depending on its reference point. A 'day' can as well,
4319    /// but we sorta special case 'day' to mean '24 hours' for cases where
4320    /// the user is dealing with civil time.
4321    fn is_variable(self) -> bool {
4322        matches!(self, Unit::Year | Unit::Month | Unit::Week | Unit::Day)
4323    }
4324
4325    /// A human readable singular description of this unit of time.
4326    pub(crate) fn singular(&self) -> &'static str {
4327        match *self {
4328            Unit::Year => "year",
4329            Unit::Month => "month",
4330            Unit::Week => "week",
4331            Unit::Day => "day",
4332            Unit::Hour => "hour",
4333            Unit::Minute => "minute",
4334            Unit::Second => "second",
4335            Unit::Millisecond => "millisecond",
4336            Unit::Microsecond => "microsecond",
4337            Unit::Nanosecond => "nanosecond",
4338        }
4339    }
4340
4341    /// A human readable plural description of this unit of time.
4342    pub(crate) fn plural(&self) -> &'static str {
4343        match *self {
4344            Unit::Year => "years",
4345            Unit::Month => "months",
4346            Unit::Week => "weeks",
4347            Unit::Day => "days",
4348            Unit::Hour => "hours",
4349            Unit::Minute => "minutes",
4350            Unit::Second => "seconds",
4351            Unit::Millisecond => "milliseconds",
4352            Unit::Microsecond => "microseconds",
4353            Unit::Nanosecond => "nanoseconds",
4354        }
4355    }
4356
4357    /// A very succinct label corresponding to this unit.
4358    pub(crate) fn compact(&self) -> &'static str {
4359        match *self {
4360            Unit::Year => "y",
4361            Unit::Month => "mo",
4362            Unit::Week => "w",
4363            Unit::Day => "d",
4364            Unit::Hour => "h",
4365            Unit::Minute => "m",
4366            Unit::Second => "s",
4367            Unit::Millisecond => "ms",
4368            Unit::Microsecond => "µs",
4369            Unit::Nanosecond => "ns",
4370        }
4371    }
4372
4373    /// Return this unit as a `usize`.
4374    ///
4375    /// This is use `unit as usize`.
4376    pub(crate) fn as_usize(&self) -> usize {
4377        *self as usize
4378    }
4379
4380    /// The inverse of `unit as usize`.
4381    fn from_usize(n: usize) -> Option<Unit> {
4382        match n {
4383            0 => Some(Unit::Nanosecond),
4384            1 => Some(Unit::Microsecond),
4385            2 => Some(Unit::Millisecond),
4386            3 => Some(Unit::Second),
4387            4 => Some(Unit::Minute),
4388            5 => Some(Unit::Hour),
4389            6 => Some(Unit::Day),
4390            7 => Some(Unit::Week),
4391            8 => Some(Unit::Month),
4392            9 => Some(Unit::Year),
4393            _ => None,
4394        }
4395    }
4396}
4397
4398#[cfg(test)]
4399impl quickcheck::Arbitrary for Unit {
4400    fn arbitrary(g: &mut quickcheck::Gen) -> Unit {
4401        Unit::from_usize(usize::arbitrary(g) % 10).unwrap()
4402    }
4403
4404    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
4405        alloc::boxed::Box::new(
4406            (*self as usize)
4407                .shrink()
4408                .map(|n| Unit::from_usize(n % 10).unwrap()),
4409        )
4410    }
4411}
4412
4413/// Options for [`Span::checked_add`] and [`Span::checked_sub`].
4414///
4415/// This type provides a way to ergonomically add two spans with an optional
4416/// relative datetime. Namely, a relative datetime is only needed when at least
4417/// one of the two spans being added (or subtracted) has a non-zero calendar
4418/// unit (years, months, weeks or days). Otherwise, an error will be returned.
4419///
4420/// Callers may use [`SpanArithmetic::days_are_24_hours`] to opt into 24-hour
4421/// invariant days (and 7-day weeks) without providing a relative datetime.
4422///
4423/// The main way to construct values of this type is with its `From` trait
4424/// implementations:
4425///
4426/// * `From<Span> for SpanArithmetic` adds (or subtracts) the given span to the
4427/// receiver in [`Span::checked_add`] (or [`Span::checked_sub`]).
4428/// * `From<(Span, civil::Date)> for SpanArithmetic` adds (or subtracts)
4429/// the given span to the receiver in [`Span::checked_add`] (or
4430/// [`Span::checked_sub`]), relative to the given date. There are also `From`
4431/// implementations for `civil::DateTime`, `Zoned` and [`SpanRelativeTo`].
4432///
4433/// # Example
4434///
4435/// ```
4436/// use jiff::ToSpan;
4437///
4438/// assert_eq!(
4439///     1.hour().checked_add(30.minutes())?,
4440///     1.hour().minutes(30).fieldwise(),
4441/// );
4442///
4443/// # Ok::<(), Box<dyn std::error::Error>>(())
4444/// ```
4445#[derive(Clone, Copy, Debug)]
4446pub struct SpanArithmetic<'a> {
4447    duration: Duration,
4448    relative: Option<SpanRelativeTo<'a>>,
4449}
4450
4451impl<'a> SpanArithmetic<'a> {
4452    /// This is a convenience function for setting the relative option on
4453    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4454    ///
4455    /// # Example
4456    ///
4457    /// When doing arithmetic on spans involving days, either a relative
4458    /// datetime must be provided, or a special assertion opting into 24-hour
4459    /// days is required. Otherwise, you get an error.
4460    ///
4461    /// ```
4462    /// use jiff::{SpanArithmetic, ToSpan};
4463    ///
4464    /// let span1 = 2.days().hours(12);
4465    /// let span2 = 12.hours();
4466    /// // No relative date provided, which results in an error.
4467    /// assert_eq!(
4468    ///     span1.checked_add(span2).unwrap_err().to_string(),
4469    ///     "using unit 'day' in a span or configuration requires that \
4470    ///      either a relative reference time be given or \
4471    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
4472    ///      invariant 24-hour days, but neither were provided",
4473    /// );
4474    /// let sum = span1.checked_add(
4475    ///     SpanArithmetic::from(span2).days_are_24_hours(),
4476    /// )?;
4477    /// assert_eq!(sum, 3.days().fieldwise());
4478    ///
4479    /// # Ok::<(), Box<dyn std::error::Error>>(())
4480    /// ```
4481    #[inline]
4482    pub fn days_are_24_hours(self) -> SpanArithmetic<'a> {
4483        self.relative(SpanRelativeTo::days_are_24_hours())
4484    }
4485}
4486
4487impl<'a> SpanArithmetic<'a> {
4488    #[inline]
4489    fn relative<R: Into<SpanRelativeTo<'a>>>(
4490        self,
4491        relative: R,
4492    ) -> SpanArithmetic<'a> {
4493        SpanArithmetic { relative: Some(relative.into()), ..self }
4494    }
4495
4496    #[inline]
4497    fn checked_add(self, span1: Span) -> Result<Span, Error> {
4498        match self.duration.to_signed()? {
4499            SDuration::Span(span2) => {
4500                span1.checked_add_span(self.relative, &span2)
4501            }
4502            SDuration::Absolute(dur2) => {
4503                span1.checked_add_duration(self.relative, dur2)
4504            }
4505        }
4506    }
4507}
4508
4509impl From<Span> for SpanArithmetic<'static> {
4510    fn from(span: Span) -> SpanArithmetic<'static> {
4511        let duration = Duration::from(span);
4512        SpanArithmetic { duration, relative: None }
4513    }
4514}
4515
4516impl<'a> From<&'a Span> for SpanArithmetic<'static> {
4517    fn from(span: &'a Span) -> SpanArithmetic<'static> {
4518        let duration = Duration::from(*span);
4519        SpanArithmetic { duration, relative: None }
4520    }
4521}
4522
4523impl From<(Span, Date)> for SpanArithmetic<'static> {
4524    #[inline]
4525    fn from((span, date): (Span, Date)) -> SpanArithmetic<'static> {
4526        SpanArithmetic::from(span).relative(date)
4527    }
4528}
4529
4530impl From<(Span, DateTime)> for SpanArithmetic<'static> {
4531    #[inline]
4532    fn from((span, datetime): (Span, DateTime)) -> SpanArithmetic<'static> {
4533        SpanArithmetic::from(span).relative(datetime)
4534    }
4535}
4536
4537impl<'a> From<(Span, &'a Zoned)> for SpanArithmetic<'a> {
4538    #[inline]
4539    fn from((span, zoned): (Span, &'a Zoned)) -> SpanArithmetic<'a> {
4540        SpanArithmetic::from(span).relative(zoned)
4541    }
4542}
4543
4544impl<'a> From<(Span, SpanRelativeTo<'a>)> for SpanArithmetic<'a> {
4545    #[inline]
4546    fn from(
4547        (span, relative): (Span, SpanRelativeTo<'a>),
4548    ) -> SpanArithmetic<'a> {
4549        SpanArithmetic::from(span).relative(relative)
4550    }
4551}
4552
4553impl<'a> From<(&'a Span, Date)> for SpanArithmetic<'static> {
4554    #[inline]
4555    fn from((span, date): (&'a Span, Date)) -> SpanArithmetic<'static> {
4556        SpanArithmetic::from(span).relative(date)
4557    }
4558}
4559
4560impl<'a> From<(&'a Span, DateTime)> for SpanArithmetic<'static> {
4561    #[inline]
4562    fn from(
4563        (span, datetime): (&'a Span, DateTime),
4564    ) -> SpanArithmetic<'static> {
4565        SpanArithmetic::from(span).relative(datetime)
4566    }
4567}
4568
4569impl<'a, 'b> From<(&'a Span, &'b Zoned)> for SpanArithmetic<'b> {
4570    #[inline]
4571    fn from((span, zoned): (&'a Span, &'b Zoned)) -> SpanArithmetic<'b> {
4572        SpanArithmetic::from(span).relative(zoned)
4573    }
4574}
4575
4576impl<'a, 'b> From<(&'a Span, SpanRelativeTo<'b>)> for SpanArithmetic<'b> {
4577    #[inline]
4578    fn from(
4579        (span, relative): (&'a Span, SpanRelativeTo<'b>),
4580    ) -> SpanArithmetic<'b> {
4581        SpanArithmetic::from(span).relative(relative)
4582    }
4583}
4584
4585impl From<SignedDuration> for SpanArithmetic<'static> {
4586    fn from(duration: SignedDuration) -> SpanArithmetic<'static> {
4587        let duration = Duration::from(duration);
4588        SpanArithmetic { duration, relative: None }
4589    }
4590}
4591
4592impl From<(SignedDuration, Date)> for SpanArithmetic<'static> {
4593    #[inline]
4594    fn from(
4595        (duration, date): (SignedDuration, Date),
4596    ) -> SpanArithmetic<'static> {
4597        SpanArithmetic::from(duration).relative(date)
4598    }
4599}
4600
4601impl From<(SignedDuration, DateTime)> for SpanArithmetic<'static> {
4602    #[inline]
4603    fn from(
4604        (duration, datetime): (SignedDuration, DateTime),
4605    ) -> SpanArithmetic<'static> {
4606        SpanArithmetic::from(duration).relative(datetime)
4607    }
4608}
4609
4610impl<'a> From<(SignedDuration, &'a Zoned)> for SpanArithmetic<'a> {
4611    #[inline]
4612    fn from(
4613        (duration, zoned): (SignedDuration, &'a Zoned),
4614    ) -> SpanArithmetic<'a> {
4615        SpanArithmetic::from(duration).relative(zoned)
4616    }
4617}
4618
4619impl From<UnsignedDuration> for SpanArithmetic<'static> {
4620    fn from(duration: UnsignedDuration) -> SpanArithmetic<'static> {
4621        let duration = Duration::from(duration);
4622        SpanArithmetic { duration, relative: None }
4623    }
4624}
4625
4626impl From<(UnsignedDuration, Date)> for SpanArithmetic<'static> {
4627    #[inline]
4628    fn from(
4629        (duration, date): (UnsignedDuration, Date),
4630    ) -> SpanArithmetic<'static> {
4631        SpanArithmetic::from(duration).relative(date)
4632    }
4633}
4634
4635impl From<(UnsignedDuration, DateTime)> for SpanArithmetic<'static> {
4636    #[inline]
4637    fn from(
4638        (duration, datetime): (UnsignedDuration, DateTime),
4639    ) -> SpanArithmetic<'static> {
4640        SpanArithmetic::from(duration).relative(datetime)
4641    }
4642}
4643
4644impl<'a> From<(UnsignedDuration, &'a Zoned)> for SpanArithmetic<'a> {
4645    #[inline]
4646    fn from(
4647        (duration, zoned): (UnsignedDuration, &'a Zoned),
4648    ) -> SpanArithmetic<'a> {
4649        SpanArithmetic::from(duration).relative(zoned)
4650    }
4651}
4652
4653/// Options for [`Span::compare`].
4654///
4655/// This type provides a way to ergonomically compare two spans with an
4656/// optional relative datetime. Namely, a relative datetime is only needed when
4657/// at least one of the two spans being compared has a non-zero calendar unit
4658/// (years, months, weeks or days). Otherwise, an error will be returned.
4659///
4660/// Callers may use [`SpanCompare::days_are_24_hours`] to opt into 24-hour
4661/// invariant days (and 7-day weeks) without providing a relative datetime.
4662///
4663/// The main way to construct values of this type is with its `From` trait
4664/// implementations:
4665///
4666/// * `From<Span> for SpanCompare` compares the given span to the receiver
4667/// in [`Span::compare`].
4668/// * `From<(Span, civil::Date)> for SpanCompare` compares the given span
4669/// to the receiver in [`Span::compare`], relative to the given date. There
4670/// are also `From` implementations for `civil::DateTime`, `Zoned` and
4671/// [`SpanRelativeTo`].
4672///
4673/// # Example
4674///
4675/// ```
4676/// use jiff::ToSpan;
4677///
4678/// let span1 = 3.hours();
4679/// let span2 = 180.minutes();
4680/// assert_eq!(span1.compare(span2)?, std::cmp::Ordering::Equal);
4681///
4682/// # Ok::<(), Box<dyn std::error::Error>>(())
4683/// ```
4684#[derive(Clone, Copy, Debug)]
4685pub struct SpanCompare<'a> {
4686    span: Span,
4687    relative: Option<SpanRelativeTo<'a>>,
4688}
4689
4690impl<'a> SpanCompare<'a> {
4691    /// This is a convenience function for setting the relative option on
4692    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4693    ///
4694    /// # Example
4695    ///
4696    /// When comparing spans involving days, either a relative datetime must be
4697    /// provided, or a special assertion opting into 24-hour days is
4698    /// required. Otherwise, you get an error.
4699    ///
4700    /// ```
4701    /// use jiff::{SpanCompare, ToSpan};
4702    ///
4703    /// let span1 = 2.days().hours(12);
4704    /// let span2 = 60.hours();
4705    /// // No relative date provided, which results in an error.
4706    /// assert_eq!(
4707    ///     span1.compare(span2).unwrap_err().to_string(),
4708    ///     "using unit 'day' in a span or configuration requires that \
4709    ///      either a relative reference time be given or \
4710    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
4711    ///      invariant 24-hour days, but neither were provided",
4712    /// );
4713    /// let ordering = span1.compare(
4714    ///     SpanCompare::from(span2).days_are_24_hours(),
4715    /// )?;
4716    /// assert_eq!(ordering, std::cmp::Ordering::Equal);
4717    ///
4718    /// # Ok::<(), Box<dyn std::error::Error>>(())
4719    /// ```
4720    #[inline]
4721    pub fn days_are_24_hours(self) -> SpanCompare<'a> {
4722        self.relative(SpanRelativeTo::days_are_24_hours())
4723    }
4724}
4725
4726impl<'a> SpanCompare<'a> {
4727    #[inline]
4728    fn new(span: Span) -> SpanCompare<'static> {
4729        SpanCompare { span, relative: None }
4730    }
4731
4732    #[inline]
4733    fn relative<R: Into<SpanRelativeTo<'a>>>(
4734        self,
4735        relative: R,
4736    ) -> SpanCompare<'a> {
4737        SpanCompare { relative: Some(relative.into()), ..self }
4738    }
4739
4740    fn compare(self, span: Span) -> Result<Ordering, Error> {
4741        let (span1, span2) = (span, self.span);
4742        let unit = span1.largest_unit().max(span2.largest_unit());
4743        let start = match self.relative {
4744            Some(r) => match r.to_relative(unit)? {
4745                Some(r) => r,
4746                None => {
4747                    let nanos1 = span1.to_invariant_nanoseconds();
4748                    let nanos2 = span2.to_invariant_nanoseconds();
4749                    return Ok(nanos1.cmp(&nanos2));
4750                }
4751            },
4752            None => {
4753                requires_relative_date_err(unit)?;
4754                let nanos1 = span1.to_invariant_nanoseconds();
4755                let nanos2 = span2.to_invariant_nanoseconds();
4756                return Ok(nanos1.cmp(&nanos2));
4757            }
4758        };
4759        let end1 = start.checked_add(span1)?.to_nanosecond();
4760        let end2 = start.checked_add(span2)?.to_nanosecond();
4761        Ok(end1.cmp(&end2))
4762    }
4763}
4764
4765impl From<Span> for SpanCompare<'static> {
4766    fn from(span: Span) -> SpanCompare<'static> {
4767        SpanCompare::new(span)
4768    }
4769}
4770
4771impl<'a> From<&'a Span> for SpanCompare<'static> {
4772    fn from(span: &'a Span) -> SpanCompare<'static> {
4773        SpanCompare::new(*span)
4774    }
4775}
4776
4777impl From<(Span, Date)> for SpanCompare<'static> {
4778    #[inline]
4779    fn from((span, date): (Span, Date)) -> SpanCompare<'static> {
4780        SpanCompare::from(span).relative(date)
4781    }
4782}
4783
4784impl From<(Span, DateTime)> for SpanCompare<'static> {
4785    #[inline]
4786    fn from((span, datetime): (Span, DateTime)) -> SpanCompare<'static> {
4787        SpanCompare::from(span).relative(datetime)
4788    }
4789}
4790
4791impl<'a> From<(Span, &'a Zoned)> for SpanCompare<'a> {
4792    #[inline]
4793    fn from((span, zoned): (Span, &'a Zoned)) -> SpanCompare<'a> {
4794        SpanCompare::from(span).relative(zoned)
4795    }
4796}
4797
4798impl<'a> From<(Span, SpanRelativeTo<'a>)> for SpanCompare<'a> {
4799    #[inline]
4800    fn from((span, relative): (Span, SpanRelativeTo<'a>)) -> SpanCompare<'a> {
4801        SpanCompare::from(span).relative(relative)
4802    }
4803}
4804
4805impl<'a> From<(&'a Span, Date)> for SpanCompare<'static> {
4806    #[inline]
4807    fn from((span, date): (&'a Span, Date)) -> SpanCompare<'static> {
4808        SpanCompare::from(span).relative(date)
4809    }
4810}
4811
4812impl<'a> From<(&'a Span, DateTime)> for SpanCompare<'static> {
4813    #[inline]
4814    fn from((span, datetime): (&'a Span, DateTime)) -> SpanCompare<'static> {
4815        SpanCompare::from(span).relative(datetime)
4816    }
4817}
4818
4819impl<'a, 'b> From<(&'a Span, &'b Zoned)> for SpanCompare<'b> {
4820    #[inline]
4821    fn from((span, zoned): (&'a Span, &'b Zoned)) -> SpanCompare<'b> {
4822        SpanCompare::from(span).relative(zoned)
4823    }
4824}
4825
4826impl<'a, 'b> From<(&'a Span, SpanRelativeTo<'b>)> for SpanCompare<'b> {
4827    #[inline]
4828    fn from(
4829        (span, relative): (&'a Span, SpanRelativeTo<'b>),
4830    ) -> SpanCompare<'b> {
4831        SpanCompare::from(span).relative(relative)
4832    }
4833}
4834
4835/// Options for [`Span::total`].
4836///
4837/// This type provides a way to ergonomically determine the number of a
4838/// particular unit in a span, with a potentially fractional component, with
4839/// an optional relative datetime. Namely, a relative datetime is only needed
4840/// when the span has a non-zero calendar unit (years, months, weeks or days).
4841/// Otherwise, an error will be returned.
4842///
4843/// Callers may use [`SpanTotal::days_are_24_hours`] to opt into 24-hour
4844/// invariant days (and 7-day weeks) without providing a relative datetime.
4845///
4846/// The main way to construct values of this type is with its `From` trait
4847/// implementations:
4848///
4849/// * `From<Unit> for SpanTotal` computes a total for the given unit in the
4850/// receiver span for [`Span::total`].
4851/// * `From<(Unit, civil::Date)> for SpanTotal` computes a total for the given
4852/// unit in the receiver span for [`Span::total`], relative to the given date.
4853/// There are also `From` implementations for `civil::DateTime`, `Zoned` and
4854/// [`SpanRelativeTo`].
4855///
4856/// # Example
4857///
4858/// This example shows how to find the number of seconds in a particular span:
4859///
4860/// ```
4861/// use jiff::{ToSpan, Unit};
4862///
4863/// let span = 3.hours().minutes(10);
4864/// assert_eq!(span.total(Unit::Second)?, 11_400.0);
4865///
4866/// # Ok::<(), Box<dyn std::error::Error>>(())
4867/// ```
4868///
4869/// # Example: 24 hour days
4870///
4871/// This shows how to find the total number of 24 hour days in `123,456,789`
4872/// seconds.
4873///
4874/// ```
4875/// use jiff::{SpanTotal, ToSpan, Unit};
4876///
4877/// let span = 123_456_789.seconds();
4878/// assert_eq!(
4879///     span.total(SpanTotal::from(Unit::Day).days_are_24_hours())?,
4880///     1428.8980208333332,
4881/// );
4882///
4883/// # Ok::<(), Box<dyn std::error::Error>>(())
4884/// ```
4885///
4886/// # Example: DST is taken into account
4887///
4888/// The month of March 2024 in `America/New_York` had 31 days, but one of those
4889/// days was 23 hours long due a transition into daylight saving time:
4890///
4891/// ```
4892/// use jiff::{civil::date, ToSpan, Unit};
4893///
4894/// let span = 744.hours();
4895/// let relative = date(2024, 3, 1).in_tz("America/New_York")?;
4896/// // Because of the short day, 744 hours is actually a little *more* than
4897/// // 1 month starting from 2024-03-01.
4898/// assert_eq!(span.total((Unit::Month, &relative))?, 1.0013888888888889);
4899///
4900/// # Ok::<(), Box<dyn std::error::Error>>(())
4901/// ```
4902///
4903/// Now compare what happens when the relative datetime is civil and not
4904/// time zone aware:
4905///
4906/// ```
4907/// use jiff::{civil::date, ToSpan, Unit};
4908///
4909/// let span = 744.hours();
4910/// let relative = date(2024, 3, 1);
4911/// assert_eq!(span.total((Unit::Month, relative))?, 1.0);
4912///
4913/// # Ok::<(), Box<dyn std::error::Error>>(())
4914/// ```
4915#[derive(Clone, Copy, Debug)]
4916pub struct SpanTotal<'a> {
4917    unit: Unit,
4918    relative: Option<SpanRelativeTo<'a>>,
4919}
4920
4921impl<'a> SpanTotal<'a> {
4922    /// This is a convenience function for setting the relative option on
4923    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4924    ///
4925    /// # Example
4926    ///
4927    /// When computing the total duration for spans involving days, either a
4928    /// relative datetime must be provided, or a special assertion opting into
4929    /// 24-hour days is required. Otherwise, you get an error.
4930    ///
4931    /// ```
4932    /// use jiff::{civil::date, SpanTotal, ToSpan, Unit};
4933    ///
4934    /// let span = 2.days().hours(12);
4935    ///
4936    /// // No relative date provided, which results in an error.
4937    /// assert_eq!(
4938    ///     span.total(Unit::Hour).unwrap_err().to_string(),
4939    ///     "using unit 'day' in a span or configuration requires that either \
4940    ///      a relative reference time be given or \
4941    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
4942    ///      invariant 24-hour days, but neither were provided",
4943    /// );
4944    ///
4945    /// // If we can assume all days are 24 hours, then we can assert it:
4946    /// let total = span.total(
4947    ///     SpanTotal::from(Unit::Hour).days_are_24_hours(),
4948    /// )?;
4949    /// assert_eq!(total, 60.0);
4950    ///
4951    /// // Or provide a relative datetime, which is preferred if possible:
4952    /// let total = span.total((Unit::Hour, date(2025, 1, 26)))?;
4953    /// assert_eq!(total, 60.0);
4954    ///
4955    /// # Ok::<(), Box<dyn std::error::Error>>(())
4956    /// ```
4957    #[inline]
4958    pub fn days_are_24_hours(self) -> SpanTotal<'a> {
4959        self.relative(SpanRelativeTo::days_are_24_hours())
4960    }
4961}
4962
4963impl<'a> SpanTotal<'a> {
4964    #[inline]
4965    fn new(unit: Unit) -> SpanTotal<'static> {
4966        SpanTotal { unit, relative: None }
4967    }
4968
4969    #[inline]
4970    fn relative<R: Into<SpanRelativeTo<'a>>>(
4971        self,
4972        relative: R,
4973    ) -> SpanTotal<'a> {
4974        SpanTotal { relative: Some(relative.into()), ..self }
4975    }
4976
4977    fn total(self, span: Span) -> Result<f64, Error> {
4978        let max_unit = self.unit.max(span.largest_unit());
4979        let relative = match self.relative {
4980            Some(r) => match r.to_relative(max_unit)? {
4981                Some(r) => r,
4982                None => {
4983                    return Ok(self.total_invariant(span));
4984                }
4985            },
4986            None => {
4987                requires_relative_date_err(max_unit)?;
4988                return Ok(self.total_invariant(span));
4989            }
4990        };
4991        let relspan = relative.into_relative_span(self.unit, span)?;
4992        if !self.unit.is_variable() {
4993            return Ok(self.total_invariant(relspan.span));
4994        }
4995
4996        assert!(self.unit >= Unit::Day);
4997        let sign = relspan.span.get_sign_ranged();
4998        let (relative_start, relative_end) = match relspan.kind {
4999            RelativeSpanKind::Civil { start, end } => {
5000                let start = Relative::Civil(start);
5001                let end = Relative::Civil(end);
5002                (start, end)
5003            }
5004            RelativeSpanKind::Zoned { start, end } => {
5005                let start = Relative::Zoned(start);
5006                let end = Relative::Zoned(end);
5007                (start, end)
5008            }
5009        };
5010        let (relative0, relative1) = clamp_relative_span(
5011            &relative_start,
5012            relspan.span.without_lower(self.unit),
5013            self.unit,
5014            sign.rinto(),
5015        )?;
5016        let denom = (relative1 - relative0).get() as f64;
5017        let numer = (relative_end.to_nanosecond() - relative0).get() as f64;
5018        let unit_val = relspan.span.get_units_ranged(self.unit).get() as f64;
5019        Ok(unit_val + (numer / denom) * (sign.get() as f64))
5020    }
5021
5022    #[inline]
5023    fn total_invariant(&self, span: Span) -> f64 {
5024        assert!(self.unit <= Unit::Week);
5025        let nanos = span.to_invariant_nanoseconds();
5026        (nanos.get() as f64) / (self.unit.nanoseconds().get() as f64)
5027    }
5028}
5029
5030impl From<Unit> for SpanTotal<'static> {
5031    #[inline]
5032    fn from(unit: Unit) -> SpanTotal<'static> {
5033        SpanTotal::new(unit)
5034    }
5035}
5036
5037impl From<(Unit, Date)> for SpanTotal<'static> {
5038    #[inline]
5039    fn from((unit, date): (Unit, Date)) -> SpanTotal<'static> {
5040        SpanTotal::from(unit).relative(date)
5041    }
5042}
5043
5044impl From<(Unit, DateTime)> for SpanTotal<'static> {
5045    #[inline]
5046    fn from((unit, datetime): (Unit, DateTime)) -> SpanTotal<'static> {
5047        SpanTotal::from(unit).relative(datetime)
5048    }
5049}
5050
5051impl<'a> From<(Unit, &'a Zoned)> for SpanTotal<'a> {
5052    #[inline]
5053    fn from((unit, zoned): (Unit, &'a Zoned)) -> SpanTotal<'a> {
5054        SpanTotal::from(unit).relative(zoned)
5055    }
5056}
5057
5058impl<'a> From<(Unit, SpanRelativeTo<'a>)> for SpanTotal<'a> {
5059    #[inline]
5060    fn from((unit, relative): (Unit, SpanRelativeTo<'a>)) -> SpanTotal<'a> {
5061        SpanTotal::from(unit).relative(relative)
5062    }
5063}
5064
5065/// Options for [`Span::round`].
5066///
5067/// This type provides a way to configure the rounding of a span. This
5068/// includes setting the smallest unit (i.e., the unit to round), the
5069/// largest unit, the rounding increment, the rounding mode (e.g., "ceil" or
5070/// "truncate") and the datetime that the span is relative to.
5071///
5072/// `Span::round` accepts anything that implements `Into<SpanRound>`. There are
5073/// a few key trait implementations that make this convenient:
5074///
5075/// * `From<Unit> for SpanRound` will construct a rounding configuration where
5076/// the smallest unit is set to the one given.
5077/// * `From<(Unit, i64)> for SpanRound` will construct a rounding configuration
5078/// where the smallest unit and the rounding increment are set to the ones
5079/// given.
5080///
5081/// In order to set other options (like the largest unit, the rounding mode
5082/// and the relative datetime), one must explicitly create a `SpanRound` and
5083/// pass it to `Span::round`.
5084///
5085/// # Example
5086///
5087/// This example shows how to find how many full 3 month quarters are in a
5088/// particular span of time.
5089///
5090/// ```
5091/// use jiff::{civil::date, RoundMode, SpanRound, ToSpan, Unit};
5092///
5093/// let span1 = 10.months().days(15);
5094/// let round = SpanRound::new()
5095///     .smallest(Unit::Month)
5096///     .increment(3)
5097///     .mode(RoundMode::Trunc)
5098///     // A relative datetime must be provided when
5099///     // rounding involves calendar units.
5100///     .relative(date(2024, 1, 1));
5101/// let span2 = span1.round(round)?;
5102/// assert_eq!(span2.get_months() / 3, 3);
5103///
5104/// # Ok::<(), Box<dyn std::error::Error>>(())
5105/// ```
5106#[derive(Clone, Copy, Debug)]
5107pub struct SpanRound<'a> {
5108    largest: Option<Unit>,
5109    smallest: Unit,
5110    mode: RoundMode,
5111    increment: i64,
5112    relative: Option<SpanRelativeTo<'a>>,
5113}
5114
5115impl<'a> SpanRound<'a> {
5116    /// Create a new default configuration for rounding a span via
5117    /// [`Span::round`].
5118    ///
5119    /// The default configuration does no rounding.
5120    #[inline]
5121    pub fn new() -> SpanRound<'static> {
5122        SpanRound {
5123            largest: None,
5124            smallest: Unit::Nanosecond,
5125            mode: RoundMode::HalfExpand,
5126            increment: 1,
5127            relative: None,
5128        }
5129    }
5130
5131    /// Set the smallest units allowed in the span returned. These are the
5132    /// units that the span is rounded to.
5133    ///
5134    /// # Errors
5135    ///
5136    /// The smallest units must be no greater than the largest units. If this
5137    /// is violated, then rounding a span with this configuration will result
5138    /// in an error.
5139    ///
5140    /// If a smallest unit bigger than days is selected without a relative
5141    /// datetime reference point, then an error is returned when using this
5142    /// configuration with [`Span::round`].
5143    ///
5144    /// # Example
5145    ///
5146    /// A basic example that rounds to the nearest minute:
5147    ///
5148    /// ```
5149    /// use jiff::{ToSpan, Unit};
5150    ///
5151    /// let span = 15.minutes().seconds(46);
5152    /// assert_eq!(span.round(Unit::Minute)?, 16.minutes().fieldwise());
5153    ///
5154    /// # Ok::<(), Box<dyn std::error::Error>>(())
5155    /// ```
5156    #[inline]
5157    pub fn smallest(self, unit: Unit) -> SpanRound<'a> {
5158        SpanRound { smallest: unit, ..self }
5159    }
5160
5161    /// Set the largest units allowed in the span returned.
5162    ///
5163    /// When a largest unit is not specified, then it defaults to the largest
5164    /// non-zero unit that is at least as big as the configured smallest
5165    /// unit. For example, given a span of `2 months 17 hours`, the default
5166    /// largest unit would be `Unit::Month`. The default implies that a span's
5167    /// units do not get "bigger" than what was given.
5168    ///
5169    /// Once a largest unit is set, there is no way to change this rounding
5170    /// configuration back to using the "automatic" default. Instead, callers
5171    /// must create a new configuration.
5172    ///
5173    /// If a largest unit is set and no other options are set, then the
5174    /// rounding operation can be said to be a "re-balancing." That is, the
5175    /// span won't lose precision, but the way in which it is expressed may
5176    /// change.
5177    ///
5178    /// # Errors
5179    ///
5180    /// The largest units, when set, must be at least as big as the smallest
5181    /// units (which defaults to [`Unit::Nanosecond`]). If this is violated,
5182    /// then rounding a span with this configuration will result in an error.
5183    ///
5184    /// If a largest unit bigger than days is selected without a relative
5185    /// datetime reference point, then an error is returned when using this
5186    /// configuration with [`Span::round`].
5187    ///
5188    /// # Example: re-balancing
5189    ///
5190    /// This shows how a span can be re-balanced without losing precision:
5191    ///
5192    /// ```
5193    /// use jiff::{SpanRound, ToSpan, Unit};
5194    ///
5195    /// let span = 86_401_123_456_789i64.nanoseconds();
5196    /// assert_eq!(
5197    ///     span.round(SpanRound::new().largest(Unit::Hour))?.fieldwise(),
5198    ///     24.hours().seconds(1).milliseconds(123).microseconds(456).nanoseconds(789),
5199    /// );
5200    ///
5201    /// # Ok::<(), Box<dyn std::error::Error>>(())
5202    /// ```
5203    ///
5204    /// If you need to use a largest unit bigger than hours, then you must
5205    /// provide a relative datetime as a reference point (otherwise an error
5206    /// will occur):
5207    ///
5208    /// ```
5209    /// use jiff::{civil::date, SpanRound, ToSpan, Unit};
5210    ///
5211    /// let span = 3_968_000.seconds();
5212    /// let round = SpanRound::new()
5213    ///     .largest(Unit::Day)
5214    ///     .relative(date(2024, 7, 1));
5215    /// assert_eq!(
5216    ///     span.round(round)?,
5217    ///     45.days().hours(22).minutes(13).seconds(20).fieldwise(),
5218    /// );
5219    ///
5220    /// # Ok::<(), Box<dyn std::error::Error>>(())
5221    /// ```
5222    ///
5223    /// As a special case for days, one can instead opt into invariant 24-hour
5224    /// days (and 7-day weeks) without providing an explicit relative date:
5225    ///
5226    /// ```
5227    /// use jiff::{SpanRound, ToSpan, Unit};
5228    ///
5229    /// let span = 86_401_123_456_789i64.nanoseconds();
5230    /// assert_eq!(
5231    ///     span.round(
5232    ///         SpanRound::new().largest(Unit::Day).days_are_24_hours(),
5233    ///     )?.fieldwise(),
5234    ///     1.day().seconds(1).milliseconds(123).microseconds(456).nanoseconds(789),
5235    /// );
5236    ///
5237    /// # Ok::<(), Box<dyn std::error::Error>>(())
5238    /// ```
5239    ///
5240    /// # Example: re-balancing while taking DST into account
5241    ///
5242    /// When given a zone aware relative datetime, rounding will even take
5243    /// DST into account:
5244    ///
5245    /// ```
5246    /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5247    ///
5248    /// let span = 2756.hours();
5249    /// let zdt = "2020-01-01T00:00+01:00[Europe/Rome]".parse::<Zoned>()?;
5250    /// let round = SpanRound::new().largest(Unit::Year).relative(&zdt);
5251    /// assert_eq!(
5252    ///     span.round(round)?,
5253    ///     3.months().days(23).hours(21).fieldwise(),
5254    /// );
5255    ///
5256    /// # Ok::<(), Box<dyn std::error::Error>>(())
5257    /// ```
5258    ///
5259    /// Now compare with the same operation, but on a civil datetime (which is
5260    /// not aware of time zone):
5261    ///
5262    /// ```
5263    /// use jiff::{civil::DateTime, SpanRound, ToSpan, Unit};
5264    ///
5265    /// let span = 2756.hours();
5266    /// let dt = "2020-01-01T00:00".parse::<DateTime>()?;
5267    /// let round = SpanRound::new().largest(Unit::Year).relative(dt);
5268    /// assert_eq!(
5269    ///     span.round(round)?,
5270    ///     3.months().days(23).hours(20).fieldwise(),
5271    /// );
5272    ///
5273    /// # Ok::<(), Box<dyn std::error::Error>>(())
5274    /// ```
5275    ///
5276    /// The result is 1 hour shorter. This is because, in the zone
5277    /// aware re-balancing, it accounts for the transition into DST at
5278    /// `2020-03-29T01:00Z`, which skips an hour. This makes the span one hour
5279    /// longer because one of the days in the span is actually only 23 hours
5280    /// long instead of 24 hours.
5281    #[inline]
5282    pub fn largest(self, unit: Unit) -> SpanRound<'a> {
5283        SpanRound { largest: Some(unit), ..self }
5284    }
5285
5286    /// Set the rounding mode.
5287    ///
5288    /// This defaults to [`RoundMode::HalfExpand`], which makes rounding work
5289    /// like how you were taught in school.
5290    ///
5291    /// # Example
5292    ///
5293    /// A basic example that rounds to the nearest minute, but changing its
5294    /// rounding mode to truncation:
5295    ///
5296    /// ```
5297    /// use jiff::{RoundMode, SpanRound, ToSpan, Unit};
5298    ///
5299    /// let span = 15.minutes().seconds(46);
5300    /// assert_eq!(
5301    ///     span.round(SpanRound::new()
5302    ///         .smallest(Unit::Minute)
5303    ///         .mode(RoundMode::Trunc),
5304    ///     )?,
5305    ///     // The default round mode does rounding like
5306    ///     // how you probably learned in school, and would
5307    ///     // result in rounding up to 16 minutes. But we
5308    ///     // change it to truncation here, which makes it
5309    ///     // round down.
5310    ///     15.minutes().fieldwise(),
5311    /// );
5312    ///
5313    /// # Ok::<(), Box<dyn std::error::Error>>(())
5314    /// ```
5315    #[inline]
5316    pub fn mode(self, mode: RoundMode) -> SpanRound<'a> {
5317        SpanRound { mode, ..self }
5318    }
5319
5320    /// Set the rounding increment for the smallest unit.
5321    ///
5322    /// The default value is `1`. Other values permit rounding the smallest
5323    /// unit to the nearest integer increment specified. For example, if the
5324    /// smallest unit is set to [`Unit::Minute`], then a rounding increment of
5325    /// `30` would result in rounding in increments of a half hour. That is,
5326    /// the only minute value that could result would be `0` or `30`.
5327    ///
5328    /// # Errors
5329    ///
5330    /// When the smallest unit is less than days, the rounding increment must
5331    /// divide evenly into the next highest unit after the smallest unit
5332    /// configured (and must not be equivalent to it). For example, if the
5333    /// smallest unit is [`Unit::Nanosecond`], then *some* of the valid values
5334    /// for the rounding increment are `1`, `2`, `4`, `5`, `100` and `500`.
5335    /// Namely, any integer that divides evenly into `1,000` nanoseconds since
5336    /// there are `1,000` nanoseconds in the next highest unit (microseconds).
5337    ///
5338    /// The error will occur when computing the span, and not when setting
5339    /// the increment here.
5340    ///
5341    /// # Example
5342    ///
5343    /// This shows how to round a span to the nearest 5 minute increment:
5344    ///
5345    /// ```
5346    /// use jiff::{ToSpan, Unit};
5347    ///
5348    /// let span = 4.hours().minutes(2).seconds(30);
5349    /// assert_eq!(
5350    ///     span.round((Unit::Minute, 5))?,
5351    ///     4.hours().minutes(5).fieldwise(),
5352    /// );
5353    ///
5354    /// # Ok::<(), Box<dyn std::error::Error>>(())
5355    /// ```
5356    #[inline]
5357    pub fn increment(self, increment: i64) -> SpanRound<'a> {
5358        SpanRound { increment, ..self }
5359    }
5360
5361    /// Set the relative datetime to use when rounding a span.
5362    ///
5363    /// A relative datetime is only required when calendar units (units greater
5364    /// than days) are involved. This includes having calendar units in the
5365    /// original span, or calendar units in the configured smallest or largest
5366    /// unit. A relative datetime is required when calendar units are used
5367    /// because the duration of a particular calendar unit (like 1 month or 1
5368    /// year) is variable and depends on the date. For example, 1 month from
5369    /// 2024-01-01 is 31 days, but 1 month from 2024-02-01 is 29 days.
5370    ///
5371    /// A relative datetime is provided by anything that implements
5372    /// `Into<SpanRelativeTo>`. There are a few convenience trait
5373    /// implementations provided:
5374    ///
5375    /// * `From<&Zoned> for SpanRelativeTo` uses a zone aware datetime to do
5376    /// rounding. In this case, rounding will take time zone transitions into
5377    /// account. In particular, when using a zoned relative datetime, not all
5378    /// days are necessarily 24 hours.
5379    /// * `From<civil::DateTime> for SpanRelativeTo` uses a civil datetime. In
5380    /// this case, all days will be considered 24 hours long.
5381    /// * `From<civil::Date> for SpanRelativeTo` uses a civil date. In this
5382    /// case, all days will be considered 24 hours long.
5383    ///
5384    /// Note that one can impose 24-hour days without providing a reference
5385    /// date via [`SpanRelativeTo::days_are_24_hours`].
5386    ///
5387    /// # Errors
5388    ///
5389    /// If rounding involves a calendar unit (units bigger than hours) and no
5390    /// relative datetime is provided, then this configuration will lead to
5391    /// an error when used with [`Span::round`].
5392    ///
5393    /// # Example
5394    ///
5395    /// This example shows very precisely how a DST transition can impact
5396    /// rounding and re-balancing. For example, consider the day `2024-11-03`
5397    /// in `America/New_York`. On this day, the 1 o'clock hour was repeated,
5398    /// making the day 24 hours long. This will be taken into account when
5399    /// rounding if a zoned datetime is provided as a reference point:
5400    ///
5401    /// ```
5402    /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5403    ///
5404    /// let zdt = "2024-11-03T00-04[America/New_York]".parse::<Zoned>()?;
5405    /// let round = SpanRound::new().largest(Unit::Hour).relative(&zdt);
5406    /// assert_eq!(1.day().round(round)?, 25.hours().fieldwise());
5407    ///
5408    /// # Ok::<(), Box<dyn std::error::Error>>(())
5409    /// ```
5410    ///
5411    /// And similarly for `2024-03-10`, where the 2 o'clock hour was skipped
5412    /// entirely:
5413    ///
5414    /// ```
5415    /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5416    ///
5417    /// let zdt = "2024-03-10T00-05[America/New_York]".parse::<Zoned>()?;
5418    /// let round = SpanRound::new().largest(Unit::Hour).relative(&zdt);
5419    /// assert_eq!(1.day().round(round)?, 23.hours().fieldwise());
5420    ///
5421    /// # Ok::<(), Box<dyn std::error::Error>>(())
5422    /// ```
5423    #[inline]
5424    pub fn relative<R: Into<SpanRelativeTo<'a>>>(
5425        self,
5426        relative: R,
5427    ) -> SpanRound<'a> {
5428        SpanRound { relative: Some(relative.into()), ..self }
5429    }
5430
5431    /// This is a convenience function for setting the relative option on
5432    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
5433    ///
5434    /// # Example
5435    ///
5436    /// When rounding spans involving days, either a relative datetime must be
5437    /// provided, or a special assertion opting into 24-hour days is
5438    /// required. Otherwise, you get an error.
5439    ///
5440    /// ```
5441    /// use jiff::{SpanRound, ToSpan, Unit};
5442    ///
5443    /// let span = 2.days().hours(12);
5444    /// // No relative date provided, which results in an error.
5445    /// assert_eq!(
5446    ///     span.round(Unit::Day).unwrap_err().to_string(),
5447    ///     "error with `smallest` rounding option: using unit 'day' in a \
5448    ///      span or configuration requires that either a relative reference \
5449    ///      time be given or `jiff::SpanRelativeTo::days_are_24_hours()` is \
5450    ///      used to indicate invariant 24-hour days, but neither were \
5451    ///      provided",
5452    /// );
5453    /// let rounded = span.round(
5454    ///     SpanRound::new().smallest(Unit::Day).days_are_24_hours(),
5455    /// )?;
5456    /// assert_eq!(rounded, 3.days().fieldwise());
5457    ///
5458    /// # Ok::<(), Box<dyn std::error::Error>>(())
5459    /// ```
5460    #[inline]
5461    pub fn days_are_24_hours(self) -> SpanRound<'a> {
5462        self.relative(SpanRelativeTo::days_are_24_hours())
5463    }
5464
5465    /// Returns the configured smallest unit on this round configuration.
5466    #[inline]
5467    pub(crate) fn get_smallest(&self) -> Unit {
5468        self.smallest
5469    }
5470
5471    /// Returns the configured largest unit on this round configuration.
5472    #[inline]
5473    pub(crate) fn get_largest(&self) -> Option<Unit> {
5474        self.largest
5475    }
5476
5477    /// Returns true only when rounding a span *may* change it. When it
5478    /// returns false, and if the span is already balanced according to
5479    /// the largest unit in this round configuration, then it is guaranteed
5480    /// that rounding is a no-op.
5481    ///
5482    /// This is useful to avoid rounding calls after doing span arithmetic
5483    /// on datetime types. This works because the "largest" unit is used to
5484    /// construct a balanced span for the difference between two datetimes.
5485    /// So we already know the span has been balanced. If this weren't the
5486    /// case, then the largest unit being different from the one in the span
5487    /// could result in rounding making a change. (And indeed, in the general
5488    /// case of span rounding below, we do a more involved check for this.)
5489    #[inline]
5490    pub(crate) fn rounding_may_change_span_ignore_largest(&self) -> bool {
5491        self.smallest > Unit::Nanosecond || self.increment > 1
5492    }
5493
5494    /// Does the actual span rounding.
5495    fn round(&self, span: Span) -> Result<Span, Error> {
5496        let existing_largest = span.largest_unit();
5497        let mode = self.mode;
5498        let smallest = self.smallest;
5499        let largest =
5500            self.largest.unwrap_or_else(|| smallest.max(existing_largest));
5501        let max = existing_largest.max(largest);
5502        let increment = increment::for_span(smallest, self.increment)?;
5503        if largest < smallest {
5504            return Err(Error::from(
5505                E::NotAllowedLargestSmallerThanSmallest { smallest, largest },
5506            ));
5507        }
5508        let relative = match self.relative {
5509            Some(ref r) => {
5510                match r.to_relative(max)? {
5511                    Some(r) => r,
5512                    None => {
5513                        // If our reference point is civil time, then its units
5514                        // are invariant as long as we are using day-or-lower
5515                        // everywhere. That is, the length of the duration is
5516                        // independent of the reference point. In which case,
5517                        // rounding is a simple matter of converting the span
5518                        // to a number of nanoseconds and then rounding that.
5519                        return Ok(round_span_invariant(
5520                            span, smallest, largest, increment, mode,
5521                        )?);
5522                    }
5523                }
5524            }
5525            None => {
5526                // This is only okay if none of our units are above 'day'.
5527                // That is, a reference point is only necessary when there is
5528                // no reasonable invariant interpretation of the span. And this
5529                // is only true when everything is less than 'day'.
5530                requires_relative_date_err(smallest)
5531                    .context(E::OptionSmallest)?;
5532                if let Some(largest) = self.largest {
5533                    requires_relative_date_err(largest)
5534                        .context(E::OptionLargest)?;
5535                }
5536                requires_relative_date_err(existing_largest)
5537                    .context(E::OptionLargestInSpan)?;
5538                assert!(max <= Unit::Week);
5539                return Ok(round_span_invariant(
5540                    span, smallest, largest, increment, mode,
5541                )?);
5542            }
5543        };
5544        relative.round(span, smallest, largest, increment, mode)
5545    }
5546}
5547
5548impl Default for SpanRound<'static> {
5549    fn default() -> SpanRound<'static> {
5550        SpanRound::new()
5551    }
5552}
5553
5554impl From<Unit> for SpanRound<'static> {
5555    fn from(unit: Unit) -> SpanRound<'static> {
5556        SpanRound::default().smallest(unit)
5557    }
5558}
5559
5560impl From<(Unit, i64)> for SpanRound<'static> {
5561    fn from((unit, increment): (Unit, i64)) -> SpanRound<'static> {
5562        SpanRound::default().smallest(unit).increment(increment)
5563    }
5564}
5565
5566/// A relative datetime for use with [`Span`] APIs.
5567///
5568/// A relative datetime can be one of the following: [`civil::Date`](Date),
5569/// [`civil::DateTime`](DateTime) or [`Zoned`]. It can be constructed from any
5570/// of the preceding types via `From` trait implementations.
5571///
5572/// A relative datetime is used to indicate how the calendar units of a `Span`
5573/// should be interpreted. For example, the span "1 month" does not have a
5574/// fixed meaning. One month from `2024-03-01` is 31 days, but one month from
5575/// `2024-04-01` is 30 days. Similar for years.
5576///
5577/// When a relative datetime in time zone aware (i.e., it is a `Zoned`), then
5578/// operations on a `Span` will also consider its day units to be variable in
5579/// length. For example, `2024-03-10` in `America/New_York` was only 23 hours
5580/// long, where as `2024-11-03` in `America/New_York` was 25 hours long. When
5581/// a relative datetime is civil, then days are considered to always be of a
5582/// fixed 24 hour length.
5583///
5584/// This type is principally used as an input to one of several different
5585/// [`Span`] APIs:
5586///
5587/// * [`Span::round`] rounds spans. A relative datetime is necessary when
5588/// dealing with calendar units. (But spans without calendar units can be
5589/// rounded without providing a relative datetime.)
5590/// * Span arithmetic via [`Span::checked_add`] and [`Span::checked_sub`].
5591/// A relative datetime is needed when adding or subtracting spans with
5592/// calendar units.
5593/// * Span comparisons via [`Span::compare`] require a relative datetime when
5594/// comparing spans with calendar units.
5595/// * Computing the "total" duration as a single floating point number via
5596/// [`Span::total`] also requires a relative datetime when dealing with
5597/// calendar units.
5598///
5599/// # Example
5600///
5601/// This example shows how to round a span with larger calendar units to
5602/// smaller units:
5603///
5604/// ```
5605/// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5606///
5607/// let zdt: Zoned = "2012-01-01[Antarctica/Troll]".parse()?;
5608/// let round = SpanRound::new().largest(Unit::Day).relative(&zdt);
5609/// assert_eq!(1.year().round(round)?, 366.days().fieldwise());
5610///
5611/// // If you tried this without a relative datetime, it would fail:
5612/// let round = SpanRound::new().largest(Unit::Day);
5613/// assert!(1.year().round(round).is_err());
5614///
5615/// # Ok::<(), Box<dyn std::error::Error>>(())
5616/// ```
5617#[derive(Clone, Copy, Debug)]
5618pub struct SpanRelativeTo<'a> {
5619    kind: SpanRelativeToKind<'a>,
5620}
5621
5622impl<'a> SpanRelativeTo<'a> {
5623    /// Creates a special marker that indicates all days ought to be assumed
5624    /// to be 24 hours without providing a relative reference time.
5625    ///
5626    /// This is relevant to the following APIs:
5627    ///
5628    /// * [`Span::checked_add`]
5629    /// * [`Span::checked_sub`]
5630    /// * [`Span::compare`]
5631    /// * [`Span::total`]
5632    /// * [`Span::round`]
5633    /// * [`Span::to_duration`]
5634    ///
5635    /// Specifically, in a previous version of Jiff, the above APIs permitted
5636    /// _silently_ assuming that days are always 24 hours when a relative
5637    /// reference date wasn't provided. In the current version of Jiff, this
5638    /// silent interpretation no longer happens and instead an error will
5639    /// occur.
5640    ///
5641    /// If you need to use these APIs with spans that contain non-zero units
5642    /// of days or weeks but without a relative reference date, then you may
5643    /// use this routine to create a special marker for `SpanRelativeTo` that
5644    /// permits the APIs above to assume days are always 24 hours.
5645    ///
5646    /// # Motivation
5647    ///
5648    /// The purpose of the marker is two-fold:
5649    ///
5650    /// * Requiring the marker is important for improving the consistency of
5651    /// `Span` APIs. Previously, some APIs (like [`Timestamp::checked_add`])
5652    /// would always return an error if the `Span` given had non-zero
5653    /// units of days or greater. On the other hand, other APIs (like
5654    /// [`Span::checked_add`]) would automatically assume days were always
5655    /// 24 hours if no relative reference time was given and either span had
5656    /// non-zero units of days. With this marker, APIs _never_ assume days are
5657    /// always 24 hours automatically.
5658    /// * When it _is_ appropriate to assume all days are 24 hours
5659    /// (for example, when only dealing with spans derived from
5660    /// [`civil`](crate::civil) datetimes) and where providing a relative
5661    /// reference datetime doesn't make sense. In this case, one _could_
5662    /// provide a "dummy" reference date since the precise date in civil time
5663    /// doesn't impact the length of a day. But a marker like the one returned
5664    /// here is more explicit for the purpose of assuming days are always 24
5665    /// hours.
5666    ///
5667    /// With that said, ideally, callers should provide a relative reference
5668    /// datetime if possible.
5669    ///
5670    /// See [Issue #48] for more discussion on this topic.
5671    ///
5672    /// # Example: different interpretations of "1 day"
5673    ///
5674    /// This example shows how "1 day" can be interpreted differently via the
5675    /// [`Span::total`] API:
5676    ///
5677    /// ```
5678    /// use jiff::{SpanRelativeTo, ToSpan, Unit, Zoned};
5679    ///
5680    /// let span = 1.day();
5681    ///
5682    /// // An error because days aren't always 24 hours:
5683    /// assert_eq!(
5684    ///     span.total(Unit::Hour).unwrap_err().to_string(),
5685    ///     "using unit 'day' in a span or configuration requires that either \
5686    ///      a relative reference time be given or \
5687    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
5688    ///      invariant 24-hour days, but neither were provided",
5689    /// );
5690    /// // Opt into invariant 24 hour days without a relative date:
5691    /// let marker = SpanRelativeTo::days_are_24_hours();
5692    /// let hours = span.total((Unit::Hour, marker))?;
5693    /// assert_eq!(hours, 24.0);
5694    /// // Days can be shorter than 24 hours:
5695    /// let zdt: Zoned = "2024-03-10[America/New_York]".parse()?;
5696    /// let hours = span.total((Unit::Hour, &zdt))?;
5697    /// assert_eq!(hours, 23.0);
5698    /// // Days can be longer than 24 hours:
5699    /// let zdt: Zoned = "2024-11-03[America/New_York]".parse()?;
5700    /// let hours = span.total((Unit::Hour, &zdt))?;
5701    /// assert_eq!(hours, 25.0);
5702    ///
5703    /// # Ok::<(), Box<dyn std::error::Error>>(())
5704    /// ```
5705    ///
5706    /// Similar behavior applies to the other APIs listed above.
5707    ///
5708    /// # Example: different interpretations of "1 week"
5709    ///
5710    /// This example shows how "1 week" can be interpreted differently via the
5711    /// [`Span::total`] API:
5712    ///
5713    /// ```
5714    /// use jiff::{SpanRelativeTo, ToSpan, Unit, Zoned};
5715    ///
5716    /// let span = 1.week();
5717    ///
5718    /// // An error because days aren't always 24 hours:
5719    /// assert_eq!(
5720    ///     span.total(Unit::Hour).unwrap_err().to_string(),
5721    ///     "using unit 'week' in a span or configuration requires that either \
5722    ///      a relative reference time be given or \
5723    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
5724    ///      invariant 24-hour days, but neither were provided",
5725    /// );
5726    /// // Opt into invariant 24 hour days without a relative date:
5727    /// let marker = SpanRelativeTo::days_are_24_hours();
5728    /// let hours = span.total((Unit::Hour, marker))?;
5729    /// assert_eq!(hours, 168.0);
5730    /// // Weeks can be shorter than 24*7 hours:
5731    /// let zdt: Zoned = "2024-03-10[America/New_York]".parse()?;
5732    /// let hours = span.total((Unit::Hour, &zdt))?;
5733    /// assert_eq!(hours, 167.0);
5734    /// // Weeks can be longer than 24*7 hours:
5735    /// let zdt: Zoned = "2024-11-03[America/New_York]".parse()?;
5736    /// let hours = span.total((Unit::Hour, &zdt))?;
5737    /// assert_eq!(hours, 169.0);
5738    ///
5739    /// # Ok::<(), Box<dyn std::error::Error>>(())
5740    /// ```
5741    ///
5742    /// # Example: working with [`civil::Date`](crate::civil::Date)
5743    ///
5744    /// A `Span` returned by computing the difference in time between two
5745    /// [`civil::Date`](crate::civil::Date)s will have a non-zero number of
5746    /// days. In older versions of Jiff, if one wanted to add spans returned by
5747    /// these APIs, you could do so without futzing with relative dates. But
5748    /// now you either need to provide a relative date:
5749    ///
5750    /// ```
5751    /// use jiff::{civil::date, ToSpan};
5752    ///
5753    /// let d1 = date(2025, 1, 18);
5754    /// let d2 = date(2025, 1, 26);
5755    /// let d3 = date(2025, 2, 14);
5756    ///
5757    /// let span1 = d2 - d1;
5758    /// let span2 = d3 - d2;
5759    /// let total = span1.checked_add((span2, d1))?;
5760    /// assert_eq!(total, 27.days().fieldwise());
5761    ///
5762    /// # Ok::<(), Box<dyn std::error::Error>>(())
5763    /// ```
5764    ///
5765    /// Or you can provide a marker indicating that days are always 24 hours.
5766    /// This is fine for this use case since one is only doing civil calendar
5767    /// arithmetic and not working with time zones:
5768    ///
5769    /// ```
5770    /// use jiff::{civil::date, SpanRelativeTo, ToSpan};
5771    ///
5772    /// let d1 = date(2025, 1, 18);
5773    /// let d2 = date(2025, 1, 26);
5774    /// let d3 = date(2025, 2, 14);
5775    ///
5776    /// let span1 = d2 - d1;
5777    /// let span2 = d3 - d2;
5778    /// let total = span1.checked_add(
5779    ///     (span2, SpanRelativeTo::days_are_24_hours()),
5780    /// )?;
5781    /// assert_eq!(total, 27.days().fieldwise());
5782    ///
5783    /// # Ok::<(), Box<dyn std::error::Error>>(())
5784    /// ```
5785    ///
5786    /// [Issue #48]: https://github.com/BurntSushi/jiff/issues/48
5787    #[inline]
5788    pub const fn days_are_24_hours() -> SpanRelativeTo<'static> {
5789        let kind = SpanRelativeToKind::DaysAre24Hours;
5790        SpanRelativeTo { kind }
5791    }
5792
5793    /// Converts this public API relative datetime into a more versatile
5794    /// internal representation of the same concept.
5795    ///
5796    /// Basically, the internal `Relative` type is `Cow` which means it isn't
5797    /// `Copy`. But it can present a more uniform API. The public API type
5798    /// doesn't have `Cow` so that it can be `Copy`.
5799    ///
5800    /// We also take this opportunity to attach some convenient data, such
5801    /// as a timestamp when the relative datetime type is civil.
5802    ///
5803    /// This can return `None` if this `SpanRelativeTo` isn't actually a
5804    /// datetime but a "marker" indicating some unit (like days) should be
5805    /// treated as invariant. Or `None` is returned when the given unit is
5806    /// always invariant (hours or smaller).
5807    ///
5808    /// # Errors
5809    ///
5810    /// If there was a problem doing this conversion, then an error is
5811    /// returned. In practice, this only occurs for a civil datetime near the
5812    /// civil datetime minimum and maximum values.
5813    fn to_relative(&self, unit: Unit) -> Result<Option<Relative<'a>>, Error> {
5814        if !unit.is_variable() {
5815            return Ok(None);
5816        }
5817        match self.kind {
5818            SpanRelativeToKind::Civil(dt) => {
5819                Ok(Some(Relative::Civil(RelativeCivil::new(dt)?)))
5820            }
5821            SpanRelativeToKind::Zoned(zdt) => {
5822                Ok(Some(Relative::Zoned(RelativeZoned {
5823                    zoned: DumbCow::Borrowed(zdt),
5824                })))
5825            }
5826            SpanRelativeToKind::DaysAre24Hours => {
5827                if matches!(unit, Unit::Year | Unit::Month) {
5828                    return Err(Error::from(
5829                        E::RequiresRelativeYearOrMonthGivenDaysAre24Hours {
5830                            unit,
5831                        },
5832                    ));
5833                }
5834                Ok(None)
5835            }
5836        }
5837    }
5838}
5839
5840#[derive(Clone, Copy, Debug)]
5841enum SpanRelativeToKind<'a> {
5842    Civil(DateTime),
5843    Zoned(&'a Zoned),
5844    DaysAre24Hours,
5845}
5846
5847impl<'a> From<&'a Zoned> for SpanRelativeTo<'a> {
5848    fn from(zdt: &'a Zoned) -> SpanRelativeTo<'a> {
5849        SpanRelativeTo { kind: SpanRelativeToKind::Zoned(zdt) }
5850    }
5851}
5852
5853impl From<DateTime> for SpanRelativeTo<'static> {
5854    fn from(dt: DateTime) -> SpanRelativeTo<'static> {
5855        SpanRelativeTo { kind: SpanRelativeToKind::Civil(dt) }
5856    }
5857}
5858
5859impl From<Date> for SpanRelativeTo<'static> {
5860    fn from(date: Date) -> SpanRelativeTo<'static> {
5861        let dt = DateTime::from_parts(date, Time::midnight());
5862        SpanRelativeTo { kind: SpanRelativeToKind::Civil(dt) }
5863    }
5864}
5865
5866/// A bit set that keeps track of all non-zero units on a `Span`.
5867///
5868/// Because of alignment, adding this to a `Span` does not make it any bigger.
5869///
5870/// The benefit of this bit set is to make it extremely cheap to enable fast
5871/// paths in various places. For example, doing arithmetic on a `Date` with an
5872/// arbitrary `Span` is pretty involved. But if you know the `Span` only
5873/// consists of non-zero units of days (and zero for all other units), then you
5874/// can take a much cheaper path.
5875#[derive(Clone, Copy)]
5876pub(crate) struct UnitSet(u16);
5877
5878impl UnitSet {
5879    /// Return a bit set representing all units as zero.
5880    #[inline]
5881    const fn empty() -> UnitSet {
5882        UnitSet(0)
5883    }
5884
5885    /// Set the given `unit` to `is_zero` status in this set.
5886    ///
5887    /// When `is_zero` is false, the unit is added to this set. Otherwise,
5888    /// the unit is removed from this set.
5889    #[inline]
5890    const fn set(self, unit: Unit, is_zero: bool) -> UnitSet {
5891        let bit = 1 << unit as usize;
5892        if is_zero {
5893            UnitSet(self.0 & !bit)
5894        } else {
5895            UnitSet(self.0 | bit)
5896        }
5897    }
5898
5899    /// Returns the set constructed from the given slice of units.
5900    #[inline]
5901    pub(crate) const fn from_slice(units: &[Unit]) -> UnitSet {
5902        let mut set = UnitSet::empty();
5903        let mut i = 0;
5904        while i < units.len() {
5905            set = set.set(units[i], false);
5906            i += 1;
5907        }
5908        set
5909    }
5910
5911    /// Returns true if and only if no units are in this set.
5912    #[inline]
5913    pub(crate) fn is_empty(&self) -> bool {
5914        self.0 == 0
5915    }
5916
5917    /// Returns true when this `Span` contains a non-zero value for the given
5918    /// unit.
5919    #[inline]
5920    pub(crate) fn contains(self, unit: Unit) -> bool {
5921        (self.0 & (1 << unit as usize)) != 0
5922    }
5923
5924    /// Returns true if and only if this `Span` contains precisely one
5925    /// non-zero unit corresponding to the unit given.
5926    #[inline]
5927    pub(crate) fn contains_only(self, unit: Unit) -> bool {
5928        self.0 == (1 << unit as usize)
5929    }
5930
5931    /// Returns this set, but with only calendar units.
5932    #[inline]
5933    pub(crate) fn only_calendar(self) -> UnitSet {
5934        UnitSet(self.0 & 0b0000_0011_1100_0000)
5935    }
5936
5937    /// Returns this set, but with only time units.
5938    #[inline]
5939    pub(crate) fn only_time(self) -> UnitSet {
5940        UnitSet(self.0 & 0b0000_0000_0011_1111)
5941    }
5942
5943    /// Returns the intersection of this set and the one given.
5944    #[inline]
5945    pub(crate) fn intersection(self, other: UnitSet) -> UnitSet {
5946        UnitSet(self.0 & other.0)
5947    }
5948
5949    /// Returns the largest unit in this set, or `None` if none are present.
5950    #[inline]
5951    pub(crate) fn largest_unit(self) -> Option<Unit> {
5952        let zeros = usize::try_from(self.0.leading_zeros()).ok()?;
5953        15usize.checked_sub(zeros).and_then(Unit::from_usize)
5954    }
5955}
5956
5957// N.B. This `Debug` impl isn't typically used.
5958//
5959// This is because the `Debug` impl for `Span` just emits itself in the
5960// friendly duration format, which doesn't include internal representation
5961// details like this set. It is included in `Span::debug`, but this isn't
5962// part of the public crate API.
5963impl core::fmt::Debug for UnitSet {
5964    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
5965        write!(f, "{{")?;
5966        let mut units = *self;
5967        let mut i = 0;
5968        while let Some(unit) = units.largest_unit() {
5969            if i > 0 {
5970                write!(f, ", ")?;
5971            }
5972            i += 1;
5973            write!(f, "{}", unit.compact())?;
5974            units = units.set(unit, false);
5975        }
5976        if i == 0 {
5977            write!(f, "∅")?;
5978        }
5979        write!(f, "}}")
5980    }
5981}
5982
5983/// An internal abstraction for managing a relative datetime for use in some
5984/// `Span` APIs.
5985///
5986/// This is effectively the same as a `SpanRelativeTo`, but uses a `Cow<Zoned>`
5987/// instead of a `&Zoned`. This makes it non-`Copy`, but allows us to craft a
5988/// more uniform API. (i.e., `relative + span = relative` instead of `relative
5989/// + span = owned_relative` or whatever.) Note that the `Copy` impl on
5990/// `SpanRelativeTo` means it has to accept a `&Zoned`. It can't ever take a
5991/// `Zoned` since it is non-Copy.
5992///
5993/// NOTE: Separately from above, I think it's plausible that this type could be
5994/// designed a bit differently. Namely, something like this:
5995///
5996/// ```text
5997/// struct Relative<'a> {
5998///     tz: Option<&'a TimeZone>,
5999///     dt: DateTime,
6000///     ts: Timestamp,
6001/// }
6002/// ```
6003///
6004/// That is, we do zone aware stuff but without an actual `Zoned` type. But I
6005/// think in order to make that work, we would need to expose most of the
6006/// `Zoned` API as functions on its component types (DateTime, Timestamp and
6007/// TimeZone). I think we are likely to want to do that for public API reasons,
6008/// but I'd like to resist it since I think it will add a lot of complexity.
6009/// Or maybe we need a `Unzoned` type that is `DateTime` and `Timestamp`, but
6010/// requires passing the time zone in to each of its methods. That might work
6011/// quite well, even if it was just an internal type.
6012///
6013/// Anyway, I'm not 100% sure the above would work, but I think it would. It
6014/// would be nicer because everything would be `Copy` all the time. We'd never
6015/// need a `Cow<TimeZone>` for example, because we never need to change or
6016/// create a new time zone.
6017#[derive(Clone, Debug)]
6018enum Relative<'a> {
6019    Civil(RelativeCivil),
6020    Zoned(RelativeZoned<'a>),
6021}
6022
6023impl<'a> Relative<'a> {
6024    /// Adds the given span to this relative datetime.
6025    ///
6026    /// This defers to either [`DateTime::checked_add`] or
6027    /// [`Zoned::checked_add`], depending on the type of relative datetime.
6028    ///
6029    /// The `Relative` datetime returned is guaranteed to have the same
6030    /// internal datetie type as `self`.
6031    ///
6032    /// # Errors
6033    ///
6034    /// This returns an error in the same cases as the underlying checked
6035    /// arithmetic APIs. In general, this occurs when adding the given `span`
6036    /// would result in overflow.
6037    fn checked_add(&'a self, span: Span) -> Result<Relative<'a>, Error> {
6038        match *self {
6039            Relative::Civil(dt) => Ok(Relative::Civil(dt.checked_add(span)?)),
6040            Relative::Zoned(ref zdt) => {
6041                Ok(Relative::Zoned(zdt.checked_add(span)?))
6042            }
6043        }
6044    }
6045
6046    fn checked_add_duration(
6047        &'a self,
6048        duration: SignedDuration,
6049    ) -> Result<Relative<'a>, Error> {
6050        match *self {
6051            Relative::Civil(dt) => {
6052                Ok(Relative::Civil(dt.checked_add_duration(duration)?))
6053            }
6054            Relative::Zoned(ref zdt) => {
6055                Ok(Relative::Zoned(zdt.checked_add_duration(duration)?))
6056            }
6057        }
6058    }
6059
6060    /// Returns the span of time from this relative datetime to the one given,
6061    /// with units as large as `largest`.
6062    ///
6063    /// # Errors
6064    ///
6065    /// This returns an error in the same cases as when the underlying
6066    /// [`DateTime::until`] or [`Zoned::until`] fail. Because this doesn't
6067    /// set or expose any rounding configuration, this can generally only
6068    /// occur when `largest` is `Unit::Nanosecond` and the span of time
6069    /// between `self` and `other` is too big to represent as a 64-bit integer
6070    /// nanosecond count.
6071    ///
6072    /// # Panics
6073    ///
6074    /// This panics if `self` and `other` are different internal datetime
6075    /// types. For example, if `self` was a civil datetime and `other` were
6076    /// a zoned datetime.
6077    fn until(&self, largest: Unit, other: &Relative) -> Result<Span, Error> {
6078        match (self, other) {
6079            (&Relative::Civil(ref dt1), &Relative::Civil(ref dt2)) => {
6080                dt1.until(largest, dt2)
6081            }
6082            (&Relative::Zoned(ref zdt1), &Relative::Zoned(ref zdt2)) => {
6083                zdt1.until(largest, zdt2)
6084            }
6085            // This would be bad if `Relative` were a public API, but in
6086            // practice, this case never occurs because we don't mixup our
6087            // `Relative` datetime types.
6088            _ => unreachable!(),
6089        }
6090    }
6091
6092    /// Converts this relative datetime to a nanosecond in UTC time.
6093    ///
6094    /// # Errors
6095    ///
6096    /// If there was a problem doing this conversion, then an error is
6097    /// returned. In practice, this only occurs for a civil datetime near the
6098    /// civil datetime minimum and maximum values.
6099    fn to_nanosecond(&self) -> NoUnits128 {
6100        match *self {
6101            Relative::Civil(dt) => dt.timestamp.as_nanosecond_ranged().rinto(),
6102            Relative::Zoned(ref zdt) => {
6103                zdt.zoned.timestamp().as_nanosecond_ranged().rinto()
6104            }
6105        }
6106    }
6107
6108    /// Create a balanced span of time relative to this datetime.
6109    ///
6110    /// The relative span returned has the same internal datetime type
6111    /// (civil or zoned) as this relative datetime.
6112    ///
6113    /// # Errors
6114    ///
6115    /// This returns an error when the span in this range cannot be
6116    /// represented. In general, this only occurs when asking for largest units
6117    /// of `Unit::Nanosecond` *and* when the span is too big to fit into a
6118    /// 64-bit nanosecond count.
6119    ///
6120    /// This can also return an error in other extreme cases, such as when
6121    /// adding the given span to this relative datetime results in overflow,
6122    /// or if this relative datetime is a civil datetime and it couldn't be
6123    /// converted to a timestamp in UTC.
6124    fn into_relative_span(
6125        self,
6126        largest: Unit,
6127        span: Span,
6128    ) -> Result<RelativeSpan<'a>, Error> {
6129        let kind = match self {
6130            Relative::Civil(start) => {
6131                let end = start.checked_add(span)?;
6132                RelativeSpanKind::Civil { start, end }
6133            }
6134            Relative::Zoned(start) => {
6135                let end = start.checked_add(span)?;
6136                RelativeSpanKind::Zoned { start, end }
6137            }
6138        };
6139        let relspan = kind.into_relative_span(largest)?;
6140        if span.get_sign_ranged() != C(0)
6141            && relspan.span.get_sign_ranged() != C(0)
6142            && span.get_sign_ranged() != relspan.span.get_sign_ranged()
6143        {
6144            // I haven't quite figured out when this case is hit. I think it's
6145            // actually impossible right? Balancing a duration should not flip
6146            // the sign.
6147            //
6148            // ref: https://github.com/fullcalendar/temporal-polyfill/blob/9e001042864394247181d1a5d591c18057ce32d2/packages/temporal-polyfill/src/internal/durationMath.ts#L236-L238
6149            unreachable!(
6150                "balanced span should have same sign as original span"
6151            )
6152        }
6153        Ok(relspan)
6154    }
6155
6156    /// Rounds the given span using the given rounding configuration.
6157    fn round(
6158        self,
6159        span: Span,
6160        smallest: Unit,
6161        largest: Unit,
6162        increment: NoUnits128,
6163        mode: RoundMode,
6164    ) -> Result<Span, Error> {
6165        let relspan = self.into_relative_span(largest, span)?;
6166        if relspan.span.get_sign_ranged() == C(0) {
6167            return Ok(relspan.span);
6168        }
6169        let nudge = match relspan.kind {
6170            RelativeSpanKind::Civil { start, end } => {
6171                if smallest > Unit::Day {
6172                    Nudge::relative_calendar(
6173                        relspan.span,
6174                        &Relative::Civil(start),
6175                        &Relative::Civil(end),
6176                        smallest,
6177                        increment,
6178                        mode,
6179                    )?
6180                } else {
6181                    let relative_end = end.timestamp.as_nanosecond_ranged();
6182                    Nudge::relative_invariant(
6183                        relspan.span,
6184                        relative_end.rinto(),
6185                        smallest,
6186                        largest,
6187                        increment,
6188                        mode,
6189                    )?
6190                }
6191            }
6192            RelativeSpanKind::Zoned { ref start, ref end } => {
6193                if smallest >= Unit::Day {
6194                    Nudge::relative_calendar(
6195                        relspan.span,
6196                        &Relative::Zoned(start.borrowed()),
6197                        &Relative::Zoned(end.borrowed()),
6198                        smallest,
6199                        increment,
6200                        mode,
6201                    )?
6202                } else if largest >= Unit::Day {
6203                    // This is a special case for zoned datetimes when rounding
6204                    // could bleed into variable units.
6205                    Nudge::relative_zoned_time(
6206                        relspan.span,
6207                        start,
6208                        smallest,
6209                        increment,
6210                        mode,
6211                    )?
6212                } else {
6213                    // Otherwise, rounding is the same as civil datetime.
6214                    let relative_end =
6215                        end.zoned.timestamp().as_nanosecond_ranged();
6216                    Nudge::relative_invariant(
6217                        relspan.span,
6218                        relative_end.rinto(),
6219                        smallest,
6220                        largest,
6221                        increment,
6222                        mode,
6223                    )?
6224                }
6225            }
6226        };
6227        nudge.bubble(&relspan, smallest, largest)
6228    }
6229}
6230
6231/// A balanced span between a range of civil or zoned datetimes.
6232///
6233/// The span is always balanced up to a certain unit as given to
6234/// `RelativeSpanKind::into_relative_span`.
6235#[derive(Clone, Debug)]
6236struct RelativeSpan<'a> {
6237    span: Span,
6238    kind: RelativeSpanKind<'a>,
6239}
6240
6241/// A civil or zoned datetime range of time.
6242#[derive(Clone, Debug)]
6243enum RelativeSpanKind<'a> {
6244    Civil { start: RelativeCivil, end: RelativeCivil },
6245    Zoned { start: RelativeZoned<'a>, end: RelativeZoned<'a> },
6246}
6247
6248impl<'a> RelativeSpanKind<'a> {
6249    /// Create a balanced `RelativeSpan` from this range of time.
6250    ///
6251    /// # Errors
6252    ///
6253    /// This returns an error when the span in this range cannot be
6254    /// represented. In general, this only occurs when asking for largest units
6255    /// of `Unit::Nanosecond` *and* when the span is too big to fit into a
6256    /// 64-bit nanosecond count.
6257    fn into_relative_span(
6258        self,
6259        largest: Unit,
6260    ) -> Result<RelativeSpan<'a>, Error> {
6261        let span = match self {
6262            RelativeSpanKind::Civil { ref start, ref end } => start
6263                .datetime
6264                .until((largest, end.datetime))
6265                .context(E::FailedSpanBetweenDateTimes { unit: largest })?,
6266            RelativeSpanKind::Zoned { ref start, ref end } => {
6267                start.zoned.until((largest, &*end.zoned)).context(
6268                    E::FailedSpanBetweenZonedDateTimes { unit: largest },
6269                )?
6270            }
6271        };
6272        Ok(RelativeSpan { span, kind: self })
6273    }
6274}
6275
6276/// A wrapper around a civil datetime and a timestamp corresponding to that
6277/// civil datetime in UTC.
6278///
6279/// Haphazardly interpreting a civil datetime in UTC is an odd and *usually*
6280/// incorrect thing to do. But the way we use it here is basically just to give
6281/// it an "anchoring" point such that we can represent it using a single
6282/// integer for rounding purposes. It is only used in a context *relative* to
6283/// another civil datetime interpreted in UTC. In this fashion, the selection
6284/// of UTC specifically doesn't really matter. We could use any time zone.
6285/// (Although, it must be a time zone without any transitions, otherwise we
6286/// could wind up with time zone aware results in a context where that would
6287/// be unexpected since this is civil time.)
6288#[derive(Clone, Copy, Debug)]
6289struct RelativeCivil {
6290    datetime: DateTime,
6291    timestamp: Timestamp,
6292}
6293
6294impl RelativeCivil {
6295    /// Creates a new relative wrapper around the given civil datetime.
6296    ///
6297    /// This wrapper bundles a timestamp for the given datetime by interpreting
6298    /// it as being in UTC. This is an "odd" thing to do, but it's only used
6299    /// in the context of determining the length of time between two civil
6300    /// datetimes. So technically, any time zone without transitions could be
6301    /// used.
6302    ///
6303    /// # Errors
6304    ///
6305    /// This returns an error if the datetime could not be converted to a
6306    /// timestamp. This only occurs near the minimum and maximum civil datetime
6307    /// values.
6308    fn new(datetime: DateTime) -> Result<RelativeCivil, Error> {
6309        let timestamp = datetime
6310            .to_zoned(TimeZone::UTC)
6311            .context(E::ConvertDateTimeToTimestamp)?
6312            .timestamp();
6313        Ok(RelativeCivil { datetime, timestamp })
6314    }
6315
6316    /// Returns the result of [`DateTime::checked_add`].
6317    ///
6318    /// # Errors
6319    ///
6320    /// Returns an error in the same cases as `DateTime::checked_add`. That is,
6321    /// when adding the span to this zoned datetime would overflow.
6322    ///
6323    /// This also returns an error if the resulting datetime could not be
6324    /// converted to a timestamp in UTC. This only occurs near the minimum and
6325    /// maximum datetime values.
6326    fn checked_add(&self, span: Span) -> Result<RelativeCivil, Error> {
6327        let datetime = self.datetime.checked_add(span)?;
6328        let timestamp = datetime
6329            .to_zoned(TimeZone::UTC)
6330            .context(E::ConvertDateTimeToTimestamp)?
6331            .timestamp();
6332        Ok(RelativeCivil { datetime, timestamp })
6333    }
6334
6335    /// Returns the result of [`DateTime::checked_add`] with an absolute
6336    /// duration.
6337    ///
6338    /// # Errors
6339    ///
6340    /// Returns an error in the same cases as `DateTime::checked_add`. That is,
6341    /// when adding the span to this zoned datetime would overflow.
6342    ///
6343    /// This also returns an error if the resulting datetime could not be
6344    /// converted to a timestamp in UTC. This only occurs near the minimum and
6345    /// maximum datetime values.
6346    fn checked_add_duration(
6347        &self,
6348        duration: SignedDuration,
6349    ) -> Result<RelativeCivil, Error> {
6350        let datetime = self.datetime.checked_add(duration)?;
6351        let timestamp = datetime
6352            .to_zoned(TimeZone::UTC)
6353            .context(E::ConvertDateTimeToTimestamp)?
6354            .timestamp();
6355        Ok(RelativeCivil { datetime, timestamp })
6356    }
6357
6358    /// Returns the result of [`DateTime::until`].
6359    ///
6360    /// # Errors
6361    ///
6362    /// Returns an error in the same cases as `DateTime::until`. That is, when
6363    /// the span for the given largest unit cannot be represented. This can
6364    /// generally only happen when `largest` is `Unit::Nanosecond` and the span
6365    /// cannot be represented as a 64-bit integer of nanoseconds.
6366    fn until(
6367        &self,
6368        largest: Unit,
6369        other: &RelativeCivil,
6370    ) -> Result<Span, Error> {
6371        self.datetime
6372            .until((largest, other.datetime))
6373            .context(E::FailedSpanBetweenDateTimes { unit: largest })
6374    }
6375}
6376
6377/// A simple wrapper around a possibly borrowed `Zoned`.
6378#[derive(Clone, Debug)]
6379struct RelativeZoned<'a> {
6380    zoned: DumbCow<'a, Zoned>,
6381}
6382
6383impl<'a> RelativeZoned<'a> {
6384    /// Returns the result of [`Zoned::checked_add`].
6385    ///
6386    /// # Errors
6387    ///
6388    /// Returns an error in the same cases as `Zoned::checked_add`. That is,
6389    /// when adding the span to this zoned datetime would overflow.
6390    fn checked_add(
6391        &self,
6392        span: Span,
6393    ) -> Result<RelativeZoned<'static>, Error> {
6394        let zoned = self.zoned.checked_add(span)?;
6395        Ok(RelativeZoned { zoned: DumbCow::Owned(zoned) })
6396    }
6397
6398    /// Returns the result of [`Zoned::checked_add`] with an absolute duration.
6399    ///
6400    /// # Errors
6401    ///
6402    /// Returns an error in the same cases as `Zoned::checked_add`. That is,
6403    /// when adding the span to this zoned datetime would overflow.
6404    fn checked_add_duration(
6405        &self,
6406        duration: SignedDuration,
6407    ) -> Result<RelativeZoned<'static>, Error> {
6408        let zoned = self.zoned.checked_add(duration)?;
6409        Ok(RelativeZoned { zoned: DumbCow::Owned(zoned) })
6410    }
6411
6412    /// Returns the result of [`Zoned::until`].
6413    ///
6414    /// # Errors
6415    ///
6416    /// Returns an error in the same cases as `Zoned::until`. That is, when
6417    /// the span for the given largest unit cannot be represented. This can
6418    /// generally only happen when `largest` is `Unit::Nanosecond` and the span
6419    /// cannot be represented as a 64-bit integer of nanoseconds.
6420    fn until(
6421        &self,
6422        largest: Unit,
6423        other: &RelativeZoned<'a>,
6424    ) -> Result<Span, Error> {
6425        self.zoned
6426            .until((largest, &*other.zoned))
6427            .context(E::FailedSpanBetweenZonedDateTimes { unit: largest })
6428    }
6429
6430    /// Returns the borrowed version of self; useful when you need to convert
6431    /// `&RelativeZoned` into `RelativeZoned` without cloning anything.
6432    fn borrowed(&'a self) -> RelativeZoned<'a> {
6433        RelativeZoned { zoned: self.zoned.borrowed() }
6434    }
6435}
6436
6437// The code below is the "core" rounding logic for spans. It was greatly
6438// inspired by this gist[1] and the fullcalendar Temporal polyfill[2]. In
6439// particular, the algorithm implemented below is a major simplification from
6440// how Temporal used to work[3]. Parts of it are still in rough and unclear
6441// shape IMO.
6442//
6443// [1]: https://gist.github.com/arshaw/36d3152c21482bcb78ea2c69591b20e0
6444// [2]: https://github.com/fullcalendar/temporal-polyfill
6445// [3]: https://github.com/tc39/proposal-temporal/issues/2792
6446
6447/// The result of a span rounding strategy. There are three:
6448///
6449/// * Rounding spans relative to civil datetimes using only invariant
6450/// units (days or less). This is achieved by converting the span to a simple
6451/// integer number of nanoseconds and then rounding that.
6452/// * Rounding spans relative to either a civil datetime or a zoned datetime
6453/// where rounding might involve changing non-uniform units. That is, when
6454/// the smallest unit is greater than days for civil datetimes and greater
6455/// than hours for zoned datetimes.
6456/// * Rounding spans relative to a zoned datetime whose smallest unit is
6457/// less than days.
6458///
6459/// Each of these might produce a bottom heavy span that needs to be
6460/// re-balanced. This type represents that result via one of three constructors
6461/// corresponding to each of the above strategies, and then provides a routine
6462/// for rebalancing via "bubbling."
6463#[derive(Debug)]
6464struct Nudge {
6465    /// A possibly bottom heavy rounded span.
6466    span: Span,
6467    /// The nanosecond timestamp corresponding to `relative + span`, where
6468    /// `span` is the (possibly bottom heavy) rounded span.
6469    rounded_relative_end: NoUnits128,
6470    /// Whether rounding may have created a bottom heavy span such that a
6471    /// calendar unit might need to be incremented after re-balancing smaller
6472    /// units.
6473    grew_big_unit: bool,
6474}
6475
6476impl Nudge {
6477    /// Performs rounding on the given span limited to invariant units.
6478    ///
6479    /// For civil datetimes, this means the smallest unit must be days or less,
6480    /// but the largest unit can be bigger. For zoned datetimes, this means
6481    /// that *both* the largest and smallest unit must be hours or less. This
6482    /// is because zoned datetimes with rounding that can spill up to days
6483    /// requires special handling.
6484    ///
6485    /// It works by converting the span to a single integer number of
6486    /// nanoseconds, rounding it and then converting back to a span.
6487    fn relative_invariant(
6488        balanced: Span,
6489        relative_end: NoUnits128,
6490        smallest: Unit,
6491        largest: Unit,
6492        increment: NoUnits128,
6493        mode: RoundMode,
6494    ) -> Result<Nudge, Error> {
6495        // Ensures this is only called when rounding invariant units.
6496        assert!(smallest <= Unit::Week);
6497
6498        let sign = balanced.get_sign_ranged();
6499        let balanced_nanos = balanced.to_invariant_nanoseconds();
6500        let rounded_nanos = mode.round_by_unit_in_nanoseconds(
6501            balanced_nanos,
6502            smallest,
6503            increment,
6504        );
6505        let span = Span::from_invariant_nanoseconds(largest, rounded_nanos)
6506            .context(E::ConvertNanoseconds { unit: largest })?
6507            .years_ranged(balanced.get_years_ranged())
6508            .months_ranged(balanced.get_months_ranged())
6509            .weeks_ranged(balanced.get_weeks_ranged());
6510
6511        let diff_nanos = rounded_nanos - balanced_nanos;
6512        let diff_days = rounded_nanos.div_ceil(t::NANOS_PER_CIVIL_DAY)
6513            - balanced_nanos.div_ceil(t::NANOS_PER_CIVIL_DAY);
6514        let grew_big_unit = diff_days.signum() == sign;
6515        let rounded_relative_end = relative_end + diff_nanos;
6516        Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6517    }
6518
6519    /// Performs rounding on the given span where the smallest unit configured
6520    /// implies that rounding will cover calendar or "non-uniform" units. (That
6521    /// is, units whose length can change based on the relative datetime.)
6522    fn relative_calendar(
6523        balanced: Span,
6524        relative_start: &Relative<'_>,
6525        relative_end: &Relative<'_>,
6526        smallest: Unit,
6527        increment: NoUnits128,
6528        mode: RoundMode,
6529    ) -> Result<Nudge, Error> {
6530        #[cfg(not(feature = "std"))]
6531        use crate::util::libm::Float;
6532
6533        assert!(smallest >= Unit::Day);
6534        let sign = balanced.get_sign_ranged();
6535        let truncated = increment
6536            * balanced.get_units_ranged(smallest).div_ceil(increment);
6537        let span = balanced
6538            .without_lower(smallest)
6539            .try_units_ranged(smallest, truncated.rinto())?;
6540        let (relative0, relative1) = clamp_relative_span(
6541            relative_start,
6542            span,
6543            smallest,
6544            NoUnits::try_rfrom("increment", increment)?
6545                .try_checked_mul("signed increment", sign)?,
6546        )?;
6547
6548        // FIXME: This is brutal. This is the only non-optional floating point
6549        // used so far in Jiff. We do expose floating point for things like
6550        // `Span::total`, but that's optional and not a core part of Jiff's
6551        // functionality. This is in the core part of Jiff's span rounding...
6552        let denom = (relative1 - relative0).get() as f64;
6553        let numer = (relative_end.to_nanosecond() - relative0).get() as f64;
6554        let exact = (truncated.get() as f64)
6555            + (numer / denom) * (sign.get() as f64) * (increment.get() as f64);
6556        let rounded = mode.round_float(exact, increment);
6557        let grew_big_unit =
6558            ((rounded.get() as f64) - exact).signum() == (sign.get() as f64);
6559
6560        let span = span.try_units_ranged(smallest, rounded.rinto())?;
6561        let rounded_relative_end =
6562            if grew_big_unit { relative1 } else { relative0 };
6563        Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6564    }
6565
6566    /// Performs rounding on the given span where the smallest unit is hours
6567    /// or less *and* the relative datetime is time zone aware.
6568    fn relative_zoned_time(
6569        balanced: Span,
6570        relative_start: &RelativeZoned<'_>,
6571        smallest: Unit,
6572        increment: NoUnits128,
6573        mode: RoundMode,
6574    ) -> Result<Nudge, Error> {
6575        let sign = balanced.get_sign_ranged();
6576        let time_nanos =
6577            balanced.only_lower(Unit::Day).to_invariant_nanoseconds();
6578        let mut rounded_time_nanos =
6579            mode.round_by_unit_in_nanoseconds(time_nanos, smallest, increment);
6580        let (relative0, relative1) = clamp_relative_span(
6581            &Relative::Zoned(relative_start.borrowed()),
6582            balanced.without_lower(Unit::Day),
6583            Unit::Day,
6584            sign.rinto(),
6585        )?;
6586        let day_nanos = relative1 - relative0;
6587        let beyond_day_nanos = rounded_time_nanos - day_nanos;
6588
6589        let mut day_delta = NoUnits::N::<0>();
6590        let rounded_relative_end =
6591            if beyond_day_nanos == C(0) || beyond_day_nanos.signum() == sign {
6592                day_delta += C(1);
6593                rounded_time_nanos = mode.round_by_unit_in_nanoseconds(
6594                    beyond_day_nanos,
6595                    smallest,
6596                    increment,
6597                );
6598                relative1 + rounded_time_nanos
6599            } else {
6600                relative0 + rounded_time_nanos
6601            };
6602
6603        let span =
6604            Span::from_invariant_nanoseconds(Unit::Hour, rounded_time_nanos)
6605                .context(E::ConvertNanoseconds { unit: Unit::Hour })?
6606                .years_ranged(balanced.get_years_ranged())
6607                .months_ranged(balanced.get_months_ranged())
6608                .weeks_ranged(balanced.get_weeks_ranged())
6609                .days_ranged(balanced.get_days_ranged() + day_delta);
6610        let grew_big_unit = day_delta != C(0);
6611        Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6612    }
6613
6614    /// This "bubbles" up the units in a potentially "bottom heavy" span to
6615    /// larger units. For example, P1m50d relative to March 1 is bottom heavy.
6616    /// This routine will bubble the days up to months to get P2m19d.
6617    ///
6618    /// # Errors
6619    ///
6620    /// This routine fails if any arithmetic on the individual units fails, or
6621    /// when span arithmetic on the relative datetime given fails.
6622    fn bubble(
6623        &self,
6624        relative: &RelativeSpan,
6625        smallest: Unit,
6626        largest: Unit,
6627    ) -> Result<Span, Error> {
6628        if !self.grew_big_unit || smallest == Unit::Week {
6629            return Ok(self.span);
6630        }
6631
6632        let smallest = smallest.max(Unit::Day);
6633        let mut balanced = self.span;
6634        let sign = balanced.get_sign_ranged();
6635        let mut unit = smallest;
6636        while let Some(u) = unit.next() {
6637            unit = u;
6638            if unit > largest {
6639                break;
6640            }
6641            // We only bubble smaller units up into weeks when the largest unit
6642            // is explicitly set to weeks. Otherwise, we leave it as-is.
6643            if unit == Unit::Week && largest != Unit::Week {
6644                continue;
6645            }
6646
6647            let span_start = balanced.without_lower(unit);
6648            let new_units = span_start
6649                .get_units_ranged(unit)
6650                .try_checked_add("bubble-units", sign)?;
6651            let span_end = span_start.try_units_ranged(unit, new_units)?;
6652            let threshold = match relative.kind {
6653                RelativeSpanKind::Civil { ref start, .. } => {
6654                    start.checked_add(span_end)?.timestamp
6655                }
6656                RelativeSpanKind::Zoned { ref start, .. } => {
6657                    start.checked_add(span_end)?.zoned.timestamp()
6658                }
6659            };
6660            let beyond =
6661                self.rounded_relative_end - threshold.as_nanosecond_ranged();
6662            if beyond == C(0) || beyond.signum() == sign {
6663                balanced = span_end;
6664            } else {
6665                break;
6666            }
6667        }
6668        Ok(balanced)
6669    }
6670}
6671
6672/// Rounds a span consisting of only invariant units.
6673///
6674/// This only applies when the max of the units in the span being rounded,
6675/// the largest configured unit and the smallest configured unit are all
6676/// invariant. That is, days or lower for spans without a relative datetime or
6677/// a relative civil datetime, and hours or lower for spans with a relative
6678/// zoned datetime.
6679///
6680/// All we do here is convert the span to an integer number of nanoseconds,
6681/// round that and then convert back. There aren't any tricky corner cases to
6682/// consider here.
6683fn round_span_invariant(
6684    span: Span,
6685    smallest: Unit,
6686    largest: Unit,
6687    increment: NoUnits128,
6688    mode: RoundMode,
6689) -> Result<Span, Error> {
6690    assert!(smallest <= Unit::Week);
6691    assert!(largest <= Unit::Week);
6692    let nanos = span.to_invariant_nanoseconds();
6693    let rounded =
6694        mode.round_by_unit_in_nanoseconds(nanos, smallest, increment);
6695    Span::from_invariant_nanoseconds(largest, rounded)
6696        .context(E::ConvertNanoseconds { unit: largest })
6697}
6698
6699/// Returns the nanosecond timestamps of `relative + span` and `relative +
6700/// {amount of unit} + span`.
6701///
6702/// This is useful for determining the actual length, in nanoseconds, of some
6703/// unit amount (usually a single unit). Usually, this is called with a span
6704/// whose units lower than `unit` are zeroed out and with an `amount` that
6705/// is `-1` or `1` or `0`. So for example, if `unit` were `Unit::Day`, then
6706/// you'd get back two nanosecond timestamps relative to the relative datetime
6707/// given that start exactly "one day" apart. (Which might be different than 24
6708/// hours, depending on the time zone.)
6709///
6710/// # Errors
6711///
6712/// This returns an error if adding the units overflows, or if doing the span
6713/// arithmetic on `relative` overflows.
6714fn clamp_relative_span(
6715    relative: &Relative<'_>,
6716    span: Span,
6717    unit: Unit,
6718    amount: NoUnits,
6719) -> Result<(NoUnits128, NoUnits128), Error> {
6720    let amount =
6721        span.get_units_ranged(unit).try_checked_add("clamp-units", amount)?;
6722    let span_amount = span.try_units_ranged(unit, amount)?;
6723    let relative0 = relative.checked_add(span)?.to_nanosecond();
6724    let relative1 = relative.checked_add(span_amount)?.to_nanosecond();
6725    Ok((relative0, relative1))
6726}
6727
6728/// A common parsing function that works in bytes.
6729///
6730/// Specifically, this parses either an ISO 8601 duration into a `Span` or
6731/// a "friendly" duration into a `Span`. It also tries to give decent error
6732/// messages.
6733///
6734/// This works because the friendly and ISO 8601 formats have non-overlapping
6735/// prefixes. Both can start with a `+` or `-`, but aside from that, an ISO
6736/// 8601 duration _always_ has to start with a `P` or `p`. We can utilize this
6737/// property to very quickly determine how to parse the input. We just need to
6738/// handle the possibly ambiguous case with a leading sign a little carefully
6739/// in order to ensure good error messages.
6740///
6741/// (We do the same thing for `SignedDuration`.)
6742#[cfg_attr(feature = "perf-inline", inline(always))]
6743fn parse_iso_or_friendly(bytes: &[u8]) -> Result<Span, Error> {
6744    let Some((&byte, tail)) = bytes.split_first() else {
6745        return Err(crate::Error::from(
6746            crate::error::fmt::Error::HybridDurationEmpty,
6747        ));
6748    };
6749    let mut first = byte;
6750    // N.B. Unsigned durations don't support negative durations (of
6751    // course), but we still check for it here so that we can defer to
6752    // the dedicated parsers. They will provide their own error messages.
6753    if first == b'+' || first == b'-' {
6754        let Some(&byte) = tail.first() else {
6755            return Err(crate::Error::from(
6756                crate::error::fmt::Error::HybridDurationPrefix { sign: first },
6757            ));
6758        };
6759        first = byte;
6760    }
6761    if first == b'P' || first == b'p' {
6762        temporal::DEFAULT_SPAN_PARSER.parse_span(bytes)
6763    } else {
6764        friendly::DEFAULT_SPAN_PARSER.parse_span(bytes)
6765    }
6766}
6767
6768fn requires_relative_date_err(unit: Unit) -> Result<(), Error> {
6769    if unit.is_variable() {
6770        return Err(Error::from(if matches!(unit, Unit::Week | Unit::Day) {
6771            E::RequiresRelativeWeekOrDay { unit }
6772        } else {
6773            E::RequiresRelativeYearOrMonth { unit }
6774        }));
6775    }
6776    Ok(())
6777}
6778
6779#[cfg(test)]
6780mod tests {
6781    use std::io::Cursor;
6782
6783    use alloc::string::ToString;
6784
6785    use crate::{civil::date, RoundMode};
6786
6787    use super::*;
6788
6789    #[test]
6790    fn test_total() {
6791        if crate::tz::db().is_definitively_empty() {
6792            return;
6793        }
6794
6795        let span = 130.hours().minutes(20);
6796        let total = span.total(Unit::Second).unwrap();
6797        assert_eq!(total, 469200.0);
6798
6799        let span = 123456789.seconds();
6800        let total = span
6801            .total(SpanTotal::from(Unit::Day).days_are_24_hours())
6802            .unwrap();
6803        assert_eq!(total, 1428.8980208333332);
6804
6805        let span = 2756.hours();
6806        let dt = date(2020, 1, 1).at(0, 0, 0, 0);
6807        let zdt = dt.in_tz("Europe/Rome").unwrap();
6808        let total = span.total((Unit::Month, &zdt)).unwrap();
6809        assert_eq!(total, 3.7958333333333334);
6810        let total = span.total((Unit::Month, dt)).unwrap();
6811        assert_eq!(total, 3.7944444444444443);
6812    }
6813
6814    #[test]
6815    fn test_compare() {
6816        if crate::tz::db().is_definitively_empty() {
6817            return;
6818        }
6819
6820        let span1 = 79.hours().minutes(10);
6821        let span2 = 79.hours().seconds(630);
6822        let span3 = 78.hours().minutes(50);
6823        let mut array = [span1, span2, span3];
6824        array.sort_by(|sp1, sp2| sp1.compare(sp2).unwrap());
6825        assert_eq!(array, [span3, span1, span2].map(SpanFieldwise));
6826
6827        let day24 = SpanRelativeTo::days_are_24_hours();
6828        let span1 = 79.hours().minutes(10);
6829        let span2 = 3.days().hours(7).seconds(630);
6830        let span3 = 3.days().hours(6).minutes(50);
6831        let mut array = [span1, span2, span3];
6832        array.sort_by(|sp1, sp2| sp1.compare((sp2, day24)).unwrap());
6833        assert_eq!(array, [span3, span1, span2].map(SpanFieldwise));
6834
6835        let dt = date(2020, 11, 1).at(0, 0, 0, 0);
6836        let zdt = dt.in_tz("America/Los_Angeles").unwrap();
6837        array.sort_by(|sp1, sp2| sp1.compare((sp2, &zdt)).unwrap());
6838        assert_eq!(array, [span1, span3, span2].map(SpanFieldwise));
6839    }
6840
6841    #[test]
6842    fn test_checked_add() {
6843        let span1 = 1.hour();
6844        let span2 = 30.minutes();
6845        let sum = span1.checked_add(span2).unwrap();
6846        span_eq!(sum, 1.hour().minutes(30));
6847
6848        let span1 = 1.hour().minutes(30);
6849        let span2 = 2.hours().minutes(45);
6850        let sum = span1.checked_add(span2).unwrap();
6851        span_eq!(sum, 4.hours().minutes(15));
6852
6853        let span = 50
6854            .years()
6855            .months(50)
6856            .days(50)
6857            .hours(50)
6858            .minutes(50)
6859            .seconds(50)
6860            .milliseconds(500)
6861            .microseconds(500)
6862            .nanoseconds(500);
6863        let relative = date(1900, 1, 1).at(0, 0, 0, 0);
6864        let sum = span.checked_add((span, relative)).unwrap();
6865        let expected = 108
6866            .years()
6867            .months(7)
6868            .days(12)
6869            .hours(5)
6870            .minutes(41)
6871            .seconds(41)
6872            .milliseconds(1)
6873            .microseconds(1)
6874            .nanoseconds(0);
6875        span_eq!(sum, expected);
6876
6877        let span = 1.month().days(15);
6878        let relative = date(2000, 2, 1).at(0, 0, 0, 0);
6879        let sum = span.checked_add((span, relative)).unwrap();
6880        span_eq!(sum, 3.months());
6881        let relative = date(2000, 3, 1).at(0, 0, 0, 0);
6882        let sum = span.checked_add((span, relative)).unwrap();
6883        span_eq!(sum, 2.months().days(30));
6884    }
6885
6886    #[test]
6887    fn test_round_day_time() {
6888        let span = 29.seconds();
6889        let rounded = span.round(Unit::Minute).unwrap();
6890        span_eq!(rounded, 0.minute());
6891
6892        let span = 30.seconds();
6893        let rounded = span.round(Unit::Minute).unwrap();
6894        span_eq!(rounded, 1.minute());
6895
6896        let span = 8.seconds();
6897        let rounded = span
6898            .round(
6899                SpanRound::new()
6900                    .smallest(Unit::Nanosecond)
6901                    .largest(Unit::Microsecond),
6902            )
6903            .unwrap();
6904        span_eq!(rounded, 8_000_000.microseconds());
6905
6906        let span = 130.minutes();
6907        let rounded = span
6908            .round(SpanRound::new().largest(Unit::Day).days_are_24_hours())
6909            .unwrap();
6910        span_eq!(rounded, 2.hours().minutes(10));
6911
6912        let span = 10.minutes().seconds(52);
6913        let rounded = span.round(Unit::Minute).unwrap();
6914        span_eq!(rounded, 11.minutes());
6915
6916        let span = 10.minutes().seconds(52);
6917        let rounded = span
6918            .round(
6919                SpanRound::new().smallest(Unit::Minute).mode(RoundMode::Trunc),
6920            )
6921            .unwrap();
6922        span_eq!(rounded, 10.minutes());
6923
6924        let span = 2.hours().minutes(34).seconds(18);
6925        let rounded =
6926            span.round(SpanRound::new().largest(Unit::Second)).unwrap();
6927        span_eq!(rounded, 9258.seconds());
6928
6929        let span = 6.minutes();
6930        let rounded = span
6931            .round(
6932                SpanRound::new()
6933                    .smallest(Unit::Minute)
6934                    .increment(5)
6935                    .mode(RoundMode::Ceil),
6936            )
6937            .unwrap();
6938        span_eq!(rounded, 10.minutes());
6939    }
6940
6941    #[test]
6942    fn test_round_relative_zoned_calendar() {
6943        if crate::tz::db().is_definitively_empty() {
6944            return;
6945        }
6946
6947        let span = 2756.hours();
6948        let relative =
6949            date(2020, 1, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6950        let options = SpanRound::new()
6951            .largest(Unit::Year)
6952            .smallest(Unit::Day)
6953            .relative(&relative);
6954        let rounded = span.round(options).unwrap();
6955        span_eq!(rounded, 3.months().days(24));
6956
6957        let span = 24.hours().nanoseconds(5);
6958        let relative = date(2000, 10, 29)
6959            .at(0, 0, 0, 0)
6960            .in_tz("America/Vancouver")
6961            .unwrap();
6962        let options = SpanRound::new()
6963            .largest(Unit::Day)
6964            .smallest(Unit::Minute)
6965            .relative(&relative)
6966            .mode(RoundMode::Expand)
6967            .increment(30);
6968        let rounded = span.round(options).unwrap();
6969        // It seems like this is the correct answer, although it apparently
6970        // differs from Temporal and the FullCalendar polyfill. I'm not sure
6971        // what accounts for the difference in the implementation.
6972        //
6973        // See: https://github.com/tc39/proposal-temporal/pull/2758#discussion_r1597255245
6974        span_eq!(rounded, 24.hours().minutes(30));
6975
6976        // Ref: https://github.com/tc39/proposal-temporal/issues/2816#issuecomment-2115608460
6977        let span = -1.month().hours(24);
6978        let relative: crate::Zoned = date(2024, 4, 11)
6979            .at(2, 0, 0, 0)
6980            .in_tz("America/New_York")
6981            .unwrap();
6982        let options =
6983            SpanRound::new().smallest(Unit::Millisecond).relative(&relative);
6984        let rounded = span.round(options).unwrap();
6985        span_eq!(rounded, -1.month().days(1).hours(1));
6986        let dt = relative.checked_add(span).unwrap();
6987        let diff = relative.until((Unit::Month, &dt)).unwrap();
6988        span_eq!(diff, -1.month().days(1).hours(1));
6989
6990        // Like the above, but don't use a datetime near a DST transition. In
6991        // this case, a day is a normal 24 hours. (Unlike above, where the
6992        // duration includes a 23 hour day, and so an additional hour has to be
6993        // added to the span to account for that.)
6994        let span = -1.month().hours(24);
6995        let relative = date(2024, 6, 11)
6996            .at(2, 0, 0, 0)
6997            .in_tz("America/New_York")
6998            .unwrap();
6999        let options =
7000            SpanRound::new().smallest(Unit::Millisecond).relative(&relative);
7001        let rounded = span.round(options).unwrap();
7002        span_eq!(rounded, -1.month().days(1));
7003    }
7004
7005    #[test]
7006    fn test_round_relative_zoned_time() {
7007        if crate::tz::db().is_definitively_empty() {
7008            return;
7009        }
7010
7011        let span = 2756.hours();
7012        let relative =
7013            date(2020, 1, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
7014        let options = SpanRound::new().largest(Unit::Year).relative(&relative);
7015        let rounded = span.round(options).unwrap();
7016        span_eq!(rounded, 3.months().days(23).hours(21));
7017
7018        let span = 2756.hours();
7019        let relative =
7020            date(2020, 9, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
7021        let options = SpanRound::new().largest(Unit::Year).relative(&relative);
7022        let rounded = span.round(options).unwrap();
7023        span_eq!(rounded, 3.months().days(23).hours(19));
7024
7025        let span = 3.hours();
7026        let relative =
7027            date(2020, 3, 8).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
7028        let options = SpanRound::new().largest(Unit::Year).relative(&relative);
7029        let rounded = span.round(options).unwrap();
7030        span_eq!(rounded, 3.hours());
7031    }
7032
7033    #[test]
7034    fn test_round_relative_day_time() {
7035        let span = 2756.hours();
7036        let options =
7037            SpanRound::new().largest(Unit::Year).relative(date(2020, 1, 1));
7038        let rounded = span.round(options).unwrap();
7039        span_eq!(rounded, 3.months().days(23).hours(20));
7040
7041        let span = 2756.hours();
7042        let options =
7043            SpanRound::new().largest(Unit::Year).relative(date(2020, 9, 1));
7044        let rounded = span.round(options).unwrap();
7045        span_eq!(rounded, 3.months().days(23).hours(20));
7046
7047        let span = 190.days();
7048        let options =
7049            SpanRound::new().largest(Unit::Year).relative(date(2020, 1, 1));
7050        let rounded = span.round(options).unwrap();
7051        span_eq!(rounded, 6.months().days(8));
7052
7053        let span = 30
7054            .days()
7055            .hours(23)
7056            .minutes(59)
7057            .seconds(59)
7058            .milliseconds(999)
7059            .microseconds(999)
7060            .nanoseconds(999);
7061        let options = SpanRound::new()
7062            .smallest(Unit::Microsecond)
7063            .largest(Unit::Year)
7064            .relative(date(2024, 5, 1));
7065        let rounded = span.round(options).unwrap();
7066        span_eq!(rounded, 1.month());
7067
7068        let span = 364
7069            .days()
7070            .hours(23)
7071            .minutes(59)
7072            .seconds(59)
7073            .milliseconds(999)
7074            .microseconds(999)
7075            .nanoseconds(999);
7076        let options = SpanRound::new()
7077            .smallest(Unit::Microsecond)
7078            .largest(Unit::Year)
7079            .relative(date(2023, 1, 1));
7080        let rounded = span.round(options).unwrap();
7081        span_eq!(rounded, 1.year());
7082
7083        let span = 365
7084            .days()
7085            .hours(23)
7086            .minutes(59)
7087            .seconds(59)
7088            .milliseconds(999)
7089            .microseconds(999)
7090            .nanoseconds(999);
7091        let options = SpanRound::new()
7092            .smallest(Unit::Microsecond)
7093            .largest(Unit::Year)
7094            .relative(date(2023, 1, 1));
7095        let rounded = span.round(options).unwrap();
7096        span_eq!(rounded, 1.year().days(1));
7097
7098        let span = 365
7099            .days()
7100            .hours(23)
7101            .minutes(59)
7102            .seconds(59)
7103            .milliseconds(999)
7104            .microseconds(999)
7105            .nanoseconds(999);
7106        let options = SpanRound::new()
7107            .smallest(Unit::Microsecond)
7108            .largest(Unit::Year)
7109            .relative(date(2024, 1, 1));
7110        let rounded = span.round(options).unwrap();
7111        span_eq!(rounded, 1.year());
7112
7113        let span = 3.hours();
7114        let options =
7115            SpanRound::new().largest(Unit::Year).relative(date(2020, 3, 8));
7116        let rounded = span.round(options).unwrap();
7117        span_eq!(rounded, 3.hours());
7118    }
7119
7120    #[test]
7121    fn span_sign() {
7122        assert_eq!(Span::new().get_sign_ranged(), C(0));
7123        assert_eq!(Span::new().days(1).get_sign_ranged(), C(1));
7124        assert_eq!(Span::new().days(-1).get_sign_ranged(), C(-1));
7125        assert_eq!(Span::new().days(1).days(0).get_sign_ranged(), C(0));
7126        assert_eq!(Span::new().days(-1).days(0).get_sign_ranged(), C(0));
7127        assert_eq!(
7128            Span::new().years(1).days(1).days(0).get_sign_ranged(),
7129            C(1)
7130        );
7131        assert_eq!(
7132            Span::new().years(-1).days(-1).days(0).get_sign_ranged(),
7133            C(-1)
7134        );
7135    }
7136
7137    #[test]
7138    fn span_size() {
7139        #[cfg(target_pointer_width = "64")]
7140        {
7141            #[cfg(debug_assertions)]
7142            {
7143                assert_eq!(core::mem::align_of::<Span>(), 8);
7144                assert_eq!(core::mem::size_of::<Span>(), 184);
7145            }
7146            #[cfg(not(debug_assertions))]
7147            {
7148                assert_eq!(core::mem::align_of::<Span>(), 8);
7149                assert_eq!(core::mem::size_of::<Span>(), 64);
7150            }
7151        }
7152    }
7153
7154    quickcheck::quickcheck! {
7155        fn prop_roundtrip_span_nanoseconds(span: Span) -> quickcheck::TestResult {
7156            let largest = span.largest_unit();
7157            if largest > Unit::Day {
7158                return quickcheck::TestResult::discard();
7159            }
7160            let nanos = span.to_invariant_nanoseconds();
7161            let got = Span::from_invariant_nanoseconds(largest, nanos).unwrap();
7162            quickcheck::TestResult::from_bool(nanos == got.to_invariant_nanoseconds())
7163        }
7164    }
7165
7166    /// # `serde` deserializer compatibility test
7167    ///
7168    /// Serde YAML used to be unable to deserialize `jiff` types,
7169    /// as deserializing from bytes is not supported by the deserializer.
7170    ///
7171    /// - <https://github.com/BurntSushi/jiff/issues/138>
7172    /// - <https://github.com/BurntSushi/jiff/discussions/148>
7173    #[test]
7174    fn span_deserialize_yaml() {
7175        let expected = Span::new()
7176            .years(1)
7177            .months(2)
7178            .weeks(3)
7179            .days(4)
7180            .hours(5)
7181            .minutes(6)
7182            .seconds(7);
7183
7184        let deserialized: Span =
7185            serde_yaml::from_str("P1y2m3w4dT5h6m7s").unwrap();
7186
7187        span_eq!(deserialized, expected);
7188
7189        let deserialized: Span =
7190            serde_yaml::from_slice("P1y2m3w4dT5h6m7s".as_bytes()).unwrap();
7191
7192        span_eq!(deserialized, expected);
7193
7194        let cursor = Cursor::new(b"P1y2m3w4dT5h6m7s");
7195        let deserialized: Span = serde_yaml::from_reader(cursor).unwrap();
7196
7197        span_eq!(deserialized, expected);
7198    }
7199
7200    #[test]
7201    fn display() {
7202        let span = Span::new()
7203            .years(1)
7204            .months(2)
7205            .weeks(3)
7206            .days(4)
7207            .hours(5)
7208            .minutes(6)
7209            .seconds(7)
7210            .milliseconds(8)
7211            .microseconds(9)
7212            .nanoseconds(10);
7213        insta::assert_snapshot!(
7214            span,
7215            @"P1Y2M3W4DT5H6M7.00800901S",
7216        );
7217        insta::assert_snapshot!(
7218            alloc::format!("{span:#}"),
7219            @"1y 2mo 3w 4d 5h 6m 7s 8ms 9µs 10ns",
7220        );
7221    }
7222
7223    /// This test ensures that we can parse `humantime` formatted durations.
7224    #[test]
7225    fn humantime_compatibility_parse() {
7226        let dur = std::time::Duration::new(60 * 60 * 24 * 411, 123_456_789);
7227        let formatted = humantime::format_duration(dur).to_string();
7228        assert_eq!(
7229            formatted,
7230            "1year 1month 15days 7h 26m 24s 123ms 456us 789ns"
7231        );
7232        let expected = 1
7233            .year()
7234            .months(1)
7235            .days(15)
7236            .hours(7)
7237            .minutes(26)
7238            .seconds(24)
7239            .milliseconds(123)
7240            .microseconds(456)
7241            .nanoseconds(789);
7242        span_eq!(formatted.parse::<Span>().unwrap(), expected);
7243    }
7244
7245    /// This test ensures that we can print a `Span` that `humantime` can
7246    /// parse.
7247    ///
7248    /// Note that this isn't the default since `humantime`'s parser is
7249    /// pretty limited. e.g., It doesn't support things like `nsecs`
7250    /// despite supporting `secs`. And other reasons. See the docs on
7251    /// `Designator::HumanTime` for why we sadly provide a custom variant for
7252    /// it.
7253    #[test]
7254    fn humantime_compatibility_print() {
7255        static PRINTER: friendly::SpanPrinter = friendly::SpanPrinter::new()
7256            .designator(friendly::Designator::HumanTime);
7257
7258        let span = 1
7259            .year()
7260            .months(1)
7261            .days(15)
7262            .hours(7)
7263            .minutes(26)
7264            .seconds(24)
7265            .milliseconds(123)
7266            .microseconds(456)
7267            .nanoseconds(789);
7268        let formatted = PRINTER.span_to_string(&span);
7269        assert_eq!(formatted, "1y 1month 15d 7h 26m 24s 123ms 456us 789ns");
7270
7271        let dur = humantime::parse_duration(&formatted).unwrap();
7272        let expected =
7273            std::time::Duration::new(60 * 60 * 24 * 411, 123_456_789);
7274        assert_eq!(dur, expected);
7275    }
7276
7277    #[test]
7278    fn from_str() {
7279        let p = |s: &str| -> Result<Span, Error> { s.parse() };
7280
7281        insta::assert_snapshot!(
7282            p("1 day").unwrap(),
7283            @"P1D",
7284        );
7285        insta::assert_snapshot!(
7286            p("+1 day").unwrap(),
7287            @"P1D",
7288        );
7289        insta::assert_snapshot!(
7290            p("-1 day").unwrap(),
7291            @"-P1D",
7292        );
7293        insta::assert_snapshot!(
7294            p("P1d").unwrap(),
7295            @"P1D",
7296        );
7297        insta::assert_snapshot!(
7298            p("+P1d").unwrap(),
7299            @"P1D",
7300        );
7301        insta::assert_snapshot!(
7302            p("-P1d").unwrap(),
7303            @"-P1D",
7304        );
7305
7306        insta::assert_snapshot!(
7307            p("").unwrap_err(),
7308            @r#"an empty string is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format"#,
7309        );
7310        insta::assert_snapshot!(
7311            p("+").unwrap_err(),
7312            @r#"found nothing after sign `+`, which is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format"#,
7313        );
7314        insta::assert_snapshot!(
7315            p("-").unwrap_err(),
7316            @r#"found nothing after sign `-`, which is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format"#,
7317        );
7318    }
7319
7320    #[test]
7321    fn serde_deserialize() {
7322        let p = |s: &str| -> Result<Span, serde_json::Error> {
7323            serde_json::from_str(&alloc::format!("\"{s}\""))
7324        };
7325
7326        insta::assert_snapshot!(
7327            p("1 day").unwrap(),
7328            @"P1D",
7329        );
7330        insta::assert_snapshot!(
7331            p("+1 day").unwrap(),
7332            @"P1D",
7333        );
7334        insta::assert_snapshot!(
7335            p("-1 day").unwrap(),
7336            @"-P1D",
7337        );
7338        insta::assert_snapshot!(
7339            p("P1d").unwrap(),
7340            @"P1D",
7341        );
7342        insta::assert_snapshot!(
7343            p("+P1d").unwrap(),
7344            @"P1D",
7345        );
7346        insta::assert_snapshot!(
7347            p("-P1d").unwrap(),
7348            @"-P1D",
7349        );
7350
7351        insta::assert_snapshot!(
7352            p("").unwrap_err(),
7353            @r#"an empty string is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format at line 1 column 2"#,
7354        );
7355        insta::assert_snapshot!(
7356            p("+").unwrap_err(),
7357            @r#"found nothing after sign `+`, which is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format at line 1 column 3"#,
7358        );
7359        insta::assert_snapshot!(
7360            p("-").unwrap_err(),
7361            @r#"found nothing after sign `-`, which is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format at line 1 column 3"#,
7362        );
7363    }
7364}