hippmem_core/time.rs
1//! Timestamp newtype (Timestamp) and the injectable Clock trait.
2//!
3//! Corresponds to ADR-007 and 02#0. Constitution §4.3: all "now" inside the library MUST be obtained via the Clock trait,
4//! and **MUST NOT** call `SystemTime::now()` directly.
5
6use serde::{Deserialize, Serialize};
7
8/// Unix millisecond timestamp (UTC). An i64 newtype. See ADR-007.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
10pub struct Timestamp(pub i64);
11
12impl Timestamp {
13 /// Extracts the inner i64 value (Unix milliseconds).
14 pub fn as_i64(&self) -> i64 {
15 self.0
16 }
17
18 /// Constructs from Unix milliseconds.
19 pub fn from_millis(ms: i64) -> Self {
20 Self(ms)
21 }
22}
23
24// ── Clock trait ──
25
26/// Injectable clock: all "now" inside the library is obtained through this trait.
27///
28/// See constitution §4.3 (test reproducibility), ADR-007.
29pub trait Clock {
30 /// Returns the current UTC timestamp (Unix milliseconds).
31 fn now(&self) -> Timestamp;
32}
33
34/// System clock: uses `std::time::SystemTime::now()` to obtain real time.
35///
36/// **For application layer and factory methods only**; library logic MUST obtain time through the `Clock` trait.
37pub struct SystemClock;
38
39impl Clock for SystemClock {
40 fn now(&self) -> Timestamp {
41 let ms = std::time::SystemTime::now()
42 .duration_since(std::time::UNIX_EPOCH)
43 .unwrap_or_default()
44 .as_millis() as i64;
45 Timestamp(ms)
46 }
47}
48
49/// Fixed clock: returns a fixed Timestamp, used for testing.
50pub struct FixedClock {
51 timestamp: Timestamp,
52}
53
54impl FixedClock {
55 /// Creates a fixed clock that always returns the given timestamp.
56 pub fn new(timestamp: Timestamp) -> Self {
57 Self { timestamp }
58 }
59
60 /// Sets a new fixed timestamp.
61 pub fn set(&mut self, timestamp: Timestamp) {
62 self.timestamp = timestamp;
63 }
64}
65
66impl Clock for FixedClock {
67 fn now(&self) -> Timestamp {
68 self.timestamp
69 }
70}