| 1 | //! Human-readable times. |
| 2 | |
| 3 | use std::time::{SystemTime, UNIX_EPOCH}; |
| 4 | |
| 5 | pub 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". |
| 13 | pub 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. |
| 35 | pub 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)] |
| 42 | mod 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 | } |