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