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(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
34#[cfg_attr(
35    any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
36    derive(Archive, Deserialize, Serialize),
37    archive(compare(PartialEq)),
38    archive_attr(derive(Clone, Copy, PartialEq, Eq, Debug, Hash))
39)]
40#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
41#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
42pub 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
59impl 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    pub const fn succ(&self) -> Weekday {
68        match *self {
69            Weekday::Mon => Weekday::Tue,
70            Weekday::Tue => Weekday::Wed,
71            Weekday::Wed => Weekday::Thu,
72            Weekday::Thu => Weekday::Fri,
73            Weekday::Fri => Weekday::Sat,
74            Weekday::Sat => Weekday::Sun,
75            Weekday::Sun => Weekday::Mon,
76        }
77    }
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    pub const fn pred(&self) -> Weekday {
87        match *self {
88            Weekday::Mon => Weekday::Sun,
89            Weekday::Tue => Weekday::Mon,
90            Weekday::Wed => Weekday::Tue,
91            Weekday::Thu => Weekday::Wed,
92            Weekday::Fri => Weekday::Thu,
93            Weekday::Sat => Weekday::Fri,
94            Weekday::Sun => Weekday::Sat,
95        }
96    }
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    pub const fn number_from_monday(&self) -> u32 {
105        self.num_days_from(Weekday::Mon) + 1
106    }
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    pub const fn number_from_sunday(&self) -> u32 {
115        self.num_days_from(Weekday::Sun) + 1
116    }
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    pub const fn num_days_from_monday(&self) -> u32 {
139        self.num_days_from(Weekday::Mon)
140    }
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    pub const fn num_days_from_sunday(&self) -> u32 {
149        self.num_days_from(Weekday::Sun)
150    }
151
152    /// Returns a day-of-week number starting from the parameter `day` (D) = 0.
153    ///
154    /// `w`:                        | `D`   | `D+1` | `D+2` | `D+3` | `D+4` | `D+5` | `D+6`
155    /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
156    /// `w.num_days_from(wd)`:      | 0     | 1     | 2     | 3     | 4     | 5     | 6
157    #[inline]
158    pub(crate) const fn num_days_from(&self, day: Weekday) -> u32 {
159        (*self as u32 + 7 - day as u32) % 7
160    }
161}
162
163impl fmt::Display for Weekday {
164    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165        f.write_str(match *self {
166            Weekday::Mon => "Mon",
167            Weekday::Tue => "Tue",
168            Weekday::Wed => "Wed",
169            Weekday::Thu => "Thu",
170            Weekday::Fri => "Fri",
171            Weekday::Sat => "Sat",
172            Weekday::Sun => "Sun",
173        })
174    }
175}
176
177/// Any weekday can be represented as an integer from 0 to 6, which equals to
178/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
179/// Do not heavily depend on this though; use explicit methods whenever possible.
180impl TryFrom<u8> for Weekday {
181    type Error = OutOfRange;
182
183    fn try_from(value: u8) -> Result<Self, Self::Error> {
184        match value {
185            0 => Ok(Weekday::Mon),
186            1 => Ok(Weekday::Tue),
187            2 => Ok(Weekday::Wed),
188            3 => Ok(Weekday::Thu),
189            4 => Ok(Weekday::Fri),
190            5 => Ok(Weekday::Sat),
191            6 => Ok(Weekday::Sun),
192            _ => Err(OutOfRange::new()),
193        }
194    }
195}
196
197/// Any weekday can be represented as an integer from 0 to 6, which equals to
198/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
199/// Do not heavily depend on this though; use explicit methods whenever possible.
200impl num_traits::FromPrimitive for Weekday {
201    #[inline]
202    fn from_i64(n: i64) -> Option<Weekday> {
203        match n {
204            0 => Some(Weekday::Mon),
205            1 => Some(Weekday::Tue),
206            2 => Some(Weekday::Wed),
207            3 => Some(Weekday::Thu),
208            4 => Some(Weekday::Fri),
209            5 => Some(Weekday::Sat),
210            6 => Some(Weekday::Sun),
211            _ => None,
212        }
213    }
214
215    #[inline]
216    fn from_u64(n: u64) -> Option<Weekday> {
217        match n {
218            0 => Some(Weekday::Mon),
219            1 => Some(Weekday::Tue),
220            2 => Some(Weekday::Wed),
221            3 => Some(Weekday::Thu),
222            4 => Some(Weekday::Fri),
223            5 => Some(Weekday::Sat),
224            6 => Some(Weekday::Sun),
225            _ => None,
226        }
227    }
228}
229
230/// An error resulting from reading `Weekday` value with `FromStr`.
231#[derive(Clone, PartialEq, Eq)]
232pub struct ParseWeekdayError {
233    pub(crate) _dummy: (),
234}
235
236#[cfg(feature = "std")]
237impl std::error::Error for ParseWeekdayError {}
238
239impl fmt::Display for ParseWeekdayError {
240    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241        f.write_fmt(format_args!("{:?}", self))
242    }
243}
244
245impl fmt::Debug for ParseWeekdayError {
246    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
247        write!(f, "ParseWeekdayError {{ .. }}")
248    }
249}
250
251// the actual `FromStr` implementation is in the `format` module to leverage the existing code
252
253#[cfg(feature = "serde")]
254mod weekday_serde {
255    use super::Weekday;
256    use core::fmt;
257    use serde::{de, ser};
258
259    impl ser::Serialize for Weekday {
260        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
261        where
262            S: ser::Serializer,
263        {
264            serializer.collect_str(&self)
265        }
266    }
267
268    struct WeekdayVisitor;
269
270    impl<'de> de::Visitor<'de> for WeekdayVisitor {
271        type Value = Weekday;
272
273        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
274            f.write_str("Weekday")
275        }
276
277        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
278        where
279            E: de::Error,
280        {
281            value.parse().map_err(|_| E::custom("short or long weekday names expected"))
282        }
283    }
284
285    impl<'de> de::Deserialize<'de> for Weekday {
286        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
287        where
288            D: de::Deserializer<'de>,
289        {
290            deserializer.deserialize_str(WeekdayVisitor)
291        }
292    }
293}
294
295#[cfg(test)]
296mod tests {
297    use super::Weekday;
298
299    #[test]
300    fn test_num_days_from() {
301        for i in 0..7 {
302            let base_day = Weekday::try_from(i).unwrap();
303
304            assert_eq!(base_day.num_days_from_monday(), base_day.num_days_from(Weekday::Mon));
305            assert_eq!(base_day.num_days_from_sunday(), base_day.num_days_from(Weekday::Sun));
306
307            assert_eq!(base_day.num_days_from(base_day), 0);
308
309            assert_eq!(base_day.num_days_from(base_day.pred()), 1);
310            assert_eq!(base_day.num_days_from(base_day.pred().pred()), 2);
311            assert_eq!(base_day.num_days_from(base_day.pred().pred().pred()), 3);
312            assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred()), 4);
313            assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred().pred()), 5);
314            assert_eq!(
315                base_day.num_days_from(base_day.pred().pred().pred().pred().pred().pred()),
316                6
317            );
318
319            assert_eq!(base_day.num_days_from(base_day.succ()), 6);
320            assert_eq!(base_day.num_days_from(base_day.succ().succ()), 5);
321            assert_eq!(base_day.num_days_from(base_day.succ().succ().succ()), 4);
322            assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ()), 3);
323            assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ().succ()), 2);
324            assert_eq!(
325                base_day.num_days_from(base_day.succ().succ().succ().succ().succ().succ()),
326                1
327            );
328        }
329    }
330
331    #[test]
332    #[cfg(feature = "serde")]
333    fn test_serde_serialize() {
334        use serde_json::to_string;
335        use Weekday::*;
336
337        let cases: Vec<(Weekday, &str)> = vec![
338            (Mon, "\"Mon\""),
339            (Tue, "\"Tue\""),
340            (Wed, "\"Wed\""),
341            (Thu, "\"Thu\""),
342            (Fri, "\"Fri\""),
343            (Sat, "\"Sat\""),
344            (Sun, "\"Sun\""),
345        ];
346
347        for (weekday, expected_str) in cases {
348            let string = to_string(&weekday).unwrap();
349            assert_eq!(string, expected_str);
350        }
351    }
352
353    #[test]
354    #[cfg(feature = "serde")]
355    fn test_serde_deserialize() {
356        use serde_json::from_str;
357        use Weekday::*;
358
359        let cases: Vec<(&str, Weekday)> = vec![
360            ("\"mon\"", Mon),
361            ("\"MONDAY\"", Mon),
362            ("\"MonDay\"", Mon),
363            ("\"mOn\"", Mon),
364            ("\"tue\"", Tue),
365            ("\"tuesday\"", Tue),
366            ("\"wed\"", Wed),
367            ("\"wednesday\"", Wed),
368            ("\"thu\"", Thu),
369            ("\"thursday\"", Thu),
370            ("\"fri\"", Fri),
371            ("\"friday\"", Fri),
372            ("\"sat\"", Sat),
373            ("\"saturday\"", Sat),
374            ("\"sun\"", Sun),
375            ("\"sunday\"", Sun),
376        ];
377
378        for (str, expected_weekday) in cases {
379            let weekday = from_str::<Weekday>(str).unwrap();
380            assert_eq!(weekday, expected_weekday);
381        }
382
383        let errors: Vec<&str> =
384            vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
385
386        for str in errors {
387            from_str::<Weekday>(str).unwrap_err();
388        }
389    }
390
391    #[test]
392    #[cfg(feature = "rkyv-validation")]
393    fn test_rkyv_validation() {
394        let mon = Weekday::Mon;
395        let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
396
397        assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
398    }
399}