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