use std::path::PathBuf;
use std::sync::OnceLock;
use mf2_runtime::TimeZone;
pub(crate) fn system() -> TimeZone {
static SYSTEM: OnceLock<TimeZone> = OnceLock::new();
*SYSTEM.get_or_init(read)
}
fn read() -> TimeZone {
if let Ok(zone) = jiff::tz::TimeZone::try_system()
&& let Some(named) = zone.iana_name().and_then(TimeZone::named)
{
return named;
}
rules().unwrap_or(TimeZone::UTC)
}
fn rules() -> Option<TimeZone> {
let tz = std::env::var("TZ").ok();
let file = match tz.as_deref().map(|v| v.strip_prefix(':').unwrap_or(v)) {
Some(value) if !value.is_empty() => {
if jiff::tz::TimeZone::posix(value).is_ok() {
return TimeZone::rules(value);
}
if !value.starts_with('/') {
return None;
}
PathBuf::from(value)
}
_ => PathBuf::from("/etc/localtime"),
};
let bytes = std::fs::read(file).ok()?;
let rule = footer(&bytes)?;
jiff::tz::TimeZone::posix(rule).ok()?;
TimeZone::rules(rule)
}
fn footer(bytes: &[u8]) -> Option<&str> {
if !bytes.starts_with(b"TZif") || bytes.get(4).is_none_or(|v| *v < b'2') {
return None;
}
let body = bytes.strip_suffix(b"\n")?;
let start = body.iter().rposition(|&b| b == b'\n')? + 1;
core::str::from_utf8(body.get(start..)?)
.ok()
.filter(|rule| !rule.is_empty())
}
#[cfg(test)]
mod tests {
use super::footer;
#[test]
fn a_tzif_file_ends_with_its_rule() {
let mut file = b"TZif2".to_vec();
file.extend_from_slice(&[0; 40]);
file.extend_from_slice(b"\nEST5EDT,M3.2.0,M11.1.0\n");
assert_eq!(footer(&file), Some("EST5EDT,M3.2.0,M11.1.0"));
let mut old = b"TZif\0".to_vec();
old.extend_from_slice(&[0; 40]);
old.extend_from_slice(b"\nEST5EDT,M3.2.0,M11.1.0\n");
assert_eq!(footer(&old), None);
file.pop();
assert_eq!(footer(&file), None);
assert_eq!(footer(b"TZif3...\n\n"), None);
assert_eq!(footer(b"#!/bin/sh\nEST5EDT\n"), None);
}
}