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