summaryrefslogtreecommitdiffstats
path: root/vendor/time/src/sys
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/time/src/sys')
-rw-r--r--vendor/time/src/sys/local_offset_at/imp.rs8
-rw-r--r--vendor/time/src/sys/local_offset_at/mod.rs23
-rw-r--r--vendor/time/src/sys/local_offset_at/unix.rs169
-rw-r--r--vendor/time/src/sys/local_offset_at/wasm_js.rs12
-rw-r--r--vendor/time/src/sys/local_offset_at/windows.rs114
-rw-r--r--vendor/time/src/sys/mod.rs9
6 files changed, 335 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 000000000..251fa667c
--- /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 000000000..f0bc4be3c
--- /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_arch = "wasm32",
+ 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 000000000..6e849892d
--- /dev/null
+++ b/vendor/time/src/sys/local_offset_at/unix.rs
@@ -0,0 +1,169 @@
+//! 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.
+///
+/// # Safety
+///
+/// This method must only be called when the process is single-threaded.
+///
+/// This method will remain `unsafe` until `std::env::set_var` is deprecated or has its behavior
+/// altered. This method is, on its own, safe. It is the presence of a safe, unsound way to set
+/// environment variables that makes it unsafe.
+unsafe fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
+ extern "C" {
+ #[cfg_attr(target_os = "netbsd", link_name = "__tzset50")]
+ fn tzset();
+ }
+
+ // 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();
+
+ // Update timezone information from system. `localtime_r` does not do this for us.
+ //
+ // Safety: tzset is thread-safe.
+ unsafe { tzset() };
+
+ // 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(&timestamp, 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(tm: libc::tm) -> Option<UtcOffset> {
+ let seconds: i32 = tm.tm_gmtoff.try_into().ok()?;
+ UtcOffset::from_hms(
+ (seconds / 3_600) as _,
+ ((seconds / 60) % 60) as _,
+ (seconds % 60) as _,
+ )
+ .ok()
+}
+
+/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
+#[cfg(all(
+ not(unsound_local_offset),
+ 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",
+ ))
+))]
+#[allow(unused_variables, clippy::missing_const_for_fn)]
+fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
+ None
+}
+
+/// 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. As such
+// it is gated behind `--cfg unsound_local_offset`. The reason it can return an incorrect value 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(all(
+ unsound_local_offset,
+ 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(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: i32 = (local_timestamp - datetime.unix_timestamp())
+ .try_into()
+ .ok()?;
+
+ UtcOffset::from_hms(
+ (diff_secs / 3_600) as _,
+ ((diff_secs / 60) % 60) as _,
+ (diff_secs % 60) as _,
+ )
+ .ok()
+}
+
+/// Obtain the system's UTC offset.
+pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
+ // Ensure that the process is single-threaded unless the user has explicitly opted out of this
+ // check. This is to prevent issues with the environment being 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 !cfg!(unsound_local_offset) && num_threads::is_single_threaded() != Some(true) {
+ return None;
+ }
+
+ // Safety: We have just confirmed that the process is single-threaded or the user has explicitly
+ // opted out of soundness.
+ let tm = unsafe { timestamp_to_tm(datetime.unix_timestamp()) }?;
+ tm_to_offset(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 000000000..fcea4b0f5
--- /dev/null
+++ b/vendor/time/src/sys/local_offset_at/wasm_js.rs
@@ -0,0 +1,12 @@
+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) * -60;
+
+ 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 000000000..fed01bf3a
--- /dev/null
+++ b/vendor/time/src/sys/local_offset_at/windows.rs
@@ -0,0 +1,114 @@
+//! Get the system's UTC offset on Windows.
+
+use core::mem::MaybeUninit;
+
+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: i64 = 10_000_000;
+ ((filetime.dwHighDateTime as i64) << 32 | filetime.dwLowDateTime as i64) / FT_TO_SECS
+}
+
+/// 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() as _,
+ wMonth: month as _,
+ wDay: day_of_month as _,
+ wDayOfWeek: 0, // ignored
+ wHour: datetime.hour() as _,
+ wMinute: datetime.minute() as _,
+ wSecond: datetime.second() as _,
+ 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: i32 = (filetime_to_secs(&ft_local) - filetime_to_secs(&ft_system))
+ .try_into()
+ .ok()?;
+
+ UtcOffset::from_hms(
+ (diff_secs / 3_600) as _,
+ ((diff_secs / 60) % 60) as _,
+ (diff_secs % 60) as _,
+ )
+ .ok()
+}
diff --git a/vendor/time/src/sys/mod.rs b/vendor/time/src/sys/mod.rs
new file mode 100644
index 000000000..90bbf7ff3
--- /dev/null
+++ b/vendor/time/src/sys/mod.rs
@@ -0,0 +1,9 @@
+//! Functions with a common interface that rely on system calls.
+
+#![allow(unsafe_code)] // We're interfacing with system calls.
+
+#[cfg(feature = "local-offset")]
+mod local_offset_at;
+
+#[cfg(feature = "local-offset")]
+pub(crate) use local_offset_at::local_offset_at;