#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timestamp(i64);
impl Timestamp {
#[must_use]
pub const fn from_unix(seconds: i64) -> Self {
Self(seconds)
}
#[must_use]
pub const fn unix(self) -> i64 {
self.0
}
}
pub trait Clock: Send + Sync {
fn now(&self) -> Timestamp;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> Timestamp {
let seconds = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|elapsed| i64::try_from(elapsed.as_secs()).unwrap_or(i64::MAX))
.unwrap_or(0);
Timestamp::from_unix(seconds)
}
}
#[derive(Debug, Clone, Copy)]
pub struct FixedClock {
at: Timestamp,
}
impl FixedClock {
#[must_use]
pub const fn new(at: Timestamp) -> Self {
Self { at }
}
}
impl Clock for FixedClock {
fn now(&self) -> Timestamp {
self.at
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fixed_clock_reports_the_given_instant() {
let clock = FixedClock::new(Timestamp::from_unix(1_752_400_000));
assert_eq!(clock.now().unix(), 1_752_400_000);
assert_eq!(clock.now(), clock.now());
}
#[test]
fn system_clock_is_after_2020() {
assert!(SystemClock.now().unix() > 1_600_000_000);
}
#[test]
fn timestamps_order_naturally() {
assert!(Timestamp::from_unix(1) < Timestamp::from_unix(2));
let debug = format!(
"{:?} {:?}",
Timestamp::from_unix(0),
FixedClock::new(Timestamp::from_unix(0))
);
assert!(debug.contains("Timestamp"));
}
}