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())) ) } }