/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: Apache-2.0 OR MIT */ pub fn windows_to_iana(windows_name: &str) -> Option<&'static str> { let key = windows_name.trim(); for (w, i) in TABLE { if w.eq_ignore_ascii_case(key) { return Some(i); } } None } pub fn resolve_to_iana(value: &str) -> Option { use calcard::common::timezone::Tz; use std::str::FromStr; let key = value.trim(); if key.is_empty() { return None; } if let Some(rest) = key.strip_prefix("tzone://Microsoft/") { return rest .eq_ignore_ascii_case("Utc") .then(|| "Etc/UTC".to_owned()); } if let Some(iana) = windows_to_iana(key) { return Some(iana.to_owned()); } Tz::from_str(key) .ok() .and_then(|tz| tz.name().map(|name| name.into_owned())) } const TABLE: &[(&str, &str)] = &[ ("UTC", "Etc/UTC"), ("Coordinated Universal Time", "Etc/UTC"), ("UTC-11", "Etc/GMT+11"), ("UTC-09", "Etc/GMT+9"), ("UTC-08", "Etc/GMT+8"), ("UTC-02", "Etc/GMT+2"), ("UTC+12", "Etc/GMT-12"), ("UTC+13", "Etc/GMT-13"), ("Dateline Standard Time", "Etc/GMT+12"), ("Aleutian Standard Time", "America/Adak"), ("Hawaiian Standard Time", "Pacific/Honolulu"), ("Marquesas Standard Time", "Pacific/Marquesas"), ("Alaskan Standard Time", "America/Anchorage"), ("Pacific Standard Time (Mexico)", "America/Tijuana"), ("Pacific Standard Time", "America/Los_Angeles"), ("US Mountain Standard Time", "America/Phoenix"), ("Mountain Standard Time (Mexico)", "America/Mazatlan"), ("Mountain Standard Time", "America/Denver"), ("Yukon Standard Time", "America/Whitehorse"), ("Central America Standard Time", "America/Guatemala"), ("Central Standard Time", "America/Chicago"), ("Easter Island Standard Time", "Pacific/Easter"), ("Central Standard Time (Mexico)", "America/Mexico_City"), ("Canada Central Standard Time", "America/Regina"), ("SA Pacific Standard Time", "America/Bogota"), ("Eastern Standard Time (Mexico)", "America/Cancun"), ("Eastern Standard Time", "America/New_York"), ("Haiti Standard Time", "America/Port-au-Prince"), ("Cuba Standard Time", "America/Havana"), ("US Eastern Standard Time", "America/Indianapolis"), ("Turks And Caicos Standard Time", "America/Grand_Turk"), ("Paraguay Standard Time", "America/Asuncion"), ("Atlantic Standard Time", "America/Halifax"), ("Venezuela Standard Time", "America/Caracas"), ("Central Brazilian Standard Time", "America/Cuiaba"), ("SA Western Standard Time", "America/La_Paz"), ("Pacific SA Standard Time", "America/Santiago"), ("Newfoundland Standard Time", "America/St_Johns"), ("Tocantins Standard Time", "America/Araguaina"), ("E. South America Standard Time", "America/Sao_Paulo"), ("SA Eastern Standard Time", "America/Cayenne"), ("Argentina Standard Time", "America/Argentina/Buenos_Aires"), ("Greenland Standard Time", "America/Godthab"), ("Montevideo Standard Time", "America/Montevideo"), ("Magallanes Standard Time", "America/Punta_Arenas"), ("Saint Pierre Standard Time", "America/Miquelon"), ("Bahia Standard Time", "America/Bahia"), ("Mid-Atlantic Standard Time", "Atlantic/South_Georgia"), ("Azores Standard Time", "Atlantic/Azores"), ("Cape Verde Standard Time", "Atlantic/Cape_Verde"), ("GMT Standard Time", "Europe/London"), ("Greenwich Standard Time", "Atlantic/Reykjavik"), ("Sao Tome Standard Time", "Africa/Sao_Tome"), ("Morocco Standard Time", "Africa/Casablanca"), ("W. Europe Standard Time", "Europe/Berlin"), ("Central Europe Standard Time", "Europe/Budapest"), ("Romance Standard Time", "Europe/Paris"), ("Central European Standard Time", "Europe/Warsaw"), ("W. Central Africa Standard Time", "Africa/Lagos"), ("Jordan Standard Time", "Asia/Amman"), ("GTB Standard Time", "Europe/Bucharest"), ("Middle East Standard Time", "Asia/Beirut"), ("Egypt Standard Time", "Africa/Cairo"), ("E. Europe Standard Time", "Europe/Chisinau"), ("Syria Standard Time", "Asia/Damascus"), ("West Bank Standard Time", "Asia/Hebron"), ("South Africa Standard Time", "Africa/Johannesburg"), ("FLE Standard Time", "Europe/Kiev"), ("Israel Standard Time", "Asia/Jerusalem"), ("South Sudan Standard Time", "Africa/Juba"), ("Kaliningrad Standard Time", "Europe/Kaliningrad"), ("Sudan Standard Time", "Africa/Khartoum"), ("Libya Standard Time", "Africa/Tripoli"), ("Namibia Standard Time", "Africa/Windhoek"), ("Arabic Standard Time", "Asia/Baghdad"), ("Turkey Standard Time", "Europe/Istanbul"), ("Arab Standard Time", "Asia/Riyadh"), ("Belarus Standard Time", "Europe/Minsk"), ("Russian Standard Time", "Europe/Moscow"), ("E. Africa Standard Time", "Africa/Nairobi"), ("Iran Standard Time", "Asia/Tehran"), ("Arabian Standard Time", "Asia/Dubai"), ("Astrakhan Standard Time", "Europe/Astrakhan"), ("Azerbaijan Standard Time", "Asia/Baku"), ("Russia Time Zone 3", "Europe/Samara"), ("Mauritius Standard Time", "Indian/Mauritius"), ("Saratov Standard Time", "Europe/Saratov"), ("Georgian Standard Time", "Asia/Tbilisi"), ("Volgograd Standard Time", "Europe/Volgograd"), ("Caucasus Standard Time", "Asia/Yerevan"), ("Afghanistan Standard Time", "Asia/Kabul"), ("West Asia Standard Time", "Asia/Tashkent"), ("Ekaterinburg Standard Time", "Asia/Yekaterinburg"), ("Pakistan Standard Time", "Asia/Karachi"), ("Qyzylorda Standard Time", "Asia/Qyzylorda"), ("India Standard Time", "Asia/Kolkata"), ("Sri Lanka Standard Time", "Asia/Colombo"), ("Nepal Standard Time", "Asia/Katmandu"), ("Central Asia Standard Time", "Asia/Almaty"), ("Bangladesh Standard Time", "Asia/Dhaka"), ("Omsk Standard Time", "Asia/Omsk"), ("Myanmar Standard Time", "Asia/Rangoon"), ("SE Asia Standard Time", "Asia/Bangkok"), ("Altai Standard Time", "Asia/Barnaul"), ("W. Mongolia Standard Time", "Asia/Hovd"), ("North Asia Standard Time", "Asia/Krasnoyarsk"), ("N. Central Asia Standard Time", "Asia/Novosibirsk"), ("Tomsk Standard Time", "Asia/Tomsk"), ("China Standard Time", "Asia/Shanghai"), ("North Asia East Standard Time", "Asia/Irkutsk"), ("Singapore Standard Time", "Asia/Singapore"), ("W. Australia Standard Time", "Australia/Perth"), ("Taipei Standard Time", "Asia/Taipei"), ("Ulaanbaatar Standard Time", "Asia/Ulaanbaatar"), ("Aus Central W. Standard Time", "Australia/Eucla"), ("Transbaikal Standard Time", "Asia/Chita"), ("Tokyo Standard Time", "Asia/Tokyo"), ("North Korea Standard Time", "Asia/Pyongyang"), ("Korea Standard Time", "Asia/Seoul"), ("Yakutsk Standard Time", "Asia/Yakutsk"), ("Cen. Australia Standard Time", "Australia/Adelaide"), ("AUS Central Standard Time", "Australia/Darwin"), ("E. Australia Standard Time", "Australia/Brisbane"), ("AUS Eastern Standard Time", "Australia/Sydney"), ("West Pacific Standard Time", "Pacific/Port_Moresby"), ("Tasmania Standard Time", "Australia/Hobart"), ("Vladivostok Standard Time", "Asia/Vladivostok"), ("Lord Howe Standard Time", "Australia/Lord_Howe"), ("Bougainville Standard Time", "Pacific/Bougainville"), ("Russia Time Zone 10", "Asia/Srednekolymsk"), ("Magadan Standard Time", "Asia/Magadan"), ("Norfolk Standard Time", "Pacific/Norfolk"), ("Sakhalin Standard Time", "Asia/Sakhalin"), ("Central Pacific Standard Time", "Pacific/Guadalcanal"), ("Russia Time Zone 11", "Asia/Kamchatka"), ("New Zealand Standard Time", "Pacific/Auckland"), ("Fiji Standard Time", "Pacific/Fiji"), ("Kamchatka Standard Time", "Asia/Kamchatka"), ("Chatham Islands Standard Time", "Pacific/Chatham"), ("Tonga Standard Time", "Pacific/Tongatapu"), ("Samoa Standard Time", "Pacific/Apia"), ("Line Islands Standard Time", "Pacific/Kiritimati"), ]; #[cfg(test)] mod tests { use super::*; #[test] fn pacific_standard_time_maps_to_los_angeles() { assert_eq!( windows_to_iana("Pacific Standard Time"), Some("America/Los_Angeles") ); } #[test] fn romance_standard_time_maps_to_paris() { assert_eq!( windows_to_iana("Romance Standard Time"), Some("Europe/Paris") ); } #[test] fn unknown_zone_returns_none() { assert_eq!(windows_to_iana("Made Up Zone"), None); } #[test] fn case_insensitive_lookup() { assert_eq!( windows_to_iana("eastern standard time"), Some("America/New_York") ); } #[test] fn whitespace_is_trimmed() { assert_eq!( windows_to_iana(" GMT Standard Time "), Some("Europe/London") ); } #[test] fn full_cldr_table_has_at_least_140_rows() { assert!(TABLE.len() >= 140, "got {} rows", TABLE.len()); } #[test] fn spot_check_coverage_across_continents() { assert_eq!(windows_to_iana("Tokyo Standard Time"), Some("Asia/Tokyo")); assert_eq!( windows_to_iana("AUS Eastern Standard Time"), Some("Australia/Sydney") ); assert_eq!( windows_to_iana("Cen. Australia Standard Time"), Some("Australia/Adelaide") ); assert_eq!( windows_to_iana("Greenland Standard Time"), Some("America/Godthab") ); assert_eq!( windows_to_iana("China Standard Time"), Some("Asia/Shanghai") ); assert_eq!( windows_to_iana("Israel Standard Time"), Some("Asia/Jerusalem") ); assert_eq!( windows_to_iana("Chatham Islands Standard Time"), Some("Pacific/Chatham") ); assert_eq!( windows_to_iana("Newfoundland Standard Time"), Some("America/St_Johns") ); assert_eq!(windows_to_iana("UTC-08"), Some("Etc/GMT+8")); assert_eq!(windows_to_iana("UTC+13"), Some("Etc/GMT-13")); assert_eq!( windows_to_iana("Yukon Standard Time"), Some("America/Whitehorse") ); assert_eq!( windows_to_iana("Aus Central W. Standard Time"), Some("Australia/Eucla") ); } #[test] fn resolve_handles_windows_iana_offset_and_microsoft_tzone() { assert_eq!( resolve_to_iana("Romance Standard Time").as_deref(), Some("Europe/Paris") ); assert_eq!( resolve_to_iana("Europe/Paris").as_deref(), Some("Europe/Paris") ); assert_eq!(resolve_to_iana("UTC").as_deref(), Some("Etc/UTC")); assert_eq!( resolve_to_iana("tzone://Microsoft/Utc").as_deref(), Some("Etc/UTC") ); assert_eq!(resolve_to_iana("tzone://Microsoft/Custom"), None); assert_eq!(resolve_to_iana("Made Up Zone"), None); assert_eq!(resolve_to_iana(""), None); } #[test] fn coordinated_universal_time_alias_resolves_to_utc() { assert_eq!( windows_to_iana("Coordinated Universal Time"), Some("Etc/UTC") ); } }