jiff/
now.rs

1/*!
2Provides a centralized helper for getting the current system time.
3
4We generally rely on the standard library for this, but the standard library
5(by design) does not provide this for the `wasm{32,64}-unknown-unknown`
6targets. Instead, Jiff provides an opt-in `js` feature that only applies on the
7aforementioned target. Specifically, when enabled, it assumes a web context and
8runs JavaScript code to get the current time.
9
10This also exposes a "fallible" API for querying monotonic time. Since we only
11use monotonic time for managing expiration for caches, in the case where we
12can't get monotonic time (easily), we just consider the cache to always be
13expired. ¯\_(ツ)_/¯
14*/
15
16pub(crate) use self::sys::*;
17
18#[cfg(not(all(
19    feature = "js",
20    any(target_arch = "wasm32", target_arch = "wasm64"),
21    target_os = "unknown"
22)))]
23mod sys {
24    pub(crate) fn system_time() -> std::time::SystemTime {
25        // `SystemTime::now()` should continue to panic on this exact target in
26        // the future as well; Instead of letting `std` panic, we panic first
27        // with a more informative error message.
28        if cfg!(all(
29            not(feature = "js"),
30            any(target_arch = "wasm32", target_arch = "wasm64"),
31            target_os = "unknown"
32        )) {
33            panic!(
34                "getting the current time in wasm32-unknown-unknown \
35                 is not possible with just the standard library, \
36                 enable Jiff's `js` feature if you are \
37                 targeting a browser environment",
38            );
39        } else {
40            std::time::SystemTime::now()
41        }
42    }
43
44    #[cfg(any(
45        feature = "tz-system",
46        feature = "tzdb-zoneinfo",
47        feature = "tzdb-concatenated"
48    ))]
49    pub(crate) fn monotonic_time() -> Option<std::time::Instant> {
50        // Same reasoning as above, but we return `None` instead of panicking,
51        // because Jiff can deal with environments that don't provide
52        // monotonic time.
53        if cfg!(all(
54            not(feature = "js"),
55            any(target_arch = "wasm32", target_arch = "wasm64"),
56            target_os = "unknown"
57        )) {
58            None
59        } else {
60            Some(std::time::Instant::now())
61        }
62    }
63}
64
65#[cfg(all(
66    feature = "js",
67    any(target_arch = "wasm32", target_arch = "wasm64"),
68    target_os = "unknown"
69))]
70mod sys {
71    pub(crate) fn system_time() -> std::time::SystemTime {
72        use std::time::{Duration, SystemTime};
73
74        #[cfg(not(feature = "std"))]
75        use crate::util::libm::Float;
76
77        let millis = js_sys::Date::new_0().get_time();
78        let sign = millis.signum();
79        let millis = millis.abs() as u64;
80        let duration = Duration::from_millis(millis);
81        let result = if sign >= 0.0 {
82            SystemTime::UNIX_EPOCH.checked_add(duration)
83        } else {
84            SystemTime::UNIX_EPOCH.checked_sub(duration)
85        };
86        // It's a little sad that we have to panic here, but the standard
87        // SystemTime::now() API is infallible, so we kind of have to match it.
88        // With that said, a panic here would be highly unusual. It would imply
89        // that the system time is set to some extreme timestamp very far in the
90        // future or the past.
91        let Some(timestamp) = result else {
92            panic!(
93                "failed to get current time: \
94             subtracting {duration:?} from Unix epoch overflowed"
95            )
96        };
97        timestamp
98    }
99
100    #[cfg(any(
101        feature = "tz-system",
102        feature = "tzdb-zoneinfo",
103        feature = "tzdb-concatenated"
104    ))]
105    pub(crate) fn monotonic_time() -> Option<std::time::Instant> {
106        // :-(
107        None
108    }
109}