aboutsummaryrefslogtreecommitdiffstats
path: root/src/date.rs
diff options
context:
space:
mode:
authorJonas Maier <>2026-03-07 20:49:52 +0100
committerJonas Maier <>2026-03-07 20:49:52 +0100
commitdf5eec13a031d41232e7407794e2f5b9a0a2d608 (patch)
treea76fba2ed68928d82d9b8352bfd0afc33d118fb0 /src/date.rs
parent86cdb8c21dec737a3f0a40311782de851c1203d1 (diff)
downloadpish-df5eec13a031d41232e7407794e2f5b9a0a2d608.tar.gz
history with relative time
Diffstat (limited to 'src/date.rs')
-rw-r--r--src/date.rs113
1 files changed, 113 insertions, 0 deletions
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<BString> {
+ 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()))
+ )
+ }
+}