Coverage Report

Created: 2025-07-23 07:29

/rust/registry/src/index.crates.io-6f17d22bba15001f/time-0.3.13/src/date.rs
Line
Count
Source (jump to first uncovered line)
1
//! The [`Date`] struct and its associated `impl`s.
2
3
use core::fmt;
4
use core::ops::{Add, Sub};
5
use core::time::Duration as StdDuration;
6
#[cfg(feature = "formatting")]
7
use std::io;
8
9
#[cfg(feature = "formatting")]
10
use crate::formatting::Formattable;
11
#[cfg(feature = "parsing")]
12
use crate::parsing::Parsable;
13
use crate::util::{days_in_year, days_in_year_month, is_leap_year, weeks_in_year};
14
use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday};
15
16
/// The minimum valid year.
17
pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") {
18
    -999_999
19
} else {
20
    -9999
21
};
22
/// The maximum valid year.
23
pub(crate) const MAX_YEAR: i32 = if cfg!(feature = "large-dates") {
24
    999_999
25
} else {
26
    9999
27
};
28
29
/// Date in the proleptic Gregorian calendar.
30
///
31
/// By default, years between ±9999 inclusive are representable. This can be expanded to ±999,999
32
/// inclusive by enabling the `large-dates` crate feature. Doing so has performance implications
33
/// and introduces some ambiguities when parsing.
34
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
35
pub struct Date {
36
    /// Bitpacked field containing both the year and ordinal.
37
    // |     xx     | xxxxxxxxxxxxxxxxxxxxx | xxxxxxxxx |
38
    // |   2 bits   |        21 bits        |  9 bits   |
39
    // | unassigned |         year          |  ordinal  |
40
    // The year is 15 bits when `large-dates` is not enabled.
41
    value: i32,
42
}
43
44
impl fmt::Debug for Date {
45
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
46
0
        f.debug_struct("Date")
47
0
            .field("year", &self.year())
48
0
            .field("ordinal", &self.ordinal())
49
0
            .finish()
50
0
    }
51
}
52
53
impl Date {
54
    /// The minimum valid `Date`.
55
    ///
56
    /// The value of this may vary depending on the feature flags enabled.
57
    pub const MIN: Self = Self::__from_ordinal_date_unchecked(MIN_YEAR, 1);
58
59
    /// The maximum valid `Date`.
60
    ///
61
    /// The value of this may vary depending on the feature flags enabled.
62
    pub const MAX: Self = Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR));
63
64
    // region: constructors
65
    /// Construct a `Date` from the year and ordinal values, the validity of which must be
66
    /// guaranteed by the caller.
67
    #[doc(hidden)]
68
19.0k
    pub const fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self {
69
19.0k
        debug_assert!(year >= MIN_YEAR);
70
19.0k
        debug_assert!(year <= MAX_YEAR);
71
19.0k
        debug_assert!(ordinal != 0);
72
19.0k
        debug_assert!(ordinal <= days_in_year(year));
73
74
19.0k
        Self {
75
19.0k
            value: (year << 9) | ordinal as i32,
76
19.0k
        }
77
19.0k
    }
78
79
    /// Attempt to create a `Date` from the year, month, and day.
80
    ///
81
    /// ```rust
82
    /// # use time::{Date, Month};
83
    /// assert!(Date::from_calendar_date(2019, Month::January, 1).is_ok());
84
    /// assert!(Date::from_calendar_date(2019, Month::December, 31).is_ok());
85
    /// ```
86
    ///
87
    /// ```rust
88
    /// # use time::{Date, Month};
89
    /// assert!(Date::from_calendar_date(2019, Month::February, 29).is_err()); // 2019 isn't a leap year.
90
    /// ```
91
13.1k
    pub const fn from_calendar_date(
92
13.1k
        year: i32,
93
13.1k
        month: Month,
94
13.1k
        day: u8,
95
13.1k
    ) -> Result<Self, error::ComponentRange> {
96
        /// Cumulative days through the beginning of a month in both common and leap years.
97
        const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [
98
            [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
99
            [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
100
        ];
101
102
13.1k
        ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
103
13.1k
        ensure_value_in_range!(day conditionally in 1 => days_in_year_month(year, month));
104
105
12.9k
        Ok(Self::__from_ordinal_date_unchecked(
106
12.9k
            year,
107
12.9k
            DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1]
108
12.9k
                + day as u16,
109
12.9k
        ))
110
13.1k
    }
111
112
    /// Attempt to create a `Date` from the year and ordinal day number.
113
    ///
114
    /// ```rust
115
    /// # use time::Date;
116
    /// assert!(Date::from_ordinal_date(2019, 1).is_ok());
117
    /// assert!(Date::from_ordinal_date(2019, 365).is_ok());
118
    /// ```
119
    ///
120
    /// ```rust
121
    /// # use time::Date;
122
    /// assert!(Date::from_ordinal_date(2019, 366).is_err()); // 2019 isn't a leap year.
123
    /// ```
124
0
    pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result<Self, error::ComponentRange> {
125
0
        ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
126
0
        ensure_value_in_range!(ordinal conditionally in 1 => days_in_year(year));
127
0
        Ok(Self::__from_ordinal_date_unchecked(year, ordinal))
128
0
    }
129
130
    /// Attempt to create a `Date` from the ISO year, week, and weekday.
131
    ///
132
    /// ```rust
133
    /// # use time::{Date, Weekday::*};
134
    /// assert!(Date::from_iso_week_date(2019, 1, Monday).is_ok());
135
    /// assert!(Date::from_iso_week_date(2019, 1, Tuesday).is_ok());
136
    /// assert!(Date::from_iso_week_date(2020, 53, Friday).is_ok());
137
    /// ```
138
    ///
139
    /// ```rust
140
    /// # use time::{Date, Weekday::*};
141
    /// assert!(Date::from_iso_week_date(2019, 53, Monday).is_err()); // 2019 doesn't have 53 weeks.
142
    /// ```
143
0
    pub const fn from_iso_week_date(
144
0
        year: i32,
145
0
        week: u8,
146
0
        weekday: Weekday,
147
0
    ) -> Result<Self, error::ComponentRange> {
148
0
        ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
149
0
        ensure_value_in_range!(week conditionally in 1 => weeks_in_year(year));
150
151
0
        let adj_year = year - 1;
152
0
        let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
153
0
            + div_floor!(adj_year, 400);
154
0
        let jan_4 = match (raw % 7) as i8 {
155
0
            -6 | 1 => 8,
156
0
            -5 | 2 => 9,
157
0
            -4 | 3 => 10,
158
0
            -3 | 4 => 4,
159
0
            -2 | 5 => 5,
160
0
            -1 | 6 => 6,
161
0
            _ => 7,
162
        };
163
0
        let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4;
164
0
165
0
        Ok(if ordinal <= 0 {
166
0
            Self::__from_ordinal_date_unchecked(
167
0
                year - 1,
168
0
                (ordinal as u16).wrapping_add(days_in_year(year - 1)),
169
0
            )
170
0
        } else if ordinal > days_in_year(year) as i16 {
171
0
            Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year))
172
        } else {
173
0
            Self::__from_ordinal_date_unchecked(year, ordinal as _)
174
        })
