1 | /*! |
2 | Provides a centralized helper for getting the current system time. |
3 | |
4 | We 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` |
6 | targets. Instead, Jiff provides an opt-in `js` feature that only applies on the |
7 | aforementioned target. Specifically, when enabled, it assumes a web context and |
8 | runs JavaScript code to get the current time. |
9 | |
10 | This also exposes a "fallible" API for querying monotonic time. Since we only |
11 | use monotonic time for managing expiration for caches, in the case where we |
12 | can't get monotonic time (easily), we just consider the cache to always be |
13 | expired. ¯\_(ツ)_/¯ |
14 | */ |
15 | |
16 | pub(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 | )))] |
23 | mod 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 | ))] |
70 | mod 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 | } |
110 | |