Skip to main content

commons/
time.rs

1//! Time handling and duration utilities.
2
3use std::time::{Duration, SystemTime, UNIX_EPOCH};
4
5/// Get the current Unix timestamp in seconds
6pub fn unix_timestamp() -> u64 {
7    SystemTime::now()
8        .duration_since(UNIX_EPOCH)
9        .unwrap_or_default()
10        .as_secs()
11}
12
13/// Get the current Unix timestamp in milliseconds
14pub fn unix_timestamp_millis() -> u64 {
15    SystemTime::now()
16        .duration_since(UNIX_EPOCH)
17        .unwrap_or_default()
18        .as_millis() as u64
19}
20
21/// Format a duration in a human-readable way
22pub fn format_duration(duration: Duration) -> String {
23    let secs = duration.as_secs();
24    let millis = duration.subsec_millis();
25
26    if secs >= 86400 {
27        let days = secs / 86400;
28        let hours = (secs % 86400) / 3600;
29        format!("{}d {}h", days, hours)
30    } else if secs >= 3600 {
31        let hours = secs / 3600;
32        let minutes = (secs % 3600) / 60;
33        format!("{}h {}m", hours, minutes)
34    } else if secs >= 60 {
35        let minutes = secs / 60;
36        let seconds = secs % 60;
37        format!("{}m {}s", minutes, seconds)
38    } else if secs > 0 {
39        format!("{}.{:03}s", secs, millis)
40    } else {
41        format!("{}ms", millis)
42    }
43}
44
45/// Parse a duration from a human-readable string
46pub fn parse_duration(s: &str) -> Result<Duration, String> {
47    let s = s.trim();
48
49    if let Some(num_str) = s.strip_suffix("ms") {
50        let num: u64 = num_str.parse().map_err(|_| "Invalid milliseconds format")?;
51        Ok(Duration::from_millis(num))
52    } else if let Some(num_str) = s.strip_suffix('s') {
53        let num: f64 = num_str.parse().map_err(|_| "Invalid seconds format")?;
54        Ok(Duration::from_secs_f64(num))
55    } else if let Some(num_str) = s.strip_suffix('m') {
56        let num: u64 = num_str.parse().map_err(|_| "Invalid minutes format")?;
57        Ok(Duration::from_secs(num * 60))
58    } else if let Some(num_str) = s.strip_suffix('h') {
59        let num: u64 = num_str.parse().map_err(|_| "Invalid hours format")?;
60        Ok(Duration::from_secs(num * 3600))
61    } else if let Some(num_str) = s.strip_suffix('d') {
62        let num: u64 = num_str.parse().map_err(|_| "Invalid days format")?;
63        Ok(Duration::from_secs(num * 86400))
64    } else {
65        // Assume seconds if no suffix
66        let num: f64 = s.parse().map_err(|_| "Invalid duration format")?;
67        Ok(Duration::from_secs_f64(num))
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_parse_duration() {
77        assert_eq!(parse_duration("100ms").unwrap(), Duration::from_millis(100));
78        assert_eq!(parse_duration("5s").unwrap(), Duration::from_secs(5));
79        assert_eq!(parse_duration("2m").unwrap(), Duration::from_secs(120));
80        assert_eq!(parse_duration("1h").unwrap(), Duration::from_secs(3600));
81        assert_eq!(parse_duration("1d").unwrap(), Duration::from_secs(86400));
82    }
83
84    #[test]
85    fn test_format_duration() {
86        assert_eq!(format_duration(Duration::from_millis(500)), "500ms");
87        assert_eq!(format_duration(Duration::from_secs(5)), "5.000s");
88        assert_eq!(format_duration(Duration::from_secs(65)), "1m 5s");
89        assert_eq!(format_duration(Duration::from_secs(3665)), "1h 1m");
90    }
91}