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, days_in_year_month, 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 <= days_in_year_month(year, month) => {}
124 _ => {
125 return Err(error::ComponentRange {
126 name: "day",
127 minimum: 1,
128 maximum: days_in_year_month(year, month) 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!(
486 /// date!(2020 - 01 - 01).to_iso_week_date(),
487 /// (2020, 1, Wednesday)
488 /// );
489 /// assert_eq!(
490 /// date!(2020 - 12 - 31).to_iso_week_date(),
491 /// (2020, 53, Thursday)
492 /// );
493 /// assert_eq!(date!(2021 - 01 - 01).to_iso_week_date(), (2020, 53, Friday));
494 /// ```
495 pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
496 let (year, ordinal) = self.to_ordinal_date();
497 let weekday = self.weekday();
498
499 match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ {
500 0 => (year - 1, weeks_in_year(year - 1), weekday),
501 53 if weeks_in_year(year) == 52 => (year + 1, 1, weekday),
502 week => (year, week, weekday),
503 }
504 }
505
506 /// Get the weekday.
507 ///
508 /// ```rust
509 /// # use time::Weekday::*;
510 /// # use time_macros::date;
511 /// assert_eq!(date!(2019 - 01 - 01).weekday(), Tuesday);
512 /// assert_eq!(date!(2019 - 02 - 01).weekday(), Friday);
513 /// assert_eq!(date!(2019 - 03 - 01).weekday(), Friday);
514 /// assert_eq!(date!(2019 - 04 - 01).weekday(), Monday);
515 /// assert_eq!(date!(2019 - 05 - 01).weekday(), Wednesday);
516 /// assert_eq!(date!(2019 - 06 - 01).weekday(), Saturday);
517 /// assert_eq!(date!(2019 - 07 - 01).weekday(), Monday);
518 /// assert_eq!(date!(2019 - 08 - 01).weekday(), Thursday);
519 /// assert_eq!(date!(2019 - 09 - 01).weekday(), Sunday);
520 /// assert_eq!(date!(2019 - 10 - 01).weekday(), Tuesday);
521 /// assert_eq!(date!(2019 - 11 - 01).weekday(), Friday);
522 /// assert_eq!(date!(2019 - 12 - 01).weekday(), Sunday);
523 /// ```
524 pub const fn weekday(self) -> Weekday {
525 match self.to_julian_day() % 7 {
526 -6 | 1 => Weekday::Tuesday,
527 -5 | 2 => Weekday::Wednesday,
528 -4 | 3 => Weekday::Thursday,
529 -3 | 4 => Weekday::Friday,
530 -2 | 5 => Weekday::Saturday,
531 -1 | 6 => Weekday::Sunday,
532 val => {
533 debug_assert!(val == 0);
534 Weekday::Monday
535 }
536 }
537 }
538
539 /// Get the next calendar date.
540 ///
541 /// ```rust
542 /// # use time::Date;
543 /// # use time_macros::date;
544 /// assert_eq!(
545 /// date!(2019 - 01 - 01).next_day(),
546 /// Some(date!(2019 - 01 - 02))
547 /// );
548 /// assert_eq!(
549 /// date!(2019 - 01 - 31).next_day(),
550 /// Some(date!(2019 - 02 - 01))
551 /// );
552 /// assert_eq!(
553 /// date!(2019 - 12 - 31).next_day(),
554 /// Some(date!(2020 - 01 - 01))
555 /// );
556 /// assert_eq!(Date::MAX.next_day(), None);
557 /// ```
558 pub const fn next_day(self) -> Option<Self> {
559 if self.ordinal() == 366 || (self.ordinal() == 365 && !is_leap_year(self.year())) {
560 if self.value.get() == Self::MAX.value.get() {
561 None
562 } else {
563 // Safety: `ordinal` is not zero.
564 unsafe { Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) }
565 }
566 } else {
567 Some(Self {
568 // Safety: `ordinal` is not zero.
569 value: unsafe { NonZeroI32::new_unchecked(self.value.get() + 1) },
570 })
571 }
572 }
573
574 /// Get the previous calendar date.
575 ///
576 /// ```rust
577 /// # use time::Date;
578 /// # use time_macros::date;
579 /// assert_eq!(
580 /// date!(2019 - 01 - 02).previous_day(),
581 /// Some(date!(2019 - 01 - 01))
582 /// );
583 /// assert_eq!(
584 /// date!(2019 - 02 - 01).previous_day(),
585 /// Some(date!(2019 - 01 - 31))
586 /// );
587 /// assert_eq!(
588 /// date!(2020 - 01 - 01).previous_day(),
589 /// Some(date!(2019 - 12 - 31))
590 /// );
591 /// assert_eq!(Date::MIN.previous_day(), None);
592 /// ```
593 pub const fn previous_day(self) -> Option<Self> {
594 if self.ordinal() != 1 {
595 Some(Self {
596 // Safety: `ordinal` is not zero.
597 value: unsafe { NonZeroI32::new_unchecked(self.value.get() - 1) },
598 })
599 } else if self.value.get() == Self::MIN.value.get() {
600 None
601 } else {
602 // Safety: `ordinal` is not zero.
603 Some(unsafe {
604 Self::__from_ordinal_date_unchecked(self.year() - 1, days_in_year(self.year() - 1))
605 })
606 }
607 }
608
609 /// Calculates the first occurrence of a weekday that is strictly later than a given `Date`.
610 ///
611 /// # Panics
612 /// Panics if an overflow occurred.
613 ///
614 /// # Examples
615 /// ```
616 /// # use time::Weekday;
617 /// # use time_macros::date;
618 /// assert_eq!(
619 /// date!(2023 - 06 - 28).next_occurrence(Weekday::Monday),
620 /// date!(2023 - 07 - 03)
621 /// );
622 /// assert_eq!(
623 /// date!(2023 - 06 - 19).next_occurrence(Weekday::Monday),
624 /// date!(2023 - 06 - 26)
625 /// );
626 /// ```
627 pub const fn next_occurrence(self, weekday: Weekday) -> Self {
628 expect_opt!(
629 self.checked_next_occurrence(weekday),
630 "overflow calculating the next occurrence of a weekday"
631 )
632 }
633
634 /// Calculates the first occurrence of a weekday that is strictly earlier than a given `Date`.
635 ///
636 /// # Panics
637 /// Panics if an overflow occurred.
638 ///
639 /// # Examples
640 /// ```
641 /// # use time::Weekday;
642 /// # use time_macros::date;
643 /// assert_eq!(
644 /// date!(2023 - 06 - 28).prev_occurrence(Weekday::Monday),
645 /// date!(2023 - 06 - 26)
646 /// );
647 /// assert_eq!(
648 /// date!(2023 - 06 - 19).prev_occurrence(Weekday::Monday),
649 /// date!(2023 - 06 - 12)
650 /// );
651 /// ```
652 pub const fn prev_occurrence(self, weekday: Weekday) -> Self {
653 expect_opt!(
654 self.checked_prev_occurrence(weekday),
655 "overflow calculating the previous occurrence of a weekday"
656 )
657 }
658
659 /// Calculates the `n`th occurrence of a weekday that is strictly later than a given `Date`.
660 ///
661 /// # Panics
662 /// Panics if an overflow occurred or if `n == 0`.
663 ///
664 /// # Examples
665 /// ```
666 /// # use time::Weekday;
667 /// # use time_macros::date;
668 /// assert_eq!(
669 /// date!(2023 - 06 - 25).nth_next_occurrence(Weekday::Monday, 5),
670 /// date!(2023 - 07 - 24)
671 /// );
672 /// assert_eq!(
673 /// date!(2023 - 06 - 26).nth_next_occurrence(Weekday::Monday, 5),
674 /// date!(2023 - 07 - 31)
675 /// );
676 /// ```
677 pub const fn nth_next_occurrence(self, weekday: Weekday, n: u8) -> Self {
678 expect_opt!(
679 self.checked_nth_next_occurrence(weekday, n),
680 "overflow calculating the next occurrence of a weekday"
681 )
682 }
683
684 /// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`.
685 ///
686 /// # Panics
687 /// Panics if an overflow occurred or if `n == 0`.
688 ///
689 /// # Examples
690 /// ```
691 /// # use time::Weekday;
692 /// # use time_macros::date;
693 /// assert_eq!(
694 /// date!(2023 - 06 - 27).nth_prev_occurrence(Weekday::Monday, 3),
695 /// date!(2023 - 06 - 12)
696 /// );
697 /// assert_eq!(
698 /// date!(2023 - 06 - 26).nth_prev_occurrence(Weekday::Monday, 3),
699 /// date!(2023 - 06 - 05)
700 /// );
701 /// ```
702 pub const fn nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Self {
703 expect_opt!(
704 self.checked_nth_prev_occurrence(weekday, n),
705 "overflow calculating the previous occurrence of a weekday"
706 )
707 }
708
709 /// Get the Julian day for the date.
710 ///
711 /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
712 /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
713 ///
714 /// ```rust
715 /// # use time_macros::date;
716 /// assert_eq!(date!(-4713 - 11 - 24).to_julian_day(), 0);
717 /// assert_eq!(date!(2000 - 01 - 01).to_julian_day(), 2_451_545);
718 /// assert_eq!(date!(2019 - 01 - 01).to_julian_day(), 2_458_485);
719 /// assert_eq!(date!(2019 - 12 - 31).to_julian_day(), 2_458_849);
720 /// ```
721 pub const fn to_julian_day(self) -> i32 {
722 let year = self.year() - 1;
723 let ordinal = self.ordinal() as i32;
724
725 ordinal + 365 * year + div_floor!(year, 4) - div_floor!(year, 100)
726 + div_floor!(year, 400)
727 + 1_721_425
728 }
729 // endregion getters
730
731 // region: checked arithmetic
732 /// Computes `self + duration`, returning `None` if an overflow occurred.
733 ///
734 /// ```rust
735 /// # use time::{Date, ext::NumericalDuration};
736 /// # use time_macros::date;
737 /// assert_eq!(Date::MAX.checked_add(1.days()), None);
738 /// assert_eq!(Date::MIN.checked_add((-2).days()), None);
739 /// assert_eq!(
740 /// date!(2020 - 12 - 31).checked_add(2.days()),
741 /// Some(date!(2021 - 01 - 02))
742 /// );
743 /// ```
744 ///
745 /// # Note
746 ///
747 /// This function only takes whole days into account.
748 ///
749 /// ```rust
750 /// # use time::{Date, ext::NumericalDuration};
751 /// # use time_macros::date;
752 /// assert_eq!(Date::MAX.checked_add(23.hours()), Some(Date::MAX));
753 /// assert_eq!(Date::MIN.checked_add((-23).hours()), Some(Date::MIN));
754 /// assert_eq!(
755 /// date!(2020 - 12 - 31).checked_add(23.hours()),
756 /// Some(date!(2020 - 12 - 31))
757 /// );
758 /// assert_eq!(
759 /// date!(2020 - 12 - 31).checked_add(47.hours()),
760 /// Some(date!(2021 - 01 - 01))
761 /// );
762 /// ```
763 pub const fn checked_add(self, duration: Duration) -> Option<Self> {
764 let whole_days = duration.whole_days();
765 if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 {
766 return None;
767 }
768
769 let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _));
770 if let Ok(date) = Self::from_julian_day(julian_day) {
771 Some(date)
772 } else {
773 None
774 }
775 }
776
777 /// Computes `self + duration`, returning `None` if an overflow occurred.
778 ///
779 /// ```rust
780 /// # use time::{Date, ext::NumericalStdDuration};
781 /// # use time_macros::date;
782 /// assert_eq!(Date::MAX.checked_add_std(1.std_days()), None);
783 /// assert_eq!(
784 /// date!(2020 - 12 - 31).checked_add_std(2.std_days()),
785 /// Some(date!(2021 - 01 - 02))
786 /// );
787 /// ```
788 ///
789 /// # Note
790 ///
791 /// This function only takes whole days into account.
792 ///
793 /// ```rust
794 /// # use time::{Date, ext::NumericalStdDuration};
795 /// # use time_macros::date;
796 /// assert_eq!(Date::MAX.checked_add_std(23.std_hours()), Some(Date::MAX));
797 /// assert_eq!(
798 /// date!(2020 - 12 - 31).checked_add_std(23.std_hours()),
799 /// Some(date!(2020 - 12 - 31))
800 /// );
801 /// assert_eq!(
802 /// date!(2020 - 12 - 31).checked_add_std(47.std_hours()),
803 /// Some(date!(2021 - 01 - 01))
804 /// );
805 /// ```
806 pub const fn checked_add_std(self, duration: StdDuration) -> Option<Self> {
807 let whole_days = duration.as_secs() / Second::per(Day) as u64;
808 if whole_days > i32::MAX as u64 {
809 return None;
810 }
811
812 let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _));
813 if let Ok(date) = Self::from_julian_day(julian_day) {
814 Some(date)
815 } else {
816 None
817 }
818 }
819
820 /// Computes `self - duration`, returning `None` if an overflow occurred.
821 ///
822 /// ```
823 /// # use time::{Date, ext::NumericalDuration};
824 /// # use time_macros::date;
825 /// assert_eq!(Date::MAX.checked_sub((-2).days()), None);
826 /// assert_eq!(Date::MIN.checked_sub(1.days()), None);
827 /// assert_eq!(
828 /// date!(2020 - 12 - 31).checked_sub(2.days()),
829 /// Some(date!(2020 - 12 - 29))
830 /// );
831 /// ```
832 ///
833 /// # Note
834 ///
835 /// This function only takes whole days into account.
836 ///
837 /// ```
838 /// # use time::{Date, ext::NumericalDuration};
839 /// # use time_macros::date;
840 /// assert_eq!(Date::MAX.checked_sub((-23).hours()), Some(Date::MAX));
841 /// assert_eq!(Date::MIN.checked_sub(23.hours()), Some(Date::MIN));
842 /// assert_eq!(
843 /// date!(2020 - 12 - 31).checked_sub(23.hours()),
844 /// Some(date!(2020 - 12 - 31))
845 /// );
846 /// assert_eq!(
847 /// date!(2020 - 12 - 31).checked_sub(47.hours()),
848 /// Some(date!(2020 - 12 - 30))
849 /// );
850 /// ```
851 pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
852 let whole_days = duration.whole_days();
853 if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 {
854 return None;
855 }
856
857 let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _));
858 if let Ok(date) = Self::from_julian_day(julian_day) {
859 Some(date)
860 } else {
861 None
862 }
863 }
864
865 /// Computes `self - duration`, returning `None` if an overflow occurred.
866 ///
867 /// ```
868 /// # use time::{Date, ext::NumericalStdDuration};
869 /// # use time_macros::date;
870 /// assert_eq!(Date::MIN.checked_sub_std(1.std_days()), None);
871 /// assert_eq!(
872 /// date!(2020 - 12 - 31).checked_sub_std(2.std_days()),
873 /// Some(date!(2020 - 12 - 29))
874 /// );
875 /// ```
876 ///
877 /// # Note
878 ///
879 /// This function only takes whole days into account.
880 ///
881 /// ```
882 /// # use time::{Date, ext::NumericalStdDuration};
883 /// # use time_macros::date;
884 /// assert_eq!(Date::MIN.checked_sub_std(23.std_hours()), Some(Date::MIN));
885 /// assert_eq!(
886 /// date!(2020 - 12 - 31).checked_sub_std(23.std_hours()),
887 /// Some(date!(2020 - 12 - 31))
888 /// );
889 /// assert_eq!(
890 /// date!(2020 - 12 - 31).checked_sub_std(47.std_hours()),
891 /// Some(date!(2020 - 12 - 30))
892 /// );
893 /// ```
894 pub const fn checked_sub_std(self, duration: StdDuration) -> Option<Self> {
895 let whole_days = duration.as_secs() / Second::per(Day) as u64;
896 if whole_days > i32::MAX as u64 {
897 return None;
898 }
899
900 let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _));
901 if let Ok(date) = Self::from_julian_day(julian_day) {
902 Some(date)
903 } else {
904 None
905 }
906 }
907
908 /// Calculates the first occurrence of a weekday that is strictly later than a given `Date`.
909 /// Returns `None` if an overflow occurred.
910 pub(crate) const fn checked_next_occurrence(self, weekday: Weekday) -> Option<Self> {
911 let day_diff = match weekday as i8 - self.weekday() as i8 {
912 1 | -6 => 1,
913 2 | -5 => 2,
914 3 | -4 => 3,
915 4 | -3 => 4,
916 5 | -2 => 5,
917 6 | -1 => 6,
918 val => {
919 debug_assert!(val == 0);
920 7
921 }
922 };
923
924 self.checked_add(Duration::days(day_diff))
925 }
926
927 /// Calculates the first occurrence of a weekday that is strictly earlier than a given `Date`.
928 /// Returns `None` if an overflow occurred.
929 pub(crate) const fn checked_prev_occurrence(self, weekday: Weekday) -> Option<Self> {
930 let day_diff = match weekday as i8 - self.weekday() as i8 {
931 1 | -6 => 6,
932 2 | -5 => 5,
933 3 | -4 => 4,
934 4 | -3 => 3,
935 5 | -2 => 2,
936 6 | -1 => 1,
937 val => {
938 debug_assert!(val == 0);
939 7
940 }
941 };
942
943 self.checked_sub(Duration::days(day_diff))
944 }
945
946 /// Calculates the `n`th occurrence of a weekday that is strictly later than a given `Date`.
947 /// Returns `None` if an overflow occurred or if `n == 0`.
948 pub(crate) const fn checked_nth_next_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> {
949 if n == 0 {
950 return None;
951 }
952
953 const_try_opt!(self.checked_next_occurrence(weekday))
954 .checked_add(Duration::weeks(n as i64 - 1))
955 }
956
957 /// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`.
958 /// Returns `None` if an overflow occurred or if `n == 0`.
959 pub(crate) const fn checked_nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> {
960 if n == 0 {
961 return None;
962 }
963
964 const_try_opt!(self.checked_prev_occurrence(weekday))
965 .checked_sub(Duration::weeks(n as i64 - 1))
966 }
967 // endregion: checked arithmetic
968
969 // region: saturating arithmetic
970 /// Computes `self + duration`, saturating value on overflow.
971 ///
972 /// ```rust
973 /// # use time::{Date, ext::NumericalDuration};
974 /// # use time_macros::date;
975 /// assert_eq!(Date::MAX.saturating_add(1.days()), Date::MAX);
976 /// assert_eq!(Date::MIN.saturating_add((-2).days()), Date::MIN);
977 /// assert_eq!(
978 /// date!(2020 - 12 - 31).saturating_add(2.days()),
979 /// date!(2021 - 01 - 02)
980 /// );
981 /// ```
982 ///
983 /// # Note
984 ///
985 /// This function only takes whole days into account.
986 ///
987 /// ```rust
988 /// # use time::ext::NumericalDuration;
989 /// # use time_macros::date;
990 /// assert_eq!(
991 /// date!(2020 - 12 - 31).saturating_add(23.hours()),
992 /// date!(2020 - 12 - 31)
993 /// );
994 /// assert_eq!(
995 /// date!(2020 - 12 - 31).saturating_add(47.hours()),
996 /// date!(2021 - 01 - 01)
997 /// );
998 /// ```
999 pub const fn saturating_add(self, duration: Duration) -> Self {
1000 if let Some(datetime) = self.checked_add(duration) {
1001 datetime
1002 } else if duration.is_negative() {
1003 Self::MIN
1004 } else {
1005 debug_assert!(duration.is_positive());
1006 Self::MAX
1007 }
1008 }
1009
1010 /// Computes `self - duration`, saturating value on overflow.
1011 ///
1012 /// ```
1013 /// # use time::{Date, ext::NumericalDuration};
1014 /// # use time_macros::date;
1015 /// assert_eq!(Date::MAX.saturating_sub((-2).days()), Date::MAX);
1016 /// assert_eq!(Date::MIN.saturating_sub(1.days()), Date::MIN);
1017 /// assert_eq!(
1018 /// date!(2020 - 12 - 31).saturating_sub(2.days()),
1019 /// date!(2020 - 12 - 29)
1020 /// );
1021 /// ```
1022 ///
1023 /// # Note
1024 ///
1025 /// This function only takes whole days into account.
1026 ///
1027 /// ```
1028 /// # use time::ext::NumericalDuration;
1029 /// # use time_macros::date;
1030 /// assert_eq!(
1031 /// date!(2020 - 12 - 31).saturating_sub(23.hours()),
1032 /// date!(2020 - 12 - 31)
1033 /// );
1034 /// assert_eq!(
1035 /// date!(2020 - 12 - 31).saturating_sub(47.hours()),
1036 /// date!(2020 - 12 - 30)
1037 /// );
1038 /// ```
1039 pub const fn saturating_sub(self, duration: Duration) -> Self {
1040 if let Some(datetime) = self.checked_sub(duration) {
1041 datetime
1042 } else if duration.is_negative() {
1043 Self::MAX
1044 } else {
1045 debug_assert!(duration.is_positive());
1046 Self::MIN
1047 }
1048 }
1049 // region: saturating arithmetic
1050
1051 // region: replacement
1052 /// Replace the year. The month and day will be unchanged.
1053 ///
1054 /// ```rust
1055 /// # use time_macros::date;
1056 /// assert_eq!(
1057 /// date!(2022 - 02 - 18).replace_year(2019),
1058 /// Ok(date!(2019 - 02 - 18))
1059 /// );
1060 /// assert!(date!(2022 - 02 - 18).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year
1061 /// assert!(date!(2022 - 02 - 18).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year
1062 /// ```
1063 #[must_use = "This method does not mutate the original `Date`."]
1064 pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
1065 ensure_ranged!(Year: year);
1066
1067 let ordinal = self.ordinal();
1068
1069 // Dates in January and February are unaffected by leap years.
1070 if ordinal <= 59 {
1071 // Safety: `ordinal` is not zero.
1072 return Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) });
1073 }
1074
1075 match (is_leap_year(self.year()), is_leap_year(year)) {
1076 (false, false) | (true, true) => {
1077 // Safety: `ordinal` is not zero.
1078 Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) })
1079 }
1080 // February 29 does not exist in common years.
1081 (true, false) if ordinal == 60 => Err(error::ComponentRange {
1082 name: "day",
1083 value: 29,
1084 minimum: 1,
1085 maximum: 28,
1086 conditional_range: true,
1087 }),
1088 // We're going from a common year to a leap year. Shift dates in March and later by
1089 // one day.
1090 // Safety: `ordinal` is not zero.
1091 (false, true) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal + 1) }),
1092 // We're going from a leap year to a common year. Shift dates in January and
1093 // February by one day.
1094 // Safety: `ordinal` is not zero.
1095 (true, false) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal - 1) }),
1096 }
1097 }
1098
1099 /// Replace the month of the year.
1100 ///
1101 /// ```rust
1102 /// # use time_macros::date;
1103 /// # use time::Month;
1104 /// assert_eq!(
1105 /// date!(2022 - 02 - 18).replace_month(Month::January),
1106 /// Ok(date!(2022 - 01 - 18))
1107 /// );
1108 /// assert!(
1109 /// date!(2022 - 01 - 30)
1110 /// .replace_month(Month::February)
1111 /// .is_err()
1112 /// ); // 30 isn't a valid day in February
1113 /// ```
1114 #[must_use = "This method does not mutate the original `Date`."]
1115 pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
1116 let (year, _, day) = self.to_calendar_date();
1117 Self::from_calendar_date(year, month, day)
1118 }
1119
1120 /// Replace the day of the month.
1121 ///
1122 /// ```rust
1123 /// # use time_macros::date;
1124 /// assert_eq!(
1125 /// date!(2022 - 02 - 18).replace_day(1),
1126 /// Ok(date!(2022 - 02 - 01))
1127 /// );
1128 /// assert!(date!(2022 - 02 - 18).replace_day(0).is_err()); // 0 isn't a valid day
1129 /// assert!(date!(2022 - 02 - 18).replace_day(30).is_err()); // 30 isn't a valid day in February
1130 /// ```
1131 #[must_use = "This method does not mutate the original `Date`."]
1132 pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
1133 match day {
1134 1..=28 => {}
1135 29..=31 if day <= days_in_year_month(self.year(), self.month()) => {}
1136 _ => {
1137 return Err(error::ComponentRange {
1138 name: "day",
1139 minimum: 1,
1140 maximum: days_in_year_month(self.year(), self.month()) as _,
1141 value: day as _,
1142 conditional_range: true,
1143 });
1144 }
1145 }
1146
1147 // Safety: `ordinal` is not zero.
1148 Ok(unsafe {
1149 Self::__from_ordinal_date_unchecked(
1150 self.year(),
1151 (self.ordinal() as i16 - self.day() as i16 + day as i16) as _,
1152 )
1153 })
1154 }
1155
1156 /// Replace the day of the year.
1157 ///
1158 /// ```rust
1159 /// # use time_macros::date;
1160 /// assert_eq!(date!(2022 - 049).replace_ordinal(1), Ok(date!(2022 - 001)));
1161 /// assert!(date!(2022 - 049).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal
1162 /// assert!(date!(2022 - 049).replace_ordinal(366).is_err()); // 2022 isn't a leap year
1163 /// ````
1164 #[must_use = "This method does not mutate the original `Date`."]
1165 pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
1166 match ordinal {
1167 1..=365 => {}
1168 366 if is_leap_year(self.year()) => {}
1169 _ => {
1170 return Err(error::ComponentRange {
1171 name: "ordinal",
1172 minimum: 1,
1173 maximum: days_in_year(self.year()) as _,
1174 value: ordinal as _,
1175 conditional_range: true,
1176 });
1177 }
1178 }
1179
1180 // Safety: `ordinal` is in range.
1181 Ok(unsafe { Self::__from_ordinal_date_unchecked(self.year(), ordinal) })
1182 }
1183 // endregion replacement
1184}
1185
1186// region: attach time
1187/// Methods to add a [`Time`] component, resulting in a [`PrimitiveDateTime`].
1188impl Date {
1189 /// Create a [`PrimitiveDateTime`] using the existing date. The [`Time`] component will be set
1190 /// to midnight.
1191 ///
1192 /// ```rust
1193 /// # use time_macros::{date, datetime};
1194 /// assert_eq!(date!(1970-01-01).midnight(), datetime!(1970-01-01 0:00));
1195 /// ```
1196 pub const fn midnight(self) -> PrimitiveDateTime {
1197 PrimitiveDateTime::new(self, Time::MIDNIGHT)
1198 }
1199
1200 /// Create a [`PrimitiveDateTime`] using the existing date and the provided [`Time`].
1201 ///
1202 /// ```rust
1203 /// # use time_macros::{date, datetime, time};
1204 /// assert_eq!(
1205 /// date!(1970-01-01).with_time(time!(0:00)),
1206 /// datetime!(1970-01-01 0:00),
1207 /// );
1208 /// ```
1209 pub const fn with_time(self, time: Time) -> PrimitiveDateTime {
1210 PrimitiveDateTime::new(self, time)
1211 }
1212
1213 /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
1214 ///
1215 /// ```rust
1216 /// # use time_macros::date;
1217 /// assert!(date!(1970 - 01 - 01).with_hms(0, 0, 0).is_ok());
1218 /// assert!(date!(1970 - 01 - 01).with_hms(24, 0, 0).is_err());
1219 /// ```
1220 pub const fn with_hms(
1221 self,
1222 hour: u8,
1223 minute: u8,
1224 second: u8,
1225 ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1226 Ok(PrimitiveDateTime::new(
1227 self,
1228 const_try!(Time::from_hms(hour, minute, second)),
1229 ))
1230 }
1231
1232 /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
1233 ///
1234 /// ```rust
1235 /// # use time_macros::date;
1236 /// assert!(date!(1970 - 01 - 01).with_hms_milli(0, 0, 0, 0).is_ok());
1237 /// assert!(date!(1970 - 01 - 01).with_hms_milli(24, 0, 0, 0).is_err());
1238 /// ```
1239 pub const fn with_hms_milli(
1240 self,
1241 hour: u8,
1242 minute: u8,
1243 second: u8,
1244 millisecond: u16,
1245 ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1246 Ok(PrimitiveDateTime::new(
1247 self,
1248 const_try!(Time::from_hms_milli(hour, minute, second, millisecond)),
1249 ))
1250 }
1251
1252 /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
1253 ///
1254 /// ```rust
1255 /// # use time_macros::date;
1256 /// assert!(date!(1970 - 01 - 01).with_hms_micro(0, 0, 0, 0).is_ok());
1257 /// assert!(date!(1970 - 01 - 01).with_hms_micro(24, 0, 0, 0).is_err());
1258 /// ```
1259 pub const fn with_hms_micro(
1260 self,
1261 hour: u8,
1262 minute: u8,
1263 second: u8,
1264 microsecond: u32,
1265 ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1266 Ok(PrimitiveDateTime::new(
1267 self,
1268 const_try!(Time::from_hms_micro(hour, minute, second, microsecond)),
1269 ))
1270 }
1271
1272 /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
1273 ///
1274 /// ```rust
1275 /// # use time_macros::date;
1276 /// assert!(date!(1970 - 01 - 01).with_hms_nano(0, 0, 0, 0).is_ok());
1277 /// assert!(date!(1970 - 01 - 01).with_hms_nano(24, 0, 0, 0).is_err());
1278 /// ```
1279 pub const fn with_hms_nano(
1280 self,
1281 hour: u8,
1282 minute: u8,
1283 second: u8,
1284 nanosecond: u32,
1285 ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1286 Ok(PrimitiveDateTime::new(
1287 self,
1288 const_try!(Time::from_hms_nano(hour, minute, second, nanosecond)),
1289 ))
1290 }
1291}
1292// endregion attach time
1293
1294// region: formatting & parsing
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,
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// endregion formatting & parsing
1432
1433// region: trait impls
1434impl Add<Duration> for Date {
1435 type Output = Self;
1436
1437 /// # Panics
1438 ///
1439 /// This may panic if an overflow occurs.
1440 fn add(self, duration: Duration) -> Self::Output {
1441 self.checked_add(duration)
1442 .expect("overflow adding duration to date")
1443 }
1444}
1445
1446impl Add<StdDuration> for Date {
1447 type Output = Self;
1448
1449 /// # Panics
1450 ///
1451 /// This may panic if an overflow occurs.
1452 fn add(self, duration: StdDuration) -> Self::Output {
1453 self.checked_add_std(duration)
1454 .expect("overflow adding duration to date")
1455 }
1456}
1457
1458impl_add_assign!(Date: Duration, StdDuration);
1459
1460impl Sub<Duration> for Date {
1461 type Output = Self;
1462
1463 /// # Panics
1464 ///
1465 /// This may panic if an overflow occurs.
1466 fn sub(self, duration: Duration) -> Self::Output {
1467 self.checked_sub(duration)
1468 .expect("overflow subtracting duration from date")
1469 }
1470}
1471
1472impl Sub<StdDuration> for Date {
1473 type Output = Self;
1474
1475 /// # Panics
1476 ///
1477 /// This may panic if an overflow occurs.
1478 fn sub(self, duration: StdDuration) -> Self::Output {
1479 self.checked_sub_std(duration)
1480 .expect("overflow subtracting duration from date")
1481 }
1482}
1483
1484impl_sub_assign!(Date: Duration, StdDuration);
1485
1486impl Sub for Date {
1487 type Output = Duration;
1488
1489 fn sub(self, other: Self) -> Self::Output {
1490 Duration::days((self.to_julian_day() - other.to_julian_day()).extend())
1491 }
1492}
1493// endregion trait impls