use std::time::{Duration, SystemTime as StdSystemTime};
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct SystemTime(u64 );
impl SystemTime {
pub const UNIX_EPOCH: Self = Self(0);
#[inline]
pub fn now() -> Self {
#[cfg(any(test, feature = "test-util"))]
if let Some(now_ns) = mock::NOW_NS.with(|t| t.get()) {
return Self(now_ns);
}
StdSystemTime::now().into()
}
#[inline]
pub fn from_unix_time_nanos(nanos: u64) -> Self {
Self(nanos)
}
#[inline]
pub fn to_unix_time_secs(&self) -> u64 {
self.to_unix_time_nanos() / 1_000_000_000
}
#[inline]
pub fn to_unix_time_nanos(&self) -> u64 {
self.0
}
}
impl From<StdSystemTime> for SystemTime {
fn from(sys_time: StdSystemTime) -> Self {
let unix_time_ns = sys_time
.duration_since(StdSystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
Self(unix_time_ns)
}
}
impl From<SystemTime> for StdSystemTime {
fn from(sys_time: SystemTime) -> Self {
StdSystemTime::UNIX_EPOCH + Duration::from_nanos(sys_time.0)
}
}
#[cfg(any(test, feature = "test-util"))]
pub use mock::{with_system_time_mock, SystemTimeMock};
#[cfg(any(test, feature = "test-util"))]
mod mock {
use std::cell::Cell;
use super::*;
thread_local! {
pub(super) static NOW_NS: Cell<Option<u64>> = const { Cell::new(None) };
}
pub fn with_system_time_mock(f: impl FnOnce(SystemTimeMock)) {
NOW_NS.with(|t| t.set(Some(0)));
f(SystemTimeMock);
NOW_NS.with(|t| t.set(None));
}
#[non_exhaustive]
pub struct SystemTimeMock;
impl SystemTimeMock {
pub fn advance(&self, duration: Duration) {
NOW_NS.with(|t| {
let mut now_ns = t.get().expect("use of moved system time mock");
now_ns += duration.as_nanos() as u64;
t.set(Some(now_ns));
})
}
}
}