1#![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
28pub 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}