Krolyxon
Commit 55c1225

ferreniumsrcrendertime.rs

63 lines · 2.0 KB · Rust

1//! Human-readable times.
2
3use std::time::{SystemTime, UNIX_EPOCH};
4
5pub fn now() -> i64 {
6 SystemTime::now()
7 .duration_since(UNIX_EPOCH)
8 .map_or(0, |d| d.as_secs() as i64)
9}
10
11/// "3 hours ago", relative to `now` (both unix seconds). Future times
12/// (clock skew) read as "just now".
13pub fn relative(then: i64, now: i64) -> String {
14 const MIN: i64 = 60;
15 const HOUR: i64 = 60 * MIN;
16 const DAY: i64 = 24 * HOUR;
17 const WEEK: i64 = 7 * DAY;
18 const MONTH: i64 = 30 * DAY;
19 const YEAR: i64 = 365 * DAY;
20
21 let delta = now - then;
22 let (n, unit) = match delta {
23 d if d < MIN => return "just now".into(),
24 d if d < HOUR => (d / MIN, "minute"),
25 d if d < DAY => (d / HOUR, "hour"),
26 d if d < 2 * WEEK => (d / DAY, "day"),
27 d if d < 2 * MONTH => (d / WEEK, "week"),
28 d if d < YEAR => (d / MONTH, "month"),
29 d => (d / YEAR, "year"),
30 };
31 format!("{n} {unit}{} ago", if n == 1 { "" } else { "s" })
32}
33
34/// RFC 3339 UTC timestamp, for `<time datetime>` and tooltips.
35pub fn iso(then: i64) -> String {
36 jiff::Timestamp::from_second(then)
37 .map(|t| t.strftime("%Y-%m-%dT%H:%M:%SZ").to_string())
38 .unwrap_or_default()
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn relative_times() {
47 let now = 1_000_000_000;
48 assert_eq!(relative(now + 5, now), "just now");
49 assert_eq!(relative(now - 59, now), "just now");
50 assert_eq!(relative(now - 60, now), "1 minute ago");
51 assert_eq!(relative(now - 2 * 3600, now), "2 hours ago");
52 assert_eq!(relative(now - 86400, now), "1 day ago");
53 assert_eq!(relative(now - 13 * 86400, now), "13 days ago");
54 assert_eq!(relative(now - 15 * 86400, now), "2 weeks ago");
55 assert_eq!(relative(now - 90 * 86400, now), "3 months ago");
56 assert_eq!(relative(now - 800 * 86400, now), "2 years ago");
57 }
58
59 #[test]
60 fn iso_format() {
61 assert_eq!(iso(0), "1970-01-01T00:00:00Z");
62 }
63}