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;
20use core::ops::{Add, AddAssign, Sub, SubAssign};
21use core::time::Duration;
22#[cfg(feature = "std")]
23use std::time::SystemTime;
24
25#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
27pub struct UtcDateTime {
28 year: i32,
30 month: u8,
32 month_day: u8,
34 hour: u8,
36 minute: u8,
38 second: u8,
40 nanoseconds: u32,
42}
43
44impl fmt::Display for UtcDateTime {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 format_date_time(f, self.year, self.month, self.month_day, self.hour, self.minute, self.second, self.nanoseconds, 0)
47 }
48}
49
50impl UtcDateTime {
51 pub const UNIX_EPOCH: Self = Self { year: 1970, month: 1, month_day: 1, hour: 0, minute: 0, second: 0, nanoseconds: 0 };
53
54 pub const MIN: Self = Self { year: i32::MIN, month: 1, month_day: 1, hour: 0, minute: 0, second: 0, nanoseconds: 0 };
56
57 pub const MAX: Self = Self { year: i32::MAX, month: 12, month_day: 31, hour: 23, minute: 59, second: 59, nanoseconds: 999_999_999 };
59
60 const MIN_UNIX_TIME: i64 = UtcDateTime::MIN.unix_time();
62
63 const MAX_UNIX_TIME: i64 = UtcDateTime::MAX.unix_time();
65
66 const fn check_unix_time(unix_time: i64) -> Result<(), TzError> {
68 if Self::MIN_UNIX_TIME <= unix_time && unix_time <= Self::MAX_UNIX_TIME { Ok(()) } else { Err(TzError::OutOfRange) }
69 }
70
71 pub const fn new(year: i32, month: u8, month_day: u8, hour: u8, minute: u8, second: u8, nanoseconds: u32) -> Result<Self, TzError> {
84 if year == i32::MAX && month == 12 && month_day == 31 && hour == 23 && minute == 59 && second == 60 {
86 return Err(TzError::OutOfRange);
87 }
88
89 if let Err(error) = check_date_time_inputs(year, month, month_day, hour, minute, second, nanoseconds) {
90 return Err(TzError::DateTime(error));
91 }
92
93 Ok(Self { year, month, month_day, hour, minute, second, nanoseconds })
94 }
95
96 pub const fn from_timespec(unix_time: i64, nanoseconds: u32) -> Result<Self, TzError> {
98 let seconds = match unix_time.checked_sub(UNIX_OFFSET_SECS) {
99 Some(seconds) => seconds,
100 None => return Err(TzError::OutOfRange),
101 };
102
103 let mut remaining_days = seconds / SECONDS_PER_DAY;
104 let mut remaining_seconds = seconds % SECONDS_PER_DAY;
105 if remaining_seconds < 0 {
106 remaining_seconds += SECONDS_PER_DAY;
107 remaining_days -= 1;
108 }
109
110 let mut cycles_400_years = remaining_days / DAYS_PER_400_YEARS;
111 remaining_days %= DAYS_PER_400_YEARS;
112 if remaining_days < 0 {
113 remaining_days += DAYS_PER_400_YEARS;
114 cycles_400_years -= 1;
115 }
116
117 let cycles_100_years = min(remaining_days / DAYS_PER_100_YEARS, 3);
118 remaining_days -= cycles_100_years * DAYS_PER_100_YEARS;
119
120 let cycles_4_years = min(remaining_days / DAYS_PER_4_YEARS, 24);
121 remaining_days -= cycles_4_years * DAYS_PER_4_YEARS;
122
123 let remaining_years = min(remaining_days / DAYS_PER_NORMAL_YEAR, 3);
124 remaining_days -= remaining_years * DAYS_PER_NORMAL_YEAR;
125
126 let mut year = OFFSET_YEAR + remaining_years + cycles_4_years * 4 + cycles_100_years * 100 + cycles_400_years * 400;
127
128 let mut month = 0;
129 while month < DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH.len() {
130 let days = DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH[month];
131 if remaining_days < days {
132 break;
133 }
134 remaining_days -= days;
135 month += 1;
136 }
137 month += 2;
138
139 if month >= MONTHS_PER_YEAR as usize {
140 month -= MONTHS_PER_YEAR as usize;
141 year += 1;
142 }
143 month += 1;
144
145 let month_day = 1 + remaining_days;
146
147 let hour = remaining_seconds / SECONDS_PER_HOUR;
148 let minute = (remaining_seconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
149 let second = remaining_seconds % SECONDS_PER_MINUTE;
150
151 let year = match try_into_i32(year) {
152 Ok(year) => year,
153 Err(error) => return Err(error),
154 };
155
156 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 })
157 }
158
159 pub const fn from_total_nanoseconds(total_nanoseconds: i128) -> Result<Self, TzError> {
161 match total_nanoseconds_to_timespec(total_nanoseconds) {
162 Ok((unix_time, nanoseconds)) => Self::from_timespec(unix_time, nanoseconds),
163 Err(error) => Err(error),
164 }
165 }
166
167 pub const fn unix_time(&self) -> i64 {
169 unix_time(self.year, self.month, self.month_day, self.hour, self.minute, self.second)
170 }
171
172 pub const fn project(&self, time_zone_ref: TimeZoneRef<'_>) -> Result<DateTime, TzError> {
177 DateTime::from_timespec(self.unix_time(), self.nanoseconds, time_zone_ref)
178 }
179
180 pub const fn checked_add(&self, duration: Duration) -> Option<Self> {
182 let total_nanoseconds = self.total_nanoseconds() + duration.as_nanos() as i128;
184
185 match Self::from_total_nanoseconds(total_nanoseconds) {
186 Ok(x) => Some(x),
187 Err(_) => None,
188 }
189 }
190
191 pub const fn checked_sub(&self, duration: Duration) -> Option<Self> {
193 let total_nanoseconds = self.total_nanoseconds() - duration.as_nanos() as i128;
195
196 match Self::from_total_nanoseconds(total_nanoseconds) {
197 Ok(x) => Some(x),
198 Err(_) => None,
199 }
200 }
201
202 pub const fn duration_since(&self, earlier: Self) -> Result<Duration, Duration> {
207 let current_total_nanoseconds = self.total_nanoseconds();
208 let earlier_total_nanoseconds = earlier.total_nanoseconds();
209
210 duration_between(earlier_total_nanoseconds, current_total_nanoseconds)
211 }
212
213 #[cfg(feature = "std")]
215 pub fn now() -> Result<Self, TzError> {
216 SystemTime::now().try_into()
217 }
218}
219
220#[cfg(feature = "std")]
221impl TryFrom<SystemTime> for UtcDateTime {
222 type Error = TzError;
223
224 fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
225 Self::from_total_nanoseconds(crate::utils::system_time::total_nanoseconds(time))
226 }
227}
228
229#[cfg(feature = "std")]
230impl From<UtcDateTime> for SystemTime {
231 fn from(utc_date_time: UtcDateTime) -> Self {
232 match duration_between(0, utc_date_time.total_nanoseconds()) {
233 Ok(duration) => SystemTime::UNIX_EPOCH + duration,
234 Err(duration) => SystemTime::UNIX_EPOCH - duration,
235 }
236 }
237}
238
239impl Add<Duration> for UtcDateTime {
240 type Output = UtcDateTime;
241
242 fn add(self, rhs: Duration) -> Self::Output {
248 self.checked_add(rhs).expect("attempt to add with overflow")
249 }
250}
251
252impl AddAssign<Duration> for UtcDateTime {
253 fn add_assign(&mut self, rhs: Duration) {
254 *self = *self + rhs
255 }
256}
257
258impl Sub<Duration> for UtcDateTime {
259 type Output = UtcDateTime;
260
261 fn sub(self, rhs: Duration) -> Self::Output {
267 self.checked_sub(rhs).expect("attempt to subtract with overflow")
268 }
269}
270
271impl SubAssign<Duration> for UtcDateTime {
272 fn sub_assign(&mut self, rhs: Duration) {
273 *self = *self - rhs
274 }
275}
276
277#[derive(Debug, Copy, Clone)]
279pub struct DateTime {
280 year: i32,
282 month: u8,
284 month_day: u8,
286 hour: u8,
288 minute: u8,
290 second: u8,
292 local_time_type: LocalTimeType,
294 unix_time: i64,
296 nanoseconds: u32,
298}
299
300impl PartialEq for DateTime {
301 fn eq(&self, other: &Self) -> bool {
302 (self.unix_time, self.nanoseconds) == (other.unix_time, other.nanoseconds)
303 }
304}
305
306impl Eq for DateTime {}
307
308impl PartialOrd for DateTime {
309 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
310 Some(self.cmp(other))
311 }
312}
313
314impl Ord for DateTime {
315 fn cmp(&self, other: &Self) -> Ordering {
316 (self.unix_time, self.nanoseconds).cmp(&(other.unix_time, other.nanoseconds))
317 }
318}
319
320impl fmt::Display for DateTime {
321 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
322 let ut_offset = self.local_time_type().ut_offset();
323 format_date_time(f, self.year, self.month, self.month_day, self.hour, self.minute, self.second, self.nanoseconds, ut_offset)
324 }
325}
326
327impl DateTime {
328 #[allow(clippy::too_many_arguments)]
342 pub const fn new(
343 year: i32,
344 month: u8,
345 month_day: u8,
346 hour: u8,
347 minute: u8,
348 second: u8,
349 nanoseconds: u32,
350 local_time_type: LocalTimeType,
351 ) -> Result<Self, TzError> {
352 if let Err(error) = check_date_time_inputs(year, month, month_day, hour, minute, second, nanoseconds) {
353 return Err(TzError::DateTime(error));
354 }
355
356 let unix_time = unix_time(year, month, month_day, hour, minute, second) - local_time_type.ut_offset() as i64;
358
359 if let Err(error) = UtcDateTime::check_unix_time(unix_time) {
361 return Err(error);
362 }
363
364 Ok(Self { year, month, month_day, hour, minute, second, local_time_type, unix_time, nanoseconds })
365 }
366
367 #[allow(clippy::too_many_arguments)]
381 #[cfg(feature = "alloc")]
382 pub fn find(
383 year: i32,
384 month: u8,
385 month_day: u8,
386 hour: u8,
387 minute: u8,
388 second: u8,
389 nanoseconds: u32,
390 time_zone_ref: TimeZoneRef<'_>,
391 ) -> Result<FoundDateTimeList, TzError> {
392 let mut found_date_time_list = FoundDateTimeList::default();
393 find_date_time(&mut found_date_time_list, year, month, month_day, hour, minute, second, nanoseconds, time_zone_ref)?;
394 Ok(found_date_time_list)
395 }
396
397 #[allow(clippy::too_many_arguments)]
449 pub fn find_n<'a>(
450 buf: &'a mut [Option<FoundDateTimeKind>],
451 year: i32,
452 month: u8,
453 month_day: u8,
454 hour: u8,
455 minute: u8,
456 second: u8,
457 nanoseconds: u32,
458 time_zone_ref: TimeZoneRef<'_>,
459 ) -> Result<FoundDateTimeListRefMut<'a>, TzError> {
460 let mut found_date_time_list = FoundDateTimeListRefMut::new(buf);
461 find_date_time(&mut found_date_time_list, year, month, month_day, hour, minute, second, nanoseconds, time_zone_ref)?;
462 Ok(found_date_time_list)
463 }
464
465 pub const fn from_timespec_and_local(unix_time: i64, nanoseconds: u32, local_time_type: LocalTimeType) -> Result<Self, TzError> {
467 let unix_time_with_offset = match unix_time.checked_add(local_time_type.ut_offset() as i64) {
468 Some(unix_time_with_offset) => unix_time_with_offset,
469 None => return Err(TzError::OutOfRange),
470 };
471
472 let utc_date_time_with_offset = match UtcDateTime::from_timespec(unix_time_with_offset, nanoseconds) {
473 Ok(utc_date_time_with_offset) => utc_date_time_with_offset,
474 Err(error) => return Err(error),
475 };
476
477 let UtcDateTime { year, month, month_day, hour, minute, second, nanoseconds } = utc_date_time_with_offset;
478 Ok(Self { year, month, month_day, hour, minute, second, local_time_type, unix_time, nanoseconds })
479 }
480
481 pub const fn from_timespec(unix_time: i64, nanoseconds: u32, time_zone_ref: TimeZoneRef<'_>) -> Result<Self, TzError> {
483 let local_time_type = match time_zone_ref.find_local_time_type(unix_time) {
484 Ok(&local_time_type) => local_time_type,
485 Err(error) => return Err(error),
486 };
487
488 Self::from_timespec_and_local(unix_time, nanoseconds, local_time_type)
489 }
490
491 pub const fn from_total_nanoseconds_and_local(total_nanoseconds: i128, local_time_type: LocalTimeType) -> Result<Self, TzError> {
493 match total_nanoseconds_to_timespec(total_nanoseconds) {
494 Ok((unix_time, nanoseconds)) => Self::from_timespec_and_local(unix_time, nanoseconds, local_time_type),
495 Err(error) => Err(error),
496 }
497 }
498
499 pub const fn from_total_nanoseconds(total_nanoseconds: i128, time_zone_ref: TimeZoneRef<'_>) -> Result<Self, TzError> {
501 match total_nanoseconds_to_timespec(total_nanoseconds) {
502 Ok((unix_time, nanoseconds)) => Self::from_timespec(unix_time, nanoseconds, time_zone_ref),
503 Err(error) => Err(error),
504 }
505 }
506
507 pub const fn project(&self, time_zone_ref: TimeZoneRef<'_>) -> Result<Self, TzError> {
512 Self::from_timespec(self.unix_time, self.nanoseconds, time_zone_ref)
513 }
514
515 pub const fn duration_since(&self, earlier: Self) -> Result<Duration, Duration> {
520 let current_total_nanoseconds = self.total_nanoseconds();
521 let earlier_total_nanoseconds = earlier.total_nanoseconds();
522
523 duration_between(earlier_total_nanoseconds, current_total_nanoseconds)
524 }
525
526 #[cfg(feature = "std")]
528 pub fn now(time_zone_ref: TimeZoneRef<'_>) -> Result<Self, TzError> {
529 let now = crate::utils::system_time::total_nanoseconds(SystemTime::now());
530 Self::from_total_nanoseconds(now, time_zone_ref)
531 }
532}
533
534impl TryFrom<DateTime> for UtcDateTime {
535 type Error = TzError;
536
537 fn try_from(date_time: DateTime) -> Result<Self, Self::Error> {
538 Self::from_timespec(date_time.unix_time, date_time.nanoseconds)
539 }
540}
541
542#[cfg(feature = "std")]
543impl From<DateTime> for SystemTime {
544 fn from(date_time: DateTime) -> Self {
545 match duration_between(0, date_time.total_nanoseconds()) {
546 Ok(duration) => SystemTime::UNIX_EPOCH + duration,
547 Err(duration) => SystemTime::UNIX_EPOCH - duration,
548 }
549 }
550}
551
552macro_rules! impl_datetime {
554 () => {
555 #[inline]
557 pub const fn year(&self) -> i32 {
558 self.year
559 }
560
561 #[inline]
563 pub const fn month(&self) -> u8 {
564 self.month
565 }
566
567 #[inline]
569 pub const fn month_day(&self) -> u8 {
570 self.month_day
571 }
572
573 #[inline]
575 pub const fn hour(&self) -> u8 {
576 self.hour
577 }
578
579 #[inline]
581 pub const fn minute(&self) -> u8 {
582 self.minute
583 }
584
585 #[inline]
587 pub const fn second(&self) -> u8 {
588 self.second
589 }
590
591 #[inline]
593 pub const fn nanoseconds(&self) -> u32 {
594 self.nanoseconds
595 }
596
597 #[inline]
599 pub const fn week_day(&self) -> u8 {
600 week_day(self.year, self.month as usize, self.month_day as i64)
601 }
602
603 #[inline]
605 pub const fn year_day(&self) -> u16 {
606 year_day(self.year, self.month as usize, self.month_day as i64)
607 }
608
609 #[inline]
611 pub const fn total_nanoseconds(&self) -> i128 {
612 nanoseconds_since_unix_epoch(self.unix_time(), self.nanoseconds)
613 }
614 };
615}
616
617impl UtcDateTime {
618 impl_datetime!();
619}
620
621impl DateTime {
622 impl_datetime!();
623
624 #[inline]
626 pub const fn local_time_type(&self) -> &LocalTimeType {
627 &self.local_time_type
628 }
629
630 #[inline]
632 pub const fn unix_time(&self) -> i64 {
633 self.unix_time
634 }
635}
636
637#[inline]
646const fn week_day(year: i32, month: usize, month_day: i64) -> u8 {
647 let days_since_unix_epoch = days_since_unix_epoch(year, month, month_day);
648 (4 + days_since_unix_epoch).rem_euclid(DAYS_PER_WEEK) as u8
649}
650
651#[inline]
660const fn year_day(year: i32, month: usize, month_day: i64) -> u16 {
661 let leap = (month >= 3 && is_leap_year(year)) as i64;
662 (CUMUL_DAYS_IN_MONTHS_NORMAL_YEAR[month - 1] + leap + month_day - 1) as u16
663}
664
665#[inline]
667pub(crate) const fn is_leap_year(year: i32) -> bool {
668 year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
669}
670
671#[inline]
682pub(crate) const fn days_since_unix_epoch(year: i32, month: usize, month_day: i64) -> i64 {
683 let is_leap_year = is_leap_year(year);
684
685 let year = year as i64;
686
687 let mut result = (year - 1970) * 365;
688
689 if year >= 1970 {
690 result += (year - 1968) / 4;
691 result -= (year - 1900) / 100;
692 result += (year - 1600) / 400;
693
694 if is_leap_year && month < 3 {
695 result -= 1;
696 }
697 } else {
698 result += (year - 1972) / 4;
699 result -= (year - 2000) / 100;
700 result += (year - 2000) / 400;
701
702 if is_leap_year && month >= 3 {
703 result += 1;
704 }
705 }
706
707 result += CUMUL_DAYS_IN_MONTHS_NORMAL_YEAR[month - 1] + month_day - 1;
708
709 result
710}
711
712#[inline]
724const fn unix_time(year: i32, month: u8, month_day: u8, hour: u8, minute: u8, second: u8) -> i64 {
725 let mut result = days_since_unix_epoch(year, month as usize, month_day as i64);
726 result *= HOURS_PER_DAY;
727 result += hour as i64;
728 result *= MINUTES_PER_HOUR;
729 result += minute as i64;
730 result *= SECONDS_PER_MINUTE;
731 result += second as i64;
732
733 result
734}
735
736#[inline]
738const fn nanoseconds_since_unix_epoch(unix_time: i64, nanoseconds: u32) -> i128 {
739 unix_time as i128 * NANOSECONDS_PER_SECOND as i128 + nanoseconds as i128
741}
742
743#[inline]
751const fn total_nanoseconds_to_timespec(total_nanoseconds: i128) -> Result<(i64, u32), TzError> {
752 let unix_time = match try_into_i64(total_nanoseconds.div_euclid(NANOSECONDS_PER_SECOND as i128)) {
753 Ok(unix_time) => unix_time,
754 Err(error) => return Err(error),
755 };
756
757 let nanoseconds = total_nanoseconds.rem_euclid(NANOSECONDS_PER_SECOND as i128) as u32;
758
759 Ok((unix_time, nanoseconds))
760}
761
762#[inline]
767const fn duration_between(before: i128, after: i128) -> Result<Duration, Duration> {
768 let total_nanoseconds_diff = after.abs_diff(before);
770
771 let secs = total_nanoseconds_diff / NANOSECONDS_PER_SECOND as u128;
772 let nanos = total_nanoseconds_diff % NANOSECONDS_PER_SECOND as u128;
773
774 let duration = Duration::new(secs as u64, nanos as u32);
776
777 if after >= before { Ok(duration) } else { Err(duration) }
778}
779
780const fn check_date_time_inputs(year: i32, month: u8, month_day: u8, hour: u8, minute: u8, second: u8, nanoseconds: u32) -> Result<(), DateTimeError> {
793 if !(1 <= month && month <= 12) {
794 return Err(DateTimeError::InvalidMonth);
795 }
796 if !(1 <= month_day && month_day <= 31) {
797 return Err(DateTimeError::InvalidMonthDay);
798 }
799 if hour > 23 {
800 return Err(DateTimeError::InvalidHour);
801 }
802 if minute > 59 {
803 return Err(DateTimeError::InvalidMinute);
804 }
805 if second > 60 {
806 return Err(DateTimeError::InvalidSecond);
807 }
808 if nanoseconds >= NANOSECONDS_PER_SECOND {
809 return Err(DateTimeError::InvalidNanoseconds);
810 }
811
812 let leap = is_leap_year(year) as i64;
813
814 let mut days_in_month = DAYS_IN_MONTHS_NORMAL_YEAR[month as usize - 1];
815 if month == 2 {
816 days_in_month += leap;
817 }
818
819 if month_day as i64 > days_in_month {
820 return Err(DateTimeError::InvalidMonthDay);
821 }
822
823 Ok(())
824}
825
826#[allow(clippy::too_many_arguments)]
841fn format_date_time(
842 f: &mut fmt::Formatter,
843 year: i32,
844 month: u8,
845 month_day: u8,
846 hour: u8,
847 minute: u8,
848 second: u8,
849 nanoseconds: u32,
850 ut_offset: i32,
851) -> fmt::Result {
852 write!(f, "{year}-{month:02}-{month_day:02}T{hour:02}:{minute:02}:{second:02}.{nanoseconds:09}")?;
853
854 if ut_offset != 0 {
855 let ut_offset = ut_offset as i64;
856 let ut_offset_abs = ut_offset.abs();
857
858 let sign = if ut_offset < 0 { '-' } else { '+' };
859
860 let offset_hour = ut_offset_abs / SECONDS_PER_HOUR;
861 let offset_minute = (ut_offset_abs / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
862 let offset_second = ut_offset_abs % SECONDS_PER_MINUTE;
863
864 write!(f, "{sign}{offset_hour:02}:{offset_minute:02}")?;
865
866 if offset_second != 0 {
867 write!(f, ":{offset_second:02}")?;
868 }
869 } else {
870 write!(f, "Z")?;
871 }
872
873 Ok(())
874}
875
876#[cfg(test)]
877mod tests {
878 use super::*;
879
880 #[cfg(feature = "alloc")]
881 use crate::timezone::TimeZone;
882
883 #[cfg(feature = "alloc")]
884 pub(super) fn check_equal_date_time(x: &DateTime, y: &DateTime) {
885 assert_eq!(x.year(), y.year());
886 assert_eq!(x.month(), y.month());
887 assert_eq!(x.month_day(), y.month_day());
888 assert_eq!(x.hour(), y.hour());
889 assert_eq!(x.minute(), y.minute());
890 assert_eq!(x.second(), y.second());
891 assert_eq!(x.local_time_type(), y.local_time_type());
892 assert_eq!(x.unix_time(), y.unix_time());
893 assert_eq!(x.nanoseconds(), y.nanoseconds());
894 }
895
896 #[cfg(feature = "alloc")]
897 #[test]
898 fn test_date_time() -> Result<(), TzError> {
899 let time_zone_utc = TimeZone::utc();
900 let utc = LocalTimeType::utc();
901
902 let time_zone_cet = TimeZone::fixed(3600)?;
903 let cet = LocalTimeType::with_ut_offset(3600)?;
904
905 let time_zone_eet = TimeZone::fixed(7200)?;
906 let eet = LocalTimeType::with_ut_offset(7200)?;
907
908 #[cfg(feature = "std")]
909 {
910 assert_eq!(DateTime::now(time_zone_utc.as_ref())?.local_time_type().ut_offset(), 0);
911 assert_eq!(DateTime::now(time_zone_cet.as_ref())?.local_time_type().ut_offset(), 3600);
912 assert_eq!(DateTime::now(time_zone_eet.as_ref())?.local_time_type().ut_offset(), 7200);
913 }
914
915 let unix_times = &[
916 -93750523134,
917 -11670955134,
918 -11670868734,
919 -8515195134,
920 -8483659134,
921 -8389051134,
922 -8388964734,
923 951825666,
924 951912066,
925 983448066,
926 1078056066,
927 1078142466,
928 4107585666,
929 32540356866,
930 ];
931
932 let nanoseconds_list = &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23];
933
934 #[rustfmt::skip]
935 let date_times_utc = &[
936 DateTime { year: -1001, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -93750523134, nanoseconds: 10 },
937 DateTime { year: 1600, month: 2, month_day: 29, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -11670955134, nanoseconds: 11 },
938 DateTime { year: 1600, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -11670868734, nanoseconds: 12 },
939 DateTime { year: 1700, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -8515195134, nanoseconds: 13 },
940 DateTime { year: 1701, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -8483659134, nanoseconds: 14 },
941 DateTime { year: 1704, month: 2, month_day: 29, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -8389051134, nanoseconds: 15 },
942 DateTime { year: 1704, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: -8388964734, nanoseconds: 16 },
943 DateTime { year: 2000, month: 2, month_day: 29, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 951825666, nanoseconds: 17 },
944 DateTime { year: 2000, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 951912066, nanoseconds: 18 },
945 DateTime { year: 2001, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 983448066, nanoseconds: 19 },
946 DateTime { year: 2004, month: 2, month_day: 29, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 1078056066, nanoseconds: 20 },
947 DateTime { year: 2004, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 1078142466, nanoseconds: 21 },
948 DateTime { year: 2100, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 4107585666, nanoseconds: 22 },
949 DateTime { year: 3001, month: 3, month_day: 1, hour: 12, minute: 1, second: 6, local_time_type: utc, unix_time: 32540356866, nanoseconds: 23 },
950 ];
951
952 #[rustfmt::skip]
953 let date_times_cet = &[
954 DateTime { year: -1001, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -93750523134, nanoseconds: 10 },
955 DateTime { year: 1600, month: 2, month_day: 29, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -11670955134, nanoseconds: 11 },
956 DateTime { year: 1600, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -11670868734, nanoseconds: 12 },
957 DateTime { year: 1700, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -8515195134, nanoseconds: 13 },
958 DateTime { year: 1701, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -8483659134, nanoseconds: 14 },
959 DateTime { year: 1704, month: 2, month_day: 29, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -8389051134, nanoseconds: 15 },
960 DateTime { year: 1704, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: -8388964734, nanoseconds: 16 },
961 DateTime { year: 2000, month: 2, month_day: 29, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 951825666, nanoseconds: 17 },
962 DateTime { year: 2000, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 951912066, nanoseconds: 18 },
963 DateTime { year: 2001, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 983448066, nanoseconds: 19 },
964 DateTime { year: 2004, month: 2, month_day: 29, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 1078056066, nanoseconds: 20 },
965 DateTime { year: 2004, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 1078142466, nanoseconds: 21 },
966 DateTime { year: 2100, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 4107585666, nanoseconds: 22 },
967 DateTime { year: 3001, month: 3, month_day: 1, hour: 13, minute: 1, second: 6, local_time_type: cet, unix_time: 32540356866, nanoseconds: 23 },
968 ];
969
970 #[rustfmt::skip]
971 let date_times_eet = &[
972 DateTime { year: -1001, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -93750523134, nanoseconds: 10 },
973 DateTime { year: 1600, month: 2, month_day: 29, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -11670955134, nanoseconds: 11 },
974 DateTime { year: 1600, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -11670868734, nanoseconds: 12 },
975 DateTime { year: 1700, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -8515195134, nanoseconds: 13 },
976 DateTime { year: 1701, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -8483659134, nanoseconds: 14 },
977 DateTime { year: 1704, month: 2, month_day: 29, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -8389051134, nanoseconds: 15 },
978 DateTime { year: 1704, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: -8388964734, nanoseconds: 16 },
979 DateTime { year: 2000, month: 2, month_day: 29, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 951825666, nanoseconds: 17 },
980 DateTime { year: 2000, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 951912066, nanoseconds: 18 },
981 DateTime { year: 2001, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 983448066, nanoseconds: 19 },
982 DateTime { year: 2004, month: 2, month_day: 29, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 1078056066, nanoseconds: 20 },
983 DateTime { year: 2004, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 1078142466, nanoseconds: 21 },
984 DateTime { year: 2100, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 4107585666, nanoseconds: 22 },
985 DateTime { year: 3001, month: 3, month_day: 1, hour: 14, minute: 1, second: 6, local_time_type: eet, unix_time: 32540356866, nanoseconds: 23 },
986 ];
987
988 for ((((&unix_time, &nanoseconds), date_time_utc), date_time_cet), date_time_eet) in
989 unix_times.iter().zip(nanoseconds_list).zip(date_times_utc).zip(date_times_cet).zip(date_times_eet)
990 {
991 let utc_date_time = UtcDateTime::from_timespec(unix_time, nanoseconds)?;
992
993 assert_eq!(UtcDateTime::from_timespec(utc_date_time.unix_time(), nanoseconds)?, utc_date_time);
994
995 assert_eq!(utc_date_time.year(), date_time_utc.year());
996 assert_eq!(utc_date_time.month(), date_time_utc.month());
997 assert_eq!(utc_date_time.month_day(), date_time_utc.month_day());
998 assert_eq!(utc_date_time.hour(), date_time_utc.hour());
999 assert_eq!(utc_date_time.minute(), date_time_utc.minute());
1000 assert_eq!(utc_date_time.second(), date_time_utc.second());
1001 assert_eq!(utc_date_time.nanoseconds(), date_time_utc.nanoseconds());
1002
1003 assert_eq!(utc_date_time.unix_time(), unix_time);
1004 assert_eq!(date_time_utc.unix_time(), unix_time);
1005 assert_eq!(date_time_cet.unix_time(), unix_time);
1006 assert_eq!(date_time_eet.unix_time(), unix_time);
1007
1008 assert_eq!(date_time_utc, date_time_cet);
1009 assert_eq!(date_time_utc, date_time_eet);
1010
1011 check_equal_date_time(&utc_date_time.project(time_zone_utc.as_ref())?, date_time_utc);
1012 check_equal_date_time(&utc_date_time.project(time_zone_cet.as_ref())?, date_time_cet);
1013 check_equal_date_time(&utc_date_time.project(time_zone_eet.as_ref())?, date_time_eet);
1014
1015 check_equal_date_time(&date_time_utc.project(time_zone_utc.as_ref())?, date_time_utc);
1016 check_equal_date_time(&date_time_cet.project(time_zone_utc.as_ref())?, date_time_utc);
1017 check_equal_date_time(&date_time_eet.project(time_zone_utc.as_ref())?, date_time_utc);
1018
1019 check_equal_date_time(&date_time_utc.project(time_zone_cet.as_ref())?, date_time_cet);
1020 check_equal_date_time(&date_time_cet.project(time_zone_cet.as_ref())?, date_time_cet);
1021 check_equal_date_time(&date_time_eet.project(time_zone_cet.as_ref())?, date_time_cet);
1022
1023 check_equal_date_time(&date_time_utc.project(time_zone_eet.as_ref())?, date_time_eet);
1024 check_equal_date_time(&date_time_cet.project(time_zone_eet.as_ref())?, date_time_eet);
1025 check_equal_date_time(&date_time_eet.project(time_zone_eet.as_ref())?, date_time_eet);
1026 }
1027
1028 Ok(())
1029 }
1030
1031 #[cfg(feature = "alloc")]
1032 #[test]
1033 fn test_date_time_leap_seconds() -> Result<(), TzError> {
1034 let utc_date_time = UtcDateTime::new(1972, 6, 30, 23, 59, 60, 1000)?;
1035
1036 assert_eq!(UtcDateTime::from_timespec(utc_date_time.unix_time(), 1000)?, UtcDateTime::new(1972, 7, 1, 0, 0, 0, 1000)?);
1037
1038 let date_time = utc_date_time.project(TimeZone::fixed(-3600)?.as_ref())?;
1039
1040 let date_time_result = DateTime {
1041 year: 1972,
1042 month: 6,
1043 month_day: 30,
1044 hour: 23,
1045 minute: 00,
1046 second: 00,
1047 local_time_type: LocalTimeType::with_ut_offset(-3600)?,
1048 unix_time: 78796800,
1049 nanoseconds: 1000,
1050 };
1051
1052 check_equal_date_time(&date_time, &date_time_result);
1053
1054 Ok(())
1055 }
1056
1057 #[cfg(feature = "alloc")]
1058 #[test]
1059 fn test_date_time_partial_eq_partial_ord() -> Result<(), TzError> {
1060 let time_zone_utc = TimeZone::utc();
1061 let time_zone_cet = TimeZone::fixed(3600)?;
1062 let time_zone_eet = TimeZone::fixed(7200)?;
1063
1064 let utc_date_time_1 = UtcDateTime::from_timespec(1, 1)?;
1065 let utc_date_time_2 = UtcDateTime::from_timespec(2, 1)?;
1066 let utc_date_time_3 = UtcDateTime::from_timespec(3, 1)?;
1067 let utc_date_time_4 = UtcDateTime::from_timespec(3, 1000)?;
1068
1069 let date_time_utc_1 = utc_date_time_1.project(time_zone_utc.as_ref())?;
1070 let date_time_utc_2 = utc_date_time_2.project(time_zone_utc.as_ref())?;
1071 let date_time_utc_3 = utc_date_time_3.project(time_zone_utc.as_ref())?;
1072 let date_time_utc_4 = utc_date_time_4.project(time_zone_utc.as_ref())?;
1073
1074 let date_time_cet_1 = utc_date_time_1.project(time_zone_cet.as_ref())?;
1075 let date_time_cet_2 = utc_date_time_2.project(time_zone_cet.as_ref())?;
1076 let date_time_cet_3 = utc_date_time_3.project(time_zone_cet.as_ref())?;
1077 let date_time_cet_4 = utc_date_time_4.project(time_zone_cet.as_ref())?;
1078
1079 let date_time_eet_1 = utc_date_time_1.project(time_zone_eet.as_ref())?;
1080 let date_time_eet_2 = utc_date_time_2.project(time_zone_eet.as_ref())?;
1081 let date_time_eet_3 = utc_date_time_3.project(time_zone_eet.as_ref())?;
1082 let date_time_eet_4 = utc_date_time_4.project(time_zone_eet.as_ref())?;
1083
1084 assert_eq!(date_time_utc_1, date_time_cet_1);
1085 assert_eq!(date_time_utc_1, date_time_eet_1);
1086
1087 assert_eq!(date_time_utc_2, date_time_cet_2);
1088 assert_eq!(date_time_utc_2, date_time_eet_2);
1089
1090 assert_eq!(date_time_utc_3, date_time_cet_3);
1091 assert_eq!(date_time_utc_3, date_time_eet_3);
1092
1093 assert_eq!(date_time_utc_4, date_time_cet_4);
1094 assert_eq!(date_time_utc_4, date_time_eet_4);
1095
1096 assert_ne!(date_time_utc_1, date_time_utc_2);
1097 assert_ne!(date_time_utc_1, date_time_utc_3);
1098 assert_ne!(date_time_utc_1, date_time_utc_4);
1099
1100 assert_eq!(date_time_utc_1.partial_cmp(&date_time_cet_1), Some(Ordering::Equal));
1101 assert_eq!(date_time_utc_1.partial_cmp(&date_time_eet_1), Some(Ordering::Equal));
1102
1103 assert_eq!(date_time_utc_2.partial_cmp(&date_time_cet_2), Some(Ordering::Equal));
1104 assert_eq!(date_time_utc_2.partial_cmp(&date_time_eet_2), Some(Ordering::Equal));
1105
1106 assert_eq!(date_time_utc_3.partial_cmp(&date_time_cet_3), Some(Ordering::Equal));
1107 assert_eq!(date_time_utc_3.partial_cmp(&date_time_eet_3), Some(Ordering::Equal));
1108
1109 assert_eq!(date_time_utc_4.partial_cmp(&date_time_cet_4), Some(Ordering::Equal));
1110 assert_eq!(date_time_utc_4.partial_cmp(&date_time_eet_4), Some(Ordering::Equal));
1111
1112 assert_eq!(date_time_utc_1.partial_cmp(&date_time_utc_2), Some(Ordering::Less));
1113 assert_eq!(date_time_utc_2.partial_cmp(&date_time_utc_3), Some(Ordering::Less));
1114 assert_eq!(date_time_utc_3.partial_cmp(&date_time_utc_4), Some(Ordering::Less));
1115
1116 Ok(())
1117 }
1118
1119 #[test]
1120 fn test_date_time_sync_and_send() {
1121 trait _AssertSyncSendStatic: Sync + Send + 'static {}
1122 impl _AssertSyncSendStatic for DateTime {}
1123 }
1124
1125 #[cfg(feature = "alloc")]
1126 #[test]
1127 fn test_date_time_ord() -> Result<(), TzError> {
1128 let utc_date_time_1 = UtcDateTime::new(1972, 6, 30, 23, 59, 59, 1000)?;
1129 let utc_date_time_2 = UtcDateTime::new(1972, 6, 30, 23, 59, 60, 1000)?;
1130 let utc_date_time_3 = UtcDateTime::new(1972, 7, 1, 0, 0, 0, 1000)?;
1131 let utc_date_time_4 = UtcDateTime::new(1972, 7, 1, 0, 0, 0, 1001)?;
1132
1133 assert_eq!(utc_date_time_1.cmp(&utc_date_time_1), Ordering::Equal);
1134 assert_eq!(utc_date_time_1.cmp(&utc_date_time_2), Ordering::Less);
1135 assert_eq!(utc_date_time_1.cmp(&utc_date_time_3), Ordering::Less);
1136 assert_eq!(utc_date_time_1.cmp(&utc_date_time_4), Ordering::Less);
1137
1138 assert_eq!(utc_date_time_2.cmp(&utc_date_time_1), Ordering::Greater);
1139 assert_eq!(utc_date_time_2.cmp(&utc_date_time_2), Ordering::Equal);
1140 assert_eq!(utc_date_time_2.cmp(&utc_date_time_3), Ordering::Less);
1141 assert_eq!(utc_date_time_2.cmp(&utc_date_time_4), Ordering::Less);
1142
1143 assert_eq!(utc_date_time_3.cmp(&utc_date_time_1), Ordering::Greater);
1144 assert_eq!(utc_date_time_3.cmp(&utc_date_time_2), Ordering::Greater);
1145 assert_eq!(utc_date_time_3.cmp(&utc_date_time_3), Ordering::Equal);
1146 assert_eq!(utc_date_time_3.cmp(&utc_date_time_4), Ordering::Less);
1147
1148 assert_eq!(utc_date_time_4.cmp(&utc_date_time_1), Ordering::Greater);
1149 assert_eq!(utc_date_time_4.cmp(&utc_date_time_2), Ordering::Greater);
1150 assert_eq!(utc_date_time_4.cmp(&utc_date_time_3), Ordering::Greater);
1151 assert_eq!(utc_date_time_4.cmp(&utc_date_time_4), Ordering::Equal);
1152
1153 assert_eq!(utc_date_time_1.cmp(&utc_date_time_1), utc_date_time_1.unix_time().cmp(&utc_date_time_1.unix_time()));
1154 assert_eq!(utc_date_time_1.cmp(&utc_date_time_2), utc_date_time_1.unix_time().cmp(&utc_date_time_2.unix_time()));
1155 assert_eq!(utc_date_time_1.cmp(&utc_date_time_3), utc_date_time_1.unix_time().cmp(&utc_date_time_3.unix_time()));
1156 assert_eq!(utc_date_time_1.cmp(&utc_date_time_4), utc_date_time_1.unix_time().cmp(&utc_date_time_4.unix_time()));
1157
1158 assert_eq!(utc_date_time_2.cmp(&utc_date_time_1), utc_date_time_2.unix_time().cmp(&utc_date_time_1.unix_time()));
1159 assert_eq!(utc_date_time_2.cmp(&utc_date_time_2), utc_date_time_2.unix_time().cmp(&utc_date_time_2.unix_time()));
1160
1161 assert_eq!(utc_date_time_3.cmp(&utc_date_time_1), utc_date_time_3.unix_time().cmp(&utc_date_time_1.unix_time()));
1162 assert_eq!(utc_date_time_3.cmp(&utc_date_time_3), utc_date_time_3.unix_time().cmp(&utc_date_time_3.unix_time()));
1163
1164 assert_eq!(utc_date_time_4.cmp(&utc_date_time_1), utc_date_time_4.unix_time().cmp(&utc_date_time_1.unix_time()));
1165 assert_eq!(utc_date_time_4.cmp(&utc_date_time_4), utc_date_time_4.unix_time().cmp(&utc_date_time_4.unix_time()));
1166
1167 let date_time_1 = utc_date_time_1.project(TimeZone::fixed(0)?.as_ref())?;
1168 let date_time_2 = utc_date_time_2.project(TimeZone::fixed(3600)?.as_ref())?;
1169 let date_time_3 = utc_date_time_3.project(TimeZone::fixed(7200)?.as_ref())?;
1170 let date_time_4 = utc_date_time_4.project(TimeZone::fixed(10800)?.as_ref())?;
1171
1172 assert_eq!(date_time_1.cmp(&date_time_1), Ordering::Equal);
1173 assert_eq!(date_time_1.cmp(&date_time_2), Ordering::Less);
1174 assert_eq!(date_time_1.cmp(&date_time_3), Ordering::Less);
1175 assert_eq!(date_time_1.cmp(&date_time_4), Ordering::Less);
1176
1177 assert_eq!(date_time_2.cmp(&date_time_1), Ordering::Greater);
1178 assert_eq!(date_time_2.cmp(&date_time_2), Ordering::Equal);
1179 assert_eq!(date_time_2.cmp(&date_time_3), Ordering::Equal);
1180 assert_eq!(date_time_2.cmp(&date_time_4), Ordering::Less);
1181
1182 assert_eq!(date_time_3.cmp(&date_time_1), Ordering::Greater);
1183 assert_eq!(date_time_3.cmp(&date_time_2), Ordering::Equal);
1184 assert_eq!(date_time_3.cmp(&date_time_3), Ordering::Equal);
1185 assert_eq!(date_time_3.cmp(&date_time_4), Ordering::Less);
1186
1187 assert_eq!(date_time_4.cmp(&date_time_1), Ordering::Greater);
1188 assert_eq!(date_time_4.cmp(&date_time_2), Ordering::Greater);
1189 assert_eq!(date_time_4.cmp(&date_time_3), Ordering::Greater);
1190 assert_eq!(date_time_4.cmp(&date_time_4), Ordering::Equal);
1191
1192 Ok(())
1193 }
1194
1195 #[cfg(feature = "alloc")]
1196 #[test]
1197 fn test_date_time_format() -> Result<(), TzError> {
1198 use alloc::string::ToString;
1199
1200 let time_zones = [
1201 TimeZone::fixed(-49550)?,
1202 TimeZone::fixed(-5400)?,
1203 TimeZone::fixed(-3600)?,
1204 TimeZone::fixed(-1800)?,
1205 TimeZone::fixed(0)?,
1206 TimeZone::fixed(1800)?,
1207 TimeZone::fixed(3600)?,
1208 TimeZone::fixed(5400)?,
1209 TimeZone::fixed(49550)?,
1210 ];
1211
1212 let utc_date_times = &[UtcDateTime::new(2000, 1, 2, 3, 4, 5, 0)?, UtcDateTime::new(2000, 1, 2, 3, 4, 5, 123_456_789)?];
1213
1214 let utc_date_time_strings = &["2000-01-02T03:04:05.000000000Z", "2000-01-02T03:04:05.123456789Z"];
1215
1216 let date_time_strings_list = &[
1217 &[
1218 "2000-01-01T13:18:15.000000000-13:45:50",
1219 "2000-01-02T01:34:05.000000000-01:30",
1220 "2000-01-02T02:04:05.000000000-01:00",
1221 "2000-01-02T02:34:05.000000000-00:30",
1222 "2000-01-02T03:04:05.000000000Z",
1223 "2000-01-02T03:34:05.000000000+00:30",
1224 "2000-01-02T04:04:05.000000000+01:00",
1225 "2000-01-02T04:34:05.000000000+01:30",
1226 "2000-01-02T16:49:55.000000000+13:45:50",
1227 ],
1228 &[
1229 "2000-01-01T13:18:15.123456789-13:45:50",
1230 "2000-01-02T01:34:05.123456789-01:30",
1231 "2000-01-02T02:04:05.123456789-01:00",
1232 "2000-01-02T02:34:05.123456789-00:30",
1233 "2000-01-02T03:04:05.123456789Z",
1234 "2000-01-02T03:34:05.123456789+00:30",
1235 "2000-01-02T04:04:05.123456789+01:00",
1236 "2000-01-02T04:34:05.123456789+01:30",
1237 "2000-01-02T16:49:55.123456789+13:45:50",
1238 ],
1239 ];
1240
1241 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) {
1242 for (time_zone, &date_time_string) in time_zones.iter().zip(date_time_strings) {
1243 assert_eq!(utc_date_time.to_string(), utc_date_time_string);
1244 assert_eq!(utc_date_time.project(time_zone.as_ref())?.to_string(), date_time_string);
1245 }
1246 }
1247
1248 Ok(())
1249 }
1250
1251 #[cfg(feature = "alloc")]
1252 #[test]
1253 fn test_date_time_duration() -> Result<(), TzError> {
1254 let utc_date_time = UtcDateTime::new(1, 2, 3, 4, 5, 6, 789_012_345)?;
1255 let date_time = utc_date_time.project(TimeZoneRef::utc())?;
1256
1257 let duration = Duration::new(24 * 3600 * 365, 999_999_999);
1258
1259 let added_utc_date_time = UtcDateTime::new(2, 2, 3, 4, 5, 7, 789_012_344)?;
1260 let substracted_utc_date_time = UtcDateTime::new(0, 2, 4, 4, 5, 5, 789_012_346)?;
1261
1262 assert_eq!(utc_date_time.checked_add(duration), Some(added_utc_date_time));
1263 assert_eq!(utc_date_time.checked_sub(duration), Some(substracted_utc_date_time));
1264
1265 assert_eq!(utc_date_time.duration_since(substracted_utc_date_time), Ok(duration));
1266 assert_eq!(date_time.duration_since(substracted_utc_date_time.project(TimeZoneRef::utc())?), Ok(duration));
1267
1268 Ok(())
1269 }
1270
1271 #[cfg(feature = "alloc")]
1272 #[test]
1273 fn test_date_time_overflow() -> Result<(), TzError> {
1274 assert!(UtcDateTime::new(i32::MIN, 1, 1, 0, 0, 0, 0).is_ok());
1275 assert!(UtcDateTime::new(i32::MAX, 12, 31, 23, 59, 59, 0).is_ok());
1276
1277 assert!(DateTime::new(i32::MIN, 1, 1, 0, 0, 0, 0, LocalTimeType::utc()).is_ok());
1278 assert!(DateTime::new(i32::MAX, 12, 31, 23, 59, 59, 0, LocalTimeType::utc()).is_ok());
1279
1280 assert!(matches!(DateTime::new(i32::MIN, 1, 1, 0, 0, 0, 0, LocalTimeType::with_ut_offset(1)?), Err(TzError::OutOfRange)));
1281 assert!(matches!(DateTime::new(i32::MAX, 12, 31, 23, 59, 59, 0, LocalTimeType::with_ut_offset(-1)?), Err(TzError::OutOfRange)));
1282
1283 assert!(matches!(UtcDateTime::new(i32::MAX, 12, 31, 23, 59, 60, 0), Err(TzError::OutOfRange)));
1284 assert!(matches!(DateTime::new(i32::MAX, 12, 31, 23, 59, 60, 0, LocalTimeType::utc()), Err(TzError::OutOfRange)));
1285 assert!(DateTime::new(i32::MAX, 12, 31, 23, 59, 60, 0, LocalTimeType::with_ut_offset(1)?).is_ok());
1286
1287 assert!(UtcDateTime::from_timespec(UtcDateTime::MIN_UNIX_TIME, 0).is_ok());
1288 assert!(UtcDateTime::from_timespec(UtcDateTime::MAX_UNIX_TIME, 0).is_ok());
1289
1290 assert!(matches!(UtcDateTime::from_timespec(UtcDateTime::MIN_UNIX_TIME - 1, 0), Err(TzError::OutOfRange)));
1291 assert!(matches!(UtcDateTime::from_timespec(UtcDateTime::MAX_UNIX_TIME + 1, 0), Err(TzError::OutOfRange)));
1292
1293 assert!(matches!(UtcDateTime::from_timespec(UtcDateTime::MIN_UNIX_TIME, 0)?.project(TimeZone::fixed(-1)?.as_ref()), Err(TzError::OutOfRange)));
1294 assert!(matches!(UtcDateTime::from_timespec(UtcDateTime::MAX_UNIX_TIME, 0)?.project(TimeZone::fixed(1)?.as_ref()), Err(TzError::OutOfRange)));
1295
1296 assert!(matches!(UtcDateTime::from_timespec(i64::MIN, 0), Err(TzError::OutOfRange)));
1297 assert!(matches!(UtcDateTime::from_timespec(i64::MAX, 0), Err(TzError::OutOfRange)));
1298
1299 assert!(DateTime::from_timespec(UtcDateTime::MIN_UNIX_TIME, 0, TimeZone::fixed(0)?.as_ref()).is_ok());
1300 assert!(DateTime::from_timespec(UtcDateTime::MAX_UNIX_TIME, 0, TimeZone::fixed(0)?.as_ref()).is_ok());
1301
1302 assert!(matches!(DateTime::from_timespec(i64::MIN, 0, TimeZone::fixed(-1)?.as_ref()), Err(TzError::OutOfRange)));
1303 assert!(matches!(DateTime::from_timespec(i64::MAX, 0, TimeZone::fixed(1)?.as_ref()), Err(TzError::OutOfRange)));
1304
1305 assert_eq!(UtcDateTime::MIN.checked_sub(Duration::from_nanos(1)), None);
1306 assert_eq!(UtcDateTime::MAX.checked_add(Duration::from_nanos(1)), None);
1307
1308 assert_eq!(UtcDateTime::MIN.checked_sub(Duration::MAX), None);
1309 assert_eq!(UtcDateTime::MAX.checked_add(Duration::MAX), None);
1310
1311 assert_eq!(UtcDateTime::MAX.duration_since(UtcDateTime::MIN), Ok(Duration::new(135536076801503999, 999999999)));
1312 assert_eq!(UtcDateTime::MIN.duration_since(UtcDateTime::MAX), Err(Duration::new(135536076801503999, 999999999)));
1313
1314 Ok(())
1315 }
1316
1317 #[test]
1318 fn test_week_day() {
1319 assert_eq!(week_day(1970, 1, 1), 4);
1320
1321 assert_eq!(week_day(2000, 1, 1), 6);
1322 assert_eq!(week_day(2000, 2, 28), 1);
1323 assert_eq!(week_day(2000, 2, 29), 2);
1324 assert_eq!(week_day(2000, 3, 1), 3);
1325 assert_eq!(week_day(2000, 12, 31), 0);
1326
1327 assert_eq!(week_day(2001, 1, 1), 1);
1328 assert_eq!(week_day(2001, 2, 28), 3);
1329 assert_eq!(week_day(2001, 3, 1), 4);
1330 assert_eq!(week_day(2001, 12, 31), 1);
1331 }
1332
1333 #[test]
1334 fn test_year_day() {
1335 assert_eq!(year_day(2000, 1, 1), 0);
1336 assert_eq!(year_day(2000, 2, 28), 58);
1337 assert_eq!(year_day(2000, 2, 29), 59);
1338 assert_eq!(year_day(2000, 3, 1), 60);
1339 assert_eq!(year_day(2000, 12, 31), 365);
1340
1341 assert_eq!(year_day(2001, 1, 1), 0);
1342 assert_eq!(year_day(2001, 2, 28), 58);
1343 assert_eq!(year_day(2001, 3, 1), 59);
1344 assert_eq!(year_day(2001, 12, 31), 364);
1345 }
1346
1347 #[test]
1348 fn test_is_leap_year() {
1349 assert!(is_leap_year(2000));
1350 assert!(!is_leap_year(2001));
1351 assert!(is_leap_year(2004));
1352 assert!(!is_leap_year(2100));
1353 assert!(!is_leap_year(2200));
1354 assert!(!is_leap_year(2300));
1355 assert!(is_leap_year(2400));
1356 }
1357
1358 #[test]
1359 fn test_days_since_unix_epoch() {
1360 assert_eq!(days_since_unix_epoch(-1001, 3, 1), -1085076);
1361 assert_eq!(days_since_unix_epoch(1600, 2, 29), -135081);
1362 assert_eq!(days_since_unix_epoch(1600, 3, 1), -135080);
1363 assert_eq!(days_since_unix_epoch(1700, 3, 1), -98556);
1364 assert_eq!(days_since_unix_epoch(1701, 3, 1), -98191);
1365 assert_eq!(days_since_unix_epoch(1704, 2, 29), -97096);
1366 assert_eq!(days_since_unix_epoch(2000, 2, 29), 11016);
1367 assert_eq!(days_since_unix_epoch(2000, 3, 1), 11017);
1368 assert_eq!(days_since_unix_epoch(2001, 3, 1), 11382);
1369 assert_eq!(days_since_unix_epoch(2004, 2, 29), 12477);
1370 assert_eq!(days_since_unix_epoch(2100, 3, 1), 47541);
1371 assert_eq!(days_since_unix_epoch(3001, 3, 1), 376624);
1372 }
1373
1374 #[test]
1375 fn test_nanoseconds_since_unix_epoch() {
1376 assert_eq!(nanoseconds_since_unix_epoch(1, 1000), 1_000_001_000);
1377 assert_eq!(nanoseconds_since_unix_epoch(0, 1000), 1000);
1378 assert_eq!(nanoseconds_since_unix_epoch(-1, 1000), -999_999_000);
1379 assert_eq!(nanoseconds_since_unix_epoch(-2, 1000), -1_999_999_000);
1380 }
1381
1382 #[test]
1383 fn test_total_nanoseconds_to_timespec() -> Result<(), TzError> {
1384 assert!(matches!(total_nanoseconds_to_timespec(1_000_001_000), Ok((1, 1000))));
1385 assert!(matches!(total_nanoseconds_to_timespec(1000), Ok((0, 1000))));
1386 assert!(matches!(total_nanoseconds_to_timespec(-999_999_000), Ok((-1, 1000))));
1387 assert!(matches!(total_nanoseconds_to_timespec(-1_999_999_000), Ok((-2, 1000))));
1388
1389 assert!(matches!(total_nanoseconds_to_timespec(i128::MAX), Err(TzError::OutOfRange)));
1390 assert!(matches!(total_nanoseconds_to_timespec(i128::MIN), Err(TzError::OutOfRange)));
1391
1392 let min_total_nanoseconds = -9223372036854775808000000000;
1393 let max_total_nanoseconds = 9223372036854775807999999999;
1394
1395 assert!(matches!(total_nanoseconds_to_timespec(min_total_nanoseconds), Ok((i64::MIN, 0))));
1396 assert!(matches!(total_nanoseconds_to_timespec(max_total_nanoseconds), Ok((i64::MAX, 999999999))));
1397
1398 assert!(matches!(total_nanoseconds_to_timespec(min_total_nanoseconds - 1), Err(TzError::OutOfRange)));
1399 assert!(matches!(total_nanoseconds_to_timespec(max_total_nanoseconds + 1), Err(TzError::OutOfRange)));
1400
1401 Ok(())
1402 }
1403
1404 #[test]
1405 fn test_duration_between() -> Result<(), TzError> {
1406 assert_eq!(duration_between(1_234_567_890, 1_234_567_890), Ok(Duration::ZERO));
1407 assert_eq!(duration_between(-1_000_001_000, 1_000_001_000), Ok(Duration::new(2, 2000)));
1408 assert_eq!(duration_between(1_000_001_000, -1_000_001_000), Err(Duration::new(2, 2000)));
1409
1410 assert_eq!(
1411 duration_between(UtcDateTime::MIN.total_nanoseconds(), UtcDateTime::MAX.total_nanoseconds()),
1412 Ok(Duration::new(135536076801503999, 999999999))
1413 );
1414
1415 Ok(())
1416 }
1417
1418 #[test]
1419 fn test_const() -> Result<(), TzError> {
1420 use crate::timezone::{AlternateTime, LeapSecond, MonthWeekDay, RuleDay, Transition, TransitionRule};
1421
1422 macro_rules! unwrap {
1423 ($x:expr) => {
1424 match $x {
1425 Ok(x) => x,
1426 Err(_) => panic!(),
1427 }
1428 };
1429 }
1430
1431 const TIME_ZONE_REF: TimeZoneRef<'static> = unwrap!(TimeZoneRef::new(
1432 &[
1433 Transition::new(-2334101314, 1),
1434 Transition::new(-1157283000, 2),
1435 Transition::new(-1155436200, 1),
1436 Transition::new(-880198200, 3),
1437 Transition::new(-769395600, 4),
1438 Transition::new(-765376200, 1),
1439 Transition::new(-712150200, 5),
1440 ],
1441 const {
1442 &[
1443 unwrap!(LocalTimeType::new(-37886, false, Some(b"LMT"))),
1444 unwrap!(LocalTimeType::new(-37800, false, Some(b"HST"))),
1445 unwrap!(LocalTimeType::new(-34200, true, Some(b"HDT"))),
1446 unwrap!(LocalTimeType::new(-34200, true, Some(b"HWT"))),
1447 unwrap!(LocalTimeType::new(-34200, true, Some(b"HPT"))),
1448 unwrap!(LocalTimeType::new(-36000, false, Some(b"HST"))),
1449 ]
1450 },
1451 &[
1452 LeapSecond::new(78796800, 1),
1453 LeapSecond::new(94694401, 2),
1454 LeapSecond::new(126230402, 3),
1455 LeapSecond::new(157766403, 4),
1456 LeapSecond::new(189302404, 5),
1457 LeapSecond::new(220924805, 6),
1458 ],
1459 const {
1460 &Some(TransitionRule::Alternate(unwrap!(AlternateTime::new(
1461 unwrap!(LocalTimeType::new(-36000, false, Some(b"HST"))),
1462 unwrap!(LocalTimeType::new(-34200, true, Some(b"HPT"))),
1463 RuleDay::MonthWeekDay(unwrap!(MonthWeekDay::new(10, 5, 0))),
1464 93600,
1465 RuleDay::MonthWeekDay(unwrap!(MonthWeekDay::new(3, 4, 4))),
1466 7200,
1467 ))))
1468 },
1469 ));
1470
1471 const UTC: TimeZoneRef<'static> = TimeZoneRef::utc();
1472
1473 const UNIX_EPOCH: UtcDateTime = unwrap!(UtcDateTime::from_timespec(0, 0));
1474 const UTC_DATE_TIME: UtcDateTime = unwrap!(UtcDateTime::new(2000, 1, 1, 0, 0, 0, 1000));
1475
1476 const DATE_TIME: DateTime = unwrap!(DateTime::new(2000, 1, 1, 1, 0, 0, 1000, unwrap!(LocalTimeType::with_ut_offset(3600))));
1477
1478 const DATE_TIME_1: DateTime = unwrap!(UTC_DATE_TIME.project(TIME_ZONE_REF));
1479 const DATE_TIME_2: DateTime = unwrap!(DATE_TIME_1.project(UTC));
1480
1481 const LOCAL_TIME_TYPE_1: &LocalTimeType = DATE_TIME_1.local_time_type();
1482 const LOCAL_TIME_TYPE_2: &LocalTimeType = DATE_TIME_2.local_time_type();
1483
1484 assert_eq!(UNIX_EPOCH.unix_time(), 0);
1485 assert_eq!(DATE_TIME.unix_time(), UTC_DATE_TIME.unix_time());
1486 assert_eq!(DATE_TIME_2.unix_time(), UTC_DATE_TIME.unix_time());
1487 assert_eq!(DATE_TIME_2.nanoseconds(), UTC_DATE_TIME.nanoseconds());
1488
1489 let date_time = UTC_DATE_TIME.project(TIME_ZONE_REF)?;
1490 assert_eq!(date_time.local_time_type().time_zone_designation(), LOCAL_TIME_TYPE_1.time_zone_designation());
1491
1492 let date_time_1 = DateTime::from_timespec(UTC_DATE_TIME.unix_time(), 1000, TIME_ZONE_REF)?;
1493 let date_time_2 = date_time_1.project(UTC)?;
1494
1495 assert_eq!(date_time, DATE_TIME_1);
1496 assert_eq!(date_time_1, DATE_TIME_1);
1497 assert_eq!(date_time_2, DATE_TIME_2);
1498
1499 let local_time_type_1 = date_time_1.local_time_type();
1500 let local_time_type_2 = date_time_2.local_time_type();
1501
1502 assert_eq!(local_time_type_1.ut_offset(), LOCAL_TIME_TYPE_1.ut_offset());
1503 assert_eq!(local_time_type_1.is_dst(), LOCAL_TIME_TYPE_1.is_dst());
1504 assert_eq!(local_time_type_1.time_zone_designation(), LOCAL_TIME_TYPE_1.time_zone_designation());
1505
1506 assert_eq!(local_time_type_2.ut_offset(), LOCAL_TIME_TYPE_2.ut_offset());
1507 assert_eq!(local_time_type_2.is_dst(), LOCAL_TIME_TYPE_2.is_dst());
1508 assert_eq!(local_time_type_2.time_zone_designation(), LOCAL_TIME_TYPE_2.time_zone_designation());
1509
1510 Ok(())
1511 }
1512}