Skip to main content

khive_types/
timestamp.rs

1//! Timestamp — microseconds since Unix epoch.
2//!
3//! No clock access — generation happens in host crates.
4
5use core::fmt;
6
7/// Microseconds since the Unix epoch, stored as a `u64`.
8#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[repr(transparent)]
11pub struct Timestamp(u64);
12
13impl Timestamp {
14    /// The Unix epoch (zero microseconds).
15    pub const EPOCH: Self = Self(0);
16    /// The maximum representable timestamp.
17    pub const MAX: Self = Self(u64::MAX);
18
19    /// Construct from a microsecond count.
20    #[inline]
21    pub const fn from_micros(micros: u64) -> Self {
22        Self(micros)
23    }
24
25    /// Construct from a millisecond count (converted to microseconds).
26    #[inline]
27    pub const fn from_millis(millis: u64) -> Self {
28        Self(millis.saturating_mul(1000))
29    }
30
31    /// Construct from a whole-second count (converted to microseconds).
32    #[inline]
33    pub const fn from_secs(secs: u64) -> Self {
34        Self(secs.saturating_mul(1_000_000))
35    }
36
37    /// Return the raw microsecond value.
38    #[inline]
39    pub const fn as_micros(&self) -> u64 {
40        self.0
41    }
42
43    /// Return the value truncated to whole milliseconds.
44    #[inline]
45    pub const fn as_millis(&self) -> u64 {
46        self.0 / 1000
47    }
48
49    /// Return the value truncated to whole seconds.
50    #[inline]
51    pub const fn as_secs(&self) -> u64 {
52        self.0 / 1_000_000
53    }
54
55    /// Return `true` if this is the epoch (zero).
56    #[inline]
57    pub const fn is_zero(&self) -> bool {
58        self.0 == 0
59    }
60
61    /// Add `micros` microseconds, saturating at `u64::MAX`.
62    #[inline]
63    pub const fn saturating_add(self, micros: u64) -> Self {
64        Self(self.0.saturating_add(micros))
65    }
66
67    /// Subtract `micros` microseconds, saturating at zero.
68    #[inline]
69    pub const fn saturating_sub(self, micros: u64) -> Self {
70        Self(self.0.saturating_sub(micros))
71    }
72
73    /// Return the elapsed microseconds between `earlier` and `self`, saturating at zero.
74    pub const fn duration_since(self, earlier: Self) -> u64 {
75        self.0.saturating_sub(earlier.0)
76    }
77}
78
79impl fmt::Debug for Timestamp {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        write!(f, "Timestamp({}µs)", self.0)
82    }
83}
84
85impl fmt::Display for Timestamp {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        write!(f, "{}µs", self.0)
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn conversions() {
97        let ts = Timestamp::from_secs(1700000000);
98        assert_eq!(ts.as_secs(), 1700000000);
99        assert_eq!(ts.as_millis(), 1700000000000);
100        assert_eq!(ts.as_micros(), 1700000000000000);
101    }
102
103    #[test]
104    fn ordering() {
105        let a = Timestamp::from_secs(1);
106        let b = Timestamp::from_secs(2);
107        assert!(a < b);
108    }
109
110    #[test]
111    fn arithmetic() {
112        let ts = Timestamp::from_secs(10);
113        assert_eq!(ts.saturating_add(1_000_000), Timestamp::from_secs(11));
114        assert_eq!(ts.saturating_sub(5_000_000), Timestamp::from_secs(5));
115        assert_eq!(Timestamp::from_secs(0).saturating_sub(1), Timestamp::EPOCH);
116    }
117
118    #[test]
119    fn duration_since() {
120        let a = Timestamp::from_secs(10);
121        let b = Timestamp::from_secs(15);
122        assert_eq!(b.duration_since(a), 5_000_000);
123    }
124}