175
0
    }
176
177
    /// Create a `Date` from the Julian day.
178
    ///
179
    /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
180
    /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
181
    ///
182
    /// ```rust
183
    /// # use time::{Date, macros::date};
184
    /// assert_eq!(Date::from_julian_day(0), Ok(date!(-4713 - 11 - 24)));
185
    /// assert_eq!(Date::from_julian_day(2_451_545), Ok(date!(2000 - 01 - 01)));
186
    /// assert_eq!(Date::from_julian_day(2_458_485), Ok(date!(2019 - 01 - 01)));
187
    /// assert_eq!(Date::from_julian_day(2_458_849), Ok(date!(2019 - 12 - 31)));
188
    /// ```
189
    #[doc(alias = "from_julian_date")]
190
0
    pub const fn from_julian_day(julian_day: i32) -> Result<Self, error::ComponentRange> {
191
0
        ensure_value_in_range!(
192
0
            julian_day in Self::MIN.to_julian_day() => Self::MAX.to_julian_day()
193
0
        );
194
0
        Ok(Self::from_julian_day_unchecked(julian_day))
195
0
    }
196
197
    /// Create a `Date` from the Julian day.
198
    ///
199
    /// This does not check the validity of the provided Julian day, and as such may result in an
200
    /// internally invalid value.
201
    #[doc(alias = "from_julian_date_unchecked")]
202
6.11k
    pub(crate) const fn from_julian_day_unchecked(julian_day: i32) -> Self {
203
6.11k
        debug_assert!(julian_day >= Self::MIN.to_julian_day());
204
6.11k
        debug_assert!(julian_day <= Self::MAX.to_julian_day());
205
206
        // To avoid a potential overflow, the value may need to be widened for some arithmetic.
207
208
6.11k
        let z = julian_day - 1_721_119;
209
6.11k
        let (mut year, mut ordinal) = if julian_day < -19_752_948 || julian_day > 23_195_514 {
210
0
            let g = 100 * z as i64 - 25;
211
0
            let a = (g / 3_652_425) as i32;
212
0
            let b = a - a / 4;
213
0
            let year = div_floor!(100 * b as i64 + g, 36525) as i32;
214
0
            let ordinal = (b + z - div_floor!(36525 * year as i64, 100) as i32) as _;
215
0
            (year, ordinal)
216
        } else {
217
6.11k
            let g = 100 * z - 25;
218
6.11k
            let a = g / 3_652_425;
219
6.11k
            let b = a - a / 4;
220
6.11k
            let year = div_floor!(100 * b + g, 36525);
221
6.11k
            let ordinal = (b + z - div_floor!(36525 * year, 100)) as _;
222
6.11k
            (year, ordinal)
223
        };
224
225
6.11k
        if is_leap_year(year) {
226
537
            ordinal += 60;
227
537
            cascade!(ordinal in 1..367 => year);
228
        } else {
229
5.57k
            ordinal += 59;
230
5.57k
            cascade!(ordinal in 1..366 => year);
231
        }
232
233
6.11k
        Self::__from_ordinal_date_unchecked(year, ordinal)
234
6.11k
    }
235
    // endregion constructors
236
237
    // region: getters
238
    /// Get the year of the date.
239
    ///
240
    /// ```rust
241
    /// # use time::macros::date;
242
    /// assert_eq!(date!(2019 - 01 - 01).year(), 2019);
243
    /// assert_eq!(date!(2019 - 12 - 31).year(), 2019);
244
    /// assert_eq!(date!(2020 - 01 - 01).year(), 2020);
245
    /// ```
246
41.0k
    pub const fn year(self) -> i32 {
247
41.0k
        self.value >> 9
248
41.0k
    }
249
250
    /// Get the month.
251
    ///
252
    /// ```rust
253
    /// # use time::{macros::date, Month};
