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}