Skip to main content

jiff/
span.rs

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