1use std::error::Error as StdError;
2use std::fmt;
3use std::str::{Chars, FromStr};
4use std::time::Duration;
5
6#[derive(Debug, PartialEq, Clone)]
8pub enum Error {
9 InvalidCharacter(usize),
15 NumberExpected(usize),
24 UnknownUnit {
32 start: usize,
34 end: usize,
36 unit: String,
38 value: u64,
40 },
41 NumberOverflow,
52 Empty,
54}
55
56impl StdError for Error {}
57
58impl fmt::Display for Error {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 Error::InvalidCharacter(offset) => write!(f, "invalid character at {}", offset),
62 Error::NumberExpected(offset) => write!(f, "expected number at {}", offset),
63 Error::UnknownUnit { unit, value, .. } if unit.is_empty() => {
64 write!(f, "time unit needed, for example {0}sec or {0}ms", value)
65 }
66 Error::UnknownUnit { unit, .. } => {
67 write!(
68 f,
69 "unknown time unit {:?}, \
70 supported units: ns, us/µs, ms, sec, min, hours, days, \
71 weeks, months, years (and few variations)",
72 unit
73 )
74 }
75 Error::NumberOverflow => write!(f, "number is too large or cannot be represented without a lack of precision (values below 1ns are not supported)"),
76 Error::Empty => write!(f, "value was empty"),
77 }
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct FormattedDuration(Duration);
84
85trait OverflowOp: Sized {
86 fn mul(self, other: Self) -> Result<Self, Error>;
87 fn add(self, other: Self) -> Result<Self, Error>;
88 fn div(self, other: Self) -> Result<Self, Error>;
89}
90
91impl OverflowOp for u64 {
92 fn mul(self, other: Self) -> Result<Self, Error> {
93 self.checked_mul(other).ok_or(Error::NumberOverflow)
94 }
95 fn add(self, other: Self) -> Result<Self, Error> {
96 self.checked_add(other).ok_or(Error::NumberOverflow)
97 }
98 fn div(self, other: Self) -> Result<Self, Error> {
99 match self % other {
100 0 => Ok(self / other),
101 _ => Err(Error::NumberOverflow),
102 }
103 }
104}
105
106#[derive(Debug, Clone, Copy)]
107struct Fraction {
108 numerator: u64,
109 denominator: u64,
110}
111
112struct Parser<'a> {
113 iter: Chars<'a>,
114 src: &'a str,
115}
116
117impl Parser<'_> {
118 fn parse(mut self) -> Result<Duration, Error> {
119 let mut n = self.parse_first_char()?.ok_or(Error::Empty)?; let mut out = Duration::ZERO;
121 'outer: loop {
122 let mut frac = None; let mut off = self.off();
124 while let Some(c) = self.iter.next() {
125 match c {
126 '0'..='9' => {
127 n = n
128 .checked_mul(10)
129 .and_then(|x| x.checked_add(c as u64 - '0' as u64))
130 .ok_or(Error::NumberOverflow)?;
131 }
132 c if c.is_whitespace() => {}
133 'a'..='z' | 'A'..='Z' | 'µ' => {
134 break;
135 }
136 '.' => {
137 frac = Some(self.parse_fractional_part(&mut off)?);
139 break;
140 }
141 _ => {
142 return Err(Error::InvalidCharacter(off));
143 }
144 }
145 off = self.off();
146 }
147 let start = off;
148 let mut off = self.off();
149 while let Some(c) = self.iter.next() {
150 match c {
151 '0'..='9' => {
152 self.parse_unit(n, frac, start, off, &mut out)?;
153 n = c as u64 - '0' as u64;
154 continue 'outer;
155 }
156 c if c.is_whitespace() => break,
157 'a'..='z' | 'A'..='Z' | 'µ' => {}
158 _ => {
159 return Err(Error::InvalidCharacter(off));
160 }
161 }
162 off = self.off();
163 }
164
165 self.parse_unit(n, frac, start, off, &mut out)?;
166 n = match self.parse_first_char()? {
167 Some(n) => n,
168 None => return Ok(out),
169 };
170 }
171 }
172
173 fn parse_first_char(&mut self) -> Result<Option<u64>, Error> {
174 let off = self.off();
175 for c in self.iter.by_ref() {
176 match c {
177 '0'..='9' => {
178 return Ok(Some(c as u64 - '0' as u64));
179 }
180 c if c.is_whitespace() => continue,
181 _ => {
182 return Err(Error::NumberExpected(off));
183 }
184 }
185 }
186 Ok(None)
187 }
188
189 fn parse_fractional_part(&mut self, off: &mut usize) -> Result<Fraction, Error> {
190 let mut numerator = 0u64;
191 let mut denominator = 1u64;
192 let mut zeros = true;
193 while let Some(c) = self.iter.next() {
194 match c {
195 '0' => {
196 denominator = denominator.checked_mul(10).ok_or(Error::NumberOverflow)?;
197 if !zeros {
198 numerator = numerator.checked_mul(10).ok_or(Error::NumberOverflow)?;
199 }
200 }
201 '1'..='9' => {
202 zeros = false;
203 denominator = denominator.checked_mul(10).ok_or(Error::NumberOverflow)?;
204 numerator = numerator
205 .checked_mul(10)
206 .and_then(|x| x.checked_add(c as u64 - '0' as u64))
207 .ok_or(Error::NumberOverflow)?;
208 }
209 c if c.is_whitespace() => {}
210 'a'..='z' | 'A'..='Z' | 'µ' => {
211 break;
212 }
213 _ => {
214 return Err(Error::InvalidCharacter(*off));
215 }
216 };
217 *off = self.off();
219 }
220 if denominator == 1 {
221 return Err(Error::InvalidCharacter(*off));
223 }
224 Ok(Fraction {
225 numerator,
226 denominator,
227 })
228 }
229
230 fn off(&self) -> usize {
231 self.src.len() - self.iter.as_str().len()
232 }
233
234 fn parse_unit(
235 &mut self,
236 n: u64,
237 frac: Option<Fraction>,
238 start: usize,
239 end: usize,
240 out: &mut Duration,
241 ) -> Result<(), Error> {
242 let unit = match Unit::from_str(&self.src[start..end]) {
243 Ok(u) => u,
244 Err(()) => {
245 return Err(Error::UnknownUnit {
246 start,
247 end,
248 unit: self.src[start..end].to_owned(),
249 value: n,
250 });
251 }
252 };
253
254 let (sec, nsec) = match unit {
256 Unit::Nanosecond => (0u64, n),
257 Unit::Microsecond => (0u64, n.mul(1000)?),
258 Unit::Millisecond => (0u64, n.mul(1_000_000)?),
259 Unit::Second => (n, 0),
260 Unit::Minute => (n.mul(60)?, 0),
261 Unit::Hour => (n.mul(3600)?, 0),
262 Unit::Day => (n.mul(86400)?, 0),
263 Unit::Week => (n.mul(86400 * 7)?, 0),
264 Unit::Month => (n.mul(2_630_016)?, 0), Unit::Year => (n.mul(31_557_600)?, 0), };
267 add_current(sec, nsec, out)?;
268
269 if let Some(Fraction {
271 numerator: n,
272 denominator: d,
273 }) = frac
274 {
275 let (sec, nsec) = match unit {
276 Unit::Nanosecond => return Err(Error::NumberOverflow),
277 Unit::Microsecond => (0, n.mul(1000)?.div(d)?),
278 Unit::Millisecond => (0, n.mul(1_000_000)?.div(d)?),
279 Unit::Second => (0, n.mul(1_000_000_000)?.div(d)?),
280 Unit::Minute => (0, n.mul(60_000_000_000)?.div(d)?),
281 Unit::Hour => (n.mul(3600)?.div(d)?, 0),
282 Unit::Day => (n.mul(86400)?.div(d)?, 0),
283 Unit::Week => (n.mul(86400 * 7)?.div(d)?, 0),
284 Unit::Month => (n.mul(2_630_016)?.div(d)?, 0), Unit::Year => (n.mul(31_557_600)?.div(d)?, 0), };
287 add_current(sec, nsec, out)?;
288 }
289
290 Ok(())
291 }
292}
293
294fn add_current(mut sec: u64, nsec: u64, out: &mut Duration) -> Result<(), Error> {
295 let mut nsec = (out.subsec_nanos() as u64).add(nsec)?;
296 if nsec > 1_000_000_000 {
297 sec = sec.add(nsec / 1_000_000_000)?;
298 nsec %= 1_000_000_000;
299 }
300 sec = out.as_secs().add(sec)?;
301 *out = Duration::new(sec, nsec as u32);
302 Ok(())
303}
304
305enum Unit {
306 Nanosecond,
307 Microsecond,
308 Millisecond,
309 Second,
310 Minute,
311 Hour,
312 Day,
313 Week,
314 Month,
315 Year,
316}
317
318impl FromStr for Unit {
319 type Err = ();
320
321 fn from_str(s: &str) -> Result<Self, Self::Err> {
322 match s {
323 "nanos" | "nsec" | "ns" => Ok(Self::Nanosecond),
324 "usec" | "us" | "µs" => Ok(Self::Microsecond),
325 "millis" | "msec" | "ms" => Ok(Self::Millisecond),
326 "seconds" | "second" | "secs" | "sec" | "s" => Ok(Self::Second),
327 "minutes" | "minute" | "min" | "mins" | "m" => Ok(Self::Minute),
328 "hours" | "hour" | "hr" | "hrs" | "h" => Ok(Self::Hour),
329 "days" | "day" | "d" => Ok(Self::Day),
330 "weeks" | "week" | "wk" | "wks" | "w" => Ok(Self::Week),
331 "months" | "month" | "M" => Ok(Self::Month),
332 "years" | "year" | "yr" | "yrs" | "y" => Ok(Self::Year),
333 _ => Err(()),
334 }
335 }
336}
337
338pub fn parse_duration(s: &str) -> Result<Duration, Error> {
365 if s == "0" {
366 return Ok(Duration::ZERO);
367 }
368 Parser {
369 iter: s.chars(),
370 src: s,
371 }
372 .parse()
373}
374
375pub fn format_duration(val: Duration) -> FormattedDuration {
393 FormattedDuration(val)
394}
395
396fn item_plural(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u64) -> fmt::Result {
397 if value > 0 {
398 if *started {
399 f.write_str(" ")?;
400 }
401 write!(f, "{}{}", value, name)?;
402 if value > 1 {
403 f.write_str("s")?;
404 }
405 *started = true;
406 }
407 Ok(())
408}
409fn item(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u32) -> fmt::Result {
410 if value > 0 {
411 if *started {
412 f.write_str(" ")?;
413 }
414 write!(f, "{}{}", value, name)?;
415 *started = true;
416 }
417 Ok(())
418}
419
420impl FormattedDuration {
421 pub fn get_ref(&self) -> &Duration {
423 &self.0
424 }
425}
426
427impl fmt::Display for FormattedDuration {
428 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
429 let secs = self.0.as_secs();
430 let nanos = self.0.subsec_nanos();
431
432 if secs == 0 && nanos == 0 {
433 f.write_str("0s")?;
434 return Ok(());
435 }
436
437 let years = secs / 31_557_600; let ydays = secs % 31_557_600;
439 let months = ydays / 2_630_016; let mdays = ydays % 2_630_016;
441 let days = mdays / 86400;
442 let day_secs = mdays % 86400;
443 let hours = day_secs / 3600;
444 let minutes = day_secs % 3600 / 60;
445 let seconds = day_secs % 60;
446
447 let millis = nanos / 1_000_000;
448 let micros = nanos / 1000 % 1000;
449 let nanosec = nanos % 1000;
450
451 let started = &mut false;
452 item_plural(f, started, "year", years)?;
453 item_plural(f, started, "month", months)?;
454 item_plural(f, started, "day", days)?;
455 item(f, started, "h", hours as u32)?;
456 item(f, started, "m", minutes as u32)?;
457 item(f, started, "s", seconds as u32)?;
458 item(f, started, "ms", millis)?;
459 #[cfg(feature = "mu")]
460 item(f, started, "µs", micros)?;
461 #[cfg(not(feature = "mu"))]
462 item(f, started, "us", micros)?;
463 item(f, started, "ns", nanosec)?;
464 Ok(())
465 }
466}
467
468#[cfg(test)]
469mod test {
470 use std::time::Duration;
471
472 use rand::Rng;
473
474 use super::Error;
475 use super::{format_duration, parse_duration};
476
477 #[test]
478 #[allow(clippy::cognitive_complexity)]
479 fn test_units() {
480 assert_eq!(parse_duration("17nsec"), Ok(Duration::new(0, 17)));
481 assert_eq!(parse_duration("17nanos"), Ok(Duration::new(0, 17)));
482 assert_eq!(parse_duration("33ns"), Ok(Duration::new(0, 33)));
483 assert_eq!(parse_duration("3usec"), Ok(Duration::new(0, 3000)));
484 assert_eq!(parse_duration("78us"), Ok(Duration::new(0, 78000)));
485 assert_eq!(parse_duration("163µs"), Ok(Duration::new(0, 163000)));
486 assert_eq!(parse_duration("31msec"), Ok(Duration::new(0, 31_000_000)));
487 assert_eq!(parse_duration("31millis"), Ok(Duration::new(0, 31_000_000)));
488 assert_eq!(parse_duration("6ms"), Ok(Duration::new(0, 6_000_000)));
489 assert_eq!(parse_duration("3000s"), Ok(Duration::new(3000, 0)));
490 assert_eq!(parse_duration("300sec"), Ok(Duration::new(300, 0)));
491 assert_eq!(parse_duration("300secs"), Ok(Duration::new(300, 0)));
492 assert_eq!(parse_duration("50seconds"), Ok(Duration::new(50, 0)));
493 assert_eq!(parse_duration("1second"), Ok(Duration::new(1, 0)));
494 assert_eq!(parse_duration("100m"), Ok(Duration::new(6000, 0)));
495 assert_eq!(parse_duration("12min"), Ok(Duration::new(720, 0)));
496 assert_eq!(parse_duration("12mins"), Ok(Duration::new(720, 0)));
497 assert_eq!(parse_duration("1minute"), Ok(Duration::new(60, 0)));
498 assert_eq!(parse_duration("7minutes"), Ok(Duration::new(420, 0)));
499 assert_eq!(parse_duration("2h"), Ok(Duration::new(7200, 0)));
500 assert_eq!(parse_duration("7hr"), Ok(Duration::new(25200, 0)));
501 assert_eq!(parse_duration("7hrs"), Ok(Duration::new(25200, 0)));
502 assert_eq!(parse_duration("1hour"), Ok(Duration::new(3600, 0)));
503 assert_eq!(parse_duration("24hours"), Ok(Duration::new(86400, 0)));
504 assert_eq!(parse_duration("1day"), Ok(Duration::new(86400, 0)));
505 assert_eq!(parse_duration("2days"), Ok(Duration::new(172_800, 0)));
506 assert_eq!(parse_duration("365d"), Ok(Duration::new(31_536_000, 0)));
507 assert_eq!(parse_duration("1week"), Ok(Duration::new(604_800, 0)));
508 assert_eq!(parse_duration("7weeks"), Ok(Duration::new(4_233_600, 0)));
509 assert_eq!(
510 parse_duration("104wks"),
511 Ok(Duration::new(2 * 31_449_600, 0))
512 );
513 assert_eq!(parse_duration("100wk"), Ok(Duration::new(60_480_000, 0)));
514 assert_eq!(parse_duration("52w"), Ok(Duration::new(31_449_600, 0)));
515 assert_eq!(parse_duration("1month"), Ok(Duration::new(2_630_016, 0)));
516 assert_eq!(
517 parse_duration("3months"),
518 Ok(Duration::new(3 * 2_630_016, 0))
519 );
520 assert_eq!(parse_duration("12M"), Ok(Duration::new(31_560_192, 0)));
521 assert_eq!(parse_duration("1year"), Ok(Duration::new(31_557_600, 0)));
522 assert_eq!(
523 parse_duration("7years"),
524 Ok(Duration::new(7 * 31_557_600, 0))
525 );
526 assert_eq!(
527 parse_duration("15yrs"),
528 Ok(Duration::new(15 * 31_557_600, 0))
529 );
530 assert_eq!(
531 parse_duration("10yr"),
532 Ok(Duration::new(10 * 31_557_600, 0))
533 );
534 assert_eq!(parse_duration("17y"), Ok(Duration::new(536_479_200, 0)));
535 }
536
537 #[test]
538 fn test_fractional_bad_input() {
539 assert!(matches!(
540 parse_duration("1.s"),
541 Err(Error::InvalidCharacter(_))
542 ));
543 assert!(matches!(
544 parse_duration("1..s"),
545 Err(Error::InvalidCharacter(_))
546 ));
547 assert!(matches!(
548 parse_duration(".1s"),
549 Err(Error::NumberExpected(_))
550 ));
551 assert!(matches!(parse_duration("."), Err(Error::NumberExpected(_))));
552 assert_eq!(
553 parse_duration("0.000123456789s"),
554 Err(Error::NumberOverflow)
555 );
556 }
557
558 #[test]
559 fn test_fractional_units() {
560 for input in &["17.5nsec", "5.1nanos", "0.0005ns"] {
562 let bad_ns_frac = parse_duration(input);
563 assert!(
564 matches!(bad_ns_frac, Err(Error::NumberOverflow)),
565 "fractions of nanoseconds should fail, but got {bad_ns_frac:?}"
566 );
567 }
568
569 assert_eq!(parse_duration("3.1usec"), Ok(Duration::new(0, 3100)));
571 assert_eq!(parse_duration("3.1us"), Ok(Duration::new(0, 3100)));
572 assert_eq!(parse_duration("3.01us"), Ok(Duration::new(0, 3010)));
573 assert_eq!(parse_duration("3.001us"), Ok(Duration::new(0, 3001)));
574 for input in &["3.0001us", "0.0001us", "0.123456us"] {
575 let bad_ms_frac = parse_duration(input);
576 assert!(
577 matches!(bad_ms_frac, Err(Error::NumberOverflow)),
578 "too small fractions of microseconds should fail, but got {bad_ms_frac:?}"
579 );
580 }
581
582 assert_eq!(parse_duration("31.1msec"), Ok(Duration::new(0, 31_100_000)));
584 assert_eq!(
585 parse_duration("31.1millis"),
586 Ok(Duration::new(0, 31_100_000))
587 );
588 assert_eq!(parse_duration("31.1ms"), Ok(Duration::new(0, 31_100_000)));
589 assert_eq!(parse_duration("31.01ms"), Ok(Duration::new(0, 31_010_000)));
590 assert_eq!(parse_duration("31.001ms"), Ok(Duration::new(0, 31_001_000)));
591 assert_eq!(
592 parse_duration("31.0001ms"),
593 Ok(Duration::new(0, 31_000_100))
594 );
595 assert_eq!(
596 parse_duration("31.00001ms"),
597 Ok(Duration::new(0, 31_000_010))
598 );
599 assert_eq!(
600 parse_duration("31.000001ms"),
601 Ok(Duration::new(0, 31_000_001))
602 );
603 assert!(matches!(
604 parse_duration("31.0000001ms"),
605 Err(Error::NumberOverflow)
606 ));
607
608 assert_eq!(parse_duration("300.0sec"), Ok(Duration::new(300, 0)));
610 assert_eq!(parse_duration("300.0secs"), Ok(Duration::new(300, 0)));
611 assert_eq!(parse_duration("300.0seconds"), Ok(Duration::new(300, 0)));
612 assert_eq!(parse_duration("300.0s"), Ok(Duration::new(300, 0)));
613 assert_eq!(parse_duration("0.0s"), Ok(Duration::new(0, 0)));
614 assert_eq!(parse_duration("0.2s"), Ok(Duration::new(0, 200_000_000)));
615 assert_eq!(parse_duration("1.2s"), Ok(Duration::new(1, 200_000_000)));
616 assert_eq!(parse_duration("1.02s"), Ok(Duration::new(1, 20_000_000)));
617 assert_eq!(parse_duration("1.002s"), Ok(Duration::new(1, 2_000_000)));
618 assert_eq!(parse_duration("1.0002s"), Ok(Duration::new(1, 200_000)));
619 assert_eq!(parse_duration("1.00002s"), Ok(Duration::new(1, 20_000)));
620 assert_eq!(parse_duration("1.000002s"), Ok(Duration::new(1, 2_000)));
621 assert_eq!(parse_duration("1.0000002s"), Ok(Duration::new(1, 200)));
622 assert_eq!(parse_duration("1.00000002s"), Ok(Duration::new(1, 20)));
623 assert_eq!(parse_duration("1.000000002s"), Ok(Duration::new(1, 2)));
624 assert_eq!(
625 parse_duration("1.123456789s"),
626 Ok(Duration::new(1, 123_456_789))
627 );
628 assert!(matches!(
629 parse_duration("1.0000000002s"),
630 Err(Error::NumberOverflow)
631 ));
632 assert!(matches!(
633 parse_duration("0.0000000002s"),
634 Err(Error::NumberOverflow)
635 ));
636
637 assert_eq!(parse_duration("100.0m"), Ok(Duration::new(6000, 0)));
639 assert_eq!(parse_duration("12.1min"), Ok(Duration::new(726, 0)));
640 assert_eq!(parse_duration("12.1mins"), Ok(Duration::new(726, 0)));
641 assert_eq!(parse_duration("1.5minute"), Ok(Duration::new(90, 0)));
642 assert_eq!(parse_duration("1.5minutes"), Ok(Duration::new(90, 0)));
643
644 assert_eq!(parse_duration("2.0h"), Ok(Duration::new(7200, 0)));
646 assert_eq!(parse_duration("2.0hr"), Ok(Duration::new(7200, 0)));
647 assert_eq!(parse_duration("2.0hrs"), Ok(Duration::new(7200, 0)));
648 assert_eq!(parse_duration("2.0hours"), Ok(Duration::new(7200, 0)));
649 assert_eq!(parse_duration("2.5h"), Ok(Duration::new(9000, 0)));
650 assert_eq!(parse_duration("0.5h"), Ok(Duration::new(1800, 0)));
651
652 assert_eq!(
654 parse_duration("1.5day"),
655 Ok(Duration::new(86400 + 86400 / 2, 0))
656 );
657 assert_eq!(
658 parse_duration("1.5days"),
659 Ok(Duration::new(86400 + 86400 / 2, 0))
660 );
661 assert_eq!(
662 parse_duration("1.5d"),
663 Ok(Duration::new(86400 + 86400 / 2, 0))
664 );
665 assert!(matches!(
666 parse_duration("0.00000005d"),
667 Err(Error::NumberOverflow)
668 ));
669 }
670
671 #[test]
672 fn test_fractional_combined() {
673 assert_eq!(parse_duration("7.120us 3ns"), Ok(Duration::new(0, 7123)));
674 assert_eq!(parse_duration("7.123us 4ns"), Ok(Duration::new(0, 7127)));
675 assert_eq!(
676 parse_duration("1.234s 789ns"),
677 Ok(Duration::new(1, 234_000_789))
678 );
679 assert_eq!(
680 parse_duration("1.234s 0.789us"),
681 Ok(Duration::new(1, 234_000_789))
682 );
683 assert_eq!(
684 parse_duration("1.234567s 0.789us"),
685 Ok(Duration::new(1, 234_567_789))
686 );
687 assert_eq!(
688 parse_duration("1.234s 1.345ms 1.678us 1ns"),
689 Ok(Duration::new(1, 235_346_679))
690 );
691 assert_eq!(
692 parse_duration("1.234s 0.345ms 0.678us 0ns"),
693 Ok(Duration::new(1, 234_345_678))
694 );
695 assert_eq!(
696 parse_duration("1.234s0.345ms0.678us0ns"),
697 Ok(Duration::new(1, 234_345_678))
698 );
699 }
700
701 #[test]
702 fn allow_0_with_no_unit() {
703 assert_eq!(parse_duration("0"), Ok(Duration::new(0, 0)));
704 }
705
706 #[test]
707 fn test_combo() {
708 assert_eq!(
709 parse_duration("20 min 17 nsec "),
710 Ok(Duration::new(1200, 17))
711 );
712 assert_eq!(parse_duration("2h 15m"), Ok(Duration::new(8100, 0)));
713 }
714
715 #[test]
716 fn all_86400_seconds() {
717 for second in 0..86400 {
718 let d = Duration::new(second, 0);
720 assert_eq!(d, parse_duration(&format_duration(d).to_string()).unwrap());
721 }
722 }
723
724 #[test]
725 fn random_second() {
726 for _ in 0..10000 {
727 let sec = rand::rng().random_range(0..253_370_764_800);
728 let d = Duration::new(sec, 0);
729 assert_eq!(d, parse_duration(&format_duration(d).to_string()).unwrap());
730 }
731 }
732
733 #[test]
734 fn random_any() {
735 for _ in 0..10000 {
736 let sec = rand::rng().random_range(0..253_370_764_800);
737 let nanos = rand::rng().random_range(0..1_000_000_000);
738 let d = Duration::new(sec, nanos);
739 assert_eq!(d, parse_duration(&format_duration(d).to_string()).unwrap());
740 }
741 }
742
743 #[test]
744 fn test_overlow() {
745 assert_eq!(
748 parse_duration("100000000000000000000ns"),
749 Err(Error::NumberOverflow)
750 );
751 assert_eq!(
752 parse_duration("100000000000000000us"),
753 Err(Error::NumberOverflow)
754 );
755 assert_eq!(
756 parse_duration("100000000000000ms"),
757 Err(Error::NumberOverflow)
758 );
759
760 assert_eq!(
761 parse_duration("100000000000000000000s"),
762 Err(Error::NumberOverflow)
763 );
764 assert_eq!(
765 parse_duration("10000000000000000000m"),
766 Err(Error::NumberOverflow)
767 );
768 assert_eq!(
769 parse_duration("1000000000000000000h"),
770 Err(Error::NumberOverflow)
771 );
772 assert_eq!(
773 parse_duration("100000000000000000d"),
774 Err(Error::NumberOverflow)
775 );
776 assert_eq!(
777 parse_duration("10000000000000000w"),
778 Err(Error::NumberOverflow)
779 );
780 assert_eq!(
781 parse_duration("1000000000000000M"),
782 Err(Error::NumberOverflow)
783 );
784 assert_eq!(
785 parse_duration("10000000000000y"),
786 Err(Error::NumberOverflow)
787 );
788 }
789
790 #[test]
791 fn test_nice_error_message() {
792 assert_eq!(
793 parse_duration("123").unwrap_err().to_string(),
794 "time unit needed, for example 123sec or 123ms"
795 );
796 assert_eq!(
797 parse_duration("10 months 1").unwrap_err().to_string(),
798 "time unit needed, for example 1sec or 1ms"
799 );
800 assert_eq!(
801 parse_duration("10nights").unwrap_err().to_string(),
802 "unknown time unit \"nights\", supported units: \
803 ns, us/µs, ms, sec, min, hours, days, weeks, months, \
804 years (and few variations)"
805 );
806 }
807
808 #[cfg(feature = "mu")]
809 #[test]
810 fn test_format_micros() {
811 assert_eq!(
812 format_duration(Duration::from_micros(123)).to_string(),
813 "123µs"
814 );
815 }
816
817 #[cfg(not(feature = "mu"))]
818 #[test]
819 fn test_format_micros() {
820 assert_eq!(
821 format_duration(Duration::from_micros(123)).to_string(),
822 "123us"
823 );
824 }
825
826 #[test]
827 fn test_error_cases() {
828 assert_eq!(
829 parse_duration("\0").unwrap_err().to_string(),
830 "expected number at 0"
831 );
832 assert_eq!(
833 parse_duration("\r").unwrap_err().to_string(),
834 "value was empty"
835 );
836 assert_eq!(
837 parse_duration("1~").unwrap_err().to_string(),
838 "invalid character at 1"
839 );
840 assert_eq!(
841 parse_duration("1Nå").unwrap_err().to_string(),
842 "invalid character at 2"
843 );
844 assert_eq!(parse_duration("222nsec221nanosmsec7s5msec572s").unwrap_err().to_string(),
845 "unknown time unit \"nanosmsec\", supported units: ns, us/µs, ms, sec, min, hours, days, weeks, months, years (and few variations)");
846 }
847}