astrotime/
date_time.rs

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/// A calendar date and time, with attosecond precision, representing the
18/// time elapsed since the start of the Common Era in a traditional way
19/// according to a particular time `Standard`.
20///
21/// `DateTime`s are type parameterized by a `Calendar` type which is either
22/// `Gregorian` or `Julian`.
23///
24/// Normal ranges for values are as follows:
25///
26/// * year: any i32 value (`-2_147_483_648` .. `2_147_483_647`)
27/// * month: `1` .. `12`
28/// * day: `1` .. `31` (or less in some months)
29/// * hour: `0` .. `23`
30/// * minute: `0` .. `59`
31/// * second: `0` .. `60` (60 is only used under leap-second time standards)
32/// * attosecond: `0` .. `999_999_999_999_999_999`
33///
34/// Even when years are negative, the other values must remain in the positive ranges
35/// as specified (i.e. negative values are not a reflection against zero, but just an
36/// extension further backwards)
37///
38/// Zero and Negative years are handled in accordance with ISO 8601, such that
39/// year 0 is 1 B.C., and year -1 is 2 B.C., etc. In general:
40/// * _n_ B.C. is represented by year 1-_n_
41/// * Year _-y_ represents year _y_+1 B.C. (for positive y).
42///
43/// This type is proleptic; that is, it allows values for dates outside the
44/// normal usage period of the `Calendar`.
45///
46/// The oldest date representable is `-2147483648-01-01 00:00:00.000000000000000000`
47///
48/// The newest date representable is `2147483647-12-31 23:59:59.999999999999999999`
49///
50/// Internally this is stored in a packed format and is 128 bits in size.
51///
52/// This represents the same thing that an `Instant` does, but it makes `Calendar` data
53/// easier to work with, and has such date precomputed and packed within.
54#[derive(Clone, Copy)] // is also Send
55#[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
63// NOTE: Day and Month are packed with 0 basis (0 = 1st day or 1st month)
64const 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;
71// We pack all values (except attos) into a u64 at the following offsets:
72const 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// Pack a value into the packed field
80#[inline]
81fn pack(packed: &mut u64, bits: u64, offset: usize, value: u64) {
82    *packed &= !bits; // zero
83    *packed |= value<<offset; // set
84}
85
86// Pack a value into the packed field, only if you know it's already zero
87#[inline]
88fn pack_without_clearing(packed: &mut u64, offset: usize, value: u64) {
89    *packed |= value<<offset; // set
90}
91
92// Unpack a value from the packed field
93#[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    /// Create a new `DateTime` with the given parts.
100    ///
101    /// # Safety
102    ///
103    /// Parameter values must be within normal ranges, or else the results
104    /// are not defined.
105    #[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    /// Create a new `DateTime` from the given parts.
130    ///
131    /// Values must be within normal ranges. See `DateTime` for details.
132    ///
133    /// # Errors
134    ///
135    /// Will return `Error::RangeError` if any input is outside of the normal
136    /// range (months from 1-12, days from 1-31, hours from 0-23, minutes from
137    /// 0-59, seconds from 0-60, attoseconds from 0-999_999_999_999_999_999)
138    #[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    /// Create a new `DateTime` from the given parts, with BC years.
158    ///
159    /// Values must be within normal ranges. See `DateTime` for details.
160    ///
161    /// # Errors
162    ///
163    /// Will return `Error::RangeError` if any input is outside of the normal
164    /// range (months from 1-12, days from 1-31, hours from 0-23, minutes from
165    /// 0-59, seconds from 0-60, attoseconds from 0-999_999_999_999_999_999)
166    #[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    /// Create a new `DateTime` from the given parts.
177    ///
178    /// Values that are out of normal ranges are allowed, including values that are negative.
179    /// This function will adjust the input your provide into a normal form.
180    ///
181    /// The types we are working with are large i64 types, but they can still overflow.
182    /// Overflow is not detected or reported (FIXME).
183    ///
184    /// # Panics
185    ///
186    /// Shouldn't panic but several math assertions may trigger if we have a bug when
187    /// compiled in development mode.
188    #[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        // NOTE: we don't check whether or not roll ups are necessary.
202        // they don't hurt when they are not necessary as they only
203        // add zero, and branch code tends to be more expensive. So we
204        // do it unconditionally.
205
206        // roll up attoseconds into seconds (handling negative values)
207        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        // roll up seconds into minutes (handling negative values)
214        let (div, modulus) = divmod_i64( second, 60 );
215        minute += div;
216        second = modulus;
217        assert!(second >= 0);
218        assert!(second < 60);
219
220        // roll up minutes into hours
221        let (div, modulus) = divmod_i64( minute, 60 );
222        hour += div;
223        minute = modulus;
224        assert!(minute >= 0);
225        assert!(minute < 60);
226
227        // roll up hours into days
228        let (div, modulus) = divmod_i64( hour, 24 );
229        day0 += div;
230        hour = modulus;
231        assert!(hour >= 0);
232        assert!(hour < 24);
233
234        // We handle the overflowing days further down
235
236        // We cannot handle overflowing months or negative months in
237        // the day_number() function, so we have to normalize months first
238        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        // Compute the day number
246        // NOTE: day may be overflowing or negative.
247        //       C::day_number needs to handle this condition.
248        let dn = C::day_number(year,
249                               (month0+1).try_into().unwrap(),
250                               day0+1).unwrap();
251
252        // Now set the date from that day number
253        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    /// Create a `DateTime` from a day number (integer).
262    ///
263    /// January 1st of 1 A.D. (Common Era) is the epoch and has a day number of 0.
264    ///
265    /// Hour, minute, second and attosecond will be zero.
266    ///
267    /// # Errors
268    ///
269    /// Will return a `Error::RangeError` if `day_number` is out of range.
270    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    /// Create a `DateTime` from a day number (integer) and day fraction (float).
276    ///
277    /// January 1st of 1 A.D. (Common Era) is the epoch and has a day number of 0.
278    ///
279    /// # Errors
280    ///
281    /// Will return a `Error::RangeError` if `day_number` is out of range.
282    ///
283    /// Will return `Error::RangeError` if `day_fraction` is <0.0 or >=1.0
284    ///
285    /// # Panics
286    ///
287    /// Panics on assertions that should only fail if there is a bug.
288    #[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            // f64's mantissa is only 52 bits wide. We can only get 52 bits of precision
302            // at maximum. So the output attoseconds will end with some zeros in any case,
303            // and we use FACTOR (larger than an attosecond) so we don't overflow.
304            let parts = (((FACTOR * 86400) as f64) * day_fraction) as i64;
305
306            // We don't need euclidean modulus here because parts is guaranteed to
307            // not be negative
308            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    /// Create a `DateTime` from a `Duration` from the calendar epoch
332    /// (with the calendar epoch represented in time `Standard` `S`, such
333    /// that no time Standard conversions are done here).
334    #[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    /// The year part
340    #[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    /// The year part in BC years
348    #[allow(clippy::cast_possible_truncation)]
349    #[must_use]
350    #[inline]
351    pub fn year_bc(&self) -> i32 {
352        1 - self.year()
353    }
354
355    /// The month part. Ranges from 1 .. 12
356    #[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    /// The month part where January is mapped to 0. Ranges from 0 .. 11
364    #[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    /// The day part. Ranges from 1 .. 31
372    #[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    /// The day part where the 1st day is mapped to 0. Ranges from 0 .. 30
380    #[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    /// The hour part. Ranges from 0 .. 23
388    #[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    /// The minute part. Ranges from 0 .. 59
396    #[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    /// The second part. Ranges from 0 .. 59
404    #[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    /// The attosecond part. Ranges from `0` .. `999_999_999_999_999_999`
412    #[must_use]
413    #[inline]
414    pub fn attosecond(&self) -> u64 {
415        self.attos
416    }
417
418    /// The date part
419    ///
420    /// Returns (year, month, day)
421    #[must_use]
422    #[inline]
423    pub fn date(&self) -> (i32, u8, u8) {
424        (self.year(), self.month(), self.day())
425    }
426
427    /// The time part
428    ///
429    /// Returns (hour, minute, second, attosecond)
430    #[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    /// Set the year, leaving other fields unchanged
437    #[inline]
438    #[allow(clippy::cast_sign_loss)]
439    pub fn set_year(&mut self, year: i32) {
440        // "year as u64" treats the sign bit as a bit in the MSB, which is what we want,
441        // because we must preserve negative years in our packing.
442        pack(&mut self.packed, YEAR_BITS, YEAR_OFFSET, year as u64);
443    }
444
445    /// Set the year with a BC year, leaving other fields unchanged
446    #[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        // "year as u64" treats the sign bit as a bit in the MSB, which is what we want,
451        // because we must preserve negative years in our packing.
452        pack(&mut self.packed, YEAR_BITS, YEAR_OFFSET, year as u64);
453    }
454
455    /// Set the month, leaving other fields unchanged
456    ///
457    /// # Errors
458    ///
459    /// Will return `Error::RangeError` if `month` is <1 or >12.
460    #[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    /// Set the day, leaving other fields unchanged
473    ///
474    /// # Errors
475    ///
476    /// Will return `Error::RangeError` if `day` is outside of the range of days
477    /// for the month.
478    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    /// Set the hour, leaving other fields unchanged
487    ///
488    /// # Errors
489    ///
490    /// Will return `Error::RangeError` if `hour` is greater than 23.
491    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    /// Set the minute, leaving other fields unchanged
500    ///
501    /// # Errors
502    ///
503    /// Will return `Error::RangeError` if `minute` is greater than 59.
504    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    /// Set the second, leaving other fields unchanged
513    ///
514    /// # Errors
515    ///
516    /// Will return `Error::RangeError` if `second` is greater than 60.
517    /// The second of '60' should only be used for leapsecond situations, but
518    /// no error is thrown if used otherwise.
519    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    /// Set the attosecond, leaving other fields unchanged
528    ///
529    /// # Errors
530    ///
531    /// Will return `Error::RangeError` if `attosecond` are out of the proscribed range
532    /// (more than 1 seconds worth of attoseconds)
533    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    /// Set the date part (year, month, day)
542    ///
543    /// # Errors
544    ///
545    /// Will return `Error::RangeError` if any input values are out of the proscribed range
546    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    /// Set the time part (hour, minute, second, attosecond)
554    ///
555    /// # Errors
556    ///
557    /// Will return `Error::RangeError` if any input values are out of the proscribed range
558    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    /// Day number (integer).
567    ///
568    /// January 1st of 1 A.D. (Common Era) is the epoch and has a day number of 0.
569    ///
570    /// # Panics
571    ///
572    /// Will only panic on a bug that caused internal values to get out of range.
573    #[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    /// Day fraction, fractional part of the day since midnight
579    ///
580    /// This isn't attosecond accurate because a day contains more attoseconds than
581    /// can fit in a f64 (which has 52 bits of precision).  However, it should be
582    /// accurate to 10,000 attoseconds.
583    #[allow(clippy::cast_precision_loss)]
584    #[must_use]
585    pub fn day_fraction(&self) -> f64 {
586        // In order to preserve as much precision as we can, we count
587        // in units of 10^-14 seconds (10,000 attoseconds).
588        // A 24-hour duration of these won't overflow a u64. Anything
589        // smaller would.
590        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    /// Duration from the calendar epoch (with the calendar epoch represented
600    /// in the time `Standard` `S`, such that no time Standard conversions are
601    /// done here).
602    ///
603    /// # Panics
604    ///
605    /// Only panics if we have an internal data consistency issue
606    #[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) // it will normalize
670    }
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        // This is right out of leap second file for 1 Jan 1972
769        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        // 3rd leap second
779        // NOTE FIXME ELSEWHERE: t1900 must not include leap seconds, or else
780        // this would be off by 2 as it does not account for the 2 leap seconds
781        // added prior to it.
782        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        // Test hour roll over that crosses a month during the end of
792        // February during a leap year
793        let dt = DateTime::<Gregorian, Tt>::new_abnormal(1972, 2, 29, 25, 0, 0, 0);
794        assert_eq!(dt.month(), 3); // mar
795        assert_eq!(dt.day(), 1); // 1st
796        assert_eq!(dt.hour(), 1);
797
798        // Test some negative values
799        let dt = DateTime::<Gregorian, Tt>::new_abnormal(2000, 1-11, 1+(365-31), -12, 60*12, 0, 0);
800        // We subtract 11 months, but add back the (365-11) days
801        // We subtract 12 hours, but add back the (60*12) minutes
802        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        // Test further negative values
811        let dt = DateTime::<Gregorian, Tt>::new_abnormal(2000, 1-60, 1+(365*4 + 366), 0, 0, 0, 0);
812        // We subtract 60 months, but add back the (365 + 365 + 365 + 366 + 365) days
813        // We subtract 12 hours, but add back the (60*12) minutes
814        assert_eq!(dt.year(), 2000);
815        assert_eq!(dt.month(), 1);
816        assert_eq!(dt.day(), 1);
817
818        // Test year rollover
819        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(); // year 1
831        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}