use crate::timezones::get_by_name;
use crate::Tz;
use thiserror::Error;
#[cfg(target_family = "wasm")]
use js_sys::{ Intl, Reflect, Array, Object };
#[cfg(target_family = "wasm")]
use wasm_bindgen::JsValue;
#[derive(Debug, Error)]
pub enum Error {
#[error("io error: {0}")]
Io(std::io::Error),
#[error("low-level os error")]
Os,
#[error("undefined timezone")]
Undetermined,
#[error("timezone name is not unicode")]
Unicode,
#[error("unknown timezone name")]
Unknown,
#[error("unsupported platform")]
Unsupported,
}
pub fn get_timezone() -> Result<&'static Tz, Error> {
cfg_if::cfg_if! {
if #[cfg(unix)] {
use std::path::Path;
let path = Path::new("/etc/localtime");
let realpath = std::fs::read_link(path).map_err(Error::Io)?;
if let Some(iana) = realpath.to_str().ok_or(Error::Unicode)?.split("/zoneinfo/").last() {
let tz = get_by_name(iana).ok_or(Error::Unknown)?;
Ok(tz)
} else {
Err(Error::Undetermined)
}
} else if #[cfg(windows)] {
unsafe {
use windows_sys::Win32::System::Time::GetDynamicTimeZoneInformation;
use windows_sys::Win32::System::Time::DYNAMIC_TIME_ZONE_INFORMATION;
let mut data: DYNAMIC_TIME_ZONE_INFORMATION = std::mem::zeroed();
let res = GetDynamicTimeZoneInformation(&mut data as _);
if res > 2 {
return Err(Error::Os);
} else {
let win_name_utf16 = &data.TimeZoneKeyName;
let mut len: usize = 0;
while win_name_utf16[len] != 0x0 {
len += 1;
}
if len == 0 {
return Err(Error::Undetermined);
}
let win_tz = String::from_utf16(&win_name_utf16[..len]).map_err(|_| Error::Unicode)?;
let tz = get_by_name(&win_tz).ok_or(Error::Unknown)?;
Ok(tz)
}
}
} else if #[cfg(target_family = "wasm")] {
let options = Intl::DateTimeFormat::new(&Array::new(), &Object::new())
.resolved_options();
let tz = Reflect::get(&options, &JsValue::from("timeZone"))
.map_err(|_| Error::Undetermined)?
.as_string()
.ok_or(Error::Unicode)?;
let tz = get_by_name(&tz).ok_or(Error::Unknown)?;
Ok(tz)
} else {
Err(Error::Unsupported)
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn get_timezone() {
let tz = super::get_timezone();
assert!(tz.is_ok());
}
}