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 month of the year. 9 /// 10 /// This enum is just a convenience implementation. 11 /// The month in dates created by DateLike objects does not return this enum. 12 /// 13 /// It is possible to convert from a date to a month independently 14 /// ``` 15 /// use chrono::prelude::*; 16 /// let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap(); 17 /// // `2019-10-28T09:10:11Z` 18 /// let month = Month::try_from(u8::try_from(date.month()).unwrap()).ok(); 19 /// assert_eq!(month, Some(Month::October)) 20 /// ``` 21 /// Or from a Month to an integer usable by dates 22 /// ``` 23 /// # use chrono::prelude::*; 24 /// let month = Month::January; 25 /// let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap(); 26 /// assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); 27 /// ``` 28 /// Allows mapping from and to month, from 1-January to 12-December. 29 /// Can be Serialized/Deserialized with serde 30 // Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior. 31 #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord)] 32 #[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] 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, PartialOrd)), 37 archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, 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 Month { 42 /// January 43 January = 0, 44 /// February 45 February = 1, 46 /// March 47 March = 2, 48 /// April 49 April = 3, 50 /// May 51 May = 4, 52 /// June 53 June = 5, 54 /// July 55 July = 6, 56 /// August 57 August = 7, 58 /// September 59 September = 8, 60 /// October 61 October = 9, 62 /// November 63 November = 10, 64 /// December 65 December = 11, 66 } 67 68 impl Month { 69 /// The next month. 70 /// 71 /// `m`: | `January` | `February` | `...` | `December` 72 /// ----------- | --------- | ---------- | --- | --------- 73 /// `m.succ()`: | `February` | `March` | `...` | `January` 74 #[inline] 75 #[must_use] succ(&self) -> Month76 pub const fn succ(&self) -> Month { 77 match *self { 78 Month::January => Month::February, 79 Month::February => Month::March, 80 Month::March => Month::April, 81 Month::April => Month::May, 82 Month::May => Month::June, 83 Month::June => Month::July, 84 Month::July => Month::August, 85 Month::August => Month::September, 86 Month::September => Month::October, 87 Month::October => Month::November, 88 Month::November => Month::December, 89 Month::December => Month::January, 90 } 91 } 92 93 /// The previous month. 94 /// 95 /// `m`: | `January` | `February` | `...` | `December` 96 /// ----------- | --------- | ---------- | --- | --------- 97 /// `m.pred()`: | `December` | `January` | `...` | `November` 98 #[inline] 99 #[must_use] pred(&self) -> Month100 pub const fn pred(&self) -> Month { 101 match *self { 102 Month::January => Month::December, 103 Month::February => Month::January, 104 Month::March => Month::February, 105 Month::April => Month::March, 106 Month::May => Month::April, 107 Month::June => Month::May, 108 Month::July => Month::June, 109 Month::August => Month::July, 110 Month::September => Month::August, 111 Month::October => Month::September, 112 Month::November => Month::October, 113 Month::December => Month::November, 114 } 115 } 116 117 /// Returns a month-of-year number starting from January = 1. 118 /// 119 /// `m`: | `January` | `February` | `...` | `December` 120 /// -------------------------| --------- | ---------- | --- | ----- 121 /// `m.number_from_month()`: | 1 | 2 | `...` | 12 122 #[inline] 123 #[must_use] number_from_month(&self) -> u32124 pub const fn number_from_month(&self) -> u32 { 125 match *self { 126 Month::January => 1, 127 Month::February => 2, 128 Month::March => 3, 129 Month::April => 4, 130 Month::May => 5, 131 Month::June => 6, 132 Month::July => 7, 133 Month::August => 8, 134 Month::September => 9, 135 Month::October => 10, 136 Month::November => 11, 137 Month::December => 12, 138 } 139 } 140 141 /// Get the name of the month 142 /// 143 /// ``` 144 /// use chrono::Month; 145 /// 146 /// assert_eq!(Month::January.name(), "January") 147 /// ``` 148 #[must_use] name(&self) -> &'static str149 pub const fn name(&self) -> &'static str { 150 match *self { 151 Month::January => "January", 152 Month::February => "February", 153 Month::March => "March", 154 Month::April => "April", 155 Month::May => "May", 156 Month::June => "June", 157 Month::July => "July", 158 Month::August => "August", 159 Month::September => "September", 160 Month::October => "October", 161 Month::November => "November", 162 Month::December => "December", 163 } 164 } 165 } 166 167 impl TryFrom<u8> for Month { 168 type Error = OutOfRange; 169 try_from(value: u8) -> Result<Self, Self::Error>170 fn try_from(value: u8) -> Result<Self, Self::Error> { 171 match value { 172 1 => Ok(Month::January), 173 2 => Ok(Month::February), 174 3 => Ok(Month::March), 175 4 => Ok(Month::April), 176 5 => Ok(Month::May), 177 6 => Ok(Month::June), 178 7 => Ok(Month::July), 179 8 => Ok(Month::August), 180 9 => Ok(Month::September), 181 10 => Ok(Month::October), 182 11 => Ok(Month::November), 183 12 => Ok(Month::December), 184 _ => Err(OutOfRange::new()), 185 } 186 } 187 } 188 189 impl num_traits::FromPrimitive for Month { 190 /// Returns an `Option<Month>` from a i64, assuming a 1-index, January = 1. 191 /// 192 /// `Month::from_i64(n: i64)`: | `1` | `2` | ... | `12` 193 /// ---------------------------| -------------------- | --------------------- | ... | ----- 194 /// ``: | Some(Month::January) | Some(Month::February) | ... | Some(Month::December) 195 196 #[inline] from_u64(n: u64) -> Option<Month>197 fn from_u64(n: u64) -> Option<Month> { 198 Self::from_u32(n as u32) 199 } 200 201 #[inline] from_i64(n: i64) -> Option<Month>202 fn from_i64(n: i64) -> Option<Month> { 203 Self::from_u32(n as u32) 204 } 205 206 #[inline] from_u32(n: u32) -> Option<Month>207 fn from_u32(n: u32) -> Option<Month> { 208 match n { 209 1 => Some(Month::January), 210 2 => Some(Month::February), 211 3 => Some(Month::March), 212 4 => Some(Month::April), 213 5 => Some(Month::May), 214 6 => Some(Month::June), 215 7 => Some(Month::July), 216 8 => Some(Month::August), 217 9 => Some(Month::September), 218 10 => Some(Month::October), 219 11 => Some(Month::November), 220 12 => Some(Month::December), 221 _ => None, 222 } 223 } 224 } 225 226 /// A duration in calendar months 227 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] 228 #[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))] 229 pub struct Months(pub(crate) u32); 230 231 impl Months { 232 /// Construct a new `Months` from a number of months new(num: u32) -> Self233 pub const fn new(num: u32) -> Self { 234 Self(num) 235 } 236 237 /// Returns the total number of months in the `Months` instance. 238 #[inline] as_u32(&self) -> u32239 pub const fn as_u32(&self) -> u32 { 240 self.0 241 } 242 } 243 244 /// An error resulting from reading `<Month>` value with `FromStr`. 245 #[derive(Clone, PartialEq, Eq)] 246 pub struct ParseMonthError { 247 pub(crate) _dummy: (), 248 } 249 250 #[cfg(feature = "std")] 251 impl std::error::Error for ParseMonthError {} 252 253 impl fmt::Display for ParseMonthError { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result254 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 255 write!(f, "ParseMonthError {{ .. }}") 256 } 257 } 258 259 impl fmt::Debug for ParseMonthError { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result260 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 261 write!(f, "ParseMonthError {{ .. }}") 262 } 263 } 264 265 #[cfg(feature = "serde")] 266 mod month_serde { 267 use super::Month; 268 use serde::{de, ser}; 269 270 use core::fmt; 271 272 impl ser::Serialize for Month { serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: ser::Serializer,273 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 274 where 275 S: ser::Serializer, 276 { 277 serializer.collect_str(self.name()) 278 } 279 } 280 281 struct MonthVisitor; 282 283 impl<'de> de::Visitor<'de> for MonthVisitor { 284 type Value = Month; 285 expecting(&self, f: &mut fmt::Formatter) -> fmt::Result286 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { 287 f.write_str("Month") 288 } 289 visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: de::Error,290 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> 291 where 292 E: de::Error, 293 { 294 value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected")) 295 } 296 } 297 298 impl<'de> de::Deserialize<'de> for Month { deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: de::Deserializer<'de>,299 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 300 where 301 D: de::Deserializer<'de>, 302 { 303 deserializer.deserialize_str(MonthVisitor) 304 } 305 } 306 } 307 308 #[cfg(test)] 309 mod tests { 310 use super::Month; 311 use crate::{Datelike, Months, OutOfRange, TimeZone, Utc}; 312 313 #[test] test_month_enum_try_from()314 fn test_month_enum_try_from() { 315 assert_eq!(Month::try_from(1), Ok(Month::January)); 316 assert_eq!(Month::try_from(2), Ok(Month::February)); 317 assert_eq!(Month::try_from(12), Ok(Month::December)); 318 assert_eq!(Month::try_from(13), Err(OutOfRange::new())); 319 320 let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap(); 321 assert_eq!(Month::try_from(date.month() as u8), Ok(Month::October)); 322 323 let month = Month::January; 324 let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap(); 325 assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); 326 } 327 328 #[test] test_month_enum_primitive_parse()329 fn test_month_enum_primitive_parse() { 330 use num_traits::FromPrimitive; 331 332 let jan_opt = Month::from_u32(1); 333 let feb_opt = Month::from_u64(2); 334 let dec_opt = Month::from_i64(12); 335 let no_month = Month::from_u32(13); 336 assert_eq!(jan_opt, Some(Month::January)); 337 assert_eq!(feb_opt, Some(Month::February)); 338 assert_eq!(dec_opt, Some(Month::December)); 339 assert_eq!(no_month, None); 340 341 let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap(); 342 assert_eq!(Month::from_u32(date.month()), Some(Month::October)); 343 344 let month = Month::January; 345 let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap(); 346 assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); 347 } 348 349 #[test] test_month_enum_succ_pred()350 fn test_month_enum_succ_pred() { 351 assert_eq!(Month::January.succ(), Month::February); 352 assert_eq!(Month::December.succ(), Month::January); 353 assert_eq!(Month::January.pred(), Month::December); 354 assert_eq!(Month::February.pred(), Month::January); 355 } 356 357 #[test] test_month_partial_ord()358 fn test_month_partial_ord() { 359 assert!(Month::January <= Month::January); 360 assert!(Month::January < Month::February); 361 assert!(Month::January < Month::December); 362 assert!(Month::July >= Month::May); 363 assert!(Month::September > Month::March); 364 } 365 366 #[test] test_months_as_u32()367 fn test_months_as_u32() { 368 assert_eq!(Months::new(0).as_u32(), 0); 369 assert_eq!(Months::new(1).as_u32(), 1); 370 assert_eq!(Months::new(u32::MAX).as_u32(), u32::MAX); 371 } 372 373 #[test] 374 #[cfg(feature = "serde")] test_serde_serialize()375 fn test_serde_serialize() { 376 use serde_json::to_string; 377 use Month::*; 378 379 let cases: Vec<(Month, &str)> = vec![ 380 (January, "\"January\""), 381 (February, "\"February\""), 382 (March, "\"March\""), 383 (April, "\"April\""), 384 (May, "\"May\""), 385 (June, "\"June\""), 386 (July, "\"July\""), 387 (August, "\"August\""), 388 (September, "\"September\""), 389 (October, "\"October\""), 390 (November, "\"November\""), 391 (December, "\"December\""), 392 ]; 393 394 for (month, expected_str) in cases { 395 let string = to_string(&month).unwrap(); 396 assert_eq!(string, expected_str); 397 } 398 } 399 400 #[test] 401 #[cfg(feature = "serde")] test_serde_deserialize()402 fn test_serde_deserialize() { 403 use serde_json::from_str; 404 use Month::*; 405 406 let cases: Vec<(&str, Month)> = vec![ 407 ("\"january\"", January), 408 ("\"jan\"", January), 409 ("\"FeB\"", February), 410 ("\"MAR\"", March), 411 ("\"mar\"", March), 412 ("\"april\"", April), 413 ("\"may\"", May), 414 ("\"june\"", June), 415 ("\"JULY\"", July), 416 ("\"august\"", August), 417 ("\"september\"", September), 418 ("\"October\"", October), 419 ("\"November\"", November), 420 ("\"DECEmbEr\"", December), 421 ]; 422 423 for (string, expected_month) in cases { 424 let month = from_str::<Month>(string).unwrap(); 425 assert_eq!(month, expected_month); 426 } 427 428 let errors: Vec<&str> = 429 vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""]; 430 431 for string in errors { 432 from_str::<Month>(string).unwrap_err(); 433 } 434 } 435 436 #[test] 437 #[cfg(feature = "rkyv-validation")] test_rkyv_validation()438 fn test_rkyv_validation() { 439 let month = Month::January; 440 let bytes = rkyv::to_bytes::<_, 1>(&month).unwrap(); 441 assert_eq!(rkyv::from_bytes::<Month>(&bytes).unwrap(), month); 442 } 443 } 444