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;
7use crate::naive::NaiveDate;
8
9#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord)]
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))]
41pub enum Month {
42 January = 0,
44 February = 1,
46 March = 2,
48 April = 3,
50 May = 4,
52 June = 5,
54 July = 6,
56 August = 7,
58 September = 8,
60 October = 9,
62 November = 10,
64 December = 11,
66}
67
68impl Month {
69 #[inline]
75 #[must_use]
76 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 #[inline]
99 #[must_use]
100 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 #[inline]
123 #[must_use]
124 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 #[must_use]
149 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 pub fn num_days(&self, year: i32) -> Option<u8> {
170 Some(match *self {
171 Month::January => 31,
172 Month::February => match NaiveDate::from_ymd_opt(year, 2, 1)?.leap_year() {
173 true => 29,
174 false => 28,
175 },
176 Month::March => 31,
177 Month::April => 30,
178 Month::May => 31,
179 Month::June => 30,
180 Month::July => 31,
181 Month::August => 31,
182 Month::September => 30,
183 Month::October => 31,
184 Month::November => 30,
185 Month::December => 31,
186 })
187 }
188}
189
190impl TryFrom<u8> for Month {
191 type Error = OutOfRange;
192
193 fn try_from(value: u8) -> Result<Self, Self::Error> {
194 match value {
195 1 => Ok(Month::January),
196 2 => Ok(Month::February),
197 3 => Ok(Month::March),
198 4 => Ok(Month::April),
199 5 => Ok(Month::May),
200 6 => Ok(Month::June),
201 7 => Ok(Month::July),
202 8 => Ok(Month::August),
203 9 => Ok(Month::September),
204 10 => Ok(Month::October),
205 11 => Ok(Month::November),
206 12 => Ok(Month::December),
207 _ => Err(OutOfRange::new()),
208 }
209 }
210}
211
212impl num_traits::FromPrimitive for Month {
213 #[inline]
219 fn from_u64(n: u64) -> Option<Month> {
220 Self::from_u32(n as u32)
221 }
222
223 #[inline]
224 fn from_i64(n: i64) -> Option<Month> {
225 Self::from_u32(n as u32)
226 }
227
228 #[inline]
229 fn from_u32(n: u32) -> Option<Month> {
230 match n {
231 1 => Some(Month::January),
232 2 => Some(Month::February),
233 3 => Some(Month::March),
234 4 => Some(Month::April),
235 5 => Some(Month::May),
236 6 => Some(Month::June),
237 7 => Some(Month::July),
238 8 => Some(Month::August),
239 9 => Some(Month::September),
240 10 => Some(Month::October),
241 11 => Some(Month::November),
242 12 => Some(Month::December),
243 _ => None,
244 }
245 }
246}
247
248#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
250#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
251pub struct Months(pub(crate) u32);
252
253impl Months {
254 pub const fn new(num: u32) -> Self {
256 Self(num)
257 }
258
259 #[inline]
261 pub const fn as_u32(&self) -> u32 {
262 self.0
263 }
264}
265
266#[derive(Clone, PartialEq, Eq)]
268pub struct ParseMonthError {
269 pub(crate) _dummy: (),
270}
271
272#[cfg(feature = "std")]
273impl std::error::Error for ParseMonthError {}
274
275#[cfg(all(not(feature = "std"), feature = "core-error"))]
276impl core::error::Error for ParseMonthError {}
277
278impl fmt::Display for ParseMonthError {
279 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280 write!(f, "ParseMonthError {{ .. }}")
281 }
282}
283
284impl fmt::Debug for ParseMonthError {
285 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286 write!(f, "ParseMonthError {{ .. }}")
287 }
288}
289
290#[cfg(feature = "serde")]
291mod month_serde {
292 use super::Month;
293 use serde::{de, ser};
294
295 use core::fmt;
296
297 impl ser::Serialize for Month {
298 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
299 where
300 S: ser::Serializer,
301 {
302 serializer.collect_str(self.name())
303 }
304 }
305
306 struct MonthVisitor;
307
308 impl de::Visitor<'_> for MonthVisitor {
309 type Value = Month;
310
311 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
312 f.write_str("Month")
313 }
314
315 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
316 where
317 E: de::Error,
318 {
319 value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected"))
320 }
321 }
322
323 impl<'de> de::Deserialize<'de> for Month {
324 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
325 where
326 D: de::Deserializer<'de>,
327 {
328 deserializer.deserialize_str(MonthVisitor)
329 }
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::Month;
336 use crate::{Datelike, Months, OutOfRange, TimeZone, Utc};
337
338 #[test]
339 fn test_month_enum_try_from() {
340 assert_eq!(Month::try_from(1), Ok(Month::January));
341 assert_eq!(Month::try_from(2), Ok(Month::February));
342 assert_eq!(Month::try_from(12), Ok(Month::December));
343 assert_eq!(Month::try_from(13), Err(OutOfRange::new()));
344
345 let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
346 assert_eq!(Month::try_from(date.month() as u8), Ok(Month::October));
347
348 let month = Month::January;
349 let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
350 assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
351 }
352
353 #[test]
354 fn test_month_enum_primitive_parse() {
355 use num_traits::FromPrimitive;
356
357 let jan_opt = Month::from_u32(1);
358 let feb_opt = Month::from_u64(2);
359 let dec_opt = Month::from_i64(12);
360 let no_month = Month::from_u32(13);
361 assert_eq!(jan_opt, Some(Month::January));
362 assert_eq!(feb_opt, Some(Month::February));
363 assert_eq!(dec_opt, Some(Month::December));
364 assert_eq!(no_month, None);
365
366 let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
367 assert_eq!(Month::from_u32(date.month()), Some(Month::October));
368
369 let month = Month::January;
370 let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
371 assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
372 }
373
374 #[test]
375 fn test_month_enum_succ_pred() {
376 assert_eq!(Month::January.succ(), Month::February);
377 assert_eq!(Month::December.succ(), Month::January);
378 assert_eq!(Month::January.pred(), Month::December);
379 assert_eq!(Month::February.pred(), Month::January);
380 }
381
382 #[test]
383 fn test_month_partial_ord() {
384 assert!(Month::January <= Month::January);
385 assert!(Month::January < Month::February);
386 assert!(Month::January < Month::December);
387 assert!(Month::July >= Month::May);
388 assert!(Month::September > Month::March);
389 }
390
391 #[test]
392 fn test_months_as_u32() {
393 assert_eq!(Months::new(0).as_u32(), 0);
394 assert_eq!(Months::new(1).as_u32(), 1);
395 assert_eq!(Months::new(u32::MAX).as_u32(), u32::MAX);
396 }
397
398 #[test]
399 #[cfg(feature = "serde")]
400 fn test_serde_serialize() {
401 use Month::*;
402 use serde_json::to_string;
403
404 let cases: Vec<(Month, &str)> = vec![
405 (January, "\"January\""),
406 (February, "\"February\""),
407 (March, "\"March\""),
408 (April, "\"April\""),
409 (May, "\"May\""),
410 (June, "\"June\""),
411 (July, "\"July\""),
412 (August, "\"August\""),
413 (September, "\"September\""),
414 (October, "\"October\""),
415 (November, "\"November\""),
416 (December, "\"December\""),
417 ];
418
419 for (month, expected_str) in cases {
420 let string = to_string(&month).unwrap();
421 assert_eq!(string, expected_str);
422 }
423 }
424
425 #[test]
426 #[cfg(feature = "serde")]
427 fn test_serde_deserialize() {
428 use Month::*;
429 use serde_json::from_str;
430
431 let cases: Vec<(&str, Month)> = vec![
432 ("\"january\"", January),
433 ("\"jan\"", January),
434 ("\"FeB\"", February),
435 ("\"MAR\"", March),
436 ("\"mar\"", March),
437 ("\"april\"", April),
438 ("\"may\"", May),
439 ("\"june\"", June),
440 ("\"JULY\"", July),
441 ("\"august\"", August),
442 ("\"september\"", September),
443 ("\"October\"", October),
444 ("\"November\"", November),
445 ("\"DECEmbEr\"", December),
446 ];
447
448 for (string, expected_month) in cases {
449 let month = from_str::<Month>(string).unwrap();
450 assert_eq!(month, expected_month);
451 }
452
453 let errors: Vec<&str> =
454 vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""];
455
456 for string in errors {
457 from_str::<Month>(string).unwrap_err();
458 }
459 }
460
461 #[test]
462 #[cfg(feature = "rkyv-validation")]
463 fn test_rkyv_validation() {
464 let month = Month::January;
465 let bytes = rkyv::to_bytes::<_, 1>(&month).unwrap();
466 assert_eq!(rkyv::from_bytes::<Month>(&bytes).unwrap(), month);
467 }
468
469 #[test]
470 fn num_days() {
471 assert_eq!(Month::January.num_days(2020), Some(31));
472 assert_eq!(Month::February.num_days(2020), Some(29));
473 assert_eq!(Month::February.num_days(2019), Some(28));
474 }
475}