| 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 | |