Coverage Report

Created: 2026-09-28 07:06

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