1use crate::units::{FromUnits, Unit};
10use core::fmt::{Display, Formatter, Write};
11use core::num::ParseFloatError;
12use core::str::Utf8Error;
13use irox_tools::buf::{Buffer, FixedU8Buf};
14use irox_tools::cfg_feature_serde;
15use irox_tools::irox_bits::{BitsError, Error, ErrorKind, FormatBits, MutBits};
16
17#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
20#[non_exhaustive]
21pub enum DurationUnit {
22 #[default]
26 Second,
27
28 Millisecond,
30
31 Microsecond,
33
34 Nanosecond,
36
37 Picosecond,
39
40 Minute,
42
43 Hour,
45
46 Day,
48
49 Year,
52}
53
54impl DurationUnit {
55 pub fn as_seconds(self, value: f64) -> f64 {
58 Duration::new(value, self)
59 .as_unit(DurationUnit::Second)
60 .value
61 }
62 pub fn as_millis(self, value: f64) -> f64 {
65 Duration::new(value, self)
66 .as_unit(DurationUnit::Millisecond)
67 .value
68 }
69
70 pub fn as_micros(self, value: f64) -> f64 {
73 Duration::new(value, self)
74 .as_unit(DurationUnit::Microsecond)
75 .value
76 }
77
78 pub fn as_nanos(self, value: f64) -> f64 {
81 Duration::new(value, self)
82 .as_unit(DurationUnit::Nanosecond)
83 .value
84 }
85
86 pub fn as_picos(self, value: f64) -> f64 {
89 Duration::new(value, self)
90 .as_unit(DurationUnit::Picosecond)
91 .value
92 }
93
94 pub fn as_minutes(self, value: f64) -> f64 {
97 Duration::new(value, self)
98 .as_unit(DurationUnit::Minute)
99 .value
100 }
101
102 pub fn as_hours(self, value: f64) -> f64 {
105 Duration::new(value, self).as_unit(DurationUnit::Hour).value
106 }
107
108 pub fn abbreviation(&self) -> &'static str {
109 match self {
110 DurationUnit::Second => "s",
111 DurationUnit::Millisecond => "ms",
112 DurationUnit::Microsecond => "us",
113 DurationUnit::Nanosecond => "ns",
114 DurationUnit::Picosecond => "ps",
115 DurationUnit::Minute => "min",
116 DurationUnit::Hour => "hr",
117 DurationUnit::Day => "dy",
118 DurationUnit::Year => "yr",
119 }
120 }
121}
122
123macro_rules! from_units_duration {
124 ($type:ident) => {
125 impl crate::units::FromUnits<$type> for DurationUnit {
126 fn from(&self, value: $type, units: Self) -> $type {
127 match self {
128 DurationUnit::Picosecond => match units {
130 DurationUnit::Picosecond => value as $type,
132 DurationUnit::Nanosecond => value * NANOS_TO_PICOS as $type,
133 DurationUnit::Microsecond => value * MICROS_TO_PICOS as $type,
134 DurationUnit::Millisecond => value * MILLIS_TO_PICOS as $type,
135 DurationUnit::Second => value * SEC_TO_PICOS as $type,
136 DurationUnit::Minute => value * MIN_TO_PICOS as $type,
137 DurationUnit::Hour => value * HOUR_TO_PICOS as $type,
138 DurationUnit::Day => value * DAY_TO_PICOS as $type,
139 DurationUnit::Year => value * YEAR_TO_PICOS as $type,
140 },
141 DurationUnit::Nanosecond => match units {
142 DurationUnit::Picosecond => value * PICOS_TO_NANOS as $type,
144 DurationUnit::Nanosecond => value as $type,
145 DurationUnit::Microsecond => value * MICROS_TO_NANOS as $type,
146 DurationUnit::Millisecond => value * MILLIS_TO_NANOS as $type,
147 DurationUnit::Second => value * SEC_TO_NANOS as $type,
148 DurationUnit::Minute => value * MIN_TO_NANOS as $type,
149 DurationUnit::Hour => value * HOUR_TO_NANOS as $type,
150 DurationUnit::Day => value * DAY_TO_NANOS as $type,
151 DurationUnit::Year => value * YEAR_TO_NANOS as $type,
152 },
153 DurationUnit::Microsecond => match units {
154 DurationUnit::Picosecond => value * PICOS_TO_MICROS as $type,
156 DurationUnit::Nanosecond => value * NANOS_TO_MICROS as $type,
157 DurationUnit::Microsecond => value as $type,
158 DurationUnit::Millisecond => value * MILLIS_TO_MICROS as $type,
159 DurationUnit::Second => value * SEC_TO_MILLIS as $type,
160 DurationUnit::Minute => value * MIN_TO_MICROS as $type,
161 DurationUnit::Hour => value * HOUR_TO_MICROS as $type,
162 DurationUnit::Day => value * DAY_TO_MICROS as $type,
163 DurationUnit::Year => value * YEAR_TO_MICROS as $type,
164 },
165 DurationUnit::Millisecond => match units {
166 DurationUnit::Picosecond => value * PICOS_TO_MILLIS as $type,
168 DurationUnit::Nanosecond => value * NANOS_TO_MILLIS as $type,
169 DurationUnit::Microsecond => value * MICROS_TO_MILLIS as $type,
170 DurationUnit::Millisecond => value as $type,
171 DurationUnit::Second => value * SEC_TO_MILLIS as $type,
172 DurationUnit::Minute => value * MIN_TO_MILLIS as $type,
173 DurationUnit::Hour => value * HOUR_TO_MILLIS as $type,
174 DurationUnit::Day => value * DAY_TO_MILLIS as $type,
175 DurationUnit::Year => value * YEAR_TO_MILLIS as $type,
176 },
177 DurationUnit::Second => match units {
178 DurationUnit::Picosecond => value * PICOS_TO_SEC as $type,
180 DurationUnit::Nanosecond => value * NANOS_TO_SEC as $type,
181 DurationUnit::Microsecond => value * MICROS_TO_SECS as $type,
182 DurationUnit::Millisecond => value * MILLIS_TO_SEC as $type,
183 DurationUnit::Second => value as $type,
184 DurationUnit::Minute => value * MIN_TO_SEC as $type,
185 DurationUnit::Hour => value * HOUR_TO_SEC as $type,
186 DurationUnit::Day => value * DAY_TO_SEC as $type,
187 DurationUnit::Year => value * YEAR_TO_SEC as $type,
188 },
189 DurationUnit::Minute => match units {
190 DurationUnit::Picosecond => value * PICOS_TO_MIN as $type,
192 DurationUnit::Nanosecond => value * NANOS_TO_MIN as $type,
193 DurationUnit::Microsecond => value * MICROS_TO_MIN as $type,
194 DurationUnit::Millisecond => value * MILLIS_TO_MIN as $type,
195 DurationUnit::Second => value * SEC_TO_MIN as $type,
196 DurationUnit::Minute => value as $type,
197 DurationUnit::Hour => value * HOUR_TO_MIN as $type,
198 DurationUnit::Day => value * DAY_TO_MIN as $type,
199 DurationUnit::Year => value * YEAR_TO_MIN as $type,
200 },
201 DurationUnit::Hour => match units {
202 DurationUnit::Picosecond => value * PICOS_TO_HOUR as $type,
204 DurationUnit::Nanosecond => value * NANOS_TO_HOUR as $type,
205 DurationUnit::Microsecond => value * MICROS_TO_HOUR as $type,
206 DurationUnit::Millisecond => value * MILLIS_TO_HOUR as $type,
207 DurationUnit::Second => value * SEC_TO_HOUR as $type,
208 DurationUnit::Minute => value * MIN_TO_HOUR as $type,
209 DurationUnit::Hour => value as $type,
210 DurationUnit::Day => value * DAY_TO_HOUR as $type,
211 DurationUnit::Year => value * YEAR_TO_HOUR as $type,
212 },
213 DurationUnit::Day => match units {
214 DurationUnit::Picosecond => value * PICOS_TO_DAY as $type,
216 DurationUnit::Nanosecond => value * NANOS_TO_DAY as $type,
217 DurationUnit::Microsecond => value * MICROS_TO_DAY as $type,
218 DurationUnit::Millisecond => value * MILLIS_TO_DAY as $type,
219 DurationUnit::Second => value * SEC_TO_DAY as $type,
220 DurationUnit::Minute => value * MIN_TO_DAY as $type,
221 DurationUnit::Hour => value * HOUR_TO_DAY as $type,
222 DurationUnit::Day => value as $type,
223 DurationUnit::Year => value * YEAR_TO_DAY as $type,
224 },
225 DurationUnit::Year => match units {
226 DurationUnit::Picosecond => value * PICOS_TO_YEAR as $type,
228 DurationUnit::Nanosecond => value * NANOS_TO_YEAR as $type,
229 DurationUnit::Microsecond => value * MICROS_TO_YEAR as $type,
230 DurationUnit::Millisecond => value * MILLIS_TO_YEAR as $type,
231 DurationUnit::Second => value * SEC_TO_YEAR as $type,
232 DurationUnit::Minute => value * MIN_TO_YEAR as $type,
233 DurationUnit::Hour => value * HOUR_TO_YEAR as $type,
234 DurationUnit::Day => value * DAY_TO_YEAR as $type,
235 DurationUnit::Year => value as $type,
236 },
237 }
238 }
239 }
240 };
241}
242
243basic_unit!(Duration, DurationUnit, Second);
244from_units_duration!(u32);
245from_units_duration!(i32);
246from_units_duration!(u64);
247from_units_duration!(i64);
248from_units_duration!(f32);
249from_units_duration!(f64);
250
251impl From<core::time::Duration> for Duration {
252 fn from(value: core::time::Duration) -> Self {
253 Duration::new(value.as_secs_f64(), DurationUnit::Second)
254 }
255}
256
257impl From<Duration> for core::time::Duration {
258 fn from(value: Duration) -> Self {
259 let secs = value.as_seconds();
260 let frac_sec = value.as_seconds_f64() - secs as f64;
261 let nanos = DurationUnit::Second.as_nanos(frac_sec) as u32;
262 core::time::Duration::new(secs, nanos)
263 }
264}
265
266impl Duration {
267 pub const fn new_seconds(value: f64) -> Duration {
270 Duration {
271 value,
272 units: DurationUnit::Second,
273 }
274 }
275
276 pub fn as_ydhms(&self) -> (u64, u16, u8, u8, u8) {
279 let mut rem = *self;
280 let years = rem.as_years();
281 rem -= Duration::from_years(years);
282 let (d, h, m, s) = rem.as_dhms();
283 (years, d as u16, h, m, s)
284 }
285
286 pub fn as_dhms(&self) -> (u64, u8, u8, u8) {
289 let mut rem = *self;
290 let days = rem.as_days();
291 rem -= Duration::from_days(days);
292 let (h, m, s) = rem.as_hms();
293 (days, h as u8, m, s)
294 }
295
296 pub fn as_hms(&self) -> (u64, u8, u8) {
299 let mut rem = *self;
300 let hours = rem.as_hours();
301 rem -= Duration::from_hours(hours);
302 let minutes = rem.as_minutes();
303 rem -= Duration::from_minutes(minutes);
304 let seconds = rem.as_seconds();
305 (hours, minutes as u8, seconds as u8)
306 }
307
308 pub fn as_seconds(&self) -> u64 {
311 self.as_unit(DurationUnit::Second).value() as u64
312 }
313
314 pub fn as_seconds_f64(&self) -> f64 {
316 self.as_unit(DurationUnit::Second).value()
317 }
318
319 pub fn as_seconds_f32(&self) -> f32 {
321 self.as_unit(DurationUnit::Second).value() as f32
322 }
323
324 pub fn as_millis(&self) -> u64 {
327 self.as_unit(DurationUnit::Millisecond).value() as u64
328 }
329 pub fn as_millis_f64(&self) -> f64 {
331 self.as_unit(DurationUnit::Millisecond).value()
332 }
333 pub fn as_millis_f32(&self) -> f32 {
335 self.as_unit(DurationUnit::Millisecond).value() as f32
336 }
337
338 pub fn as_micros(&self) -> u64 {
341 self.as_unit(DurationUnit::Microsecond).value() as u64
342 }
343 pub fn as_micros_f64(&self) -> f64 {
345 self.as_unit(DurationUnit::Microsecond).value()
346 }
347 pub fn as_micros_f32(&self) -> f32 {
349 self.as_unit(DurationUnit::Microsecond).value() as f32
350 }
351
352 pub fn as_nanos(&self) -> u64 {
355 self.as_unit(DurationUnit::Nanosecond).value() as u64
356 }
357 pub fn as_nanos_f64(&self) -> f64 {
359 self.as_unit(DurationUnit::Nanosecond).value()
360 }
361 pub fn as_nanos_f32(&self) -> f32 {
363 self.as_unit(DurationUnit::Nanosecond).value() as f32
364 }
365
366 pub fn as_picos(&self) -> u64 {
369 self.as_unit(DurationUnit::Picosecond).value() as u64
370 }
371 pub fn as_picos_f64(&self) -> f64 {
373 self.as_unit(DurationUnit::Picosecond).value()
374 }
375 pub fn as_picos_f32(&self) -> f32 {
377 self.as_unit(DurationUnit::Picosecond).value() as f32
378 }
379
380 pub fn as_minutes(&self) -> u64 {
383 self.as_unit(DurationUnit::Minute).value() as u64
384 }
385
386 pub fn as_hours(&self) -> u64 {
389 self.as_unit(DurationUnit::Hour).value() as u64
390 }
391
392 pub fn as_days(&self) -> u64 {
395 self.as_unit(DurationUnit::Day).value() as u64
396 }
397
398 pub fn as_years(&self) -> u64 {
401 self.as_unit(DurationUnit::Year).value() as u64
402 }
403}
404
405impl Duration {
407 pub const fn from_micros(micros: u64) -> Duration {
419 Duration::new(micros as f64, DurationUnit::Microsecond)
420 }
421
422 pub const fn from_millis(millis: u64) -> Duration {
434 Duration::new(millis as f64, DurationUnit::Millisecond)
435 }
436
437 pub const fn from_nanos(nanos: u64) -> Duration {
449 Duration::new(nanos as f64, DurationUnit::Nanosecond)
450 }
451
452 pub const fn from_picos(picos: u64) -> Duration {
464 Duration::new(picos as f64, DurationUnit::Picosecond)
465 }
466
467 pub const fn from_minutes(minutes: u64) -> Duration {
479 Duration::new(minutes as f64, DurationUnit::Minute)
480 }
481
482 pub const fn from_hours(hours: u64) -> Duration {
494 Duration::new(hours as f64, DurationUnit::Hour)
495 }
496
497 pub const fn from_days(days: u64) -> Duration {
509 Duration::new(days as f64, DurationUnit::Day)
510 }
511
512 pub const fn from_years(years: u64) -> Duration {
524 Duration::new(years as f64, DurationUnit::Year)
525 }
526
527 pub const fn from_seconds(seconds: u64) -> Duration {
539 Duration::new(seconds as f64, DurationUnit::Second)
540 }
541
542 pub const fn from_seconds_f64(seconds: f64) -> Duration {
554 Duration::new(seconds, DurationUnit::Second)
555 }
556
557 pub fn from_hms(hours: u64, minutes: u64, seconds: u64) -> Duration {
558 Duration::from_hours(hours)
559 + Duration::from_minutes(minutes)
560 + Duration::from_seconds(seconds)
561 }
562}
563
564impl Display for Duration {
565 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
566 f.write_fmt(format_args!("{} {}", self.value, self.units.abbreviation()))
567 }
568}
569
570pub const PICOS_TO_NANOS: f64 = 1e-3;
572pub const NANOS_TO_MICROS: f64 = 1e-3;
573pub const MICROS_TO_MILLIS: f64 = 1e-3;
574pub const MILLIS_TO_SEC: f64 = 1e-3;
575pub const SEC_TO_MIN: f64 = 1. / MIN_TO_SEC;
576pub const MIN_TO_HOUR: f64 = 1. / HOUR_TO_MIN;
577pub const HOUR_TO_DAY: f64 = 1. / DAY_TO_HOUR;
578pub const DAY_TO_YEAR: f64 = 1. / YEAR_TO_DAY;
579
580pub const YEAR_TO_DAY: f64 = 365_f64;
582pub const DAY_TO_HOUR: f64 = 24_f64;
583pub const HOUR_TO_MIN: f64 = 60_f64;
584pub const MIN_TO_SEC: f64 = 60_f64;
585pub const SEC_TO_MILLIS: f64 = 1e3;
586pub const MILLIS_TO_MICROS: f64 = 1e3;
587pub const MICROS_TO_NANOS: f64 = 1e3;
588pub const NANOS_TO_PICOS: f64 = 1e3;
589
590pub const YEAR_TO_HOUR: f64 = YEAR_TO_DAY * DAY_TO_HOUR;
592pub const DAY_TO_MIN: f64 = DAY_TO_HOUR * HOUR_TO_MIN;
593pub const HOUR_TO_SEC: f64 = HOUR_TO_MIN * MIN_TO_SEC;
594pub const MIN_TO_MILLIS: f64 = MIN_TO_SEC * SEC_TO_MILLIS;
595pub const SEC_TO_MICROS: f64 = SEC_TO_MILLIS * MILLIS_TO_MICROS;
596pub const MILLIS_TO_NANOS: f64 = MILLIS_TO_MICROS * MICROS_TO_NANOS;
597pub const MICROS_TO_PICOS: f64 = MICROS_TO_NANOS * NANOS_TO_PICOS;
598
599pub const PICOS_TO_MICROS: f64 = PICOS_TO_NANOS * NANOS_TO_MICROS;
601pub const NANOS_TO_MILLIS: f64 = NANOS_TO_MICROS * MICROS_TO_MILLIS;
602pub const MICROS_TO_SECS: f64 = MICROS_TO_MILLIS * MILLIS_TO_SEC;
603pub const MILLIS_TO_MIN: f64 = MILLIS_TO_SEC * SEC_TO_MIN;
604pub const SEC_TO_HOUR: f64 = SEC_TO_MIN * MIN_TO_HOUR;
605pub const MIN_TO_DAY: f64 = MIN_TO_HOUR * HOUR_TO_DAY;
606pub const HOUR_TO_YEAR: f64 = HOUR_TO_DAY * DAY_TO_YEAR;
607
608pub const YEAR_TO_MIN: f64 = YEAR_TO_HOUR * HOUR_TO_MIN;
610pub const DAY_TO_SEC: f64 = DAY_TO_MIN * MIN_TO_SEC;
611pub const HOUR_TO_MILLIS: f64 = HOUR_TO_SEC * SEC_TO_MILLIS;
612pub const MIN_TO_MICROS: f64 = MIN_TO_MILLIS * MILLIS_TO_MICROS;
613pub const SEC_TO_NANOS: f64 = SEC_TO_MICROS * MICROS_TO_NANOS;
614pub const MILLIS_TO_PICOS: f64 = MILLIS_TO_NANOS * NANOS_TO_PICOS;
615
616pub const PICOS_TO_MILLIS: f64 = PICOS_TO_MICROS * MICROS_TO_MILLIS;
618pub const NANOS_TO_SEC: f64 = NANOS_TO_MILLIS * MILLIS_TO_SEC;
619pub const MICROS_TO_MIN: f64 = MICROS_TO_SECS * SEC_TO_MIN;
620pub const MILLIS_TO_HOUR: f64 = MILLIS_TO_MIN * MIN_TO_HOUR;
621pub const SEC_TO_DAY: f64 = SEC_TO_HOUR * HOUR_TO_DAY;
622pub const MIN_TO_YEAR: f64 = MIN_TO_DAY * DAY_TO_YEAR;
623
624pub const YEAR_TO_SEC: f64 = YEAR_TO_MIN * MIN_TO_SEC;
626pub const DAY_TO_MILLIS: f64 = DAY_TO_SEC * SEC_TO_MILLIS;
627pub const HOUR_TO_MICROS: f64 = HOUR_TO_MILLIS * MILLIS_TO_MICROS;
628pub const MIN_TO_NANOS: f64 = MIN_TO_MICROS * MICROS_TO_NANOS;
629pub const SEC_TO_PICOS: f64 = SEC_TO_NANOS * NANOS_TO_PICOS;
630
631pub const PICOS_TO_SEC: f64 = PICOS_TO_MILLIS * MILLIS_TO_SEC;
633pub const NANOS_TO_MIN: f64 = NANOS_TO_SEC * SEC_TO_MIN;
634pub const MICROS_TO_HOUR: f64 = MICROS_TO_MIN * MIN_TO_HOUR;
635pub const MILLIS_TO_DAY: f64 = MILLIS_TO_HOUR * HOUR_TO_DAY;
636pub const SEC_TO_YEAR: f64 = SEC_TO_DAY * DAY_TO_YEAR;
637
638pub const YEAR_TO_MILLIS: f64 = YEAR_TO_SEC * SEC_TO_MILLIS;
640pub const DAY_TO_MICROS: f64 = DAY_TO_MILLIS * MILLIS_TO_MICROS;
641pub const HOUR_TO_NANOS: f64 = HOUR_TO_MICROS * MICROS_TO_NANOS;
642pub const MIN_TO_PICOS: f64 = MIN_TO_NANOS * NANOS_TO_PICOS;
643
644pub const PICOS_TO_MIN: f64 = PICOS_TO_SEC * SEC_TO_MIN;
646pub const NANOS_TO_HOUR: f64 = NANOS_TO_MIN * MIN_TO_HOUR;
647pub const MICROS_TO_DAY: f64 = MICROS_TO_HOUR * HOUR_TO_DAY;
648pub const MILLIS_TO_YEAR: f64 = MILLIS_TO_DAY * DAY_TO_YEAR;
649
650pub const YEAR_TO_MICROS: f64 = YEAR_TO_MILLIS * MILLIS_TO_MICROS;
652pub const DAY_TO_NANOS: f64 = DAY_TO_MICROS * MICROS_TO_NANOS;
653pub const HOUR_TO_PICOS: f64 = HOUR_TO_NANOS * NANOS_TO_PICOS;
654
655pub const PICOS_TO_HOUR: f64 = PICOS_TO_MIN * MIN_TO_HOUR;
657pub const NANOS_TO_DAY: f64 = NANOS_TO_HOUR * HOUR_TO_DAY;
658pub const MICROS_TO_YEAR: f64 = MICROS_TO_DAY * DAY_TO_YEAR;
659
660pub const YEAR_TO_NANOS: f64 = YEAR_TO_MICROS * MICROS_TO_NANOS;
662pub const DAY_TO_PICOS: f64 = DAY_TO_NANOS * NANOS_TO_PICOS;
663
664pub const PICOS_TO_DAY: f64 = PICOS_TO_HOUR * HOUR_TO_DAY;
666pub const NANOS_TO_YEAR: f64 = NANOS_TO_DAY * DAY_TO_YEAR;
667
668pub const YEAR_TO_PICOS: f64 = YEAR_TO_NANOS * NANOS_TO_PICOS;
670
671pub const PICOS_TO_YEAR: f64 = PICOS_TO_DAY * DAY_TO_YEAR;
673
674impl Duration {
675 pub fn write_iso8601_exact_to<T: MutBits>(&self, fmt: &mut T) -> Result<(), core::fmt::Error> {
676 let mut fmt = FormatBits(fmt);
677 fmt.write_str("P")?;
678 let y = self.as_years();
679 if y > 0 {
680 write!(fmt, "{y}Y")?;
681 }
682 let mut r = self - Duration::from_years(y);
683 let (d, h, m, _s) = r.as_dhms();
684 if d > 0 {
685 write!(fmt, "{d}D")?;
686 r -= Duration::from_days(d);
687 }
688 if r.as_seconds_f64() > 0.0 {
689 fmt.write_str("T")?;
690 }
691 if h > 0 {
692 write!(fmt, "{h}H")?;
693 r -= Duration::from_hours(h as u64);
694 }
695 if m > 0 {
696 write!(fmt, "{m}M")?;
697 r -= Duration::from_minutes(m as u64);
698 }
699 let s = r.as_seconds_f64();
700 if s.abs() > 0.0 {
701 write!(fmt, "{s:02.}S")?;
702 }
703
704 Ok(())
705 }
706 pub fn write_iso8601_sec_to<T: MutBits>(&self, fmt: &mut T) -> Result<(), core::fmt::Error> {
707 let mut fmt = FormatBits(fmt);
708 fmt.write_str("P")?;
709 let y = self.as_years();
710 if y > 0 {
711 write!(fmt, "{y}Y")?;
712 }
713 let mut r = self - Duration::from_years(y);
714 let (d, h, m, s) = r.as_dhms();
715 if d > 0 {
716 write!(fmt, "{d}D")?;
717 r -= Duration::from_days(d);
718 }
719 if r.as_seconds_f64() > 0.0 {
720 fmt.write_str("T")?;
721 }
722 if h > 0 {
723 write!(fmt, "{h}H")?;
724 r -= Duration::from_hours(h as u64);
725 }
726 if m > 0 {
727 write!(fmt, "{m}M")?;
728 r -= Duration::from_minutes(m as u64);
729 }
730 if s > 0 {
731 write!(fmt, "{s}S")?;
732 }
733
734 Ok(())
735 }
736
737 pub fn parse_iso8601_from(val: &str) -> Result<Self, ParseError> {
742 let b = val.as_bytes();
743 let mut seconds = 0.0f64;
744 let mut minutes = 0.0f64;
745 let mut hours = 0.0f64;
746 let mut days = 0.0f64;
747 let mut months = 0.0f64;
748 let mut years = 0.0f64;
749
750 let mut encountered_t = false;
751 let mut buf: FixedU8Buf<25> = Default::default();
752 for v in b {
753 match v {
754 b'S' | b's' => {
755 seconds += buf.as_f64()?;
756 buf.clear();
757 }
758 b'M' | b'm' => {
759 if encountered_t {
760 minutes += buf.as_f64()?;
761 } else {
762 months += buf.as_f64()?;
763 }
764 buf.clear();
765 }
766 b'H' | b'h' => {
767 hours += buf.as_f64()?;
768 buf.clear();
769 }
770 b'D' | b'd' => {
771 days += buf.as_f64()?;
772 buf.clear();
773 }
774 b'Y' | b'y' => {
775 years += buf.as_f64()?;
776 buf.clear();
777 }
778 b'0'..=b'9' | b'.' => {
779 if buf.push_back(*v).is_err() {
780 return Err(Error::new(
781 ErrorKind::OutOfMemory,
782 "Buffer overflowed parsing number in Duration::parse_iso8601_from",
783 )
784 .into());
785 }
786 }
787 b'T' | b't' => {
788 encountered_t = true;
789 }
790 _ => {}
791 }
792 }
793 Ok(Duration::new(seconds, DurationUnit::Second)
794 + Duration::new(minutes, DurationUnit::Minute)
795 + Duration::new(hours, DurationUnit::Hour)
796 + Duration::new(days, DurationUnit::Day)
797 + Duration::new(months * 30., DurationUnit::Day)
798 + Duration::new(years, DurationUnit::Year))
799 }
800}
801#[derive(Eq, PartialEq, Clone, Debug)]
802pub enum ParseError {
803 UTF8(Utf8Error),
804 Bits(BitsError),
805 Float(ParseFloatError),
806}
807impl Display for ParseError {
808 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
809 match self {
810 ParseError::UTF8(u) => {
811 write!(f, "UTF8Error: {u}")
812 }
813 ParseError::Bits(b) => {
814 write!(f, "BitsError: {b}")
815 }
816 ParseError::Float(e) => {
817 write!(f, "FloatError: {e}")
818 }
819 }
820 }
821}
822macro_rules! implerrorfrom {
823 ($id:ident, $en:ident) => {
824 impl From<$id> for ParseError {
825 fn from(value: $id) -> Self {
826 Self::$en(value)
827 }
828 }
829 };
830}
831implerrorfrom!(Utf8Error, UTF8);
832implerrorfrom!(BitsError, Bits);
833implerrorfrom!(ParseFloatError, Float);
834
835cfg_feature_serde! {
836 struct DurationVisitor;
837 impl serde::de::Visitor<'_> for DurationVisitor {
838 type Value = Duration;
839
840 fn expecting(&self, fmt: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
841 write!(fmt, "The visitor expects to receive a string formatted as a ISO 8601 Duration")
842 }
843 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: serde::de::Error {
844 Duration::parse_iso8601_from(v).map_err(serde::de::Error::custom)
845 }
846 }
847 impl<'de> serde::Deserialize<'de> for Duration {
848 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
849 deserializer.deserialize_str(DurationVisitor)
850 }
851 }
852 impl serde::Serialize for Duration {
853 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
854 let mut buf : FixedU8Buf<128> = Default::default();
855 self.write_iso8601_exact_to(&mut buf).map_err(serde::ser::Error::custom)?;
856 let s = buf.as_str().map_err(serde::ser::Error::custom)?;
857 serializer.serialize_str(s)
858 }
859 }
860}
861
862#[cfg(all(test, feature = "alloc"))]
863mod tests {
864 use core::fmt::Error;
865 extern crate alloc;
866 use crate::units::duration::Duration;
867
868 #[test]
869 pub fn test_iso8601() -> Result<(), Error> {
870 let mut s = alloc::string::String::new();
871 let d = Duration::from_years(1);
872 d.write_iso8601_exact_to(&mut s)?;
873 assert_eq!("P1Y", s);
874
875 assert_eq!(Ok(d), Duration::parse_iso8601_from(&s));
876
877 let d = Duration::from_hms(1, 2, 3);
878 s.clear();
879 d.write_iso8601_sec_to(&mut s)?;
880 assert_eq!("PT1H2M3S", s);
881 assert_eq!(Ok(d), Duration::parse_iso8601_from(&s));
882
883 let d = Duration::from_seconds_f64(123456.789);
884 s.clear();
885 d.write_iso8601_exact_to(&mut s)?;
886 assert_eq!("P1DT10H17M36.78900000000431S", s);
887 assert_eq!(Ok(d), Duration::parse_iso8601_from(&s));
888
889 Ok(())
890 }
891
892 #[test]
893 #[cfg(all(feature = "serde", feature = "std"))]
894 pub fn serde_test() -> Result<(), Error> {
895 #[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Debug)]
896 struct Test {
897 a: Duration,
898 }
899 impl Default for Test {
900 fn default() -> Self {
901 Self {
902 a: Duration::default(),
903 }
904 }
905 }
906 let a = Test {
907 a: Duration::from_days(1),
908 };
909 let s = serde_json::to_string(&a).unwrap_or_default();
910 assert_eq!(s, "{\"a\":\"P1D\"}");
911 let b: Test = serde_json::from_str(&s).unwrap();
912 assert_eq!(a, b);
913 Ok(())
914 }
915}