jiff/fmt/
util.rs

1use crate::{
2    error::{fmt::util::Error as E, ErrorContext},
3    fmt::Parsed,
4    util::{
5        b::{self, Sign},
6        parse,
7    },
8    Error, SignedDuration, Span, Unit,
9};
10
11/// A container for holding a partially parsed duration.
12///
13/// This is used for parsing into `Span`, `SignedDuration` and (hopefully
14/// soon) `std::time::Duration`. It's _also_ used for both the ISO 8601
15/// duration and "friendly" format.
16///
17/// This replaced a significant chunk of code that was bespoke to each
18/// combination of duration type _and_ format.
19///
20/// The idea behind it is that we parse each duration component as an unsigned
21/// 64-bit integer and keep track of the sign separately. This is a critical
22/// aspect that was motivated by being able to roundtrip all legal values of
23/// a 96-bit signed integer number of nanoseconds (i.e., `SignedDuration`).
24/// In particular, if we used `i64` to represent each component, then it
25/// makes it much more difficult to parse, e.g., `9223372036854775808
26/// seconds ago`. Namely, `9223372036854775808` is not a valid `i64` but
27/// `-9223372036854775808` is. Notably, the sign is indicated by a suffix,
28/// so we don't know it's negative when parsing the integer itself. So we
29/// represent all components as their unsigned absolute value and apply the
30/// sign at the end.
31///
32/// This also centralizes a lot of thorny duration math and opens up the
33/// opportunity for tighter optimization.
34#[derive(Debug, Default)]
35pub(crate) struct DurationUnits {
36    /// The parsed unit values in descending order. That is, nanoseconds are
37    /// at index 0 while years are at index 9.
38    values: [u64; 10],
39    /// Any fractional component parsed. The fraction is necessarily a fraction
40    /// of the minimum unit if present.
41    fraction: Option<u32>,
42    /// The sign of the duration. This may be set at any time.
43    ///
44    /// Note that this defaults to zero! So callers will always want to set
45    /// this.
46    sign: Sign,
47    /// The smallest unit value that was explicitly set.
48    min: Option<Unit>,
49    /// The largest unit value that was explicitly set.
50    max: Option<Unit>,
51    /// Whether there are any non-zero units.
52    any_non_zero_units: bool,
53}
54
55impl DurationUnits {
56    /// Set the duration component value for the given unit.
57    ///
58    /// The value here is always unsigned. To deal with negative values, set
59    /// the sign independently. It will be accounted for when using one of this
60    /// type's methods for converting to a concrete duration type.
61    ///
62    /// # Panics
63    ///
64    /// When this is called after `set_fraction`.
65    ///
66    /// # Errors
67    ///
68    /// Since this is meant to be used in service of duration parsing and all
69    /// duration parsing proceeds from largest to smallest units, this will
70    /// return an error if the given unit is bigger than or equal to any
71    /// previously set unit. This also implies that this can only be called
72    /// at most once for each unit value.
73    #[cfg_attr(feature = "perf-inline", inline(always))]
74    pub(crate) fn set_unit_value(
75        &mut self,
76        unit: Unit,
77        value: u64,
78    ) -> Result<(), Error> {
79        assert!(self.fraction.is_none());
80
81        if let Some(min) = self.min {
82            if min <= unit {
83                return Err(Error::from(E::OutOfOrderUnits {
84                    found: unit,
85                    previous: min,
86                }));
87            }
88        }
89        // Given the above check, the given unit must be smaller than any we
90        // have seen so far.
91        self.min = Some(unit);
92        // The maximum unit is always the first unit set, since we can never
93        // see a unit bigger than it without an error occurring.
94        if self.max.is_none() {
95            self.max = Some(unit);
96        }
97        self.values[unit.as_usize()] = value;
98        self.any_non_zero_units = self.any_non_zero_units || value != 0;
99        Ok(())
100    }
101
102    /// A convenience routine for setting values parsed from an `HH:MM:SS`
103    /// format (including the fraction).
104    ///
105    /// # Errors
106    ///
107    /// This forwards errors from `DurationUnits::set_unit_value`. It will also
108    /// return an error is the minimum parsed unit (so far) is smaller than
109    /// days. (Since `HH:MM:SS` can only appear after units of years, months,
110    /// weeks or days.)
111    pub(crate) fn set_hms(
112        &mut self,
113        hours: u64,
114        minutes: u64,
115        seconds: u64,
116        fraction: Option<u32>,
117    ) -> Result<(), Error> {
118        if let Some(min) = self.min {
119            if min <= Unit::Hour {
120                return Err(Error::from(E::OutOfOrderHMS { found: min }));
121            }
122        }
123        self.set_unit_value(Unit::Hour, hours)?;
124        self.set_unit_value(Unit::Minute, minutes)?;
125        self.set_unit_value(Unit::Second, seconds)?;
126        if let Some(fraction) = fraction {
127            self.set_fraction(fraction)?;
128        }
129        Ok(())
130    }
131
132    /// Set the fractional value.
133    ///
134    /// This is always interpreted as a fraction of the minimal unit.
135    ///
136    /// Callers must ensure this is called after the last call to
137    /// `DurationUnits::set_unit_value`.
138    ///
139    /// # Panics
140    ///
141    /// When `fraction` is not in the range `0..=999_999_999`. Callers are
142    /// expected to uphold this invariant.
143    ///
144    /// # Errors
145    ///
146    /// This will return an error if the minimum unit is `Unit::Nanosecond`.
147    /// (Because fractional nanoseconds are not supported.) This will also
148    /// return an error if the minimum unit is bigger than `Unit::Hour`.
149    pub(crate) fn set_fraction(&mut self, fraction: u32) -> Result<(), Error> {
150        assert!(fraction <= 999_999_999);
151        if let Some(min) = self.min {
152            if min > Unit::Hour || min == Unit::Nanosecond {
153                return Err(Error::from(E::NotAllowedFractionalUnit {
154                    found: min,
155                }));
156            }
157        }
158        self.fraction = Some(fraction);
159        Ok(())
160    }
161
162    /// Set the sign associated with the components.
163    ///
164    /// The sign applies to the entire duration. There is no support for
165    /// having some components signed and some unsigned.
166    ///
167    /// If no sign is set, then it is assumed to be zero. Note also that
168    /// even if a sign is explicitly set *and* all unit values are zero,
169    /// then the sign will be set to zero.
170    pub(crate) fn set_sign(&mut self, sign: Sign) {
171        self.sign = sign;
172    }
173
174    /// Convert these duration components to a `Span`.
175    ///
176    /// # Errors
177    ///
178    /// If any individual unit exceeds the limits of a `Span`, or if the units
179    /// combine to exceed what can be represented by a `Span`, then this
180    /// returns an error.
181    ///
182    /// This also returns an error if no units were set.
183    #[cfg_attr(feature = "perf-inline", inline(always))]
184    pub(crate) fn to_span(&self) -> Result<Span, Error> {
185        // When every unit value is less than this, *and* there is
186        // no fractional component, then we trigger a fast path that
187        // doesn't need to bother with error handling and careful
188        // handling of the sign.
189        //
190        // Why do we use the maximum year value? Because years are
191        // the "biggest" unit, it follows that there can't be any
192        // other unit whose limit is smaller than years as a
193        // dimenionless quantity. That is, if all parsed unit values
194        // are no bigger than the maximum year, then we know all
195        // parsed unit values are necessarily within their
196        // appropriate limits.
197        const LIMIT: u64 = b::SpanYears::MAX as u64;
198
199        // If we have a fraction or a particularly large unit,
200        // bail out to the general case.
201        if self.fraction.is_some()
202            || self.values.iter().any(|&value| value > LIMIT)
203            // If no unit was set, it's an error case.
204            || self.max.is_none()
205        {
206            return self.to_span_general();
207        }
208
209        let mut span = Span::new();
210
211        let years = self.values[Unit::Year.as_usize()] as i16;
212        let months = self.values[Unit::Month.as_usize()] as i32;
213        let weeks = self.values[Unit::Week.as_usize()] as i32;
214        let days = self.values[Unit::Day.as_usize()] as i32;
215        let hours = self.values[Unit::Hour.as_usize()] as i32;
216        let mins = self.values[Unit::Minute.as_usize()] as i64;
217        let secs = self.values[Unit::Second.as_usize()] as i64;
218        let millis = self.values[Unit::Millisecond.as_usize()] as i64;
219        let micros = self.values[Unit::Microsecond.as_usize()] as i64;
220        let nanos = self.values[Unit::Nanosecond.as_usize()] as i64;
221
222        span = span.years_unchecked(years);
223        span = span.months_unchecked(months);
224        span = span.weeks_unchecked(weeks);
225        span = span.days_unchecked(days);
226        span = span.hours_unchecked(hours);
227        span = span.minutes_unchecked(mins);
228        span = span.seconds_unchecked(secs);
229        span = span.milliseconds_unchecked(millis);
230        span = span.microseconds_unchecked(micros);
231        span = span.nanoseconds_unchecked(nanos);
232
233        // The unchecked setters above don't manipulate
234        // the sign, which defaults to zero. So we need to
235        // set it even when it's positive.
236        span = span.sign_unchecked(self.get_sign());
237
238        Ok(span)
239    }
240
241    /// The "general" implementation of `DurationUnits::to_span`.
242    ///
243    /// This handles all possible cases, including fractional units, with good
244    /// error handling. Basically, we take this path when we think an error
245    /// _could_ occur. But this function is more bloaty and does more work, so
246    /// the more it can be avoided, the better.
247    #[cold]
248    #[inline(never)]
249    fn to_span_general(&self) -> Result<Span, Error> {
250        #[cfg_attr(feature = "perf-inline", inline(always))]
251        fn set_time_unit(
252            unit: Unit,
253            value: i64,
254            span: Span,
255            set: impl FnOnce(Span) -> Result<Span, Error>,
256        ) -> Result<Span, Error> {
257            #[cold]
258            #[inline(never)]
259            fn fractional_fallback(
260                err: Error,
261                unit: Unit,
262                value: i64,
263                span: Span,
264            ) -> Result<Span, Error> {
265                // Fractional calendar units aren't supported. Neither are
266                // fractional nanoseconds. So there's nothing we can do in
267                // this case.
268                if unit > Unit::Hour || unit == Unit::Nanosecond {
269                    Err(err)
270                } else {
271                    // This is annoying, but because we can write out a larger
272                    // number of hours/minutes/seconds than what we actually
273                    // support, we need to be prepared to parse an unbalanced
274                    // span if our time units are too big here. In essence,
275                    // this lets a single time unit "overflow" into smaller
276                    // units if it exceeds the limits.
277                    fractional_time_to_span(unit, value, 0, span)
278                }
279            }
280
281            set(span)
282                .or_else(|err| fractional_fallback(err, unit, value, span))
283                .context(E::FailedValueSet { unit })
284        }
285
286        let (min, _) = self.get_min_max_units()?;
287        let mut span = Span::new();
288
289        if self.values[Unit::Year.as_usize()] != 0 {
290            let value = self.get_unit_value(Unit::Year)?;
291            span = span
292                .try_years(value)
293                .context(E::FailedValueSet { unit: Unit::Year })?;
294        }
295        if self.values[Unit::Month.as_usize()] != 0 {
296            let value = self.get_unit_value(Unit::Month)?;
297            span = span
298                .try_months(value)
299                .context(E::FailedValueSet { unit: Unit::Month })?;
300        }
301        if self.values[Unit::Week.as_usize()] != 0 {
302            let value = self.get_unit_value(Unit::Week)?;
303            span = span
304                .try_weeks(value)
305                .context(E::FailedValueSet { unit: Unit::Week })?;
306        }
307        if self.values[Unit::Day.as_usize()] != 0 {
308            let value = self.get_unit_value(Unit::Day)?;
309            span = span
310                .try_days(value)
311                .context(E::FailedValueSet { unit: Unit::Day })?;
312        }
313        if self.values[Unit::Hour.as_usize()] != 0 {
314            let value = self.get_unit_value(Unit::Hour)?;
315            span = set_time_unit(Unit::Hour, value, span, |span| {
316                span.try_hours(value)
317            })?;
318        }
319        if self.values[Unit::Minute.as_usize()] != 0 {
320            let value = self.get_unit_value(Unit::Minute)?;
321            span = set_time_unit(Unit::Minute, value, span, |span| {
322                span.try_minutes(value)
323            })?;
324        }
325        if self.values[Unit::Second.as_usize()] != 0 {
326            let value = self.get_unit_value(Unit::Second)?;
327            span = set_time_unit(Unit::Second, value, span, |span| {
328                span.try_seconds(value)
329            })?;
330        }
331        if self.values[Unit::Millisecond.as_usize()] != 0 {
332            let value = self.get_unit_value(Unit::Millisecond)?;
333            span = set_time_unit(Unit::Millisecond, value, span, |span| {
334                span.try_milliseconds(value)
335            })?;
336        }
337        if self.values[Unit::Microsecond.as_usize()] != 0 {
338            let value = self.get_unit_value(Unit::Microsecond)?;
339            span = set_time_unit(Unit::Microsecond, value, span, |span| {
340                span.try_microseconds(value)
341            })?;
342        }
343        if self.values[Unit::Nanosecond.as_usize()] != 0 {
344            let value = self.get_unit_value(Unit::Nanosecond)?;
345            span = set_time_unit(Unit::Nanosecond, value, span, |span| {
346                span.try_nanoseconds(value)
347            })?;
348        }
349
350        if let Some(fraction) = self.get_fraction()? {
351            let value = self.get_unit_value(min)?;
352            span = fractional_time_to_span(min, value, fraction, span)?;
353        }
354
355        Ok(span)
356    }
357
358    /// Convert these duration components to a `SignedDuration`.
359    ///
360    /// # Errors
361    ///
362    /// If the total number of nanoseconds represented by all units combined
363    /// exceeds what can bit in a 96-bit signed integer, then an error is
364    /// returned.
365    ///
366    /// An error is also returned if any calendar units (days or greater) were
367    /// set or if no units were set.
368    #[cfg_attr(feature = "perf-inline", inline(always))]
369    pub(crate) fn to_signed_duration(&self) -> Result<SignedDuration, Error> {
370        // When every unit value is less than this, *and* there is
371        // no fractional component, then we trigger a fast path that
372        // doesn't need to bother with error handling and careful
373        // handling of the sign.
374        //
375        // Why `999`? Well, I think it's nice to use one limit for all
376        // units to make the comparisons simpler (although we could
377        // use more targeted values to admit more cases, I didn't try
378        // that). But specifically, this means we can have `999ms 999us
379        // 999ns` as a maximal subsecond value without overflowing
380        // the nanosecond component of a `SignedDuration`. This lets
381        // us "just do math" without needing to check each result and
382        // handle errors.
383        const LIMIT: u64 = 999;
384
385        if self.fraction.is_some()
386            || self.values[..Unit::Day.as_usize()]
387                .iter()
388                .any(|&value| value > LIMIT)
389            || self.max.map_or(true, |max| max > Unit::Hour)
390        {
391            return self.to_signed_duration_general();
392        }
393
394        let hours = self.values[Unit::Hour.as_usize()] as i64;
395        let mins = self.values[Unit::Minute.as_usize()] as i64;
396        let secs = self.values[Unit::Second.as_usize()] as i64;
397        let millis = self.values[Unit::Millisecond.as_usize()] as i32;
398        let micros = self.values[Unit::Microsecond.as_usize()] as i32;
399        let nanos = self.values[Unit::Nanosecond.as_usize()] as i32;
400
401        let total_secs = (hours * 3600) + (mins * 60) + secs;
402        let total_nanos = (millis * 1_000_000) + (micros * 1_000) + nanos;
403        let mut sdur =
404            SignedDuration::new_without_nano_overflow(total_secs, total_nanos);
405        if self.get_sign().is_negative() {
406            sdur = -sdur;
407        }
408
409        Ok(sdur)
410    }
411
412    /// The "general" implementation of `DurationUnits::to_signed_duration`.
413    ///
414    /// This handles all possible cases, including fractional units, with good
415    /// error handling. Basically, we take this path when we think an error
416    /// _could_ occur. But this function is more bloaty and does more work, so
417    /// the more it can be avoided, the better.
418    #[cold]
419    #[inline(never)]
420    fn to_signed_duration_general(&self) -> Result<SignedDuration, Error> {
421        let (min, max) = self.get_min_max_units()?;
422        if max > Unit::Hour {
423            return Err(Error::from(E::NotAllowedCalendarUnit { unit: max }));
424        }
425
426        let mut sdur = SignedDuration::ZERO;
427        if self.values[Unit::Hour.as_usize()] != 0 {
428            let value = self.get_unit_value(Unit::Hour)?;
429            sdur = SignedDuration::try_from_hours(value)
430                .and_then(|nanos| sdur.checked_add(nanos))
431                .ok_or(E::OverflowForUnit { unit: Unit::Hour })?;
432        }
433        if self.values[Unit::Minute.as_usize()] != 0 {
434            let value = self.get_unit_value(Unit::Minute)?;
435            sdur = SignedDuration::try_from_mins(value)
436                .and_then(|nanos| sdur.checked_add(nanos))
437                .ok_or(E::OverflowForUnit { unit: Unit::Minute })?;
438        }
439        if self.values[Unit::Second.as_usize()] != 0 {
440            let value = self.get_unit_value(Unit::Second)?;
441            sdur = SignedDuration::from_secs(value)
442                .checked_add(sdur)
443                .ok_or(E::OverflowForUnit { unit: Unit::Second })?;
444        }
445        if self.values[Unit::Millisecond.as_usize()] != 0 {
446            let value = self.get_unit_value(Unit::Millisecond)?;
447            sdur = SignedDuration::from_millis(value)
448                .checked_add(sdur)
449                .ok_or(E::OverflowForUnit { unit: Unit::Millisecond })?;
450        }
451        if self.values[Unit::Microsecond.as_usize()] != 0 {
452            let value = self.get_unit_value(Unit::Microsecond)?;
453            sdur = SignedDuration::from_micros(value)
454                .checked_add(sdur)
455                .ok_or(E::OverflowForUnit { unit: Unit::Microsecond })?;
456        }
457        if self.values[Unit::Nanosecond.as_usize()] != 0 {
458            let value = self.get_unit_value(Unit::Nanosecond)?;
459            sdur = SignedDuration::from_nanos(value)
460                .checked_add(sdur)
461                .ok_or(E::OverflowForUnit { unit: Unit::Nanosecond })?;
462        }
463
464        if let Some(fraction) = self.get_fraction()? {
465            sdur = sdur
466                .checked_add(fractional_duration(min, fraction)?)
467                .ok_or(E::OverflowForUnitFractional { unit: min })?;
468        }
469
470        Ok(sdur)
471    }
472
473    /// Convert these duration components to a `core::time::Duration`.
474    ///
475    /// # Errors
476    ///
477    /// If the total number of nanoseconds represented by all units combined
478    /// exceeds what can bit in a 96-bit signed integer, then an error is
479    /// returned.
480    ///
481    /// An error is also returned if any calendar units (days or greater) were
482    /// set or if no units were set.
483    #[cfg_attr(feature = "perf-inline", inline(always))]
484    pub(crate) fn to_unsigned_duration(
485        &self,
486    ) -> Result<core::time::Duration, Error> {
487        // When every unit value is less than this, *and* there is
488        // no fractional component, then we trigger a fast path that
489        // doesn't need to bother with error handling and careful
490        // handling of the sign.
491        //
492        // Why `999`? Well, I think it's nice to use one limit for all
493        // units to make the comparisons simpler (although we could
494        // use more targeted values to admit more cases, I didn't try
495        // that). But specifically, this means we can have `999ms 999us
496        // 999ns` as a maximal subsecond value without overflowing
497        // the nanosecond component of a `core::time::Duration`. This lets
498        // us "just do math" without needing to check each result and
499        // handle errors.
500        const LIMIT: u64 = 999;
501
502        if self.fraction.is_some()
503            || self.values[..Unit::Day.as_usize()]
504                .iter()
505                .any(|&value| value > LIMIT)
506            || self.max.map_or(true, |max| max > Unit::Hour)
507            || self.sign.is_negative()
508        {
509            return self.to_unsigned_duration_general();
510        }
511
512        let hours = self.values[Unit::Hour.as_usize()];
513        let mins = self.values[Unit::Minute.as_usize()];
514        let secs = self.values[Unit::Second.as_usize()];
515        let millis = self.values[Unit::Millisecond.as_usize()] as u32;
516        let micros = self.values[Unit::Microsecond.as_usize()] as u32;
517        let nanos = self.values[Unit::Nanosecond.as_usize()] as u32;
518
519        let total_secs = (hours * 3600) + (mins * 60) + secs;
520        let total_nanos = (millis * 1_000_000) + (micros * 1_000) + nanos;
521        let sdur = core::time::Duration::new(total_secs, total_nanos);
522
523        Ok(sdur)
524    }
525
526    /// The "general" implementation of `DurationUnits::to_unsigned_duration`.
527    ///
528    /// This handles all possible cases, including fractional units, with good
529    /// error handling. Basically, we take this path when we think an error
530    /// _could_ occur. But this function is more bloaty and does more work, so
531    /// the more it can be avoided, the better.
532    #[cold]
533    #[inline(never)]
534    fn to_unsigned_duration_general(
535        &self,
536    ) -> Result<core::time::Duration, Error> {
537        #[inline]
538        const fn try_from_hours(hours: u64) -> Option<core::time::Duration> {
539            // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
540            const MAX_HOUR: u64 = u64::MAX / (60 * 60);
541            if hours > MAX_HOUR {
542                return None;
543            }
544            Some(core::time::Duration::from_secs(hours * 60 * 60))
545        }
546
547        #[inline]
548        const fn try_from_mins(mins: u64) -> Option<core::time::Duration> {
549            // OK because SECS_PER_MINUTE!={-1,0}.
550            const MAX_MINUTE: u64 = u64::MAX / 60;
551            if mins > MAX_MINUTE {
552                return None;
553            }
554            Some(core::time::Duration::from_secs(mins * 60))
555        }
556
557        if self.sign.is_negative() {
558            return Err(Error::from(E::NotAllowedNegative));
559        }
560
561        let (min, max) = self.get_min_max_units()?;
562        if max > Unit::Hour {
563            return Err(Error::from(E::NotAllowedCalendarUnit { unit: max }));
564        }
565
566        let mut sdur = core::time::Duration::ZERO;
567        if self.values[Unit::Hour.as_usize()] != 0 {
568            let value = self.values[Unit::Hour.as_usize()];
569            sdur = try_from_hours(value)
570                .and_then(|nanos| sdur.checked_add(nanos))
571                .ok_or(E::OverflowForUnit { unit: Unit::Hour })?;
572        }
573        if self.values[Unit::Minute.as_usize()] != 0 {
574            let value = self.values[Unit::Minute.as_usize()];
575            sdur = try_from_mins(value)
576                .and_then(|nanos| sdur.checked_add(nanos))
577                .ok_or(E::OverflowForUnit { unit: Unit::Minute })?;
578        }
579        if self.values[Unit::Second.as_usize()] != 0 {
580            let value = self.values[Unit::Second.as_usize()];
581            sdur = core::time::Duration::from_secs(value)
582                .checked_add(sdur)
583                .ok_or(E::OverflowForUnit { unit: Unit::Second })?;
584        }
585        if self.values[Unit::Millisecond.as_usize()] != 0 {
586            let value = self.values[Unit::Millisecond.as_usize()];
587            sdur = core::time::Duration::from_millis(value)
588                .checked_add(sdur)
589                .ok_or(E::OverflowForUnit { unit: Unit::Millisecond })?;
590        }
591        if self.values[Unit::Microsecond.as_usize()] != 0 {
592            let value = self.values[Unit::Microsecond.as_usize()];
593            sdur = core::time::Duration::from_micros(value)
594                .checked_add(sdur)
595                .ok_or(E::OverflowForUnit { unit: Unit::Microsecond })?;
596        }
597        if self.values[Unit::Nanosecond.as_usize()] != 0 {
598            let value = self.values[Unit::Nanosecond.as_usize()];
599            sdur = core::time::Duration::from_nanos(value)
600                .checked_add(sdur)
601                .ok_or(E::OverflowForUnit { unit: Unit::Nanosecond })?;
602        }
603
604        if let Some(fraction) = self.get_fraction()? {
605            sdur = sdur
606                .checked_add(
607                    fractional_duration(min, fraction)?.unsigned_abs(),
608                )
609                .ok_or(E::OverflowForUnitFractional { unit: Unit::Hour })?;
610        }
611
612        Ok(sdur)
613    }
614
615    /// Returns the minimum unit set.
616    ///
617    /// This only returns `None` when no units have been set.
618    pub(crate) fn get_min(&self) -> Option<Unit> {
619        self.min
620    }
621
622    /// Returns the minimum and maximum units set.
623    ///
624    /// This returns an error if no units were set. (Since this means there
625    /// were no parsed duration components.)
626    fn get_min_max_units(&self) -> Result<(Unit, Unit), Error> {
627        let (Some(min), Some(max)) = (self.min, self.max) else {
628            return Err(Error::from(E::EmptyDuration));
629        };
630        Ok((min, max))
631    }
632
633    /// Returns the corresponding unit value using the set signed-ness.
634    #[cfg_attr(feature = "perf-inline", inline(always))]
635    fn get_unit_value(&self, unit: Unit) -> Result<i64, Error> {
636        const I64_MIN_ABS: u64 = i64::MIN.unsigned_abs();
637
638        #[cold]
639        #[inline(never)]
640        fn general(unit: Unit, value: u64, sign: Sign) -> Result<i64, Error> {
641            // As a weird special case, when we need to represent i64::MIN,
642            // we'll have a unit value of `|i64::MIN|` as a `u64`. We can't
643            // convert that to a positive `i64` first, since it will overflow.
644            if sign.is_negative() && value == I64_MIN_ABS {
645                return Ok(i64::MIN);
646            }
647            // Otherwise, if a conversion to `i64` fails, then that failure
648            // is correct.
649            let mut value = i64::try_from(value)
650                .map_err(|_| E::SignedOverflowForUnit { unit })?;
651            if sign.is_negative() {
652                value = value
653                    .checked_neg()
654                    .ok_or(E::SignedOverflowForUnit { unit })?;
655            }
656            Ok(value)
657        }
658
659        let sign = self.get_sign();
660        let value = self.values[unit.as_usize()];
661        if value >= I64_MIN_ABS {
662            return general(unit, value, sign);
663        }
664        let mut value = value as i64;
665        if sign.is_negative() {
666            value = -value;
667        }
668        Ok(value)
669    }
670
671    /// Returns the fraction using the set signed-ness.
672    ///
673    /// This returns `None` when no fraction has been set.
674    fn get_fraction(&self) -> Result<Option<i32>, Error> {
675        let Some(fraction) = self.fraction else {
676            return Ok(None);
677        };
678        // OK because `set_fraction` guarantees `0..=999_999_999`.
679        let mut fraction = fraction as i32;
680        if self.get_sign().is_negative() {
681            // OK because `set_fraction` guarantees `0..=999_999_999`.
682            fraction = -fraction;
683        }
684        Ok(Some(fraction))
685    }
686
687    /// Returns the sign that should be applied to each individual unit.
688    fn get_sign(&self) -> Sign {
689        if self.any_non_zero_units {
690            self.sign
691        } else {
692            Sign::Zero
693        }
694    }
695}
696
697/// Parses an optional fractional number from the start of `input`.
698///
699/// If `input` does not begin with a `.` (or a `,`), then this returns `None`
700/// and no input is consumed. Otherwise, up to 9 ASCII digits are parsed after
701/// the decimal separator.
702///
703/// While this is most typically used to parse the fractional component of
704/// second units, it is also used to parse the fractional component of hours or
705/// minutes in ISO 8601 duration parsing, and milliseconds and microseconds in
706/// the "friendly" duration format. The return type in that case is obviously a
707/// misnomer, but the range of possible values is still correct. (That is, the
708/// fractional component of an hour is still limited to 9 decimal places per
709/// the Temporal spec.)
710///
711/// The number returned is guaranteed to be in the range `0..=999_999_999`.
712#[cfg_attr(feature = "perf-inline", inline(always))]
713pub(crate) fn parse_temporal_fraction<'i>(
714    input: &'i [u8],
715) -> Result<Parsed<'i, Option<u32>>, Error> {
716    // TimeFraction :::
717    //   TemporalDecimalFraction
718    //
719    // TemporalDecimalFraction :::
720    //   TemporalDecimalSeparator DecimalDigit
721    //   TemporalDecimalSeparator DecimalDigit DecimalDigit
722    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
723    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
724    //                            DecimalDigit
725    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
726    //                            DecimalDigit DecimalDigit
727    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
728    //                            DecimalDigit DecimalDigit DecimalDigit
729    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
730    //                            DecimalDigit DecimalDigit DecimalDigit
731    //                            DecimalDigit
732    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
733    //                            DecimalDigit DecimalDigit DecimalDigit
734    //                            DecimalDigit DecimalDigit
735    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
736    //                            DecimalDigit DecimalDigit DecimalDigit
737    //                            DecimalDigit DecimalDigit DecimalDigit
738    //
739    // TemporalDecimalSeparator ::: one of
740    //   . ,
741    //
742    // DecimalDigit :: one of
743    //   0 1 2 3 4 5 6 7 8 9
744
745    #[inline(never)]
746    fn imp<'i>(mut input: &'i [u8]) -> Result<Parsed<'i, Option<u32>>, Error> {
747        let mkdigits = parse::slicer(input);
748        while mkdigits(input).len() <= 8
749            && input.first().map_or(false, u8::is_ascii_digit)
750        {
751            input = &input[1..];
752        }
753        let digits = mkdigits(input);
754        if digits.is_empty() {
755            return Err(Error::from(E::MissingFractionalDigits));
756        }
757        // I believe this error can never happen, since we know we have no more
758        // than 9 ASCII digits. Any sequence of 9 ASCII digits can be parsed
759        // into an `i64`.
760        let nanoseconds =
761            parse::fraction(digits).context(E::InvalidFraction)?;
762        // OK because parsing is forcefully limited to 9 digits,
763        // which can never be greater than `999_999_99`,
764        // which is less than `u32::MAX`.
765        let nanoseconds = nanoseconds as u32;
766        Ok(Parsed { value: Some(nanoseconds), input })
767    }
768
769    if input.is_empty() || (input[0] != b'.' && input[0] != b',') {
770        return Ok(Parsed { value: None, input });
771    }
772    imp(&input[1..])
773}
774
775/// This routine returns a span based on the given unit and value with
776/// fractional time applied to it.
777///
778/// For example, given a span like `P1dT1.5h`, the `unit` would be
779/// `Unit::Hour`, the `value` would be `1` and the `fraction` would be
780/// `500_000_000`. The span given would just be `1d`. The span returned would
781/// be `P1dT1h30m`.
782///
783/// Note that `fraction` can be a fractional hour, minute, second, millisecond
784/// or microsecond (even though its type suggests its only a fraction of a
785/// second). When milliseconds or microseconds, the given fraction has any
786/// sub-nanosecond precision truncated.
787///
788/// # Errors
789///
790/// This can error if the resulting units would be too large for the limits on
791/// a `span`. This also errors if `unit` is not `Hour`, `Minute`, `Second`,
792/// `Millisecond` or `Microsecond`.
793#[inline(never)]
794fn fractional_time_to_span(
795    unit: Unit,
796    value: i64,
797    fraction: i32,
798    mut span: Span,
799) -> Result<Span, Error> {
800    const MAX_HOURS: i64 = b::SpanHours::MAX as i64;
801    const MAX_MINS: i64 = b::SpanMinutes::MAX;
802    const MAX_SECS: i64 = b::SpanSeconds::MAX;
803    const MAX_MILLIS: i128 = b::SpanMilliseconds::MAX as i128;
804    const MAX_MICROS: i128 = b::SpanMicroseconds::MAX as i128;
805    const MIN_HOURS: i64 = b::SpanHours::MIN as i64;
806    const MIN_MINS: i64 = b::SpanMinutes::MIN;
807    const MIN_SECS: i64 = b::SpanSeconds::MIN;
808    const MIN_MILLIS: i128 = b::SpanMilliseconds::MIN as i128;
809    const MIN_MICROS: i128 = b::SpanMicroseconds::MIN as i128;
810
811    // We switch everything over to nanoseconds and then divy that up as
812    // appropriate. In general, we always create a balanced span, but there
813    // are some cases where we can't. For example, if one serializes a span
814    // with both the maximum number of seconds and the maximum number of
815    // milliseconds, then this just can't be balanced due to the limits on
816    // each of the units. When this kind of span is serialized to a string,
817    // it results in a second value that is actually bigger than the maximum
818    // allowed number of seconds in a span. So here, we have to reverse that
819    // operation and spread the seconds over smaller units. This in turn
820    // creates an unbalanced span. Annoying.
821    //
822    // The above is why we have `if unit_value > MAX { <do adjustments> }` in
823    // the balancing code below. Basically, if we overshoot our limit, we back
824    // out anything over the limit and carry it over to the lesser units. If
825    // our value is truly too big, then the final call to set nanoseconds will
826    // fail.
827    let mut sdur = fractional_time_to_duration(unit, value, fraction)?;
828
829    if unit >= Unit::Hour && !sdur.is_zero() {
830        let (mut hours, rem) = sdur.as_hours_with_remainder();
831        sdur = rem;
832        if hours > MAX_HOURS {
833            sdur += SignedDuration::from_hours(hours - MAX_HOURS);
834            hours = MAX_HOURS;
835        } else if hours < MIN_HOURS {
836            sdur += SignedDuration::from_hours(hours - MIN_HOURS);
837            hours = MIN_HOURS;
838        }
839        // OK because we just checked that our units are in range.
840        span = span.hours(hours);
841    }
842    if unit >= Unit::Minute && !sdur.is_zero() {
843        let (mut mins, rem) = sdur.as_mins_with_remainder();
844        sdur = rem;
845        if mins > MAX_MINS {
846            sdur += SignedDuration::from_mins(mins - MAX_MINS);
847            mins = MAX_MINS;
848        } else if mins < MIN_MINS {
849            sdur += SignedDuration::from_mins(mins - MIN_MINS);
850            mins = MIN_MINS;
851        }
852        // OK because we just checked that our units are in range.
853        span = span.minutes(mins);
854    }
855    if unit >= Unit::Second && !sdur.is_zero() {
856        let (mut secs, rem) = sdur.as_secs_with_remainder();
857        sdur = rem;
858        if secs > MAX_SECS {
859            sdur += SignedDuration::from_secs(secs - MAX_SECS);
860            secs = MAX_SECS;
861        } else if secs < MIN_SECS {
862            sdur += SignedDuration::from_secs(secs - MIN_SECS);
863            secs = MIN_SECS;
864        }
865        // OK because we just checked that our units are in range.
866        span = span.seconds(secs);
867    }
868    if unit >= Unit::Millisecond && !sdur.is_zero() {
869        let (mut millis, rem) = sdur.as_millis_with_remainder();
870        sdur = rem;
871        if millis > MAX_MILLIS {
872            sdur += SignedDuration::from_millis_i128(millis - MAX_MILLIS);
873            millis = MAX_MILLIS;
874        } else if millis < MIN_MILLIS {
875            sdur += SignedDuration::from_millis_i128(millis - MIN_MILLIS);
876            millis = MIN_MILLIS;
877        }
878        // OK because we just checked that our units are in range.
879        span = span.milliseconds(i64::try_from(millis).unwrap());
880    }
881    if unit >= Unit::Microsecond && !sdur.is_zero() {
882        let (mut micros, rem) = sdur.as_micros_with_remainder();
883        sdur = rem;
884        if micros > MAX_MICROS {
885            sdur += SignedDuration::from_micros_i128(micros - MAX_MICROS);
886            micros = MAX_MICROS;
887        } else if micros < MIN_MICROS {
888            sdur += SignedDuration::from_micros_i128(micros - MIN_MICROS);
889            micros = MIN_MICROS;
890        }
891        // OK because we just checked that our units are in range.
892        span = span.microseconds(i64::try_from(micros).unwrap());
893    }
894    if !sdur.is_zero() {
895        let nanos = sdur.as_nanos();
896        let nanos64 =
897            i64::try_from(nanos).map_err(|_| E::InvalidFractionNanos)?;
898        span =
899            span.try_nanoseconds(nanos64).context(E::InvalidFractionNanos)?;
900    }
901
902    Ok(span)
903}
904
905/// Like `fractional_time_to_span`, but just converts the fraction of the given
906/// unit to a signed duration.
907///
908/// Since a signed duration doesn't keep track of individual units, there is
909/// no loss of fidelity between it and ISO 8601 durations like there is for
910/// `Span`.
911///
912/// Note that `fraction` can be a fractional hour, minute, second, millisecond
913/// or microsecond (even though its type suggests it's only a fraction of a
914/// second). When milliseconds or microseconds, the given fraction has any
915/// sub-nanosecond precision truncated.
916///
917/// # Errors
918///
919/// This returns an error if `unit` is not `Hour`, `Minute`, `Second`,
920/// `Millisecond` or `Microsecond`.
921#[inline(never)]
922fn fractional_time_to_duration(
923    unit: Unit,
924    value: i64,
925    fraction: i32,
926) -> Result<SignedDuration, Error> {
927    let sdur = duration_unit_value(unit, value)?;
928    let fraction_dur = fractional_duration(unit, fraction)?;
929    Ok(sdur
930        .checked_add(fraction_dur)
931        .ok_or(E::OverflowForUnitFractional { unit })?)
932}
933
934/// Converts the fraction of the given unit to a signed duration.
935///
936/// Since a signed duration doesn't keep track of individual units, there is
937/// no loss of fidelity between it and ISO 8601 durations like there is for
938/// `Span`. Thus, we can do something far less complicated.
939///
940/// # Panics
941///
942/// When `fraction` isn't in the range `-999_999_999..=999_999_999`.
943///
944/// # Errors
945///
946/// This returns an error if `unit` is not `Hour`, `Minute`, `Second`,
947/// `Millisecond` or `Microsecond`.
948#[inline(never)]
949fn fractional_duration(
950    unit: Unit,
951    fraction: i32,
952) -> Result<SignedDuration, Error> {
953    let fraction = i64::from(fraction);
954    let nanos = match unit {
955        Unit::Hour => fraction * b::SECS_PER_HOUR,
956        Unit::Minute => fraction * b::SECS_PER_MIN,
957        Unit::Second => fraction,
958        Unit::Millisecond => fraction / b::NANOS_PER_MICRO,
959        Unit::Microsecond => fraction / b::NANOS_PER_MILLI,
960        unit => {
961            return Err(Error::from(E::NotAllowedFractionalUnit {
962                found: unit,
963            }));
964        }
965    };
966    Ok(SignedDuration::from_nanos(nanos))
967}
968
969/// Returns the given parsed value, interpreted as the given unit, as a
970/// `SignedDuration`.
971///
972/// If the given unit is not supported for signed durations (i.e., calendar
973/// units), or if converting the given value to a `SignedDuration` for the
974/// given units overflows, then an error is returned.
975#[cfg_attr(feature = "perf-inline", inline(always))]
976fn duration_unit_value(
977    unit: Unit,
978    value: i64,
979) -> Result<SignedDuration, Error> {
980    // Convert our parsed unit into a number of nanoseconds.
981    //
982    // Note also that overflow isn't possible here for units less than minutes,
983    // since a `SignedDuration` supports all `i64` second values.
984    let sdur = match unit {
985        Unit::Hour => {
986            let seconds = value
987                .checked_mul(b::SECS_PER_HOUR)
988                .ok_or(E::ConversionToSecondsFailed { unit: Unit::Hour })?;
989            SignedDuration::from_secs(seconds)
990        }
991        Unit::Minute => {
992            let seconds = value
993                .checked_mul(b::SECS_PER_MIN)
994                .ok_or(E::ConversionToSecondsFailed { unit: Unit::Minute })?;
995            SignedDuration::from_secs(seconds)
996        }
997        Unit::Second => SignedDuration::from_secs(value),
998        Unit::Millisecond => SignedDuration::from_millis(value),
999        Unit::Microsecond => SignedDuration::from_micros(value),
1000        Unit::Nanosecond => SignedDuration::from_nanos(value),
1001        unsupported => {
1002            return Err(Error::from(E::NotAllowedCalendarUnit {
1003                unit: unsupported,
1004            }))
1005        }
1006    };
1007    Ok(sdur)
1008}