Coverage Report

Created: 2026-05-30 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}