cror / src /common /time.rs
xcolorday's picture
Upload 442 files
6b356c4 verified
Raw
History Blame Contribute Delete
7.97 kB
//! 项目和构建时间追踪模块
//!
//! 提供跨平台的时间基准点定义和优雅的时间差显示功能。
//! 支持项目启动时间(按天计算)和构建时间(按分钟计算)的追踪。
use std::fmt;
pub use super::build::BUILD_EPOCH;
/// Unix 系统的时间基准点(2024-12-23 01:30:48 UTC)
#[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,
})
};
/// Windows 系统的时间基准点(2024-12-23 01:30:48 UTC)
#[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 {
/// 计算自项目 EPOCH 以来的时间
///
/// # Panics
///
/// 如果系统时间早于项目 EPOCH 时间则会 panic
#[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)
}
/// 从总天数创建 ProjectAge 实例
///
/// 使用简化的时间计算:
/// - 1年 = 365天
/// - 1月 = 30天
/// - 包含基本的闰年调整
#[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 {
/// 计算自构建 EPOCH 以来的时间
///
/// # Panics
///
/// 如果系统时间早于构建 EPOCH 时间则会 panic
#[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");
}
}