chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

718
vendor/iana-time-zone/src/ffi_utils.rs vendored Normal file
View File

@@ -0,0 +1,718 @@
//! Cross platform FFI helpers.
#[cfg(any(test, target_os = "android"))]
use std::ffi::CStr;
/// A buffer to store the timezone name when calling the C API.
#[cfg(any(test, target_vendor = "apple", target_env = "ohos"))]
pub(crate) mod buffer {
/// The longest name in the IANA time zone database is 32 ASCII characters long.
pub const MAX_LEN: usize = 64;
/// Return a buffer to store the timezone name.
///
/// The buffer is used to store the timezone name when calling the C API.
pub const fn tzname_buf() -> [u8; MAX_LEN] {
[0; MAX_LEN]
}
}
// 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.
//
// TODO: Use a `c"..."` literal when MSRV is upgraded beyond 1.77.0.
// https://doc.rust-lang.org/edition-guide/rust-2021/c-string-literals.html
#[cfg(any(test, target_os = "android"))]
const ANDROID_TIMEZONE_PROPERTY_NAME: &[u8] = b"persist.sys.timezone\0";
/// Return a [`CStr`] to access the timezone from an Android system properties
/// environment.
#[cfg(any(test, target_os = "android"))]
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 core::mem::size_of_val;
use std::ffi::CStr;
use super::buffer::{tzname_buf, MAX_LEN};
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();
}
/// An exhaustive set of IANA timezone names for testing.
///
/// Pulled from Wikipedia as of Sat March 22, 2025:
///
/// - <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>
/// - <https://en.wikipedia.org/w/index.php?title=List_of_tz_database_time_zones&oldid=1281103182>
static KNOWN_TIMEZONE_NAMES: &[&str] = &[
"Africa/Abidjan",
"Africa/Accra",
"Africa/Addis_Ababa",
"Africa/Algiers",
"Africa/Asmara",
"Africa/Asmera",
"Africa/Bamako",
"Africa/Bangui",
"Africa/Banjul",
"Africa/Bissau",
"Africa/Blantyre",
"Africa/Brazzaville",
"Africa/Bujumbura",
"Africa/Cairo",
"Africa/Casablanca",
"Africa/Conakry",
"Africa/Dakar",
"Africa/Dar_es_Salaam",
"Africa/Djibouti",
"Africa/Douala",
"Africa/El_Aaiun",
"Africa/Freetown",
"Africa/Gaborone",
"Africa/Harare",
"Africa/Johannesburg",
"Africa/Juba",
"Africa/Kampala",
"Africa/Khartoum",
"Africa/Kigali",
"Africa/Libreville",
"Africa/Lome",
"Africa/Luanda",
"Africa/Lusaka",
"Africa/Malabo",
"Africa/Maseru",
"Africa/Mbabane",
"Africa/Mogadishu",
"Africa/Monrovia",
"Africa/Nairobi",
"Africa/Ndjamena",
"Africa/Niamey",
"Africa/Nouakchott",
"Africa/Ouagadougou",
"Africa/Porto-Novo",
"Africa/Sao_Tome",
"Africa/Timbuktu",
"Africa/Tripoli",
"Africa/Tunis",
"Africa/Windhoek",
"America/Anguilla",
"America/Antigua",
"America/Argentina/ComodRivadavia",
"America/Aruba",
"America/Asuncion",
"America/Atka",
"America/Barbados",
"America/Belize",
"America/Bogota",
"America/Buenos_Aires",
"America/Caracas",
"America/Catamarca",
"America/Cayenne",
"America/Cayman",
"America/Coral_Harbour",
"America/Cordoba",
"America/Costa_Rica",
"America/Curacao",
"America/Dominica",
"America/El_Salvador",
"America/Ensenada",
"America/Fort_Wayne",
"America/Godthab",
"America/Grand_Turk",
"America/Grenada",
"America/Guadeloupe",
"America/Guatemala",
"America/Guyana",
"America/Havana",
"America/Indianapolis",
"America/Jamaica",
"America/Jujuy",
"America/Knox_IN",
"America/Kralendijk",
"America/La_Paz",
"America/Lima",
"America/Louisville",
"America/Lower_Princes",
"America/Managua",
"America/Marigot",
"America/Martinique",
"America/Mendoza",
"America/Miquelon",
"America/Montevideo",
"America/Montreal",
"America/Montserrat",
"America/Nassau",
"America/Nipigon",
"America/Pangnirtung",
"America/Paramaribo",
"America/Port-au-Prince",
"America/Port_of_Spain",
"America/Porto_Acre",
"America/Rainy_River",
"America/Rosario",
"America/Santa_Isabel",
"America/Santo_Domingo",
"America/Shiprock",
"America/St_Barthelemy",
"America/St_Kitts",
"America/St_Lucia",
"America/St_Thomas",
"America/St_Vincent",
"America/Tegucigalpa",
"America/Thunder_Bay",
"America/Tortola",
"America/Virgin",
"America/Yellowknife",
"Antarctica/South_Pole",
"Arctic/Longyearbyen",
"Asia/Aden",
"Asia/Amman",
"Asia/Ashgabat",
"Asia/Ashkhabad",
"Asia/Baghdad",
"Asia/Bahrain",
"Asia/Baku",
"Asia/Beirut",
"Asia/Bishkek",
"Asia/Brunei",
"Asia/Calcutta",
"Asia/Choibalsan",
"Asia/Chongqing",
"Asia/Chungking",
"Asia/Colombo",
"Asia/Dacca",
"Asia/Damascus",
"Asia/Dhaka",
"Asia/Dili",
"Asia/Dushanbe",
"Asia/Harbin",
"Asia/Hong_Kong",
"Asia/Istanbul",
"Asia/Jerusalem",
"Asia/Kabul",
"Asia/Karachi",
"Asia/Kashgar",
"Asia/Kathmandu",
"Asia/Katmandu",
"Asia/Kolkata",
"Asia/Kuwait",
"Asia/Macao",
"Asia/Macau",
"Asia/Manila",
"Asia/Muscat",
"Asia/Phnom_Penh",
"Asia/Pyongyang",
"Asia/Qatar",
"Asia/Rangoon",
"Asia/Saigon",
"Asia/Seoul",
"Asia/Taipei",
"Asia/Tbilisi",
"Asia/Tehran",
"Asia/Tel_Aviv",
"Asia/Thimbu",
"Asia/Thimphu",
"Asia/Ujung_Pandang",
"Asia/Ulan_Bator",
"Asia/Vientiane",
"Asia/Yangon",
"Asia/Yerevan",
"Atlantic/Bermuda",
"Atlantic/Cape_Verde",
"Atlantic/Faeroe",
"Atlantic/Faroe",
"Atlantic/Jan_Mayen",
"Atlantic/Reykjavik",
"Atlantic/South_Georgia",
"Atlantic/St_Helena",
"Atlantic/Stanley",
"Australia/ACT",
"Australia/Canberra",
"Australia/Currie",
"Australia/LHI",
"Australia/North",
"Australia/NSW",
"Australia/Queensland",
"Australia/South",
"Australia/Tasmania",
"Australia/Victoria",
"Australia/West",
"Australia/Yancowinna",
"Brazil/Acre",
"Brazil/DeNoronha",
"Brazil/East",
"Brazil/West",
"Canada/Atlantic",
"Canada/Central",
"Canada/Eastern",
"Canada/Mountain",
"Canada/Newfoundland",
"Canada/Pacific",
"Canada/Saskatchewan",
"Canada/Yukon",
"CET",
"Chile/Continental",
"Chile/EasterIsland",
"CST6CDT",
"Cuba",
"EET",
"Egypt",
"Eire",
"EST",
"EST5EDT",
"Etc/GMT",
"Etc/GMT+0",
"Etc/GMT+1",
"Etc/GMT+10",
"Etc/GMT+11",
"Etc/GMT+12",
"Etc/GMT+2",
"Etc/GMT+3",
"Etc/GMT+4",
"Etc/GMT+5",
"Etc/GMT+6",
"Etc/GMT+7",
"Etc/GMT+8",
"Etc/GMT+9",
"Etc/GMT-0",
"Etc/GMT-1",
"Etc/GMT-10",
"Etc/GMT-11",
"Etc/GMT-12",
"Etc/GMT-13",
"Etc/GMT-14",
"Etc/GMT-2",
"Etc/GMT-3",
"Etc/GMT-4",
"Etc/GMT-5",
"Etc/GMT-6",
"Etc/GMT-7",
"Etc/GMT-8",
"Etc/GMT-9",
"Etc/GMT0",
"Etc/Greenwich",
"Etc/UCT",
"Etc/Universal",
"Etc/UTC",
"Etc/Zulu",
"Europe/Amsterdam",
"Europe/Andorra",
"Europe/Athens",
"Europe/Belfast",
"Europe/Belgrade",
"Europe/Bratislava",
"Europe/Brussels",
"Europe/Bucharest",
"Europe/Budapest",
"Europe/Chisinau",
"Europe/Copenhagen",
"Europe/Dublin",
"Europe/Gibraltar",
"Europe/Guernsey",
"Europe/Helsinki",
"Europe/Isle_of_Man",
"Europe/Istanbul",
"Europe/Jersey",
"Europe/Kiev",
"Europe/Ljubljana",
"Europe/London",
"Europe/Luxembourg",
"Europe/Malta",
"Europe/Mariehamn",
"Europe/Minsk",
"Europe/Monaco",
"Europe/Nicosia",
"Europe/Oslo",
"Europe/Paris",
"Europe/Podgorica",
"Europe/Prague",
"Europe/Riga",
"Europe/Rome",
"Europe/San_Marino",
"Europe/Sarajevo",
"Europe/Skopje",
"Europe/Sofia",
"Europe/Stockholm",
"Europe/Tallinn",
"Europe/Tirane",
"Europe/Tiraspol",
"Europe/Uzhgorod",
"Europe/Vaduz",
"Europe/Vatican",
"Europe/Vienna",
"Europe/Vilnius",
"Europe/Warsaw",
"Europe/Zagreb",
"Europe/Zaporozhye",
"Factory",
"GB",
"GB-Eire",
"GMT",
"GMT+0",
"GMT-0",
"GMT0",
"Greenwich",
"Hongkong",
"HST",
"Iceland",
"Indian/Antananarivo",
"Indian/Chagos",
"Indian/Christmas",
"Indian/Cocos",
"Indian/Comoro",
"Indian/Kerguelen",
"Indian/Mahe",
"Indian/Mauritius",
"Indian/Mayotte",
"Indian/Reunion",
"Iran",
"Israel",
"Jamaica",
"Japan",
"Kwajalein",
"Libya",
"MET",
"Mexico/BajaNorte",
"Mexico/BajaSur",
"Mexico/General",
"MST",
"MST7MDT",
"Navajo",
"NZ",
"NZ-CHAT",
"Pacific/Apia",
"Pacific/Efate",
"Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Fiji",
"Pacific/Funafuti",
"Pacific/Guam",
"Pacific/Johnston",
"Pacific/Nauru",
"Pacific/Niue",
"Pacific/Norfolk",
"Pacific/Noumea",
"Pacific/Palau",
"Pacific/Pitcairn",
"Pacific/Ponape",
"Pacific/Rarotonga",
"Pacific/Saipan",
"Pacific/Samoa",
"Pacific/Tongatapu",
"Pacific/Truk",
"Pacific/Wallis",
"Pacific/Yap",
"Poland",
"Portugal",
"PRC",
"PST8PDT",
"ROC",
"ROK",
"Singapore",
"Turkey",
"UCT",
"Universal",
"US/Alaska",
"US/Aleutian",
"US/Arizona",
"US/Central",
"US/East-Indiana",
"US/Eastern",
"US/Hawaii",
"US/Indiana-Starke",
"US/Michigan",
"US/Mountain",
"US/Pacific",
"US/Samoa",
"UTC",
"W-SU",
"WET",
"Zulu",
"America/Rio_Branco",
"America/Maceio",
"America/Metlakatla",
"America/Juneau",
"America/Sitka",
"America/Adak",
"America/Yakutat",
"America/Anchorage",
"America/Nome",
"America/Manaus",
"America/Eirunepe",
"Asia/Aqtobe",
"America/Blanc-Sablon",
"America/Puerto_Rico",
"America/Goose_Bay",
"America/Moncton",
"America/Glace_Bay",
"America/Halifax",
"America/Noronha",
"Asia/Atyrau",
"Atlantic/Azores",
"America/Bahia",
"America/Bahia_Banderas",
"America/Tijuana",
"America/Mazatlan",
"Asia/Hovd",
"Asia/Shanghai",
"Asia/Makassar",
"Asia/Pontianak",
"Pacific/Bougainville",
"America/Fortaleza",
"America/Sao_Paulo",
"America/Argentina/Buenos_Aires",
"Europe/Busingen",
"Europe/Zurich",
"America/Merida",
"Atlantic/Canary",
"Antarctica/Casey",
"America/Argentina/Catamarca",
"America/Indiana/Tell_City",
"America/Indiana/Knox",
"America/Menominee",
"America/North_Dakota/Beulah",
"America/North_Dakota/New_Salem",
"America/North_Dakota/Center",
"America/Rankin_Inlet",
"America/Resolute",
"America/Winnipeg",
"America/Chicago",
"Africa/Maputo",
"America/Mexico_City",
"Africa/Ceuta",
"Pacific/Chatham",
"America/Chihuahua",
"America/Ojinaga",
"America/Ciudad_Juarez",
"Pacific/Chuuk",
"America/Matamoros",
"Europe/Simferopol",
"Asia/Dubai",
"America/Swift_Current",
"America/Regina",
"Antarctica/Davis",
"Africa/Lubumbashi",
"Africa/Kinshasa",
"Antarctica/DumontDUrville",
"America/Monterrey",
"Pacific/Easter",
"America/Indiana/Marengo",
"America/Indiana/Vincennes",
"America/Indiana/Indianapolis",
"America/Indiana/Petersburg",
"America/Indiana/Winamac",
"America/Indiana/Vevay",
"America/Kentucky/Louisville",
"America/Kentucky/Monticello",
"America/Detroit",
"America/Iqaluit",
"America/Toronto",
"America/New_York",
"America/Guayaquil",
"America/Atikokan",
"America/Panama",
"Asia/Tokyo",
"Pacific/Galapagos",
"Pacific/Gambier",
"Asia/Gaza",
"Pacific/Tarawa",
"Pacific/Honolulu",
"Asia/Jakarta",
"America/Argentina/Jujuy",
"Indian/Maldives",
"Pacific/Kosrae",
"Pacific/Kwajalein",
"America/Argentina/La_Rioja",
"Pacific/Kiritimati",
"Australia/Lord_Howe",
"Antarctica/Macquarie",
"Atlantic/Madeira",
"Asia/Kuala_Lumpur",
"Asia/Aqtau",
"Pacific/Marquesas",
"America/Cuiaba",
"America/Campo_Grande",
"Antarctica/Mawson",
"America/Argentina/Mendoza",
"Pacific/Pago_Pago",
"Pacific/Midway",
"America/Argentina/Cordoba",
"America/Santiago",
"Asia/Nicosia",
"Europe/Berlin",
"America/Nuuk",
"Asia/Almaty",
"Pacific/Majuro",
"Asia/Ulaanbaatar",
"Europe/Kyiv",
"America/Edmonton",
"America/Boise",
"America/Inuvik",
"America/Cambridge_Bay",
"America/Denver",
"Europe/Kaliningrad",
"Europe/Kirov",
"Europe/Moscow",
"Europe/Volgograd",
"Europe/Astrakhan",
"Europe/Samara",
"Europe/Saratov",
"Europe/Ulyanovsk",
"Asia/Yekaterinburg",
"Asia/Omsk",
"Asia/Barnaul",
"Asia/Novokuznetsk",
"Asia/Krasnoyarsk",
"Asia/Novosibirsk",
"Asia/Tomsk",
"Asia/Irkutsk",
"Asia/Yakutsk",
"Asia/Khandyga",
"Asia/Chita",
"Asia/Vladivostok",
"Asia/Ust-Nera",
"Asia/Magadan",
"Asia/Srednekolymsk",
"Asia/Sakhalin",
"Asia/Anadyr",
"Asia/Kamchatka",
"America/Phoenix",
"America/Creston",
"America/Dawson_Creek",
"America/Fort_Nelson",
"America/Whitehorse",
"America/Dawson",
"America/Danmarkshavn",
"Asia/Jayapura",
"Australia/Sydney",
"Australia/Broken_Hill",
"Pacific/Auckland",
"Antarctica/McMurdo",
"America/St_Johns",
"Asia/Bangkok",
"Asia/Famagusta",
"Australia/Darwin",
"America/Los_Angeles",
"America/Vancouver",
"Antarctica/Palmer",
"Pacific/Port_Moresby",
"America/Belem",
"America/Santarem",
"Asia/Singapore",
"America/Recife",
"Pacific/Kanton",
"Pacific/Guadalcanal",
"Pacific/Pohnpei",
"Europe/Lisbon",
"Asia/Qostanay",
"Australia/Brisbane",
"Australia/Lindeman",
"America/Cancun",
"Asia/Qyzylorda",
"America/Punta_Arenas",
"America/Porto_Velho",
"America/Boa_Vista",
"Antarctica/Rothera",
"Asia/Kuching",
"America/Argentina/Salta",
"America/Argentina/San_Juan",
"America/Argentina/San_Luis",
"America/Argentina/Rio_Gallegos",
"America/Scoresbysund",
"Pacific/Tahiti",
"America/Hermosillo",
"Australia/Adelaide",
"Asia/Ho_Chi_Minh",
"Europe/Madrid",
"Antarctica/Syowa",
"Asia/Riyadh",
"Australia/Hobart",
"America/Thule",
"America/Argentina/Ushuaia",
"America/Araguaina",
"Antarctica/Troll",
"America/Argentina/Tucuman",
"Asia/Tashkent",
"Asia/Samarkand",
"Australia/Melbourne",
"Antarctica/Vostok",
"Pacific/Wake",
"Africa/Lagos",
"Asia/Hebron",
"Asia/Oral",
"Australia/Eucla",
"Australia/Perth",
"Asia/Urumqi",
];
#[test]
fn test_tzname_buffer_fits_all_iana_names() {
let buf = tzname_buf();
let max_len = buf.len();
let mut failed_tz_names = vec![];
for &tz in KNOWN_TIMEZONE_NAMES {
// Require max_len + 1 to account for an optional NUL terminator.
if tz.len() >= max_len {
failed_tz_names.push(tz);
}
}
assert!(
failed_tz_names.is_empty(),
"One or more timezone names exceed the buffer length of {}. Max length of found timezone: {}\n{:?}",
max_len,
failed_tz_names.iter().map(|s| s.len()).max().unwrap(),
failed_tz_names
);
}
#[test]
fn test_tzname_buffer_correct_size() {
assert_eq!(
MAX_LEN, 64,
"Buffer length changed unexpectedly, ensure consistency with documented limit."
);
assert_eq!(
tzname_buf().len(),
MAX_LEN,
"Buffer length changed unexpectedly, ensure consistency with documented limit."
);
assert_eq!(
size_of_val(&tzname_buf()),
MAX_LEN,
"Buffer length changed unexpectedly, ensure consistency with documented limit."
);
}
}

