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