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