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