Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 8 additions & 31 deletions zoneinfo/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,10 @@ pub struct CompiledTransitions {
///
/// This is used in the case where a time predates a transition time.
pub initial_record: LocalTimeRecord,

/// The full set of calculated time zone transitions
pub transitions: BTreeSet<Transition>,

/// The POSIX time zone string
///
/// This string should be used to calculate the time zone beyond the last available transition.
Expand Down Expand Up @@ -125,7 +127,7 @@ use crate::{
posix::PosixTimeZone,
types::{QualifiedTimeKind, Time},
tzif::TzifBlockV2,
zone::{ZoneBuildContext, ZoneRecord},
zone::ZoneRecord,
ZoneInfoData,
};

Expand All @@ -145,7 +147,7 @@ impl ZoneInfoCompiler {
if let Some(zone) = self.data.zones.get_mut(target) {
zone.associate_rules(&self.data.rules);
}
self.build_for_zone(target)
self.build_zone_internal(target)
}

pub fn build(&mut self) -> CompiledTransitionsMap {
Expand All @@ -154,45 +156,20 @@ impl ZoneInfoCompiler {
// TODO: Validate and resolve settings here.
let mut zoneinfo = CompiledTransitionsMap::default();
for identifier in self.data.zones.keys() {
let transition_data = self.build_for_zone(identifier);
let transition_data = self.build_zone_internal(identifier);
let _ = zoneinfo.data.insert(identifier.clone(), transition_data);
}
zoneinfo
}

/// Builds the `ZoneInfoTransitionData` for a provided zone identifier (AKA IANA identifier)
///
/// NOTE: Make sure to associate first!
pub(crate) fn build_for_zone(&self, target: &str) -> CompiledTransitions {
/// The internal method for retrieving a zone table and compiling it.
pub(crate) fn build_zone_internal(&self, target: &str) -> CompiledTransitions {
let zone_table = self
.data
.zones
.get(target)
.expect("Invalid identifier provided.");
let initial_record = zone_table.get_first_local_record();
let mut transitions = BTreeSet::default();
if let Some(until_date) = zone_table.get_first_until_date() {
// TODO: Handle max year better.
let range = until_date.date.year..=2037;

let mut build_context = ZoneBuildContext::new(&initial_record);
for year in range {
build_context.update(year, until_date);
zone_table.calculate_transitions_for_year(
year,
&mut build_context,
&mut transitions,
);
}
}

let posix_time_zone = zone_table.get_posix_time_zone();

CompiledTransitions {
initial_record,
transitions,
posix_time_zone,
}
zone_table.compile()
}

pub fn get_posix_time_zone(&mut self, target: &str) -> Option<PosixTimeZone> {
Expand Down
1 change: 0 additions & 1 deletion zoneinfo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ extern crate alloc;

use alloc::string::String;
use parser::ZoneInfoParseError;
use utils::epoch_seconds_for_year;

use hashbrown::HashMap;

Expand Down
104 changes: 25 additions & 79 deletions zoneinfo/src/rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,14 @@

use core::ops::RangeInclusive;

use alloc::{borrow::ToOwned, collections::BTreeSet, string::String, vec, vec::Vec};
use alloc::{borrow::ToOwned, string::String, vec, vec::Vec};

use crate::{
compiler::Transition,
parser::{next_split, ContextParse, LineParseContext, ZoneInfoParseError},
types::{DayOfMonth, Month, QualifiedTime, QualifiedTimeKind, Time, ToYear},
types::{DayOfMonth, Month, QualifiedTime, Time, ToYear},
utils::{self, epoch_seconds_for_epoch_days},
zone::ZoneBuildContext,
};

/// An internal struct for returning the applicable rules found
/// for a year.
#[derive(Debug)]
pub(crate) struct ApplicableRules {
// Preloaded saving of the applicable rules' dst
pub(crate) saving: Time,
pub(crate) transitions: BTreeSet<Transition>,
}

#[derive(Debug)]
pub struct LastRules {
pub standard: Rule,
Expand Down Expand Up @@ -67,83 +56,35 @@ impl Rules {
self.rules.push(rule);
}

// NOTE: To be precise, we will need the savings value active across year boundaries.
pub(crate) fn get_rules_for_year(
&self,
year: i32,
std_offset: &Time,
use_until: i64,
ctx: &mut ZoneBuildContext,
) -> ApplicableRules {
let ordered = self
.rules
pub(crate) fn rules_for_year(&self, year: i32) -> Vec<Rule> {
self.rules
.iter()
.filter_map(|rule| {
if rule.range().contains(&year) {
let transition_time =
rule.transition_time_for_year(year, std_offset, &Time::default());
Some(Transition {
at_time: transition_time,
offset: std_offset.as_secs() + rule.save.as_secs(),
dst: rule.is_dst(),
savings: rule.save,
letter: rule.letter.clone(),
time_type: rule.at.time_kind(),
format: String::new(),
})
} else {
None
}
})
.collect::<BTreeSet<_>>();

let mut saving = ctx.saving;

// We must first build an ordered collection of transitions, as rules
// are unordered, but transition savings values must be evaluated in
// order.
let transitions = ordered
.into_iter()
.filter_map(|mut transition| {
let new_time = match transition.time_type {
QualifiedTimeKind::Local => transition.at_time - saving.as_secs(),
_ => transition.at_time,
};
// Check and see if this transition is valid for use until
if new_time < use_until {
saving = transition.savings;
transition.at_time = new_time;
Some(transition)
} else {
None
}
})
.collect::<BTreeSet<_>>();
.filter(|rule| rule.range().contains(&year))
.cloned()
.collect()
}

ApplicableRules {
saving,
transitions,
}
pub(crate) fn find_initial_transition_letter(&self) -> Option<String> {
let first_rule = self
.rules
.iter()
.find(|rule| rule.save == Time::default())
.expect("A rule must exist with a SAVE = 0");
first_rule.letter.clone()
}

/// A method to search for the last applicable savings for a transition point.
///
/// The last savings needs to be searched for from the beginning because the
/// rules are sorted by start date, not the end date. So, in theory, a rule
/// could be the second rule of ten, but still be active longer then the
/// following eight rules.
pub(crate) fn search_last_active_savings(&self, transition_point: i64) -> Time {
// Reasonable assumption: when searching for a last savings value,
pub(crate) fn search_last_active_rule(&self, transition_point: i64) -> Option<&Rule> {
// Reasonable assumption: when searching for a last Rule,
// we are dealing with an orphan. This means we do not need to check years
// with an upper bound or inside them
let mut rule_savings = (i64::MIN, Time::default());
let mut rule_savings = (i64::MIN, None);
for rule in &self.rules {
let year = rule.to.map(ToYear::to_i32).unwrap_or(i32::from(rule.from));
let epoch_days = epoch_days_for_rule_date(year, rule.in_month, rule.on_date);
let rule_date_in_seconds = epoch_seconds_for_epoch_days(epoch_days);
// But we do want to keep track of the savings.
if rule_date_in_seconds < transition_point && rule_savings.0 < rule_date_in_seconds {
rule_savings = (rule_date_in_seconds, rule.save)
rule_savings = (rule_date_in_seconds, Some(rule))
} else if transition_point < rule_date_in_seconds {
break;
}
Expand Down Expand Up @@ -221,7 +162,12 @@ impl Rule {
}

/// Returns the transition time for that year
fn transition_time_for_year(&self, year: i32, std_offset: &Time, saving: &Time) -> i64 {
pub(crate) fn transition_time_for_year(
&self,
year: i32,
std_offset: &Time,
saving: &Time,
) -> i64 {
let epoch_days = epoch_days_for_rule_date(year, self.in_month, self.on_date);
let epoch_seconds = epoch_seconds_for_epoch_days(epoch_days);
epoch_seconds
Expand Down
9 changes: 0 additions & 9 deletions zoneinfo/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use core::fmt::Write;
use alloc::{borrow::ToOwned, string::String};

use crate::{
compiler::Transition,
parser::{next_split, ContextParse, LineParseContext, TryFromStr, ZoneInfoParseError},
rule::epoch_days_for_rule_date,
utils,
Expand Down Expand Up @@ -49,14 +48,6 @@ pub enum AbbreviationFormat {
}

impl AbbreviationFormat {
pub(crate) fn format_with_transition(&self, transition: &Transition) -> String {
self.format(
transition.offset,
transition.letter.as_deref(),
transition.dst,
)
}

pub fn format(&self, offset: i64, letter: Option<&str>, is_dst: bool) -> String {
match self {
Self::String(s) => s.clone(),
Expand Down
5 changes: 0 additions & 5 deletions zoneinfo/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ pub(crate) fn epoch_days_for_year(y: i32) -> i32 {
+ (y - 1601).div_euclid(400)
}

// TODO: test limits
pub(crate) fn epoch_seconds_for_year(y: i32) -> i64 {
SECONDS_PER_DAY * i64::from(epoch_days_for_year(y))
}

pub(crate) fn epoch_seconds_for_epoch_days(epoch_days: i32) -> i64 {
epoch_days as i64 * SECONDS_PER_DAY
}
Expand Down
Loading
Loading