Skip to main content

ppoppo_clock/
native.rs

1use crate::{Clock, Timer};
2use futures::future::BoxFuture;
3use jiff::tz::TimeZone;
4use jiff::Timestamp;
5use std::time::Duration;
6
7/// Real wall clock backed by the system clock.
8pub struct WallClock;
9
10impl Clock for WallClock {
11    fn now(&self) -> Timestamp {
12        Timestamp::now()
13    }
14}
15
16/// Read the host's local timezone from the OS (`TZ` env var, then
17/// `/etc/localtime`) and resolve it in jiff's tzdb.
18///
19/// Falls back to `TimeZone::UTC` when the OS zone can't be determined — the
20/// same degraded default as [`crate::wasm::browser_local_tz`], its wasm twin.
21/// Call once at the composition root (e.g. CCC's `Session`); do not sample it
22/// per render site (that would reintroduce the ambient-authority leak the
23/// clock port exists to prevent).
24pub fn system_tz() -> TimeZone {
25    TimeZone::system()
26}
27
28/// Tokio-backed async timer.
29pub struct TokioTimer;
30
31impl Timer for TokioTimer {
32    fn sleep(&self, dur: Duration) -> BoxFuture<'static, ()> {
33        Box::pin(async move {
34            tokio::time::sleep(dur).await;
35        })
36    }
37
38    fn next_tick(&self) -> BoxFuture<'static, ()> {
39        Box::pin(async {
40            tokio::task::yield_now().await;
41        })
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use crate::Clock as ClockTrait;
49    use crate::Timer as TimerTrait;
50    use std::sync::Arc;
51    use std::time::Instant;
52
53    fn assert_send_sync<T: Send + Sync>() {}
54
55    #[test]
56    fn wall_clock_and_tokio_timer_are_send_sync() {
57        assert_send_sync::<WallClock>();
58        assert_send_sync::<TokioTimer>();
59    }
60
61    #[test]
62    fn now_is_recent() {
63        let before = Timestamp::now();
64        let clock = WallClock;
65        let t = clock.now();
66        let after = Timestamp::now();
67        assert!(t >= before - jiff::SignedDuration::from_secs(1));
68        assert!(t <= after + jiff::SignedDuration::from_secs(1));
69    }
70
71    #[test]
72    fn now_unix_millis_positive_and_recent() {
73        let before_ms = Timestamp::now().as_millisecond();
74        let clock = WallClock;
75        let ms = clock.now_unix_millis();
76        let after_ms = Timestamp::now().as_millisecond();
77        assert!(ms > 0, "unix millis must be positive");
78        assert!(ms >= before_ms - 100, "must be >= before sample");
79        assert!(ms <= after_ms + 100, "must be <= after sample");
80    }
81
82    #[tokio::test]
83    async fn tokio_timer_sleep_completes() {
84        let timer = TokioTimer;
85        let start = Instant::now();
86        timer.sleep(Duration::from_millis(50)).await;
87        let elapsed = start.elapsed();
88        assert!(elapsed.as_millis() >= 45, "sleep must take at least 45ms");
89        assert!(elapsed.as_millis() < 500, "sleep must not take more than 500ms");
90    }
91
92    #[tokio::test]
93    async fn tokio_timer_next_tick_completes_immediately() {
94        let timer = TokioTimer;
95        let start = Instant::now();
96        timer.next_tick().await;
97        assert!(start.elapsed().as_millis() < 100);
98    }
99
100    #[test]
101    fn wall_clock_arc_dyn_dispatch() {
102        let c: Arc<dyn ClockTrait> = Arc::new(WallClock);
103        let _ = c.now();
104    }
105
106    #[test]
107    fn tokio_timer_arc_dyn_dispatch() {
108        let t: Arc<dyn TimerTrait> = Arc::new(TokioTimer);
109        let _ = t.sleep(Duration::ZERO);
110    }
111}