1use core::fmt;
2
3#[cfg(feature = "rkyv")]
4use rkyv::{Archive, Deserialize, Serialize};
5
6use crate::OutOfRange;
7
8#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord)]
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 = 0,
38 February = 1,
40 March = 2,
42 April = 3,
44 May = 4,
46 June = 5,
48 July = 6,
50 August = 7,
52 September = 8,
54 October = 9,
56 November = 10,
58 December = 11,
60}
61
62impl Month {
63 #[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 #[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 #[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 #[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 #[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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
222#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
223pub struct Months(pub(crate) u32);
224
225impl Months {
226 pub const fn new(num: u32) -> Self {
228 Self(num)
229 }
230}
231
232#[derive(Clone, PartialEq, Eq)]
234pub struct ParseMonthError {
235 pub(crate) _dummy: (),
236}
237
238#[cfg(feature = "std")]
239#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
240impl std::error::Error for ParseMonthError {}
241
242impl fmt::Display for ParseMonthError {
243 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
244 write!(f, "ParseMonthError {{ .. }}")
245 }
246}
247
248impl fmt::Debug for ParseMonthError {
249 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250 write!(f, "ParseMonthError {{ .. }}")
251 }
252}
253
254#[cfg(feature = "serde")]
255#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
256mod month_serde {
257 use super::Month;
258 use serde::{de, ser};
259
260 use core::fmt;
261
262 impl ser::Serialize for Month {
263 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
264 where
265 S: ser::Serializer,
266 {
267 serializer.collect_str(self.name())
268 }
269 }
270
271 struct MonthVisitor;
272
273 impl<'de> de::Visitor<'de> for MonthVisitor {
274 type Value = Month;
275
276 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
277 f.write_str("Month")
278 }
279
280 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
281 where
282 E: de::Error,
283 {
284 value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected"))
285 }
286 }
287
288 impl<'de> de::Deserialize<'de> for Month {
289 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
290 where
291 D: de::Deserializer<'de>,
292 {
293 deserializer.deserialize_str(MonthVisitor)
294 }
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::Month;
301 use crate::{Datelike, OutOfRange, TimeZone, Utc};
302
303 #[test]
304 fn test_month_enum_try_from() {
305 assert_eq!(Month::try_from(1), Ok(Month::January));
306 assert_eq!(Month::try_from(2), Ok(Month::February));
307 assert_eq!(Month::try_from(12), Ok(Month::December));
308 assert_eq!(Month::try_from(13), Err(OutOfRange::new()));
309
310 let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
311 assert_eq!(Month::try_from(date.month() as u8), Ok(Month::October));
312
313 let month = Month::January;
314 let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
315 assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
316 }
317
318 #[test]
319 fn test_month_enum_primitive_parse() {
320 use num_traits::FromPrimitive;
321
322 let jan_opt = Month::from_u32(1);
323 let feb_opt = Month::from_u64(2);
324 let dec_opt = Month::from_i64(12);
325 let no_month = Month::from_u32(13);
326 assert_eq!(jan_opt, Some(Month::January));
327 assert_eq!(feb_opt, Some(Month::February));
328 assert_eq!(dec_opt, Some(Month::December));
329 assert_eq!(no_month, None);
330
331 let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
332 assert_eq!(Month::from_u32(date.month()), Some(Month::October));
333
334 let month = Month::January;
335 let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
336 assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
337 }
338
339 #[test]
340 fn test_month_enum_succ_pred() {
341 assert_eq!(Month::January.succ(), Month::February);
342 assert_eq!(Month::December.succ(), Month::January);
343 assert_eq!(Month::January.pred(), Month::December);
344 assert_eq!(Month::February.pred(), Month::January);
345 }
346
347 #[test]
348 fn test_month_partial_ord() {
349 assert!(Month::January <= Month::January);
350 assert!(Month::January < Month::February);
351 assert!(Month::January < Month::December);
352 assert!(Month::July >= Month::May);
353 assert!(Month::September > Month::March);
354 }
355
356 #[test]
357 #[cfg(feature = "serde")]
358 fn test_serde_serialize() {
359 use serde_json::to_string;
360 use Month::*;
361
362 let cases: Vec<(Month, &str)> = vec![
363 (January, "\"January\""),
364 (February, "\"February\""),
365 (March, "\"March\""),
366 (April, "\"April\""),
367 (May, "\"May\""),
368 (June, "\"June\""),
369 (July, "\"July\""),
370 (August, "\"August\""),
371 (September, "\"September\""),
372 (October, "\"October\""),
373 (November, "\"November\""),
374 (December, "\"December\""),
375 ];
376
377 for (month, expected_str) in cases {
378 let string = to_string(&month).unwrap();
379 assert_eq!(string, expected_str);
380 }
381 }
382
383 #[test]
384 #[cfg(feature = "serde")]
385 fn test_serde_deserialize() {
386 use serde_json::from_str;
387 use Month::*;
388
389 let cases: Vec<(&str, Month)> = vec![
390 ("\"january\"", January),
391 ("\"jan\"", January),
392 ("\"FeB\"", February),
393 ("\"MAR\"", March),
394 ("\"mar\"", March),
395 ("\"april\"", April),
396 ("\"may\"", May),
397 ("\"june\"", June),
398 ("\"JULY\"", July),
399 ("\"august\"", August),
400 ("\"september\"", September),
401 ("\"October\"", October),
402 ("\"November\"", November),
403 ("\"DECEmbEr\"", December),
404 ];
405
406 for (string, expected_month) in cases {
407 let month = from_str::<Month>(string).unwrap();
408 assert_eq!(month, expected_month);
409 }
410
411 let errors: Vec<&str> =
412 vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""];
413
414 for string in errors {
415 from_str::<Month>(string).unwrap_err();
416 }
417 }
418}