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