Skip to main content

universal_time/
system.rs

1use core::time::Duration;
2
3/// Wall clock time represented as a duration since the Unix epoch.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct SystemTime {
6    since_unix_epoch: Duration,
7}
8
9/// Unix epoch (January 1, 1970).
10pub const UNIX_EPOCH: SystemTime = SystemTime::from_unix_duration(Duration::ZERO);
11
12impl SystemTime {
13    /// Creates a `SystemTime` from a duration since Unix epoch.
14    #[inline]
15    pub const fn from_unix_duration(since_unix_epoch: Duration) -> Self {
16        Self { since_unix_epoch }
17    }
18
19    /// Returns this timestamp as duration since Unix epoch.
20    #[inline]
21    pub const fn as_unix_duration(self) -> Duration {
22        self.since_unix_epoch
23    }
24
25    /// Returns the current system time.
26    ///
27    /// # Platform behavior
28    ///
29    /// - With `std` feature on supported platforms: uses `std::time::SystemTime`
30    /// - Without `std` or on WASM unknown: uses the provider defined via `define_time_provider!` macro
31    ///
32    /// If no provider is defined on platforms without std, you'll get a **link error** at compile time.
33    #[inline]
34    pub fn now() -> Self {
35        #[cfg(all(
36            feature = "std",
37            not(all(target_family = "wasm", target_os = "unknown"))
38        ))]
39        {
40            let now = std::time::SystemTime::now();
41            let since_unix_epoch = now
42                .duration_since(std::time::UNIX_EPOCH)
43                .unwrap_or(Duration::ZERO);
44            Self::from_unix_duration(since_unix_epoch)
45        }
46
47        #[cfg(any(
48            not(feature = "std"),
49            all(feature = "std", target_family = "wasm", target_os = "unknown")
50        ))]
51        {
52            crate::global::get_time_provider().system_time()
53        }
54    }
55
56    /// Returns the duration since another `SystemTime`.
57    pub fn duration_since(&self, earlier: SystemTime) -> Result<Duration, Duration> {
58        if self.since_unix_epoch >= earlier.since_unix_epoch {
59            Ok(self.since_unix_epoch - earlier.since_unix_epoch)
60        } else {
61            Err(earlier.since_unix_epoch - self.since_unix_epoch)
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn unix_epoch_is_zero() {
72        assert_eq!(UNIX_EPOCH.as_unix_duration(), Duration::ZERO);
73    }
74
75    #[test]
76    fn roundtrip_unix_duration() {
77        let duration = Duration::from_secs(123) + Duration::from_nanos(456);
78        let time = SystemTime::from_unix_duration(duration);
79        assert_eq!(time.as_unix_duration(), duration);
80    }
81
82    #[test]
83    fn duration_since_forward() {
84        let earlier = SystemTime::from_unix_duration(Duration::from_secs(10));
85        let later = SystemTime::from_unix_duration(Duration::from_secs(12));
86        assert_eq!(later.duration_since(earlier), Ok(Duration::from_secs(2)));
87    }
88
89    #[test]
90    fn duration_since_backward() {
91        let earlier = SystemTime::from_unix_duration(Duration::from_secs(10));
92        let later = SystemTime::from_unix_duration(Duration::from_secs(12));
93        assert_eq!(earlier.duration_since(later), Err(Duration::from_secs(2)));
94    }
95
96    #[test]
97    #[cfg(all(
98        feature = "std",
99        not(all(target_family = "wasm", target_os = "unknown"))
100    ))]
101    fn now_is_after_unix_epoch() {
102        let now = SystemTime::now();
103        assert!(now.duration_since(UNIX_EPOCH).is_ok());
104    }
105}