use std::io::ErrorKind;
use once_cell::race::OnceBool;
use once_cell::sync::OnceCell;
use time::macros::offset;
use time::OffsetDateTime;
use time::UtcOffset;
use tz::{TimeZone, TzError};
pub const THAILAND_UTC_OFFSET: UtcOffset = offset!(+7:00:00);
pub fn local_now() -> OffsetDateTime {
let now = OffsetDateTime::now_utc();
let unix_timestamp = now.unix_timestamp();
let local_utc_offset = local_utc_offset_impl(Some(unix_timestamp));
now.to_offset(local_utc_offset)
}
pub fn local_utc_offset() -> UtcOffset {
local_utc_offset_impl(None)
}
fn local_utc_offset_impl(unix_timestamp: Option<i64>) -> UtcOffset {
static TIMEZONE_IS_CACHED: OnceBool = OnceBool::new();
let cache_timezone = TIMEZONE_IS_CACHED.get_or_init(|| {
std::env::var("BEID_CACHE_TIMEZONE")
.map(|s| matches!(s.as_str(), "1" | "true" | "yes" | "on"))
.unwrap_or(true)
});
if cfg!(unix) {
get_unix_local_utc_offset(cache_timezone, unix_timestamp)
.expect("UtcOffset for unix platform")
} else {
UtcOffset::current_local_offset().expect("UtcOffset for non-unix platform")
}
}
fn is_io_not_found(error: &TzError) -> bool {
matches!(error, TzError::IoError(err) if err.kind() == ErrorKind::NotFound)
}
fn get_unix_timezone() -> Result<TimeZone, TzError> {
if let Ok(tz_string) = std::env::var("TZ") {
TimeZone::from_posix_tz(&tz_string).or_else(|_| TimeZone::local())
} else {
TimeZone::local()
}
}
fn get_unix_local_utc_offset(
cache_timezone: bool,
unix_timestamp: Option<i64>,
) -> Result<UtcOffset, TzError> {
static TIMEZONE: OnceCell<TimeZone> = OnceCell::new();
let mut non_cached_timezone = None;
let timezone_result = if cache_timezone {
TIMEZONE.get_or_try_init(get_unix_timezone)
} else {
get_unix_timezone().map(|v| &*non_cached_timezone.insert(v))
};
let timezone = match timezone_result {
Ok(val) => val,
Err(err) if is_io_not_found(&err) => {
return Ok(UtcOffset::UTC);
}
Err(err) => return Err(err),
};
let local_time_type = match unix_timestamp {
Some(unix_timestamp) => timezone.find_local_time_type(unix_timestamp)?,
None => timezone.find_current_local_time_type()?,
};
let seconds = local_time_type.ut_offset();
Ok(UtcOffset::from_whole_seconds(seconds).expect("tz-rs returns valid utc offset seconds"))
}