use std::{
sync::Once,
time::{Duration, SystemTime, UNIX_EPOCH},
};
pub trait TimeSource: Send + Sync {
fn now(&self) -> Duration;
}
pub struct SystemTimeSource;
impl TimeSource for SystemTimeSource {
fn now(&self) -> Duration {
duration_since_epoch(SystemTime::now())
}
}
pub struct FixedTimeSource {
duration: Duration,
}
impl FixedTimeSource {
pub fn new(duration: Duration) -> Self {
Self { duration }
}
}
impl TimeSource for FixedTimeSource {
fn now(&self) -> Duration {
self.duration
}
}
pub(crate) fn duration_since_epoch(time: SystemTime) -> Duration {
if let Ok(d) = time.duration_since(UNIX_EPOCH) {
d
} else {
static WARN_ONCE: Once = Once::new();
WARN_ONCE.call_once(|| {
tracing::warn!(
"system clock is before Unix epoch; \
timestamps will be zero until clock is corrected"
);
});
Duration::ZERO
}
}
#[cfg(test)]
#[expect(clippy::allow_attributes, reason = "blanket test suppressions")]
#[allow(clippy::unwrap_used, reason = "tests")]
mod tests {
use super::*;
#[test]
fn system_time_source_returns_nonzero() {
let ts = SystemTimeSource;
assert!(ts.now().as_secs() > 0, "wall clock should be after epoch");
}
#[test]
fn fixed_time_source_returns_exact_value() {
let d = Duration::from_secs(1_700_000_000);
let ts = FixedTimeSource::new(d);
assert_eq!(ts.now(), d, "should return the fixed duration");
}
#[test]
fn pre_epoch_returns_zero() {
let pre_epoch = UNIX_EPOCH - Duration::from_secs(1);
let result = duration_since_epoch(pre_epoch);
assert_eq!(result, Duration::ZERO, "pre-epoch should return ZERO");
}
#[test]
fn post_epoch_returns_positive() {
let result = duration_since_epoch(SystemTime::now());
assert!(result.as_secs() > 0, "post-epoch should return positive duration");
}
}