254
    /// assert_eq!(date!(2019 - 01 - 01).month(), Month::January);
255
    /// assert_eq!(date!(2019 - 12 - 31).month(), Month::December);
256
    /// ```
257
12.5k
    pub const fn month(self) -> Month {
258
12.5k
        self.month_day().0
259
12.5k
    }
260
261
    /// Get the day of the month.
262
    ///
263
    /// The returned value will always be in the range `1..=31`.
264
    ///
265
    /// ```rust
266
    /// # use time::macros::date;
267
    /// assert_eq!(date!(2019 - 01 - 01).day(), 1);
268
    /// assert_eq!(date!(2019 - 12 - 31).day(), 31);
269
    /// ```
270
12.5k
    pub const fn day(self) -> u8 {
271
12.5k
        self.month_day().1
272
12.5k
    }
273
274
    /// Get the month and day. This is more efficient than fetching the components individually.
275
    // For whatever reason, rustc has difficulty optimizing this function. It's significantly faster
276
    // to write the statements out by hand.
277
25.0k
    pub(crate) const fn month_day(self) -> (Month, u8) {
278
        /// The number of days up to and including the given month. Common years
279
        /// are first, followed by leap years.
280
        const CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP: [[u16; 11]; 2] = [
281
            [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
282
            [31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
283
        ];
284
285
25.0k
        let days = CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP[is_leap_year(self.year()) as usize];
286
25.0k
        let ordinal = self.ordinal();
287
25.0k
288
25.0k
        if ordinal > days[10] {
289
968
            (Month::December, (ordinal - days[10]) as _)
290
24.0k
        } else if ordinal > days[9] {
291
5.99k
            (Month::November, (ordinal - days[9]) as _)
292
18.0k
        } else if ordinal > days[8] {
293
3.07k
            (Month::October, (ordinal - days[8]) as _)
294
14.9k
        } else if ordinal > days[7] {
295
114
            (Month::September, (ordinal - days[7]) as _)
296
14.8k
        } else if ordinal > days[6] {
297
350
            (Month::August, (ordinal - days[6]) as _)
298
14.5k
        } else if ordinal > days[5] {
299
216
            (Month::July, (ordinal - days[5]) as _)
300
14.3k
        } else if ordinal > days[4] {
301
104
            (Month::June, (ordinal - days[4]) as _)
302
14.2k
        } else if ordinal > days[3] {
303
2.51k
            (Month::May, (ordinal - days[3]) as _)
304
11.6k
        } else if ordinal > days[2] {
305
484
            (Month::April, (ordinal - days[2]) as _)
306
11.2k
        } else if ordinal > days[1] {
307
1.52k
            (Month::March, (ordinal - days[1]) as _)
308
9.67k
        } else if ordinal > days[0] {
309
6.00k
            (Month::February, (ordinal - days[0]) as _)
310
        } else {
311
3.67k
            (Month::January, ordinal as _)
312
        }
313
25.0k
    }
314
315
    /// Get the day of the year.
316
    ///
317
    /// The returned value will always be in the range `1..=366` (`1..=365` for common years).
318
    ///
319
    /// ```rust
320
    /// # use time::macros::date;
321
    /// assert_eq!(date!(2019 - 01 - 01).ordinal(), 1);
322
    /// assert_eq!(date!(2019 - 12 - 31).ordinal(), 365);
323
    /// ```
324
28.5k
    pub const fn ordinal(self) -> u16 {
325
28.5k
        (self.value & 0x1FF) as _
326
28.5k
    }
327
328
    /// Get the ISO 8601 year and week number.
329
0
    pub(crate) const fn iso_year_week(self) -> (i32, u8) {
330
0
        let (year, ordinal) = self.to_ordinal_date();
331
0
332
0
        match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ {
333
0
            0 => (year - 1, weeks_in_year(year - 1)),
334
0
            53 if weeks_in_year(year) == 52 => (year + 1, 1),
335
0
            week => (year, week),
336
        }
337
0
    }
338
339
    /// Get the ISO week number.
340
    ///
341
    /// The returned value will always be in the range `1..=53`.
342
    ///
343
    /// ```rust
344
    /// # use time::macros::date;
345
    /// assert_eq!(date!(2019 - 01 - 01).iso_week(), 1);
346
    /// assert_eq!(date!(2019 - 10 - 04).iso_week(), 40);
347
    /// assert_eq!(date!(2020 - 01 - 01).iso_week(), 1);
348
    /// assert_eq!(date!(2020 - 12 - 31).iso_week(), 53);
349
    /// assert_eq!(date!(2021 - 01 - 01).iso_week(), 53);
350
    /// ```
351
0
    pub const fn iso_week(self) -> u8 {
352
0
        self.iso_year_week().1
353
0
    }
354
355
    /// Get the week number where week 1 begins on the first Sunday.
356
    ///
357
    /// The returned value will always be in the range `0..=53`.
358
    ///
359
    /// ```rust
360
    /// # use time::macros::date;
361
    /// assert_eq!(date!(2019 - 01 - 01).sunday_based_week(), 0);
362
    /// assert_eq!(date!(2020 - 01 - 01).sunday_based_week(), 0);
363
    /// assert_eq!(date!(2020 - 12 - 31).sunday_based_week(), 52);
364
    /// assert_eq!(date!(2021 - 01 - 01).sunday_based_week(), 0);
365
    /// ```
366
0
    pub const fn sunday_based_week(self) -> u8 {
367
0
        ((self.ordinal() as i16 - self.weekday().number_days_from_sunday() as i16 + 6) / 7) as _
368
0
    }
369
370
    /// Get the week number where week 1 begins on the first Monday.
371
    ///
372
    /// The returned value will always be in the range `0..=53`.
373
    ///
374
    /// ```rust
375
    /// # use time::macros::date;
376
    /// assert_eq!(date!(2019 - 01 - 01).monday_based_week(), 0);
377
    /// assert_eq!(date!(2020 - 01 - 01).monday_based_week(), 0);
378
    /// assert_eq!(date!(2020 - 12 - 31).monday_based_week(), 52);
379
    /// assert_eq!(date!(2021 - 01 - 01).monday_based_week(), 0);
380
    /// ```
381
0
    pub const fn monday_based_week(self) -> u8 {
382
0
        ((self.ordinal() as i16 - self.weekday().number_days_from_monday() as i16 + 6) / 7) as _
383
0
    }
384
385
    /// Get the year, month, and day.
386
    ///
387
    /// ```rust
388
    /// # use time::{macros::date, Month};
389
    /// assert_eq!(
390
    ///     date!(2019 - 01 - 01).to_calendar_date(),
391
    ///     (2019, Month::January, 1)
392
    /// );
393
    /// ```
394
0
    pub const fn to_calendar_date(self) -> (i32, Month, u8) {
395
0
        let (month, day) = self.month_day();
396
0
        (self.year(), month, day)
397
0
    }
398
399
    /// Get the year and ordinal day number.
400
    ///
401
    /// ```rust
402
    /// # use time::macros::date;
403
    /// assert_eq!(date!(2019 - 01 - 01).to_ordinal_date(), (2019, 1));
404
    /// ```
405
0
    pub const fn to_ordinal_date(self) -> (i32, u16) {
406
0
        (self.year(), self.ordinal())
407
0
    }
408
409
    /// Get the ISO 8601 year, week number, and weekday.
410
    ///
411
    /// ```rust
412
    /// # use time::{Weekday::*, macros::date};
413
    /// assert_eq!(date!(2019 - 01 - 01).to_iso_week_date(), (2019, 1, Tuesday));
414
    /// assert_eq!(date!(2019 - 10 - 04).to_iso_week_date(), (2019, 40, Friday));
415
    /// assert_eq!(
416
    ///     date!(2020 - 01 - 01).to_iso_week_date(),
417
    ///     (2020, 1, Wednesday)
418
    /// );
419
    /// assert_eq!(
420
    ///     date!(2020 - 12 - 31).to_iso_week_date(),
421
    ///     (2020, 53, Thursday)
422
    /// );
423
    /// assert_eq!(date!(2021 - 01 - 01).to_iso_week_date(), (2020, 53, Friday));
424
    /// ```
425
0
    pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
426
0
        let (year, ordinal) = self.to_ordinal_date();
427
0
        let weekday = self.weekday();
428
0
429
0
        match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ {
430
0
            0 => (year - 1, weeks_in_year(year - 1), weekday),
431
0
            53 if weeks_in_year(year) == 52 => (year + 1, 1, weekday),
432
0
            week => (year, week, weekday),
433
        }
434
0
    }
435
436
    /// Get the weekday.
437
    ///
438
    /// ```rust
439
    /// # use time::{Weekday::*, macros::date};
440
    /// assert_eq!(date!(2019 - 01 - 01).weekday(), Tuesday);
441
    /// assert_eq!(date!(2019 - 02 - 01).weekday(), Friday);
442
    /// assert_eq!(date!(2019 - 03 - 01).weekday(), Friday);
443
    /// assert_eq!(date!(2019 - 04 - 01).weekday(), Monday);
444
    /// assert_eq!(date!(2019 - 05 - 01).weekday(), Wednesday);
445
    /// assert_eq!(date!(2019 - 06 - 01).weekday(), Saturday);
446
    /// assert_eq!(date!(2019 - 07 - 01).weekday(), Monday);
447
    /// assert_eq!(date!(2019 - 08 - 01).weekday(), Thursday);
448
    /// assert_eq!(date!(2019 - 09 - 01).weekday(), Sunday);
449
    /// assert_eq!(date!(2019 - 10 - 01).weekday(), Tuesday);
450
    /// assert_eq!(date!(2019 - 11 - 01).weekday(), Friday);
451
    /// assert_eq!(date!(2019 - 12 - 01).weekday(), Sunday);
452
    /// ```
453
0
    pub const fn weekday(self) -> Weekday {
454
0
        match self.to_julian_day() % 7 {
455
0
            -6 | 1 => Weekday::Tuesday,
456
0
            -5 | 2 => Weekday::Wednesday,
457
0
            -4 | 3 => Weekday::Thursday,
458
0
            -3 | 4 => Weekday::Friday,
459
0
            -2 | 5 => Weekday::Saturday,
460
0
            -1 | 6 => Weekday::Sunday,
461
0
            val => {
462
0
                debug_assert!(val == 0);
463
0
                Weekday::Monday
464
            }
465
        }
466
0
    }
467
468
    /// Get the next calendar date.
469
    ///
470
    /// ```rust
471
    /// # use time::{Date, macros::date};
472
    /// assert_eq!(
473
    ///     date!(2019 - 01 - 01).next_day(),
474
    ///     Some(date!(2019 - 01 - 02))
475
    /// );
476
    /// assert_eq!(
477
    ///     date!(2019 - 01 - 31).next_day(),
478
    ///     Some(date!(2019 - 02 - 01))
479
    /// );
480
    /// assert_eq!(
481
    ///     date!(2019 - 12 - 31).next_day(),
482
    ///     Some(date!(2020 - 01 - 01))
483
    /// );
484
    /// assert_eq!(Date::MAX.next_day(), None);
485
    /// ```
486
0
    pub const fn next_day(self) -> Option<Self> {
487
0
        if self.ordinal() == 366 || (self.ordinal() == 365 && !is_leap_year(self.year())) {
488
0
            if self.value == Self::MAX.value {
489
0
                None
490
            } else {
491
0
                Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1))
492
            }
493
        } else {
494
0
            Some(Self {
495
0
                value: self.value + 1,
496
0
            })
497
        }
