Files
inbuxa-migrate/src/exchange/tz.rs
T

300 lines
11 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* 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<String> {
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")
);
}
}