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