chrono/
weekday.rs

1use core::fmt;
2
3#[cfg(feature = "rkyv")]
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(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
35#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
36pub enum Weekday {
37    /// Monday.
38    Mon = 0,
39    /// Tuesday.
40    Tue = 1,
41    /// Wednesday.
42    Wed = 2,
43    /// Thursday.
44    Thu = 3,
45    /// Friday.
46    Fri = 4,
47    /// Saturday.
48    Sat = 5,
49    /// Sunday.
50    Sun = 6,
51}
52
53impl Weekday {
54    /// The next day in the week.
55    ///
56    /// `w`:        | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
57    /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
58    /// `w.succ()`: | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` | `Mon`
59    #[inline]
60    #[must_use]
61    pub const fn succ(&self) -> Weekday {
62        match *self {
63            Weekday::Mon => Weekday::Tue,
64            Weekday::Tue => Weekday::Wed,
65            Weekday::Wed => Weekday::Thu,
66            Weekday::Thu => Weekday::Fri,
67            Weekday::Fri => Weekday::Sat,
68            Weekday::Sat => Weekday::Sun,
69            Weekday::Sun => Weekday::Mon,
70        }
71    }
72
73    /// The previous day in the week.
74    ///
75    /// `w`:        | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
76    /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
77    /// `w.pred()`: | `Sun` | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat`
78    #[inline]
79    #[must_use]
80    pub const fn pred(&self) -> Weekday {
81        match *self {
82            Weekday::Mon => Weekday::Sun,
83            Weekday::Tue => Weekday::Mon,
84            Weekday::Wed => Weekday::Tue,
85            Weekday::Thu => Weekday::Wed,
86            Weekday::Fri => Weekday::Thu,
87            Weekday::Sat => Weekday::Fri,
88            Weekday::Sun => Weekday::Sat,
89        }
90    }
91
92    /// Returns a day-of-week number starting from Monday = 1. (ISO 8601 weekday number)
93    ///
94    /// `w`:                      | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
95    /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
96    /// `w.number_from_monday()`: | 1     | 2     | 3     | 4     | 5     | 6     | 7
97    #[inline]
98    pub const fn number_from_monday(&self) -> u32 {
99        self.num_days_from(Weekday::Mon) + 1
100    }
101
102    /// Returns a day-of-week number starting from Sunday = 1.
103    ///
104    /// `w`:                      | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
105    /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
106    /// `w.number_from_sunday()`: | 2     | 3     | 4     | 5     | 6     | 7     | 1
107    #[inline]
108    pub const fn number_from_sunday(&self) -> u32 {
109        self.num_days_from(Weekday::Sun) + 1
110    }
111
112    /// Returns a day-of-week number starting from Monday = 0.
113    ///
114    /// `w`:                        | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
115    /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
116    /// `w.num_days_from_monday()`: | 0     | 1     | 2     | 3     | 4     | 5     | 6
117    ///
118    /// # Example
119    ///
120    #[cfg_attr(not(feature = "clock"), doc = "```ignore")]
121    #[cfg_attr(feature = "clock", doc = "```rust")]
122    /// # use chrono::{Local, Datelike};
123    /// // MTWRFSU is occasionally used as a single-letter abbreviation of the weekdays.
124    /// // Use `num_days_from_monday` to index into the array.
125    /// const MTWRFSU: [char; 7] = ['M', 'T', 'W', 'R', 'F', 'S', 'U'];
126    ///
127    /// let today = Local::now().weekday();
128    /// println!("{}", MTWRFSU[today.num_days_from_monday() as usize]);
129    /// ```
130    #[inline]
131    pub const fn num_days_from_monday(&self) -> u32 {
132        self.num_days_from(Weekday::Mon)
133    }
134
135    /// Returns a day-of-week number starting from Sunday = 0.
136    ///
137    /// `w`:                        | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
138    /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
139    /// `w.num_days_from_sunday()`: | 1     | 2     | 3     | 4     | 5     | 6     | 0
140    #[inline]
141    pub const fn num_days_from_sunday(&self) -> u32 {
142        self.num_days_from(Weekday::Sun)
143    }
144
145    /// Returns a day-of-week number starting from the parameter `day` (D) = 0.
146    ///
147    /// `w`:                        | `D`   | `D+1` | `D+2` | `D+3` | `D+4` | `D+5` | `D+6`
148    /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
149    /// `w.num_days_from(wd)`:      | 0     | 1     | 2     | 3     | 4     | 5     | 6
150    #[inline]
151    pub(crate) const fn num_days_from(&self, day: Weekday) -> u32 {
152        (*self as u32 + 7 - day as u32) % 7
153    }
154}
155
156impl fmt::Display for Weekday {
157    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158        f.write_str(match *self {
159            Weekday::Mon => "Mon",
160            Weekday::Tue => "Tue",
161            Weekday::Wed => "Wed",
162            Weekday::Thu => "Thu",
163            Weekday::Fri => "Fri",
164            Weekday::Sat => "Sat",
165            Weekday::Sun => "Sun",
166        })
167    }
168}
169
170/// Any weekday can be represented as an integer from 0 to 6, which equals to
171/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
172/// Do not heavily depend on this though; use explicit methods whenever possible.
173impl TryFrom<u8> for Weekday {
174    type Error = OutOfRange;
175
176    fn try_from(value: u8) -> Result<Self, Self::Error> {
177        match value {
178            0 => Ok(Weekday::Mon),
179            1 => Ok(Weekday::Tue),
180            2 => Ok(Weekday::Wed),
181            3 => Ok(Weekday::Thu),
182            4 => Ok(Weekday::Fri),
183            5 => Ok(Weekday::Sat),
184            6 => Ok(Weekday::Sun),
185            _ => Err(OutOfRange::new()),
186        }
187    }
188}
189
190/// Any weekday can be represented as an integer from 0 to 6, which equals to
191/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
192/// Do not heavily depend on this though; use explicit methods whenever possible.
193impl num_traits::FromPrimitive for Weekday {
194    #[inline]
195    fn from_i64(n: i64) -> Option<Weekday> {
196        match n {
197            0 => Some(Weekday::Mon),
198            1 => Some(Weekday::Tue),
199            2 => Some(Weekday::Wed),
200            3 => Some(Weekday::Thu),
201            4 => Some(Weekday::Fri),
202            5 => Some(Weekday::Sat),
203            6 => Some(Weekday::Sun),
204            _ => None,
205        }
206    }
207
208    #[inline]
209    fn from_u64(n: u64) -> Option<Weekday> {
210        match n {
211            0 => Some(Weekday::Mon),
212            1 => Some(Weekday::Tue),
213            2 => Some(Weekday::Wed),
214            3 => Some(Weekday::Thu),
215            4 => Some(Weekday::Fri),
216            5 => Some(Weekday::Sat),
217            6 => Some(Weekday::Sun),
218            _ => None,
219        }
220    }
221}
222
223/// An error resulting from reading `Weekday` value with `FromStr`.
224#[derive(Clone, PartialEq, Eq)]
225pub struct ParseWeekdayError {
226    pub(crate) _dummy: (),
227}
228
229#[cfg(feature = "std")]
230#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
231impl std::error::Error for ParseWeekdayError {}
232
233impl fmt::Display for ParseWeekdayError {
234    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
235        f.write_fmt(format_args!("{:?}", self))
236    }
237}
238
239impl fmt::Debug for ParseWeekdayError {
240    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241        write!(f, "ParseWeekdayError {{ .. }}")
242    }
243}
244
245// the actual `FromStr` implementation is in the `format` module to leverage the existing code
246
247#[cfg(feature = "serde")]
248#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
249mod weekday_serde {
250    use super::Weekday;
251    use core::fmt;
252    use serde::{de, ser};
253
254    impl ser::Serialize for Weekday {
255        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
256        where
257            S: ser::Serializer,
258        {
259            serializer.collect_str(&self)
260        }
261    }
262
263    struct WeekdayVisitor;
264
265    impl<'de> de::Visitor<'de> for WeekdayVisitor {
266        type Value = Weekday;
267
268        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
269            f.write_str("Weekday")
270        }
271
272        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
273        where
274            E: de::Error,
275        {
276            value.parse().map_err(|_| E::custom("short or long weekday names expected"))
277        }
278    }
279
280    impl<'de> de::Deserialize<'de> for Weekday {
281        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
282        where
283            D: de::Deserializer<'de>,
284        {
285            deserializer.deserialize_str(WeekdayVisitor)
286        }
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::Weekday;
293
294    #[test]
295    fn test_num_days_from() {
296        for i in 0..7 {
297            let base_day = Weekday::try_from(i).unwrap();
298
299            assert_eq!(base_day.num_days_from_monday(), base_day.num_days_from(Weekday::Mon));
300            assert_eq!(base_day.num_days_from_sunday(), base_day.num_days_from(Weekday::Sun));
301
302            assert_eq!(base_day.num_days_from(base_day), 0);
303
304            assert_eq!(base_day.num_days_from(base_day.pred()), 1);
305            assert_eq!(base_day.num_days_from(base_day.pred().pred()), 2);
306            assert_eq!(base_day.num_days_from(base_day.pred().pred().pred()), 3);
307            assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred()), 4);
308            assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred().pred()), 5);
309            assert_eq!(
310                base_day.num_days_from(base_day.pred().pred().pred().pred().pred().pred()),
311                6
312            );
313
314            assert_eq!(base_day.num_days_from(base_day.succ()), 6);
315            assert_eq!(base_day.num_days_from(base_day.succ().succ()), 5);
316            assert_eq!(base_day.num_days_from(base_day.succ().succ().succ()), 4);
317            assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ()), 3);
318            assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ().succ()), 2);
319            assert_eq!(
320                base_day.num_days_from(base_day.succ().succ().succ().succ().succ().succ()),
321                1
322            );
323        }
324    }
325
326    #[test]
327    #[cfg(feature = "serde")]
328    fn test_serde_serialize() {
329        use serde_json::to_string;
330        use Weekday::*;
331
332        let cases: Vec<(Weekday, &str)> = vec![
333            (Mon, "\"Mon\""),
334            (Tue, "\"Tue\""),
335            (Wed, "\"Wed\""),
336            (Thu, "\"Thu\""),
337            (Fri, "\"Fri\""),
338            (Sat, "\"Sat\""),
339            (Sun, "\"Sun\""),
340        ];
341
342        for (weekday, expected_str) in cases {
343            let string = to_string(&weekday).unwrap();
344            assert_eq!(string, expected_str);
345        }
346    }
347
348    #[test]
349    #[cfg(feature = "serde")]
350    fn test_serde_deserialize() {
351        use serde_json::from_str;
352        use Weekday::*;
353
354        let cases: Vec<(&str, Weekday)> = vec![
355            ("\"mon\"", Mon),
356            ("\"MONDAY\"", Mon),
357            ("\"MonDay\"", Mon),
358            ("\"mOn\"", Mon),
359            ("\"tue\"", Tue),
360            ("\"tuesday\"", Tue),
361            ("\"wed\"", Wed),
362            ("\"wednesday\"", Wed),
363            ("\"thu\"", Thu),
364            ("\"thursday\"", Thu),
365            ("\"fri\"", Fri),
366            ("\"friday\"", Fri),
367            ("\"sat\"", Sat),
368            ("\"saturday\"", Sat),
369            ("\"sun\"", Sun),
370            ("\"sunday\"", Sun),
371        ];
372
373        for (str, expected_weekday) in cases {
374            let weekday = from_str::<Weekday>(str).unwrap();
375            assert_eq!(weekday, expected_weekday);
376        }
377
378        let errors: Vec<&str> =
379            vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
380
381        for str in errors {
382            from_str::<Weekday>(str).unwrap_err();
383        }
384    }
385}