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