1use std::fmt;
3use std::time;
4
5#[derive(Clone, Debug)]
7pub struct FormattedTime {
8 prefix: &'static str,
9 suffix: &'static str,
10 duration: time::Duration,
11 t: time::SystemTime,
12}
13
14impl fmt::Display for FormattedTime {
15 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16 write!(
17 f,
18 "{} ({}{}{})",
19 humantime::format_rfc3339_seconds(self.t),
20 self.prefix,
21 humantime::format_duration(self.duration),
22 self.suffix
23 )
24 }
25}
26
27pub fn format_time(t: time::SystemTime) -> FormattedTime {
29 format_time_wrt(t, time::SystemTime::now())
30}
31
32pub fn format_time_wrt(t: time::SystemTime, now: time::SystemTime) -> FormattedTime {
40 let (prefix, suffix, duration) = match now.duration_since(t) {
41 Ok(duration) => ("", " ago", duration),
42 Err(err) => ("in ", "", err.duration()),
43 };
44
45 let duration = time::Duration::from_secs(duration.as_secs());
46
47 FormattedTime {
48 prefix,
49 suffix,
50 duration,
51 t,
52 }
53}
54
55pub mod human_duration {
56 use serde::{Deserialize as _, de::Error as _};
57
58 pub fn deserialize<'de, D>(d: D) -> Result<core::time::Duration, D::Error>
59 where
60 D: serde::Deserializer<'de>,
61 {
62 let s = String::deserialize(d)?;
65
66 humantime::parse_duration(&s).map_err(D::Error::custom)
67 }
68
69 pub fn serialize<S>(duration: &core::time::Duration, s: S) -> Result<S::Ok, S::Error>
70 where
71 S: serde::ser::Serializer,
72 {
73 s.collect_str(&humantime::format_duration(*duration))
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn test_format_time() {
83 let epoch = time::UNIX_EPOCH;
84 let t = epoch + time::Duration::from_secs_f64(0.002003004);
85 assert_eq!(
86 format_time_wrt(t, epoch).to_string(),
87 "1970-01-01T00:00:00Z (in 0s)".to_string()
88 );
89 assert_eq!(
90 format_time_wrt(epoch, t).to_string(),
91 "1970-01-01T00:00:00Z (0s ago)".to_string()
92 );
93 let t2 = epoch + time::Duration::from_secs_f64(1699535603.723209);
94 assert_eq!(
95 format_time_wrt(epoch, t2).to_string(),
96 "1970-01-01T00:00:00Z (53years 10months 7days 21h 37m 23s ago)".to_string()
97 );
98 assert_eq!(
99 format_time_wrt(t2, epoch).to_string(),
100 "2023-11-09T13:13:23Z (in 53years 10months 7days 21h 37m 23s)".to_string()
101 );
102 }
103
104 #[derive(serde::Deserialize, serde::Serialize)]
105 struct TestStruct {
106 #[serde(with = "human_duration")]
107 duration: core::time::Duration,
108 }
109
110 #[test]
111 fn test_human_duration() {
112 let ts: TestStruct = serde_json::from_str(r#"{"duration": "1w 5s"}"#).unwrap();
113 assert_eq!(
114 ts.duration,
115 core::time::Duration::from_secs(5 + 7 * 24 * 60 * 60)
116 );
117 assert_eq!(
118 serde_json::to_string(&ts).unwrap(),
119 r#"{"duration":"7days 5s"}"#.to_string()
120 );
121 }
122}