Coverage Report

Created: 2026-01-17 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/chrono-0.4.43/src/weekday.rs
Line
Count
Source
1
use core::fmt;
2
3
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
4
use rkyv::{Archive, Deserialize, Serialize};
5
6
use crate::OutOfRange;
7
8
/// The day of week.
9
///
10
/// The order of the days of week depends on the context.
11
/// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.)
12
/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result.
13
///
14
/// # Example
15
/// ```
16
/// use chrono::Weekday;
17
///
18
/// let monday = "Monday".parse::<Weekday>().unwrap();
19
/// assert_eq!(monday, Weekday::Mon);
20
///
21
/// let sunday = Weekday::try_from(6).unwrap();
22
/// assert_eq!(sunday, Weekday::Sun);
23
///
24
/// assert_eq!(sunday.num_days_from_monday(), 6); // starts counting with Monday = 0
25
/// assert_eq!(sunday.number_from_monday(), 7); // starts counting with Monday = 1
26
/// assert_eq!(sunday.num_days_from_sunday(), 0); // starts counting with Sunday = 0
27
/// assert_eq!(sunday.number_from_sunday(), 1); // starts counting with Sunday = 1
28
///
29
/// assert_eq!(sunday.succ(), monday);
30
/// assert_eq!(sunday.pred(), Weekday::Sat);
31
/// ```
32
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
33
#[cfg_attr(
34
    any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
35
    derive(Archive, Deserialize, Serialize),
36
    archive(compare(PartialEq)),
37
    archive_attr(derive(Clone, Copy, PartialEq, Eq, Debug, Hash))