498
0
    }
499
500
    /// Get the previous calendar date.
501
    ///
502
    /// ```rust
503
    /// # use time::{Date, macros::date};
504
    /// assert_eq!(
505
    ///     date!(2019 - 01 - 02).previous_day(),
506
    ///     Some(date!(2019 - 01 - 01))
507
    /// );
508
    /// assert_eq!(
509
    ///     date!(2019 - 02 - 01).previous_day(),
510
    ///     Some(date!(2019 - 01 - 31))
511
    /// );
512
    /// assert_eq!(
513
    ///     date!(2020 - 01 - 01).previous_day(),
514
    ///     Some(date!(2019 - 12 - 31))
515
    /// );
516
    /// assert_eq!(Date::MIN.previous_day(), None);
517
    /// ```
518
0
    pub const fn previous_day(self) -> Option<Self> {
519
0
        if self.ordinal() != 1 {
520
0
            Some(Self {
521
0
                value: self.value - 1,
522
0
            })
523
0
        } else if self.value == Self::MIN.value {
524
0
            None
525
        } else {
526
0
            Some(Self::__from_ordinal_date_unchecked(
527
0
                self.year() - 1,
528
0
                days_in_year(self.year() - 1),
529
0
            ))
530
        }
531
0
    }
532
533
    /// Get the Julian day for the date.
