1use crate::posix::{year_of, PosixTz};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct ZoneType {
12 pub abbrev: &'static str,
14 pub offset: i32,
16 pub is_dst: bool,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Transition {
23 pub when: i64,
25 pub type_idx: usize,
27 pub is_std: bool,
29 pub is_ut: bool,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct LeapSecond {
36 pub when: i64,
38 pub correction: i32,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct RangeTransition {
49 pub when: i64,
51 pub zone_type: ZoneType,
53}
54
55#[derive(Debug, Clone, Copy)]
61pub struct Zone {
62 pub(crate) name: &'static str,
63 pub(crate) version: u8,
64 pub(crate) types: &'static [ZoneType],
65 pub(crate) transitions: &'static [Transition],
66 pub(crate) leap_seconds: &'static [LeapSecond],
67 pub(crate) extend: Option<PosixTz<'static>>,
68 pub(crate) extend_raw: &'static str,
69}
70
71impl Zone {
72 pub fn name(&self) -> &'static str {
74 self.name
75 }
76
77 pub fn version(&self) -> u8 {
79 self.version
80 }
81
82 pub fn types(&self) -> &'static [ZoneType] {
84 self.types
85 }
86
87 pub fn transitions(&self) -> &'static [Transition] {
89 self.transitions
90 }
91
92 pub fn leap_seconds(&self) -> &'static [LeapSecond] {
94 self.leap_seconds
95 }
96
97 pub fn extend(&self) -> Option<&PosixTz<'static>> {
99 self.extend.as_ref()
100 }
101
102 pub fn extend_raw(&self) -> &'static str {
104 self.extend_raw
105 }
106
107 pub fn type_count(&self) -> usize {
109 self.types.len()
110 }
111
112 pub fn type_at(&self, i: usize) -> ZoneType {
114 self.types[i]
115 }
116
117 pub fn lookup(&self, unix: i64) -> ZoneType {
122 let tr = self.transitions;
123 if tr.is_empty() {
124 return self.types.first().copied().unwrap_or(ZoneType {
125 abbrev: "UTC",
126 offset: 0,
127 is_dst: false,
128 });
129 }
130
131 let lo = tr.partition_point(|t| t.when <= unix);
133
134 if lo == 0 {
135 for zt in self.types {
137 if !zt.is_dst {
138 return *zt;
139 }
140 }
141 return self.types[0];
142 }
143
144 if lo == tr.len() {
145 if let Some(ext) = &self.extend {
146 let (abbrev, offset, is_dst) = ext.lookup(unix);
147 return ZoneType {
148 abbrev,
149 offset,
150 is_dst,
151 };
152 }
153 }
154
155 self.types[tr[lo - 1].type_idx]
156 }
157
158 pub fn transitions_for_range(&self, start_unix: i64, end_unix: i64) -> RangeIter {
162 let last_stored = self.transitions.last().map(|t| t.when).unwrap_or(i64::MIN);
163 let generate = self.extend.map(|e| e.has_dst()).unwrap_or(false);
164 RangeIter {
165 zone: *self,
166 start_unix,
167 end_unix,
168 stored_idx: 0,
169 stored_done: false,
170 last_stored,
171 generate,
172 year: year_of(start_unix),
173 end_year: year_of(end_unix),
174 pending: [None, None],
175 pending_i: 0,
176 }
177 }
178}
179
180pub struct RangeIter {
182 zone: Zone,
183 start_unix: i64,
184 end_unix: i64,
185 stored_idx: usize,
186 stored_done: bool,
187 last_stored: i64,
188 generate: bool,
189 year: i32,
190 end_year: i32,
191 pending: [Option<RangeTransition>; 2],
192 pending_i: usize,
193}
194
195impl Iterator for RangeIter {
196 type Item = RangeTransition;
197
198 fn next(&mut self) -> Option<RangeTransition> {
199 if !self.stored_done {
201 let tr = self.zone.transitions;
202 while self.stored_idx < tr.len() {
203 let t = tr[self.stored_idx];
204 if t.when >= self.end_unix {
205 self.stored_done = true;
206 break;
207 }
208 self.stored_idx += 1;
209 if t.when >= self.start_unix {
210 return Some(RangeTransition {
211 when: t.when,
212 zone_type: self.zone.types[t.type_idx],
213 });
214 }
215 }
216 self.stored_done = true;
217 }
218
219 if !self.generate {
221 return None;
222 }
223 let ext = self.zone.extend.expect("generate implies extend");
224 loop {
225 while self.pending_i < 2 {
227 let item = self.pending[self.pending_i].take();
228 self.pending_i += 1;
229 if let Some(t) = item {
230 return Some(t);
231 }
232 }
233
234 if self.year > self.end_year {
235 return None;
236 }
237
238 let year = self.year;
240 self.year += 1;
241 self.pending = [None, None];
242 self.pending_i = 0;
243
244 if let Some((dst_start, dst_end)) = ext.transitions_for_year(year) {
245 let dst_type = ZoneType {
246 abbrev: ext.dst_abbrev,
247 offset: ext.dst_offset,
248 is_dst: true,
249 };
250 let std_type = ZoneType {
251 abbrev: ext.std_abbrev,
252 offset: ext.std_offset,
253 is_dst: false,
254 };
255 let mut buf: [Option<RangeTransition>; 2] = [None, None];
256 let mut n = 0;
257 if self.in_range(dst_start) {
258 buf[n] = Some(RangeTransition {
259 when: dst_start,
260 zone_type: dst_type,
261 });
262 n += 1;
263 }
264 if self.in_range(dst_end) {
265 buf[n] = Some(RangeTransition {
266 when: dst_end,
267 zone_type: std_type,
268 });
269 n += 1;
270 }
271 if n == 2 && buf[0].map(|t| t.when) > buf[1].map(|t| t.when) {
273 buf.swap(0, 1);
274 }
275 self.pending = buf;
276 }
277 }
278 }
279}
280
281impl RangeIter {
282 fn in_range(&self, when: i64) -> bool {
283 when >= self.start_unix && when < self.end_unix && when > self.last_stored
284 }
285}