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
275impl fmt::Display for ParseMonthError {
276 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277 write!(f, "ParseMonthError {{ .. }}")
278 }
279}
280
281impl fmt::Debug for ParseMonthError {
282 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283 write!(f, "ParseMonthError {{ .. }}")
284 }
285}
286
287#[cfg(feature = "serde")]
288mod month_serde {
289 use super::Month;
290 use serde::{de, ser};
291
292 use core::fmt;
293
294 impl ser::Serialize for Month {
295 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
296 where
297 S: ser::Serializer,
298 {
299 serializer.collect_str(self.name())
300 }
301 }
302
303 struct MonthVisitor;
304
305 impl de::Visitor<'_> for MonthVisitor {
306 type Value = Month;
307
308 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
309 f.write_str("Month")
310 }
311
312 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
313 where
314 E: de::Error,
315 {
316 value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected"))
317 }
318 }
319
320 impl<'de> de::Deserialize<'de> for Month {
321 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
322 where
323 D: de::Deserializer<'de>,
324 {
325 deserializer.deserialize_str(MonthVisitor)
326 }
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::Month;
333 use crate::{Datelike, Months, OutOfRange, TimeZone, Utc};
334
335 #[test]
336 fn test_month_enum_try_from() {
337 assert_eq!(Month::try_from(1), Ok(Month::January));
338 assert_eq!(Month::try_from(2), Ok(Month::February));
339 assert_eq!(Month::try_from(12), Ok(Month::December));
340 assert_eq!(Month::try_from(13), Err(OutOfRange::new()));
341
342 let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
343 assert_eq!(Month::try_from(date.month() as u8), Ok(Month::October));
344
345 let month = Month::January;
346 let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
347 assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
348 }
349
350 #[test]
351 fn test_month_enum_primitive_parse() {
352 use num_traits::FromPrimitive;
353
354 let jan_opt = Month::from_u32(1);
355 let feb_opt = Month::from_u64(2);
356 let dec_opt = Month::from_i64(12);
357 let no_month = Month::from_u32(13);
358 assert_eq!(jan_opt, Some(Month::January));
359 assert_eq!(feb_opt, Some(Month::February));
360 assert_eq!(dec_opt, Some(Month::December));
361 assert_eq!(no_month, None);
362
363 let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
364 assert_eq!(Month::from_u32(date.month()), Some(Month::October));
365
366 let month = Month::January;
367 let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
368 assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
369 }
370
371 #[test]
372 fn test_month_enum_succ_pred() {
373 assert_eq!(Month::January.succ(), Month::February);
374 assert_eq!(Month::December.succ(), Month::January);
375 assert_eq!(Month::January.pred(), Month::December);
376 assert_eq!(Month::February.pred(), Month::January);
377 }
378
379 #[test]
380 fn test_month_partial_ord() {
381 assert!(Month::January <= Month::January);
382 assert!(Month::January < Month::February);
383 assert!(Month::January < Month::December);
384 assert!(Month::July >= Month::May);
385 assert!(Month::September > Month::March);
386 }
387
388 #[test]
389 fn test_months_as_u32() {
390 assert_eq!(Months::new(0).as_u32(), 0);
391 assert_eq!(Months::new(1).as_u32(), 1);
392 assert_eq!(Months::new(u32::MAX).as_u32(), u32::MAX);
393 }
394
395 #[test]
396 #[cfg(feature = "serde")]
397 fn test_serde_serialize() {
398 use Month::*;
399 use serde_json::to_string;
400
401 let cases: Vec<(Month, &str)> = vec![
402 (January, "\"January\""),
403 (February, "\"February\""),
404 (March, "\"March\""),
405 (April, "\"April\""),
406 (May, "\"May\""),
407 (June, "\"June\""),
408 (July, "\"July\""),
409 (August, "\"August\""),
410 (September, "\"September\""),
411 (October, "\"October\""),
412 (November, "\"November\""),
413 (December, "\"December\""),
414 ];
415
416 for (month, expected_str) in cases {
417 let string = to_string(&month).unwrap();
418 assert_eq!(string, expected_str);
419 }
420 }
421
422 #[test]
423 #[cfg(feature = "serde")]
424 fn test_serde_deserialize() {
425 use Month::*;
426 use serde_json::from_str;
427
428 let cases: Vec<(&str, Month)> = vec![
429 ("\"january\"", January),
430 ("\"jan\"", January),
431 ("\"FeB\"", February),
432 ("\"MAR\"", March),
433 ("\"mar\"", March),
434 ("\"april\"", April),
435 ("\"may\"", May),
436 ("\"june\"", June),
437 ("\"JULY\"", July),
438 ("\"august\"", August),
439 ("\"september\"", September),
440 ("\"October\"", October),
441 ("\"November\"", November),
442 ("\"DECEmbEr\"", December),
443 ];
444
445 for (string, expected_month) in cases {
446 let month = from_str::<Month>(string).unwrap();
447 assert_eq!(month, expected_month);
448 }
449
450 let errors: Vec<&str> =
451 vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""];
452
453 for string in errors {
454 from_str::<Month>(string).unwrap_err();
455 }
456 }
457
458 #[test]
459 #[cfg(feature = "rkyv-validation")]
460 fn test_rkyv_validation() {
461 let month = Month::January;
462 let bytes = rkyv::to_bytes::<_, 1>(&month).unwrap();
463 assert_eq!(rkyv::from_bytes::<Month>(&bytes).unwrap(), month);
464 }
465
466 #[test]
467 fn num_days() {
468 assert_eq!(Month::January.num_days(2020), Some(31));
469 assert_eq!(Month::February.num_days(2020), Some(29));
470 assert_eq!(Month::February.num_days(2019), Some(28));
471 }
472}