Skip to main content

astrotime/
date_time.rs

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/// A calendar date and time, with attosecond precision, representing the
20/// time elapsed since the start of the Common Era in a traditional way
21/// according to a particular time `Standard`.
22///
23/// `DateTime`s are type parameterized by a `Calendar` type which is either
24/// `Gregorian` or `Julian`.
25///
26/// Normal ranges for values are as follows:
27///
28/// * year: any i32 value (`-2_147_483_648` .. `2_147_483_647`)
29/// * month: `1` .. `12`
30/// * day: `1` .. `31` (or less in some months)
31/// * hour: `0` .. `23`
32/// * minute: `0` .. `59`
33/// * second: `0` .. `60` (60 is only used under leap-second time standards)
34/// * attosecond: `0` .. `999_999_999_999_999_999`
35///
36/// Even when years are negative, the other values must remain in the positive ranges
37/// as specified (i.e. negative values are not a reflection against zero, but just an
38/// extension further backwards)
39///
40/// Zero and Negative years are handled in accordance with ISO 8601, such that
41/// year 0 is 1 B.C., and year -1 is 2 B.C., etc. In general:
42/// * _n_ B.C. is represented by year 1-_n_
43/// * Year _-y_ represents year _y_+1 B.C. (for positive y).
44///
45/// This type is proleptic; that is, it allows values for dates outside the
46/// normal usage period of the `Calendar`.
47///
48/// The oldest date representable is `-2147483648-01-01 00:00:00.000000000000000000`
49///
50/// The newest date representable is `2147483647-12-31 23:59:59.999999999999999999`
51///
52/// Internally this is stored in a packed format and is 128 bits in size.
53///
54/// This represents the same thing that an `Instant` does, but it makes `Calendar` data
55/// easier to work with, and has such date precomputed and packed within.
56#[derive(Clone, Copy)] // is also Send
57#[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
65// NOTE: Day and Month are packed with 0 basis (0 = 1st day or 1st month)
66const 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;
73// We pack all values (except attos) into a u64 at the following offsets:
74const 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// Pack a value into the packed field
82#[inline]
83const fn pack(packed: &mut u64, bits: u64, offset: usize, value: u64) {
84    *packed &= !bits; // zero
85    *packed |= value << offset; // set
86}
87
88// Pack a value into the packed field, only if you know it's already zero
89#[inline]
90const fn pack_without_clearing(packed: &mut u64, offset: usize, value: u64) {
91    *packed |= value << offset; // set
92}
93
94// Unpack a value from the packed field
95#[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    /// Create a new `DateTime` with the given parts.
102    ///
103    /// # Safety
104    ///
105    /// Parameter values must be within normal ranges, or else the results
106    /// are not defined.
107    #[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    /// Create a new `DateTime` from the given parts.
136    ///
137    /// Values must be within normal ranges. See `DateTime` for details.
138    ///
139    /// # Errors
140    ///
141    /// Will return `Error::RangeError` if any input is outside of the normal
142    /// range (months from 1-12, days from 1-31, hours from 0-23, minutes from
143    /// 0-59, seconds from 0-60, attoseconds from 0-999_999_999_999_999_999)
144    #[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    /// Create a new `DateTime` from the given parts, with BC years.
177    ///
178    /// Values must be within normal ranges. See `DateTime` for details.
179    ///
180    /// # Errors
181    ///
182    /// Will return `Error::RangeError` if any input is outside of the normal
183    /// range (months from 1-12, days from 1-31, hours from 0-23, minutes from
184    /// 0-59, seconds from 0-60, attoseconds from 0-999_999_999_999_999_999)
185    #[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    /// Create a new `DateTime` from the given parts that may be out of range.
200    ///
201    /// Values that are out of normal ranges are allowed, including values that are negative.
202    /// This function will adjust the input your provide into a normal form.
203    ///
204    /// The types we are working with are large i64 types, but they can still overflow.
205    /// Overflow is not detected or reported (FIXME).
206    ///
207    /// # Warning
208    ///
209    /// This does not make any leap second adjustments under Utc. We presume that after
210    /// rolling up the numbers, that is the right calendar date.
211    ///
212    /// # Panics
213    ///
214    /// Shouldn't panic but several math assertions may trigger if we have a bug when
215    /// compiled in development mode.
216    #[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        // NOTE: we don't check whether or not roll ups are necessary.
234        // they don't hurt when they are not necessary as they only
235        // add zero, and branch code tends to be more expensive. So we
236        // do it unconditionally.
237
238        // roll up attoseconds into seconds (handling negative values)
239        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        // roll up seconds into minutes (handling negative values)
246        let (div, modulus) = divmod_i64(second, 60);
247        minute += div;
248        second = modulus;
249        assert!(second >= 0);
250        assert!(second < 60);
251
252        // roll up minutes into hours
253        let (div, modulus) = divmod_i64(minute, 60);
254        hour += div;
255        minute = modulus;
256        assert!(minute >= 0);
257        assert!(minute < 60);
258
259        // roll up hours into days
260        let (div, modulus) = divmod_i64(hour, 24);
261        day0 += div;
262        hour = modulus;
263        assert!(hour >= 0);
264        assert!(hour < 24);
265
266        // We handle the overflowing days further down
267
268        // We cannot handle overflowing months or negative months in
269        // the day_number() function, so we have to normalize months first
270        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        // Compute the day number
278        // NOTE: day may be overflowing or negative.
279        //       C::day_number needs to handle this condition.
280        let dn = C::day_number(year, (month0 + 1).try_into().unwrap(), day0 + 1).unwrap();
281
282        // Now set the date from that day number
283        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    /// Create a `DateTime` from a day number (integer).
299    ///
300    /// January 1st of 1 A.D. (Common Era) is the epoch and has a day number of 0.
301    ///
302    /// Hour, minute, second and attosecond will be zero.
303    ///
304    /// # Errors
305    ///
306    /// Will return a `Error::RangeError` if `day_number` is out of range.
307    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    /// Create a `DateTime` from a day number (integer) and day fraction (float).
313    ///
314    /// January 1st of 1 A.D. (Common Era) is the epoch and has a day number of 0.
315    ///
316    /// # Errors
317    ///
318    /// Will return a `Error::RangeError` if `day_number` is out of range.
319    ///
320    /// Will return `Error::RangeError` if `day_fraction` is <0.0 or >=1.0
321    ///
322    /// # Panics
323    ///
324    /// Panics on assertions that should only fail if there is a bug.
325    #[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            // f64's mantissa is only 52 bits wide. We can only get 52 bits of precision
341            // at maximum. So the output attoseconds will end with some zeros in any case,
342            // and we use FACTOR (larger than an attosecond) so we don't overflow.
343            let parts = (((FACTOR * 86400) as f64) * day_fraction) as i64;
344
345            // We don't need euclidean modulus here because parts is guaranteed to
346            // not be negative
347            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    /// Create a `DateTime` from a `Duration` from the calendar epoch
366    /// (with the calendar epoch represented in time `Standard` `S`, such
367    /// that no time Standard conversions are done here).
368    ///
369    /// When using TCG, there is precision loss since we use double
370    /// precision floating point arithmetic, which is less precise than
371    /// the integers used in `Duration`).
372    #[must_use]
373    #[allow(clippy::missing_panics_doc)] // literal value won't
374    #[allow(clippy::cast_precision_loss)]
375    pub fn from_duration_from_epoch(duration: Duration) -> Self {
376        // We will construct a calendar-applicable Duration,
377        // only for computing the calendar date, NOT for actual instant
378        // usage (as it would be wrong).
379        let mut cal_duration: Duration = duration;
380
381        let mut inside_a_leap: bool = false;
382
383        // Leap second adjustment
384        if S::abbrev() == "UTC" {
385            let instant = C::epoch() + duration;
386
387            let leaps = crate::leaps::leap_seconds_elapsed_at(instant);
388
389            // Check if inside a leap second
390            inside_a_leap =
391                crate::leaps::leap_seconds_elapsed_at(instant + Duration::new(1, 0)) > leaps;
392
393            // Remove the leap seconds:
394            cal_duration -= Duration::new(leaps, 0);
395        }
396
397        // (Maybe) change time standards
398        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); // because we removed 1 second just above
418            output.set_second(60).unwrap();
419        }
420
421        output
422    }
423
424    /// The year part
425    #[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    /// The year part in BC years
433    #[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    /// The month part. Ranges from 1 .. 12
441    #[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    /// The month part where January is mapped to 0. Ranges from 0 .. 11
449    #[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    /// The day part. Ranges from 1 .. 31
457    #[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    /// The day part where the 1st day is mapped to 0. Ranges from 0 .. 30
465    #[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    /// The hour part. Ranges from 0 .. 23
473    #[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    /// The minute part. Ranges from 0 .. 59
481    #[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    /// The second part. Ranges from 0 .. 59
489    #[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    /// The attosecond part. Ranges from `0` .. `999_999_999_999_999_999`
497    #[must_use]
498    #[inline]
499    pub const fn attosecond(&self) -> u64 {
500        self.attos
501    }
502
503    /// The day of the week from 1 (Monday) .. 7 (Sunday) (ISO 8601)
504    #[must_use]
505    #[inline]
506    #[allow(clippy::cast_possible_truncation)] // Bound by 0-7, it won't
507    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    /// The date part
513    ///
514    /// Returns (year, month, day)
515    #[must_use]
516    #[inline]
517    pub const fn date(&self) -> (i32, u8, u8) {
518        (self.year(), self.month(), self.day())
519    }
520
521    /// The time part
522    ///
523    /// Returns (hour, minute, second, attosecond)
524    #[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    /// Set the year, leaving other fields unchanged
531    #[inline]
532    #[allow(clippy::cast_sign_loss)]
533    pub const fn set_year(&mut self, year: i32) {
534        // "year as u64" treats the sign bit as a bit in the MSB, which is what we want,
535        // because we must preserve negative years in our packing.
536        pack(&mut self.packed, YEAR_BITS, YEAR_OFFSET, year as u64);
537    }
538
539    /// Set the year with a BC year, leaving other fields unchanged
540    #[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        // "year as u64" treats the sign bit as a bit in the MSB, which is what we want,
545        // because we must preserve negative years in our packing.
546        pack(&mut self.packed, YEAR_BITS, YEAR_OFFSET, year as u64);
547    }
548
549    /// Set the month, leaving other fields unchanged
550    ///
551    /// # Errors
552    ///
553    /// Will return `Error::RangeError` if `month` is <1 or >12.
554    #[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    /// Set the day, leaving other fields unchanged
572    ///
573    /// # Errors
574    ///
575    /// Will return `Error::RangeError` if `day` is outside of the range of days
576    /// for the month.
577    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    /// Set the hour, leaving other fields unchanged
586    ///
587    /// # Errors
588    ///
589    /// Will return `Error::RangeError` if `hour` is greater than 23.
590    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    /// Set the minute, leaving other fields unchanged
599    ///
600    /// # Errors
601    ///
602    /// Will return `Error::RangeError` if `minute` is greater than 59.
603    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    /// Set the second, leaving other fields unchanged
617    ///
618    /// # Errors
619    ///
620    /// Will return `Error::RangeError` if `second` is greater than 60.
621    /// The second of '60' should only be used for leapsecond situations, but
622    /// no error is thrown if used otherwise.
623    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    /// Set the attosecond, leaving other fields unchanged
637    ///
638    /// # Errors
639    ///
640    /// Will return `Error::RangeError` if `attosecond` are out of the proscribed range
641    /// (more than 1 seconds worth of attoseconds)
642    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    /// Set the date part (year, month, day)
651    ///
652    /// # Errors
653    ///
654    /// Will return `Error::RangeError` if any input values are out of the proscribed range
655    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    /// Set the time part (hour, minute, second, attosecond)
663    ///
664    /// # Errors
665    ///
666    /// Will return `Error::RangeError` if any input values are out of the proscribed range
667    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    /// Day number (integer).
676    ///
677    /// January 1st of 1 A.D. (Common Era) is the epoch and has a day number of 0.
678    ///
679    /// # Panics
680    ///
681    /// Will only panic on a bug that caused internal values to get out of range.
682    #[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    /// Day fraction, fractional part of the day since midnight
688    ///
689    /// This isn't attosecond accurate because a day contains more attoseconds than
690    /// can fit in a f64 (which has 52 bits of precision).  However, it should be
691    /// accurate to 10,000 attoseconds.
692    #[allow(clippy::cast_precision_loss)]
693    #[must_use]
694    pub fn day_fraction(&self) -> f64 {
695        // In order to preserve as much precision as we can, we count
696        // in units of 10^-14 seconds (10,000 attoseconds).
697        // A 24-hour duration of these won't overflow a u64. Anything
698        // smaller would.
699        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    /// Duration from the calendar epoch.
709    ///
710    /// When using TCG, there is precision loss since we use double
711    /// precision floating point arithmetic, which is less precise than
712    /// the integers used in `Duration`).
713    ///
714    /// # Panics
715    ///
716    /// Shouldn't panic unless this library has a bug.
717    #[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        // Shift time standards into Tt because calendar epochs are in Tt
730        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        // Leap second adjustment
743        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) // it will normalize
836    }
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
906// For converting calendars between time standards, we cannot
907// use generics because we cannot specify that the two standards
908// are not equal. So we specify all pairwise combinations:
909impl<C: Calendar> From<DateTime<C, Tt>> for DateTime<C, Tai> {
910    fn from(s1: DateTime<C, Tt>) -> Self {
911        // Convert through Instant
912        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        // Convert through Instant
919        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        // Convert through Instant
926        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        // Convert through Instant
934        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        // Convert through Instant
941        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        // Convert through Instant
948        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        // Convert through Instant
956        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        // Convert through Instant
963        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        // Convert through Instant
970        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        // Convert through Instant
978        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        // Convert through Instant
985        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        // Convert through Instant
992        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        // This is right out of leap second file for 1 Jan 1972
1034        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        // 3rd leap second
1044        // NOTE FIXME ELSEWHERE: t1900 must not include leap seconds, or else
1045        // this would be off by 2 as it does not account for the 2 leap seconds
1046        // added prior to it.
1047        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        // Test hour roll over that crosses a month during the end of
1057        // February during a leap year
1058        let dt = DateTime::<Gregorian, Tt>::new_abnormal(1972, 2, 29, 25, 0, 0, 0);
1059        assert_eq!(dt.month(), 3); // mar
1060        assert_eq!(dt.day(), 1); // 1st
1061        assert_eq!(dt.hour(), 1);
1062
1063        // Test some negative values
1064        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        // We subtract 11 months, but add back the (365-11) days
1074        // We subtract 12 hours, but add back the (60*12) minutes
1075        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        // Test further negative values
1084        let dt =
1085            DateTime::<Gregorian, Tt>::new_abnormal(2000, 1 - 60, 1 + (365 * 4 + 366), 0, 0, 0, 0);
1086        // We subtract 60 months, but add back the (365 + 365 + 365 + 366 + 365) days
1087        // We subtract 12 hours, but add back the (60*12) minutes
1088        assert_eq!(dt.year(), 2000);
1089        assert_eq!(dt.month(), 1);
1090        assert_eq!(dt.day(), 1);
1091
1092        // Test year rollover
1093        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(); // year 1
1103        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()); // FAILLING, but why?
1351        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        // Test UTC in the vacinity of a leap second (1 January 1999)
1401        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        // Because we have to use floating point, we
1417        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}