duration_str/
ext.rs

1use crate::unit::TimeUnit;
2use std::time::Duration;
3
4pub trait HumanFormat {
5    fn human_format(&self) -> String;
6}
7
8const ONE_SECOND_SECOND: u64 = 1;
9const ONE_MINUTE_SECOND: u64 = 60 * ONE_SECOND_SECOND;
10const ONE_HOUR_SECOND: u64 = 60 * ONE_MINUTE_SECOND;
11const ONE_DAY_SECOND: u64 = 24 * ONE_HOUR_SECOND;
12const ONE_WEEK_SECOND: u64 = 7 * ONE_DAY_SECOND;
13const ONE_MONTH_SECOND: u64 = 30 * ONE_DAY_SECOND;
14const ONE_YEAR_SECOND: u64 = 365 * ONE_DAY_SECOND;
15
16fn accrual(val: u64, unit: TimeUnit, format: &mut String) {
17    if val > 0 {
18        if !format.is_empty() {
19            format.push(' ');
20        }
21        format.push_str(&format!("{}{}", val, unit));
22    }
23}
24
25fn format_inner(seconds: u64, nanos: u32) -> String {
26    if seconds == 0 && nanos == 0 {
27        return "0s".to_string();
28    }
29
30    let year = seconds / ONE_YEAR_SECOND;
31    let ydays = seconds % ONE_YEAR_SECOND;
32    let month = ydays / ONE_MONTH_SECOND;
33    let mdays = ydays % ONE_MONTH_SECOND;
34    let week = mdays / ONE_WEEK_SECOND;
35    let wdays = mdays % ONE_WEEK_SECOND;
36    let day = wdays / ONE_DAY_SECOND;
37    let day_secs = wdays % ONE_DAY_SECOND;
38    let hour = day_secs / ONE_HOUR_SECOND;
39    let minutes = day_secs % ONE_HOUR_SECOND / ONE_MINUTE_SECOND;
40    let second = day_secs % ONE_MINUTE_SECOND;
41
42    let (millis, micros, nano) = (nanos / 1_000_000, nanos / 1000 % 1000, nanos % 1000);
43
44    let mut format = String::new();
45    accrual(year, TimeUnit::Year, &mut format);
46    accrual(month, TimeUnit::Month, &mut format);
47    accrual(week, TimeUnit::Week, &mut format);
48    accrual(day, TimeUnit::Day, &mut format);
49    accrual(hour, TimeUnit::Hour, &mut format);
50    accrual(minutes, TimeUnit::Minute, &mut format);
51    accrual(second, TimeUnit::Second, &mut format);
52    accrual(millis as u64, TimeUnit::MilliSecond, &mut format);
53    accrual(micros as u64, TimeUnit::MicroSecond, &mut format);
54    accrual(nano as u64, TimeUnit::NanoSecond, &mut format);
55
56    format
57}
58
59impl HumanFormat for Duration {
60    fn human_format(&self) -> String {
61        let seconds = self.as_secs();
62        let nanos = self.subsec_nanos();
63        format_inner(seconds, nanos)
64    }
65}
66
67#[cfg(all(feature = "chrono", feature = "serde"))]
68use chrono::Duration as CDuration;
69
70#[cfg(all(feature = "time", feature = "serde"))]
71use time::Duration as TDuration;
72
73#[cfg(all(feature = "chrono", feature = "serde"))]
74impl HumanFormat for CDuration {
75    fn human_format(&self) -> String {
76        let seconds = self.num_seconds() as _;
77        let nanos = self.subsec_nanos() as _;
78        format_inner(seconds, nanos)
79    }
80}
81
82#[cfg(all(feature = "time", feature = "serde"))]
83impl HumanFormat for TDuration {
84    fn human_format(&self) -> String {
85        let seconds = self.as_seconds_f64() as _;
86        let nanos = self.subsec_nanoseconds() as _;
87        format_inner(seconds, nanos)
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94    use crate::parse;
95
96    #[test]
97    fn test_human_format() {
98        let duration = parse("0s").unwrap();
99        assert_eq!(duration.human_format(), "0s");
100
101        let duration = parse("1y 12d 3s").unwrap();
102        assert_eq!(duration.human_format(), "1y 1w 5d 3s");
103
104        let duration = parse("751d 1mon 3week 5d 2ns").unwrap();
105        assert_eq!(duration.human_format(), "2y 2mon 2w 3d 2ns");
106
107        let duration = parse("    7h    ").unwrap();
108        assert_eq!(duration.human_format(), "7h");
109
110        let duration = parse("    7h  1s  ").unwrap();
111        assert_eq!(duration.human_format(), "7h 1s");
112
113        let duration = parse("    7h  0s  ").unwrap();
114        assert_eq!(duration.human_format(), "7h");
115    }
116
117    #[cfg(all(feature = "serde", feature = "chrono"))]
118    #[test]
119    fn test_human_format_chrono() {
120        let duration = crate::parse_chrono("0s").unwrap();
121        assert_eq!(duration.human_format(), "0s");
122
123        let duration = crate::parse_chrono("1y 12d 3s").unwrap();
124        assert_eq!(duration.human_format(), "1y 1w 5d 3s");
125
126        let duration = crate::parse_chrono("751d 1mon 3week 5d 2ns").unwrap();
127        assert_eq!(duration.human_format(), "2y 2mon 2w 3d 2ns");
128
129        let duration = crate::parse_chrono("    7h    ").unwrap();
130        assert_eq!(duration.human_format(), "7h");
131
132        let duration = crate::parse_chrono("    7h  1s  ").unwrap();
133        assert_eq!(duration.human_format(), "7h 1s");
134
135        let duration = crate::parse_chrono("    7h  0s  ").unwrap();
136        assert_eq!(duration.human_format(), "7h");
137    }
138
139    #[cfg(all(feature = "serde", feature = "time"))]
140    #[test]
141    fn test_human_format_time() {
142        let duration = crate::parse_time("0s").unwrap();
143        assert_eq!(duration.human_format(), "0s");
144
145        let duration = crate::parse_time("1y 12d 3s").unwrap();
146        assert_eq!(duration.human_format(), "1y 1w 5d 3s");
147
148        let duration = crate::parse_time("751d 1mon 3week 5d 2ns").unwrap();
149        assert_eq!(duration.human_format(), "2y 2mon 2w 3d 2ns");
150
151        let duration = crate::parse_time("    7h    ").unwrap();
152        assert_eq!(duration.human_format(), "7h");
153
154        let duration = crate::parse_time("    7h  1s  ").unwrap();
155        assert_eq!(duration.human_format(), "7h 1s");
156
157        let duration = crate::parse_time("    7h  0s  ").unwrap();
158        assert_eq!(duration.human_format(), "7h");
159    }
160}