use time::OffsetDateTime;
pub trait Clock: Send + Sync {
fn now(&self) -> OffsetDateTime;
}
pub(crate) fn nanos(at: OffsetDateTime) -> i64 {
i64::try_from(at.unix_timestamp_nanos()).expect("a time before 2262")
}
pub(crate) fn from_nanos(at: i64) -> Option<OffsetDateTime> {
OffsetDateTime::from_unix_timestamp_nanos(i128::from(at)).ok()
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> OffsetDateTime {
OffsetDateTime::now_utc()
}
}
#[cfg(test)]
pub(crate) use manual::ManualClock;
#[cfg(test)]
mod manual {
use std::sync::Mutex;
use time::OffsetDateTime;
use super::Clock;
pub(crate) struct ManualClock(Mutex<OffsetDateTime>);
impl ManualClock {
pub(crate) fn at(now: OffsetDateTime) -> ManualClock {
ManualClock(Mutex::new(now))
}
pub(crate) fn set(&self, now: OffsetDateTime) {
*self.0.lock().expect("clock") = now;
}
}
impl Clock for ManualClock {
fn now(&self) -> OffsetDateTime {
*self.0.lock().expect("clock")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_system_clock_is_close_to_now() {
let before = OffsetDateTime::now_utc();
let now = SystemClock.now();
let after = OffsetDateTime::now_utc();
assert!(
before <= now && now <= after,
"the system clock reads the time now: {before} <= {now} <= {after}"
);
assert!(
now.offset().is_utc(),
"the trait promises UTC, and comparing instants alone cannot tell"
);
}
}