Skip to main content

orbit_core/
epoch.rs

1//! Tick and epoch helpers for Orbit frame versions.
2//!
3//! Ring counters answer "where in this ring did the write land?".
4//! An [`OrbitEpoch`] answers "when did this sample claim to be
5//! captured?". Keeping that distinction explicit lets semantic layers
6//! use `Frame::ver` consistently without making the ring itself know
7//! about clocks.
8
9use std::time::{Duration, SystemTime, UNIX_EPOCH};
10
11/// Milliseconds since the Unix epoch, carried in `Frame::ver` by
12/// time-sensitive Orbit primitives.
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[repr(transparent)]
15pub struct OrbitEpoch(u64);
16
17impl OrbitEpoch {
18    pub const ZERO: Self = Self(0);
19
20    pub fn now() -> Self {
21        Self::from_system_time(SystemTime::now())
22    }
23
24    pub fn from_system_time(time: SystemTime) -> Self {
25        let millis = time
26            .duration_since(UNIX_EPOCH)
27            .unwrap_or_default()
28            .as_millis()
29            .min(u128::from(u64::MAX)) as u64;
30        Self(millis)
31    }
32
33    pub const fn from_unix_ms(epoch_ms: u64) -> Self {
34        Self(epoch_ms)
35    }
36
37    pub const fn as_unix_ms(self) -> u64 {
38        self.0
39    }
40
41    pub fn age_at(
42        self,
43        now: Self
44    ) -> Duration {
45        Duration::from_millis(now.0.saturating_sub(self.0))
46    }
47
48    pub fn is_fresh_at(
49        self,
50        now: Self,
51        max_age: Duration
52    ) -> bool {
53        self.age_at(now) <= max_age
54    }
55}
56
57impl From<u64> for OrbitEpoch {
58    fn from(value: u64) -> Self {
59        Self::from_unix_ms(value)
60    }
61}
62
63impl From<OrbitEpoch> for u64 {
64    fn from(value: OrbitEpoch) -> Self {
65        value.as_unix_ms()
66    }
67}
68
69impl std::fmt::Display for OrbitEpoch {
70    fn fmt(
71        &self,
72        f: &mut std::fmt::Formatter<'_>
73    ) -> std::fmt::Result {
74        write!(f, "{}", self.0)
75    }
76}