Coverage Report

Created: 2025-07-11 07:02

/rust/registry/src/index.crates.io-6f17d22bba15001f/chrono-0.4.41/src/weekday.rs
Line
Count
Source (jump to first uncovered line)
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(feature = "std")]
242
impl std::error::Error for ParseWeekdayError {}
243
244
impl fmt::Display for ParseWeekdayError {
245
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246
0
        f.write_fmt(format_args!("{:?}", self))
247
0
    }
248
}
249
250
impl fmt::Debug for ParseWeekdayError {
251
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252
0
        write!(f, "ParseWeekdayError {{ .. }}")
253
0
    }
254
}
255
256
// the actual `FromStr` implementation is in the `format` module to leverage the existing code
257
258
#[cfg(feature = "serde")]
259
mod weekday_serde {
260
    use super::Weekday;
261
    use core::fmt;
262
    use serde::{de, ser};
263
264
    impl ser::Serialize for Weekday {
265
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
266
        where
267
            S: ser::Serializer,
268
        {
269
            serializer.collect_str(&self)
270
        }
271
    }
272
273
    struct WeekdayVisitor;
274
275
    impl de::Visitor<'_> for WeekdayVisitor {
276
        type Value = Weekday;
277
278
        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
279
            f.write_str("Weekday")
280
        }
281
282
        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
283
        where
284
            E: de::Error,
285
        {
286
            value.parse().map_err(|_| E::custom("short or long weekday names expected"))
287
        }
288
    }
289
290
    impl<'de> de::Deserialize<'de> for Weekday {
291
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292
        where
293
            D: de::Deserializer<'de>,
294
        {
295
            deserializer.deserialize_str(WeekdayVisitor)
296
        }
297
    }
298
}
299
300
#[cfg(test)]
301
mod tests {
302
    use super::Weekday;
303
304
    #[test]
305
    fn test_days_since() {
306
        for i in 0..7 {
307
            let base_day = Weekday::try_from(i).unwrap();
308
309
            assert_eq!(base_day.num_days_from_monday(), base_day.days_since(Weekday::Mon));
310
            assert_eq!(base_day.num_days_from_sunday(), base_day.days_since(Weekday::Sun));
311
312
            assert_eq!(base_day.days_since(base_day), 0);
313
314
            assert_eq!(base_day.days_since(base_day.pred()), 1);
315
            assert_eq!(base_day.days_since(base_day.pred().pred()), 2);
316
            assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3);
317
            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4);
318
            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred()), 5);
319
            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()), 6);
320
321
            assert_eq!(base_day.days_since(base_day.succ()), 6);
322
            assert_eq!(base_day.days_since(base_day.succ().succ()), 5);
323
            assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4);
324
            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3);
325
            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ()), 2);
326
            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()), 1);
327
        }
328
    }
329
330
    #[test]
331
    fn test_formatting_alignment() {
332
        // No exhaustive testing here as we just delegate the
333
        // implementation to Formatter::pad. Just some basic smoke
334
        // testing to ensure that it's in fact being done.
335
        assert_eq!(format!("{:x>7}", Weekday::Mon), "xxxxMon");
336
        assert_eq!(format!("{:^7}", Weekday::Mon), "  Mon  ");
337
        assert_eq!(format!("{:Z<7}", Weekday::Mon), "MonZZZZ");
338
    }
339
340
    #[test]
341
    #[cfg(feature = "serde")]
342
    fn test_serde_serialize() {
343
        use Weekday::*;
344
        use serde_json::to_string;
345
346
        let cases: Vec<(Weekday, &str)> = vec![
347
            (Mon, "\"Mon\""),
348
            (Tue, "\"Tue\""),
349
            (Wed, "\"Wed\""),
350
            (Thu, "\"Thu\""),
351
            (Fri, "\"Fri\""),
352
            (Sat, "\"Sat\""),
353
            (Sun, "\"Sun\""),
354
        ];
355
356
        for (weekday, expected_str) in cases {
357
            let string = to_string(&weekday).unwrap();
358
            assert_eq!(string, expected_str);
359
        }
360
    }
361
362
    #[test]
363
    #[cfg(feature = "serde")]
364
    fn test_serde_deserialize() {
365
        use Weekday::*;
366
        use serde_json::from_str;
367
368
        let cases: Vec<(&str, Weekday)> = vec![
369
            ("\"mon\"", Mon),
370
            ("\"MONDAY\"", Mon),
371
            ("\"MonDay\"", Mon),
372
            ("\"mOn\"", Mon),
373
            ("\"tue\"", Tue),
374
            ("\"tuesday\"", Tue),
375
            ("\"wed\"", Wed),
376
            ("\"wednesday\"", Wed),
377
            ("\"thu\"", Thu),
378
            ("\"thursday\"", Thu),
379
            ("\"fri\"", Fri),
380
            ("\"friday\"", Fri),
381
            ("\"sat\"", Sat),
382
            ("\"saturday\"", Sat),
383
            ("\"sun\"", Sun),
384
            ("\"sunday\"", Sun),
385
        ];
386
387
        for (str, expected_weekday) in cases {
388
            let weekday = from_str::<Weekday>(str).unwrap();
389
            assert_eq!(weekday, expected_weekday);
390
        }
391
392
        let errors: Vec<&str> =
393
            vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
394
395
        for str in errors {
396
            from_str::<Weekday>(str).unwrap_err();
397
        }
398
    }
399
400
    #[test]
401
    #[cfg(feature = "rkyv-validation")]
402
    fn test_rkyv_validation() {
403
        let mon = Weekday::Mon;
404
        let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
405
406
        assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
407
    }
408
}