Skip to main content

devela/phys/time/source/
impl_source.rs

1// devela/src/phys/time/source/impl_source.rs
2//
3//!
4//
5// TOC
6// - blanket bridge
7// - SystemTime
8// - SystemInstant
9// - JsInstant
10//
11// NOTE: other impls in:
12// - phys::time::source::fake::{TimeFake, TimeFakeRef}
13// - sys::os::linux::time::instant::{LinuxInstant, LinuxTime}
14
15use crate::{TimePoint, TimeScale, TimeSource, TimeSourceCfg};
16
17/// Blanket bridge that lifts any non-configurable `TimeSource` into
18/// `TimeSourceCfg` by using `()` as its trivial configuration.
19#[rustfmt::skip]
20impl<T, P> TimeSourceCfg<P> for T
21where
22    T: TimeSource<P>,
23    P: TimePoint,
24{
25    type Config = ();
26
27    fn time_is_monotonic((): ()) -> bool { T::time_is_monotonic() }
28    fn time_is_absolute((): ()) -> bool { T::time_is_absolute() }
29    fn time_scale((): ()) -> TimeScale { T::time_scale() }
30
31    fn time_now((): ()) -> P { T::time_now() }
32    fn time_point_value((): (), point: P) -> u64 { T::time_point_value(point) }
33    fn time_elapsed_value((): (), elapsed: P::Elapsed) -> u64 { T::time_elapsed_value(elapsed) }
34
35    fn time_now_millis((): ()) -> u64 { T::time_now_millis() }
36    fn time_now_micros((): ()) -> u64 { T::time_now_micros() }
37    fn time_now_nanos((): ()) -> u64 { T::time_now_nanos() }
38    fn time_now_millis_f64((): ()) -> f64 { T::time_now_millis_f64() }
39}
40
41#[rustfmt::skip]
42#[cfg(feature = "std")]
43#[cfg_attr(nightly_doc, doc(cfg(feature = "std")))]
44mod impl_std {
45    use crate::{Duration, UNIX_EPOCH, OnceLock, SystemInstant, SystemTime};
46    use super::*;
47    impl TimeSource<SystemTime> for SystemTime { fn time_is_monotonic() -> bool { false }
48        fn time_is_absolute() -> bool { true }
49        fn time_scale() -> TimeScale { TimeScale::Nanos }
50        fn time_now() -> SystemTime { SystemTime::now() }
51        fn time_point_value(point: SystemTime) -> u64 {
52            point.duration_since(UNIX_EPOCH).expect("backwards system time").as_nanos() as u64
53        }
54        fn time_elapsed_value(elapsed: Duration) -> u64 { elapsed.as_nanos() as u64 }
55    }
56    /// Unix-time `u64` projection of [`SystemTime`] in nanoseconds.
57    ///
58    /// This is the canonical wide numeric projection:
59    /// At nanosecond resolution, `u64` spans about 584 years.
60    impl TimeSource<u64> for SystemTime {
61        fn time_is_monotonic() -> bool { false }
62        fn time_is_absolute() -> bool { true }
63        fn time_scale() -> TimeScale { TimeScale::Nanos }
64        fn time_now() -> u64 { SystemTime::now()
65            .duration_since(UNIX_EPOCH).expect("backwards system time").as_nanos() as u64
66        }
67        fn time_point_value(point: u64) -> u64 { point }
68        fn time_elapsed_value(elapsed: u64) -> u64 { elapsed }
69    }
70    /// Unix-time `u32` projection of [`SystemTime`] in seconds.
71    ///
72    /// This gives a compact absolute projection with a range of about 136 years
73    /// from the Unix epoch, so it remains valid until February 2106.
74    impl TimeSource<u32> for SystemTime {
75        fn time_is_monotonic() -> bool { false }
76        fn time_is_absolute() -> bool { true }
77        fn time_scale() -> TimeScale { TimeScale::Seconds }
78        fn time_now() -> u32 {
79            u32::try_from(
80                SystemTime::now()
81                    .duration_since(UNIX_EPOCH)
82                    .expect("backwards system time")
83                    .as_secs()
84            ).expect("SystemTime u32 second projection overflow")
85        }
86        fn time_point_value(point: u32) -> u64 { point.into() }
87        fn time_elapsed_value(elapsed: u32) -> u64 { elapsed.into() }
88    }
89
90    /// Process-local base instant used to derive a numeric timeline for `SystemInstant`.
91    ///
92    /// `SystemInstant` does not expose a global or absolute epoch;
93    /// it only supports measuring durations between two instants.
94    /// To fit the `TimeSource` numeric timeline model, a synthetic,
95    /// process-local origin is established on first use.
96    ///
97    /// All timestamps produced by the `SystemInstant` time source
98    /// are measured as the elapsed duration since this base instant.
99    ///
100    /// This base is initialized lazily and exactly once.
101    static SYSTEM_INSTANT_BASE: OnceLock<SystemInstant> = OnceLock::new();
102
103    impl TimeSource<SystemInstant> for SystemInstant {
104        fn time_is_monotonic() -> bool { true }
105        fn time_is_absolute() -> bool { false }
106        fn time_scale() -> TimeScale { TimeScale::Nanos }
107        fn time_now() -> SystemInstant { SystemInstant::now() }
108        fn time_point_value(point: SystemInstant) -> u64 {
109            let base = SYSTEM_INSTANT_BASE.get_or_init(SystemInstant::now);
110            point.duration_since(*base).as_nanos() as u64
111        }
112        fn time_elapsed_value(elapsed: Duration) -> u64 { elapsed.as_nanos() as u64 }
113    }
114    /// Process-local monotonic `u64` projection of [`SystemInstant`] in nanoseconds.
115    ///
116    /// This is the canonical wide numeric projection:
117    /// At nanosecond resolution, `u64` spans about 584 years.
118    impl TimeSource<u64> for SystemInstant {
119        fn time_is_monotonic() -> bool { true }
120        fn time_is_absolute() -> bool { false }
121        fn time_scale() -> TimeScale { TimeScale::Nanos }
122        fn time_now() -> u64 {
123            let base = SYSTEM_INSTANT_BASE.get_or_init(SystemInstant::now);
124            base.elapsed().as_nanos() as u64
125        }
126        fn time_point_value(point: u64) -> u64 { point }
127        fn time_elapsed_value(elapsed: u64) -> u64 { elapsed }
128    }
129    /// Process-local monotonic `u32` projection of [`SystemInstant`] in microseconds.
130    ///
131    /// This compact projection trades range for storage size while keeping
132    /// sub-millisecond precision useful for short and medium-lived runtimes.
133    ///
134    /// At microsecond resolution, `u32` spans about 71.6 minutes,
135    impl TimeSource<u32> for SystemInstant {
136        fn time_is_monotonic() -> bool { true }
137        fn time_is_absolute() -> bool { false }
138        fn time_scale() -> TimeScale { TimeScale::Micros }
139        fn time_now() -> u32 {
140            let base = SYSTEM_INSTANT_BASE.get_or_init(SystemInstant::now);
141            u32::try_from(base.elapsed().as_micros())
142                .expect("SystemInstant u32 projection overflow")
143        }
144        fn time_point_value(point: u32) -> u64 { point.into() }
145        fn time_elapsed_value(elapsed: u32) -> u64 { elapsed.into() }
146    }
147}
148
149#[rustfmt::skip]
150#[cfg(all(feature = "web", not(windows)))]
151mod impl_js {
152    use crate::JsInstant;
153    use super::*;
154
155    /// Relative `u64` projection of [`JsInstant`] in milliseconds.
156    ///
157    /// This is the canonical wide numeric projection for the browser high-resolution
158    /// time origin. Milliseconds are the native scale exposed by the type's public API.
159    ///
160    /// At millisecond resolution, `u64` spans about 584 million years.
161    impl TimeSource<u64> for JsInstant {
162        fn time_is_monotonic() -> bool { true }
163        fn time_is_absolute() -> bool { false }
164        fn time_scale() -> TimeScale { TimeScale::Millis }
165        fn time_now() -> u64 { JsInstant::now().as_millis_f64() as u64 }
166        fn time_point_value(point: u64) -> u64 { point }
167        fn time_elapsed_value(elapsed: u64) -> u64 { elapsed }
168        fn time_now_millis_f64() -> f64 { JsInstant::now().as_millis_f64() }
169    }
170    /// Relative `u32` projection of [`JsInstant`] in milliseconds.
171    ///
172    /// This compact projection favors storage size while keeping a practical range
173    /// for browser sessions and medium-lived applications.
174    ///
175    /// At millisecond resolution, `u32` spans about 49.7 days.
176    impl TimeSource<u32> for JsInstant {
177        fn time_is_monotonic() -> bool { true }
178        fn time_is_absolute() -> bool { false }
179        fn time_scale() -> TimeScale { TimeScale::Millis }
180        fn time_now() -> u32 {
181            u32::try_from(JsInstant::now().as_millis_f64() as u64)
182                .expect("JsInstant u32 millisecond projection overflow")
183        }
184        fn time_point_value(point: u32) -> u64 { point.into() }
185        fn time_elapsed_value(elapsed: u32) -> u64 { elapsed.into() }
186        fn time_now_millis_f64() -> f64 { JsInstant::now().as_millis_f64() }
187    }
188}