Coverage Report

Created: 2025-10-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/chrono-0.4.42/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
    }
Unexecuted instantiation: <chrono::weekday::Weekday>::number_from_monday
Unexecuted instantiation: <chrono::weekday::Weekday>::number_from_monday
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
    }
Unexecuted instantiation: <chrono::weekday::Weekday>::num_days_from_sunday
Unexecuted instantiation: <chrono::weekday::Weekday>::num_days_from_sunday
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 { 7 + lhs - rhs } else { lhs - rhs }
165
0
    }
166
}
167
168
impl fmt::Display for Weekday {
169
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170
0
        f.pad(match *self {
171
0
            Weekday::Mon => "Mon",
172
0
            Weekday::Tue => "Tue",
173
0
            Weekday::Wed => "Wed",
174
0
            Weekday::Thu => "Thu",
175
0
            Weekday::Fri => "Fri",
176
0
            Weekday::Sat => "Sat",
177
0
            Weekday::Sun => "Sun",
178
        })
179
0
    }
180
}
181
182
/// Any weekday can be represented as an integer from 0 to 6, which equals to
183
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
184
/// Do not heavily depend on this though; use explicit methods whenever possible.
185
impl TryFrom<u8> for Weekday {
186
    type Error = OutOfRange;
187
188
0
    fn try_from(value: u8) -> Result<Self, Self::Error> {
189
0
        match value {
190
0
            0 => Ok(Weekday::Mon),
191
0
            1 => Ok(Weekday::Tue),
192
0
            2 => Ok(Weekday::Wed),
193
0
            3 => Ok(Weekday::Thu),
194
0
            4 => Ok(Weekday::Fri),
195
0
            5 => Ok(Weekday::Sat),
196
0
            6 => Ok(Weekday::Sun),
197
0
            _ => Err(OutOfRange::new()),
198
        }
199
0
    }
200
}
201
202
/// Any weekday can be represented as an integer from 0 to 6, which equals to
203
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
204
/// Do not heavily depend on this though; use explicit methods whenever possible.
205
impl num_traits::FromPrimitive for Weekday {
206
    #[inline]
207
0
    fn from_i64(n: i64) -> Option<Weekday> {
208
0
        match n {
209
0
            0 => Some(Weekday::Mon),
210
0
            1 => Some(Weekday::Tue),
211
0
            2 => Some(Weekday::Wed),
212
0
            3 => Some(Weekday::Thu),
213
0
            4 => Some(Weekday::Fri),
214
0
            5 => Some(Weekday::Sat),
215
0
            6 => Some(Weekday::Sun),
216
0
            _ => None,
217
        }
218
0
    }
219
220
    #[inline]
221
0
    fn from_u64(n: u64) -> Option<Weekday> {
222
0
        match n {
223
0
            0 => Some(Weekday::Mon),
224
0
            1 => Some(Weekday::Tue),
225
0
            2 => Some(Weekday::Wed),
226
0
            3 => Some(Weekday::Thu),
227
0
            4 => Some(Weekday::Fri),
228
0
            5 => Some(Weekday::Sat),
229
0
            6 => Some(Weekday::Sun),
230
0
            _ => None,
231
        }
232
0
    }
233
}
234
235
/// An error resulting from reading `Weekday` value with `FromStr`.
236
#[derive(Clone, PartialEq, Eq)]
237
pub struct ParseWeekdayError {
238
    pub(crate) _dummy: (),
239
}
240
241
#[cfg(all(not(feature = "std"), feature = "core-error"))]
242
impl core::error::Error for ParseWeekdayError {}
243
244
#[cfg(feature = "std")]
245
impl std::error::Error for ParseWeekdayError {}
246
247
impl fmt::Display for ParseWeekdayError {
248
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
249
0
        f.write_fmt(format_args!("{self:?}"))
250
0
    }
251
}
252
253
impl fmt::Debug for ParseWeekdayError {
254
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
255
0
        write!(f, "ParseWeekdayError {{ .. }}")
256
0
    }
257
}
258
259
// the actual `FromStr` implementation is in the `format` module to leverage the existing code
260
261
#[cfg(feature = "serde")]
262
mod weekday_serde {
263
    use super::Weekday;
264
    use core::fmt;
265
    use serde::{de, ser};
266
267
    impl ser::Serialize for Weekday {
268
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
269
        where
270
            S: ser::Serializer,
271
        {
272
            serializer.collect_str(&self)
273
        }
274
    }
275
276
    struct WeekdayVisitor;
277
278
    impl de::Visitor<'_> for WeekdayVisitor {
279
        type Value = Weekday;
280
281
        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
282
            f.write_str("Weekday")
283
        }
