use std::sync::atomic::{AtomicI64, Ordering};
#[allow(dead_code)]
pub trait Clock: Send + Sync {
fn now(&self) -> i64;
}
#[allow(dead_code)]
pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs().min(i64::MAX as u64) as i64)
.unwrap_or(0) }
}
#[allow(dead_code)]
pub struct FixedClock {
secs: AtomicI64,
}
impl FixedClock {
#[allow(dead_code)]
pub fn new(secs: i64) -> Self {
Self {
secs: AtomicI64::new(secs),
}
}
#[allow(dead_code)]
pub fn advance_days(&self, days: f64) {
let delta = (days * 86_400.0) as i64;
self.secs.fetch_add(delta, Ordering::SeqCst);
}
}
impl Clock for FixedClock {
fn now(&self) -> i64 {
self.secs.load(Ordering::SeqCst)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_system_clock_returns_a_plausible_unix_time() {
assert!(SystemClock.now() > 1_577_836_800);
}
#[test]
fn test_fixed_clock_is_deterministic_and_advanceable() {
let c = FixedClock::new(1_000);
assert_eq!(c.now(), 1_000);
assert_eq!(c.now(), 1_000); c.advance_days(2.0);
assert_eq!(c.now(), 1_000 + 2 * 86_400);
c.advance_days(0.5);
assert_eq!(c.now(), 1_000 + 2 * 86_400 + 43_200);
}
#[test]
fn test_system_clock_now_is_non_negative() {
let t = SystemClock.now();
assert!(t >= 0, "B3: SystemClock::now() must be >= 0, got {t}");
}
#[test]
fn test_fixed_clock_shared_behind_trait_object() {
let c = FixedClock::new(500);
let dyn_clock: &dyn Clock = &c;
assert_eq!(dyn_clock.now(), 500);
c.advance_days(1.0);
assert_eq!(dyn_clock.now(), 500 + 86_400); }
}