use crate::posix::{year_of, PosixTz};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ZoneType {
pub abbrev: &'static str,
pub offset: i32,
pub is_dst: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Transition {
pub when: i64,
pub type_idx: usize,
pub is_std: bool,
pub is_ut: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LeapSecond {
pub when: i64,
pub correction: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RangeTransition {
pub when: i64,
pub zone_type: ZoneType,
}
#[derive(Debug, Clone, Copy)]
pub struct Zone {
pub(crate) name: &'static str,
pub(crate) version: u8,
pub(crate) types: &'static [ZoneType],
pub(crate) transitions: &'static [Transition],
pub(crate) leap_seconds: &'static [LeapSecond],
pub(crate) extend: Option<PosixTz<'static>>,
pub(crate) extend_raw: &'static str,
}
impl Zone {
pub fn name(&self) -> &'static str {
self.name
}
pub fn version(&self) -> u8 {
self.version
}
pub fn types(&self) -> &'static [ZoneType] {
self.types
}
pub fn transitions(&self) -> &'static [Transition] {
self.transitions
}
pub fn leap_seconds(&self) -> &'static [LeapSecond] {
self.leap_seconds
}
pub fn extend(&self) -> Option<&PosixTz<'static>> {
self.extend.as_ref()
}
pub fn extend_raw(&self) -> &'static str {
self.extend_raw
}
pub fn type_count(&self) -> usize {
self.types.len()
}
pub fn type_at(&self, i: usize) -> ZoneType {
self.types[i]
}
pub fn lookup(&self, unix: i64) -> ZoneType {
let tr = self.transitions;
if tr.is_empty() {
return self.types.first().copied().unwrap_or(ZoneType {
abbrev: "UTC",
offset: 0,
is_dst: false,
});
}
let lo = tr.partition_point(|t| t.when <= unix);
if lo == 0 {
for zt in self.types {
if !zt.is_dst {
return *zt;
}
}
return self.types[0];
}
if lo == tr.len() {
if let Some(ext) = &self.extend {
let (abbrev, offset, is_dst) = ext.lookup(unix);
return ZoneType {
abbrev,
offset,
is_dst,
};
}
}
self.types[tr[lo - 1].type_idx]
}
pub fn transitions_for_range(&self, start_unix: i64, end_unix: i64) -> RangeIter {
let last_stored = self.transitions.last().map(|t| t.when).unwrap_or(i64::MIN);
let generate = self.extend.map(|e| e.has_dst()).unwrap_or(false);
RangeIter {
zone: *self,
start_unix,
end_unix,
stored_idx: 0,
stored_done: false,
last_stored,
generate,
year: year_of(start_unix),
end_year: year_of(end_unix),
pending: [None, None],
pending_i: 0,
}
}
}
pub struct RangeIter {
zone: Zone,
start_unix: i64,
end_unix: i64,
stored_idx: usize,
stored_done: bool,
last_stored: i64,
generate: bool,
year: i32,
end_year: i32,
pending: [Option<RangeTransition>; 2],
pending_i: usize,
}
impl Iterator for RangeIter {
type Item = RangeTransition;
fn next(&mut self) -> Option<RangeTransition> {
if !self.stored_done {
let tr = self.zone.transitions;
while self.stored_idx < tr.len() {
let t = tr[self.stored_idx];
if t.when >= self.end_unix {
self.stored_done = true;
break;
}
self.stored_idx += 1;
if t.when >= self.start_unix {
return Some(RangeTransition {
when: t.when,
zone_type: self.zone.types[t.type_idx],
});
}
}
self.stored_done = true;
}
if !self.generate {
return None;
}
let ext = self.zone.extend.expect("generate implies extend");
loop {
while self.pending_i < 2 {
let item = self.pending[self.pending_i].take();
self.pending_i += 1;
if let Some(t) = item {
return Some(t);
}
}
if self.year > self.end_year {
return None;
}
let year = self.year;
self.year += 1;
self.pending = [None, None];
self.pending_i = 0;
if let Some((dst_start, dst_end)) = ext.transitions_for_year(year) {
let dst_type = ZoneType {
abbrev: ext.dst_abbrev,
offset: ext.dst_offset,
is_dst: true,
};
let std_type = ZoneType {
abbrev: ext.std_abbrev,
offset: ext.std_offset,
is_dst: false,
};
let mut buf: [Option<RangeTransition>; 2] = [None, None];
let mut n = 0;
if self.in_range(dst_start) {
buf[n] = Some(RangeTransition {
when: dst_start,
zone_type: dst_type,
});
n += 1;
}
if self.in_range(dst_end) {
buf[n] = Some(RangeTransition {
when: dst_end,
zone_type: std_type,
});
n += 1;
}
if n == 2 && buf[0].map(|t| t.when) > buf[1].map(|t| t.when) {
buf.swap(0, 1);
}
self.pending = buf;
}
}
}
}
impl RangeIter {
fn in_range(&self, when: i64) -> bool {
when >= self.start_unix && when < self.end_unix && when > self.last_stored
}
}