use std::fs::{read_link, read_to_string}; pub(crate) fn get_timezone_inner() -> Result { etc_localtime().or_else(|_| etc_timezone()) } fn etc_timezone() -> Result { // 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 { // Per : // “ 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. // 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) }