1use crate::datetime::*;
4use crate::timezone::TransitionRule;
5use crate::Result;
6
7#[cfg(feature = "alloc")]
8use alloc::vec::Vec;
9
10#[derive(Debug, Copy, Clone, PartialEq)]
12pub enum FoundDateTimeKind {
13 Normal(DateTime),
15 Skipped {
21 before_transition: DateTime,
23 after_transition: DateTime,
25 },
26}
27
28#[cfg(feature = "alloc")]
33#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
34#[derive(Debug, Default, Clone, PartialEq)]
35pub struct FoundDateTimeList(Vec<FoundDateTimeKind>);
36
37#[cfg(feature = "alloc")]
38impl FoundDateTimeList {
39 pub fn unique(&self) -> Option<DateTime> {
41 match *self.0.as_slice() {
42 [FoundDateTimeKind::Normal(date_time)] => Some(date_time),
43 _ => None,
44 }
45 }
46
47 pub fn earliest(&self) -> Option<DateTime> {
49 match *self.0.first()? {
51 FoundDateTimeKind::Normal(date_time) => Some(date_time),
52 FoundDateTimeKind::Skipped { before_transition, .. } => Some(before_transition),
53 }
54 }
55
56 pub fn latest(&self) -> Option<DateTime> {
58 match *self.0.last()? {
60 FoundDateTimeKind::Normal(date_time) => Some(date_time),
61 FoundDateTimeKind::Skipped { after_transition, .. } => Some(after_transition),
62 }
63 }
64
65 pub fn into_inner(self) -> Vec<FoundDateTimeKind> {
67 self.0
68 }
69}
70
71#[derive(Debug, PartialEq)]
73pub struct FoundDateTimeListRefMut<'a> {
74 buf: &'a mut [Option<FoundDateTimeKind>],
76 current_index: usize,
78 count: usize,
80}
81
82impl<'a> FoundDateTimeListRefMut<'a> {
83 pub fn new(buf: &'a mut [Option<FoundDateTimeKind>]) -> Self {
85 Self { buf, current_index: 0, count: 0 }
86 }
87
88 pub fn unique(&self) -> Option<DateTime> {
90 let mut iter = self.data().iter().flatten();
91 let first = iter.next();
92 let second = iter.next();
93
94 match (first, second) {
95 (Some(FoundDateTimeKind::Normal(date_time)), None) => Some(*date_time),
96 _ => None,
97 }
98 }
99
100 pub fn earliest(&self) -> Option<DateTime> {
102 match *self.data().iter().flatten().next()? {
104 FoundDateTimeKind::Normal(date_time) => Some(date_time),
105 FoundDateTimeKind::Skipped { before_transition, .. } => Some(before_transition),
106 }
107 }
108
109 pub fn latest(&self) -> Option<DateTime> {
111 match *self.data().iter().flatten().next_back()? {
113 FoundDateTimeKind::Normal(date_time) => Some(date_time),
114 FoundDateTimeKind::Skipped { after_transition, .. } => Some(after_transition),
115 }
116 }
117
118 pub fn data(&self) -> &[Option<FoundDateTimeKind>] {
120 &self.buf[..self.current_index]
121 }
122
123 pub fn count(&self) -> usize {
125 self.count
126 }
127
128 pub fn is_exhaustive(&self) -> bool {
130 self.current_index == self.count
131 }
132}
133
134pub(super) trait DateTimeList {
136 fn push(&mut self, found_date_time: FoundDateTimeKind);
138}
139
140#[cfg(feature = "alloc")]
141impl DateTimeList for FoundDateTimeList {
142 fn push(&mut self, found_date_time: FoundDateTimeKind) {
143 self.0.push(found_date_time);
144 }
145}
146
147impl<'a> DateTimeList for FoundDateTimeListRefMut<'a> {
148 fn push(&mut self, found_date_time: FoundDateTimeKind) {
149 if let Some(x) = self.buf.get_mut(self.current_index) {
150 *x = Some(found_date_time);
151 self.current_index += 1
152 }
153
154 self.count += 1;
155 }
156}
157
158#[allow(clippy::too_many_arguments)]
173pub(super) fn find_date_time(
174 found_date_time_list: &mut impl DateTimeList,
175 year: i32,
176 month: u8,
177 month_day: u8,
178 hour: u8,
179 minute: u8,
180 second: u8,
181 nanoseconds: u32,
182 time_zone_ref: TimeZoneRef,
183) -> Result<()> {
184 let transitions = time_zone_ref.transitions();
185 let local_time_types = time_zone_ref.local_time_types();
186 let extra_rule = time_zone_ref.extra_rule();
187
188 if transitions.is_empty() && extra_rule.is_none() {
189 let date_time = DateTime::new(year, month, month_day, hour, minute, second, nanoseconds, local_time_types[0])?;
190 found_date_time_list.push(FoundDateTimeKind::Normal(date_time));
191 return Ok(());
192 }
193
194 let new_datetime = |local_time_type, unix_time| DateTime { year, month, month_day, hour, minute, second, local_time_type, unix_time, nanoseconds };
195
196 check_date_time_inputs(year, month, month_day, hour, minute, second, nanoseconds)?;
197 let utc_unix_time = unix_time(year, month, month_day, hour, minute, second);
198
199 if !transitions.is_empty() {
201 let mut last_cached_time = None;
202
203 let mut get_time = |local_time_type_index: usize| {
204 match last_cached_time {
205 Some((index, value)) if index == local_time_type_index => Result::Ok(value),
206 _ => {
207 let unix_time = utc_unix_time - local_time_types[local_time_type_index].ut_offset() as i64;
209 let unix_leap_time = time_zone_ref.unix_time_to_unix_leap_time(unix_time)?;
210
211 last_cached_time = Some((local_time_type_index, (unix_time, unix_leap_time)));
212 Result::Ok((unix_time, unix_leap_time))
213 }
214 }
215 };
216
217 let mut previous_transition_unix_leap_time = i64::MIN;
218 let mut previous_local_time_type_index = 0;
219
220 for (index, transition) in transitions.iter().enumerate() {
222 let local_time_type_before = local_time_types[previous_local_time_type_index];
223 let (unix_time_before, unix_leap_time_before) = get_time(previous_local_time_type_index)?;
224
225 if previous_transition_unix_leap_time <= unix_leap_time_before && unix_leap_time_before < transition.unix_leap_time() {
226 UtcDateTime::check_unix_time(unix_time_before)?;
227 found_date_time_list.push(FoundDateTimeKind::Normal(new_datetime(local_time_type_before, unix_time_before)));
228 } else {
229 if index < transitions.len() - 1 || extra_rule.is_some() {
231 let local_time_type_after = local_time_types[transition.local_time_type_index()];
232 let (_, unix_leap_time_after) = get_time(transition.local_time_type_index())?;
233
234 if unix_leap_time_before >= transition.unix_leap_time() && unix_leap_time_after < transition.unix_leap_time() {
236 let transition_unix_time = time_zone_ref.unix_leap_time_to_unix_time(transition.unix_leap_time())?;
237
238 found_date_time_list.push(FoundDateTimeKind::Skipped {
239 before_transition: DateTime::from_timespec_and_local(transition_unix_time, nanoseconds, local_time_type_before)?,
240 after_transition: DateTime::from_timespec_and_local(transition_unix_time, nanoseconds, local_time_type_after)?,
241 });
242 }
243 }
244 }
245
246 previous_transition_unix_leap_time = transition.unix_leap_time();
247 previous_local_time_type_index = transition.local_time_type_index();
248 }
249 }
250
251 match extra_rule {
253 None => {}
254 Some(TransitionRule::Fixed(local_time_type)) => {
255 let unix_time = utc_unix_time - local_time_type.ut_offset() as i64;
257
258 let condition = match transitions.last() {
259 Some(last_transition) => unix_time >= time_zone_ref.unix_leap_time_to_unix_time(last_transition.unix_leap_time())?,
260 None => true,
261 };
262
263 if condition {
264 UtcDateTime::check_unix_time(unix_time)?;
265 found_date_time_list.push(FoundDateTimeKind::Normal(new_datetime(*local_time_type, unix_time)));
266 }
267 }
268 Some(TransitionRule::Alternate(alternate_time)) => {
269 let std_ut_offset = alternate_time.std().ut_offset() as i64;
270 let dst_ut_offset = alternate_time.dst().ut_offset() as i64;
271
272 let unix_time_std = utc_unix_time - std_ut_offset;
274 let unix_time_dst = utc_unix_time - dst_ut_offset;
275
276 let dst_start_time_in_utc = alternate_time.dst_start_time() as i64 - std_ut_offset;
277 let dst_end_time_in_utc = alternate_time.dst_end_time() as i64 - dst_ut_offset;
278
279 UtcDateTime::check_unix_time(unix_time_std)?;
281 UtcDateTime::check_unix_time(unix_time_dst)?;
282
283 if !(i32::MIN + 2..=i32::MAX - 2).contains(&year) {
285 return Err(OutOfRangeError("out of range date time").into());
286 }
287
288 let mut additional_transition_times = [
292 alternate_time.dst_start().unix_time(year - 1, dst_start_time_in_utc),
293 alternate_time.dst_end().unix_time(year - 1, dst_end_time_in_utc),
294 alternate_time.dst_start().unix_time(year, dst_start_time_in_utc),
295 alternate_time.dst_end().unix_time(year, dst_end_time_in_utc),
296 alternate_time.dst_start().unix_time(year + 1, dst_start_time_in_utc),
297 alternate_time.dst_end().unix_time(year + 1, dst_end_time_in_utc),
298 i64::MAX,
299 ];
300
301 let sorted = additional_transition_times.windows(2).all(|x| x[0] <= x[1]);
303
304 if !sorted {
305 for chunk in additional_transition_times.chunks_exact_mut(2) {
306 chunk.swap(0, 1);
307 }
308 };
309
310 let transition_start = (alternate_time.std(), alternate_time.dst(), unix_time_std, unix_time_dst);
311 let transition_end = (alternate_time.dst(), alternate_time.std(), unix_time_dst, unix_time_std);
312
313 let additional_transitions = if sorted {
314 [&transition_start, &transition_end, &transition_start, &transition_end, &transition_start, &transition_end, &transition_start]
315 } else {
316 [&transition_end, &transition_start, &transition_end, &transition_start, &transition_end, &transition_start, &transition_end]
317 };
318
319 let mut previous_transition_unix_time = match transitions.last() {
320 Some(last_transition) => time_zone_ref.unix_leap_time_to_unix_time(last_transition.unix_leap_time())?,
321 None => i64::MIN,
322 };
323
324 if let Some(first_valid) = additional_transition_times.iter().position(|&unix_time| previous_transition_unix_time < unix_time) {
326 let valid_transition_times = &additional_transition_times[first_valid..];
327 let valid_transitions = &additional_transitions[first_valid..];
328
329 let valid_iter = valid_transition_times.iter().copied().zip(valid_transitions.iter().copied());
330
331 for (transition_unix_time, &(&local_time_type_before, &local_time_type_after, unix_time_before, unix_time_after)) in valid_iter {
332 if previous_transition_unix_time <= unix_time_before && unix_time_before < transition_unix_time {
333 found_date_time_list.push(FoundDateTimeKind::Normal(new_datetime(local_time_type_before, unix_time_before)));
334 } else {
335 if unix_time_before >= transition_unix_time && unix_time_after < transition_unix_time {
337 found_date_time_list.push(FoundDateTimeKind::Skipped {
338 before_transition: DateTime::from_timespec_and_local(transition_unix_time, nanoseconds, local_time_type_before)?,
339 after_transition: DateTime::from_timespec_and_local(transition_unix_time, nanoseconds, local_time_type_after)?,
340 });
341 }
342 }
343
344 previous_transition_unix_time = transition_unix_time;
345 }
346 }
347 }
348 }
349
350 Ok(())
351}
352
353#[cfg(feature = "alloc")]
354#[cfg(test)]
355mod test {
356 use super::*;
357 use crate::datetime::test::check_equal_date_time;
358 use crate::timezone::*;
359
360 use alloc::vec;
361
362 fn check_equal_option_date_time(x: &Option<DateTime>, y: &Option<DateTime>) {
363 match (x, y) {
364 (None, None) => (),
365 (Some(x), Some(y)) => check_equal_date_time(x, y),
366 _ => panic!("not equal"),
367 }
368 }
369
370 enum Check {
371 Normal([i32; 1]),
372 Skipped([(i32, u8, u8, u8, u8, u8, i32); 2]),
373 }
374
375 fn check(
376 time_zone_ref: TimeZoneRef,
377 posssible_date_time_results: &[Check],
378 searched: (i32, u8, u8, u8, u8, u8),
379 result_indices: &[usize],
380 unique: Option<[usize; 2]>,
381 earlier: Option<[usize; 2]>,
382 later: Option<[usize; 2]>,
383 ) -> Result<()> {
384 let new_date_time = |(year, month, month_day, hour, minute, second, ut_offset)| {
385 Result::Ok(DateTime::new(year, month, month_day, hour, minute, second, 0, LocalTimeType::with_ut_offset(ut_offset)?)?)
386 };
387
388 let (year, month, month_day, hour, minute, second) = searched;
389
390 let mut found_date_times = FoundDateTimeList::default();
391 find_date_time(&mut found_date_times, year, month, month_day, hour, minute, second, 0, time_zone_ref)?;
392
393 let mut buf = vec![None; result_indices.len()];
394 let mut found_date_time_list = FoundDateTimeListRefMut::new(&mut buf);
395 find_date_time(&mut found_date_time_list, year, month, month_day, hour, minute, second, 0, time_zone_ref)?;
396
397 let indexed_date_time = |[index_1, index_2]: [usize; 2]| match posssible_date_time_results[index_1] {
398 Check::Normal(arr) => new_date_time((year, month, month_day, hour, minute, second, arr[index_2])),
399 Check::Skipped(arr) => new_date_time(arr[index_2]),
400 };
401
402 check_equal_option_date_time(&found_date_times.unique(), &unique.map(indexed_date_time).transpose()?);
403 check_equal_option_date_time(&found_date_times.earliest(), &earlier.map(indexed_date_time).transpose()?);
404 check_equal_option_date_time(&found_date_times.latest(), &later.map(indexed_date_time).transpose()?);
405
406 let found_date_times_inner = found_date_times.into_inner();
407 assert_eq!(found_date_times_inner.len(), result_indices.len());
408
409 assert!(found_date_time_list.is_exhaustive());
410 assert_eq!(found_date_times_inner, buf.iter().copied().flatten().collect::<Vec<_>>());
411
412 for (found_date_time, &result_index) in found_date_times_inner.iter().zip(result_indices) {
413 match posssible_date_time_results[result_index] {
414 Check::Normal([ut_offset]) => {
415 assert_eq!(*found_date_time, FoundDateTimeKind::Normal(new_date_time((year, month, month_day, hour, minute, second, ut_offset))?));
416 }
417 Check::Skipped([before, after]) => {
418 let skipped = FoundDateTimeKind::Skipped { before_transition: new_date_time(before)?, after_transition: new_date_time(after)? };
419 assert_eq!(*found_date_time, skipped);
420 }
421 };
422 }
423
424 Ok(())
425 }
426
427 #[test]
428 fn test_find_date_time_fixed() -> Result<()> {
429 let local_time_type = LocalTimeType::with_ut_offset(3600)?;
430
431 let results = &[Check::Normal([3600])];
432
433 let time_zone_1 = TimeZone::new(vec![], vec![local_time_type], vec![], None)?;
434 let time_zone_2 = TimeZone::new(vec![], vec![local_time_type], vec![], Some(TransitionRule::Fixed(local_time_type)))?;
435
436 check(time_zone_1.as_ref(), results, (2000, 1, 1, 0, 0, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
437 check(time_zone_2.as_ref(), results, (2000, 1, 1, 0, 0, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
438
439 let time_zone_3 = TimeZone::new(vec![Transition::new(0, 0)], vec![local_time_type], vec![], Some(TransitionRule::Fixed(local_time_type)))?;
440
441 check(time_zone_3.as_ref(), results, (1960, 1, 1, 0, 0, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
442 check(time_zone_3.as_ref(), results, (1980, 1, 1, 0, 0, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
443
444 Ok(())
445 }
446
447 #[test]
448 fn test_find_date_time_no_offset() -> Result<()> {
449 let local_time_types = [
450 LocalTimeType::new(0, false, Some(b"STD1"))?,
451 LocalTimeType::new(0, true, Some(b"DST1"))?,
452 LocalTimeType::new(0, false, Some(b"STD2"))?,
453 LocalTimeType::new(0, true, Some(b"DST2"))?,
454 ];
455
456 let time_zone = TimeZone::new(
457 vec![Transition::new(3600, 1), Transition::new(7200, 2)],
458 local_time_types.to_vec(),
459 vec![],
460 Some(TransitionRule::Alternate(AlternateTime::new(
461 local_time_types[2],
462 local_time_types[3],
463 RuleDay::Julian0WithLeap(Julian0WithLeap::new(0)?),
464 10800,
465 RuleDay::Julian0WithLeap(Julian0WithLeap::new(0)?),
466 14400,
467 )?)),
468 )?;
469
470 let time_zone_ref = time_zone.as_ref();
471
472 let find_unique_local_time_type = |year, month, month_day, hour, minute, second, nanoseconds| {
473 let mut found_date_time_list = FoundDateTimeList::default();
474 find_date_time(&mut found_date_time_list, year, month, month_day, hour, minute, second, nanoseconds, time_zone_ref)?;
475
476 let mut buf = [None; 1];
477 let mut found_date_time_list_ref_mut = FoundDateTimeListRefMut::new(&mut buf);
478 find_date_time(&mut found_date_time_list_ref_mut, year, month, month_day, hour, minute, second, 0, time_zone_ref)?;
479 assert!(found_date_time_list_ref_mut.is_exhaustive());
480
481 let datetime_1 = found_date_time_list.unique().unwrap();
482 let datetime_2 = found_date_time_list_ref_mut.unique().unwrap();
483 assert_eq!(datetime_1, datetime_2);
484
485 Result::Ok(*datetime_1.local_time_type())
486 };
487
488 assert_eq!(local_time_types[0], find_unique_local_time_type(1970, 1, 1, 0, 30, 0, 0)?);
489 assert_eq!(local_time_types[1], find_unique_local_time_type(1970, 1, 1, 1, 30, 0, 0)?);
490 assert_eq!(local_time_types[2], find_unique_local_time_type(1970, 1, 1, 2, 30, 0, 0)?);
491 assert_eq!(local_time_types[3], find_unique_local_time_type(1970, 1, 1, 3, 30, 0, 0)?);
492 assert_eq!(local_time_types[2], find_unique_local_time_type(1970, 1, 1, 4, 30, 0, 0)?);
493
494 Ok(())
495 }
496
497 #[test]
498 fn test_find_date_time_extra_rule_only() -> Result<()> {
499 let time_zone = TimeZone::new(
500 vec![],
501 vec![LocalTimeType::utc(), LocalTimeType::with_ut_offset(3600)?],
502 vec![],
503 Some(TransitionRule::Alternate(AlternateTime::new(
504 LocalTimeType::utc(),
505 LocalTimeType::with_ut_offset(3600)?,
506 RuleDay::Julian1WithoutLeap(Julian1WithoutLeap::new(1)?),
507 7200,
508 RuleDay::Julian1WithoutLeap(Julian1WithoutLeap::new(1)?),
509 12600,
510 )?)),
511 )?;
512
513 let time_zone_ref = time_zone.as_ref();
514
515 let results = &[
516 Check::Normal([0]),
517 Check::Normal([3600]),
518 Check::Skipped([(2000, 1, 1, 2, 0, 0, 0), (2000, 1, 1, 3, 0, 0, 3600)]),
519 Check::Skipped([(2010, 1, 1, 2, 0, 0, 0), (2010, 1, 1, 3, 0, 0, 3600)]),
520 ];
521
522 check(time_zone_ref, results, (2000, 1, 1, 1, 45, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
523 check(time_zone_ref, results, (2000, 1, 1, 2, 15, 0), &[2], None, Some([2, 0]), Some([2, 1]))?;
524 check(time_zone_ref, results, (2000, 1, 1, 2, 45, 0), &[2, 0], None, Some([2, 0]), Some([0, 0]))?;
525 check(time_zone_ref, results, (2000, 1, 1, 3, 15, 0), &[1, 0], None, Some([1, 0]), Some([0, 0]))?;
526 check(time_zone_ref, results, (2000, 1, 1, 3, 45, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
527
528 check(time_zone_ref, results, (2010, 1, 1, 1, 45, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
529 check(time_zone_ref, results, (2010, 1, 1, 2, 15, 0), &[3], None, Some([3, 0]), Some([3, 1]))?;
530 check(time_zone_ref, results, (2010, 1, 1, 2, 45, 0), &[3, 0], None, Some([3, 0]), Some([0, 0]))?;
531 check(time_zone_ref, results, (2010, 1, 1, 3, 15, 0), &[1, 0], None, Some([1, 0]), Some([0, 0]))?;
532 check(time_zone_ref, results, (2010, 1, 1, 3, 45, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
533
534 Ok(())
535 }
536
537 #[test]
538 fn test_find_date_time_transitions_only() -> Result<()> {
539 let time_zone = TimeZone::new(
540 vec![
541 Transition::new(0, 0),
542 Transition::new(7200, 1),
543 Transition::new(14400, 2),
544 Transition::new(25200, 3),
545 Transition::new(28800, 4),
546 Transition::new(32400, 0),
547 ],
548 vec![
549 LocalTimeType::new(0, false, None)?,
550 LocalTimeType::new(3600, false, None)?,
551 LocalTimeType::new(-10800, false, None)?,
552 LocalTimeType::new(-19800, false, None)?,
553 LocalTimeType::new(-16200, false, None)?,
554 ],
555 vec![],
556 None,
557 )?;
558
559 let time_zone_ref = time_zone.as_ref();
560
561 let results = &[
562 Check::Normal([0]),
563 Check::Normal([3600]),
564 Check::Normal([-10800]),
565 Check::Normal([-19800]),
566 Check::Normal([-16200]),
567 Check::Skipped([(1970, 1, 1, 2, 0, 0, 0), (1970, 1, 1, 3, 0, 0, 3600)]),
568 Check::Skipped([(1970, 1, 1, 2, 30, 0, -19800), (1970, 1, 1, 3, 30, 0, -16200)]),
569 ];
570
571 check(time_zone_ref, results, (1970, 1, 1, 0, 0, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
572 check(time_zone_ref, results, (1970, 1, 1, 1, 0, 0), &[0, 2], None, Some([0, 0]), Some([2, 0]))?;
573 check(time_zone_ref, results, (1970, 1, 1, 1, 15, 0), &[0, 2], None, Some([0, 0]), Some([2, 0]))?;
574 check(time_zone_ref, results, (1970, 1, 1, 1, 30, 0), &[0, 2, 3], None, Some([0, 0]), Some([3, 0]))?;
575 check(time_zone_ref, results, (1970, 1, 1, 1, 45, 0), &[0, 2, 3], None, Some([0, 0]), Some([3, 0]))?;
576 check(time_zone_ref, results, (1970, 1, 1, 2, 0, 0), &[5, 2, 3], None, Some([5, 0]), Some([3, 0]))?;
577 check(time_zone_ref, results, (1970, 1, 1, 2, 15, 0), &[5, 2, 3], None, Some([5, 0]), Some([3, 0]))?;
578 check(time_zone_ref, results, (1970, 1, 1, 2, 30, 0), &[5, 2, 6], None, Some([5, 0]), Some([6, 1]))?;
579 check(time_zone_ref, results, (1970, 1, 1, 2, 45, 0), &[5, 2, 6], None, Some([5, 0]), Some([6, 1]))?;
580 check(time_zone_ref, results, (1970, 1, 1, 3, 0, 0), &[1, 2, 6], None, Some([1, 0]), Some([6, 1]))?;
581 check(time_zone_ref, results, (1970, 1, 1, 3, 15, 0), &[1, 2, 6], None, Some([1, 0]), Some([6, 1]))?;
582 check(time_zone_ref, results, (1970, 1, 1, 3, 30, 0), &[1, 2, 4], None, Some([1, 0]), Some([4, 0]))?;
583 check(time_zone_ref, results, (1970, 1, 1, 3, 45, 0), &[1, 2, 4], None, Some([1, 0]), Some([4, 0]))?;
584 check(time_zone_ref, results, (1970, 1, 1, 4, 0, 0), &[1, 4], None, Some([1, 0]), Some([4, 0]))?;
585 check(time_zone_ref, results, (1970, 1, 1, 4, 15, 0), &[1, 4], None, Some([1, 0]), Some([4, 0]))?;
586 check(time_zone_ref, results, (1970, 1, 1, 4, 30, 0), &[1], Some([1, 0]), Some([1, 0]), Some([1, 0]))?;
587 check(time_zone_ref, results, (1970, 1, 1, 4, 45, 0), &[1], Some([1, 0]), Some([1, 0]), Some([1, 0]))?;
588 check(time_zone_ref, results, (1970, 1, 1, 5, 0, 0), &[], None, None, None)?;
589
590 Ok(())
591 }
592
593 #[test]
594 fn test_find_date_time_transitions_with_extra_rule() -> Result<()> {
595 let time_zone = TimeZone::new(
596 vec![Transition::new(0, 0), Transition::new(3600, 1), Transition::new(7200, 0), Transition::new(10800, 2)],
597 vec![LocalTimeType::utc(), LocalTimeType::with_ut_offset(i32::MAX)?, LocalTimeType::with_ut_offset(3600)?],
598 vec![],
599 Some(TransitionRule::Alternate(AlternateTime::new(
600 LocalTimeType::utc(),
601 LocalTimeType::with_ut_offset(3600)?,
602 RuleDay::Julian1WithoutLeap(Julian1WithoutLeap::new(300)?),
603 0,
604 RuleDay::Julian1WithoutLeap(Julian1WithoutLeap::new(90)?),
605 3600,
606 )?)),
607 )?;
608
609 let time_zone_ref = time_zone.as_ref();
610
611 let results = &[
612 Check::Normal([0]),
613 Check::Normal([3600]),
614 Check::Normal([i32::MAX]),
615 Check::Skipped([(1970, 1, 1, 1, 0, 0, 0), (2038, 1, 19, 4, 14, 7, i32::MAX)]),
616 Check::Skipped([(1970, 1, 1, 3, 0, 0, 0), (1970, 1, 1, 4, 0, 0, 3600)]),
617 Check::Skipped([(1970, 10, 27, 0, 0, 0, 0), (1970, 10, 27, 1, 0, 0, 3600)]),
618 Check::Skipped([(2000, 10, 27, 0, 0, 0, 0), (2000, 10, 27, 1, 0, 0, 3600)]),
619 Check::Skipped([(2030, 10, 27, 0, 0, 0, 0), (2030, 10, 27, 1, 0, 0, 3600)]),
620 Check::Skipped([(2038, 10, 27, 0, 0, 0, 0), (2038, 10, 27, 1, 0, 0, 3600)]),
621 ];
622
623 check(time_zone_ref, results, (1970, 1, 1, 0, 30, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
624 check(time_zone_ref, results, (1970, 1, 1, 1, 30, 0), &[3], None, Some([3, 0]), Some([3, 1]))?;
625 check(time_zone_ref, results, (1970, 1, 1, 2, 30, 0), &[3, 0], None, Some([3, 0]), Some([0, 0]))?;
626 check(time_zone_ref, results, (1970, 1, 1, 3, 30, 0), &[3, 4], None, Some([3, 0]), Some([4, 1]))?;
627 check(time_zone_ref, results, (1970, 1, 1, 4, 30, 0), &[3, 1], None, Some([3, 0]), Some([1, 0]))?;
628
629 check(time_zone_ref, results, (1970, 2, 1, 0, 0, 0), &[3, 1], None, Some([3, 0]), Some([1, 0]))?;
630 check(time_zone_ref, results, (1970, 3, 31, 0, 30, 0), &[3, 1, 0], None, Some([3, 0]), Some([0, 0]))?;
631 check(time_zone_ref, results, (1970, 6, 1, 0, 0, 0), &[3, 0], None, Some([3, 0]), Some([0, 0]))?;
632 check(time_zone_ref, results, (1970, 10, 27, 0, 30, 0), &[3, 5], None, Some([3, 0]), Some([5, 1]))?;
633 check(time_zone_ref, results, (1970, 11, 1, 0, 0, 0), &[3, 1], None, Some([3, 0]), Some([1, 0]))?;
634
635 check(time_zone_ref, results, (2000, 2, 1, 0, 0, 0), &[3, 1], None, Some([3, 0]), Some([1, 0]))?;
636 check(time_zone_ref, results, (2000, 3, 31, 0, 30, 0), &[3, 1, 0], None, Some([3, 0]), Some([0, 0]))?;
637 check(time_zone_ref, results, (2000, 6, 1, 0, 0, 0), &[3, 0], None, Some([3, 0]), Some([0, 0]))?;
638 check(time_zone_ref, results, (2000, 10, 27, 0, 30, 0), &[3, 6], None, Some([3, 0]), Some([6, 1]))?;
639 check(time_zone_ref, results, (2000, 11, 1, 0, 0, 0), &[3, 1], None, Some([3, 0]), Some([1, 0]))?;
640
641 check(time_zone_ref, results, (2030, 2, 1, 0, 0, 0), &[3, 1], None, Some([3, 0]), Some([1, 0]))?;
642 check(time_zone_ref, results, (2030, 3, 31, 0, 30, 0), &[3, 1, 0], None, Some([3, 0]), Some([0, 0]))?;
643 check(time_zone_ref, results, (2030, 6, 1, 0, 0, 0), &[3, 0], None, Some([3, 0]), Some([0, 0]))?;
644 check(time_zone_ref, results, (2030, 10, 27, 0, 30, 0), &[3, 7], None, Some([3, 0]), Some([7, 1]))?;
645 check(time_zone_ref, results, (2030, 11, 1, 0, 0, 0), &[3, 1], None, Some([3, 0]), Some([1, 0]))?;
646
647 check(time_zone_ref, results, (2038, 1, 19, 5, 0, 0), &[2, 1], None, Some([2, 0]), Some([1, 0]))?;
648 check(time_zone_ref, results, (2038, 2, 1, 0, 0, 0), &[1], Some([1, 0]), Some([1, 0]), Some([1, 0]))?;
649 check(time_zone_ref, results, (2038, 3, 31, 0, 30, 0), &[1, 0], None, Some([1, 0]), Some([0, 0]))?;
650 check(time_zone_ref, results, (2038, 6, 1, 0, 0, 0), &[0], Some([0, 0]), Some([0, 0]), Some([0, 0]))?;
651 check(time_zone_ref, results, (2038, 10, 27, 0, 30, 0), &[8], None, Some([8, 0]), Some([8, 1]))?;
652 check(time_zone_ref, results, (2038, 11, 1, 0, 0, 0), &[1], Some([1, 0]), Some([1, 0]), Some([1, 0]))?;
653
654 Ok(())
655 }
656
657 #[test]
658 fn test_find_date_time_ref_mut() -> Result<()> {
659 let transitions = &[Transition::new(3600, 1), Transition::new(86400, 0), Transition::new(i64::MAX, 0)];
660 let local_time_types = &[LocalTimeType::new(0, false, Some(b"STD"))?, LocalTimeType::new(3600, true, Some(b"DST"))?];
661 let time_zone_ref = TimeZoneRef::new(transitions, local_time_types, &[], &None)?;
662
663 let mut small_buf = [None; 1];
664 let mut found_date_time_small_list = FoundDateTimeListRefMut::new(&mut small_buf);
665 find_date_time(&mut found_date_time_small_list, 1970, 1, 2, 0, 30, 0, 0, time_zone_ref)?;
666 assert!(!found_date_time_small_list.is_exhaustive());
667
668 let mut buf = [None; 2];
669 let mut found_date_time_list_1 = FoundDateTimeListRefMut::new(&mut buf);
670 find_date_time(&mut found_date_time_list_1, 1970, 1, 2, 0, 30, 0, 0, time_zone_ref)?;
671 let data = found_date_time_list_1.data();
672 assert!(found_date_time_list_1.is_exhaustive());
673 assert_eq!(found_date_time_list_1.count(), 2);
674 assert!(matches!(data, [Some(FoundDateTimeKind::Normal(..)), Some(FoundDateTimeKind::Normal(..))]));
675
676 let mut found_date_time_list_2 = FoundDateTimeListRefMut::new(&mut buf);
677 find_date_time(&mut found_date_time_list_2, 1970, 1, 1, 1, 30, 0, 0, time_zone_ref)?;
678 let data = found_date_time_list_2.data();
679 assert!(found_date_time_list_2.is_exhaustive());
680 assert_eq!(found_date_time_list_2.count(), 1);
681 assert!(found_date_time_list_2.unique().is_none());
682 assert!(matches!(data, &[Some(FoundDateTimeKind::Skipped { .. })]));
683
684 Ok(())
685 }
686}