127
vendor/iana-time-zone/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,127 @@
#![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:
//! ```rust
//! let tz_str = iana_time_zone::get_timezone()?;
//! let tz: chrono_tz::Tz = tz_str.parse()?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
#[allow(dead_code)]
mod ffi_utils;
#[cfg_attr(
any(all(target_os = "linux", not(target_env = "ohos")), target_os = "hurd"),
path = "tz_linux.rs"
)]
#[cfg_attr(all(target_os = "linux", target_env = "ohos"), path = "tz_ohos.rs")]
#[cfg_attr(target_os = "windows", path = "tz_windows.rs")]
#[cfg_attr(target_vendor = "apple", path = "tz_darwin.rs")]
#[cfg_attr(
all(target_arch = "wasm32", target_os = "unknown"),
path = "tz_wasm32_unknown.rs"
)]
#[cfg_attr(
all(target_arch = "wasm32", target_os = "emscripten"),
path = "tz_wasm32_emscripten.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 = "aix", path = "tz_aix.rs")]
#[cfg_attr(target_os = "android", path = "tz_android.rs")]
#[cfg_attr(target_os = "haiku", path = "tz_haiku.rs")]
#[cfg_attr(
all(target_arch = "wasm32", target_os = "wasi"),
path = "tz_wasm32_wasi.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());
}
}

