Coverage Report

Created: 2026-02-14 06:16

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