1use std::cmp::{Ordering, PartialEq};
2use std::convert::TryFrom;
3use std::fmt;
4use std::hash::{Hash, Hasher};
5use std::marker::PhantomData;
6use std::ops::{Add, Sub};
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::calendar::{Calendar, Gregorian, Julian};
12use crate::duration::Duration;
13use crate::epoch::Epoch;
14use crate::error::Error;
15use crate::instant::Instant;
16use crate::standard::{Standard, Tai, Tcg, Tt, Utc};
17use crate::{ATTOS_PER_SEC_F64, ATTOS_PER_SEC_I64, ATTOS_PER_SEC_U64};
18
19#[derive(Clone, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58pub struct DateTime<C: Calendar, S: Standard> {
59 packed: u64,
60 attos: u64,
61 _cal: PhantomData<C>,
62 _std: PhantomData<S>,
63}
64
65const YEAR_BITS: u64 = 0xFFFF_FFFF_0000_0000;
67const SECOND_BITS: u64 = 0x0000_0000_FC00_0000;
68const MINUTE_BITS: u64 = 0x0000_0000_03F0_0000;
69const HOUR_BITS: u64 = 0x0000_0000_000F_8000;
70const DAY0_BITS: u64 = 0x0000_0000_0000_7C00;
71const _RESERVED_BITS: u64 = 0x0000_0000_0000_03F0;
72const MONTH0_BITS: u64 = 0x0000_0000_0000_000F;
73const YEAR_OFFSET: usize = 32;
75const SECOND_OFFSET: usize = 26;
76const MINUTE_OFFSET: usize = 20;
77const HOUR_OFFSET: usize = 15;
78const DAY0_OFFSET: usize = 10;
79const MONTH0_OFFSET: usize = 0;
80
81#[inline]
83const fn pack(packed: &mut u64, bits: u64, offset: usize, value: u64) {
84 *packed &= !bits; *packed |= value << offset; }
87
88#[inline]
90const fn pack_without_clearing(packed: &mut u64, offset: usize, value: u64) {
91 *packed |= value << offset; }
93
94#[inline]
96const fn unpack(packed: u64, bits: u64, offset: usize) -> u64 {
97 (packed & bits) >> offset
98}
99
100impl<C: Calendar, S: Standard> DateTime<C, S> {
101 #[allow(clippy::cast_sign_loss)]
108 #[allow(clippy::cast_lossless)]
109 #[must_use]
110 pub const unsafe fn new_unchecked(
111 year: i32,
112 month: u8,
113 day: u8,
114 hour: u8,
115 minute: u8,
116 second: u8,
117 attosecond: u64,
118 ) -> Self {
119 let mut packed: u64 = 0;
120 pack_without_clearing(&mut packed, YEAR_OFFSET, year as u64);
121 pack_without_clearing(&mut packed, SECOND_OFFSET, second as u64);
122 pack_without_clearing(&mut packed, MINUTE_OFFSET, minute as u64);
123 pack_without_clearing(&mut packed, HOUR_OFFSET, hour as u64);
124 pack_without_clearing(&mut packed, DAY0_OFFSET, (day - 1) as u64);
125 pack_without_clearing(&mut packed, MONTH0_OFFSET, (month - 1) as u64);
126
127 Self {
128 packed,
129 attos: attosecond,
130 _cal: PhantomData,
131 _std: PhantomData,
132 }
133 }
134
135 #[allow(clippy::manual_range_contains)]
145 pub fn new(
146 year: i32,
147 month: u8,
148 day: u8,
149 hour: u8,
150 minute: u8,
151 second: u8,
152 attosecond: u64,
153 ) -> Result<Self, Error> {
154 if month < 1 || month > 12 {
155 return Err(Error::RangeError);
156 }
157 if day < 1 || day > C::month_days(month, year) {
158 return Err(Error::RangeError);
159 }
160 if hour > 23 {
161 return Err(Error::RangeError);
162 }
163 if minute > 59 {
164 return Err(Error::RangeError);
165 }
166 if second > 60 {
167 return Err(Error::RangeError);
168 }
169 if attosecond > 999_999_999_999_999_999 {
170 return Err(Error::RangeError);
171 }
172
173 Ok(unsafe { Self::new_unchecked(year, month, day, hour, minute, second, attosecond) })
174 }
175
176 #[allow(clippy::manual_range_contains)]
186 pub fn new_bc(
187 bc_year: i32,
188 month: u8,
189 day: u8,
190 hour: u8,
191 minute: u8,
192 second: u8,
193 attosecond: u64,
194 ) -> Result<Self, Error> {
195 let year = 1 - bc_year;
196 Self::new(year, month, day, hour, minute, second, attosecond)
197 }
198
199 #[must_use]
217 #[allow(clippy::cast_sign_loss)]
218 #[allow(clippy::cast_possible_truncation)]
219 pub fn new_abnormal(
220 mut year: i32,
221 month: i64,
222 day: i64,
223 mut hour: i64,
224 mut minute: i64,
225 mut second: i64,
226 mut attosecond: i64,
227 ) -> Self {
228 use crate::divmod_i64;
229
230 let mut month0 = month - 1;
231 let mut day0 = day - 1;
232
233 let (div, modulus) = divmod_i64(attosecond, ATTOS_PER_SEC_I64);
240 second += div;
241 attosecond = modulus;
242 assert!(attosecond >= 0);
243 assert!(attosecond < ATTOS_PER_SEC_I64);
244
245 let (div, modulus) = divmod_i64(second, 60);
247 minute += div;
248 second = modulus;
249 assert!(second >= 0);
250 assert!(second < 60);
251
252 let (div, modulus) = divmod_i64(minute, 60);
254 hour += div;
255 minute = modulus;
256 assert!(minute >= 0);
257 assert!(minute < 60);
258
259 let (div, modulus) = divmod_i64(hour, 24);
261 day0 += div;
262 hour = modulus;
263 assert!(hour >= 0);
264 assert!(hour < 24);
265
266 let (div, modulus) = divmod_i64(month0, 12);
271 year += div as i32;
272
273 month0 = modulus;
274 assert!(month0 >= 0);
275 assert!(month0 < 12);
276
277 let dn = C::day_number(year, (month0 + 1).try_into().unwrap(), day0 + 1).unwrap();
281
282 let (y, m, d) = C::from_day_number(dn).unwrap();
284
285 unsafe {
286 Self::new_unchecked(
287 y,
288 m,
289 d,
290 hour as u8,
291 minute as u8,
292 second as u8,
293 attosecond as u64,
294 )
295 }
296 }
297
298 pub fn from_day_number(day_number: i64) -> Result<Self, Error> {
308 let (year, month, day) = C::from_day_number(day_number)?;
309 unsafe { Ok(Self::new_unchecked(year, month, day, 0, 0, 0, 0)) }
310 }
311
312 #[allow(clippy::cast_possible_truncation)]
326 #[allow(clippy::cast_precision_loss)]
327 #[allow(clippy::cast_sign_loss)]
328 pub fn from_day_number_and_fraction(day_number: i64, day_fraction: f64) -> Result<Self, Error> {
329 if day_fraction < 0.0 {
330 return Err(Error::RangeError);
331 }
332 if day_fraction >= 1.0 {
333 return Err(Error::RangeError);
334 }
335
336 let (year, month, day) = C::from_day_number(day_number)?;
337 let (hour, min, sec, atto) = {
338 const FACTOR: i64 = 100_000_000_000_000;
339
340 let parts = (((FACTOR * 86400) as f64) * day_fraction) as i64;
344
345 let mut s = parts / FACTOR;
348 let atto = parts % FACTOR * 10000;
349
350 let mut m = s / 60;
351 s %= 60;
352 assert!(s < 60);
353
354 let h = m / 60;
355 assert!(h < 24);
356 m %= 60;
357 assert!(m < 60);
358
359 (h as u8, m as u8, s as u8, atto as u64)
360 };
361
362 Ok(unsafe { Self::new_unchecked(year, month, day, hour, min, sec, atto) })
363 }
364
365 #[must_use]
373 #[allow(clippy::missing_panics_doc)] #[allow(clippy::cast_precision_loss)]
375 pub fn from_duration_from_epoch(duration: Duration) -> Self {
376 let mut cal_duration: Duration = duration;
380
381 let mut inside_a_leap: bool = false;
382
383 if S::abbrev() == "UTC" {
385 let instant = C::epoch() + duration;
386
387 let leaps = crate::leaps::leap_seconds_elapsed_at(instant);
388
389 inside_a_leap =
391 crate::leaps::leap_seconds_elapsed_at(instant + Duration::new(1, 0)) > leaps;
392
393 cal_duration -= Duration::new(leaps, 0);
395 }
396
397 cal_duration -= S::tt_offset();
399
400 if let Some(scale) = S::tt_scale() {
401 let dur_since_sync = {
402 let instant = C::epoch() + duration;
403 let dss = instant - Epoch::TimeStandard.as_instant();
404 dss.secs as f64 + dss.attos as f64 / ATTOS_PER_SEC_F64
405 };
406 let shift = dur_since_sync * scale;
407 cal_duration += Duration::from_seconds(shift);
408 }
409
410 if inside_a_leap {
411 cal_duration -= Duration::new(1, 0);
412 }
413
414 let mut output = Self::new_abnormal(1, 1, 1, 0, 0, cal_duration.secs, cal_duration.attos);
415
416 if inside_a_leap {
417 assert_eq!(output.second(), 59); output.set_second(60).unwrap();
419 }
420
421 output
422 }
423
424 #[allow(clippy::cast_possible_truncation)]
426 #[must_use]
427 #[inline]
428 pub const fn year(&self) -> i32 {
429 unpack(self.packed, YEAR_BITS, YEAR_OFFSET) as i32
430 }
431
432 #[allow(clippy::cast_possible_truncation)]
434 #[must_use]
435 #[inline]
436 pub const fn year_bc(&self) -> i32 {
437 1 - self.year()
438 }
439
440 #[allow(clippy::cast_possible_truncation)]
442 #[must_use]
443 #[inline]
444 pub const fn month(&self) -> u8 {
445 unpack(self.packed, MONTH0_BITS, MONTH0_OFFSET) as u8 + 1
446 }
447
448 #[allow(clippy::cast_possible_truncation)]
450 #[must_use]
451 #[inline]
452 pub const fn month0(&self) -> u8 {
453 unpack(self.packed, MONTH0_BITS, MONTH0_OFFSET) as u8
454 }
455
456 #[allow(clippy::cast_possible_truncation)]
458 #[must_use]
459 #[inline]
460 pub const fn day(&self) -> u8 {
461 unpack(self.packed, DAY0_BITS, DAY0_OFFSET) as u8 + 1
462 }
463
464 #[allow(clippy::cast_possible_truncation)]
466 #[must_use]
467 #[inline]
468 pub const fn day0(&self) -> u8 {
469 unpack(self.packed, DAY0_BITS, DAY0_OFFSET) as u8
470 }
471
472 #[allow(clippy::cast_possible_truncation)]
474 #[must_use]
475 #[inline]
476 pub const fn hour(&self) -> u8 {
477 unpack(self.packed, HOUR_BITS, HOUR_OFFSET) as u8
478 }
479
480 #[allow(clippy::cast_possible_truncation)]
482 #[must_use]
483 #[inline]
484 pub const fn minute(&self) -> u8 {
485 unpack(self.packed, MINUTE_BITS, MINUTE_OFFSET) as u8
486 }
487
488 #[allow(clippy::cast_possible_truncation)]
490 #[must_use]
491 #[inline]
492 pub const fn second(&self) -> u8 {
493 unpack(self.packed, SECOND_BITS, SECOND_OFFSET) as u8
494 }
495
496 #[must_use]
498 #[inline]
499 pub const fn attosecond(&self) -> u64 {
500 self.attos
501 }
502
503 #[must_use]
505 #[inline]
506 #[allow(clippy::cast_possible_truncation)] pub fn weekday(&self) -> u8 {
508 let offset = if C::is_gregorian() { 0 } else { 5 };
509 (self.day_number() + offset).rem_euclid(7) as u8 + 1
510 }
511
512 #[must_use]
516 #[inline]
517 pub const fn date(&self) -> (i32, u8, u8) {
518 (self.year(), self.month(), self.day())
519 }
520
521 #[must_use]
525 #[inline]
526 pub const fn time(&self) -> (u8, u8, u8, u64) {
527 (self.hour(), self.minute(), self.second(), self.attosecond())
528 }
529
530 #[inline]
532 #[allow(clippy::cast_sign_loss)]
533 pub const fn set_year(&mut self, year: i32) {
534 pack(&mut self.packed, YEAR_BITS, YEAR_OFFSET, year as u64);
537 }
538
539 #[inline]
541 #[allow(clippy::cast_sign_loss)]
542 pub const fn set_year_bc(&mut self, year_bc: i32) {
543 let year = 1 - year_bc;
544 pack(&mut self.packed, YEAR_BITS, YEAR_OFFSET, year as u64);
547 }
548
549 #[allow(clippy::manual_range_contains)]
555 pub fn set_month(&mut self, month: u8) -> Result<(), Error> {
556 if month < 1 || month > 12 {
557 return Err(Error::RangeError);
558 }
559 if self.day() > C::month_days(month, self.year()) {
560 return Err(Error::RangeError);
561 }
562 pack(
563 &mut self.packed,
564 MONTH0_BITS,
565 MONTH0_OFFSET,
566 u64::from(month - 1),
567 );
568 Ok(())
569 }
570
571 pub fn set_day(&mut self, day: u8) -> Result<(), Error> {
578 if day < 1 || day > C::month_days(self.month(), self.year()) {
579 return Err(Error::RangeError);
580 }
581 pack(&mut self.packed, DAY0_BITS, DAY0_OFFSET, u64::from(day - 1));
582 Ok(())
583 }
584
585 pub fn set_hour(&mut self, hour: u8) -> Result<(), Error> {
591 if hour > 23 {
592 return Err(Error::RangeError);
593 }
594 pack(&mut self.packed, HOUR_BITS, HOUR_OFFSET, u64::from(hour));
595 Ok(())
596 }
597
598 pub fn set_minute(&mut self, minute: u8) -> Result<(), Error> {
604 if minute > 59 {
605 return Err(Error::RangeError);
606 }
607 pack(
608 &mut self.packed,
609 MINUTE_BITS,
610 MINUTE_OFFSET,
611 u64::from(minute),
612 );
613 Ok(())
614 }
615
616 pub fn set_second(&mut self, second: u8) -> Result<(), Error> {
624 if second > 60 {
625 return Err(Error::RangeError);
626 }
627 pack(
628 &mut self.packed,
629 SECOND_BITS,
630 SECOND_OFFSET,
631 u64::from(second),
632 );
633 Ok(())
634 }
635
636 pub const fn set_attosecond(&mut self, attosecond: u64) -> Result<(), Error> {
643 if attosecond > ATTOS_PER_SEC_U64 {
644 return Err(Error::RangeError);
645 }
646 self.attos = attosecond;
647 Ok(())
648 }
649
650 pub fn set_date(&mut self, date: (i32, u8, u8)) -> Result<(), Error> {
656 self.set_year(date.0);
657 self.set_month(date.1)?;
658 self.set_day(date.2)?;
659 Ok(())
660 }
661
662 pub fn set_time(&mut self, date: (u8, u8, u8, u64)) -> Result<(), Error> {
668 self.set_hour(date.0)?;
669 self.set_minute(date.1)?;
670 self.set_second(date.2)?;
671 self.set_attosecond(date.3)?;
672 Ok(())
673 }
674
675 #[must_use]
683 pub fn day_number(&self) -> i64 {
684 C::day_number(self.year(), self.month(), i64::from(self.day())).unwrap()
685 }
686
687 #[allow(clippy::cast_precision_loss)]
693 #[must_use]
694 pub fn day_fraction(&self) -> f64 {
695 const FACTOR: u64 = 100_000_000_000_000;
700
701 (u64::from(self.hour()) * 3600 * FACTOR
702 + u64::from(self.minute()) * 60 * FACTOR
703 + u64::from(self.second()) * FACTOR
704 + (self.attosecond() / 10000)) as f64
705 / 8_640_000_000_000_000_000.
706 }
707
708 #[must_use]
718 #[allow(clippy::cast_precision_loss)]
719 pub fn duration_from_epoch(&self) -> Duration {
720 let naive = {
721 let dn = self.day_number();
722 let seconds = dn * 86400
723 + i64::from(self.hour()) * 3600
724 + i64::from(self.minute()) * 60
725 + i64::from(self.second());
726 Duration::new(seconds, i64::try_from(self.attosecond()).unwrap())
727 };
728
729 let mut d = naive + S::tt_offset();
731
732 if let Some(scale) = S::tt_scale() {
733 let dur_since_sync = {
734 let instant = C::epoch() + d;
735 let dss = instant - Epoch::TimeStandard.as_instant();
736 dss.secs as f64 + dss.attos as f64 / ATTOS_PER_SEC_F64
737 };
738 let shift = dur_since_sync * scale;
739 d -= Duration::from_seconds(shift);
740 }
741
742 if S::abbrev() == "UTC" {
744 let close: Instant = C::epoch() + d;
745 let approx_leaps = crate::leaps::leap_seconds_elapsed_at(close);
746 let actual_leaps =
747 crate::leaps::leap_seconds_elapsed_at(close + Duration::new(approx_leaps + 1, 0));
748 d += Duration::new(actual_leaps, 0);
749 }
750
751 d
752 }
753}
754
755impl<C: Calendar, S: Standard> fmt::Debug for DateTime<C, S> {
756 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
757 write!(
758 f,
759 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:018} {} {}",
760 self.year(),
761 self.month(),
762 self.day(),
763 self.hour(),
764 self.minute(),
765 self.second(),
766 self.attosecond(),
767 C::name(),
768 S::abbrev()
769 )
770 }
771}
772
773impl<C: Calendar, S: Standard> fmt::Display for DateTime<C, S> {
774 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
775 write!(
776 f,
777 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:018} {} {}",
778 self.year(),
779 self.month(),
780 self.day(),
781 self.hour(),
782 self.minute(),
783 self.second(),
784 self.attosecond(),
785 C::name(),
786 S::abbrev()
787 )
788 }
789}
790
791impl<C: Calendar, S: Standard> Add<Duration> for DateTime<C, S> {
792 type Output = Self;
793
794 #[allow(clippy::cast_possible_wrap)]
795 fn add(self, rhs: Duration) -> Self {
796 Self::new_abnormal(
797 self.year(),
798 i64::from(self.month()),
799 i64::from(self.day()),
800 i64::from(self.hour()),
801 i64::from(self.minute()),
802 i64::from(self.second()) + rhs.seconds_part(),
803 self.attosecond() as i64 + rhs.attos_part(),
804 )
805 }
806}
807
808impl<C: Calendar, S: Standard> Sub<Duration> for DateTime<C, S> {
809 type Output = Self;
810
811 #[allow(clippy::cast_possible_wrap)]
812 fn sub(self, rhs: Duration) -> Self {
813 Self::new_abnormal(
814 self.year(),
815 i64::from(self.month()),
816 i64::from(self.day()),
817 i64::from(self.hour()),
818 i64::from(self.minute()),
819 i64::from(self.second()) - rhs.seconds_part(),
820 self.attosecond() as i64 - rhs.attos_part(),
821 )
822 }
823}
824
825impl<C: Calendar, S: Standard> Sub for DateTime<C, S> {
826 type Output = Duration;
827
828 #[allow(clippy::cast_possible_wrap)]
829 fn sub(self, other: Self) -> Duration {
830 let secs = (self.day_number() - other.day_number()) * 86400
831 + (i64::from(self.hour()) - i64::from(other.hour())) * 3600
832 + (i64::from(self.minute()) - i64::from(other.minute())) * 60
833 + (i64::from(self.second()) - i64::from(other.second()));
834 let attos = self.attosecond() as i64 - other.attosecond() as i64;
835 Duration::new(secs, attos) }
837}
838
839impl<C: Calendar, S: Standard> PartialEq<Self> for DateTime<C, S> {
840 fn eq(&self, other: &Self) -> bool {
841 self.packed == other.packed && self.attos == other.attos
842 }
843}
844
845impl<C: Calendar, S: Standard> Eq for DateTime<C, S> {}
846
847impl<C: Calendar, S: Standard> Ord for DateTime<C, S> {
848 fn cmp(&self, other: &Self) -> Ordering {
849 if self.year() != other.year() {
850 return self.year().cmp(&other.year());
851 }
852 if self.month() != other.month() {
853 return self.month().cmp(&other.month());
854 }
855 if self.day() != other.day() {
856 return self.day().cmp(&other.day());
857 }
858 if self.hour() != other.hour() {
859 return self.hour().cmp(&other.hour());
860 }
861 if self.minute() != other.minute() {
862 return self.minute().cmp(&other.minute());
863 }
864 if self.second() != other.second() {
865 return self.second().cmp(&other.second());
866 }
867 self.attosecond().cmp(&other.attosecond())
868 }
869}
870
871impl<C: Calendar, S: Standard> PartialOrd<Self> for DateTime<C, S> {
872 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
873 Some(self.cmp(other))
874 }
875}
876
877impl<C: Calendar, S: Standard> Hash for DateTime<C, S> {
878 fn hash<H: Hasher>(&self, state: &mut H) {
879 self.packed.hash(state);
880 self.attos.hash(state);
881 }
882}
883
884unsafe impl<C: Calendar, S: Standard> Send for DateTime<C, S> {}
885
886impl<S: Standard> TryFrom<DateTime<Gregorian, S>> for DateTime<Julian, S> {
887 type Error = Error;
888 fn try_from(input: DateTime<Gregorian, S>) -> Result<Self, Self::Error> {
889 let dn = input.day_number() + 2;
890 let mut r = Self::from_day_number(dn)?;
891 r.set_time(input.time())?;
892 Ok(r)
893 }
894}
895
896impl<S: Standard> TryFrom<DateTime<Julian, S>> for DateTime<Gregorian, S> {
897 type Error = Error;
898 fn try_from(input: DateTime<Julian, S>) -> Result<Self, Self::Error> {
899 let dn = input.day_number() - 2;
900 let mut r = Self::from_day_number(dn)?;
901 r.set_time(input.time())?;
902 Ok(r)
903 }
904}
905
906impl<C: Calendar> From<DateTime<C, Tt>> for DateTime<C, Tai> {
910 fn from(s1: DateTime<C, Tt>) -> Self {
911 let i: Instant = s1.into();
913 i.into()
914 }
915}
916impl<C: Calendar> From<DateTime<C, Tt>> for DateTime<C, Utc> {
917 fn from(s1: DateTime<C, Tt>) -> Self {
918 let i: Instant = s1.into();
920 i.into()
921 }
922}
923impl<C: Calendar> From<DateTime<C, Tt>> for DateTime<C, Tcg> {
924 fn from(s1: DateTime<C, Tt>) -> Self {
925 let i: Instant = s1.into();
927 i.into()
928 }
929}
930
931impl<C: Calendar> From<DateTime<C, Tai>> for DateTime<C, Tt> {
932 fn from(s1: DateTime<C, Tai>) -> Self {
933 let i: Instant = s1.into();
935 i.into()
936 }
937}
938impl<C: Calendar> From<DateTime<C, Tai>> for DateTime<C, Utc> {
939 fn from(s1: DateTime<C, Tai>) -> Self {
940 let i: Instant = s1.into();
942 i.into()
943 }
944}
945impl<C: Calendar> From<DateTime<C, Tai>> for DateTime<C, Tcg> {
946 fn from(s1: DateTime<C, Tai>) -> Self {
947 let i: Instant = s1.into();
949 i.into()
950 }
951}
952
953impl<C: Calendar> From<DateTime<C, Utc>> for DateTime<C, Tt> {
954 fn from(s1: DateTime<C, Utc>) -> Self {
955 let i: Instant = s1.into();
957 i.into()
958 }
959}
960impl<C: Calendar> From<DateTime<C, Utc>> for DateTime<C, Tai> {
961 fn from(s1: DateTime<C, Utc>) -> Self {
962 let i: Instant = s1.into();
964 i.into()
965 }
966}
967impl<C: Calendar> From<DateTime<C, Utc>> for DateTime<C, Tcg> {
968 fn from(s1: DateTime<C, Utc>) -> Self {
969 let i: Instant = s1.into();
971 i.into()
972 }
973}
974
975impl<C: Calendar> From<DateTime<C, Tcg>> for DateTime<C, Tt> {
976 fn from(s1: DateTime<C, Tcg>) -> Self {
977 let i: Instant = s1.into();
979 i.into()
980 }
981}
982impl<C: Calendar> From<DateTime<C, Tcg>> for DateTime<C, Tai> {
983 fn from(s1: DateTime<C, Tcg>) -> Self {
984 let i: Instant = s1.into();
986 i.into()
987 }
988}
989impl<C: Calendar> From<DateTime<C, Tcg>> for DateTime<C, Utc> {
990 fn from(s1: DateTime<C, Tcg>) -> Self {
991 let i: Instant = s1.into();
993 i.into()
994 }
995}
996
997#[cfg(test)]
998mod test {
999 use super::DateTime;
1000 use crate::calendar::{Gregorian, Julian};
1001 use crate::standard::{Tai, Tcg, Tt, Utc};
1002 use crate::{ATTOS_PER_SEC_I64, ATTOS_PER_SEC_U64};
1003 use crate::{Duration, Epoch, Instant};
1004
1005 #[test]
1006 fn test_range_errors() {
1007 assert!(DateTime::<Gregorian, Tt>::new(2000, 0, 31, 0, 0, 0, 0).is_err());
1008 assert!(DateTime::<Gregorian, Tt>::new(2000, 13, 31, 0, 0, 0, 0).is_err());
1009 assert!(DateTime::<Gregorian, Tt>::new(2000, 6, 0, 0, 0, 0, 0).is_err());
1010 assert!(DateTime::<Gregorian, Tt>::new(2000, 6, 31, 0, 0, 0, 0).is_err());
1011 assert!(DateTime::<Gregorian, Tt>::new(2000, 7, 32, 0, 0, 0, 0).is_err());
1012 assert!(DateTime::<Gregorian, Tt>::new(2003, 2, 29, 0, 0, 0, 0).is_err());
1013 assert!(DateTime::<Gregorian, Tt>::new(2004, 2, 29, 24, 0, 0, 0).is_err());
1014 assert!(DateTime::<Gregorian, Tt>::new(2004, 2, 29, 0, 60, 0, 0).is_err());
1015 assert!(DateTime::<Gregorian, Tt>::new(2004, 2, 29, 0, 0, 61, 0).is_err());
1016 assert!(DateTime::<Gregorian, Tt>::new(2004, 2, 29, 0, 0, 0, ATTOS_PER_SEC_U64).is_err());
1017
1018 let _ = DateTime::<Gregorian, Tt>::new_abnormal(0, 1, 31, 0, 0, 0, 0);
1019 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 0, 31, 0, 0, 0, 0);
1020 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 13, 31, 0, 0, 0, 0);
1021 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 6, 0, 0, 0, 0, 0);
1022 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 6, 31, 0, 0, 0, 0);
1023 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 7, 32, 0, 0, 0, 0);
1024 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2003, 2, 29, 0, 0, 0, 0);
1025 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2004, 2, 29, 24, 0, 0, 0);
1026 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2004, 2, 29, 0, 60, 0, 0);
1027 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2004, 2, 29, 0, 0, 61, 0);
1028 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2004, 2, 29, 0, 0, 0, ATTOS_PER_SEC_I64);
1029 }
1030
1031 #[test]
1032 fn test_normalize() {
1033 let dt = DateTime::<Gregorian, Tt>::new_abnormal(1900, 1, 1, 0, 0, 2272060800, 0);
1035 assert_eq!(dt.year(), 1972);
1036 assert_eq!(dt.month(), 1);
1037 assert_eq!(dt.day(), 1);
1038 assert_eq!(dt.hour(), 0);
1039 assert_eq!(dt.minute(), 0);
1040 assert_eq!(dt.second(), 0);
1041 assert_eq!(dt.attosecond(), 0);
1042
1043 let dt = DateTime::<Gregorian, Tt>::new_abnormal(1900, 1, 1, 0, 0, 2303683200, 0);
1048 assert_eq!(dt.year(), 1973);
1049 assert_eq!(dt.month(), 1);
1050 assert_eq!(dt.day(), 1);
1051 assert_eq!(dt.hour(), 0);
1052 assert_eq!(dt.minute(), 0);
1053 assert_eq!(dt.second(), 0);
1054 assert_eq!(dt.attosecond(), 0);
1055
1056 let dt = DateTime::<Gregorian, Tt>::new_abnormal(1972, 2, 29, 25, 0, 0, 0);
1059 assert_eq!(dt.month(), 3); assert_eq!(dt.day(), 1); assert_eq!(dt.hour(), 1);
1062
1063 let dt = DateTime::<Gregorian, Tt>::new_abnormal(
1065 2000,
1066 1 - 11,
1067 1 + (365 - 31),
1068 -12,
1069 60 * 12,
1070 0,
1071 0,
1072 );
1073 assert_eq!(dt.year(), 2000);
1076 assert_eq!(dt.month(), 1);
1077 assert_eq!(dt.day(), 1);
1078 assert_eq!(dt.hour(), 0);
1079 assert_eq!(dt.minute(), 0);
1080 assert_eq!(dt.second(), 0);
1081 assert_eq!(dt.attosecond(), 0);
1082
1083 let dt =
1085 DateTime::<Gregorian, Tt>::new_abnormal(2000, 1 - 60, 1 + (365 * 4 + 366), 0, 0, 0, 0);
1086 assert_eq!(dt.year(), 2000);
1089 assert_eq!(dt.month(), 1);
1090 assert_eq!(dt.day(), 1);
1091
1092 let dt = DateTime::<Gregorian, Tt>::new_abnormal(1970, 12, 31, 25, 0, 0, 0);
1094 assert_eq!(dt.year(), 1971);
1095 assert_eq!(dt.month(), 1);
1096 assert_eq!(dt.day(), 1);
1097 assert_eq!(dt.hour(), 1);
1098 }
1099
1100 #[test]
1101 fn test_day_number() {
1102 let dt = DateTime::<Gregorian, Tt>::new(1, 1, 1, 0, 0, 0, 0).unwrap(); assert_eq!(dt.day_number(), 0);
1104
1105 let dt2 = DateTime::<Gregorian, Tt>::from_day_number(dt.day_number()).unwrap();
1106 assert_eq!(dt, dt2);
1107
1108 let dt = DateTime::<Gregorian, Tt>::new(2000, 1, 1, 0, 0, 0, 0).unwrap();
1109 assert_eq!(dt.day_number(), 730119);
1110
1111 let dt2 = DateTime::<Gregorian, Tt>::from_day_number(dt.day_number()).unwrap();
1112 assert_eq!(dt, dt2);
1113
1114 assert_eq!(dt2.day_number(), dt.day_number())
1115 }
1116
1117 #[test]
1118 fn test_day_fraction() {
1119 use float_cmp::ApproxEq;
1120 let g1 = DateTime::<Gregorian, Tt>::new(2000, 1, 1, 12, 0, 0, 0).unwrap();
1121 assert!(g1.day_fraction().approx_eq(0.5, (0.0, 1)));
1122 let g2 = DateTime::<Gregorian, Tt>::new(2000, 1, 1, 18, 0, 0, 0).unwrap();
1123 assert!(g2.day_fraction().approx_eq(0.75, (0.0, 1)));
1124 let g3 = DateTime::<Gregorian, Tt>::new(2000, 1, 1, 0, 0, 1, 0).unwrap();
1125 assert!(g3.day_fraction().approx_eq(1. / 86400., (0.0, 1)));
1126
1127 let g4 =
1128 DateTime::<Gregorian, Tt>::from_day_number_and_fraction(g1.day_number(), 0.75).unwrap();
1129 assert_eq!(g4, g2);
1130
1131 let g4 =
1132 DateTime::<Gregorian, Tt>::from_day_number_and_fraction(g1.day_number(), 19. / 97.)
1133 .unwrap();
1134 assert!(g4.day_fraction().approx_eq(19. / 97., (0.0, 1)));
1135 }
1136
1137 #[test]
1138 fn test_extractors() {
1139 let g = DateTime::<Gregorian, Tt>::new(1965, 3, 7, 14, 29, 42, 500_000_000_000_000_000)
1140 .unwrap();
1141 assert_eq!(g.year(), 1965);
1142 assert_eq!(g.month(), 3);
1143 assert_eq!(g.month0(), 2);
1144 assert_eq!(g.day(), 7);
1145 assert_eq!(g.day0(), 6);
1146 assert_eq!(g.hour(), 14);
1147 assert_eq!(g.minute(), 29);
1148 assert_eq!(g.second(), 42);
1149 assert_eq!(g.attosecond(), 500_000_000_000_000_000);
1150 }
1151
1152 #[test]
1153 fn test_setters() {
1154 let mut g = DateTime::<Gregorian, Tt>::new(1965, 3, 7, 14, 29, 42, 500_000_000_000_000_000)
1155 .unwrap();
1156
1157 g.set_year(1921);
1158 assert_eq!(g.year(), 1921);
1159
1160 g.set_month(1).unwrap();
1161 assert_eq!(g.month(), 1);
1162
1163 g.set_day(17).unwrap();
1164 assert_eq!(g.day(), 17);
1165
1166 g.set_hour(3).unwrap();
1167 assert_eq!(g.hour(), 3);
1168
1169 g.set_minute(55).unwrap();
1170 assert_eq!(g.minute(), 55);
1171
1172 g.set_second(51).unwrap();
1173 assert_eq!(g.second(), 51);
1174
1175 g.set_attosecond(123_456_789_012_345_678).unwrap();
1176 assert_eq!(g.attosecond(), 123_456_789_012_345_678);
1177
1178 let h = DateTime::<Gregorian, Tt>::new(1921, 1, 17, 3, 55, 51, 123_456_789_012_345_678)
1179 .unwrap();
1180
1181 assert_eq!(g, h);
1182
1183 let mut g = DateTime::<Gregorian, Tt>::new(1997, 3, 30, 17, 24, 06, 2340897).unwrap();
1184 assert!(g.set_month(2).is_err());
1185 assert_eq!(g.month(), 3);
1186 assert!(g.set_day(28).is_ok());
1187 assert!(g.set_month(2).is_ok());
1188 assert_eq!(g.month(), 2);
1189 assert_eq!(g.day(), 28);
1190 }
1191
1192 #[test]
1193 fn test_comparison() {
1194 let g = DateTime::<Gregorian, Tt>::new(1965, 3, 7, 14, 29, 42, 500_000_000_000_000_000)
1195 .unwrap();
1196 let h = DateTime::<Gregorian, Tt>::new(1966, 1, 17, 3, 55, 51, 123_456_789_012_345_678)
1197 .unwrap();
1198 let i = DateTime::<Gregorian, Tt>::new(1966, 3, 7, 14, 29, 42, 500_000_000_000_000_000)
1199 .unwrap();
1200 let j = DateTime::<Gregorian, Tt>::new(1966, 3, 7, 14, 29, 42, 500_000_000_000_000_000)
1201 .unwrap();
1202 assert!(g < h);
1203 assert!(h < i);
1204 assert!(i == j);
1205 }
1206
1207 #[test]
1208 fn test_math() {
1209 let g = DateTime::<Gregorian, Tt>::new(1996, 3, 2, 0, 0, 0, 50).unwrap();
1210 let week_less_150ns = Duration::new(86400 * 7, 150);
1211 let earlier = g - week_less_150ns;
1212 assert_eq!(earlier.year(), 1996);
1213 assert_eq!(earlier.month(), 2);
1214 assert_eq!(earlier.day(), 23);
1215 assert_eq!(earlier.hour(), 23);
1216 assert_eq!(earlier.minute(), 59);
1217 assert_eq!(earlier.second(), 59);
1218 assert_eq!(earlier.attosecond(), ATTOS_PER_SEC_U64 - 100);
1219
1220 let g1 = DateTime::<Gregorian, Tt>::new(2000, 1, 1, 0, 0, 0, 0).unwrap();
1221 let g2 = DateTime::<Gregorian, Tt>::new(2001, 2, 2, 1, 3, 5, 11).unwrap();
1222 let diff = g2 - g1;
1223 assert_eq!(
1224 diff.seconds_part(),
1225 366 * 86400 + 31 * 86400 + 1 * 86400 + 1 * 3600 + 3 * 60 + 5
1226 );
1227 assert_eq!(diff.attos_part(), 11);
1228 }
1229
1230 #[test]
1231 fn test_print_extremes() {
1232 let min = DateTime::<Gregorian, Tt>::new(std::i32::MIN, 1, 1, 0, 0, 0, 0).unwrap();
1233 println!("Min gregorian: {}", min);
1234 let max = DateTime::<Gregorian, Tt>::new(
1235 std::i32::MAX,
1236 12,
1237 31,
1238 23,
1239 59,
1240 59,
1241 999_999_999_999_999_999,
1242 )
1243 .unwrap();
1244 println!("Max gregorian: {}", max);
1245 }
1246
1247 #[test]
1248 fn test_bc_day_numbers() {
1249 let mar1 = DateTime::<Gregorian, Tt>::new(0, 3, 1, 0, 0, 0, 0).unwrap();
1250 let feb29 = DateTime::<Gregorian, Tt>::new(0, 2, 29, 0, 0, 0, 0).unwrap();
1251 let feb28 = DateTime::<Gregorian, Tt>::new(0, 2, 28, 0, 0, 0, 0).unwrap();
1252 assert_eq!(mar1.day_number(), -306);
1253 assert_eq!(feb29.day_number(), -307);
1254 assert_eq!(feb28.day_number(), -308);
1255
1256 let mar1x = DateTime::<Gregorian, Tt>::from_day_number(-306).unwrap();
1257 let feb29x = DateTime::<Gregorian, Tt>::from_day_number(-307).unwrap();
1258 let feb28x = DateTime::<Gregorian, Tt>::from_day_number(-308).unwrap();
1259 assert_eq!(mar1, mar1x);
1260 assert_eq!(feb29, feb29x);
1261 assert_eq!(feb28, feb28x);
1262 }
1263
1264 #[test]
1265 fn test_convert_calendar() {
1266 let j = DateTime::<Julian, Tt>::new(1582, 10, 5, 0, 0, 0, 0).unwrap();
1267 let g = DateTime::<Gregorian, Tt>::new(1582, 10, 15, 0, 0, 0, 0).unwrap();
1268 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
1269 assert_eq!(j, j2);
1270 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
1271 assert_eq!(g, g2);
1272
1273 let j = DateTime::<Julian, Tt>::new(1582, 10, 4, 0, 0, 0, 0).unwrap();
1274 let g = DateTime::<Gregorian, Tt>::new(1582, 10, 14, 0, 0, 0, 0).unwrap();
1275 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
1276 assert_eq!(j, j2);
1277 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
1278 assert_eq!(g, g2);
1279
1280 let j = DateTime::<Julian, Tt>::new(-4713, 1, 1, 0, 0, 0, 0).unwrap();
1281 let g = DateTime::<Gregorian, Tt>::new(-4714, 11, 24, 0, 0, 0, 0).unwrap();
1282 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
1283 assert_eq!(j, j2);
1284 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
1285 assert_eq!(g, g2);
1286
1287 let j = DateTime::<Julian, Tt>::new(1, 1, 3, 0, 0, 0, 0).unwrap();
1288 let g = DateTime::<Gregorian, Tt>::new(1, 1, 1, 0, 0, 0, 0).unwrap();
1289 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
1290 assert_eq!(j, j2);
1291 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
1292 assert_eq!(g, g2);
1293
1294 let j = DateTime::<Julian, Tt>::new(1, 1, 1, 0, 0, 0, 0).unwrap();
1295 let g = DateTime::<Gregorian, Tt>::new(0, 12, 30, 0, 0, 0, 0).unwrap();
1296 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
1297 assert_eq!(j, j2);
1298 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
1299 assert_eq!(g, g2);
1300 }
1301
1302 #[test]
1303 fn test_epoch_duration() {
1304 let g = DateTime::<Gregorian, Tt>::new(1582, 10, 14, 0, 0, 0, 0).unwrap();
1305 let h = DateTime::<Gregorian, Tt>::from_duration_from_epoch(g.duration_from_epoch());
1306 assert_eq!(g, h);
1307
1308 let g = DateTime::<Julian, Tt>::new(1582, 10, 14, 11, 0, 5, 130).unwrap();
1309 let h = DateTime::<Julian, Tt>::from_duration_from_epoch(g.duration_from_epoch());
1310 assert_eq!(g, h);
1311 }
1312
1313 #[test]
1314 fn test_weekday() {
1315 let g = DateTime::<Gregorian, Tt>::new(2026, 2, 1, 12, 0, 0, 0).unwrap();
1316 assert_eq!(g.weekday(), 7);
1317 let j = DateTime::<Julian, Tt>::new(2026, 1, 19, 12, 0, 0, 0).unwrap();
1318 assert_eq!(j.weekday(), 7);
1319 }
1320
1321 #[test]
1322 fn test_datetime_instant_conversions_in_tt() {
1323 let p1: Instant = Epoch::J1991_25.as_instant();
1324 let d: DateTime<Gregorian, Tt> = p1.into();
1325 let p2: Instant = d.into();
1326 assert_eq!(p1, p2);
1327
1328 let d1 = DateTime::<Gregorian, Tt>::new(1993, 6, 30, 0, 0, 27, 0).unwrap();
1329 let p: Instant = d1.into();
1330 let d2: DateTime<Gregorian, Tt> = p.into();
1331 assert_eq!(d1, d2);
1332 }
1333
1334 #[test]
1335 fn test_datetime_instant_conversions_without_leapseconds() {
1336 let p1: Instant = Epoch::J1991_25.as_instant();
1337 let d: DateTime<Gregorian, Tai> = p1.into();
1338 let p2: Instant = d.into();
1339 assert_eq!(p1, p2);
1340
1341 let d1 = DateTime::<Gregorian, Tai>::new(1993, 6, 30, 0, 0, 27, 0).unwrap();
1342 let p: Instant = d1.into();
1343 let d2: DateTime<Gregorian, Tai> = p.into();
1344 assert_eq!(d1, d2);
1345 }
1346
1347 #[test]
1348 fn test_datetime_instant_conversions_with_leapseconds() {
1349 let leap_instant = Instant::from_ntp_date(2429913600, 0);
1350 assert_eq!(leap_instant, Epoch::Y1977.as_instant()); let date: DateTime<Gregorian, Utc> = leap_instant.into();
1352 let expected_date = DateTime::<Gregorian, Utc>::new(1977, 1, 1, 0, 0, 0, 0).unwrap();
1353 assert_eq!(date, expected_date);
1354
1355 let plus_three = leap_instant + Duration::new(3, 0);
1356 let date: DateTime<Gregorian, Utc> = plus_three.into();
1357 let expected_date = DateTime::<Gregorian, Utc>::new(1977, 1, 1, 0, 0, 3, 0).unwrap();
1358 assert_eq!(date, expected_date);
1359
1360 let minus_three = leap_instant - Duration::new(3, 0);
1361 let date: DateTime<Gregorian, Utc> = minus_three.into();
1362 let expected_date = DateTime::<Gregorian, Utc>::new(1976, 12, 31, 23, 59, 58, 0).unwrap();
1363 assert_eq!(date, expected_date);
1364
1365 let minus_half = leap_instant - Duration::new(0, ATTOS_PER_SEC_I64 / 2);
1366 let date: DateTime<Gregorian, Utc> = minus_half.into();
1367 let expected_date =
1368 DateTime::<Gregorian, Utc>::new(1976, 12, 31, 23, 59, 60, ATTOS_PER_SEC_U64 / 2)
1369 .unwrap();
1370 assert_eq!(date, expected_date);
1371 }
1372
1373 #[test]
1374 fn test_time_standard_conversions() {
1375 let p: Instant =
1376 From::from(DateTime::<Gregorian, Tai>::new(1993, 6, 30, 0, 0, 27, 0).unwrap());
1377 let q: DateTime<Gregorian, Utc> = From::from(p);
1378 assert_eq!(
1379 q,
1380 DateTime::<Gregorian, Utc>::new(1993, 6, 30, 0, 0, 0, 0).unwrap()
1381 );
1382
1383 let p: Instant = Epoch::Unix.as_instant();
1384 let q: DateTime<Gregorian, Utc> = From::from(p);
1385 assert_eq!(
1386 q,
1387 DateTime::<Gregorian, Utc>::new(1970, 1, 1, 0, 0, 0, 0).unwrap()
1388 );
1389
1390 let y2k: Instant = Epoch::Y2k.as_instant();
1391 let q: DateTime<Gregorian, Utc> = From::from(y2k);
1392 assert_eq!(
1393 q,
1394 DateTime::<Gregorian, Utc>::new(2000, 1, 1, 0, 0, 0, 0).unwrap()
1395 );
1396 }
1397
1398 #[test]
1399 fn test_instant_datetime_utc_range() {
1400 let leap_instant: Instant = From::from(
1402 DateTime::<Gregorian, Tt>::new(1999, 1, 1, 0, 0, 0, 0).unwrap()
1403 - Duration::new(32 + 32, 184_000_000_000_000_000),
1404 );
1405 for s in -100..100 {
1406 println!("s={}", s);
1407 let a = leap_instant + Duration::new(s, 0);
1408 let dt: DateTime<Gregorian, Utc> = a.into();
1409 let b: Instant = dt.into();
1410 assert_eq!(a, b);
1411 }
1412 }
1413
1414 #[test]
1415 fn test_tcg() {
1416 let d = DateTime::<Gregorian, Tt>::new(2020, 1, 1, 0, 0, 0, 0).unwrap();
1418 let d2: DateTime<Gregorian, Tcg> = d.into();
1419 let d3: DateTime<Gregorian, Tt> = d2.into();
1420 let diff = d3 - d;
1421 assert_eq!(diff.secs, 0);
1422 assert!(diff.attos.abs() < 1_000_000_000_000);
1423
1424 let d = DateTime::<Gregorian, Tai>::new(2020, 1, 1, 0, 0, 0, 0).unwrap();
1425 let d2: DateTime<Gregorian, Tcg> = d.into();
1426 let d3: DateTime<Gregorian, Tai> = d2.into();
1427 let diff = d3 - d;
1428 assert_eq!(diff.secs, 0);
1429 assert!(diff.attos.abs() < 1_000_000_000_000);
1430
1431 let d = DateTime::<Gregorian, Utc>::new(2020, 1, 1, 0, 0, 0, 0).unwrap();
1432 let d2: DateTime<Gregorian, Tcg> = d.into();
1433 let d3: DateTime<Gregorian, Utc> = d2.into();
1434 let diff = d3 - d;
1435 assert_eq!(diff.secs, 0);
1436 assert!(diff.attos.abs() < 1_000_000_000_000);
1437 }
1438}