9
vendor/iana-time-zone/src/platform.rs vendored Normal file
View File

@@ -0,0 +1,9 @@
pub fn get_timezone_inner() -> 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), wasip, emscripten, iOS, Illumos, Android, AIX, Solaris and Haiku.",
);

7
vendor/iana-time-zone/src/tz_aix.rs vendored Normal file
View File

@@ -0,0 +1,7 @@
use std::env;
use std::fs::OpenOptions;
use std::io::{BufRead, BufReader};
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
env::var("TZ").map_err(|_| crate::GetTimezoneError::OsError)
}

27
vendor/iana-time-zone/src/tz_android.rs vendored Normal file
View File

@@ -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() }
}

179
vendor/iana-time-zone/src/tz_darwin.rs vendored Normal file
View File

@@ -0,0 +1,179 @@
use crate::ffi_utils::buffer::{tzname_buf, MAX_LEN};
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
get_timezone().ok_or(crate::GetTimezoneError::OsError)
}
#[inline]
fn get_timezone() -> Option<String> {
let mut buf = tzname_buf();
// 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, CFTimeZoneResetSystem,
};
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 {
/// Creates a new `SystemTimeZone` by querying the current Darwin system
/// timezone.
///
/// This function implicitly calls `CFTimeZoneResetSystem` to invalidate
/// the cached timezone and ensure we always retrieve the current system
/// timezone.
///
/// Due to CoreFoundation's internal caching mechanism, subsequent calls
/// to `CFTimeZoneCopySystem` do not reflect system timezone changes
/// made while the process is running. Thus, we explicitly call
/// `CFTimeZoneResetSystem` first to invalidate the cached value and
/// ensure we always retrieve the current system timezone.
pub(crate) fn new() -> Option<Self> {
// SAFETY:
// - Both `CFTimeZoneResetSystem` and `CFTimeZoneCopySystem` are FFI
// calls to macOS CoreFoundation.
// - `CFTimeZoneResetSystem` safely invalidates the cached timezone
// without any external invariants.
// - The pointer returned by `CFTimeZoneCopySystem` is managed and
// released properly within `Drop`.
let v: CFTimeZoneRef = unsafe {
// First, clear the potentially cached timezone. This call will
// take the global lock on the timezone data.
//
// See <https://github.com/strawlab/iana-time-zone/issues/145#issuecomment-2745934606>
// for context on why we reset the timezone here.
CFTimeZoneResetSystem();
// Fetch the current value. This will likely allocate. This call
// will again take the global lock on the timezone data.
CFTimeZoneCopySystem()
};
if v.is_null() {
None
} else {
Some(SystemTimeZone(v))
}
}
/// Get the time zone name as a [`StringRef`].
///
/// The lifetime of the `StringRef` is bound to our lifetime. Mutable
/// access is also prevented by taking a reference to `self`.
///
/// [`StringRef`]: super::string_ref::StringRef
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 core::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: `string` 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 and will return NULL if the internal representation
// of the `CFString`` is not compatible with the requested
// encoding.
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,
};
// SAFETY: `StringRef` is only ever created with a valid `CFStringRef`.
let converted_bytes = unsafe {
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()
}
}
}

