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