astrolabe/
datetime.rs

1use crate::offset::Offset;
2use crate::util::constants::DAYS_TO_1970;
3use crate::{
4    errors::{invalid_format::create_invalid_format, AstrolabeError},
5    util::{
6        constants::{
7            DAYS_TO_1970_I64, NANOS_PER_DAY, NANOS_PER_SEC, SECS_PER_DAY_U64, SECS_PER_HOUR_U64,
8            SECS_PER_MINUTE_U64,
9        },
10        date::{
11            convert::{
12                date_to_days, days_to_date, days_to_doy, days_to_wday, months_between,
13                year_doy_to_days, years_between,
14            },
15            manipulate::{
16                add_days, add_months, add_years, set_day, set_day_of_year, set_month, set_year,
17                sub_days, sub_months, sub_years,
18            },
19        },
20        format::format_part,
21        offset::{add_offset_to_dn, remove_offset_from_dn},
22        parse::{
23            parse_format_string, parse_offset, parse_part, ParseUnit, ParsedDate, ParsedTime,
24            Period,
25        },
26        time::{
27            convert::{
28                days_nanos_to_hours, days_nanos_to_micros, days_nanos_to_millis,
29                days_nanos_to_minutes, days_nanos_to_nanos, days_nanos_to_seconds,
30                days_nanos_to_secs, nanos_to_days_nanos, nanos_to_subhour_nanos,
31                nanos_to_submicro_nanos, nanos_to_submilli_nanos, nanos_to_subminute_nanos,
32                nanos_to_subsecond, nanos_to_subsecond_nanos, nanos_to_time, secs_to_days_nanos,
33                since_i128, since_i64, time_to_day_seconds,
34            },
35            manipulate::{
36                add_hours, add_micros, add_millis, add_minutes, add_seconds,
37                clear_nanos_until_micro, clear_nanos_until_milli, clear_nanos_until_minute,
38                clear_nanos_until_nanos, clear_nanos_until_second, set_hour, set_micro, set_milli,
39                set_minute, set_nano, set_second, sub_hours, sub_micros, sub_millis, sub_minutes,
40                sub_seconds,
41            },
42        },
43    },
44    Date, DateUtilities, OffsetUtilities, Precision, Time, TimeUtilities,
45};
46use std::time::{SystemTime, UNIX_EPOCH};
47use std::{
48    cmp,
49    fmt::Display,
50    ops::{Add, AddAssign, Sub, SubAssign},
51    str::FromStr,
52    time::Duration,
53};
54
55/// Combined date and time.
56/// Date is in the proleptic Gregorian calendar and clock time is with nanosecond precision.
57///
58/// See the [`DateUtilites`](#impl-DateUtilities-for-DateTime) and [`TimeUtilities`](#impl-TimeUtilities-for-DateTime) implementations for get, set and manipulation methods.
59///
60/// [`OffsetUtilities`](#impl-OffsetUtilities-for-DateTime) implements methods for setting and getting the offset.
61///
62/// Range: `30. June -5879611 00:00:00`..=`12. July 5879611 23:59:59`. Please note that year 0 does not exist. After year -1 follows year 1.
63#[derive(Debug, Default, Clone, Copy, Eq)]
64pub struct DateTime {
65    pub(crate) days: i32,
66    pub(crate) nanoseconds: u64,
67    pub(crate) offset: Offset,
68}
69
70impl DateTime {
71    /// Creates a new [`DateTime`] instance with [`SystemTime::now()`].
72    ///
73    /// ```rust
74    /// # use astrolabe::{DateTime, DateUtilities};
75    /// let date_time = DateTime::now();
76    /// assert!(2021 < date_time.year());
77    /// ```
78    pub fn now() -> Self {
79        let duration = SystemTime::now()
80            .duration_since(UNIX_EPOCH)
81            .expect("Time went backwards");
82
83        let days = duration.as_secs() / SECS_PER_DAY_U64 + DAYS_TO_1970;
84        let nanoseconds =
85            duration.as_secs() % SECS_PER_DAY_U64 * NANOS_PER_SEC + duration.subsec_nanos() as u64;
86
87        Self {
88            days: days as i32,
89            nanoseconds,
90            offset: Offset::default(),
91        }
92    }
93
94    /// Creates a new [`DateTime`] instance with [`SystemTime::now()`] with the local timezone as the offset.
95    ///
96    /// ```rust
97    /// # use astrolabe::{DateTime, DateUtilities, Offset, OffsetUtilities};
98    /// let date_time = DateTime::now_local();
99    /// assert!(2021 < date_time.year());
100    /// assert_eq!(date_time.get_offset(), Offset::Local);
101    /// ```
102    pub fn now_local() -> Self {
103        Self::now().set_offset(Offset::Local)
104    }
105
106    /// Creates a new [`DateTime`] instance from year, month, day (day of month), hour, minute and seconds.
107    ///
108    /// Returns an [`OutOfRange`](AstrolabeError::OutOfRange) error if the provided values are invalid.
109    ///
110    /// ```rust
111    /// # use astrolabe::DateTime;
112    /// let date_time = DateTime::from_ymdhms(2022, 05, 02, 12, 32, 1).unwrap();
113    /// assert_eq!("2022/05/02 12:32:01", date_time.format("yyyy/MM/dd HH:mm:ss"));
114    /// ```
115    pub fn from_ymdhms(
116        year: i32,
117        month: u32,
118        day: u32,
119        hour: u32,
120        minute: u32,
121        second: u32,
122    ) -> Result<Self, AstrolabeError> {
123        let days = date_to_days(year, month, day)?;
124        let seconds = time_to_day_seconds(hour, minute, second)? as u64;
125        Ok(Self {
126            days,
127            nanoseconds: seconds * NANOS_PER_SEC,
128            offset: Offset::default(),
129        })
130    }
131
132    /// Returns the DateTime as year, month, day (day of month), hour, minute and seconds.
133    ///
134    /// ```rust
135    /// # use astrolabe::DateTime;
136    /// let date_time = DateTime::from_ymdhms(2022, 05, 02, 12, 32, 1).unwrap();
137    /// let (year, month, day, hour, minute, second) = date_time.as_ymdhms();
138    /// assert_eq!(2022, year);
139    /// assert_eq!(5, month);
140    /// assert_eq!(2, day);
141    /// assert_eq!(12, hour);
142    /// assert_eq!(32, minute);
143    /// assert_eq!(1, second);
144    /// ```
145    pub fn as_ymdhms(&self) -> (i32, u32, u32, u32, u32, u32) {
146        let (year, month, day) = self.as_ymd();
147        let (hour, minute, second) = self.as_hms();
148        (year, month, day, hour, minute, second)
149    }
150
151    /// Creates a new [`DateTime`] instance from year, month and day (day of month).
152    ///
153    /// Returns an [`OutOfRange`](AstrolabeError::OutOfRange) error if the provided values are invalid.
154    ///
155    /// ```rust
156    /// # use astrolabe::DateTime;
157    /// let date_time = DateTime::from_ymd(2022, 05, 02).unwrap();
158    /// assert_eq!("2022/05/02", date_time.format("yyyy/MM/dd"));
159    /// ```
160    pub fn from_ymd(year: i32, month: u32, day: u32) -> Result<Self, AstrolabeError> {
161        let days = date_to_days(year, month, day)?;
162
163        Ok(Self {
164            days,
165            nanoseconds: 0,
166            offset: Offset::default(),
167        })
168    }
169
170    /// Returns the DateTime as year, month and day (day of month).
171    ///
172    /// ```rust
173    /// # use astrolabe::DateTime;
174    /// let date_time = DateTime::from_ymd(2022, 05, 02).unwrap();
175    /// let (year, month, day) = date_time.as_ymd();
176    /// assert_eq!(2022, year);
177    /// assert_eq!(5, month);
178    /// assert_eq!(2, day);
179    /// ```
180    pub fn as_ymd(&self) -> (i32, u32, u32) {
181        days_to_date(self.days)
182    }
183
184    /// Creates a new [`DateTime`] instance from hour, minute and seconds.
185    ///
186    /// Returns an [`OutOfRange`](AstrolabeError::OutOfRange) error if the provided values are invalid.
187    ///
188    /// ```rust
189    /// # use astrolabe::DateTime;
190    /// let date_time = DateTime::from_hms(12, 32, 12).unwrap();
191    /// assert_eq!("0001/01/01 12:32:12", date_time.format("yyyy/MM/dd HH:mm:ss"));
192    /// ```
193    pub fn from_hms(hour: u32, minute: u32, second: u32) -> Result<Self, AstrolabeError> {
194        let seconds = time_to_day_seconds(hour, minute, second)? as u64;
195
196        Ok(Self {
197            days: 0,
198            nanoseconds: seconds * NANOS_PER_SEC,
199            offset: Offset::default(),
200        })
201    }
202
203    /// Returns the DateTime as hour, minute and seconds.
204    ///
205    /// ```rust
206    /// # use astrolabe::DateTime;
207    /// let date_time = DateTime::from_hms(12, 12, 12).unwrap();
208    /// let (hour, minute, second) = date_time.as_hms();
209    /// assert_eq!(12, hour);
210    /// assert_eq!(12, minute);
211    /// assert_eq!(12, second);
212    /// ```
213    pub fn as_hms(&self) -> (u32, u32, u32) {
214        let seconds = self.nanoseconds / NANOS_PER_SEC;
215
216        let hour = seconds / 3600;
217        let minute = (seconds % 3600) / 60;
218        let second = seconds % 60;
219
220        (hour as u32, minute as u32, second as u32)
221    }
222
223    /// Creates a new [`DateTime`] with the specified time.
224    ///
225    /// ```rust
226    /// # use astrolabe::{DateTime, Time};
227    /// let time = Time::from_hms(12, 32, 1).unwrap();
228    /// let date_time = DateTime::from_ymd(2022, 5, 2).unwrap().set_time(time);
229    /// assert_eq!("2022/05/02 12:32:01", date_time.format("yyyy/MM/dd HH:mm:ss"));
230    /// ```
231    pub fn set_time(&self, time: Time) -> Self {
232        Self {
233            days: self.days,
234            nanoseconds: time.as_nanos(),
235            offset: self.offset,
236        }
237    }
238
239    /// Creates a new [`DateTime`] instance from an RFC 3339 timestamp string.
240    ///
241    /// ```rust
242    /// # use astrolabe::DateTime;
243    /// let date_time = DateTime::parse_rfc3339("2022-05-02T15:30:20Z").unwrap();
244    /// assert_eq!("2022/05/02 15:30:20", date_time.format("yyyy/MM/dd HH:mm:ss"));
245    /// ```
246    pub fn parse_rfc3339(string: &str) -> Result<Self, AstrolabeError> {
247        if string.len() < 20 {
248            return Err(create_invalid_format(
249                "RFC 3339 string cannot be shorter than 20 chars".to_string(),
250            ));
251        }
252
253        let year = string[0..4].parse::<i32>().map_err(|_| {
254            create_invalid_format("Failed parsing year from RFC 3339 string".to_string())
255        })?;
256        let month = string[5..7].parse::<u32>().map_err(|_| {
257            create_invalid_format("Failed parsing month from RFC 3339 string".to_string())
258        })?;
259        let day = string[8..10].parse::<u32>().map_err(|_| {
260            create_invalid_format("Failed parsing day from RFC 3339 string".to_string())
261        })?;
262        let hour = string[11..13].parse::<u32>().map_err(|_| {
263            create_invalid_format("Failed parsing hour from RFC 3339 string".to_string())
264        })?;
265        let minute = string[14..16].parse::<u32>().map_err(|_| {
266            create_invalid_format("Failed parsing minute from RFC 3339 string".to_string())
267        })?;
268        let second = string[17..19].parse::<u32>().map_err(|_| {
269            create_invalid_format("Failed parsing second from RFC 3339 string".to_string())
270        })?;
271
272        let (nanos, offset) = if string.chars().nth(19).unwrap() == '.' {
273            let nanos_string = string[20..]
274                .chars()
275                .take_while(|&char| char != 'Z' && char != '+' && char != '-')
276                .collect::<String>();
277            let nanos = nanos_string.parse::<u64>().map_err(|_| {
278                create_invalid_format("Failed parsing subseconds from RFC 3339 string".to_string())
279            })? * (1000000000 / 10_u64.pow(nanos_string.len() as u32));
280
281            let offset_substring = string[20..]
282                .chars()
283                .position(|char| char == 'Z' || char == '+' || char == '-')
284                .ok_or_else(|| {
285                    create_invalid_format("Failed parsing offset from RFC 3339 string".to_string())
286                })?;
287            let offset = parse_offset(&string[20 + offset_substring..])?;
288
289            (nanos, offset)
290        } else {
291            let offset = parse_offset(&string[19..])?;
292            (0, offset)
293        };
294
295        let days = date_to_days(year, month, day)?;
296        let seconds = time_to_day_seconds(hour, minute, second)? as u64;
297
298        Ok(Self {
299            days,
300            nanoseconds: seconds * NANOS_PER_SEC + nanos,
301            offset: Offset::default(),
302        }
303        .as_offset(Offset::Fixed(offset)))
304    }
305
306    /// Format as an RFC 3339 timestamp (`2022-05-02T15:30:20Z`).
307    ///
308    /// Use the [`Precision`] enum to specify decimal places after seconds:
309    /// * [`Precision::Seconds`] -> `2022-05-02T15:30:20Z`
310    /// * [`Precision::Centis`] -> `2022-05-02T15:30:20.00Z`
311    /// * [`Precision::Millis`] -> `2022-05-02T15:30:20.000Z`
312    /// * [`Precision::Micros`] -> `2022-05-02T15:30:20.000000Z`
313    /// * [`Precision::Nanos`] -> `2022-05-02T15:30:20.000000000Z`
314    ///
315    /// ```rust
316    /// # use astrolabe::{DateTime, Precision};
317    /// let date_time = DateTime::from_ymdhms(2022, 5, 2, 15, 30, 20).unwrap();
318    /// assert_eq!("2022-05-02T15:30:20Z", date_time.format_rfc3339(Precision::Seconds));
319    /// // Equivalent to:
320    /// assert_eq!("2022-05-02T15:30:20Z", date_time.format("yyyy-MM-ddTHH:mm:ssXXX"));
321    /// ```
322    pub fn format_rfc3339(&self, precision: Precision) -> String {
323        match precision {
324            Precision::Seconds => self.format("yyyy-MM-ddTHH:mm:ssXXX"),
325            Precision::Centis => self.format("yyyy-MM-ddTHH:mm:ss.nnXXX"),
326            Precision::Millis => self.format("yyyy-MM-ddTHH:mm:ss.nnnXXX"),
327            Precision::Micros => self.format("yyyy-MM-ddTHH:mm:ss.nnnnXXX"),
328            Precision::Nanos => self.format("yyyy-MM-ddTHH:mm:ss.nnnnnXXX"),
329        }
330    }
331
332    /// Parses a string with a given format and creates a new [`DateTime`] instance from it. See [`DateTime::format`] for a list of available symbols.
333    ///
334    /// Returns an [`InvalidFormat`](AstrolabeError::InvalidFormat) error if the given string could not be parsed with the given format.
335    ///
336    /// ```rust
337    /// # use astrolabe::DateTime;
338    /// let date_time = DateTime::parse("2022-05-02 12:32:01", "yyyy-MM-dd HH:mm:ss").unwrap();
339    /// assert_eq!("2022/05/02 12:32:01", date_time.format("yyyy/MM/dd HH:mm:ss"));
340    /// ```
341    pub fn parse(string: &str, format: &str) -> Result<Self, AstrolabeError> {
342        let parts = parse_format_string(format);
343
344        let mut date = ParsedDate::default();
345        let mut time = ParsedTime::default();
346        let mut string = string.to_string();
347
348        for part in parts {
349            // Escaped apostrophes
350            if part.starts_with('\u{0000}') {
351                string.replace_range(0..part.len(), "");
352                continue;
353            }
354
355            // Escaped parts
356            if part.starts_with('\'') {
357                string.replace_range(0..part.len() - if part.ends_with('\'') { 2 } else { 1 }, "");
358                continue;
359            }
360
361            let parsed_part = parse_part(&part, &mut string)?;
362            if let Some(parsed_part) = parsed_part {
363                match parsed_part.unit {
364                    ParseUnit::Year => date.year = Some(parsed_part.value as i32),
365                    ParseUnit::Month => date.month = Some(parsed_part.value as u32),
366                    ParseUnit::DayOfMonth => date.day_of_month = Some(parsed_part.value as u32),
367                    ParseUnit::DayOfYear => date.day_of_year = Some(parsed_part.value as u32),
368                    ParseUnit::Hour => time.hour = Some(parsed_part.value as u64),
369                    ParseUnit::PeriodHour => time.period_hour = Some(parsed_part.value as u64),
370                    ParseUnit::Period => {
371                        time.period = Some(if parsed_part.value == 0 {
372                            Period::AM
373                        } else {
374                            Period::PM
375                        })
376                    }
377                    ParseUnit::Minute => time.minute = Some(parsed_part.value as u64),
378                    ParseUnit::Second => time.second = Some(parsed_part.value as u64),
379                    ParseUnit::Decis => time.decis = Some(parsed_part.value as u64),
380                    ParseUnit::Centis => time.centis = Some(parsed_part.value as u64),
381                    ParseUnit::Millis => time.millis = Some(parsed_part.value as u64),
382                    ParseUnit::Micros => time.micros = Some(parsed_part.value as u64),
383                    ParseUnit::Nanos => time.nanos = Some(parsed_part.value as u64),
384                    ParseUnit::Offset => time.offset = Some(parsed_part.value as i32),
385                };
386            };
387        }
388
389        // Use day of year if present, otherwise use month + day of month
390        let mut date_time = if date.day_of_year.is_some() {
391            let days = year_doy_to_days(date.year.unwrap_or(1), date.day_of_year.unwrap(), false)?;
392            Self {
393                days,
394                ..Default::default()
395            }
396        } else {
397            Self::from_ymd(
398                date.year.unwrap_or(1),
399                date.month.unwrap_or(1),
400                date.day_of_month.unwrap_or(1),
401            )?
402        };
403
404        let mut nanoseconds = 0;
405
406        if time.hour.is_some() {
407            nanoseconds += time.hour.unwrap_or(0) * SECS_PER_HOUR_U64 * NANOS_PER_SEC;
408        } else {
409            nanoseconds += (time.period_hour.unwrap_or(0)
410                + time.period.unwrap_or(Period::AM) as u64)
411                * SECS_PER_HOUR_U64
412                * NANOS_PER_SEC;
413        }
414        nanoseconds += time.minute.unwrap_or(0) * SECS_PER_MINUTE_U64 * NANOS_PER_SEC;
415        nanoseconds += time.second.unwrap_or(0) * NANOS_PER_SEC;
416        nanoseconds += time.decis.unwrap_or(0) * 100_000_000;
417        nanoseconds += time.centis.unwrap_or(0) * 10_000_000;
418        nanoseconds += time.millis.unwrap_or(0) * 1_000_000;
419        nanoseconds += time.micros.unwrap_or(0) * 1_000;
420        nanoseconds += time.nanos.unwrap_or(0);
421
422        date_time = date_time.set_time(Time::from_nanos(nanoseconds)?);
423
424        if let Some(offset) = time.offset {
425            date_time = date_time.as_offset(Offset::from_seconds(offset)?);
426        }
427
428        Ok(date_time)
429    }
430
431    /// Formatting with format strings based on [Unicode Date Field Symbols](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table).
432    ///
433    /// Please note that not all symbols are implemented. If you need something that is not implemented, please open an issue on [GitHub](https://github.com/GiyoMoon/astrolabe/issues) describing your need.
434    ///
435    /// # Available Symbols:
436    ///
437    /// | Field Type                 | Pattern  | Examples                       | Hint                                     |
438    /// | -------------------------- | -------- | ------------------------------ | ---------------------------------------- |
439    /// | era                        | G..GGG   | AD                             |                                          |
440    /// |                            | GGGG     | Anno Domini                    | *                                        |
441    /// |                            | GGGGG    | A                              |                                          |
442    /// | year                       | y        | 2, 20, 201, 2017, 20173        |                                          |
443    /// |                            | yy       | 02, 20, 01, 17, 73             |                                          |
444    /// |                            | yyy      | 002, 020, 201, 2017, 20173     |                                          |
445    /// |                            | yyyy     | 0002, 0020, 0201, 2017, 20173  |                                          |
446    /// |                            | yyyyy+   | ...                            | Unlimited length,<br/>padded with zeros. |
447    /// | quarter                    | q        | 2                              | *                                        |
448    /// |                            | qq       | 02                             |                                          |
449    /// |                            | qqq      | Q2                             |                                          |
450    /// |                            | qqqq     | 2nd quarter                    |                                          |
451    /// |                            | qqqqq    | 2                              |                                          |
452    /// | month                      | M        | 9, 12                          |                                          |
453    /// |                            | MM       | 09, 12                         |                                          |
454    /// |                            | MMM      | Sep                            |                                          |
455    /// |                            | MMMM     | September                      | *                                        |
456    /// |                            | MMMMM    | S                              |                                          |
457    /// | week                       | w        | 8, 27                          | Week of year                             |
458    /// |                            | ww       | 08, 27                         | *                                        |
459    /// | days                       | d        | 1                              | Day of month                             |
460    /// |                            | dd       | 01                             | *                                        |
461    /// |                            | D        | 1, 24, 135                     | Day of year, *                           |
462    /// |                            | DD       | 01, 24, 135                    |                                          |
463    /// |                            | DDD      | 001, 024, 135                  |                                          |
464    /// | week day                   | e        | 3                              | 1-7, 1 is Sunday, *                      |
465    /// |                            | ee       | 03                             | 1-7, 1 is Sunday                         |
466    /// |                            | eee      | Tue                            |                                          |
467    /// |                            | eeee     | Tuesday                        |                                          |
468    /// |                            | eeeee    | T                              |                                          |
469    /// |                            | eeeeee   | Tu                             |                                          |
470    /// |                            | eeeeeee  | 2                              | 1-7, 1 is Monday                         |
471    /// |                            | eeeeeeee | 02                             | 1-7, 1 is Monday                         |
472    /// | AM, PM                     | a..aa    | AM, PM                         |                                          |
473    /// |                            | aaa      | am, pm                         | *                                        |
474    /// |                            | aaaa     | a.m., p.m.                     |                                          |
475    /// |                            | aaaaa    | a, p                           |                                          |
476    /// | AM, PM,<br/>noon, midnight | b..bb    | AM, PM,<br/>noon, midnight     |                                          |
477    /// |                            | bbb      | am, pm,<br/>noon, midnight     | *                                        |
478    /// |                            | bbbb     | a.m., p.m.,<br/>noon, midnight |                                          |
479    /// |                            | bbbbb    | a, p, n, mi                    |                                          |
480    /// | hour                       | h        | 1, 12                          | [1-12]                                   |
481    /// |                            | hh       | 01, 12                         | *                                        |
482    /// |                            | H        | 0, 23                          | [0-23]                                   |
483    /// |                            | HH       | 00, 23                         | *                                        |
484    /// |                            | K        | 0, 11                          | [0-11]                                   |
485    /// |                            | KK       | 00, 11                         | *                                        |
486    /// |                            | k        | 1, 24                          | [1-24]                                   |
487    /// |                            | kk       | 01, 24                         | *                                        |
488    /// | minute                     | m        | 0, 59                          |                                          |
489    /// |                            | mm       | 00, 59                         | *                                        |
490    /// | second                     | s        | 0, 59                          |                                          |
491    /// |                            | ss       | 00, 59                         | *                                        |
492    /// | subsecond values           | n        | 1, 9                           | Deciseconds                              |
493    /// |                            | nn       | 01, 99                         | Centiseconds                             |
494    /// |                            | nnn      | 001, 999                       | Milliseconds, *                          |
495    /// |                            | nnnn     | 000001, 999999                 | Microseconds                             |
496    /// |                            | nnnnn    | 000000001, 999999999           | Nanoseconds                              |
497    /// | zone                       | X        | -08, +0530, Z                  |                                          |
498    /// |                            | XX       | -0800, Z                       |                                          |
499    /// |                            | XXX      | -08:00, Z                      | *                                        |
500    /// |                            | XXXX     | -0800, -075258, Z              |                                          |
501    /// |                            | XXXXX    | -08:00, -07:52:58, Z           |                                          |
502    /// |                            | x        | -08, +0530, +00                | Like X but without Z                     |
503    /// |                            | xx       | -0800, +0000                   |                                          |
504    /// |                            | xxx      | -08:00, +00:00                 | *                                        |
505    /// |                            | xxxx     | -0800, -075258, +0000          |                                          |
506    /// |                            | xxxxx    | -08:00, -07:52:58, +00:00      |                                          |
507    ///
508    /// `*` = Default
509    ///
510    /// If the sequence is longer than listed in the table, the output will be the same as the default pattern for this unit (marked with `*`).
511    ///
512    /// Surround any character with apostrophes (`'`) to escape them.
513    /// If you want escape `'`, write `''`.
514    ///
515    /// ```rust
516    /// # use astrolabe::DateTime;
517    /// let date_time = DateTime::from_ymdhms(2022, 5, 2, 12, 32, 1).unwrap();
518    /// assert_eq!("2022/05/02 12:32:01", date_time.format("yyyy/MM/dd HH:mm:ss"));
519    /// // Escape characters
520    /// assert_eq!("2022/MM/dd 12:32:01", date_time.format("yyyy/'MM/dd' HH:mm:ss"));
521    /// assert_eq!("2022/'05/02' 12:32:01", date_time.format("yyyy/''MM/dd'' HH:mm:ss"));
522    /// ```
523    ///
524    pub fn format(&self, format: &str) -> String {
525        let offset_seconds = self.offset.resolve();
526        let parts = parse_format_string(format);
527        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
528
529        parts
530            .iter()
531            .flat_map(|part| -> Vec<char> {
532                // Escaped apostrophes
533                if part.starts_with('\u{0000}') {
534                    return part.replace('\u{0000}', "'").chars().collect::<Vec<char>>();
535                }
536
537                // Escape parts starting with apostrophe
538                if part.starts_with('\'') {
539                    let part = part.replace('\u{0000}', "'");
540                    return part[1..part.len() - usize::from(part.ends_with('\''))]
541                        .chars()
542                        .collect::<Vec<char>>();
543                }
544
545                format_part(part, days, nanoseconds, offset_seconds)
546                    .chars()
547                    .collect::<Vec<char>>()
548            })
549            .collect::<String>()
550    }
551
552    /// Returns the duration between the provided DateTime.
553    pub fn duration_between(&self, compare: &Self) -> Duration {
554        let lower = cmp::min(self, compare);
555        let upper = cmp::max(self, compare);
556
557        let mut days = upper.days as i64 - lower.days as i64;
558        let mut nanos = upper.nanoseconds as i64 - lower.nanoseconds as i64;
559        if lower.nanoseconds > upper.nanoseconds {
560            days -= 1;
561            nanos += NANOS_PER_DAY as i64;
562        };
563
564        let days_duration = Duration::from_secs(days.unsigned_abs() * SECS_PER_DAY_U64);
565        let nanos_duration = Duration::from_nanos(nanos.unsigned_abs());
566        days_duration + nanos_duration
567    }
568}
569
570// ########################################
571//
572//  DateUtility trait implementation
573//
574// ########################################
575
576impl DateUtilities for DateTime {
577    fn year(&self) -> i32 {
578        let days = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).0;
579
580        days_to_date(days).0
581    }
582
583    fn month(&self) -> u32 {
584        let days = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).0;
585
586        days_to_date(days).1
587    }
588
589    fn day(&self) -> u32 {
590        let days = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).0;
591
592        days_to_date(days).2
593    }
594
595    fn day_of_year(&self) -> u32 {
596        let days = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).0;
597
598        days_to_doy(days)
599    }
600
601    fn weekday(&self) -> u8 {
602        let days = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).0;
603
604        days_to_wday(days, false) as u8
605    }
606
607    fn from_timestamp(timestamp: i64) -> Self {
608        let date_time = Self::from_seconds(timestamp + DAYS_TO_1970_I64 * SECS_PER_DAY_U64 as i64);
609        match date_time {
610            Ok(date_time) => date_time,
611            Err(e) => panic!("{}", e),
612        }
613    }
614
615    fn timestamp(&self) -> i64 {
616        self.as_seconds() - DAYS_TO_1970_I64 * SECS_PER_DAY_U64 as i64
617    }
618
619    fn set_year(&self, year: i32) -> Result<Self, AstrolabeError> {
620        let offset_seconds = self.offset.resolve();
621        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
622
623        let new_days = set_year(days, year)?;
624
625        Ok(Self {
626            days: remove_offset_from_dn(new_days, nanoseconds, offset_seconds).0,
627            nanoseconds: self.nanoseconds,
628            offset: self.offset,
629        })
630    }
631
632    fn set_month(&self, month: u32) -> Result<Self, AstrolabeError> {
633        let offset_seconds = self.offset.resolve();
634        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
635
636        let new_days = set_month(days, month)?;
637
638        Ok(Self {
639            days: remove_offset_from_dn(new_days, nanoseconds, offset_seconds).0,
640            nanoseconds: self.nanoseconds,
641            offset: self.offset,
642        })
643    }
644
645    fn set_day(&self, day: u32) -> Result<Self, AstrolabeError> {
646        let offset_seconds = self.offset.resolve();
647        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
648
649        let new_days = set_day(days, day)?;
650
651        Ok(Self {
652            days: remove_offset_from_dn(new_days, nanoseconds, offset_seconds).0,
653            nanoseconds: self.nanoseconds,
654            offset: self.offset,
655        })
656    }
657
658    fn set_day_of_year(&self, day_of_year: u32) -> Result<Self, AstrolabeError> {
659        let offset_seconds = self.offset.resolve();
660        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
661
662        let new_days = set_day_of_year(days, day_of_year)?;
663
664        Ok(Self {
665            days: remove_offset_from_dn(new_days, nanoseconds, offset_seconds).0,
666            nanoseconds: self.nanoseconds,
667            offset: self.offset,
668        })
669    }
670
671    fn add_years(&self, years: u32) -> Self {
672        let new_days = add_years(self.days, years);
673
674        let new_days = match new_days {
675            Ok(new_days) => new_days,
676            Err(e) => panic!("{}", e),
677        };
678
679        Self {
680            days: new_days,
681            nanoseconds: self.nanoseconds,
682            offset: self.offset,
683        }
684    }
685
686    fn add_months(&self, months: u32) -> Self {
687        let new_days = add_months(self.days, months);
688
689        let new_days = match new_days {
690            Ok(new_days) => new_days,
691            Err(e) => panic!("{}", e),
692        };
693
694        Self {
695            days: new_days,
696            nanoseconds: self.nanoseconds,
697            offset: self.offset,
698        }
699    }
700
701    fn add_days(&self, days: u32) -> Self {
702        let new_days = add_days(self.days, days);
703
704        let new_days = match new_days {
705            Ok(new_days) => new_days,
706            Err(e) => panic!("{}", e),
707        };
708
709        Self {
710            days: new_days,
711            nanoseconds: self.nanoseconds,
712            offset: self.offset,
713        }
714    }
715
716    fn sub_years(&self, years: u32) -> Self {
717        let new_days = sub_years(self.days, years);
718
719        let new_days = match new_days {
720            Ok(new_days) => new_days,
721            Err(e) => panic!("{}", e),
722        };
723
724        Self {
725            days: new_days,
726            nanoseconds: self.nanoseconds,
727            offset: self.offset,
728        }
729    }
730
731    fn sub_months(&self, months: u32) -> Self {
732        let new_days = sub_months(self.days, months);
733
734        let new_days = match new_days {
735            Ok(new_days) => new_days,
736            Err(e) => panic!("{}", e),
737        };
738
739        Self {
740            days: new_days,
741            nanoseconds: self.nanoseconds,
742            offset: self.offset,
743        }
744    }
745
746    fn sub_days(&self, days: u32) -> Self {
747        let new_days = sub_days(self.days, days);
748
749        let new_days = match new_days {
750            Ok(new_days) => new_days,
751            Err(e) => panic!("{}", e),
752        };
753
754        Self {
755            days: new_days,
756            nanoseconds: self.nanoseconds,
757            offset: self.offset,
758        }
759    }
760
761    fn clear_until_year(&self) -> Self {
762        Self {
763            offset: self.offset,
764            ..Default::default()
765        }
766    }
767
768    fn clear_until_month(&self) -> Self {
769        let year = days_to_date(self.days).0;
770        let new_days = date_to_days(year, 1, 1).unwrap();
771        Self {
772            days: new_days,
773            offset: self.offset,
774            ..Default::default()
775        }
776    }
777
778    fn clear_until_day(&self) -> Self {
779        let (year, month, _) = days_to_date(self.days);
780        let new_days = date_to_days(year, month, 1).unwrap();
781        Self {
782            days: new_days,
783            offset: self.offset,
784            ..Default::default()
785        }
786    }
787
788    fn years_since(&self, compare: &Self) -> i32 {
789        years_between(
790            self.days,
791            self.nanoseconds,
792            compare.days,
793            compare.nanoseconds,
794        )
795    }
796
797    fn months_since(&self, compare: &Self) -> i32 {
798        months_between(
799            self.days,
800            self.nanoseconds,
801            compare.days,
802            compare.nanoseconds,
803        )
804    }
805
806    fn days_since(&self, compare: &Self) -> i64 {
807        let extra_day = if self.days > compare.days && self.nanoseconds < compare.nanoseconds {
808            -1
809        } else if self.days < compare.days && self.nanoseconds > compare.nanoseconds {
810            1
811        } else {
812            0
813        };
814
815        self.days as i64 - compare.days as i64 + extra_day
816    }
817}
818
819// ########################################
820//
821//  TimeUtility trait implementation
822//
823// ########################################
824
825impl TimeUtilities for DateTime {
826    fn hour(&self) -> u32 {
827        let nanoseconds = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).1;
828
829        nanos_to_time(nanoseconds).0
830    }
831
832    fn minute(&self) -> u32 {
833        let nanoseconds = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).1;
834
835        nanos_to_time(nanoseconds).1
836    }
837
838    fn second(&self) -> u32 {
839        let nanoseconds = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).1;
840
841        nanos_to_time(nanoseconds).2
842    }
843
844    fn milli(&self) -> u32 {
845        let nanoseconds = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).1;
846
847        nanos_to_subsecond(nanoseconds).0
848    }
849
850    fn micro(&self) -> u32 {
851        let nanoseconds = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).1;
852
853        nanos_to_subsecond(nanoseconds).1
854    }
855
856    fn nano(&self) -> u32 {
857        let nanoseconds = add_offset_to_dn(self.days, self.nanoseconds, self.offset.resolve()).1;
858
859        nanos_to_subsecond(nanoseconds).2
860    }
861
862    fn set_hour(&self, hour: u32) -> Result<Self, AstrolabeError> {
863        let offset_seconds = self.offset.resolve();
864
865        let (days, nanos) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
866
867        let new_nanos = set_hour(nanos, hour)?;
868
869        let (new_days, new_nanos) = remove_offset_from_dn(days, new_nanos, offset_seconds);
870
871        Ok(Self {
872            days: new_days,
873            nanoseconds: new_nanos,
874            offset: self.offset,
875        })
876    }
877
878    fn set_minute(&self, minute: u32) -> Result<Self, AstrolabeError> {
879        let offset_seconds = self.offset.resolve();
880
881        let (days, nanos) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
882
883        let new_nanos = set_minute(nanos, minute)?;
884
885        let (new_days, new_nanos) = remove_offset_from_dn(days, new_nanos, offset_seconds);
886
887        Ok(Self {
888            days: new_days,
889            nanoseconds: new_nanos,
890            offset: self.offset,
891        })
892    }
893
894    fn set_second(&self, second: u32) -> Result<Self, AstrolabeError> {
895        let offset_seconds = self.offset.resolve();
896
897        let (days, nanos) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
898
899        let new_nanos = set_second(nanos, second)?;
900
901        let (new_days, new_nanos) = remove_offset_from_dn(days, new_nanos, offset_seconds);
902
903        Ok(Self {
904            days: new_days,
905            nanoseconds: new_nanos,
906            offset: self.offset,
907        })
908    }
909
910    fn set_milli(&self, milli: u32) -> Result<Self, AstrolabeError> {
911        let offset_seconds = self.offset.resolve();
912
913        let (days, nanos) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
914
915        let new_nanos = set_milli(nanos, milli)?;
916
917        let (new_days, new_nanos) = remove_offset_from_dn(days, new_nanos, offset_seconds);
918
919        Ok(Self {
920            days: new_days,
921            nanoseconds: new_nanos,
922            offset: self.offset,
923        })
924    }
925
926    fn set_micro(&self, micro: u32) -> Result<Self, AstrolabeError> {
927        let offset_seconds = self.offset.resolve();
928
929        let (days, nanos) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
930
931        let new_nanos = set_micro(nanos, micro)?;
932
933        let (new_days, new_nanos) = remove_offset_from_dn(days, new_nanos, offset_seconds);
934
935        Ok(Self {
936            days: new_days,
937            nanoseconds: new_nanos,
938            offset: self.offset,
939        })
940    }
941
942    fn set_nano(&self, nano: u32) -> Result<Self, AstrolabeError> {
943        let offset_seconds = self.offset.resolve();
944
945        let (days, nanos) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
946
947        let new_nanos = set_nano(nanos, nano)?;
948
949        let (new_days, new_nanos) = remove_offset_from_dn(days, new_nanos, offset_seconds);
950
951        Ok(Self {
952            days: new_days,
953            nanoseconds: new_nanos,
954            offset: self.offset,
955        })
956    }
957
958    /// Panics if the provided value would result in an out of range datetime.
959    fn add_hours(&self, hours: u32) -> Self {
960        let total_nanos =
961            self.days as i128 * NANOS_PER_DAY as i128 + add_hours(self.nanoseconds, hours) as i128;
962
963        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
964            panic!(
965                "Adding {} hours would result into an out of range datetime",
966                hours
967            )
968        });
969
970        Self {
971            days,
972            nanoseconds,
973            offset: self.offset,
974        }
975    }
976
977    /// Panics if the provided value would result in an out of range datetime.
978    fn add_minutes(&self, minutes: u32) -> Self {
979        let total_nanos = self.days as i128 * NANOS_PER_DAY as i128
980            + add_minutes(self.nanoseconds, minutes) as i128;
981
982        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
983            panic!(
984                "Adding {} minutes would result into an out of range datetime",
985                minutes
986            )
987        });
988
989        Self {
990            days,
991            nanoseconds,
992            offset: self.offset,
993        }
994    }
995
996    /// Panics if the provided value would result in an out of range datetime.
997    fn add_seconds(&self, seconds: u32) -> Self {
998        let total_nanos = self.days as i128 * NANOS_PER_DAY as i128
999            + add_seconds(self.nanoseconds, seconds) as i128;
1000
1001        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1002            panic!(
1003                "Adding {} seconds would result into an out of range datetime",
1004                seconds
1005            )
1006        });
1007
1008        Self {
1009            days,
1010            nanoseconds,
1011            offset: self.offset,
1012        }
1013    }
1014
1015    fn add_millis(&self, millis: u32) -> Self {
1016        let total_nanos = self.days as i128 * NANOS_PER_DAY as i128
1017            + add_millis(self.nanoseconds, millis) as i128;
1018
1019        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1020            panic!(
1021                "Adding {} milliseconds would result into an out of range datetime",
1022                millis
1023            )
1024        });
1025
1026        Self {
1027            days,
1028            nanoseconds,
1029            offset: self.offset,
1030        }
1031    }
1032
1033    fn add_micros(&self, micros: u32) -> Self {
1034        let total_nanos = self.days as i128 * NANOS_PER_DAY as i128
1035            + add_micros(self.nanoseconds, micros) as i128;
1036
1037        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1038            panic!(
1039                "Adding {} microseconds would result into an out of range datetime",
1040                micros
1041            )
1042        });
1043
1044        Self {
1045            days,
1046            nanoseconds,
1047            offset: self.offset,
1048        }
1049    }
1050
1051    fn add_nanos(&self, nanos: u32) -> Self {
1052        let total_nanos =
1053            self.days as i128 * NANOS_PER_DAY as i128 + self.nanoseconds as i128 + nanos as i128;
1054
1055        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1056            panic!(
1057                "Adding {} nanoseconds would result into an out of range datetime",
1058                nanos
1059            )
1060        });
1061
1062        Self {
1063            days,
1064            nanoseconds,
1065            offset: self.offset,
1066        }
1067    }
1068
1069    fn sub_hours(&self, hours: u32) -> Self {
1070        let total_nanos = self.days as i128 * NANOS_PER_DAY as i128
1071            + sub_hours(self.nanoseconds as i64, hours) as i128;
1072
1073        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1074            panic!(
1075                "Subtracting {} hours would result into an out of range datetime",
1076                hours
1077            )
1078        });
1079
1080        Self {
1081            days,
1082            nanoseconds,
1083            offset: self.offset,
1084        }
1085    }
1086
1087    fn sub_minutes(&self, minutes: u32) -> Self {
1088        let total_nanos = self.days as i128 * NANOS_PER_DAY as i128
1089            + sub_minutes(self.nanoseconds as i64, minutes) as i128;
1090
1091        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1092            panic!(
1093                "Subtracting {} minutes would result into an out of range datetime",
1094                minutes
1095            )
1096        });
1097
1098        Self {
1099            days,
1100            nanoseconds,
1101            offset: self.offset,
1102        }
1103    }
1104
1105    fn sub_seconds(&self, seconds: u32) -> Self {
1106        let total_nanos = self.days as i128 * NANOS_PER_DAY as i128
1107            + sub_seconds(self.nanoseconds as i64, seconds) as i128;
1108
1109        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1110            panic!(
1111                "Subtracting {} seconds would result into an out of range datetime",
1112                seconds
1113            )
1114        });
1115
1116        Self {
1117            days,
1118            nanoseconds,
1119            offset: self.offset,
1120        }
1121    }
1122
1123    fn sub_millis(&self, millis: u32) -> Self {
1124        let total_nanos = self.days as i128 * NANOS_PER_DAY as i128
1125            + sub_millis(self.nanoseconds as i64, millis) as i128;
1126
1127        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1128            panic!(
1129                "Subtracting {} milliseconds would result into an out of range datetime",
1130                millis
1131            )
1132        });
1133
1134        Self {
1135            days,
1136            nanoseconds,
1137            offset: self.offset,
1138        }
1139    }
1140
1141    fn sub_micros(&self, micros: u32) -> Self {
1142        let total_nanos = self.days as i128 * NANOS_PER_DAY as i128
1143            + sub_micros(self.nanoseconds as i64, micros) as i128;
1144
1145        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1146            panic!(
1147                "Subtracting {} microseconds would result into an out of range datetime",
1148                micros
1149            )
1150        });
1151
1152        Self {
1153            days,
1154            nanoseconds,
1155            offset: self.offset,
1156        }
1157    }
1158
1159    fn sub_nanos(&self, nanos: u32) -> Self {
1160        let total_nanos =
1161            self.days as i128 * NANOS_PER_DAY as i128 + self.nanoseconds as i128 - nanos as i128;
1162
1163        let (days, nanoseconds) = nanos_to_days_nanos(total_nanos).unwrap_or_else(|_| {
1164            panic!(
1165                "Subtracting {} nanoseconds would result into an out of range datetime",
1166                nanos
1167            )
1168        });
1169
1170        Self {
1171            days,
1172            nanoseconds,
1173            offset: self.offset,
1174        }
1175    }
1176
1177    fn clear_until_hour(&self) -> Self {
1178        let offset_seconds = self.offset.resolve();
1179
1180        let (days, _) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
1181        let (days, nanos) = remove_offset_from_dn(days, 0, offset_seconds);
1182        Self {
1183            days,
1184            nanoseconds: nanos,
1185            offset: self.offset,
1186        }
1187    }
1188
1189    fn clear_until_minute(&self) -> Self {
1190        let offset_seconds = self.offset.resolve();
1191
1192        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
1193        let (days, nanoseconds) =
1194            remove_offset_from_dn(days, clear_nanos_until_minute(nanoseconds), offset_seconds);
1195        Self {
1196            days,
1197            nanoseconds,
1198            offset: self.offset,
1199        }
1200    }
1201
1202    fn clear_until_second(&self) -> Self {
1203        let offset_seconds = self.offset.resolve();
1204
1205        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
1206        let (days, nanoseconds) =
1207            remove_offset_from_dn(days, clear_nanos_until_second(nanoseconds), offset_seconds);
1208        Self {
1209            days,
1210            nanoseconds,
1211            offset: self.offset,
1212        }
1213    }
1214
1215    fn clear_until_milli(&self) -> Self {
1216        let offset_seconds = self.offset.resolve();
1217
1218        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
1219        let (days, nanoseconds) =
1220            remove_offset_from_dn(days, clear_nanos_until_milli(nanoseconds), offset_seconds);
1221        Self {
1222            days,
1223            nanoseconds,
1224            offset: self.offset,
1225        }
1226    }
1227
1228    fn clear_until_micro(&self) -> Self {
1229        let offset_seconds = self.offset.resolve();
1230
1231        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
1232        let (days, nanoseconds) =
1233            remove_offset_from_dn(days, clear_nanos_until_micro(nanoseconds), offset_seconds);
1234        Self {
1235            days,
1236            nanoseconds,
1237            offset: self.offset,
1238        }
1239    }
1240
1241    fn clear_until_nano(&self) -> Self {
1242        let offset_seconds = self.offset.resolve();
1243
1244        let (days, nanoseconds) = add_offset_to_dn(self.days, self.nanoseconds, offset_seconds);
1245        let (days, nanoseconds) =
1246            remove_offset_from_dn(days, clear_nanos_until_nanos(nanoseconds), offset_seconds);
1247        Self {
1248            days,
1249            nanoseconds,
1250            offset: self.offset,
1251        }
1252    }
1253
1254    type SubDayReturn = i64;
1255
1256    fn hours_since(&self, compare: &Self) -> Self::SubDayReturn {
1257        let self_total_hours = days_nanos_to_hours(self.days, self.nanoseconds);
1258        let self_subhour_nanos = nanos_to_subhour_nanos(self.nanoseconds);
1259
1260        let compare_total_hours = days_nanos_to_hours(compare.days, compare.nanoseconds);
1261        let compare_subhour_nanos = nanos_to_subhour_nanos(compare.nanoseconds);
1262
1263        since_i64(
1264            self_total_hours,
1265            self_subhour_nanos,
1266            compare_total_hours,
1267            compare_subhour_nanos,
1268        )
1269    }
1270
1271    fn minutes_since(&self, compare: &Self) -> Self::SubDayReturn {
1272        let self_total_minutes = days_nanos_to_minutes(self.days, self.nanoseconds);
1273        let self_subminute_nanos = nanos_to_subminute_nanos(self.nanoseconds);
1274
1275        let compare_total_minutes = days_nanos_to_minutes(compare.days, compare.nanoseconds);
1276        let compare_subminute_nanos = nanos_to_subminute_nanos(compare.nanoseconds);
1277
1278        since_i64(
1279            self_total_minutes,
1280            self_subminute_nanos,
1281            compare_total_minutes,
1282            compare_subminute_nanos,
1283        )
1284    }
1285
1286    fn seconds_since(&self, compare: &Self) -> Self::SubDayReturn {
1287        let self_total_seconds = days_nanos_to_seconds(self.days, self.nanoseconds);
1288        let self_subsecond_nanos = nanos_to_subsecond_nanos(self.nanoseconds);
1289
1290        let compare_total_seconds = days_nanos_to_seconds(compare.days, compare.nanoseconds);
1291        let compare_subsecond_nanos = nanos_to_subsecond_nanos(compare.nanoseconds);
1292
1293        since_i64(
1294            self_total_seconds,
1295            self_subsecond_nanos,
1296            compare_total_seconds,
1297            compare_subsecond_nanos,
1298        )
1299    }
1300
1301    type SubSecReturn = i128;
1302
1303    fn millis_since(&self, compare: &Self) -> Self::SubSecReturn {
1304        let self_total_millis = days_nanos_to_millis(self.days, self.nanoseconds);
1305        let self_submilli_nanos = nanos_to_submilli_nanos(self.nanoseconds);
1306
1307        let compare_total_millis = days_nanos_to_millis(compare.days, compare.nanoseconds);
1308        let compare_submilli_nanos = nanos_to_submilli_nanos(compare.nanoseconds);
1309
1310        since_i128(
1311            self_total_millis,
1312            self_submilli_nanos,
1313            compare_total_millis,
1314            compare_submilli_nanos,
1315        )
1316    }
1317
1318    fn micros_since(&self, compare: &Self) -> Self::SubSecReturn {
1319        let self_total_micros = days_nanos_to_micros(self.days, self.nanoseconds);
1320        let self_submicro_nanos = nanos_to_submicro_nanos(self.nanoseconds);
1321
1322        let compare_total_micros = days_nanos_to_micros(compare.days, compare.nanoseconds);
1323        let compare_submicro_nanos = nanos_to_submicro_nanos(compare.nanoseconds);
1324
1325        since_i128(
1326            self_total_micros,
1327            self_submicro_nanos,
1328            compare_total_micros,
1329            compare_submicro_nanos,
1330        )
1331    }
1332
1333    fn nanos_since(&self, compare: &Self) -> Self::SubSecReturn {
1334        let self_total_nanos = days_nanos_to_nanos(self.days, self.nanoseconds);
1335
1336        let compare_total_nanos = days_nanos_to_nanos(compare.days, compare.nanoseconds);
1337
1338        self_total_nanos - compare_total_nanos
1339    }
1340}
1341
1342// ########################################
1343//
1344//  OffsetUtility trait implementation
1345//
1346// ########################################
1347
1348impl OffsetUtilities for DateTime {
1349    fn set_offset(&self, offset: Offset) -> Self {
1350        let offset_seconds = offset.resolve();
1351
1352        let offset_days = (self.as_seconds() + offset_seconds as i64) / SECS_PER_DAY_U64 as i64;
1353        let offset_nanos = (self.nanoseconds / NANOS_PER_SEC) as i64 + offset_seconds as i64;
1354        if offset_days < i32::MIN as i64
1355            || offset_days > i32::MAX as i64
1356            || (offset_days == i32::MIN as i64 && offset_nanos.is_negative())
1357        {
1358            panic!("Offset would result in an out of range date");
1359        }
1360
1361        Self {
1362            days: self.days,
1363            nanoseconds: self.nanoseconds,
1364            offset,
1365        }
1366    }
1367
1368    fn as_offset(&self, offset: Offset) -> Self {
1369        let new_nanos = self.as_nanos() - offset.resolve() as i128 * NANOS_PER_SEC as i128;
1370        Self::from_nanos(new_nanos).set_offset(offset)
1371    }
1372
1373    fn get_offset(&self) -> Offset {
1374        self.offset
1375    }
1376}
1377
1378// ########################################
1379//
1380//  Private helper functions
1381//
1382// ########################################
1383
1384impl DateTime {
1385    /// Creates a new [`DateTime`] instance from seconds.
1386    pub(crate) fn from_seconds(seconds: i64) -> Result<Self, AstrolabeError> {
1387        let (days, nanoseconds) = secs_to_days_nanos(seconds)?;
1388
1389        Ok(Self {
1390            days,
1391            nanoseconds,
1392            offset: Offset::default(),
1393        })
1394    }
1395
1396    /// Returns the number of seconds since January 1, 0001 00:00:00 UTC. (Negative if date is before)
1397    pub(crate) fn as_seconds(&self) -> i64 {
1398        days_nanos_to_secs(self.days, self.nanoseconds)
1399    }
1400
1401    /// Creates a new [`DateTime`] instance from nanoseconds.
1402    pub(crate) fn from_nanos(nanos: i128) -> Self {
1403        let (days, nanoseconds) = nanos_to_days_nanos(nanos).unwrap();
1404
1405        Self {
1406            days,
1407            nanoseconds,
1408            offset: Offset::default(),
1409        }
1410    }
1411
1412    /// Returns the number of nanoseconds since January 1, 0001 00:00:00 UTC. (Negative if date is before)
1413    pub(crate) fn as_nanos(&self) -> i128 {
1414        days_nanos_to_nanos(self.days, self.nanoseconds)
1415    }
1416}
1417
1418// ########################################
1419//
1420//  Standard trait implementations
1421//
1422// ########################################
1423
1424impl From<&DateTime> for DateTime {
1425    fn from(date_time: &DateTime) -> Self {
1426        Self {
1427            days: date_time.days,
1428            nanoseconds: date_time.nanoseconds,
1429            offset: date_time.offset,
1430        }
1431    }
1432}
1433
1434impl From<Date> for DateTime {
1435    fn from(value: Date) -> Self {
1436        Self {
1437            days: value.days,
1438            nanoseconds: 0,
1439            offset: Offset::default(),
1440        }
1441    }
1442}
1443impl From<&Date> for DateTime {
1444    fn from(value: &Date) -> Self {
1445        Self {
1446            days: value.days,
1447            nanoseconds: 0,
1448            offset: Offset::default(),
1449        }
1450    }
1451}
1452
1453impl From<Time> for DateTime {
1454    fn from(value: Time) -> Self {
1455        Self {
1456            days: 0,
1457            nanoseconds: value.as_nanos(),
1458            offset: value.get_offset(),
1459        }
1460    }
1461}
1462impl From<&Time> for DateTime {
1463    fn from(time: &Time) -> Self {
1464        Self {
1465            days: 0,
1466            nanoseconds: time.as_nanos(),
1467            offset: time.get_offset(),
1468        }
1469    }
1470}
1471
1472impl Display for DateTime {
1473    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1474        write!(f, "{}", self.format("yyyy/MM/dd HH:mm:ss"))
1475    }
1476}
1477
1478impl PartialEq for DateTime {
1479    fn eq(&self, rhs: &Self) -> bool {
1480        self.as_nanos() == rhs.as_nanos()
1481    }
1482}
1483impl PartialOrd for DateTime {
1484    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1485        Some(self.cmp(other))
1486    }
1487}
1488
1489impl Ord for DateTime {
1490    fn cmp(&self, other: &Self) -> cmp::Ordering {
1491        self.as_nanos().cmp(&other.as_nanos())
1492    }
1493}
1494
1495impl FromStr for DateTime {
1496    type Err = AstrolabeError;
1497
1498    fn from_str(s: &str) -> Result<Self, Self::Err> {
1499        Self::parse_rfc3339(s)
1500    }
1501}
1502
1503impl Add<Time> for DateTime {
1504    type Output = Self;
1505
1506    fn add(self, rhs: Time) -> Self::Output {
1507        let nanos = self.as_nanos() + rhs.as_nanos() as i128;
1508        Self {
1509            days: (nanos / NANOS_PER_DAY as i128) as i32,
1510            nanoseconds: (nanos % NANOS_PER_DAY as i128) as u64,
1511            offset: self.offset,
1512        }
1513    }
1514}
1515impl AddAssign<Time> for DateTime {
1516    fn add_assign(&mut self, rhs: Time) {
1517        *self = *self + rhs;
1518    }
1519}
1520
1521impl Sub<Time> for DateTime {
1522    type Output = Self;
1523
1524    fn sub(self, rhs: Time) -> Self::Output {
1525        let nanos = self.as_nanos() - rhs.as_nanos() as i128;
1526        Self {
1527            days: (nanos / NANOS_PER_DAY as i128) as i32,
1528            nanoseconds: (nanos % NANOS_PER_DAY as i128) as u64,
1529            offset: self.offset,
1530        }
1531    }
1532}
1533impl SubAssign<Time> for DateTime {
1534    fn sub_assign(&mut self, rhs: Time) {
1535        *self = *self - rhs;
1536    }
1537}
1538
1539impl Add<Duration> for DateTime {
1540    type Output = Self;
1541
1542    fn add(self, rhs: Duration) -> Self::Output {
1543        let nanos = self.as_nanos() + rhs.as_nanos() as i128;
1544        Self {
1545            days: (nanos / NANOS_PER_DAY as i128) as i32,
1546            nanoseconds: (nanos % NANOS_PER_DAY as i128) as u64,
1547            offset: self.offset,
1548        }
1549    }
1550}
1551impl AddAssign<Duration> for DateTime {
1552    fn add_assign(&mut self, rhs: Duration) {
1553        *self = *self + rhs;
1554    }
1555}
1556
1557impl Sub<Duration> for DateTime {
1558    type Output = Self;
1559
1560    fn sub(self, rhs: Duration) -> Self::Output {
1561        let nanos = self.as_nanos() - rhs.as_nanos() as i128;
1562        Self {
1563            days: (nanos / NANOS_PER_DAY as i128) as i32,
1564            nanoseconds: (nanos % NANOS_PER_DAY as i128) as u64,
1565            offset: self.offset,
1566        }
1567    }
1568}
1569impl SubAssign<Duration> for DateTime {
1570    fn sub_assign(&mut self, rhs: Duration) {
1571        *self = *self - rhs;
1572    }
1573}