jiff/
span.rs

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