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
2457        let seconds = nanos / t::NANOS_PER_SECOND;
2458        let seconds = i64::from(seconds);
2459        let subsec_nanos = nanos % t::NANOS_PER_SECOND;
2460        // OK because % 1_000_000_000 above guarantees that the result fits
2461        // in a i32.
2462        let subsec_nanos = i32::try_from(subsec_nanos).unwrap();
2463
2464        // SignedDuration::new can panic if |subsec_nanos| >= 1_000_000_000
2465        // and seconds == {i64::MIN,i64::MAX}. But this can never happen
2466        // because we guaranteed by construction above that |subsec_nanos| <
2467        // 1_000_000_000.
2468        SignedDuration::new(seconds, subsec_nanos)
2469    }
2470}
2471
2472/// Crate internal APIs that operate on ranged integer types.
2473impl Span {
2474    #[inline]
2475    pub(crate) fn years_ranged(self, years: t::SpanYears) -> Span {
2476        let mut span = Span { years: years.abs(), ..self };
2477        span.sign = self.resign(years, &span);
2478        span.units = span.units.set(Unit::Year, years == C(0));
2479        span
2480    }
2481
2482    #[inline]
2483    pub(crate) fn months_ranged(self, months: t::SpanMonths) -> Span {
2484        let mut span = Span { months: months.abs(), ..self };
2485        span.sign = self.resign(months, &span);
2486        span.units = span.units.set(Unit::Month, months == C(0));
2487        span
2488    }
2489
2490    #[inline]
2491    pub(crate) fn weeks_ranged(self, weeks: t::SpanWeeks) -> Span {
2492        let mut span = Span { weeks: weeks.abs(), ..self };
2493        span.sign = self.resign(weeks, &span);
2494        span.units = span.units.set(Unit::Week, weeks == C(0));
2495        span
2496    }
2497
2498    #[inline]
2499    pub(crate) fn days_ranged(self, days: t::SpanDays) -> Span {
2500        let mut span = Span { days: days.abs(), ..self };
2501        span.sign = self.resign(days, &span);
2502        span.units = span.units.set(Unit::Day, days == C(0));
2503        span
2504    }
2505
2506    #[inline]
2507    pub(crate) fn hours_ranged(self, hours: t::SpanHours) -> Span {
2508        let mut span = Span { hours: hours.abs(), ..self };
2509        span.sign = self.resign(hours, &span);
2510        span.units = span.units.set(Unit::Hour, hours == C(0));
2511        span
2512    }
2513
2514    #[inline]
2515    pub(crate) fn minutes_ranged(self, minutes: t::SpanMinutes) -> Span {
2516        let mut span = Span { minutes: minutes.abs(), ..self };
2517        span.sign = self.resign(minutes, &span);
2518        span.units = span.units.set(Unit::Minute, minutes == C(0));
2519        span
2520    }
2521
2522    #[inline]
2523    pub(crate) fn seconds_ranged(self, seconds: t::SpanSeconds) -> Span {
2524        let mut span = Span { seconds: seconds.abs(), ..self };
2525        span.sign = self.resign(seconds, &span);
2526        span.units = span.units.set(Unit::Second, seconds == C(0));
2527        span
2528    }
2529
2530    #[inline]
2531    fn milliseconds_ranged(self, milliseconds: t::SpanMilliseconds) -> Span {
2532        let mut span = Span { milliseconds: milliseconds.abs(), ..self };
2533        span.sign = self.resign(milliseconds, &span);
2534        span.units = span.units.set(Unit::Millisecond, milliseconds == C(0));
2535        span
2536    }
2537
2538    #[inline]
2539    fn microseconds_ranged(self, microseconds: t::SpanMicroseconds) -> Span {
2540        let mut span = Span { microseconds: microseconds.abs(), ..self };
2541        span.sign = self.resign(microseconds, &span);
2542        span.units = span.units.set(Unit::Microsecond, microseconds == C(0));
2543        span
2544    }
2545
2546    #[inline]
2547    pub(crate) fn nanoseconds_ranged(
2548        self,
2549        nanoseconds: t::SpanNanoseconds,
2550    ) -> Span {
2551        let mut span = Span { nanoseconds: nanoseconds.abs(), ..self };
2552        span.sign = self.resign(nanoseconds, &span);
2553        span.units = span.units.set(Unit::Nanosecond, nanoseconds == C(0));
2554        span
2555    }
2556
2557    #[inline]
2558    fn try_days_ranged(
2559        self,
2560        days: impl TryRInto<t::SpanDays>,
2561    ) -> Result<Span, Error> {
2562        let days = days.try_rinto("days")?;
2563        Ok(self.days_ranged(days))
2564    }
2565
2566    #[inline]
2567    pub(crate) fn try_hours_ranged(
2568        self,
2569        hours: impl TryRInto<t::SpanHours>,
2570    ) -> Result<Span, Error> {
2571        let hours = hours.try_rinto("hours")?;
2572        Ok(self.hours_ranged(hours))
2573    }
2574
2575    #[inline]
2576    pub(crate) fn try_minutes_ranged(
2577        self,
2578        minutes: impl TryRInto<t::SpanMinutes>,
2579    ) -> Result<Span, Error> {
2580        let minutes = minutes.try_rinto("minutes")?;
2581        Ok(self.minutes_ranged(minutes))
2582    }
2583
2584    #[inline]
2585    pub(crate) fn try_seconds_ranged(
2586        self,
2587        seconds: impl TryRInto<t::SpanSeconds>,
2588    ) -> Result<Span, Error> {
2589        let seconds = seconds.try_rinto("seconds")?;
2590        Ok(self.seconds_ranged(seconds))
2591    }
2592
2593    #[inline]
2594    pub(crate) fn try_milliseconds_ranged(
2595        self,
2596        milliseconds: impl TryRInto<t::SpanMilliseconds>,
2597    ) -> Result<Span, Error> {
2598        let milliseconds = milliseconds.try_rinto("milliseconds")?;
2599        Ok(self.milliseconds_ranged(milliseconds))
2600    }
2601
2602    #[inline]
2603    pub(crate) fn try_microseconds_ranged(
2604        self,
2605        microseconds: impl TryRInto<t::SpanMicroseconds>,
2606    ) -> Result<Span, Error> {
2607        let microseconds = microseconds.try_rinto("microseconds")?;
2608        Ok(self.microseconds_ranged(microseconds))
2609    }
2610
2611    #[inline]
2612    pub(crate) fn try_nanoseconds_ranged(
2613        self,
2614        nanoseconds: impl TryRInto<t::SpanNanoseconds>,
2615    ) -> Result<Span, Error> {
2616        let nanoseconds = nanoseconds.try_rinto("nanoseconds")?;
2617        Ok(self.nanoseconds_ranged(nanoseconds))
2618    }
2619
2620    #[inline]
2621    fn try_units_ranged(
2622        self,
2623        unit: Unit,
2624        value: NoUnits,
2625    ) -> Result<Span, Error> {
2626        Ok(match unit {
2627            Unit::Year => self.years_ranged(value.try_rinto("years")?),
2628            Unit::Month => self.months_ranged(value.try_rinto("months")?),
2629            Unit::Week => self.weeks_ranged(value.try_rinto("weeks")?),
2630            Unit::Day => self.days_ranged(value.try_rinto("days")?),
2631            Unit::Hour => self.hours_ranged(value.try_rinto("hours")?),
2632            Unit::Minute => self.minutes_ranged(value.try_rinto("minutes")?),
2633            Unit::Second => self.seconds_ranged(value.try_rinto("seconds")?),
2634            Unit::Millisecond => {
2635                self.milliseconds_ranged(value.try_rinto("milliseconds")?)
2636            }
2637            Unit::Microsecond => {
2638                self.microseconds_ranged(value.try_rinto("microseconds")?)
2639            }
2640            Unit::Nanosecond => {
2641                self.nanoseconds_ranged(value.try_rinto("nanoseconds")?)
2642            }
2643        })
2644    }
2645
2646    #[inline]
2647    pub(crate) fn get_years_ranged(&self) -> t::SpanYears {
2648        self.years * self.sign
2649    }
2650
2651    #[inline]
2652    pub(crate) fn get_months_ranged(&self) -> t::SpanMonths {
2653        self.months * self.sign
2654    }
2655
2656    #[inline]
2657    pub(crate) fn get_weeks_ranged(&self) -> t::SpanWeeks {
2658        self.weeks * self.sign
2659    }
2660
2661    #[inline]
2662    pub(crate) fn get_days_ranged(&self) -> t::SpanDays {
2663        self.days * self.sign
2664    }
2665
2666    #[inline]
2667    pub(crate) fn get_hours_ranged(&self) -> t::SpanHours {
2668        self.hours * self.sign
2669    }
2670
2671    #[inline]
2672    pub(crate) fn get_minutes_ranged(&self) -> t::SpanMinutes {
2673        self.minutes * self.sign
2674    }
2675
2676    #[inline]
2677    pub(crate) fn get_seconds_ranged(&self) -> t::SpanSeconds {
2678        self.seconds * self.sign
2679    }
2680
2681    #[inline]
2682    pub(crate) fn get_milliseconds_ranged(&self) -> t::SpanMilliseconds {
2683        self.milliseconds * self.sign
2684    }
2685
2686    #[inline]
2687    pub(crate) fn get_microseconds_ranged(&self) -> t::SpanMicroseconds {
2688        self.microseconds * self.sign
2689    }
2690
2691    #[inline]
2692    pub(crate) fn get_nanoseconds_ranged(&self) -> t::SpanNanoseconds {
2693        self.nanoseconds * self.sign
2694    }
2695
2696    #[inline]
2697    fn get_sign_ranged(&self) -> ri8<-1, 1> {
2698        self.sign
2699    }
2700
2701    #[inline]
2702    fn get_units_ranged(&self, unit: Unit) -> NoUnits {
2703        match unit {
2704            Unit::Year => self.get_years_ranged().rinto(),
2705            Unit::Month => self.get_months_ranged().rinto(),
2706            Unit::Week => self.get_weeks_ranged().rinto(),
2707            Unit::Day => self.get_days_ranged().rinto(),
2708            Unit::Hour => self.get_hours_ranged().rinto(),
2709            Unit::Minute => self.get_minutes_ranged().rinto(),
2710            Unit::Second => self.get_seconds_ranged().rinto(),
2711            Unit::Millisecond => self.get_milliseconds_ranged().rinto(),
2712            Unit::Microsecond => self.get_microseconds_ranged().rinto(),
2713            Unit::Nanosecond => self.get_nanoseconds_ranged().rinto(),
2714        }
2715    }
2716}
2717
2718/// Crate internal APIs that permit setting units without checks.
2719///
2720/// Callers should be very careful when using these. These notably also do
2721/// not handle updating the sign on the `Span` and require the precisely
2722/// correct integer primitive.
2723impl Span {
2724    #[inline]
2725    pub(crate) fn years_unchecked(self, years: i16) -> Span {
2726        let mut span =
2727            Span { years: t::SpanYears::new_unchecked(years), ..self };
2728        span.units = span.units.set(Unit::Year, years == 0);
2729        span
2730    }
2731
2732    #[inline]
2733    pub(crate) fn months_unchecked(self, months: i32) -> Span {
2734        let mut span =
2735            Span { months: t::SpanMonths::new_unchecked(months), ..self };
2736        span.units = span.units.set(Unit::Month, months == 0);
2737        span
2738    }
2739
2740    #[inline]
2741    pub(crate) fn weeks_unchecked(self, weeks: i32) -> Span {
2742        let mut span =
2743            Span { weeks: t::SpanWeeks::new_unchecked(weeks), ..self };
2744        span.units = span.units.set(Unit::Week, weeks == 0);
2745        span
2746    }
2747
2748    #[inline]
2749    pub(crate) fn days_unchecked(self, days: i32) -> Span {
2750        let mut span = Span { days: t::SpanDays::new_unchecked(days), ..self };
2751        span.units = span.units.set(Unit::Day, days == 0);
2752        span
2753    }
2754
2755    #[inline]
2756    pub(crate) fn hours_unchecked(self, hours: i32) -> Span {
2757        let mut span =
2758            Span { hours: t::SpanHours::new_unchecked(hours), ..self };
2759        span.units = span.units.set(Unit::Hour, hours == 0);
2760        span
2761    }
2762
2763    #[inline]
2764    pub(crate) fn minutes_unchecked(self, minutes: i64) -> Span {
2765        let mut span =
2766            Span { minutes: t::SpanMinutes::new_unchecked(minutes), ..self };
2767        span.units = span.units.set(Unit::Minute, minutes == 0);
2768        span
2769    }
2770
2771    #[inline]
2772    pub(crate) fn seconds_unchecked(self, seconds: i64) -> Span {
2773        let mut span =
2774            Span { seconds: t::SpanSeconds::new_unchecked(seconds), ..self };
2775        span.units = span.units.set(Unit::Second, seconds == 0);
2776        span
2777    }
2778
2779    #[inline]
2780    pub(crate) fn milliseconds_unchecked(self, milliseconds: i64) -> Span {
2781        let mut span = Span {
2782            milliseconds: t::SpanMilliseconds::new_unchecked(milliseconds),
2783            ..self
2784        };
2785        span.units = span.units.set(Unit::Millisecond, milliseconds == 0);
2786        span
2787    }
2788
2789    #[inline]
2790    pub(crate) fn microseconds_unchecked(self, microseconds: i64) -> Span {
2791        let mut span = Span {
2792            microseconds: t::SpanMicroseconds::new_unchecked(microseconds),
2793            ..self
2794        };
2795        span.units = span.units.set(Unit::Microsecond, microseconds == 0);
2796        span
2797    }
2798
2799    #[inline]
2800    pub(crate) fn nanoseconds_unchecked(self, nanoseconds: i64) -> Span {
2801        let mut span = Span {
2802            nanoseconds: t::SpanNanoseconds::new_unchecked(nanoseconds),
2803            ..self
2804        };
2805        span.units = span.units.set(Unit::Nanosecond, nanoseconds == 0);
2806        span
2807    }
2808
2809    #[inline]
2810    pub(crate) fn sign_unchecked(self, sign: Sign) -> Span {
2811        Span { sign, ..self }
2812    }
2813}
2814
2815/// Crate internal helper routines.
2816impl Span {
2817    /// Converts the given number of nanoseconds to a `Span` whose units do not
2818    /// exceed `largest`.
2819    ///
2820    /// Note that `largest` is capped at `Unit::Week`. Note though that if
2821    /// any unit greater than `Unit::Week` is given, then it is treated as
2822    /// `Unit::Day`. The only way to get weeks in the `Span` returned is to
2823    /// specifically request `Unit::Week`.
2824    ///
2825    /// And also note that days in this context are civil days. That is, they
2826    /// are always 24 hours long. Callers needing to deal with variable length
2827    /// days should do so outside of this routine and should not provide a
2828    /// `largest` unit bigger than `Unit::Hour`.
2829    pub(crate) fn from_invariant_nanoseconds(
2830        largest: Unit,
2831        nanos: NoUnits128,
2832    ) -> Result<Span, Error> {
2833        let mut span = Span::new();
2834        match largest {
2835            Unit::Week => {
2836                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2837                span = span.try_nanoseconds_ranged(
2838                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2839                )?;
2840                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2841                span = span.try_microseconds_ranged(
2842                    micros.rem_ceil(t::MICROS_PER_MILLI),
2843                )?;
2844                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2845                span = span.try_milliseconds_ranged(
2846                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2847                )?;
2848                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2849                span = span.try_seconds_ranged(
2850                    secs.rem_ceil(t::SECONDS_PER_MINUTE),
2851                )?;
2852                let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2853                span = span
2854                    .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2855                let days = hours.div_ceil(t::HOURS_PER_CIVIL_DAY);
2856                span = span.try_hours_ranged(
2857                    hours.rem_ceil(t::HOURS_PER_CIVIL_DAY),
2858                )?;
2859                let weeks = days.div_ceil(t::DAYS_PER_CIVIL_WEEK);
2860                span = span
2861                    .try_days_ranged(days.rem_ceil(t::DAYS_PER_CIVIL_WEEK))?;
2862                span = span.weeks_ranged(weeks.try_rinto("weeks")?);
2863                Ok(span)
2864            }
2865            Unit::Year | Unit::Month | Unit::Day => {
2866                // Unit::Year | Unit::Month | Unit::Week | Unit::Day => {
2867                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2868                span = span.try_nanoseconds_ranged(
2869                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2870                )?;
2871                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2872                span = span.try_microseconds_ranged(
2873                    micros.rem_ceil(t::MICROS_PER_MILLI),
2874                )?;
2875                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2876                span = span.try_milliseconds_ranged(
2877                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2878                )?;
2879                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2880                span = span.try_seconds_ranged(
2881                    secs.rem_ceil(t::SECONDS_PER_MINUTE),
2882                )?;
2883                let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2884                span = span
2885                    .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2886                let days = hours.div_ceil(t::HOURS_PER_CIVIL_DAY);
2887                span = span.try_hours_ranged(
2888                    hours.rem_ceil(t::HOURS_PER_CIVIL_DAY),
2889                )?;
2890                span = span.try_days_ranged(days)?;
2891                Ok(span)
2892            }
2893            Unit::Hour => {
2894                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2895                span = span.try_nanoseconds_ranged(
2896                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2897                )?;
2898                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2899                span = span.try_microseconds_ranged(
2900                    micros.rem_ceil(t::MICROS_PER_MILLI),
2901                )?;
2902                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2903                span = span.try_milliseconds_ranged(
2904                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2905                )?;
2906                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2907                span = span.try_seconds_ranged(
2908                    secs.rem_ceil(t::SECONDS_PER_MINUTE),
2909                )?;
2910                let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2911                span = span
2912                    .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2913                span = span.try_hours_ranged(hours)?;
2914                Ok(span)
2915            }
2916            Unit::Minute => {
2917                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2918                span = span.try_nanoseconds_ranged(
2919                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2920                )?;
2921                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2922                span = span.try_microseconds_ranged(
2923                    micros.rem_ceil(t::MICROS_PER_MILLI),
2924                )?;
2925                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2926                span = span.try_milliseconds_ranged(
2927                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2928                )?;
2929                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2930                span =
2931                    span.try_seconds(secs.rem_ceil(t::SECONDS_PER_MINUTE))?;
2932                span = span.try_minutes_ranged(mins)?;
2933                Ok(span)
2934            }
2935            Unit::Second => {
2936                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2937                span = span.try_nanoseconds_ranged(
2938                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2939                )?;
2940                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2941                span = span.try_microseconds_ranged(
2942                    micros.rem_ceil(t::MICROS_PER_MILLI),
2943                )?;
2944                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2945                span = span.try_milliseconds_ranged(
2946                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2947                )?;
2948                span = span.try_seconds_ranged(secs)?;
2949                Ok(span)
2950            }
2951            Unit::Millisecond => {
2952                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2953                span = span.try_nanoseconds_ranged(
2954                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2955                )?;
2956                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2957                span = span.try_microseconds_ranged(
2958                    micros.rem_ceil(t::MICROS_PER_MILLI),
2959                )?;
2960                span = span.try_milliseconds_ranged(millis)?;
2961                Ok(span)
2962            }
2963            Unit::Microsecond => {
2964                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2965                span = span.try_nanoseconds_ranged(
2966                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2967                )?;
2968                span = span.try_microseconds_ranged(micros)?;
2969                Ok(span)
2970            }
2971            Unit::Nanosecond => {
2972                span = span.try_nanoseconds_ranged(nanos)?;
2973                Ok(span)
2974            }
2975        }
2976    }
2977
2978    /// Converts the non-variable units of this `Span` to a total number of
2979    /// nanoseconds.
2980    ///
2981    /// This includes days and weeks, even though they can be of irregular
2982    /// length during time zone transitions. If this applies, then callers
2983    /// should set the days and weeks to `0` before calling this routine.
2984    ///
2985    /// All units above weeks are always ignored.
2986    #[inline]
2987    pub(crate) fn to_invariant_nanoseconds(&self) -> NoUnits128 {
2988        let mut nanos = NoUnits128::rfrom(self.get_nanoseconds_ranged());
2989        nanos += NoUnits128::rfrom(self.get_microseconds_ranged())
2990            * t::NANOS_PER_MICRO;
2991        nanos += NoUnits128::rfrom(self.get_milliseconds_ranged())
2992            * t::NANOS_PER_MILLI;
2993        nanos +=
2994            NoUnits128::rfrom(self.get_seconds_ranged()) * t::NANOS_PER_SECOND;
2995        nanos +=
2996            NoUnits128::rfrom(self.get_minutes_ranged()) * t::NANOS_PER_MINUTE;
2997        nanos +=
2998            NoUnits128::rfrom(self.get_hours_ranged()) * t::NANOS_PER_HOUR;
2999        nanos +=
3000            NoUnits128::rfrom(self.get_days_ranged()) * t::NANOS_PER_CIVIL_DAY;
3001        nanos += NoUnits128::rfrom(self.get_weeks_ranged())
3002            * t::NANOS_PER_CIVIL_WEEK;
3003        nanos
3004    }
3005
3006    /// Converts the non-variable units of this `Span` to a total number of
3007    /// seconds if there is no fractional second component. Otherwise,
3008    /// `None` is returned.
3009    ///
3010    /// This is useful for short-circuiting in arithmetic operations when
3011    /// it's faster to only deal with seconds. And in particular, acknowledges
3012    /// that nanosecond precision durations are somewhat rare.
3013    ///
3014    /// This includes days and weeks, even though they can be of irregular
3015    /// length during time zone transitions. If this applies, then callers
3016    /// should set the days and weeks to `0` before calling this routine.
3017    ///
3018    /// All units above weeks are always ignored.
3019    #[inline]
3020    pub(crate) fn to_invariant_seconds(&self) -> Option<NoUnits> {
3021        if self.has_fractional_seconds() {
3022            return None;
3023        }
3024        let mut seconds = NoUnits::rfrom(self.get_seconds_ranged());
3025        seconds +=
3026            NoUnits::rfrom(self.get_minutes_ranged()) * t::SECONDS_PER_MINUTE;
3027        seconds +=
3028            NoUnits::rfrom(self.get_hours_ranged()) * t::SECONDS_PER_HOUR;
3029        seconds +=
3030            NoUnits::rfrom(self.get_days_ranged()) * t::SECONDS_PER_CIVIL_DAY;
3031        seconds += NoUnits::rfrom(self.get_weeks_ranged())
3032            * t::SECONDS_PER_CIVIL_WEEK;
3033        Some(seconds)
3034    }
3035
3036    /// Rebalances the invariant units (days or lower) on this span so that
3037    /// the largest possible non-zero unit is the one given.
3038    ///
3039    /// Units above day are ignored and dropped.
3040    ///
3041    /// If the given unit is greater than days, then it is treated as-if it
3042    /// were days.
3043    ///
3044    /// # Errors
3045    ///
3046    /// This can return an error in the case of lop-sided units. For example,
3047    /// if this span has maximal values for all units, then rebalancing is
3048    /// not possible because the number of days after balancing would exceed
3049    /// the limit.
3050    #[cfg(test)] // currently only used in zic parser?
3051    #[inline]
3052    pub(crate) fn rebalance(self, unit: Unit) -> Result<Span, Error> {
3053        Span::from_invariant_nanoseconds(unit, self.to_invariant_nanoseconds())
3054    }
3055
3056    /// Returns true if and only if this span has at least one non-zero
3057    /// fractional second unit.
3058    #[inline]
3059    pub(crate) fn has_fractional_seconds(&self) -> bool {
3060        self.milliseconds != C(0)
3061            || self.microseconds != C(0)
3062            || self.nanoseconds != C(0)
3063    }
3064
3065    /// Returns an equivalent span, but with all non-calendar (units below
3066    /// days) set to zero.
3067    #[cfg_attr(feature = "perf-inline", inline(always))]
3068    pub(crate) fn only_calendar(self) -> Span {
3069        let mut span = self;
3070        span.hours = t::SpanHours::N::<0>();
3071        span.minutes = t::SpanMinutes::N::<0>();
3072        span.seconds = t::SpanSeconds::N::<0>();
3073        span.milliseconds = t::SpanMilliseconds::N::<0>();
3074        span.microseconds = t::SpanMicroseconds::N::<0>();
3075        span.nanoseconds = t::SpanNanoseconds::N::<0>();
3076        if span.sign != C(0)
3077            && span.years == C(0)
3078            && span.months == C(0)
3079            && span.weeks == C(0)
3080            && span.days == C(0)
3081        {
3082            span.sign = t::Sign::N::<0>();
3083        }
3084        span.units = span.units.only_calendar();
3085        span
3086    }
3087
3088    /// Returns an equivalent span, but with all calendar (units above
3089    /// hours) set to zero.
3090    #[cfg_attr(feature = "perf-inline", inline(always))]
3091    pub(crate) fn only_time(self) -> Span {
3092        let mut span = self;
3093        span.years = t::SpanYears::N::<0>();
3094        span.months = t::SpanMonths::N::<0>();
3095        span.weeks = t::SpanWeeks::N::<0>();
3096        span.days = t::SpanDays::N::<0>();
3097        if span.sign != C(0)
3098            && span.hours == C(0)
3099            && span.minutes == C(0)
3100            && span.seconds == C(0)
3101            && span.milliseconds == C(0)
3102            && span.microseconds == C(0)
3103            && span.nanoseconds == C(0)
3104        {
3105            span.sign = t::Sign::N::<0>();
3106        }
3107        span.units = span.units.only_time();
3108        span
3109    }
3110
3111    /// Returns an equivalent span, but with all units greater than or equal to
3112    /// the one given set to zero.
3113    #[cfg_attr(feature = "perf-inline", inline(always))]
3114    pub(crate) fn only_lower(self, unit: Unit) -> Span {
3115        let mut span = self;
3116        // Unit::Nanosecond is the minimum, so nothing can be smaller than it.
3117        if unit <= Unit::Microsecond {
3118            span = span.microseconds_ranged(C(0).rinto());
3119        }
3120        if unit <= Unit::Millisecond {
3121            span = span.milliseconds_ranged(C(0).rinto());
3122        }
3123        if unit <= Unit::Second {
3124            span = span.seconds_ranged(C(0).rinto());
3125        }
3126        if unit <= Unit::Minute {
3127            span = span.minutes_ranged(C(0).rinto());
3128        }
3129        if unit <= Unit::Hour {
3130            span = span.hours_ranged(C(0).rinto());
3131        }
3132        if unit <= Unit::Day {
3133            span = span.days_ranged(C(0).rinto());
3134        }
3135        if unit <= Unit::Week {
3136            span = span.weeks_ranged(C(0).rinto());
3137        }
3138        if unit <= Unit::Month {
3139            span = span.months_ranged(C(0).rinto());
3140        }
3141        if unit <= Unit::Year {
3142            span = span.years_ranged(C(0).rinto());
3143        }
3144        span
3145    }
3146
3147    /// Returns an equivalent span, but with all units less than the one given
3148    /// set to zero.
3149    #[cfg_attr(feature = "perf-inline", inline(always))]
3150    pub(crate) fn without_lower(self, unit: Unit) -> Span {
3151        let mut span = self;
3152        if unit > Unit::Nanosecond {
3153            span = span.nanoseconds_ranged(C(0).rinto());
3154        }
3155        if unit > Unit::Microsecond {
3156            span = span.microseconds_ranged(C(0).rinto());
3157        }
3158        if unit > Unit::Millisecond {
3159            span = span.milliseconds_ranged(C(0).rinto());
3160        }
3161        if unit > Unit::Second {
3162            span = span.seconds_ranged(C(0).rinto());
3163        }
3164        if unit > Unit::Minute {
3165            span = span.minutes_ranged(C(0).rinto());
3166        }
3167        if unit > Unit::Hour {
3168            span = span.hours_ranged(C(0).rinto());
3169        }
3170        if unit > Unit::Day {
3171            span = span.days_ranged(C(0).rinto());
3172        }
3173        if unit > Unit::Week {
3174            span = span.weeks_ranged(C(0).rinto());
3175        }
3176        if unit > Unit::Month {
3177            span = span.months_ranged(C(0).rinto());
3178        }
3179        // Unit::Year is the max, so nothing can be bigger than it.
3180        span
3181    }
3182
3183    /// Returns an error corresponding to the smallest non-time non-zero unit.
3184    ///
3185    /// If all non-time units are zero, then this returns `None`.
3186    #[cfg_attr(feature = "perf-inline", inline(always))]
3187    pub(crate) fn smallest_non_time_non_zero_unit_error(
3188        &self,
3189    ) -> Option<Error> {
3190        let non_time_unit = self.largest_calendar_unit()?;
3191        Some(Error::from(E::NotAllowedCalendarUnits { unit: non_time_unit }))
3192    }
3193
3194    /// Returns the largest non-zero calendar unit, or `None` if there are no
3195    /// non-zero calendar units.
3196    #[inline]
3197    pub(crate) fn largest_calendar_unit(&self) -> Option<Unit> {
3198        self.units().only_calendar().largest_unit()
3199    }
3200
3201    /// Returns the largest non-zero unit in this span.
3202    ///
3203    /// If all components of this span are zero, then `Unit::Nanosecond` is
3204    /// returned.
3205    #[inline]
3206    pub(crate) fn largest_unit(&self) -> Unit {
3207        self.units().largest_unit().unwrap_or(Unit::Nanosecond)
3208    }
3209
3210    /// Returns the set of units on this `Span`.
3211    #[inline]
3212    pub(crate) fn units(&self) -> UnitSet {
3213        self.units
3214    }
3215
3216    /// Returns a string containing the value of all non-zero fields.
3217    ///
3218    /// This is useful for debugging. Normally, this would be the "alternate"
3219    /// debug impl (perhaps), but that's what insta uses and I preferred having
3220    /// the friendly format used there since it is much more terse.
3221    #[cfg(feature = "alloc")]
3222    #[allow(dead_code)]
3223    pub(crate) fn debug(&self) -> alloc::string::String {
3224        use core::fmt::Write;
3225
3226        let mut buf = alloc::string::String::new();
3227        write!(buf, "Span {{ sign: {:?}, units: {:?}", self.sign, self.units)
3228            .unwrap();
3229        if self.years != C(0) {
3230            write!(buf, ", years: {:?}", self.years).unwrap();
3231        }
3232        if self.months != C(0) {
3233            write!(buf, ", months: {:?}", self.months).unwrap();
3234        }
3235        if self.weeks != C(0) {
3236            write!(buf, ", weeks: {:?}", self.weeks).unwrap();
3237        }
3238        if self.days != C(0) {
3239            write!(buf, ", days: {:?}", self.days).unwrap();
3240        }
3241        if self.hours != C(0) {
3242            write!(buf, ", hours: {:?}", self.hours).unwrap();
3243        }
3244        if self.minutes != C(0) {
3245            write!(buf, ", minutes: {:?}", self.minutes).unwrap();
3246        }
3247        if self.seconds != C(0) {
3248            write!(buf, ", seconds: {:?}", self.seconds).unwrap();
3249        }
3250        if self.milliseconds != C(0) {
3251            write!(buf, ", milliseconds: {:?}", self.milliseconds).unwrap();
3252        }
3253        if self.microseconds != C(0) {
3254            write!(buf, ", microseconds: {:?}", self.microseconds).unwrap();
3255        }
3256        if self.nanoseconds != C(0) {
3257            write!(buf, ", nanoseconds: {:?}", self.nanoseconds).unwrap();
3258        }
3259        buf.push_str(" }}");
3260        buf
3261    }
3262
3263    /// Given some new units to set on this span and the span updates with the
3264    /// new units, this determines the what the sign of `new` should be.
3265    #[inline]
3266    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3267        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3268            // Negative units anywhere always makes the entire span negative.
3269            if units < C(0) {
3270                return Sign::N::<-1>();
3271            }
3272            let mut new_is_zero = new.sign == C(0) && units == C(0);
3273            // When `units == 0` and it was previously non-zero, then
3274            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3275            // when it should be true. So in this case, we need to re-check all
3276            // the units to set the sign correctly.
3277            if units == C(0) {
3278                new_is_zero = new.years == C(0)
3279                    && new.months == C(0)
3280                    && new.weeks == C(0)
3281                    && new.days == C(0)
3282                    && new.hours == C(0)
3283                    && new.minutes == C(0)
3284                    && new.seconds == C(0)
3285                    && new.milliseconds == C(0)
3286                    && new.microseconds == C(0)
3287                    && new.nanoseconds == C(0);
3288            }
3289            match (span.is_zero(), new_is_zero) {
3290                (_, true) => Sign::N::<0>(),
3291                (true, false) => units.signum().rinto(),
3292                // If the old and new span are both non-zero, and we know our
3293                // new units are not negative, then the sign remains unchanged.
3294                (false, false) => new.sign,
3295            }
3296        }
3297        imp(self, units.rinto(), new)
3298    }
3299}
3300
3301impl Default for Span {
3302    #[inline]
3303    fn default() -> Span {
3304        Span {
3305            sign: ri8::N::<0>(),
3306            units: UnitSet::empty(),
3307            years: C(0).rinto(),
3308            months: C(0).rinto(),
3309            weeks: C(0).rinto(),
3310            days: C(0).rinto(),
3311            hours: C(0).rinto(),
3312            minutes: C(0).rinto(),
3313            seconds: C(0).rinto(),
3314            milliseconds: C(0).rinto(),
3315            microseconds: C(0).rinto(),
3316            nanoseconds: C(0).rinto(),
3317        }
3318    }
3319}
3320
3321impl core::fmt::Debug for Span {
3322    #[inline]
3323    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3324        use crate::fmt::StdFmtWrite;
3325
3326        friendly::DEFAULT_SPAN_PRINTER
3327            .print_span(self, StdFmtWrite(f))
3328            .map_err(|_| core::fmt::Error)
3329    }
3330}
3331
3332impl core::fmt::Display for Span {
3333    #[inline]
3334    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3335        use crate::fmt::StdFmtWrite;
3336
3337        if f.alternate() {
3338            friendly::DEFAULT_SPAN_PRINTER
3339                .print_span(self, StdFmtWrite(f))
3340                .map_err(|_| core::fmt::Error)
3341        } else {
3342            temporal::DEFAULT_SPAN_PRINTER
3343                .print_span(self, StdFmtWrite(f))
3344                .map_err(|_| core::fmt::Error)
3345        }
3346    }
3347}
3348
3349impl core::str::FromStr for Span {
3350    type Err = Error;
3351
3352    #[inline]
3353    fn from_str(string: &str) -> Result<Span, Error> {
3354        parse_iso_or_friendly(string.as_bytes())
3355    }
3356}
3357
3358impl core::ops::Neg for Span {
3359    type Output = Span;
3360
3361    #[inline]
3362    fn neg(self) -> Span {
3363        self.negate()
3364    }
3365}
3366
3367/// This multiplies each unit in a span by an integer.
3368///
3369/// This panics on overflow. For checked arithmetic, use [`Span::checked_mul`].
3370impl core::ops::Mul<i64> for Span {
3371    type Output = Span;
3372
3373    #[inline]
3374    fn mul(self, rhs: i64) -> Span {
3375        self.checked_mul(rhs)
3376            .expect("multiplying `Span` by a scalar overflowed")
3377    }
3378}
3379
3380/// This multiplies each unit in a span by an integer.
3381///
3382/// This panics on overflow. For checked arithmetic, use [`Span::checked_mul`].
3383impl core::ops::Mul<Span> for i64 {
3384    type Output = Span;
3385
3386    #[inline]
3387    fn mul(self, rhs: Span) -> Span {
3388        rhs.checked_mul(self)
3389            .expect("multiplying `Span` by a scalar overflowed")
3390    }
3391}
3392
3393/// Converts a `Span` to a [`std::time::Duration`].
3394///
3395/// # Errors
3396///
3397/// This can fail for only two reasons:
3398///
3399/// * The span is negative. This is an error because a `std::time::Duration` is
3400///   unsigned.)
3401/// * The span has any non-zero units greater than hours. This is an error
3402///   because it's impossible to determine the length of, e.g., a month without
3403///   a reference date.
3404///
3405/// This can never result in overflow because a `Duration` can represent a
3406/// bigger span of time than `Span` when limited to units of hours or lower.
3407///
3408/// If you need to convert a `Span` to a `Duration` that has non-zero
3409/// units bigger than hours, then please use [`Span::to_duration`] with a
3410/// corresponding relative date.
3411///
3412/// # Example: maximal span
3413///
3414/// This example shows the maximum possible span using units of hours or
3415/// smaller, and the corresponding `Duration` value:
3416///
3417/// ```
3418/// use std::time::Duration;
3419///
3420/// use jiff::Span;
3421///
3422/// let sp = Span::new()
3423///     .hours(175_307_616)
3424///     .minutes(10_518_456_960i64)
3425///     .seconds(631_107_417_600i64)
3426///     .milliseconds(631_107_417_600_000i64)
3427///     .microseconds(631_107_417_600_000_000i64)
3428///     .nanoseconds(9_223_372_036_854_775_807i64);
3429/// let duration = Duration::try_from(sp)?;
3430/// assert_eq!(duration, Duration::new(3_164_760_460_036, 854_775_807));
3431///
3432/// # Ok::<(), Box<dyn std::error::Error>>(())
3433/// ```
3434///
3435/// # Example: converting a negative span
3436///
3437/// Since a `Span` is signed and a `Duration` is unsigned, converting
3438/// a negative `Span` to `Duration` will always fail. One can use
3439/// [`Span::signum`] to get the sign of the span and [`Span::abs`] to make the
3440/// span positive before converting it to a `Duration`:
3441///
3442/// ```
3443/// use std::time::Duration;
3444///
3445/// use jiff::{Span, ToSpan};
3446///
3447/// let span = -86_400.seconds().nanoseconds(1);
3448/// let (sign, duration) = (span.signum(), Duration::try_from(span.abs())?);
3449/// assert_eq!((sign, duration), (-1, Duration::new(86_400, 1)));
3450///
3451/// # Ok::<(), Box<dyn std::error::Error>>(())
3452/// ```
3453impl TryFrom<Span> for UnsignedDuration {
3454    type Error = Error;
3455
3456    #[inline]
3457    fn try_from(sp: Span) -> Result<UnsignedDuration, Error> {
3458        // This isn't needed, but improves error messages.
3459        if sp.is_negative() {
3460            return Err(Error::from(E::ConvertNegative));
3461        }
3462        SignedDuration::try_from(sp).and_then(UnsignedDuration::try_from)
3463    }
3464}
3465
3466/// Converts a [`std::time::Duration`] to a `Span`.
3467///
3468/// The span returned from this conversion will only ever have non-zero units
3469/// of seconds or smaller.
3470///
3471/// # Errors
3472///
3473/// This only fails when the given `Duration` overflows the maximum number of
3474/// seconds representable by a `Span`.
3475///
3476/// # Example
3477///
3478/// This shows a basic conversion:
3479///
3480/// ```
3481/// use std::time::Duration;
3482///
3483/// use jiff::{Span, ToSpan};
3484///
3485/// let duration = Duration::new(86_400, 123_456_789);
3486/// let span = Span::try_from(duration)?;
3487/// // A duration-to-span conversion always results in a span with
3488/// // non-zero units no bigger than seconds.
3489/// assert_eq!(
3490///     span.fieldwise(),
3491///     86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
3492/// );
3493///
3494/// # Ok::<(), Box<dyn std::error::Error>>(())
3495/// ```
3496///
3497/// # Example: rounding
3498///
3499/// This example shows how to convert a `Duration` to a `Span`, and then round
3500/// it up to bigger units given a relative date:
3501///
3502/// ```
3503/// use std::time::Duration;
3504///
3505/// use jiff::{civil::date, Span, SpanRound, ToSpan, Unit};
3506///
3507/// let duration = Duration::new(450 * 86_401, 0);
3508/// let span = Span::try_from(duration)?;
3509/// // We get back a simple span of just seconds:
3510/// assert_eq!(span.fieldwise(), Span::new().seconds(450 * 86_401));
3511/// // But we can balance it up to bigger units:
3512/// let options = SpanRound::new()
3513///     .largest(Unit::Year)
3514///     .relative(date(2024, 1, 1));
3515/// assert_eq!(
3516///     span.round(options)?,
3517///     1.year().months(2).days(25).minutes(7).seconds(30).fieldwise(),
3518/// );
3519///
3520/// # Ok::<(), Box<dyn std::error::Error>>(())
3521/// ```
3522impl TryFrom<UnsignedDuration> for Span {
3523    type Error = Error;
3524
3525    #[inline]
3526    fn try_from(d: UnsignedDuration) -> Result<Span, Error> {
3527        let seconds = i64::try_from(d.as_secs())
3528            .map_err(|_| Error::slim_range("unsigned duration seconds"))?;
3529        let nanoseconds = i64::from(d.subsec_nanos());
3530        let milliseconds = nanoseconds / t::NANOS_PER_MILLI.value();
3531        let microseconds = (nanoseconds % t::NANOS_PER_MILLI.value())
3532            / t::NANOS_PER_MICRO.value();
3533        let nanoseconds = nanoseconds % t::NANOS_PER_MICRO.value();
3534
3535        let span = Span::new().try_seconds(seconds)?;
3536        // These are all OK because `Duration::subsec_nanos` is guaranteed to
3537        // return less than 1_000_000_000 nanoseconds. And splitting that up
3538        // into millis, micros and nano components is guaranteed to fit into
3539        // the limits of a `Span`.
3540        Ok(span
3541            .milliseconds(milliseconds)
3542            .microseconds(microseconds)
3543            .nanoseconds(nanoseconds))
3544    }
3545}
3546
3547/// Converts a `Span` to a [`SignedDuration`].
3548///
3549/// # Errors
3550///
3551/// This can fail for only when the span has any non-zero units greater than
3552/// hours. This is an error because it's impossible to determine the length of,
3553/// e.g., a month without a reference date.
3554///
3555/// This can never result in overflow because a `SignedDuration` can represent
3556/// a bigger span of time than `Span` when limited to units of hours or lower.
3557///
3558/// If you need to convert a `Span` to a `SignedDuration` that has non-zero
3559/// units bigger than hours, then please use [`Span::to_duration`] with a
3560/// corresponding relative date.
3561///
3562/// # Example: maximal span
3563///
3564/// This example shows the maximum possible span using units of hours or
3565/// smaller, and the corresponding `SignedDuration` value:
3566///
3567/// ```
3568/// use jiff::{SignedDuration, Span};
3569///
3570/// let sp = Span::new()
3571///     .hours(175_307_616)
3572///     .minutes(10_518_456_960i64)
3573///     .seconds(631_107_417_600i64)
3574///     .milliseconds(631_107_417_600_000i64)
3575///     .microseconds(631_107_417_600_000_000i64)
3576///     .nanoseconds(9_223_372_036_854_775_807i64);
3577/// let duration = SignedDuration::try_from(sp)?;
3578/// assert_eq!(duration, SignedDuration::new(3_164_760_460_036, 854_775_807));
3579///
3580/// # Ok::<(), Box<dyn std::error::Error>>(())
3581/// ```
3582impl TryFrom<Span> for SignedDuration {
3583    type Error = Error;
3584
3585    #[inline]
3586    fn try_from(sp: Span) -> Result<SignedDuration, Error> {
3587        requires_relative_date_err(sp.largest_unit())
3588            .context(E::ConvertSpanToSignedDuration)?;
3589        Ok(sp.to_duration_invariant())
3590    }
3591}
3592
3593/// Converts a [`SignedDuration`] to a `Span`.
3594///
3595/// The span returned from this conversion will only ever have non-zero units
3596/// of seconds or smaller.
3597///
3598/// # Errors
3599///
3600/// This only fails when the given `SignedDuration` overflows the maximum
3601/// number of seconds representable by a `Span`.
3602///
3603/// # Example
3604///
3605/// This shows a basic conversion:
3606///
3607/// ```
3608/// use jiff::{SignedDuration, Span, ToSpan};
3609///
3610/// let duration = SignedDuration::new(86_400, 123_456_789);
3611/// let span = Span::try_from(duration)?;
3612/// // A duration-to-span conversion always results in a span with
3613/// // non-zero units no bigger than seconds.
3614/// assert_eq!(
3615///     span.fieldwise(),
3616///     86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
3617/// );
3618///
3619/// # Ok::<(), Box<dyn std::error::Error>>(())
3620/// ```
3621///
3622/// # Example: rounding
3623///
3624/// This example shows how to convert a `SignedDuration` to a `Span`, and then
3625/// round it up to bigger units given a relative date:
3626///
3627/// ```
3628/// use jiff::{civil::date, SignedDuration, Span, SpanRound, ToSpan, Unit};
3629///
3630/// let duration = SignedDuration::new(450 * 86_401, 0);
3631/// let span = Span::try_from(duration)?;
3632/// // We get back a simple span of just seconds:
3633/// assert_eq!(span.fieldwise(), Span::new().seconds(450 * 86_401));
3634/// // But we can balance it up to bigger units:
3635/// let options = SpanRound::new()
3636///     .largest(Unit::Year)
3637///     .relative(date(2024, 1, 1));
3638/// assert_eq!(
3639///     span.round(options)?,
3640///     1.year().months(2).days(25).minutes(7).seconds(30).fieldwise(),
3641/// );
3642///
3643/// # Ok::<(), Box<dyn std::error::Error>>(())
3644/// ```
3645impl TryFrom<SignedDuration> for Span {
3646    type Error = Error;
3647
3648    #[inline]
3649    fn try_from(d: SignedDuration) -> Result<Span, Error> {
3650        let seconds = d.as_secs();
3651        let nanoseconds = i64::from(d.subsec_nanos());
3652        let milliseconds = nanoseconds / t::NANOS_PER_MILLI.value();
3653        let microseconds = (nanoseconds % t::NANOS_PER_MILLI.value())
3654            / t::NANOS_PER_MICRO.value();
3655        let nanoseconds = nanoseconds % t::NANOS_PER_MICRO.value();
3656
3657        let span = Span::new().try_seconds(seconds)?;
3658        // These are all OK because `|SignedDuration::subsec_nanos|` is
3659        // guaranteed to return less than 1_000_000_000 nanoseconds. And
3660        // splitting that up into millis, micros and nano components is
3661        // guaranteed to fit into the limits of a `Span`.
3662        Ok(span
3663            .milliseconds(milliseconds)
3664            .microseconds(microseconds)
3665            .nanoseconds(nanoseconds))
3666    }
3667}
3668
3669#[cfg(feature = "serde")]
3670impl serde_core::Serialize for Span {
3671    #[inline]
3672    fn serialize<S: serde_core::Serializer>(
3673        &self,
3674        serializer: S,
3675    ) -> Result<S::Ok, S::Error> {
3676        serializer.collect_str(self)
3677    }
3678}
3679
3680#[cfg(feature = "serde")]
3681impl<'de> serde_core::Deserialize<'de> for Span {
3682    #[inline]
3683    fn deserialize<D: serde_core::Deserializer<'de>>(
3684        deserializer: D,
3685    ) -> Result<Span, D::Error> {
3686        use serde_core::de;
3687
3688        struct SpanVisitor;
3689
3690        impl<'de> de::Visitor<'de> for SpanVisitor {
3691            type Value = Span;
3692
3693            fn expecting(
3694                &self,
3695                f: &mut core::fmt::Formatter,
3696            ) -> core::fmt::Result {
3697                f.write_str("a span duration string")
3698            }
3699
3700            #[inline]
3701            fn visit_bytes<E: de::Error>(
3702                self,
3703                value: &[u8],
3704            ) -> Result<Span, E> {
3705                parse_iso_or_friendly(value).map_err(de::Error::custom)
3706            }
3707
3708            #[inline]
3709            fn visit_str<E: de::Error>(self, value: &str) -> Result<Span, E> {
3710                self.visit_bytes(value.as_bytes())
3711            }
3712        }
3713
3714        deserializer.deserialize_str(SpanVisitor)
3715    }
3716}
3717
3718#[cfg(test)]
3719impl quickcheck::Arbitrary for Span {
3720    fn arbitrary(g: &mut quickcheck::Gen) -> Span {
3721        // In order to sample from the full space of possible spans, we need
3722        // to provide a relative datetime. But if we do that, then it's
3723        // possible the span plus the datetime overflows. So we pick one
3724        // datetime and shrink the size of the span we can produce.
3725        type Nanos = ri64<-631_107_417_600_000_000, 631_107_417_600_000_000>;
3726        let nanos = Nanos::arbitrary(g).get();
3727        let relative =
3728            SpanRelativeTo::from(DateTime::constant(0, 1, 1, 0, 0, 0, 0));
3729        let round =
3730            SpanRound::new().largest(Unit::arbitrary(g)).relative(relative);
3731        Span::new().nanoseconds(nanos).round(round).unwrap()
3732    }
3733
3734    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
3735        alloc::boxed::Box::new(
3736            (
3737                (
3738                    self.get_years_ranged(),
3739                    self.get_months_ranged(),
3740                    self.get_weeks_ranged(),
3741                    self.get_days_ranged(),
3742                ),
3743                (
3744                    self.get_hours_ranged(),
3745                    self.get_minutes_ranged(),
3746                    self.get_seconds_ranged(),
3747                    self.get_milliseconds_ranged(),
3748                ),
3749                (
3750                    self.get_microseconds_ranged(),
3751                    self.get_nanoseconds_ranged(),
3752                ),
3753            )
3754                .shrink()
3755                .filter_map(
3756                    |(
3757                        (years, months, weeks, days),
3758                        (hours, minutes, seconds, milliseconds),
3759                        (microseconds, nanoseconds),
3760                    )| {
3761                        let span = Span::new()
3762                            .years_ranged(years)
3763                            .months_ranged(months)
3764                            .weeks_ranged(weeks)
3765                            .days_ranged(days)
3766                            .hours_ranged(hours)
3767                            .minutes_ranged(minutes)
3768                            .seconds_ranged(seconds)
3769                            .milliseconds_ranged(milliseconds)
3770                            .microseconds_ranged(microseconds)
3771                            .nanoseconds_ranged(nanoseconds);
3772                        Some(span)
3773                    },
3774                ),
3775        )
3776    }
3777}
3778
3779/// A wrapper for [`Span`] that implements the `Hash`, `Eq` and `PartialEq`
3780/// traits.
3781///
3782/// A `SpanFieldwise` is meant to make it easy to compare two spans in a "dumb"
3783/// way based purely on its unit values, while still providing a speed bump
3784/// to avoid accidentally doing this comparison on `Span` directly. This is
3785/// distinct from something like [`Span::compare`] that performs a comparison
3786/// on the actual elapsed time of two spans.
3787///
3788/// It is generally discouraged to use `SpanFieldwise` since spans that
3789/// represent an equivalent elapsed amount of time may compare unequal.
3790/// However, in some cases, it is useful to be able to assert precise field
3791/// values. For example, Jiff itself makes heavy use of fieldwise comparisons
3792/// for tests.
3793///
3794/// # Construction
3795///
3796/// While callers may use `SpanFieldwise(span)` (where `span` has type [`Span`])
3797/// to construct a value of this type, callers may find [`Span::fieldwise`]
3798/// more convenient. Namely, `Span::fieldwise` may avoid the need to explicitly
3799/// import `SpanFieldwise`.
3800///
3801/// # Trait implementations
3802///
3803/// In addition to implementing the `Hash`, `Eq` and `PartialEq` traits, this
3804/// type also provides `PartialEq` impls for comparing a `Span` with a
3805/// `SpanFieldwise`. This simplifies comparisons somewhat while still requiring
3806/// that at least one of the values has an explicit fieldwise comparison type.
3807///
3808/// # Safety
3809///
3810/// This type is guaranteed to have the same layout in memory as [`Span`].
3811///
3812/// # Example: the difference between `SpanFieldwise` and [`Span::compare`]
3813///
3814/// In short, `SpanFieldwise` considers `2 hours` and `120 minutes` to be
3815/// distinct values, but `Span::compare` considers them to be equivalent:
3816///
3817/// ```
3818/// use std::cmp::Ordering;
3819/// use jiff::ToSpan;
3820///
3821/// assert_ne!(120.minutes().fieldwise(), 2.hours().fieldwise());
3822/// assert_eq!(120.minutes().compare(2.hours())?, Ordering::Equal);
3823///
3824/// // These comparisons are allowed between a `Span` and a `SpanFieldwise`.
3825/// // Namely, as long as one value is "fieldwise," then the comparison is OK.
3826/// assert_ne!(120.minutes().fieldwise(), 2.hours());
3827/// assert_ne!(120.minutes(), 2.hours().fieldwise());
3828///
3829/// # Ok::<(), Box<dyn std::error::Error>>(())
3830/// ```
3831#[derive(Clone, Copy, Debug, Default)]
3832#[repr(transparent)]
3833pub struct SpanFieldwise(pub Span);
3834
3835// Exists so that things like `-1.day().fieldwise()` works as expected.
3836impl core::ops::Neg for SpanFieldwise {
3837    type Output = SpanFieldwise;
3838
3839    #[inline]
3840    fn neg(self) -> SpanFieldwise {
3841        SpanFieldwise(self.0.negate())
3842    }
3843}
3844
3845impl Eq for SpanFieldwise {}
3846
3847impl PartialEq for SpanFieldwise {
3848    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3849        self.0.sign == rhs.0.sign
3850            && self.0.years == rhs.0.years
3851            && self.0.months == rhs.0.months
3852            && self.0.weeks == rhs.0.weeks
3853            && self.0.days == rhs.0.days
3854            && self.0.hours == rhs.0.hours
3855            && self.0.minutes == rhs.0.minutes
3856            && self.0.seconds == rhs.0.seconds
3857            && self.0.milliseconds == rhs.0.milliseconds
3858            && self.0.microseconds == rhs.0.microseconds
3859            && self.0.nanoseconds == rhs.0.nanoseconds
3860    }
3861}
3862
3863impl<'a> PartialEq<SpanFieldwise> for &'a SpanFieldwise {
3864    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3865        *self == rhs
3866    }
3867}
3868
3869impl PartialEq<Span> for SpanFieldwise {
3870    fn eq(&self, rhs: &Span) -> bool {
3871        self == rhs.fieldwise()
3872    }
3873}
3874
3875impl PartialEq<SpanFieldwise> for Span {
3876    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3877        self.fieldwise() == *rhs
3878    }
3879}
3880
3881impl<'a> PartialEq<SpanFieldwise> for &'a Span {
3882    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3883        self.fieldwise() == *rhs
3884    }
3885}
3886
3887impl core::hash::Hash for SpanFieldwise {
3888    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
3889        self.0.sign.hash(state);
3890        self.0.years.hash(state);
3891        self.0.months.hash(state);
3892        self.0.weeks.hash(state);
3893        self.0.days.hash(state);
3894        self.0.hours.hash(state);
3895        self.0.minutes.hash(state);
3896        self.0.seconds.hash(state);
3897        self.0.milliseconds.hash(state);
3898        self.0.microseconds.hash(state);
3899        self.0.nanoseconds.hash(state);
3900    }
3901}
3902
3903impl From<Span> for SpanFieldwise {
3904    fn from(span: Span) -> SpanFieldwise {
3905        SpanFieldwise(span)
3906    }
3907}
3908
3909impl From<SpanFieldwise> for Span {
3910    fn from(span: SpanFieldwise) -> Span {
3911        span.0
3912    }
3913}
3914
3915/// A trait for enabling concise literals for creating [`Span`] values.
3916///
3917/// In short, this trait lets you write something like `5.seconds()` or
3918/// `1.day()` to create a [`Span`]. Once a `Span` has been created, you can
3919/// use its mutator methods to add more fields. For example,
3920/// `1.day().hours(10)` is equivalent to `Span::new().days(1).hours(10)`.
3921///
3922/// This trait is implemented for the following integer types: `i8`, `i16`,
3923/// `i32` and `i64`.
3924///
3925/// Note that this trait is provided as a convenience and should generally
3926/// only be used for literals in your source code. You should not use this
3927/// trait on numbers provided by end users. Namely, if the number provided
3928/// is not within Jiff's span limits, then these trait methods will panic.
3929/// Instead, use fallible mutator constructors like [`Span::try_days`]
3930/// or [`Span::try_seconds`].
3931///
3932/// # Example
3933///
3934/// ```
3935/// use jiff::ToSpan;
3936///
3937/// assert_eq!(5.days().to_string(), "P5D");
3938/// assert_eq!(5.days().hours(10).to_string(), "P5DT10H");
3939///
3940/// // Negation works and it doesn't matter where the sign goes. It can be
3941/// // applied to the span itself or to the integer.
3942/// assert_eq!((-5.days()).to_string(), "-P5D");
3943/// assert_eq!((-5).days().to_string(), "-P5D");
3944/// ```
3945///
3946/// # Example: alternative via span parsing
3947///
3948/// Another way of tersely building a `Span` value is by parsing a ISO 8601
3949/// duration string:
3950///
3951/// ```
3952/// use jiff::Span;
3953///
3954/// let span = "P5y2m15dT23h30m10s".parse::<Span>()?;
3955/// assert_eq!(
3956///     span.fieldwise(),
3957///     Span::new().years(5).months(2).days(15).hours(23).minutes(30).seconds(10),
3958/// );
3959///
3960/// # Ok::<(), Box<dyn std::error::Error>>(())
3961/// ```
3962pub trait ToSpan: Sized {
3963    /// Create a new span from this integer in units of years.
3964    ///
3965    /// # Panics
3966    ///
3967    /// When `Span::new().years(self)` would panic.
3968    fn years(self) -> Span;
3969
3970    /// Create a new span from this integer in units of months.
3971    ///
3972    /// # Panics
3973    ///
3974    /// When `Span::new().months(self)` would panic.
3975    fn months(self) -> Span;
3976
3977    /// Create a new span from this integer in units of weeks.
3978    ///
3979    /// # Panics
3980    ///
3981    /// When `Span::new().weeks(self)` would panic.
3982    fn weeks(self) -> Span;
3983
3984    /// Create a new span from this integer in units of days.
3985    ///
3986    /// # Panics
3987    ///
3988    /// When `Span::new().days(self)` would panic.
3989    fn days(self) -> Span;
3990
3991    /// Create a new span from this integer in units of hours.
3992    ///
3993    /// # Panics
3994    ///
3995    /// When `Span::new().hours(self)` would panic.
3996    fn hours(self) -> Span;
3997
3998    /// Create a new span from this integer in units of minutes.
3999    ///
4000    /// # Panics
4001    ///
4002    /// When `Span::new().minutes(self)` would panic.
4003    fn minutes(self) -> Span;
4004
4005    /// Create a new span from this integer in units of seconds.
4006    ///
4007    /// # Panics
4008    ///
4009    /// When `Span::new().seconds(self)` would panic.
4010    fn seconds(self) -> Span;
4011
4012    /// Create a new span from this integer in units of milliseconds.
4013    ///
4014    /// # Panics
4015    ///
4016    /// When `Span::new().milliseconds(self)` would panic.
4017    fn milliseconds(self) -> Span;
4018
4019    /// Create a new span from this integer in units of microseconds.
4020    ///
4021    /// # Panics
4022    ///
4023    /// When `Span::new().microseconds(self)` would panic.
4024    fn microseconds(self) -> Span;
4025
4026    /// Create a new span from this integer in units of nanoseconds.
4027    ///
4028    /// # Panics
4029    ///
4030    /// When `Span::new().nanoseconds(self)` would panic.
4031    fn nanoseconds(self) -> Span;
4032
4033    /// Equivalent to `years()`, but reads better for singular units.
4034    #[inline]
4035    fn year(self) -> Span {
4036        self.years()
4037    }
4038
4039    /// Equivalent to `months()`, but reads better for singular units.
4040    #[inline]
4041    fn month(self) -> Span {
4042        self.months()
4043    }
4044
4045    /// Equivalent to `weeks()`, but reads better for singular units.
4046    #[inline]
4047    fn week(self) -> Span {
4048        self.weeks()
4049    }
4050
4051    /// Equivalent to `days()`, but reads better for singular units.
4052    #[inline]
4053    fn day(self) -> Span {
4054        self.days()
4055    }
4056
4057    /// Equivalent to `hours()`, but reads better for singular units.
4058    #[inline]
4059    fn hour(self) -> Span {
4060        self.hours()
4061    }
4062
4063    /// Equivalent to `minutes()`, but reads better for singular units.
4064    #[inline]
4065    fn minute(self) -> Span {
4066        self.minutes()
4067    }
4068
4069    /// Equivalent to `seconds()`, but reads better for singular units.
4070    #[inline]
4071    fn second(self) -> Span {
4072        self.seconds()
4073    }
4074
4075    /// Equivalent to `milliseconds()`, but reads better for singular units.
4076    #[inline]
4077    fn millisecond(self) -> Span {
4078        self.milliseconds()
4079    }
4080
4081    /// Equivalent to `microseconds()`, but reads better for singular units.
4082    #[inline]
4083    fn microsecond(self) -> Span {
4084        self.microseconds()
4085    }
4086
4087    /// Equivalent to `nanoseconds()`, but reads better for singular units.
4088    #[inline]
4089    fn nanosecond(self) -> Span {
4090        self.nanoseconds()
4091    }
4092}
4093
4094macro_rules! impl_to_span {
4095    ($ty:ty) => {
4096        impl ToSpan for $ty {
4097            #[inline]
4098            fn years(self) -> Span {
4099                Span::new().years(self)
4100            }
4101            #[inline]
4102            fn months(self) -> Span {
4103                Span::new().months(self)
4104            }
4105            #[inline]
4106            fn weeks(self) -> Span {
4107                Span::new().weeks(self)
4108            }
4109            #[inline]
4110            fn days(self) -> Span {
4111                Span::new().days(self)
4112            }
4113            #[inline]
4114            fn hours(self) -> Span {
4115                Span::new().hours(self)
4116            }
4117            #[inline]
4118            fn minutes(self) -> Span {
4119                Span::new().minutes(self)
4120            }
4121            #[inline]
4122            fn seconds(self) -> Span {
4123                Span::new().seconds(self)
4124            }
4125            #[inline]
4126            fn milliseconds(self) -> Span {
4127                Span::new().milliseconds(self)
4128            }
4129            #[inline]
4130            fn microseconds(self) -> Span {
4131                Span::new().microseconds(self)
4132            }
4133            #[inline]
4134            fn nanoseconds(self) -> Span {
4135                Span::new().nanoseconds(self)
4136            }
4137        }
4138    };
4139}
4140
4141impl_to_span!(i8);
4142impl_to_span!(i16);
4143impl_to_span!(i32);
4144impl_to_span!(i64);
4145
4146/// A way to refer to a single calendar or clock unit.
4147///
4148/// This type is principally used in APIs involving a [`Span`], which is a
4149/// duration of time. For example, routines like [`Zoned::until`] permit
4150/// specifying the largest unit of the span returned:
4151///
4152/// ```
4153/// use jiff::{Unit, Zoned};
4154///
4155/// let zdt1: Zoned = "2024-07-06 17:40-04[America/New_York]".parse()?;
4156/// let zdt2: Zoned = "2024-11-05 08:00-05[America/New_York]".parse()?;
4157/// let span = zdt1.until((Unit::Year, &zdt2))?;
4158/// assert_eq!(format!("{span:#}"), "3mo 29d 14h 20m");
4159///
4160/// # Ok::<(), Box<dyn std::error::Error>>(())
4161/// ```
4162///
4163/// But a `Unit` is also used in APIs for rounding datetimes themselves:
4164///
4165/// ```
4166/// use jiff::{Unit, Zoned};
4167///
4168/// let zdt: Zoned = "2024-07-06 17:44:22.158-04[America/New_York]".parse()?;
4169/// let nearest_minute = zdt.round(Unit::Minute)?;
4170/// assert_eq!(
4171///     nearest_minute.to_string(),
4172///     "2024-07-06T17:44:00-04:00[America/New_York]",
4173/// );
4174///
4175/// # Ok::<(), Box<dyn std::error::Error>>(())
4176/// ```
4177///
4178/// # Example: ordering
4179///
4180/// This example demonstrates that `Unit` has an ordering defined such that
4181/// bigger units compare greater than smaller units.
4182///
4183/// ```
4184/// use jiff::Unit;
4185///
4186/// assert!(Unit::Year > Unit::Nanosecond);
4187/// assert!(Unit::Day > Unit::Hour);
4188/// assert!(Unit::Hour > Unit::Minute);
4189/// assert!(Unit::Hour > Unit::Minute);
4190/// assert_eq!(Unit::Hour, Unit::Hour);
4191/// ```
4192#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
4193pub enum Unit {
4194    /// A Gregorian calendar year. It usually has 365 days for non-leap years,
4195    /// and 366 days for leap years.
4196    Year = 9,
4197    /// A Gregorian calendar month. It usually has one of 28, 29, 30 or 31
4198    /// days.
4199    Month = 8,
4200    /// A week is 7 days that either begins on Sunday or Monday.
4201    Week = 7,
4202    /// A day is usually 24 hours, but some days may have different lengths
4203    /// due to time zone transitions.
4204    Day = 6,
4205    /// An hour is always 60 minutes.
4206    Hour = 5,
4207    /// A minute is always 60 seconds. (Jiff behaves as if leap seconds do not
4208    /// exist.)
4209    Minute = 4,
4210    /// A second is always 1,000 milliseconds.
4211    Second = 3,
4212    /// A millisecond is always 1,000 microseconds.
4213    Millisecond = 2,
4214    /// A microsecond is always 1,000 nanoseconds.
4215    Microsecond = 1,
4216    /// A nanosecond is the smallest granularity of time supported by Jiff.
4217    Nanosecond = 0,
4218}
4219
4220impl Unit {
4221    /// Returns the next biggest unit, if one exists.
4222    pub(crate) fn next(&self) -> Option<Unit> {
4223        match *self {
4224            Unit::Year => None,
4225            Unit::Month => Some(Unit::Year),
4226            Unit::Week => Some(Unit::Month),
4227            Unit::Day => Some(Unit::Week),
4228            Unit::Hour => Some(Unit::Day),
4229            Unit::Minute => Some(Unit::Hour),
4230            Unit::Second => Some(Unit::Minute),
4231            Unit::Millisecond => Some(Unit::Second),
4232            Unit::Microsecond => Some(Unit::Millisecond),
4233            Unit::Nanosecond => Some(Unit::Microsecond),
4234        }
4235    }
4236
4237    /*
4238    /// Returns the next smallest unit, if one exists.
4239    pub(crate) fn prev(&self) -> Option<Unit> {
4240        match *self {
4241            Unit::Year => Some(Unit::Month),
4242            Unit::Month => Some(Unit::Week),
4243            Unit::Week => Some(Unit::Day),
4244            Unit::Day => Some(Unit::Hour),
4245            Unit::Hour => Some(Unit::Minute),
4246            Unit::Minute => Some(Unit::Second),
4247            Unit::Second => Some(Unit::Millisecond),
4248            Unit::Millisecond => Some(Unit::Microsecond),
4249            Unit::Microsecond => Some(Unit::Nanosecond),
4250            Unit::Nanosecond => None,
4251        }
4252    }
4253    */
4254
4255    /// Returns the number of nanoseconds in this unit as a 128-bit integer.
4256    ///
4257    /// # Panics
4258    ///
4259    /// When this unit is always variable. That is, years or months.
4260    pub(crate) fn nanoseconds(self) -> NoUnits128 {
4261        match self {
4262            Unit::Nanosecond => Constant(1),
4263            Unit::Microsecond => t::NANOS_PER_MICRO,
4264            Unit::Millisecond => t::NANOS_PER_MILLI,
4265            Unit::Second => t::NANOS_PER_SECOND,
4266            Unit::Minute => t::NANOS_PER_MINUTE,
4267            Unit::Hour => t::NANOS_PER_HOUR,
4268            Unit::Day => t::NANOS_PER_CIVIL_DAY,
4269            Unit::Week => t::NANOS_PER_CIVIL_WEEK,
4270            unit => unreachable!("{unit:?} has no definitive time interval"),
4271        }
4272        .rinto()
4273    }
4274
4275    /// Returns true when this unit is definitively variable.
4276    ///
4277    /// In effect, this is any unit bigger than 'day', because any such unit
4278    /// can vary in time depending on its reference point. A 'day' can as well,
4279    /// but we sorta special case 'day' to mean '24 hours' for cases where
4280    /// the user is dealing with civil time.
4281    fn is_variable(self) -> bool {
4282        matches!(self, Unit::Year | Unit::Month | Unit::Week | Unit::Day)
4283    }
4284
4285    /// A human readable singular description of this unit of time.
4286    pub(crate) fn singular(&self) -> &'static str {
4287        match *self {
4288            Unit::Year => "year",
4289            Unit::Month => "month",
4290            Unit::Week => "week",
4291            Unit::Day => "day",
4292            Unit::Hour => "hour",
4293            Unit::Minute => "minute",
4294            Unit::Second => "second",
4295            Unit::Millisecond => "millisecond",
4296            Unit::Microsecond => "microsecond",
4297            Unit::Nanosecond => "nanosecond",
4298        }
4299    }
4300
4301    /// A human readable plural description of this unit of time.
4302    pub(crate) fn plural(&self) -> &'static str {
4303        match *self {
4304            Unit::Year => "years",
4305            Unit::Month => "months",
4306            Unit::Week => "weeks",
4307            Unit::Day => "days",
4308            Unit::Hour => "hours",
4309            Unit::Minute => "minutes",
4310            Unit::Second => "seconds",
4311            Unit::Millisecond => "milliseconds",
4312            Unit::Microsecond => "microseconds",
4313            Unit::Nanosecond => "nanoseconds",
4314        }
4315    }
4316
4317    /// A very succinct label corresponding to this unit.
4318    pub(crate) fn compact(&self) -> &'static str {
4319        match *self {
4320            Unit::Year => "y",
4321            Unit::Month => "mo",
4322            Unit::Week => "w",
4323            Unit::Day => "d",
4324            Unit::Hour => "h",
4325            Unit::Minute => "m",
4326            Unit::Second => "s",
4327            Unit::Millisecond => "ms",
4328            Unit::Microsecond => "µs",
4329            Unit::Nanosecond => "ns",
4330        }
4331    }
4332
4333    /// Return this unit as a `usize`.
4334    ///
4335    /// This is use `unit as usize`.
4336    pub(crate) fn as_usize(&self) -> usize {
4337        *self as usize
4338    }
4339
4340    /// The inverse of `unit as usize`.
4341    fn from_usize(n: usize) -> Option<Unit> {
4342        match n {
4343            0 => Some(Unit::Nanosecond),
4344            1 => Some(Unit::Microsecond),
4345            2 => Some(Unit::Millisecond),
4346            3 => Some(Unit::Second),
4347            4 => Some(Unit::Minute),
4348            5 => Some(Unit::Hour),
4349            6 => Some(Unit::Day),
4350            7 => Some(Unit::Week),
4351            8 => Some(Unit::Month),
4352            9 => Some(Unit::Year),
4353            _ => None,
4354        }
4355    }
4356}
4357
4358#[cfg(test)]
4359impl quickcheck::Arbitrary for Unit {
4360    fn arbitrary(g: &mut quickcheck::Gen) -> Unit {
4361        Unit::from_usize(usize::arbitrary(g) % 10).unwrap()
4362    }
4363
4364    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
4365        alloc::boxed::Box::new(
4366            (*self as usize)
4367                .shrink()
4368                .map(|n| Unit::from_usize(n % 10).unwrap()),
4369        )
4370    }
4371}
4372
4373/// Options for [`Span::checked_add`] and [`Span::checked_sub`].
4374///
4375/// This type provides a way to ergonomically add two spans with an optional
4376/// relative datetime. Namely, a relative datetime is only needed when at least
4377/// one of the two spans being added (or subtracted) has a non-zero calendar
4378/// unit (years, months, weeks or days). Otherwise, an error will be returned.
4379///
4380/// Callers may use [`SpanArithmetic::days_are_24_hours`] to opt into 24-hour
4381/// invariant days (and 7-day weeks) without providing a relative datetime.
4382///
4383/// The main way to construct values of this type is with its `From` trait
4384/// implementations:
4385///
4386/// * `From<Span> for SpanArithmetic` adds (or subtracts) the given span to the
4387/// receiver in [`Span::checked_add`] (or [`Span::checked_sub`]).
4388/// * `From<(Span, civil::Date)> for SpanArithmetic` adds (or subtracts)
4389/// the given span to the receiver in [`Span::checked_add`] (or
4390/// [`Span::checked_sub`]), relative to the given date. There are also `From`
4391/// implementations for `civil::DateTime`, `Zoned` and [`SpanRelativeTo`].
4392///
4393/// # Example
4394///
4395/// ```
4396/// use jiff::ToSpan;
4397///
4398/// assert_eq!(
4399///     1.hour().checked_add(30.minutes())?,
4400///     1.hour().minutes(30).fieldwise(),
4401/// );
4402///
4403/// # Ok::<(), Box<dyn std::error::Error>>(())
4404/// ```
4405#[derive(Clone, Copy, Debug)]
4406pub struct SpanArithmetic<'a> {
4407    duration: Duration,
4408    relative: Option<SpanRelativeTo<'a>>,
4409}
4410
4411impl<'a> SpanArithmetic<'a> {
4412    /// This is a convenience function for setting the relative option on
4413    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4414    ///
4415    /// # Example
4416    ///
4417    /// When doing arithmetic on spans involving days, either a relative
4418    /// datetime must be provided, or a special assertion opting into 24-hour
4419    /// days is required. Otherwise, you get an error.
4420    ///
4421    /// ```
4422    /// use jiff::{SpanArithmetic, ToSpan};
4423    ///
4424    /// let span1 = 2.days().hours(12);
4425    /// let span2 = 12.hours();
4426    /// // No relative date provided, which results in an error.
4427    /// assert_eq!(
4428    ///     span1.checked_add(span2).unwrap_err().to_string(),
4429    ///     "using unit 'day' in a span or configuration requires that \
4430    ///      either a relative reference time be given or \
4431    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
4432    ///      invariant 24-hour days, but neither were provided",
4433    /// );
4434    /// let sum = span1.checked_add(
4435    ///     SpanArithmetic::from(span2).days_are_24_hours(),
4436    /// )?;
4437    /// assert_eq!(sum, 3.days().fieldwise());
4438    ///
4439    /// # Ok::<(), Box<dyn std::error::Error>>(())
4440    /// ```
4441    #[inline]
4442    pub fn days_are_24_hours(self) -> SpanArithmetic<'a> {
4443        self.relative(SpanRelativeTo::days_are_24_hours())
4444    }
4445}
4446
4447impl<'a> SpanArithmetic<'a> {
4448    #[inline]
4449    fn relative<R: Into<SpanRelativeTo<'a>>>(
4450        self,
4451        relative: R,
4452    ) -> SpanArithmetic<'a> {
4453        SpanArithmetic { relative: Some(relative.into()), ..self }
4454    }
4455
4456    #[inline]
4457    fn checked_add(self, span1: Span) -> Result<Span, Error> {
4458        match self.duration.to_signed()? {
4459            SDuration::Span(span2) => {
4460                span1.checked_add_span(self.relative, &span2)
4461            }
4462            SDuration::Absolute(dur2) => {
4463                span1.checked_add_duration(self.relative, dur2)
4464            }
4465        }
4466    }
4467}
4468
4469impl From<Span> for SpanArithmetic<'static> {
4470    fn from(span: Span) -> SpanArithmetic<'static> {
4471        let duration = Duration::from(span);
4472        SpanArithmetic { duration, relative: None }
4473    }
4474}
4475
4476impl<'a> From<&'a Span> for SpanArithmetic<'static> {
4477    fn from(span: &'a Span) -> SpanArithmetic<'static> {
4478        let duration = Duration::from(*span);
4479        SpanArithmetic { duration, relative: None }
4480    }
4481}
4482
4483impl From<(Span, Date)> for SpanArithmetic<'static> {
4484    #[inline]
4485    fn from((span, date): (Span, Date)) -> SpanArithmetic<'static> {
4486        SpanArithmetic::from(span).relative(date)
4487    }
4488}
4489
4490impl From<(Span, DateTime)> for SpanArithmetic<'static> {
4491    #[inline]
4492    fn from((span, datetime): (Span, DateTime)) -> SpanArithmetic<'static> {
4493        SpanArithmetic::from(span).relative(datetime)
4494    }
4495}
4496
4497impl<'a> From<(Span, &'a Zoned)> for SpanArithmetic<'a> {
4498    #[inline]
4499    fn from((span, zoned): (Span, &'a Zoned)) -> SpanArithmetic<'a> {
4500        SpanArithmetic::from(span).relative(zoned)
4501    }
4502}
4503
4504impl<'a> From<(Span, SpanRelativeTo<'a>)> for SpanArithmetic<'a> {
4505    #[inline]
4506    fn from(
4507        (span, relative): (Span, SpanRelativeTo<'a>),
4508    ) -> SpanArithmetic<'a> {
4509        SpanArithmetic::from(span).relative(relative)
4510    }
4511}
4512
4513impl<'a> From<(&'a Span, Date)> for SpanArithmetic<'static> {
4514    #[inline]
4515    fn from((span, date): (&'a Span, Date)) -> SpanArithmetic<'static> {
4516        SpanArithmetic::from(span).relative(date)
4517    }
4518}
4519
4520impl<'a> From<(&'a Span, DateTime)> for SpanArithmetic<'static> {
4521    #[inline]
4522    fn from(
4523        (span, datetime): (&'a Span, DateTime),
4524    ) -> SpanArithmetic<'static> {
4525        SpanArithmetic::from(span).relative(datetime)
4526    }
4527}
4528
4529impl<'a, 'b> From<(&'a Span, &'b Zoned)> for SpanArithmetic<'b> {
4530    #[inline]
4531    fn from((span, zoned): (&'a Span, &'b Zoned)) -> SpanArithmetic<'b> {
4532        SpanArithmetic::from(span).relative(zoned)
4533    }
4534}
4535
4536impl<'a, 'b> From<(&'a Span, SpanRelativeTo<'b>)> for SpanArithmetic<'b> {
4537    #[inline]
4538    fn from(
4539        (span, relative): (&'a Span, SpanRelativeTo<'b>),
4540    ) -> SpanArithmetic<'b> {
4541        SpanArithmetic::from(span).relative(relative)
4542    }
4543}
4544
4545impl From<SignedDuration> for SpanArithmetic<'static> {
4546    fn from(duration: SignedDuration) -> SpanArithmetic<'static> {
4547        let duration = Duration::from(duration);
4548        SpanArithmetic { duration, relative: None }
4549    }
4550}
4551
4552impl From<(SignedDuration, Date)> for SpanArithmetic<'static> {
4553    #[inline]
4554    fn from(
4555        (duration, date): (SignedDuration, Date),
4556    ) -> SpanArithmetic<'static> {
4557        SpanArithmetic::from(duration).relative(date)
4558    }
4559}
4560
4561impl From<(SignedDuration, DateTime)> for SpanArithmetic<'static> {
4562    #[inline]
4563    fn from(
4564        (duration, datetime): (SignedDuration, DateTime),
4565    ) -> SpanArithmetic<'static> {
4566        SpanArithmetic::from(duration).relative(datetime)
4567    }
4568}
4569
4570impl<'a> From<(SignedDuration, &'a Zoned)> for SpanArithmetic<'a> {
4571    #[inline]
4572    fn from(
4573        (duration, zoned): (SignedDuration, &'a Zoned),
4574    ) -> SpanArithmetic<'a> {
4575        SpanArithmetic::from(duration).relative(zoned)
4576    }
4577}
4578
4579impl From<UnsignedDuration> for SpanArithmetic<'static> {
4580    fn from(duration: UnsignedDuration) -> SpanArithmetic<'static> {
4581        let duration = Duration::from(duration);
4582        SpanArithmetic { duration, relative: None }
4583    }
4584}
4585
4586impl From<(UnsignedDuration, Date)> for SpanArithmetic<'static> {
4587    #[inline]
4588    fn from(
4589        (duration, date): (UnsignedDuration, Date),
4590    ) -> SpanArithmetic<'static> {
4591        SpanArithmetic::from(duration).relative(date)
4592    }
4593}
4594
4595impl From<(UnsignedDuration, DateTime)> for SpanArithmetic<'static> {
4596    #[inline]
4597    fn from(
4598        (duration, datetime): (UnsignedDuration, DateTime),
4599    ) -> SpanArithmetic<'static> {
4600        SpanArithmetic::from(duration).relative(datetime)
4601    }
4602}
4603
4604impl<'a> From<(UnsignedDuration, &'a Zoned)> for SpanArithmetic<'a> {
4605    #[inline]
4606    fn from(
4607        (duration, zoned): (UnsignedDuration, &'a Zoned),
4608    ) -> SpanArithmetic<'a> {
4609        SpanArithmetic::from(duration).relative(zoned)
4610    }
4611}
4612
4613/// Options for [`Span::compare`].
4614///
4615/// This type provides a way to ergonomically compare two spans with an
4616/// optional relative datetime. Namely, a relative datetime is only needed when
4617/// at least one of the two spans being compared has a non-zero calendar unit
4618/// (years, months, weeks or days). Otherwise, an error will be returned.
4619///
4620/// Callers may use [`SpanCompare::days_are_24_hours`] to opt into 24-hour
4621/// invariant days (and 7-day weeks) without providing a relative datetime.
4622///
4623/// The main way to construct values of this type is with its `From` trait
4624/// implementations:
4625///
4626/// * `From<Span> for SpanCompare` compares the given span to the receiver
4627/// in [`Span::compare`].
4628/// * `From<(Span, civil::Date)> for SpanCompare` compares the given span
4629/// to the receiver in [`Span::compare`], relative to the given date. There
4630/// are also `From` implementations for `civil::DateTime`, `Zoned` and
4631/// [`SpanRelativeTo`].
4632///
4633/// # Example
4634///
4635/// ```
4636/// use jiff::ToSpan;
4637///
4638/// let span1 = 3.hours();
4639/// let span2 = 180.minutes();
4640/// assert_eq!(span1.compare(span2)?, std::cmp::Ordering::Equal);
4641///
4642/// # Ok::<(), Box<dyn std::error::Error>>(())
4643/// ```
4644#[derive(Clone, Copy, Debug)]
4645pub struct SpanCompare<'a> {
4646    span: Span,
4647    relative: Option<SpanRelativeTo<'a>>,
4648}
4649
4650impl<'a> SpanCompare<'a> {
4651    /// This is a convenience function for setting the relative option on
4652    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4653    ///
4654    /// # Example
4655    ///
4656    /// When comparing spans involving days, either a relative datetime must be
4657    /// provided, or a special assertion opting into 24-hour days is
4658    /// required. Otherwise, you get an error.
4659    ///
4660    /// ```
4661    /// use jiff::{SpanCompare, ToSpan};
4662    ///
4663    /// let span1 = 2.days().hours(12);
4664    /// let span2 = 60.hours();
4665    /// // No relative date provided, which results in an error.
4666    /// assert_eq!(
4667    ///     span1.compare(span2).unwrap_err().to_string(),
4668    ///     "using unit 'day' in a span or configuration requires that \
4669    ///      either a relative reference time be given or \
4670    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
4671    ///      invariant 24-hour days, but neither were provided",
4672    /// );
4673    /// let ordering = span1.compare(
4674    ///     SpanCompare::from(span2).days_are_24_hours(),
4675    /// )?;
4676    /// assert_eq!(ordering, std::cmp::Ordering::Equal);
4677    ///
4678    /// # Ok::<(), Box<dyn std::error::Error>>(())
4679    /// ```
4680    #[inline]
4681    pub fn days_are_24_hours(self) -> SpanCompare<'a> {
4682        self.relative(SpanRelativeTo::days_are_24_hours())
4683    }
4684}
4685
4686impl<'a> SpanCompare<'a> {
4687    #[inline]
4688    fn new(span: Span) -> SpanCompare<'static> {
4689        SpanCompare { span, relative: None }
4690    }
4691
4692    #[inline]
4693    fn relative<R: Into<SpanRelativeTo<'a>>>(
4694        self,
4695        relative: R,
4696    ) -> SpanCompare<'a> {
4697        SpanCompare { relative: Some(relative.into()), ..self }
4698    }
4699
4700    fn compare(self, span: Span) -> Result<Ordering, Error> {
4701        let (span1, span2) = (span, self.span);
4702        let unit = span1.largest_unit().max(span2.largest_unit());
4703        let start = match self.relative {
4704            Some(r) => match r.to_relative(unit)? {
4705                Some(r) => r,
4706                None => {
4707                    let nanos1 = span1.to_invariant_nanoseconds();
4708                    let nanos2 = span2.to_invariant_nanoseconds();
4709                    return Ok(nanos1.cmp(&nanos2));
4710                }
4711            },
4712            None => {
4713                requires_relative_date_err(unit)?;
4714                let nanos1 = span1.to_invariant_nanoseconds();
4715                let nanos2 = span2.to_invariant_nanoseconds();
4716                return Ok(nanos1.cmp(&nanos2));
4717            }
4718        };
4719        let end1 = start.checked_add(span1)?.to_nanosecond();
4720        let end2 = start.checked_add(span2)?.to_nanosecond();
4721        Ok(end1.cmp(&end2))
4722    }
4723}
4724
4725impl From<Span> for SpanCompare<'static> {
4726    fn from(span: Span) -> SpanCompare<'static> {
4727        SpanCompare::new(span)
4728    }
4729}
4730
4731impl<'a> From<&'a Span> for SpanCompare<'static> {
4732    fn from(span: &'a Span) -> SpanCompare<'static> {
4733        SpanCompare::new(*span)
4734    }
4735}
4736
4737impl From<(Span, Date)> for SpanCompare<'static> {
4738    #[inline]
4739    fn from((span, date): (Span, Date)) -> SpanCompare<'static> {
4740        SpanCompare::from(span).relative(date)
4741    }
4742}
4743
4744impl From<(Span, DateTime)> for SpanCompare<'static> {
4745    #[inline]
4746    fn from((span, datetime): (Span, DateTime)) -> SpanCompare<'static> {
4747        SpanCompare::from(span).relative(datetime)
4748    }
4749}
4750
4751impl<'a> From<(Span, &'a Zoned)> for SpanCompare<'a> {
4752    #[inline]
4753    fn from((span, zoned): (Span, &'a Zoned)) -> SpanCompare<'a> {
4754        SpanCompare::from(span).relative(zoned)
4755    }
4756}
4757
4758impl<'a> From<(Span, SpanRelativeTo<'a>)> for SpanCompare<'a> {
4759    #[inline]
4760    fn from((span, relative): (Span, SpanRelativeTo<'a>)) -> SpanCompare<'a> {
4761        SpanCompare::from(span).relative(relative)
4762    }
4763}
4764
4765impl<'a> From<(&'a Span, Date)> for SpanCompare<'static> {
4766    #[inline]
4767    fn from((span, date): (&'a Span, Date)) -> SpanCompare<'static> {
4768        SpanCompare::from(span).relative(date)
4769    }
4770}
4771
4772impl<'a> From<(&'a Span, DateTime)> for SpanCompare<'static> {
4773    #[inline]
4774    fn from((span, datetime): (&'a Span, DateTime)) -> SpanCompare<'static> {
4775        SpanCompare::from(span).relative(datetime)
4776    }
4777}
4778
4779impl<'a, 'b> From<(&'a Span, &'b Zoned)> for SpanCompare<'b> {
4780    #[inline]
4781    fn from((span, zoned): (&'a Span, &'b Zoned)) -> SpanCompare<'b> {
4782        SpanCompare::from(span).relative(zoned)
4783    }
4784}
4785
4786impl<'a, 'b> From<(&'a Span, SpanRelativeTo<'b>)> for SpanCompare<'b> {
4787    #[inline]
4788    fn from(
4789        (span, relative): (&'a Span, SpanRelativeTo<'b>),
4790    ) -> SpanCompare<'b> {
4791        SpanCompare::from(span).relative(relative)
4792    }
4793}
4794
4795/// Options for [`Span::total`].
4796///
4797/// This type provides a way to ergonomically determine the number of a
4798/// particular unit in a span, with a potentially fractional component, with
4799/// an optional relative datetime. Namely, a relative datetime is only needed
4800/// when the span has a non-zero calendar unit (years, months, weeks or days).
4801/// Otherwise, an error will be returned.
4802///
4803/// Callers may use [`SpanTotal::days_are_24_hours`] to opt into 24-hour
4804/// invariant days (and 7-day weeks) without providing a relative datetime.
4805///
4806/// The main way to construct values of this type is with its `From` trait
4807/// implementations:
4808///
4809/// * `From<Unit> for SpanTotal` computes a total for the given unit in the
4810/// receiver span for [`Span::total`].
4811/// * `From<(Unit, civil::Date)> for SpanTotal` computes a total for the given
4812/// unit in the receiver span for [`Span::total`], relative to the given date.
4813/// There are also `From` implementations for `civil::DateTime`, `Zoned` and
4814/// [`SpanRelativeTo`].
4815///
4816/// # Example
4817///
4818/// This example shows how to find the number of seconds in a particular span:
4819///
4820/// ```
4821/// use jiff::{ToSpan, Unit};
4822///
4823/// let span = 3.hours().minutes(10);
4824/// assert_eq!(span.total(Unit::Second)?, 11_400.0);
4825///
4826/// # Ok::<(), Box<dyn std::error::Error>>(())
4827/// ```
4828///
4829/// # Example: 24 hour days
4830///
4831/// This shows how to find the total number of 24 hour days in `123,456,789`
4832/// seconds.
4833///
4834/// ```
4835/// use jiff::{SpanTotal, ToSpan, Unit};
4836///
4837/// let span = 123_456_789.seconds();
4838/// assert_eq!(
4839///     span.total(SpanTotal::from(Unit::Day).days_are_24_hours())?,
4840///     1428.8980208333332,
4841/// );
4842///
4843/// # Ok::<(), Box<dyn std::error::Error>>(())
4844/// ```
4845///
4846/// # Example: DST is taken into account
4847///
4848/// The month of March 2024 in `America/New_York` had 31 days, but one of those
4849/// days was 23 hours long due a transition into daylight saving time:
4850///
4851/// ```
4852/// use jiff::{civil::date, ToSpan, Unit};
4853///
4854/// let span = 744.hours();
4855/// let relative = date(2024, 3, 1).in_tz("America/New_York")?;
4856/// // Because of the short day, 744 hours is actually a little *more* than
4857/// // 1 month starting from 2024-03-01.
4858/// assert_eq!(span.total((Unit::Month, &relative))?, 1.0013888888888889);
4859///
4860/// # Ok::<(), Box<dyn std::error::Error>>(())
4861/// ```
4862///
4863/// Now compare what happens when the relative datetime is civil and not
4864/// time zone aware:
4865///
4866/// ```
4867/// use jiff::{civil::date, ToSpan, Unit};
4868///
4869/// let span = 744.hours();
4870/// let relative = date(2024, 3, 1);
4871/// assert_eq!(span.total((Unit::Month, relative))?, 1.0);
4872///
4873/// # Ok::<(), Box<dyn std::error::Error>>(())
4874/// ```
4875#[derive(Clone, Copy, Debug)]
4876pub struct SpanTotal<'a> {
4877    unit: Unit,
4878    relative: Option<SpanRelativeTo<'a>>,
4879}
4880
4881impl<'a> SpanTotal<'a> {
4882    /// This is a convenience function for setting the relative option on
4883    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4884    ///
4885    /// # Example
4886    ///
4887    /// When computing the total duration for spans involving days, either a
4888    /// relative datetime must be provided, or a special assertion opting into
4889    /// 24-hour days is required. Otherwise, you get an error.
4890    ///
4891    /// ```
4892    /// use jiff::{civil::date, SpanTotal, ToSpan, Unit};
4893    ///
4894    /// let span = 2.days().hours(12);
4895    ///
4896    /// // No relative date provided, which results in an error.
4897    /// assert_eq!(
4898    ///     span.total(Unit::Hour).unwrap_err().to_string(),
4899    ///     "using unit 'day' in a span or configuration requires that either \
4900    ///      a relative reference time be given or \
4901    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
4902    ///      invariant 24-hour days, but neither were provided",
4903    /// );
4904    ///
4905    /// // If we can assume all days are 24 hours, then we can assert it:
4906    /// let total = span.total(
4907    ///     SpanTotal::from(Unit::Hour).days_are_24_hours(),
4908    /// )?;
4909    /// assert_eq!(total, 60.0);
4910    ///
4911    /// // Or provide a relative datetime, which is preferred if possible:
4912    /// let total = span.total((Unit::Hour, date(2025, 1, 26)))?;
4913    /// assert_eq!(total, 60.0);
4914    ///
4915    /// # Ok::<(), Box<dyn std::error::Error>>(())
4916    /// ```
4917    #[inline]
4918    pub fn days_are_24_hours(self) -> SpanTotal<'a> {
4919        self.relative(SpanRelativeTo::days_are_24_hours())
4920    }
4921}
4922
4923impl<'a> SpanTotal<'a> {
4924    #[inline]
4925    fn new(unit: Unit) -> SpanTotal<'static> {
4926        SpanTotal { unit, relative: None }
4927    }
4928
4929    #[inline]
4930    fn relative<R: Into<SpanRelativeTo<'a>>>(
4931        self,
4932        relative: R,
4933    ) -> SpanTotal<'a> {
4934        SpanTotal { relative: Some(relative.into()), ..self }
4935    }
4936
4937    fn total(self, span: Span) -> Result<f64, Error> {
4938        let max_unit = self.unit.max(span.largest_unit());
4939        let relative = match self.relative {
4940            Some(r) => match r.to_relative(max_unit)? {
4941                Some(r) => r,
4942                None => {
4943                    return Ok(self.total_invariant(span));
4944                }
4945            },
4946            None => {
4947                requires_relative_date_err(max_unit)?;
4948                return Ok(self.total_invariant(span));
4949            }
4950        };
4951        let relspan = relative.into_relative_span(self.unit, span)?;
4952        if !self.unit.is_variable() {
4953            return Ok(self.total_invariant(relspan.span));
4954        }
4955
4956        assert!(self.unit >= Unit::Day);
4957        let sign = relspan.span.get_sign_ranged();
4958        let (relative_start, relative_end) = match relspan.kind {
4959            RelativeSpanKind::Civil { start, end } => {
4960                let start = Relative::Civil(start);
4961                let end = Relative::Civil(end);
4962                (start, end)
4963            }
4964            RelativeSpanKind::Zoned { start, end } => {
4965                let start = Relative::Zoned(start);
4966                let end = Relative::Zoned(end);
4967                (start, end)
4968            }
4969        };
4970        let (relative0, relative1) = clamp_relative_span(
4971            &relative_start,
4972            relspan.span.without_lower(self.unit),
4973            self.unit,
4974            sign.rinto(),
4975        )?;
4976        let denom = (relative1 - relative0).get() as f64;
4977        let numer = (relative_end.to_nanosecond() - relative0).get() as f64;
4978        let unit_val = relspan.span.get_units_ranged(self.unit).get() as f64;
4979        Ok(unit_val + (numer / denom) * (sign.get() as f64))
4980    }
4981
4982    #[inline]
4983    fn total_invariant(&self, span: Span) -> f64 {
4984        assert!(self.unit <= Unit::Week);
4985        let nanos = span.to_invariant_nanoseconds();
4986        (nanos.get() as f64) / (self.unit.nanoseconds().get() as f64)
4987    }
4988}
4989
4990impl From<Unit> for SpanTotal<'static> {
4991    #[inline]
4992    fn from(unit: Unit) -> SpanTotal<'static> {
4993        SpanTotal::new(unit)
4994    }
4995}
4996
4997impl From<(Unit, Date)> for SpanTotal<'static> {
4998    #[inline]
4999    fn from((unit, date): (Unit, Date)) -> SpanTotal<'static> {
5000        SpanTotal::from(unit).relative(date)
5001    }
5002}
5003
5004impl From<(Unit, DateTime)> for SpanTotal<'static> {
5005    #[inline]
5006    fn from((unit, datetime): (Unit, DateTime)) -> SpanTotal<'static> {
5007        SpanTotal::from(unit).relative(datetime)
5008    }
5009}
5010
5011impl<'a> From<(Unit, &'a Zoned)> for SpanTotal<'a> {
5012    #[inline]
5013    fn from((unit, zoned): (Unit, &'a Zoned)) -> SpanTotal<'a> {
5014        SpanTotal::from(unit).relative(zoned)
5015    }
5016}
5017
5018impl<'a> From<(Unit, SpanRelativeTo<'a>)> for SpanTotal<'a> {
5019    #[inline]
5020    fn from((unit, relative): (Unit, SpanRelativeTo<'a>)) -> SpanTotal<'a> {
5021        SpanTotal::from(unit).relative(relative)
5022    }
5023}
5024
5025/// Options for [`Span::round`].
5026///
5027/// This type provides a way to configure the rounding of a span. This
5028/// includes setting the smallest unit (i.e., the unit to round), the
5029/// largest unit, the rounding increment, the rounding mode (e.g., "ceil" or
5030/// "truncate") and the datetime that the span is relative to.
5031///
5032/// `Span::round` accepts anything that implements `Into<SpanRound>`. There are
5033/// a few key trait implementations that make this convenient:
5034///
5035/// * `From<Unit> for SpanRound` will construct a rounding configuration where
5036/// the smallest unit is set to the one given.
5037/// * `From<(Unit, i64)> for SpanRound` will construct a rounding configuration
5038/// where the smallest unit and the rounding increment are set to the ones
5039/// given.
5040///
5041/// In order to set other options (like the largest unit, the rounding mode
5042/// and the relative datetime), one must explicitly create a `SpanRound` and
5043/// pass it to `Span::round`.
5044///
5045/// # Example
5046///
5047/// This example shows how to find how many full 3 month quarters are in a
5048/// particular span of time.
5049///
5050/// ```
5051/// use jiff::{civil::date, RoundMode, SpanRound, ToSpan, Unit};
5052///
5053/// let span1 = 10.months().days(15);
5054/// let round = SpanRound::new()
5055///     .smallest(Unit::Month)
5056///     .increment(3)
5057///     .mode(RoundMode::Trunc)
5058///     // A relative datetime must be provided when
5059///     // rounding involves calendar units.
5060///     .relative(date(2024, 1, 1));
5061/// let span2 = span1.round(round)?;
5062/// assert_eq!(span2.get_months() / 3, 3);
5063///
5064/// # Ok::<(), Box<dyn std::error::Error>>(())
5065/// ```
5066#[derive(Clone, Copy, Debug)]
5067pub struct SpanRound<'a> {
5068    largest: Option<Unit>,
5069    smallest: Unit,
5070    mode: RoundMode,
5071    increment: i64,
5072    relative: Option<SpanRelativeTo<'a>>,
5073}
5074
5075impl<'a> SpanRound<'a> {
5076    /// Create a new default configuration for rounding a span via
5077    /// [`Span::round`].
5078    ///
5079    /// The default configuration does no rounding.
5080    #[inline]
5081    pub fn new() -> SpanRound<'static> {
5082        SpanRound {
5083            largest: None,
5084            smallest: Unit::Nanosecond,
5085            mode: RoundMode::HalfExpand,
5086            increment: 1,
5087            relative: None,
5088        }
5089    }
5090
5091    /// Set the smallest units allowed in the span returned. These are the
5092    /// units that the span is rounded to.
5093    ///
5094    /// # Errors
5095    ///
5096    /// The smallest units must be no greater than the largest units. If this
5097    /// is violated, then rounding a span with this configuration will result
5098    /// in an error.
5099    ///
5100    /// If a smallest unit bigger than days is selected without a relative
5101    /// datetime reference point, then an error is returned when using this
5102    /// configuration with [`Span::round`].
5103    ///
5104    /// # Example
5105    ///
5106    /// A basic example that rounds to the nearest minute:
5107    ///
5108    /// ```
5109    /// use jiff::{ToSpan, Unit};
5110    ///
5111    /// let span = 15.minutes().seconds(46);
5112    /// assert_eq!(span.round(Unit::Minute)?, 16.minutes().fieldwise());
5113    ///
5114    /// # Ok::<(), Box<dyn std::error::Error>>(())
5115    /// ```
5116    #[inline]
5117    pub fn smallest(self, unit: Unit) -> SpanRound<'a> {
5118        SpanRound { smallest: unit, ..self }
5119    }
5120
5121    /// Set the largest units allowed in the span returned.
5122    ///
5123    /// When a largest unit is not specified, then it defaults to the largest
5124    /// non-zero unit that is at least as big as the configured smallest
5125    /// unit. For example, given a span of `2 months 17 hours`, the default
5126    /// largest unit would be `Unit::Month`. The default implies that a span's
5127    /// units do not get "bigger" than what was given.
5128    ///
5129    /// Once a largest unit is set, there is no way to change this rounding
5130    /// configuration back to using the "automatic" default. Instead, callers
5131    /// must create a new configuration.
5132    ///
5133    /// If a largest unit is set and no other options are set, then the
5134    /// rounding operation can be said to be a "re-balancing." That is, the
5135    /// span won't lose precision, but the way in which it is expressed may
5136    /// change.
5137    ///
5138    /// # Errors
5139    ///
5140    /// The largest units, when set, must be at least as big as the smallest
5141    /// units (which defaults to [`Unit::Nanosecond`]). If this is violated,
5142    /// then rounding a span with this configuration will result in an error.
5143    ///
5144    /// If a largest unit bigger than days is selected without a relative
5145    /// datetime reference point, then an error is returned when using this
5146    /// configuration with [`Span::round`].
5147    ///
5148    /// # Example: re-balancing
5149    ///
5150    /// This shows how a span can be re-balanced without losing precision:
5151    ///
5152    /// ```
5153    /// use jiff::{SpanRound, ToSpan, Unit};
5154    ///
5155    /// let span = 86_401_123_456_789i64.nanoseconds();
5156    /// assert_eq!(
5157    ///     span.round(SpanRound::new().largest(Unit::Hour))?.fieldwise(),
5158    ///     24.hours().seconds(1).milliseconds(123).microseconds(456).nanoseconds(789),
5159    /// );
5160    ///
5161    /// # Ok::<(), Box<dyn std::error::Error>>(())
5162    /// ```
5163    ///
5164    /// If you need to use a largest unit bigger than hours, then you must
5165    /// provide a relative datetime as a reference point (otherwise an error
5166    /// will occur):
5167    ///
5168    /// ```
5169    /// use jiff::{civil::date, SpanRound, ToSpan, Unit};
5170    ///
5171    /// let span = 3_968_000.seconds();
5172    /// let round = SpanRound::new()
5173    ///     .largest(Unit::Day)
5174    ///     .relative(date(2024, 7, 1));
5175    /// assert_eq!(
5176    ///     span.round(round)?,
5177    ///     45.days().hours(22).minutes(13).seconds(20).fieldwise(),
5178    /// );
5179    ///
5180    /// # Ok::<(), Box<dyn std::error::Error>>(())
5181    /// ```
5182    ///
5183    /// As a special case for days, one can instead opt into invariant 24-hour
5184    /// days (and 7-day weeks) without providing an explicit relative date:
5185    ///
5186    /// ```
5187    /// use jiff::{SpanRound, ToSpan, Unit};
5188    ///
5189    /// let span = 86_401_123_456_789i64.nanoseconds();
5190    /// assert_eq!(
5191    ///     span.round(
5192    ///         SpanRound::new().largest(Unit::Day).days_are_24_hours(),
5193    ///     )?.fieldwise(),
5194    ///     1.day().seconds(1).milliseconds(123).microseconds(456).nanoseconds(789),
5195    /// );
5196    ///
5197    /// # Ok::<(), Box<dyn std::error::Error>>(())
5198    /// ```
5199    ///
5200    /// # Example: re-balancing while taking DST into account
5201    ///
5202    /// When given a zone aware relative datetime, rounding will even take
5203    /// DST into account:
5204    ///
5205    /// ```
5206    /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5207    ///
5208    /// let span = 2756.hours();
5209    /// let zdt = "2020-01-01T00:00+01:00[Europe/Rome]".parse::<Zoned>()?;
5210    /// let round = SpanRound::new().largest(Unit::Year).relative(&zdt);
5211    /// assert_eq!(
5212    ///     span.round(round)?,
5213    ///     3.months().days(23).hours(21).fieldwise(),
5214    /// );
5215    ///
5216    /// # Ok::<(), Box<dyn std::error::Error>>(())
5217    /// ```
5218    ///
5219    /// Now compare with the same operation, but on a civil datetime (which is
5220    /// not aware of time zone):
5221    ///
5222    /// ```
5223    /// use jiff::{civil::DateTime, SpanRound, ToSpan, Unit};
5224    ///
5225    /// let span = 2756.hours();
5226    /// let dt = "2020-01-01T00:00".parse::<DateTime>()?;
5227    /// let round = SpanRound::new().largest(Unit::Year).relative(dt);
5228    /// assert_eq!(
5229    ///     span.round(round)?,
5230    ///     3.months().days(23).hours(20).fieldwise(),
5231    /// );
5232    ///
5233    /// # Ok::<(), Box<dyn std::error::Error>>(())
5234    /// ```
5235    ///
5236    /// The result is 1 hour shorter. This is because, in the zone
5237    /// aware re-balancing, it accounts for the transition into DST at
5238    /// `2020-03-29T01:00Z`, which skips an hour. This makes the span one hour
5239    /// longer because one of the days in the span is actually only 23 hours
5240    /// long instead of 24 hours.
5241    #[inline]
5242    pub fn largest(self, unit: Unit) -> SpanRound<'a> {
5243        SpanRound { largest: Some(unit), ..self }
5244    }
5245
5246    /// Set the rounding mode.
5247    ///
5248    /// This defaults to [`RoundMode::HalfExpand`], which makes rounding work
5249    /// like how you were taught in school.
5250    ///
5251    /// # Example
5252    ///
5253    /// A basic example that rounds to the nearest minute, but changing its
5254    /// rounding mode to truncation:
5255    ///
5256    /// ```
5257    /// use jiff::{RoundMode, SpanRound, ToSpan, Unit};
5258    ///
5259    /// let span = 15.minutes().seconds(46);
5260    /// assert_eq!(
5261    ///     span.round(SpanRound::new()
5262    ///         .smallest(Unit::Minute)
5263    ///         .mode(RoundMode::Trunc),
5264    ///     )?,
5265    ///     // The default round mode does rounding like
5266    ///     // how you probably learned in school, and would
5267    ///     // result in rounding up to 16 minutes. But we
5268    ///     // change it to truncation here, which makes it
5269    ///     // round down.
5270    ///     15.minutes().fieldwise(),
5271    /// );
5272    ///
5273    /// # Ok::<(), Box<dyn std::error::Error>>(())
5274    /// ```
5275    #[inline]
5276    pub fn mode(self, mode: RoundMode) -> SpanRound<'a> {
5277        SpanRound { mode, ..self }
5278    }
5279
5280    /// Set the rounding increment for the smallest unit.
5281    ///
5282    /// The default value is `1`. Other values permit rounding the smallest
5283    /// unit to the nearest integer increment specified. For example, if the
5284    /// smallest unit is set to [`Unit::Minute`], then a rounding increment of
5285    /// `30` would result in rounding in increments of a half hour. That is,
5286    /// the only minute value that could result would be `0` or `30`.
5287    ///
5288    /// # Errors
5289    ///
5290    /// When the smallest unit is less than days, the rounding increment must
5291    /// divide evenly into the next highest unit after the smallest unit
5292    /// configured (and must not be equivalent to it). For example, if the
5293    /// smallest unit is [`Unit::Nanosecond`], then *some* of the valid values
5294    /// for the rounding increment are `1`, `2`, `4`, `5`, `100` and `500`.
5295    /// Namely, any integer that divides evenly into `1,000` nanoseconds since
5296    /// there are `1,000` nanoseconds in the next highest unit (microseconds).
5297    ///
5298    /// The error will occur when computing the span, and not when setting
5299    /// the increment here.
5300    ///
5301    /// # Example
5302    ///
5303    /// This shows how to round a span to the nearest 5 minute increment:
5304    ///
5305    /// ```
5306    /// use jiff::{ToSpan, Unit};
5307    ///
5308    /// let span = 4.hours().minutes(2).seconds(30);
5309    /// assert_eq!(
5310    ///     span.round((Unit::Minute, 5))?,
5311    ///     4.hours().minutes(5).fieldwise(),
5312    /// );
5313    ///
5314    /// # Ok::<(), Box<dyn std::error::Error>>(())
5315    /// ```
5316    #[inline]
5317    pub fn increment(self, increment: i64) -> SpanRound<'a> {
5318        SpanRound { increment, ..self }
5319    }
5320
5321    /// Set the relative datetime to use when rounding a span.
5322    ///
5323    /// A relative datetime is only required when calendar units (units greater
5324    /// than days) are involved. This includes having calendar units in the
5325    /// original span, or calendar units in the configured smallest or largest
5326    /// unit. A relative datetime is required when calendar units are used
5327    /// because the duration of a particular calendar unit (like 1 month or 1
5328    /// year) is variable and depends on the date. For example, 1 month from
5329    /// 2024-01-01 is 31 days, but 1 month from 2024-02-01 is 29 days.
5330    ///
5331    /// A relative datetime is provided by anything that implements
5332    /// `Into<SpanRelativeTo>`. There are a few convenience trait
5333    /// implementations provided:
5334    ///
5335    /// * `From<&Zoned> for SpanRelativeTo` uses a zone aware datetime to do
5336    /// rounding. In this case, rounding will take time zone transitions into
5337    /// account. In particular, when using a zoned relative datetime, not all
5338    /// days are necessarily 24 hours.
5339    /// * `From<civil::DateTime> for SpanRelativeTo` uses a civil datetime. In
5340    /// this case, all days will be considered 24 hours long.
5341    /// * `From<civil::Date> for SpanRelativeTo` uses a civil date. In this
5342    /// case, all days will be considered 24 hours long.
5343    ///
5344    /// Note that one can impose 24-hour days without providing a reference
5345    /// date via [`SpanRelativeTo::days_are_24_hours`].
5346    ///
5347    /// # Errors
5348    ///
5349    /// If rounding involves a calendar unit (units bigger than hours) and no
5350    /// relative datetime is provided, then this configuration will lead to
5351    /// an error when used with [`Span::round`].
5352    ///
5353    /// # Example
5354    ///
5355    /// This example shows very precisely how a DST transition can impact
5356    /// rounding and re-balancing. For example, consider the day `2024-11-03`
5357    /// in `America/New_York`. On this day, the 1 o'clock hour was repeated,
5358    /// making the day 24 hours long. This will be taken into account when
5359    /// rounding if a zoned datetime is provided as a reference point:
5360    ///
5361    /// ```
5362    /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5363    ///
5364    /// let zdt = "2024-11-03T00-04[America/New_York]".parse::<Zoned>()?;
5365    /// let round = SpanRound::new().largest(Unit::Hour).relative(&zdt);
5366    /// assert_eq!(1.day().round(round)?, 25.hours().fieldwise());
5367    ///
5368    /// # Ok::<(), Box<dyn std::error::Error>>(())
5369    /// ```
5370    ///
5371    /// And similarly for `2024-03-10`, where the 2 o'clock hour was skipped
5372    /// entirely:
5373    ///
5374    /// ```
5375    /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5376    ///
5377    /// let zdt = "2024-03-10T00-05[America/New_York]".parse::<Zoned>()?;
5378    /// let round = SpanRound::new().largest(Unit::Hour).relative(&zdt);
5379    /// assert_eq!(1.day().round(round)?, 23.hours().fieldwise());
5380    ///
5381    /// # Ok::<(), Box<dyn std::error::Error>>(())
5382    /// ```
5383    #[inline]
5384    pub fn relative<R: Into<SpanRelativeTo<'a>>>(
5385        self,
5386        relative: R,
5387    ) -> SpanRound<'a> {
5388        SpanRound { relative: Some(relative.into()), ..self }
5389    }
5390
5391    /// This is a convenience function for setting the relative option on
5392    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
5393    ///
5394    /// # Example
5395    ///
5396    /// When rounding spans involving days, either a relative datetime must be
5397    /// provided, or a special assertion opting into 24-hour days is
5398    /// required. Otherwise, you get an error.
5399    ///
5400    /// ```
5401    /// use jiff::{SpanRound, ToSpan, Unit};
5402    ///
5403    /// let span = 2.days().hours(12);
5404    /// // No relative date provided, which results in an error.
5405    /// assert_eq!(
5406    ///     span.round(Unit::Day).unwrap_err().to_string(),
5407    ///     "error with `smallest` rounding option: using unit 'day' in a \
5408    ///      span or configuration requires that either a relative reference \
5409    ///      time be given or `jiff::SpanRelativeTo::days_are_24_hours()` is \
5410    ///      used to indicate invariant 24-hour days, but neither were \
5411    ///      provided",
5412    /// );
5413    /// let rounded = span.round(
5414    ///     SpanRound::new().smallest(Unit::Day).days_are_24_hours(),
5415    /// )?;
5416    /// assert_eq!(rounded, 3.days().fieldwise());
5417    ///
5418    /// # Ok::<(), Box<dyn std::error::Error>>(())
5419    /// ```
5420    #[inline]
5421    pub fn days_are_24_hours(self) -> SpanRound<'a> {
5422        self.relative(SpanRelativeTo::days_are_24_hours())
5423    }
5424
5425    /// Returns the configured smallest unit on this round configuration.
5426    #[inline]
5427    pub(crate) fn get_smallest(&self) -> Unit {
5428        self.smallest
5429    }
5430
5431    /// Returns the configured largest unit on this round configuration.
5432    #[inline]
5433    pub(crate) fn get_largest(&self) -> Option<Unit> {
5434        self.largest
5435    }
5436
5437    /// Returns true only when rounding a span *may* change it. When it
5438    /// returns false, and if the span is already balanced according to
5439    /// the largest unit in this round configuration, then it is guaranteed
5440    /// that rounding is a no-op.
5441    ///
5442    /// This is useful to avoid rounding calls after doing span arithmetic
5443    /// on datetime types. This works because the "largest" unit is used to
5444    /// construct a balanced span for the difference between two datetimes.
5445    /// So we already know the span has been balanced. If this weren't the
5446    /// case, then the largest unit being different from the one in the span
5447    /// could result in rounding making a change. (And indeed, in the general
5448    /// case of span rounding below, we do a more involved check for this.)
5449    #[inline]
5450    pub(crate) fn rounding_may_change_span_ignore_largest(&self) -> bool {
5451        self.smallest > Unit::Nanosecond || self.increment > 1
5452    }
5453
5454    /// Does the actual span rounding.
5455    fn round(&self, span: Span) -> Result<Span, Error> {
5456        let existing_largest = span.largest_unit();
5457        let mode = self.mode;
5458        let smallest = self.smallest;
5459        let largest =
5460            self.largest.unwrap_or_else(|| smallest.max(existing_largest));
5461        let max = existing_largest.max(largest);
5462        let increment = increment::for_span(smallest, self.increment)?;
5463        if largest < smallest {
5464            return Err(Error::from(
5465                E::NotAllowedLargestSmallerThanSmallest { smallest, largest },
5466            ));
5467        }
5468        let relative = match self.relative {
5469            Some(ref r) => {
5470                match r.to_relative(max)? {
5471                    Some(r) => r,
5472                    None => {
5473                        // If our reference point is civil time, then its units
5474                        // are invariant as long as we are using day-or-lower
5475                        // everywhere. That is, the length of the duration is
5476                        // independent of the reference point. In which case,
5477                        // rounding is a simple matter of converting the span
5478                        // to a number of nanoseconds and then rounding that.
5479                        return Ok(round_span_invariant(
5480                            span, smallest, largest, increment, mode,
5481                        )?);
5482                    }
5483                }
5484            }
5485            None => {
5486                // This is only okay if none of our units are above 'day'.
5487                // That is, a reference point is only necessary when there is
5488                // no reasonable invariant interpretation of the span. And this
5489                // is only true when everything is less than 'day'.
5490                requires_relative_date_err(smallest)
5491                    .context(E::OptionSmallest)?;
5492                if let Some(largest) = self.largest {
5493                    requires_relative_date_err(largest)
5494                        .context(E::OptionLargest)?;
5495                }
5496                requires_relative_date_err(existing_largest)
5497                    .context(E::OptionLargestInSpan)?;
5498                assert!(max <= Unit::Week);
5499                return Ok(round_span_invariant(
5500                    span, smallest, largest, increment, mode,
5501                )?);
5502            }
5503        };
5504        relative.round(span, smallest, largest, increment, mode)
5505    }
5506}
5507
5508impl Default for SpanRound<'static> {
5509    fn default() -> SpanRound<'static> {
5510        SpanRound::new()
5511    }
5512}
5513
5514impl From<Unit> for SpanRound<'static> {
5515    fn from(unit: Unit) -> SpanRound<'static> {
5516        SpanRound::default().smallest(unit)
5517    }
5518}
5519
5520impl From<(Unit, i64)> for SpanRound<'static> {
5521    fn from((unit, increment): (Unit, i64)) -> SpanRound<'static> {
5522        SpanRound::default().smallest(unit).increment(increment)
5523    }
5524}
5525
5526/// A relative datetime for use with [`Span`] APIs.
5527///
5528/// A relative datetime can be one of the following: [`civil::Date`](Date),
5529/// [`civil::DateTime`](DateTime) or [`Zoned`]. It can be constructed from any
5530/// of the preceding types via `From` trait implementations.
5531///
5532/// A relative datetime is used to indicate how the calendar units of a `Span`
5533/// should be interpreted. For example, the span "1 month" does not have a
5534/// fixed meaning. One month from `2024-03-01` is 31 days, but one month from
5535/// `2024-04-01` is 30 days. Similar for years.
5536///
5537/// When a relative datetime in time zone aware (i.e., it is a `Zoned`), then
5538/// operations on a `Span` will also consider its day units to be variable in
5539/// length. For example, `2024-03-10` in `America/New_York` was only 23 hours
5540/// long, where as `2024-11-03` in `America/New_York` was 25 hours long. When
5541/// a relative datetime is civil, then days are considered to always be of a
5542/// fixed 24 hour length.
5543///
5544/// This type is principally used as an input to one of several different
5545/// [`Span`] APIs:
5546///
5547/// * [`Span::round`] rounds spans. A relative datetime is necessary when
5548/// dealing with calendar units. (But spans without calendar units can be
5549/// rounded without providing a relative datetime.)
5550/// * Span arithmetic via [`Span::checked_add`] and [`Span::checked_sub`].
5551/// A relative datetime is needed when adding or subtracting spans with
5552/// calendar units.
5553/// * Span comparisons via [`Span::compare`] require a relative datetime when
5554/// comparing spans with calendar units.
5555/// * Computing the "total" duration as a single floating point number via
5556/// [`Span::total`] also requires a relative datetime when dealing with
5557/// calendar units.
5558///
5559/// # Example
5560///
5561/// This example shows how to round a span with larger calendar units to
5562/// smaller units:
5563///
5564/// ```
5565/// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5566///
5567/// let zdt: Zoned = "2012-01-01[Antarctica/Troll]".parse()?;
5568/// let round = SpanRound::new().largest(Unit::Day).relative(&zdt);
5569/// assert_eq!(1.year().round(round)?, 366.days().fieldwise());
5570///
5571/// // If you tried this without a relative datetime, it would fail:
5572/// let round = SpanRound::new().largest(Unit::Day);
5573/// assert!(1.year().round(round).is_err());
5574///
5575/// # Ok::<(), Box<dyn std::error::Error>>(())
5576/// ```
5577#[derive(Clone, Copy, Debug)]
5578pub struct SpanRelativeTo<'a> {
5579    kind: SpanRelativeToKind<'a>,
5580}
5581
5582impl<'a> SpanRelativeTo<'a> {
5583    /// Creates a special marker that indicates all days ought to be assumed
5584    /// to be 24 hours without providing a relative reference time.
5585    ///
5586    /// This is relevant to the following APIs:
5587    ///
5588    /// * [`Span::checked_add`]
5589    /// * [`Span::checked_sub`]
5590    /// * [`Span::compare`]
5591    /// * [`Span::total`]
5592    /// * [`Span::round`]
5593    /// * [`Span::to_duration`]
5594    ///
5595    /// Specifically, in a previous version of Jiff, the above APIs permitted
5596    /// _silently_ assuming that days are always 24 hours when a relative
5597    /// reference date wasn't provided. In the current version of Jiff, this
5598    /// silent interpretation no longer happens and instead an error will
5599    /// occur.
5600    ///
5601    /// If you need to use these APIs with spans that contain non-zero units
5602    /// of days or weeks but without a relative reference date, then you may
5603    /// use this routine to create a special marker for `SpanRelativeTo` that
5604    /// permits the APIs above to assume days are always 24 hours.
5605    ///
5606    /// # Motivation
5607    ///
5608    /// The purpose of the marker is two-fold:
5609    ///
5610    /// * Requiring the marker is important for improving the consistency of
5611    /// `Span` APIs. Previously, some APIs (like [`Timestamp::checked_add`])
5612    /// would always return an error if the `Span` given had non-zero
5613    /// units of days or greater. On the other hand, other APIs (like
5614    /// [`Span::checked_add`]) would automatically assume days were always
5615    /// 24 hours if no relative reference time was given and either span had
5616    /// non-zero units of days. With this marker, APIs _never_ assume days are
5617    /// always 24 hours automatically.
5618    /// * When it _is_ appropriate to assume all days are 24 hours
5619    /// (for example, when only dealing with spans derived from
5620    /// [`civil`](crate::civil) datetimes) and where providing a relative
5621    /// reference datetime doesn't make sense. In this case, one _could_
5622    /// provide a "dummy" reference date since the precise date in civil time
5623    /// doesn't impact the length of a day. But a marker like the one returned
5624    /// here is more explicit for the purpose of assuming days are always 24
5625    /// hours.
5626    ///
5627    /// With that said, ideally, callers should provide a relative reference
5628    /// datetime if possible.
5629    ///
5630    /// See [Issue #48] for more discussion on this topic.
5631    ///
5632    /// # Example: different interpretations of "1 day"
5633    ///
5634    /// This example shows how "1 day" can be interpreted differently via the
5635    /// [`Span::total`] API:
5636    ///
5637    /// ```
5638    /// use jiff::{SpanRelativeTo, ToSpan, Unit, Zoned};
5639    ///
5640    /// let span = 1.day();
5641    ///
5642    /// // An error because days aren't always 24 hours:
5643    /// assert_eq!(
5644    ///     span.total(Unit::Hour).unwrap_err().to_string(),
5645    ///     "using unit 'day' in a span or configuration requires that either \
5646    ///      a relative reference time be given or \
5647    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
5648    ///      invariant 24-hour days, but neither were provided",
5649    /// );
5650    /// // Opt into invariant 24 hour days without a relative date:
5651    /// let marker = SpanRelativeTo::days_are_24_hours();
5652    /// let hours = span.total((Unit::Hour, marker))?;
5653    /// assert_eq!(hours, 24.0);
5654    /// // Days can be shorter than 24 hours:
5655    /// let zdt: Zoned = "2024-03-10[America/New_York]".parse()?;
5656    /// let hours = span.total((Unit::Hour, &zdt))?;
5657    /// assert_eq!(hours, 23.0);
5658    /// // Days can be longer than 24 hours:
5659    /// let zdt: Zoned = "2024-11-03[America/New_York]".parse()?;
5660    /// let hours = span.total((Unit::Hour, &zdt))?;
5661    /// assert_eq!(hours, 25.0);
5662    ///
5663    /// # Ok::<(), Box<dyn std::error::Error>>(())
5664    /// ```
5665    ///
5666    /// Similar behavior applies to the other APIs listed above.
5667    ///
5668    /// # Example: different interpretations of "1 week"
5669    ///
5670    /// This example shows how "1 week" can be interpreted differently via the
5671    /// [`Span::total`] API:
5672    ///
5673    /// ```
5674    /// use jiff::{SpanRelativeTo, ToSpan, Unit, Zoned};
5675    ///
5676    /// let span = 1.week();
5677    ///
5678    /// // An error because days aren't always 24 hours:
5679    /// assert_eq!(
5680    ///     span.total(Unit::Hour).unwrap_err().to_string(),
5681    ///     "using unit 'week' in a span or configuration requires that either \
5682    ///      a relative reference time be given or \
5683    ///      `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
5684    ///      invariant 24-hour days, but neither were provided",
5685    /// );
5686    /// // Opt into invariant 24 hour days without a relative date:
5687    /// let marker = SpanRelativeTo::days_are_24_hours();
5688    /// let hours = span.total((Unit::Hour, marker))?;
5689    /// assert_eq!(hours, 168.0);
5690    /// // Weeks can be shorter than 24*7 hours:
5691    /// let zdt: Zoned = "2024-03-10[America/New_York]".parse()?;
5692    /// let hours = span.total((Unit::Hour, &zdt))?;
5693    /// assert_eq!(hours, 167.0);
5694    /// // Weeks can be longer than 24*7 hours:
5695    /// let zdt: Zoned = "2024-11-03[America/New_York]".parse()?;
5696    /// let hours = span.total((Unit::Hour, &zdt))?;
5697    /// assert_eq!(hours, 169.0);
5698    ///
5699    /// # Ok::<(), Box<dyn std::error::Error>>(())
5700    /// ```
5701    ///
5702    /// # Example: working with [`civil::Date`](crate::civil::Date)
5703    ///
5704    /// A `Span` returned by computing the difference in time between two
5705    /// [`civil::Date`](crate::civil::Date)s will have a non-zero number of
5706    /// days. In older versions of Jiff, if one wanted to add spans returned by
5707    /// these APIs, you could do so without futzing with relative dates. But
5708    /// now you either need to provide a relative date:
5709    ///
5710    /// ```
5711    /// use jiff::{civil::date, ToSpan};
5712    ///
5713    /// let d1 = date(2025, 1, 18);
5714    /// let d2 = date(2025, 1, 26);
5715    /// let d3 = date(2025, 2, 14);
5716    ///
5717    /// let span1 = d2 - d1;
5718    /// let span2 = d3 - d2;
5719    /// let total = span1.checked_add((span2, d1))?;
5720    /// assert_eq!(total, 27.days().fieldwise());
5721    ///
5722    /// # Ok::<(), Box<dyn std::error::Error>>(())
5723    /// ```
5724    ///
5725    /// Or you can provide a marker indicating that days are always 24 hours.
5726    /// This is fine for this use case since one is only doing civil calendar
5727    /// arithmetic and not working with time zones:
5728    ///
5729    /// ```
5730    /// use jiff::{civil::date, SpanRelativeTo, ToSpan};
5731    ///
5732    /// let d1 = date(2025, 1, 18);
5733    /// let d2 = date(2025, 1, 26);
5734    /// let d3 = date(2025, 2, 14);
5735    ///
5736    /// let span1 = d2 - d1;
5737    /// let span2 = d3 - d2;
5738    /// let total = span1.checked_add(
5739    ///     (span2, SpanRelativeTo::days_are_24_hours()),
5740    /// )?;
5741    /// assert_eq!(total, 27.days().fieldwise());
5742    ///
5743    /// # Ok::<(), Box<dyn std::error::Error>>(())
5744    /// ```
5745    ///
5746    /// [Issue #48]: https://github.com/BurntSushi/jiff/issues/48
5747    #[inline]
5748    pub const fn days_are_24_hours() -> SpanRelativeTo<'static> {
5749        let kind = SpanRelativeToKind::DaysAre24Hours;
5750        SpanRelativeTo { kind }
5751    }
5752
5753    /// Converts this public API relative datetime into a more versatile
5754    /// internal representation of the same concept.
5755    ///
5756    /// Basically, the internal `Relative` type is `Cow` which means it isn't
5757    /// `Copy`. But it can present a more uniform API. The public API type
5758    /// doesn't have `Cow` so that it can be `Copy`.
5759    ///
5760    /// We also take this opportunity to attach some convenient data, such
5761    /// as a timestamp when the relative datetime type is civil.
5762    ///
5763    /// This can return `None` if this `SpanRelativeTo` isn't actually a
5764    /// datetime but a "marker" indicating some unit (like days) should be
5765    /// treated as invariant. Or `None` is returned when the given unit is
5766    /// always invariant (hours or smaller).
5767    ///
5768    /// # Errors
5769    ///
5770    /// If there was a problem doing this conversion, then an error is
5771    /// returned. In practice, this only occurs for a civil datetime near the
5772    /// civil datetime minimum and maximum values.
5773    fn to_relative(&self, unit: Unit) -> Result<Option<Relative<'a>>, Error> {
5774        if !unit.is_variable() {
5775            return Ok(None);
5776        }
5777        match self.kind {
5778            SpanRelativeToKind::Civil(dt) => {
5779                Ok(Some(Relative::Civil(RelativeCivil::new(dt)?)))
5780            }
5781            SpanRelativeToKind::Zoned(zdt) => {
5782                Ok(Some(Relative::Zoned(RelativeZoned {
5783                    zoned: DumbCow::Borrowed(zdt),
5784                })))
5785            }
5786            SpanRelativeToKind::DaysAre24Hours => {
5787                if matches!(unit, Unit::Year | Unit::Month) {
5788                    return Err(Error::from(
5789                        E::RequiresRelativeYearOrMonthGivenDaysAre24Hours {
5790                            unit,
5791                        },
5792                    ));
5793                }
5794                Ok(None)
5795            }
5796        }
5797    }
5798}
5799
5800#[derive(Clone, Copy, Debug)]
5801enum SpanRelativeToKind<'a> {
5802    Civil(DateTime),
5803    Zoned(&'a Zoned),
5804    DaysAre24Hours,
5805}
5806
5807impl<'a> From<&'a Zoned> for SpanRelativeTo<'a> {
5808    fn from(zdt: &'a Zoned) -> SpanRelativeTo<'a> {
5809        SpanRelativeTo { kind: SpanRelativeToKind::Zoned(zdt) }
5810    }
5811}
5812
5813impl From<DateTime> for SpanRelativeTo<'static> {
5814    fn from(dt: DateTime) -> SpanRelativeTo<'static> {
5815        SpanRelativeTo { kind: SpanRelativeToKind::Civil(dt) }
5816    }
5817}
5818
5819impl From<Date> for SpanRelativeTo<'static> {
5820    fn from(date: Date) -> SpanRelativeTo<'static> {
5821        let dt = DateTime::from_parts(date, Time::midnight());
5822        SpanRelativeTo { kind: SpanRelativeToKind::Civil(dt) }
5823    }
5824}
5825
5826/// A bit set that keeps track of all non-zero units on a `Span`.
5827///
5828/// Because of alignment, adding this to a `Span` does not make it any bigger.
5829///
5830/// The benefit of this bit set is to make it extremely cheap to enable fast
5831/// paths in various places. For example, doing arithmetic on a `Date` with an
5832/// arbitrary `Span` is pretty involved. But if you know the `Span` only
5833/// consists of non-zero units of days (and zero for all other units), then you
5834/// can take a much cheaper path.
5835#[derive(Clone, Copy)]
5836pub(crate) struct UnitSet(u16);
5837
5838impl UnitSet {
5839    /// Return a bit set representing all units as zero.
5840    #[inline]
5841    fn empty() -> UnitSet {
5842        UnitSet(0)
5843    }
5844
5845    /// Set the given `unit` to `is_zero` status in this set.
5846    ///
5847    /// When `is_zero` is false, the unit is added to this set. Otherwise,
5848    /// the unit is removed from this set.
5849    #[inline]
5850    fn set(self, unit: Unit, is_zero: bool) -> UnitSet {
5851        let bit = 1 << unit as usize;
5852        if is_zero {
5853            UnitSet(self.0 & !bit)
5854        } else {
5855            UnitSet(self.0 | bit)
5856        }
5857    }
5858
5859    /// Returns true if and only if no units are in this set.
5860    #[inline]
5861    pub(crate) fn is_empty(&self) -> bool {
5862        self.0 == 0
5863    }
5864
5865    /// Returns true if and only if this `Span` contains precisely one
5866    /// non-zero unit corresponding to the unit given.
5867    #[inline]
5868    pub(crate) fn contains_only(self, unit: Unit) -> bool {
5869        self.0 == (1 << unit as usize)
5870    }
5871
5872    /// Returns this set, but with only calendar units.
5873    #[inline]
5874    pub(crate) fn only_calendar(self) -> UnitSet {
5875        UnitSet(self.0 & 0b0000_0011_1100_0000)
5876    }
5877
5878    /// Returns this set, but with only time units.
5879    #[inline]
5880    pub(crate) fn only_time(self) -> UnitSet {
5881        UnitSet(self.0 & 0b0000_0000_0011_1111)
5882    }
5883
5884    /// Returns the largest unit in this set, or `None` if none are present.
5885    #[inline]
5886    pub(crate) fn largest_unit(self) -> Option<Unit> {
5887        let zeros = usize::try_from(self.0.leading_zeros()).ok()?;
5888        15usize.checked_sub(zeros).and_then(Unit::from_usize)
5889    }
5890}
5891
5892// N.B. This `Debug` impl isn't typically used.
5893//
5894// This is because the `Debug` impl for `Span` just emits itself in the
5895// friendly duration format, which doesn't include internal representation
5896// details like this set. It is included in `Span::debug`, but this isn't
5897// part of the public crate API.
5898impl core::fmt::Debug for UnitSet {
5899    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
5900        write!(f, "{{")?;
5901        let mut units = *self;
5902        let mut i = 0;
5903        while let Some(unit) = units.largest_unit() {
5904            if i > 0 {
5905                write!(f, ", ")?;
5906            }
5907            i += 1;
5908            write!(f, "{}", unit.compact())?;
5909            units = units.set(unit, false);
5910        }
5911        if i == 0 {
5912            write!(f, "∅")?;
5913        }
5914        write!(f, "}}")
5915    }
5916}
5917
5918/// An internal abstraction for managing a relative datetime for use in some
5919/// `Span` APIs.
5920///
5921/// This is effectively the same as a `SpanRelativeTo`, but uses a `Cow<Zoned>`
5922/// instead of a `&Zoned`. This makes it non-`Copy`, but allows us to craft a
5923/// more uniform API. (i.e., `relative + span = relative` instead of `relative
5924/// + span = owned_relative` or whatever.) Note that the `Copy` impl on
5925/// `SpanRelativeTo` means it has to accept a `&Zoned`. It can't ever take a
5926/// `Zoned` since it is non-Copy.
5927///
5928/// NOTE: Separately from above, I think it's plausible that this type could be
5929/// designed a bit differently. Namely, something like this:
5930///
5931/// ```text
5932/// struct Relative<'a> {
5933///     tz: Option<&'a TimeZone>,
5934///     dt: DateTime,
5935///     ts: Timestamp,
5936/// }
5937/// ```
5938///
5939/// That is, we do zone aware stuff but without an actual `Zoned` type. But I
5940/// think in order to make that work, we would need to expose most of the
5941/// `Zoned` API as functions on its component types (DateTime, Timestamp and
5942/// TimeZone). I think we are likely to want to do that for public API reasons,
5943/// but I'd like to resist it since I think it will add a lot of complexity.
5944/// Or maybe we need a `Unzoned` type that is `DateTime` and `Timestamp`, but
5945/// requires passing the time zone in to each of its methods. That might work
5946/// quite well, even if it was just an internal type.
5947///
5948/// Anyway, I'm not 100% sure the above would work, but I think it would. It
5949/// would be nicer because everything would be `Copy` all the time. We'd never
5950/// need a `Cow<TimeZone>` for example, because we never need to change or
5951/// create a new time zone.
5952#[derive(Clone, Debug)]
5953enum Relative<'a> {
5954    Civil(RelativeCivil),
5955    Zoned(RelativeZoned<'a>),
5956}
5957
5958impl<'a> Relative<'a> {
5959    /// Adds the given span to this relative datetime.
5960    ///
5961    /// This defers to either [`DateTime::checked_add`] or
5962    /// [`Zoned::checked_add`], depending on the type of relative datetime.
5963    ///
5964    /// The `Relative` datetime returned is guaranteed to have the same
5965    /// internal datetie type as `self`.
5966    ///
5967    /// # Errors
5968    ///
5969    /// This returns an error in the same cases as the underlying checked
5970    /// arithmetic APIs. In general, this occurs when adding the given `span`
5971    /// would result in overflow.
5972    fn checked_add(&'a self, span: Span) -> Result<Relative<'a>, Error> {
5973        match *self {
5974            Relative::Civil(dt) => Ok(Relative::Civil(dt.checked_add(span)?)),
5975            Relative::Zoned(ref zdt) => {
5976                Ok(Relative::Zoned(zdt.checked_add(span)?))
5977            }
5978        }
5979    }
5980
5981    fn checked_add_duration(
5982        &'a self,
5983        duration: SignedDuration,
5984    ) -> Result<Relative<'a>, Error> {
5985        match *self {
5986            Relative::Civil(dt) => {
5987                Ok(Relative::Civil(dt.checked_add_duration(duration)?))
5988            }
5989            Relative::Zoned(ref zdt) => {
5990                Ok(Relative::Zoned(zdt.checked_add_duration(duration)?))
5991            }
5992        }
5993    }
5994
5995    /// Returns the span of time from this relative datetime to the one given,
5996    /// with units as large as `largest`.
5997    ///
5998    /// # Errors
5999    ///
6000    /// This returns an error in the same cases as when the underlying
6001    /// [`DateTime::until`] or [`Zoned::until`] fail. Because this doesn't
6002    /// set or expose any rounding configuration, this can generally only
6003    /// occur when `largest` is `Unit::Nanosecond` and the span of time
6004    /// between `self` and `other` is too big to represent as a 64-bit integer
6005    /// nanosecond count.
6006    ///
6007    /// # Panics
6008    ///
6009    /// This panics if `self` and `other` are different internal datetime
6010    /// types. For example, if `self` was a civil datetime and `other` were
6011    /// a zoned datetime.
6012    fn until(&self, largest: Unit, other: &Relative) -> Result<Span, Error> {
6013        match (self, other) {
6014            (&Relative::Civil(ref dt1), &Relative::Civil(ref dt2)) => {
6015                dt1.until(largest, dt2)
6016            }
6017            (&Relative::Zoned(ref zdt1), &Relative::Zoned(ref zdt2)) => {
6018                zdt1.until(largest, zdt2)
6019            }
6020            // This would be bad if `Relative` were a public API, but in
6021            // practice, this case never occurs because we don't mixup our
6022            // `Relative` datetime types.
6023            _ => unreachable!(),
6024        }
6025    }
6026
6027    /// Converts this relative datetime to a nanosecond in UTC time.
6028    ///
6029    /// # Errors
6030    ///
6031    /// If there was a problem doing this conversion, then an error is
6032    /// returned. In practice, this only occurs for a civil datetime near the
6033    /// civil datetime minimum and maximum values.
6034    fn to_nanosecond(&self) -> NoUnits128 {
6035        match *self {
6036            Relative::Civil(dt) => dt.timestamp.as_nanosecond_ranged().rinto(),
6037            Relative::Zoned(ref zdt) => {
6038                zdt.zoned.timestamp().as_nanosecond_ranged().rinto()
6039            }
6040        }
6041    }
6042
6043    /// Create a balanced span of time relative to this datetime.
6044    ///
6045    /// The relative span returned has the same internal datetime type
6046    /// (civil or zoned) as this relative datetime.
6047    ///
6048    /// # Errors
6049    ///
6050    /// This returns an error when the span in this range cannot be
6051    /// represented. In general, this only occurs when asking for largest units
6052    /// of `Unit::Nanosecond` *and* when the span is too big to fit into a
6053    /// 64-bit nanosecond count.
6054    ///
6055    /// This can also return an error in other extreme cases, such as when
6056    /// adding the given span to this relative datetime results in overflow,
6057    /// or if this relative datetime is a civil datetime and it couldn't be
6058    /// converted to a timestamp in UTC.
6059    fn into_relative_span(
6060        self,
6061        largest: Unit,
6062        span: Span,
6063    ) -> Result<RelativeSpan<'a>, Error> {
6064        let kind = match self {
6065            Relative::Civil(start) => {
6066                let end = start.checked_add(span)?;
6067                RelativeSpanKind::Civil { start, end }
6068            }
6069            Relative::Zoned(start) => {
6070                let end = start.checked_add(span)?;
6071                RelativeSpanKind::Zoned { start, end }
6072            }
6073        };
6074        let relspan = kind.into_relative_span(largest)?;
6075        if span.get_sign_ranged() != C(0)
6076            && relspan.span.get_sign_ranged() != C(0)
6077            && span.get_sign_ranged() != relspan.span.get_sign_ranged()
6078        {
6079            // I haven't quite figured out when this case is hit. I think it's
6080            // actually impossible right? Balancing a duration should not flip
6081            // the sign.
6082            //
6083            // ref: https://github.com/fullcalendar/temporal-polyfill/blob/9e001042864394247181d1a5d591c18057ce32d2/packages/temporal-polyfill/src/internal/durationMath.ts#L236-L238
6084            unreachable!(
6085                "balanced span should have same sign as original span"
6086            )
6087        }
6088        Ok(relspan)
6089    }
6090
6091    /// Rounds the given span using the given rounding configuration.
6092    fn round(
6093        self,
6094        span: Span,
6095        smallest: Unit,
6096        largest: Unit,
6097        increment: NoUnits128,
6098        mode: RoundMode,
6099    ) -> Result<Span, Error> {
6100        let relspan = self.into_relative_span(largest, span)?;
6101        if relspan.span.get_sign_ranged() == C(0) {
6102            return Ok(relspan.span);
6103        }
6104        let nudge = match relspan.kind {
6105            RelativeSpanKind::Civil { start, end } => {
6106                if smallest > Unit::Day {
6107                    Nudge::relative_calendar(
6108                        relspan.span,
6109                        &Relative::Civil(start),
6110                        &Relative::Civil(end),
6111                        smallest,
6112                        increment,
6113                        mode,
6114                    )?
6115                } else {
6116                    let relative_end = end.timestamp.as_nanosecond_ranged();
6117                    Nudge::relative_invariant(
6118                        relspan.span,
6119                        relative_end.rinto(),
6120                        smallest,
6121                        largest,
6122                        increment,
6123                        mode,
6124                    )?
6125                }
6126            }
6127            RelativeSpanKind::Zoned { ref start, ref end } => {
6128                if smallest >= Unit::Day {
6129                    Nudge::relative_calendar(
6130                        relspan.span,
6131                        &Relative::Zoned(start.borrowed()),
6132                        &Relative::Zoned(end.borrowed()),
6133                        smallest,
6134                        increment,
6135                        mode,
6136                    )?
6137                } else if largest >= Unit::Day {
6138                    // This is a special case for zoned datetimes when rounding
6139                    // could bleed into variable units.
6140                    Nudge::relative_zoned_time(
6141                        relspan.span,
6142                        start,
6143                        smallest,
6144                        increment,
6145                        mode,
6146                    )?
6147                } else {
6148                    // Otherwise, rounding is the same as civil datetime.
6149                    let relative_end =
6150                        end.zoned.timestamp().as_nanosecond_ranged();
6151                    Nudge::relative_invariant(
6152                        relspan.span,
6153                        relative_end.rinto(),
6154                        smallest,
6155                        largest,
6156                        increment,
6157                        mode,
6158                    )?
6159                }
6160            }
6161        };
6162        nudge.bubble(&relspan, smallest, largest)
6163    }
6164}
6165
6166/// A balanced span between a range of civil or zoned datetimes.
6167///
6168/// The span is always balanced up to a certain unit as given to
6169/// `RelativeSpanKind::into_relative_span`.
6170#[derive(Clone, Debug)]
6171struct RelativeSpan<'a> {
6172    span: Span,
6173    kind: RelativeSpanKind<'a>,
6174}
6175
6176/// A civil or zoned datetime range of time.
6177#[derive(Clone, Debug)]
6178enum RelativeSpanKind<'a> {
6179    Civil { start: RelativeCivil, end: RelativeCivil },
6180    Zoned { start: RelativeZoned<'a>, end: RelativeZoned<'a> },
6181}
6182
6183impl<'a> RelativeSpanKind<'a> {
6184    /// Create a balanced `RelativeSpan` from this range of time.
6185    ///
6186    /// # Errors
6187    ///
6188    /// This returns an error when the span in this range cannot be
6189    /// represented. In general, this only occurs when asking for largest units
6190    /// of `Unit::Nanosecond` *and* when the span is too big to fit into a
6191    /// 64-bit nanosecond count.
6192    fn into_relative_span(
6193        self,
6194        largest: Unit,
6195    ) -> Result<RelativeSpan<'a>, Error> {
6196        let span = match self {
6197            RelativeSpanKind::Civil { ref start, ref end } => start
6198                .datetime
6199                .until((largest, end.datetime))
6200                .context(E::FailedSpanBetweenDateTimes { unit: largest })?,
6201            RelativeSpanKind::Zoned { ref start, ref end } => {
6202                start.zoned.until((largest, &*end.zoned)).context(
6203                    E::FailedSpanBetweenZonedDateTimes { unit: largest },
6204                )?
6205            }
6206        };
6207        Ok(RelativeSpan { span, kind: self })
6208    }
6209}
6210
6211/// A wrapper around a civil datetime and a timestamp corresponding to that
6212/// civil datetime in UTC.
6213///
6214/// Haphazardly interpreting a civil datetime in UTC is an odd and *usually*
6215/// incorrect thing to do. But the way we use it here is basically just to give
6216/// it an "anchoring" point such that we can represent it using a single
6217/// integer for rounding purposes. It is only used in a context *relative* to
6218/// another civil datetime interpreted in UTC. In this fashion, the selection
6219/// of UTC specifically doesn't really matter. We could use any time zone.
6220/// (Although, it must be a time zone without any transitions, otherwise we
6221/// could wind up with time zone aware results in a context where that would
6222/// be unexpected since this is civil time.)
6223#[derive(Clone, Copy, Debug)]
6224struct RelativeCivil {
6225    datetime: DateTime,
6226    timestamp: Timestamp,
6227}
6228
6229impl RelativeCivil {
6230    /// Creates a new relative wrapper around the given civil datetime.
6231    ///
6232    /// This wrapper bundles a timestamp for the given datetime by interpreting
6233    /// it as being in UTC. This is an "odd" thing to do, but it's only used
6234    /// in the context of determining the length of time between two civil
6235    /// datetimes. So technically, any time zone without transitions could be
6236    /// used.
6237    ///
6238    /// # Errors
6239    ///
6240    /// This returns an error if the datetime could not be converted to a
6241    /// timestamp. This only occurs near the minimum and maximum civil datetime
6242    /// values.
6243    fn new(datetime: DateTime) -> Result<RelativeCivil, Error> {
6244        let timestamp = datetime
6245            .to_zoned(TimeZone::UTC)
6246            .context(E::ConvertDateTimeToTimestamp)?
6247            .timestamp();
6248        Ok(RelativeCivil { datetime, timestamp })
6249    }
6250
6251    /// Returns the result of [`DateTime::checked_add`].
6252    ///
6253    /// # Errors
6254    ///
6255    /// Returns an error in the same cases as `DateTime::checked_add`. That is,
6256    /// when adding the span to this zoned datetime would overflow.
6257    ///
6258    /// This also returns an error if the resulting datetime could not be
6259    /// converted to a timestamp in UTC. This only occurs near the minimum and
6260    /// maximum datetime values.
6261    fn checked_add(&self, span: Span) -> Result<RelativeCivil, Error> {
6262        let datetime = self.datetime.checked_add(span)?;
6263        let timestamp = datetime
6264            .to_zoned(TimeZone::UTC)
6265            .context(E::ConvertDateTimeToTimestamp)?
6266            .timestamp();
6267        Ok(RelativeCivil { datetime, timestamp })
6268    }
6269
6270    /// Returns the result of [`DateTime::checked_add`] with an absolute
6271    /// duration.
6272    ///
6273    /// # Errors
6274    ///
6275    /// Returns an error in the same cases as `DateTime::checked_add`. That is,
6276    /// when adding the span to this zoned datetime would overflow.
6277    ///
6278    /// This also returns an error if the resulting datetime could not be
6279    /// converted to a timestamp in UTC. This only occurs near the minimum and
6280    /// maximum datetime values.
6281    fn checked_add_duration(
6282        &self,
6283        duration: SignedDuration,
6284    ) -> Result<RelativeCivil, Error> {
6285        let datetime = self.datetime.checked_add(duration)?;
6286        let timestamp = datetime
6287            .to_zoned(TimeZone::UTC)
6288            .context(E::ConvertDateTimeToTimestamp)?
6289            .timestamp();
6290        Ok(RelativeCivil { datetime, timestamp })
6291    }
6292
6293    /// Returns the result of [`DateTime::until`].
6294    ///
6295    /// # Errors
6296    ///
6297    /// Returns an error in the same cases as `DateTime::until`. That is, when
6298    /// the span for the given largest unit cannot be represented. This can
6299    /// generally only happen when `largest` is `Unit::Nanosecond` and the span
6300    /// cannot be represented as a 64-bit integer of nanoseconds.
6301    fn until(
6302        &self,
6303        largest: Unit,
6304        other: &RelativeCivil,
6305    ) -> Result<Span, Error> {
6306        self.datetime
6307            .until((largest, other.datetime))
6308            .context(E::FailedSpanBetweenDateTimes { unit: largest })
6309    }
6310}
6311
6312/// A simple wrapper around a possibly borrowed `Zoned`.
6313#[derive(Clone, Debug)]
6314struct RelativeZoned<'a> {
6315    zoned: DumbCow<'a, Zoned>,
6316}
6317
6318impl<'a> RelativeZoned<'a> {
6319    /// Returns the result of [`Zoned::checked_add`].
6320    ///
6321    /// # Errors
6322    ///
6323    /// Returns an error in the same cases as `Zoned::checked_add`. That is,
6324    /// when adding the span to this zoned datetime would overflow.
6325    fn checked_add(
6326        &self,
6327        span: Span,
6328    ) -> Result<RelativeZoned<'static>, Error> {
6329        let zoned = self.zoned.checked_add(span)?;
6330        Ok(RelativeZoned { zoned: DumbCow::Owned(zoned) })
6331    }
6332
6333    /// Returns the result of [`Zoned::checked_add`] with an absolute duration.
6334    ///
6335    /// # Errors
6336    ///
6337    /// Returns an error in the same cases as `Zoned::checked_add`. That is,
6338    /// when adding the span to this zoned datetime would overflow.
6339    fn checked_add_duration(
6340        &self,
6341        duration: SignedDuration,
6342    ) -> Result<RelativeZoned<'static>, Error> {
6343        let zoned = self.zoned.checked_add(duration)?;
6344        Ok(RelativeZoned { zoned: DumbCow::Owned(zoned) })
6345    }
6346
6347    /// Returns the result of [`Zoned::until`].
6348    ///
6349    /// # Errors
6350    ///
6351    /// Returns an error in the same cases as `Zoned::until`. That is, when
6352    /// the span for the given largest unit cannot be represented. This can
6353    /// generally only happen when `largest` is `Unit::Nanosecond` and the span
6354    /// cannot be represented as a 64-bit integer of nanoseconds.
6355    fn until(
6356        &self,
6357        largest: Unit,
6358        other: &RelativeZoned<'a>,
6359    ) -> Result<Span, Error> {
6360        self.zoned
6361            .until((largest, &*other.zoned))
6362            .context(E::FailedSpanBetweenZonedDateTimes { unit: largest })
6363    }
6364
6365    /// Returns the borrowed version of self; useful when you need to convert
6366    /// `&RelativeZoned` into `RelativeZoned` without cloning anything.
6367    fn borrowed(&'a self) -> RelativeZoned<'a> {
6368        RelativeZoned { zoned: self.zoned.borrowed() }
6369    }
6370}
6371
6372// The code below is the "core" rounding logic for spans. It was greatly
6373// inspired by this gist[1] and the fullcalendar Temporal polyfill[2]. In
6374// particular, the algorithm implemented below is a major simplification from
6375// how Temporal used to work[3]. Parts of it are still in rough and unclear
6376// shape IMO.
6377//
6378// [1]: https://gist.github.com/arshaw/36d3152c21482bcb78ea2c69591b20e0
6379// [2]: https://github.com/fullcalendar/temporal-polyfill
6380// [3]: https://github.com/tc39/proposal-temporal/issues/2792
6381
6382/// The result of a span rounding strategy. There are three:
6383///
6384/// * Rounding spans relative to civil datetimes using only invariant
6385/// units (days or less). This is achieved by converting the span to a simple
6386/// integer number of nanoseconds and then rounding that.
6387/// * Rounding spans relative to either a civil datetime or a zoned datetime
6388/// where rounding might involve changing non-uniform units. That is, when
6389/// the smallest unit is greater than days for civil datetimes and greater
6390/// than hours for zoned datetimes.
6391/// * Rounding spans relative to a zoned datetime whose smallest unit is
6392/// less than days.
6393///
6394/// Each of these might produce a bottom heavy span that needs to be
6395/// re-balanced. This type represents that result via one of three constructors
6396/// corresponding to each of the above strategies, and then provides a routine
6397/// for rebalancing via "bubbling."
6398#[derive(Debug)]
6399struct Nudge {
6400    /// A possibly bottom heavy rounded span.
6401    span: Span,
6402    /// The nanosecond timestamp corresponding to `relative + span`, where
6403    /// `span` is the (possibly bottom heavy) rounded span.
6404    rounded_relative_end: NoUnits128,
6405    /// Whether rounding may have created a bottom heavy span such that a
6406    /// calendar unit might need to be incremented after re-balancing smaller
6407    /// units.
6408    grew_big_unit: bool,
6409}
6410
6411impl Nudge {
6412    /// Performs rounding on the given span limited to invariant units.
6413    ///
6414    /// For civil datetimes, this means the smallest unit must be days or less,
6415    /// but the largest unit can be bigger. For zoned datetimes, this means
6416    /// that *both* the largest and smallest unit must be hours or less. This
6417    /// is because zoned datetimes with rounding that can spill up to days
6418    /// requires special handling.
6419    ///
6420    /// It works by converting the span to a single integer number of
6421    /// nanoseconds, rounding it and then converting back to a span.
6422    fn relative_invariant(
6423        balanced: Span,
6424        relative_end: NoUnits128,
6425        smallest: Unit,
6426        largest: Unit,
6427        increment: NoUnits128,
6428        mode: RoundMode,
6429    ) -> Result<Nudge, Error> {
6430        // Ensures this is only called when rounding invariant units.
6431        assert!(smallest <= Unit::Week);
6432
6433        let sign = balanced.get_sign_ranged();
6434        let balanced_nanos = balanced.to_invariant_nanoseconds();
6435        let rounded_nanos = mode.round_by_unit_in_nanoseconds(
6436            balanced_nanos,
6437            smallest,
6438            increment,
6439        );
6440        let span = Span::from_invariant_nanoseconds(largest, rounded_nanos)
6441            .context(E::ConvertNanoseconds { unit: largest })?
6442            .years_ranged(balanced.get_years_ranged())
6443            .months_ranged(balanced.get_months_ranged())
6444            .weeks_ranged(balanced.get_weeks_ranged());
6445
6446        let diff_nanos = rounded_nanos - balanced_nanos;
6447        let diff_days = rounded_nanos.div_ceil(t::NANOS_PER_CIVIL_DAY)
6448            - balanced_nanos.div_ceil(t::NANOS_PER_CIVIL_DAY);
6449        let grew_big_unit = diff_days.signum() == sign;
6450        let rounded_relative_end = relative_end + diff_nanos;
6451        Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6452    }
6453
6454    /// Performs rounding on the given span where the smallest unit configured
6455    /// implies that rounding will cover calendar or "non-uniform" units. (That
6456    /// is, units whose length can change based on the relative datetime.)
6457    fn relative_calendar(
6458        balanced: Span,
6459        relative_start: &Relative<'_>,
6460        relative_end: &Relative<'_>,
6461        smallest: Unit,
6462        increment: NoUnits128,
6463        mode: RoundMode,
6464    ) -> Result<Nudge, Error> {
6465        #[cfg(not(feature = "std"))]
6466        use crate::util::libm::Float;
6467
6468        assert!(smallest >= Unit::Day);
6469        let sign = balanced.get_sign_ranged();
6470        let truncated = increment
6471            * balanced.get_units_ranged(smallest).div_ceil(increment);
6472        let span = balanced
6473            .without_lower(smallest)
6474            .try_units_ranged(smallest, truncated.rinto())?;
6475        let (relative0, relative1) = clamp_relative_span(
6476            relative_start,
6477            span,
6478            smallest,
6479            NoUnits::try_rfrom("increment", increment)?
6480                .try_checked_mul("signed increment", sign)?,
6481        )?;
6482
6483        // FIXME: This is brutal. This is the only non-optional floating point
6484        // used so far in Jiff. We do expose floating point for things like
6485        // `Span::total`, but that's optional and not a core part of Jiff's
6486        // functionality. This is in the core part of Jiff's span rounding...
6487        let denom = (relative1 - relative0).get() as f64;
6488        let numer = (relative_end.to_nanosecond() - relative0).get() as f64;
6489        let exact = (truncated.get() as f64)
6490            + (numer / denom) * (sign.get() as f64) * (increment.get() as f64);
6491        let rounded = mode.round_float(exact, increment);
6492        let grew_big_unit =
6493            ((rounded.get() as f64) - exact).signum() == (sign.get() as f64);
6494
6495        let span = span.try_units_ranged(smallest, rounded.rinto())?;
6496        let rounded_relative_end =
6497            if grew_big_unit { relative1 } else { relative0 };
6498        Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6499    }
6500
6501    /// Performs rounding on the given span where the smallest unit is hours
6502    /// or less *and* the relative datetime is time zone aware.
6503    fn relative_zoned_time(
6504        balanced: Span,
6505        relative_start: &RelativeZoned<'_>,
6506        smallest: Unit,
6507        increment: NoUnits128,
6508        mode: RoundMode,
6509    ) -> Result<Nudge, Error> {
6510        let sign = balanced.get_sign_ranged();
6511        let time_nanos =
6512            balanced.only_lower(Unit::Day).to_invariant_nanoseconds();
6513        let mut rounded_time_nanos =
6514            mode.round_by_unit_in_nanoseconds(time_nanos, smallest, increment);
6515        let (relative0, relative1) = clamp_relative_span(
6516            // FIXME: Find a way to drop this clone.
6517            &Relative::Zoned(relative_start.clone()),
6518            balanced.without_lower(Unit::Day),
6519            Unit::Day,
6520            sign.rinto(),
6521        )?;
6522        let day_nanos = relative1 - relative0;
6523        let beyond_day_nanos = rounded_time_nanos - day_nanos;
6524
6525        let mut day_delta = NoUnits::N::<0>();
6526        let rounded_relative_end =
6527            if beyond_day_nanos == C(0) || beyond_day_nanos.signum() == sign {
6528                day_delta += C(1);
6529                rounded_time_nanos = mode.round_by_unit_in_nanoseconds(
6530                    beyond_day_nanos,
6531                    smallest,
6532                    increment,
6533                );
6534                relative1 + rounded_time_nanos
6535            } else {
6536                relative0 + rounded_time_nanos
6537            };
6538
6539        let span =
6540            Span::from_invariant_nanoseconds(Unit::Hour, rounded_time_nanos)
6541                .context(E::ConvertNanoseconds { unit: Unit::Hour })?
6542                .years_ranged(balanced.get_years_ranged())
6543                .months_ranged(balanced.get_months_ranged())
6544                .weeks_ranged(balanced.get_weeks_ranged())
6545                .days_ranged(balanced.get_days_ranged() + day_delta);
6546        let grew_big_unit = day_delta != C(0);
6547        Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6548    }
6549
6550    /// This "bubbles" up the units in a potentially "bottom heavy" span to
6551    /// larger units. For example, P1m50d relative to March 1 is bottom heavy.
6552    /// This routine will bubble the days up to months to get P2m19d.
6553    ///
6554    /// # Errors
6555    ///
6556    /// This routine fails if any arithmetic on the individual units fails, or
6557    /// when span arithmetic on the relative datetime given fails.
6558    fn bubble(
6559        &self,
6560        relative: &RelativeSpan,
6561        smallest: Unit,
6562        largest: Unit,
6563    ) -> Result<Span, Error> {
6564        if !self.grew_big_unit || smallest == Unit::Week {
6565            return Ok(self.span);
6566        }
6567
6568        let smallest = smallest.max(Unit::Day);
6569        let mut balanced = self.span;
6570        let sign = balanced.get_sign_ranged();
6571        let mut unit = smallest;
6572        while let Some(u) = unit.next() {
6573            unit = u;
6574            if unit > largest {
6575                break;
6576            }
6577            // We only bubble smaller units up into weeks when the largest unit
6578            // is explicitly set to weeks. Otherwise, we leave it as-is.
6579            if unit == Unit::Week && largest != Unit::Week {
6580                continue;
6581            }
6582
6583            let span_start = balanced.without_lower(unit);
6584            let new_units = span_start
6585                .get_units_ranged(unit)
6586                .try_checked_add("bubble-units", sign)?;
6587            let span_end = span_start.try_units_ranged(unit, new_units)?;
6588            let threshold = match relative.kind {
6589                RelativeSpanKind::Civil { ref start, .. } => {
6590                    start.checked_add(span_end)?.timestamp
6591                }
6592                RelativeSpanKind::Zoned { ref start, .. } => {
6593                    start.checked_add(span_end)?.zoned.timestamp()
6594                }
6595            };
6596            let beyond =
6597                self.rounded_relative_end - threshold.as_nanosecond_ranged();
6598            if beyond == C(0) || beyond.signum() == sign {
6599                balanced = span_end;
6600            } else {
6601                break;
6602            }
6603        }
6604        Ok(balanced)
6605    }
6606}
6607
6608/// Rounds a span consisting of only invariant units.
6609///
6610/// This only applies when the max of the units in the span being rounded,
6611/// the largest configured unit and the smallest configured unit are all
6612/// invariant. That is, days or lower for spans without a relative datetime or
6613/// a relative civil datetime, and hours or lower for spans with a relative
6614/// zoned datetime.
6615///
6616/// All we do here is convert the span to an integer number of nanoseconds,
6617/// round that and then convert back. There aren't any tricky corner cases to
6618/// consider here.
6619fn round_span_invariant(
6620    span: Span,
6621    smallest: Unit,
6622    largest: Unit,
6623    increment: NoUnits128,
6624    mode: RoundMode,
6625) -> Result<Span, Error> {
6626    assert!(smallest <= Unit::Week);
6627    assert!(largest <= Unit::Week);
6628    let nanos = span.to_invariant_nanoseconds();
6629    let rounded =
6630        mode.round_by_unit_in_nanoseconds(nanos, smallest, increment);
6631    Span::from_invariant_nanoseconds(largest, rounded)
6632        .context(E::ConvertNanoseconds { unit: largest })
6633}
6634
6635/// Returns the nanosecond timestamps of `relative + span` and `relative +
6636/// {amount of unit} + span`.
6637///
6638/// This is useful for determining the actual length, in nanoseconds, of some
6639/// unit amount (usually a single unit). Usually, this is called with a span
6640/// whose units lower than `unit` are zeroed out and with an `amount` that
6641/// is `-1` or `1` or `0`. So for example, if `unit` were `Unit::Day`, then
6642/// you'd get back two nanosecond timestamps relative to the relative datetime
6643/// given that start exactly "one day" apart. (Which might be different than 24
6644/// hours, depending on the time zone.)
6645///
6646/// # Errors
6647///
6648/// This returns an error if adding the units overflows, or if doing the span
6649/// arithmetic on `relative` overflows.
6650fn clamp_relative_span(
6651    relative: &Relative<'_>,
6652    span: Span,
6653    unit: Unit,
6654    amount: NoUnits,
6655) -> Result<(NoUnits128, NoUnits128), Error> {
6656    let amount =
6657        span.get_units_ranged(unit).try_checked_add("clamp-units", amount)?;
6658    let span_amount = span.try_units_ranged(unit, amount)?;
6659    let relative0 = relative.checked_add(span)?.to_nanosecond();
6660    let relative1 = relative.checked_add(span_amount)?.to_nanosecond();
6661    Ok((relative0, relative1))
6662}
6663
6664/// A common parsing function that works in bytes.
6665///
6666/// Specifically, this parses either an ISO 8601 duration into a `Span` or
6667/// a "friendly" duration into a `Span`. It also tries to give decent error
6668/// messages.
6669///
6670/// This works because the friendly and ISO 8601 formats have non-overlapping
6671/// prefixes. Both can start with a `+` or `-`, but aside from that, an ISO
6672/// 8601 duration _always_ has to start with a `P` or `p`. We can utilize this
6673/// property to very quickly determine how to parse the input. We just need to
6674/// handle the possibly ambiguous case with a leading sign a little carefully
6675/// in order to ensure good error messages.
6676///
6677/// (We do the same thing for `SignedDuration`.)
6678#[cfg_attr(feature = "perf-inline", inline(always))]
6679fn parse_iso_or_friendly(bytes: &[u8]) -> Result<Span, Error> {
6680    let Some((&byte, tail)) = bytes.split_first() else {
6681        return Err(crate::Error::from(
6682            crate::error::fmt::Error::HybridDurationEmpty,
6683        ));
6684    };
6685    let mut first = byte;
6686    // N.B. Unsigned durations don't support negative durations (of
6687    // course), but we still check for it here so that we can defer to
6688    // the dedicated parsers. They will provide their own error messages.
6689    if first == b'+' || first == b'-' {
6690        let Some(&byte) = tail.first() else {
6691            return Err(crate::Error::from(
6692                crate::error::fmt::Error::HybridDurationPrefix { sign: first },
6693            ));
6694        };
6695        first = byte;
6696    }
6697    if first == b'P' || first == b'p' {
6698        temporal::DEFAULT_SPAN_PARSER.parse_span(bytes)
6699    } else {
6700        friendly::DEFAULT_SPAN_PARSER.parse_span(bytes)
6701    }
6702}
6703
6704fn requires_relative_date_err(unit: Unit) -> Result<(), Error> {
6705    if unit.is_variable() {
6706        return Err(Error::from(if matches!(unit, Unit::Week | Unit::Day) {
6707            E::RequiresRelativeWeekOrDay { unit }
6708        } else {
6709            E::RequiresRelativeYearOrMonth { unit }
6710        }));
6711    }
6712    Ok(())
6713}
6714
6715#[cfg(test)]
6716mod tests {
6717    use std::io::Cursor;
6718
6719    use alloc::string::ToString;
6720
6721    use crate::{civil::date, RoundMode};
6722
6723    use super::*;
6724
6725    #[test]
6726    fn test_total() {
6727        if crate::tz::db().is_definitively_empty() {
6728            return;
6729        }
6730
6731        let span = 130.hours().minutes(20);
6732        let total = span.total(Unit::Second).unwrap();
6733        assert_eq!(total, 469200.0);
6734
6735        let span = 123456789.seconds();
6736        let total = span
6737            .total(SpanTotal::from(Unit::Day).days_are_24_hours())
6738            .unwrap();
6739        assert_eq!(total, 1428.8980208333332);
6740
6741        let span = 2756.hours();
6742        let dt = date(2020, 1, 1).at(0, 0, 0, 0);
6743        let zdt = dt.in_tz("Europe/Rome").unwrap();
6744        let total = span.total((Unit::Month, &zdt)).unwrap();
6745        assert_eq!(total, 3.7958333333333334);
6746        let total = span.total((Unit::Month, dt)).unwrap();
6747        assert_eq!(total, 3.7944444444444443);
6748    }
6749
6750    #[test]
6751    fn test_compare() {
6752        if crate::tz::db().is_definitively_empty() {
6753            return;
6754        }
6755
6756        let span1 = 79.hours().minutes(10);
6757        let span2 = 79.hours().seconds(630);
6758        let span3 = 78.hours().minutes(50);
6759        let mut array = [span1, span2, span3];
6760        array.sort_by(|sp1, sp2| sp1.compare(sp2).unwrap());
6761        assert_eq!(array, [span3, span1, span2].map(SpanFieldwise));
6762
6763        let day24 = SpanRelativeTo::days_are_24_hours();
6764        let span1 = 79.hours().minutes(10);
6765        let span2 = 3.days().hours(7).seconds(630);
6766        let span3 = 3.days().hours(6).minutes(50);
6767        let mut array = [span1, span2, span3];
6768        array.sort_by(|sp1, sp2| sp1.compare((sp2, day24)).unwrap());
6769        assert_eq!(array, [span3, span1, span2].map(SpanFieldwise));
6770
6771        let dt = date(2020, 11, 1).at(0, 0, 0, 0);
6772        let zdt = dt.in_tz("America/Los_Angeles").unwrap();
6773        array.sort_by(|sp1, sp2| sp1.compare((sp2, &zdt)).unwrap());
6774        assert_eq!(array, [span1, span3, span2].map(SpanFieldwise));
6775    }
6776
6777    #[test]
6778    fn test_checked_add() {
6779        let span1 = 1.hour();
6780        let span2 = 30.minutes();
6781        let sum = span1.checked_add(span2).unwrap();
6782        span_eq!(sum, 1.hour().minutes(30));
6783
6784        let span1 = 1.hour().minutes(30);
6785        let span2 = 2.hours().minutes(45);
6786        let sum = span1.checked_add(span2).unwrap();
6787        span_eq!(sum, 4.hours().minutes(15));
6788
6789        let span = 50
6790            .years()
6791            .months(50)
6792            .days(50)
6793            .hours(50)
6794            .minutes(50)
6795            .seconds(50)
6796            .milliseconds(500)
6797            .microseconds(500)
6798            .nanoseconds(500);
6799        let relative = date(1900, 1, 1).at(0, 0, 0, 0);
6800        let sum = span.checked_add((span, relative)).unwrap();
6801        let expected = 108
6802            .years()
6803            .months(7)
6804            .days(12)
6805            .hours(5)
6806            .minutes(41)
6807            .seconds(41)
6808            .milliseconds(1)
6809            .microseconds(1)
6810            .nanoseconds(0);
6811        span_eq!(sum, expected);
6812
6813        let span = 1.month().days(15);
6814        let relative = date(2000, 2, 1).at(0, 0, 0, 0);
6815        let sum = span.checked_add((span, relative)).unwrap();
6816        span_eq!(sum, 3.months());
6817        let relative = date(2000, 3, 1).at(0, 0, 0, 0);
6818        let sum = span.checked_add((span, relative)).unwrap();
6819        span_eq!(sum, 2.months().days(30));
6820    }
6821
6822    #[test]
6823    fn test_round_day_time() {
6824        let span = 29.seconds();
6825        let rounded = span.round(Unit::Minute).unwrap();
6826        span_eq!(rounded, 0.minute());
6827
6828        let span = 30.seconds();
6829        let rounded = span.round(Unit::Minute).unwrap();
6830        span_eq!(rounded, 1.minute());
6831
6832        let span = 8.seconds();
6833        let rounded = span
6834            .round(
6835                SpanRound::new()
6836                    .smallest(Unit::Nanosecond)
6837                    .largest(Unit::Microsecond),
6838            )
6839            .unwrap();
6840        span_eq!(rounded, 8_000_000.microseconds());
6841
6842        let span = 130.minutes();
6843        let rounded = span
6844            .round(SpanRound::new().largest(Unit::Day).days_are_24_hours())
6845            .unwrap();
6846        span_eq!(rounded, 2.hours().minutes(10));
6847
6848        let span = 10.minutes().seconds(52);
6849        let rounded = span.round(Unit::Minute).unwrap();
6850        span_eq!(rounded, 11.minutes());
6851
6852        let span = 10.minutes().seconds(52);
6853        let rounded = span
6854            .round(
6855                SpanRound::new().smallest(Unit::Minute).mode(RoundMode::Trunc),
6856            )
6857            .unwrap();
6858        span_eq!(rounded, 10.minutes());
6859
6860        let span = 2.hours().minutes(34).seconds(18);
6861        let rounded =
6862            span.round(SpanRound::new().largest(Unit::Second)).unwrap();
6863        span_eq!(rounded, 9258.seconds());
6864
6865        let span = 6.minutes();
6866        let rounded = span
6867            .round(
6868                SpanRound::new()
6869                    .smallest(Unit::Minute)
6870                    .increment(5)
6871                    .mode(RoundMode::Ceil),
6872            )
6873            .unwrap();
6874        span_eq!(rounded, 10.minutes());
6875    }
6876
6877    #[test]
6878    fn test_round_relative_zoned_calendar() {
6879        if crate::tz::db().is_definitively_empty() {
6880            return;
6881        }
6882
6883        let span = 2756.hours();
6884        let relative =
6885            date(2020, 1, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6886        let options = SpanRound::new()
6887            .largest(Unit::Year)
6888            .smallest(Unit::Day)
6889            .relative(&relative);
6890        let rounded = span.round(options).unwrap();
6891        span_eq!(rounded, 3.months().days(24));
6892
6893        let span = 24.hours().nanoseconds(5);
6894        let relative = date(2000, 10, 29)
6895            .at(0, 0, 0, 0)
6896            .in_tz("America/Vancouver")
6897            .unwrap();
6898        let options = SpanRound::new()
6899            .largest(Unit::Day)
6900            .smallest(Unit::Minute)
6901            .relative(&relative)
6902            .mode(RoundMode::Expand)
6903            .increment(30);
6904        let rounded = span.round(options).unwrap();
6905        // It seems like this is the correct answer, although it apparently
6906        // differs from Temporal and the FullCalendar polyfill. I'm not sure
6907        // what accounts for the difference in the implementation.
6908        //
6909        // See: https://github.com/tc39/proposal-temporal/pull/2758#discussion_r1597255245
6910        span_eq!(rounded, 24.hours().minutes(30));
6911
6912        // Ref: https://github.com/tc39/proposal-temporal/issues/2816#issuecomment-2115608460
6913        let span = -1.month().hours(24);
6914        let relative: crate::Zoned = date(2024, 4, 11)
6915            .at(2, 0, 0, 0)
6916            .in_tz("America/New_York")
6917            .unwrap();
6918        let options =
6919            SpanRound::new().smallest(Unit::Millisecond).relative(&relative);
6920        let rounded = span.round(options).unwrap();
6921        span_eq!(rounded, -1.month().days(1).hours(1));
6922        let dt = relative.checked_add(span).unwrap();
6923        let diff = relative.until((Unit::Month, &dt)).unwrap();
6924        span_eq!(diff, -1.month().days(1).hours(1));
6925
6926        // Like the above, but don't use a datetime near a DST transition. In
6927        // this case, a day is a normal 24 hours. (Unlike above, where the
6928        // duration includes a 23 hour day, and so an additional hour has to be
6929        // added to the span to account for that.)
6930        let span = -1.month().hours(24);
6931        let relative = date(2024, 6, 11)
6932            .at(2, 0, 0, 0)
6933            .in_tz("America/New_York")
6934            .unwrap();
6935        let options =
6936            SpanRound::new().smallest(Unit::Millisecond).relative(&relative);
6937        let rounded = span.round(options).unwrap();
6938        span_eq!(rounded, -1.month().days(1));
6939    }
6940
6941    #[test]
6942    fn test_round_relative_zoned_time() {
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().largest(Unit::Year).relative(&relative);
6951        let rounded = span.round(options).unwrap();
6952        span_eq!(rounded, 3.months().days(23).hours(21));
6953
6954        let span = 2756.hours();
6955        let relative =
6956            date(2020, 9, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6957        let options = SpanRound::new().largest(Unit::Year).relative(&relative);
6958        let rounded = span.round(options).unwrap();
6959        span_eq!(rounded, 3.months().days(23).hours(19));
6960
6961        let span = 3.hours();
6962        let relative =
6963            date(2020, 3, 8).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6964        let options = SpanRound::new().largest(Unit::Year).relative(&relative);
6965        let rounded = span.round(options).unwrap();
6966        span_eq!(rounded, 3.hours());
6967    }
6968
6969    #[test]
6970    fn test_round_relative_day_time() {
6971        let span = 2756.hours();
6972        let options =
6973            SpanRound::new().largest(Unit::Year).relative(date(2020, 1, 1));
6974        let rounded = span.round(options).unwrap();
6975        span_eq!(rounded, 3.months().days(23).hours(20));
6976
6977        let span = 2756.hours();
6978        let options =
6979            SpanRound::new().largest(Unit::Year).relative(date(2020, 9, 1));
6980        let rounded = span.round(options).unwrap();
6981        span_eq!(rounded, 3.months().days(23).hours(20));
6982
6983        let span = 190.days();
6984        let options =
6985            SpanRound::new().largest(Unit::Year).relative(date(2020, 1, 1));
6986        let rounded = span.round(options).unwrap();
6987        span_eq!(rounded, 6.months().days(8));
6988
6989        let span = 30
6990            .days()
6991            .hours(23)
6992            .minutes(59)
6993            .seconds(59)
6994            .milliseconds(999)
6995            .microseconds(999)
6996            .nanoseconds(999);
6997        let options = SpanRound::new()
6998            .smallest(Unit::Microsecond)
6999            .largest(Unit::Year)
7000            .relative(date(2024, 5, 1));
7001        let rounded = span.round(options).unwrap();
7002        span_eq!(rounded, 1.month());
7003
7004        let span = 364
7005            .days()
7006            .hours(23)
7007            .minutes(59)
7008            .seconds(59)
7009            .milliseconds(999)
7010            .microseconds(999)
7011            .nanoseconds(999);
7012        let options = SpanRound::new()
7013            .smallest(Unit::Microsecond)
7014            .largest(Unit::Year)
7015            .relative(date(2023, 1, 1));
7016        let rounded = span.round(options).unwrap();
7017        span_eq!(rounded, 1.year());
7018
7019        let span = 365
7020            .days()
7021            .hours(23)
7022            .minutes(59)
7023            .seconds(59)
7024            .milliseconds(999)
7025            .microseconds(999)
7026            .nanoseconds(999);
7027        let options = SpanRound::new()
7028            .smallest(Unit::Microsecond)
7029            .largest(Unit::Year)
7030            .relative(date(2023, 1, 1));
7031        let rounded = span.round(options).unwrap();
7032        span_eq!(rounded, 1.year().days(1));
7033
7034        let span = 365
7035            .days()
7036            .hours(23)
7037            .minutes(59)
7038            .seconds(59)
7039            .milliseconds(999)
7040            .microseconds(999)
7041            .nanoseconds(999);
7042        let options = SpanRound::new()
7043            .smallest(Unit::Microsecond)
7044            .largest(Unit::Year)
7045            .relative(date(2024, 1, 1));
7046        let rounded = span.round(options).unwrap();
7047        span_eq!(rounded, 1.year());
7048
7049        let span = 3.hours();
7050        let options =
7051            SpanRound::new().largest(Unit::Year).relative(date(2020, 3, 8));
7052        let rounded = span.round(options).unwrap();
7053        span_eq!(rounded, 3.hours());
7054    }
7055
7056    #[test]
7057    fn span_sign() {
7058        assert_eq!(Span::new().get_sign_ranged(), C(0));
7059        assert_eq!(Span::new().days(1).get_sign_ranged(), C(1));
7060        assert_eq!(Span::new().days(-1).get_sign_ranged(), C(-1));
7061        assert_eq!(Span::new().days(1).days(0).get_sign_ranged(), C(0));
7062        assert_eq!(Span::new().days(-1).days(0).get_sign_ranged(), C(0));
7063        assert_eq!(
7064            Span::new().years(1).days(1).days(0).get_sign_ranged(),
7065            C(1)
7066        );
7067        assert_eq!(
7068            Span::new().years(-1).days(-1).days(0).get_sign_ranged(),
7069            C(-1)
7070        );
7071    }
7072
7073    #[test]
7074    fn span_size() {
7075        #[cfg(target_pointer_width = "64")]
7076        {
7077            #[cfg(debug_assertions)]
7078            {
7079                assert_eq!(core::mem::align_of::<Span>(), 8);
7080                assert_eq!(core::mem::size_of::<Span>(), 184);
7081            }
7082            #[cfg(not(debug_assertions))]
7083            {
7084                assert_eq!(core::mem::align_of::<Span>(), 8);
7085                assert_eq!(core::mem::size_of::<Span>(), 64);
7086            }
7087        }
7088    }
7089
7090    quickcheck::quickcheck! {
7091        fn prop_roundtrip_span_nanoseconds(span: Span) -> quickcheck::TestResult {
7092            let largest = span.largest_unit();
7093            if largest > Unit::Day {
7094                return quickcheck::TestResult::discard();
7095            }
7096            let nanos = span.to_invariant_nanoseconds();
7097            let got = Span::from_invariant_nanoseconds(largest, nanos).unwrap();
7098            quickcheck::TestResult::from_bool(nanos == got.to_invariant_nanoseconds())
7099        }
7100    }
7101
7102    /// # `serde` deserializer compatibility test
7103    ///
7104    /// Serde YAML used to be unable to deserialize `jiff` types,
7105    /// as deserializing from bytes is not supported by the deserializer.
7106    ///
7107    /// - <https://github.com/BurntSushi/jiff/issues/138>
7108    /// - <https://github.com/BurntSushi/jiff/discussions/148>
7109    #[test]
7110    fn span_deserialize_yaml() {
7111        let expected = Span::new()
7112            .years(1)
7113            .months(2)
7114            .weeks(3)
7115            .days(4)
7116            .hours(5)
7117            .minutes(6)
7118            .seconds(7);
7119
7120        let deserialized: Span =
7121            serde_yaml::from_str("P1y2m3w4dT5h6m7s").unwrap();
7122
7123        span_eq!(deserialized, expected);
7124
7125        let deserialized: Span =
7126            serde_yaml::from_slice("P1y2m3w4dT5h6m7s".as_bytes()).unwrap();
7127
7128        span_eq!(deserialized, expected);
7129
7130        let cursor = Cursor::new(b"P1y2m3w4dT5h6m7s");
7131        let deserialized: Span = serde_yaml::from_reader(cursor).unwrap();
7132
7133        span_eq!(deserialized, expected);
7134    }
7135
7136    #[test]
7137    fn display() {
7138        let span = Span::new()
7139            .years(1)
7140            .months(2)
7141            .weeks(3)
7142            .days(4)
7143            .hours(5)
7144            .minutes(6)
7145            .seconds(7)
7146            .milliseconds(8)
7147            .microseconds(9)
7148            .nanoseconds(10);
7149        insta::assert_snapshot!(
7150            span,
7151            @"P1Y2M3W4DT5H6M7.00800901S",
7152        );
7153        insta::assert_snapshot!(
7154            alloc::format!("{span:#}"),
7155            @"1y 2mo 3w 4d 5h 6m 7s 8ms 9µs 10ns",
7156        );
7157    }
7158
7159    /// This test ensures that we can parse `humantime` formatted durations.
7160    #[test]
7161    fn humantime_compatibility_parse() {
7162        let dur = std::time::Duration::new(60 * 60 * 24 * 411, 123_456_789);
7163        let formatted = humantime::format_duration(dur).to_string();
7164        assert_eq!(
7165            formatted,
7166            "1year 1month 15days 7h 26m 24s 123ms 456us 789ns"
7167        );
7168        let expected = 1
7169            .year()
7170            .months(1)
7171            .days(15)
7172            .hours(7)
7173            .minutes(26)
7174            .seconds(24)
7175            .milliseconds(123)
7176            .microseconds(456)
7177            .nanoseconds(789);
7178        span_eq!(formatted.parse::<Span>().unwrap(), expected);
7179    }
7180
7181    /// This test ensures that we can print a `Span` that `humantime` can
7182    /// parse.
7183    ///
7184    /// Note that this isn't the default since `humantime`'s parser is
7185    /// pretty limited. e.g., It doesn't support things like `nsecs`
7186    /// despite supporting `secs`. And other reasons. See the docs on
7187    /// `Designator::HumanTime` for why we sadly provide a custom variant for
7188    /// it.
7189    #[test]
7190    fn humantime_compatibility_print() {
7191        static PRINTER: friendly::SpanPrinter = friendly::SpanPrinter::new()
7192            .designator(friendly::Designator::HumanTime);
7193
7194        let span = 1
7195            .year()
7196            .months(1)
7197            .days(15)
7198            .hours(7)
7199            .minutes(26)
7200            .seconds(24)
7201            .milliseconds(123)
7202            .microseconds(456)
7203            .nanoseconds(789);
7204        let formatted = PRINTER.span_to_string(&span);
7205        assert_eq!(formatted, "1y 1month 15d 7h 26m 24s 123ms 456us 789ns");
7206
7207        let dur = humantime::parse_duration(&formatted).unwrap();
7208        let expected =
7209            std::time::Duration::new(60 * 60 * 24 * 411, 123_456_789);
7210        assert_eq!(dur, expected);
7211    }
7212
7213    #[test]
7214    fn from_str() {
7215        let p = |s: &str| -> Result<Span, Error> { s.parse() };
7216
7217        insta::assert_snapshot!(
7218            p("1 day").unwrap(),
7219            @"P1D",
7220        );
7221        insta::assert_snapshot!(
7222            p("+1 day").unwrap(),
7223            @"P1D",
7224        );
7225        insta::assert_snapshot!(
7226            p("-1 day").unwrap(),
7227            @"-P1D",
7228        );
7229        insta::assert_snapshot!(
7230            p("P1d").unwrap(),
7231            @"P1D",
7232        );
7233        insta::assert_snapshot!(
7234            p("+P1d").unwrap(),
7235            @"P1D",
7236        );
7237        insta::assert_snapshot!(
7238            p("-P1d").unwrap(),
7239            @"-P1D",
7240        );
7241
7242        insta::assert_snapshot!(
7243            p("").unwrap_err(),
7244            @r#"an empty string is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format"#,
7245        );
7246        insta::assert_snapshot!(
7247            p("+").unwrap_err(),
7248            @r#"found nothing after sign `+`, which is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format"#,
7249        );
7250        insta::assert_snapshot!(
7251            p("-").unwrap_err(),
7252            @r#"found nothing after sign `-`, which is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format"#,
7253        );
7254    }
7255
7256    #[test]
7257    fn serde_deserialize() {
7258        let p = |s: &str| -> Result<Span, serde_json::Error> {
7259            serde_json::from_str(&alloc::format!("\"{s}\""))
7260        };
7261
7262        insta::assert_snapshot!(
7263            p("1 day").unwrap(),
7264            @"P1D",
7265        );
7266        insta::assert_snapshot!(
7267            p("+1 day").unwrap(),
7268            @"P1D",
7269        );
7270        insta::assert_snapshot!(
7271            p("-1 day").unwrap(),
7272            @"-P1D",
7273        );
7274        insta::assert_snapshot!(
7275            p("P1d").unwrap(),
7276            @"P1D",
7277        );
7278        insta::assert_snapshot!(
7279            p("+P1d").unwrap(),
7280            @"P1D",
7281        );
7282        insta::assert_snapshot!(
7283            p("-P1d").unwrap(),
7284            @"-P1D",
7285        );
7286
7287        insta::assert_snapshot!(
7288            p("").unwrap_err(),
7289            @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"#,
7290        );
7291        insta::assert_snapshot!(
7292            p("+").unwrap_err(),
7293            @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"#,
7294        );
7295        insta::assert_snapshot!(
7296            p("-").unwrap_err(),
7297            @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"#,
7298        );
7299    }
7300}