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(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))]
42 pub 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 
59 impl 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]
succ(&self) -> Weekday67     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]
pred(&self) -> Weekday86     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]
number_from_monday(&self) -> u32104     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]
number_from_sunday(&self) -> u32114     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]
num_days_from_monday(&self) -> u32137     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]
num_days_from_sunday(&self) -> u32147     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]
num_days_from(&self, day: Weekday) -> u32157     pub(crate) const fn num_days_from(&self, day: Weekday) -> u32 {
158         (*self as u32 + 7 - day as u32) % 7
159     }
160 }
161 
162 impl fmt::Display for Weekday {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result163     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.
179 impl TryFrom<u8> for Weekday {
180     type Error = OutOfRange;
181 
try_from(value: u8) -> Result<Self, Self::Error>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.
199 impl num_traits::FromPrimitive for Weekday {
200     #[inline]
from_i64(n: i64) -> Option<Weekday>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]
from_u64(n: u64) -> Option<Weekday>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)]
231 pub struct ParseWeekdayError {
232     pub(crate) _dummy: (),
233 }
234 
235 #[cfg(feature = "std")]
236 impl std::error::Error for ParseWeekdayError {}
237 
238 impl fmt::Display for ParseWeekdayError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result239     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
240         f.write_fmt(format_args!("{:?}", self))
241     }
242 }
243 
244 impl fmt::Debug for ParseWeekdayError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result245     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")]
253 mod weekday_serde {
254     use super::Weekday;
255     use core::fmt;
256     use serde::{de, ser};
257 
258     impl ser::Serialize for Weekday {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: ser::Serializer,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 
expecting(&self, f: &mut fmt::Formatter) -> fmt::Result272         fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
273             f.write_str("Weekday")
274         }
275 
visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: de::Error,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 {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: de::Deserializer<'de>,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)]
295 mod tests {
296     use super::Weekday;
297 
298     #[test]
test_num_days_from()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")]
test_serde_serialize()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")]
test_serde_deserialize()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")]
test_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 }
399