1
2use std::cmp::{PartialEq, Ordering};
3use std::fmt;
4use std::hash::{Hash, Hasher};
5use std::marker::PhantomData;
6use std::ops::{Add, Sub};
7use std::convert::TryFrom;
8
9#[cfg(feature = "serde")]
10use serde::{Serialize, Deserialize};
11
12use crate::calendar::{Calendar, Julian, Gregorian};
13use crate::duration::Duration;
14use crate::error::Error;
15use crate::standard::Standard;
16
17#[derive(Clone, Copy)] #[cfg_attr(feature ="serde", derive(Serialize, Deserialize))]
56pub struct DateTime<C: Calendar, S: Standard> {
57 packed: u64,
58 attos: u64,
59 _cal: PhantomData<C>,
60 _std: PhantomData<S>
61}
62
63const YEAR_BITS: u64 = 0xFFFF_FFFF_0000_0000;
65const SECOND_BITS: u64 = 0x0000_0000_FC00_0000;
66const MINUTE_BITS: u64 = 0x0000_0000_03F0_0000;
67const HOUR_BITS: u64 = 0x0000_0000_000F_8000;
68const DAY0_BITS: u64 = 0x0000_0000_0000_7C00;
69const _RESERVED_BITS: u64 = 0x0000_0000_0000_03F0;
70const MONTH0_BITS: u64 = 0x0000_0000_0000_000F;
71const YEAR_OFFSET: usize = 32;
73const SECOND_OFFSET: usize = 26;
74const MINUTE_OFFSET: usize = 20;
75const HOUR_OFFSET: usize = 15;
76const DAY0_OFFSET: usize = 10;
77const MONTH0_OFFSET: usize = 0;
78
79#[inline]
81fn pack(packed: &mut u64, bits: u64, offset: usize, value: u64) {
82 *packed &= !bits; *packed |= value<<offset; }
85
86#[inline]
88fn pack_without_clearing(packed: &mut u64, offset: usize, value: u64) {
89 *packed |= value<<offset; }
91
92#[inline]
94const fn unpack(packed: u64, bits: u64, offset: usize) -> u64 {
95 (packed & bits) >> offset
96}
97
98impl<C: Calendar, S: Standard> DateTime<C, S> {
99 #[allow(clippy::cast_sign_loss)]
106 #[allow(clippy::cast_lossless)]
107 #[must_use]
108 pub unsafe fn new_unchecked(year: i32, month: u8, day: u8,
109 hour: u8, minute: u8, second: u8,
110 attosecond: u64)
111 -> Self
112 {
113 let mut packed: u64 = 0;
114 pack_without_clearing(&mut packed, YEAR_OFFSET, year as u64);
115 pack_without_clearing(&mut packed, SECOND_OFFSET, second as u64);
116 pack_without_clearing(&mut packed, MINUTE_OFFSET, minute as u64);
117 pack_without_clearing(&mut packed, HOUR_OFFSET, hour as u64);
118 pack_without_clearing(&mut packed, DAY0_OFFSET, (day-1) as u64);
119 pack_without_clearing(&mut packed, MONTH0_OFFSET, (month-1) as u64);
120
121 Self {
122 packed,
123 attos: attosecond,
124 _cal: PhantomData,
125 _std: PhantomData,
126 }
127 }
128
129 #[allow(clippy::manual_range_contains)]
139 pub fn new(year: i32, month: u8, day: u8,
140 hour: u8, minute: u8, second: u8,
141 attosecond: u64)
142 -> Result<Self, Error>
143 {
144 if month<1 || month>12 { return Err(Error::RangeError); }
145 if day<1 || day>C::month_days(month, year) { return Err(Error::RangeError); }
146 if hour>23 { return Err(Error::RangeError); }
147 if minute>59 { return Err(Error::RangeError); }
148 if second>60 { return Err(Error::RangeError); }
149 if attosecond>999_999_999_999_999_999 { return Err(Error::RangeError); }
150
151 Ok(unsafe {
152 Self::new_unchecked(year, month, day,
153 hour, minute, second, attosecond)
154 })
155 }
156
157 #[allow(clippy::manual_range_contains)]
167 pub fn new_bc(bc_year: i32, month: u8, day: u8,
168 hour: u8, minute: u8, second: u8,
169 attosecond: u64)
170 -> Result<Self, Error>
171 {
172 let year = 1-bc_year;
173 Self::new(year, month, day, hour, minute, second, attosecond)
174 }
175
176 #[must_use]
189 #[allow(clippy::cast_sign_loss)]
190 #[allow(clippy::cast_possible_truncation)]
191 pub fn new_abnormal(mut year: i32, month: i64, day: i64,
192 mut hour: i64, mut minute: i64, mut second: i64,
193 mut attosecond: i64)
194 -> Self
195 {
196 use crate::divmod_i64;
197
198 let mut month0 = month - 1;
199 let mut day0 = day - 1;
200
201 let (div, modulus) = divmod_i64(attosecond, 1_000_000_000_000_000_000);
208 second += div;
209 attosecond = modulus;
210 assert!(attosecond >= 0);
211 assert!(attosecond < 1_000_000_000_000_000_000);
212
213 let (div, modulus) = divmod_i64( second, 60 );
215 minute += div;
216 second = modulus;
217 assert!(second >= 0);
218 assert!(second < 60);
219
220 let (div, modulus) = divmod_i64( minute, 60 );
222 hour += div;
223 minute = modulus;
224 assert!(minute >= 0);
225 assert!(minute < 60);
226
227 let (div, modulus) = divmod_i64( hour, 24 );
229 day0 += div;
230 hour = modulus;
231 assert!(hour >= 0);
232 assert!(hour < 24);
233
234 let (div, modulus) = divmod_i64( month0, 12 );
239 year += div as i32;
240
241 month0 = modulus;
242 assert!(month0 >= 0);
243 assert!(month0 < 12);
244
245 let dn = C::day_number(year,
249 (month0+1).try_into().unwrap(),
250 day0+1).unwrap();
251
252 let (y,m,d) = C::from_day_number(dn).unwrap();
254
255 unsafe {
256 Self::new_unchecked(y, m, d,
257 hour as u8, minute as u8, second as u8, attosecond as u64)
258 }
259 }
260
261 pub fn from_day_number(day_number: i64) -> Result<Self, Error> {
271 let (year, month, day) = C::from_day_number(day_number)?;
272 unsafe { Ok(Self::new_unchecked(year, month, day, 0, 0, 0, 0)) }
273 }
274
275 #[allow(clippy::cast_possible_truncation)]
289 #[allow(clippy::cast_precision_loss)]
290 #[allow(clippy::cast_sign_loss)]
291 pub fn from_day_number_and_fraction(day_number: i64, day_fraction: f64)
292 -> Result<Self, Error>
293 {
294 if day_fraction<0.0 { return Err(Error::RangeError); }
295 if day_fraction>=1.0 { return Err(Error::RangeError); }
296
297 let (year, month, day) = C::from_day_number(day_number)?;
298 let (hour, min, sec, atto) = {
299 const FACTOR: i64 = 100_000_000_000_000;
300
301 let parts = (((FACTOR * 86400) as f64) * day_fraction) as i64;
305
306 let mut s = parts / FACTOR;
309 let atto = parts % FACTOR * 10000;
310
311 let mut m = s / 60;
312 s %= 60;
313 assert!(s<60);
314
315 let h = m / 60;
316 assert!(h<24);
317 m %= 60;
318 assert!(m<60);
319
320 (h as u8,
321 m as u8,
322 s as u8,
323 atto as u64)
324 };
325
326 Ok(unsafe {
327 Self::new_unchecked(year, month, day, hour, min, sec, atto)
328 })
329 }
330
331 #[must_use]
335 pub fn from_duration_from_epoch(duration: Duration) -> Self {
336 Self::new_abnormal(1, 1, 1, 0, 0, duration.secs, duration.attos)
337 }
338
339 #[allow(clippy::cast_possible_truncation)]
341 #[must_use]
342 #[inline]
343 pub fn year(&self) -> i32 {
344 unpack(self.packed, YEAR_BITS, YEAR_OFFSET) as i32
345 }
346
347 #[allow(clippy::cast_possible_truncation)]
349 #[must_use]
350 #[inline]
351 pub fn year_bc(&self) -> i32 {
352 1 - self.year()
353 }
354
355 #[allow(clippy::cast_possible_truncation)]
357 #[must_use]
358 #[inline]
359 pub fn month(&self) -> u8 {
360 unpack(self.packed, MONTH0_BITS, MONTH0_OFFSET) as u8 + 1
361 }
362
363 #[allow(clippy::cast_possible_truncation)]
365 #[must_use]
366 #[inline]
367 pub fn month0(&self) -> u8 {
368 unpack(self.packed, MONTH0_BITS, MONTH0_OFFSET) as u8
369 }
370
371 #[allow(clippy::cast_possible_truncation)]
373 #[must_use]
374 #[inline]
375 pub fn day(&self) -> u8 {
376 unpack(self.packed, DAY0_BITS, DAY0_OFFSET) as u8 + 1
377 }
378
379 #[allow(clippy::cast_possible_truncation)]
381 #[must_use]
382 #[inline]
383 pub fn day0(&self) -> u8 {
384 unpack(self.packed, DAY0_BITS, DAY0_OFFSET) as u8
385 }
386
387 #[allow(clippy::cast_possible_truncation)]
389 #[must_use]
390 #[inline]
391 pub fn hour(&self) -> u8 {
392 unpack(self.packed, HOUR_BITS, HOUR_OFFSET) as u8
393 }
394
395 #[allow(clippy::cast_possible_truncation)]
397 #[must_use]
398 #[inline]
399 pub fn minute(&self) -> u8 {
400 unpack(self.packed, MINUTE_BITS, MINUTE_OFFSET) as u8
401 }
402
403 #[allow(clippy::cast_possible_truncation)]
405 #[must_use]
406 #[inline]
407 pub fn second(&self) -> u8 {
408 unpack(self.packed, SECOND_BITS, SECOND_OFFSET) as u8
409 }
410
411 #[must_use]
413 #[inline]
414 pub fn attosecond(&self) -> u64 {
415 self.attos
416 }
417
418 #[must_use]
422 #[inline]
423 pub fn date(&self) -> (i32, u8, u8) {
424 (self.year(), self.month(), self.day())
425 }
426
427 #[must_use]
431 #[inline]
432 pub fn time(&self) -> (u8, u8, u8, u64) {
433 (self.hour(), self.minute(), self.second(), self.attosecond())
434 }
435
436 #[inline]
438 #[allow(clippy::cast_sign_loss)]
439 pub fn set_year(&mut self, year: i32) {
440 pack(&mut self.packed, YEAR_BITS, YEAR_OFFSET, year as u64);
443 }
444
445 #[inline]
447 #[allow(clippy::cast_sign_loss)]
448 pub fn set_year_bc(&mut self, year_bc: i32) {
449 let year = 1 - year_bc;
450 pack(&mut self.packed, YEAR_BITS, YEAR_OFFSET, year as u64);
453 }
454
455 #[allow(clippy::manual_range_contains)]
461 pub fn set_month(&mut self, month: u8) -> Result<(), Error> {
462 if month<1 || month>12 {
463 return Err(Error::RangeError);
464 }
465 if self.day() > C::month_days(month, self.year()) {
466 return Err(Error::RangeError);
467 }
468 pack(&mut self.packed, MONTH0_BITS, MONTH0_OFFSET, u64::from(month-1));
469 Ok(())
470 }
471
472 pub fn set_day(&mut self, day: u8) -> Result<(), Error> {
479 if day<1 || day> C::month_days(self.month(), self.year()) {
480 return Err(Error::RangeError);
481 }
482 pack(&mut self.packed, DAY0_BITS, DAY0_OFFSET, u64::from(day-1));
483 Ok(())
484 }
485
486 pub fn set_hour(&mut self, hour: u8) -> Result<(), Error> {
492 if hour>23 {
493 return Err(Error::RangeError);
494 }
495 pack(&mut self.packed, HOUR_BITS, HOUR_OFFSET, u64::from(hour));
496 Ok(())
497 }
498
499 pub fn set_minute(&mut self, minute: u8) -> Result<(), Error> {
505 if minute>59 {
506 return Err(Error::RangeError);
507 }
508 pack(&mut self.packed, MINUTE_BITS, MINUTE_OFFSET, u64::from(minute));
509 Ok(())
510 }
511
512 pub fn set_second(&mut self, second: u8) -> Result<(), Error> {
520 if second>60 {
521 return Err(Error::RangeError);
522 }
523 pack(&mut self.packed, SECOND_BITS, SECOND_OFFSET, u64::from(second));
524 Ok(())
525 }
526
527 pub fn set_attosecond(&mut self, attosecond: u64) -> Result<(), Error> {
534 if attosecond>1_000_000_000_000_000_000 {
535 return Err(Error::RangeError);
536 }
537 self.attos = attosecond;
538 Ok(())
539 }
540
541 pub fn set_date(&mut self, date: (i32, u8, u8)) -> Result<(), Error> {
547 self.set_year(date.0);
548 self.set_month(date.1)?;
549 self.set_day(date.2)?;
550 Ok(())
551 }
552
553 pub fn set_time(&mut self, date: (u8, u8, u8, u64)) -> Result<(), Error> {
559 self.set_hour(date.0)?;
560 self.set_minute(date.1)?;
561 self.set_second(date.2)?;
562 self.set_attosecond(date.3)?;
563 Ok(())
564 }
565
566 #[must_use]
574 pub fn day_number(&self) -> i64 {
575 C::day_number(self.year(), self.month(), i64::from(self.day())).unwrap()
576 }
577
578 #[allow(clippy::cast_precision_loss)]
584 #[must_use]
585 pub fn day_fraction(&self) -> f64 {
586 const FACTOR: u64 = 100_000_000_000_000;
591
592 ( u64::from(self.hour()) * 3600 * FACTOR
593 + u64::from(self.minute()) * 60 * FACTOR
594 + u64::from(self.second()) * FACTOR
595 + (self.attosecond()/10000) as u64)
596 as f64 / 8_640_000_000_000_000_000.
597 }
598
599 #[must_use]
607 pub fn duration_from_epoch(&self) -> Duration {
608 let dn = self.day_number();
609 let seconds = dn * 86400
610 + i64::from(self.hour()) * 3600
611 + i64::from(self.minute()) * 60
612 + i64::from(self.second());
613
614 Duration::new(seconds, i64::try_from(self.attosecond()).unwrap())
615 }
616}
617
618impl<C: Calendar, S: Standard> fmt::Debug for DateTime<C, S> {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 write!(f, "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:018} {} {}",
621 self.year(), self.month(), self.day(), self.hour(), self.minute(), self.second(),
622 self.attosecond(), C::name(), S::abbrev())
623 }
624}
625
626impl<C: Calendar, S: Standard> fmt::Display for DateTime<C, S> {
627 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
628 write!(f, "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:018} {} {}",
629 self.year(), self.month(), self.day(), self.hour(), self.minute(), self.second(),
630 self.attosecond(), C::name(), S::abbrev())
631 }
632}
633
634impl<C: Calendar, S: Standard> Add<Duration> for DateTime<C, S> {
635 type Output = Self;
636
637 #[allow(clippy::cast_possible_wrap)]
638 fn add(self, rhs: Duration) -> Self {
639 Self::new_abnormal(self.year(), i64::from(self.month()), i64::from(self.day()),
640 i64::from(self.hour()), i64::from(self.minute()),
641 i64::from(self.second()) + rhs.seconds_part(),
642 self.attosecond() as i64 + rhs.attos_part() as i64)
643 }
644}
645
646impl<C: Calendar, S: Standard> Sub<Duration> for DateTime<C, S> {
647 type Output = Self;
648
649 #[allow(clippy::cast_possible_wrap)]
650 fn sub(self, rhs: Duration) -> Self {
651 Self::new_abnormal(self.year(), i64::from(self.month()), i64::from(self.day()),
652 i64::from(self.hour()), i64::from(self.minute()),
653 i64::from(self.second()) - rhs.seconds_part(),
654 self.attosecond() as i64 - rhs.attos_part() as i64)
655 }
656}
657
658impl<C: Calendar, S: Standard> Sub for DateTime<C, S> {
659 type Output = Duration;
660
661 #[allow(clippy::cast_possible_wrap)]
662 fn sub(self, other: Self) -> Duration {
663 let secs =
664 (self.day_number() - other.day_number()) * 86400
665 + (i64::from(self.hour()) - i64::from(other.hour())) * 3600
666 + (i64::from(self.minute()) - i64::from(other.minute())) * 60
667 + (i64::from(self.second()) - i64::from(other.second()));
668 let attos = self.attosecond() as i64 - other.attosecond() as i64;
669 Duration::new(secs, attos) }
671}
672
673impl<C: Calendar, S: Standard> PartialEq<Self> for DateTime<C, S> {
674 fn eq(&self, other: &Self) -> bool {
675 self.packed == other.packed && self.attos == other.attos
676 }
677}
678
679impl<C: Calendar, S: Standard> Eq for DateTime<C, S> { }
680
681impl<C: Calendar, S: Standard> Ord for DateTime<C, S> {
682 fn cmp(&self, other: &Self) -> Ordering {
683 if self.year() != other.year() { return self.year().cmp(&other.year()) }
684 if self.month() != other.month() { return self.month().cmp(&other.month()) }
685 if self.day() != other.day() { return self.day().cmp(&other.day()) }
686 if self.hour() != other.hour() { return self.hour().cmp(&other.hour()) }
687 if self.minute() != other.minute() { return self.minute().cmp(&other.minute()) }
688 if self.second() != other.second() { return self.second().cmp(&other.second()) }
689 self.attosecond().cmp(&other.attosecond())
690 }
691}
692
693impl<C: Calendar, S: Standard> PartialOrd<Self> for DateTime<C, S> {
694 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
695 Some(self.cmp(other))
696 }
697}
698
699impl<C: Calendar, S: Standard> Hash for DateTime<C, S> {
700 fn hash<H: Hasher>(&self, state: &mut H) {
701 self.packed.hash(state);
702 self.attos.hash(state);
703 }
704}
705
706unsafe impl<C: Calendar, S: Standard> Send for DateTime<C, S> {}
707
708impl<S: Standard> TryFrom<DateTime<Gregorian, S>> for DateTime<Julian, S> {
709 type Error = Error;
710 fn try_from(input: DateTime<Gregorian, S>) -> Result<Self, Self::Error> {
711 let dn = input.day_number() + 2;
712 let mut r = Self::from_day_number(dn)?;
713 r.set_time(input.time())?;
714 Ok(r)
715 }
716}
717
718impl<S: Standard> TryFrom<DateTime<Julian, S>> for DateTime<Gregorian, S> {
719 type Error = Error;
720 fn try_from(input: DateTime<Julian, S>) -> Result<Self, Self::Error> {
721 let dn = input.day_number() - 2;
722 let mut r = Self::from_day_number(dn)?;
723 r.set_time(input.time())?;
724 Ok(r)
725 }
726}
727
728
729#[cfg(test)]
730mod test {
731 use super::DateTime;
732 use crate::calendar::{Gregorian, Julian};
733 use crate::duration::Duration;
734 use crate::standard::Tt;
735
736 #[test]
737 fn test_range_errors() {
738 crate::setup_logging();
739
740 assert!(DateTime::<Gregorian, Tt>::new(2000, 0, 31, 0, 0, 0, 0).is_err());
741 assert!(DateTime::<Gregorian, Tt>::new(2000, 13, 31, 0, 0, 0, 0).is_err());
742 assert!(DateTime::<Gregorian, Tt>::new(2000, 6, 0, 0, 0, 0, 0).is_err());
743 assert!(DateTime::<Gregorian, Tt>::new(2000, 6, 31, 0, 0, 0, 0).is_err());
744 assert!(DateTime::<Gregorian, Tt>::new(2000, 7, 32, 0, 0, 0, 0).is_err());
745 assert!(DateTime::<Gregorian, Tt>::new(2003, 2, 29, 0, 0, 0, 0).is_err());
746 assert!(DateTime::<Gregorian, Tt>::new(2004, 2, 29, 24, 0, 0, 0).is_err());
747 assert!(DateTime::<Gregorian, Tt>::new(2004, 2, 29, 0, 60, 0, 0).is_err());
748 assert!(DateTime::<Gregorian, Tt>::new(2004, 2, 29, 0, 0, 61, 0).is_err());
749 assert!(DateTime::<Gregorian, Tt>::new(2004, 2, 29, 0, 0, 0, 1_000_000_000_000_000_000).is_err());
750
751 let _ = DateTime::<Gregorian, Tt>::new_abnormal(0, 1, 31, 0, 0, 0, 0);
752 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 0, 31, 0, 0, 0, 0);
753 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 13, 31, 0, 0, 0, 0);
754 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 6, 0, 0, 0, 0, 0);
755 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 6, 31, 0, 0, 0, 0);
756 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2000, 7, 32, 0, 0, 0, 0);
757 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2003, 2, 29, 0, 0, 0, 0);
758 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2004, 2, 29, 24, 0, 0, 0);
759 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2004, 2, 29, 0, 60, 0, 0);
760 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2004, 2, 29, 0, 0, 61, 0);
761 let _ = DateTime::<Gregorian, Tt>::new_abnormal(2004, 2, 29, 0, 0, 0, 1_000_000_000_000_000_000);
762 }
763
764 #[test]
765 fn test_normalize() {
766 crate::setup_logging();
767
768 let dt = DateTime::<Gregorian, Tt>::new_abnormal(1900,1,1,0,0,2272060800,0);
770 assert_eq!(dt.year(), 1972);
771 assert_eq!(dt.month(), 1);
772 assert_eq!(dt.day(), 1);
773 assert_eq!(dt.hour(), 0);
774 assert_eq!(dt.minute(), 0);
775 assert_eq!(dt.second(), 0);
776 assert_eq!(dt.attosecond(), 0);
777
778 let dt = DateTime::<Gregorian, Tt>::new_abnormal(1900,1,1,0,0,2303683200,0);
783 assert_eq!(dt.year(), 1973);
784 assert_eq!(dt.month(), 1);
785 assert_eq!(dt.day(), 1);
786 assert_eq!(dt.hour(), 0);
787 assert_eq!(dt.minute(), 0);
788 assert_eq!(dt.second(), 0);
789 assert_eq!(dt.attosecond(), 0);
790
791 let dt = DateTime::<Gregorian, Tt>::new_abnormal(1972, 2, 29, 25, 0, 0, 0);
794 assert_eq!(dt.month(), 3); assert_eq!(dt.day(), 1); assert_eq!(dt.hour(), 1);
797
798 let dt = DateTime::<Gregorian, Tt>::new_abnormal(2000, 1-11, 1+(365-31), -12, 60*12, 0, 0);
800 assert_eq!(dt.year(), 2000);
803 assert_eq!(dt.month(), 1);
804 assert_eq!(dt.day(), 1);
805 assert_eq!(dt.hour(), 0);
806 assert_eq!(dt.minute(), 0);
807 assert_eq!(dt.second(), 0);
808 assert_eq!(dt.attosecond(), 0);
809
810 let dt = DateTime::<Gregorian, Tt>::new_abnormal(2000, 1-60, 1+(365*4 + 366), 0, 0, 0, 0);
812 assert_eq!(dt.year(), 2000);
815 assert_eq!(dt.month(), 1);
816 assert_eq!(dt.day(), 1);
817
818 let dt = DateTime::<Gregorian, Tt>::new_abnormal(1970, 12, 31, 25, 0, 0, 0);
820 assert_eq!(dt.year(), 1971);
821 assert_eq!(dt.month(), 1);
822 assert_eq!(dt.day(), 1);
823 assert_eq!(dt.hour(), 1);
824 }
825
826 #[test]
827 fn test_day_number() {
828 crate::setup_logging();
829
830 let dt = DateTime::<Gregorian, Tt>::new(1,1,1,0,0,0,0).unwrap(); assert_eq!(dt.day_number(), 0);
832
833 let dt2 = DateTime::<Gregorian, Tt>::from_day_number(dt.day_number()).unwrap();
834 assert_eq!(dt,dt2);
835
836 let dt = DateTime::<Gregorian, Tt>::new(2000,1,1,0,0,0,0).unwrap();
837 assert_eq!(dt.day_number(), 730119);
838
839 let dt2 = DateTime::<Gregorian, Tt>::from_day_number(dt.day_number()).unwrap();
840 assert_eq!(dt,dt2);
841
842 assert_eq!(dt2.day_number(), dt.day_number())
843 }
844
845 #[test]
846 fn test_day_fraction() {
847 crate::setup_logging();
848
849 use float_cmp::ApproxEq;
850 let g1 = DateTime::<Gregorian, Tt>::new(2000, 1, 1, 12, 0, 0, 0).unwrap();
851 assert!( g1.day_fraction().approx_eq(0.5, (0.0, 1) ));
852 let g2 = DateTime::<Gregorian, Tt>::new(2000, 1, 1, 18, 0, 0, 0).unwrap();
853 assert!( g2.day_fraction().approx_eq(0.75, (0.0, 1) ));
854 let g3 = DateTime::<Gregorian, Tt>::new(2000, 1, 1, 0, 0, 1, 0).unwrap();
855 assert!( g3.day_fraction().approx_eq(1./86400., (0.0, 1) ));
856
857 let g4 = DateTime::<Gregorian, Tt>::from_day_number_and_fraction(g1.day_number(), 0.75).unwrap();
858 assert_eq!(g4, g2);
859
860 let g4 = DateTime::<Gregorian, Tt>::from_day_number_and_fraction(g1.day_number(), 19./97.).unwrap();
861 assert!(g4.day_fraction().approx_eq(19./97., (0.0, 1) ));
862 }
863
864 #[test]
865 fn test_extractors() {
866 crate::setup_logging();
867
868 let g = DateTime::<Gregorian, Tt>::new(1965, 3, 7, 14, 29, 42, 500_000_000_000_000_000).unwrap();
869 assert_eq!( g.year(), 1965 );
870 assert_eq!( g.month(), 3 );
871 assert_eq!( g.month0(), 2 );
872 assert_eq!( g.day(), 7 );
873 assert_eq!( g.day0(), 6 );
874 assert_eq!( g.hour(), 14);
875 assert_eq!( g.minute(), 29);
876 assert_eq!( g.second(), 42);
877 assert_eq!( g.attosecond(), 500_000_000_000_000_000);
878 }
879
880 #[test]
881 fn test_setters() {
882 crate::setup_logging();
883
884 let mut g = DateTime::<Gregorian, Tt>::new(1965, 3, 7, 14, 29, 42, 500_000_000_000_000_000).unwrap();
885
886 g.set_year(1921);
887 assert_eq!( g.year(), 1921 );
888
889 g.set_month(1).unwrap();
890 assert_eq!( g.month(), 1 );
891
892 g.set_day(17).unwrap();
893 assert_eq!( g.day(), 17 );
894
895 g.set_hour(3).unwrap();
896 assert_eq!( g.hour(), 3 );
897
898 g.set_minute(55).unwrap();
899 assert_eq!( g.minute(), 55 );
900
901 g.set_second(51).unwrap();
902 assert_eq!( g.second(), 51 );
903
904 g.set_attosecond(123_456_789_012_345_678).unwrap();
905 assert_eq!( g.attosecond(), 123_456_789_012_345_678 );
906
907 let h = DateTime::<Gregorian, Tt>::new(1921, 1, 17, 3, 55, 51, 123_456_789_012_345_678).unwrap();
908
909 assert_eq!(g, h);
910
911 let mut g = DateTime::<Gregorian, Tt>::new(1997, 3, 30, 17, 24, 06, 2340897).unwrap();
912 assert!(g.set_month(2).is_err());
913 assert_eq!(g.month(), 3);
914 assert!(g.set_day(28).is_ok());
915 assert!(g.set_month(2).is_ok());
916 assert_eq!(g.month(), 2);
917 assert_eq!(g.day(), 28);
918 }
919
920 #[test]
921 fn test_comparison() {
922 crate::setup_logging();
923
924 let g = DateTime::<Gregorian, Tt>::new(1965, 3, 7, 14, 29, 42, 500_000_000_000_000_000).unwrap();
925 let h = DateTime::<Gregorian, Tt>::new(1966, 1, 17, 3, 55, 51, 123_456_789_012_345_678).unwrap();
926 let i = DateTime::<Gregorian, Tt>::new(1966, 3, 7, 14, 29, 42, 500_000_000_000_000_000).unwrap();
927 let j = DateTime::<Gregorian, Tt>::new(1966, 3, 7, 14, 29, 42, 500_000_000_000_000_000).unwrap();
928 assert!( g < h );
929 assert!( h < i );
930 assert!( i == j);
931 }
932
933 #[test]
934 fn test_math() {
935 crate::setup_logging();
936
937 let g = DateTime::<Gregorian, Tt>::new(1996, 3, 2, 0, 0, 0, 50).unwrap();
938 let week_less_150ns = Duration::new(86400 * 7, 150);
939 let earlier = g - week_less_150ns;
940 assert_eq!(earlier.year(), 1996);
941 assert_eq!(earlier.month(), 2);
942 assert_eq!(earlier.day(), 23);
943 assert_eq!(earlier.hour(), 23);
944 assert_eq!(earlier.minute(), 59);
945 assert_eq!(earlier.second(), 59);
946 assert_eq!(earlier.attosecond(), 1_000_000_000_000_000_000 - 100);
947
948 let g1 = DateTime::<Gregorian, Tt>::new(2000, 1, 1, 0, 0, 0, 0).unwrap();
949 let g2 = DateTime::<Gregorian, Tt>::new(2001, 2, 2, 1, 3, 5, 11).unwrap();
950 let diff = g2 - g1;
951 assert_eq!(diff.seconds_part(),
952 366*86400 + 31*86400 + 1*86400 + 1*3600 + 3*60 + 5);
953 assert_eq!(diff.attos_part(), 11);
954 }
955
956 #[test]
957 fn test_print_extremes() {
958 crate::setup_logging();
959
960 let min = DateTime::<Gregorian, Tt>::new(std::i32::MIN, 1, 1, 0, 0, 0, 0).unwrap();
961 info!("Min gregorian: {}", min);
962 let max = DateTime::<Gregorian, Tt>::new(std::i32::MAX, 12, 31, 23, 59, 59, 999_999_999_999_999_999).unwrap();
963 info!("Max gregorian: {}", max);
964 }
965
966 #[test]
967 fn test_bc_day_numbers() {
968 crate::setup_logging();
969
970 let mar1 = DateTime::<Gregorian, Tt>::new(0,3,1,0,0,0,0).unwrap();
971 let feb29 = DateTime::<Gregorian, Tt>::new(0,2,29,0,0,0,0).unwrap();
972 let feb28 = DateTime::<Gregorian, Tt>::new(0,2,28,0,0,0,0).unwrap();
973 assert_eq!(mar1.day_number(), -306);
974 assert_eq!(feb29.day_number(), -307);
975 assert_eq!(feb28.day_number(), -308);
976
977 let mar1x = DateTime::<Gregorian, Tt>::from_day_number(-306).unwrap();
978 let feb29x = DateTime::<Gregorian, Tt>::from_day_number(-307).unwrap();
979 let feb28x = DateTime::<Gregorian, Tt>::from_day_number(-308).unwrap();
980 assert_eq!(mar1, mar1x);
981 assert_eq!(feb29, feb29x);
982 assert_eq!(feb28, feb28x);
983 }
984
985 #[test]
986 fn test_convert_calendar() {
987 crate::setup_logging();
988
989 let j = DateTime::<Julian, Tt>::new(1582,10,5,0,0,0,0).unwrap();
990 let g = DateTime::<Gregorian, Tt>::new(1582,10,15,0,0,0,0).unwrap();
991 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
992 assert_eq!(j, j2);
993 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
994 assert_eq!(g, g2);
995
996 let j = DateTime::<Julian, Tt>::new(1582,10,4,0,0,0,0).unwrap();
997 let g = DateTime::<Gregorian, Tt>::new(1582,10,14,0,0,0,0).unwrap();
998 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
999 assert_eq!(j, j2);
1000 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
1001 assert_eq!(g, g2);
1002
1003 let j = DateTime::<Julian, Tt>::new(-4713,1,1,0,0,0,0).unwrap();
1004 let g = DateTime::<Gregorian, Tt>::new(-4714,11,24,0,0,0,0).unwrap();
1005 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
1006 assert_eq!(j, j2);
1007 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
1008 assert_eq!(g, g2);
1009
1010 let j = DateTime::<Julian, Tt>::new(1,1,3,0,0,0,0).unwrap();
1011 let g = DateTime::<Gregorian, Tt>::new(1,1,1,0,0,0,0).unwrap();
1012 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
1013 assert_eq!(j, j2);
1014 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
1015 assert_eq!(g, g2);
1016
1017 let j = DateTime::<Julian, Tt>::new(1,1,1,0,0,0,0).unwrap();
1018 let g = DateTime::<Gregorian, Tt>::new(0,12,30,0,0,0,0).unwrap();
1019 let j2: DateTime<Julian, Tt> = TryFrom::try_from(g).unwrap();
1020 assert_eq!(j, j2);
1021 let g2: DateTime<Gregorian, Tt> = TryFrom::try_from(j).unwrap();
1022 assert_eq!(g, g2);
1023 }
1024
1025 #[test]
1026 fn test_epoch_duration() {
1027 crate::setup_logging();
1028
1029 let g = DateTime::<Gregorian, Tt>::new(1582,10,14,0,0,0,0).unwrap();
1030 let h = DateTime::<Gregorian, Tt>::from_duration_from_epoch(g.duration_from_epoch());
1031 assert_eq!(g, h);
1032
1033 let g = DateTime::<Julian, Tt>::new(1582,10,14,11,0,5,130).unwrap();
1034 let h = DateTime::<Julian, Tt>::from_duration_from_epoch(g.duration_from_epoch());
1035 assert_eq!(g, h);
1036 }
1037}