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