human_duration/
lib.rs

1//! Human-readable duration
2//!
3//! human-duration converts a [`std::time::Duration`] to a human readable string.
4//!
5//! Examples:
6//!
7//! ```
8//! use human_duration::human_duration;
9//!
10//! let duration = std::time::Duration::new(120, 30_000_000);
11//! assert_eq!(human_duration(&duration), "2m 0s 30ms");
12//!
13//! let duration = std::time::Duration::new(9000, 0);
14//! assert_eq!(human_duration(&duration), "2h 30m 0s 0ms");
15//! ```
16
17#![forbid(unsafe_code)]
18#![warn(missing_docs)]
19
20use std::time::Duration;
21
22static SECONDS_PER_YEAR: u64 = 31_536_000;
23static SECONDS_PER_MONTH: u64 = 2_628_000;
24static SECONDS_PER_DAY: u64 = 86400;
25static SECONDS_PER_HOUR: u64 = 3600;
26static SECONDS_PER_MINUTE: u64 = 60;
27
28/// Takes a [`std::time::Duration`] and returns a formatted [`String`].
29///
30/// Examples:
31/// ```
32/// use human_duration::human_duration;
33///
34/// let duration = std::time::Duration::new(5, 0);
35/// assert_eq!(human_duration(&duration), "5s 0ms");
36///
37/// let duration = std::time::Duration::new(125, 0);
38/// assert_eq!(human_duration(&duration), "2m 5s 0ms");
39///
40/// let duration = std::time::Duration::new(45_000_000, 0);
41/// assert_eq!(human_duration(&duration), "1y 5mon 3d 18h 0m 0s 0ms");
42/// ```
43pub fn human_duration(duration: &Duration) -> String {
44    let seconds = duration.as_secs();
45    let millis = duration.subsec_millis();
46
47    let years = seconds / SECONDS_PER_YEAR;
48    let mut remainder = seconds % SECONDS_PER_YEAR;
49
50    let months = remainder / SECONDS_PER_MONTH;
51    remainder %= SECONDS_PER_MONTH;
52
53    let days = remainder / SECONDS_PER_DAY;
54    remainder %= SECONDS_PER_DAY;
55
56    let hours = remainder / SECONDS_PER_HOUR;
57    remainder %= SECONDS_PER_HOUR;
58
59    let minutes = remainder / SECONDS_PER_MINUTE;
60    remainder %= SECONDS_PER_MINUTE;
61
62    let mut output = Vec::with_capacity(7);
63
64    if years > 0 {
65        let f_years = format!("{years}y");
66        output.push(f_years);
67    }
68
69    if !output.is_empty() || months > 0 {
70        let f_months = format!("{months}mon");
71        output.push(f_months);
72    }
73
74    if !output.is_empty() || days > 0 {
75        let f_days = format!("{days}d");
76        output.push(f_days);
77    }
78
79    if !output.is_empty() || hours > 0 {
80        let f_hours = format!("{hours}h");
81        output.push(f_hours);
82    }
83
84    if !output.is_empty() || minutes > 0 {
85        let f_minutes = format!("{minutes}m");
86        output.push(f_minutes);
87    }
88
89    if !output.is_empty() || seconds > 0 {
90        let f_seconds = format!("{remainder}s");
91        output.push(f_seconds);
92    }
93
94    let f_millis = format!("{millis}ms");
95    output.push(f_millis);
96
97    output.join(" ")
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_human_duration() {
106        let result = human_duration(&std::time::Duration::new(0, 0));
107        assert_eq!(result, "0ms");
108
109        let result = human_duration(&std::time::Duration::new(30, 5_000_000));
110        assert_eq!(result, "30s 5ms");
111
112        let result = human_duration(&std::time::Duration::new(60, 50_000_000));
113        assert_eq!(result, "1m 0s 50ms");
114
115        let result = human_duration(&std::time::Duration::new(92, 0));
116        assert_eq!(result, "1m 32s 0ms");
117
118        let result = human_duration(&std::time::Duration::new(3600, 0));
119        assert_eq!(result, "1h 0m 0s 0ms");
120
121        let result = human_duration(&std::time::Duration::new(3666, 0));
122        assert_eq!(result, "1h 1m 6s 0ms");
123
124        let result = human_duration(&std::time::Duration::new(86400, 337_000_000));
125        assert_eq!(result, "1d 0h 0m 0s 337ms");
126
127        let result = human_duration(&std::time::Duration::new(86680, 0));
128        assert_eq!(result, "1d 0h 4m 40s 0ms");
129
130        let result = human_duration(&std::time::Duration::new(2_628_000, 0));
131        assert_eq!(result, "1mon 0d 0h 0m 0s 0ms");
132
133        let result = human_duration(&std::time::Duration::new(2_828_000, 0));
134        assert_eq!(result, "1mon 2d 7h 33m 20s 0ms");
135
136        let result = human_duration(&std::time::Duration::new(31_536_000, 0));
137        assert_eq!(result, "1y 0mon 0d 0h 0m 0s 0ms");
138
139        let result = human_duration(&std::time::Duration::new(34_536_000, 0));
140        assert_eq!(result, "1y 1mon 4d 7h 20m 0s 0ms");
141    }
142}