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(
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))]
41pub 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
58impl 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]
66 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]
85 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]
103 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]
113 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]
137 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]
147 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 /// ```
161 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
172impl fmt::Display for Weekday {
173 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.
189impl TryFrom<u8> for Weekday {
190 type Error = OutOfRange;
191
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.
209impl num_traits::FromPrimitive for Weekday {
210 #[inline]
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]
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)]
241pub struct ParseWeekdayError {
242 pub(crate) _dummy: (),
243}
244
245#[cfg(feature = "std")]
246impl std::error::Error for ParseWeekdayError {}
247
248impl fmt::Display for ParseWeekdayError {
249 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250 f.write_fmt(format_args!("{:?}", self))
251 }
252}
253
254impl fmt::Debug for ParseWeekdayError {
255 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")]
263mod weekday_serde {
264 use super::Weekday;
265 use core::fmt;
266 use serde::{de, ser};
267
268 impl ser::Serialize for Weekday {
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
282 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
283 f.write_str("Weekday")
284 }
285
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 {
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)]
305mod tests {
306 use super::Weekday;
307
308 #[test]
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]
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")]
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")]
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")]
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