1mod find;
4
5#[doc(inline)]
6#[cfg(feature = "alloc")]
7pub use find::FoundDateTimeList;
8#[doc(inline)]
9pub use find::{FoundDateTimeKind, FoundDateTimeListRefMut};
10
11use crate::constants::*;
12use crate::datetime::find::find_date_time;
13use crate::error::TzError;
14use crate::error::datetime::DateTimeError;
15use crate::timezone::{LocalTimeType, TimeZoneRef};
16use crate::utils::{min, try_into_i32, try_into_i64};
17
18use core::cmp::Ordering;
19use core::fmt;
20
21#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
23pub struct UtcDateTime {
24 year: i32,
26 month: u8,
28 month_day: u8,
30 hour: u8,
32 minute: u8,
34 second: u8,
36 nanoseconds: u32,
38}
39
40impl fmt::Display for UtcDateTime {
41 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42 format_date_time(f, self.year, self.month, self.month_day, self.hour, self.minute, self.second, self.nanoseconds, 0)
43 }
44}
45
46impl UtcDateTime {
47 const MIN_UNIX_TIME: i64 = -67768100567971200;
49 const MAX_UNIX_TIME: i64 = 67767976233532799;
51
52 const fn check_unix_time(unix_time: i64) -> Result<(), TzError> {
54 if Self::MIN_UNIX_TIME <= unix_time && unix_time <= Self::MAX_UNIX_TIME { Ok(()) } else { Err(TzError::OutOfRange) }
55 }
56
57 pub const fn new(year: i32, month: u8, month_day: u8, hour: u8, minute: u8, second: u8, nanoseconds: u32) -> Result<Self, TzError> {
70 if year == i32::MAX && month == 12 && month_day == 31 && hour == 23 && minute == 59 && second == 60 {
72 return Err(TzError::OutOfRange);
73 }
74
75 if let Err(error) = check_date_time_inputs(year, month, month_day, hour, minute, second, nanoseconds) {
76 return Err(TzError::DateTime(error));
77 }
78
79 Ok(Self { year, month, month_day, hour, minute, second, nanoseconds })
80 }
81
82 pub const fn from_timespec(unix_time: i64, nanoseconds: u32) -> Result<Self, TzError> {
84 let seconds = match unix_time.checked_sub(UNIX_OFFSET_SECS) {
85 Some(seconds) => seconds,
86 None => return Err(TzError::OutOfRange),
87 };
88
89 let mut remaining_days = seconds / SECONDS_PER_DAY;
90 let mut remaining_seconds = seconds % SECONDS_PER_DAY;
91 if remaining_seconds < 0 {
92 remaining_seconds += SECONDS_PER_DAY;
93 remaining_days -= 1;
94 }
95
96 let mut cycles_400_years = remaining_days / DAYS_PER_400_YEARS;
97 remaining_days %= DAYS_PER_400_YEARS;
98 if remaining_days < 0 {
99 remaining_days += DAYS_PER_400_YEARS;
100 cycles_400_years -= 1;
101 }
102
103 let cycles_100_years = min(remaining_days / DAYS_PER_100_YEARS, 3);
104 remaining_days -= cycles_100_years * DAYS_PER_100_YEARS;
105
106 let cycles_4_years = min(remaining_days / DAYS_PER_4_YEARS, 24);
107 remaining_days -= cycles_4_years * DAYS_PER_4_YEARS;
108
109 let remaining_years = min(remaining_days / DAYS_PER_NORMAL_YEAR, 3);
110 remaining_days -= remaining_years * DAYS_PER_NORMAL_YEAR;
111
112 let mut year = OFFSET_YEAR + remaining_years + cycles_4_years * 4 + cycles_100_years * 100 + cycles_400_years * 400;
113
114 let mut month = 0;
115 while month < DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH.len() {
116 let days = DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH[month];
117 if remaining_days < days {
118 break;
119 }
120 remaining_days -= days;
121 month += 1;
122 }
123 month += 2;
124
125 if month >= MONTHS_PER_YEAR as usize {
126 month -= MONTHS_PER_YEAR as usize;
127 year += 1;
128 }
129 month += 1;
130
131 let month_day = 1 + remaining_days;
132
133 let hour = remaining_seconds / SECONDS_PER_HOUR;
134 let minute = (remaining_seconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
135 let second = remaining_seconds % SECONDS_PER_MINUTE;
136
137 let year = match try_into_i32(year) {
138 Ok(year) => year,
139 Err(error) => return Err(error),
140 };
141
142 Ok(Self { year, month: month as u8, month_day: month_day as u8, hour: hour as u8, minute: minute as u8, second: second as u8, nanoseconds })
143 }
144
145 pub const fn from_total_nanoseconds(total_nanoseconds: i128) -> Result<Self, TzError> {
147 match total_nanoseconds_to_timespec(total_nanoseconds) {
148 Ok((unix_time, nanoseconds)) => Self::from_timespec(unix_time, nanoseconds),
149 Err(error) => Err(error),
150 }
151 }
152
153 pub const fn unix_time(&self) -> i64 {
155 unix_time(self.year, self.month, self.month_day, self.hour, self.minute, self.second)
156 }
157
158 pub const fn project(&self, time_zone_ref: TimeZoneRef<'_>) -> Result<DateTime, TzError> {
163 DateTime::from_timespec(self.unix_time(), self.nanoseconds, time_zone_ref)
164 }
165
166 #[cfg(feature = "std")]
168 pub fn now() -> Result<Self, TzError> {
169 Self::from_total_nanoseconds(crate::utils::current_total_nanoseconds())
170 }
171}
172
173#[derive(Debug, Copy, Clone)]
175pub struct DateTime {
176 year: i32,
178 month: u8,
180 month_day: u8,
182 hour: u8,
184 minute: u8,
186 second: u8,
188 local_time_type: LocalTimeType,
190 unix_time: i64,
192 nanoseconds: u32,
194}
195
196impl PartialEq for DateTime {
197 fn eq(&self, other: &Self) -> bool {
198 (self.unix_time, self.nanoseconds) == (other.unix_time, other.nanoseconds)
199 }
200}
201
202impl PartialOrd for DateTime {
203 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
204 (self.unix_time, self.nanoseconds).partial_cmp(&(other.unix_time, other.nanoseconds))
205 }
206}
207
208impl fmt::Display for DateTime {
209 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210 let ut_offset = self.local_time_type().ut_offset();
211 format_date_time(f, self.year, self.month, self.month_day, self.hour, self.minute, self.second, self.nanoseconds, ut_offset)
212 }
213}
214
215impl DateTime {
216 #[allow(clippy::too_many_arguments)]
230 pub const fn new(
231 year: i32,
232 month: u8,
233 month_day: u8,
234 hour: u8,
235 minute: u8,
236 second: u8,
237 nanoseconds: u32,
238 local_time_type: LocalTimeType,
239 ) -> Result<Self, TzError> {
240 if let Err(error) = check_date_time_inputs(year, month, month_day, hour, minute, second, nanoseconds) {
241 return Err(TzError::DateTime(error));
242 }
243
244 let unix_time = unix_time(year, month, month_day, hour, minute, second) - local_time_type.ut_offset() as i64;
246
247 if let Err(error) = UtcDateTime::check_unix_time(unix_time) {
249 return Err(error);
250 }
251
252 Ok(Self { year, month, month_day, hour, minute, second, local_time_type, unix_time, nanoseconds })
253 }
254
255 #[allow(clippy::too_many_arguments)]
269 #[cfg(feature = "alloc")]
270 pub fn find(
271 year: i32,
272 month: u8,
273 month_day: u8,
274 hour: u8,
275 minute: u8,
276 second: u8,
277 nanoseconds: u32,
278 time_zone_ref: TimeZoneRef<'_>,
279 ) -> Result<FoundDateTimeList, TzError> {
280 let mut found_date_time_list = FoundDateTimeList::default();
281 find_date_time(&mut found_date_time_list, year, month, month_day, hour, minute, second, nanoseconds, time_zone_ref)?;
282 Ok(found_date_time_list)
283 }
284
285 #[allow(clippy::too_many_arguments)]
337 pub fn find_n<'a>(
338 buf: &'a mut [Option<FoundDateTimeKind>],
339 year: i32,
340 month: u8,
341 month_day: u8,
342 hour: u8,
343 minute: u8,
344 second: u8,
345 nanoseconds: u32,
346 time_zone_ref: TimeZoneRef<'_>,
347 ) -> Result<FoundDateTimeListRefMut<'a>, TzError> {
348 let mut found_date_time_list = FoundDateTimeListRefMut::new(buf);
349 find_date_time(&mut found_date_time_list, year, month, month_day, hour, minute, second, nanoseconds, time_zone_ref)?;
350 Ok(found_date_time_list)
351 }
352
353 pub const fn from_timespec_and_local(unix_time: i64, nanoseconds: u32, local_time_type: LocalTimeType) -> Result<Self, TzError> {
355 let unix_time_with_offset = match unix_time.checked_add(local_time_type.ut_offset() as i64) {
356 Some(unix_time_with_offset) => unix_time_with_offset,
357 None => return Err(TzError::OutOfRange),
358 };
359
360 let utc_date_time_with_offset = match UtcDateTime::from_timespec(unix_time_with_offset, nanoseconds) {
361 Ok(utc_date_time_with_offset) => utc_date_time_with_offset,
362 Err(error) => return Err(error),
363 };
364
365 let UtcDateTime { year, month, month_day, hour, minute, second, nanoseconds } = utc_date_time_with_offset;
366 Ok(Self { year, month, month_day, hour, minute, second, local_time_type, unix_time, nanoseconds })
367 }
368
369 pub const fn from_timespec(unix_time: i64, nanoseconds: u32, time_zone_ref: TimeZoneRef<'_>) -> Result<Self, TzError> {
371 let local_time_type = match time_zone_ref.find_local_time_type(unix_time) {
372 Ok(&local_time_type) => local_time_type,
373 Err(error) => return Err(error),
374 };
375
376 Self::from_timespec_and_local(unix_time, nanoseconds, local_time_type)
377 }
378
379 pub const fn from_total_nanoseconds_and_local(total_nanoseconds: i128, local_time_type: LocalTimeType) -> Result<Self, TzError> {
381 match total_nanoseconds_to_timespec(total_nanoseconds) {
382 Ok((unix_time, nanoseconds)) => Self::from_timespec_and_local(unix_time, nanoseconds, local_time_type),
383 Err(error) => Err(error),
384 }
385 }
386
387 pub const fn from_total_nanoseconds(total_nanoseconds: i128, time_zone_ref: TimeZoneRef<'_>) -> Result<Self, TzError> {
389 match total_nanoseconds_to_timespec(total_nanoseconds) {
390 Ok((unix_time, nanoseconds)) => Self::from_timespec(unix_time, nanoseconds, time_zone_ref),
391 Err(error) => Err(error),
392 }
393 }
394
395 pub const fn project(&self, time_zone_ref: TimeZoneRef<'_>) -> Result<Self, TzError> {
400 Self::from_timespec(self.unix_time, self.nanoseconds, time_zone_ref)
401 }
402
403 #[cfg(feature = "std")]
405 pub fn now(time_zone_ref: TimeZoneRef<'_>) -> Result<Self, TzError> {
406 let now = crate::utils::current_total_nanoseconds();
407 Self::from_total_nanoseconds(now, time_zone_ref)
408 }
409}
410
411macro_rules! impl_datetime {
413 () => {
414 #[inline]
416 pub const fn year(&self) -> i32 {
417 self.year
418 }
419
420 #[inline]
422 pub const fn month(&self) -> u8 {
423 self.month
424 }
425
426 #[inline]
428 pub const fn month_day(&self) -> u8 {
429 self.month_day
430 }
431
432 #[inline]
434 pub const fn hour(&self) -> u8 {
435 self.hour
436 }
437
438 #[inline]
440 pub const fn minute(&self) -> u8 {
441 self.minute
442 }
443
444 #[inline]
446 pub const fn second(&self) -> u8 {
447 self.second
448 }
449
450 #[inline]
452 pub const fn nanoseconds(&self) -> u32 {
453 self.nanoseconds
454 }
455
456 #[inline]
458 pub const fn week_day(&self) -> u8 {
459 week_day(self.year, self.month as usize, self.month_day as i64)
460 }
461
462 #[inline]
464 pub const fn year_day(&self) -> u16 {
465 year_day(self.year, self.month as usize, self.month_day as i64)
466 }
467
468 #[inline]
470 pub const fn total_nanoseconds(&self) -> i128 {
471 nanoseconds_since_unix_epoch(self.unix_time(), self.nanoseconds)
472 }
473 };
474}
475
476impl UtcDateTime {
477 impl_datetime!();
478}
479
480impl DateTime {
481 impl_datetime!();
482
483 #[inline]
485 pub const fn local_time_type(&self) -> &LocalTimeType {
486 &self.local_time_type
487 }
488
489 #[inline]
491 pub const fn unix_time(&self) -> i64 {
492 self.unix_time
493 }
494}
495
496#[inline]
505const fn week_day(year: i32, month: usize, month_day: i64) -> u8 {
506 let days_since_unix_epoch = days_since_unix_epoch(year, month, month_day);
507 (4 + days_since_unix_epoch).rem_euclid(DAYS_PER_WEEK) as u8
508}
509
510#[inline]
519const fn year_day(year: i32, month: usize, month_day: i64) -> u16 {
520 let leap = (month >= 3 && is_leap_year(year)) as i64;
521 (CUMUL_DAYS_IN_MONTHS_NORMAL_YEAR[month - 1] + leap + month_day - 1) as u16
522}
523
524#[inline]
526pub(crate) const fn is_leap_year(year: i32) -> bool {
527 year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
528}
529
530#[inline]
541pub(crate) const fn days_since_unix_epoch(year: i32, month: usize, month_day: i64) -> i64 {
542 let is_leap_year = is_leap_year(year);
543
544 let year = year as i64;
545
546 let mut result = (year - 1970) * 365;
547
548 if year >= 1970 {
549 result += (year - 1968) / 4;
550 result -= (year - 1900) / 100;
551 result += (year - 1600) / 400;
552
553 if is_leap_year && month < 3 {
554 result -= 1;
555 }
556 } else {
557 result += (year - 1972) / 4;
558 result -= (year - 2000) / 100;
559 result += (year - 2000) / 400;
560
561 if is_leap_year && month >= 3 {
562 result += 1;
563 }
564 }
565
566 result += CUMUL_DAYS_IN_MONTHS_NORMAL_YEAR[month - 1] + month_day - 1;
567
568 result
569}
570
571#[inline]
583const fn unix_time(year: i32, month: u8, month_day: u8, hour: u8, minute: u8, second: u8) -> i64 {
584 let mut result = days_since_unix_epoch(year, month as usize, month_day as i64);
585 result *= HOURS_PER_DAY;
586 result += hour as i64;
587 result *= MINUTES_PER_HOUR;
588 result += minute as i64;
589 result *= SECONDS_PER_MINUTE;
590 result += second as i64;
591
592 result
593}
594
595#[inline]
597const fn nanoseconds_since_unix_epoch(unix_time: i64, nanoseconds: u32) -> i128 {
598 unix_time as i128 * NANOSECONDS_PER_SECOND as i128 + nanoseconds as i128
600}
601
602#[inline]
610const fn total_nanoseconds_to_timespec(total_nanoseconds: i128) -> Result<(i64, u32), TzError> {
611 let unix_time = match try_into_i64(total_nanoseconds.div_euclid(NANOSECONDS_PER_SECOND as i128)) {
612 Ok(unix_time) => unix_time,
613 Err(error) => return Err(error),
614 };
615
616 let nanoseconds = total_nanoseconds.rem_euclid(NANOSECONDS_PER_SECOND as i128) as u32;
617
618 Ok((unix_time, nanoseconds))
619}
620
621const fn check_date_time_inputs(year: i32, month: u8, month_day: u8, hour: u8, minute: u8, second: u8, nanoseconds: u32) -> Result<(), DateTimeError> {
634 if !(1 <= month && month <= 12) {
635 return Err(DateTimeError::InvalidMonth);
636 }
637 if !(1 <= month_day && month_day <= 31) {
638 return Err(DateTimeError::InvalidMonthDay);
639 }
640 if hour > 23 {
641 return Err(DateTimeError::InvalidHour);
642 }
643 if minute > 59 {
644 return Err(DateTimeError::InvalidMinute);
645 }
646 if second > 60 {
647 return Err(DateTimeError::InvalidSecond);
648 }
649 if nanoseconds >= NANOSECONDS_PER_SECOND {
650 return Err(DateTimeError::InvalidNanoseconds);
651 }
652
653 let leap = is_leap_year(year) as i64;
654
655 let mut days_in_month = DAYS_IN_MONTHS_NORMAL_YEAR[month as usize - 1];
656 if month == 2 {
657 days_in_month += leap;
658 }
659
660 if month_day as i64 > days_in_month {
661 return Err(DateTimeError::InvalidMonthDay);
662 }
663
664 Ok(())
665}
666
667#[allow(clippy::too_many_arguments)]
682fn format_date_time(
683 f: &mut fmt::Formatter,
684 year: i32,
685 month: u8,
686 month_day: u8,
687 hour: u8,
688 minute: u8,
689 second: u8,
690 nanoseconds: u32,
691 ut_offset: i32,
692) -> fmt::Result {
693 write!(f, "{year}-{month:02}-{month_day:02}T{hour:02}:{minute:02}:{second:02}.{nanoseconds:09}")?;
694
695 if ut_offset != 0 {
696 let ut_offset = ut_offset as i64;
697 let ut_offset_abs = ut_offset.abs();
698
699 let sign = if ut_offset < 0 { '-' } else { '+' };
700
701 let offset_hour = ut_offset_abs / SECONDS_PER_HOUR;
702 let offset_minute = (ut_offset_abs / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
703 let offset_second = ut_offset_abs % SECONDS_PER_MINUTE;
704
705 write!(f, "{sign}{offset_hour:02}:{offset_minute:02}")?;
706
707 if offset_second != 0 {
708 write!(f, ":{offset_second:02}")?;
709 }
710 } else {
711 write!(f, "Z")?;
712 }
713
714 Ok(())
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720
721 #[cfg(feature = "alloc")]
722 use crate::timezone::TimeZone;
723
724 #[cfg(feature = "alloc")]
725 pub(super) fn check_equal_date_time(x: &DateTime, y: &DateTime) {
726 assert_eq!(x.year(), y.year());
727 assert_eq!(x.month(), y.month());
728 assert_eq!(x.month_day(), y.month_day());
729 assert_eq!(x.hour(), y.hour());
730 assert_eq!(x.minute(), y.minute());
731 assert_eq!(x.second(), y.second());
732 assert_eq!(x.local_time_type(), y.local_time_type());
733 assert_eq!(x.unix_time(), y.unix_time());
734 assert_eq!(x.nanoseconds(), y.nanoseconds());
735 }
736
737 #[cfg(feature = "alloc")]
738 #[test]
739 fn test_date_time() -> Result<(), TzError> {
740 let time_zone_utc = TimeZone::utc();
741 let utc = LocalTimeType::utc();
742
743 let time_zone_cet = TimeZone::fixed(3600)?;
744 let cet = LocalTimeType::with_ut_offset(3600)?;
745
746 let time_zone_eet = TimeZone::fixed(7200)?;
747 let eet = LocalTimeType::with_ut_offset(7200)?;
748
749 #[cfg(feature = "std")]
750 {
751 assert_eq!(DateTime::now(time_zone_utc.as_ref())?.local_time_type().ut_offset(), 0);
752 assert_eq!(DateTime::now(time_zone_cet.as_ref())?.local_time_type().ut_offset(), 3600);
753 assert_eq!(DateTime::now(time_zone_eet.as_ref())?.local_time_type().ut_offset(), 7200);
754 }
755
756 let unix_times = &[
757 -93750523134,
758 -11670955134,
759 -11670868734,
760 -8515195134,
761 -8483659134,
762 -8389051134,
763 -8388964734,
764 951825666,
765 951912066,
766 983448066,
767 1078056066,
768 1078142466,
769 4107585666,
770 32540356866,
771 ];
772
773 let nanoseconds_list = &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23];
774
775 #[rustfmt::skip]
776 let date_times_utc = &[
777 DateTime { year: -1001, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -93750523134, nanoseconds: 10 },
778 DateTime { year: 1600, month: 2, month_day: 29, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -11670955134, nanoseconds: 11 },
779 DateTime { year: 1600, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -11670868734, nanoseconds: 12 },
780 DateTime { year: 1700, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -8515195134, nanoseconds: 13 },
781 DateTime { year: 1701, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -8483659134, nanoseconds: 14 },
782 DateTime { year: 1704, month: 2, month_day: 29, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -8389051134, nanoseconds: 15 },
783 DateTime { year: 1704, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -8388964734, nanoseconds: 16 },
784 DateTime { year: 2000, month: 2, month_day: 29, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 951825666, nanoseconds: 17 },
785 DateTime { year: 2000, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 951912066, nanoseconds: 18 },
786 DateTime { year: 2001, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 983448066, nanoseconds: 19 },
787 DateTime { year: 2004, month: 2, month_day: 29, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 1078056066, nanoseconds: 20 },
788 DateTime { year: 2004, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 1078142466, nanoseconds: 21 },
789 DateTime { year: 2100, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 4107585666, nanoseconds: 22 },
790 DateTime { year: 3001, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 32540356866, nanoseconds: 23 },
791 ];
792
793 #[rustfmt::skip]
794 let date_times_cet = &[
795 DateTime { year: -1001, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -93750523134, nanoseconds: 10 },
796 DateTime { year: 1600, month: 2, month_day: 29, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -11670955134, nanoseconds: 11 },
797 DateTime { year: 1600, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -11670868734, nanoseconds: 12 },
798 DateTime { year: 1700, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -8515195134, nanoseconds: 13 },
799 DateTime { year: 1701, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -8483659134, nanoseconds: 14 },
800 DateTime { year: 1704, month: 2, month_day: 29, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -8389051134, nanoseconds: 15 },
801 DateTime { year: 1704, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -8388964734, nanoseconds: 16 },
802 DateTime { year: 2000, month: 2, month_day: 29, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 951825666, nanoseconds: 17 },
803 DateTime { year: 2000, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 951912066, nanoseconds: 18 },
804 DateTime { year: 2001, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 983448066, nanoseconds: 19 },
805 DateTime { year: 2004, month: 2, month_day: 29, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 1078056066, nanoseconds: 20 },
806 DateTime { year: 2004, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 1078142466, nanoseconds: 21 },
807 DateTime { year: 2100, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 4107585666, nanoseconds: 22 },
808 DateTime { year: 3001, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 32540356866, nanoseconds: 23 },
809 ];
810
811 #[rustfmt::skip]
812 let date_times_eet = &[
813 DateTime { year: -1001, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -93750523134, nanoseconds: 10 },
814 DateTime { year: 1600, month: 2, month_day: 29, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -11670955134, nanoseconds: 11 },
815 DateTime { year: 1600, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -11670868734, nanoseconds: 12 },
816 DateTime { year: 1700, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -8515195134, nanoseconds: 13 },
817 DateTime { year: 1701, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -8483659134, nanoseconds: 14 },
818 DateTime { year: 1704, month: 2, month_day: 29, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -8389051134, nanoseconds: 15 },
819 DateTime { year: 1704, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -8388964734, nanoseconds: 16 },
820 DateTime { year: 2000, month: 2, month_day: 29, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 951825666, nanoseconds: 17 },
821 DateTime { year: 2000, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 951912066, nanoseconds: 18 },
822 DateTime { year: 2001, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 983448066, nanoseconds: 19 },
823 DateTime { year: 2004, month: 2, month_day: 29, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 1078056066, nanoseconds: 20 },
824 DateTime { year: 2004, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 1078142466, nanoseconds: 21 },
825 DateTime { year: 2100, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 4107585666, nanoseconds: 22 },
826 DateTime { year: 3001, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 32540356866, nanoseconds: 23 },
827 ];
828
829 for ((((&unix_time, &nanoseconds), date_time_utc), date_time_cet), date_time_eet) in
830 unix_times.iter().zip(nanoseconds_list).zip(date_times_utc).zip(date_times_cet).zip(date_times_eet)
831 {
832 let utc_date_time = UtcDateTime::from_timespec(unix_time, nanoseconds)?;
833
834 assert_eq!(UtcDateTime::from_timespec(utc_date_time.unix_time(), nanoseconds)?, utc_date_time);
835
836 assert_eq!(utc_date_time.year(), date_time_utc.year());
837 assert_eq!(utc_date_time.month(), date_time_utc.month());
838 assert_eq!(utc_date_time.month_day(), date_time_utc.month_day());
839 assert_eq!(utc_date_time.hour(), date_time_utc.hour());
840 assert_eq!(utc_date_time.minute(), date_time_utc.minute());
841 assert_eq!(utc_date_time.second(), date_time_utc.second());
842 assert_eq!(utc_date_time.nanoseconds(), date_time_utc.nanoseconds());
843
844 assert_eq!(utc_date_time.unix_time(), unix_time);
845 assert_eq!(date_time_utc.unix_time(), unix_time);
846 assert_eq!(date_time_cet.unix_time(), unix_time);
847 assert_eq!(date_time_eet.unix_time(), unix_time);
848
849 assert_eq!(date_time_utc, date_time_cet);
850 assert_eq!(date_time_utc, date_time_eet);
851
852 check_equal_date_time(&utc_date_time.project(time_zone_utc.as_ref())?, date_time_utc);
853 check_equal_date_time(&utc_date_time.project(time_zone_cet.as_ref())?, date_time_cet);
854 check_equal_date_time(&utc_date_time.project(time_zone_eet.as_ref())?, date_time_eet);
855
856 check_equal_date_time(&date_time_utc.project(time_zone_utc.as_ref())?, date_time_utc);
857 check_equal_date_time(&date_time_cet.project(time_zone_utc.as_ref())?, date_time_utc);
858 check_equal_date_time(&date_time_eet.project(time_zone_utc.as_ref())?, date_time_utc);
859
860 check_equal_date_time(&date_time_utc.project(time_zone_cet.as_ref())?, date_time_cet);
861 check_equal_date_time(&date_time_cet.project(time_zone_cet.as_ref())?, date_time_cet);
862 check_equal_date_time(&date_time_eet.project(time_zone_cet.as_ref())?, date_time_cet);
863
864 check_equal_date_time(&date_time_utc.project(time_zone_eet.as_ref())?, date_time_eet);
865 check_equal_date_time(&date_time_cet.project(time_zone_eet.as_ref())?, date_time_eet);
866 check_equal_date_time(&date_time_eet.project(time_zone_eet.as_ref())?, date_time_eet);
867 }
868
869 Ok(())
870 }
871
872 #[cfg(feature = "alloc")]
873 #[test]
874 fn test_date_time_leap_seconds() -> Result<(), TzError> {
875 let utc_date_time = UtcDateTime::new(1972, 6, 30, 23, 59, 60, 1000)?;
876
877 assert_eq!(UtcDateTime::from_timespec(utc_date_time.unix_time(), 1000)?, UtcDateTime::new(1972, 7, 1, 0, 0, 0, 1000)?);
878
879 let date_time = utc_date_time.project(TimeZone::fixed(-3600)?.as_ref())?;
880
881 let date_time_result = DateTime {
882 year: 1972,
883 month: 6,
884 month_day: 30,
885 hour: 23,
886 minute: 00,
887 second: 00,
888 local_time_type: LocalTimeType::with_ut_offset(-3600)?,
889 unix_time: 78796800,
890 nanoseconds: 1000,
891 };
892
893 check_equal_date_time(&date_time, &date_time_result);
894
895 Ok(())
896 }
897
898 #[cfg(feature = "alloc")]
899 #[test]
900 fn test_date_time_partial_eq_partial_ord() -> Result<(), TzError> {
901 let time_zone_utc = TimeZone::utc();
902 let time_zone_cet = TimeZone::fixed(3600)?;
903 let time_zone_eet = TimeZone::fixed(7200)?;
904
905 let utc_date_time_1 = UtcDateTime::from_timespec(1, 1)?;
906 let utc_date_time_2 = UtcDateTime::from_timespec(2, 1)?;
907 let utc_date_time_3 = UtcDateTime::from_timespec(3, 1)?;
908 let utc_date_time_4 = UtcDateTime::from_timespec(3, 1000)?;
909
910 let date_time_utc_1 = utc_date_time_1.project(time_zone_utc.as_ref())?;
911 let date_time_utc_2 = utc_date_time_2.project(time_zone_utc.as_ref())?;
912 let date_time_utc_3 = utc_date_time_3.project(time_zone_utc.as_ref())?;
913 let date_time_utc_4 = utc_date_time_4.project(time_zone_utc.as_ref())?;
914
915 let date_time_cet_1 = utc_date_time_1.project(time_zone_cet.as_ref())?;
916 let date_time_cet_2 = utc_date_time_2.project(time_zone_cet.as_ref())?;
917 let date_time_cet_3 = utc_date_time_3.project(time_zone_cet.as_ref())?;
918 let date_time_cet_4 = utc_date_time_4.project(time_zone_cet.as_ref())?;
919
920 let date_time_eet_1 = utc_date_time_1.project(time_zone_eet.as_ref())?;
921 let date_time_eet_2 = utc_date_time_2.project(time_zone_eet.as_ref())?;
922 let date_time_eet_3 = utc_date_time_3.project(time_zone_eet.as_ref())?;
923 let date_time_eet_4 = utc_date_time_4.project(time_zone_eet.as_ref())?;
924
925 assert_eq!(date_time_utc_1, date_time_cet_1);
926 assert_eq!(date_time_utc_1, date_time_eet_1);
927
928 assert_eq!(date_time_utc_2, date_time_cet_2);
929 assert_eq!(date_time_utc_2, date_time_eet_2);
930
931 assert_eq!(date_time_utc_3, date_time_cet_3);
932 assert_eq!(date_time_utc_3, date_time_eet_3);
933
934 assert_eq!(date_time_utc_4, date_time_cet_4);
935 assert_eq!(date_time_utc_4, date_time_eet_4);
936
937 assert_ne!(date_time_utc_1, date_time_utc_2);
938 assert_ne!(date_time_utc_1, date_time_utc_3);
939 assert_ne!(date_time_utc_1, date_time_utc_4);
940
941 assert_eq!(date_time_utc_1.partial_cmp(&date_time_cet_1), Some(Ordering::Equal));
942 assert_eq!(date_time_utc_1.partial_cmp(&date_time_eet_1), Some(Ordering::Equal));
943
944 assert_eq!(date_time_utc_2.partial_cmp(&date_time_cet_2), Some(Ordering::Equal));
945 assert_eq!(date_time_utc_2.partial_cmp(&date_time_eet_2), Some(Ordering::Equal));
946
947 assert_eq!(date_time_utc_3.partial_cmp(&date_time_cet_3), Some(Ordering::Equal));
948 assert_eq!(date_time_utc_3.partial_cmp(&date_time_eet_3), Some(Ordering::Equal));
949
950 assert_eq!(date_time_utc_4.partial_cmp(&date_time_cet_4), Some(Ordering::Equal));
951 assert_eq!(date_time_utc_4.partial_cmp(&date_time_eet_4), Some(Ordering::Equal));
952
953 assert_eq!(date_time_utc_1.partial_cmp(&date_time_utc_2), Some(Ordering::Less));
954 assert_eq!(date_time_utc_2.partial_cmp(&date_time_utc_3), Some(Ordering::Less));
955 assert_eq!(date_time_utc_3.partial_cmp(&date_time_utc_4), Some(Ordering::Less));
956
957 Ok(())
958 }
959
960 #[test]
961 fn test_date_time_sync_and_send() {
962 trait _AssertSyncSendStatic: Sync + Send + 'static {}
963 impl _AssertSyncSendStatic for DateTime {}
964 }
965
966 #[test]
967 fn test_utc_date_time_ord() -> Result<(), TzError> {
968 let utc_date_time_1 = UtcDateTime::new(1972, 6, 30, 23, 59, 59, 1000)?;
969 let utc_date_time_2 = UtcDateTime::new(1972, 6, 30, 23, 59, 60, 1000)?;
970 let utc_date_time_3 = UtcDateTime::new(1972, 7, 1, 0, 0, 0, 1000)?;
971 let utc_date_time_4 = UtcDateTime::new(1972, 7, 1, 0, 0, 0, 1001)?;
972
973 assert_eq!(utc_date_time_1.cmp(&utc_date_time_1), Ordering::Equal);
974 assert_eq!(utc_date_time_1.cmp(&utc_date_time_2), Ordering::Less);
975 assert_eq!(utc_date_time_1.cmp(&utc_date_time_3), Ordering::Less);
976 assert_eq!(utc_date_time_1.cmp(&utc_date_time_4), Ordering::Less);
977
978 assert_eq!(utc_date_time_2.cmp(&utc_date_time_1), Ordering::Greater);
979 assert_eq!(utc_date_time_2.cmp(&utc_date_time_2), Ordering::Equal);
980 assert_eq!(utc_date_time_2.cmp(&utc_date_time_3), Ordering::Less);
981 assert_eq!(utc_date_time_2.cmp(&utc_date_time_4), Ordering::Less);
982
983 assert_eq!(utc_date_time_3.cmp(&utc_date_time_1), Ordering::Greater);
984 assert_eq!(utc_date_time_3.cmp(&utc_date_time_2), Ordering::Greater);
985 assert_eq!(utc_date_time_3.cmp(&utc_date_time_3), Ordering::Equal);
986 assert_eq!(utc_date_time_3.cmp(&utc_date_time_4), Ordering::Less);
987
988 assert_eq!(utc_date_time_4.cmp(&utc_date_time_1), Ordering::Greater);
989 assert_eq!(utc_date_time_4.cmp(&utc_date_time_2), Ordering::Greater);
990 assert_eq!(utc_date_time_4.cmp(&utc_date_time_3), Ordering::Greater);
991 assert_eq!(utc_date_time_4.cmp(&utc_date_time_4), Ordering::Equal);
992
993 assert_eq!(utc_date_time_1.cmp(&utc_date_time_1), utc_date_time_1.unix_time().cmp(&utc_date_time_1.unix_time()));
994 assert_eq!(utc_date_time_1.cmp(&utc_date_time_2), utc_date_time_1.unix_time().cmp(&utc_date_time_2.unix_time()));
995 assert_eq!(utc_date_time_1.cmp(&utc_date_time_3), utc_date_time_1.unix_time().cmp(&utc_date_time_3.unix_time()));
996 assert_eq!(utc_date_time_1.cmp(&utc_date_time_4), utc_date_time_1.unix_time().cmp(&utc_date_time_4.unix_time()));
997
998 assert_eq!(utc_date_time_2.cmp(&utc_date_time_1), utc_date_time_2.unix_time().cmp(&utc_date_time_1.unix_time()));
999 assert_eq!(utc_date_time_2.cmp(&utc_date_time_2), utc_date_time_2.unix_time().cmp(&utc_date_time_2.unix_time()));
1000
1001 assert_eq!(utc_date_time_3.cmp(&utc_date_time_1), utc_date_time_3.unix_time().cmp(&utc_date_time_1.unix_time()));
1002 assert_eq!(utc_date_time_3.cmp(&utc_date_time_3), utc_date_time_3.unix_time().cmp(&utc_date_time_3.unix_time()));
1003
1004 assert_eq!(utc_date_time_4.cmp(&utc_date_time_1), utc_date_time_4.unix_time().cmp(&utc_date_time_1.unix_time()));
1005 assert_eq!(utc_date_time_4.cmp(&utc_date_time_4), utc_date_time_4.unix_time().cmp(&utc_date_time_4.unix_time()));
1006
1007 Ok(())
1008 }
1009
1010 #[cfg(feature = "alloc")]
1011 #[test]
1012 fn test_date_time_format() -> Result<(), TzError> {
1013 use alloc::string::ToString;
1014
1015 let time_zones = [
1016 TimeZone::fixed(-49550)?,
1017 TimeZone::fixed(-5400)?,
1018 TimeZone::fixed(-3600)?,
1019 TimeZone::fixed(-1800)?,
1020 TimeZone::fixed(0)?,
1021 TimeZone::fixed(1800)?,
1022 TimeZone::fixed(3600)?,
1023 TimeZone::fixed(5400)?,
1024 TimeZone::fixed(49550)?,
1025 ];
1026
1027 let utc_date_times = &[UtcDateTime::new(2000, 1, 2, 3, 4, 5, 0)?, UtcDateTime::new(2000, 1, 2, 3, 4, 5, 123_456_789)?];
1028
1029 let utc_date_time_strings = &["2000-01-02T03:04:05.000000000Z", "2000-01-02T03:04:05.123456789Z"];
1030
1031 let date_time_strings_list = &[
1032 &[
1033 "2000-01-01T13:18:15.000000000-13:45:50",
1034 "2000-01-02T01:34:05.000000000-01:30",
1035 "2000-01-02T02:04:05.000000000-01:00",
1036 "2000-01-02T02:34:05.000000000-00:30",
1037 "2000-01-02T03:04:05.000000000Z",
1038 "2000-01-02T03:34:05.000000000+00:30",
1039 "2000-01-02T04:04:05.000000000+01:00",
1040 "2000-01-02T04:34:05.000000000+01:30",
1041 "2000-01-02T16:49:55.000000000+13:45:50",
1042 ],
1043 &[
1044 "2000-01-01T13:18:15.123456789-13:45:50",
1045 "2000-01-02T01:34:05.123456789-01:30",
1046 "2000-01-02T02:04:05.123456789-01:00",
1047 "2000-01-02T02:34:05.123456789-00:30",
1048 "2000-01-02T03:04:05.123456789Z",
1049 "2000-01-02T03:34:05.123456789+00:30",
1050 "2000-01-02T04:04:05.123456789+01:00",
1051 "2000-01-02T04:34:05.123456789+01:30",
1052 "2000-01-02T16:49:55.123456789+13:45:50",
1053 ],
1054 ];
1055
1056 for ((utc_date_time, &utc_date_time_string), &date_time_strings) in utc_date_times.iter().zip(utc_date_time_strings).zip(date_time_strings_list) {
1057 for (time_zone, &date_time_string) in time_zones.iter().zip(date_time_strings) {
1058 assert_eq!(utc_date_time.to_string(), utc_date_time_string);
1059 assert_eq!(utc_date_time.project(time_zone.as_ref())?.to_string(), date_time_string);
1060 }
1061 }
1062
1063 Ok(())
1064 }
1065
1066 #[cfg(feature = "alloc")]
1067 #[test]
1068 fn test_date_time_overflow() -> Result<(), TzError> {
1069 assert!(UtcDateTime::new(i32::MIN, 1, 1, 0, 0, 0, 0).is_ok());
1070 assert!(UtcDateTime::new(i32::MAX, 12, 31, 23, 59, 59, 0).is_ok());
1071
1072 assert!(DateTime::new(i32::MIN, 1, 1, 0, 0, 0, 0, LocalTimeType::utc()).is_ok());
1073 assert!(DateTime::new(i32::MAX, 12, 31, 23, 59, 59, 0, LocalTimeType::utc()).is_ok());
1074
1075 assert!(matches!(DateTime::new(i32::MIN, 1, 1, 0, 0, 0, 0, LocalTimeType::with_ut_offset(1)?), Err(TzError::OutOfRange)));
1076 assert!(matches!(DateTime::new(i32::MAX, 12, 31, 23, 59, 59, 0, LocalTimeType::with_ut_offset(-1)?), Err(TzError::OutOfRange)));
1077
1078 assert!(matches!(UtcDateTime::new(i32::MAX, 12, 31, 23, 59, 60, 0), Err(TzError::OutOfRange)));
1079 assert!(matches!(DateTime::new(i32::MAX, 12, 31, 23, 59, 60, 0, LocalTimeType::utc()), Err(TzError::OutOfRange)));
1080 assert!(DateTime::new(i32::MAX, 12, 31, 23, 59, 60, 0, LocalTimeType::with_ut_offset(1)?).is_ok());
1081
1082 assert!(UtcDateTime::from_timespec(UtcDateTime::MIN_UNIX_TIME, 0).is_ok());
1083 assert!(UtcDateTime::from_timespec(UtcDateTime::MAX_UNIX_TIME, 0).is_ok());
1084
1085 assert!(matches!(UtcDateTime::from_timespec(UtcDateTime::MIN_UNIX_TIME - 1, 0), Err(TzError::OutOfRange)));
1086 assert!(matches!(UtcDateTime::from_timespec(UtcDateTime::MAX_UNIX_TIME + 1, 0), Err(TzError::OutOfRange)));
1087
1088 assert!(matches!(UtcDateTime::from_timespec(UtcDateTime::MIN_UNIX_TIME, 0)?.project(TimeZone::fixed(-1)?.as_ref()), Err(TzError::OutOfRange)));
1089 assert!(matches!(UtcDateTime::from_timespec(UtcDateTime::MAX_UNIX_TIME, 0)?.project(TimeZone::fixed(1)?.as_ref()), Err(TzError::OutOfRange)));
1090
1091 assert!(matches!(UtcDateTime::from_timespec(i64::MIN, 0), Err(TzError::OutOfRange)));
1092 assert!(matches!(UtcDateTime::from_timespec(i64::MAX, 0), Err(TzError::OutOfRange)));
1093
1094 assert!(DateTime::from_timespec(UtcDateTime::MIN_UNIX_TIME, 0, TimeZone::fixed(0)?.as_ref()).is_ok());
1095 assert!(DateTime::from_timespec(UtcDateTime::MAX_UNIX_TIME, 0, TimeZone::fixed(0)?.as_ref()).is_ok());
1096
1097 assert!(matches!(DateTime::from_timespec(i64::MIN, 0, TimeZone::fixed(-1)?.as_ref()), Err(TzError::OutOfRange)));
1098 assert!(matches!(DateTime::from_timespec(i64::MAX, 0, TimeZone::fixed(1)?.as_ref()), Err(TzError::OutOfRange)));
1099
1100 Ok(())
1101 }
1102
1103 #[test]
1104 fn test_week_day() {
1105 assert_eq!(week_day(1970, 1, 1), 4);
1106
1107 assert_eq!(week_day(2000, 1, 1), 6);
1108 assert_eq!(week_day(2000, 2, 28), 1);
1109 assert_eq!(week_day(2000, 2, 29), 2);
1110 assert_eq!(week_day(2000, 3, 1), 3);
1111 assert_eq!(week_day(2000, 12, 31), 0);
1112
1113 assert_eq!(week_day(2001, 1, 1), 1);
1114 assert_eq!(week_day(2001, 2, 28), 3);
1115 assert_eq!(week_day(2001, 3, 1), 4);
1116 assert_eq!(week_day(2001, 12, 31), 1);
1117 }
1118
1119 #[test]
1120 fn test_year_day() {
1121 assert_eq!(year_day(2000, 1, 1), 0);
1122 assert_eq!(year_day(2000, 2, 28), 58);
1123 assert_eq!(year_day(2000, 2, 29), 59);
1124 assert_eq!(year_day(2000, 3, 1), 60);
1125 assert_eq!(year_day(2000, 12, 31), 365);
1126
1127 assert_eq!(year_day(2001, 1, 1), 0);
1128 assert_eq!(year_day(2001, 2, 28), 58);
1129 assert_eq!(year_day(2001, 3, 1), 59);
1130 assert_eq!(year_day(2001, 12, 31), 364);
1131 }
1132
1133 #[test]
1134 fn test_is_leap_year() {
1135 assert!(is_leap_year(2000));
1136 assert!(!is_leap_year(2001));
1137 assert!(is_leap_year(2004));
1138 assert!(!is_leap_year(2100));
1139 assert!(!is_leap_year(2200));
1140 assert!(!is_leap_year(2300));
1141 assert!(is_leap_year(2400));
1142 }
1143
1144 #[test]
1145 fn test_days_since_unix_epoch() {
1146 assert_eq!(days_since_unix_epoch(-1001, 3, 1), -1085076);
1147 assert_eq!(days_since_unix_epoch(1600, 2, 29), -135081);
1148 assert_eq!(days_since_unix_epoch(1600, 3, 1), -135080);
1149 assert_eq!(days_since_unix_epoch(1700, 3, 1), -98556);
1150 assert_eq!(days_since_unix_epoch(1701, 3, 1), -98191);
1151 assert_eq!(days_since_unix_epoch(1704, 2, 29), -97096);
1152 assert_eq!(days_since_unix_epoch(2000, 2, 29), 11016);
1153 assert_eq!(days_since_unix_epoch(2000, 3, 1), 11017);
1154 assert_eq!(days_since_unix_epoch(2001, 3, 1), 11382);
1155 assert_eq!(days_since_unix_epoch(2004, 2, 29), 12477);
1156 assert_eq!(days_since_unix_epoch(2100, 3, 1), 47541);
1157 assert_eq!(days_since_unix_epoch(3001, 3, 1), 376624);
1158 }
1159
1160 #[test]
1161 fn test_nanoseconds_since_unix_epoch() {
1162 assert_eq!(nanoseconds_since_unix_epoch(1, 1000), 1_000_001_000);
1163 assert_eq!(nanoseconds_since_unix_epoch(0, 1000), 1000);
1164 assert_eq!(nanoseconds_since_unix_epoch(-1, 1000), -999_999_000);
1165 assert_eq!(nanoseconds_since_unix_epoch(-2, 1000), -1_999_999_000);
1166 }
1167
1168 #[test]
1169 fn test_total_nanoseconds_to_timespec() -> Result<(), TzError> {
1170 assert!(matches!(total_nanoseconds_to_timespec(1_000_001_000), Ok((1, 1000))));
1171 assert!(matches!(total_nanoseconds_to_timespec(1000), Ok((0, 1000))));
1172 assert!(matches!(total_nanoseconds_to_timespec(-999_999_000), Ok((-1, 1000))));
1173 assert!(matches!(total_nanoseconds_to_timespec(-1_999_999_000), Ok((-2, 1000))));
1174
1175 assert!(matches!(total_nanoseconds_to_timespec(i128::MAX), Err(TzError::OutOfRange)));
1176 assert!(matches!(total_nanoseconds_to_timespec(i128::MIN), Err(TzError::OutOfRange)));
1177
1178 let min_total_nanoseconds = -9223372036854775808000000000;
1179 let max_total_nanoseconds = 9223372036854775807999999999;
1180
1181 assert!(matches!(total_nanoseconds_to_timespec(min_total_nanoseconds), Ok((i64::MIN, 0))));
1182 assert!(matches!(total_nanoseconds_to_timespec(max_total_nanoseconds), Ok((i64::MAX, 999999999))));
1183
1184 assert!(matches!(total_nanoseconds_to_timespec(min_total_nanoseconds - 1), Err(TzError::OutOfRange)));
1185 assert!(matches!(total_nanoseconds_to_timespec(max_total_nanoseconds + 1), Err(TzError::OutOfRange)));
1186
1187 Ok(())
1188 }
1189
1190 #[test]
1191 fn test_const() -> Result<(), TzError> {
1192 use crate::timezone::{AlternateTime, LeapSecond, MonthWeekDay, RuleDay, Transition, TransitionRule};
1193
1194 macro_rules! unwrap {
1195 ($x:expr) => {
1196 match $x {
1197 Ok(x) => x,
1198 Err(_) => panic!(),
1199 }
1200 };
1201 }
1202
1203 const TIME_ZONE_REF: TimeZoneRef<'static> = unwrap!(TimeZoneRef::new(
1204 &[
1205 Transition::new(-2334101314, 1),
1206 Transition::new(-1157283000, 2),
1207 Transition::new(-1155436200, 1),
1208 Transition::new(-880198200, 3),
1209 Transition::new(-769395600, 4),
1210 Transition::new(-765376200, 1),
1211 Transition::new(-712150200, 5),
1212 ],
1213 const {
1214 &[
1215 unwrap!(LocalTimeType::new(-37886, false, Some(b"LMT"))),
1216 unwrap!(LocalTimeType::new(-37800, false, Some(b"HST"))),
1217 unwrap!(LocalTimeType::new(-34200, true, Some(b"HDT"))),
1218 unwrap!(LocalTimeType::new(-34200, true, Some(b"HWT"))),
1219 unwrap!(LocalTimeType::new(-34200, true, Some(b"HPT"))),
1220 unwrap!(LocalTimeType::new(-36000, false, Some(b"HST"))),
1221 ]
1222 },
1223 &[
1224 LeapSecond::new(78796800, 1),
1225 LeapSecond::new(94694401, 2),
1226 LeapSecond::new(126230402, 3),
1227 LeapSecond::new(157766403, 4),
1228 LeapSecond::new(189302404, 5),
1229 LeapSecond::new(220924805, 6),
1230 ],
1231 const {
1232 &Some(TransitionRule::Alternate(unwrap!(AlternateTime::new(
1233 unwrap!(LocalTimeType::new(-36000, false, Some(b"HST"))),
1234 unwrap!(LocalTimeType::new(-34200, true, Some(b"HPT"))),
1235 RuleDay::MonthWeekDay(unwrap!(MonthWeekDay::new(10, 5, 0))),
1236 93600,
1237 RuleDay::MonthWeekDay(unwrap!(MonthWeekDay::new(3, 4, 4))),
1238 7200,
1239 ))))
1240 },
1241 ));
1242
1243 const UTC: TimeZoneRef<'static> = TimeZoneRef::utc();
1244
1245 const UNIX_EPOCH: UtcDateTime = unwrap!(UtcDateTime::from_timespec(0, 0));
1246 const UTC_DATE_TIME: UtcDateTime = unwrap!(UtcDateTime::new(2000, 1, 1, 0, 0, 0, 1000));
1247
1248 const DATE_TIME: DateTime = unwrap!(DateTime::new(2000, 1, 1, 1, 0, 0, 1000, unwrap!(LocalTimeType::with_ut_offset(3600))));
1249
1250 const DATE_TIME_1: DateTime = unwrap!(UTC_DATE_TIME.project(TIME_ZONE_REF));
1251 const DATE_TIME_2: DateTime = unwrap!(DATE_TIME_1.project(UTC));
1252
1253 const LOCAL_TIME_TYPE_1: &LocalTimeType = DATE_TIME_1.local_time_type();
1254 const LOCAL_TIME_TYPE_2: &LocalTimeType = DATE_TIME_2.local_time_type();
1255
1256 assert_eq!(UNIX_EPOCH.unix_time(), 0);
1257 assert_eq!(DATE_TIME.unix_time(), UTC_DATE_TIME.unix_time());
1258 assert_eq!(DATE_TIME_2.unix_time(), UTC_DATE_TIME.unix_time());
1259 assert_eq!(DATE_TIME_2.nanoseconds(), UTC_DATE_TIME.nanoseconds());
1260
1261 let date_time = UTC_DATE_TIME.project(TIME_ZONE_REF)?;
1262 assert_eq!(date_time.local_time_type().time_zone_designation(), LOCAL_TIME_TYPE_1.time_zone_designation());
1263
1264 let date_time_1 = DateTime::from_timespec(UTC_DATE_TIME.unix_time(), 1000, TIME_ZONE_REF)?;
1265 let date_time_2 = date_time_1.project(UTC)?;
1266
1267 assert_eq!(date_time, DATE_TIME_1);
1268 assert_eq!(date_time_1, DATE_TIME_1);
1269 assert_eq!(date_time_2, DATE_TIME_2);
1270
1271 let local_time_type_1 = date_time_1.local_time_type();
1272 let local_time_type_2 = date_time_2.local_time_type();
1273
1274 assert_eq!(local_time_type_1.ut_offset(), LOCAL_TIME_TYPE_1.ut_offset());
1275 assert_eq!(local_time_type_1.is_dst(), LOCAL_TIME_TYPE_1.is_dst());
1276 assert_eq!(local_time_type_1.time_zone_designation(), LOCAL_TIME_TYPE_1.time_zone_designation());
1277
1278 assert_eq!(local_time_type_2.ut_offset(), LOCAL_TIME_TYPE_2.ut_offset());
1279 assert_eq!(local_time_type_2.is_dst(), LOCAL_TIME_TYPE_2.is_dst());
1280 assert_eq!(local_time_type_2.time_zone_designation(), LOCAL_TIME_TYPE_2.time_zone_designation());
1281
1282 Ok(())
1283 }
1284}