| |
| |
| |
| |
|
|
| use std::fmt; |
|
|
| pub use super::build::BUILD_EPOCH; |
|
|
| |
| #[cfg(unix)] |
| pub const EPOCH: std::time::SystemTime = unsafe { |
| #[allow(dead_code)] |
| struct UnixSystemTime { |
| tv_sec: i64, |
| tv_nsec: u32, |
| } |
|
|
| ::core::intrinsics::transmute_unchecked(UnixSystemTime { |
| tv_sec: 1734915448, |
| tv_nsec: 0, |
| }) |
| }; |
|
|
| |
| #[cfg(windows)] |
| pub const EPOCH: std::time::SystemTime = unsafe { |
| #[allow(dead_code)] |
| struct WindowsFileTime { |
| dw_low_date_time: u32, |
| dw_high_date_time: u32, |
| } |
|
|
| const INTERVALS_PER_SEC: u64 = 10_000_000; |
| const INTERVALS_TO_UNIX_EPOCH: u64 = 11_644_473_600 * INTERVALS_PER_SEC; |
| const TARGET_INTERVALS: u64 = INTERVALS_TO_UNIX_EPOCH + 1734915448 * INTERVALS_PER_SEC; |
|
|
| ::core::intrinsics::transmute_unchecked(WindowsFileTime { |
| dw_low_date_time: TARGET_INTERVALS as u32, |
| dw_high_date_time: (TARGET_INTERVALS >> 32) as u32, |
| }) |
| }; |
|
|
| |
| |
| |
| pub fn print_project_age() { |
| let age = ProjectAge::since_epoch(); |
| println!("Project started {age} ago"); |
| } |
|
|
| |
| |
| |
| pub fn print_build_age() { |
| let age = BuildAge::since_build(); |
| println!("Program built {age} ago"); |
| } |
|
|
| |
| |
| |
| #[derive(Debug, Clone, Copy)] |
| struct ProjectAge { |
| years: u64, |
| months: u64, |
| days: u64, |
| } |
|
|
| impl ProjectAge { |
| |
| |
| |
| |
| |
| #[inline] |
| pub fn since_epoch() -> Self { |
| let duration = std::time::SystemTime::now() |
| .duration_since(EPOCH) |
| .expect("system time before program epoch"); |
|
|
| Self::from_days(duration.as_secs() / 86400) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| #[inline] |
| fn from_days(total_days: u64) -> Self { |
| if total_days == 0 { |
| return Self { |
| years: 0, |
| months: 0, |
| days: 0, |
| }; |
| } |
|
|
| let years = total_days / 365; |
| let remaining_days = total_days % 365; |
|
|
| |
| let leap_adjustment = years / 4; |
| let adjusted_days = remaining_days.saturating_sub(leap_adjustment); |
|
|
| let months = adjusted_days / 30; |
| let days = adjusted_days % 30; |
|
|
| Self { |
| years, |
| months, |
| days, |
| } |
| } |
| } |
|
|
| impl fmt::Display for ProjectAge { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| match (self.years, self.months, self.days) { |
| (0, 0, 0) => write!(f, "less than 1 day"), |
| (0, 0, d) => write!(f, "{d} day{}", if d == 1 { "" } else { "s" }), |
| (0, m, 0) => write!(f, "{m} month{}", if m == 1 { "" } else { "s" }), |
| (0, m, d) => write!( |
| f, |
| "{m} month{} and {d} day{}", |
| if m == 1 { "" } else { "s" }, |
| if d == 1 { "" } else { "s" } |
| ), |
| (y, 0, 0) => write!(f, "{y} year{}", if y == 1 { "" } else { "s" }), |
| (y, 0, d) => write!( |
| f, |
| "{y} year{} and {d} day{}", |
| if y == 1 { "" } else { "s" }, |
| if d == 1 { "" } else { "s" } |
| ), |
| (y, m, 0) => write!( |
| f, |
| "{y} year{} and {m} month{}", |
| if y == 1 { "" } else { "s" }, |
| if m == 1 { "" } else { "s" } |
| ), |
| (y, m, d) => write!( |
| f, |
| "{y} year{}, {m} month{}, and {d} day{}", |
| if y == 1 { "" } else { "s" }, |
| if m == 1 { "" } else { "s" }, |
| if d == 1 { "" } else { "s" } |
| ), |
| } |
| } |
| } |
|
|
| |
| |
| |
| #[derive(Debug, Clone, Copy)] |
| struct BuildAge { |
| minutes: u64, |
| } |
|
|
| impl BuildAge { |
| |
| |
| |
| |
| |
| #[inline] |
| pub fn since_build() -> Self { |
| let duration = std::time::SystemTime::now() |
| .duration_since(BUILD_EPOCH) |
| .expect("system time before build epoch"); |
|
|
| Self { |
| minutes: duration.as_secs() / 60, |
| } |
| } |
| } |
|
|
| impl fmt::Display for BuildAge { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| match self.minutes { |
| 0 => write!(f, "just now"), |
| 1 => write!(f, "1 minute"), |
| m if m < 60 => write!(f, "{m} minutes"), |
| m if m < 120 => { |
| let mins = m % 60; |
| if mins == 0 { |
| write!(f, "1 hour") |
| } else { |
| write!( |
| f, |
| "1 hour and {mins} minute{}", |
| if mins == 1 { "" } else { "s" } |
| ) |
| } |
| } |
| m if m < 1440 => { |
| let hours = m / 60; |
| let mins = m % 60; |
| if mins == 0 { |
| write!(f, "{hours} hour{}", if hours == 1 { "" } else { "s" }) |
| } else { |
| write!( |
| f, |
| "{hours} hour{} and {mins} minute{}", |
| if hours == 1 { "" } else { "s" }, |
| if mins == 1 { "" } else { "s" } |
| ) |
| } |
| } |
| m => { |
| let days = m / 1440; |
| let remaining_hours = (m % 1440) / 60; |
| if remaining_hours == 0 { |
| write!(f, "{days} day{}", if days == 1 { "" } else { "s" }) |
| } else { |
| write!( |
| f, |
| "{days} day{} and {remaining_hours} hour{}", |
| if days == 1 { "" } else { "s" }, |
| if remaining_hours == 1 { "" } else { "s" } |
| ) |
| } |
| } |
| } |
| } |
| } |
|
|
| #[cfg(test)] |
| mod tests { |
| use super::*; |
|
|
| #[test] |
| fn test_project_age_display() { |
| let age = ProjectAge { |
| years: 0, |
| months: 0, |
| days: 0, |
| }; |
| assert_eq!(format!("{}", age), "less than 1 day"); |
|
|
| let age = ProjectAge { |
| years: 1, |
| months: 2, |
| days: 3, |
| }; |
| assert_eq!(format!("{}", age), "1 year, 2 months, and 3 days"); |
|
|
| let age = ProjectAge { |
| years: 2, |
| months: 1, |
| days: 1, |
| }; |
| assert_eq!(format!("{}", age), "2 years, 1 month, and 1 day"); |
| } |
|
|
| #[test] |
| fn test_build_age_display() { |
| let age = BuildAge { minutes: 0 }; |
| assert_eq!(format!("{}", age), "just now"); |
|
|
| let age = BuildAge { minutes: 1 }; |
| assert_eq!(format!("{}", age), "1 minute"); |
|
|
| let age = BuildAge { minutes: 65 }; |
| assert_eq!(format!("{}", age), "1 hour and 5 minutes"); |
|
|
| let age = BuildAge { minutes: 120 }; |
| assert_eq!(format!("{}", age), "2 hours"); |
| } |
| } |
|
|