summaryrefslogtreecommitdiffstats
path: root/vendor/iana-time-zone/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/iana-time-zone/src
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/iana-time-zone/src')
-rw-r--r--vendor/iana-time-zone/src/ffi_utils.rs49
-rw-r--r--vendor/iana-time-zone/src/lib.rs113
-rw-r--r--vendor/iana-time-zone/src/platform.rs9
-rw-r--r--vendor/iana-time-zone/src/tz_android.rs27
-rw-r--r--vendor/iana-time-zone/src/tz_freebsd.rs7
-rw-r--r--vendor/iana-time-zone/src/tz_haiku.rs3
-rw-r--r--vendor/iana-time-zone/src/tz_illumos.rs22
-rw-r--r--vendor/iana-time-zone/src/tz_linux.rs45
-rw-r--r--vendor/iana-time-zone/src/tz_macos.rs144
-rw-r--r--vendor/iana-time-zone/src/tz_netbsd.rs25
-rw-r--r--vendor/iana-time-zone/src/tz_wasm32.rs21
-rw-r--r--vendor/iana-time-zone/src/tz_windows.rs13
12 files changed, 478 insertions, 0 deletions
diff --git a/vendor/iana-time-zone/src/ffi_utils.rs b/vendor/iana-time-zone/src/ffi_utils.rs
new file mode 100644
index 000000000..842b850e8
--- /dev/null
+++ b/vendor/iana-time-zone/src/ffi_utils.rs
@@ -0,0 +1,49 @@
+//! Cross platform FFI helpers.
+
+use std::ffi::CStr;
+
+// The system property named 'persist.sys.timezone' contains the name of the
+// current timezone.
+//
+// From https://android.googlesource.com/platform/bionic/+/gingerbread-release/libc/docs/OVERVIEW.TXT#79:
+//
+// > The name of the current timezone is taken from the TZ environment variable,
+// > if defined. Otherwise, the system property named 'persist.sys.timezone' is
+// > checked instead.
+const ANDROID_TIMEZONE_PROPERTY_NAME: &[u8] = b"persist.sys.timezone\0";
+
+/// Return a [`CStr`] to access the timezone from an Android system properties
+/// environment.
+pub(crate) fn android_timezone_property_name() -> &'static CStr {
+ // In tests or debug mode, opt into extra runtime checks.
+ if cfg!(any(test, debug_assertions)) {
+ return CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap();
+ }
+
+ // SAFETY: the key is NUL-terminated and there are no other NULs, this
+ // invariant is checked in tests.
+ unsafe { CStr::from_bytes_with_nul_unchecked(ANDROID_TIMEZONE_PROPERTY_NAME) }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::ffi::CStr;
+
+ use super::{android_timezone_property_name, ANDROID_TIMEZONE_PROPERTY_NAME};
+
+ #[test]
+ fn test_android_timezone_property_name_is_valid_cstr() {
+ CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap();
+
+ let mut invalid_property_name = ANDROID_TIMEZONE_PROPERTY_NAME.to_owned();
+ invalid_property_name.push(b'\0');
+ CStr::from_bytes_with_nul(&invalid_property_name).unwrap_err();
+ }
+
+ #[test]
+ fn test_android_timezone_property_name_getter() {
+ let key = android_timezone_property_name().to_bytes_with_nul();
+ assert_eq!(key, ANDROID_TIMEZONE_PROPERTY_NAME);
+ std::str::from_utf8(key).unwrap();
+ }
+}
diff --git a/vendor/iana-time-zone/src/lib.rs b/vendor/iana-time-zone/src/lib.rs
new file mode 100644
index 000000000..fe4e3fb84
--- /dev/null
+++ b/vendor/iana-time-zone/src/lib.rs
@@ -0,0 +1,113 @@
+#![warn(clippy::all)]
+#![warn(clippy::cargo)]
+#![warn(clippy::undocumented_unsafe_blocks)]
+#![allow(unknown_lints)]
+#![warn(missing_copy_implementations)]
+#![warn(missing_debug_implementations)]
+#![warn(missing_docs)]
+#![warn(rust_2018_idioms)]
+#![warn(trivial_casts, trivial_numeric_casts)]
+#![warn(unused_qualifications)]
+#![warn(variant_size_differences)]
+
+//! get the IANA time zone for the current system
+//!
+//! This small utility crate provides the
+//! [`get_timezone()`](fn.get_timezone.html) function.
+//!
+//! ```rust
+//! // Get the current time zone as a string.
+//! let tz_str = iana_time_zone::get_timezone()?;
+//! println!("The current time zone is: {}", tz_str);
+//! # Ok::<(), iana_time_zone::GetTimezoneError>(())
+//! ```
+//!
+//! The resulting string can be parsed to a
+//! [`chrono-tz::Tz`](https://docs.rs/chrono-tz/latest/chrono_tz/enum.Tz.html)
+//! variant like this:
+//! ```ignore
+//! let tz_str = iana_time_zone::get_timezone()?;
+//! let tz: chrono_tz::Tz = tz_str.parse()?;
+//! ```
+
+#[allow(dead_code)]
+mod ffi_utils;
+
+#[cfg_attr(target_os = "linux", path = "tz_linux.rs")]
+#[cfg_attr(target_os = "windows", path = "tz_windows.rs")]
+#[cfg_attr(any(target_os = "macos", target_os = "ios"), path = "tz_macos.rs")]
+#[cfg_attr(
+ all(target_arch = "wasm32", not(target_os = "wasi")),
+ path = "tz_wasm32.rs"
+)]
+#[cfg_attr(
+ any(target_os = "freebsd", target_os = "dragonfly"),
+ path = "tz_freebsd.rs"
+)]
+#[cfg_attr(
+ any(target_os = "netbsd", target_os = "openbsd"),
+ path = "tz_netbsd.rs"
+)]
+#[cfg_attr(
+ any(target_os = "illumos", target_os = "solaris"),
+ path = "tz_illumos.rs"
+)]
+#[cfg_attr(target_os = "android", path = "tz_android.rs")]
+#[cfg_attr(target_os = "haiku", path = "tz_haiku.rs")]
+mod platform;
+
+/// Error types
+#[derive(Debug)]
+pub enum GetTimezoneError {
+ /// Failed to parse
+ FailedParsingString,
+ /// Wrapped IO error
+ IoError(std::io::Error),
+ /// Platform-specific error from the operating system
+ OsError,
+}
+
+impl std::error::Error for GetTimezoneError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ GetTimezoneError::FailedParsingString => None,
+ GetTimezoneError::IoError(err) => Some(err),
+ GetTimezoneError::OsError => None,
+ }
+ }
+}
+
+impl std::fmt::Display for GetTimezoneError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ f.write_str(match self {
+ GetTimezoneError::FailedParsingString => "GetTimezoneError::FailedParsingString",
+ GetTimezoneError::IoError(err) => return err.fmt(f),
+ GetTimezoneError::OsError => "OsError",
+ })
+ }
+}
+
+impl From<std::io::Error> for GetTimezoneError {
+ fn from(orig: std::io::Error) -> Self {
+ GetTimezoneError::IoError(orig)
+ }
+}
+
+/// Get the current IANA time zone as a string.
+///
+/// See the module-level documentation for a usage example and more details
+/// about this function.
+#[inline]
+pub fn get_timezone() -> Result<String, GetTimezoneError> {
+ platform::get_timezone_inner()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn get_current() {
+ println!("current: {}", get_timezone().unwrap());
+ }
+}
diff --git a/vendor/iana-time-zone/src/platform.rs b/vendor/iana-time-zone/src/platform.rs
new file mode 100644
index 000000000..5992bf377
--- /dev/null
+++ b/vendor/iana-time-zone/src/platform.rs
@@ -0,0 +1,9 @@
+pub fn get_timezone_inner() -> std::result::Result<String, crate::GetTimezoneError> {
+ Err(crate::GetTimezoneError::OsError)
+}
+
+#[cfg(not(feature = "fallback"))]
+compile_error!(
+ "iana-time-zone is currently implemented for Linux, Window, MacOS, FreeBSD, NetBSD, \
+ OpenBSD, Dragonfly, WebAssembly (browser), iOS, Illumos, Android, Solaris and Haiku.",
+);
diff --git a/vendor/iana-time-zone/src/tz_android.rs b/vendor/iana-time-zone/src/tz_android.rs
new file mode 100644
index 000000000..27255b52f
--- /dev/null
+++ b/vendor/iana-time-zone/src/tz_android.rs
@@ -0,0 +1,27 @@
+use std::sync::Once;
+
+use android_system_properties::AndroidSystemProperties;
+
+use crate::ffi_utils::android_timezone_property_name;
+
+pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
+ let key = android_timezone_property_name();
+
+ get_properties()
+ .and_then(|properties| properties.get_from_cstr(key))
+ .ok_or(crate::GetTimezoneError::OsError)
+}
+
+fn get_properties() -> Option<&'static AndroidSystemProperties> {
+ static INITIALIZED: Once = Once::new();
+ static mut PROPERTIES: Option<AndroidSystemProperties> = None;
+
+ INITIALIZED.call_once(|| {
+ let properties = AndroidSystemProperties::new();
+ // SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once.
+ unsafe { PROPERTIES = Some(properties) };
+ });
+
+ // SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once.
+ unsafe { PROPERTIES.as_ref() }
+}
diff --git a/vendor/iana-time-zone/src/tz_freebsd.rs b/vendor/iana-time-zone/src/tz_freebsd.rs
new file mode 100644
index 000000000..4d55e1536
--- /dev/null
+++ b/vendor/iana-time-zone/src/tz_freebsd.rs
@@ -0,0 +1,7 @@
+pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
+ // see https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/19
+ let mut contents = std::fs::read_to_string("/var/db/zoneinfo")?;
+ // Trim to the correct length without allocating.
+ contents.truncate(contents.trim_end().len());
+ Ok(contents)
+}
diff --git a/vendor/iana-time-zone/src/tz_haiku.rs b/vendor/iana-time-zone/src/tz_haiku.rs
new file mode 100644
index 000000000..d78372b6d
--- /dev/null
+++ b/vendor/iana-time-zone/src/tz_haiku.rs
@@ -0,0 +1,3 @@
+pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
+ iana_time_zone_haiku::get_timezone().ok_or(crate::GetTimezoneError::OsError)
+}
diff --git a/vendor/iana-time-zone/src/tz_illumos.rs b/vendor/iana-time-zone/src/tz_illumos.rs
new file mode 100644
index 000000000..17b099b10
--- /dev/null
+++ b/vendor/iana-time-zone/src/tz_illumos.rs
@@ -0,0 +1,22 @@
+use std::fs::OpenOptions;
+use std::io::{BufRead, BufReader};
+
+pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
+ // https://illumos.org/man/5/TIMEZONE
+ // https://docs.oracle.com/cd/E23824_01/html/821-1473/uc-timezone-4.html
+
+ let file = OpenOptions::new().read(true).open("/etc/default/init")?;
+ let mut reader = BufReader::with_capacity(1536, file);
+ let mut line = String::with_capacity(80);
+ loop {
+ line.clear();
+ let count = reader.read_line(&mut line)?;
+ if count == 0 {
+ return Err(crate::GetTimezoneError::FailedParsingString);
+ } else if line.starts_with("TZ=") {
+ line.truncate(line.trim_end().len());
+ line.replace_range(..3, "");
+ return Ok(line);
+ }
+ }
+}
diff --git a/vendor/iana-time-zone/src/tz_linux.rs b/vendor/iana-time-zone/src/tz_linux.rs
new file mode 100644
index 000000000..c1133e1cd
--- /dev/null
+++ b/vendor/iana-time-zone/src/tz_linux.rs
@@ -0,0 +1,45 @@
+use std::fs::{read_link, read_to_string};
+
+pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
+ etc_localtime().or_else(|_| etc_timezone())
+}
+
+fn etc_timezone() -> Result<String, crate::GetTimezoneError> {
+ // see https://stackoverflow.com/a/12523283
+ let mut contents = read_to_string("/etc/timezone")?;
+ // Trim to the correct length without allocating.
+ contents.truncate(contents.trim_end().len());
+ Ok(contents)
+}
+
+fn etc_localtime() -> Result<String, crate::GetTimezoneError> {
+ // Per <https://www.man7.org/linux/man-pages/man5/localtime.5.html>:
+ // “ The /etc/localtime file configures the system-wide timezone of the local system that is
+ // used by applications for presentation to the user. It should be an absolute or relative
+ // symbolic link pointing to /usr/share/zoneinfo/, followed by a timezone identifier such as
+ // "Europe/Berlin" or "Etc/UTC". The resulting link should lead to the corresponding binary
+ // tzfile(5) timezone data for the configured timezone. ”
+
+ // Systemd does not canonicalize the link, but only checks if it is prefixed by
+ // "/usr/share/zoneinfo/" or "../usr/share/zoneinfo/". So we do the same.
+ // <https://github.com/systemd/systemd/blob/9102c625a673a3246d7e73d8737f3494446bad4e/src/basic/time-util.c#L1493>
+
+ const PREFIXES: &[&str] = &[
+ "/usr/share/zoneinfo/", // absolute path
+ "../usr/share/zoneinfo/", // relative path
+ "/etc/zoneinfo/", // absolute path for NixOS
+ "../etc/zoneinfo/", // relative path for NixOS
+ ];
+ let mut s = read_link("/etc/localtime")?
+ .into_os_string()
+ .into_string()
+ .map_err(|_| crate::GetTimezoneError::FailedParsingString)?;
+ for &prefix in PREFIXES {
+ if s.starts_with(prefix) {
+ // Trim to the correct length without allocating.
+ s.replace_range(..prefix.len(), "");
+ return Ok(s);
+ }
+ }
+ Err(crate::GetTimezoneError::FailedParsingString)
+}
diff --git a/vendor/iana-time-zone/src/tz_macos.rs b/vendor/iana-time-zone/src/tz_macos.rs
new file mode 100644
index 000000000..70c39e83f
--- /dev/null
+++ b/vendor/iana-time-zone/src/tz_macos.rs
@@ -0,0 +1,144 @@
+pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
+ get_timezone().ok_or(crate::GetTimezoneError::OsError)
+}
+
+#[inline]
+fn get_timezone() -> Option<String> {
+ // The longest name in the IANA time zone database is 25 ASCII characters long.
+ const MAX_LEN: usize = 32;
+ let mut buf = [0; MAX_LEN];
+
+ // Get system time zone, and borrow its name.
+ let tz = system_time_zone::SystemTimeZone::new()?;
+ let name = tz.name()?;
+
+ // If the name is encoded in UTF-8, copy it directly.
+ let name = if let Some(name) = name.as_utf8() {
+ name
+ } else {
+ // Otherwise convert the name to UTF-8.
+ name.to_utf8(&mut buf)?
+ };
+
+ if name.is_empty() || name.len() >= MAX_LEN {
+ // The name should not be empty, or excessively long.
+ None
+ } else {
+ Some(name.to_owned())
+ }
+}
+
+mod system_time_zone {
+ //! create a safe wrapper around `CFTimeZoneRef`
+
+ use core_foundation_sys::base::{CFRelease, CFTypeRef};
+ use core_foundation_sys::timezone::{CFTimeZoneCopySystem, CFTimeZoneGetName, CFTimeZoneRef};
+
+ pub(crate) struct SystemTimeZone(CFTimeZoneRef);
+
+ impl Drop for SystemTimeZone {
+ fn drop(&mut self) {
+ // SAFETY: `SystemTimeZone` is only ever created with a valid `CFTimeZoneRef`.
+ unsafe { CFRelease(self.0 as CFTypeRef) };
+ }
+ }
+
+ impl SystemTimeZone {
+ pub(crate) fn new() -> Option<Self> {
+ // SAFETY: No invariants to uphold. We'll release the pointer when we don't need it anymore.
+ let v: CFTimeZoneRef = unsafe { CFTimeZoneCopySystem() };
+ if v.is_null() {
+ None
+ } else {
+ Some(SystemTimeZone(v))
+ }
+ }
+
+ /// Get the time zone name as a [super::string_ref::StringRef].
+ ///
+ /// The lifetime of the `StringRef` is bound to our lifetime. Mutable
+ /// access is also prevented by taking a reference to `self`.
+ pub(crate) fn name(&self) -> Option<super::string_ref::StringRef<'_, Self>> {
+ // SAFETY: `SystemTimeZone` is only ever created with a valid `CFTimeZoneRef`.
+ let string = unsafe { CFTimeZoneGetName(self.0) };
+ if string.is_null() {
+ None
+ } else {
+ // SAFETY: here we ensure that `string` is a valid pointer.
+ Some(unsafe { super::string_ref::StringRef::new(string, self) })
+ }
+ }
+ }
+}
+
+mod string_ref {
+ //! create safe wrapper around `CFStringRef`
+
+ use std::convert::TryInto;
+
+ use core_foundation_sys::base::{Boolean, CFRange};
+ use core_foundation_sys::string::{
+ kCFStringEncodingUTF8, CFStringGetBytes, CFStringGetCStringPtr, CFStringGetLength,
+ CFStringRef,
+ };
+
+ pub(crate) struct StringRef<'a, T> {
+ string: CFStringRef,
+ // We exclude mutable access to the parent by taking a reference to the
+ // parent (rather than, for example, just using a marker to enforce the
+ // parent's lifetime).
+ _parent: &'a T,
+ }
+
+ impl<'a, T> StringRef<'a, T> {
+ // SAFETY: `StringRef` must be valid pointer
+ pub(crate) unsafe fn new(string: CFStringRef, _parent: &'a T) -> Self {
+ Self { string, _parent }
+ }
+
+ pub(crate) fn as_utf8(&self) -> Option<&'a str> {
+ // SAFETY: `StringRef` is only ever created with a valid `CFStringRef`.
+ let v = unsafe { CFStringGetCStringPtr(self.string, kCFStringEncodingUTF8) };
+ if !v.is_null() {
+ // SAFETY: `CFStringGetCStringPtr()` returns NUL-terminated strings.
+ let v = unsafe { std::ffi::CStr::from_ptr(v) };
+ if let Ok(v) = v.to_str() {
+ return Some(v);
+ }
+ }
+ None
+ }
+
+ pub(crate) fn to_utf8<'b>(&self, buf: &'b mut [u8]) -> Option<&'b str> {
+ // SAFETY: `StringRef` is only ever created with a valid `CFStringRef`.
+ let length = unsafe { CFStringGetLength(self.string) };
+
+ let mut buf_bytes = 0;
+ let range = CFRange {
+ location: 0,
+ length,
+ };
+
+ let converted_bytes = unsafe {
+ // SAFETY: `StringRef` is only ever created with a valid `CFStringRef`.
+ CFStringGetBytes(
+ self.string,
+ range,
+ kCFStringEncodingUTF8,
+ b'\0',
+ false as Boolean,
+ buf.as_mut_ptr(),
+ buf.len() as isize,
+ &mut buf_bytes,
+ )
+ };
+ if converted_bytes != length {
+ return None;
+ }
+
+ let len = buf_bytes.try_into().ok()?;
+ let s = buf.get(..len)?;
+ std::str::from_utf8(s).ok()
+ }
+ }
+}
diff --git a/vendor/iana-time-zone/src/tz_netbsd.rs b/vendor/iana-time-zone/src/tz_netbsd.rs
new file mode 100644
index 000000000..84cf8b05e
--- /dev/null
+++ b/vendor/iana-time-zone/src/tz_netbsd.rs
@@ -0,0 +1,25 @@
+use std::fs::read_link;
+
+pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
+ // see https://www.cyberciti.biz/faq/openbsd-time-zone-howto/
+
+ // This is a backport of the Linux implementation.
+ // NetBSDs is less than thorough how the softlink should be set up.
+
+ const PREFIXES: &[&str] = &[
+ "/usr/share/zoneinfo/", // absolute path
+ "../usr/share/zoneinfo/", // relative path
+ ];
+ let mut s = read_link("/etc/localtime")?
+ .into_os_string()
+ .into_string()
+ .map_err(|_| crate::GetTimezoneError::FailedParsingString)?;
+ for &prefix in PREFIXES {
+ if s.starts_with(prefix) {
+ // Trim to the correct length without allocating.
+ s.replace_range(..prefix.len(), "");
+ return Ok(s);
+ }
+ }
+ Err(crate::GetTimezoneError::FailedParsingString)
+}
diff --git a/vendor/iana-time-zone/src/tz_wasm32.rs b/vendor/iana-time-zone/src/tz_wasm32.rs
new file mode 100644
index 000000000..69c36b553
--- /dev/null
+++ b/vendor/iana-time-zone/src/tz_wasm32.rs
@@ -0,0 +1,21 @@
+use js_sys::{Array, Intl, Object, Reflect};
+use wasm_bindgen::JsValue;
+
+pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
+ let intl = Intl::DateTimeFormat::new(&Array::new(), &Object::new()).resolved_options();
+ Reflect::get(&intl, &JsValue::from_str("timeZone"))
+ .ok()
+ .and_then(|tz| tz.as_string())
+ .ok_or(crate::GetTimezoneError::OsError)
+}
+
+#[cfg(test)]
+mod tests {
+ use wasm_bindgen_test::*;
+
+ #[wasm_bindgen_test]
+ fn pass() {
+ let tz = super::get_timezone_inner().unwrap();
+ console_log!("tz={:?}", tz);
+ }
+}
diff --git a/vendor/iana-time-zone/src/tz_windows.rs b/vendor/iana-time-zone/src/tz_windows.rs
new file mode 100644
index 000000000..eaa5d5a48
--- /dev/null
+++ b/vendor/iana-time-zone/src/tz_windows.rs
@@ -0,0 +1,13 @@
+use windows::Globalization::Calendar;
+
+impl From<windows::core::Error> for crate::GetTimezoneError {
+ fn from(orig: windows::core::Error) -> Self {
+ crate::GetTimezoneError::IoError(std::io::Error::new(std::io::ErrorKind::Other, orig))
+ }
+}
+
+pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
+ let cal = Calendar::new()?;
+ let tz_hstring = cal.GetTimeZone()?;
+ Ok(tz_hstring.to_string())
+}