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
2457 let seconds = nanos / t::NANOS_PER_SECOND;
2458 let seconds = i64::from(seconds);
2459 let subsec_nanos = nanos % t::NANOS_PER_SECOND;
2460 // OK because % 1_000_000_000 above guarantees that the result fits
2461 // in a i32.
2462 let subsec_nanos = i32::try_from(subsec_nanos).unwrap();
2463
2464 // SignedDuration::new can panic if |subsec_nanos| >= 1_000_000_000
2465 // and seconds == {i64::MIN,i64::MAX}. But this can never happen
2466 // because we guaranteed by construction above that |subsec_nanos| <
2467 // 1_000_000_000.
2468 SignedDuration::new(seconds, subsec_nanos)
2469 }
2470}
2471
2472/// Crate internal APIs that operate on ranged integer types.
2473impl Span {
2474 #[inline]
2475 pub(crate) fn years_ranged(self, years: t::SpanYears) -> Span {
2476 let mut span = Span { years: years.abs(), ..self };
2477 span.sign = self.resign(years, &span);
2478 span.units = span.units.set(Unit::Year, years == C(0));
2479 span
2480 }
2481
2482 #[inline]
2483 pub(crate) fn months_ranged(self, months: t::SpanMonths) -> Span {
2484 let mut span = Span { months: months.abs(), ..self };
2485 span.sign = self.resign(months, &span);
2486 span.units = span.units.set(Unit::Month, months == C(0));
2487 span
2488 }
2489
2490 #[inline]
2491 pub(crate) fn weeks_ranged(self, weeks: t::SpanWeeks) -> Span {
2492 let mut span = Span { weeks: weeks.abs(), ..self };
2493 span.sign = self.resign(weeks, &span);
2494 span.units = span.units.set(Unit::Week, weeks == C(0));
2495 span
2496 }
2497
2498 #[inline]
2499 pub(crate) fn days_ranged(self, days: t::SpanDays) -> Span {
2500 let mut span = Span { days: days.abs(), ..self };
2501 span.sign = self.resign(days, &span);
2502 span.units = span.units.set(Unit::Day, days == C(0));
2503 span
2504 }
2505
2506 #[inline]
2507 pub(crate) fn hours_ranged(self, hours: t::SpanHours) -> Span {
2508 let mut span = Span { hours: hours.abs(), ..self };
2509 span.sign = self.resign(hours, &span);
2510 span.units = span.units.set(Unit::Hour, hours == C(0));
2511 span
2512 }
2513
2514 #[inline]
2515 pub(crate) fn minutes_ranged(self, minutes: t::SpanMinutes) -> Span {
2516 let mut span = Span { minutes: minutes.abs(), ..self };
2517 span.sign = self.resign(minutes, &span);
2518 span.units = span.units.set(Unit::Minute, minutes == C(0));
2519 span
2520 }
2521
2522 #[inline]
2523 pub(crate) fn seconds_ranged(self, seconds: t::SpanSeconds) -> Span {
2524 let mut span = Span { seconds: seconds.abs(), ..self };
2525 span.sign = self.resign(seconds, &span);
2526 span.units = span.units.set(Unit::Second, seconds == C(0));
2527 span
2528 }
2529
2530 #[inline]
2531 fn milliseconds_ranged(self, milliseconds: t::SpanMilliseconds) -> Span {
2532 let mut span = Span { milliseconds: milliseconds.abs(), ..self };
2533 span.sign = self.resign(milliseconds, &span);
2534 span.units = span.units.set(Unit::Millisecond, milliseconds == C(0));
2535 span
2536 }
2537
2538 #[inline]
2539 fn microseconds_ranged(self, microseconds: t::SpanMicroseconds) -> Span {
2540 let mut span = Span { microseconds: microseconds.abs(), ..self };
2541 span.sign = self.resign(microseconds, &span);
2542 span.units = span.units.set(Unit::Microsecond, microseconds == C(0));
2543 span
2544 }
2545
2546 #[inline]
2547 pub(crate) fn nanoseconds_ranged(
2548 self,
2549 nanoseconds: t::SpanNanoseconds,
2550 ) -> Span {
2551 let mut span = Span { nanoseconds: nanoseconds.abs(), ..self };
2552 span.sign = self.resign(nanoseconds, &span);
2553 span.units = span.units.set(Unit::Nanosecond, nanoseconds == C(0));
2554 span
2555 }
2556
2557 #[inline]
2558 fn try_days_ranged(
2559 self,
2560 days: impl TryRInto<t::SpanDays>,
2561 ) -> Result<Span, Error> {
2562 let days = days.try_rinto("days")?;
2563 Ok(self.days_ranged(days))
2564 }
2565
2566 #[inline]
2567 pub(crate) fn try_hours_ranged(
2568 self,
2569 hours: impl TryRInto<t::SpanHours>,
2570 ) -> Result<Span, Error> {
2571 let hours = hours.try_rinto("hours")?;
2572 Ok(self.hours_ranged(hours))
2573 }
2574
2575 #[inline]
2576 pub(crate) fn try_minutes_ranged(
2577 self,
2578 minutes: impl TryRInto<t::SpanMinutes>,
2579 ) -> Result<Span, Error> {
2580 let minutes = minutes.try_rinto("minutes")?;
2581 Ok(self.minutes_ranged(minutes))
2582 }
2583
2584 #[inline]
2585 pub(crate) fn try_seconds_ranged(
2586 self,
2587 seconds: impl TryRInto<t::SpanSeconds>,
2588 ) -> Result<Span, Error> {
2589 let seconds = seconds.try_rinto("seconds")?;
2590 Ok(self.seconds_ranged(seconds))
2591 }
2592
2593 #[inline]
2594 pub(crate) fn try_milliseconds_ranged(
2595 self,
2596 milliseconds: impl TryRInto<t::SpanMilliseconds>,
2597 ) -> Result<Span, Error> {
2598 let milliseconds = milliseconds.try_rinto("milliseconds")?;
2599 Ok(self.milliseconds_ranged(milliseconds))
2600 }
2601
2602 #[inline]
2603 pub(crate) fn try_microseconds_ranged(
2604 self,
2605 microseconds: impl TryRInto<t::SpanMicroseconds>,
2606 ) -> Result<Span, Error> {
2607 let microseconds = microseconds.try_rinto("microseconds")?;
2608 Ok(self.microseconds_ranged(microseconds))
2609 }
2610
2611 #[inline]
2612 pub(crate) fn try_nanoseconds_ranged(
2613 self,
2614 nanoseconds: impl TryRInto<t::SpanNanoseconds>,
2615 ) -> Result<Span, Error> {
2616 let nanoseconds = nanoseconds.try_rinto("nanoseconds")?;
2617 Ok(self.nanoseconds_ranged(nanoseconds))
2618 }
2619
2620 #[inline]
2621 fn try_units_ranged(
2622 self,
2623 unit: Unit,
2624 value: NoUnits,
2625 ) -> Result<Span, Error> {
2626 Ok(match unit {
2627 Unit::Year => self.years_ranged(value.try_rinto("years")?),
2628 Unit::Month => self.months_ranged(value.try_rinto("months")?),
2629 Unit::Week => self.weeks_ranged(value.try_rinto("weeks")?),
2630 Unit::Day => self.days_ranged(value.try_rinto("days")?),
2631 Unit::Hour => self.hours_ranged(value.try_rinto("hours")?),
2632 Unit::Minute => self.minutes_ranged(value.try_rinto("minutes")?),
2633 Unit::Second => self.seconds_ranged(value.try_rinto("seconds")?),
2634 Unit::Millisecond => {
2635 self.milliseconds_ranged(value.try_rinto("milliseconds")?)
2636 }
2637 Unit::Microsecond => {
2638 self.microseconds_ranged(value.try_rinto("microseconds")?)
2639 }
2640 Unit::Nanosecond => {
2641 self.nanoseconds_ranged(value.try_rinto("nanoseconds")?)
2642 }
2643 })
2644 }
2645
2646 #[inline]
2647 pub(crate) fn get_years_ranged(&self) -> t::SpanYears {
2648 self.years * self.sign
2649 }
2650
2651 #[inline]
2652 pub(crate) fn get_months_ranged(&self) -> t::SpanMonths {
2653 self.months * self.sign
2654 }
2655
2656 #[inline]
2657 pub(crate) fn get_weeks_ranged(&self) -> t::SpanWeeks {
2658 self.weeks * self.sign
2659 }
2660
2661 #[inline]
2662 pub(crate) fn get_days_ranged(&self) -> t::SpanDays {
2663 self.days * self.sign
2664 }
2665
2666 #[inline]
2667 pub(crate) fn get_hours_ranged(&self) -> t::SpanHours {
2668 self.hours * self.sign
2669 }
2670
2671 #[inline]
2672 pub(crate) fn get_minutes_ranged(&self) -> t::SpanMinutes {
2673 self.minutes * self.sign
2674 }
2675
2676 #[inline]
2677 pub(crate) fn get_seconds_ranged(&self) -> t::SpanSeconds {
2678 self.seconds * self.sign
2679 }
2680
2681 #[inline]
2682 pub(crate) fn get_milliseconds_ranged(&self) -> t::SpanMilliseconds {
2683 self.milliseconds * self.sign
2684 }
2685
2686 #[inline]
2687 pub(crate) fn get_microseconds_ranged(&self) -> t::SpanMicroseconds {
2688 self.microseconds * self.sign
2689 }
2690
2691 #[inline]
2692 pub(crate) fn get_nanoseconds_ranged(&self) -> t::SpanNanoseconds {
2693 self.nanoseconds * self.sign
2694 }
2695
2696 #[inline]
2697 fn get_sign_ranged(&self) -> ri8<-1, 1> {
2698 self.sign
2699 }
2700
2701 #[inline]
2702 fn get_units_ranged(&self, unit: Unit) -> NoUnits {
2703 match unit {
2704 Unit::Year => self.get_years_ranged().rinto(),
2705 Unit::Month => self.get_months_ranged().rinto(),
2706 Unit::Week => self.get_weeks_ranged().rinto(),
2707 Unit::Day => self.get_days_ranged().rinto(),
2708 Unit::Hour => self.get_hours_ranged().rinto(),
2709 Unit::Minute => self.get_minutes_ranged().rinto(),
2710 Unit::Second => self.get_seconds_ranged().rinto(),
2711 Unit::Millisecond => self.get_milliseconds_ranged().rinto(),
2712 Unit::Microsecond => self.get_microseconds_ranged().rinto(),
2713 Unit::Nanosecond => self.get_nanoseconds_ranged().rinto(),
2714 }
2715 }
2716}
2717
2718/// Crate internal APIs that permit setting units without checks.
2719///
2720/// Callers should be very careful when using these. These notably also do
2721/// not handle updating the sign on the `Span` and require the precisely
2722/// correct integer primitive.
2723impl Span {
2724 #[inline]
2725 pub(crate) fn years_unchecked(self, years: i16) -> Span {
2726 let mut span =
2727 Span { years: t::SpanYears::new_unchecked(years), ..self };
2728 span.units = span.units.set(Unit::Year, years == 0);
2729 span
2730 }
2731
2732 #[inline]
2733 pub(crate) fn months_unchecked(self, months: i32) -> Span {
2734 let mut span =
2735 Span { months: t::SpanMonths::new_unchecked(months), ..self };
2736 span.units = span.units.set(Unit::Month, months == 0);
2737 span
2738 }
2739
2740 #[inline]
2741 pub(crate) fn weeks_unchecked(self, weeks: i32) -> Span {
2742 let mut span =
2743 Span { weeks: t::SpanWeeks::new_unchecked(weeks), ..self };
2744 span.units = span.units.set(Unit::Week, weeks == 0);
2745 span
2746 }
2747
2748 #[inline]
2749 pub(crate) fn days_unchecked(self, days: i32) -> Span {
2750 let mut span = Span { days: t::SpanDays::new_unchecked(days), ..self };
2751 span.units = span.units.set(Unit::Day, days == 0);
2752 span
2753 }
2754
2755 #[inline]
2756 pub(crate) fn hours_unchecked(self, hours: i32) -> Span {
2757 let mut span =
2758 Span { hours: t::SpanHours::new_unchecked(hours), ..self };
2759 span.units = span.units.set(Unit::Hour, hours == 0);
2760 span
2761 }
2762
2763 #[inline]
2764 pub(crate) fn minutes_unchecked(self, minutes: i64) -> Span {
2765 let mut span =
2766 Span { minutes: t::SpanMinutes::new_unchecked(minutes), ..self };
2767 span.units = span.units.set(Unit::Minute, minutes == 0);
2768 span
2769 }
2770
2771 #[inline]
2772 pub(crate) fn seconds_unchecked(self, seconds: i64) -> Span {
2773 let mut span =
2774 Span { seconds: t::SpanSeconds::new_unchecked(seconds), ..self };
2775 span.units = span.units.set(Unit::Second, seconds == 0);
2776 span
2777 }
2778
2779 #[inline]
2780 pub(crate) fn milliseconds_unchecked(self, milliseconds: i64) -> Span {
2781 let mut span = Span {
2782 milliseconds: t::SpanMilliseconds::new_unchecked(milliseconds),
2783 ..self
2784 };
2785 span.units = span.units.set(Unit::Millisecond, milliseconds == 0);
2786 span
2787 }
2788
2789 #[inline]
2790 pub(crate) fn microseconds_unchecked(self, microseconds: i64) -> Span {
2791 let mut span = Span {
2792 microseconds: t::SpanMicroseconds::new_unchecked(microseconds),
2793 ..self
2794 };
2795 span.units = span.units.set(Unit::Microsecond, microseconds == 0);
2796 span
2797 }
2798
2799 #[inline]
2800 pub(crate) fn nanoseconds_unchecked(self, nanoseconds: i64) -> Span {
2801 let mut span = Span {
2802 nanoseconds: t::SpanNanoseconds::new_unchecked(nanoseconds),
2803 ..self
2804 };
2805 span.units = span.units.set(Unit::Nanosecond, nanoseconds == 0);
2806 span
2807 }
2808
2809 #[inline]
2810 pub(crate) fn sign_unchecked(self, sign: Sign) -> Span {
2811 Span { sign, ..self }
2812 }
2813}
2814
2815/// Crate internal helper routines.
2816impl Span {
2817 /// Converts the given number of nanoseconds to a `Span` whose units do not
2818 /// exceed `largest`.
2819 ///
2820 /// Note that `largest` is capped at `Unit::Week`. Note though that if
2821 /// any unit greater than `Unit::Week` is given, then it is treated as
2822 /// `Unit::Day`. The only way to get weeks in the `Span` returned is to
2823 /// specifically request `Unit::Week`.
2824 ///
2825 /// And also note that days in this context are civil days. That is, they
2826 /// are always 24 hours long. Callers needing to deal with variable length
2827 /// days should do so outside of this routine and should not provide a
2828 /// `largest` unit bigger than `Unit::Hour`.
2829 pub(crate) fn from_invariant_nanoseconds(
2830 largest: Unit,
2831 nanos: NoUnits128,
2832 ) -> Result<Span, Error> {
2833 let mut span = Span::new();
2834 match largest {
2835 Unit::Week => {
2836 let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2837 span = span.try_nanoseconds_ranged(
2838 nanos.rem_ceil(t::NANOS_PER_MICRO),
2839 )?;
2840 let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2841 span = span.try_microseconds_ranged(
2842 micros.rem_ceil(t::MICROS_PER_MILLI),
2843 )?;
2844 let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2845 span = span.try_milliseconds_ranged(
2846 millis.rem_ceil(t::MILLIS_PER_SECOND),
2847 )?;
2848 let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2849 span = span.try_seconds_ranged(
2850 secs.rem_ceil(t::SECONDS_PER_MINUTE),
2851 )?;
2852 let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2853 span = span
2854 .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2855 let days = hours.div_ceil(t::HOURS_PER_CIVIL_DAY);
2856 span = span.try_hours_ranged(
2857 hours.rem_ceil(t::HOURS_PER_CIVIL_DAY),
2858 )?;
2859 let weeks = days.div_ceil(t::DAYS_PER_CIVIL_WEEK);
2860 span = span
2861 .try_days_ranged(days.rem_ceil(t::DAYS_PER_CIVIL_WEEK))?;
2862 span = span.weeks_ranged(weeks.try_rinto("weeks")?);
2863 Ok(span)
2864 }
2865 Unit::Year | Unit::Month | Unit::Day => {
2866 // Unit::Year | Unit::Month | Unit::Week | Unit::Day => {
2867 let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2868 span = span.try_nanoseconds_ranged(
2869 nanos.rem_ceil(t::NANOS_PER_MICRO),
2870 )?;
2871 let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2872 span = span.try_microseconds_ranged(
2873 micros.rem_ceil(t::MICROS_PER_MILLI),
2874 )?;
2875 let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2876 span = span.try_milliseconds_ranged(
2877 millis.rem_ceil(t::MILLIS_PER_SECOND),
2878 )?;
2879 let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2880 span = span.try_seconds_ranged(
2881 secs.rem_ceil(t::SECONDS_PER_MINUTE),
2882 )?;
2883 let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2884 span = span
2885 .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2886 let days = hours.div_ceil(t::HOURS_PER_CIVIL_DAY);
2887 span = span.try_hours_ranged(
2888 hours.rem_ceil(t::HOURS_PER_CIVIL_DAY),
2889 )?;
2890 span = span.try_days_ranged(days)?;
2891 Ok(span)
2892 }
2893 Unit::Hour => {
2894 let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2895 span = span.try_nanoseconds_ranged(
2896 nanos.rem_ceil(t::NANOS_PER_MICRO),
2897 )?;
2898 let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2899 span = span.try_microseconds_ranged(
2900 micros.rem_ceil(t::MICROS_PER_MILLI),
2901 )?;
2902 let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2903 span = span.try_milliseconds_ranged(
2904 millis.rem_ceil(t::MILLIS_PER_SECOND),
2905 )?;
2906 let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2907 span = span.try_seconds_ranged(
2908 secs.rem_ceil(t::SECONDS_PER_MINUTE),
2909 )?;
2910 let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2911 span = span
2912 .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2913 span = span.try_hours_ranged(hours)?;
2914 Ok(span)
2915 }
2916 Unit::Minute => {
2917 let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2918 span = span.try_nanoseconds_ranged(
2919 nanos.rem_ceil(t::NANOS_PER_MICRO),
2920 )?;
2921 let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2922 span = span.try_microseconds_ranged(
2923 micros.rem_ceil(t::MICROS_PER_MILLI),
2924 )?;
2925 let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2926 span = span.try_milliseconds_ranged(
2927 millis.rem_ceil(t::MILLIS_PER_SECOND),
2928 )?;
2929 let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2930 span =
2931 span.try_seconds(secs.rem_ceil(t::SECONDS_PER_MINUTE))?;
2932 span = span.try_minutes_ranged(mins)?;
2933 Ok(span)
2934 }
2935 Unit::Second => {
2936 let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2937 span = span.try_nanoseconds_ranged(
2938 nanos.rem_ceil(t::NANOS_PER_MICRO),
2939 )?;
2940 let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2941 span = span.try_microseconds_ranged(
2942 micros.rem_ceil(t::MICROS_PER_MILLI),
2943 )?;
2944 let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2945 span = span.try_milliseconds_ranged(
2946 millis.rem_ceil(t::MILLIS_PER_SECOND),
2947 )?;
2948 span = span.try_seconds_ranged(secs)?;
2949 Ok(span)
2950 }
2951 Unit::Millisecond => {
2952 let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2953 span = span.try_nanoseconds_ranged(
2954 nanos.rem_ceil(t::NANOS_PER_MICRO),
2955 )?;
2956 let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2957 span = span.try_microseconds_ranged(
2958 micros.rem_ceil(t::MICROS_PER_MILLI),
2959 )?;
2960 span = span.try_milliseconds_ranged(millis)?;
2961 Ok(span)
2962 }
2963 Unit::Microsecond => {
2964 let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2965 span = span.try_nanoseconds_ranged(
2966 nanos.rem_ceil(t::NANOS_PER_MICRO),
2967 )?;
2968 span = span.try_microseconds_ranged(micros)?;
2969 Ok(span)
2970 }
2971 Unit::Nanosecond => {
2972 span = span.try_nanoseconds_ranged(nanos)?;
2973 Ok(span)
2974 }
2975 }
2976 }
2977
2978 /// Converts the non-variable units of this `Span` to a total number of
2979 /// nanoseconds.
2980 ///
2981 /// This includes days and weeks, even though they can be of irregular
2982 /// length during time zone transitions. If this applies, then callers
2983 /// should set the days and weeks to `0` before calling this routine.
2984 ///
2985 /// All units above weeks are always ignored.
2986 #[inline]
2987 pub(crate) fn to_invariant_nanoseconds(&self) -> NoUnits128 {
2988 let mut nanos = NoUnits128::rfrom(self.get_nanoseconds_ranged());
2989 nanos += NoUnits128::rfrom(self.get_microseconds_ranged())
2990 * t::NANOS_PER_MICRO;
2991 nanos += NoUnits128::rfrom(self.get_milliseconds_ranged())
2992 * t::NANOS_PER_MILLI;
2993 nanos +=
2994 NoUnits128::rfrom(self.get_seconds_ranged()) * t::NANOS_PER_SECOND;
2995 nanos +=
2996 NoUnits128::rfrom(self.get_minutes_ranged()) * t::NANOS_PER_MINUTE;
2997 nanos +=
2998 NoUnits128::rfrom(self.get_hours_ranged()) * t::NANOS_PER_HOUR;
2999 nanos +=
3000 NoUnits128::rfrom(self.get_days_ranged()) * t::NANOS_PER_CIVIL_DAY;
3001 nanos += NoUnits128::rfrom(self.get_weeks_ranged())
3002 * t::NANOS_PER_CIVIL_WEEK;
3003 nanos
3004 }
3005
3006 /// Converts the non-variable units of this `Span` to a total number of
3007 /// seconds if there is no fractional second component. Otherwise,
3008 /// `None` is returned.
3009 ///
3010 /// This is useful for short-circuiting in arithmetic operations when
3011 /// it's faster to only deal with seconds. And in particular, acknowledges
3012 /// that nanosecond precision durations are somewhat rare.
3013 ///
3014 /// This includes days and weeks, even though they can be of irregular
3015 /// length during time zone transitions. If this applies, then callers
3016 /// should set the days and weeks to `0` before calling this routine.
3017 ///
3018 /// All units above weeks are always ignored.
3019 #[inline]
3020 pub(crate) fn to_invariant_seconds(&self) -> Option<NoUnits> {
3021 if self.has_fractional_seconds() {
3022 return None;
3023 }
3024 let mut seconds = NoUnits::rfrom(self.get_seconds_ranged());
3025 seconds +=
3026 NoUnits::rfrom(self.get_minutes_ranged()) * t::SECONDS_PER_MINUTE;
3027 seconds +=
3028 NoUnits::rfrom(self.get_hours_ranged()) * t::SECONDS_PER_HOUR;
3029 seconds +=
3030 NoUnits::rfrom(self.get_days_ranged()) * t::SECONDS_PER_CIVIL_DAY;
3031 seconds += NoUnits::rfrom(self.get_weeks_ranged())
3032 * t::SECONDS_PER_CIVIL_WEEK;
3033 Some(seconds)
3034 }
3035
3036 /// Rebalances the invariant units (days or lower) on this span so that
3037 /// the largest possible non-zero unit is the one given.
3038 ///
3039 /// Units above day are ignored and dropped.
3040 ///
3041 /// If the given unit is greater than days, then it is treated as-if it
3042 /// were days.
3043 ///
3044 /// # Errors
3045 ///
3046 /// This can return an error in the case of lop-sided units. For example,
3047 /// if this span has maximal values for all units, then rebalancing is
3048 /// not possible because the number of days after balancing would exceed
3049 /// the limit.
3050 #[cfg(test)] // currently only used in zic parser?
3051 #[inline]
3052 pub(crate) fn rebalance(self, unit: Unit) -> Result<Span, Error> {
3053 Span::from_invariant_nanoseconds(unit, self.to_invariant_nanoseconds())
3054 }
3055
3056 /// Returns true if and only if this span has at least one non-zero
3057 /// fractional second unit.
3058 #[inline]
3059 pub(crate) fn has_fractional_seconds(&self) -> bool {
3060 self.milliseconds != C(0)
3061 || self.microseconds != C(0)
3062 || self.nanoseconds != C(0)
3063 }
3064
3065 /// Returns an equivalent span, but with all non-calendar (units below
3066 /// days) set to zero.
3067 #[cfg_attr(feature = "perf-inline", inline(always))]
3068 pub(crate) fn only_calendar(self) -> Span {
3069 let mut span = self;
3070 span.hours = t::SpanHours::N::<0>();
3071 span.minutes = t::SpanMinutes::N::<0>();
3072 span.seconds = t::SpanSeconds::N::<0>();
3073 span.milliseconds = t::SpanMilliseconds::N::<0>();
3074 span.microseconds = t::SpanMicroseconds::N::<0>();
3075 span.nanoseconds = t::SpanNanoseconds::N::<0>();
3076 if span.sign != C(0)
3077 && span.years == C(0)
3078 && span.months == C(0)
3079 && span.weeks == C(0)
3080 && span.days == C(0)
3081 {
3082 span.sign = t::Sign::N::<0>();
3083 }
3084 span.units = span.units.only_calendar();
3085 span
3086 }
3087
3088 /// Returns an equivalent span, but with all calendar (units above
3089 /// hours) set to zero.
3090 #[cfg_attr(feature = "perf-inline", inline(always))]
3091 pub(crate) fn only_time(self) -> Span {
3092 let mut span = self;
3093 span.years = t::SpanYears::N::<0>();
3094 span.months = t::SpanMonths::N::<0>();
3095 span.weeks = t::SpanWeeks::N::<0>();
3096 span.days = t::SpanDays::N::<0>();
3097 if span.sign != C(0)
3098 && span.hours == C(0)
3099 && span.minutes == C(0)
3100 && span.seconds == C(0)
3101 && span.milliseconds == C(0)
3102 && span.microseconds == C(0)
3103 && span.nanoseconds == C(0)
3104 {
3105 span.sign = t::Sign::N::<0>();
3106 }
3107 span.units = span.units.only_time();
3108 span
3109 }
3110
3111 /// Returns an equivalent span, but with all units greater than or equal to
3112 /// the one given set to zero.
3113 #[cfg_attr(feature = "perf-inline", inline(always))]
3114 pub(crate) fn only_lower(self, unit: Unit) -> Span {
3115 let mut span = self;
3116 // Unit::Nanosecond is the minimum, so nothing can be smaller than it.
3117 if unit <= Unit::Microsecond {
3118 span = span.microseconds_ranged(C(0).rinto());
3119 }
3120 if unit <= Unit::Millisecond {
3121 span = span.milliseconds_ranged(C(0).rinto());
3122 }
3123 if unit <= Unit::Second {
3124 span = span.seconds_ranged(C(0).rinto());
3125 }
3126 if unit <= Unit::Minute {
3127 span = span.minutes_ranged(C(0).rinto());
3128 }
3129 if unit <= Unit::Hour {
3130 span = span.hours_ranged(C(0).rinto());
3131 }
3132 if unit <= Unit::Day {
3133 span = span.days_ranged(C(0).rinto());
3134 }
3135 if unit <= Unit::Week {
3136 span = span.weeks_ranged(C(0).rinto());
3137 }
3138 if unit <= Unit::Month {
3139 span = span.months_ranged(C(0).rinto());
3140 }
3141 if unit <= Unit::Year {
3142 span = span.years_ranged(C(0).rinto());
3143 }
3144 span
3145 }
3146
3147 /// Returns an equivalent span, but with all units less than the one given
3148 /// set to zero.
3149 #[cfg_attr(feature = "perf-inline", inline(always))]
3150 pub(crate) fn without_lower(self, unit: Unit) -> Span {
3151 let mut span = self;
3152 if unit > Unit::Nanosecond {
3153 span = span.nanoseconds_ranged(C(0).rinto());
3154 }
3155 if unit > Unit::Microsecond {
3156 span = span.microseconds_ranged(C(0).rinto());
3157 }
3158 if unit > Unit::Millisecond {
3159 span = span.milliseconds_ranged(C(0).rinto());
3160 }
3161 if unit > Unit::Second {
3162 span = span.seconds_ranged(C(0).rinto());
3163 }
3164 if unit > Unit::Minute {
3165 span = span.minutes_ranged(C(0).rinto());
3166 }
3167 if unit > Unit::Hour {
3168 span = span.hours_ranged(C(0).rinto());
3169 }
3170 if unit > Unit::Day {
3171 span = span.days_ranged(C(0).rinto());
3172 }
3173 if unit > Unit::Week {
3174 span = span.weeks_ranged(C(0).rinto());
3175 }
3176 if unit > Unit::Month {
3177 span = span.months_ranged(C(0).rinto());
3178 }
3179 // Unit::Year is the max, so nothing can be bigger than it.
3180 span
3181 }
3182
3183 /// Returns an error corresponding to the smallest non-time non-zero unit.
3184 ///
3185 /// If all non-time units are zero, then this returns `None`.
3186 #[cfg_attr(feature = "perf-inline", inline(always))]
3187 pub(crate) fn smallest_non_time_non_zero_unit_error(
3188 &self,
3189 ) -> Option<Error> {
3190 let non_time_unit = self.largest_calendar_unit()?;
3191 Some(Error::from(E::NotAllowedCalendarUnits { unit: non_time_unit }))
3192 }
3193
3194 /// Returns the largest non-zero calendar unit, or `None` if there are no
3195 /// non-zero calendar units.
3196 #[inline]
3197 pub(crate) fn largest_calendar_unit(&self) -> Option<Unit> {
3198 self.units().only_calendar().largest_unit()
3199 }
3200
3201 /// Returns the largest non-zero unit in this span.
3202 ///
3203 /// If all components of this span are zero, then `Unit::Nanosecond` is
3204 /// returned.
3205 #[inline]
3206 pub(crate) fn largest_unit(&self) -> Unit {
3207 self.units().largest_unit().unwrap_or(Unit::Nanosecond)
3208 }
3209
3210 /// Returns the set of units on this `Span`.
3211 #[inline]
3212 pub(crate) fn units(&self) -> UnitSet {
3213 self.units
3214 }
3215
3216 /// Returns a string containing the value of all non-zero fields.
3217 ///
3218 /// This is useful for debugging. Normally, this would be the "alternate"
3219 /// debug impl (perhaps), but that's what insta uses and I preferred having
3220 /// the friendly format used there since it is much more terse.
3221 #[cfg(feature = "alloc")]
3222 #[allow(dead_code)]
3223 pub(crate) fn debug(&self) -> alloc::string::String {
3224 use core::fmt::Write;
3225
3226 let mut buf = alloc::string::String::new();
3227 write!(buf, "Span {{ sign: {:?}, units: {:?}", self.sign, self.units)
3228 .unwrap();
3229 if self.years != C(0) {
3230 write!(buf, ", years: {:?}", self.years).unwrap();
3231 }
3232 if self.months != C(0) {
3233 write!(buf, ", months: {:?}", self.months).unwrap();
3234 }
3235 if self.weeks != C(0) {
3236 write!(buf, ", weeks: {:?}", self.weeks).unwrap();
3237 }
3238 if self.days != C(0) {
3239 write!(buf, ", days: {:?}", self.days).unwrap();
3240 }
3241 if self.hours != C(0) {
3242 write!(buf, ", hours: {:?}", self.hours).unwrap();
3243 }
3244 if self.minutes != C(0) {
3245 write!(buf, ", minutes: {:?}", self.minutes).unwrap();
3246 }
3247 if self.seconds != C(0) {
3248 write!(buf, ", seconds: {:?}", self.seconds).unwrap();
3249 }
3250 if self.milliseconds != C(0) {
3251 write!(buf, ", milliseconds: {:?}", self.milliseconds).unwrap();
3252 }
3253 if self.microseconds != C(0) {
3254 write!(buf, ", microseconds: {:?}", self.microseconds).unwrap();
3255 }
3256 if self.nanoseconds != C(0) {
3257 write!(buf, ", nanoseconds: {:?}", self.nanoseconds).unwrap();
3258 }
3259 buf.push_str(" }}");
3260 buf
3261 }
3262
3263 /// Given some new units to set on this span and the span updates with the
3264 /// new units, this determines the what the sign of `new` should be.
3265 #[inline]
3266 fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3267 fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3268 // Negative units anywhere always makes the entire span negative.
3269 if units < C(0) {
3270 return Sign::N::<-1>();
3271 }
3272 let mut new_is_zero = new.sign == C(0) && units == C(0);
3273 // When `units == 0` and it was previously non-zero, then
3274 // `new.sign` won't be `0` and thus `new_is_zero` will be false
3275 // when it should be true. So in this case, we need to re-check all
3276 // the units to set the sign correctly.
3277 if units == C(0) {
3278 new_is_zero = new.years == C(0)
3279 && new.months == C(0)
3280 && new.weeks == C(0)
3281 && new.days == C(0)
3282 && new.hours == C(0)
3283 && new.minutes == C(0)
3284 && new.seconds == C(0)
3285 && new.milliseconds == C(0)
3286 && new.microseconds == C(0)
3287 && new.nanoseconds == C(0);
3288 }
3289 match (span.is_zero(), new_is_zero) {
3290 (_, true) => Sign::N::<0>(),
3291 (true, false) => units.signum().rinto(),
3292 // If the old and new span are both non-zero, and we know our
3293 // new units are not negative, then the sign remains unchanged.
3294 (false, false) => new.sign,
3295 }
3296 }
3297 imp(self, units.rinto(), new)
3298 }
3299}
3300
3301impl Default for Span {
3302 #[inline]
3303 fn default() -> Span {
3304 Span {
3305 sign: ri8::N::<0>(),
3306 units: UnitSet::empty(),
3307 years: C(0).rinto(),
3308 months: C(0).rinto(),
3309 weeks: C(0).rinto(),
3310 days: C(0).rinto(),
3311 hours: C(0).rinto(),
3312 minutes: C(0).rinto(),
3313 seconds: C(0).rinto(),
3314 milliseconds: C(0).rinto(),
3315 microseconds: C(0).rinto(),
3316 nanoseconds: C(0).rinto(),
3317 }
3318 }
3319}
3320
3321impl core::fmt::Debug for Span {
3322 #[inline]
3323 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3324 use crate::fmt::StdFmtWrite;
3325
3326 friendly::DEFAULT_SPAN_PRINTER
3327 .print_span(self, StdFmtWrite(f))
3328 .map_err(|_| core::fmt::Error)
3329 }
3330}
3331
3332impl core::fmt::Display for Span {
3333 #[inline]
3334 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3335 use crate::fmt::StdFmtWrite;
3336
3337 if f.alternate() {
3338 friendly::DEFAULT_SPAN_PRINTER
3339 .print_span(self, StdFmtWrite(f))
3340 .map_err(|_| core::fmt::Error)
3341 } else {
3342 temporal::DEFAULT_SPAN_PRINTER
3343 .print_span(self, StdFmtWrite(f))
3344 .map_err(|_| core::fmt::Error)
3345 }
3346 }
3347}
3348
3349impl core::str::FromStr for Span {
3350 type Err = Error;
3351
3352 #[inline]
3353 fn from_str(string: &str) -> Result<Span, Error> {
3354 parse_iso_or_friendly(string.as_bytes())
3355 }
3356}
3357
3358impl core::ops::Neg for Span {
3359 type Output = Span;
3360
3361 #[inline]
3362 fn neg(self) -> Span {
3363 self.negate()
3364 }
3365}
3366
3367/// This multiplies each unit in a span by an integer.
3368///
3369/// This panics on overflow. For checked arithmetic, use [`Span::checked_mul`].
3370impl core::ops::Mul<i64> for Span {
3371 type Output = Span;
3372
3373 #[inline]
3374 fn mul(self, rhs: i64) -> Span {
3375 self.checked_mul(rhs)
3376 .expect("multiplying `Span` by a scalar overflowed")
3377 }
3378}
3379
3380/// This multiplies each unit in a span by an integer.
3381///
3382/// This panics on overflow. For checked arithmetic, use [`Span::checked_mul`].
3383impl core::ops::Mul<Span> for i64 {
3384 type Output = Span;
3385
3386 #[inline]
3387 fn mul(self, rhs: Span) -> Span {
3388 rhs.checked_mul(self)
3389 .expect("multiplying `Span` by a scalar overflowed")
3390 }
3391}
3392
3393/// Converts a `Span` to a [`std::time::Duration`].
3394///
3395/// # Errors
3396///
3397/// This can fail for only two reasons:
3398///
3399/// * The span is negative. This is an error because a `std::time::Duration` is
3400/// unsigned.)
3401/// * The span has any non-zero units greater than hours. This is an error
3402/// because it's impossible to determine the length of, e.g., a month without
3403/// a reference date.
3404///
3405/// This can never result in overflow because a `Duration` can represent a
3406/// bigger span of time than `Span` when limited to units of hours or lower.
3407///
3408/// If you need to convert a `Span` to a `Duration` that has non-zero
3409/// units bigger than hours, then please use [`Span::to_duration`] with a
3410/// corresponding relative date.
3411///
3412/// # Example: maximal span
3413///
3414/// This example shows the maximum possible span using units of hours or
3415/// smaller, and the corresponding `Duration` value:
3416///
3417/// ```
3418/// use std::time::Duration;
3419///
3420/// use jiff::Span;
3421///
3422/// let sp = Span::new()
3423/// .hours(175_307_616)
3424/// .minutes(10_518_456_960i64)
3425/// .seconds(631_107_417_600i64)
3426/// .milliseconds(631_107_417_600_000i64)
3427/// .microseconds(631_107_417_600_000_000i64)
3428/// .nanoseconds(9_223_372_036_854_775_807i64);
3429/// let duration = Duration::try_from(sp)?;
3430/// assert_eq!(duration, Duration::new(3_164_760_460_036, 854_775_807));
3431///
3432/// # Ok::<(), Box<dyn std::error::Error>>(())
3433/// ```
3434///
3435/// # Example: converting a negative span
3436///
3437/// Since a `Span` is signed and a `Duration` is unsigned, converting
3438/// a negative `Span` to `Duration` will always fail. One can use
3439/// [`Span::signum`] to get the sign of the span and [`Span::abs`] to make the
3440/// span positive before converting it to a `Duration`:
3441///
3442/// ```
3443/// use std::time::Duration;
3444///
3445/// use jiff::{Span, ToSpan};
3446///
3447/// let span = -86_400.seconds().nanoseconds(1);
3448/// let (sign, duration) = (span.signum(), Duration::try_from(span.abs())?);
3449/// assert_eq!((sign, duration), (-1, Duration::new(86_400, 1)));
3450///
3451/// # Ok::<(), Box<dyn std::error::Error>>(())
3452/// ```
3453impl TryFrom<Span> for UnsignedDuration {
3454 type Error = Error;
3455
3456 #[inline]
3457 fn try_from(sp: Span) -> Result<UnsignedDuration, Error> {
3458 // This isn't needed, but improves error messages.
3459 if sp.is_negative() {
3460 return Err(Error::from(E::ConvertNegative));
3461 }
3462 SignedDuration::try_from(sp).and_then(UnsignedDuration::try_from)
3463 }
3464}
3465
3466/// Converts a [`std::time::Duration`] to a `Span`.
3467///
3468/// The span returned from this conversion will only ever have non-zero units
3469/// of seconds or smaller.
3470///
3471/// # Errors
3472///
3473/// This only fails when the given `Duration` overflows the maximum number of
3474/// seconds representable by a `Span`.
3475///
3476/// # Example
3477///
3478/// This shows a basic conversion:
3479///
3480/// ```
3481/// use std::time::Duration;
3482///
3483/// use jiff::{Span, ToSpan};
3484///
3485/// let duration = Duration::new(86_400, 123_456_789);
3486/// let span = Span::try_from(duration)?;
3487/// // A duration-to-span conversion always results in a span with
3488/// // non-zero units no bigger than seconds.
3489/// assert_eq!(
3490/// span.fieldwise(),
3491/// 86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
3492/// );
3493///
3494/// # Ok::<(), Box<dyn std::error::Error>>(())
3495/// ```
3496///
3497/// # Example: rounding
3498///
3499/// This example shows how to convert a `Duration` to a `Span`, and then round
3500/// it up to bigger units given a relative date:
3501///
3502/// ```
3503/// use std::time::Duration;
3504///
3505/// use jiff::{civil::date, Span, SpanRound, ToSpan, Unit};
3506///
3507/// let duration = Duration::new(450 * 86_401, 0);
3508/// let span = Span::try_from(duration)?;
3509/// // We get back a simple span of just seconds:
3510/// assert_eq!(span.fieldwise(), Span::new().seconds(450 * 86_401));
3511/// // But we can balance it up to bigger units:
3512/// let options = SpanRound::new()
3513/// .largest(Unit::Year)
3514/// .relative(date(2024, 1, 1));
3515/// assert_eq!(
3516/// span.round(options)?,
3517/// 1.year().months(2).days(25).minutes(7).seconds(30).fieldwise(),
3518/// );
3519///
3520/// # Ok::<(), Box<dyn std::error::Error>>(())
3521/// ```
3522impl TryFrom<UnsignedDuration> for Span {
3523 type Error = Error;
3524
3525 #[inline]
3526 fn try_from(d: UnsignedDuration) -> Result<Span, Error> {
3527 let seconds = i64::try_from(d.as_secs())
3528 .map_err(|_| Error::slim_range("unsigned duration seconds"))?;
3529 let nanoseconds = i64::from(d.subsec_nanos());
3530 let milliseconds = nanoseconds / t::NANOS_PER_MILLI.value();
3531 let microseconds = (nanoseconds % t::NANOS_PER_MILLI.value())
3532 / t::NANOS_PER_MICRO.value();
3533 let nanoseconds = nanoseconds % t::NANOS_PER_MICRO.value();
3534
3535 let span = Span::new().try_seconds(seconds)?;
3536 // These are all OK because `Duration::subsec_nanos` is guaranteed to
3537 // return less than 1_000_000_000 nanoseconds. And splitting that up
3538 // into millis, micros and nano components is guaranteed to fit into
3539 // the limits of a `Span`.
3540 Ok(span
3541 .milliseconds(milliseconds)
3542 .microseconds(microseconds)
3543 .nanoseconds(nanoseconds))
3544 }
3545}
3546
3547/// Converts a `Span` to a [`SignedDuration`].
3548///
3549/// # Errors
3550///
3551/// This can fail for only when the span has any non-zero units greater than
3552/// hours. This is an error because it's impossible to determine the length of,
3553/// e.g., a month without a reference date.
3554///
3555/// This can never result in overflow because a `SignedDuration` can represent
3556/// a bigger span of time than `Span` when limited to units of hours or lower.
3557///
3558/// If you need to convert a `Span` to a `SignedDuration` that has non-zero
3559/// units bigger than hours, then please use [`Span::to_duration`] with a
3560/// corresponding relative date.
3561///
3562/// # Example: maximal span
3563///
3564/// This example shows the maximum possible span using units of hours or
3565/// smaller, and the corresponding `SignedDuration` value:
3566///
3567/// ```
3568/// use jiff::{SignedDuration, Span};
3569///
3570/// let sp = Span::new()
3571/// .hours(175_307_616)
3572/// .minutes(10_518_456_960i64)
3573/// .seconds(631_107_417_600i64)
3574/// .milliseconds(631_107_417_600_000i64)
3575/// .microseconds(631_107_417_600_000_000i64)
3576/// .nanoseconds(9_223_372_036_854_775_807i64);
3577/// let duration = SignedDuration::try_from(sp)?;
3578/// assert_eq!(duration, SignedDuration::new(3_164_760_460_036, 854_775_807));
3579///
3580/// # Ok::<(), Box<dyn std::error::Error>>(())
3581/// ```
3582impl TryFrom<Span> for SignedDuration {
3583 type Error = Error;
3584
3585 #[inline]
3586 fn try_from(sp: Span) -> Result<SignedDuration, Error> {
3587 requires_relative_date_err(sp.largest_unit())
3588 .context(E::ConvertSpanToSignedDuration)?;
3589 Ok(sp.to_duration_invariant())
3590 }
3591}
3592
3593/// Converts a [`SignedDuration`] to a `Span`.
3594///
3595/// The span returned from this conversion will only ever have non-zero units
3596/// of seconds or smaller.
3597///
3598/// # Errors
3599///
3600/// This only fails when the given `SignedDuration` overflows the maximum
3601/// number of seconds representable by a `Span`.
3602///
3603/// # Example
3604///
3605/// This shows a basic conversion:
3606///
3607/// ```
3608/// use jiff::{SignedDuration, Span, ToSpan};
3609///
3610/// let duration = SignedDuration::new(86_400, 123_456_789);
3611/// let span = Span::try_from(duration)?;
3612/// // A duration-to-span conversion always results in a span with
3613/// // non-zero units no bigger than seconds.
3614/// assert_eq!(
3615/// span.fieldwise(),
3616/// 86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
3617/// );
3618///
3619/// # Ok::<(), Box<dyn std::error::Error>>(())
3620/// ```
3621///
3622/// # Example: rounding
3623///
3624/// This example shows how to convert a `SignedDuration` to a `Span`, and then
3625/// round it up to bigger units given a relative date:
3626///
3627/// ```
3628/// use jiff::{civil::date, SignedDuration, Span, SpanRound, ToSpan, Unit};
3629///
3630/// let duration = SignedDuration::new(450 * 86_401, 0);
3631/// let span = Span::try_from(duration)?;
3632/// // We get back a simple span of just seconds:
3633/// assert_eq!(span.fieldwise(), Span::new().seconds(450 * 86_401));
3634/// // But we can balance it up to bigger units:
3635/// let options = SpanRound::new()
3636/// .largest(Unit::Year)
3637/// .relative(date(2024, 1, 1));
3638/// assert_eq!(
3639/// span.round(options)?,
3640/// 1.year().months(2).days(25).minutes(7).seconds(30).fieldwise(),
3641/// );
3642///
3643/// # Ok::<(), Box<dyn std::error::Error>>(())
3644/// ```
3645impl TryFrom<SignedDuration> for Span {
3646 type Error = Error;
3647
3648 #[inline]
3649 fn try_from(d: SignedDuration) -> Result<Span, Error> {
3650 let seconds = d.as_secs();
3651 let nanoseconds = i64::from(d.subsec_nanos());
3652 let milliseconds = nanoseconds / t::NANOS_PER_MILLI.value();
3653 let microseconds = (nanoseconds % t::NANOS_PER_MILLI.value())
3654 / t::NANOS_PER_MICRO.value();
3655 let nanoseconds = nanoseconds % t::NANOS_PER_MICRO.value();
3656
3657 let span = Span::new().try_seconds(seconds)?;
3658 // These are all OK because `|SignedDuration::subsec_nanos|` is
3659 // guaranteed to return less than 1_000_000_000 nanoseconds. And
3660 // splitting that up into millis, micros and nano components is
3661 // guaranteed to fit into the limits of a `Span`.
3662 Ok(span
3663 .milliseconds(milliseconds)
3664 .microseconds(microseconds)
3665 .nanoseconds(nanoseconds))
3666 }
3667}
3668
3669#[cfg(feature = "serde")]
3670impl serde_core::Serialize for Span {
3671 #[inline]
3672 fn serialize<S: serde_core::Serializer>(
3673 &self,
3674 serializer: S,
3675 ) -> Result<S::Ok, S::Error> {
3676 serializer.collect_str(self)
3677 }
3678}
3679
3680#[cfg(feature = "serde")]
3681impl<'de> serde_core::Deserialize<'de> for Span {
3682 #[inline]
3683 fn deserialize<D: serde_core::Deserializer<'de>>(
3684 deserializer: D,
3685 ) -> Result<Span, D::Error> {
3686 use serde_core::de;
3687
3688 struct SpanVisitor;
3689
3690 impl<'de> de::Visitor<'de> for SpanVisitor {
3691 type Value = Span;
3692
3693 fn expecting(
3694 &self,
3695 f: &mut core::fmt::Formatter,
3696 ) -> core::fmt::Result {
3697 f.write_str("a span duration string")
3698 }
3699
3700 #[inline]
3701 fn visit_bytes<E: de::Error>(
3702 self,
3703 value: &[u8],
3704 ) -> Result<Span, E> {
3705 parse_iso_or_friendly(value).map_err(de::Error::custom)
3706 }
3707
3708 #[inline]
3709 fn visit_str<E: de::Error>(self, value: &str) -> Result<Span, E> {
3710 self.visit_bytes(value.as_bytes())
3711 }
3712 }
3713
3714 deserializer.deserialize_str(SpanVisitor)
3715 }
3716}
3717
3718#[cfg(test)]
3719impl quickcheck::Arbitrary for Span {
3720 fn arbitrary(g: &mut quickcheck::Gen) -> Span {
3721 // In order to sample from the full space of possible spans, we need
3722 // to provide a relative datetime. But if we do that, then it's
3723 // possible the span plus the datetime overflows. So we pick one
3724 // datetime and shrink the size of the span we can produce.
3725 type Nanos = ri64<-631_107_417_600_000_000, 631_107_417_600_000_000>;
3726 let nanos = Nanos::arbitrary(g).get();
3727 let relative =
3728 SpanRelativeTo::from(DateTime::constant(0, 1, 1, 0, 0, 0, 0));
3729 let round =
3730 SpanRound::new().largest(Unit::arbitrary(g)).relative(relative);
3731 Span::new().nanoseconds(nanos).round(round).unwrap()
3732 }
3733
3734 fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
3735 alloc::boxed::Box::new(
3736 (
3737 (
3738 self.get_years_ranged(),
3739 self.get_months_ranged(),
3740 self.get_weeks_ranged(),
3741 self.get_days_ranged(),
3742 ),
3743 (
3744 self.get_hours_ranged(),
3745 self.get_minutes_ranged(),
3746 self.get_seconds_ranged(),
3747 self.get_milliseconds_ranged(),
3748 ),
3749 (
3750 self.get_microseconds_ranged(),
3751 self.get_nanoseconds_ranged(),
3752 ),
3753 )
3754 .shrink()
3755 .filter_map(
3756 |(
3757 (years, months, weeks, days),
3758 (hours, minutes, seconds, milliseconds),
3759 (microseconds, nanoseconds),
3760 )| {
3761 let span = Span::new()
3762 .years_ranged(years)
3763 .months_ranged(months)
3764 .weeks_ranged(weeks)
3765 .days_ranged(days)
3766 .hours_ranged(hours)
3767 .minutes_ranged(minutes)
3768 .seconds_ranged(seconds)
3769 .milliseconds_ranged(milliseconds)
3770 .microseconds_ranged(microseconds)
3771 .nanoseconds_ranged(nanoseconds);
3772 Some(span)
3773 },
3774 ),
3775 )
3776 }
3777}
3778
3779/// A wrapper for [`Span`] that implements the `Hash`, `Eq` and `PartialEq`
3780/// traits.
3781///
3782/// A `SpanFieldwise` is meant to make it easy to compare two spans in a "dumb"
3783/// way based purely on its unit values, while still providing a speed bump
3784/// to avoid accidentally doing this comparison on `Span` directly. This is
3785/// distinct from something like [`Span::compare`] that performs a comparison
3786/// on the actual elapsed time of two spans.
3787///
3788/// It is generally discouraged to use `SpanFieldwise` since spans that
3789/// represent an equivalent elapsed amount of time may compare unequal.
3790/// However, in some cases, it is useful to be able to assert precise field
3791/// values. For example, Jiff itself makes heavy use of fieldwise comparisons
3792/// for tests.
3793///
3794/// # Construction
3795///
3796/// While callers may use `SpanFieldwise(span)` (where `span` has type [`Span`])
3797/// to construct a value of this type, callers may find [`Span::fieldwise`]
3798/// more convenient. Namely, `Span::fieldwise` may avoid the need to explicitly
3799/// import `SpanFieldwise`.
3800///
3801/// # Trait implementations
3802///
3803/// In addition to implementing the `Hash`, `Eq` and `PartialEq` traits, this
3804/// type also provides `PartialEq` impls for comparing a `Span` with a
3805/// `SpanFieldwise`. This simplifies comparisons somewhat while still requiring
3806/// that at least one of the values has an explicit fieldwise comparison type.
3807///
3808/// # Safety
3809///
3810/// This type is guaranteed to have the same layout in memory as [`Span`].
3811///
3812/// # Example: the difference between `SpanFieldwise` and [`Span::compare`]
3813///
3814/// In short, `SpanFieldwise` considers `2 hours` and `120 minutes` to be
3815/// distinct values, but `Span::compare` considers them to be equivalent:
3816///
3817/// ```
3818/// use std::cmp::Ordering;
3819/// use jiff::ToSpan;
3820///
3821/// assert_ne!(120.minutes().fieldwise(), 2.hours().fieldwise());
3822/// assert_eq!(120.minutes().compare(2.hours())?, Ordering::Equal);
3823///
3824/// // These comparisons are allowed between a `Span` and a `SpanFieldwise`.
3825/// // Namely, as long as one value is "fieldwise," then the comparison is OK.
3826/// assert_ne!(120.minutes().fieldwise(), 2.hours());
3827/// assert_ne!(120.minutes(), 2.hours().fieldwise());
3828///
3829/// # Ok::<(), Box<dyn std::error::Error>>(())
3830/// ```
3831#[derive(Clone, Copy, Debug, Default)]
3832#[repr(transparent)]
3833pub struct SpanFieldwise(pub Span);
3834
3835// Exists so that things like `-1.day().fieldwise()` works as expected.
3836impl core::ops::Neg for SpanFieldwise {
3837 type Output = SpanFieldwise;
3838
3839 #[inline]
3840 fn neg(self) -> SpanFieldwise {
3841 SpanFieldwise(self.0.negate())
3842 }
3843}
3844
3845impl Eq for SpanFieldwise {}
3846
3847impl PartialEq for SpanFieldwise {
3848 fn eq(&self, rhs: &SpanFieldwise) -> bool {
3849 self.0.sign == rhs.0.sign
3850 && self.0.years == rhs.0.years
3851 && self.0.months == rhs.0.months
3852 && self.0.weeks == rhs.0.weeks
3853 && self.0.days == rhs.0.days
3854 && self.0.hours == rhs.0.hours
3855 && self.0.minutes == rhs.0.minutes
3856 && self.0.seconds == rhs.0.seconds
3857 && self.0.milliseconds == rhs.0.milliseconds
3858 && self.0.microseconds == rhs.0.microseconds
3859 && self.0.nanoseconds == rhs.0.nanoseconds
3860 }
3861}
3862
3863impl<'a> PartialEq<SpanFieldwise> for &'a SpanFieldwise {
3864 fn eq(&self, rhs: &SpanFieldwise) -> bool {
3865 *self == rhs
3866 }
3867}
3868
3869impl PartialEq<Span> for SpanFieldwise {
3870 fn eq(&self, rhs: &Span) -> bool {
3871 self == rhs.fieldwise()
3872 }
3873}
3874
3875impl PartialEq<SpanFieldwise> for Span {
3876 fn eq(&self, rhs: &SpanFieldwise) -> bool {
3877 self.fieldwise() == *rhs
3878 }
3879}
3880
3881impl<'a> PartialEq<SpanFieldwise> for &'a Span {
3882 fn eq(&self, rhs: &SpanFieldwise) -> bool {
3883 self.fieldwise() == *rhs
3884 }
3885}
3886
3887impl core::hash::Hash for SpanFieldwise {
3888 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
3889 self.0.sign.hash(state);
3890 self.0.years.hash(state);
3891 self.0.months.hash(state);
3892 self.0.weeks.hash(state);
3893 self.0.days.hash(state);
3894 self.0.hours.hash(state);
3895 self.0.minutes.hash(state);
3896 self.0.seconds.hash(state);
3897 self.0.milliseconds.hash(state);
3898 self.0.microseconds.hash(state);
3899 self.0.nanoseconds.hash(state);
3900 }
3901}
3902
3903impl From<Span> for SpanFieldwise {
3904 fn from(span: Span) -> SpanFieldwise {
3905 SpanFieldwise(span)
3906 }
3907}
3908
3909impl From<SpanFieldwise> for Span {
3910 fn from(span: SpanFieldwise) -> Span {
3911 span.0
3912 }
3913}
3914
3915/// A trait for enabling concise literals for creating [`Span`] values.
3916///
3917/// In short, this trait lets you write something like `5.seconds()` or
3918/// `1.day()` to create a [`Span`]. Once a `Span` has been created, you can
3919/// use its mutator methods to add more fields. For example,
3920/// `1.day().hours(10)` is equivalent to `Span::new().days(1).hours(10)`.
3921///
3922/// This trait is implemented for the following integer types: `i8`, `i16`,
3923/// `i32` and `i64`.
3924///
3925/// Note that this trait is provided as a convenience and should generally
3926/// only be used for literals in your source code. You should not use this
3927/// trait on numbers provided by end users. Namely, if the number provided
3928/// is not within Jiff's span limits, then these trait methods will panic.
3929/// Instead, use fallible mutator constructors like [`Span::try_days`]
3930/// or [`Span::try_seconds`].
3931///
3932/// # Example
3933///
3934/// ```
3935/// use jiff::ToSpan;
3936///
3937/// assert_eq!(5.days().to_string(), "P5D");
3938/// assert_eq!(5.days().hours(10).to_string(), "P5DT10H");
3939///
3940/// // Negation works and it doesn't matter where the sign goes. It can be
3941/// // applied to the span itself or to the integer.
3942/// assert_eq!((-5.days()).to_string(), "-P5D");
3943/// assert_eq!((-5).days().to_string(), "-P5D");
3944/// ```
3945///
3946/// # Example: alternative via span parsing
3947///
3948/// Another way of tersely building a `Span` value is by parsing a ISO 8601
3949/// duration string:
3950///
3951/// ```
3952/// use jiff::Span;
3953///
3954/// let span = "P5y2m15dT23h30m10s".parse::<Span>()?;
3955/// assert_eq!(
3956/// span.fieldwise(),
3957/// Span::new().years(5).months(2).days(15).hours(23).minutes(30).seconds(10),
3958/// );
3959///
3960/// # Ok::<(), Box<dyn std::error::Error>>(())
3961/// ```
3962pub trait ToSpan: Sized {
3963 /// Create a new span from this integer in units of years.
3964 ///
3965 /// # Panics
3966 ///
3967 /// When `Span::new().years(self)` would panic.
3968 fn years(self) -> Span;
3969
3970 /// Create a new span from this integer in units of months.
3971 ///
3972 /// # Panics
3973 ///
3974 /// When `Span::new().months(self)` would panic.
3975 fn months(self) -> Span;
3976
3977 /// Create a new span from this integer in units of weeks.
3978 ///
3979 /// # Panics
3980 ///
3981 /// When `Span::new().weeks(self)` would panic.
3982 fn weeks(self) -> Span;
3983
3984 /// Create a new span from this integer in units of days.
3985 ///
3986 /// # Panics
3987 ///
3988 /// When `Span::new().days(self)` would panic.
3989 fn days(self) -> Span;
3990
3991 /// Create a new span from this integer in units of hours.
3992 ///
3993 /// # Panics
3994 ///
3995 /// When `Span::new().hours(self)` would panic.
3996 fn hours(self) -> Span;
3997
3998 /// Create a new span from this integer in units of minutes.
3999 ///
4000 /// # Panics
4001 ///
4002 /// When `Span::new().minutes(self)` would panic.
4003 fn minutes(self) -> Span;
4004
4005 /// Create a new span from this integer in units of seconds.
4006 ///
4007 /// # Panics
4008 ///
4009 /// When `Span::new().seconds(self)` would panic.
4010 fn seconds(self) -> Span;
4011
4012 /// Create a new span from this integer in units of milliseconds.
4013 ///
4014 /// # Panics
4015 ///
4016 /// When `Span::new().milliseconds(self)` would panic.
4017 fn milliseconds(self) -> Span;
4018
4019 /// Create a new span from this integer in units of microseconds.
4020 ///
4021 /// # Panics
4022 ///
4023 /// When `Span::new().microseconds(self)` would panic.
4024 fn microseconds(self) -> Span;
4025
4026 /// Create a new span from this integer in units of nanoseconds.
4027 ///
4028 /// # Panics
4029 ///
4030 /// When `Span::new().nanoseconds(self)` would panic.
4031 fn nanoseconds(self) -> Span;
4032
4033 /// Equivalent to `years()`, but reads better for singular units.
4034 #[inline]
4035 fn year(self) -> Span {
4036 self.years()
4037 }
4038
4039 /// Equivalent to `months()`, but reads better for singular units.
4040 #[inline]
4041 fn month(self) -> Span {
4042 self.months()
4043 }
4044
4045 /// Equivalent to `weeks()`, but reads better for singular units.
4046 #[inline]
4047 fn week(self) -> Span {
4048 self.weeks()
4049 }
4050
4051 /// Equivalent to `days()`, but reads better for singular units.
4052 #[inline]
4053 fn day(self) -> Span {
4054 self.days()
4055 }
4056
4057 /// Equivalent to `hours()`, but reads better for singular units.
4058 #[inline]
4059 fn hour(self) -> Span {
4060 self.hours()
4061 }
4062
4063 /// Equivalent to `minutes()`, but reads better for singular units.
4064 #[inline]
4065 fn minute(self) -> Span {
4066 self.minutes()
4067 }
4068
4069 /// Equivalent to `seconds()`, but reads better for singular units.
4070 #[inline]
4071 fn second(self) -> Span {
4072 self.seconds()
4073 }
4074
4075 /// Equivalent to `milliseconds()`, but reads better for singular units.
4076 #[inline]
4077 fn millisecond(self) -> Span {
4078 self.milliseconds()
4079 }
4080
4081 /// Equivalent to `microseconds()`, but reads better for singular units.
4082 #[inline]
4083 fn microsecond(self) -> Span {
4084 self.microseconds()
4085 }
4086
4087 /// Equivalent to `nanoseconds()`, but reads better for singular units.
4088 #[inline]
4089 fn nanosecond(self) -> Span {
4090 self.nanoseconds()
4091 }
4092}
4093
4094macro_rules! impl_to_span {
4095 ($ty:ty) => {
4096 impl ToSpan for $ty {
4097 #[inline]
4098 fn years(self) -> Span {
4099 Span::new().years(self)
4100 }
4101 #[inline]
4102 fn months(self) -> Span {
4103 Span::new().months(self)
4104 }
4105 #[inline]
4106 fn weeks(self) -> Span {
4107 Span::new().weeks(self)
4108 }
4109 #[inline]
4110 fn days(self) -> Span {
4111 Span::new().days(self)
4112 }
4113 #[inline]
4114 fn hours(self) -> Span {
4115 Span::new().hours(self)
4116 }
4117 #[inline]
4118 fn minutes(self) -> Span {
4119 Span::new().minutes(self)
4120 }
4121 #[inline]
4122 fn seconds(self) -> Span {
4123 Span::new().seconds(self)
4124 }
4125 #[inline]
4126 fn milliseconds(self) -> Span {
4127 Span::new().milliseconds(self)
4128 }
4129 #[inline]
4130 fn microseconds(self) -> Span {
4131 Span::new().microseconds(self)
4132 }
4133 #[inline]
4134 fn nanoseconds(self) -> Span {
4135 Span::new().nanoseconds(self)
4136 }
4137 }
4138 };
4139}
4140
4141impl_to_span!(i8);
4142impl_to_span!(i16);
4143impl_to_span!(i32);
4144impl_to_span!(i64);
4145
4146/// A way to refer to a single calendar or clock unit.
4147///
4148/// This type is principally used in APIs involving a [`Span`], which is a
4149/// duration of time. For example, routines like [`Zoned::until`] permit
4150/// specifying the largest unit of the span returned:
4151///
4152/// ```
4153/// use jiff::{Unit, Zoned};
4154///
4155/// let zdt1: Zoned = "2024-07-06 17:40-04[America/New_York]".parse()?;
4156/// let zdt2: Zoned = "2024-11-05 08:00-05[America/New_York]".parse()?;
4157/// let span = zdt1.until((Unit::Year, &zdt2))?;
4158/// assert_eq!(format!("{span:#}"), "3mo 29d 14h 20m");
4159///
4160/// # Ok::<(), Box<dyn std::error::Error>>(())
4161/// ```
4162///
4163/// But a `Unit` is also used in APIs for rounding datetimes themselves:
4164///
4165/// ```
4166/// use jiff::{Unit, Zoned};
4167///
4168/// let zdt: Zoned = "2024-07-06 17:44:22.158-04[America/New_York]".parse()?;
4169/// let nearest_minute = zdt.round(Unit::Minute)?;
4170/// assert_eq!(
4171/// nearest_minute.to_string(),
4172/// "2024-07-06T17:44:00-04:00[America/New_York]",
4173/// );
4174///
4175/// # Ok::<(), Box<dyn std::error::Error>>(())
4176/// ```
4177///
4178/// # Example: ordering
4179///
4180/// This example demonstrates that `Unit` has an ordering defined such that
4181/// bigger units compare greater than smaller units.
4182///
4183/// ```
4184/// use jiff::Unit;
4185///
4186/// assert!(Unit::Year > Unit::Nanosecond);
4187/// assert!(Unit::Day > Unit::Hour);
4188/// assert!(Unit::Hour > Unit::Minute);
4189/// assert!(Unit::Hour > Unit::Minute);
4190/// assert_eq!(Unit::Hour, Unit::Hour);
4191/// ```
4192#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
4193pub enum Unit {
4194 /// A Gregorian calendar year. It usually has 365 days for non-leap years,
4195 /// and 366 days for leap years.
4196 Year = 9,
4197 /// A Gregorian calendar month. It usually has one of 28, 29, 30 or 31
4198 /// days.
4199 Month = 8,
4200 /// A week is 7 days that either begins on Sunday or Monday.
4201 Week = 7,
4202 /// A day is usually 24 hours, but some days may have different lengths
4203 /// due to time zone transitions.
4204 Day = 6,
4205 /// An hour is always 60 minutes.
4206 Hour = 5,
4207 /// A minute is always 60 seconds. (Jiff behaves as if leap seconds do not
4208 /// exist.)
4209 Minute = 4,
4210 /// A second is always 1,000 milliseconds.
4211 Second = 3,
4212 /// A millisecond is always 1,000 microseconds.
4213 Millisecond = 2,
4214 /// A microsecond is always 1,000 nanoseconds.
4215 Microsecond = 1,
4216 /// A nanosecond is the smallest granularity of time supported by Jiff.
4217 Nanosecond = 0,
4218}
4219
4220impl Unit {
4221 /// Returns the next biggest unit, if one exists.
4222 pub(crate) fn next(&self) -> Option<Unit> {
4223 match *self {
4224 Unit::Year => None,
4225 Unit::Month => Some(Unit::Year),
4226 Unit::Week => Some(Unit::Month),
4227 Unit::Day => Some(Unit::Week),
4228 Unit::Hour => Some(Unit::Day),
4229 Unit::Minute => Some(Unit::Hour),
4230 Unit::Second => Some(Unit::Minute),
4231 Unit::Millisecond => Some(Unit::Second),
4232 Unit::Microsecond => Some(Unit::Millisecond),
4233 Unit::Nanosecond => Some(Unit::Microsecond),
4234 }
4235 }
4236
4237 /*
4238 /// Returns the next smallest unit, if one exists.
4239 pub(crate) fn prev(&self) -> Option<Unit> {
4240 match *self {
4241 Unit::Year => Some(Unit::Month),
4242 Unit::Month => Some(Unit::Week),
4243 Unit::Week => Some(Unit::Day),
4244 Unit::Day => Some(Unit::Hour),
4245 Unit::Hour => Some(Unit::Minute),
4246 Unit::Minute => Some(Unit::Second),
4247 Unit::Second => Some(Unit::Millisecond),
4248 Unit::Millisecond => Some(Unit::Microsecond),
4249 Unit::Microsecond => Some(Unit::Nanosecond),
4250 Unit::Nanosecond => None,
4251 }
4252 }
4253 */
4254
4255 /// Returns the number of nanoseconds in this unit as a 128-bit integer.
4256 ///
4257 /// # Panics
4258 ///
4259 /// When this unit is always variable. That is, years or months.
4260 pub(crate) fn nanoseconds(self) -> NoUnits128 {
4261 match self {
4262 Unit::Nanosecond => Constant(1),
4263 Unit::Microsecond => t::NANOS_PER_MICRO,
4264 Unit::Millisecond => t::NANOS_PER_MILLI,
4265 Unit::Second => t::NANOS_PER_SECOND,
4266 Unit::Minute => t::NANOS_PER_MINUTE,
4267 Unit::Hour => t::NANOS_PER_HOUR,
4268 Unit::Day => t::NANOS_PER_CIVIL_DAY,
4269 Unit::Week => t::NANOS_PER_CIVIL_WEEK,
4270 unit => unreachable!("{unit:?} has no definitive time interval"),
4271 }
4272 .rinto()
4273 }
4274
4275 /// Returns true when this unit is definitively variable.
4276 ///
4277 /// In effect, this is any unit bigger than 'day', because any such unit
4278 /// can vary in time depending on its reference point. A 'day' can as well,
4279 /// but we sorta special case 'day' to mean '24 hours' for cases where
4280 /// the user is dealing with civil time.
4281 fn is_variable(self) -> bool {
4282 matches!(self, Unit::Year | Unit::Month | Unit::Week | Unit::Day)
4283 }
4284
4285 /// A human readable singular description of this unit of time.
4286 pub(crate) fn singular(&self) -> &'static str {
4287 match *self {
4288 Unit::Year => "year",
4289 Unit::Month => "month",
4290 Unit::Week => "week",
4291 Unit::Day => "day",
4292 Unit::Hour => "hour",
4293 Unit::Minute => "minute",
4294 Unit::Second => "second",
4295 Unit::Millisecond => "millisecond",
4296 Unit::Microsecond => "microsecond",
4297 Unit::Nanosecond => "nanosecond",
4298 }
4299 }
4300
4301 /// A human readable plural description of this unit of time.
4302 pub(crate) fn plural(&self) -> &'static str {
4303 match *self {
4304 Unit::Year => "years",
4305 Unit::Month => "months",
4306 Unit::Week => "weeks",
4307 Unit::Day => "days",
4308 Unit::Hour => "hours",
4309 Unit::Minute => "minutes",
4310 Unit::Second => "seconds",
4311 Unit::Millisecond => "milliseconds",
4312 Unit::Microsecond => "microseconds",
4313 Unit::Nanosecond => "nanoseconds",
4314 }
4315 }
4316
4317 /// A very succinct label corresponding to this unit.
4318 pub(crate) fn compact(&self) -> &'static str {
4319 match *self {
4320 Unit::Year => "y",
4321 Unit::Month => "mo",
4322 Unit::Week => "w",
4323 Unit::Day => "d",
4324 Unit::Hour => "h",
4325 Unit::Minute => "m",
4326 Unit::Second => "s",
4327 Unit::Millisecond => "ms",
4328 Unit::Microsecond => "µs",
4329 Unit::Nanosecond => "ns",
4330 }
4331 }
4332
4333 /// Return this unit as a `usize`.
4334 ///
4335 /// This is use `unit as usize`.
4336 pub(crate) fn as_usize(&self) -> usize {
4337 *self as usize
4338 }
4339
4340 /// The inverse of `unit as usize`.
4341 fn from_usize(n: usize) -> Option<Unit> {
4342 match n {
4343 0 => Some(Unit::Nanosecond),
4344 1 => Some(Unit::Microsecond),
4345 2 => Some(Unit::Millisecond),
4346 3 => Some(Unit::Second),
4347 4 => Some(Unit::Minute),
4348 5 => Some(Unit::Hour),
4349 6 => Some(Unit::Day),
4350 7 => Some(Unit::Week),
4351 8 => Some(Unit::Month),
4352 9 => Some(Unit::Year),
4353 _ => None,
4354 }
4355 }
4356}
4357
4358#[cfg(test)]
4359impl quickcheck::Arbitrary for Unit {
4360 fn arbitrary(g: &mut quickcheck::Gen) -> Unit {
4361 Unit::from_usize(usize::arbitrary(g) % 10).unwrap()
4362 }
4363
4364 fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
4365 alloc::boxed::Box::new(
4366 (*self as usize)
4367 .shrink()
4368 .map(|n| Unit::from_usize(n % 10).unwrap()),
4369 )
4370 }
4371}
4372
4373/// Options for [`Span::checked_add`] and [`Span::checked_sub`].
4374///
4375/// This type provides a way to ergonomically add two spans with an optional
4376/// relative datetime. Namely, a relative datetime is only needed when at least
4377/// one of the two spans being added (or subtracted) has a non-zero calendar
4378/// unit (years, months, weeks or days). Otherwise, an error will be returned.
4379///
4380/// Callers may use [`SpanArithmetic::days_are_24_hours`] to opt into 24-hour
4381/// invariant days (and 7-day weeks) without providing a relative datetime.
4382///
4383/// The main way to construct values of this type is with its `From` trait
4384/// implementations:
4385///
4386/// * `From<Span> for SpanArithmetic` adds (or subtracts) the given span to the
4387/// receiver in [`Span::checked_add`] (or [`Span::checked_sub`]).
4388/// * `From<(Span, civil::Date)> for SpanArithmetic` adds (or subtracts)
4389/// the given span to the receiver in [`Span::checked_add`] (or
4390/// [`Span::checked_sub`]), relative to the given date. There are also `From`
4391/// implementations for `civil::DateTime`, `Zoned` and [`SpanRelativeTo`].
4392///
4393/// # Example
4394///
4395/// ```
4396/// use jiff::ToSpan;
4397///
4398/// assert_eq!(
4399/// 1.hour().checked_add(30.minutes())?,
4400/// 1.hour().minutes(30).fieldwise(),
4401/// );
4402///
4403/// # Ok::<(), Box<dyn std::error::Error>>(())
4404/// ```
4405#[derive(Clone, Copy, Debug)]
4406pub struct SpanArithmetic<'a> {
4407 duration: Duration,
4408 relative: Option<SpanRelativeTo<'a>>,
4409}
4410
4411impl<'a> SpanArithmetic<'a> {
4412 /// This is a convenience function for setting the relative option on
4413 /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4414 ///
4415 /// # Example
4416 ///
4417 /// When doing arithmetic on spans involving days, either a relative
4418 /// datetime must be provided, or a special assertion opting into 24-hour
4419 /// days is required. Otherwise, you get an error.
4420 ///
4421 /// ```
4422 /// use jiff::{SpanArithmetic, ToSpan};
4423 ///
4424 /// let span1 = 2.days().hours(12);
4425 /// let span2 = 12.hours();
4426 /// // No relative date provided, which results in an error.
4427 /// assert_eq!(
4428 /// span1.checked_add(span2).unwrap_err().to_string(),
4429 /// "using unit 'day' in a span or configuration requires that \
4430 /// either a relative reference time be given or \
4431 /// `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
4432 /// invariant 24-hour days, but neither were provided",
4433 /// );
4434 /// let sum = span1.checked_add(
4435 /// SpanArithmetic::from(span2).days_are_24_hours(),
4436 /// )?;
4437 /// assert_eq!(sum, 3.days().fieldwise());
4438 ///
4439 /// # Ok::<(), Box<dyn std::error::Error>>(())
4440 /// ```
4441 #[inline]
4442 pub fn days_are_24_hours(self) -> SpanArithmetic<'a> {
4443 self.relative(SpanRelativeTo::days_are_24_hours())
4444 }
4445}
4446
4447impl<'a> SpanArithmetic<'a> {
4448 #[inline]
4449 fn relative<R: Into<SpanRelativeTo<'a>>>(
4450 self,
4451 relative: R,
4452 ) -> SpanArithmetic<'a> {
4453 SpanArithmetic { relative: Some(relative.into()), ..self }
4454 }
4455
4456 #[inline]
4457 fn checked_add(self, span1: Span) -> Result<Span, Error> {
4458 match self.duration.to_signed()? {
4459 SDuration::Span(span2) => {
4460 span1.checked_add_span(self.relative, &span2)
4461 }
4462 SDuration::Absolute(dur2) => {
4463 span1.checked_add_duration(self.relative, dur2)
4464 }
4465 }
4466 }
4467}
4468
4469impl From<Span> for SpanArithmetic<'static> {
4470 fn from(span: Span) -> SpanArithmetic<'static> {
4471 let duration = Duration::from(span);
4472 SpanArithmetic { duration, relative: None }
4473 }
4474}
4475
4476impl<'a> From<&'a Span> for SpanArithmetic<'static> {
4477 fn from(span: &'a Span) -> SpanArithmetic<'static> {
4478 let duration = Duration::from(*span);
4479 SpanArithmetic { duration, relative: None }
4480 }
4481}
4482
4483impl From<(Span, Date)> for SpanArithmetic<'static> {
4484 #[inline]
4485 fn from((span, date): (Span, Date)) -> SpanArithmetic<'static> {
4486 SpanArithmetic::from(span).relative(date)
4487 }
4488}
4489
4490impl From<(Span, DateTime)> for SpanArithmetic<'static> {
4491 #[inline]
4492 fn from((span, datetime): (Span, DateTime)) -> SpanArithmetic<'static> {
4493 SpanArithmetic::from(span).relative(datetime)
4494 }
4495}
4496
4497impl<'a> From<(Span, &'a Zoned)> for SpanArithmetic<'a> {
4498 #[inline]
4499 fn from((span, zoned): (Span, &'a Zoned)) -> SpanArithmetic<'a> {
4500 SpanArithmetic::from(span).relative(zoned)
4501 }
4502}
4503
4504impl<'a> From<(Span, SpanRelativeTo<'a>)> for SpanArithmetic<'a> {
4505 #[inline]
4506 fn from(
4507 (span, relative): (Span, SpanRelativeTo<'a>),
4508 ) -> SpanArithmetic<'a> {
4509 SpanArithmetic::from(span).relative(relative)
4510 }
4511}
4512
4513impl<'a> From<(&'a Span, Date)> for SpanArithmetic<'static> {
4514 #[inline]
4515 fn from((span, date): (&'a Span, Date)) -> SpanArithmetic<'static> {
4516 SpanArithmetic::from(span).relative(date)
4517 }
4518}
4519
4520impl<'a> From<(&'a Span, DateTime)> for SpanArithmetic<'static> {
4521 #[inline]
4522 fn from(
4523 (span, datetime): (&'a Span, DateTime),
4524 ) -> SpanArithmetic<'static> {
4525 SpanArithmetic::from(span).relative(datetime)
4526 }
4527}
4528
4529impl<'a, 'b> From<(&'a Span, &'b Zoned)> for SpanArithmetic<'b> {
4530 #[inline]
4531 fn from((span, zoned): (&'a Span, &'b Zoned)) -> SpanArithmetic<'b> {
4532 SpanArithmetic::from(span).relative(zoned)
4533 }
4534}
4535
4536impl<'a, 'b> From<(&'a Span, SpanRelativeTo<'b>)> for SpanArithmetic<'b> {
4537 #[inline]
4538 fn from(
4539 (span, relative): (&'a Span, SpanRelativeTo<'b>),
4540 ) -> SpanArithmetic<'b> {
4541 SpanArithmetic::from(span).relative(relative)
4542 }
4543}
4544
4545impl From<SignedDuration> for SpanArithmetic<'static> {
4546 fn from(duration: SignedDuration) -> SpanArithmetic<'static> {
4547 let duration = Duration::from(duration);
4548 SpanArithmetic { duration, relative: None }
4549 }
4550}
4551
4552impl From<(SignedDuration, Date)> for SpanArithmetic<'static> {
4553 #[inline]
4554 fn from(
4555 (duration, date): (SignedDuration, Date),
4556 ) -> SpanArithmetic<'static> {
4557 SpanArithmetic::from(duration).relative(date)
4558 }
4559}
4560
4561impl From<(SignedDuration, DateTime)> for SpanArithmetic<'static> {
4562 #[inline]
4563 fn from(
4564 (duration, datetime): (SignedDuration, DateTime),
4565 ) -> SpanArithmetic<'static> {
4566 SpanArithmetic::from(duration).relative(datetime)
4567 }
4568}
4569
4570impl<'a> From<(SignedDuration, &'a Zoned)> for SpanArithmetic<'a> {
4571 #[inline]
4572 fn from(
4573 (duration, zoned): (SignedDuration, &'a Zoned),
4574 ) -> SpanArithmetic<'a> {
4575 SpanArithmetic::from(duration).relative(zoned)
4576 }
4577}
4578
4579impl From<UnsignedDuration> for SpanArithmetic<'static> {
4580 fn from(duration: UnsignedDuration) -> SpanArithmetic<'static> {
4581 let duration = Duration::from(duration);
4582 SpanArithmetic { duration, relative: None }
4583 }
4584}
4585
4586impl From<(UnsignedDuration, Date)> for SpanArithmetic<'static> {
4587 #[inline]
4588 fn from(
4589 (duration, date): (UnsignedDuration, Date),
4590 ) -> SpanArithmetic<'static> {
4591 SpanArithmetic::from(duration).relative(date)
4592 }
4593}
4594
4595impl From<(UnsignedDuration, DateTime)> for SpanArithmetic<'static> {
4596 #[inline]
4597 fn from(
4598 (duration, datetime): (UnsignedDuration, DateTime),
4599 ) -> SpanArithmetic<'static> {
4600 SpanArithmetic::from(duration).relative(datetime)
4601 }
4602}
4603
4604impl<'a> From<(UnsignedDuration, &'a Zoned)> for SpanArithmetic<'a> {
4605 #[inline]
4606 fn from(
4607 (duration, zoned): (UnsignedDuration, &'a Zoned),
4608 ) -> SpanArithmetic<'a> {
4609 SpanArithmetic::from(duration).relative(zoned)
4610 }
4611}
4612
4613/// Options for [`Span::compare`].
4614///
4615/// This type provides a way to ergonomically compare two spans with an
4616/// optional relative datetime. Namely, a relative datetime is only needed when
4617/// at least one of the two spans being compared has a non-zero calendar unit
4618/// (years, months, weeks or days). Otherwise, an error will be returned.
4619///
4620/// Callers may use [`SpanCompare::days_are_24_hours`] to opt into 24-hour
4621/// invariant days (and 7-day weeks) without providing a relative datetime.
4622///
4623/// The main way to construct values of this type is with its `From` trait
4624/// implementations:
4625///
4626/// * `From<Span> for SpanCompare` compares the given span to the receiver
4627/// in [`Span::compare`].
4628/// * `From<(Span, civil::Date)> for SpanCompare` compares the given span
4629/// to the receiver in [`Span::compare`], relative to the given date. There
4630/// are also `From` implementations for `civil::DateTime`, `Zoned` and
4631/// [`SpanRelativeTo`].
4632///
4633/// # Example
4634///
4635/// ```
4636/// use jiff::ToSpan;
4637///
4638/// let span1 = 3.hours();
4639/// let span2 = 180.minutes();
4640/// assert_eq!(span1.compare(span2)?, std::cmp::Ordering::Equal);
4641///
4642/// # Ok::<(), Box<dyn std::error::Error>>(())
4643/// ```
4644#[derive(Clone, Copy, Debug)]
4645pub struct SpanCompare<'a> {
4646 span: Span,
4647 relative: Option<SpanRelativeTo<'a>>,
4648}
4649
4650impl<'a> SpanCompare<'a> {
4651 /// This is a convenience function for setting the relative option on
4652 /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4653 ///
4654 /// # Example
4655 ///
4656 /// When comparing spans involving days, either a relative datetime must be
4657 /// provided, or a special assertion opting into 24-hour days is
4658 /// required. Otherwise, you get an error.
4659 ///
4660 /// ```
4661 /// use jiff::{SpanCompare, ToSpan};
4662 ///
4663 /// let span1 = 2.days().hours(12);
4664 /// let span2 = 60.hours();
4665 /// // No relative date provided, which results in an error.
4666 /// assert_eq!(
4667 /// span1.compare(span2).unwrap_err().to_string(),
4668 /// "using unit 'day' in a span or configuration requires that \
4669 /// either a relative reference time be given or \
4670 /// `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
4671 /// invariant 24-hour days, but neither were provided",
4672 /// );
4673 /// let ordering = span1.compare(
4674 /// SpanCompare::from(span2).days_are_24_hours(),
4675 /// )?;
4676 /// assert_eq!(ordering, std::cmp::Ordering::Equal);
4677 ///
4678 /// # Ok::<(), Box<dyn std::error::Error>>(())
4679 /// ```
4680 #[inline]
4681 pub fn days_are_24_hours(self) -> SpanCompare<'a> {
4682 self.relative(SpanRelativeTo::days_are_24_hours())
4683 }
4684}
4685
4686impl<'a> SpanCompare<'a> {
4687 #[inline]
4688 fn new(span: Span) -> SpanCompare<'static> {
4689 SpanCompare { span, relative: None }
4690 }
4691
4692 #[inline]
4693 fn relative<R: Into<SpanRelativeTo<'a>>>(
4694 self,
4695 relative: R,
4696 ) -> SpanCompare<'a> {
4697 SpanCompare { relative: Some(relative.into()), ..self }
4698 }
4699
4700 fn compare(self, span: Span) -> Result<Ordering, Error> {
4701 let (span1, span2) = (span, self.span);
4702 let unit = span1.largest_unit().max(span2.largest_unit());
4703 let start = match self.relative {
4704 Some(r) => match r.to_relative(unit)? {
4705 Some(r) => r,
4706 None => {
4707 let nanos1 = span1.to_invariant_nanoseconds();
4708 let nanos2 = span2.to_invariant_nanoseconds();
4709 return Ok(nanos1.cmp(&nanos2));
4710 }
4711 },
4712 None => {
4713 requires_relative_date_err(unit)?;
4714 let nanos1 = span1.to_invariant_nanoseconds();
4715 let nanos2 = span2.to_invariant_nanoseconds();
4716 return Ok(nanos1.cmp(&nanos2));
4717 }
4718 };
4719 let end1 = start.checked_add(span1)?.to_nanosecond();
4720 let end2 = start.checked_add(span2)?.to_nanosecond();
4721 Ok(end1.cmp(&end2))
4722 }
4723}
4724
4725impl From<Span> for SpanCompare<'static> {
4726 fn from(span: Span) -> SpanCompare<'static> {
4727 SpanCompare::new(span)
4728 }
4729}
4730
4731impl<'a> From<&'a Span> for SpanCompare<'static> {
4732 fn from(span: &'a Span) -> SpanCompare<'static> {
4733 SpanCompare::new(*span)
4734 }
4735}
4736
4737impl From<(Span, Date)> for SpanCompare<'static> {
4738 #[inline]
4739 fn from((span, date): (Span, Date)) -> SpanCompare<'static> {
4740 SpanCompare::from(span).relative(date)
4741 }
4742}
4743
4744impl From<(Span, DateTime)> for SpanCompare<'static> {
4745 #[inline]
4746 fn from((span, datetime): (Span, DateTime)) -> SpanCompare<'static> {
4747 SpanCompare::from(span).relative(datetime)
4748 }
4749}
4750
4751impl<'a> From<(Span, &'a Zoned)> for SpanCompare<'a> {
4752 #[inline]
4753 fn from((span, zoned): (Span, &'a Zoned)) -> SpanCompare<'a> {
4754 SpanCompare::from(span).relative(zoned)
4755 }
4756}
4757
4758impl<'a> From<(Span, SpanRelativeTo<'a>)> for SpanCompare<'a> {
4759 #[inline]
4760 fn from((span, relative): (Span, SpanRelativeTo<'a>)) -> SpanCompare<'a> {
4761 SpanCompare::from(span).relative(relative)
4762 }
4763}
4764
4765impl<'a> From<(&'a Span, Date)> for SpanCompare<'static> {
4766 #[inline]
4767 fn from((span, date): (&'a Span, Date)) -> SpanCompare<'static> {
4768 SpanCompare::from(span).relative(date)
4769 }
4770}
4771
4772impl<'a> From<(&'a Span, DateTime)> for SpanCompare<'static> {
4773 #[inline]
4774 fn from((span, datetime): (&'a Span, DateTime)) -> SpanCompare<'static> {
4775 SpanCompare::from(span).relative(datetime)
4776 }
4777}
4778
4779impl<'a, 'b> From<(&'a Span, &'b Zoned)> for SpanCompare<'b> {
4780 #[inline]
4781 fn from((span, zoned): (&'a Span, &'b Zoned)) -> SpanCompare<'b> {
4782 SpanCompare::from(span).relative(zoned)
4783 }
4784}
4785
4786impl<'a, 'b> From<(&'a Span, SpanRelativeTo<'b>)> for SpanCompare<'b> {
4787 #[inline]
4788 fn from(
4789 (span, relative): (&'a Span, SpanRelativeTo<'b>),
4790 ) -> SpanCompare<'b> {
4791 SpanCompare::from(span).relative(relative)
4792 }
4793}
4794
4795/// Options for [`Span::total`].
4796///
4797/// This type provides a way to ergonomically determine the number of a
4798/// particular unit in a span, with a potentially fractional component, with
4799/// an optional relative datetime. Namely, a relative datetime is only needed
4800/// when the span has a non-zero calendar unit (years, months, weeks or days).
4801/// Otherwise, an error will be returned.
4802///
4803/// Callers may use [`SpanTotal::days_are_24_hours`] to opt into 24-hour
4804/// invariant days (and 7-day weeks) without providing a relative datetime.
4805///
4806/// The main way to construct values of this type is with its `From` trait
4807/// implementations:
4808///
4809/// * `From<Unit> for SpanTotal` computes a total for the given unit in the
4810/// receiver span for [`Span::total`].
4811/// * `From<(Unit, civil::Date)> for SpanTotal` computes a total for the given
4812/// unit in the receiver span for [`Span::total`], relative to the given date.
4813/// There are also `From` implementations for `civil::DateTime`, `Zoned` and
4814/// [`SpanRelativeTo`].
4815///
4816/// # Example
4817///
4818/// This example shows how to find the number of seconds in a particular span:
4819///
4820/// ```
4821/// use jiff::{ToSpan, Unit};
4822///
4823/// let span = 3.hours().minutes(10);
4824/// assert_eq!(span.total(Unit::Second)?, 11_400.0);
4825///
4826/// # Ok::<(), Box<dyn std::error::Error>>(())
4827/// ```
4828///
4829/// # Example: 24 hour days
4830///
4831/// This shows how to find the total number of 24 hour days in `123,456,789`
4832/// seconds.
4833///
4834/// ```
4835/// use jiff::{SpanTotal, ToSpan, Unit};
4836///
4837/// let span = 123_456_789.seconds();
4838/// assert_eq!(
4839/// span.total(SpanTotal::from(Unit::Day).days_are_24_hours())?,
4840/// 1428.8980208333332,
4841/// );
4842///
4843/// # Ok::<(), Box<dyn std::error::Error>>(())
4844/// ```
4845///
4846/// # Example: DST is taken into account
4847///
4848/// The month of March 2024 in `America/New_York` had 31 days, but one of those
4849/// days was 23 hours long due a transition into daylight saving time:
4850///
4851/// ```
4852/// use jiff::{civil::date, ToSpan, Unit};
4853///
4854/// let span = 744.hours();
4855/// let relative = date(2024, 3, 1).in_tz("America/New_York")?;
4856/// // Because of the short day, 744 hours is actually a little *more* than
4857/// // 1 month starting from 2024-03-01.
4858/// assert_eq!(span.total((Unit::Month, &relative))?, 1.0013888888888889);
4859///
4860/// # Ok::<(), Box<dyn std::error::Error>>(())
4861/// ```
4862///
4863/// Now compare what happens when the relative datetime is civil and not
4864/// time zone aware:
4865///
4866/// ```
4867/// use jiff::{civil::date, ToSpan, Unit};
4868///
4869/// let span = 744.hours();
4870/// let relative = date(2024, 3, 1);
4871/// assert_eq!(span.total((Unit::Month, relative))?, 1.0);
4872///
4873/// # Ok::<(), Box<dyn std::error::Error>>(())
4874/// ```
4875#[derive(Clone, Copy, Debug)]
4876pub struct SpanTotal<'a> {
4877 unit: Unit,
4878 relative: Option<SpanRelativeTo<'a>>,
4879}
4880
4881impl<'a> SpanTotal<'a> {
4882 /// This is a convenience function for setting the relative option on
4883 /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4884 ///
4885 /// # Example
4886 ///
4887 /// When computing the total duration for spans involving days, either a
4888 /// relative datetime must be provided, or a special assertion opting into
4889 /// 24-hour days is required. Otherwise, you get an error.
4890 ///
4891 /// ```
4892 /// use jiff::{civil::date, SpanTotal, ToSpan, Unit};
4893 ///
4894 /// let span = 2.days().hours(12);
4895 ///
4896 /// // No relative date provided, which results in an error.
4897 /// assert_eq!(
4898 /// span.total(Unit::Hour).unwrap_err().to_string(),
4899 /// "using unit 'day' in a span or configuration requires that either \
4900 /// a relative reference time be given or \
4901 /// `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
4902 /// invariant 24-hour days, but neither were provided",
4903 /// );
4904 ///
4905 /// // If we can assume all days are 24 hours, then we can assert it:
4906 /// let total = span.total(
4907 /// SpanTotal::from(Unit::Hour).days_are_24_hours(),
4908 /// )?;
4909 /// assert_eq!(total, 60.0);
4910 ///
4911 /// // Or provide a relative datetime, which is preferred if possible:
4912 /// let total = span.total((Unit::Hour, date(2025, 1, 26)))?;
4913 /// assert_eq!(total, 60.0);
4914 ///
4915 /// # Ok::<(), Box<dyn std::error::Error>>(())
4916 /// ```
4917 #[inline]
4918 pub fn days_are_24_hours(self) -> SpanTotal<'a> {
4919 self.relative(SpanRelativeTo::days_are_24_hours())
4920 }
4921}
4922
4923impl<'a> SpanTotal<'a> {
4924 #[inline]
4925 fn new(unit: Unit) -> SpanTotal<'static> {
4926 SpanTotal { unit, relative: None }
4927 }
4928
4929 #[inline]
4930 fn relative<R: Into<SpanRelativeTo<'a>>>(
4931 self,
4932 relative: R,
4933 ) -> SpanTotal<'a> {
4934 SpanTotal { relative: Some(relative.into()), ..self }
4935 }
4936
4937 fn total(self, span: Span) -> Result<f64, Error> {
4938 let max_unit = self.unit.max(span.largest_unit());
4939 let relative = match self.relative {
4940 Some(r) => match r.to_relative(max_unit)? {
4941 Some(r) => r,
4942 None => {
4943 return Ok(self.total_invariant(span));
4944 }
4945 },
4946 None => {
4947 requires_relative_date_err(max_unit)?;
4948 return Ok(self.total_invariant(span));
4949 }
4950 };
4951 let relspan = relative.into_relative_span(self.unit, span)?;
4952 if !self.unit.is_variable() {
4953 return Ok(self.total_invariant(relspan.span));
4954 }
4955
4956 assert!(self.unit >= Unit::Day);
4957 let sign = relspan.span.get_sign_ranged();
4958 let (relative_start, relative_end) = match relspan.kind {
4959 RelativeSpanKind::Civil { start, end } => {
4960 let start = Relative::Civil(start);
4961 let end = Relative::Civil(end);
4962 (start, end)
4963 }
4964 RelativeSpanKind::Zoned { start, end } => {
4965 let start = Relative::Zoned(start);
4966 let end = Relative::Zoned(end);
4967 (start, end)
4968 }
4969 };
4970 let (relative0, relative1) = clamp_relative_span(
4971 &relative_start,
4972 relspan.span.without_lower(self.unit),
4973 self.unit,
4974 sign.rinto(),
4975 )?;
4976 let denom = (relative1 - relative0).get() as f64;
4977 let numer = (relative_end.to_nanosecond() - relative0).get() as f64;
4978 let unit_val = relspan.span.get_units_ranged(self.unit).get() as f64;
4979 Ok(unit_val + (numer / denom) * (sign.get() as f64))
4980 }
4981
4982 #[inline]
4983 fn total_invariant(&self, span: Span) -> f64 {
4984 assert!(self.unit <= Unit::Week);
4985 let nanos = span.to_invariant_nanoseconds();
4986 (nanos.get() as f64) / (self.unit.nanoseconds().get() as f64)
4987 }
4988}
4989
4990impl From<Unit> for SpanTotal<'static> {
4991 #[inline]
4992 fn from(unit: Unit) -> SpanTotal<'static> {
4993 SpanTotal::new(unit)
4994 }
4995}
4996
4997impl From<(Unit, Date)> for SpanTotal<'static> {
4998 #[inline]
4999 fn from((unit, date): (Unit, Date)) -> SpanTotal<'static> {
5000 SpanTotal::from(unit).relative(date)
5001 }
5002}
5003
5004impl From<(Unit, DateTime)> for SpanTotal<'static> {
5005 #[inline]
5006 fn from((unit, datetime): (Unit, DateTime)) -> SpanTotal<'static> {
5007 SpanTotal::from(unit).relative(datetime)
5008 }
5009}
5010
5011impl<'a> From<(Unit, &'a Zoned)> for SpanTotal<'a> {
5012 #[inline]
5013 fn from((unit, zoned): (Unit, &'a Zoned)) -> SpanTotal<'a> {
5014 SpanTotal::from(unit).relative(zoned)
5015 }
5016}
5017
5018impl<'a> From<(Unit, SpanRelativeTo<'a>)> for SpanTotal<'a> {
5019 #[inline]
5020 fn from((unit, relative): (Unit, SpanRelativeTo<'a>)) -> SpanTotal<'a> {
5021 SpanTotal::from(unit).relative(relative)
5022 }
5023}
5024
5025/// Options for [`Span::round`].
5026///
5027/// This type provides a way to configure the rounding of a span. This
5028/// includes setting the smallest unit (i.e., the unit to round), the
5029/// largest unit, the rounding increment, the rounding mode (e.g., "ceil" or
5030/// "truncate") and the datetime that the span is relative to.
5031///
5032/// `Span::round` accepts anything that implements `Into<SpanRound>`. There are
5033/// a few key trait implementations that make this convenient:
5034///
5035/// * `From<Unit> for SpanRound` will construct a rounding configuration where
5036/// the smallest unit is set to the one given.
5037/// * `From<(Unit, i64)> for SpanRound` will construct a rounding configuration
5038/// where the smallest unit and the rounding increment are set to the ones
5039/// given.
5040///
5041/// In order to set other options (like the largest unit, the rounding mode
5042/// and the relative datetime), one must explicitly create a `SpanRound` and
5043/// pass it to `Span::round`.
5044///
5045/// # Example
5046///
5047/// This example shows how to find how many full 3 month quarters are in a
5048/// particular span of time.
5049///
5050/// ```
5051/// use jiff::{civil::date, RoundMode, SpanRound, ToSpan, Unit};
5052///
5053/// let span1 = 10.months().days(15);
5054/// let round = SpanRound::new()
5055/// .smallest(Unit::Month)
5056/// .increment(3)
5057/// .mode(RoundMode::Trunc)
5058/// // A relative datetime must be provided when
5059/// // rounding involves calendar units.
5060/// .relative(date(2024, 1, 1));
5061/// let span2 = span1.round(round)?;
5062/// assert_eq!(span2.get_months() / 3, 3);
5063///
5064/// # Ok::<(), Box<dyn std::error::Error>>(())
5065/// ```
5066#[derive(Clone, Copy, Debug)]
5067pub struct SpanRound<'a> {
5068 largest: Option<Unit>,
5069 smallest: Unit,
5070 mode: RoundMode,
5071 increment: i64,
5072 relative: Option<SpanRelativeTo<'a>>,
5073}
5074
5075impl<'a> SpanRound<'a> {
5076 /// Create a new default configuration for rounding a span via
5077 /// [`Span::round`].
5078 ///
5079 /// The default configuration does no rounding.
5080 #[inline]
5081 pub fn new() -> SpanRound<'static> {
5082 SpanRound {
5083 largest: None,
5084 smallest: Unit::Nanosecond,
5085 mode: RoundMode::HalfExpand,
5086 increment: 1,
5087 relative: None,
5088 }
5089 }
5090
5091 /// Set the smallest units allowed in the span returned. These are the
5092 /// units that the span is rounded to.
5093 ///
5094 /// # Errors
5095 ///
5096 /// The smallest units must be no greater than the largest units. If this
5097 /// is violated, then rounding a span with this configuration will result
5098 /// in an error.
5099 ///
5100 /// If a smallest unit bigger than days is selected without a relative
5101 /// datetime reference point, then an error is returned when using this
5102 /// configuration with [`Span::round`].
5103 ///
5104 /// # Example
5105 ///
5106 /// A basic example that rounds to the nearest minute:
5107 ///
5108 /// ```
5109 /// use jiff::{ToSpan, Unit};
5110 ///
5111 /// let span = 15.minutes().seconds(46);
5112 /// assert_eq!(span.round(Unit::Minute)?, 16.minutes().fieldwise());
5113 ///
5114 /// # Ok::<(), Box<dyn std::error::Error>>(())
5115 /// ```
5116 #[inline]
5117 pub fn smallest(self, unit: Unit) -> SpanRound<'a> {
5118 SpanRound { smallest: unit, ..self }
5119 }
5120
5121 /// Set the largest units allowed in the span returned.
5122 ///
5123 /// When a largest unit is not specified, then it defaults to the largest
5124 /// non-zero unit that is at least as big as the configured smallest
5125 /// unit. For example, given a span of `2 months 17 hours`, the default
5126 /// largest unit would be `Unit::Month`. The default implies that a span's
5127 /// units do not get "bigger" than what was given.
5128 ///
5129 /// Once a largest unit is set, there is no way to change this rounding
5130 /// configuration back to using the "automatic" default. Instead, callers
5131 /// must create a new configuration.
5132 ///
5133 /// If a largest unit is set and no other options are set, then the
5134 /// rounding operation can be said to be a "re-balancing." That is, the
5135 /// span won't lose precision, but the way in which it is expressed may
5136 /// change.
5137 ///
5138 /// # Errors
5139 ///
5140 /// The largest units, when set, must be at least as big as the smallest
5141 /// units (which defaults to [`Unit::Nanosecond`]). If this is violated,
5142 /// then rounding a span with this configuration will result in an error.
5143 ///
5144 /// If a largest unit bigger than days is selected without a relative
5145 /// datetime reference point, then an error is returned when using this
5146 /// configuration with [`Span::round`].
5147 ///
5148 /// # Example: re-balancing
5149 ///
5150 /// This shows how a span can be re-balanced without losing precision:
5151 ///
5152 /// ```
5153 /// use jiff::{SpanRound, ToSpan, Unit};
5154 ///
5155 /// let span = 86_401_123_456_789i64.nanoseconds();
5156 /// assert_eq!(
5157 /// span.round(SpanRound::new().largest(Unit::Hour))?.fieldwise(),
5158 /// 24.hours().seconds(1).milliseconds(123).microseconds(456).nanoseconds(789),
5159 /// );
5160 ///
5161 /// # Ok::<(), Box<dyn std::error::Error>>(())
5162 /// ```
5163 ///
5164 /// If you need to use a largest unit bigger than hours, then you must
5165 /// provide a relative datetime as a reference point (otherwise an error
5166 /// will occur):
5167 ///
5168 /// ```
5169 /// use jiff::{civil::date, SpanRound, ToSpan, Unit};
5170 ///
5171 /// let span = 3_968_000.seconds();
5172 /// let round = SpanRound::new()
5173 /// .largest(Unit::Day)
5174 /// .relative(date(2024, 7, 1));
5175 /// assert_eq!(
5176 /// span.round(round)?,
5177 /// 45.days().hours(22).minutes(13).seconds(20).fieldwise(),
5178 /// );
5179 ///
5180 /// # Ok::<(), Box<dyn std::error::Error>>(())
5181 /// ```
5182 ///
5183 /// As a special case for days, one can instead opt into invariant 24-hour
5184 /// days (and 7-day weeks) without providing an explicit relative date:
5185 ///
5186 /// ```
5187 /// use jiff::{SpanRound, ToSpan, Unit};
5188 ///
5189 /// let span = 86_401_123_456_789i64.nanoseconds();
5190 /// assert_eq!(
5191 /// span.round(
5192 /// SpanRound::new().largest(Unit::Day).days_are_24_hours(),
5193 /// )?.fieldwise(),
5194 /// 1.day().seconds(1).milliseconds(123).microseconds(456).nanoseconds(789),
5195 /// );
5196 ///
5197 /// # Ok::<(), Box<dyn std::error::Error>>(())
5198 /// ```
5199 ///
5200 /// # Example: re-balancing while taking DST into account
5201 ///
5202 /// When given a zone aware relative datetime, rounding will even take
5203 /// DST into account:
5204 ///
5205 /// ```
5206 /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5207 ///
5208 /// let span = 2756.hours();
5209 /// let zdt = "2020-01-01T00:00+01:00[Europe/Rome]".parse::<Zoned>()?;
5210 /// let round = SpanRound::new().largest(Unit::Year).relative(&zdt);
5211 /// assert_eq!(
5212 /// span.round(round)?,
5213 /// 3.months().days(23).hours(21).fieldwise(),
5214 /// );
5215 ///
5216 /// # Ok::<(), Box<dyn std::error::Error>>(())
5217 /// ```
5218 ///
5219 /// Now compare with the same operation, but on a civil datetime (which is
5220 /// not aware of time zone):
5221 ///
5222 /// ```
5223 /// use jiff::{civil::DateTime, SpanRound, ToSpan, Unit};
5224 ///
5225 /// let span = 2756.hours();
5226 /// let dt = "2020-01-01T00:00".parse::<DateTime>()?;
5227 /// let round = SpanRound::new().largest(Unit::Year).relative(dt);
5228 /// assert_eq!(
5229 /// span.round(round)?,
5230 /// 3.months().days(23).hours(20).fieldwise(),
5231 /// );
5232 ///
5233 /// # Ok::<(), Box<dyn std::error::Error>>(())
5234 /// ```
5235 ///
5236 /// The result is 1 hour shorter. This is because, in the zone
5237 /// aware re-balancing, it accounts for the transition into DST at
5238 /// `2020-03-29T01:00Z`, which skips an hour. This makes the span one hour
5239 /// longer because one of the days in the span is actually only 23 hours
5240 /// long instead of 24 hours.
5241 #[inline]
5242 pub fn largest(self, unit: Unit) -> SpanRound<'a> {
5243 SpanRound { largest: Some(unit), ..self }
5244 }
5245
5246 /// Set the rounding mode.
5247 ///
5248 /// This defaults to [`RoundMode::HalfExpand`], which makes rounding work
5249 /// like how you were taught in school.
5250 ///
5251 /// # Example
5252 ///
5253 /// A basic example that rounds to the nearest minute, but changing its
5254 /// rounding mode to truncation:
5255 ///
5256 /// ```
5257 /// use jiff::{RoundMode, SpanRound, ToSpan, Unit};
5258 ///
5259 /// let span = 15.minutes().seconds(46);
5260 /// assert_eq!(
5261 /// span.round(SpanRound::new()
5262 /// .smallest(Unit::Minute)
5263 /// .mode(RoundMode::Trunc),
5264 /// )?,
5265 /// // The default round mode does rounding like
5266 /// // how you probably learned in school, and would
5267 /// // result in rounding up to 16 minutes. But we
5268 /// // change it to truncation here, which makes it
5269 /// // round down.
5270 /// 15.minutes().fieldwise(),
5271 /// );
5272 ///
5273 /// # Ok::<(), Box<dyn std::error::Error>>(())
5274 /// ```
5275 #[inline]
5276 pub fn mode(self, mode: RoundMode) -> SpanRound<'a> {
5277 SpanRound { mode, ..self }
5278 }
5279
5280 /// Set the rounding increment for the smallest unit.
5281 ///
5282 /// The default value is `1`. Other values permit rounding the smallest
5283 /// unit to the nearest integer increment specified. For example, if the
5284 /// smallest unit is set to [`Unit::Minute`], then a rounding increment of
5285 /// `30` would result in rounding in increments of a half hour. That is,
5286 /// the only minute value that could result would be `0` or `30`.
5287 ///
5288 /// # Errors
5289 ///
5290 /// When the smallest unit is less than days, the rounding increment must
5291 /// divide evenly into the next highest unit after the smallest unit
5292 /// configured (and must not be equivalent to it). For example, if the
5293 /// smallest unit is [`Unit::Nanosecond`], then *some* of the valid values
5294 /// for the rounding increment are `1`, `2`, `4`, `5`, `100` and `500`.
5295 /// Namely, any integer that divides evenly into `1,000` nanoseconds since
5296 /// there are `1,000` nanoseconds in the next highest unit (microseconds).
5297 ///
5298 /// The error will occur when computing the span, and not when setting
5299 /// the increment here.
5300 ///
5301 /// # Example
5302 ///
5303 /// This shows how to round a span to the nearest 5 minute increment:
5304 ///
5305 /// ```
5306 /// use jiff::{ToSpan, Unit};
5307 ///
5308 /// let span = 4.hours().minutes(2).seconds(30);
5309 /// assert_eq!(
5310 /// span.round((Unit::Minute, 5))?,
5311 /// 4.hours().minutes(5).fieldwise(),
5312 /// );
5313 ///
5314 /// # Ok::<(), Box<dyn std::error::Error>>(())
5315 /// ```
5316 #[inline]
5317 pub fn increment(self, increment: i64) -> SpanRound<'a> {
5318 SpanRound { increment, ..self }
5319 }
5320
5321 /// Set the relative datetime to use when rounding a span.
5322 ///
5323 /// A relative datetime is only required when calendar units (units greater
5324 /// than days) are involved. This includes having calendar units in the
5325 /// original span, or calendar units in the configured smallest or largest
5326 /// unit. A relative datetime is required when calendar units are used
5327 /// because the duration of a particular calendar unit (like 1 month or 1
5328 /// year) is variable and depends on the date. For example, 1 month from
5329 /// 2024-01-01 is 31 days, but 1 month from 2024-02-01 is 29 days.
5330 ///
5331 /// A relative datetime is provided by anything that implements
5332 /// `Into<SpanRelativeTo>`. There are a few convenience trait
5333 /// implementations provided:
5334 ///
5335 /// * `From<&Zoned> for SpanRelativeTo` uses a zone aware datetime to do
5336 /// rounding. In this case, rounding will take time zone transitions into
5337 /// account. In particular, when using a zoned relative datetime, not all
5338 /// days are necessarily 24 hours.
5339 /// * `From<civil::DateTime> for SpanRelativeTo` uses a civil datetime. In
5340 /// this case, all days will be considered 24 hours long.
5341 /// * `From<civil::Date> for SpanRelativeTo` uses a civil date. In this
5342 /// case, all days will be considered 24 hours long.
5343 ///
5344 /// Note that one can impose 24-hour days without providing a reference
5345 /// date via [`SpanRelativeTo::days_are_24_hours`].
5346 ///
5347 /// # Errors
5348 ///
5349 /// If rounding involves a calendar unit (units bigger than hours) and no
5350 /// relative datetime is provided, then this configuration will lead to
5351 /// an error when used with [`Span::round`].
5352 ///
5353 /// # Example
5354 ///
5355 /// This example shows very precisely how a DST transition can impact
5356 /// rounding and re-balancing. For example, consider the day `2024-11-03`
5357 /// in `America/New_York`. On this day, the 1 o'clock hour was repeated,
5358 /// making the day 24 hours long. This will be taken into account when
5359 /// rounding if a zoned datetime is provided as a reference point:
5360 ///
5361 /// ```
5362 /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5363 ///
5364 /// let zdt = "2024-11-03T00-04[America/New_York]".parse::<Zoned>()?;
5365 /// let round = SpanRound::new().largest(Unit::Hour).relative(&zdt);
5366 /// assert_eq!(1.day().round(round)?, 25.hours().fieldwise());
5367 ///
5368 /// # Ok::<(), Box<dyn std::error::Error>>(())
5369 /// ```
5370 ///
5371 /// And similarly for `2024-03-10`, where the 2 o'clock hour was skipped
5372 /// entirely:
5373 ///
5374 /// ```
5375 /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5376 ///
5377 /// let zdt = "2024-03-10T00-05[America/New_York]".parse::<Zoned>()?;
5378 /// let round = SpanRound::new().largest(Unit::Hour).relative(&zdt);
5379 /// assert_eq!(1.day().round(round)?, 23.hours().fieldwise());
5380 ///
5381 /// # Ok::<(), Box<dyn std::error::Error>>(())
5382 /// ```
5383 #[inline]
5384 pub fn relative<R: Into<SpanRelativeTo<'a>>>(
5385 self,
5386 relative: R,
5387 ) -> SpanRound<'a> {
5388 SpanRound { relative: Some(relative.into()), ..self }
5389 }
5390
5391 /// This is a convenience function for setting the relative option on
5392 /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
5393 ///
5394 /// # Example
5395 ///
5396 /// When rounding spans involving days, either a relative datetime must be
5397 /// provided, or a special assertion opting into 24-hour days is
5398 /// required. Otherwise, you get an error.
5399 ///
5400 /// ```
5401 /// use jiff::{SpanRound, ToSpan, Unit};
5402 ///
5403 /// let span = 2.days().hours(12);
5404 /// // No relative date provided, which results in an error.
5405 /// assert_eq!(
5406 /// span.round(Unit::Day).unwrap_err().to_string(),
5407 /// "error with `smallest` rounding option: using unit 'day' in a \
5408 /// span or configuration requires that either a relative reference \
5409 /// time be given or `jiff::SpanRelativeTo::days_are_24_hours()` is \
5410 /// used to indicate invariant 24-hour days, but neither were \
5411 /// provided",
5412 /// );
5413 /// let rounded = span.round(
5414 /// SpanRound::new().smallest(Unit::Day).days_are_24_hours(),
5415 /// )?;
5416 /// assert_eq!(rounded, 3.days().fieldwise());
5417 ///
5418 /// # Ok::<(), Box<dyn std::error::Error>>(())
5419 /// ```
5420 #[inline]
5421 pub fn days_are_24_hours(self) -> SpanRound<'a> {
5422 self.relative(SpanRelativeTo::days_are_24_hours())
5423 }
5424
5425 /// Returns the configured smallest unit on this round configuration.
5426 #[inline]
5427 pub(crate) fn get_smallest(&self) -> Unit {
5428 self.smallest
5429 }
5430
5431 /// Returns the configured largest unit on this round configuration.
5432 #[inline]
5433 pub(crate) fn get_largest(&self) -> Option<Unit> {
5434 self.largest
5435 }
5436
5437 /// Returns true only when rounding a span *may* change it. When it
5438 /// returns false, and if the span is already balanced according to
5439 /// the largest unit in this round configuration, then it is guaranteed
5440 /// that rounding is a no-op.
5441 ///
5442 /// This is useful to avoid rounding calls after doing span arithmetic
5443 /// on datetime types. This works because the "largest" unit is used to
5444 /// construct a balanced span for the difference between two datetimes.
5445 /// So we already know the span has been balanced. If this weren't the
5446 /// case, then the largest unit being different from the one in the span
5447 /// could result in rounding making a change. (And indeed, in the general
5448 /// case of span rounding below, we do a more involved check for this.)
5449 #[inline]
5450 pub(crate) fn rounding_may_change_span_ignore_largest(&self) -> bool {
5451 self.smallest > Unit::Nanosecond || self.increment > 1
5452 }
5453
5454 /// Does the actual span rounding.
5455 fn round(&self, span: Span) -> Result<Span, Error> {
5456 let existing_largest = span.largest_unit();
5457 let mode = self.mode;
5458 let smallest = self.smallest;
5459 let largest =
5460 self.largest.unwrap_or_else(|| smallest.max(existing_largest));
5461 let max = existing_largest.max(largest);
5462 let increment = increment::for_span(smallest, self.increment)?;
5463 if largest < smallest {
5464 return Err(Error::from(
5465 E::NotAllowedLargestSmallerThanSmallest { smallest, largest },
5466 ));
5467 }
5468 let relative = match self.relative {
5469 Some(ref r) => {
5470 match r.to_relative(max)? {
5471 Some(r) => r,
5472 None => {
5473 // If our reference point is civil time, then its units
5474 // are invariant as long as we are using day-or-lower
5475 // everywhere. That is, the length of the duration is
5476 // independent of the reference point. In which case,
5477 // rounding is a simple matter of converting the span
5478 // to a number of nanoseconds and then rounding that.
5479 return Ok(round_span_invariant(
5480 span, smallest, largest, increment, mode,
5481 )?);
5482 }
5483 }
5484 }
5485 None => {
5486 // This is only okay if none of our units are above 'day'.
5487 // That is, a reference point is only necessary when there is
5488 // no reasonable invariant interpretation of the span. And this
5489 // is only true when everything is less than 'day'.
5490 requires_relative_date_err(smallest)
5491 .context(E::OptionSmallest)?;
5492 if let Some(largest) = self.largest {
5493 requires_relative_date_err(largest)
5494 .context(E::OptionLargest)?;
5495 }
5496 requires_relative_date_err(existing_largest)
5497 .context(E::OptionLargestInSpan)?;
5498 assert!(max <= Unit::Week);
5499 return Ok(round_span_invariant(
5500 span, smallest, largest, increment, mode,
5501 )?);
5502 }
5503 };
5504 relative.round(span, smallest, largest, increment, mode)
5505 }
5506}
5507
5508impl Default for SpanRound<'static> {
5509 fn default() -> SpanRound<'static> {
5510 SpanRound::new()
5511 }
5512}
5513
5514impl From<Unit> for SpanRound<'static> {
5515 fn from(unit: Unit) -> SpanRound<'static> {
5516 SpanRound::default().smallest(unit)
5517 }
5518}
5519
5520impl From<(Unit, i64)> for SpanRound<'static> {
5521 fn from((unit, increment): (Unit, i64)) -> SpanRound<'static> {
5522 SpanRound::default().smallest(unit).increment(increment)
5523 }
5524}
5525
5526/// A relative datetime for use with [`Span`] APIs.
5527///
5528/// A relative datetime can be one of the following: [`civil::Date`](Date),
5529/// [`civil::DateTime`](DateTime) or [`Zoned`]. It can be constructed from any
5530/// of the preceding types via `From` trait implementations.
5531///
5532/// A relative datetime is used to indicate how the calendar units of a `Span`
5533/// should be interpreted. For example, the span "1 month" does not have a
5534/// fixed meaning. One month from `2024-03-01` is 31 days, but one month from
5535/// `2024-04-01` is 30 days. Similar for years.
5536///
5537/// When a relative datetime in time zone aware (i.e., it is a `Zoned`), then
5538/// operations on a `Span` will also consider its day units to be variable in
5539/// length. For example, `2024-03-10` in `America/New_York` was only 23 hours
5540/// long, where as `2024-11-03` in `America/New_York` was 25 hours long. When
5541/// a relative datetime is civil, then days are considered to always be of a
5542/// fixed 24 hour length.
5543///
5544/// This type is principally used as an input to one of several different
5545/// [`Span`] APIs:
5546///
5547/// * [`Span::round`] rounds spans. A relative datetime is necessary when
5548/// dealing with calendar units. (But spans without calendar units can be
5549/// rounded without providing a relative datetime.)
5550/// * Span arithmetic via [`Span::checked_add`] and [`Span::checked_sub`].
5551/// A relative datetime is needed when adding or subtracting spans with
5552/// calendar units.
5553/// * Span comparisons via [`Span::compare`] require a relative datetime when
5554/// comparing spans with calendar units.
5555/// * Computing the "total" duration as a single floating point number via
5556/// [`Span::total`] also requires a relative datetime when dealing with
5557/// calendar units.
5558///
5559/// # Example
5560///
5561/// This example shows how to round a span with larger calendar units to
5562/// smaller units:
5563///
5564/// ```
5565/// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5566///
5567/// let zdt: Zoned = "2012-01-01[Antarctica/Troll]".parse()?;
5568/// let round = SpanRound::new().largest(Unit::Day).relative(&zdt);
5569/// assert_eq!(1.year().round(round)?, 366.days().fieldwise());
5570///
5571/// // If you tried this without a relative datetime, it would fail:
5572/// let round = SpanRound::new().largest(Unit::Day);
5573/// assert!(1.year().round(round).is_err());
5574///
5575/// # Ok::<(), Box<dyn std::error::Error>>(())
5576/// ```
5577#[derive(Clone, Copy, Debug)]
5578pub struct SpanRelativeTo<'a> {
5579 kind: SpanRelativeToKind<'a>,
5580}
5581
5582impl<'a> SpanRelativeTo<'a> {
5583 /// Creates a special marker that indicates all days ought to be assumed
5584 /// to be 24 hours without providing a relative reference time.
5585 ///
5586 /// This is relevant to the following APIs:
5587 ///
5588 /// * [`Span::checked_add`]
5589 /// * [`Span::checked_sub`]
5590 /// * [`Span::compare`]
5591 /// * [`Span::total`]
5592 /// * [`Span::round`]
5593 /// * [`Span::to_duration`]
5594 ///
5595 /// Specifically, in a previous version of Jiff, the above APIs permitted
5596 /// _silently_ assuming that days are always 24 hours when a relative
5597 /// reference date wasn't provided. In the current version of Jiff, this
5598 /// silent interpretation no longer happens and instead an error will
5599 /// occur.
5600 ///
5601 /// If you need to use these APIs with spans that contain non-zero units
5602 /// of days or weeks but without a relative reference date, then you may
5603 /// use this routine to create a special marker for `SpanRelativeTo` that
5604 /// permits the APIs above to assume days are always 24 hours.
5605 ///
5606 /// # Motivation
5607 ///
5608 /// The purpose of the marker is two-fold:
5609 ///
5610 /// * Requiring the marker is important for improving the consistency of
5611 /// `Span` APIs. Previously, some APIs (like [`Timestamp::checked_add`])
5612 /// would always return an error if the `Span` given had non-zero
5613 /// units of days or greater. On the other hand, other APIs (like
5614 /// [`Span::checked_add`]) would automatically assume days were always
5615 /// 24 hours if no relative reference time was given and either span had
5616 /// non-zero units of days. With this marker, APIs _never_ assume days are
5617 /// always 24 hours automatically.
5618 /// * When it _is_ appropriate to assume all days are 24 hours
5619 /// (for example, when only dealing with spans derived from
5620 /// [`civil`](crate::civil) datetimes) and where providing a relative
5621 /// reference datetime doesn't make sense. In this case, one _could_
5622 /// provide a "dummy" reference date since the precise date in civil time
5623 /// doesn't impact the length of a day. But a marker like the one returned
5624 /// here is more explicit for the purpose of assuming days are always 24
5625 /// hours.
5626 ///
5627 /// With that said, ideally, callers should provide a relative reference
5628 /// datetime if possible.
5629 ///
5630 /// See [Issue #48] for more discussion on this topic.
5631 ///
5632 /// # Example: different interpretations of "1 day"
5633 ///
5634 /// This example shows how "1 day" can be interpreted differently via the
5635 /// [`Span::total`] API:
5636 ///
5637 /// ```
5638 /// use jiff::{SpanRelativeTo, ToSpan, Unit, Zoned};
5639 ///
5640 /// let span = 1.day();
5641 ///
5642 /// // An error because days aren't always 24 hours:
5643 /// assert_eq!(
5644 /// span.total(Unit::Hour).unwrap_err().to_string(),
5645 /// "using unit 'day' in a span or configuration requires that either \
5646 /// a relative reference time be given or \
5647 /// `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
5648 /// invariant 24-hour days, but neither were provided",
5649 /// );
5650 /// // Opt into invariant 24 hour days without a relative date:
5651 /// let marker = SpanRelativeTo::days_are_24_hours();
5652 /// let hours = span.total((Unit::Hour, marker))?;
5653 /// assert_eq!(hours, 24.0);
5654 /// // Days can be shorter than 24 hours:
5655 /// let zdt: Zoned = "2024-03-10[America/New_York]".parse()?;
5656 /// let hours = span.total((Unit::Hour, &zdt))?;
5657 /// assert_eq!(hours, 23.0);
5658 /// // Days can be longer than 24 hours:
5659 /// let zdt: Zoned = "2024-11-03[America/New_York]".parse()?;
5660 /// let hours = span.total((Unit::Hour, &zdt))?;
5661 /// assert_eq!(hours, 25.0);
5662 ///
5663 /// # Ok::<(), Box<dyn std::error::Error>>(())
5664 /// ```
5665 ///
5666 /// Similar behavior applies to the other APIs listed above.
5667 ///
5668 /// # Example: different interpretations of "1 week"
5669 ///
5670 /// This example shows how "1 week" can be interpreted differently via the
5671 /// [`Span::total`] API:
5672 ///
5673 /// ```
5674 /// use jiff::{SpanRelativeTo, ToSpan, Unit, Zoned};
5675 ///
5676 /// let span = 1.week();
5677 ///
5678 /// // An error because days aren't always 24 hours:
5679 /// assert_eq!(
5680 /// span.total(Unit::Hour).unwrap_err().to_string(),
5681 /// "using unit 'week' in a span or configuration requires that either \
5682 /// a relative reference time be given or \
5683 /// `jiff::SpanRelativeTo::days_are_24_hours()` is used to indicate \
5684 /// invariant 24-hour days, but neither were provided",
5685 /// );
5686 /// // Opt into invariant 24 hour days without a relative date:
5687 /// let marker = SpanRelativeTo::days_are_24_hours();
5688 /// let hours = span.total((Unit::Hour, marker))?;
5689 /// assert_eq!(hours, 168.0);
5690 /// // Weeks can be shorter than 24*7 hours:
5691 /// let zdt: Zoned = "2024-03-10[America/New_York]".parse()?;
5692 /// let hours = span.total((Unit::Hour, &zdt))?;
5693 /// assert_eq!(hours, 167.0);
5694 /// // Weeks can be longer than 24*7 hours:
5695 /// let zdt: Zoned = "2024-11-03[America/New_York]".parse()?;
5696 /// let hours = span.total((Unit::Hour, &zdt))?;
5697 /// assert_eq!(hours, 169.0);
5698 ///
5699 /// # Ok::<(), Box<dyn std::error::Error>>(())
5700 /// ```
5701 ///
5702 /// # Example: working with [`civil::Date`](crate::civil::Date)
5703 ///
5704 /// A `Span` returned by computing the difference in time between two
5705 /// [`civil::Date`](crate::civil::Date)s will have a non-zero number of
5706 /// days. In older versions of Jiff, if one wanted to add spans returned by
5707 /// these APIs, you could do so without futzing with relative dates. But
5708 /// now you either need to provide a relative date:
5709 ///
5710 /// ```
5711 /// use jiff::{civil::date, ToSpan};
5712 ///
5713 /// let d1 = date(2025, 1, 18);
5714 /// let d2 = date(2025, 1, 26);
5715 /// let d3 = date(2025, 2, 14);
5716 ///
5717 /// let span1 = d2 - d1;
5718 /// let span2 = d3 - d2;
5719 /// let total = span1.checked_add((span2, d1))?;
5720 /// assert_eq!(total, 27.days().fieldwise());
5721 ///
5722 /// # Ok::<(), Box<dyn std::error::Error>>(())
5723 /// ```
5724 ///
5725 /// Or you can provide a marker indicating that days are always 24 hours.
5726 /// This is fine for this use case since one is only doing civil calendar
5727 /// arithmetic and not working with time zones:
5728 ///
5729 /// ```
5730 /// use jiff::{civil::date, SpanRelativeTo, ToSpan};
5731 ///
5732 /// let d1 = date(2025, 1, 18);
5733 /// let d2 = date(2025, 1, 26);
5734 /// let d3 = date(2025, 2, 14);
5735 ///
5736 /// let span1 = d2 - d1;
5737 /// let span2 = d3 - d2;
5738 /// let total = span1.checked_add(
5739 /// (span2, SpanRelativeTo::days_are_24_hours()),
5740 /// )?;
5741 /// assert_eq!(total, 27.days().fieldwise());
5742 ///
5743 /// # Ok::<(), Box<dyn std::error::Error>>(())
5744 /// ```
5745 ///
5746 /// [Issue #48]: https://github.com/BurntSushi/jiff/issues/48
5747 #[inline]
5748 pub const fn days_are_24_hours() -> SpanRelativeTo<'static> {
5749 let kind = SpanRelativeToKind::DaysAre24Hours;
5750 SpanRelativeTo { kind }
5751 }
5752
5753 /// Converts this public API relative datetime into a more versatile
5754 /// internal representation of the same concept.
5755 ///
5756 /// Basically, the internal `Relative` type is `Cow` which means it isn't
5757 /// `Copy`. But it can present a more uniform API. The public API type
5758 /// doesn't have `Cow` so that it can be `Copy`.
5759 ///
5760 /// We also take this opportunity to attach some convenient data, such
5761 /// as a timestamp when the relative datetime type is civil.
5762 ///
5763 /// This can return `None` if this `SpanRelativeTo` isn't actually a
5764 /// datetime but a "marker" indicating some unit (like days) should be
5765 /// treated as invariant. Or `None` is returned when the given unit is
5766 /// always invariant (hours or smaller).
5767 ///
5768 /// # Errors
5769 ///
5770 /// If there was a problem doing this conversion, then an error is
5771 /// returned. In practice, this only occurs for a civil datetime near the
5772 /// civil datetime minimum and maximum values.
5773 fn to_relative(&self, unit: Unit) -> Result<Option<Relative<'a>>, Error> {
5774 if !unit.is_variable() {
5775 return Ok(None);
5776 }
5777 match self.kind {
5778 SpanRelativeToKind::Civil(dt) => {
5779 Ok(Some(Relative::Civil(RelativeCivil::new(dt)?)))
5780 }
5781 SpanRelativeToKind::Zoned(zdt) => {
5782 Ok(Some(Relative::Zoned(RelativeZoned {
5783 zoned: DumbCow::Borrowed(zdt),
5784 })))
5785 }
5786 SpanRelativeToKind::DaysAre24Hours => {
5787 if matches!(unit, Unit::Year | Unit::Month) {
5788 return Err(Error::from(
5789 E::RequiresRelativeYearOrMonthGivenDaysAre24Hours {
5790 unit,
5791 },
5792 ));
5793 }
5794 Ok(None)
5795 }
5796 }
5797 }
5798}
5799
5800#[derive(Clone, Copy, Debug)]
5801enum SpanRelativeToKind<'a> {
5802 Civil(DateTime),
5803 Zoned(&'a Zoned),
5804 DaysAre24Hours,
5805}
5806
5807impl<'a> From<&'a Zoned> for SpanRelativeTo<'a> {
5808 fn from(zdt: &'a Zoned) -> SpanRelativeTo<'a> {
5809 SpanRelativeTo { kind: SpanRelativeToKind::Zoned(zdt) }
5810 }
5811}
5812
5813impl From<DateTime> for SpanRelativeTo<'static> {
5814 fn from(dt: DateTime) -> SpanRelativeTo<'static> {
5815 SpanRelativeTo { kind: SpanRelativeToKind::Civil(dt) }
5816 }
5817}
5818
5819impl From<Date> for SpanRelativeTo<'static> {
5820 fn from(date: Date) -> SpanRelativeTo<'static> {
5821 let dt = DateTime::from_parts(date, Time::midnight());
5822 SpanRelativeTo { kind: SpanRelativeToKind::Civil(dt) }
5823 }
5824}
5825
5826/// A bit set that keeps track of all non-zero units on a `Span`.
5827///
5828/// Because of alignment, adding this to a `Span` does not make it any bigger.
5829///
5830/// The benefit of this bit set is to make it extremely cheap to enable fast
5831/// paths in various places. For example, doing arithmetic on a `Date` with an
5832/// arbitrary `Span` is pretty involved. But if you know the `Span` only
5833/// consists of non-zero units of days (and zero for all other units), then you
5834/// can take a much cheaper path.
5835#[derive(Clone, Copy)]
5836pub(crate) struct UnitSet(u16);
5837
5838impl UnitSet {
5839 /// Return a bit set representing all units as zero.
5840 #[inline]
5841 fn empty() -> UnitSet {
5842 UnitSet(0)
5843 }
5844
5845 /// Set the given `unit` to `is_zero` status in this set.
5846 ///
5847 /// When `is_zero` is false, the unit is added to this set. Otherwise,
5848 /// the unit is removed from this set.
5849 #[inline]
5850 fn set(self, unit: Unit, is_zero: bool) -> UnitSet {
5851 let bit = 1 << unit as usize;
5852 if is_zero {
5853 UnitSet(self.0 & !bit)
5854 } else {
5855 UnitSet(self.0 | bit)
5856 }
5857 }
5858
5859 /// Returns true if and only if no units are in this set.
5860 #[inline]
5861 pub(crate) fn is_empty(&self) -> bool {
5862 self.0 == 0
5863 }
5864
5865 /// Returns true if and only if this `Span` contains precisely one
5866 /// non-zero unit corresponding to the unit given.
5867 #[inline]
5868 pub(crate) fn contains_only(self, unit: Unit) -> bool {
5869 self.0 == (1 << unit as usize)
5870 }
5871
5872 /// Returns this set, but with only calendar units.
5873 #[inline]
5874 pub(crate) fn only_calendar(self) -> UnitSet {
5875 UnitSet(self.0 & 0b0000_0011_1100_0000)
5876 }
5877
5878 /// Returns this set, but with only time units.
5879 #[inline]
5880 pub(crate) fn only_time(self) -> UnitSet {
5881 UnitSet(self.0 & 0b0000_0000_0011_1111)
5882 }
5883
5884 /// Returns the largest unit in this set, or `None` if none are present.
5885 #[inline]
5886 pub(crate) fn largest_unit(self) -> Option<Unit> {
5887 let zeros = usize::try_from(self.0.leading_zeros()).ok()?;
5888 15usize.checked_sub(zeros).and_then(Unit::from_usize)
5889 }
5890}
5891
5892// N.B. This `Debug` impl isn't typically used.
5893//
5894// This is because the `Debug` impl for `Span` just emits itself in the
5895// friendly duration format, which doesn't include internal representation
5896// details like this set. It is included in `Span::debug`, but this isn't
5897// part of the public crate API.
5898impl core::fmt::Debug for UnitSet {
5899 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
5900 write!(f, "{{")?;
5901 let mut units = *self;
5902 let mut i = 0;
5903 while let Some(unit) = units.largest_unit() {
5904 if i > 0 {
5905 write!(f, ", ")?;
5906 }
5907 i += 1;
5908 write!(f, "{}", unit.compact())?;
5909 units = units.set(unit, false);
5910 }
5911 if i == 0 {
5912 write!(f, "∅")?;
5913 }
5914 write!(f, "}}")
5915 }
5916}
5917
5918/// An internal abstraction for managing a relative datetime for use in some
5919/// `Span` APIs.
5920///
5921/// This is effectively the same as a `SpanRelativeTo`, but uses a `Cow<Zoned>`
5922/// instead of a `&Zoned`. This makes it non-`Copy`, but allows us to craft a
5923/// more uniform API. (i.e., `relative + span = relative` instead of `relative
5924/// + span = owned_relative` or whatever.) Note that the `Copy` impl on
5925/// `SpanRelativeTo` means it has to accept a `&Zoned`. It can't ever take a
5926/// `Zoned` since it is non-Copy.
5927///
5928/// NOTE: Separately from above, I think it's plausible that this type could be
5929/// designed a bit differently. Namely, something like this:
5930///
5931/// ```text
5932/// struct Relative<'a> {
5933/// tz: Option<&'a TimeZone>,
5934/// dt: DateTime,
5935/// ts: Timestamp,
5936/// }
5937/// ```
5938///
5939/// That is, we do zone aware stuff but without an actual `Zoned` type. But I
5940/// think in order to make that work, we would need to expose most of the
5941/// `Zoned` API as functions on its component types (DateTime, Timestamp and
5942/// TimeZone). I think we are likely to want to do that for public API reasons,
5943/// but I'd like to resist it since I think it will add a lot of complexity.
5944/// Or maybe we need a `Unzoned` type that is `DateTime` and `Timestamp`, but
5945/// requires passing the time zone in to each of its methods. That might work
5946/// quite well, even if it was just an internal type.
5947///
5948/// Anyway, I'm not 100% sure the above would work, but I think it would. It
5949/// would be nicer because everything would be `Copy` all the time. We'd never
5950/// need a `Cow<TimeZone>` for example, because we never need to change or
5951/// create a new time zone.
5952#[derive(Clone, Debug)]
5953enum Relative<'a> {
5954 Civil(RelativeCivil),
5955 Zoned(RelativeZoned<'a>),
5956}
5957
5958impl<'a> Relative<'a> {
5959 /// Adds the given span to this relative datetime.
5960 ///
5961 /// This defers to either [`DateTime::checked_add`] or
5962 /// [`Zoned::checked_add`], depending on the type of relative datetime.
5963 ///
5964 /// The `Relative` datetime returned is guaranteed to have the same
5965 /// internal datetie type as `self`.
5966 ///
5967 /// # Errors
5968 ///
5969 /// This returns an error in the same cases as the underlying checked
5970 /// arithmetic APIs. In general, this occurs when adding the given `span`
5971 /// would result in overflow.
5972 fn checked_add(&'a self, span: Span) -> Result<Relative<'a>, Error> {
5973 match *self {
5974 Relative::Civil(dt) => Ok(Relative::Civil(dt.checked_add(span)?)),
5975 Relative::Zoned(ref zdt) => {
5976 Ok(Relative::Zoned(zdt.checked_add(span)?))
5977 }
5978 }
5979 }
5980
5981 fn checked_add_duration(
5982 &'a self,
5983 duration: SignedDuration,
5984 ) -> Result<Relative<'a>, Error> {
5985 match *self {
5986 Relative::Civil(dt) => {
5987 Ok(Relative::Civil(dt.checked_add_duration(duration)?))
5988 }
5989 Relative::Zoned(ref zdt) => {
5990 Ok(Relative::Zoned(zdt.checked_add_duration(duration)?))
5991 }
5992 }
5993 }
5994
5995 /// Returns the span of time from this relative datetime to the one given,
5996 /// with units as large as `largest`.
5997 ///
5998 /// # Errors
5999 ///
6000 /// This returns an error in the same cases as when the underlying
6001 /// [`DateTime::until`] or [`Zoned::until`] fail. Because this doesn't
6002 /// set or expose any rounding configuration, this can generally only
6003 /// occur when `largest` is `Unit::Nanosecond` and the span of time
6004 /// between `self` and `other` is too big to represent as a 64-bit integer
6005 /// nanosecond count.
6006 ///
6007 /// # Panics
6008 ///
6009 /// This panics if `self` and `other` are different internal datetime
6010 /// types. For example, if `self` was a civil datetime and `other` were
6011 /// a zoned datetime.
6012 fn until(&self, largest: Unit, other: &Relative) -> Result<Span, Error> {
6013 match (self, other) {
6014 (&Relative::Civil(ref dt1), &Relative::Civil(ref dt2)) => {
6015 dt1.until(largest, dt2)
6016 }
6017 (&Relative::Zoned(ref zdt1), &Relative::Zoned(ref zdt2)) => {
6018 zdt1.until(largest, zdt2)
6019 }
6020 // This would be bad if `Relative` were a public API, but in
6021 // practice, this case never occurs because we don't mixup our
6022 // `Relative` datetime types.
6023 _ => unreachable!(),
6024 }
6025 }
6026
6027 /// Converts this relative datetime to a nanosecond in UTC time.
6028 ///
6029 /// # Errors
6030 ///
6031 /// If there was a problem doing this conversion, then an error is
6032 /// returned. In practice, this only occurs for a civil datetime near the
6033 /// civil datetime minimum and maximum values.
6034 fn to_nanosecond(&self) -> NoUnits128 {
6035 match *self {
6036 Relative::Civil(dt) => dt.timestamp.as_nanosecond_ranged().rinto(),
6037 Relative::Zoned(ref zdt) => {
6038 zdt.zoned.timestamp().as_nanosecond_ranged().rinto()
6039 }
6040 }
6041 }
6042
6043 /// Create a balanced span of time relative to this datetime.
6044 ///
6045 /// The relative span returned has the same internal datetime type
6046 /// (civil or zoned) as this relative datetime.
6047 ///
6048 /// # Errors
6049 ///
6050 /// This returns an error when the span in this range cannot be
6051 /// represented. In general, this only occurs when asking for largest units
6052 /// of `Unit::Nanosecond` *and* when the span is too big to fit into a
6053 /// 64-bit nanosecond count.
6054 ///
6055 /// This can also return an error in other extreme cases, such as when
6056 /// adding the given span to this relative datetime results in overflow,
6057 /// or if this relative datetime is a civil datetime and it couldn't be
6058 /// converted to a timestamp in UTC.
6059 fn into_relative_span(
6060 self,
6061 largest: Unit,
6062 span: Span,
6063 ) -> Result<RelativeSpan<'a>, Error> {
6064 let kind = match self {
6065 Relative::Civil(start) => {
6066 let end = start.checked_add(span)?;
6067 RelativeSpanKind::Civil { start, end }
6068 }
6069 Relative::Zoned(start) => {
6070 let end = start.checked_add(span)?;
6071 RelativeSpanKind::Zoned { start, end }
6072 }
6073 };
6074 let relspan = kind.into_relative_span(largest)?;
6075 if span.get_sign_ranged() != C(0)
6076 && relspan.span.get_sign_ranged() != C(0)
6077 && span.get_sign_ranged() != relspan.span.get_sign_ranged()
6078 {
6079 // I haven't quite figured out when this case is hit. I think it's
6080 // actually impossible right? Balancing a duration should not flip
6081 // the sign.
6082 //
6083 // ref: https://github.com/fullcalendar/temporal-polyfill/blob/9e001042864394247181d1a5d591c18057ce32d2/packages/temporal-polyfill/src/internal/durationMath.ts#L236-L238
6084 unreachable!(
6085 "balanced span should have same sign as original span"
6086 )
6087 }
6088 Ok(relspan)
6089 }
6090
6091 /// Rounds the given span using the given rounding configuration.
6092 fn round(
6093 self,
6094 span: Span,
6095 smallest: Unit,
6096 largest: Unit,
6097 increment: NoUnits128,
6098 mode: RoundMode,
6099 ) -> Result<Span, Error> {
6100 let relspan = self.into_relative_span(largest, span)?;
6101 if relspan.span.get_sign_ranged() == C(0) {
6102 return Ok(relspan.span);
6103 }
6104 let nudge = match relspan.kind {
6105 RelativeSpanKind::Civil { start, end } => {
6106 if smallest > Unit::Day {
6107 Nudge::relative_calendar(
6108 relspan.span,
6109 &Relative::Civil(start),
6110 &Relative::Civil(end),
6111 smallest,
6112 increment,
6113 mode,
6114 )?
6115 } else {
6116 let relative_end = end.timestamp.as_nanosecond_ranged();
6117 Nudge::relative_invariant(
6118 relspan.span,
6119 relative_end.rinto(),
6120 smallest,
6121 largest,
6122 increment,
6123 mode,
6124 )?
6125 }
6126 }
6127 RelativeSpanKind::Zoned { ref start, ref end } => {
6128 if smallest >= Unit::Day {
6129 Nudge::relative_calendar(
6130 relspan.span,
6131 &Relative::Zoned(start.borrowed()),
6132 &Relative::Zoned(end.borrowed()),
6133 smallest,
6134 increment,
6135 mode,
6136 )?
6137 } else if largest >= Unit::Day {
6138 // This is a special case for zoned datetimes when rounding
6139 // could bleed into variable units.
6140 Nudge::relative_zoned_time(
6141 relspan.span,
6142 start,
6143 smallest,
6144 increment,
6145 mode,
6146 )?
6147 } else {
6148 // Otherwise, rounding is the same as civil datetime.
6149 let relative_end =
6150 end.zoned.timestamp().as_nanosecond_ranged();
6151 Nudge::relative_invariant(
6152 relspan.span,
6153 relative_end.rinto(),
6154 smallest,
6155 largest,
6156 increment,
6157 mode,
6158 )?
6159 }
6160 }
6161 };
6162 nudge.bubble(&relspan, smallest, largest)
6163 }
6164}
6165
6166/// A balanced span between a range of civil or zoned datetimes.
6167///
6168/// The span is always balanced up to a certain unit as given to
6169/// `RelativeSpanKind::into_relative_span`.
6170#[derive(Clone, Debug)]
6171struct RelativeSpan<'a> {
6172 span: Span,
6173 kind: RelativeSpanKind<'a>,
6174}
6175
6176/// A civil or zoned datetime range of time.
6177#[derive(Clone, Debug)]
6178enum RelativeSpanKind<'a> {
6179 Civil { start: RelativeCivil, end: RelativeCivil },
6180 Zoned { start: RelativeZoned<'a>, end: RelativeZoned<'a> },
6181}
6182
6183impl<'a> RelativeSpanKind<'a> {
6184 /// Create a balanced `RelativeSpan` from this range of time.
6185 ///
6186 /// # Errors
6187 ///
6188 /// This returns an error when the span in this range cannot be
6189 /// represented. In general, this only occurs when asking for largest units
6190 /// of `Unit::Nanosecond` *and* when the span is too big to fit into a
6191 /// 64-bit nanosecond count.
6192 fn into_relative_span(
6193 self,
6194 largest: Unit,
6195 ) -> Result<RelativeSpan<'a>, Error> {
6196 let span = match self {
6197 RelativeSpanKind::Civil { ref start, ref end } => start
6198 .datetime
6199 .until((largest, end.datetime))
6200 .context(E::FailedSpanBetweenDateTimes { unit: largest })?,
6201 RelativeSpanKind::Zoned { ref start, ref end } => {
6202 start.zoned.until((largest, &*end.zoned)).context(
6203 E::FailedSpanBetweenZonedDateTimes { unit: largest },
6204 )?
6205 }
6206 };
6207 Ok(RelativeSpan { span, kind: self })
6208 }
6209}
6210
6211/// A wrapper around a civil datetime and a timestamp corresponding to that
6212/// civil datetime in UTC.
6213///
6214/// Haphazardly interpreting a civil datetime in UTC is an odd and *usually*
6215/// incorrect thing to do. But the way we use it here is basically just to give
6216/// it an "anchoring" point such that we can represent it using a single
6217/// integer for rounding purposes. It is only used in a context *relative* to
6218/// another civil datetime interpreted in UTC. In this fashion, the selection
6219/// of UTC specifically doesn't really matter. We could use any time zone.
6220/// (Although, it must be a time zone without any transitions, otherwise we
6221/// could wind up with time zone aware results in a context where that would
6222/// be unexpected since this is civil time.)
6223#[derive(Clone, Copy, Debug)]
6224struct RelativeCivil {
6225 datetime: DateTime,
6226 timestamp: Timestamp,
6227}
6228
6229impl RelativeCivil {
6230 /// Creates a new relative wrapper around the given civil datetime.
6231 ///
6232 /// This wrapper bundles a timestamp for the given datetime by interpreting
6233 /// it as being in UTC. This is an "odd" thing to do, but it's only used
6234 /// in the context of determining the length of time between two civil
6235 /// datetimes. So technically, any time zone without transitions could be
6236 /// used.
6237 ///
6238 /// # Errors
6239 ///
6240 /// This returns an error if the datetime could not be converted to a
6241 /// timestamp. This only occurs near the minimum and maximum civil datetime
6242 /// values.
6243 fn new(datetime: DateTime) -> Result<RelativeCivil, Error> {
6244 let timestamp = datetime
6245 .to_zoned(TimeZone::UTC)
6246 .context(E::ConvertDateTimeToTimestamp)?
6247 .timestamp();
6248 Ok(RelativeCivil { datetime, timestamp })
6249 }
6250
6251 /// Returns the result of [`DateTime::checked_add`].
6252 ///
6253 /// # Errors
6254 ///
6255 /// Returns an error in the same cases as `DateTime::checked_add`. That is,
6256 /// when adding the span to this zoned datetime would overflow.
6257 ///
6258 /// This also returns an error if the resulting datetime could not be
6259 /// converted to a timestamp in UTC. This only occurs near the minimum and
6260 /// maximum datetime values.
6261 fn checked_add(&self, span: Span) -> Result<RelativeCivil, Error> {
6262 let datetime = self.datetime.checked_add(span)?;
6263 let timestamp = datetime
6264 .to_zoned(TimeZone::UTC)
6265 .context(E::ConvertDateTimeToTimestamp)?
6266 .timestamp();
6267 Ok(RelativeCivil { datetime, timestamp })
6268 }
6269
6270 /// Returns the result of [`DateTime::checked_add`] with an absolute
6271 /// duration.
6272 ///
6273 /// # Errors
6274 ///
6275 /// Returns an error in the same cases as `DateTime::checked_add`. That is,
6276 /// when adding the span to this zoned datetime would overflow.
6277 ///
6278 /// This also returns an error if the resulting datetime could not be
6279 /// converted to a timestamp in UTC. This only occurs near the minimum and
6280 /// maximum datetime values.
6281 fn checked_add_duration(
6282 &self,
6283 duration: SignedDuration,
6284 ) -> Result<RelativeCivil, Error> {
6285 let datetime = self.datetime.checked_add(duration)?;
6286 let timestamp = datetime
6287 .to_zoned(TimeZone::UTC)
6288 .context(E::ConvertDateTimeToTimestamp)?
6289 .timestamp();
6290 Ok(RelativeCivil { datetime, timestamp })
6291 }
6292
6293 /// Returns the result of [`DateTime::until`].
6294 ///
6295 /// # Errors
6296 ///
6297 /// Returns an error in the same cases as `DateTime::until`. That is, when
6298 /// the span for the given largest unit cannot be represented. This can
6299 /// generally only happen when `largest` is `Unit::Nanosecond` and the span
6300 /// cannot be represented as a 64-bit integer of nanoseconds.
6301 fn until(
6302 &self,
6303 largest: Unit,
6304 other: &RelativeCivil,
6305 ) -> Result<Span, Error> {
6306 self.datetime
6307 .until((largest, other.datetime))
6308 .context(E::FailedSpanBetweenDateTimes { unit: largest })
6309 }
6310}
6311
6312/// A simple wrapper around a possibly borrowed `Zoned`.
6313#[derive(Clone, Debug)]
6314struct RelativeZoned<'a> {
6315 zoned: DumbCow<'a, Zoned>,
6316}
6317
6318impl<'a> RelativeZoned<'a> {
6319 /// Returns the result of [`Zoned::checked_add`].
6320 ///
6321 /// # Errors
6322 ///
6323 /// Returns an error in the same cases as `Zoned::checked_add`. That is,
6324 /// when adding the span to this zoned datetime would overflow.
6325 fn checked_add(
6326 &self,
6327 span: Span,
6328 ) -> Result<RelativeZoned<'static>, Error> {
6329 let zoned = self.zoned.checked_add(span)?;
6330 Ok(RelativeZoned { zoned: DumbCow::Owned(zoned) })
6331 }
6332
6333 /// Returns the result of [`Zoned::checked_add`] with an absolute duration.
6334 ///
6335 /// # Errors
6336 ///
6337 /// Returns an error in the same cases as `Zoned::checked_add`. That is,
6338 /// when adding the span to this zoned datetime would overflow.
6339 fn checked_add_duration(
6340 &self,
6341 duration: SignedDuration,
6342 ) -> Result<RelativeZoned<'static>, Error> {
6343 let zoned = self.zoned.checked_add(duration)?;
6344 Ok(RelativeZoned { zoned: DumbCow::Owned(zoned) })
6345 }
6346
6347 /// Returns the result of [`Zoned::until`].
6348 ///
6349 /// # Errors
6350 ///
6351 /// Returns an error in the same cases as `Zoned::until`. That is, when
6352 /// the span for the given largest unit cannot be represented. This can
6353 /// generally only happen when `largest` is `Unit::Nanosecond` and the span
6354 /// cannot be represented as a 64-bit integer of nanoseconds.
6355 fn until(
6356 &self,
6357 largest: Unit,
6358 other: &RelativeZoned<'a>,
6359 ) -> Result<Span, Error> {
6360 self.zoned
6361 .until((largest, &*other.zoned))
6362 .context(E::FailedSpanBetweenZonedDateTimes { unit: largest })
6363 }
6364
6365 /// Returns the borrowed version of self; useful when you need to convert
6366 /// `&RelativeZoned` into `RelativeZoned` without cloning anything.
6367 fn borrowed(&'a self) -> RelativeZoned<'a> {
6368 RelativeZoned { zoned: self.zoned.borrowed() }
6369 }
6370}
6371
6372// The code below is the "core" rounding logic for spans. It was greatly
6373// inspired by this gist[1] and the fullcalendar Temporal polyfill[2]. In
6374// particular, the algorithm implemented below is a major simplification from
6375// how Temporal used to work[3]. Parts of it are still in rough and unclear
6376// shape IMO.
6377//
6378// [1]: https://gist.github.com/arshaw/36d3152c21482bcb78ea2c69591b20e0
6379// [2]: https://github.com/fullcalendar/temporal-polyfill
6380// [3]: https://github.com/tc39/proposal-temporal/issues/2792
6381
6382/// The result of a span rounding strategy. There are three:
6383///
6384/// * Rounding spans relative to civil datetimes using only invariant
6385/// units (days or less). This is achieved by converting the span to a simple
6386/// integer number of nanoseconds and then rounding that.
6387/// * Rounding spans relative to either a civil datetime or a zoned datetime
6388/// where rounding might involve changing non-uniform units. That is, when
6389/// the smallest unit is greater than days for civil datetimes and greater
6390/// than hours for zoned datetimes.
6391/// * Rounding spans relative to a zoned datetime whose smallest unit is
6392/// less than days.
6393///
6394/// Each of these might produce a bottom heavy span that needs to be
6395/// re-balanced. This type represents that result via one of three constructors
6396/// corresponding to each of the above strategies, and then provides a routine
6397/// for rebalancing via "bubbling."
6398#[derive(Debug)]
6399struct Nudge {
6400 /// A possibly bottom heavy rounded span.
6401 span: Span,
6402 /// The nanosecond timestamp corresponding to `relative + span`, where
6403 /// `span` is the (possibly bottom heavy) rounded span.
6404 rounded_relative_end: NoUnits128,
6405 /// Whether rounding may have created a bottom heavy span such that a
6406 /// calendar unit might need to be incremented after re-balancing smaller
6407 /// units.
6408 grew_big_unit: bool,
6409}
6410
6411impl Nudge {
6412 /// Performs rounding on the given span limited to invariant units.
6413 ///
6414 /// For civil datetimes, this means the smallest unit must be days or less,
6415 /// but the largest unit can be bigger. For zoned datetimes, this means
6416 /// that *both* the largest and smallest unit must be hours or less. This
6417 /// is because zoned datetimes with rounding that can spill up to days
6418 /// requires special handling.
6419 ///
6420 /// It works by converting the span to a single integer number of
6421 /// nanoseconds, rounding it and then converting back to a span.
6422 fn relative_invariant(
6423 balanced: Span,
6424 relative_end: NoUnits128,
6425 smallest: Unit,
6426 largest: Unit,
6427 increment: NoUnits128,
6428 mode: RoundMode,
6429 ) -> Result<Nudge, Error> {
6430 // Ensures this is only called when rounding invariant units.
6431 assert!(smallest <= Unit::Week);
6432
6433 let sign = balanced.get_sign_ranged();
6434 let balanced_nanos = balanced.to_invariant_nanoseconds();
6435 let rounded_nanos = mode.round_by_unit_in_nanoseconds(
6436 balanced_nanos,
6437 smallest,
6438 increment,
6439 );
6440 let span = Span::from_invariant_nanoseconds(largest, rounded_nanos)
6441 .context(E::ConvertNanoseconds { unit: largest })?
6442 .years_ranged(balanced.get_years_ranged())
6443 .months_ranged(balanced.get_months_ranged())
6444 .weeks_ranged(balanced.get_weeks_ranged());
6445
6446 let diff_nanos = rounded_nanos - balanced_nanos;
6447 let diff_days = rounded_nanos.div_ceil(t::NANOS_PER_CIVIL_DAY)
6448 - balanced_nanos.div_ceil(t::NANOS_PER_CIVIL_DAY);
6449 let grew_big_unit = diff_days.signum() == sign;
6450 let rounded_relative_end = relative_end + diff_nanos;
6451 Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6452 }
6453
6454 /// Performs rounding on the given span where the smallest unit configured
6455 /// implies that rounding will cover calendar or "non-uniform" units. (That
6456 /// is, units whose length can change based on the relative datetime.)
6457 fn relative_calendar(
6458 balanced: Span,
6459 relative_start: &Relative<'_>,
6460 relative_end: &Relative<'_>,
6461 smallest: Unit,
6462 increment: NoUnits128,
6463 mode: RoundMode,
6464 ) -> Result<Nudge, Error> {
6465 #[cfg(not(feature = "std"))]
6466 use crate::util::libm::Float;
6467
6468 assert!(smallest >= Unit::Day);
6469 let sign = balanced.get_sign_ranged();
6470 let truncated = increment
6471 * balanced.get_units_ranged(smallest).div_ceil(increment);
6472 let span = balanced
6473 .without_lower(smallest)
6474 .try_units_ranged(smallest, truncated.rinto())?;
6475 let (relative0, relative1) = clamp_relative_span(
6476 relative_start,
6477 span,
6478 smallest,
6479 NoUnits::try_rfrom("increment", increment)?
6480 .try_checked_mul("signed increment", sign)?,
6481 )?;
6482
6483 // FIXME: This is brutal. This is the only non-optional floating point
6484 // used so far in Jiff. We do expose floating point for things like
6485 // `Span::total`, but that's optional and not a core part of Jiff's
6486 // functionality. This is in the core part of Jiff's span rounding...
6487 let denom = (relative1 - relative0).get() as f64;
6488 let numer = (relative_end.to_nanosecond() - relative0).get() as f64;
6489 let exact = (truncated.get() as f64)
6490 + (numer / denom) * (sign.get() as f64) * (increment.get() as f64);
6491 let rounded = mode.round_float(exact, increment);
6492 let grew_big_unit =
6493 ((rounded.get() as f64) - exact).signum() == (sign.get() as f64);
6494
6495 let span = span.try_units_ranged(smallest, rounded.rinto())?;
6496 let rounded_relative_end =
6497 if grew_big_unit { relative1 } else { relative0 };
6498 Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6499 }
6500
6501 /// Performs rounding on the given span where the smallest unit is hours
6502 /// or less *and* the relative datetime is time zone aware.
6503 fn relative_zoned_time(
6504 balanced: Span,
6505 relative_start: &RelativeZoned<'_>,
6506 smallest: Unit,
6507 increment: NoUnits128,
6508 mode: RoundMode,
6509 ) -> Result<Nudge, Error> {
6510 let sign = balanced.get_sign_ranged();
6511 let time_nanos =
6512 balanced.only_lower(Unit::Day).to_invariant_nanoseconds();
6513 let mut rounded_time_nanos =
6514 mode.round_by_unit_in_nanoseconds(time_nanos, smallest, increment);
6515 let (relative0, relative1) = clamp_relative_span(
6516 // FIXME: Find a way to drop this clone.
6517 &Relative::Zoned(relative_start.clone()),
6518 balanced.without_lower(Unit::Day),
6519 Unit::Day,
6520 sign.rinto(),
6521 )?;
6522 let day_nanos = relative1 - relative0;
6523 let beyond_day_nanos = rounded_time_nanos - day_nanos;
6524
6525 let mut day_delta = NoUnits::N::<0>();
6526 let rounded_relative_end =
6527 if beyond_day_nanos == C(0) || beyond_day_nanos.signum() == sign {
6528 day_delta += C(1);
6529 rounded_time_nanos = mode.round_by_unit_in_nanoseconds(
6530 beyond_day_nanos,
6531 smallest,
6532 increment,
6533 );
6534 relative1 + rounded_time_nanos
6535 } else {
6536 relative0 + rounded_time_nanos
6537 };
6538
6539 let span =
6540 Span::from_invariant_nanoseconds(Unit::Hour, rounded_time_nanos)
6541 .context(E::ConvertNanoseconds { unit: Unit::Hour })?
6542 .years_ranged(balanced.get_years_ranged())
6543 .months_ranged(balanced.get_months_ranged())
6544 .weeks_ranged(balanced.get_weeks_ranged())
6545 .days_ranged(balanced.get_days_ranged() + day_delta);
6546 let grew_big_unit = day_delta != C(0);
6547 Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6548 }
6549
6550 /// This "bubbles" up the units in a potentially "bottom heavy" span to
6551 /// larger units. For example, P1m50d relative to March 1 is bottom heavy.
6552 /// This routine will bubble the days up to months to get P2m19d.
6553 ///
6554 /// # Errors
6555 ///
6556 /// This routine fails if any arithmetic on the individual units fails, or
6557 /// when span arithmetic on the relative datetime given fails.
6558 fn bubble(
6559 &self,
6560 relative: &RelativeSpan,
6561 smallest: Unit,
6562 largest: Unit,
6563 ) -> Result<Span, Error> {
6564 if !self.grew_big_unit || smallest == Unit::Week {
6565 return Ok(self.span);
6566 }
6567
6568 let smallest = smallest.max(Unit::Day);
6569 let mut balanced = self.span;
6570 let sign = balanced.get_sign_ranged();
6571 let mut unit = smallest;
6572 while let Some(u) = unit.next() {
6573 unit = u;
6574 if unit > largest {
6575 break;
6576 }
6577 // We only bubble smaller units up into weeks when the largest unit
6578 // is explicitly set to weeks. Otherwise, we leave it as-is.
6579 if unit == Unit::Week && largest != Unit::Week {
6580 continue;
6581 }
6582
6583 let span_start = balanced.without_lower(unit);
6584 let new_units = span_start
6585 .get_units_ranged(unit)
6586 .try_checked_add("bubble-units", sign)?;
6587 let span_end = span_start.try_units_ranged(unit, new_units)?;
6588 let threshold = match relative.kind {
6589 RelativeSpanKind::Civil { ref start, .. } => {
6590 start.checked_add(span_end)?.timestamp
6591 }
6592 RelativeSpanKind::Zoned { ref start, .. } => {
6593 start.checked_add(span_end)?.zoned.timestamp()
6594 }
6595 };
6596 let beyond =
6597 self.rounded_relative_end - threshold.as_nanosecond_ranged();
6598 if beyond == C(0) || beyond.signum() == sign {
6599 balanced = span_end;
6600 } else {
6601 break;
6602 }
6603 }
6604 Ok(balanced)
6605 }
6606}
6607
6608/// Rounds a span consisting of only invariant units.
6609///
6610/// This only applies when the max of the units in the span being rounded,
6611/// the largest configured unit and the smallest configured unit are all
6612/// invariant. That is, days or lower for spans without a relative datetime or
6613/// a relative civil datetime, and hours or lower for spans with a relative
6614/// zoned datetime.
6615///
6616/// All we do here is convert the span to an integer number of nanoseconds,
6617/// round that and then convert back. There aren't any tricky corner cases to
6618/// consider here.
6619fn round_span_invariant(
6620 span: Span,
6621 smallest: Unit,
6622 largest: Unit,
6623 increment: NoUnits128,
6624 mode: RoundMode,
6625) -> Result<Span, Error> {
6626 assert!(smallest <= Unit::Week);
6627 assert!(largest <= Unit::Week);
6628 let nanos = span.to_invariant_nanoseconds();
6629 let rounded =
6630 mode.round_by_unit_in_nanoseconds(nanos, smallest, increment);
6631 Span::from_invariant_nanoseconds(largest, rounded)
6632 .context(E::ConvertNanoseconds { unit: largest })
6633}
6634
6635/// Returns the nanosecond timestamps of `relative + span` and `relative +
6636/// {amount of unit} + span`.
6637///
6638/// This is useful for determining the actual length, in nanoseconds, of some
6639/// unit amount (usually a single unit). Usually, this is called with a span
6640/// whose units lower than `unit` are zeroed out and with an `amount` that
6641/// is `-1` or `1` or `0`. So for example, if `unit` were `Unit::Day`, then
6642/// you'd get back two nanosecond timestamps relative to the relative datetime
6643/// given that start exactly "one day" apart. (Which might be different than 24
6644/// hours, depending on the time zone.)
6645///
6646/// # Errors
6647///
6648/// This returns an error if adding the units overflows, or if doing the span
6649/// arithmetic on `relative` overflows.
6650fn clamp_relative_span(
6651 relative: &Relative<'_>,
6652 span: Span,
6653 unit: Unit,
6654 amount: NoUnits,
6655) -> Result<(NoUnits128, NoUnits128), Error> {
6656 let amount =
6657 span.get_units_ranged(unit).try_checked_add("clamp-units", amount)?;
6658 let span_amount = span.try_units_ranged(unit, amount)?;
6659 let relative0 = relative.checked_add(span)?.to_nanosecond();
6660 let relative1 = relative.checked_add(span_amount)?.to_nanosecond();
6661 Ok((relative0, relative1))
6662}
6663
6664/// A common parsing function that works in bytes.
6665///
6666/// Specifically, this parses either an ISO 8601 duration into a `Span` or
6667/// a "friendly" duration into a `Span`. It also tries to give decent error
6668/// messages.
6669///
6670/// This works because the friendly and ISO 8601 formats have non-overlapping
6671/// prefixes. Both can start with a `+` or `-`, but aside from that, an ISO
6672/// 8601 duration _always_ has to start with a `P` or `p`. We can utilize this
6673/// property to very quickly determine how to parse the input. We just need to
6674/// handle the possibly ambiguous case with a leading sign a little carefully
6675/// in order to ensure good error messages.
6676///
6677/// (We do the same thing for `SignedDuration`.)
6678#[cfg_attr(feature = "perf-inline", inline(always))]
6679fn parse_iso_or_friendly(bytes: &[u8]) -> Result<Span, Error> {
6680 let Some((&byte, tail)) = bytes.split_first() else {
6681 return Err(crate::Error::from(
6682 crate::error::fmt::Error::HybridDurationEmpty,
6683 ));
6684 };
6685 let mut first = byte;
6686 // N.B. Unsigned durations don't support negative durations (of
6687 // course), but we still check for it here so that we can defer to
6688 // the dedicated parsers. They will provide their own error messages.
6689 if first == b'+' || first == b'-' {
6690 let Some(&byte) = tail.first() else {
6691 return Err(crate::Error::from(
6692 crate::error::fmt::Error::HybridDurationPrefix { sign: first },
6693 ));
6694 };
6695 first = byte;
6696 }
6697 if first == b'P' || first == b'p' {
6698 temporal::DEFAULT_SPAN_PARSER.parse_span(bytes)
6699 } else {
6700 friendly::DEFAULT_SPAN_PARSER.parse_span(bytes)
6701 }
6702}
6703
6704fn requires_relative_date_err(unit: Unit) -> Result<(), Error> {
6705 if unit.is_variable() {
6706 return Err(Error::from(if matches!(unit, Unit::Week | Unit::Day) {
6707 E::RequiresRelativeWeekOrDay { unit }
6708 } else {
6709 E::RequiresRelativeYearOrMonth { unit }
6710 }));
6711 }
6712 Ok(())
6713}
6714
6715#[cfg(test)]
6716mod tests {
6717 use std::io::Cursor;
6718
6719 use alloc::string::ToString;
6720
6721 use crate::{civil::date, RoundMode};
6722
6723 use super::*;
6724
6725 #[test]
6726 fn test_total() {
6727 if crate::tz::db().is_definitively_empty() {
6728 return;
6729 }
6730
6731 let span = 130.hours().minutes(20);
6732 let total = span.total(Unit::Second).unwrap();
6733 assert_eq!(total, 469200.0);
6734
6735 let span = 123456789.seconds();
6736 let total = span
6737 .total(SpanTotal::from(Unit::Day).days_are_24_hours())
6738 .unwrap();
6739 assert_eq!(total, 1428.8980208333332);
6740
6741 let span = 2756.hours();
6742 let dt = date(2020, 1, 1).at(0, 0, 0, 0);
6743 let zdt = dt.in_tz("Europe/Rome").unwrap();
6744 let total = span.total((Unit::Month, &zdt)).unwrap();
6745 assert_eq!(total, 3.7958333333333334);
6746 let total = span.total((Unit::Month, dt)).unwrap();
6747 assert_eq!(total, 3.7944444444444443);
6748 }
6749
6750 #[test]
6751 fn test_compare() {
6752 if crate::tz::db().is_definitively_empty() {
6753 return;
6754 }
6755
6756 let span1 = 79.hours().minutes(10);
6757 let span2 = 79.hours().seconds(630);
6758 let span3 = 78.hours().minutes(50);
6759 let mut array = [span1, span2, span3];
6760 array.sort_by(|sp1, sp2| sp1.compare(sp2).unwrap());
6761 assert_eq!(array, [span3, span1, span2].map(SpanFieldwise));
6762
6763 let day24 = SpanRelativeTo::days_are_24_hours();
6764 let span1 = 79.hours().minutes(10);
6765 let span2 = 3.days().hours(7).seconds(630);
6766 let span3 = 3.days().hours(6).minutes(50);
6767 let mut array = [span1, span2, span3];
6768 array.sort_by(|sp1, sp2| sp1.compare((sp2, day24)).unwrap());
6769 assert_eq!(array, [span3, span1, span2].map(SpanFieldwise));
6770
6771 let dt = date(2020, 11, 1).at(0, 0, 0, 0);
6772 let zdt = dt.in_tz("America/Los_Angeles").unwrap();
6773 array.sort_by(|sp1, sp2| sp1.compare((sp2, &zdt)).unwrap());
6774 assert_eq!(array, [span1, span3, span2].map(SpanFieldwise));
6775 }
6776
6777 #[test]
6778 fn test_checked_add() {
6779 let span1 = 1.hour();
6780 let span2 = 30.minutes();
6781 let sum = span1.checked_add(span2).unwrap();
6782 span_eq!(sum, 1.hour().minutes(30));
6783
6784 let span1 = 1.hour().minutes(30);
6785 let span2 = 2.hours().minutes(45);
6786 let sum = span1.checked_add(span2).unwrap();
6787 span_eq!(sum, 4.hours().minutes(15));
6788
6789 let span = 50
6790 .years()
6791 .months(50)
6792 .days(50)
6793 .hours(50)
6794 .minutes(50)
6795 .seconds(50)
6796 .milliseconds(500)
6797 .microseconds(500)
6798 .nanoseconds(500);
6799 let relative = date(1900, 1, 1).at(0, 0, 0, 0);
6800 let sum = span.checked_add((span, relative)).unwrap();
6801 let expected = 108
6802 .years()
6803 .months(7)
6804 .days(12)
6805 .hours(5)
6806 .minutes(41)
6807 .seconds(41)
6808 .milliseconds(1)
6809 .microseconds(1)
6810 .nanoseconds(0);
6811 span_eq!(sum, expected);
6812
6813 let span = 1.month().days(15);
6814 let relative = date(2000, 2, 1).at(0, 0, 0, 0);
6815 let sum = span.checked_add((span, relative)).unwrap();
6816 span_eq!(sum, 3.months());
6817 let relative = date(2000, 3, 1).at(0, 0, 0, 0);
6818 let sum = span.checked_add((span, relative)).unwrap();
6819 span_eq!(sum, 2.months().days(30));
6820 }
6821
6822 #[test]
6823 fn test_round_day_time() {
6824 let span = 29.seconds();
6825 let rounded = span.round(Unit::Minute).unwrap();
6826 span_eq!(rounded, 0.minute());
6827
6828 let span = 30.seconds();
6829 let rounded = span.round(Unit::Minute).unwrap();
6830 span_eq!(rounded, 1.minute());
6831
6832 let span = 8.seconds();
6833 let rounded = span
6834 .round(
6835 SpanRound::new()
6836 .smallest(Unit::Nanosecond)
6837 .largest(Unit::Microsecond),
6838 )
6839 .unwrap();
6840 span_eq!(rounded, 8_000_000.microseconds());
6841
6842 let span = 130.minutes();
6843 let rounded = span
6844 .round(SpanRound::new().largest(Unit::Day).days_are_24_hours())
6845 .unwrap();
6846 span_eq!(rounded, 2.hours().minutes(10));
6847
6848 let span = 10.minutes().seconds(52);
6849 let rounded = span.round(Unit::Minute).unwrap();
6850 span_eq!(rounded, 11.minutes());
6851
6852 let span = 10.minutes().seconds(52);
6853 let rounded = span
6854 .round(
6855 SpanRound::new().smallest(Unit::Minute).mode(RoundMode::Trunc),
6856 )
6857 .unwrap();
6858 span_eq!(rounded, 10.minutes());
6859
6860 let span = 2.hours().minutes(34).seconds(18);
6861 let rounded =
6862 span.round(SpanRound::new().largest(Unit::Second)).unwrap();
6863 span_eq!(rounded, 9258.seconds());
6864
6865 let span = 6.minutes();
6866 let rounded = span
6867 .round(
6868 SpanRound::new()
6869 .smallest(Unit::Minute)
6870 .increment(5)
6871 .mode(RoundMode::Ceil),
6872 )
6873 .unwrap();
6874 span_eq!(rounded, 10.minutes());
6875 }
6876
6877 #[test]
6878 fn test_round_relative_zoned_calendar() {
6879 if crate::tz::db().is_definitively_empty() {
6880 return;
6881 }
6882
6883 let span = 2756.hours();
6884 let relative =
6885 date(2020, 1, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6886 let options = SpanRound::new()
6887 .largest(Unit::Year)
6888 .smallest(Unit::Day)
6889 .relative(&relative);
6890 let rounded = span.round(options).unwrap();
6891 span_eq!(rounded, 3.months().days(24));
6892
6893 let span = 24.hours().nanoseconds(5);
6894 let relative = date(2000, 10, 29)
6895 .at(0, 0, 0, 0)
6896 .in_tz("America/Vancouver")
6897 .unwrap();
6898 let options = SpanRound::new()
6899 .largest(Unit::Day)
6900 .smallest(Unit::Minute)
6901 .relative(&relative)
6902 .mode(RoundMode::Expand)
6903 .increment(30);
6904 let rounded = span.round(options).unwrap();
6905 // It seems like this is the correct answer, although it apparently
6906 // differs from Temporal and the FullCalendar polyfill. I'm not sure
6907 // what accounts for the difference in the implementation.
6908 //
6909 // See: https://github.com/tc39/proposal-temporal/pull/2758#discussion_r1597255245
6910 span_eq!(rounded, 24.hours().minutes(30));
6911
6912 // Ref: https://github.com/tc39/proposal-temporal/issues/2816#issuecomment-2115608460
6913 let span = -1.month().hours(24);
6914 let relative: crate::Zoned = date(2024, 4, 11)
6915 .at(2, 0, 0, 0)
6916 .in_tz("America/New_York")
6917 .unwrap();
6918 let options =
6919 SpanRound::new().smallest(Unit::Millisecond).relative(&relative);
6920 let rounded = span.round(options).unwrap();
6921 span_eq!(rounded, -1.month().days(1).hours(1));
6922 let dt = relative.checked_add(span).unwrap();
6923 let diff = relative.until((Unit::Month, &dt)).unwrap();
6924 span_eq!(diff, -1.month().days(1).hours(1));
6925
6926 // Like the above, but don't use a datetime near a DST transition. In
6927 // this case, a day is a normal 24 hours. (Unlike above, where the
6928 // duration includes a 23 hour day, and so an additional hour has to be
6929 // added to the span to account for that.)
6930 let span = -1.month().hours(24);
6931 let relative = date(2024, 6, 11)
6932 .at(2, 0, 0, 0)
6933 .in_tz("America/New_York")
6934 .unwrap();
6935 let options =
6936 SpanRound::new().smallest(Unit::Millisecond).relative(&relative);
6937 let rounded = span.round(options).unwrap();
6938 span_eq!(rounded, -1.month().days(1));
6939 }
6940
6941 #[test]
6942 fn test_round_relative_zoned_time() {
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().largest(Unit::Year).relative(&relative);
6951 let rounded = span.round(options).unwrap();
6952 span_eq!(rounded, 3.months().days(23).hours(21));
6953
6954 let span = 2756.hours();
6955 let relative =
6956 date(2020, 9, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6957 let options = SpanRound::new().largest(Unit::Year).relative(&relative);
6958 let rounded = span.round(options).unwrap();
6959 span_eq!(rounded, 3.months().days(23).hours(19));
6960
6961 let span = 3.hours();
6962 let relative =
6963 date(2020, 3, 8).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6964 let options = SpanRound::new().largest(Unit::Year).relative(&relative);
6965 let rounded = span.round(options).unwrap();
6966 span_eq!(rounded, 3.hours());
6967 }
6968
6969 #[test]
6970 fn test_round_relative_day_time() {
6971 let span = 2756.hours();
6972 let options =
6973 SpanRound::new().largest(Unit::Year).relative(date(2020, 1, 1));
6974 let rounded = span.round(options).unwrap();
6975 span_eq!(rounded, 3.months().days(23).hours(20));
6976
6977 let span = 2756.hours();
6978 let options =
6979 SpanRound::new().largest(Unit::Year).relative(date(2020, 9, 1));
6980 let rounded = span.round(options).unwrap();
6981 span_eq!(rounded, 3.months().days(23).hours(20));
6982
6983 let span = 190.days();
6984 let options =
6985 SpanRound::new().largest(Unit::Year).relative(date(2020, 1, 1));
6986 let rounded = span.round(options).unwrap();
6987 span_eq!(rounded, 6.months().days(8));
6988
6989 let span = 30
6990 .days()
6991 .hours(23)
6992 .minutes(59)
6993 .seconds(59)
6994 .milliseconds(999)
6995 .microseconds(999)
6996 .nanoseconds(999);
6997 let options = SpanRound::new()
6998 .smallest(Unit::Microsecond)
6999 .largest(Unit::Year)
7000 .relative(date(2024, 5, 1));
7001 let rounded = span.round(options).unwrap();
7002 span_eq!(rounded, 1.month());
7003
7004 let span = 364
7005 .days()
7006 .hours(23)
7007 .minutes(59)
7008 .seconds(59)
7009 .milliseconds(999)
7010 .microseconds(999)
7011 .nanoseconds(999);
7012 let options = SpanRound::new()
7013 .smallest(Unit::Microsecond)
7014 .largest(Unit::Year)
7015 .relative(date(2023, 1, 1));
7016 let rounded = span.round(options).unwrap();
7017 span_eq!(rounded, 1.year());
7018
7019 let span = 365
7020 .days()
7021 .hours(23)
7022 .minutes(59)
7023 .seconds(59)
7024 .milliseconds(999)
7025 .microseconds(999)
7026 .nanoseconds(999);
7027 let options = SpanRound::new()
7028 .smallest(Unit::Microsecond)
7029 .largest(Unit::Year)
7030 .relative(date(2023, 1, 1));
7031 let rounded = span.round(options).unwrap();
7032 span_eq!(rounded, 1.year().days(1));
7033
7034 let span = 365
7035 .days()
7036 .hours(23)
7037 .minutes(59)
7038 .seconds(59)
7039 .milliseconds(999)
7040 .microseconds(999)
7041 .nanoseconds(999);
7042 let options = SpanRound::new()
7043 .smallest(Unit::Microsecond)
7044 .largest(Unit::Year)
7045 .relative(date(2024, 1, 1));
7046 let rounded = span.round(options).unwrap();
7047 span_eq!(rounded, 1.year());
7048
7049 let span = 3.hours();
7050 let options =
7051 SpanRound::new().largest(Unit::Year).relative(date(2020, 3, 8));
7052 let rounded = span.round(options).unwrap();
7053 span_eq!(rounded, 3.hours());
7054 }
7055
7056 #[test]
7057 fn span_sign() {
7058 assert_eq!(Span::new().get_sign_ranged(), C(0));
7059 assert_eq!(Span::new().days(1).get_sign_ranged(), C(1));
7060 assert_eq!(Span::new().days(-1).get_sign_ranged(), C(-1));
7061 assert_eq!(Span::new().days(1).days(0).get_sign_ranged(), C(0));
7062 assert_eq!(Span::new().days(-1).days(0).get_sign_ranged(), C(0));
7063 assert_eq!(
7064 Span::new().years(1).days(1).days(0).get_sign_ranged(),
7065 C(1)
7066 );
7067 assert_eq!(
7068 Span::new().years(-1).days(-1).days(0).get_sign_ranged(),
7069 C(-1)
7070 );
7071 }
7072
7073 #[test]
7074 fn span_size() {
7075 #[cfg(target_pointer_width = "64")]
7076 {
7077 #[cfg(debug_assertions)]
7078 {
7079 assert_eq!(core::mem::align_of::<Span>(), 8);
7080 assert_eq!(core::mem::size_of::<Span>(), 184);
7081 }
7082 #[cfg(not(debug_assertions))]
7083 {
7084 assert_eq!(core::mem::align_of::<Span>(), 8);
7085 assert_eq!(core::mem::size_of::<Span>(), 64);
7086 }
7087 }
7088 }
7089
7090 quickcheck::quickcheck! {
7091 fn prop_roundtrip_span_nanoseconds(span: Span) -> quickcheck::TestResult {
7092 let largest = span.largest_unit();
7093 if largest > Unit::Day {
7094 return quickcheck::TestResult::discard();
7095 }
7096 let nanos = span.to_invariant_nanoseconds();
7097 let got = Span::from_invariant_nanoseconds(largest, nanos).unwrap();
7098 quickcheck::TestResult::from_bool(nanos == got.to_invariant_nanoseconds())
7099 }
7100 }
7101
7102 /// # `serde` deserializer compatibility test
7103 ///
7104 /// Serde YAML used to be unable to deserialize `jiff` types,
7105 /// as deserializing from bytes is not supported by the deserializer.
7106 ///
7107 /// - <https://github.com/BurntSushi/jiff/issues/138>
7108 /// - <https://github.com/BurntSushi/jiff/discussions/148>
7109 #[test]
7110 fn span_deserialize_yaml() {
7111 let expected = Span::new()
7112 .years(1)
7113 .months(2)
7114 .weeks(3)
7115 .days(4)
7116 .hours(5)
7117 .minutes(6)
7118 .seconds(7);
7119
7120 let deserialized: Span =
7121 serde_yaml::from_str("P1y2m3w4dT5h6m7s").unwrap();
7122
7123 span_eq!(deserialized, expected);
7124
7125 let deserialized: Span =
7126 serde_yaml::from_slice("P1y2m3w4dT5h6m7s".as_bytes()).unwrap();
7127
7128 span_eq!(deserialized, expected);
7129
7130 let cursor = Cursor::new(b"P1y2m3w4dT5h6m7s");
7131 let deserialized: Span = serde_yaml::from_reader(cursor).unwrap();
7132
7133 span_eq!(deserialized, expected);
7134 }
7135
7136 #[test]
7137 fn display() {
7138 let span = Span::new()
7139 .years(1)
7140 .months(2)
7141 .weeks(3)
7142 .days(4)
7143 .hours(5)
7144 .minutes(6)
7145 .seconds(7)
7146 .milliseconds(8)
7147 .microseconds(9)
7148 .nanoseconds(10);
7149 insta::assert_snapshot!(
7150 span,
7151 @"P1Y2M3W4DT5H6M7.00800901S",
7152 );
7153 insta::assert_snapshot!(
7154 alloc::format!("{span:#}"),
7155 @"1y 2mo 3w 4d 5h 6m 7s 8ms 9µs 10ns",
7156 );
7157 }
7158
7159 /// This test ensures that we can parse `humantime` formatted durations.
7160 #[test]
7161 fn humantime_compatibility_parse() {
7162 let dur = std::time::Duration::new(60 * 60 * 24 * 411, 123_456_789);
7163 let formatted = humantime::format_duration(dur).to_string();
7164 assert_eq!(
7165 formatted,
7166 "1year 1month 15days 7h 26m 24s 123ms 456us 789ns"
7167 );
7168 let expected = 1
7169 .year()
7170 .months(1)
7171 .days(15)
7172 .hours(7)
7173 .minutes(26)
7174 .seconds(24)
7175 .milliseconds(123)
7176 .microseconds(456)
7177 .nanoseconds(789);
7178 span_eq!(formatted.parse::<Span>().unwrap(), expected);
7179 }
7180
7181 /// This test ensures that we can print a `Span` that `humantime` can
7182 /// parse.
7183 ///
7184 /// Note that this isn't the default since `humantime`'s parser is
7185 /// pretty limited. e.g., It doesn't support things like `nsecs`
7186 /// despite supporting `secs`. And other reasons. See the docs on
7187 /// `Designator::HumanTime` for why we sadly provide a custom variant for
7188 /// it.
7189 #[test]
7190 fn humantime_compatibility_print() {
7191 static PRINTER: friendly::SpanPrinter = friendly::SpanPrinter::new()
7192 .designator(friendly::Designator::HumanTime);
7193
7194 let span = 1
7195 .year()
7196 .months(1)
7197 .days(15)
7198 .hours(7)
7199 .minutes(26)
7200 .seconds(24)
7201 .milliseconds(123)
7202 .microseconds(456)
7203 .nanoseconds(789);
7204 let formatted = PRINTER.span_to_string(&span);
7205 assert_eq!(formatted, "1y 1month 15d 7h 26m 24s 123ms 456us 789ns");
7206
7207 let dur = humantime::parse_duration(&formatted).unwrap();
7208 let expected =
7209 std::time::Duration::new(60 * 60 * 24 * 411, 123_456_789);
7210 assert_eq!(dur, expected);
7211 }
7212
7213 #[test]
7214 fn from_str() {
7215 let p = |s: &str| -> Result<Span, Error> { s.parse() };
7216
7217 insta::assert_snapshot!(
7218 p("1 day").unwrap(),
7219 @"P1D",
7220 );
7221 insta::assert_snapshot!(
7222 p("+1 day").unwrap(),
7223 @"P1D",
7224 );
7225 insta::assert_snapshot!(
7226 p("-1 day").unwrap(),
7227 @"-P1D",
7228 );
7229 insta::assert_snapshot!(
7230 p("P1d").unwrap(),
7231 @"P1D",
7232 );
7233 insta::assert_snapshot!(
7234 p("+P1d").unwrap(),
7235 @"P1D",
7236 );
7237 insta::assert_snapshot!(
7238 p("-P1d").unwrap(),
7239 @"-P1D",
7240 );
7241
7242 insta::assert_snapshot!(
7243 p("").unwrap_err(),
7244 @r#"an empty string is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format"#,
7245 );
7246 insta::assert_snapshot!(
7247 p("+").unwrap_err(),
7248 @r#"found nothing after sign `+`, which is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format"#,
7249 );
7250 insta::assert_snapshot!(
7251 p("-").unwrap_err(),
7252 @r#"found nothing after sign `-`, which is not a valid duration in either the ISO 8601 format or Jiff's "friendly" format"#,
7253 );
7254 }
7255
7256 #[test]
7257 fn serde_deserialize() {
7258 let p = |s: &str| -> Result<Span, serde_json::Error> {
7259 serde_json::from_str(&alloc::format!("\"{s}\""))
7260 };
7261
7262 insta::assert_snapshot!(
7263 p("1 day").unwrap(),
7264 @"P1D",
7265 );
7266 insta::assert_snapshot!(
7267 p("+1 day").unwrap(),
7268 @"P1D",
7269 );
7270 insta::assert_snapshot!(
7271 p("-1 day").unwrap(),
7272 @"-P1D",
7273 );
7274 insta::assert_snapshot!(
7275 p("P1d").unwrap(),
7276 @"P1D",
7277 );
7278 insta::assert_snapshot!(
7279 p("+P1d").unwrap(),
7280 @"P1D",
7281 );
7282 insta::assert_snapshot!(
7283 p("-P1d").unwrap(),
7284 @"-P1D",
7285 );
7286
7287 insta::assert_snapshot!(
7288 p("").unwrap_err(),
7289 @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"#,
7290 );
7291 insta::assert_snapshot!(
7292 p("+").unwrap_err(),
7293 @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"#,
7294 );
7295 insta::assert_snapshot!(
7296 p("-").unwrap_err(),
7297 @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"#,
7298 );
7299 }
7300}