1#[cfg(feature = "formatting")]
4use alloc::string::String;
5use core::fmt;
6use core::mem::MaybeUninit;
7use core::num::NonZero;
8use core::ops::{Add, AddAssign, Sub, SubAssign};
9use core::time::Duration as StdDuration;
10#[cfg(feature = "formatting")]
11use std::io;
12
13use deranged::{ri32, ru8, ru32};
14use num_conv::prelude::*;
15use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay};
16
17#[cfg(feature = "formatting")]
18use crate::formatting::Formattable;
19use crate::internal_macros::{const_try, const_try_opt, div_floor, ensure_ranged};
20use crate::num_fmt::{four_to_six_digits, str_from_raw_parts, two_digits_zero_padded};
21#[cfg(feature = "parsing")]
22use crate::parsing::Parsable;
23use crate::unit::*;
24use crate::util::{days_in_month_leap, range_validated, weeks_in_year};
25use crate::{Duration, Month, PrimitiveDateTime, Time, Weekday, error, hint};
26
27type Year = ri32<MIN_YEAR, MAX_YEAR>;
28
29pub(crate) const MIN_YEAR: i32 = if falsecfg!(feature = "large-dates") {
31 -999_999
32} else {
33 -9999
34};
35pub(crate) const MAX_YEAR: i32 = if falsecfg!(feature = "large-dates") {
37 999_999
38} else {
39 9999
40};
41
42#[derive(#[automatically_derived]
impl ::core::clone::Clone for Date {
#[inline]
fn clone(&self) -> Date {
let _: ::core::clone::AssertParamIsClone<NonZero<i32>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Date { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for Date {
#[inline]
fn eq(&self, other: &Date) -> bool { self.value == other.value }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for Date {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<NonZero<i32>>;
}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for Date {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.value, state)
}
}Hash, #[automatically_derived]
impl ::core::cmp::PartialOrd for Date {
#[inline]
fn partial_cmp(&self, other: &Date)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
}
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for Date {
#[inline]
fn cmp(&self, other: &Date) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.value, &other.value)
}
}Ord)]
48pub struct Date {
49 value: NonZero<i32>,
55}
56
57impl Date {
58 #[inline]
65 pub(crate) const fn as_i32(self) -> i32 {
66 self.value.get()
67 }
68
69 pub(crate) const UNIX_EPOCH: Self = unsafe { Self::__from_ordinal_date_unchecked(1970, 1) };
72
73 pub const MIN: Self = unsafe { Self::__from_ordinal_date_unchecked(MIN_YEAR, 1) };
78
79 pub const MAX: Self = unsafe {
84 Self::__from_ordinal_date_unchecked(MAX_YEAR, range_validated::days_in_year(MAX_YEAR))
85 };
86
87 #[inline]
95 #[track_caller]
96 const unsafe fn from_parts(year: i32, is_leap_year: bool, ordinal: u16) -> Self {
97 if true {
if !(year >= MIN_YEAR) {
::core::panicking::panic("assertion failed: year >= MIN_YEAR")
};
};debug_assert!(year >= MIN_YEAR);
98 if true {
if !(year <= MAX_YEAR) {
::core::panicking::panic("assertion failed: year <= MAX_YEAR")
};
};debug_assert!(year <= MAX_YEAR);
99 if true {
if !(ordinal != 0) {
::core::panicking::panic("assertion failed: ordinal != 0")
};
};debug_assert!(ordinal != 0);
100 if true {
if !(ordinal <= range_validated::days_in_year(year)) {
::core::panicking::panic("assertion failed: ordinal <= range_validated::days_in_year(year)")
};
};debug_assert!(ordinal <= range_validated::days_in_year(year));
101 if true {
if !(range_validated::is_leap_year(year) == is_leap_year) {
::core::panicking::panic("assertion failed: range_validated::is_leap_year(year) == is_leap_year")
};
};debug_assert!(range_validated::is_leap_year(year) == is_leap_year);
102
103 Self {
104 value: unsafe {
106 NonZero::new_unchecked((year << 10) | ((is_leap_year as i32) << 9) | ordinal as i32)
107 },
108 }
109 }
110
111 #[doc(hidden)]
119 #[inline]
120 #[track_caller]
121 pub const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self {
122 unsafe { Self::from_parts(year, range_validated::is_leap_year(year), ordinal) }
125 }
126
127 #[inline]
140 pub const fn from_calendar_date(
141 year: i32,
142 month: Month,
143 day: u8,
144 ) -> Result<Self, error::ComponentRange> {
145 const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [
147 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
148 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
149 ];
150
151 match <Year>::new(year) {
Some(val) => val,
None => {
crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional("year"));
}
};ensure_ranged!(Year: year);
152
153 let is_leap_year = range_validated::is_leap_year(year);
154 match day {
155 1..=28 => {}
156 29..=31 if day <= days_in_month_leap(month as u8, is_leap_year) => hint::cold_path(),
157 _ => {
158 hint::cold_path();
159 return Err(error::ComponentRange::conditional("day"));
160 }
161 }
162
163 Ok(unsafe {
165 Self::from_parts(
166 year,
167 is_leap_year,
168 DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year as usize][month as usize - 1] + day as u16,
169 )
170 })
171 }
172
173 #[inline]
186 pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result<Self, error::ComponentRange> {
187 match <Year>::new(year) {
Some(val) => val,
None => {
crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional("year"));
}
};ensure_ranged!(Year: year);
188
189 let is_leap_year = range_validated::is_leap_year(year);
190 match ordinal {
191 1..=365 => {}
192 366 if is_leap_year => hint::cold_path(),
193 _ => {
194 hint::cold_path();
195 return Err(error::ComponentRange::conditional("ordinal"));
196 }
197 }
198
199 Ok(unsafe { Self::from_parts(year, is_leap_year, ordinal) })
201 }
202
203 pub const fn from_iso_week_date(
217 year: i32,
218 week: u8,
219 weekday: Weekday,
220 ) -> Result<Self, error::ComponentRange> {
221 match <Year>::new(year) {
Some(val) => val,
None => {
crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional("year"));
}
};ensure_ranged!(Year: year);
222 match week {
223 1..=52 => {}
224 53 if week <= weeks_in_year(year) => hint::cold_path(),
225 _ => {
226 hint::cold_path();
227 return Err(error::ComponentRange::conditional("week"));
228 }
229 }
230
231 let adj_year = year - 1;
232 let raw = 365 * adj_year + match (adj_year, 4) {
(this, rhs) => {
let d = this / rhs;
let r = this % rhs;
let correction = (this ^ rhs) >> (size_of_val(&this) * 8 - 1);
if r != 0 { d + correction } else { d }
}
}div_floor!(adj_year, 4) - match (adj_year, 100) {
(this, rhs) => {
let d = this / rhs;
let r = this % rhs;
let correction = (this ^ rhs) >> (size_of_val(&this) * 8 - 1);
if r != 0 { d + correction } else { d }
}
}div_floor!(adj_year, 100)
233 + match (adj_year, 400) {
(this, rhs) => {
let d = this / rhs;
let r = this % rhs;
let correction = (this ^ rhs) >> (size_of_val(&this) * 8 - 1);
if r != 0 { d + correction } else { d }
}
}div_floor!(adj_year, 400);
234 let jan_4 = match (raw % 7) as i8 {
235 -6 | 1 => 8,
236 -5 | 2 => 9,
237 -4 | 3 => 10,
238 -3 | 4 => 4,
239 -2 | 5 => 5,
240 -1 | 6 => 6,
241 _ => 7,
242 };
243 let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4;
244
245 if ordinal <= 0 {
246 return Ok(unsafe {
248 Self::__from_ordinal_date_unchecked(
249 year - 1,
250 ordinal
251 .cast_unsigned()
252 .wrapping_add(range_validated::days_in_year(year - 1)),
253 )
254 });
255 }
256
257 let is_leap_year = range_validated::is_leap_year(year);
258 let days_in_year = if is_leap_year { 366 } else { 365 };
259 let ordinal = ordinal.cast_unsigned();
260 Ok(if ordinal > days_in_year {
261 if hint::unlikely(year == MAX_YEAR) {
263 return Err(error::ComponentRange::conditional("weekday"));
264 }
265 unsafe { Self::__from_ordinal_date_unchecked(year + 1, ordinal - days_in_year) }
267 } else {
268 unsafe { Self::from_parts(year, is_leap_year, ordinal) }
270 })
271 }
272
273 #[doc(alias = "from_julian_date")]
284 #[inline]
285 pub const fn from_julian_day(julian_day: i32) -> Result<Self, error::ComponentRange> {
286 type JulianDay = ri32<{ Date::MIN.to_julian_day() }, { Date::MAX.to_julian_day() }>;
287 match <JulianDay>::new(julian_day) {
Some(val) => val,
None => {
crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional("julian_day"));
}
};ensure_ranged!(JulianDay: julian_day);
288 Ok(unsafe { Self::from_julian_day_unchecked(julian_day) })
290 }
291
292 #[inline]
299 pub(crate) const unsafe fn from_julian_day_unchecked(julian_day: i32) -> Self {
300 if true {
if !(julian_day >= Self::MIN.to_julian_day()) {
::core::panicking::panic("assertion failed: julian_day >= Self::MIN.to_julian_day()")
};
};debug_assert!(julian_day >= Self::MIN.to_julian_day());
301 if true {
if !(julian_day <= Self::MAX.to_julian_day()) {
::core::panicking::panic("assertion failed: julian_day <= Self::MAX.to_julian_day()")
};
};debug_assert!(julian_day <= Self::MAX.to_julian_day());
302
303 const ERAS: u32 = 5_949;
304 const D_SHIFT: u32 = 146097 * ERAS - 1_721_060;
306 const Y_SHIFT: u32 = 400 * ERAS;
308
309 const CEN_MUL: u32 = ((4u64 << 47) / 146_097) as u32;
310 const JUL_MUL: u32 = ((4u64 << 40) / 1_461 + 1) as u32;
311 const CEN_CUT: u32 = ((365u64 << 32) / 36_525) as u32;
312
313 let day = julian_day.cast_unsigned().wrapping_add(D_SHIFT);
314 let c_n = (day as u64 * CEN_MUL as u64) >> 15;
315 let cen = (c_n >> 32) as u32;
316 let cpt = c_n as u32;
317 let ijy = cpt > CEN_CUT || cen.is_multiple_of(4);
318 let jul = day - cen / 4 + cen;
319 let y_n = (jul as u64 * JUL_MUL as u64) >> 8;
320 let yrs = (y_n >> 32) as u32;
321 let ypt = y_n as u32;
322
323 let year = yrs.wrapping_sub(Y_SHIFT).cast_signed();
324 let ordinal = ((ypt as u64 * 1_461) >> 34) as u32 + ijy as u32;
325 let leap = yrs.is_multiple_of(4) & ijy;
326
327 unsafe { Self::from_parts(year, leap, ordinal as u16) }
330 }
331
332 #[inline]
337 const fn is_in_leap_year(self) -> bool {
338 (self.value.get() >> 9) & 1 == 1
339 }
340
341 #[inline]
350 pub const fn year(self) -> i32 {
351 self.value.get() >> 10
352 }
353
354 #[inline]
363 pub const fn month(self) -> Month {
364 let ordinal = self.ordinal() as u32;
365 let jan_feb_len = 59 + self.is_in_leap_year() as u32;
366
367 let (month_adj, ordinal_adj) = if ordinal <= jan_feb_len {
368 (0, 0)
369 } else {
370 (2, jan_feb_len)
371 };
372
373 let ordinal = ordinal - ordinal_adj;
374 let month = ((ordinal * 268 + 8031) >> 13) + month_adj;
375
376 unsafe {
378 match Month::from_number(NonZero::new_unchecked(month as u8)) {
379 Ok(month) => month,
380 Err(_) => core::hint::unreachable_unchecked(),
381 }
382 }
383 }
384
385 #[inline]
395 pub const fn day(self) -> u8 {
396 let ordinal = self.ordinal() as u32;
397 let jan_feb_len = 59 + self.is_in_leap_year() as u32;
398
399 let ordinal_adj = if ordinal <= jan_feb_len {
400 0
401 } else {
402 jan_feb_len
403 };
404
405 let ordinal = ordinal - ordinal_adj;
406 let month = (ordinal * 268 + 8031) >> 13;
407 let days_in_preceding_months = (month * 3917 - 3866) >> 7;
408 (ordinal - days_in_preceding_months) as u8
409 }
410
411 #[inline]
421 pub const fn ordinal(self) -> u16 {
422 (self.value.get() & 0x1FF) as u16
423 }
424
425 #[inline]
427 pub(crate) const fn iso_year_week(self) -> (i32, u8) {
428 let (year, ordinal) = self.to_ordinal_date();
429
430 match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as u8 {
431 0 => (year - 1, weeks_in_year(year - 1)),
432 53 if weeks_in_year(year) == 52 => (year + 1, 1),
433 week => (year, week),
434 }
435 }
436
437 #[inline]
450 pub const fn iso_week(self) -> u8 {
451 self.iso_year_week().1
452 }
453
454 #[inline]
466 pub const fn sunday_based_week(self) -> u8 {
467 ((self.ordinal().cast_signed() - self.weekday().number_days_from_sunday() as i16 + 6) / 7)
468 as u8
469 }
470
471 #[inline]
483 pub const fn monday_based_week(self) -> u8 {
484 ((self.ordinal().cast_signed() - self.weekday().number_days_from_monday() as i16 + 6) / 7)
485 as u8
486 }
487
488 #[inline]
499 pub const fn to_calendar_date(self) -> (i32, Month, u8) {
500 let (year, ordinal) = self.to_ordinal_date();
501 let ordinal = ordinal as u32;
502 let jan_feb_len = 59 + self.is_in_leap_year() as u32;
503
504 let (month_adj, ordinal_adj) = if ordinal <= jan_feb_len {
505 (0, 0)
506 } else {
507 (2, jan_feb_len)
508 };
509
510 let ordinal = ordinal - ordinal_adj;
511 let month = (ordinal * 268 + 8031) >> 13;
512 let days_in_preceding_months = (month * 3917 - 3866) >> 7;
513 let day = ordinal - days_in_preceding_months;
514 let month = month + month_adj;
515
516 (
517 year,
518 unsafe {
520 match Month::from_number(NonZero::new_unchecked(month as u8)) {
521 Ok(month) => month,
522 Err(_) => core::hint::unreachable_unchecked(),
523 }
524 },
525 day as u8,
526 )
527 }
528
529 #[inline]
536 pub const fn to_ordinal_date(self) -> (i32, u16) {
537 (self.year(), self.ordinal())
538 }
539
540 #[inline]
552 pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
553 let (year, ordinal) = self.to_ordinal_date();
554 let weekday = self.weekday();
555
556 match ((ordinal + 10 - weekday.number_from_monday() as u16) / 7) as u8 {
557 0 => (year - 1, weeks_in_year(year - 1), weekday),
558 53 if weeks_in_year(year) == 52 => (year + 1, 1, weekday),
559 week => (year, week, weekday),
560 }
561 }
562
563 #[inline]
582 pub const fn weekday(self) -> Weekday {
583 match self.to_julian_day() % 7 {
584 -6 | 1 => Weekday::Tuesday,
585 -5 | 2 => Weekday::Wednesday,
586 -4 | 3 => Weekday::Thursday,
587 -3 | 4 => Weekday::Friday,
588 -2 | 5 => Weekday::Saturday,
589 -1 | 6 => Weekday::Sunday,
590 val => {
591 if true {
if !(val == 0) { ::core::panicking::panic("assertion failed: val == 0") };
};debug_assert!(val == 0);
592 Weekday::Monday
593 }
594 }
595 }
596
597 #[inline]
608 pub const fn next_day(self) -> Option<Self> {
609 let is_last_day_of_year = #[allow(non_exhaustive_omitted_patterns)] match self.value.get() & 0x3FF {
365 | 878 => true,
_ => false,
}matches!(self.value.get() & 0x3FF, 365 | 878);
610 if hint::unlikely(is_last_day_of_year) {
611 if self.value.get() == Self::MAX.value.get() {
612 None
613 } else {
614 unsafe { Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) }
616 }
617 } else {
618 Some(Self {
619 value: unsafe { NonZero::new_unchecked(self.value.get() + 1) },
621 })
622 }
623 }
624
625 #[inline]
636 pub const fn previous_day(self) -> Option<Self> {
637 if hint::likely(self.ordinal() != 1) {
638 Some(Self {
639 value: unsafe { NonZero::new_unchecked(self.value.get() - 1) },
641 })
642 } else if self.value.get() == Self::MIN.value.get() {
643 None
644 } else {
645 let year = self.year() - 1;
646 let is_leap_year = range_validated::is_leap_year(year);
647 let ordinal = if is_leap_year { 366 } else { 365 };
648 Some(unsafe { Self::from_parts(year, is_leap_year, ordinal) })
650 }
651 }
652
653 #[inline]
672 #[track_caller]
673 pub const fn next_occurrence(self, weekday: Weekday) -> Self {
674 self.checked_next_occurrence(weekday)
675 .expect("overflow calculating the next occurrence of a weekday")
676 }
677
678 #[inline]
697 #[track_caller]
698 pub const fn prev_occurrence(self, weekday: Weekday) -> Self {
699 self.checked_prev_occurrence(weekday)
700 .expect("overflow calculating the previous occurrence of a weekday")
701 }
702
703 #[inline]
722 #[track_caller]
723 pub const fn nth_next_occurrence(self, weekday: Weekday, n: u8) -> Self {
724 self.checked_nth_next_occurrence(weekday, n)
725 .expect("overflow calculating the next occurrence of a weekday")
726 }
727
728 #[inline]
747 #[track_caller]
748 pub const fn nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Self {
749 self.checked_nth_prev_occurrence(weekday, n)
750 .expect("overflow calculating the previous occurrence of a weekday")
751 }
752
753 #[inline]
763 pub const fn to_julian_day(self) -> i32 {
764 let (year, ordinal) = self.to_ordinal_date();
765
766 let adj_year = year + 999_999;
769 let century = adj_year / 100;
770
771 let days_before_year = (1461 * adj_year as i64 / 4) as i32 - century + century / 4;
772 days_before_year + ordinal as i32 - 363_521_075
773 }
774
775 #[inline]
807 pub const fn checked_add(self, duration: Duration) -> Option<Self> {
808 let whole_days = duration.whole_days();
809 if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 {
810 return None;
811 }
812
813 let year = self.year();
814 let is_leap_year = self.is_in_leap_year();
815 let ordinal = self.ordinal() as i32;
816
817 let days_in_year = if is_leap_year { 366 } else { 365 };
818 let whole_days = whole_days as i32;
819
820 if let Some(new_ordinal) = ordinal.checked_add(whole_days)
822 && new_ordinal >= 1
823 && new_ordinal <= days_in_year
824 {
825 return Some(unsafe { Self::from_parts(year, is_leap_year, new_ordinal as u16) });
827 }
828
829 let julian_day = match self.to_julian_day().checked_add(whole_days) {
Some(value) => value,
None => { crate::hint::cold_path(); return None; }
}const_try_opt!(self.to_julian_day().checked_add(whole_days));
830 if let Ok(date) = Self::from_julian_day(julian_day) {
831 Some(date)
832 } else {
833 None
834 }
835 }
836
837 #[inline]
867 pub const fn checked_add_std(self, duration: StdDuration) -> Option<Self> {
868 let whole_days = duration.as_secs() / Second::per_t::<u64>(Day);
869 if whole_days > i32::MAX as u64 {
870 return None;
871 }
872
873 let year = self.year();
874 let is_leap_year = self.is_in_leap_year();
875 let ordinal = self.ordinal() as i32;
876
877 let days_in_year = if is_leap_year { 366 } else { 365 };
878 let whole_days = whole_days as i32;
879
880 if let Some(new_ordinal) = ordinal.checked_add(whole_days)
882 && new_ordinal >= 1
883 && new_ordinal <= days_in_year
884 {
885 return Some(unsafe { Self::from_parts(year, is_leap_year, new_ordinal as u16) });
887 }
888
889 let julian_day = match self.to_julian_day().checked_add(whole_days) {
Some(value) => value,
None => { crate::hint::cold_path(); return None; }
}const_try_opt!(self.to_julian_day().checked_add(whole_days));
890 if let Ok(date) = Self::from_julian_day(julian_day) {
891 Some(date)
892 } else {
893 None
894 }
895 }
896
897 #[inline]
929 pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
930 let whole_days = duration.whole_days();
931 if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 {
932 return None;
933 }
934
935 let year = self.year();
936 let is_leap_year = self.is_in_leap_year();
937 let ordinal = self.ordinal() as i32;
938
939 let days_in_year = if is_leap_year { 366 } else { 365 };
940 let whole_days = whole_days as i32;
941
942 if let Some(new_ordinal) = ordinal.checked_sub(whole_days)
944 && new_ordinal >= 1
945 && new_ordinal <= days_in_year
946 {
947 return Some(unsafe { Self::from_parts(year, is_leap_year, new_ordinal as u16) });
949 }
950
951 let julian_day = match self.to_julian_day().checked_sub(whole_days) {
Some(value) => value,
None => { crate::hint::cold_path(); return None; }
}const_try_opt!(self.to_julian_day().checked_sub(whole_days));
952 if let Ok(date) = Self::from_julian_day(julian_day) {
953 Some(date)
954 } else {
955 None
956 }
957 }
958
959 #[inline]
989 pub const fn checked_sub_std(self, duration: StdDuration) -> Option<Self> {
990 let whole_days = duration.as_secs() / Second::per_t::<u64>(Day);
991 if whole_days > i32::MAX as u64 {
992 return None;
993 }
994
995 let year = self.year();
996 let is_leap_year = self.is_in_leap_year();
997 let ordinal = self.ordinal() as i32;
998
999 let days_in_year = if is_leap_year { 366 } else { 365 };
1000 let whole_days = whole_days as i32;
1001
1002 if let Some(new_ordinal) = ordinal.checked_sub(whole_days)
1004 && new_ordinal >= 1
1005 && new_ordinal <= days_in_year
1006 {
1007 return Some(unsafe { Self::from_parts(year, is_leap_year, new_ordinal as u16) });
1009 }
1010
1011 let julian_day = match self.to_julian_day().checked_sub(whole_days) {
Some(value) => value,
None => { crate::hint::cold_path(); return None; }
}const_try_opt!(self.to_julian_day().checked_sub(whole_days));
1012 if let Ok(date) = Self::from_julian_day(julian_day) {
1013 Some(date)
1014 } else {
1015 None
1016 }
1017 }
1018
1019 #[inline]
1022 pub(crate) const fn checked_next_occurrence(self, weekday: Weekday) -> Option<Self> {
1023 let day_diff = match weekday as i8 - self.weekday() as i8 {
1024 1 | -6 => 1,
1025 2 | -5 => 2,
1026 3 | -4 => 3,
1027 4 | -3 => 4,
1028 5 | -2 => 5,
1029 6 | -1 => 6,
1030 val => {
1031 if true {
if !(val == 0) { ::core::panicking::panic("assertion failed: val == 0") };
};debug_assert!(val == 0);
1032 7
1033 }
1034 };
1035
1036 self.checked_add(Duration::days(day_diff))
1037 }
1038
1039 #[inline]
1042 pub(crate) const fn checked_prev_occurrence(self, weekday: Weekday) -> Option<Self> {
1043 let day_diff = match weekday as i8 - self.weekday() as i8 {
1044 1 | -6 => 6,
1045 2 | -5 => 5,
1046 3 | -4 => 4,
1047 4 | -3 => 3,
1048 5 | -2 => 2,
1049 6 | -1 => 1,
1050 val => {
1051 if true {
if !(val == 0) { ::core::panicking::panic("assertion failed: val == 0") };
};debug_assert!(val == 0);
1052 7
1053 }
1054 };
1055
1056 self.checked_sub(Duration::days(day_diff))
1057 }
1058
1059 #[inline]
1062 pub(crate) const fn checked_nth_next_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> {
1063 if n == 0 {
1064 return None;
1065 }
1066
1067 match self.checked_next_occurrence(weekday) {
Some(value) => value,
None => { crate::hint::cold_path(); return None; }
}const_try_opt!(self.checked_next_occurrence(weekday))
1068 .checked_add(Duration::weeks(n as i64 - 1))
1069 }
1070
1071 #[inline]
1074 pub(crate) const fn checked_nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> {
1075 if n == 0 {
1076 return None;
1077 }
1078
1079 match self.checked_prev_occurrence(weekday) {
Some(value) => value,
None => { crate::hint::cold_path(); return None; }
}const_try_opt!(self.checked_prev_occurrence(weekday))
1080 .checked_sub(Duration::weeks(n as i64 - 1))
1081 }
1082
1083 #[inline]
1113 pub const fn saturating_add(self, duration: Duration) -> Self {
1114 if let Some(datetime) = self.checked_add(duration) {
1115 datetime
1116 } else if duration.is_negative() {
1117 Self::MIN
1118 } else {
1119 if true {
if !duration.is_positive() {
::core::panicking::panic("assertion failed: duration.is_positive()")
};
};debug_assert!(duration.is_positive());
1120 Self::MAX
1121 }
1122 }
1123
1124 #[inline]
1154 pub const fn saturating_sub(self, duration: Duration) -> Self {
1155 if let Some(datetime) = self.checked_sub(duration) {
1156 datetime
1157 } else if duration.is_negative() {
1158 Self::MAX
1159 } else {
1160 if true {
if !duration.is_positive() {
::core::panicking::panic("assertion failed: duration.is_positive()")
};
};debug_assert!(duration.is_positive());
1161 Self::MIN
1162 }
1163 }
1164
1165 #[inline]
1177 #[must_use = "This method does not mutate the original `Date`."]
1178 pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
1179 match <Year>::new(year) {
Some(val) => val,
None => {
crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional("year"));
}
};ensure_ranged!(Year: year);
1180
1181 let new_is_leap_year = range_validated::is_leap_year(year);
1182 let ordinal = self.ordinal();
1183
1184 if ordinal <= 59 {
1186 return Ok(unsafe { Self::from_parts(year, new_is_leap_year, ordinal) });
1188 }
1189
1190 match (self.is_in_leap_year(), new_is_leap_year) {
1191 (false, false) | (true, true) => {
1192 Ok(Self {
1193 value: unsafe {
1196 NonZero::new_unchecked((year << 10) | (self.value.get() & 0x3FF))
1197 },
1198 })
1199 }
1200 (true, false) if ordinal == 60 => Err(error::ComponentRange::conditional("day")),
1202 (false, true) => Ok(unsafe { Self::from_parts(year, true, ordinal + 1) }),
1206 (true, false) => Ok(unsafe { Self::from_parts(year, false, ordinal - 1) }),
1210 }
1211 }
1212
1213 #[inline]
1227 #[must_use = "This method does not mutate the original `Date`."]
1228 pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
1229 const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [
1231 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
1232 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
1233 ];
1234
1235 let (year, ordinal) = self.to_ordinal_date();
1236 let mut ordinal = ordinal as u32;
1237 let is_leap_year = self.is_in_leap_year();
1238 let jan_feb_len = 59 + is_leap_year as u32;
1239
1240 if ordinal > jan_feb_len {
1241 ordinal -= jan_feb_len;
1242 }
1243 let current_month = (ordinal * 268 + 8031) >> 13;
1244 let days_in_preceding_months = (current_month * 3917 - 3866) >> 7;
1245 let day = (ordinal - days_in_preceding_months) as u8;
1246
1247 match day {
1248 1..=28 => {}
1249 29..=31 if day <= days_in_month_leap(month as u8, is_leap_year) => hint::cold_path(),
1250 _ => {
1251 hint::cold_path();
1252 return Err(error::ComponentRange::conditional("day"));
1253 }
1254 }
1255
1256 Ok(unsafe {
1258 Self::from_parts(
1259 year,
1260 is_leap_year,
1261 DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year as usize][month as usize - 1] + day as u16,
1262 )
1263 })
1264 }
1265
1266 #[inline]
1275 #[must_use = "This method does not mutate the original `Date`."]
1276 pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
1277 let is_leap_year = self.is_in_leap_year();
1278 match day {
1279 1..=28 => {}
1280 29..=31 if day <= days_in_month_leap(self.month() as u8, is_leap_year) => {
1281 hint::cold_path()
1282 }
1283 _ => {
1284 hint::cold_path();
1285 return Err(error::ComponentRange::conditional("day"));
1286 }
1287 }
1288
1289 Ok(unsafe {
1291 Self::from_parts(
1292 self.year(),
1293 is_leap_year,
1294 (self.ordinal().cast_signed() - self.day() as i16 + day as i16).cast_unsigned(),
1295 )
1296 })
1297 }
1298
1299 #[inline]
1308 #[must_use = "This method does not mutate the original `Date`."]
1309 pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
1310 let is_leap_year = self.is_in_leap_year();
1311 match ordinal {
1312 1..=365 => {}
1313 366 if is_leap_year => hint::cold_path(),
1314 _ => {
1315 hint::cold_path();
1316 return Err(error::ComponentRange::conditional("ordinal"));
1317 }
1318 }
1319
1320 Ok(unsafe { Self::from_parts(self.year(), is_leap_year, ordinal) })
1322 }
1323}
1324
1325impl Date {
1327 #[inline]
1335 pub const fn midnight(self) -> PrimitiveDateTime {
1336 PrimitiveDateTime::new(self, Time::MIDNIGHT)
1337 }
1338
1339 #[inline]
1349 pub const fn with_time(self, time: Time) -> PrimitiveDateTime {
1350 PrimitiveDateTime::new(self, time)
1351 }
1352
1353 #[inline]
1361 pub const fn with_hms(
1362 self,
1363 hour: u8,
1364 minute: u8,
1365 second: u8,
1366 ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1367 Ok(PrimitiveDateTime::new(
1368 self,
1369 match Time::from_hms(hour, minute, second) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(Time::from_hms(hour, minute, second)),
1370 ))
1371 }
1372
1373 #[inline]
1381 pub const fn with_hms_milli(
1382 self,
1383 hour: u8,
1384 minute: u8,
1385 second: u8,
1386 millisecond: u16,
1387 ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1388 Ok(PrimitiveDateTime::new(
1389 self,
1390 match Time::from_hms_milli(hour, minute, second, millisecond) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(Time::from_hms_milli(hour, minute, second, millisecond)),
1391 ))
1392 }
1393
1394 #[inline]
1402 pub const fn with_hms_micro(
1403 self,
1404 hour: u8,
1405 minute: u8,
1406 second: u8,
1407 microsecond: u32,
1408 ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1409 Ok(PrimitiveDateTime::new(
1410 self,
1411 match Time::from_hms_micro(hour, minute, second, microsecond) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(Time::from_hms_micro(hour, minute, second, microsecond)),
1412 ))
1413 }
1414
1415 #[inline]
1423 pub const fn with_hms_nano(
1424 self,
1425 hour: u8,
1426 minute: u8,
1427 second: u8,
1428 nanosecond: u32,
1429 ) -> Result<PrimitiveDateTime, error::ComponentRange> {
1430 Ok(PrimitiveDateTime::new(
1431 self,
1432 match Time::from_hms_nano(hour, minute, second, nanosecond) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(Time::from_hms_nano(hour, minute, second, nanosecond)),
1433 ))
1434 }
1435}
1436
1437#[cfg(feature = "formatting")]
1438impl Date {
1439 #[inline]
1441 pub fn format_into(
1442 self,
1443 output: &mut (impl io::Write + ?Sized),
1444 format: &(impl Formattable + ?Sized),
1445 ) -> Result<usize, error::Format> {
1446 format.format_into(output, &self, &mut Default::default())
1447 }
1448
1449 #[inline]
1459 pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
1460 format.format(&self, &mut Default::default())
1461 }
1462}
1463
1464#[cfg(feature = "parsing")]
1465impl Date {
1466 #[inline]
1477 pub fn parse(
1478 input: &str,
1479 description: &(impl Parsable + ?Sized),
1480 ) -> Result<Self, error::Parse> {
1481 description.parse_date(input.as_bytes())
1482 }
1483}
1484
1485mod private {
1486 #[non_exhaustive]
1488 #[derive(#[automatically_derived]
impl ::core::fmt::Debug for DateMetadata {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "DateMetadata")
}
}Debug)]
1489 pub struct DateMetadata;
1490}
1491use private::DateMetadata;
1492
1493impl SmartDisplay for Date {
1497 type Metadata = DateMetadata;
1498
1499 #[inline]
1500 fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
1501 use crate::ext::DigitCount as _;
1502
1503 let year_sign_width =
1504 if self.year() < 0 || (falsecfg!(feature = "large-dates") && self.year() >= 10_000) {
1505 1
1506 } else {
1507 0
1508 };
1509 let year_width = self.year().unsigned_abs().num_digits().clamp(4, 6);
1510 let formatted_width = year_sign_width + year_width + 6; Metadata::new(formatted_width as usize, self, DateMetadata)
1513 }
1514
1515 #[inline]
1516 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1517 fmt::Display::fmt(self, f)
1518 }
1519}
1520
1521impl Date {
1522 pub(crate) const DISPLAY_BUFFER_SIZE: usize = 13;
1525
1526 #[inline]
1528 pub(crate) fn fmt_into_buffer(
1529 self,
1530 buf: &mut [MaybeUninit<u8>; Self::DISPLAY_BUFFER_SIZE],
1531 ) -> usize {
1532 let mut idx = 0;
1533 let (year, month, day) = self.to_calendar_date();
1534
1535 let neg = year.is_negative() as u8;
1538 let pos = (falsecfg!(feature = "large-dates") && year - 10_000 >= 0) as u8;
1539 let sign = b'+' + 2 * neg; buf[idx] = MaybeUninit::new(sign);
1543 idx += (neg | pos) as usize;
1544
1545 let [first_two, second_two, third_two] =
1547 four_to_six_digits(unsafe { ru32::new_unchecked(year.unsigned_abs()) });
1548 unsafe {
1554 first_two
1555 .as_ptr()
1556 .copy_to_nonoverlapping(buf.as_mut_ptr().add(idx).cast(), first_two.len());
1557 }
1558 idx += first_two.len();
1559 unsafe {
1561 second_two
1562 .as_ptr()
1563 .copy_to_nonoverlapping(buf.as_mut_ptr().add(idx).cast(), 2);
1564 }
1565 idx += 2;
1566 unsafe {
1568 third_two
1569 .as_ptr()
1570 .copy_to_nonoverlapping(buf.as_mut_ptr().add(idx).cast(), 2);
1571 }
1572 idx += 2;
1573
1574 buf[idx] = MaybeUninit::new(b'-');
1575 idx += 1;
1576
1577 unsafe {
1579 two_digits_zero_padded(ru8::new_unchecked(u8::from(month)))
1580 .as_ptr()
1581 .copy_to_nonoverlapping(buf.as_mut_ptr().add(idx).cast(), 2);
1582 }
1583 idx += 2;
1584
1585 buf[idx] = MaybeUninit::new(b'-');
1586 idx += 1;
1587
1588 unsafe {
1590 two_digits_zero_padded(ru8::new_unchecked(day))
1591 .as_ptr()
1592 .copy_to_nonoverlapping(buf.as_mut_ptr().add(idx).cast(), 2);
1593 }
1594 idx += 2;
1595
1596 idx
1597 }
1598}
1599
1600impl fmt::Display for Date {
1601 #[inline]
1602 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1603 let mut buf = [MaybeUninit::uninit(); 13];
1604 let len = self.fmt_into_buffer(&mut buf);
1605 let s = unsafe { str_from_raw_parts((&raw const buf).cast(), len) };
1607 f.pad(s)
1608 }
1609}
1610
1611impl fmt::Debug for Date {
1612 #[inline]
1613 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1614 fmt::Display::fmt(self, f)
1615 }
1616}
1617
1618impl Add<Duration> for Date {
1619 type Output = Self;
1620
1621 #[inline]
1625 #[track_caller]
1626 fn add(self, duration: Duration) -> Self::Output {
1627 self.checked_add(duration)
1628 .expect("overflow adding duration to date")
1629 }
1630}
1631
1632impl Add<StdDuration> for Date {
1633 type Output = Self;
1634
1635 #[inline]
1639 #[track_caller]
1640 fn add(self, duration: StdDuration) -> Self::Output {
1641 self.checked_add_std(duration)
1642 .expect("overflow adding duration to date")
1643 }
1644}
1645
1646impl AddAssign<Duration> for Date {
1647 #[inline]
1651 #[track_caller]
1652 fn add_assign(&mut self, rhs: Duration) {
1653 *self = *self + rhs;
1654 }
1655}
1656
1657impl AddAssign<StdDuration> for Date {
1658 #[inline]
1662 #[track_caller]
1663 fn add_assign(&mut self, rhs: StdDuration) {
1664 *self = *self + rhs;
1665 }
1666}
1667
1668impl Sub<Duration> for Date {
1669 type Output = Self;
1670
1671 #[inline]
1675 #[track_caller]
1676 fn sub(self, duration: Duration) -> Self::Output {
1677 self.checked_sub(duration)
1678 .expect("overflow subtracting duration from date")
1679 }
1680}
1681
1682impl Sub<StdDuration> for Date {
1683 type Output = Self;
1684
1685 #[inline]
1689 #[track_caller]
1690 fn sub(self, duration: StdDuration) -> Self::Output {
1691 self.checked_sub_std(duration)
1692 .expect("overflow subtracting duration from date")
1693 }
1694}
1695
1696impl SubAssign<Duration> for Date {
1697 #[inline]
1701 #[track_caller]
1702 fn sub_assign(&mut self, rhs: Duration) {
1703 *self = *self - rhs;
1704 }
1705}
1706
1707impl SubAssign<StdDuration> for Date {
1708 #[inline]
1712 #[track_caller]
1713 fn sub_assign(&mut self, rhs: StdDuration) {
1714 *self = *self - rhs;
1715 }
1716}
1717
1718impl Sub for Date {
1719 type Output = Duration;
1720
1721 #[inline]
1722 fn sub(self, other: Self) -> Self::Output {
1723 Duration::days((self.to_julian_day() - other.to_julian_day()).widen())
1724 }
1725}