38
)]
39
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
40
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
41
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42
pub enum Weekday {
43
    /// Monday.
44
    Mon = 0,
45
    /// Tuesday.
46
    Tue = 1,
47
    /// Wednesday.
48
    Wed = 2,
49
    /// Thursday.
50
    Thu = 3,
51
    /// Friday.
52
    Fri = 4,
53
    /// Saturday.
54
    Sat = 5,
55
    /// Sunday.
56
    Sun = 6,
57
}
58
59
impl Weekday {
60
    /// The next day in the week.
61
    ///
62
    /// `w`:        | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
63
    /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
64
    /// `w.succ()`: | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` | `Mon`
65
    #[inline]
66
    #[must_use]
67
0
    pub const fn succ(&self) -> Weekday {
68
0
        match *self {
69
0
            Weekday::Mon => Weekday::Tue,
70
0
            Weekday::Tue => Weekday::Wed,
71
0
            Weekday::Wed => Weekday::Thu,
72
0
            Weekday::Thu => Weekday::Fri,
73
0
            Weekday::Fri => Weekday::Sat,
74
0
            Weekday::Sat => Weekday::Sun,
75
0
            Weekday::Sun => Weekday::Mon,
76
        }
77
0
    }
78
79
    /// The previous day in the week.
80
    ///
81
    /// `w`:        | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
82
    /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
83
    /// `w.pred()`: | `Sun` | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat`
84
    #[inline]
85
    #[must_use]
86
0
    pub const fn pred(&self) -> Weekday {
87
0
        match *self {
88
0
            Weekday::Mon => Weekday::Sun,
89
0
            Weekday::Tue => Weekday::Mon,
90
0
            Weekday::Wed => Weekday::Tue,
91
0
            Weekday::Thu => Weekday::Wed,
92
0
            Weekday::Fri => Weekday::Thu,
93
0
            Weekday::Sat => Weekday::Fri,
94
0
            Weekday::Sun => Weekday::Sat,
95
        }
96
0
    }
97
98
    /// Returns a day-of-week number starting from Monday = 1. (ISO 8601 weekday number)
99
    ///
100
    /// `w`:                      | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
101
    /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
102
    /// `w.number_from_monday()`: | 1     | 2     | 3     | 4     | 5     | 6     | 7
103
    #[inline]
104
0
    pub const fn number_from_monday(&self) -> u32 {
105
0
        self.days_since(Weekday::Mon) + 1
106
0
    }
Unexecuted instantiation: <chrono::weekday::Weekday>::number_from_monday
Unexecuted instantiation: <chrono::weekday::Weekday>::number_from_monday
107
108
    /// Returns a day-of-week number starting from Sunday = 1.
109
    ///
110
    /// `w`:                      | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
111
    /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
112
    /// `w.number_from_sunday()`: | 2     | 3     | 4     | 5     | 6     | 7     | 1
113
    #[inline]
114
0
    pub const fn number_from_sunday(&self) -> u32 {
115
0
        self.days_since(Weekday::Sun) + 1
116
0
    }
117
118
    /// Returns a day-of-week number starting from Monday = 0.
119
    ///
120
    /// `w`:                        | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
121
    /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
122
    /// `w.num_days_from_monday()`: | 0     | 1     | 2     | 3     | 4     | 5     | 6
123
    ///
124
    /// # Example
125
    ///
126
    /// ```
127
    /// # #[cfg(feature = "clock")] {
128
    /// # use chrono::{Local, Datelike};
129
    /// // MTWRFSU is occasionally used as a single-letter abbreviation of the weekdays.
130
    /// // Use `num_days_from_monday` to index into the array.
131
    /// const MTWRFSU: [char; 7] = ['M', 'T', 'W', 'R', 'F', 'S', 'U'];
132
    ///
133
    /// let today = Local::now().weekday();
134
    /// println!("{}", MTWRFSU[today.num_days_from_monday() as usize]);
135
    /// # }
136
    /// ```
137
    #[inline]
138
0
    pub const fn num_days_from_monday(&self) -> u32 {
139
0
        self.days_since(Weekday::Mon)
140
0
    }
141
142
    /// Returns a day-of-week number starting from Sunday = 0.
143
    ///
144
    /// `w`:                        | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
145
    /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
146
    /// `w.num_days_from_sunday()`: | 1     | 2     | 3     | 4     | 5     | 6     | 0
147
    #[inline]
148
0
    pub const fn num_days_from_sunday(&self) -> u32 {
149
0
        self.days_since(Weekday::Sun)
150
0
    }
Unexecuted instantiation: <chrono::weekday::Weekday>::num_days_from_sunday
Unexecuted instantiation: <chrono::weekday::Weekday>::num_days_from_sunday
151
152
    /// The number of days since the given day.
153
    ///
154
    /// # Examples
155
    ///
156
    /// ```
157
    /// use chrono::Weekday::*;
158
    /// assert_eq!(Mon.days_since(Mon), 0);
159
    /// assert_eq!(Sun.days_since(Tue), 5);
160
    /// assert_eq!(Wed.days_since(Sun), 3);
161
    /// ```
162
0
    pub const fn days_since(&self, other: Weekday) -> u32 {
163
0
        let lhs = *self as u32;
164
0
        let rhs = other as u32;
165
0
        if lhs < rhs { 7 + lhs - rhs } else { lhs - rhs }
166
0
    }
167
}
168
169
impl fmt::Display for Weekday {
170
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171
0
        f.pad(match *self {
172
0
            Weekday::Mon => "Mon",
173
0
            Weekday::Tue => "Tue",
174
0
            Weekday::Wed => "Wed",
175
0
            Weekday::Thu => "Thu",
176
0
            Weekday::Fri => "Fri",
177
0
            Weekday::Sat => "Sat",
178
0
            Weekday::Sun => "Sun",
179
        })
180
0
    }
