diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
| commit | 8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch) | |
| tree | 22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/time/src/sys | |
| parent | 4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff) | |
chore: add vendor directory
Diffstat (limited to 'vendor/time/src/sys')
| -rw-r--r-- | vendor/time/src/sys/local_offset_at/imp.rs | 8 | ||||
| -rw-r--r-- | vendor/time/src/sys/local_offset_at/mod.rs | 23 | ||||
| -rw-r--r-- | vendor/time/src/sys/local_offset_at/unix.rs | 101 | ||||
| -rw-r--r-- | vendor/time/src/sys/local_offset_at/wasm_js.rs | 16 | ||||
| -rw-r--r-- | vendor/time/src/sys/local_offset_at/windows.rs | 113 | ||||
| -rw-r--r-- | vendor/time/src/sys/mod.rs | 11 | ||||
| -rw-r--r-- | vendor/time/src/sys/refresh_tz/imp.rs | 9 | ||||
| -rw-r--r-- | vendor/time/src/sys/refresh_tz/mod.rs | 17 | ||||
| -rw-r--r-- | vendor/time/src/sys/refresh_tz/unix.rs | 48 |
9 files changed, 346 insertions, 0 deletions
diff --git a/vendor/time/src/sys/local_offset_at/imp.rs b/vendor/time/src/sys/local_offset_at/imp.rs new file mode 100644 index 00000000..251fa667 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/imp.rs @@ -0,0 +1,8 @@ +//! A fallback for any OS not covered. + +use crate::{OffsetDateTime, UtcOffset}; + +#[allow(clippy::missing_docs_in_private_items)] +pub(super) fn local_offset_at(_datetime: OffsetDateTime) -> Option<UtcOffset> { + None +} diff --git a/vendor/time/src/sys/local_offset_at/mod.rs b/vendor/time/src/sys/local_offset_at/mod.rs new file mode 100644 index 00000000..04cfffd5 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/mod.rs @@ -0,0 +1,23 @@ +//! A method to obtain the local offset from UTC. + +#![allow(clippy::missing_const_for_fn)] + +#[cfg_attr(target_family = "windows", path = "windows.rs")] +#[cfg_attr(target_family = "unix", path = "unix.rs")] +#[cfg_attr( + all( + target_family = "wasm", + not(any(target_os = "emscripten", target_os = "wasi")), + feature = "wasm-bindgen" + ), + path = "wasm_js.rs" +)] +mod imp; + +use crate::{OffsetDateTime, UtcOffset}; + +/// Attempt to obtain the system's UTC offset. If the offset cannot be determined, `None` is +/// returned. +pub(crate) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> { + imp::local_offset_at(datetime) +} diff --git a/vendor/time/src/sys/local_offset_at/unix.rs b/vendor/time/src/sys/local_offset_at/unix.rs new file mode 100644 index 00000000..d036a0e0 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/unix.rs @@ -0,0 +1,101 @@ +//! Get the system's UTC offset on Unix. + +use core::mem::MaybeUninit; + +use crate::{OffsetDateTime, UtcOffset}; + +/// Convert the given Unix timestamp to a `libc::tm`. Returns `None` on any error. +fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> { + // The exact type of `timestamp` beforehand can vary, so this conversion is necessary. + #[allow(clippy::useless_conversion)] + let timestamp = timestamp.try_into().ok()?; + + let mut tm = MaybeUninit::uninit(); + + // Safety: We are calling a system API, which mutates the `tm` variable. If a null + // pointer is returned, an error occurred. + let tm_ptr = unsafe { libc::localtime_r(×tamp, tm.as_mut_ptr()) }; + + if tm_ptr.is_null() { + None + } else { + // Safety: The value was initialized, as we no longer have a null pointer. + Some(unsafe { tm.assume_init() }) + } +} + +/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error. +// This is available to any target known to have the `tm_gmtoff` extension. +#[cfg(any( + target_os = "redox", + target_os = "linux", + target_os = "l4re", + target_os = "android", + target_os = "emscripten", + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "netbsd", + target_os = "haiku", +))] +fn tm_to_offset(_unix_timestamp: i64, tm: libc::tm) -> Option<UtcOffset> { + let seconds = tm.tm_gmtoff.try_into().ok()?; + UtcOffset::from_whole_seconds(seconds).ok() +} + +/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error. +/// +/// This method can return an incorrect value, as it only approximates the `tm_gmtoff` field. The +/// reason for this is that daylight saving time does not start on the same date every year, nor are +/// the rules for daylight saving time the same for every year. This implementation assumes 1970 is +/// equivalent to every other year, which is not always the case. +#[cfg(not(any( + target_os = "redox", + target_os = "linux", + target_os = "l4re", + target_os = "android", + target_os = "emscripten", + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "netbsd", + target_os = "haiku", +)))] +fn tm_to_offset(unix_timestamp: i64, tm: libc::tm) -> Option<UtcOffset> { + use crate::Date; + + let mut tm = tm; + if tm.tm_sec == 60 { + // Leap seconds are not currently supported. + tm.tm_sec = 59; + } + + let local_timestamp = + Date::from_ordinal_date(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1) + .ok()? + .with_hms( + tm.tm_hour.try_into().ok()?, + tm.tm_min.try_into().ok()?, + tm.tm_sec.try_into().ok()?, + ) + .ok()? + .assume_utc() + .unix_timestamp(); + + let diff_secs = (local_timestamp - unix_timestamp).try_into().ok()?; + + UtcOffset::from_whole_seconds(diff_secs).ok() +} + +/// Obtain the system's UTC offset. +pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> { + let unix_timestamp = datetime.unix_timestamp(); + let tm = timestamp_to_tm(unix_timestamp)?; + tm_to_offset(unix_timestamp, tm) +} diff --git a/vendor/time/src/sys/local_offset_at/wasm_js.rs b/vendor/time/src/sys/local_offset_at/wasm_js.rs new file mode 100644 index 00000000..a985e7c4 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/wasm_js.rs @@ -0,0 +1,16 @@ +use num_conv::prelude::*; + +use crate::convert::*; +use crate::{OffsetDateTime, UtcOffset}; + +/// Obtain the system's UTC offset. +pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> { + let js_date: js_sys::Date = datetime.into(); + // The number of minutes returned by getTimezoneOffset() is positive if the local time zone + // is behind UTC, and negative if the local time zone is ahead of UTC. For example, + // for UTC+10, -600 will be returned. + let timezone_offset = + (js_date.get_timezone_offset() as i32) * -Minute::per(Hour).cast_signed().extend::<i32>(); + + UtcOffset::from_whole_seconds(timezone_offset).ok() +} diff --git a/vendor/time/src/sys/local_offset_at/windows.rs b/vendor/time/src/sys/local_offset_at/windows.rs new file mode 100644 index 00000000..d1442920 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/windows.rs @@ -0,0 +1,113 @@ +//! Get the system's UTC offset on Windows. + +use core::mem::MaybeUninit; + +use num_conv::prelude::*; + +use crate::convert::*; +use crate::{OffsetDateTime, UtcOffset}; + +// ffi: WINAPI FILETIME struct +#[repr(C)] +#[allow(non_snake_case, clippy::missing_docs_in_private_items)] +struct FileTime { + dwLowDateTime: u32, + dwHighDateTime: u32, +} + +// ffi: WINAPI SYSTEMTIME struct +#[repr(C)] +#[allow(non_snake_case, clippy::missing_docs_in_private_items)] +struct SystemTime { + wYear: u16, + wMonth: u16, + wDayOfWeek: u16, + wDay: u16, + wHour: u16, + wMinute: u16, + wSecond: u16, + wMilliseconds: u16, +} + +#[link(name = "kernel32")] +extern "system" { + // https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime + fn SystemTimeToFileTime(lpSystemTime: *const SystemTime, lpFileTime: *mut FileTime) -> i32; + + // https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime + fn SystemTimeToTzSpecificLocalTime( + lpTimeZoneInformation: *const core::ffi::c_void, // We only pass `nullptr` here + lpUniversalTime: *const SystemTime, + lpLocalTime: *mut SystemTime, + ) -> i32; +} + +/// Convert a `SYSTEMTIME` to a `FILETIME`. Returns `None` if any error occurred. +fn systemtime_to_filetime(systime: &SystemTime) -> Option<FileTime> { + let mut ft = MaybeUninit::uninit(); + + // Safety: `SystemTimeToFileTime` is thread-safe. + if 0 == unsafe { SystemTimeToFileTime(systime, ft.as_mut_ptr()) } { + // failed + None + } else { + // Safety: The call succeeded. + Some(unsafe { ft.assume_init() }) + } +} + +/// Convert a `FILETIME` to an `i64`, representing a number of seconds. +fn filetime_to_secs(filetime: &FileTime) -> i64 { + /// FILETIME represents 100-nanosecond intervals + const FT_TO_SECS: u64 = Nanosecond::per(Second) as u64 / 100; + ((filetime.dwHighDateTime.extend::<u64>() << 32 | filetime.dwLowDateTime.extend::<u64>()) + / FT_TO_SECS) as i64 +} + +/// Convert an [`OffsetDateTime`] to a `SYSTEMTIME`. +fn offset_to_systemtime(datetime: OffsetDateTime) -> SystemTime { + let (_, month, day_of_month) = datetime.to_offset(UtcOffset::UTC).date().to_calendar_date(); + SystemTime { + wYear: datetime.year().cast_unsigned().truncate(), + wMonth: u8::from(month).extend(), + wDay: day_of_month.extend(), + wDayOfWeek: 0, // ignored + wHour: datetime.hour().extend(), + wMinute: datetime.minute().extend(), + wSecond: datetime.second().extend(), + wMilliseconds: datetime.millisecond(), + } +} + +/// Obtain the system's UTC offset. +pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> { + // This function falls back to UTC if any system call fails. + let systime_utc = offset_to_systemtime(datetime.to_offset(UtcOffset::UTC)); + + // Safety: `local_time` is only read if it is properly initialized, and + // `SystemTimeToTzSpecificLocalTime` is thread-safe. + let systime_local = unsafe { + let mut local_time = MaybeUninit::uninit(); + + if 0 == SystemTimeToTzSpecificLocalTime( + core::ptr::null(), // use system's current timezone + &systime_utc, + local_time.as_mut_ptr(), + ) { + // call failed + return None; + } else { + local_time.assume_init() + } + }; + + // Convert SYSTEMTIMEs to FILETIMEs so we can perform arithmetic on them. + let ft_system = systemtime_to_filetime(&systime_utc)?; + let ft_local = systemtime_to_filetime(&systime_local)?; + + let diff_secs = (filetime_to_secs(&ft_local) - filetime_to_secs(&ft_system)) + .try_into() + .ok()?; + + UtcOffset::from_whole_seconds(diff_secs).ok() +} diff --git a/vendor/time/src/sys/mod.rs b/vendor/time/src/sys/mod.rs new file mode 100644 index 00000000..76ca93b5 --- /dev/null +++ b/vendor/time/src/sys/mod.rs @@ -0,0 +1,11 @@ +//! Functions with a common interface that rely on system calls. + +#[cfg(feature = "local-offset")] +mod local_offset_at; +#[cfg(feature = "local-offset")] +mod refresh_tz; + +#[cfg(feature = "local-offset")] +pub(crate) use self::local_offset_at::local_offset_at; +#[cfg(feature = "local-offset")] +pub(crate) use self::refresh_tz::{refresh_tz, refresh_tz_unchecked}; diff --git a/vendor/time/src/sys/refresh_tz/imp.rs b/vendor/time/src/sys/refresh_tz/imp.rs new file mode 100644 index 00000000..6f8b1328 --- /dev/null +++ b/vendor/time/src/sys/refresh_tz/imp.rs @@ -0,0 +1,9 @@ +//! A fallback for any OS not covered. + +#[allow(clippy::missing_docs_in_private_items)] +pub(super) unsafe fn refresh_tz_unchecked() {} + +#[allow(clippy::missing_docs_in_private_items)] +pub(super) fn refresh_tz() -> Option<()> { + Some(()) +} diff --git a/vendor/time/src/sys/refresh_tz/mod.rs b/vendor/time/src/sys/refresh_tz/mod.rs new file mode 100644 index 00000000..90ddaa99 --- /dev/null +++ b/vendor/time/src/sys/refresh_tz/mod.rs @@ -0,0 +1,17 @@ +#[cfg_attr(target_family = "unix", path = "unix.rs")] +mod imp; + +/// Update time zone information from the system. +/// +/// For safety documentation, see [`time::util::refresh_tz`]. +pub(crate) unsafe fn refresh_tz_unchecked() { + // Safety: The caller must uphold the safety requirements. + unsafe { imp::refresh_tz_unchecked() } +} + +/// Attempt to update time zone information from the system. +/// +/// Returns `None` if the call is not known to be sound. +pub(crate) fn refresh_tz() -> Option<()> { + imp::refresh_tz() +} diff --git a/vendor/time/src/sys/refresh_tz/unix.rs b/vendor/time/src/sys/refresh_tz/unix.rs new file mode 100644 index 00000000..6c9813e2 --- /dev/null +++ b/vendor/time/src/sys/refresh_tz/unix.rs @@ -0,0 +1,48 @@ +/// Whether the operating system has a thread-safe environment. This allows bypassing the check for +/// if the process is multi-threaded. +// This is the same value as `cfg!(target_os = "x")`. +// Use byte-strings to work around current limitations of const eval. +const OS_HAS_THREAD_SAFE_ENVIRONMENT: bool = match std::env::consts::OS.as_bytes() { + // https://github.com/illumos/illumos-gate/blob/0fb96ba1f1ce26ff8b286f8f928769a6afcb00a6/usr/src/lib/libc/port/gen/getenv.c + b"illumos" + // https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/getenv.c + // https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/setenv.c + | b"netbsd" + => true, + _ => false, +}; + +/// Update time zone information from the system. +/// +/// For safety documentation, see [`time::util::refresh_tz`]. +pub(super) unsafe fn refresh_tz_unchecked() { + extern "C" { + #[cfg_attr(target_os = "netbsd", link_name = "__tzset50")] + fn tzset(); + } + + // Safety: The caller must uphold the safety requirements. + unsafe { tzset() }; +} + +/// Attempt to update time zone information from the system. Returns `None` if the call is not known +/// to be sound. +pub(super) fn refresh_tz() -> Option<()> { + // Refresh $TZ if and only if the call is known to be sound. + // + // Soundness can be guaranteed either by knowledge of the operating system or knowledge that the + // process is single-threaded. If the process is single-threaded, then the environment cannot + // be mutated by a different thread in the process while execution of this function is taking + // place, which can cause a segmentation fault by dereferencing a dangling pointer. + // + // If the `num_threads` crate is incapable of determining the number of running threads, then + // we conservatively return `None` to avoid a soundness bug. + + if OS_HAS_THREAD_SAFE_ENVIRONMENT || num_threads::is_single_threaded() == Some(true) { + // Safety: The caller must uphold the safety requirements. + unsafe { refresh_tz_unchecked() }; + Some(()) + } else { + None + } +} |
