#[cfg(any(target_os = "linux", unsound_local_offset))]
use core::convert::TryInto;
#[cfg(any(target_os = "linux", unsound_local_offset))]
use core::mem::MaybeUninit;
use crate::{OffsetDateTime, UtcOffset};
#[cfg(not(any(target_os = "linux", unsound_local_offset)))]
#[allow(clippy::missing_const_for_fn)]
pub(super) fn local_offset_at(_datetime: OffsetDateTime) -> Option<UtcOffset> {
None
}
#[cfg(any(target_os = "linux", unsound_local_offset))]
unsafe fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
extern "C" {
#[cfg_attr(target_os = "netbsd", link_name = "__tzset50")]
fn tzset();
}
#[allow(clippy::useless_conversion)]
let timestamp = timestamp.try_into().ok()?;
let mut tm = MaybeUninit::uninit();
tzset();
let tm_ptr = libc::localtime_r(×tamp, tm.as_mut_ptr());
if tm_ptr.is_null() {
None
} else {
Some(tm.assume_init())
}
}
#[cfg(any(target_os = "linux", unsound_local_offset))]
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
let seconds: i32 = tm.tm_gmtoff.try_into().ok()?;
UtcOffset::from_hms(
(seconds / 3_600) as _,
((seconds / 60) % 60) as _,
(seconds % 60) as _,
)
.ok()
}
#[cfg(unsound_local_offset)]
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
use core::convert::TryFrom;
use crate::Date;
let mut tm = tm;
if tm.tm_sec == 60 {
tm.tm_sec = 59;
}
let local_timestamp =
Date::from_ordinal_date(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1)
.ok()?
.with_hms(
tm.tm_hour.try_into().ok()?,
tm.tm_min.try_into().ok()?,
tm.tm_sec.try_into().ok()?,
)
.ok()?
.assume_utc()
.unix_timestamp();
let diff_secs: i32 = (local_timestamp - datetime.unix_timestamp())
.try_into()
.ok()?;
UtcOffset::from_hms(
(diff_secs / 3_600) as _,
((diff_secs / 60) % 60) as _,
(diff_secs % 60) as _,
)
.ok()
}
#[cfg(target_os = "linux")]
fn process_is_single_threaded() -> Option<bool> {
std::fs::read_dir("/proc/self/task")
.ok()
.map(|mut tasks| tasks.nth(1).is_none())
}
#[cfg(any(target_os = "linux", unsound_local_offset))]
pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
if !cfg!(unsound_local_offset) && !matches!(process_is_single_threaded(), Some(true)) {
return None;
}
let tm = unsafe { timestamp_to_tm(datetime.unix_timestamp())? };
tm_to_offset(tm)
}