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