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