use crate::{
FmtUtc2k,
Utc2k,
};
use once_cell::sync::OnceCell;
use std::ops::Neg;
use tz::timezone::{
LocalTimeType,
TimeZone,
};
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "local")))]
#[derive(Debug, Clone, Copy, Default, Eq, Hash, PartialEq)]
pub struct LocalOffset {
unixtime: u32,
offset: i32,
}
impl From<u32> for LocalOffset {
#[inline]
fn from(unixtime: u32) -> Self {
let offset = offset(unixtime);
Self { unixtime, offset }
}
}
impl From<Utc2k> for LocalOffset {
#[inline]
fn from(src: Utc2k) -> Self { Self::from(src.unixtime()) }
}
impl Neg for LocalOffset {
type Output = Self;
fn neg(self) -> Self::Output {
if self.offset == i32::MIN {
Self {
unixtime: self.unixtime,
offset: i32::MAX,
}
}
else {
Self {
unixtime: self.unixtime,
offset: self.offset.wrapping_neg(),
}
}
}
}
impl LocalOffset {
#[inline]
#[must_use]
pub fn now() -> Self { Self::from(crate::unixtime()) }
#[must_use]
pub const fn localtime(self) -> u32 {
if self.offset < 0 {
self.unixtime.saturating_sub(self.offset.unsigned_abs())
}
else {
self.unixtime.saturating_add(self.offset.unsigned_abs())
}
}
#[inline]
#[must_use]
pub const fn offset(self) -> i32 { self.offset }
#[inline]
#[must_use]
pub const fn unixtime(self) -> u32 { self.unixtime }
}
impl From<LocalOffset> for i32 {
fn from(src: LocalOffset) -> Self { src.offset }
}
impl From<LocalOffset> for FmtUtc2k {
fn from(src: LocalOffset) -> Self { Self::from(Utc2k::from(src)) }
}
impl From<LocalOffset> for Utc2k {
#[inline]
fn from(src: LocalOffset) -> Self { Self::from(src.localtime()) }
}
static TZ: OnceCell<TimeZone> = OnceCell::new();
fn offset(now: u32) -> i32 {
TZ.get_or_init(|| TimeZone::local().unwrap_or_else(|_| TimeZone::utc()))
.find_local_time_type(i64::from(now))
.map_or(0, LocalTimeType::ut_offset)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn neg() {
let off = LocalOffset::now();
let off2 = -off;
assert_eq!(off, -off2); }
#[test]
fn now() {
let now = crate::unixtime();
assert_eq!(LocalOffset::now().offset, LocalOffset::from(now).offset);
}
}