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