284
285
        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
286
        where
287
            E: de::Error,
288
        {
289
            value.parse().map_err(|_| E::custom("short or long weekday names expected"))
290
        }
291
    }
292
293
    impl<'de> de::Deserialize<'de> for Weekday {
294
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
295
        where
296
            D: de::Deserializer<'de>,
297
        {
298
            deserializer.deserialize_str(WeekdayVisitor)
299
        }
300
    }
301
}
302
303
#[cfg(test)]
304
mod tests {
305
    use super::Weekday;
306
307
    #[test]
308
    fn test_days_since() {
309
        for i in 0..7 {
310
            let base_day = Weekday::try_from(i).unwrap();
311
312
            assert_eq!(base_day.num_days_from_monday(), base_day.days_since(Weekday::Mon));
313
            assert_eq!(base_day.num_days_from_sunday(), base_day.days_since(Weekday::Sun));
314
315
            assert_eq!(base_day.days_since(base_day), 0);
316
317
            assert_eq!(base_day.days_since(base_day.pred()), 1);
318
            assert_eq!(base_day.days_since(base_day.pred().pred()), 2);
319
            assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3);
320
            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4);
321
            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred()), 5);
322
            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()), 6);
323
324
            assert_eq!(base_day.days_since(base_day.succ()), 6);
325
            assert_eq!(base_day.days_since(base_day.succ().succ()), 5);
326
            assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4);
327
            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3);
328
            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ()), 2);
329
            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()), 1);
330
        }
331
    }
332
333
    #[test]
334
    fn test_formatting_alignment() {
335
        // No exhaustive testing here as we just delegate the
336
        // implementation to Formatter::pad. Just some basic smoke
337
        // testing to ensure that it's in fact being done.
338
        assert_eq!(format!("{:x>7}", Weekday::Mon), "xxxxMon");
339
        assert_eq!(format!("{:^7}", Weekday::Mon), "  Mon  ");
340
        assert_eq!(format!("{:Z<7}", Weekday::Mon), "MonZZZZ");
341
    }
342
343
    #[test]
344
    #[cfg(feature = "serde")]
345
    fn test_serde_serialize() {
346
        use Weekday::*;
347
        use serde_json::to_string;
348
349
        let cases: Vec<(Weekday, &str)> = vec![
350
            (Mon, "\"Mon\""),
351
            (Tue, "\"Tue\""),
352
            (Wed, "\"Wed\""),
353
            (Thu, "\"Thu\""),
354
            (Fri, "\"Fri\""),
355
            (Sat, "\"Sat\""),
356
            (Sun, "\"Sun\""),
357
        ];
358
359
        for (weekday, expected_str) in cases {
360
            let string = to_string(&weekday).unwrap();
361
            assert_eq!(string, expected_str);
362
        }
363
    }
364
365
    #[test]
366
    #[cfg(feature = "serde")]
367
    fn test_serde_deserialize() {
368
        use Weekday::*;
369
        use serde_json::from_str;
370
371
        let cases: Vec<(&str, Weekday)> = vec![
372
            ("\"mon\"", Mon),
373
            ("\"MONDAY\"", Mon),
374
            ("\"MonDay\"", Mon),
375
            ("\"mOn\"", Mon),
376
            ("\"tue\"", Tue),
377
            ("\"tuesday\"", Tue),
378
            ("\"wed\"", Wed),
379
            ("\"wednesday\"", Wed),
380
            ("\"thu\"", Thu),
381
            ("\"thursday\"", Thu),
382
            ("\"fri\"", Fri),
383
            ("\"friday\"", Fri),
384
            ("\"sat\"", Sat),
385
            ("\"saturday\"", Sat),
386
            ("\"sun\"", Sun),
387
            ("\"sunday\"", Sun),
388
        ];
389
390
        for (str, expected_weekday) in cases {
391
            let weekday = from_str::<Weekday>(str).unwrap();
392
            assert_eq!(weekday, expected_weekday);
393
        }
394
395
        let errors: Vec<&str> =
396
            vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
397
398
        for str in errors {
399
            from_str::<Weekday>(str).unwrap_err();
400
        }
401
    }
402
403
    #[test]
404
    #[cfg(feature = "rkyv-validation")]
405
    fn test_rkyv_validation() {
406
        let mon = Weekday::Mon;
407
        let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
408
409
        assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
410
    }
411
}