534
    ///
535
    /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
536
    /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
537
    ///
538
    /// ```rust
539
    /// # use time::macros::date;
540
    /// assert_eq!(date!(-4713 - 11 - 24).to_julian_day(), 0);
541
    /// assert_eq!(date!(2000 - 01 - 01).to_julian_day(), 2_451_545);
542
    /// assert_eq!(date!(2019 - 01 - 01).to_julian_day(), 2_458_485);
543
    /// assert_eq!(date!(2019 - 12 - 31).to_julian_day(), 2_458_849);
544
    /// ```
545
3.52k
    pub const fn to_julian_day(self) -> i32 {
546
3.52k
        let year = self.year() - 1;
547
3.52k
        let ordinal = self.ordinal() as i32;
548
3.52k
549
3.52k
        ordinal + 365 * year + div_floor!(year, 4) - div_floor!(year, 100)
550
3.52k
            + div_floor!(year, 400)
551
            + 1_721_425
552
3.52k
    }
553
    // endregion getters
554
555
    // region: checked arithmetic
556
    /// Computes `self + duration`, returning `None` if an overflow occurred.
557
    ///
558
    /// ```rust
559
    /// # use time::{Date, ext::NumericalDuration, macros::date};
560
    /// assert_eq!(Date::MAX.checked_add(1.days()), None);
561
    /// assert_eq!(Date::MIN.checked_add((-2).days()), None);
562
    /// assert_eq!(
563
    ///     date!(2020 - 12 - 31).checked_add(2.days()),
564
    ///     Some(date!(2021 - 01 - 02))
565
    /// );
566
    /// ```
567
    ///
568
    /// # Note
569
    ///
570
    /// This function only takes whole days into account.
571
    ///
572
    /// ```rust
573
    /// # use time::{Date, ext::NumericalDuration, macros::date};
574
    /// assert_eq!(Date::MAX.checked_add(23.hours()), Some(Date::MAX));
575
    /// assert_eq!(Date::MIN.checked_add((-23).hours()), Some(Date::MIN));
576
    /// assert_eq!(
577
    ///     date!(2020 - 12 - 31).checked_add(23.hours()),
578
    ///     Some(date!(2020 - 12 - 31))
579
    /// );
580
    /// assert_eq!(
581
    ///     date!(2020 - 12 - 31).checked_add(47.hours()),
582
    ///     Some(date!(2021 - 01 - 01))
583
    /// );
584
    /// ```
585
0
    pub const fn checked_add(self, duration: Duration) -> Option<Self> {
586
0
        let whole_days = duration.whole_days();
587
0
        if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 {
588
0
            return None;
589
0
        }
590
591
0
        let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _));
592
0
        if let Ok(date) = Self::from_julian_day(julian_day) {
593
0
            Some(date)
594
        } else {
595
0
            None
596
        }
597
0
    }
598
599
    /// Computes `self - duration`, returning `None` if an overflow occurred.
600
    ///
601
    /// ```
602
    /// # use time::{Date, ext::NumericalDuration, macros::date};
603
    /// assert_eq!(Date::MAX.checked_sub((-2).days()), None);
604
    /// assert_eq!(Date::MIN.checked_sub(1.days()), None);
605
    /// assert_eq!(
606
    ///     date!(2020 - 12 - 31).checked_sub(2.days()),
607
    ///     Some(date!(2020 - 12 - 29))
608
    /// );
609
    /// ```
610
    ///
611
    /// # Note
612
    ///
613
    /// This function only takes whole days into account.
614
    ///
615
    /// ```
616
    /// # use time::{Date, ext::NumericalDuration, macros::date};
