1use crate::{
2 error::{err, ErrorContext},
3 fmt::Parsed,
4 util::{c::Sign, escape, parse, t},
5 Error, SignedDuration, Span, Unit,
6};
7
8#[derive(Clone, Copy, Debug)]
20pub(crate) struct DecimalFormatter {
21 force_sign: Option<bool>,
22 minimum_digits: u8,
23 padding_byte: u8,
24}
25
26impl DecimalFormatter {
27 pub(crate) const fn new() -> DecimalFormatter {
29 DecimalFormatter {
30 force_sign: None,
31 minimum_digits: 0,
32 padding_byte: b'0',
33 }
34 }
35
36 #[cfg_attr(feature = "perf-inline", inline(always))]
39 pub(crate) const fn format_signed(&self, value: i64) -> Decimal {
40 Decimal::signed(self, value)
41 }
42
43 #[cfg_attr(feature = "perf-inline", inline(always))]
46 pub(crate) const fn format_unsigned(&self, value: u64) -> Decimal {
47 Decimal::unsigned(self, value)
48 }
49
50 #[cfg(test)]
58 pub(crate) const fn force_sign(
59 self,
60 zero_is_positive: bool,
61 ) -> DecimalFormatter {
62 DecimalFormatter { force_sign: Some(zero_is_positive), ..self }
63 }
64
65 pub(crate) const fn padding(self, mut digits: u8) -> DecimalFormatter {
73 if digits > Decimal::MAX_I64_DIGITS {
74 digits = Decimal::MAX_I64_DIGITS;
75 }
76 DecimalFormatter { minimum_digits: digits, ..self }
77 }
78
79 pub(crate) const fn padding_byte(self, byte: u8) -> DecimalFormatter {
83 DecimalFormatter { padding_byte: byte, ..self }
84 }
85
86 const fn get_signed_minimum_digits(&self) -> u8 {
88 if self.minimum_digits <= Decimal::MAX_I64_DIGITS {
89 self.minimum_digits
90 } else {
91 Decimal::MAX_I64_DIGITS
92 }
93 }
94
95 const fn get_unsigned_minimum_digits(&self) -> u8 {
97 if self.minimum_digits <= Decimal::MAX_U64_DIGITS {
98 self.minimum_digits
99 } else {
100 Decimal::MAX_U64_DIGITS
101 }
102 }
103}
104
105impl Default for DecimalFormatter {
106 fn default() -> DecimalFormatter {
107 DecimalFormatter::new()
108 }
109}
110
111#[derive(Debug)]
113pub(crate) struct Decimal {
114 buf: [u8; Self::MAX_LEN as usize],
115 start: u8,
116 end: u8,
117}
118
119impl Decimal {
120 const MAX_LEN: u8 = 20;
123 const MAX_I64_DIGITS: u8 = 19;
125 const MAX_U64_DIGITS: u8 = 20;
127
128 #[cfg_attr(feature = "perf-inline", inline(always))]
131 const fn unsigned(
132 formatter: &DecimalFormatter,
133 mut value: u64,
134 ) -> Decimal {
135 let mut decimal = Decimal {
136 buf: [0; Self::MAX_LEN as usize],
137 start: Self::MAX_LEN,
138 end: Self::MAX_LEN,
139 };
140 loop {
141 decimal.start -= 1;
142
143 let digit = (value % 10) as u8;
144 value /= 10;
145 decimal.buf[decimal.start as usize] = b'0' + digit;
146 if value == 0 {
147 break;
148 }
149 }
150
151 while decimal.len() < formatter.get_unsigned_minimum_digits() {
152 decimal.start -= 1;
153 decimal.buf[decimal.start as usize] = formatter.padding_byte;
154 }
155 decimal
156 }
157
158 #[cfg_attr(feature = "perf-inline", inline(always))]
161 const fn signed(formatter: &DecimalFormatter, mut value: i64) -> Decimal {
162 if value >= 0 && formatter.force_sign.is_none() {
164 let mut decimal = Decimal {
165 buf: [0; Self::MAX_LEN as usize],
166 start: Self::MAX_LEN,
167 end: Self::MAX_LEN,
168 };
169 loop {
170 decimal.start -= 1;
171
172 let digit = (value % 10) as u8;
173 value /= 10;
174 decimal.buf[decimal.start as usize] = b'0' + digit;
175 if value == 0 {
176 break;
177 }
178 }
179
180 while decimal.len() < formatter.get_signed_minimum_digits() {
181 decimal.start -= 1;
182 decimal.buf[decimal.start as usize] = formatter.padding_byte;
183 }
184 return decimal;
185 }
186 Decimal::signed_cold(formatter, value)
187 }
188
189 #[cold]
190 #[inline(never)]
191 const fn signed_cold(formatter: &DecimalFormatter, value: i64) -> Decimal {
192 let sign = value.signum();
193 let Some(mut value) = value.checked_abs() else {
194 let buf = [
195 b'-', b'9', b'2', b'2', b'3', b'3', b'7', b'2', b'0', b'3',
196 b'6', b'8', b'5', b'4', b'7', b'7', b'5', b'8', b'0', b'8',
197 ];
198 return Decimal { buf, start: 0, end: Self::MAX_LEN };
199 };
200 let mut decimal = Decimal {
201 buf: [0; Self::MAX_LEN as usize],
202 start: Self::MAX_LEN,
203 end: Self::MAX_LEN,
204 };
205 loop {
206 decimal.start -= 1;
207
208 let digit = (value % 10) as u8;
209 value /= 10;
210 decimal.buf[decimal.start as usize] = b'0' + digit;
211 if value == 0 {
212 break;
213 }
214 }
215 while decimal.len() < formatter.get_signed_minimum_digits() {
216 decimal.start -= 1;
217 decimal.buf[decimal.start as usize] = formatter.padding_byte;
218 }
219 if sign < 0 {
220 decimal.start -= 1;
221 decimal.buf[decimal.start as usize] = b'-';
222 } else if let Some(zero_is_positive) = formatter.force_sign {
223 let ascii_sign =
224 if sign > 0 || zero_is_positive { b'+' } else { b'-' };
225 decimal.start -= 1;
226 decimal.buf[decimal.start as usize] = ascii_sign;
227 }
228 decimal
229 }
230
231 #[inline]
234 const fn len(&self) -> u8 {
235 self.end - self.start
236 }
237
238 #[inline]
242 fn as_bytes(&self) -> &[u8] {
243 &self.buf[usize::from(self.start)..usize::from(self.end)]
244 }
245
246 #[inline]
248 pub(crate) fn as_str(&self) -> &str {
249 unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
253 }
254}
255
256#[derive(Clone, Copy, Debug)]
262pub(crate) struct FractionalFormatter {
263 precision: Option<u8>,
264}
265
266impl FractionalFormatter {
267 pub(crate) const fn new() -> FractionalFormatter {
269 FractionalFormatter { precision: None }
270 }
271
272 pub(crate) const fn format(&self, value: u32) -> Fractional {
275 Fractional::new(self, value)
276 }
277
278 pub(crate) const fn precision(
285 self,
286 precision: Option<u8>,
287 ) -> FractionalFormatter {
288 let precision = match precision {
289 None => None,
290 Some(p) if p > 9 => Some(9),
291 Some(p) => Some(p),
292 };
293 FractionalFormatter { precision, ..self }
294 }
295
296 pub(crate) fn will_write_digits(self, value: u32) -> bool {
302 self.precision.map_or_else(|| value != 0, |p| p > 0)
303 }
304
305 pub(crate) fn has_non_zero_fixed_precision(self) -> bool {
312 self.precision.map_or(false, |p| p > 0)
313 }
314
315 pub(crate) fn has_zero_fixed_precision(self) -> bool {
318 self.precision.map_or(false, |p| p == 0)
319 }
320}
321
322#[derive(Debug)]
324pub(crate) struct Fractional {
325 buf: [u8; Self::MAX_LEN as usize],
326 end: u8,
327}
328
329impl Fractional {
330 const MAX_LEN: u8 = 9;
332
333 pub(crate) const fn new(
344 formatter: &FractionalFormatter,
345 mut value: u32,
346 ) -> Fractional {
347 assert!(value <= 999_999_999);
348 let mut fractional = Fractional {
349 buf: [b'0'; Self::MAX_LEN as usize],
350 end: Self::MAX_LEN,
351 };
352 let mut i = 9;
353 loop {
354 i -= 1;
355
356 let digit = (value % 10) as u8;
357 value /= 10;
358 fractional.buf[i] += digit;
359 if value == 0 {
360 break;
361 }
362 }
363 if let Some(precision) = formatter.precision {
364 fractional.end = precision;
365 } else {
366 while fractional.end > 0
367 && fractional.buf[fractional.end as usize - 1] == b'0'
368 {
369 fractional.end -= 1;
370 }
371 }
372 fractional
373 }
374
375 pub(crate) fn as_bytes(&self) -> &[u8] {
380 &self.buf[..usize::from(self.end)]
381 }
382
383 pub(crate) fn as_str(&self) -> &str {
386 unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
390 }
391}
392
393#[derive(Debug, Default)]
417pub(crate) struct DurationUnits {
418 values: [u64; 10],
421 fraction: Option<u32>,
424 sign: Sign,
429 min: Option<Unit>,
431 max: Option<Unit>,
433 any_non_zero_units: bool,
435}
436
437impl DurationUnits {
438 #[cfg_attr(feature = "perf-inline", inline(always))]
456 pub(crate) fn set_unit_value(
457 &mut self,
458 unit: Unit,
459 value: u64,
460 ) -> Result<(), Error> {
461 assert!(self.fraction.is_none());
462
463 if let Some(min) = self.min {
464 if min <= unit {
465 return Err(err!(
466 "found value {value:?} with unit {unit} \
467 after unit {prev_unit}, but units must be \
468 written from largest to smallest \
469 (and they can't be repeated)",
470 unit = unit.singular(),
471 prev_unit = min.singular(),
472 ));
473 }
474 }
475 self.min = Some(unit);
478 if self.max.is_none() {
481 self.max = Some(unit);
482 }
483 self.values[unit.as_usize()] = value;
484 self.any_non_zero_units = self.any_non_zero_units || value != 0;
485 Ok(())
486 }
487
488 pub(crate) fn set_hms(
498 &mut self,
499 hours: u64,
500 minutes: u64,
501 seconds: u64,
502 fraction: Option<u32>,
503 ) -> Result<(), Error> {
504 if let Some(min) = self.min {
505 if min <= Unit::Hour {
506 return Err(err!(
507 "found `HH:MM:SS` after unit {min}, \
508 but `HH:MM:SS` can only appear after \
509 years, months, weeks or days",
510 min = min.singular(),
511 ));
512 }
513 }
514 self.set_unit_value(Unit::Hour, hours)?;
515 self.set_unit_value(Unit::Minute, minutes)?;
516 self.set_unit_value(Unit::Second, seconds)?;
517 if let Some(fraction) = fraction {
518 self.set_fraction(fraction)?;
519 }
520 Ok(())
521 }
522
523 pub(crate) fn set_fraction(&mut self, fraction: u32) -> Result<(), Error> {
541 assert!(fraction <= 999_999_999);
542 if self.min == Some(Unit::Nanosecond) {
543 return Err(err!("fractional nanoseconds are not supported"));
544 }
545 if let Some(min) = self.min {
546 if min > Unit::Hour {
547 return Err(err!(
548 "fractional {plural} are not supported",
549 plural = min.plural()
550 ));
551 }
552 }
553 self.fraction = Some(fraction);
554 Ok(())
555 }
556
557 pub(crate) fn set_sign(&mut self, sign: Sign) {
566 self.sign = sign;
567 }
568
569 #[cfg_attr(feature = "perf-inline", inline(always))]
579 pub(crate) fn to_span(&self) -> Result<Span, Error> {
580 const LIMIT: u64 = t::SpanYears::MAX_SELF.get_unchecked() as u64;
593
594 if self.fraction.is_some()
597 || self.values.iter().any(|&value| value > LIMIT)
598 || self.max.is_none()
600 {
601 return self.to_span_general();
602 }
603
604 let mut span = Span::new();
605
606 let years = self.values[Unit::Year.as_usize()] as i16;
607 let months = self.values[Unit::Month.as_usize()] as i32;
608 let weeks = self.values[Unit::Week.as_usize()] as i32;
609 let days = self.values[Unit::Day.as_usize()] as i32;
610 let hours = self.values[Unit::Hour.as_usize()] as i32;
611 let mins = self.values[Unit::Minute.as_usize()] as i64;
612 let secs = self.values[Unit::Second.as_usize()] as i64;
613 let millis = self.values[Unit::Millisecond.as_usize()] as i64;
614 let micros = self.values[Unit::Microsecond.as_usize()] as i64;
615 let nanos = self.values[Unit::Nanosecond.as_usize()] as i64;
616
617 span = span.years_unchecked(years);
618 span = span.months_unchecked(months);
619 span = span.weeks_unchecked(weeks);
620 span = span.days_unchecked(days);
621 span = span.hours_unchecked(hours);
622 span = span.minutes_unchecked(mins);
623 span = span.seconds_unchecked(secs);
624 span = span.milliseconds_unchecked(millis);
625 span = span.microseconds_unchecked(micros);
626 span = span.nanoseconds_unchecked(nanos);
627
628 span = span.sign_unchecked(self.get_sign().as_ranged_integer());
632
633 Ok(span)
634 }
635
636 #[cold]
643 #[inline(never)]
644 fn to_span_general(&self) -> Result<Span, Error> {
645 fn error_context(unit: Unit, value: i64) -> Error {
646 err!(
647 "failed to set value {value:?} as {unit} unit on span",
648 unit = unit.singular(),
649 )
650 }
651
652 #[cfg_attr(feature = "perf-inline", inline(always))]
653 fn set_time_unit(
654 unit: Unit,
655 value: i64,
656 span: Span,
657 set: impl FnOnce(Span) -> Result<Span, Error>,
658 ) -> Result<Span, Error> {
659 #[cold]
660 #[inline(never)]
661 fn fractional_fallback(
662 err: Error,
663 unit: Unit,
664 value: i64,
665 span: Span,
666 ) -> Result<Span, Error> {
667 if unit > Unit::Hour || unit == Unit::Nanosecond {
671 Err(err)
672 } else {
673 fractional_time_to_span(unit, value, 0, span)
680 }
681 }
682
683 set(span)
684 .or_else(|err| fractional_fallback(err, unit, value, span))
685 .with_context(|| error_context(unit, value))
686 }
687
688 let (min, _) = self.get_min_max_units()?;
689 let mut span = Span::new();
690
691 if self.values[Unit::Year.as_usize()] != 0 {
692 let value = self.get_unit_value(Unit::Year)?;
693 span = span
694 .try_years(value)
695 .with_context(|| error_context(Unit::Year, value))?;
696 }
697 if self.values[Unit::Month.as_usize()] != 0 {
698 let value = self.get_unit_value(Unit::Month)?;
699 span = span
700 .try_months(value)
701 .with_context(|| error_context(Unit::Month, value))?;
702 }
703 if self.values[Unit::Week.as_usize()] != 0 {
704 let value = self.get_unit_value(Unit::Week)?;
705 span = span
706 .try_weeks(value)
707 .with_context(|| error_context(Unit::Week, value))?;
708 }
709 if self.values[Unit::Day.as_usize()] != 0 {
710 let value = self.get_unit_value(Unit::Day)?;
711 span = span
712 .try_days(value)
713 .with_context(|| error_context(Unit::Day, value))?;
714 }
715 if self.values[Unit::Hour.as_usize()] != 0 {
716 let value = self.get_unit_value(Unit::Hour)?;
717 span = set_time_unit(Unit::Hour, value, span, |span| {
718 span.try_hours(value)
719 })?;
720 }
721 if self.values[Unit::Minute.as_usize()] != 0 {
722 let value = self.get_unit_value(Unit::Minute)?;
723 span = set_time_unit(Unit::Minute, value, span, |span| {
724 span.try_minutes(value)
725 })?;
726 }
727 if self.values[Unit::Second.as_usize()] != 0 {
728 let value = self.get_unit_value(Unit::Second)?;
729 span = set_time_unit(Unit::Second, value, span, |span| {
730 span.try_seconds(value)
731 })?;
732 }
733 if self.values[Unit::Millisecond.as_usize()] != 0 {
734 let value = self.get_unit_value(Unit::Millisecond)?;
735 span = set_time_unit(Unit::Millisecond, value, span, |span| {
736 span.try_milliseconds(value)
737 })?;
738 }
739 if self.values[Unit::Microsecond.as_usize()] != 0 {
740 let value = self.get_unit_value(Unit::Microsecond)?;
741 span = set_time_unit(Unit::Microsecond, value, span, |span| {
742 span.try_microseconds(value)
743 })?;
744 }
745 if self.values[Unit::Nanosecond.as_usize()] != 0 {
746 let value = self.get_unit_value(Unit::Nanosecond)?;
747 span = set_time_unit(Unit::Nanosecond, value, span, |span| {
748 span.try_nanoseconds(value)
749 })?;
750 }
751
752 if let Some(fraction) = self.get_fraction()? {
753 let value = self.get_unit_value(min)?;
754 span = fractional_time_to_span(min, value, fraction, span)?;
755 }
756
757 Ok(span)
758 }
759
760 #[cfg_attr(feature = "perf-inline", inline(always))]
771 pub(crate) fn to_signed_duration(&self) -> Result<SignedDuration, Error> {
772 const LIMIT: u64 = 999;
786
787 if self.fraction.is_some()
788 || self.values[..Unit::Day.as_usize()]
789 .iter()
790 .any(|&value| value > LIMIT)
791 || self.max.map_or(true, |max| max > Unit::Hour)
792 {
793 return self.to_signed_duration_general();
794 }
795
796 let hours = self.values[Unit::Hour.as_usize()] as i64;
797 let mins = self.values[Unit::Minute.as_usize()] as i64;
798 let secs = self.values[Unit::Second.as_usize()] as i64;
799 let millis = self.values[Unit::Millisecond.as_usize()] as i32;
800 let micros = self.values[Unit::Microsecond.as_usize()] as i32;
801 let nanos = self.values[Unit::Nanosecond.as_usize()] as i32;
802
803 let total_secs = (hours * 3600) + (mins * 60) + secs;
804 let total_nanos = (millis * 1_000_000) + (micros * 1_000) + nanos;
805 let mut sdur =
806 SignedDuration::new_without_nano_overflow(total_secs, total_nanos);
807 if self.get_sign().is_negative() {
808 sdur = -sdur;
809 }
810
811 Ok(sdur)
812 }
813
814 #[cold]
821 #[inline(never)]
822 fn to_signed_duration_general(&self) -> Result<SignedDuration, Error> {
823 let (min, max) = self.get_min_max_units()?;
824 if max > Unit::Hour {
825 return Err(err!(
826 "parsing {unit} units into a `SignedDuration` is not supported \
827 (perhaps try parsing into a `Span` instead)",
828 unit = max.singular(),
829 ));
830 }
831
832 let mut sdur = SignedDuration::ZERO;
833 if self.values[Unit::Hour.as_usize()] != 0 {
834 let value = self.get_unit_value(Unit::Hour)?;
835 sdur = SignedDuration::try_from_hours(value)
836 .and_then(|nanos| sdur.checked_add(nanos))
837 .ok_or_else(|| {
838 err!(
839 "accumulated `SignedDuration` of `{sdur:?}` \
840 overflowed when adding {value} of unit {unit}",
841 unit = Unit::Hour.singular(),
842 )
843 })?;
844 }
845 if self.values[Unit::Minute.as_usize()] != 0 {
846 let value = self.get_unit_value(Unit::Minute)?;
847 sdur = SignedDuration::try_from_mins(value)
848 .and_then(|nanos| sdur.checked_add(nanos))
849 .ok_or_else(|| {
850 err!(
851 "accumulated `SignedDuration` of `{sdur:?}` \
852 overflowed when adding {value} of unit {unit}",
853 unit = Unit::Minute.singular(),
854 )
855 })?;
856 }
857 if self.values[Unit::Second.as_usize()] != 0 {
858 let value = self.get_unit_value(Unit::Second)?;
859 sdur = SignedDuration::from_secs(value)
860 .checked_add(sdur)
861 .ok_or_else(|| {
862 err!(
863 "accumulated `SignedDuration` of `{sdur:?}` \
864 overflowed when adding {value} of unit {unit}",
865 unit = Unit::Second.singular(),
866 )
867 })?;
868 }
869 if self.values[Unit::Millisecond.as_usize()] != 0 {
870 let value = self.get_unit_value(Unit::Millisecond)?;
871 sdur = SignedDuration::from_millis(value)
872 .checked_add(sdur)
873 .ok_or_else(|| {
874 err!(
875 "accumulated `SignedDuration` of `{sdur:?}` \
876 overflowed when adding {value} of unit {unit}",
877 unit = Unit::Millisecond.singular(),
878 )
879 })?;
880 }
881 if self.values[Unit::Microsecond.as_usize()] != 0 {
882 let value = self.get_unit_value(Unit::Microsecond)?;
883 sdur = SignedDuration::from_micros(value)
884 .checked_add(sdur)
885 .ok_or_else(|| {
886 err!(
887 "accumulated `SignedDuration` of `{sdur:?}` \
888 overflowed when adding {value} of unit {unit}",
889 unit = Unit::Microsecond.singular(),
890 )
891 })?;
892 }
893 if self.values[Unit::Nanosecond.as_usize()] != 0 {
894 let value = self.get_unit_value(Unit::Nanosecond)?;
895 sdur = SignedDuration::from_nanos(value)
896 .checked_add(sdur)
897 .ok_or_else(|| {
898 err!(
899 "accumulated `SignedDuration` of `{sdur:?}` \
900 overflowed when adding {value} of unit {unit}",
901 unit = Unit::Nanosecond.singular(),
902 )
903 })?;
904 }
905
906 if let Some(fraction) = self.get_fraction()? {
907 sdur = sdur
908 .checked_add(fractional_duration(min, fraction)?)
909 .ok_or_else(|| {
910 err!(
911 "accumulated `SignedDuration` of `{sdur:?}` \
912 overflowed when adding 0.{fraction} of unit {unit}",
913 unit = min.singular(),
914 )
915 })?;
916 }
917
918 Ok(sdur)
919 }
920
921 #[cfg_attr(feature = "perf-inline", inline(always))]
932 pub(crate) fn to_unsigned_duration(
933 &self,
934 ) -> Result<core::time::Duration, Error> {
935 const LIMIT: u64 = 999;
949
950 if self.fraction.is_some()
951 || self.values[..Unit::Day.as_usize()]
952 .iter()
953 .any(|&value| value > LIMIT)
954 || self.max.map_or(true, |max| max > Unit::Hour)
955 || self.sign.is_negative()
956 {
957 return self.to_unsigned_duration_general();
958 }
959
960 let hours = self.values[Unit::Hour.as_usize()];
961 let mins = self.values[Unit::Minute.as_usize()];
962 let secs = self.values[Unit::Second.as_usize()];
963 let millis = self.values[Unit::Millisecond.as_usize()] as u32;
964 let micros = self.values[Unit::Microsecond.as_usize()] as u32;
965 let nanos = self.values[Unit::Nanosecond.as_usize()] as u32;
966
967 let total_secs = (hours * 3600) + (mins * 60) + secs;
968 let total_nanos = (millis * 1_000_000) + (micros * 1_000) + nanos;
969 let sdur = core::time::Duration::new(total_secs, total_nanos);
970
971 Ok(sdur)
972 }
973
974 #[cold]
981 #[inline(never)]
982 fn to_unsigned_duration_general(
983 &self,
984 ) -> Result<core::time::Duration, Error> {
985 #[inline]
986 const fn try_from_hours(hours: u64) -> Option<core::time::Duration> {
987 const MAX_HOUR: u64 = u64::MAX / (60 * 60);
989 if hours > MAX_HOUR {
990 return None;
991 }
992 Some(core::time::Duration::from_secs(hours * 60 * 60))
993 }
994
995 #[inline]
996 const fn try_from_mins(mins: u64) -> Option<core::time::Duration> {
997 const MAX_MINUTE: u64 = u64::MAX / 60;
999 if mins > MAX_MINUTE {
1000 return None;
1001 }
1002 Some(core::time::Duration::from_secs(mins * 60))
1003 }
1004
1005 if self.sign.is_negative() {
1006 return Err(err!(
1007 "cannot parse negative duration into unsigned \
1008 `std::time::Duration`",
1009 ));
1010 }
1011
1012 let (min, max) = self.get_min_max_units()?;
1013 if max > Unit::Hour {
1014 return Err(err!(
1015 "parsing {unit} units into a `std::time::Duration` \
1016 is not supported (perhaps try parsing into a `Span` instead)",
1017 unit = max.singular(),
1018 ));
1019 }
1020
1021 let mut sdur = core::time::Duration::ZERO;
1022 if self.values[Unit::Hour.as_usize()] != 0 {
1023 let value = self.values[Unit::Hour.as_usize()];
1024 sdur = try_from_hours(value)
1025 .and_then(|nanos| sdur.checked_add(nanos))
1026 .ok_or_else(|| {
1027 err!(
1028 "accumulated `std::time::Duration` of `{sdur:?}` \
1029 overflowed when adding {value} of unit {unit}",
1030 unit = Unit::Hour.singular(),
1031 )
1032 })?;
1033 }
1034 if self.values[Unit::Minute.as_usize()] != 0 {
1035 let value = self.values[Unit::Minute.as_usize()];
1036 sdur = try_from_mins(value)
1037 .and_then(|nanos| sdur.checked_add(nanos))
1038 .ok_or_else(|| {
1039 err!(
1040 "accumulated `std::time::Duration` of `{sdur:?}` \
1041 overflowed when adding {value} of unit {unit}",
1042 unit = Unit::Minute.singular(),
1043 )
1044 })?;
1045 }
1046 if self.values[Unit::Second.as_usize()] != 0 {
1047 let value = self.values[Unit::Second.as_usize()];
1048 sdur = core::time::Duration::from_secs(value)
1049 .checked_add(sdur)
1050 .ok_or_else(|| {
1051 err!(
1052 "accumulated `std::time::Duration` of `{sdur:?}` \
1053 overflowed when adding {value} of unit {unit}",
1054 unit = Unit::Second.singular(),
1055 )
1056 })?;
1057 }
1058 if self.values[Unit::Millisecond.as_usize()] != 0 {
1059 let value = self.values[Unit::Millisecond.as_usize()];
1060 sdur = core::time::Duration::from_millis(value)
1061 .checked_add(sdur)
1062 .ok_or_else(|| {
1063 err!(
1064 "accumulated `std::time::Duration` of `{sdur:?}` \
1065 overflowed when adding {value} of unit {unit}",
1066 unit = Unit::Millisecond.singular(),
1067 )
1068 })?;
1069 }
1070 if self.values[Unit::Microsecond.as_usize()] != 0 {
1071 let value = self.values[Unit::Microsecond.as_usize()];
1072 sdur = core::time::Duration::from_micros(value)
1073 .checked_add(sdur)
1074 .ok_or_else(|| {
1075 err!(
1076 "accumulated `std::time::Duration` of `{sdur:?}` \
1077 overflowed when adding {value} of unit {unit}",
1078 unit = Unit::Microsecond.singular(),
1079 )
1080 })?;
1081 }
1082 if self.values[Unit::Nanosecond.as_usize()] != 0 {
1083 let value = self.values[Unit::Nanosecond.as_usize()];
1084 sdur = core::time::Duration::from_nanos(value)
1085 .checked_add(sdur)
1086 .ok_or_else(|| {
1087 err!(
1088 "accumulated `std::time::Duration` of `{sdur:?}` \
1089 overflowed when adding {value} of unit {unit}",
1090 unit = Unit::Nanosecond.singular(),
1091 )
1092 })?;
1093 }
1094
1095 if let Some(fraction) = self.get_fraction()? {
1096 sdur = sdur
1097 .checked_add(
1098 fractional_duration(min, fraction)?.unsigned_abs(),
1099 )
1100 .ok_or_else(|| {
1101 err!(
1102 "accumulated `std::time::Duration` of `{sdur:?}` \
1103 overflowed when adding 0.{fraction} of unit {unit}",
1104 unit = min.singular(),
1105 )
1106 })?;
1107 }
1108
1109 Ok(sdur)
1110 }
1111
1112 pub(crate) fn get_min(&self) -> Option<Unit> {
1116 self.min
1117 }
1118
1119 fn get_min_max_units(&self) -> Result<(Unit, Unit), Error> {
1124 let (Some(min), Some(max)) = (self.min, self.max) else {
1125 return Err(err!("no parsed duration components"));
1126 };
1127 Ok((min, max))
1128 }
1129
1130 #[cfg_attr(feature = "perf-inline", inline(always))]
1132 fn get_unit_value(&self, unit: Unit) -> Result<i64, Error> {
1133 const I64_MIN_ABS: u64 = i64::MIN.unsigned_abs();
1134
1135 #[cold]
1136 #[inline(never)]
1137 fn general(unit: Unit, value: u64, sign: Sign) -> Result<i64, Error> {
1138 if sign.is_negative() && value == I64_MIN_ABS {
1142 return Ok(i64::MIN);
1143 }
1144 let mut value = i64::try_from(value).map_err(|_| {
1147 err!(
1148 "`{sign}{value}` {unit} is too big (or small) \
1149 to fit into a signed 64-bit integer",
1150 unit = unit.plural()
1151 )
1152 })?;
1153 if sign.is_negative() {
1154 value = value.checked_neg().ok_or_else(|| {
1155 err!(
1156 "`{sign}{value}` {unit} is too big (or small) \
1157 to fit into a signed 64-bit integer",
1158 unit = unit.plural()
1159 )
1160 })?;
1161 }
1162 Ok(value)
1163 }
1164
1165 let sign = self.get_sign();
1166 let value = self.values[unit.as_usize()];
1167 if value >= I64_MIN_ABS {
1168 return general(unit, value, sign);
1169 }
1170 let mut value = value as i64;
1171 if sign.is_negative() {
1172 value = -value;
1173 }
1174 Ok(value)
1175 }
1176
1177 fn get_fraction(&self) -> Result<Option<i32>, Error> {
1181 let Some(fraction) = self.fraction else {
1182 return Ok(None);
1183 };
1184 let mut fraction = fraction as i32;
1186 if self.get_sign().is_negative() {
1187 fraction = -fraction;
1189 }
1190 Ok(Some(fraction))
1191 }
1192
1193 fn get_sign(&self) -> Sign {
1195 if self.any_non_zero_units {
1196 self.sign
1197 } else {
1198 Sign::Zero
1199 }
1200 }
1201}
1202
1203#[cfg_attr(feature = "perf-inline", inline(always))]
1219pub(crate) fn parse_temporal_fraction<'i>(
1220 input: &'i [u8],
1221) -> Result<Parsed<'i, Option<u32>>, Error> {
1222 #[inline(never)]
1252 fn imp<'i>(mut input: &'i [u8]) -> Result<Parsed<'i, Option<u32>>, Error> {
1253 let mkdigits = parse::slicer(input);
1254 while mkdigits(input).len() <= 8
1255 && input.first().map_or(false, u8::is_ascii_digit)
1256 {
1257 input = &input[1..];
1258 }
1259 let digits = mkdigits(input);
1260 if digits.is_empty() {
1261 return Err(err!(
1262 "found decimal after seconds component, \
1263 but did not find any decimal digits after decimal",
1264 ));
1265 }
1266 let nanoseconds = parse::fraction(digits).map_err(|err| {
1270 err!(
1271 "failed to parse {digits:?} as fractional component \
1272 (up to 9 digits, nanosecond precision): {err}",
1273 digits = escape::Bytes(digits),
1274 )
1275 })?;
1276 let nanoseconds = nanoseconds as u32;
1280 Ok(Parsed { value: Some(nanoseconds), input })
1281 }
1282
1283 if input.is_empty() || (input[0] != b'.' && input[0] != b',') {
1284 return Ok(Parsed { value: None, input });
1285 }
1286 imp(&input[1..])
1287}
1288
1289#[inline(never)]
1308fn fractional_time_to_span(
1309 unit: Unit,
1310 value: i64,
1311 fraction: i32,
1312 mut span: Span,
1313) -> Result<Span, Error> {
1314 const MAX_HOURS: i64 = t::SpanHours::MAX_SELF.get_unchecked() as i64;
1315 const MAX_MINS: i64 = t::SpanMinutes::MAX_SELF.get_unchecked() as i64;
1316 const MAX_SECS: i64 = t::SpanSeconds::MAX_SELF.get_unchecked() as i64;
1317 const MAX_MILLIS: i128 =
1318 t::SpanMilliseconds::MAX_SELF.get_unchecked() as i128;
1319 const MAX_MICROS: i128 =
1320 t::SpanMicroseconds::MAX_SELF.get_unchecked() as i128;
1321 const MIN_HOURS: i64 = t::SpanHours::MIN_SELF.get_unchecked() as i64;
1322 const MIN_MINS: i64 = t::SpanMinutes::MIN_SELF.get_unchecked() as i64;
1323 const MIN_SECS: i64 = t::SpanSeconds::MIN_SELF.get_unchecked() as i64;
1324 const MIN_MILLIS: i128 =
1325 t::SpanMilliseconds::MIN_SELF.get_unchecked() as i128;
1326 const MIN_MICROS: i128 =
1327 t::SpanMicroseconds::MIN_SELF.get_unchecked() as i128;
1328
1329 let mut sdur = fractional_time_to_duration(unit, value, fraction)?;
1346
1347 if unit >= Unit::Hour && !sdur.is_zero() {
1348 let (mut hours, rem) = sdur.as_hours_with_remainder();
1349 sdur = rem;
1350 if hours > MAX_HOURS {
1351 sdur += SignedDuration::from_hours(hours - MAX_HOURS);
1352 hours = MAX_HOURS;
1353 } else if hours < MIN_HOURS {
1354 sdur += SignedDuration::from_hours(hours - MIN_HOURS);
1355 hours = MIN_HOURS;
1356 }
1357 span = span.hours(hours);
1359 }
1360 if unit >= Unit::Minute && !sdur.is_zero() {
1361 let (mut mins, rem) = sdur.as_mins_with_remainder();
1362 sdur = rem;
1363 if mins > MAX_MINS {
1364 sdur += SignedDuration::from_mins(mins - MAX_MINS);
1365 mins = MAX_MINS;
1366 } else if mins < MIN_MINS {
1367 sdur += SignedDuration::from_mins(mins - MIN_MINS);
1368 mins = MIN_MINS;
1369 }
1370 span = span.minutes(mins);
1372 }
1373 if unit >= Unit::Second && !sdur.is_zero() {
1374 let (mut secs, rem) = sdur.as_secs_with_remainder();
1375 sdur = rem;
1376 if secs > MAX_SECS {
1377 sdur += SignedDuration::from_secs(secs - MAX_SECS);
1378 secs = MAX_SECS;
1379 } else if secs < MIN_SECS {
1380 sdur += SignedDuration::from_secs(secs - MIN_SECS);
1381 secs = MIN_SECS;
1382 }
1383 span = span.seconds(secs);
1385 }
1386 if unit >= Unit::Millisecond && !sdur.is_zero() {
1387 let (mut millis, rem) = sdur.as_millis_with_remainder();
1388 sdur = rem;
1389 if millis > MAX_MILLIS {
1390 sdur += SignedDuration::from_millis_i128(millis - MAX_MILLIS);
1391 millis = MAX_MILLIS;
1392 } else if millis < MIN_MILLIS {
1393 sdur += SignedDuration::from_millis_i128(millis - MIN_MILLIS);
1394 millis = MIN_MILLIS;
1395 }
1396 span = span.milliseconds(i64::try_from(millis).unwrap());
1398 }
1399 if unit >= Unit::Microsecond && !sdur.is_zero() {
1400 let (mut micros, rem) = sdur.as_micros_with_remainder();
1401 sdur = rem;
1402 if micros > MAX_MICROS {
1403 sdur += SignedDuration::from_micros_i128(micros - MAX_MICROS);
1404 micros = MAX_MICROS;
1405 } else if micros < MIN_MICROS {
1406 sdur += SignedDuration::from_micros_i128(micros - MIN_MICROS);
1407 micros = MIN_MICROS;
1408 }
1409 span = span.microseconds(i64::try_from(micros).unwrap());
1411 }
1412 if !sdur.is_zero() {
1413 let nanos = sdur.as_nanos();
1414 let nanos64 = i64::try_from(nanos).map_err(|_| {
1415 err!(
1416 "failed to set nanosecond value {nanos} (it overflows \
1417 `i64`) on span determined from {value}.{fraction}",
1418 )
1419 })?;
1420 span = span.try_nanoseconds(nanos64).with_context(|| {
1421 err!(
1422 "failed to set nanosecond value {nanos64} on span \
1423 determined from {value}.{fraction}",
1424 )
1425 })?;
1426 }
1427
1428 Ok(span)
1429}
1430
1431#[inline(never)]
1448fn fractional_time_to_duration(
1449 unit: Unit,
1450 value: i64,
1451 fraction: i32,
1452) -> Result<SignedDuration, Error> {
1453 let sdur = duration_unit_value(unit, value)?;
1454 let fraction_dur = fractional_duration(unit, fraction)?;
1455 sdur.checked_add(fraction_dur).ok_or_else(|| {
1456 err!(
1457 "accumulated `SignedDuration` of `{sdur:?}` overflowed \
1458 when adding `{fraction_dur:?}` (from fractional {unit} units)",
1459 unit = unit.singular(),
1460 )
1461 })
1462}
1463
1464#[inline(never)]
1479fn fractional_duration(
1480 unit: Unit,
1481 fraction: i32,
1482) -> Result<SignedDuration, Error> {
1483 let fraction = i64::from(fraction);
1484 let nanos = match unit {
1485 Unit::Hour => fraction * t::SECONDS_PER_HOUR.value(),
1486 Unit::Minute => fraction * t::SECONDS_PER_MINUTE.value(),
1487 Unit::Second => fraction,
1488 Unit::Millisecond => fraction / t::NANOS_PER_MICRO.value(),
1489 Unit::Microsecond => fraction / t::NANOS_PER_MILLI.value(),
1490 unit => {
1491 return Err(err!(
1492 "fractional {unit} units are not allowed",
1493 unit = unit.singular(),
1494 ))
1495 }
1496 };
1497 Ok(SignedDuration::from_nanos(nanos))
1498}
1499
1500#[cfg_attr(feature = "perf-inline", inline(always))]
1507fn duration_unit_value(
1508 unit: Unit,
1509 value: i64,
1510) -> Result<SignedDuration, Error> {
1511 let sdur = match unit {
1516 Unit::Hour => {
1517 let seconds = value
1518 .checked_mul(t::SECONDS_PER_HOUR.value())
1519 .ok_or_else(|| {
1520 err!("converting {value} hours to seconds overflows i64")
1521 })?;
1522 SignedDuration::from_secs(seconds)
1523 }
1524 Unit::Minute => {
1525 let seconds = value
1526 .checked_mul(t::SECONDS_PER_MINUTE.value())
1527 .ok_or_else(|| {
1528 err!("converting {value} minutes to seconds overflows i64")
1529 })?;
1530 SignedDuration::from_secs(seconds)
1531 }
1532 Unit::Second => SignedDuration::from_secs(value),
1533 Unit::Millisecond => SignedDuration::from_millis(value),
1534 Unit::Microsecond => SignedDuration::from_micros(value),
1535 Unit::Nanosecond => SignedDuration::from_nanos(value),
1536 unsupported => {
1537 return Err(err!(
1538 "parsing {unit} units into a `SignedDuration` is not supported \
1539 (perhaps try parsing into a `Span` instead)",
1540 unit = unsupported.singular(),
1541 ));
1542 }
1543 };
1544 Ok(sdur)
1545}
1546
1547#[cfg(test)]
1548mod tests {
1549 use alloc::string::ToString;
1550
1551 use super::*;
1552
1553 #[test]
1554 fn decimal() {
1555 let x = DecimalFormatter::new().format_signed(i64::MIN);
1556 assert_eq!(x.as_str(), "-9223372036854775808");
1557
1558 let x = DecimalFormatter::new().format_signed(i64::MIN + 1);
1559 assert_eq!(x.as_str(), "-9223372036854775807");
1560
1561 let x = DecimalFormatter::new().format_signed(i64::MAX);
1562 assert_eq!(x.as_str(), "9223372036854775807");
1563
1564 let x =
1565 DecimalFormatter::new().force_sign(true).format_signed(i64::MAX);
1566 assert_eq!(x.as_str(), "+9223372036854775807");
1567
1568 let x = DecimalFormatter::new().format_signed(0);
1569 assert_eq!(x.as_str(), "0");
1570
1571 let x = DecimalFormatter::new().force_sign(true).format_signed(0);
1572 assert_eq!(x.as_str(), "+0");
1573
1574 let x = DecimalFormatter::new().force_sign(false).format_signed(0);
1575 assert_eq!(x.as_str(), "-0");
1576
1577 let x = DecimalFormatter::new().padding(4).format_signed(0);
1578 assert_eq!(x.as_str(), "0000");
1579
1580 let x = DecimalFormatter::new().padding(4).format_signed(789);
1581 assert_eq!(x.as_str(), "0789");
1582
1583 let x = DecimalFormatter::new().padding(4).format_signed(-789);
1584 assert_eq!(x.as_str(), "-0789");
1585
1586 let x = DecimalFormatter::new()
1587 .force_sign(true)
1588 .padding(4)
1589 .format_signed(789);
1590 assert_eq!(x.as_str(), "+0789");
1591 }
1592
1593 #[test]
1594 fn fractional_auto() {
1595 let f = |n| FractionalFormatter::new().format(n).as_str().to_string();
1596
1597 assert_eq!(f(0), "");
1598 assert_eq!(f(123_000_000), "123");
1599 assert_eq!(f(123_456_000), "123456");
1600 assert_eq!(f(123_456_789), "123456789");
1601 assert_eq!(f(456_789), "000456789");
1602 assert_eq!(f(789), "000000789");
1603 }
1604
1605 #[test]
1606 fn fractional_precision() {
1607 let f = |precision, n| {
1608 FractionalFormatter::new()
1609 .precision(Some(precision))
1610 .format(n)
1611 .as_str()
1612 .to_string()
1613 };
1614
1615 assert_eq!(f(0, 0), "");
1616 assert_eq!(f(1, 0), "0");
1617 assert_eq!(f(9, 0), "000000000");
1618
1619 assert_eq!(f(3, 123_000_000), "123");
1620 assert_eq!(f(6, 123_000_000), "123000");
1621 assert_eq!(f(9, 123_000_000), "123000000");
1622
1623 assert_eq!(f(3, 123_456_000), "123");
1624 assert_eq!(f(6, 123_456_000), "123456");
1625 assert_eq!(f(9, 123_456_000), "123456000");
1626
1627 assert_eq!(f(3, 123_456_789), "123");
1628 assert_eq!(f(6, 123_456_789), "123456");
1629 assert_eq!(f(9, 123_456_789), "123456789");
1630
1631 assert_eq!(f(2, 889_000_000), "88");
1633 assert_eq!(f(2, 999_000_000), "99");
1634 }
1635}