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