617
    /// assert_eq!(Date::MAX.checked_sub((-23).hours()), Some(Date::MAX));
618
    /// assert_eq!(Date::MIN.checked_sub(23.hours()), Some(Date::MIN));
619
    /// assert_eq!(
620
    ///     date!(2020 - 12 - 31).checked_sub(23.hours()),
621
    ///     Some(date!(2020 - 12 - 31))
622
    /// );
623
    /// assert_eq!(
624
    ///     date!(2020 - 12 - 31).checked_sub(47.hours()),
625
    ///     Some(date!(2020 - 12 - 30))
626
    /// );
627
    /// ```
628
0
    pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
629
0
        let whole_days = duration.whole_days();
630
0
        if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 {
631
0
            return None;
632
0
        }
633
634
0
        let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _));
635
0
        if let Ok(date) = Self::from_julian_day(julian_day) {
636
0
            Some(date)
637
        } else {
638
0
            None
639
        }
640
0
    }
641
    // endregion: checked arithmetic
642
643
    // region: saturating arithmetic
644
    /// Computes `self + duration`, saturating value on overflow.
645
    ///
646
    /// ```rust
647
    /// # use time::{Date, ext::NumericalDuration, macros::date};
648
    /// assert_eq!(Date::MAX.saturating_add(1.days()), Date::MAX);
649
    /// assert_eq!(Date::MIN.saturating_add((-2).days()), Date::MIN);
650
    /// assert_eq!(
651
    ///     date!(2020 - 12 - 31).saturating_add(2.days()),
652
    ///     date!(2021 - 01 - 02)
653
    /// );
654
    /// ```
655
    ///
656
    /// # Note
657
    ///
658
    /// This function only takes whole days into account.
659
    ///
660
    /// ```rust
661
    /// # use time::{ext::NumericalDuration, macros::date};
662
    /// assert_eq!(
663
    ///     date!(2020 - 12 - 31).saturating_add(23.hours()),
664
    ///     date!(2020 - 12 - 31)
665
    /// );
666
    /// assert_eq!(
667
    ///     date!(2020 - 12 - 31).saturating_add(47.hours()),
668
    ///     date!(2021 - 01 - 01)
669
    /// );
670
    /// ```
671
0
    pub const fn saturating_add(self, duration: Duration) -> Self {
672
0
        if let Some(datetime) = self.checked_add(duration) {
673
0
            datetime
674
0
        } else if duration.is_negative() {
675
0
            Self::MIN
676
        } else {
677
0
            debug_assert!(duration.is_positive());
678
0
            Self::MAX
679
        }
680
0
    }
681
682
    /// Computes `self - duration`, saturating value on overflow.
683
    ///
684
    /// ```
685
    /// # use time::{Date, ext::NumericalDuration, macros::date};
686
    /// assert_eq!(Date::MAX.saturating_sub((-2).days()), Date::MAX);
687
    /// assert_eq!(Date::MIN.saturating_sub(1.days()), Date::MIN);
688
    /// assert_eq!(
689
    ///     date!(2020 - 12 - 31).saturating_sub(2.days()),
690
    ///     date!(2020 - 12 - 29)
691
    /// );
692
    /// ```
693
    ///
694
    /// # Note
695
    ///
696
    /// This function only takes whole days into account.
697
    ///
698
    /// ```
699
    /// # use time::{ext::NumericalDuration, macros::date};
700
    /// assert_eq!(
701
    ///     date!(2020 - 12 - 31).saturating_sub(23.hours()),
702
    ///     date!(2020 - 12 - 31)
703
    /// );
704
    /// assert_eq!(
705
    ///     date!(2020 - 12 - 31).saturating_sub(47.hours()),
706
    ///     date!(2020 - 12 - 30)
707
    /// );
708
    /// ```
709
0
    pub const fn saturating_sub(self, duration: Duration) -> Self {
710
0
        if let Some(datetime) = self.checked_sub(duration) {
711
0
            datetime
712
0
        } else if duration.is_negative() {
713
0
            Self::MAX
714
        } else {
715
0
            debug_assert!(duration.is_positive());
716
0
            Self::MIN
717
        }
718
0
    }
719
    // region: saturating arithmetic
720
721
    // region: replacement
722
    /// Replace the year. The month and day will be unchanged.
723
    ///
724
    /// ```rust
725
    /// # use time::macros::date;
726
    /// assert_eq!(
727
    ///     date!(2022 - 02 - 18).replace_year(2019),
728
    ///     Ok(date!(2019 - 02 - 18))
729
    /// );
730
    /// assert!(date!(2022 - 02 - 18).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year
731
    /// assert!(date!(2022 - 02 - 18).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year
732
    /// ```
733
    #[must_use = "This method does not mutate the original `Date`."]
734
0
    pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
735
0
        ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
736
737
0
        let ordinal = self.ordinal();
738
0
739
0
        // Dates in January and February are unaffected by leap years.
740
0
        if ordinal <= 59 {
741
0
            return Ok(Self::__from_ordinal_date_unchecked(year, ordinal));
742
0
        }
743
0
744
0
        match (is_leap_year(self.year()), is_leap_year(year)) {
745
0
            (false, false) | (true, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal)),
746
            // February 29 does not exist in common years.
747
0
            (true, false) if ordinal == 60 => Err(error::ComponentRange {
748
0
                name: "day",
749
0
                value: 29,
750
0
                minimum: 1,
751
0
                maximum: 28,
752
0
                conditional_range: true,
753
0
            }),
