1//! Date and time types unconcerned with timezones.
2//!
3//! They are primarily building blocks for other types
4//! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)),
5//! but can be also used for the simpler date and time handling.
67use core::hash::{Hash, Hasher};
8use core::ops::RangeInclusive;
910use crate::Weekday;
11use crate::expect;
1213pub(crate) mod date;
14pub(crate) mod datetime;
15mod internals;
16pub(crate) mod isoweek;
17pub(crate) mod time;
1819#[allow(deprecated)]
20pub use self::date::{MAX_DATE, MIN_DATE};
21pub use self::date::{NaiveDate, NaiveDateDaysIterator, NaiveDateWeeksIterator};
22#[allow(deprecated)]
23pub use self::datetime::{MAX_DATETIME, MIN_DATETIME, NaiveDateTime};
24pub use self::isoweek::IsoWeek;
25pub use self::time::NaiveTime;
2627#[cfg(feature = "__internal_bench")]
28#[doc(hidden)]
29pub use self::internals::YearFlags as __BenchYearFlags;
3031/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
32/// day of the week.
33#[derive(#[automatically_derived]
impl ::core::clone::Clone for NaiveWeek {
#[inline]
fn clone(&self) -> NaiveWeek {
let _: ::core::clone::AssertParamIsClone<NaiveDate>;
let _: ::core::clone::AssertParamIsClone<Weekday>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for NaiveWeek { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for NaiveWeek {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "NaiveWeek",
"date", &self.date, "start", &&self.start)
}
}Debug, #[automatically_derived]
impl ::core::cmp::Eq for NaiveWeek {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<NaiveDate>;
let _: ::core::cmp::AssertParamIsEq<Weekday>;
}
}Eq)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub struct NaiveWeek {
36 date: NaiveDate,
37 start: Weekday,
38}
3940impl NaiveWeek {
41/// Create a new `NaiveWeek`
42pub(crate) const fn new(date: NaiveDate, start: Weekday) -> Self {
43Self { date, start }
44 }
4546/// Returns a date representing the first day of the week.
47 ///
48 /// # Panics
49 ///
50 /// Panics if the first day of the week happens to fall just out of range of `NaiveDate`
51 /// (more than ca. 262,000 years away from common era).
52 ///
53 /// # Examples
54 ///
55 /// ```
56 /// use chrono::{NaiveDate, Weekday};
57 ///
58 /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
59 /// let week = date.week(Weekday::Mon);
60 /// assert!(week.first_day() <= date);
61 /// ```
62#[inline]
63 #[must_use]
64pub const fn first_day(&self) -> NaiveDate {
65expect(self.checked_first_day(), "first weekday out of range for `NaiveDate`")
66 }
6768/// Returns a date representing the first day of the week or
69 /// `None` if the date is out of `NaiveDate`'s range
70 /// (more than ca. 262,000 years away from common era).
71 ///
72 /// # Examples
73 ///
74 /// ```
75 /// use chrono::{NaiveDate, Weekday};
76 ///
77 /// let date = NaiveDate::MIN;
78 /// let week = date.week(Weekday::Mon);
79 /// if let Some(first_day) = week.checked_first_day() {
80 /// assert!(first_day == date);
81 /// } else {
82 /// // error handling code
83 /// return;
84 /// };
85 /// ```
86#[inline]
87 #[must_use]
88pub const fn checked_first_day(&self) -> Option<NaiveDate> {
89let start = self.start.num_days_from_monday() as i32;
90let ref_day = self.date.weekday().num_days_from_monday() as i32;
91// Calculate the number of days to subtract from `self.date`.
92 // Do not construct an intermediate date beyond `self.date`, because that may be out of
93 // range if `date` is close to `NaiveDate::MAX`.
94let days = start - ref_day - if start > ref_day { 7 } else { 0 };
95self.date.add_days(days)
96 }
9798/// Returns a date representing the last day of the week.
99 ///
100 /// # Panics
101 ///
102 /// Panics if the last day of the week happens to fall just out of range of `NaiveDate`
103 /// (more than ca. 262,000 years away from common era).
104 ///
105 /// # Examples
106 ///
107 /// ```
108 /// use chrono::{NaiveDate, Weekday};
109 ///
110 /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
111 /// let week = date.week(Weekday::Mon);
112 /// assert!(week.last_day() >= date);
113 /// ```
114#[inline]
115 #[must_use]
116pub const fn last_day(&self) -> NaiveDate {
117expect(self.checked_last_day(), "last weekday out of range for `NaiveDate`")
118 }
119120/// Returns a date representing the last day of the week or
121 /// `None` if the date is out of `NaiveDate`'s range
122 /// (more than ca. 262,000 years away from common era).
123 ///
124 /// # Examples
125 ///
126 /// ```
127 /// use chrono::{NaiveDate, Weekday};
128 ///
129 /// let date = NaiveDate::MAX;
130 /// let week = date.week(Weekday::Mon);
131 /// if let Some(last_day) = week.checked_last_day() {
132 /// assert!(last_day == date);
133 /// } else {
134 /// // error handling code
135 /// return;
136 /// };
137 /// ```
138#[inline]
139 #[must_use]
140pub const fn checked_last_day(&self) -> Option<NaiveDate> {
141let end = self.start.pred().num_days_from_monday() as i32;
142let ref_day = self.date.weekday().num_days_from_monday() as i32;
143// Calculate the number of days to add to `self.date`.
144 // Do not construct an intermediate date before `self.date` (like with `first_day()`),
145 // because that may be out of range if `date` is close to `NaiveDate::MIN`.
146let days = end - ref_day + if end < ref_day { 7 } else { 0 };
147self.date.add_days(days)
148 }
149150/// Returns a [`RangeInclusive<T>`] representing the whole week bounded by
151 /// [first_day](NaiveWeek::first_day) and [last_day](NaiveWeek::last_day) functions.
152 ///
153 /// # Panics
154 ///
155 /// Panics if the either the first or last day of the week happens to fall just out of range of
156 /// `NaiveDate` (more than ca. 262,000 years away from common era).
157 ///
158 /// # Examples
159 ///
160 /// ```
161 /// use chrono::{NaiveDate, Weekday};
162 ///
163 /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
164 /// let week = date.week(Weekday::Mon);
165 /// let days = week.days();
166 /// assert!(days.contains(&date));
167 /// ```
168#[inline]
169 #[must_use]
170pub const fn days(&self) -> RangeInclusive<NaiveDate> {
171// `expect` doesn't work because `RangeInclusive` is not `Copy`
172match self.checked_days() {
173Some(val) => val,
174None => {
::core::panicking::panic_display(&"first or last weekday is out of range for `NaiveDate`");
}panic!("{}", "first or last weekday is out of range for `NaiveDate`"),
175 }
176 }
177178/// Returns an [`Option<RangeInclusive<T>>`] representing the whole week bounded by
179 /// [checked_first_day](NaiveWeek::checked_first_day) and
180 /// [checked_last_day](NaiveWeek::checked_last_day) functions.
181 ///
182 /// Returns `None` if either of the boundaries are out of `NaiveDate`'s range
183 /// (more than ca. 262,000 years away from common era).
184 ///
185 ///
186 /// # Examples
187 ///
188 /// ```
189 /// use chrono::{NaiveDate, Weekday};
190 ///
191 /// let date = NaiveDate::MAX;
192 /// let week = date.week(Weekday::Mon);
193 /// let _days = match week.checked_days() {
194 /// Some(d) => d,
195 /// None => {
196 /// // error handling code
197 /// return;
198 /// }
199 /// };
200 /// ```
201#[inline]
202 #[must_use]
203pub const fn checked_days(&self) -> Option<RangeInclusive<NaiveDate>> {
204match (self.checked_first_day(), self.checked_last_day()) {
205 (Some(first), Some(last)) => Some(first..=last),
206 (_, _) => None,
207 }
208 }
209}
210211impl PartialEqfor NaiveWeek {
212fn eq(&self, other: &Self) -> bool {
213self.first_day() == other.first_day()
214 }
215}
216217impl Hashfor NaiveWeek {
218fn hash<H: Hasher>(&self, state: &mut H) {
219self.first_day().hash(state);
220 }
221}
222223/// A duration in calendar days.
224///
225/// This is useful because when using `TimeDelta` it is possible that adding `TimeDelta::days(1)`
226/// doesn't increment the day value as expected due to it being a fixed number of seconds. This
227/// difference applies only when dealing with `DateTime<TimeZone>` data types and in other cases
228/// `TimeDelta::days(n)` and `Days::new(n)` are equivalent.
229#[derive(#[automatically_derived]
impl ::core::clone::Clone for Days {
#[inline]
fn clone(&self) -> Days {
let _: ::core::clone::AssertParamIsClone<u64>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Days { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for Days {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Days", &&self.0)
}
}Debug, #[automatically_derived]
impl ::core::cmp::Eq for Days {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u64>;
}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for Days {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, #[automatically_derived]
impl ::core::cmp::PartialEq for Days {
#[inline]
fn eq(&self, other: &Days) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::PartialOrd for Days {
#[inline]
fn partial_cmp(&self, other: &Days)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
}
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for Days {
#[inline]
fn cmp(&self, other: &Days) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}Ord)]
230#[cfg_attr(feature = "defmt", derive(defmt::Format))]
231pub struct Days(pub(crate) u64);
232233impl Days {
234/// Construct a new `Days` from a number of days
235pub const fn new(num: u64) -> Self {
236Self(num)
237 }
238}
239240/// Serialization/Deserialization of `NaiveDateTime` in alternate formats
241///
242/// The various modules in here are intended to be used with serde's [`with` annotation] to
243/// serialize as something other than the default ISO 8601 format.
244///
245/// [`with` annotation]: https://serde.rs/field-attrs.html#with
246#[cfg(feature = "serde")]
247pub mod serde {
248pub use super::datetime::serde::*;
249}
250251#[cfg(test)]
252mod test {
253use crate::{NaiveDate, NaiveWeek, Weekday};
254use std::hash::{DefaultHasher, Hash, Hasher};
255#[test]
256fn test_naiveweek() {
257let date = NaiveDate::from_ymd_opt(2022, 5, 18).unwrap();
258let asserts = [
259 (Weekday::Mon, "Mon 2022-05-16", "Sun 2022-05-22"),
260 (Weekday::Tue, "Tue 2022-05-17", "Mon 2022-05-23"),
261 (Weekday::Wed, "Wed 2022-05-18", "Tue 2022-05-24"),
262 (Weekday::Thu, "Thu 2022-05-12", "Wed 2022-05-18"),
263 (Weekday::Fri, "Fri 2022-05-13", "Thu 2022-05-19"),
264 (Weekday::Sat, "Sat 2022-05-14", "Fri 2022-05-20"),
265 (Weekday::Sun, "Sun 2022-05-15", "Sat 2022-05-21"),
266 ];
267for (start, first_day, last_day) in asserts {
268let week = date.week(start);
269let days = week.days();
270assert_eq!(Ok(week.first_day()), NaiveDate::parse_from_str(first_day, "%a %Y-%m-%d"));
271assert_eq!(Ok(week.last_day()), NaiveDate::parse_from_str(last_day, "%a %Y-%m-%d"));
272assert!(days.contains(&date));
273 }
274 }
275276#[test]
277fn test_naiveweek_min_max() {
278let date_max = NaiveDate::MAX;
279assert!(date_max.week(Weekday::Mon).first_day() <= date_max);
280let date_min = NaiveDate::MIN;
281assert!(date_min.week(Weekday::Mon).last_day() >= date_min);
282 }
283284#[test]
285fn test_naiveweek_checked_no_panic() {
286let date_max = NaiveDate::MAX;
287if let Some(last) = date_max.week(Weekday::Mon).checked_last_day() {
288assert!(last == date_max);
289 }
290let date_min = NaiveDate::MIN;
291if let Some(first) = date_min.week(Weekday::Mon).checked_first_day() {
292assert!(first == date_min);
293 }
294let _ = date_min.week(Weekday::Mon).checked_days();
295let _ = date_max.week(Weekday::Mon).checked_days();
296 }
297298#[test]
299fn test_naiveweek_eq() {
300let a =
301 NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Mon };
302let b =
303 NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 4).unwrap(), start: Weekday::Mon };
304assert_eq!(a, b);
305306let c =
307 NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Sun };
308assert_ne!(a, c);
309assert_ne!(b, c);
310 }
311312#[test]
313fn test_naiveweek_hash() {
314let a =
315 NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Mon };
316let b =
317 NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 4).unwrap(), start: Weekday::Mon };
318let c =
319 NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Sun };
320321let mut hasher = DefaultHasher::default();
322 a.hash(&mut hasher);
323let a_hash = hasher.finish();
324325 hasher = DefaultHasher::default();
326 b.hash(&mut hasher);
327let b_hash = hasher.finish();
328329 hasher = DefaultHasher::default();
330 c.hash(&mut hasher);
331let c_hash = hasher.finish();
332333assert_eq!(a_hash, b_hash);
334assert_ne!(b_hash, c_hash);
335assert_ne!(a_hash, c_hash);
336 }
337}