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