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