Files
OxideJS-New/crates/oxide_builtins/src/temporal/time_zone.rs
T

1414 lines
74 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 时区数据源与 TZif 解析器:IANA tzdata 2026b 全量树 vendored 于 `third_party/tzdb/`,
//! 引擎内置 legacy 别名表,不读宿主系统 zoneinfo。
//!
//! 关键约定:
//! - 数据版本钉住 tzdata 2026b,全量树随仓库,保证各环境逐字节一致;legacy 区名
//! (2026b 已移除)由引擎内 `LEGACY_ALIASES` 表解析,独立于数据版本。
//! - 偏移一律以秒存(i32),保亚分钟精度(LMT、Lord Howe 半小时 DST 等);
//! `transitions` 按纪元秒严格升序,最后一次 transition 之后的偏移预存于 `last_offset`。
//! - 闰秒不建模:闰秒表只用于推进解析指针,Temporal 时间线为连续纳秒轴。
//! - 刷新程序:从宿主 /usr/share/zoneinfo 以 `cp -rL` 复制 11 个区域目录加顶层区文件
//! (排除机器相关的 localtime 与源文件 tzdata.zi / leap-seconds.list),并更新本
//! 注释的版本行。
use std::sync::LazyLock;
use super::common::fixed_offset_seconds;
/// 单个时区的解析结果:transition 表加偏移查找所需的预存值。
///
/// `transitions` 为 `(纪元秒, 该时刻起生效的偏移秒)`,按纪元秒严格升序;
/// 早于首个 transition 的偏移取 `lmt_offset`(首个 time type,即 LMT),
/// 晚于末个 transition 的偏移取 `last_offset`(POSIX TZ 串语义,与末次 transition 一致)。
pub(crate) struct TzifZone {
lmt_offset: i32,
transitions: Vec<(i64, i32)>,
last_offset: i32,
}
/// TZif 解析失败原因。
#[derive(Debug)]
pub(crate) enum TzifError {
/// 文件在声明的块边界前截断。
Truncated,
/// 魔数不是 `TZif`。
BadMagic,
/// 版本字节不是 0 / 2 / 3 / 4。
BadVersion,
}
/// TZif 头的 6 个计数字段(isutcnt / isstdcnt / leapcnt / timecnt / typecnt / charcnt)。
#[derive(Clone, Copy)]
struct Counts {
isutcnt: u32,
isstdcnt: u32,
leapcnt: u32,
timecnt: u32,
typecnt: u32,
charcnt: u32,
}
/// 单个数据块的解析结果:transition 时间、transition 类型索引、本地时间类型偏移。
struct ParsedBlock {
trans: Vec<i64>,
ttypes: Vec<u8>,
types: Vec<i32>,
}
/// 读 44 字节头的 6 个计数字段。
///
/// # 边界与前提
/// - 调用方须保证 `off + 44` 不越界(本函数自行校验)。
///
/// # 注意事项
/// - 计数字段从偏移 20 起(magic 4 + version 1 + 保留 15)。
fn read_counts(bytes: &[u8], off: usize) -> Result<Counts, TzifError> {
if bytes.len() < off + 44 {
return Err(TzifError::Truncated);
}
let d = |i: usize| u32::from_be_bytes(bytes[off + 20 + i * 4..off + 24 + i * 4].try_into().unwrap());
Ok(Counts {
isutcnt: d(0),
isstdcnt: d(1),
leapcnt: d(2),
timecnt: d(3),
typecnt: d(4),
charcnt: d(5),
})
}
/// 数据块总字节数(transition 时间 + 类型 + 本地时间类型记录 + 区名 + 闰秒 + 指示位)。
fn block_size(c: &Counts, time_size: usize) -> usize {
c.timecnt as usize * time_size
+ c.timecnt as usize
+ c.typecnt as usize * 6
+ c.charcnt as usize
+ c.leapcnt as usize * (time_size + 4)
+ c.isstdcnt as usize
+ c.isutcnt as usize
}
/// 读一个数据块:transition 时间、transition 类型索引、本地时间类型偏移。
///
/// # 边界与前提
/// - 调用方须保证块完整(本函数按 `block_size` 校验总长)。
/// - `time_size` 为 4(v1)或 8(v2+)。
///
/// # 注意事项
/// - transition 类型索引越界(超出 typecnt)视为截断,防越界读。
fn read_block(bytes: &[u8], off: usize, c: &Counts, time_size: usize) -> Result<ParsedBlock, TzifError> {
let need = off + block_size(c, time_size);
if bytes.len() < need {
return Err(TzifError::Truncated);
}
let mut p = off;
// transition 时间(有符号,v1 为 32 位、v2+ 为 64 位)。
let mut trans = Vec::with_capacity(c.timecnt as usize);
for _ in 0..c.timecnt {
let v = if time_size == 8 {
i64::from_be_bytes(bytes[p..p + 8].try_into().unwrap())
} else {
i32::from_be_bytes(bytes[p..p + 4].try_into().unwrap()) as i64
};
trans.push(v);
p += time_size;
}
// transition 类型索引(每 transition 一字节)。
let ttypes = bytes[p..p + c.timecnt as usize].to_vec();
p += c.timecnt as usize;
// 本地时间类型记录(6 字节:utoff 4 + isdst 1 + idx 1),只取 utoff。
let mut types = Vec::with_capacity(c.typecnt as usize);
for _ in 0..c.typecnt {
let utoff = i32::from_be_bytes(bytes[p..p + 4].try_into().unwrap());
types.push(utoff);
p += 6;
}
for &idx in &ttypes {
if idx >= c.typecnt as u8 {
return Err(TzifError::Truncated);
}
}
Ok(ParsedBlock { trans, ttypes, types })
}
/// 解析 TZif 二进制为 `TzifZone`。
///
/// # 步骤
/// 1. 校验魔数与版本字节。
/// 2. 读 v1 头;版本 ≥ 2 时跳过 v1 块、读 v2+ 头,一律用 64 位块(1970 年前
/// transition 为负值自然容纳);版本 1 用 32 位块。
/// 3. 由 transition 类型索引与本地时间类型记录组装 `(纪元秒, 偏移秒)` 表;
/// LMT 取首个 time type,末偏移取末次 transition 的偏移。
///
/// # 边界与前提
/// - 闰秒表按 `leapcnt * (time_size + 4)` 推进指针,不建模。
/// - POSIX TZ 串(footer)读出后丢弃:末偏移已由末次 transition 给出。
///
/// # 注意事项
/// - 纯解析,无副作用;调用方负责把结果挂到静态表。
pub(crate) fn parse_tzif(bytes: &[u8]) -> Result<TzifZone, TzifError> {
if bytes.len() < 44 {
return Err(TzifError::Truncated);
}
if &bytes[0..4] != b"TZif" {
return Err(TzifError::BadMagic);
}
let version = bytes[4];
if !matches!(version, 0 | b'2' | b'3' | b'4') {
return Err(TzifError::BadVersion);
}
// v1 头计数(版本 ≥ 2 时 v1 块是占位,只用于算跳过长度)。
let c1 = read_counts(bytes, 0)?;
let block = if version >= b'2' {
let v1_size = block_size(&c1, 4);
let off = 44 + v1_size;
if bytes.len() < off + 44 {
return Err(TzifError::Truncated);
}
if &bytes[off..off + 4] != b"TZif" {
return Err(TzifError::BadMagic);
}
let c2 = read_counts(bytes, off)?;
read_block(bytes, off + 44, &c2, 8)?
} else {
read_block(bytes, 44, &c1, 4)?
};
// LMT 是首个 time type 的偏移;末偏移取末次 transition 的偏移(POSIX TZ 串语义)。
let lmt_offset = block.types.first().copied().unwrap_or(0);
let transitions: Vec<(i64, i32)> = block
.trans
.iter()
.enumerate()
.map(|(i, &t)| (t, block.types[block.ttypes[i] as usize]))
.collect();
let last_offset = transitions.last().map(|&(_, o)| o).unwrap_or(lmt_offset);
Ok(TzifZone {
lmt_offset,
transitions,
last_offset,
})
}
const ZONE_FILES: &[(&str, &[u8])] = &[
("Africa/Abidjan", include_bytes!("../../../../third_party/tzdb/Africa/Abidjan")),
("Africa/Accra", include_bytes!("../../../../third_party/tzdb/Africa/Accra")),
("Africa/Addis_Ababa", include_bytes!("../../../../third_party/tzdb/Africa/Addis_Ababa")),
("Africa/Algiers", include_bytes!("../../../../third_party/tzdb/Africa/Algiers")),
("Africa/Asmara", include_bytes!("../../../../third_party/tzdb/Africa/Asmara")),
("Africa/Bamako", include_bytes!("../../../../third_party/tzdb/Africa/Bamako")),
("Africa/Bangui", include_bytes!("../../../../third_party/tzdb/Africa/Bangui")),
("Africa/Banjul", include_bytes!("../../../../third_party/tzdb/Africa/Banjul")),
("Africa/Bissau", include_bytes!("../../../../third_party/tzdb/Africa/Bissau")),
("Africa/Blantyre", include_bytes!("../../../../third_party/tzdb/Africa/Blantyre")),
("Africa/Brazzaville", include_bytes!("../../../../third_party/tzdb/Africa/Brazzaville")),
("Africa/Bujumbura", include_bytes!("../../../../third_party/tzdb/Africa/Bujumbura")),
("Africa/Cairo", include_bytes!("../../../../third_party/tzdb/Africa/Cairo")),
("Africa/Casablanca", include_bytes!("../../../../third_party/tzdb/Africa/Casablanca")),
("Africa/Ceuta", include_bytes!("../../../../third_party/tzdb/Africa/Ceuta")),
("Africa/Conakry", include_bytes!("../../../../third_party/tzdb/Africa/Conakry")),
("Africa/Dakar", include_bytes!("../../../../third_party/tzdb/Africa/Dakar")),
(
"Africa/Dar_es_Salaam",
include_bytes!("../../../../third_party/tzdb/Africa/Dar_es_Salaam"),
),
("Africa/Djibouti", include_bytes!("../../../../third_party/tzdb/Africa/Djibouti")),
("Africa/Douala", include_bytes!("../../../../third_party/tzdb/Africa/Douala")),
("Africa/El_Aaiun", include_bytes!("../../../../third_party/tzdb/Africa/El_Aaiun")),
("Africa/Freetown", include_bytes!("../../../../third_party/tzdb/Africa/Freetown")),
("Africa/Gaborone", include_bytes!("../../../../third_party/tzdb/Africa/Gaborone")),
("Africa/Harare", include_bytes!("../../../../third_party/tzdb/Africa/Harare")),
("Africa/Johannesburg", include_bytes!("../../../../third_party/tzdb/Africa/Johannesburg")),
("Africa/Juba", include_bytes!("../../../../third_party/tzdb/Africa/Juba")),
("Africa/Kampala", include_bytes!("../../../../third_party/tzdb/Africa/Kampala")),
("Africa/Khartoum", include_bytes!("../../../../third_party/tzdb/Africa/Khartoum")),
("Africa/Kigali", include_bytes!("../../../../third_party/tzdb/Africa/Kigali")),
("Africa/Kinshasa", include_bytes!("../../../../third_party/tzdb/Africa/Kinshasa")),
("Africa/Lagos", include_bytes!("../../../../third_party/tzdb/Africa/Lagos")),
("Africa/Libreville", include_bytes!("../../../../third_party/tzdb/Africa/Libreville")),
("Africa/Lome", include_bytes!("../../../../third_party/tzdb/Africa/Lome")),
("Africa/Luanda", include_bytes!("../../../../third_party/tzdb/Africa/Luanda")),
("Africa/Lubumbashi", include_bytes!("../../../../third_party/tzdb/Africa/Lubumbashi")),
("Africa/Lusaka", include_bytes!("../../../../third_party/tzdb/Africa/Lusaka")),
("Africa/Malabo", include_bytes!("../../../../third_party/tzdb/Africa/Malabo")),
("Africa/Maputo", include_bytes!("../../../../third_party/tzdb/Africa/Maputo")),
("Africa/Maseru", include_bytes!("../../../../third_party/tzdb/Africa/Maseru")),
("Africa/Mbabane", include_bytes!("../../../../third_party/tzdb/Africa/Mbabane")),
("Africa/Mogadishu", include_bytes!("../../../../third_party/tzdb/Africa/Mogadishu")),
("Africa/Monrovia", include_bytes!("../../../../third_party/tzdb/Africa/Monrovia")),
("Africa/Nairobi", include_bytes!("../../../../third_party/tzdb/Africa/Nairobi")),
("Africa/Ndjamena", include_bytes!("../../../../third_party/tzdb/Africa/Ndjamena")),
("Africa/Niamey", include_bytes!("../../../../third_party/tzdb/Africa/Niamey")),
("Africa/Nouakchott", include_bytes!("../../../../third_party/tzdb/Africa/Nouakchott")),
("Africa/Ouagadougou", include_bytes!("../../../../third_party/tzdb/Africa/Ouagadougou")),
("Africa/Porto-Novo", include_bytes!("../../../../third_party/tzdb/Africa/Porto-Novo")),
("Africa/Sao_Tome", include_bytes!("../../../../third_party/tzdb/Africa/Sao_Tome")),
("Africa/Timbuktu", include_bytes!("../../../../third_party/tzdb/Africa/Timbuktu")),
("Africa/Tripoli", include_bytes!("../../../../third_party/tzdb/Africa/Tripoli")),
("Africa/Tunis", include_bytes!("../../../../third_party/tzdb/Africa/Tunis")),
("Africa/Windhoek", include_bytes!("../../../../third_party/tzdb/Africa/Windhoek")),
("America/Adak", include_bytes!("../../../../third_party/tzdb/America/Adak")),
("America/Anchorage", include_bytes!("../../../../third_party/tzdb/America/Anchorage")),
("America/Anguilla", include_bytes!("../../../../third_party/tzdb/America/Anguilla")),
("America/Antigua", include_bytes!("../../../../third_party/tzdb/America/Antigua")),
("America/Araguaina", include_bytes!("../../../../third_party/tzdb/America/Araguaina")),
(
"America/Argentina/Buenos_Aires",
include_bytes!("../../../../third_party/tzdb/America/Argentina/Buenos_Aires"),
),
(
"America/Argentina/Catamarca",
include_bytes!("../../../../third_party/tzdb/America/Argentina/Catamarca"),
),
(
"America/Argentina/Cordoba",
include_bytes!("../../../../third_party/tzdb/America/Argentina/Cordoba"),
),
(
"America/Argentina/Jujuy",
include_bytes!("../../../../third_party/tzdb/America/Argentina/Jujuy"),
),
(
"America/Argentina/La_Rioja",
include_bytes!("../../../../third_party/tzdb/America/Argentina/La_Rioja"),
),
(
"America/Argentina/Mendoza",
include_bytes!("../../../../third_party/tzdb/America/Argentina/Mendoza"),
),
(
"America/Argentina/Rio_Gallegos",
include_bytes!("../../../../third_party/tzdb/America/Argentina/Rio_Gallegos"),
),
(
"America/Argentina/Salta",
include_bytes!("../../../../third_party/tzdb/America/Argentina/Salta"),
),
(
"America/Argentina/San_Juan",
include_bytes!("../../../../third_party/tzdb/America/Argentina/San_Juan"),
),
(
"America/Argentina/San_Luis",
include_bytes!("../../../../third_party/tzdb/America/Argentina/San_Luis"),
),
(
"America/Argentina/Tucuman",
include_bytes!("../../../../third_party/tzdb/America/Argentina/Tucuman"),
),
(
"America/Argentina/Ushuaia",
include_bytes!("../../../../third_party/tzdb/America/Argentina/Ushuaia"),
),
("America/Aruba", include_bytes!("../../../../third_party/tzdb/America/Aruba")),
("America/Asuncion", include_bytes!("../../../../third_party/tzdb/America/Asuncion")),
("America/Atikokan", include_bytes!("../../../../third_party/tzdb/America/Atikokan")),
("America/Atka", include_bytes!("../../../../third_party/tzdb/America/Atka")),
("America/Bahia", include_bytes!("../../../../third_party/tzdb/America/Bahia")),
(
"America/Bahia_Banderas",
include_bytes!("../../../../third_party/tzdb/America/Bahia_Banderas"),
),
("America/Barbados", include_bytes!("../../../../third_party/tzdb/America/Barbados")),
("America/Belem", include_bytes!("../../../../third_party/tzdb/America/Belem")),
("America/Belize", include_bytes!("../../../../third_party/tzdb/America/Belize")),
(
"America/Blanc-Sablon",
include_bytes!("../../../../third_party/tzdb/America/Blanc-Sablon"),
),
("America/Boa_Vista", include_bytes!("../../../../third_party/tzdb/America/Boa_Vista")),
("America/Bogota", include_bytes!("../../../../third_party/tzdb/America/Bogota")),
("America/Boise", include_bytes!("../../../../third_party/tzdb/America/Boise")),
(
"America/Cambridge_Bay",
include_bytes!("../../../../third_party/tzdb/America/Cambridge_Bay"),
),
(
"America/Campo_Grande",
include_bytes!("../../../../third_party/tzdb/America/Campo_Grande"),
),
("America/Cancun", include_bytes!("../../../../third_party/tzdb/America/Cancun")),
("America/Caracas", include_bytes!("../../../../third_party/tzdb/America/Caracas")),
("America/Cayenne", include_bytes!("../../../../third_party/tzdb/America/Cayenne")),
("America/Cayman", include_bytes!("../../../../third_party/tzdb/America/Cayman")),
("America/Chicago", include_bytes!("../../../../third_party/tzdb/America/Chicago")),
("America/Chihuahua", include_bytes!("../../../../third_party/tzdb/America/Chihuahua")),
(
"America/Ciudad_Juarez",
include_bytes!("../../../../third_party/tzdb/America/Ciudad_Juarez"),
),
(
"America/Coral_Harbour",
include_bytes!("../../../../third_party/tzdb/America/Coral_Harbour"),
),
("America/Costa_Rica", include_bytes!("../../../../third_party/tzdb/America/Costa_Rica")),
("America/Coyhaique", include_bytes!("../../../../third_party/tzdb/America/Coyhaique")),
("America/Creston", include_bytes!("../../../../third_party/tzdb/America/Creston")),
("America/Cuiaba", include_bytes!("../../../../third_party/tzdb/America/Cuiaba")),
("America/Curacao", include_bytes!("../../../../third_party/tzdb/America/Curacao")),
(
"America/Danmarkshavn",
include_bytes!("../../../../third_party/tzdb/America/Danmarkshavn"),
),
("America/Dawson", include_bytes!("../../../../third_party/tzdb/America/Dawson")),
(
"America/Dawson_Creek",
include_bytes!("../../../../third_party/tzdb/America/Dawson_Creek"),
),
("America/Denver", include_bytes!("../../../../third_party/tzdb/America/Denver")),
("America/Detroit", include_bytes!("../../../../third_party/tzdb/America/Detroit")),
("America/Dominica", include_bytes!("../../../../third_party/tzdb/America/Dominica")),
("America/Edmonton", include_bytes!("../../../../third_party/tzdb/America/Edmonton")),
("America/Eirunepe", include_bytes!("../../../../third_party/tzdb/America/Eirunepe")),
("America/El_Salvador", include_bytes!("../../../../third_party/tzdb/America/El_Salvador")),
("America/Ensenada", include_bytes!("../../../../third_party/tzdb/America/Ensenada")),
("America/Fort_Nelson", include_bytes!("../../../../third_party/tzdb/America/Fort_Nelson")),
("America/Fortaleza", include_bytes!("../../../../third_party/tzdb/America/Fortaleza")),
("America/Glace_Bay", include_bytes!("../../../../third_party/tzdb/America/Glace_Bay")),
("America/Goose_Bay", include_bytes!("../../../../third_party/tzdb/America/Goose_Bay")),
("America/Grand_Turk", include_bytes!("../../../../third_party/tzdb/America/Grand_Turk")),
("America/Grenada", include_bytes!("../../../../third_party/tzdb/America/Grenada")),
("America/Guadeloupe", include_bytes!("../../../../third_party/tzdb/America/Guadeloupe")),
("America/Guatemala", include_bytes!("../../../../third_party/tzdb/America/Guatemala")),
("America/Guayaquil", include_bytes!("../../../../third_party/tzdb/America/Guayaquil")),
("America/Guyana", include_bytes!("../../../../third_party/tzdb/America/Guyana")),
("America/Halifax", include_bytes!("../../../../third_party/tzdb/America/Halifax")),
("America/Havana", include_bytes!("../../../../third_party/tzdb/America/Havana")),
("America/Hermosillo", include_bytes!("../../../../third_party/tzdb/America/Hermosillo")),
(
"America/Indiana/Indianapolis",
include_bytes!("../../../../third_party/tzdb/America/Indiana/Indianapolis"),
),
(
"America/Indiana/Knox",
include_bytes!("../../../../third_party/tzdb/America/Indiana/Knox"),
),
(
"America/Indiana/Marengo",
include_bytes!("../../../../third_party/tzdb/America/Indiana/Marengo"),
),
(
"America/Indiana/Petersburg",
include_bytes!("../../../../third_party/tzdb/America/Indiana/Petersburg"),
),
(
"America/Indiana/Tell_City",
include_bytes!("../../../../third_party/tzdb/America/Indiana/Tell_City"),
),
(
"America/Indiana/Vevay",
include_bytes!("../../../../third_party/tzdb/America/Indiana/Vevay"),
),
(
"America/Indiana/Vincennes",
include_bytes!("../../../../third_party/tzdb/America/Indiana/Vincennes"),
),
(
"America/Indiana/Winamac",
include_bytes!("../../../../third_party/tzdb/America/Indiana/Winamac"),
),
("America/Inuvik", include_bytes!("../../../../third_party/tzdb/America/Inuvik")),
("America/Iqaluit", include_bytes!("../../../../third_party/tzdb/America/Iqaluit")),
("America/Jamaica", include_bytes!("../../../../third_party/tzdb/America/Jamaica")),
("America/Juneau", include_bytes!("../../../../third_party/tzdb/America/Juneau")),
(
"America/Kentucky/Louisville",
include_bytes!("../../../../third_party/tzdb/America/Kentucky/Louisville"),
),
(
"America/Kentucky/Monticello",
include_bytes!("../../../../third_party/tzdb/America/Kentucky/Monticello"),
),
("America/Kralendijk", include_bytes!("../../../../third_party/tzdb/America/Kralendijk")),
("America/La_Paz", include_bytes!("../../../../third_party/tzdb/America/La_Paz")),
("America/Lima", include_bytes!("../../../../third_party/tzdb/America/Lima")),
("America/Los_Angeles", include_bytes!("../../../../third_party/tzdb/America/Los_Angeles")),
(
"America/Lower_Princes",
include_bytes!("../../../../third_party/tzdb/America/Lower_Princes"),
),
("America/Maceio", include_bytes!("../../../../third_party/tzdb/America/Maceio")),
("America/Managua", include_bytes!("../../../../third_party/tzdb/America/Managua")),
("America/Manaus", include_bytes!("../../../../third_party/tzdb/America/Manaus")),
("America/Marigot", include_bytes!("../../../../third_party/tzdb/America/Marigot")),
("America/Martinique", include_bytes!("../../../../third_party/tzdb/America/Martinique")),
("America/Matamoros", include_bytes!("../../../../third_party/tzdb/America/Matamoros")),
("America/Mazatlan", include_bytes!("../../../../third_party/tzdb/America/Mazatlan")),
("America/Menominee", include_bytes!("../../../../third_party/tzdb/America/Menominee")),
("America/Merida", include_bytes!("../../../../third_party/tzdb/America/Merida")),
("America/Metlakatla", include_bytes!("../../../../third_party/tzdb/America/Metlakatla")),
("America/Mexico_City", include_bytes!("../../../../third_party/tzdb/America/Mexico_City")),
("America/Miquelon", include_bytes!("../../../../third_party/tzdb/America/Miquelon")),
("America/Moncton", include_bytes!("../../../../third_party/tzdb/America/Moncton")),
("America/Monterrey", include_bytes!("../../../../third_party/tzdb/America/Monterrey")),
("America/Montevideo", include_bytes!("../../../../third_party/tzdb/America/Montevideo")),
("America/Montreal", include_bytes!("../../../../third_party/tzdb/America/Montreal")),
("America/Montserrat", include_bytes!("../../../../third_party/tzdb/America/Montserrat")),
("America/Nassau", include_bytes!("../../../../third_party/tzdb/America/Nassau")),
("America/New_York", include_bytes!("../../../../third_party/tzdb/America/New_York")),
("America/Nipigon", include_bytes!("../../../../third_party/tzdb/America/Nipigon")),
("America/Nome", include_bytes!("../../../../third_party/tzdb/America/Nome")),
("America/Noronha", include_bytes!("../../../../third_party/tzdb/America/Noronha")),
(
"America/North_Dakota/Beulah",
include_bytes!("../../../../third_party/tzdb/America/North_Dakota/Beulah"),
),
(
"America/North_Dakota/Center",
include_bytes!("../../../../third_party/tzdb/America/North_Dakota/Center"),
),
(
"America/North_Dakota/New_Salem",
include_bytes!("../../../../third_party/tzdb/America/North_Dakota/New_Salem"),
),
("America/Nuuk", include_bytes!("../../../../third_party/tzdb/America/Nuuk")),
("America/Ojinaga", include_bytes!("../../../../third_party/tzdb/America/Ojinaga")),
("America/Panama", include_bytes!("../../../../third_party/tzdb/America/Panama")),
("America/Pangnirtung", include_bytes!("../../../../third_party/tzdb/America/Pangnirtung")),
("America/Paramaribo", include_bytes!("../../../../third_party/tzdb/America/Paramaribo")),
("America/Phoenix", include_bytes!("../../../../third_party/tzdb/America/Phoenix")),
(
"America/Port-au-Prince",
include_bytes!("../../../../third_party/tzdb/America/Port-au-Prince"),
),
(
"America/Port_of_Spain",
include_bytes!("../../../../third_party/tzdb/America/Port_of_Spain"),
),
("America/Porto_Acre", include_bytes!("../../../../third_party/tzdb/America/Porto_Acre")),
("America/Porto_Velho", include_bytes!("../../../../third_party/tzdb/America/Porto_Velho")),
("America/Puerto_Rico", include_bytes!("../../../../third_party/tzdb/America/Puerto_Rico")),
(
"America/Punta_Arenas",
include_bytes!("../../../../third_party/tzdb/America/Punta_Arenas"),
),
("America/Rainy_River", include_bytes!("../../../../third_party/tzdb/America/Rainy_River")),
(
"America/Rankin_Inlet",
include_bytes!("../../../../third_party/tzdb/America/Rankin_Inlet"),
),
("America/Recife", include_bytes!("../../../../third_party/tzdb/America/Recife")),
("America/Regina", include_bytes!("../../../../third_party/tzdb/America/Regina")),
("America/Resolute", include_bytes!("../../../../third_party/tzdb/America/Resolute")),
("America/Rio_Branco", include_bytes!("../../../../third_party/tzdb/America/Rio_Branco")),
(
"America/Santa_Isabel",
include_bytes!("../../../../third_party/tzdb/America/Santa_Isabel"),
),
("America/Santarem", include_bytes!("../../../../third_party/tzdb/America/Santarem")),
("America/Santiago", include_bytes!("../../../../third_party/tzdb/America/Santiago")),
(
"America/Santo_Domingo",
include_bytes!("../../../../third_party/tzdb/America/Santo_Domingo"),
),
("America/Sao_Paulo", include_bytes!("../../../../third_party/tzdb/America/Sao_Paulo")),
(
"America/Scoresbysund",
include_bytes!("../../../../third_party/tzdb/America/Scoresbysund"),
),
("America/Shiprock", include_bytes!("../../../../third_party/tzdb/America/Shiprock")),
("America/Sitka", include_bytes!("../../../../third_party/tzdb/America/Sitka")),
(
"America/St_Barthelemy",
include_bytes!("../../../../third_party/tzdb/America/St_Barthelemy"),
),
("America/St_Johns", include_bytes!("../../../../third_party/tzdb/America/St_Johns")),
("America/St_Kitts", include_bytes!("../../../../third_party/tzdb/America/St_Kitts")),
("America/St_Lucia", include_bytes!("../../../../third_party/tzdb/America/St_Lucia")),
("America/St_Thomas", include_bytes!("../../../../third_party/tzdb/America/St_Thomas")),
("America/St_Vincent", include_bytes!("../../../../third_party/tzdb/America/St_Vincent")),
(
"America/Swift_Current",
include_bytes!("../../../../third_party/tzdb/America/Swift_Current"),
),
("America/Tegucigalpa", include_bytes!("../../../../third_party/tzdb/America/Tegucigalpa")),
("America/Thule", include_bytes!("../../../../third_party/tzdb/America/Thule")),
("America/Thunder_Bay", include_bytes!("../../../../third_party/tzdb/America/Thunder_Bay")),
("America/Tijuana", include_bytes!("../../../../third_party/tzdb/America/Tijuana")),
("America/Toronto", include_bytes!("../../../../third_party/tzdb/America/Toronto")),
("America/Tortola", include_bytes!("../../../../third_party/tzdb/America/Tortola")),
("America/Vancouver", include_bytes!("../../../../third_party/tzdb/America/Vancouver")),
("America/Virgin", include_bytes!("../../../../third_party/tzdb/America/Virgin")),
("America/Whitehorse", include_bytes!("../../../../third_party/tzdb/America/Whitehorse")),
("America/Winnipeg", include_bytes!("../../../../third_party/tzdb/America/Winnipeg")),
("America/Yakutat", include_bytes!("../../../../third_party/tzdb/America/Yakutat")),
("America/Yellowknife", include_bytes!("../../../../third_party/tzdb/America/Yellowknife")),
("Antarctica/Casey", include_bytes!("../../../../third_party/tzdb/Antarctica/Casey")),
("Antarctica/Davis", include_bytes!("../../../../third_party/tzdb/Antarctica/Davis")),
(
"Antarctica/DumontDUrville",
include_bytes!("../../../../third_party/tzdb/Antarctica/DumontDUrville"),
),
(
"Antarctica/Macquarie",
include_bytes!("../../../../third_party/tzdb/Antarctica/Macquarie"),
),
("Antarctica/Mawson", include_bytes!("../../../../third_party/tzdb/Antarctica/Mawson")),
("Antarctica/McMurdo", include_bytes!("../../../../third_party/tzdb/Antarctica/McMurdo")),
("Antarctica/Palmer", include_bytes!("../../../../third_party/tzdb/Antarctica/Palmer")),
("Antarctica/Rothera", include_bytes!("../../../../third_party/tzdb/Antarctica/Rothera")),
("Antarctica/Syowa", include_bytes!("../../../../third_party/tzdb/Antarctica/Syowa")),
("Antarctica/Troll", include_bytes!("../../../../third_party/tzdb/Antarctica/Troll")),
("Antarctica/Vostok", include_bytes!("../../../../third_party/tzdb/Antarctica/Vostok")),
("Arctic/Longyearbyen", include_bytes!("../../../../third_party/tzdb/Arctic/Longyearbyen")),
("Asia/Aden", include_bytes!("../../../../third_party/tzdb/Asia/Aden")),
("Asia/Almaty", include_bytes!("../../../../third_party/tzdb/Asia/Almaty")),
("Asia/Amman", include_bytes!("../../../../third_party/tzdb/Asia/Amman")),
("Asia/Anadyr", include_bytes!("../../../../third_party/tzdb/Asia/Anadyr")),
("Asia/Aqtau", include_bytes!("../../../../third_party/tzdb/Asia/Aqtau")),
("Asia/Aqtobe", include_bytes!("../../../../third_party/tzdb/Asia/Aqtobe")),
("Asia/Ashgabat", include_bytes!("../../../../third_party/tzdb/Asia/Ashgabat")),
("Asia/Atyrau", include_bytes!("../../../../third_party/tzdb/Asia/Atyrau")),
("Asia/Baghdad", include_bytes!("../../../../third_party/tzdb/Asia/Baghdad")),
("Asia/Bahrain", include_bytes!("../../../../third_party/tzdb/Asia/Bahrain")),
("Asia/Baku", include_bytes!("../../../../third_party/tzdb/Asia/Baku")),
("Asia/Bangkok", include_bytes!("../../../../third_party/tzdb/Asia/Bangkok")),
("Asia/Barnaul", include_bytes!("../../../../third_party/tzdb/Asia/Barnaul")),
("Asia/Beirut", include_bytes!("../../../../third_party/tzdb/Asia/Beirut")),
("Asia/Bishkek", include_bytes!("../../../../third_party/tzdb/Asia/Bishkek")),
("Asia/Brunei", include_bytes!("../../../../third_party/tzdb/Asia/Brunei")),
("Asia/Chita", include_bytes!("../../../../third_party/tzdb/Asia/Chita")),
("Asia/Chongqing", include_bytes!("../../../../third_party/tzdb/Asia/Chongqing")),
("Asia/Colombo", include_bytes!("../../../../third_party/tzdb/Asia/Colombo")),
("Asia/Damascus", include_bytes!("../../../../third_party/tzdb/Asia/Damascus")),
("Asia/Dhaka", include_bytes!("../../../../third_party/tzdb/Asia/Dhaka")),
("Asia/Dili", include_bytes!("../../../../third_party/tzdb/Asia/Dili")),
("Asia/Dubai", include_bytes!("../../../../third_party/tzdb/Asia/Dubai")),
("Asia/Dushanbe", include_bytes!("../../../../third_party/tzdb/Asia/Dushanbe")),
("Asia/Famagusta", include_bytes!("../../../../third_party/tzdb/Asia/Famagusta")),
("Asia/Gaza", include_bytes!("../../../../third_party/tzdb/Asia/Gaza")),
("Asia/Harbin", include_bytes!("../../../../third_party/tzdb/Asia/Harbin")),
("Asia/Hebron", include_bytes!("../../../../third_party/tzdb/Asia/Hebron")),
("Asia/Ho_Chi_Minh", include_bytes!("../../../../third_party/tzdb/Asia/Ho_Chi_Minh")),
("Asia/Hong_Kong", include_bytes!("../../../../third_party/tzdb/Asia/Hong_Kong")),
("Asia/Hovd", include_bytes!("../../../../third_party/tzdb/Asia/Hovd")),
("Asia/Irkutsk", include_bytes!("../../../../third_party/tzdb/Asia/Irkutsk")),
("Asia/Istanbul", include_bytes!("../../../../third_party/tzdb/Asia/Istanbul")),
("Asia/Jakarta", include_bytes!("../../../../third_party/tzdb/Asia/Jakarta")),
("Asia/Jayapura", include_bytes!("../../../../third_party/tzdb/Asia/Jayapura")),
("Asia/Jerusalem", include_bytes!("../../../../third_party/tzdb/Asia/Jerusalem")),
("Asia/Kabul", include_bytes!("../../../../third_party/tzdb/Asia/Kabul")),
("Asia/Kamchatka", include_bytes!("../../../../third_party/tzdb/Asia/Kamchatka")),
("Asia/Karachi", include_bytes!("../../../../third_party/tzdb/Asia/Karachi")),
("Asia/Kashgar", include_bytes!("../../../../third_party/tzdb/Asia/Kashgar")),
("Asia/Kathmandu", include_bytes!("../../../../third_party/tzdb/Asia/Kathmandu")),
("Asia/Khandyga", include_bytes!("../../../../third_party/tzdb/Asia/Khandyga")),
("Asia/Kolkata", include_bytes!("../../../../third_party/tzdb/Asia/Kolkata")),
("Asia/Krasnoyarsk", include_bytes!("../../../../third_party/tzdb/Asia/Krasnoyarsk")),
("Asia/Kuala_Lumpur", include_bytes!("../../../../third_party/tzdb/Asia/Kuala_Lumpur")),
("Asia/Kuching", include_bytes!("../../../../third_party/tzdb/Asia/Kuching")),
("Asia/Kuwait", include_bytes!("../../../../third_party/tzdb/Asia/Kuwait")),
("Asia/Macau", include_bytes!("../../../../third_party/tzdb/Asia/Macau")),
("Asia/Magadan", include_bytes!("../../../../third_party/tzdb/Asia/Magadan")),
("Asia/Makassar", include_bytes!("../../../../third_party/tzdb/Asia/Makassar")),
("Asia/Manila", include_bytes!("../../../../third_party/tzdb/Asia/Manila")),
("Asia/Muscat", include_bytes!("../../../../third_party/tzdb/Asia/Muscat")),
("Asia/Nicosia", include_bytes!("../../../../third_party/tzdb/Asia/Nicosia")),
("Asia/Novokuznetsk", include_bytes!("../../../../third_party/tzdb/Asia/Novokuznetsk")),
("Asia/Novosibirsk", include_bytes!("../../../../third_party/tzdb/Asia/Novosibirsk")),
("Asia/Omsk", include_bytes!("../../../../third_party/tzdb/Asia/Omsk")),
("Asia/Oral", include_bytes!("../../../../third_party/tzdb/Asia/Oral")),
("Asia/Phnom_Penh", include_bytes!("../../../../third_party/tzdb/Asia/Phnom_Penh")),
("Asia/Pontianak", include_bytes!("../../../../third_party/tzdb/Asia/Pontianak")),
("Asia/Pyongyang", include_bytes!("../../../../third_party/tzdb/Asia/Pyongyang")),
("Asia/Qatar", include_bytes!("../../../../third_party/tzdb/Asia/Qatar")),
("Asia/Qostanay", include_bytes!("../../../../third_party/tzdb/Asia/Qostanay")),
("Asia/Qyzylorda", include_bytes!("../../../../third_party/tzdb/Asia/Qyzylorda")),
("Asia/Riyadh", include_bytes!("../../../../third_party/tzdb/Asia/Riyadh")),
("Asia/Sakhalin", include_bytes!("../../../../third_party/tzdb/Asia/Sakhalin")),
("Asia/Samarkand", include_bytes!("../../../../third_party/tzdb/Asia/Samarkand")),
("Asia/Seoul", include_bytes!("../../../../third_party/tzdb/Asia/Seoul")),
("Asia/Shanghai", include_bytes!("../../../../third_party/tzdb/Asia/Shanghai")),
("Asia/Singapore", include_bytes!("../../../../third_party/tzdb/Asia/Singapore")),
("Asia/Srednekolymsk", include_bytes!("../../../../third_party/tzdb/Asia/Srednekolymsk")),
("Asia/Taipei", include_bytes!("../../../../third_party/tzdb/Asia/Taipei")),
("Asia/Tashkent", include_bytes!("../../../../third_party/tzdb/Asia/Tashkent")),
("Asia/Tbilisi", include_bytes!("../../../../third_party/tzdb/Asia/Tbilisi")),
("Asia/Tehran", include_bytes!("../../../../third_party/tzdb/Asia/Tehran")),
("Asia/Tel_Aviv", include_bytes!("../../../../third_party/tzdb/Asia/Tel_Aviv")),
("Asia/Thimphu", include_bytes!("../../../../third_party/tzdb/Asia/Thimphu")),
("Asia/Tokyo", include_bytes!("../../../../third_party/tzdb/Asia/Tokyo")),
("Asia/Tomsk", include_bytes!("../../../../third_party/tzdb/Asia/Tomsk")),
("Asia/Ulaanbaatar", include_bytes!("../../../../third_party/tzdb/Asia/Ulaanbaatar")),
("Asia/Urumqi", include_bytes!("../../../../third_party/tzdb/Asia/Urumqi")),
("Asia/Ust-Nera", include_bytes!("../../../../third_party/tzdb/Asia/Ust-Nera")),
("Asia/Vientiane", include_bytes!("../../../../third_party/tzdb/Asia/Vientiane")),
("Asia/Vladivostok", include_bytes!("../../../../third_party/tzdb/Asia/Vladivostok")),
("Asia/Yakutsk", include_bytes!("../../../../third_party/tzdb/Asia/Yakutsk")),
("Asia/Yangon", include_bytes!("../../../../third_party/tzdb/Asia/Yangon")),
("Asia/Yekaterinburg", include_bytes!("../../../../third_party/tzdb/Asia/Yekaterinburg")),
("Asia/Yerevan", include_bytes!("../../../../third_party/tzdb/Asia/Yerevan")),
("Atlantic/Azores", include_bytes!("../../../../third_party/tzdb/Atlantic/Azores")),
("Atlantic/Bermuda", include_bytes!("../../../../third_party/tzdb/Atlantic/Bermuda")),
("Atlantic/Canary", include_bytes!("../../../../third_party/tzdb/Atlantic/Canary")),
("Atlantic/Cape_Verde", include_bytes!("../../../../third_party/tzdb/Atlantic/Cape_Verde")),
("Atlantic/Faroe", include_bytes!("../../../../third_party/tzdb/Atlantic/Faroe")),
("Atlantic/Jan_Mayen", include_bytes!("../../../../third_party/tzdb/Atlantic/Jan_Mayen")),
("Atlantic/Madeira", include_bytes!("../../../../third_party/tzdb/Atlantic/Madeira")),
("Atlantic/Reykjavik", include_bytes!("../../../../third_party/tzdb/Atlantic/Reykjavik")),
(
"Atlantic/South_Georgia",
include_bytes!("../../../../third_party/tzdb/Atlantic/South_Georgia"),
),
("Atlantic/St_Helena", include_bytes!("../../../../third_party/tzdb/Atlantic/St_Helena")),
("Atlantic/Stanley", include_bytes!("../../../../third_party/tzdb/Atlantic/Stanley")),
("Australia/Adelaide", include_bytes!("../../../../third_party/tzdb/Australia/Adelaide")),
("Australia/Brisbane", include_bytes!("../../../../third_party/tzdb/Australia/Brisbane")),
(
"Australia/Broken_Hill",
include_bytes!("../../../../third_party/tzdb/Australia/Broken_Hill"),
),
("Australia/Canberra", include_bytes!("../../../../third_party/tzdb/Australia/Canberra")),
("Australia/Currie", include_bytes!("../../../../third_party/tzdb/Australia/Currie")),
("Australia/Darwin", include_bytes!("../../../../third_party/tzdb/Australia/Darwin")),
("Australia/Eucla", include_bytes!("../../../../third_party/tzdb/Australia/Eucla")),
("Australia/Hobart", include_bytes!("../../../../third_party/tzdb/Australia/Hobart")),
("Australia/Lindeman", include_bytes!("../../../../third_party/tzdb/Australia/Lindeman")),
("Australia/Lord_Howe", include_bytes!("../../../../third_party/tzdb/Australia/Lord_Howe")),
("Australia/Melbourne", include_bytes!("../../../../third_party/tzdb/Australia/Melbourne")),
("Australia/Perth", include_bytes!("../../../../third_party/tzdb/Australia/Perth")),
("Australia/Sydney", include_bytes!("../../../../third_party/tzdb/Australia/Sydney")),
(
"Australia/Yancowinna",
include_bytes!("../../../../third_party/tzdb/Australia/Yancowinna"),
),
("Etc/GMT", include_bytes!("../../../../third_party/tzdb/Etc/GMT")),
("Etc/GMT+0", include_bytes!("../../../../third_party/tzdb/Etc/GMT+0")),
("Etc/GMT+1", include_bytes!("../../../../third_party/tzdb/Etc/GMT+1")),
("Etc/GMT+10", include_bytes!("../../../../third_party/tzdb/Etc/GMT+10")),
("Etc/GMT+11", include_bytes!("../../../../third_party/tzdb/Etc/GMT+11")),
("Etc/GMT+12", include_bytes!("../../../../third_party/tzdb/Etc/GMT+12")),
("Etc/GMT+2", include_bytes!("../../../../third_party/tzdb/Etc/GMT+2")),
("Etc/GMT+3", include_bytes!("../../../../third_party/tzdb/Etc/GMT+3")),
("Etc/GMT+4", include_bytes!("../../../../third_party/tzdb/Etc/GMT+4")),
("Etc/GMT+5", include_bytes!("../../../../third_party/tzdb/Etc/GMT+5")),
("Etc/GMT+6", include_bytes!("../../../../third_party/tzdb/Etc/GMT+6")),
("Etc/GMT+7", include_bytes!("../../../../third_party/tzdb/Etc/GMT+7")),
("Etc/GMT+8", include_bytes!("../../../../third_party/tzdb/Etc/GMT+8")),
("Etc/GMT+9", include_bytes!("../../../../third_party/tzdb/Etc/GMT+9")),
("Etc/GMT-0", include_bytes!("../../../../third_party/tzdb/Etc/GMT-0")),
("Etc/GMT-1", include_bytes!("../../../../third_party/tzdb/Etc/GMT-1")),
("Etc/GMT-10", include_bytes!("../../../../third_party/tzdb/Etc/GMT-10")),
("Etc/GMT-11", include_bytes!("../../../../third_party/tzdb/Etc/GMT-11")),
("Etc/GMT-12", include_bytes!("../../../../third_party/tzdb/Etc/GMT-12")),
("Etc/GMT-13", include_bytes!("../../../../third_party/tzdb/Etc/GMT-13")),
("Etc/GMT-14", include_bytes!("../../../../third_party/tzdb/Etc/GMT-14")),
("Etc/GMT-2", include_bytes!("../../../../third_party/tzdb/Etc/GMT-2")),
("Etc/GMT-3", include_bytes!("../../../../third_party/tzdb/Etc/GMT-3")),
("Etc/GMT-4", include_bytes!("../../../../third_party/tzdb/Etc/GMT-4")),
("Etc/GMT-5", include_bytes!("../../../../third_party/tzdb/Etc/GMT-5")),
("Etc/GMT-6", include_bytes!("../../../../third_party/tzdb/Etc/GMT-6")),
("Etc/GMT-7", include_bytes!("../../../../third_party/tzdb/Etc/GMT-7")),
("Etc/GMT-8", include_bytes!("../../../../third_party/tzdb/Etc/GMT-8")),
("Etc/GMT-9", include_bytes!("../../../../third_party/tzdb/Etc/GMT-9")),
("Etc/GMT0", include_bytes!("../../../../third_party/tzdb/Etc/GMT0")),
("Etc/Greenwich", include_bytes!("../../../../third_party/tzdb/Etc/Greenwich")),
("Etc/UCT", include_bytes!("../../../../third_party/tzdb/Etc/UCT")),
("Etc/UTC", include_bytes!("../../../../third_party/tzdb/Etc/UTC")),
("Etc/Universal", include_bytes!("../../../../third_party/tzdb/Etc/Universal")),
("Etc/Zulu", include_bytes!("../../../../third_party/tzdb/Etc/Zulu")),
("Europe/Amsterdam", include_bytes!("../../../../third_party/tzdb/Europe/Amsterdam")),
("Europe/Andorra", include_bytes!("../../../../third_party/tzdb/Europe/Andorra")),
("Europe/Astrakhan", include_bytes!("../../../../third_party/tzdb/Europe/Astrakhan")),
("Europe/Athens", include_bytes!("../../../../third_party/tzdb/Europe/Athens")),
("Europe/Belfast", include_bytes!("../../../../third_party/tzdb/Europe/Belfast")),
("Europe/Belgrade", include_bytes!("../../../../third_party/tzdb/Europe/Belgrade")),
("Europe/Berlin", include_bytes!("../../../../third_party/tzdb/Europe/Berlin")),
("Europe/Bratislava", include_bytes!("../../../../third_party/tzdb/Europe/Bratislava")),
("Europe/Brussels", include_bytes!("../../../../third_party/tzdb/Europe/Brussels")),
("Europe/Bucharest", include_bytes!("../../../../third_party/tzdb/Europe/Bucharest")),
("Europe/Budapest", include_bytes!("../../../../third_party/tzdb/Europe/Budapest")),
("Europe/Busingen", include_bytes!("../../../../third_party/tzdb/Europe/Busingen")),
("Europe/Chisinau", include_bytes!("../../../../third_party/tzdb/Europe/Chisinau")),
("Europe/Copenhagen", include_bytes!("../../../../third_party/tzdb/Europe/Copenhagen")),
("Europe/Dublin", include_bytes!("../../../../third_party/tzdb/Europe/Dublin")),
("Europe/Gibraltar", include_bytes!("../../../../third_party/tzdb/Europe/Gibraltar")),
("Europe/Guernsey", include_bytes!("../../../../third_party/tzdb/Europe/Guernsey")),
("Europe/Helsinki", include_bytes!("../../../../third_party/tzdb/Europe/Helsinki")),
("Europe/Isle_of_Man", include_bytes!("../../../../third_party/tzdb/Europe/Isle_of_Man")),
("Europe/Istanbul", include_bytes!("../../../../third_party/tzdb/Europe/Istanbul")),
("Europe/Jersey", include_bytes!("../../../../third_party/tzdb/Europe/Jersey")),
("Europe/Kaliningrad", include_bytes!("../../../../third_party/tzdb/Europe/Kaliningrad")),
("Europe/Kirov", include_bytes!("../../../../third_party/tzdb/Europe/Kirov")),
("Europe/Kyiv", include_bytes!("../../../../third_party/tzdb/Europe/Kyiv")),
("Europe/Lisbon", include_bytes!("../../../../third_party/tzdb/Europe/Lisbon")),
("Europe/Ljubljana", include_bytes!("../../../../third_party/tzdb/Europe/Ljubljana")),
("Europe/London", include_bytes!("../../../../third_party/tzdb/Europe/London")),
("Europe/Luxembourg", include_bytes!("../../../../third_party/tzdb/Europe/Luxembourg")),
("Europe/Madrid", include_bytes!("../../../../third_party/tzdb/Europe/Madrid")),
("Europe/Malta", include_bytes!("../../../../third_party/tzdb/Europe/Malta")),
("Europe/Mariehamn", include_bytes!("../../../../third_party/tzdb/Europe/Mariehamn")),
("Europe/Minsk", include_bytes!("../../../../third_party/tzdb/Europe/Minsk")),
("Europe/Monaco", include_bytes!("../../../../third_party/tzdb/Europe/Monaco")),
("Europe/Moscow", include_bytes!("../../../../third_party/tzdb/Europe/Moscow")),
("Europe/Nicosia", include_bytes!("../../../../third_party/tzdb/Europe/Nicosia")),
("Europe/Oslo", include_bytes!("../../../../third_party/tzdb/Europe/Oslo")),
("Europe/Paris", include_bytes!("../../../../third_party/tzdb/Europe/Paris")),
("Europe/Podgorica", include_bytes!("../../../../third_party/tzdb/Europe/Podgorica")),
("Europe/Prague", include_bytes!("../../../../third_party/tzdb/Europe/Prague")),
("Europe/Riga", include_bytes!("../../../../third_party/tzdb/Europe/Riga")),
("Europe/Rome", include_bytes!("../../../../third_party/tzdb/Europe/Rome")),
("Europe/Samara", include_bytes!("../../../../third_party/tzdb/Europe/Samara")),
("Europe/San_Marino", include_bytes!("../../../../third_party/tzdb/Europe/San_Marino")),
("Europe/Sarajevo", include_bytes!("../../../../third_party/tzdb/Europe/Sarajevo")),
("Europe/Saratov", include_bytes!("../../../../third_party/tzdb/Europe/Saratov")),
("Europe/Simferopol", include_bytes!("../../../../third_party/tzdb/Europe/Simferopol")),
("Europe/Skopje", include_bytes!("../../../../third_party/tzdb/Europe/Skopje")),
("Europe/Sofia", include_bytes!("../../../../third_party/tzdb/Europe/Sofia")),
("Europe/Stockholm", include_bytes!("../../../../third_party/tzdb/Europe/Stockholm")),
("Europe/Tallinn", include_bytes!("../../../../third_party/tzdb/Europe/Tallinn")),
("Europe/Tirane", include_bytes!("../../../../third_party/tzdb/Europe/Tirane")),
("Europe/Tiraspol", include_bytes!("../../../../third_party/tzdb/Europe/Tiraspol")),
("Europe/Ulyanovsk", include_bytes!("../../../../third_party/tzdb/Europe/Ulyanovsk")),
("Europe/Vaduz", include_bytes!("../../../../third_party/tzdb/Europe/Vaduz")),
("Europe/Vatican", include_bytes!("../../../../third_party/tzdb/Europe/Vatican")),
("Europe/Vienna", include_bytes!("../../../../third_party/tzdb/Europe/Vienna")),
("Europe/Vilnius", include_bytes!("../../../../third_party/tzdb/Europe/Vilnius")),
("Europe/Volgograd", include_bytes!("../../../../third_party/tzdb/Europe/Volgograd")),
("Europe/Warsaw", include_bytes!("../../../../third_party/tzdb/Europe/Warsaw")),
("Europe/Zagreb", include_bytes!("../../../../third_party/tzdb/Europe/Zagreb")),
("Europe/Zurich", include_bytes!("../../../../third_party/tzdb/Europe/Zurich")),
("Factory", include_bytes!("../../../../third_party/tzdb/Factory")),
("GMT", include_bytes!("../../../../third_party/tzdb/GMT")),
("Indian/Antananarivo", include_bytes!("../../../../third_party/tzdb/Indian/Antananarivo")),
("Indian/Chagos", include_bytes!("../../../../third_party/tzdb/Indian/Chagos")),
("Indian/Christmas", include_bytes!("../../../../third_party/tzdb/Indian/Christmas")),
("Indian/Cocos", include_bytes!("../../../../third_party/tzdb/Indian/Cocos")),
("Indian/Comoro", include_bytes!("../../../../third_party/tzdb/Indian/Comoro")),
("Indian/Kerguelen", include_bytes!("../../../../third_party/tzdb/Indian/Kerguelen")),
("Indian/Mahe", include_bytes!("../../../../third_party/tzdb/Indian/Mahe")),
("Indian/Maldives", include_bytes!("../../../../third_party/tzdb/Indian/Maldives")),
("Indian/Mauritius", include_bytes!("../../../../third_party/tzdb/Indian/Mauritius")),
("Indian/Mayotte", include_bytes!("../../../../third_party/tzdb/Indian/Mayotte")),
("Indian/Reunion", include_bytes!("../../../../third_party/tzdb/Indian/Reunion")),
("Pacific/Apia", include_bytes!("../../../../third_party/tzdb/Pacific/Apia")),
("Pacific/Auckland", include_bytes!("../../../../third_party/tzdb/Pacific/Auckland")),
(
"Pacific/Bougainville",
include_bytes!("../../../../third_party/tzdb/Pacific/Bougainville"),
),
("Pacific/Chatham", include_bytes!("../../../../third_party/tzdb/Pacific/Chatham")),
("Pacific/Chuuk", include_bytes!("../../../../third_party/tzdb/Pacific/Chuuk")),
("Pacific/Easter", include_bytes!("../../../../third_party/tzdb/Pacific/Easter")),
("Pacific/Efate", include_bytes!("../../../../third_party/tzdb/Pacific/Efate")),
("Pacific/Fakaofo", include_bytes!("../../../../third_party/tzdb/Pacific/Fakaofo")),
("Pacific/Fiji", include_bytes!("../../../../third_party/tzdb/Pacific/Fiji")),
("Pacific/Funafuti", include_bytes!("../../../../third_party/tzdb/Pacific/Funafuti")),
("Pacific/Galapagos", include_bytes!("../../../../third_party/tzdb/Pacific/Galapagos")),
("Pacific/Gambier", include_bytes!("../../../../third_party/tzdb/Pacific/Gambier")),
("Pacific/Guadalcanal", include_bytes!("../../../../third_party/tzdb/Pacific/Guadalcanal")),
("Pacific/Guam", include_bytes!("../../../../third_party/tzdb/Pacific/Guam")),
("Pacific/Honolulu", include_bytes!("../../../../third_party/tzdb/Pacific/Honolulu")),
("Pacific/Johnston", include_bytes!("../../../../third_party/tzdb/Pacific/Johnston")),
("Pacific/Kanton", include_bytes!("../../../../third_party/tzdb/Pacific/Kanton")),
("Pacific/Kiritimati", include_bytes!("../../../../third_party/tzdb/Pacific/Kiritimati")),
("Pacific/Kosrae", include_bytes!("../../../../third_party/tzdb/Pacific/Kosrae")),
("Pacific/Kwajalein", include_bytes!("../../../../third_party/tzdb/Pacific/Kwajalein")),
("Pacific/Majuro", include_bytes!("../../../../third_party/tzdb/Pacific/Majuro")),
("Pacific/Marquesas", include_bytes!("../../../../third_party/tzdb/Pacific/Marquesas")),
("Pacific/Midway", include_bytes!("../../../../third_party/tzdb/Pacific/Midway")),
("Pacific/Nauru", include_bytes!("../../../../third_party/tzdb/Pacific/Nauru")),
("Pacific/Niue", include_bytes!("../../../../third_party/tzdb/Pacific/Niue")),
("Pacific/Norfolk", include_bytes!("../../../../third_party/tzdb/Pacific/Norfolk")),
("Pacific/Noumea", include_bytes!("../../../../third_party/tzdb/Pacific/Noumea")),
("Pacific/Pago_Pago", include_bytes!("../../../../third_party/tzdb/Pacific/Pago_Pago")),
("Pacific/Palau", include_bytes!("../../../../third_party/tzdb/Pacific/Palau")),
("Pacific/Pitcairn", include_bytes!("../../../../third_party/tzdb/Pacific/Pitcairn")),
("Pacific/Pohnpei", include_bytes!("../../../../third_party/tzdb/Pacific/Pohnpei")),
(
"Pacific/Port_Moresby",
include_bytes!("../../../../third_party/tzdb/Pacific/Port_Moresby"),
),
("Pacific/Rarotonga", include_bytes!("../../../../third_party/tzdb/Pacific/Rarotonga")),
("Pacific/Saipan", include_bytes!("../../../../third_party/tzdb/Pacific/Saipan")),
("Pacific/Samoa", include_bytes!("../../../../third_party/tzdb/Pacific/Samoa")),
("Pacific/Tahiti", include_bytes!("../../../../third_party/tzdb/Pacific/Tahiti")),
("Pacific/Tarawa", include_bytes!("../../../../third_party/tzdb/Pacific/Tarawa")),
("Pacific/Tongatapu", include_bytes!("../../../../third_party/tzdb/Pacific/Tongatapu")),
("Pacific/Wake", include_bytes!("../../../../third_party/tzdb/Pacific/Wake")),
("Pacific/Wallis", include_bytes!("../../../../third_party/tzdb/Pacific/Wallis")),
("Pacific/Yap", include_bytes!("../../../../third_party/tzdb/Pacific/Yap")),
("UTC", include_bytes!("../../../../third_party/tzdb/UTC")),
("posixrules", include_bytes!("../../../../third_party/tzdb/posixrules")),
];
const LEGACY_ALIASES: &[(&str, &str)] = &[
("Africa/Accra", "Africa/Abidjan"),
("Africa/Addis_Ababa", "Africa/Nairobi"),
("Africa/Asmara", "Africa/Nairobi"),
("Africa/Asmera", "Africa/Nairobi"),
("Africa/Bamako", "Africa/Abidjan"),
("Africa/Bangui", "Africa/Lagos"),
("Africa/Banjul", "Africa/Abidjan"),
("Africa/Blantyre", "Africa/Maputo"),
("Africa/Brazzaville", "Africa/Lagos"),
("Africa/Bujumbura", "Africa/Maputo"),
("Africa/Conakry", "Africa/Abidjan"),
("Africa/Dakar", "Africa/Abidjan"),
("Africa/Dar_es_Salaam", "Africa/Nairobi"),
("Africa/Djibouti", "Africa/Nairobi"),
("Africa/Douala", "Africa/Lagos"),
("Africa/Freetown", "Africa/Abidjan"),
("Africa/Gaborone", "Africa/Maputo"),
("Africa/Harare", "Africa/Maputo"),
("Africa/Kampala", "Africa/Nairobi"),
("Africa/Kigali", "Africa/Maputo"),
("Africa/Kinshasa", "Africa/Lagos"),
("Africa/Libreville", "Africa/Lagos"),
("Africa/Lome", "Africa/Abidjan"),
("Africa/Luanda", "Africa/Lagos"),
("Africa/Lubumbashi", "Africa/Maputo"),
("Africa/Lusaka", "Africa/Maputo"),
("Africa/Malabo", "Africa/Lagos"),
("Africa/Maseru", "Africa/Johannesburg"),
("Africa/Mbabane", "Africa/Johannesburg"),
("Africa/Mogadishu", "Africa/Nairobi"),
("Africa/Niamey", "Africa/Lagos"),
("Africa/Nouakchott", "Africa/Abidjan"),
("Africa/Ouagadougou", "Africa/Abidjan"),
("Africa/Porto-Novo", "Africa/Lagos"),
("Africa/Timbuktu", "Africa/Abidjan"),
("America/Anguilla", "America/Puerto_Rico"),
("America/Antigua", "America/Puerto_Rico"),
("America/Argentina/ComodRivadavia", "America/Argentina/Catamarca"),
("America/Aruba", "America/Puerto_Rico"),
("America/Atikokan", "America/Panama"),
("America/Atka", "America/Adak"),
("America/Blanc-Sablon", "America/Puerto_Rico"),
("America/Buenos_Aires", "America/Argentina/Buenos_Aires"),
("America/Catamarca", "America/Argentina/Catamarca"),
("America/Cayman", "America/Panama"),
("America/Coral_Harbour", "America/Panama"),
("America/Cordoba", "America/Argentina/Cordoba"),
("America/Creston", "America/Phoenix"),
("America/Curacao", "America/Puerto_Rico"),
("America/Dominica", "America/Puerto_Rico"),
("America/Ensenada", "America/Tijuana"),
("America/Fort_Wayne", "America/Indiana/Indianapolis"),
("America/Godthab", "America/Nuuk"),
("America/Grenada", "America/Puerto_Rico"),
("America/Guadeloupe", "America/Puerto_Rico"),
("America/Indianapolis", "America/Indiana/Indianapolis"),
("America/Jujuy", "America/Argentina/Jujuy"),
("America/Knox_IN", "America/Indiana/Knox"),
("America/Kralendijk", "America/Puerto_Rico"),
("America/Louisville", "America/Kentucky/Louisville"),
("America/Lower_Princes", "America/Puerto_Rico"),
("America/Marigot", "America/Puerto_Rico"),
("America/Mendoza", "America/Argentina/Mendoza"),
("America/Montreal", "America/Toronto"),
("America/Montserrat", "America/Puerto_Rico"),
("America/Nassau", "America/Toronto"),
("America/Nipigon", "America/Toronto"),
("America/Pangnirtung", "America/Iqaluit"),
("America/Port_of_Spain", "America/Puerto_Rico"),
("America/Porto_Acre", "America/Rio_Branco"),
("America/Rainy_River", "America/Winnipeg"),
("America/Rosario", "America/Argentina/Cordoba"),
("America/Santa_Isabel", "America/Tijuana"),
("America/Shiprock", "America/Denver"),
("America/St_Barthelemy", "America/Puerto_Rico"),
("America/St_Kitts", "America/Puerto_Rico"),
("America/St_Lucia", "America/Puerto_Rico"),
("America/St_Thomas", "America/Puerto_Rico"),
("America/St_Vincent", "America/Puerto_Rico"),
("America/Thunder_Bay", "America/Toronto"),
("America/Tortola", "America/Puerto_Rico"),
("America/Virgin", "America/Puerto_Rico"),
("America/Yellowknife", "America/Edmonton"),
("Antarctica/DumontDUrville", "Pacific/Port_Moresby"),
("Antarctica/McMurdo", "Pacific/Auckland"),
("Antarctica/South_Pole", "Pacific/Auckland"),
("Antarctica/Syowa", "Asia/Riyadh"),
("Arctic/Longyearbyen", "Europe/Berlin"),
("Asia/Aden", "Asia/Riyadh"),
("Asia/Ashkhabad", "Asia/Ashgabat"),
("Asia/Bahrain", "Asia/Qatar"),
("Asia/Brunei", "Asia/Kuching"),
("Asia/Calcutta", "Asia/Kolkata"),
("Asia/Choibalsan", "Asia/Ulaanbaatar"),
("Asia/Chongqing", "Asia/Shanghai"),
("Asia/Chungking", "Asia/Shanghai"),
("Asia/Dacca", "Asia/Dhaka"),
("Asia/Harbin", "Asia/Shanghai"),
("Asia/Istanbul", "Europe/Istanbul"),
("Asia/Kashgar", "Asia/Urumqi"),
("Asia/Katmandu", "Asia/Kathmandu"),
("Asia/Kuala_Lumpur", "Asia/Singapore"),
("Asia/Kuwait", "Asia/Riyadh"),
("Asia/Macao", "Asia/Macau"),
("Asia/Muscat", "Asia/Dubai"),
("Asia/Phnom_Penh", "Asia/Bangkok"),
("Asia/Rangoon", "Asia/Yangon"),
("Asia/Saigon", "Asia/Ho_Chi_Minh"),
("Asia/Tel_Aviv", "Asia/Jerusalem"),
("Asia/Thimbu", "Asia/Thimphu"),
("Asia/Ujung_Pandang", "Asia/Makassar"),
("Asia/Ulan_Bator", "Asia/Ulaanbaatar"),
("Asia/Vientiane", "Asia/Bangkok"),
("Atlantic/Faeroe", "Atlantic/Faroe"),
("Atlantic/Jan_Mayen", "Europe/Berlin"),
("Atlantic/Reykjavik", "Africa/Abidjan"),
("Atlantic/St_Helena", "Africa/Abidjan"),
("Australia/ACT", "Australia/Sydney"),
("Australia/Canberra", "Australia/Sydney"),
("Australia/Currie", "Australia/Hobart"),
("Australia/LHI", "Australia/Lord_Howe"),
("Australia/NSW", "Australia/Sydney"),
("Australia/North", "Australia/Darwin"),
("Australia/Queensland", "Australia/Brisbane"),
("Australia/South", "Australia/Adelaide"),
("Australia/Tasmania", "Australia/Hobart"),
("Australia/Victoria", "Australia/Melbourne"),
("Australia/West", "Australia/Perth"),
("Australia/Yancowinna", "Australia/Broken_Hill"),
("Brazil/Acre", "America/Rio_Branco"),
("Brazil/DeNoronha", "America/Noronha"),
("Brazil/East", "America/Sao_Paulo"),
("Brazil/West", "America/Manaus"),
("CET", "Europe/Brussels"),
("CST6CDT", "America/Chicago"),
("Canada/Atlantic", "America/Halifax"),
("Canada/Central", "America/Winnipeg"),
("Canada/Eastern", "America/Toronto"),
("Canada/Mountain", "America/Edmonton"),
("Canada/Newfoundland", "America/St_Johns"),
("Canada/Pacific", "America/Vancouver"),
("Canada/Saskatchewan", "America/Regina"),
("Canada/Yukon", "America/Whitehorse"),
("Chile/Continental", "America/Santiago"),
("Chile/EasterIsland", "Pacific/Easter"),
("Cuba", "America/Havana"),
("EET", "Europe/Athens"),
("EST", "America/Panama"),
("EST5EDT", "America/New_York"),
("Egypt", "Africa/Cairo"),
("Eire", "Europe/Dublin"),
("Etc/GMT+0", "Etc/GMT"),
("Etc/GMT-0", "Etc/GMT"),
("Etc/GMT0", "Etc/GMT"),
("Etc/Greenwich", "Etc/GMT"),
("Etc/Ignored", "Etc/UTC"),
("Etc/UCT", "Etc/UTC"),
("Etc/Universal", "Etc/UTC"),
("Etc/Zulu", "Etc/UTC"),
("Europe/Amsterdam", "Europe/Brussels"),
("Europe/Belfast", "Europe/London"),
("Europe/Bratislava", "Europe/Prague"),
("Europe/Busingen", "Europe/Zurich"),
("Europe/Copenhagen", "Europe/Berlin"),
("Europe/Guernsey", "Europe/London"),
("Europe/Isle_of_Man", "Europe/London"),
("Europe/Jersey", "Europe/London"),
("Europe/Kiev", "Europe/Kyiv"),
("Europe/Ljubljana", "Europe/Belgrade"),
("Europe/Luxembourg", "Europe/Brussels"),
("Europe/Mariehamn", "Europe/Helsinki"),
("Europe/Monaco", "Europe/Paris"),
("Europe/Nicosia", "Asia/Nicosia"),
("Europe/Oslo", "Europe/Berlin"),
("Europe/Podgorica", "Europe/Belgrade"),
("Europe/San_Marino", "Europe/Rome"),
("Europe/Sarajevo", "Europe/Belgrade"),
("Europe/Skopje", "Europe/Belgrade"),
("Europe/Stockholm", "Europe/Berlin"),
("Europe/Tiraspol", "Europe/Chisinau"),
("Europe/Uzhgorod", "Europe/Kyiv"),
("Europe/Vaduz", "Europe/Zurich"),
("Europe/Vatican", "Europe/Rome"),
("Europe/Zagreb", "Europe/Belgrade"),
("Europe/Zaporozhye", "Europe/Kyiv"),
("GB", "Europe/London"),
("GB-Eire", "Europe/London"),
("GMT+0", "Etc/GMT"),
("GMT-0", "Etc/GMT"),
("GMT0", "Etc/GMT"),
("Greenwich", "Etc/GMT"),
("HST", "Pacific/Honolulu"),
("Hongkong", "Asia/Hong_Kong"),
("Iceland", "Africa/Abidjan"),
("Indian/Antananarivo", "Africa/Nairobi"),
("Indian/Christmas", "Asia/Bangkok"),
("Indian/Cocos", "Asia/Yangon"),
("Indian/Comoro", "Africa/Nairobi"),
("Indian/Kerguelen", "Indian/Maldives"),
("Indian/Mahe", "Asia/Dubai"),
("Indian/Mayotte", "Africa/Nairobi"),
("Indian/Reunion", "Asia/Dubai"),
("Iran", "Asia/Tehran"),
("Israel", "Asia/Jerusalem"),
("Jamaica", "America/Jamaica"),
("Japan", "Asia/Tokyo"),
("Kwajalein", "Pacific/Kwajalein"),
("Libya", "Africa/Tripoli"),
("MET", "Europe/Brussels"),
("MST", "America/Phoenix"),
("MST7MDT", "America/Denver"),
("Mexico/BajaNorte", "America/Tijuana"),
("Mexico/BajaSur", "America/Mazatlan"),
("Mexico/General", "America/Mexico_City"),
("NZ", "Pacific/Auckland"),
("NZ-CHAT", "Pacific/Chatham"),
("Navajo", "America/Denver"),
("PRC", "Asia/Shanghai"),
("PST8PDT", "America/Los_Angeles"),
("Pacific/Chuuk", "Pacific/Port_Moresby"),
("Pacific/Enderbury", "Pacific/Kanton"),
("Pacific/Funafuti", "Pacific/Tarawa"),
("Pacific/Johnston", "Pacific/Honolulu"),
("Pacific/Majuro", "Pacific/Tarawa"),
("Pacific/Midway", "Pacific/Pago_Pago"),
("Pacific/Pohnpei", "Pacific/Guadalcanal"),
("Pacific/Ponape", "Pacific/Guadalcanal"),
("Pacific/Saipan", "Pacific/Guam"),
("Pacific/Samoa", "Pacific/Pago_Pago"),
("Pacific/Truk", "Pacific/Port_Moresby"),
("Pacific/Wake", "Pacific/Tarawa"),
("Pacific/Wallis", "Pacific/Tarawa"),
("Pacific/Yap", "Pacific/Port_Moresby"),
("Poland", "Europe/Warsaw"),
("Portugal", "Europe/Lisbon"),
("ROC", "Asia/Taipei"),
("ROK", "Asia/Seoul"),
("Singapore", "Asia/Singapore"),
("Turkey", "Europe/Istanbul"),
("UCT", "Etc/UTC"),
("US/Alaska", "America/Anchorage"),
("US/Aleutian", "America/Adak"),
("US/Arizona", "America/Phoenix"),
("US/Central", "America/Chicago"),
("US/East-Indiana", "America/Indiana/Indianapolis"),
("US/Eastern", "America/New_York"),
("US/Hawaii", "Pacific/Honolulu"),
("US/Indiana-Starke", "America/Indiana/Knox"),
("US/Michigan", "America/Detroit"),
("US/Mountain", "America/Denver"),
("US/Pacific", "America/Los_Angeles"),
("US/Samoa", "Pacific/Pago_Pago"),
("UTC", "Etc/UTC"),
("Universal", "Etc/UTC"),
("W-SU", "Europe/Moscow"),
("WET", "Europe/Lisbon"),
("Zulu", "Etc/UTC"),
];
/// 区表:首次使用时解析全部 vendored 区文件,按名排序供二分查找。
struct ZoneTable {
zones: Vec<(&'static str, TzifZone)>,
}
static ZONE_TABLE: LazyLock<ZoneTable> = LazyLock::new(|| {
let mut zones = ZONE_FILES
.iter()
.filter_map(|(name, bytes)| parse_tzif(bytes).ok().map(|z| (*name, z)))
.collect::<Vec<_>>();
zones.sort_by(|a, b| a.0.cmp(b.0));
ZoneTable { zones }
});
/// 按 IANA 区名(含 legacy 别名)查区数据。
///
/// # 步骤
/// 1. 先解 legacy 别名(`LEGACY_ALIASES` 二分),未命中则原名。
/// 2. 在区表二分查找,命中返回其 `TzifZone`。
///
/// # 边界与前提
/// - 别名只解一层:别名目标是现行区名,不再二次解别名。
///
/// # 注意事项
/// - 首次调用触发全量解析(毫秒级),之后为纯二分。
pub(crate) fn tz_zone_data(name: &str) -> Option<&'static TzifZone> {
let target = match LEGACY_ALIASES.binary_search_by(|a| a.0.cmp(name)) {
Ok(i) => LEGACY_ALIASES[i].1,
Err(_) => name,
};
match ZONE_TABLE.zones.binary_search_by(|z| z.0.cmp(target)) {
Ok(i) => Some(&ZONE_TABLE.zones[i].1),
Err(_) => None,
}
}
/// 按 IANA 区名(含 legacy 别名)查规范区名。
///
/// # 步骤
/// 1. 先解 legacy 别名(`LEGACY_ALIASES` 二分),未命中则原名。
/// 2. 在区表二分查找,命中返回其规范名(`&'static str`)。
///
/// # 边界与前提
/// - 别名只解一层:别名目标是现行区名,不再二次解别名。
pub(crate) fn tz_canonical_name(name: &str) -> Option<&'static str> {
let target = match LEGACY_ALIASES.binary_search_by(|a| a.0.cmp(name)) {
Ok(i) => LEGACY_ALIASES[i].1,
Err(_) => name,
};
ZONE_TABLE
.zones
.binary_search_by(|z| z.0.cmp(target))
.ok()
.map(|i| ZONE_TABLE.zones[i].0)
}
/// 查某区在 `epoch_s`(纪元秒)生效的 UTC 偏移(秒)。
///
/// # 步骤
/// 1. 查区数据,未命中返回 `None`。
/// 2. `partition_point` 找最后一个 `time <= epoch_s` 的 transition:
/// 无(早于首个)取 `lmt_offset`,有取该 transition 的偏移。
///
/// # 边界与前提
/// - 早于首个 transition 取 LMT;晚于末个 transition 取末偏移(`last_offset`
/// 与末次 transition 一致,故 `partition_point` 已覆盖)。
fn tz_offset_seconds_of(z: &TzifZone, epoch_s: i64) -> i64 {
if z.transitions.is_empty() {
return i64::from(z.last_offset);
}
let idx = z.transitions.partition_point(|t| t.0 <= epoch_s);
if idx == 0 {
i64::from(z.lmt_offset)
} else if idx == z.transitions.len() {
i64::from(z.last_offset)
} else {
i64::from(z.transitions[idx - 1].1)
}
}
/// 查某区在 `epoch_s`(纪元秒)生效的 UTC 偏移(秒)。
pub(crate) fn tz_offset_seconds(zone: &str, epoch_s: i64) -> Option<i64> {
tz_zone_data(zone).map(|z| tz_offset_seconds_of(z, epoch_s))
}
/// GetNamedTimeZoneEpochNanoseconds(规范 14.6.3,宿主定义):墙历时刻的全部候选纪元。
///
/// # 步骤
/// 1. 取墙历(按 UTC 解释)相邻的三个区间偏移:所在区间、下一 transition 后、
/// 上一 transition 前。
/// 2. 对每个偏移 o 构造候选 e = wall - o;校验 e 落在 o 的区间内
/// (offset_at(e) == o),成立才收。
/// 3. 去重升序返回(重叠 = 2 个、间隙 = 0 个、无歧义 = 1 个)。
///
/// # 边界与前提
/// - 区表未命中返回 `None`(调用方决定错误)。
/// - 偏移量域 ±24 小时,有效候选必在墙历(按 UTC 解释)±24 小时内,
/// 三个相邻区间覆盖全部候选。
pub(crate) fn named_zone_possible_epoch_ns(zone: &str, wall_ns: i128) -> Option<Vec<i128>> {
let z = tz_zone_data(zone)?;
let wall_s = wall_ns.div_euclid(1_000_000_000) as i64;
let idx = z.transitions.partition_point(|t| t.0 <= wall_s);
let current = if idx == 0 { z.lmt_offset } else { z.transitions[idx - 1].1 };
let mut candidates = Vec::with_capacity(3);
let mut consider = |offset: i32| {
let candidate = wall_ns - i128::from(offset) * 1_000_000_000;
if tz_offset_seconds_of(z, wall_s - i64::from(offset)) == i64::from(offset) {
candidates.push(candidate);
}
};
consider(current);
if idx < z.transitions.len() {
consider(z.transitions[idx].1);
}
if idx > 0 {
let previous = if idx - 1 == 0 { z.lmt_offset } else { z.transitions[idx - 2].1 };
consider(previous);
}
candidates.sort_unstable();
candidates.dedup();
Some(candidates)
}
/// 找 0 候选墙历所落间隙的 transition,返回间隙前后偏移(秒)。
///
/// # 步骤
/// 1. 墙历(按 UTC 解释)在 transition 前区间:查下一 transition 是否正偏移跳变
/// 且墙历落间隙 (t + o_before, t + o_after)。
/// 2. 墙历在 transition 后区间:查上一 transition 同条件。
///
/// # 边界与前提
/// - 区表未命中或墙历不落在任何间隙内返回 `None`(调用方决定错误)。
pub(crate) fn tz_gap_offsets(zone: &str, wall_ns: i128) -> Option<(i64, i64)> {
let z = tz_zone_data(zone)?;
let wall_s = wall_ns.div_euclid(1_000_000_000) as i64;
let idx = z.transitions.partition_point(|t| t.0 <= wall_s);
// 墙历在 transition 前区间:间隙由下一 transition 产生。
// 间隙起点(墙历 = t + o_before)候选集为空,计入间隙。
if idx < z.transitions.len() {
let (t, offset_after) = z.transitions[idx];
let offset_before = if idx == 0 { z.lmt_offset } else { z.transitions[idx - 1].1 };
if offset_after > offset_before
&& wall_ns >= i128::from(t) * 1_000_000_000 + i128::from(offset_before) * 1_000_000_000
&& wall_ns < i128::from(t) * 1_000_000_000 + i128::from(offset_after) * 1_000_000_000
{
return Some((i64::from(offset_before), i64::from(offset_after)));
}
}
// 墙历在 transition 后区间:间隙由上一 transition 产生。
if idx > 0 {
let (t, offset_after) = z.transitions[idx - 1];
let offset_before = if idx - 1 == 0 { z.lmt_offset } else { z.transitions[idx - 2].1 };
if offset_after > offset_before
&& wall_ns >= i128::from(t) * 1_000_000_000 + i128::from(offset_before) * 1_000_000_000
&& wall_ns < i128::from(t) * 1_000_000_000 + i128::from(offset_after) * 1_000_000_000
{
return Some((i64::from(offset_before), i64::from(offset_after)));
}
}
None
}
/// 统一时区偏移查找(epoch 依赖):固定偏移区(数值偏移串)走快速路径恒返常量,
/// IANA 区走 transition 表二分。
///
/// # 步骤
/// 1. 数值偏移串(±HH:MM / ±HH / ±HHMM / ±HH:MM:SS)直接解析为秒返回。
/// 2. 未命中走 `tz_offset_seconds`(IANA 区,含 legacy 别名)。
///
/// # 边界与前提
/// - 固定偏移区是 zone-aware 路径的退化情形:偏移与 epoch 无关,恒返回常量。
/// - 非法区名(非数值偏移串且不在区表)返回 `None`。
pub(crate) fn zone_offset_seconds(zone: &str, epoch_s: i64) -> Option<i64> {
if let Some(offset) = fixed_offset_seconds(zone) {
return Some(offset);
}
tz_offset_seconds(zone, epoch_s)
}
/// 判断 `name` 是否为可接受的 IANA 区名(含 legacy 别名)。
#[allow(dead_code)]
pub(crate) fn tz_is_iana_zone(name: &str) -> bool {
tz_zone_data(name).is_some()
}
/// 查 `epoch_s` 之后(严格大于)的下一个 transition 纪元秒。
#[allow(dead_code)]
pub(crate) fn tz_next_transition(zone: &str, epoch_s: i64) -> Option<i64> {
let z = tz_zone_data(zone)?;
z.transitions.iter().find(|t| t.0 > epoch_s).map(|t| t.0)
}
/// 查 `epoch_s` 之前(严格小于)的上一个 transition 纪元秒。
#[allow(dead_code)]
pub(crate) fn tz_prev_transition(zone: &str, epoch_s: i64) -> Option<i64> {
let z = tz_zone_data(zone)?;
z.transitions.iter().rev().find(|t| t.0 < epoch_s).map(|t| t.0)
}
#[cfg(test)]
mod tests {
use super::*;
/// 两个区名是否解析到同一份区数据(指针恒等)。
fn same_zone(a: &str, b: &str) -> bool {
match (tz_zone_data(a), tz_zone_data(b)) {
(Some(x), Some(y)) => std::ptr::eq(x as *const _, y as *const _),
(None, None) => true,
_ => false,
}
}
#[test]
fn new_york_1883_lmt_transition() {
// 1883 LMT transition(最早 transition,1970 年前,亚分钟 LMT -04:56:02)。
let z = tz_zone_data("America/New_York").unwrap();
assert_eq!(z.transitions[0].0, -2717650800);
assert_eq!(z.lmt_offset, -17762);
assert_eq!(tz_offset_seconds("America/New_York", -2717650800 - 1), Some(-17762));
assert_eq!(tz_offset_seconds("America/New_York", -2717650800), Some(-18000));
}
#[test]
fn monrovia_sub_minute_offset() {
// 亚分钟偏移 -00:44:30;1970-01-01T12:00-00:44:30 对应纪元 45870 秒。
assert_eq!(tz_offset_seconds("Africa/Monrovia", 45870), Some(-2670));
assert_eq!(tz_offset_seconds("Africa/Monrovia", 0), Some(-2670));
}
#[test]
fn niue_sub_minute_lmt_transition() {
// 1952-10-15T23:59:59-11:19:40(亚分钟 LMT -11:19:40 → -11:20:00)。
assert_eq!(tz_offset_seconds("Pacific/Niue", -543069620 - 1), Some(-40780));
assert_eq!(tz_offset_seconds("Pacific/Niue", -543069620), Some(-40800));
}
#[test]
fn apia_2011_skip_day() {
// 2011-12-30 跳变(24 小时日):-10:00 → +14:00。
assert_eq!(tz_offset_seconds("Pacific/Apia", 1325239200 - 1), Some(-36000));
assert_eq!(tz_offset_seconds("Pacific/Apia", 1325239200), Some(50400));
}
#[test]
fn vancouver_2000_dst_start() {
// 2000-04-02 DST 开始(23 小时日):-08:00 → -07:00。
assert_eq!(tz_offset_seconds("America/Vancouver", 954669600 - 1), Some(-28800));
assert_eq!(tz_offset_seconds("America/Vancouver", 954669600), Some(-25200));
}
#[test]
fn lord_howe_half_hour_dst() {
// 半小时 DST:+10:30 → +11:00。
assert_eq!(tz_offset_seconds("Australia/Lord_Howe", 941297400 - 1), Some(37800));
assert_eq!(tz_offset_seconds("Australia/Lord_Howe", 941297400), Some(39600));
}
#[test]
fn casey_2010_revert_to_utc8() {
// 2010-03-05 02:00(+11:00 当地)一次性回到 +08:00,源表为一次性日期而非循环规则。
assert_eq!(tz_offset_seconds("Antarctica/Casey", 1267714800 - 1), Some(39600));
assert_eq!(tz_offset_seconds("Antarctica/Casey", 1267714800), Some(28800));
}
#[test]
fn riyadh_and_paris_lmt() {
// 1850 年 Riyadh LMT +03:06:52;1800 年 Paris LMT +00:09:21。
assert_eq!(tz_offset_seconds("Asia/Riyadh", -3786825600), Some(11212));
assert_eq!(tz_offset_seconds("Europe/Paris", -5364662400), Some(561));
}
#[test]
fn legacy_alias_hits() {
// 语料钉住的 legacy 区名命中别名表,解析到同一区数据(指针恒等)。
assert!(same_zone("Asia/Calcutta", "Asia/Kolkata"));
assert!(same_zone("Asia/Katmandu", "Asia/Kathmandu"));
assert!(same_zone("Etc/Ignored", "Etc/UTC"));
assert!(tz_is_iana_zone("Asia/Calcutta"));
assert!(tz_is_iana_zone("Etc/Ignored"));
}
#[test]
fn invalid_zone_returns_none() {
// 非法区名返回 None。
assert!(tz_zone_data("Mars/Olympus_Mons").is_none());
assert_eq!(tz_offset_seconds("Not/AZone", 0), None);
assert!(!tz_is_iana_zone("Not/AZone"));
assert_eq!(tz_next_transition("Not/AZone", 0), None);
assert_eq!(tz_prev_transition("Not/AZone", 0), None);
}
#[test]
fn next_and_prev_transition() {
// transition 前后边界。
assert_eq!(tz_next_transition("Pacific/Apia", 1325239200 - 1), Some(1325239200));
assert_eq!(tz_prev_transition("Pacific/Apia", 1325239200), Some(1316872800));
assert_eq!(tz_prev_transition("Pacific/Apia", -2445424384), None);
assert_eq!(tz_next_transition("Etc/UTC", 0), None);
}
#[test]
fn parse_tzif_version_header() {
// 版本头:魔数 / 版本字节 / 截断 / 非法版本。
assert!(matches!(parse_tzif(b"TZif"), Err(TzifError::Truncated)));
let mut bad_magic = b"XZif2".to_vec();
bad_magic.extend_from_slice(&[0u8; 39]);
assert!(matches!(parse_tzif(&bad_magic), Err(TzifError::BadMagic)));
let mut bad_ver = b"TZif".to_vec();
bad_ver.push(b'9');
bad_ver.extend_from_slice(&[0u8; 40]);
assert!(matches!(parse_tzif(&bad_ver), Err(TzifError::BadVersion)));
}
}