754
            // We're going from a common year to a leap year. Shift dates in March and later by
755
            // one day.
756
0
            (false, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal + 1)),
757
            // We're going from a leap year to a common year. Shift dates in January and
758
            // February by one day.
759
0
            (true, false) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal - 1)),
760
        }
761
0
    }
762
763
    /// Replace the month of the year.
764
    ///
765
    /// ```rust
766
    /// # use time::macros::date;
767
    /// # use time::Month;
768
    /// assert_eq!(
769
    ///     date!(2022 - 02 - 18).replace_month(Month::January),
770
    ///     Ok(date!(2022 - 01 - 18))
771
    /// );
772
    /// assert!(
773
    ///     date!(2022 - 01 - 30)
774
    ///         .replace_month(Month::February)
775
    ///         .is_err()
776
    /// ); // 30 isn't a valid day in February
777
    /// ```
778
    #[must_use = "This method does not mutate the original `Date`."]
779
0
    pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
780
0
        let (year, _, day) = self.to_calendar_date();
781
0
        Self::from_calendar_date(year, month, day)
782
0
    }
783
784
    /// Replace the day of the month.
785
    ///
786
    /// ```rust
787
    /// # use time::macros::date;
788
    /// assert_eq!(
789
    ///     date!(2022 - 02 - 18).replace_day(1),
790
    ///     Ok(date!(2022 - 02 - 01))
791
    /// );
792
    /// assert!(date!(2022 - 02 - 18).replace_day(0).is_err()); // 0 isn't a valid day
793
    /// assert!(date!(2022 - 02 - 18).replace_day(30).is_err()); // 30 isn't a valid day in February
794
    /// ```
795
    #[must_use = "This method does not mutate the original `Date`."]
796
0
    pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
797
0
        // Days 1-28 are present in every month, so we can skip checking.
798
0
        if day == 0 || day >= 29 {
799
0
            ensure_value_in_range!(
800
0
                day conditionally in 1 => days_in_year_month(self.year(), self.month())
801
0
            );
802
0
        }
803
804
0
        Ok(Self::__from_ordinal_date_unchecked(
805
0
            self.year(),
806
0
            (self.ordinal() as i16 - self.day() as i16 + day as i16) as _,
807
0
        ))
808
0
    }
809
    // endregion replacement
810
}
811
812
// region: attach time
813
/// Methods to add a [`Time`] component, resulting in a [`PrimitiveDateTime`].
814
impl Date {
815
    /// Create a [`PrimitiveDateTime`] using the existing date. The [`Time`] component will be set
816
    /// to midnight.
817
    ///
818
    /// ```rust
819
    /// # use time::macros::{date, datetime};
820
    /// assert_eq!(date!(1970-01-01).midnight(), datetime!(1970-01-01 0:00));
821
    /// ```
822
0
    pub const fn midnight(self) -> PrimitiveDateTime {
823
0
        PrimitiveDateTime::new(self, Time::MIDNIGHT)
824
0
    }
825
826
    /// Create a [`PrimitiveDateTime`] using the existing date and the provided [`Time`].
827
    ///
828
    /// ```rust
829
    /// # use time::macros::{date, datetime, time};
830
    /// assert_eq!(
831
    ///     date!(1970-01-01).with_time(time!(0:00)),
832
    ///     datetime!(1970-01-01 0:00),
833
    /// );
834
    /// ```
835
6.39k
    pub const fn with_time(self, time: Time) -> PrimitiveDateTime {
836
6.39k
        PrimitiveDateTime::new(self, time)
837
6.39k
    }
838
839
    /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
840
    ///
841
    /// ```rust
842
    /// # use time::macros::date;
843
    /// assert!(date!(1970 - 01 - 01).with_hms(0, 0, 0).is_ok());
844
    /// assert!(date!(1970 - 01 - 01).with_hms(24, 0, 0).is_err());
845
    /// ```
846
0
    pub const fn with_hms(
847
0
        self,
848
0
        hour: u8,
849
0
        minute: u8,
850
0
        second: u8,
851
0
    ) -> Result<PrimitiveDateTime, error::ComponentRange> {
852
0
        Ok(PrimitiveDateTime::new(
853
0
            self,
854
0
            const_try!(Time::from_hms(hour, minute, second)),
855
        ))
856
0
    }
857
858
    /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
859
    ///
860
    /// ```rust
861
    /// # use time::macros::date;
862
    /// assert!(date!(1970 - 01 - 01).with_hms_milli(0, 0, 0, 0).is_ok());
863
    /// assert!(date!(1970 - 01 - 01).with_hms_milli(24, 0, 0, 0).is_err());
864
    /// ```
865
0
    pub const fn with_hms_milli(
866
0
        self,
867
0
        hour: u8,
868
0
        minute: u8,
869
0
        second: u8,
870
0
        millisecond: u16,
871
0
    ) -> Result<PrimitiveDateTime, error::ComponentRange> {
872
0
        Ok(PrimitiveDateTime::new(
873
0
            self,
874
0
            const_try!(Time::from_hms_milli(hour, minute, second, millisecond)),
875
        ))
876
0
    }
877
878
    /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
879
    ///
880
    /// ```rust
881
    /// # use time::macros::date;
882
    /// assert!(date!(1970 - 01 - 01).with_hms_micro(0, 0, 0, 0).is_ok());
883
    /// assert!(date!(1970 - 01 - 01).with_hms_micro(24, 0, 0, 0).is_err());
884
    /// ```
885
0
    pub const fn with_hms_micro(
886
0
        self,
887
0
        hour: u8,
888
0
        minute: u8,
889
0
        second: u8,
890
0
        microsecond: u32,
891
0
    ) -> Result<PrimitiveDateTime, error::ComponentRange> {
892
0
        Ok(PrimitiveDateTime::new(
893
0
            self,
894
0
            const_try!(Time::from_hms_micro(hour, minute, second, microsecond)),
895
        ))
896
0
    }
897
898
    /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
899
    ///
900
    /// ```rust
901
    /// # use time::macros::date;
902
    /// assert!(date!(1970 - 01 - 01).with_hms_nano(0, 0, 0, 0).is_ok());
903
    /// assert!(date!(1970 - 01 - 01).with_hms_nano(24, 0, 0, 0).is_err());
904
    /// ```
905
0
    pub const fn with_hms_nano(
906
0
        self,
907
0
        hour: u8,
908
0
        minute: u8,
909
0
        second: u8,
910
0
        nanosecond: u32,
911
0
    ) -> Result<PrimitiveDateTime, error::ComponentRange> {
912
0
        Ok(PrimitiveDateTime::new(
913
0
            self,
914
0
            const_try!(Time::from_hms_nano(hour, minute, second, nanosecond)),
915
        ))
916
0
    }
917
}
918
// endregion attach time
919
920
// region: formatting & parsing
921
#[cfg(feature = "formatting")]
922
impl Date {
923
    /// Format the `Date` using the provided [format description](crate::format_description).
924
0
    pub fn format_into(
925
0
        self,
926
0
        output: &mut impl io::Write,
927
0
        format: &(impl Formattable + ?Sized),
928
0
    ) -> Result<usize, error::Format> {
929
0
        format.format_into(output, Some(self), None, None)
930
0
    }
931
932
    /// Format the `Date` using the provided [format description](crate::format_description).
933
    ///
934
    /// ```rust
935
    /// # use time::{format_description, macros::date};
936
    /// let format = format_description::parse("[year]-[month]-[day]")?;
937
    /// assert_eq!(date!(2020 - 01 - 02).format(&format)?, "2020-01-02");
938
    /// # Ok::<_, time::Error>(())
939
    /// ```
940
0
    pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
941
0
        format.format(Some(self), None, None)
942
0
    }