181
}
182
183
/// Any weekday can be represented as an integer from 0 to 6, which equals to
184
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
185
/// Do not heavily depend on this though; use explicit methods whenever possible.
186
impl TryFrom<u8> for Weekday {
187
    type Error = OutOfRange;
188
189
0
    fn try_from(value: u8) -> Result<Self, Self::Error> {
190
0
        match value {
191
0
            0 => Ok(Weekday::Mon),
192
0
            1 => Ok(Weekday::Tue),
193
0
            2 => Ok(Weekday::Wed),
194
0
            3 => Ok(Weekday::Thu),
195
0
            4 => Ok(Weekday::Fri),
196
0
            5 => Ok(Weekday::Sat),
197
0
            6 => Ok(Weekday::Sun),
198
0
            _ => Err(OutOfRange::new()),
199
        }
200
0
    }
201
}
202
203
/// Any weekday can be represented as an integer from 0 to 6, which equals to
204
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
205
/// Do not heavily depend on this though; use explicit methods whenever possible.
206
impl num_traits::FromPrimitive for Weekday {
207
    #[inline]
208
0
    fn from_i64(n: i64) -> Option<Weekday> {
209
0
        match n {
210
0
            0 => Some(Weekday::Mon),
211
0
            1 => Some(Weekday::Tue),
212
0
            2 => Some(Weekday::Wed),
213
0
            3 => Some(Weekday::Thu),
214
0
            4 => Some(Weekday::Fri),
215
0
            5 => Some(Weekday::Sat),
216
0
            6 => Some(Weekday::Sun),
217
0
            _ => None,
218
        }
219
0
    }
220
221
    #[inline]
222
0
    fn from_u64(n: u64) -> Option<Weekday> {
223
0
        match n {
224
0
            0 => Some(Weekday::Mon),
225
0
            1 => Some(Weekday::Tue),
226
0
            2 => Some(Weekday::Wed),
227
0
            3 => Some(Weekday::Thu),
228
0
            4 => Some(Weekday::Fri),
229
0
            5 => Some(Weekday::Sat),
230
0
            6 => Some(Weekday::Sun),
231
0
            _ => None,
232
        }
233
0
    }
234
}
235
236
/// An error resulting from reading `Weekday` value with `FromStr`.
237
#[derive(Clone, PartialEq, Eq)]
238
pub struct ParseWeekdayError {
239
    pub(crate) _dummy: (),
240
}
241
242
#[cfg(all(not(feature = "std"), feature = "core-error"))]
243
impl core::error::Error for ParseWeekdayError {}
244
245
#[cfg(feature = "std")]
246
impl std::error::Error for ParseWeekdayError {}
247
248
impl fmt::Display for ParseWeekdayError {
249
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250
0
        f.write_fmt(format_args!("{self:?}"))
251
0
    }
252
}
253
254
impl fmt::Debug for ParseWeekdayError {
255
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256
0
        write!(f, "ParseWeekdayError {{ .. }}")
257
0
    }
258
}
259
260
#[cfg(feature = "defmt")]
261
impl defmt::Format for ParseWeekdayError {
262
    fn format(&self, fmt: defmt::Formatter) {
263
        defmt::write!(fmt, "ParseWeekdayError {{ .. }}")
264
    }
265
}
266
267
// the actual `FromStr` implementation is in the `format` module to leverage the existing code
268
269
#[cfg(feature = "serde")]
270
mod weekday_serde {
271
    use super::Weekday;
272
    use core::fmt;
273
    use serde::{de, ser};
274
275
    impl ser::Serialize for Weekday {
276
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
277
        where
278
            S: ser::Serializer,
279
        {
280
            serializer.collect_str(&self)
281
        }
282
    }
283
284
    struct WeekdayVisitor;
285
286
    impl de::Visitor<'_> for WeekdayVisitor {
287
        type Value = Weekday;
288
289
        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
290
            f.write_str("Weekday")
291
        }
292
293
        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
294
        where
295
            E: de::Error,
296
        {
297
            value.parse().map_err(|_| E::custom("short or long weekday names expected"))
298
        }
299
    }
300
301
    impl<'de> de::Deserialize<'de> for Weekday {
302
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
303
        where
304
            D: de::Deserializer<'de>,
305
        {
306
            deserializer.deserialize_str(WeekdayVisitor)
307
        }
308
    }
