Skip to main content

jiff/
span.rs

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