time/
date.rs

1//! The [`Date`] struct and its associated `impl`s.
2
3#[cfg(feature = "formatting")]
4use alloc::string::String;
5use core::num::NonZeroI32;
6use core::ops::{Add, Sub};
7use core::time::Duration as StdDuration;
8use core::{cmp, fmt};
9#[cfg(feature = "formatting")]
10use std::io;
11
12use deranged::RangedI32;
13use num_conv::prelude::*;
14use powerfmt::ext::FormatterExt;
15use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
16
17use crate::convert::*;
18use crate::ext::DigitCount;
19#[cfg(feature = "formatting")]
20use crate::formatting::Formattable;
21use crate::internal_macros::{
22    cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, impl_add_assign,
23    impl_sub_assign,
24};
25#[cfg(feature = "parsing")]
26use crate::parsing::Parsable;
27use crate::util::{days_in_year, days_in_year_month, is_leap_year, weeks_in_year};
28use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday};
29
30type Year = RangedI32<MIN_YEAR, MAX_YEAR>;
31
32/// The minimum valid year.
33pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") {
34    -999_999
35} else {
36    -9999
37};
38/// The maximum valid year.
39pub(crate) const MAX_YEAR: i32 = if cfg!(feature = "large-dates") {
40    999_999
41} else {
42    9999
43};
44
45/// Date in the proleptic Gregorian calendar.
46///
47/// By default, years between ±9999 inclusive are representable. This can be expanded to ±999,999
48/// inclusive by enabling the `large-dates` crate feature. Doing so has performance implications
49/// and introduces some ambiguities when parsing.
50#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
51pub struct Date {
52    /// Bitpacked field containing both the year and ordinal.
53    // |     xx     | xxxxxxxxxxxxxxxxxxxxx | xxxxxxxxx |
54    // |   2 bits   |        21 bits        |  9 bits   |
55    // | unassigned |         year          |  ordinal  |
56    // The year is 15 bits when `large-dates` is not enabled.
57    value: NonZeroI32,
58}
59
60impl Date {
61    /// The minimum valid `Date`.
62    ///
63    /// The value of this may vary depending on the feature flags enabled.
64    // Safety: `ordinal` is not zero.
65    #[allow(clippy::undocumented_unsafe_blocks)]
66    pub const MIN: Self = unsafe { Self::__from_ordinal_date_unchecked(MIN_YEAR, 1) };
67
68    /// The maximum valid `Date`.
69    ///
70    /// The value of this may vary depending on the feature flags enabled.
71    // Safety: `ordinal` is not zero.
72    #[allow(clippy::undocumented_unsafe_blocks)]
73    pub const MAX: Self =
74        unsafe { Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR)) };
75
76    // region: constructors
77    /// Construct a `Date` from the year and ordinal values, the validity of which must be
78    /// guaranteed by the caller.
79    ///
80    /// # Safety
81    ///
82    /// `ordinal` must not be zero. `year` should be in the range `MIN_YEAR..=MAX_YEAR`, but this
83    /// is not a safety invariant.
84    #[doc(hidden)]
85    pub const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self {
86        debug_assert!(year >= MIN_YEAR);
87        debug_assert!(year <= MAX_YEAR);
88        debug_assert!(ordinal != 0);
89        debug_assert!(ordinal <= days_in_year(year));
90
91        Self {
92            // Safety: The caller must guarantee that `ordinal` is not zero.
93            value: unsafe { NonZeroI32::new_unchecked((year << 9) | ordinal as i32) },
94        }
95    }
96
97    /// Attempt to create a `Date` from the year, month, and day.
98    ///
99    /// ```rust
100    /// # use time::{Date, Month};
101    /// assert!(Date::from_calendar_date(2019, Month::January, 1).is_ok());
102    /// assert!(Date::from_calendar_date(2019, Month::December, 31).is_ok());
103    /// ```
104    ///
105    /// ```rust
106    /// # use time::{Date, Month};
107    /// assert!(Date::from_calendar_date(2019, Month::February, 29).is_err()); // 2019 isn't a leap year.
108    /// ```
109    pub const fn from_calendar_date(
110        year: i32,
111        month: Month,
112        day: u8,
113    ) -> Result<Self, error::ComponentRange> {
114        /// Cumulative days through the beginning of a month in both common and leap years.
115        const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [
116            [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
117            [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
118        ];
119
120        ensure_ranged!(Year: year);
121        match day {
122            1..=28 => {}
123            29..=31 if day <= days_in_year_month(year, month) => {}
124            _ => {
125                return Err(error::ComponentRange {
126                    name: "day",
127                    minimum: 1,
128                    maximum: days_in_year_month(year, month) as _,
129                    value: day as _,
130                    conditional_range: true,
131                });
132            }
133        }
134
135        // Safety: `ordinal` is not zero.
136        Ok(unsafe {
137            Self::__from_ordinal_date_unchecked(
138                year,
139                DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1]
140                    + day as u16,
141            )
142        })
143    }
144
145    /// Attempt to create a `Date` from the year and ordinal day number.
146    ///
147    /// ```rust
148    /// # use time::Date;
149    /// assert!(Date::from_ordinal_date(2019, 1).is_ok());
150    /// assert!(Date::from_ordinal_date(2019, 365).is_ok());
151    /// ```
152    ///
153    /// ```rust
154    /// # use time::Date;
155    /// assert!(Date::from_ordinal_date(2019, 366).is_err()); // 2019 isn't a leap year.
156    /// ```
157    pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result<Self, error::ComponentRange> {
158        ensure_ranged!(Year: year);
159        match ordinal {
160            1..=365 => {}
161            366 if is_leap_year(year) => {}
162            _ => {
163                return Err(error::ComponentRange {
164                    name: "ordinal",
165                    minimum: 1,
166                    maximum: days_in_year(year) as _,
167                    value: ordinal as _,
168                    conditional_range: true,
169                });
170            }
171        }
172
173        // Safety: `ordinal` is not zero.
174        Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) })
175    }
176
177    /// Attempt to create a `Date` from the ISO year, week, and weekday.
178    ///
179    /// ```rust
180    /// # use time::{Date, Weekday::*};
181    /// assert!(Date::from_iso_week_date(2019, 1, Monday).is_ok());
182    /// assert!(Date::from_iso_week_date(2019, 1, Tuesday).is_ok());
183    /// assert!(Date::from_iso_week_date(2020, 53, Friday).is_ok());
184    /// ```
185    ///
186    /// ```rust
187    /// # use time::{Date, Weekday::*};
188    /// assert!(Date::from_iso_week_date(2019, 53, Monday).is_err()); // 2019 doesn't have 53 weeks.
189    /// ```
190    pub const fn from_iso_week_date(
191        year: i32,
192        week: u8,
193        weekday: Weekday,
194    ) -> Result<Self, error::ComponentRange> {
195        ensure_ranged!(Year: year);
196        match week {
197            1..=52 => {}
198            53 if week <= weeks_in_year(year) => {}
199            _ => {
200                return Err(error::ComponentRange {
201                    name: "week",
202                    minimum: 1,
203                    maximum: weeks_in_year(year) as _,
204                    value: week as _,
205                    conditional_range: true,
206                });
207            }
208        }
209
210        let adj_year = year - 1;
211        let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
212            + div_floor!(adj_year, 400);
213        let jan_4 = match (raw % 7) as i8 {
214            -6 | 1 => 8,
215            -5 | 2 => 9,
216            -4 | 3 => 10,
217            -3 | 4 => 4,
218            -2 | 5 => 5,
219            -1 | 6 => 6,
220            _ => 7,
221        };
222        let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4;
223
224        Ok(if ordinal <= 0 {
225            // Safety: `ordinal` is not zero.
226            unsafe {
227                Self::__from_ordinal_date_unchecked(
228                    year - 1,
229                    (ordinal as u16).wrapping_add(days_in_year(year - 1)),
230                )
231            }
232        } else if ordinal > days_in_year(year) as i16 {
233            // Safety: `ordinal` is not zero.
234            unsafe {
235                Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year))
236            }
237        } else {
238            // Safety: `ordinal` is not zero.
239            unsafe { Self::__from_ordinal_date_unchecked(year, ordinal as _) }
240        })
241    }
242
243    /// Create a `Date` from the Julian day.
244    ///
245    /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
246    /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
247    ///
248    /// ```rust
249    /// # use time::Date;
250    /// # use time_macros::date;
251    /// assert_eq!(Date::from_julian_day(0), Ok(date!(-4713 - 11 - 24)));
252    /// assert_eq!(Date::from_julian_day(2_451_545), Ok(date!(2000 - 01 - 01)));
253    /// assert_eq!(Date::from_julian_day(2_458_485), Ok(date!(2019 - 01 - 01)));
254    /// assert_eq!(Date::from_julian_day(2_458_849), Ok(date!(2019 - 12 - 31)));
255    /// ```
256    #[doc(alias = "from_julian_date")]
257    pub const fn from_julian_day(julian_day: i32) -> Result<Self, error::ComponentRange> {
258        type JulianDay = RangedI32<{ Date::MIN.to_julian_day() }, { Date::MAX.to_julian_day() }>;
259        ensure_ranged!(JulianDay: julian_day);
260        Ok(Self::from_julian_day_unchecked(julian_day))
261    }
262
263    /// Create a `Date` from the Julian day.
264    ///
265    /// This does not check the validity of the provided Julian day, and as such may result in an
266    /// internally invalid value.
267    #[doc(alias = "from_julian_date_unchecked")]
268    pub(crate) const fn from_julian_day_unchecked(julian_day: i32) -> Self {
269        debug_assert!(julian_day >= Self::MIN.to_julian_day());
270        debug_assert!(julian_day <= Self::MAX.to_julian_day());
271
272        // To avoid a potential overflow, the value may need to be widened for some arithmetic.
273
274        let z = julian_day - 1_721_119;
275        let (mut year, mut ordinal) = if julian_day < -19_752_948 || julian_day > 23_195_514 {
276            let g = 100 * z as i64 - 25;
277            let a = (g / 3_652_425) as i32;
278            let b = a - a / 4;
279            let year = div_floor!(100 * b as i64 + g, 36525) as i32;
280            let ordinal = (b + z - div_floor!(36525 * year as i64, 100) as i32) as _;
281            (year, ordinal)
282        } else {
283            let g = 100 * z - 25;
284            let a = g / 3_652_425;
285            let b = a - a / 4;
286            let year = div_floor!(100 * b + g, 36525);
287            let ordinal = (b + z - div_floor!(36525 * year, 100)) as _;
288            (year, ordinal)
289        };
290
291        if is_leap_year(year) {
292            ordinal += 60;
293            cascade!(ordinal in 1..367 => year);
294        } else {
295            ordinal += 59;
296            cascade!(ordinal in 1..366 => year);
297        }
298
299        // Safety: `ordinal` is not zero.
300        unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }
301    }
302    // endregion constructors
303
304    // region: getters
305    /// Get the year of the date.
306    ///
307    /// ```rust
308    /// # use time_macros::date;
309    /// assert_eq!(date!(2019 - 01 - 01).year(), 2019);
310    /// assert_eq!(date!(2019 - 12 - 31).year(), 2019);
311    /// assert_eq!(date!(2020 - 01 - 01).year(), 2020);
312    /// ```
313    pub const fn year(self) -> i32 {
314        self.value.get() >> 9
315    }
316
317    /// Get the month.
318    ///
319    /// ```rust
320    /// # use time::Month;
321    /// # use time_macros::date;
322    /// assert_eq!(date!(2019 - 01 - 01).month(), Month::January);
323    /// assert_eq!(date!(2019 - 12 - 31).month(), Month::December);
324    /// ```
325    pub const fn month(self) -> Month {
326        self.month_day().0
327    }
328
329    /// Get the day of the month.
330    ///
331    /// The returned value will always be in the range `1..=31`.
332    ///
333    /// ```rust
334    /// # use time_macros::date;
335    /// assert_eq!(date!(2019 - 01 - 01).day(), 1);
336    /// assert_eq!(date!(2019 - 12 - 31).day(), 31);
337    /// ```
338    pub const fn day(self) -> u8 {
339        self.month_day().1
340    }
341
342    /// Get the month and day. This is more efficient than fetching the components individually.
343    // For whatever reason, rustc has difficulty optimizing this function. It's significantly faster
344    // to write the statements out by hand.
345    pub(crate) const fn month_day(self) -> (Month, u8) {
346        /// The number of days up to and including the given month. Common years
347        /// are first, followed by leap years.
348        const CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP: [[u16; 11]; 2] = [
349            [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
350            [31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
351        ];
352
353        let days = CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP[is_leap_year(self.year()) as usize];
354        let ordinal = self.ordinal();
355
356        if ordinal > days[10] {
357            (Month::December, (ordinal - days[10]) as _)
358        } else if ordinal > days[9] {
359            (Month::November, (ordinal - days[9]) as _)
360        } else if ordinal > days[8] {
361            (Month::October, (ordinal - days[8]) as _)
362        } else if ordinal > days[7] {
363            (Month::September, (ordinal - days[7]) as _)
364        } else if ordinal > days[6] {
365            (Month::August, (ordinal - days[6]) as _)
366        } else if ordinal > days[5] {
367            (Month::July, (ordinal - days[5]) as _)
368        } else if ordinal > days[4] {
369            (Month::June, (ordinal - days[4]) as _)
370        } else if ordinal > days[3] {
371            (Month::May, (ordinal - days[3]) as _)
372        } else if ordinal > days[2] {
373            (Month::April, (ordinal - days[2]) as _)
374        } else if ordinal > days[1] {
375            (Month::March, (ordinal - days[1]) as _)
376        } else if ordinal > days[0] {
377            (Month::February, (ordinal - days[0]) as _)
378        } else {
379            (Month::January, ordinal as _)
380        }
381    }
382
383    /// Get the day of the year.
384    ///
385    /// The returned value will always be in the range `1..=366` (`1..=365` for common years).
386    ///
387    /// ```rust
388    /// # use time_macros::date;
389    /// assert_eq!(date!(2019 - 01 - 01).ordinal(), 1);
390    /// assert_eq!(date!(2019 - 12 - 31).ordinal(), 365);
391    /// ```
392    pub const fn ordinal(self) -> u16 {
393        (self.value.get() & 0x1FF) as _
394    }
395
396    /// Get the ISO 8601 year and week number.
397    pub(crate) const fn iso_year_week(self) -> (i32, u8) {
398        let (year, ordinal) = self.to_ordinal_date();
399
400        match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ {
401            0 => (year - 1, weeks_in_year(year - 1)),
402            53 if weeks_in_year(year) == 52 => (year + 1, 1),
403            week => (year, week),
404        }
405    }
406
407    /// Get the ISO week number.
408    ///
409    /// The returned value will always be in the range `1..=53`.
410    ///
411    /// ```rust
412    /// # use time_macros::date;
413    /// assert_eq!(date!(2019 - 01 - 01).iso_week(), 1);
414    /// assert_eq!(date!(2019 - 10 - 04).iso_week(), 40);
415    /// assert_eq!(date!(2020 - 01 - 01).iso_week(), 1);
416    /// assert_eq!(date!(2020 - 12 - 31).iso_week(), 53);
417    /// assert_eq!(date!(2021 - 01 - 01).iso_week(), 53);
418    /// ```
419    pub const fn iso_week(self) -> u8 {
420        self.iso_year_week().1
421    }
422
423    /// Get the week number where week 1 begins on the first Sunday.
424    ///
425    /// The returned value will always be in the range `0..=53`.
426    ///
427    /// ```rust
428    /// # use time_macros::date;
429    /// assert_eq!(date!(2019 - 01 - 01).sunday_based_week(), 0);
430    /// assert_eq!(date!(2020 - 01 - 01).sunday_based_week(), 0);
431    /// assert_eq!(date!(2020 - 12 - 31).sunday_based_week(), 52);
432    /// assert_eq!(date!(2021 - 01 - 01).sunday_based_week(), 0);
433    /// ```
434    pub const fn sunday_based_week(self) -> u8 {
435        ((self.ordinal() as i16 - self.weekday().number_days_from_sunday() as i16 + 6) / 7) as _
436    }
437
438    /// Get the week number where week 1 begins on the first Monday.
439    ///
440    /// The returned value will always be in the range `0..=53`.
441    ///
442    /// ```rust
443    /// # use time_macros::date;
444    /// assert_eq!(date!(2019 - 01 - 01).monday_based_week(), 0);
445    /// assert_eq!(date!(2020 - 01 - 01).monday_based_week(), 0);
446    /// assert_eq!(date!(2020 - 12 - 31).monday_based_week(), 52);
447    /// assert_eq!(date!(2021 - 01 - 01).monday_based_week(), 0);
448    /// ```
449    pub const fn monday_based_week(self) -> u8 {
450        ((self.ordinal() as i16 - self.weekday().number_days_from_monday() as i16 + 6) / 7) as _
451    }
452
453    /// Get the year, month, and day.
454    ///
455    /// ```rust
456    /// # use time::Month;
457    /// # use time_macros::date;
458    /// assert_eq!(
459    ///     date!(2019 - 01 - 01).to_calendar_date(),
460    ///     (2019, Month::January, 1)
461    /// );
462    /// ```
463    pub const fn to_calendar_date(self) -> (i32, Month, u8) {
464        let (month, day) = self.month_day();
465        (self.year(), month, day)
466    }
467
468    /// Get the year and ordinal day number.
469    ///
470    /// ```rust
471    /// # use time_macros::date;
472    /// assert_eq!(date!(2019 - 01 - 01).to_ordinal_date(), (2019, 1));
473    /// ```
474    pub const fn to_ordinal_date(self) -> (i32, u16) {
475        (self.year(), self.ordinal())
476    }
477
478    /// Get the ISO 8601 year, week number, and weekday.
479    ///
480    /// ```rust
481    /// # use time::Weekday::*;
482    /// # use time_macros::date;
483    /// assert_eq!(date!(2019 - 01 - 01).to_iso_week_date(), (2019, 1, Tuesday));
484    /// assert_eq!(date!(2019 - 10 - 04).to_iso_week_date(), (2019, 40, Friday));
485    /// assert_eq!(
486    ///     date!(2020 - 01 - 01).to_iso_week_date(),
487    ///     (2020, 1, Wednesday)
488    /// );
489    /// assert_eq!(
490    ///     date!(2020 - 12 - 31).to_iso_week_date(),
491    ///     (2020, 53, Thursday)
492    /// );
493    /// assert_eq!(date!(2021 - 01 - 01).to_iso_week_date(), (2020, 53, Friday));
494    /// ```
495    pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
496        let (year, ordinal) = self.to_ordinal_date();
497        let weekday = self.weekday();
498
499        match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ {
500            0 => (year - 1, weeks_in_year(year - 1), weekday),
501            53 if weeks_in_year(year) == 52 => (year + 1, 1, weekday),
502            week => (year, week, weekday),
503        }
504    }
505
506    /// Get the weekday.
507    ///
508    /// ```rust
509    /// # use time::Weekday::*;
510    /// # use time_macros::date;
511    /// assert_eq!(date!(2019 - 01 - 01).weekday(), Tuesday);
512    /// assert_eq!(date!(2019 - 02 - 01).weekday(), Friday);
513    /// assert_eq!(date!(2019 - 03 - 01).weekday(), Friday);
514    /// assert_eq!(date!(2019 - 04 - 01).weekday(), Monday);
515    /// assert_eq!(date!(2019 - 05 - 01).weekday(), Wednesday);
516    /// assert_eq!(date!(2019 - 06 - 01).weekday(), Saturday);
517    /// assert_eq!(date!(2019 - 07 - 01).weekday(), Monday);
518    /// assert_eq!(date!(2019 - 08 - 01).weekday(), Thursday);
519    /// assert_eq!(date!(2019 - 09 - 01).weekday(), Sunday);
520    /// assert_eq!(date!(2019 - 10 - 01).weekday(), Tuesday);
521    /// assert_eq!(date!(2019 - 11 - 01).weekday(), Friday);
522    /// assert_eq!(date!(2019 - 12 - 01).weekday(), Sunday);
523    /// ```
524    pub const fn weekday(self) -> Weekday {
525        match self.to_julian_day() % 7 {
526            -6 | 1 => Weekday::Tuesday,
527            -5 | 2 => Weekday::Wednesday,
528            -4 | 3 => Weekday::Thursday,
529            -3 | 4 => Weekday::Friday,
530            -2 | 5 => Weekday::Saturday,
531            -1 | 6 => Weekday::Sunday,
532            val => {
533                debug_assert!(val == 0);
534                Weekday::Monday
535            }
536        }
537    }
538
539    /// Get the next calendar date.
540    ///
541    /// ```rust
542    /// # use time::Date;
543    /// # use time_macros::date;
544    /// assert_eq!(
545    ///     date!(2019 - 01 - 01).next_day(),
546    ///     Some(date!(2019 - 01 - 02))
547    /// );
548    /// assert_eq!(
549    ///     date!(2019 - 01 - 31).next_day(),
550    ///     Some(date!(2019 - 02 - 01))
551    /// );
552    /// assert_eq!(
553    ///     date!(2019 - 12 - 31).next_day(),
554    ///     Some(date!(2020 - 01 - 01))
555    /// );
556    /// assert_eq!(Date::MAX.next_day(), None);
557    /// ```
558    pub const fn next_day(self) -> Option<Self> {
559        if self.ordinal() == 366 || (self.ordinal() == 365 && !is_leap_year(self.year())) {
560            if self.value.get() == Self::MAX.value.get() {
561                None
562            } else {
563                // Safety: `ordinal` is not zero.
564                unsafe { Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) }
565            }
566        } else {
567            Some(Self {
568                // Safety: `ordinal` is not zero.
569                value: unsafe { NonZeroI32::new_unchecked(self.value.get() + 1) },
570            })
571        }
572    }
573
574    /// Get the previous calendar date.
575    ///
576    /// ```rust
577    /// # use time::Date;
578    /// # use time_macros::date;
579    /// assert_eq!(
580    ///     date!(2019 - 01 - 02).previous_day(),
581    ///     Some(date!(2019 - 01 - 01))
582    /// );
583    /// assert_eq!(
584    ///     date!(2019 - 02 - 01).previous_day(),
585    ///     Some(date!(2019 - 01 - 31))
586    /// );
587    /// assert_eq!(
588    ///     date!(2020 - 01 - 01).previous_day(),
589    ///     Some(date!(2019 - 12 - 31))
590    /// );
591    /// assert_eq!(Date::MIN.previous_day(), None);
592    /// ```
593    pub const fn previous_day(self) -> Option<Self> {
594        if self.ordinal() != 1 {
595            Some(Self {
596                // Safety: `ordinal` is not zero.
597                value: unsafe { NonZeroI32::new_unchecked(self.value.get() - 1) },
598            })
599        } else if self.value.get() == Self::MIN.value.get() {
600            None
601        } else {
602            // Safety: `ordinal` is not zero.
603            Some(unsafe {
604                Self::__from_ordinal_date_unchecked(self.year() - 1, days_in_year(self.year() - 1))
605            })
606        }
607    }
608
609    /// Calculates the first occurrence of a weekday that is strictly later than a given `Date`.
610    ///
611    /// # Panics
612    /// Panics if an overflow occurred.
613    ///
614    /// # Examples
615    /// ```
616    /// # use time::Weekday;
617    /// # use time_macros::date;
618    /// assert_eq!(
619    ///     date!(2023 - 06 - 28).next_occurrence(Weekday::Monday),
620    ///     date!(2023 - 07 - 03)
621    /// );
622    /// assert_eq!(
623    ///     date!(2023 - 06 - 19).next_occurrence(Weekday::Monday),
624    ///     date!(2023 - 06 - 26)
625    /// );
626    /// ```
627    pub const fn next_occurrence(self, weekday: Weekday) -> Self {
628        expect_opt!(
629            self.checked_next_occurrence(weekday),
630            "overflow calculating the next occurrence of a weekday"
631        )
632    }
633
634    /// Calculates the first occurrence of a weekday that is strictly earlier than a given `Date`.
635    ///
636    /// # Panics
637    /// Panics if an overflow occurred.
638    ///
639    /// # Examples
640    /// ```
641    /// # use time::Weekday;
642    /// # use time_macros::date;
643    /// assert_eq!(
644    ///     date!(2023 - 06 - 28).prev_occurrence(Weekday::Monday),
645    ///     date!(2023 - 06 - 26)
646    /// );
647    /// assert_eq!(
648    ///     date!(2023 - 06 - 19).prev_occurrence(Weekday::Monday),
649    ///     date!(2023 - 06 - 12)
650    /// );
651    /// ```
652    pub const fn prev_occurrence(self, weekday: Weekday) -> Self {
653        expect_opt!(
654            self.checked_prev_occurrence(weekday),
655            "overflow calculating the previous occurrence of a weekday"
656        )
657    }
658
659    /// Calculates the `n`th occurrence of a weekday that is strictly later than a given `Date`.
660    ///
661    /// # Panics
662    /// Panics if an overflow occurred or if `n == 0`.
663    ///
664    /// # Examples
665    /// ```
666    /// # use time::Weekday;
667    /// # use time_macros::date;
668    /// assert_eq!(
669    ///     date!(2023 - 06 - 25).nth_next_occurrence(Weekday::Monday, 5),
670    ///     date!(2023 - 07 - 24)
671    /// );
672    /// assert_eq!(
673    ///     date!(2023 - 06 - 26).nth_next_occurrence(Weekday::Monday, 5),
674    ///     date!(2023 - 07 - 31)
675    /// );
676    /// ```
677    pub const fn nth_next_occurrence(self, weekday: Weekday, n: u8) -> Self {
678        expect_opt!(
679            self.checked_nth_next_occurrence(weekday, n),
680            "overflow calculating the next occurrence of a weekday"
681        )
682    }
683
684    /// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`.
685    ///
686    /// # Panics
687    /// Panics if an overflow occurred or if `n == 0`.
688    ///
689    /// # Examples
690    /// ```
691    /// # use time::Weekday;
692    /// # use time_macros::date;
693    /// assert_eq!(
694    ///     date!(2023 - 06 - 27).nth_prev_occurrence(Weekday::Monday, 3),
695    ///     date!(2023 - 06 - 12)
696    /// );
697    /// assert_eq!(
698    ///     date!(2023 - 06 - 26).nth_prev_occurrence(Weekday::Monday, 3),
699    ///     date!(2023 - 06 - 05)
700    /// );
701    /// ```
702    pub const fn nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Self {
703        expect_opt!(
704            self.checked_nth_prev_occurrence(weekday, n),
705            "overflow calculating the previous occurrence of a weekday"
706        )
707    }
708
709    /// Get the Julian day for the date.
710    ///
711    /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
712    /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
713    ///
714    /// ```rust
715    /// # use time_macros::date;
716    /// assert_eq!(date!(-4713 - 11 - 24).to_julian_day(), 0);
717    /// assert_eq!(date!(2000 - 01 - 01).to_julian_day(), 2_451_545);
718    /// assert_eq!(date!(2019 - 01 - 01).to_julian_day(), 2_458_485);
719    /// assert_eq!(date!(2019 - 12 - 31).to_julian_day(), 2_458_849);
720    /// ```
721    pub const fn to_julian_day(self) -> i32 {
722        let year = self.year() - 1;
723        let ordinal = self.ordinal() as i32;
724
725        ordinal + 365 * year + div_floor!(year, 4) - div_floor!(year, 100)
726            + div_floor!(year, 400)
727            + 1_721_425
728    }
729    // endregion getters
730
731    // region: checked arithmetic
732    /// Computes `self + duration`, returning `None` if an overflow occurred.
733    ///
734    /// ```rust
735    /// # use time::{Date, ext::NumericalDuration};
736    /// # use time_macros::date;
737    /// assert_eq!(Date::MAX.checked_add(1.days()), None);
738    /// assert_eq!(Date::MIN.checked_add((-2).days()), None);
739    /// assert_eq!(
740    ///     date!(2020 - 12 - 31).checked_add(2.days()),
741    ///     Some(date!(2021 - 01 - 02))
742    /// );
743    /// ```
744    ///
745    /// # Note
746    ///
747    /// This function only takes whole days into account.
748    ///
749    /// ```rust
750    /// # use time::{Date, ext::NumericalDuration};
751    /// # use time_macros::date;
752    /// assert_eq!(Date::MAX.checked_add(23.hours()), Some(Date::MAX));
753    /// assert_eq!(Date::MIN.checked_add((-23).hours()), Some(Date::MIN));
754    /// assert_eq!(
755    ///     date!(2020 - 12 - 31).checked_add(23.hours()),
756    ///     Some(date!(2020 - 12 - 31))
757    /// );
758    /// assert_eq!(
759    ///     date!(2020 - 12 - 31).checked_add(47.hours()),
760    ///     Some(date!(2021 - 01 - 01))
761    /// );
762    /// ```
763    pub const fn checked_add(self, duration: Duration) -> Option<Self> {
764        let whole_days = duration.whole_days();
765        if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 {
766            return None;
767        }
768
769        let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _));
770        if let Ok(date) = Self::from_julian_day(julian_day) {
771            Some(date)
772        } else {
773            None
774        }
775    }
776
777    /// Computes `self + duration`, returning `None` if an overflow occurred.
778    ///
779    /// ```rust
780    /// # use time::{Date, ext::NumericalStdDuration};
781    /// # use time_macros::date;
782    /// assert_eq!(Date::MAX.checked_add_std(1.std_days()), None);
783    /// assert_eq!(
784    ///     date!(2020 - 12 - 31).checked_add_std(2.std_days()),
785    ///     Some(date!(2021 - 01 - 02))
786    /// );
787    /// ```
788    ///
789    /// # Note
790    ///
791    /// This function only takes whole days into account.
792    ///
793    /// ```rust
794    /// # use time::{Date, ext::NumericalStdDuration};
795    /// # use time_macros::date;
796    /// assert_eq!(Date::MAX.checked_add_std(23.std_hours()), Some(Date::MAX));
797    /// assert_eq!(
798    ///     date!(2020 - 12 - 31).checked_add_std(23.std_hours()),
799    ///     Some(date!(2020 - 12 - 31))
800    /// );
801    /// assert_eq!(
802    ///     date!(2020 - 12 - 31).checked_add_std(47.std_hours()),
803    ///     Some(date!(2021 - 01 - 01))
804    /// );
805    /// ```
806    pub const fn checked_add_std(self, duration: StdDuration) -> Option<Self> {
807        let whole_days = duration.as_secs() / Second::per(Day) as u64;
808        if whole_days > i32::MAX as u64 {
809            return None;
810        }
811
812        let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _));
813        if let Ok(date) = Self::from_julian_day(julian_day) {
814            Some(date)
815        } else {
816            None
817        }
818    }
819
820    /// Computes `self - duration`, returning `None` if an overflow occurred.
821    ///
822    /// ```
823    /// # use time::{Date, ext::NumericalDuration};
824    /// # use time_macros::date;
825    /// assert_eq!(Date::MAX.checked_sub((-2).days()), None);
826    /// assert_eq!(Date::MIN.checked_sub(1.days()), None);
827    /// assert_eq!(
828    ///     date!(2020 - 12 - 31).checked_sub(2.days()),
829    ///     Some(date!(2020 - 12 - 29))
830    /// );
831    /// ```
832    ///
833    /// # Note
834    ///
835    /// This function only takes whole days into account.
836    ///
837    /// ```
838    /// # use time::{Date, ext::NumericalDuration};
839    /// # use time_macros::date;
840    /// assert_eq!(Date::MAX.checked_sub((-23).hours()), Some(Date::MAX));
841    /// assert_eq!(Date::MIN.checked_sub(23.hours()), Some(Date::MIN));
842    /// assert_eq!(
843    ///     date!(2020 - 12 - 31).checked_sub(23.hours()),
844    ///     Some(date!(2020 - 12 - 31))
845    /// );
846    /// assert_eq!(
847    ///     date!(2020 - 12 - 31).checked_sub(47.hours()),
848    ///     Some(date!(2020 - 12 - 30))
849    /// );
850    /// ```
851    pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
852        let whole_days = duration.whole_days();
853        if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 {
854            return None;
855        }
856
857        let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _));
858        if let Ok(date) = Self::from_julian_day(julian_day) {
859            Some(date)
860        } else {
861            None
862        }
863    }
864
865    /// Computes `self - duration`, returning `None` if an overflow occurred.
866    ///
867    /// ```
868    /// # use time::{Date, ext::NumericalStdDuration};
869    /// # use time_macros::date;
870    /// assert_eq!(Date::MIN.checked_sub_std(1.std_days()), None);
871    /// assert_eq!(
872    ///     date!(2020 - 12 - 31).checked_sub_std(2.std_days()),
873    ///     Some(date!(2020 - 12 - 29))
874    /// );
875    /// ```
876    ///
877    /// # Note
878    ///
879    /// This function only takes whole days into account.
880    ///
881    /// ```
882    /// # use time::{Date, ext::NumericalStdDuration};
883    /// # use time_macros::date;
884    /// assert_eq!(Date::MIN.checked_sub_std(23.std_hours()), Some(Date::MIN));
885    /// assert_eq!(
886    ///     date!(2020 - 12 - 31).checked_sub_std(23.std_hours()),
887    ///     Some(date!(2020 - 12 - 31))
888    /// );
889    /// assert_eq!(
890    ///     date!(2020 - 12 - 31).checked_sub_std(47.std_hours()),
891    ///     Some(date!(2020 - 12 - 30))
892    /// );
893    /// ```
894    pub const fn checked_sub_std(self, duration: StdDuration) -> Option<Self> {
895        let whole_days = duration.as_secs() / Second::per(Day) as u64;
896        if whole_days > i32::MAX as u64 {
897            return None;
898        }
899
900        let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _));
901        if let Ok(date) = Self::from_julian_day(julian_day) {
902            Some(date)
903        } else {
904            None
905        }
906    }
907
908    /// Calculates the first occurrence of a weekday that is strictly later than a given `Date`.
909    /// Returns `None` if an overflow occurred.
910    pub(crate) const fn checked_next_occurrence(self, weekday: Weekday) -> Option<Self> {
911        let day_diff = match weekday as i8 - self.weekday() as i8 {
912            1 | -6 => 1,
913            2 | -5 => 2,
914            3 | -4 => 3,
915            4 | -3 => 4,
916            5 | -2 => 5,
917            6 | -1 => 6,
918            val => {
919                debug_assert!(val == 0);
920                7
921            }
922        };
923
924        self.checked_add(Duration::days(day_diff))
925    }
926
927    /// Calculates the first occurrence of a weekday that is strictly earlier than a given `Date`.
928    /// Returns `None` if an overflow occurred.
929    pub(crate) const fn checked_prev_occurrence(self, weekday: Weekday) -> Option<Self> {
930        let day_diff = match weekday as i8 - self.weekday() as i8 {
931            1 | -6 => 6,
932            2 | -5 => 5,
933            3 | -4 => 4,
934            4 | -3 => 3,
935            5 | -2 => 2,
936            6 | -1 => 1,
937            val => {
938                debug_assert!(val == 0);
939                7
940            }
941        };
942
943        self.checked_sub(Duration::days(day_diff))
944    }
945
946    /// Calculates the `n`th occurrence of a weekday that is strictly later than a given `Date`.
947    /// Returns `None` if an overflow occurred or if `n == 0`.
948    pub(crate) const fn checked_nth_next_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> {
949        if n == 0 {
950            return None;
951        }
952
953        const_try_opt!(self.checked_next_occurrence(weekday))
954            .checked_add(Duration::weeks(n as i64 - 1))
955    }
956
957    /// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`.
958    /// Returns `None` if an overflow occurred or if `n == 0`.
959    pub(crate) const fn checked_nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> {
960        if n == 0 {
961            return None;
962        }
963
964        const_try_opt!(self.checked_prev_occurrence(weekday))
965            .checked_sub(Duration::weeks(n as i64 - 1))
966    }
967    // endregion: checked arithmetic
968
969    // region: saturating arithmetic
970    /// Computes `self + duration`, saturating value on overflow.
971    ///
972    /// ```rust
973    /// # use time::{Date, ext::NumericalDuration};
974    /// # use time_macros::date;
975    /// assert_eq!(Date::MAX.saturating_add(1.days()), Date::MAX);
976    /// assert_eq!(Date::MIN.saturating_add((-2).days()), Date::MIN);
977    /// assert_eq!(
978    ///     date!(2020 - 12 - 31).saturating_add(2.days()),
979    ///     date!(2021 - 01 - 02)
980    /// );
981    /// ```
982    ///
983    /// # Note
984    ///
985    /// This function only takes whole days into account.
986    ///
987    /// ```rust
988    /// # use time::ext::NumericalDuration;
989    /// # use time_macros::date;
990    /// assert_eq!(
991    ///     date!(2020 - 12 - 31).saturating_add(23.hours()),
992    ///     date!(2020 - 12 - 31)
993    /// );
994    /// assert_eq!(
995    ///     date!(2020 - 12 - 31).saturating_add(47.hours()),
996    ///     date!(2021 - 01 - 01)
997    /// );
998    /// ```
999    pub const fn saturating_add(self, duration: Duration) -> Self {
1000        if let Some(datetime) = self.checked_add(duration) {
1001            datetime
1002        } else if duration.is_negative() {
1003            Self::MIN
1004        } else {
1005            debug_assert!(duration.is_positive());
1006            Self::MAX
1007        }
1008    }
1009
1010    /// Computes `self - duration`, saturating value on overflow.
1011    ///
1012    /// ```
1013    /// # use time::{Date, ext::NumericalDuration};
1014    /// # use time_macros::date;
1015    /// assert_eq!(Date::MAX.saturating_sub((-2).days()), Date::MAX);
1016    /// assert_eq!(Date::MIN.saturating_sub(1.days()), Date::MIN);
1017    /// assert_eq!(
1018    ///     date!(2020 - 12 - 31).saturating_sub(2.days()),
1019    ///     date!(2020 - 12 - 29)
1020    /// );
1021    /// ```
1022    ///
1023    /// # Note
1024    ///
1025    /// This function only takes whole days into account.
1026    ///
1027    /// ```
1028    /// # use time::ext::NumericalDuration;
1029    /// # use time_macros::date;
1030    /// assert_eq!(
1031    ///     date!(2020 - 12 - 31).saturating_sub(23.hours()),
1032    ///     date!(2020 - 12 - 31)
1033    /// );
1034    /// assert_eq!(
1035    ///     date!(2020 - 12 - 31).saturating_sub(47.hours()),
1036    ///     date!(2020 - 12 - 30)
1037    /// );
1038    /// ```
1039    pub const fn saturating_sub(self, duration: Duration) -> Self {
1040        if let Some(datetime) = self.checked_sub(duration) {
1041            datetime
1042        } else if duration.is_negative() {
1043            Self::MAX
1044        } else {
1045            debug_assert!(duration.is_positive());
1046            Self::MIN
1047        }
1048    }
1049    // region: saturating arithmetic
1050
1051    // region: replacement
1052    /// Replace the year. The month and day will be unchanged.
1053    ///
1054    /// ```rust
1055    /// # use time_macros::date;
1056    /// assert_eq!(
1057    ///     date!(2022 - 02 - 18).replace_year(2019),
1058    ///     Ok(date!(2019 - 02 - 18))
1059    /// );
1060    /// assert!(date!(2022 - 02 - 18).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year
1061    /// assert!(date!(2022 - 02 - 18).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year
1062    /// ```
1063    #[must_use = "This method does not mutate the original `Date`."]
1064    pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
1065        ensure_ranged!(Year: year);
1066
1067        let ordinal = self.ordinal();
1068
1069        // Dates in January and February are unaffected by leap years.
1070        if ordinal <= 59 {
1071            // Safety: `ordinal` is not zero.
1072            return Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) });
1073        }
1074
1075        match (is_leap_year(self.year()), is_leap_year(year)) {
1076            (false, false) | (true, true) => {
1077                // Safety: `ordinal` is not zero.
1078                Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) })
1079            }
1080            // February 29 does not exist in common years.
1081            (true, false) if ordinal == 60 => Err(error::ComponentRange {
1082                name: "day",
1083                value: 29,
1084                minimum: 1,
1085                maximum: 28,
1086                conditional_range: true,
1087            }),
1088            // We're going from a common year to a leap year. Shift dates in March and later by
1089            // one day.
1090            // Safety: `ordinal` is not zero.
1091            (false, true) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal + 1) }),
1092            // We're going from a leap year to a common year. Shift dates in January and
1093            // February by one day.
1094            // Safety: `ordinal` is not zero.
1095            (true, false) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal - 1) }),
1096        }
1097    }
1098
1099    /// Replace the month of the year.
1100    ///
1101    /// ```rust
1102    /// # use time_macros::date;
1103    /// # use time::Month;
1104    /// assert_eq!(
1105    ///     date!(2022 - 02 - 18).replace_month(Month::January),
1106    ///     Ok(date!(2022 - 01 - 18))
1107    /// );
1108    /// assert!(
1109    ///     date!(2022 - 01 - 30)
1110    ///         .replace_month(Month::February)
1111    ///         .is_err()
1112    /// ); // 30 isn't a valid day in February
1113    /// ```
1114    #[must_use = "This method does not mutate the original `Date`."]
1115    pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
1116        let (year, _, day) = self.to_calendar_date();
1117        Self::from_calendar_date(year, month, day)
1118    }
1119
1120    /// Replace the day of the month.
1121    ///
1122    /// ```rust
1123    /// # use time_macros::date;
1124    /// assert_eq!(
1125    ///     date!(2022 - 02 - 18).replace_day(1),
1126    ///     Ok(date!(2022 - 02 - 01))
1127    /// );
1128    /// assert!(date!(2022 - 02 - 18).replace_day(0).is_err()); // 0 isn't a valid day
1129    /// assert!(date!(2022 - 02 - 18).replace_day(30).is_err()); // 30 isn't a valid day in February
1130    /// ```
1131    #[must_use = "This method does not mutate the original `Date`."]
1132    pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
1133        match day {
1134            1..=28 => {}
1135            29..=31 if day <= days_in_year_month(self.year(), self.month()) => {}
1136            _ => {
1137                return Err(error::ComponentRange {
1138                    name: "day",
1139                    minimum: 1,
1140                    maximum: days_in_year_month(self.year(), self.month()) as _,
1141                    value: day as _,
1142                    conditional_range: true,
1143                });
1144            }
1145        }
1146
1147        // Safety: `ordinal` is not zero.
1148        Ok(unsafe {
1149            Self::__from_ordinal_date_unchecked(
1150                self.year(),
1151                (self.ordinal() as i16 - self.day() as i16 + day as i16) as _,
1152            )
1153        })
1154    }
1155
1156    /// Replace the day of the year.
1157    ///
1158    /// ```rust
1159    /// # use time_macros::date;
1160    /// assert_eq!(date!(2022 - 049).replace_ordinal(1), Ok(date!(2022 - 001)));
1161    /// assert!(date!(2022 - 049).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal
1162    /// assert!(date!(2022 - 049).replace_ordinal(366).is_err()); // 2022 isn't a leap year
1163    /// ````
1164    #[must_use = "This method does not mutate the original `Date`."]
1165    pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
1166        match ordinal {
1167            1..=365 => {}
1168            366 if is_leap_year(self.year()) => {}
1169            _ => {
1170                return Err(error::ComponentRange {
1171                    name: "ordinal",
1172                    minimum: 1,
1173                    maximum: days_in_year(self.year()) as _,
1174                    value: ordinal as _,
1175                    conditional_range: true,
1176                });
1177            }
1178        }
1179
1180        // Safety: `ordinal` is in range.
1181        Ok(unsafe { Self::__from_ordinal_date_unchecked(self.year(), ordinal) })
1182    }
1183    // endregion replacement
1184}
1185
1186// region: attach time
1187/// Methods to add a [`Time`] component, resulting in a [`PrimitiveDateTime`].
1188impl Date {
1189    /// Create a [`PrimitiveDateTime`] using the existing date. The [`Time`] component will be set
1190    /// to midnight.
1191    ///
1192    /// ```rust
1193    /// # use time_macros::{date, datetime};
1194    /// assert_eq!(date!(1970-01-01).midnight(), datetime!(1970-01-01 0:00));
1195    /// ```
1196    pub const fn midnight(self) -> PrimitiveDateTime {
1197        PrimitiveDateTime::new(self, Time::MIDNIGHT)
1198    }
1199
1200    /// Create a [`PrimitiveDateTime`] using the existing date and the provided [`Time`].
1201    ///
1202    /// ```rust
1203    /// # use time_macros::{date, datetime, time};
1204    /// assert_eq!(
1205    ///     date!(1970-01-01).with_time(time!(0:00)),
1206    ///     datetime!(1970-01-01 0:00),
1207    /// );
1208    /// ```
1209    pub const fn with_time(self, time: Time) -> PrimitiveDateTime {
1210        PrimitiveDateTime::new(self, time)
1211    }
1212
1213    /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
1214    ///
1215    /// ```rust
1216    /// # use time_macros::date;
1217    /// assert!(date!(1970 - 01 - 01).with_hms(0, 0, 0).is_ok());
1218    /// assert!(date!(1970 - 01 - 01).with_hms(24, 0, 0).is_err());
1219    /// ```
1220    pub const fn with_hms(
1221        self,
1222        hour: u8,
1223        minute: u8,
1224        second: u8,
1225    ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1226        Ok(PrimitiveDateTime::new(
1227            self,
1228            const_try!(Time::from_hms(hour, minute, second)),
1229        ))
1230    }
1231
1232    /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
1233    ///
1234    /// ```rust
1235    /// # use time_macros::date;
1236    /// assert!(date!(1970 - 01 - 01).with_hms_milli(0, 0, 0, 0).is_ok());
1237    /// assert!(date!(1970 - 01 - 01).with_hms_milli(24, 0, 0, 0).is_err());
1238    /// ```
1239    pub const fn with_hms_milli(
1240        self,
1241        hour: u8,
1242        minute: u8,
1243        second: u8,
1244        millisecond: u16,
1245    ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1246        Ok(PrimitiveDateTime::new(
1247            self,
1248            const_try!(Time::from_hms_milli(hour, minute, second, millisecond)),
1249        ))
1250    }
1251
1252    /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
1253    ///
1254    /// ```rust
1255    /// # use time_macros::date;
1256    /// assert!(date!(1970 - 01 - 01).with_hms_micro(0, 0, 0, 0).is_ok());
1257    /// assert!(date!(1970 - 01 - 01).with_hms_micro(24, 0, 0, 0).is_err());
1258    /// ```
1259    pub const fn with_hms_micro(
1260        self,
1261        hour: u8,
1262        minute: u8,
1263        second: u8,
1264        microsecond: u32,
1265    ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1266        Ok(PrimitiveDateTime::new(
1267            self,
1268            const_try!(Time::from_hms_micro(hour, minute, second, microsecond)),
1269        ))
1270    }
1271
1272    /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
1273    ///
1274    /// ```rust
1275    /// # use time_macros::date;
1276    /// assert!(date!(1970 - 01 - 01).with_hms_nano(0, 0, 0, 0).is_ok());
1277    /// assert!(date!(1970 - 01 - 01).with_hms_nano(24, 0, 0, 0).is_err());
1278    /// ```
1279    pub const fn with_hms_nano(
1280        self,
1281        hour: u8,
1282        minute: u8,
1283        second: u8,
1284        nanosecond: u32,
1285    ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1286        Ok(PrimitiveDateTime::new(
1287            self,
1288            const_try!(Time::from_hms_nano(hour, minute, second, nanosecond)),
1289        ))
1290    }
1291}
1292// endregion attach time
1293
1294// region: formatting & parsing
1295#[cfg(feature = "formatting")]
1296impl Date {
1297    /// Format the `Date` using the provided [format description](crate::format_description).
1298    pub fn format_into(
1299        self,
1300        output: &mut impl io::Write,
1301        format: &(impl Formattable + ?Sized),
1302    ) -> Result<usize, error::Format> {
1303        format.format_into(output, Some(self), None, None)
1304    }
1305
1306    /// Format the `Date` using the provided [format description](crate::format_description).
1307    ///
1308    /// ```rust
1309    /// # use time::{format_description};
1310    /// # use time_macros::date;
1311    /// let format = format_description::parse("[year]-[month]-[day]")?;
1312    /// assert_eq!(date!(2020 - 01 - 02).format(&format)?, "2020-01-02");
1313    /// # Ok::<_, time::Error>(())
1314    /// ```
1315    pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
1316        format.format(Some(self), None, None)
1317    }
1318}
1319
1320#[cfg(feature = "parsing")]
1321impl Date {
1322    /// Parse a `Date` from the input using the provided [format
1323    /// description](crate::format_description).
1324    ///
1325    /// ```rust
1326    /// # use time::Date;
1327    /// # use time_macros::{date, format_description};
1328    /// let format = format_description!("[year]-[month]-[day]");
1329    /// assert_eq!(Date::parse("2020-01-02", &format)?, date!(2020 - 01 - 02));
1330    /// # Ok::<_, time::Error>(())
1331    /// ```
1332    pub fn parse(
1333        input: &str,
1334        description: &(impl Parsable + ?Sized),
1335    ) -> Result<Self, error::Parse> {
1336        description.parse_date(input.as_bytes())
1337    }
1338}
1339
1340mod private {
1341    #[non_exhaustive]
1342    #[derive(Debug, Clone, Copy)]
1343    pub struct DateMetadata {
1344        /// The width of the year component, including the sign.
1345        pub(super) year_width: u8,
1346        /// Whether the sign should be displayed.
1347        pub(super) display_sign: bool,
1348        pub(super) year: i32,
1349        pub(super) month: u8,
1350        pub(super) day: u8,
1351    }
1352}
1353use private::DateMetadata;
1354
1355impl SmartDisplay for Date {
1356    type Metadata = DateMetadata;
1357
1358    fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
1359        let (year, month, day) = self.to_calendar_date();
1360
1361        // There is a minimum of four digits for any year.
1362        let mut year_width = cmp::max(year.unsigned_abs().num_digits(), 4);
1363        let display_sign = if !(0..10_000).contains(&year) {
1364            // An extra character is required for the sign.
1365            year_width += 1;
1366            true
1367        } else {
1368            false
1369        };
1370
1371        let formatted_width = year_width.extend::<usize>()
1372            + smart_display::padded_width_of!(
1373                "-",
1374                u8::from(month) => width(2),
1375                "-",
1376                day => width(2),
1377            );
1378
1379        Metadata::new(
1380            formatted_width,
1381            self,
1382            DateMetadata {
1383                year_width,
1384                display_sign,
1385                year,
1386                month: u8::from(month),
1387                day,
1388            },
1389        )
1390    }
1391
1392    fn fmt_with_metadata(
1393        &self,
1394        f: &mut fmt::Formatter<'_>,
1395        metadata: Metadata<Self>,
1396    ) -> fmt::Result {
1397        let DateMetadata {
1398            year_width,
1399            display_sign,
1400            year,
1401            month,
1402            day,
1403        } = *metadata;
1404        let year_width = year_width.extend();
1405
1406        if display_sign {
1407            f.pad_with_width(
1408                metadata.unpadded_width(),
1409                format_args!("{year:+0year_width$}-{month:02}-{day:02}"),
1410            )
1411        } else {
1412            f.pad_with_width(
1413                metadata.unpadded_width(),
1414                format_args!("{year:0year_width$}-{month:02}-{day:02}"),
1415            )
1416        }
1417    }
1418}
1419
1420impl fmt::Display for Date {
1421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1422        SmartDisplay::fmt(self, f)
1423    }
1424}
1425
1426impl fmt::Debug for Date {
1427    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1428        fmt::Display::fmt(self, f)
1429    }
1430}
1431// endregion formatting & parsing
1432
1433// region: trait impls
1434impl Add<Duration> for Date {
1435    type Output = Self;
1436
1437    /// # Panics
1438    ///
1439    /// This may panic if an overflow occurs.
1440    fn add(self, duration: Duration) -> Self::Output {
1441        self.checked_add(duration)
1442            .expect("overflow adding duration to date")
1443    }
1444}
1445
1446impl Add<StdDuration> for Date {
1447    type Output = Self;
1448
1449    /// # Panics
1450    ///
1451    /// This may panic if an overflow occurs.
1452    fn add(self, duration: StdDuration) -> Self::Output {
1453        self.checked_add_std(duration)
1454            .expect("overflow adding duration to date")
1455    }
1456}
1457
1458impl_add_assign!(Date: Duration, StdDuration);
1459
1460impl Sub<Duration> for Date {
1461    type Output = Self;
1462
1463    /// # Panics
1464    ///
1465    /// This may panic if an overflow occurs.
1466    fn sub(self, duration: Duration) -> Self::Output {
1467        self.checked_sub(duration)
1468            .expect("overflow subtracting duration from date")
1469    }
1470}
1471
1472impl Sub<StdDuration> for Date {
1473    type Output = Self;
1474
1475    /// # Panics
1476    ///
1477    /// This may panic if an overflow occurs.
1478    fn sub(self, duration: StdDuration) -> Self::Output {
1479        self.checked_sub_std(duration)
1480            .expect("overflow subtracting duration from date")
1481    }
1482}
1483
1484impl_sub_assign!(Date: Duration, StdDuration);
1485
1486impl Sub for Date {
1487    type Output = Duration;
1488
1489    fn sub(self, other: Self) -> Self::Output {
1490        Duration::days((self.to_julian_day() - other.to_julian_day()).extend())
1491    }
1492}
1493// endregion trait impls