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