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