pub mod iana;
mod offset;
pub mod windows;
#[doc(inline)]
pub use crate::provider::{TimeZone, TimeZoneVariant};
pub use offset::InvalidOffsetError;
pub use offset::UtcOffset;
pub use offset::UtcOffsetCalculator;
pub use offset::UtcOffsets;
#[doc(no_inline)]
pub use iana::IanaParser;
#[doc(no_inline)]
pub use windows::WindowsParser;
use crate::{scaffold::IntoOption, Time};
use core::fmt;
use icu_calendar::{Date, Iso};
pub mod models {
use super::*;
mod private {
pub trait Sealed {}
}
pub trait TimeZoneModel: private::Sealed {
type TimeZoneVariant: IntoOption<TimeZoneVariant> + fmt::Debug + Copy;
type LocalTime: IntoOption<(Date<Iso>, Time)> + fmt::Debug + Copy;
}
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Base {}
impl private::Sealed for Base {}
impl TimeZoneModel for Base {
type TimeZoneVariant = ();
type LocalTime = ();
}
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AtTime {}
impl private::Sealed for AtTime {}
impl TimeZoneModel for AtTime {
type TimeZoneVariant = ();
type LocalTime = (Date<Iso>, Time);
}
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Full {}
impl private::Sealed for Full {}
impl TimeZoneModel for Full {
type TimeZoneVariant = TimeZoneVariant;
type LocalTime = (Date<Iso>, Time);
}
}
#[derive(Debug, PartialEq, Eq)]
#[allow(clippy::exhaustive_structs)] pub struct TimeZoneInfo<Model: models::TimeZoneModel> {
time_zone_id: TimeZone,
offset: Option<UtcOffset>,
local_time: Model::LocalTime,
zone_variant: Model::TimeZoneVariant,
}
impl<Model: models::TimeZoneModel> Clone for TimeZoneInfo<Model> {
fn clone(&self) -> Self {
*self
}
}
impl<Model: models::TimeZoneModel> Copy for TimeZoneInfo<Model> {}
impl<Model: models::TimeZoneModel> TimeZoneInfo<Model> {
pub fn time_zone_id(self) -> TimeZone {
self.time_zone_id
}
pub fn offset(self) -> Option<UtcOffset> {
self.offset
}
}
impl<Model> TimeZoneInfo<Model>
where
Model: models::TimeZoneModel<LocalTime = (Date<Iso>, Time)>,
{
pub fn local_time(self) -> (Date<Iso>, Time) {
self.local_time
}
}
impl<Model> TimeZoneInfo<Model>
where
Model: models::TimeZoneModel<TimeZoneVariant = TimeZoneVariant>,
{
pub fn zone_variant(self) -> TimeZoneVariant {
self.zone_variant
}
}
impl TimeZone {
pub const fn with_offset(self, offset: Option<UtcOffset>) -> TimeZoneInfo<models::Base> {
TimeZoneInfo {
offset,
time_zone_id: self,
local_time: (),
zone_variant: (),
}
}
pub const fn without_offset(self) -> TimeZoneInfo<models::Base> {
TimeZoneInfo {
offset: None,
time_zone_id: self,
local_time: (),
zone_variant: (),
}
}
}
impl TimeZoneInfo<models::Base> {
pub const fn unknown() -> Self {
TimeZone::unknown().with_offset(None)
}
pub const fn utc() -> Self {
TimeZone(tinystr::tinystr!(8, "utc")).with_offset(Some(UtcOffset::zero()))
}
pub const fn at_time(self, local_time: (Date<Iso>, Time)) -> TimeZoneInfo<models::AtTime> {
TimeZoneInfo {
offset: self.offset,
time_zone_id: self.time_zone_id,
local_time,
zone_variant: (),
}
}
}
impl TimeZoneInfo<models::AtTime> {
pub const fn with_zone_variant(
self,
zone_variant: TimeZoneVariant,
) -> TimeZoneInfo<models::Full> {
TimeZoneInfo {
offset: self.offset,
time_zone_id: self.time_zone_id,
local_time: self.local_time,
zone_variant,
}
}
pub fn infer_zone_variant(
self,
calculator: &UtcOffsetCalculator,
) -> TimeZoneInfo<models::Full> {
let Some(offset) = self.offset else {
return TimeZone::unknown()
.with_offset(self.offset)
.at_time(self.local_time)
.with_zone_variant(TimeZoneVariant::Standard);
};
let Some(zone_variant) = calculator
.compute_offsets_from_time_zone(self.time_zone_id, self.local_time)
.and_then(|os| {
if os.standard == offset {
Some(TimeZoneVariant::Standard)
} else if os.daylight == Some(offset) {
Some(TimeZoneVariant::Daylight)
} else {
None
}
})
else {
return TimeZone::unknown()
.with_offset(self.offset)
.at_time(self.local_time)
.with_zone_variant(TimeZoneVariant::Standard);
};
self.with_zone_variant(zone_variant)
}
}
impl TimeZoneVariant {
pub const fn from_rearguard_isdst(isdst: bool) -> Self {
if isdst {
TimeZoneVariant::Daylight
} else {
TimeZoneVariant::Standard
}
}
}