use crate::posix_tz::intermediate::ParsedTz;
use crate::posix_tz::parser::Date;
use crate::posix_tz::{Error, ParseError};
use crate::timezone_impl::TzOffset;
use crate::Tz;
use time::{OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
pub enum TzOrExpandedOffset<'a> {
Expanded(ExpandedTzOffset<'a>),
Tz(TzOffset),
}
pub struct ExpandedTzOffset<'a> {
mode: ExpandedMode<'a>,
is_dst: bool,
}
impl<'a> crate::Offset for ExpandedTzOffset<'a> {
fn to_utc(&self) -> UtcOffset {
self.mode.offset
}
fn name(&self) -> &str {
self.mode.name
}
fn is_dst(&self) -> bool {
self.is_dst
}
}
#[derive(Copy, Clone)]
pub struct ExpandedMode<'a> {
name: &'a str,
offset: UtcOffset, }
pub struct Rule {
start: (Date, Time),
end: (Date, Time),
}
pub struct ExpandedTz<'a> {
std: ExpandedMode<'a>,
dst: Option<ExpandedMode<'a>>,
rule: Option<Rule>,
}
impl<'a> ExpandedTz<'a> {
pub fn get_offset_utc(
&self,
date_time: &OffsetDateTime,
) -> Result<ExpandedTzOffset<'a>, Error> {
match self.dst {
None => Ok(ExpandedTzOffset {
mode: self.std,
is_dst: false,
}),
Some(dst) => match &self.rule {
None => {
use crate::Offset;
use crate::TimeZone;
let timezone = crate::timezones::db::america::NEW_YORK;
let tz_offset = timezone.get_offset_utc(date_time);
Ok(ExpandedTzOffset {
mode: if tz_offset.is_dst() { dst } else { self.std },
is_dst: tz_offset.is_dst(),
})
}
Some(rule) => {
let start = PrimitiveDateTime::new(
rule.start.0.to_date(date_time.year())?,
rule.start.1,
)
.assume_offset(self.std.offset);
let end =
PrimitiveDateTime::new(rule.end.0.to_date(date_time.year())?, rule.end.1)
.assume_offset(dst.offset);
if date_time >= &start && date_time < &end {
Ok(ExpandedTzOffset {
mode: dst,
is_dst: true,
})
} else {
Ok(ExpandedTzOffset {
mode: self.std,
is_dst: false,
})
}
}
},
}
}
}
pub enum TzOrExpanded<'a> {
Tz(&'static Tz),
Expanded(ExpandedTz<'a>),
}
pub fn parse_abstract(input: ParsedTz) -> Result<TzOrExpanded, ParseError> {
match input {
ParsedTz::Existing(v) => Ok(TzOrExpanded::Tz(v)),
ParsedTz::Expanded((std, dst)) => {
let tmp = -std.offset.to_seconds();
let std_offset =
UtcOffset::from_whole_seconds(tmp).map_err(ParseError::ComponentRange)?;
let std = ExpandedMode {
name: std.name,
offset: std_offset,
};
let (dst, rule) = match dst {
None => (None, None),
Some(v) => {
let offset = v.offset.map(|v| -v.to_seconds()).unwrap_or(tmp + 3600);
let dst_offset = UtcOffset::from_whole_seconds(offset)
.map_err(ParseError::ComponentRange)?;
let rule = match &v.rule {
None => None,
Some(v) => {
let default =
unsafe { time::Time::from_hms(2, 0, 0).unwrap_unchecked() };
let start_time =
v.start.1.as_ref().map(|v| v.to_time()).unwrap_or(default);
let end_time = v.end.1.as_ref().map(|v| v.to_time()).unwrap_or(default);
Some(Rule {
start: (v.start.0, start_time),
end: (v.end.0, end_time),
})
}
};
(
Some(ExpandedMode {
name: v.name,
offset: dst_offset,
}),
rule,
)
}
};
Ok(TzOrExpanded::Expanded(ExpandedTz { std, dst, rule }))
}
}
}