309
}
310
311
#[cfg(test)]
312
mod tests {
313
    use super::Weekday;
314
315
    #[test]
316
    fn test_days_since() {
317
        for i in 0..7 {
318
            let base_day = Weekday::try_from(i).unwrap();
319
320
            assert_eq!(base_day.num_days_from_monday(), base_day.days_since(Weekday::Mon));
321
            assert_eq!(base_day.num_days_from_sunday(), base_day.days_since(Weekday::Sun));
322
323
            assert_eq!(base_day.days_since(base_day), 0);
324
325
            assert_eq!(base_day.days_since(base_day.pred()), 1);
326
            assert_eq!(base_day.days_since(base_day.pred().pred()), 2);
327
            assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3);
328
            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4);
329
            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred()), 5);
330
            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()), 6);
331
332
            assert_eq!(base_day.days_since(base_day.succ()), 6);
333
            assert_eq!(base_day.days_since(base_day.succ().succ()), 5);
334
            assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4);
335
            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3);
336
            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ()), 2);
337
            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()), 1);
338
        }
339
    }
340
341
    #[test]
342
    fn test_formatting_alignment() {
343
        // No exhaustive testing here as we just delegate the
344
        // implementation to Formatter::pad. Just some basic smoke
345
        // testing to ensure that it's in fact being done.
346
        assert_eq!(format!("{:x>7}", Weekday::Mon), "xxxxMon");
347
        assert_eq!(format!("{:^7}", Weekday::Mon), "  Mon  ");
348
        assert_eq!(format!("{:Z<7}", Weekday::Mon), "MonZZZZ");
349
    }
350
351
    #[test]
352
    #[cfg(feature = "serde")]
353
    fn test_serde_serialize() {
354
        use Weekday::*;
355
        use serde_json::to_string;
356
357
        let cases: Vec<(Weekday, &str)> = vec![
358
            (Mon, "\"Mon\""),
359
            (Tue, "\"Tue\""),
360
            (Wed, "\"Wed\""),
361
            (Thu, "\"Thu\""),
362
            (Fri, "\"Fri\""),
363
            (Sat, "\"Sat\""),
364
            (Sun, "\"Sun\""),
365
        ];
366
367
        for (weekday, expected_str) in cases {
368
            let string = to_string(&weekday).unwrap();
369
            assert_eq!(string, expected_str);
370
        }
371
    }
372
373
    #[test]
374
    #[cfg(feature = "serde")]
375
    fn test_serde_deserialize() {
376
        use Weekday::*;
377
        use serde_json::from_str;
378
379
        let cases: Vec<(&str, Weekday)> = vec![
380
            ("\"mon\"", Mon),
381
            ("\"MONDAY\"", Mon),
382
            ("\"MonDay\"", Mon),
383
            ("\"mOn\"", Mon),
384
            ("\"tue\"", Tue),
385
            ("\"tuesday\"", Tue),
386
            ("\"wed\"", Wed),
387
            ("\"wednesday\"", Wed),
388
            ("\"thu\"", Thu),
389
            ("\"thursday\"", Thu),
390
            ("\"fri\"", Fri),
391
            ("\"friday\"", Fri),
392
            ("\"sat\"", Sat),
393
            ("\"saturday\"", Sat),
394
            ("\"sun\"", Sun),
395
            ("\"sunday\"", Sun),
396
        ];
397
398
        for (str, expected_weekday) in cases {
399
            let weekday = from_str::<Weekday>(str).unwrap();
400
            assert_eq!(weekday, expected_weekday);
401
        }
402
403
        let errors: Vec<&str> =
404
            vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
405
406
        for str in errors {
407
            from_str::<Weekday>(str).unwrap_err();
408
        }
409
    }
410
411
    #[test]
412
    #[cfg(feature = "rkyv-validation")]
413
    fn test_rkyv_validation() {
414
        let mon = Weekday::Mon;
415
        let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
416
417
        assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
418
    }
419
}