/rust/registry/src/index.crates.io-1949cf8c6b5b557f/chrono-0.4.44/src/naive/mod.rs
Line | Count | Source |
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. |
6 | | |
7 | | use core::hash::{Hash, Hasher}; |
8 | | use core::ops::RangeInclusive; |
9 | | |
10 | | use crate::Weekday; |
11 | | use crate::expect; |
12 | | |
13 | | pub(crate) mod date; |
14 | | pub(crate) mod datetime; |
15 | | mod internals; |
16 | | pub(crate) mod isoweek; |
17 | | pub(crate) mod time; |
18 | | |
19 | | #[allow(deprecated)] |
20 | | pub use self::date::{MAX_DATE, MIN_DATE}; |
21 | | pub use self::date::{NaiveDate, NaiveDateDaysIterator, NaiveDateWeeksIterator}; |
22 | | #[allow(deprecated)] |
23 | | pub use self::datetime::{MAX_DATETIME, MIN_DATETIME, NaiveDateTime}; |
24 | | pub use self::isoweek::IsoWeek; |
25 | | pub use self::time::NaiveTime; |
26 | | |
27 | | #[cfg(feature = "__internal_bench")] |
28 | | #[doc(hidden)] |
29 | | pub use self::internals::YearFlags as __BenchYearFlags; |
30 | | |
31 | | /// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first |
32 | | /// day of the week. |
33 | | #[derive(Clone, Copy, Debug, Eq)] |
34 | | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
35 | | pub struct NaiveWeek { |
36 | | date: NaiveDate, |
37 | | start: Weekday, |
38 | | } |
39 | | |
40 | | impl NaiveWeek { |
41 | | /// Create a new `NaiveWeek` |
42 | 0 | pub(crate) const fn new(date: NaiveDate, start: Weekday) -> Self { |
43 | 0 | Self { date, start } |
44 | 0 | } |
45 | | |
46 | | /// 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] |
64 | | #[track_caller] |
65 | 0 | pub const fn first_day(&self) -> NaiveDate { |
66 | 0 | expect(self.checked_first_day(), "first weekday out of range for `NaiveDate`") |
67 | 0 | } |
68 | | |
69 | | /// Returns a date representing the first day of the week or |
70 | | /// `None` if the date is out of `NaiveDate`'s range |
71 | | /// (more than ca. 262,000 years away from common era). |
72 | | /// |
73 | | /// # Examples |
74 | | /// |
75 | | /// ``` |
76 | | /// use chrono::{NaiveDate, Weekday}; |
77 | | /// |
78 | | /// let date = NaiveDate::MIN; |
79 | | /// let week = date.week(Weekday::Mon); |
80 | | /// if let Some(first_day) = week.checked_first_day() { |
81 | | /// assert!(first_day == date); |
82 | | /// } else { |
83 | | /// // error handling code |
84 | | /// return; |
85 | | /// }; |
86 | | /// ``` |
87 | | #[inline] |
88 | | #[must_use] |
89 | 0 | pub const fn checked_first_day(&self) -> Option<NaiveDate> { |
90 | 0 | let start = self.start.num_days_from_monday() as i32; |
91 | 0 | let ref_day = self.date.weekday().num_days_from_monday() as i32; |
92 | | // Calculate the number of days to subtract from `self.date`. |
93 | | // Do not construct an intermediate date beyond `self.date`, because that may be out of |
94 | | // range if `date` is close to `NaiveDate::MAX`. |
95 | 0 | let days = start - ref_day - if start > ref_day { 7 } else { 0 }; |
96 | 0 | self.date.add_days(days) |
97 | 0 | } |
98 | | |
99 | | /// Returns a date representing the last day of the week. |
100 | | /// |
101 | | /// # Panics |
102 | | /// |
103 | | /// Panics if the last day of the week happens to fall just out of range of `NaiveDate` |
104 | | /// (more than ca. 262,000 years away from common era). |
105 | | /// |
106 | | /// # Examples |
107 | | /// |
108 | | /// ``` |
109 | | /// use chrono::{NaiveDate, Weekday}; |
110 | | /// |
111 | | /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap(); |
112 | | /// let week = date.week(Weekday::Mon); |
113 | | /// assert!(week.last_day() >= date); |
114 | | /// ``` |
115 | | #[inline] |
116 | | #[must_use] |
117 | | #[track_caller] |
118 | 0 | pub const fn last_day(&self) -> NaiveDate { |
119 | 0 | expect(self.checked_last_day(), "last weekday out of range for `NaiveDate`") |
120 | 0 | } |
121 | | |
122 | | /// Returns a date representing the last day of the week or |
123 | | /// `None` if the date is out of `NaiveDate`'s range |
124 | | /// (more than ca. 262,000 years away from common era). |
125 | | /// |
126 | | /// # Examples |
127 | | /// |
128 | | /// ``` |
129 | | /// use chrono::{NaiveDate, Weekday}; |
130 | | /// |
131 | | /// let date = NaiveDate::MAX; |
132 | | /// let week = date.week(Weekday::Mon); |
133 | | /// if let Some(last_day) = week.checked_last_day() { |
134 | | /// assert!(last_day == date); |
135 | | /// } else { |
136 | | /// // error handling code |
137 | | /// return; |
138 | | /// }; |
139 | | /// ``` |
140 | | #[inline] |
141 | | #[must_use] |
142 | 0 | pub const fn checked_last_day(&self) -> Option<NaiveDate> { |
143 | 0 | let end = self.start.pred().num_days_from_monday() as i32; |
144 | 0 | let ref_day = self.date.weekday().num_days_from_monday() as i32; |
145 | | // Calculate the number of days to add to `self.date`. |
146 | | // Do not construct an intermediate date before `self.date` (like with `first_day()`), |
147 | | // because that may be out of range if `date` is close to `NaiveDate::MIN`. |
148 | 0 | let days = end - ref_day + if end < ref_day { 7 } else { 0 }; |
149 | 0 | self.date.add_days(days) |
150 | 0 | } |
151 | | |
152 | | /// Returns a [`RangeInclusive<T>`] representing the whole week bounded by |
153 | | /// [first_day](NaiveWeek::first_day) and [last_day](NaiveWeek::last_day) functions. |
154 | | /// |
155 | | /// # Panics |
156 | | /// |
157 | | /// Panics if the either the first or last day of the week happens to fall just out of range of |
158 | | /// `NaiveDate` (more than ca. 262,000 years away from common era). |
159 | | /// |
160 | | /// # Examples |
161 | | /// |
162 | | /// ``` |
163 | | /// use chrono::{NaiveDate, Weekday}; |
164 | | /// |
165 | | /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap(); |
166 | | /// let week = date.week(Weekday::Mon); |
167 | | /// let days = week.days(); |
168 | | /// assert!(days.contains(&date)); |
169 | | /// ``` |
170 | | #[inline] |
171 | | #[must_use] |
172 | | #[track_caller] |
173 | 0 | pub const fn days(&self) -> RangeInclusive<NaiveDate> { |
174 | | // `expect` doesn't work because `RangeInclusive` is not `Copy` |
175 | 0 | match self.checked_days() { |
176 | 0 | Some(val) => val, |
177 | 0 | None => panic!("{}", "first or last weekday is out of range for `NaiveDate`"), |
178 | | } |
179 | 0 | } |
180 | | |
181 | | /// Returns an [`Option<RangeInclusive<T>>`] representing the whole week bounded by |
182 | | /// [checked_first_day](NaiveWeek::checked_first_day) and |
183 | | /// [checked_last_day](NaiveWeek::checked_last_day) functions. |
184 | | /// |
185 | | /// Returns `None` if either of the boundaries are out of `NaiveDate`'s range |
186 | | /// (more than ca. 262,000 years away from common era). |
187 | | /// |
188 | | /// |
189 | | /// # Examples |
190 | | /// |
191 | | /// ``` |
192 | | /// use chrono::{NaiveDate, Weekday}; |
193 | | /// |
194 | | /// let date = NaiveDate::MAX; |
195 | | /// let week = date.week(Weekday::Mon); |
196 | | /// let _days = match week.checked_days() { |
197 | | /// Some(d) => d, |
198 | | /// None => { |
199 | | /// // error handling code |
200 | | /// return; |
201 | | /// } |
202 | | /// }; |
203 | | /// ``` |
204 | | #[inline] |
205 | | #[must_use] |
206 | 0 | pub const fn checked_days(&self) -> Option<RangeInclusive<NaiveDate>> { |
207 | 0 | match (self.checked_first_day(), self.checked_last_day()) { |
208 | 0 | (Some(first), Some(last)) => Some(first..=last), |
209 | 0 | (_, _) => None, |
210 | | } |
211 | 0 | } |
212 | | } |
213 | | |
214 | | impl PartialEq for NaiveWeek { |
215 | 0 | fn eq(&self, other: &Self) -> bool { |
216 | 0 | self.first_day() == other.first_day() |
217 | 0 | } |
218 | | } |
219 | | |
220 | | impl Hash for NaiveWeek { |
221 | 0 | fn hash<H: Hasher>(&self, state: &mut H) { |
222 | 0 | self.first_day().hash(state); |
223 | 0 | } |
224 | | } |
225 | | |
226 | | /// A duration in calendar days. |
227 | | /// |
228 | | /// This is useful because when using `TimeDelta` it is possible that adding `TimeDelta::days(1)` |
229 | | /// doesn't increment the day value as expected due to it being a fixed number of seconds. This |
230 | | /// difference applies only when dealing with `DateTime<TimeZone>` data types and in other cases |
231 | | /// `TimeDelta::days(n)` and `Days::new(n)` are equivalent. |
232 | | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] |
233 | | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
234 | | pub struct Days(pub(crate) u64); |
235 | | |
236 | | impl Days { |
237 | | /// Construct a new `Days` from a number of days |
238 | 0 | pub const fn new(num: u64) -> Self { |
239 | 0 | Self(num) |
240 | 0 | } |
241 | | } |
242 | | |
243 | | /// Serialization/Deserialization of `NaiveDateTime` in alternate formats |
244 | | /// |
245 | | /// The various modules in here are intended to be used with serde's [`with` annotation] to |
246 | | /// serialize as something other than the default ISO 8601 format. |
247 | | /// |
248 | | /// [`with` annotation]: https://serde.rs/field-attrs.html#with |
249 | | #[cfg(feature = "serde")] |
250 | | pub mod serde { |
251 | | pub use super::datetime::serde::*; |
252 | | } |
253 | | |
254 | | #[cfg(test)] |
255 | | mod test { |
256 | | use crate::{NaiveDate, NaiveWeek, Weekday}; |
257 | | use std::hash::{DefaultHasher, Hash, Hasher}; |
258 | | #[test] |
259 | | fn test_naiveweek() { |
260 | | let date = NaiveDate::from_ymd_opt(2022, 5, 18).unwrap(); |
261 | | let asserts = [ |
262 | | (Weekday::Mon, "Mon 2022-05-16", "Sun 2022-05-22"), |
263 | | (Weekday::Tue, "Tue 2022-05-17", "Mon 2022-05-23"), |
264 | | (Weekday::Wed, "Wed 2022-05-18", "Tue 2022-05-24"), |
265 | | (Weekday::Thu, "Thu 2022-05-12", "Wed 2022-05-18"), |
266 | | (Weekday::Fri, "Fri 2022-05-13", "Thu 2022-05-19"), |
267 | | (Weekday::Sat, "Sat 2022-05-14", "Fri 2022-05-20"), |
268 | | (Weekday::Sun, "Sun 2022-05-15", "Sat 2022-05-21"), |
269 | | ]; |
270 | | for (start, first_day, last_day) in asserts { |
271 | | let week = date.week(start); |
272 | | let days = week.days(); |
273 | | assert_eq!(Ok(week.first_day()), NaiveDate::parse_from_str(first_day, "%a %Y-%m-%d")); |
274 | | assert_eq!(Ok(week.last_day()), NaiveDate::parse_from_str(last_day, "%a %Y-%m-%d")); |
275 | | assert!(days.contains(&date)); |
276 | | } |
277 | | } |
278 | | |
279 | | #[test] |
280 | | fn test_naiveweek_min_max() { |
281 | | let date_max = NaiveDate::MAX; |
282 | | assert!(date_max.week(Weekday::Mon).first_day() <= date_max); |
283 | | let date_min = NaiveDate::MIN; |
284 | | assert!(date_min.week(Weekday::Mon).last_day() >= date_min); |
285 | | } |
286 | | |
287 | | #[test] |
288 | | fn test_naiveweek_checked_no_panic() { |
289 | | let date_max = NaiveDate::MAX; |
290 | | if let Some(last) = date_max.week(Weekday::Mon).checked_last_day() { |
291 | | assert!(last == date_max); |
292 | | } |
293 | | let date_min = NaiveDate::MIN; |
294 | | if let Some(first) = date_min.week(Weekday::Mon).checked_first_day() { |
295 | | assert!(first == date_min); |
296 | | } |
297 | | let _ = date_min.week(Weekday::Mon).checked_days(); |
298 | | let _ = date_max.week(Weekday::Mon).checked_days(); |
299 | | } |
300 | | |
301 | | #[test] |
302 | | fn test_naiveweek_eq() { |
303 | | let a = |
304 | | NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Mon }; |
305 | | let b = |
306 | | NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 4).unwrap(), start: Weekday::Mon }; |
307 | | assert_eq!(a, b); |
308 | | |
309 | | let c = |
310 | | NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Sun }; |
311 | | assert_ne!(a, c); |
312 | | assert_ne!(b, c); |
313 | | } |
314 | | |
315 | | #[test] |
316 | | fn test_naiveweek_hash() { |
317 | | let a = |
318 | | NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Mon }; |
319 | | let b = |
320 | | NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 4).unwrap(), start: Weekday::Mon }; |
321 | | let c = |
322 | | NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Sun }; |
323 | | |
324 | | let mut hasher = DefaultHasher::default(); |
325 | | a.hash(&mut hasher); |
326 | | let a_hash = hasher.finish(); |
327 | | |
328 | | hasher = DefaultHasher::default(); |
329 | | b.hash(&mut hasher); |
330 | | let b_hash = hasher.finish(); |
331 | | |
332 | | hasher = DefaultHasher::default(); |
333 | | c.hash(&mut hasher); |
334 | | let c_hash = hasher.finish(); |
335 | | |
336 | | assert_eq!(a_hash, b_hash); |
337 | | assert_ne!(b_hash, c_hash); |
338 | | assert_ne!(a_hash, c_hash); |
339 | | } |
340 | | } |