From df5eec13a031d41232e7407794e2f5b9a0a2d608 Mon Sep 17 00:00:00 2001 From: Jonas Maier <> Date: Sat, 7 Mar 2026 20:49:52 +0100 Subject: history with relative time --- src/date.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/date.rs (limited to 'src/date.rs') diff --git a/src/date.rs b/src/date.rs new file mode 100644 index 0000000..149dc79 --- /dev/null +++ b/src/date.rs @@ -0,0 +1,113 @@ +use core::fmt; +use std::{ + process::Command, + time::{Duration, SystemTime}, +}; + +use crate::BString; + +#[derive(Clone)] +pub struct DateTime { + sys: SystemTime, +} + +impl DateTime { + pub fn from_unix(unix: u64) -> Self { + Self { + sys: SystemTime::UNIX_EPOCH + Duration::from_secs(unix), + } + } + pub fn now() -> Self { + Self { + sys: SystemTime::now(), + } + } + fn format(&self) -> std::io::Result { + let mut out = Command::new("date") + .arg("-d") + .arg(format!( + "@{}", + self.sys + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() + )) + .output()? + .stdout; + while let Some(x) = out.last() + && x.is_ascii_whitespace() + { + out.pop(); + } + Ok(out) + } + + fn unix(&self) -> u64 { + self.sys + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() + } + + pub fn relative_to(&self, other: &Self) -> String { + let a = self.unix(); + let b = other.unix(); + + let (pre, post, mut diff) = if a < b { + ("in ", "", b - a) + } else { + ("", " ago", a - b) + }; + + let unit = if diff < 60 { + "s" + } else if { + diff /= 60; + diff < 60 + } { + "m" + } else if { + diff /= 60; + diff < 24 + } { + "h" + } else if { + diff /= 24; + diff < 365 + } { + "d" + } else { + diff /= 365; + "y" + }; + + format!("{pre}{diff}{unit}{post}") + } + + pub const fn longest_reasonable_delta() -> usize { + 7 + } +} + +#[test] +fn long_delta() { + assert_eq!( + DateTime::now() + .relative_to(&DateTime { + // 30 years ago + sys: SystemTime::now() - Duration::from_secs(60 * 60 * 24 * 365 * 30) + }) + .len(), + DateTime::longest_reasonable_delta() + ); +} + +impl fmt::Display for DateTime { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + String::from_utf8_lossy(&self.format().unwrap_or_else(|_| Vec::new())) + ) + } +} -- cgit v1.2.3