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