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