rskit_util/time/
duration.rs1use crate::parse_decimal_scaled;
4use std::time::Duration;
5
6#[must_use]
17pub fn format_duration(d: Duration) -> String {
18 let secs = d.as_secs_f64();
19 if secs >= 3600.0 {
20 format!("{:.2}h", secs / 3600.0)
21 } else if secs >= 60.0 {
22 format!("{:.2}m", secs / 60.0)
23 } else if secs >= 1.0 {
24 format!("{secs:.2}s")
25 } else if d.as_micros() >= 1000 {
26 format!("{}ms", d.as_millis())
27 } else if d.as_nanos() >= 1000 {
28 format!("{}μs", d.as_micros())
29 } else {
30 format!("{}ns", d.as_nanos())
31 }
32}
33
34#[must_use]
47pub fn parse_duration(s: &str) -> Option<Duration> {
48 let s = s.trim().to_lowercase();
49 let (num_part, unit_part) = s.split_at(s.find(|c: char| c.is_alphabetic()).unwrap_or(s.len()));
50 let unit = unit_part.trim();
51
52 let nanos_per_unit = match unit {
53 "ns" => 1_u128,
54 "us" | "μs" => 1_000,
55 "ms" => 1_000_000,
56 "s" | "" => 1_000_000_000,
57 "m" | "min" => 60 * 1_000_000_000,
58 "h" | "hr" => 3_600 * 1_000_000_000,
59 "d" | "day" => 86_400 * 1_000_000_000,
60 _ => return None,
61 };
62
63 let nanos = parse_decimal_scaled(num_part.trim(), nanos_per_unit)?;
64 if nanos > Duration::MAX.as_nanos() {
65 return None;
66 }
67 let secs = nanos / 1_000_000_000;
68 let subsec_nanos = nanos % 1_000_000_000;
69 Some(Duration::new(
70 u64::try_from(secs).ok()?,
71 u32::try_from(subsec_nanos).ok()?,
72 ))
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_format_duration() {
81 assert_eq!(format_duration(Duration::from_hours(2)), "2.00h");
82 assert_eq!(format_duration(Duration::from_secs(150)), "2.50m");
83 assert_eq!(format_duration(Duration::from_secs(5)), "5.00s");
84 assert_eq!(format_duration(Duration::from_millis(250)), "250ms");
85 }
86
87 #[test]
88 fn test_parse_duration() {
89 assert_eq!(parse_duration("5"), Some(Duration::from_secs(5)));
90 assert_eq!(parse_duration("5s"), Some(Duration::from_secs(5)));
91 assert_eq!(parse_duration("10m"), Some(Duration::from_mins(10)));
92 assert_eq!(parse_duration("1.5h"), Some(Duration::from_mins(90)));
93 assert_eq!(parse_duration("1.5ms"), Some(Duration::from_micros(1500)));
94 assert_eq!(parse_duration("-1s"), None);
95 assert_eq!(parse_duration("invalid"), None);
96 }
97}