View File

@@ -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)
}

3
vendor/iana-time-zone/src/tz_haiku.rs vendored Normal file
View File

@@ -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)
}

22
vendor/iana-time-zone/src/tz_illumos.rs vendored Normal file
View File

@@ -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);
}
}
}

184
vendor/iana-time-zone/src/tz_linux.rs vendored Normal file
View File

@@ -0,0 +1,184 @@
use std::fs::{read_link, read_to_string};
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
etc_localtime()
.or_else(|_| etc_timezone())
.or_else(|_| openwrt::etc_config_system())
}
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)
}
mod openwrt {
use std::io::BufRead;
use std::{fs, io, iter};
pub(crate) fn etc_config_system() -> Result<String, crate::GetTimezoneError> {
let f = fs::OpenOptions::new()
.read(true)
.open("/etc/config/system")?;
let mut f = io::BufReader::new(f);
let mut in_system_section = false;
let mut line = String::with_capacity(80);
// prefer option "zonename" (IANA time zone) over option "timezone" (POSIX time zone)
let mut timezone = None;
loop {
line.clear();
f.read_line(&mut line)?;
if line.is_empty() {
break;
}
let mut iter = IterWords(&line);
let mut next = || iter.next().transpose();
if let Some(keyword) = next()? {
if keyword == "config" {
in_system_section = next()? == Some("system") && next()?.is_none();
} else if in_system_section && keyword == "option" {
if let Some(key) = next()? {
if key == "zonename" {
if let (Some(zonename), None) = (next()?, next()?) {
return Ok(zonename.to_owned());
}
} else if key == "timezone" {
if let (Some(value), None) = (next()?, next()?) {
timezone = Some(value.to_owned());
}
}
}
}
}
}
timezone.ok_or(crate::GetTimezoneError::OsError)
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
struct BrokenQuote;
impl From<BrokenQuote> for crate::GetTimezoneError {
fn from(_: BrokenQuote) -> Self {
crate::GetTimezoneError::FailedParsingString
}
}
/// Iterated over all words in a OpenWRT config line.
struct IterWords<'a>(&'a str);
impl<'a> Iterator for IterWords<'a> {
type Item = Result<&'a str, BrokenQuote>;
fn next(&mut self) -> Option<Self::Item> {
match read_word(self.0) {
Ok(Some((item, tail))) => {
self.0 = tail;
Some(Ok(item))
}
Ok(None) => {
self.0 = "";
None
}
Err(err) => {
self.0 = "";
Some(Err(err))
}
}
}
}
impl iter::FusedIterator for IterWords<'_> {}
/// Read the next word in a OpenWRT config line. Strip any surrounding quotation marks.
///
/// Returns
///
/// * a tuple `Some((word, remaining_line))` if found,
/// * `None` if the line is exhausted, or
/// * `Err(BrokenQuote)` if the line could not be parsed.
#[allow(clippy::manual_strip)] // needs to be compatile to 1.36
fn read_word(s: &str) -> Result<Option<(&str, &str)>, BrokenQuote> {
let s = s.trim_start();
if s.is_empty() || s.starts_with('#') {
Ok(None)
} else if s.starts_with('\'') {
let mut iter = s[1..].splitn(2, '\'');
match (iter.next(), iter.next()) {
(Some(item), Some(tail)) => Ok(Some((item, tail))),
_ => Err(BrokenQuote),
}
} else if s.starts_with('"') {
let mut iter = s[1..].splitn(2, '"');
match (iter.next(), iter.next()) {
(Some(item), Some(tail)) => Ok(Some((item, tail))),
_ => Err(BrokenQuote),
}
} else {
let mut iter = s.splitn(2, |c: char| c.is_whitespace());
match (iter.next(), iter.next()) {
(Some(item), Some(tail)) => Ok(Some((item, tail))),
_ => Ok(Some((s, ""))),
}
}
}
#[cfg(test)]
#[test]
fn test_read_word() {
assert_eq!(
read_word(" option timezone 'CST-8'\n").unwrap(),
Some(("option", "timezone 'CST-8'\n")),
);
assert_eq!(
read_word("timezone 'CST-8'\n").unwrap(),
Some(("timezone", "'CST-8'\n")),
);
assert_eq!(read_word("'CST-8'\n").unwrap(), Some(("CST-8", "\n")));
assert_eq!(read_word("\n").unwrap(), None);
assert_eq!(
read_word(r#""time 'Zone'""#).unwrap(),
Some(("time 'Zone'", "")),
);
assert_eq!(read_word("'CST-8").unwrap_err(), BrokenQuote);
}
}

25
vendor/iana-time-zone/src/tz_netbsd.rs vendored Normal file
View File

@@ -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)
}

