diff --git a/provider/src/experimental_tzif/datagen.rs b/provider/src/experimental_tzif/datagen.rs index bfbc30c4e..f91b85528 100644 --- a/provider/src/experimental_tzif/datagen.rs +++ b/provider/src/experimental_tzif/datagen.rs @@ -10,6 +10,8 @@ impl From<&zoneinfo_rs::tzif::LocalTimeRecord> for LocalTimeRecord { fn from(value: &zoneinfo_rs::tzif::LocalTimeRecord) -> Self { Self { offset: value.offset, + is_dst: value.is_dst, + index: value.index, } } } @@ -22,14 +24,15 @@ impl ZeroTzif<'_> { let mapped_local_records: Vec = tzif.local_time_types.iter().map(Into::into).collect(); let types = ZeroVec::alloc_from_slice(&mapped_local_records); - // TODO: handle this much better. let posix = PosixZone::from(&data.posix_time_zone); + let designations = ZeroVec::alloc_from_slice(&tzif.designations); Self { transitions, transition_types, types, posix, + designations, } } } diff --git a/provider/src/experimental_tzif/mod.rs b/provider/src/experimental_tzif/mod.rs index a51142437..43b18e918 100644 --- a/provider/src/experimental_tzif/mod.rs +++ b/provider/src/experimental_tzif/mod.rs @@ -36,6 +36,8 @@ impl ZoneInfoProvider<'_> { } } +/// A zero-copy TZif data struct for time zone data provided by IANA's tzdb (also known +/// as the Olsen database). #[zerovec::make_varule(ZeroTzifULE)] #[derive(PartialEq, Debug, Clone)] #[zerovec::skip_derive(Ord)] @@ -47,11 +49,16 @@ impl ZoneInfoProvider<'_> { #[cfg_attr(feature = "datagen", zerovec::derive(Serialize))] #[cfg_attr(feature = "datagen", databake(path = timezone_provider::experimental_tzif))] pub struct ZeroTzif<'data> { + /// The time in UTC epoch seconds where a transition occurs. pub transitions: ZeroVec<'data, i64>, + /// An index identify the local time type for the corresponding transition. pub transition_types: ZeroVec<'data, u8>, - // NOTE: zoneinfo64 does a fun little bitmap str + /// The available local time types pub types: ZeroVec<'data, LocalTimeRecord>, + /// The POSIX time zone data for this TZif pub posix: PosixZone, + /// The available time zone designations. + pub designations: ZeroVec<'data, char>, } #[zerovec::make_ule(LocalTimeRecordULE)] @@ -62,5 +69,10 @@ pub struct ZeroTzif<'data> { )] #[cfg_attr(feature = "datagen", databake(path = timezone_provider::experimental_tzif))] pub struct LocalTimeRecord { + /// The offset from UTC in seconds pub offset: i64, + /// Whether the current local time type is considered DST or not + pub(crate) is_dst: bool, + /// The index into the designations array. + pub index: u8, } diff --git a/zoneinfo/src/tzif.rs b/zoneinfo/src/tzif.rs index 02af42433..483080176 100644 --- a/zoneinfo/src/tzif.rs +++ b/zoneinfo/src/tzif.rs @@ -19,37 +19,51 @@ pub struct TzifBlockV2 { pub transition_times: Vec, pub transition_types: Vec, pub local_time_types: Vec, // TODO: Add other fields as needed + pub designations: Vec, } impl TzifBlockV2 { pub fn from_transition_data(data: &CompiledTransitions) -> Self { let mut local_time_set = IndexSet::new(); + let mut designation_set = DesignationSet::default(); + + let index = designation_set + .insert_and_retrieve_index(data.initial_record.designation.chars().collect()); local_time_set.insert(LocalTimeRecord { offset: data.initial_record.offset, is_dst: data.initial_record.saving.as_secs() != 0, + index: index as u8, }); let mut transition_times = Vec::default(); let mut transition_types = Vec::default(); for transition in &data.transitions { - let _ = local_time_set.insert(LocalTimeRecord { + let index = + designation_set.insert_and_retrieve_index(transition.format.chars().collect()); + let local_time_record = LocalTimeRecord { offset: transition.offset, is_dst: transition.dst, - }); + index: index as u8, + }; transition_times.push(transition.at_time); - for (index, time_type) in local_time_set.iter().enumerate() { - if time_type.offset == transition.offset { - transition_types.push(index as u8); + match local_time_set.get_index_of(&local_time_record) { + Some(i) => transition_types.push(i as u8), + None => { + let _ = local_time_set.insert(local_time_record); + transition_types.push(local_time_set.len() as u8 - 1); } } } let local_time_types = local_time_set.into_iter().collect::>(); + let designations = designation_set.to_vec(); + Self { transition_times, transition_types, local_time_types, + designations, } } } @@ -59,4 +73,44 @@ impl TzifBlockV2 { pub struct LocalTimeRecord { pub offset: i64, pub is_dst: bool, + pub index: u8, +} + +#[derive(Debug, Default, Clone)] +pub struct DesignationSet { + pub designations: IndexSet>, + pub indices: Vec, + pub next_index: usize, +} + +impl DesignationSet { + // Inserts the a designation if it doesn't exist, returns the designation index. + pub fn insert_and_retrieve_index(&mut self, mut designation: Vec) -> usize { + // Add a null character + designation.push('\0'); + // Check if the designation already exists. + let Some(index) = self.designations.get_index_of(&designation) else { + let designation_len = designation.len(); + + // Insert the new designation into the set + let _ = self.designations.insert(designation); + + // Get the designation index and cache it. + let designation_index = self.next_index; + self.indices.push(designation_index); + + // Calculate the next index to give out. + self.next_index += designation_len; + + return designation_index; + }; + self.indices[index] + } + + pub fn to_vec(self) -> Vec { + self.designations + .into_iter() + .collect::>>() + .concat() + } }