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