47
vendor/iana-time-zone/src/tz_ohos.rs vendored Normal file
View File

@@ -0,0 +1,47 @@
//! OpenHarmony doesn't have `/etc/localtime`, we have to use it's "Time Service" to get the timezone information:
//!
//! - [API Reference](https://gitee.com/openharmony/docs/blob/43726785b4033887cd1a838aaaca5e255897a71e/en/application-dev/reference/apis-basic-services-kit/_time_service.md#oh_timeservice_gettimezone)
use crate::ffi_utils::buffer::{tzname_buf, MAX_LEN};
use crate::GetTimezoneError;
use std::ffi::{c_char, CStr};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(C)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
enum TimeService_ErrCode {
TIMESERVICE_ERR_OK = 0,
TIMESERVICE_ERR_INTERNAL_ERROR = 13000001,
TIMESERVICE_ERR_INVALID_PARAMETER = 13000002,
}
#[link(name = "time_service_ndk", kind = "dylib")]
extern "C" {
fn OH_TimeService_GetTimeZone(timeZone: *mut c_char, len: u32) -> TimeService_ErrCode;
}
/// TODO: Change this `CStr::from_bytes_until_nul` once MSRV was bumped above 1.69.0
fn from_bytes_until_nul(bytes: &[u8]) -> Option<&CStr> {
let nul_pos = bytes.iter().position(|&b| b == 0)?;
// SAFETY:
// 1. nul_pos + 1 <= bytes.len()
// 2. We know there is a nul byte at nul_pos, so this slice (ending at the nul byte) is a well-formed C string.
Some(unsafe { CStr::from_bytes_with_nul_unchecked(&bytes[..=nul_pos]) })
}
pub(crate) fn get_timezone_inner() -> Result<String, GetTimezoneError> {
let mut time_zone = tzname_buf();
// SAFETY:
// `time_zone` is a valid buffer with a length of 40 bytes.
let ret = unsafe {
OH_TimeService_GetTimeZone(time_zone.as_mut_ptr().cast::<c_char>(), MAX_LEN as u32 - 1)
};
if ret != TimeService_ErrCode::TIMESERVICE_ERR_OK {
return Err(GetTimezoneError::OsError);
}
from_bytes_until_nul(&time_zone)
.and_then(|x| x.to_str().ok())
.map(|x| x.to_owned())
.ok_or(GetTimezoneError::OsError)
}

