chrono/
weekday.rs

1use core::fmt;
2
3#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
4use rkyv::{Archive, Deserialize, Serialize};
5
6use 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))]
41pub 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
58impl 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    pub const fn succ(&self) -> Weekday {
67        match *self {
68            Weekday::Mon => Weekday::Tue,
69            Weekday::Tue => Weekday::Wed,
70            Weekday::Wed => Weekday::Thu,
71            Weekday::Thu => Weekday::Fri,
72            Weekday::Fri => Weekday::Sat,
73            Weekday::Sat => Weekday::Sun,
74            Weekday::Sun => Weekday::Mon,
75        }
76    }
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    pub const fn pred(&self) -> Weekday {
86        match *self {
87            Weekday::Mon => Weekday::Sun,
88            Weekday::Tue => Weekday::Mon,
89            Weekday::Wed => Weekday::Tue,
90            Weekday::Thu => Weekday::Wed,
91            Weekday::Fri => Weekday::Thu,
92            Weekday::Sat => Weekday::Fri,
93            Weekday::Sun => Weekday::Sat,
94        }
95    }
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    pub const fn number_from_monday(&self) -> u32 {
104        self.days_since(Weekday::Mon) + 1
105    }
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    pub const fn number_from_sunday(&self) -> u32 {
114        self.days_since(Weekday::Sun) + 1
115    }
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    pub const fn num_days_from_monday(&self) -> u32 {
138        self.days_since(Weekday::Mon)
139    }
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    pub const fn num_days_from_sunday(&self) -> u32 {
148        self.days_since(Weekday::Sun)
149    }
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    pub const fn days_since(&self, other: Weekday) -> u32 {
162        let lhs = *self as u32;
163        let rhs = other as u32;
164        if lhs < rhs { 7 + lhs - rhs } else { lhs - rhs }
165    }
166}
167
168impl fmt::Display for Weekday {
169    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170        f.pad(match *self {
171            Weekday::Mon => "Mon",
172            Weekday::Tue => "Tue",
173            Weekday::Wed => "Wed",
174            Weekday::Thu => "Thu",
175            Weekday::Fri => "Fri",
176            Weekday::Sat => "Sat",
177            Weekday::Sun => "Sun",
178        })
179    }
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.
185impl TryFrom<u8> for Weekday {
186    type Error = OutOfRange;
187
188    fn try_from(value: u8) -> Result<Self, Self::Error> {
189        match value {
190            0 => Ok(Weekday::Mon),
191            1 => Ok(Weekday::Tue),
192            2 => Ok(Weekday::Wed),
193            3 => Ok(Weekday::Thu),
194            4 => Ok(Weekday::Fri),
195            5 => Ok(Weekday::Sat),
196            6 => Ok(Weekday::Sun),
197            _ => Err(OutOfRange::new()),
198        }
199    }
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.
205impl num_traits::FromPrimitive for Weekday {
206    #[inline]
207    fn from_i64(n: i64) -> Option<Weekday> {
208        match n {
209            0 => Some(Weekday::Mon),
210            1 => Some(Weekday::Tue),
211            2 => Some(Weekday::Wed),
212            3 => Some(Weekday::Thu),
213            4 => Some(Weekday::Fri),
214            5 => Some(Weekday::Sat),
215            6 => Some(Weekday::Sun),
216            _ => None,
217        }
218    }
219
220    #[inline]
221    fn from_u64(n: u64) -> Option<Weekday> {
222        match n {
223            0 => Some(Weekday::Mon),
224            1 => Some(Weekday::Tue),
225            2 => Some(Weekday::Wed),
226            3 => Some(Weekday::Thu),
227            4 => Some(Weekday::Fri),
228            5 => Some(Weekday::Sat),
229            6 => Some(Weekday::Sun),
230            _ => None,
231        }
232    }
233}
234
235/// An error resulting from reading `Weekday` value with `FromStr`.
236#[derive(Clone, PartialEq, Eq)]
237pub struct ParseWeekdayError {
238    pub(crate) _dummy: (),
239}
240
241#[cfg(feature = "std")]
242impl std::error::Error for ParseWeekdayError {}
243
244impl fmt::Display for ParseWeekdayError {
245    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246        f.write_fmt(format_args!("{:?}", self))
247    }
248}
249
250impl fmt::Debug for ParseWeekdayError {
251    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252        write!(f, "ParseWeekdayError {{ .. }}")
253    }
254}
255
256// the actual `FromStr` implementation is in the `format` module to leverage the existing code
257
258#[cfg(feature = "serde")]
259mod 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)]
301mod 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}