basic_human_duration/
lib.rs

1use std::fmt;
2use time::Duration;
3
4pub trait ChronoHumanDuration {
5    type Displayer: fmt::Display;
6    fn format_human(&self) -> Self::Displayer;
7}
8
9impl ChronoHumanDuration for Duration {
10    type Displayer = Displayer;
11    fn format_human(&self) -> Self::Displayer {
12        Displayer { d: *self }
13    }
14}
15
16pub struct Displayer {
17    d: Duration,
18}
19
20impl fmt::Display for Displayer {
21    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22        let mut wrote = false;
23        let d = self.d;
24
25        let months = d.whole_weeks() / 4;
26        if months > 0 {
27            write!(f, "{} month{}", months, if months > 1 { "s" } else { "" })?;
28            wrote = true;
29        } else {
30            let weeks = d.whole_weeks();
31            if weeks > 0 {
32                write!(
33                    f,
34                    "{}{} week{}",
35                    if wrote { ", " } else { "" },
36                    weeks,
37                    if weeks > 1 { "s" } else { "" }
38                )?;
39                wrote = true;
40            } else {
41                let days = d.whole_days();
42                if days > 0 {
43                    write!(
44                        f,
45                        "{}{} day{}",
46                        if wrote { ", " } else { "" },
47                        days,
48                        if days > 1 { "s" } else { "" }
49                    )?;
50                    wrote = true;
51                } else {
52                    let hours = d.whole_hours();
53                    if hours > 0 {
54                        write!(
55                            f,
56                            "{}{} hour{}",
57                            if wrote { ", " } else { "" },
58                            hours,
59                            if hours > 1 { "s" } else { "" }
60                        )?;
61                        wrote = true;
62                    } else {
63                        let minutes = d.whole_minutes();
64                        if minutes > 0 {
65                            write!(
66                                f,
67                                "{}{} minute{}",
68                                if wrote { ", " } else { "" },
69                                minutes,
70                                if minutes > 1 { "s" } else { "" }
71                            )?;
72                            wrote = true;
73                        }
74                    }
75                }
76            }
77        }
78
79        if wrote {
80            write!(f, " ago")
81        } else {
82            write!(f, "just now")
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use time::Duration;
90    use crate::ChronoHumanDuration;
91
92    #[test]
93    fn it_works() {
94        let d = Duration::weeks(2) + Duration::days(3) + Duration::hours(2) + Duration::minutes(20);
95        assert_eq!(d.format_human().to_string(), "2 weeks ago");
96        let d = Duration::weeks(1);
97        assert_eq!(d.format_human().to_string(), "1 week ago");
98        let d = Duration::days(2) + Duration::hours(2);
99        assert_eq!(d.format_human().to_string(), "2 days ago");
100        let d = Duration::days(1);
101        assert_eq!(d.format_human().to_string(), "1 day ago");
102        let d = Duration::hours(2);
103        assert_eq!(d.format_human().to_string(), "2 hours ago");
104        let d = Duration::hours(1);
105        assert_eq!(d.format_human().to_string(), "1 hour ago");
106        let d = Duration::minutes(2);
107        assert_eq!(d.format_human().to_string(), "2 minutes ago");
108        let d = Duration::minutes(1);
109        assert_eq!(d.format_human().to_string(), "1 minute ago");
110        let d = Duration::seconds(60);
111        assert_eq!(d.format_human().to_string(), "1 minute ago");
112        let d = Duration::seconds(59);
113        assert_eq!(d.format_human().to_string(), "just now");
114        let d = Duration::seconds(1);
115        assert_eq!(d.format_human().to_string(), "just now");
116    }
117}