View File

@@ -0,0 +1,30 @@
use crate::GetTimezoneError;
use std::ffi::CStr;
use std::os::raw::c_char;
use std::ptr::NonNull;
extern "C" {
fn emscripten_run_script_string(script: *const c_char) -> *mut c_char;
}
pub(crate) fn get_timezone_inner() -> Result<String, GetTimezoneError> {
const SCRIPT: &CStr = {
match CStr::from_bytes_with_nul(
"Intl.DateTimeFormat().resolvedOptions().timeZone\0".as_bytes(),
) {
Ok(s) => s,
Err(_) => panic!("Invalid UTF-8 data"),
}
};
unsafe {
NonNull::new(emscripten_run_script_string(SCRIPT.as_ptr()))
.ok_or_else(|| GetTimezoneError::OsError)
.and_then(|ptr| {
CStr::from_ptr(ptr.as_ptr())
.to_owned()
.into_string()
.map_err(|_| GetTimezoneError::FailedParsingString)
})
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,3 @@
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
std::env::var("TZ").or_else(|_| Ok("Etc/UTC".to_owned()))
}

16
vendor/iana-time-zone/src/tz_windows.rs vendored Normal file
View File

@@ -0,0 +1,16 @@
#[path = "windows_bindings.rs"]
#[allow(missing_debug_implementations, clippy::undocumented_unsafe_blocks)]
mod windows_bindings;
use windows_bindings::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())
}

File diff suppressed because it is too large Load Diff