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