943
}
944
945
#[cfg(feature = "parsing")]
946
impl Date {
947
    /// Parse a `Date` from the input using the provided [format
948
    /// description](crate::format_description).
949
    ///
950
    /// ```rust
951
    /// # use time::{format_description, macros::date, Date};
952
    /// let format = format_description::parse("[year]-[month]-[day]")?;
953
    /// assert_eq!(Date::parse("2020-01-02", &format)?, date!(2020 - 01 - 02));
954
    /// # Ok::<_, time::Error>(())
955
    /// ```
956
0
    pub fn parse(
957
0
        input: &str,
958
0
        description: &(impl Parsable + ?Sized),
959
0
    ) -> Result<Self, error::Parse> {
960
0
        description.parse_date(input.as_bytes())
961
0
    }
962
}
963
964
impl fmt::Display for Date {
965
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
966
0
        if cfg!(feature = "large-dates") && self.year().abs() >= 10_000 {
967
0
            write!(
968
0
                f,
969
0
                "{:+}-{:02}-{:02}",
970
0
                self.year(),
971
0
                self.month() as u8,
972
0
                self.day()
973
0
            )
974
        } else {
975
0
            write!(
976
0
                f,
977
0
                "{:0width$}-{:02}-{:02}",
978
0
                self.year(),
979
0
                self.month() as u8,
980
0
                self.day(),
981
0
                width = 4 + (self.year() < 0) as usize
982
0
            )
983
        }
984
0
    }
985
}
986
// endregion formatting & parsing
987
988
// region: trait impls
989
impl Add<Duration> for Date {
990
    type Output = Self;
991
992
0
    fn add(self, duration: Duration) -> Self::Output {
993
0
        self.checked_add(duration)
994
0
            .expect("overflow adding duration to date")
995
0
    }
996
}
997
998
impl Add<StdDuration> for Date {
999
    type Output = Self;
1000
1001
0
    fn add(self, duration: StdDuration) -> Self::Output {
1002
0
        Self::from_julian_day(self.to_julian_day() + (duration.as_secs() / 86_400) as i32)
1003
0
            .expect("overflow adding duration to date")
1004
0
    }
1005
}
1006
1007
impl_add_assign!(Date: Duration, StdDuration);
1008
1009
impl Sub<Duration> for Date {
1010
    type Output = Self;
1011
1012
0
    fn sub(self, duration: Duration) -> Self::Output {
1013
0
        self.checked_sub(duration)
1014
0
            .expect("overflow subtracting duration from date")
1015
0
    }
1016
}
1017
1018
impl Sub<StdDuration> for Date {
1019
    type Output = Self;
1020
1021
0
    fn sub(self, duration: StdDuration) -> Self::Output {
1022
0
        Self::from_julian_day(self.to_julian_day() - (duration.as_secs() / 86_400) as i32)
1023
0
            .expect("overflow subtracting duration from date")
1024
0
    }
1025
}
1026
1027
impl_sub_assign!(Date: Duration, StdDuration);
1028
1029
impl Sub for Date {
1030
    type Output = Duration;
1031
1032
0
    fn sub(self, other: Self) -> Self::Output {
1033
0
        Duration::days((self.to_julian_day() - other.to_julian_day()) as _)
1034
0
    }
1035
}
1036
// endregion trait impls