Coverage Report

Created: 2026-01-10 06:44

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