Coverage Report

Created: 2025-02-25 06:39

/rust/registry/src/index.crates.io-6f17d22bba15001f/chrono-0.4.39/src/month.rs
Line
Count
Source (jump to first uncovered line)
1
use core::fmt;
2
3
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
4
use rkyv::{Archive, Deserialize, Serialize};
5
6
use crate::OutOfRange;
7
8
/// The month of the year.
9
///
10
/// This enum is just a convenience implementation.
11
/// The month in dates created by DateLike objects does not return this enum.
12
///
13
/// It is possible to convert from a date to a month independently
14
/// ```
15
/// use chrono::prelude::*;
16
/// let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
17
/// // `2019-10-28T09:10:11Z`
18
/// let month = Month::try_from(u8::try_from(date.month()).unwrap()).ok();
19
/// assert_eq!(month, Some(Month::October))
20
/// ```
21
/// Or from a Month to an integer usable by dates
22
/// ```
23
/// # use chrono::prelude::*;
24
/// let month = Month::January;
25
/// let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
26
/// assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
27
/// ```
28
/// Allows mapping from and to month, from 1-January to 12-December.
29
/// Can be Serialized/Deserialized with serde
30
// Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior.
31
#[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))]
40
pub enum Month {
41
    /// January
42
    January = 0,
43
    /// February
44
    February = 1,
45
    /// March
46
    March = 2,
47
    /// April
48
    April = 3,
49
    /// May
50
    May = 4,
51
    /// June
52
    June = 5,
53
    /// July
54
    July = 6,
55
    /// August
56
    August = 7,
57
    /// September
58
    September = 8,
59
    /// October
60
    October = 9,
61
    /// November
62
    November = 10,
63
    /// December
64
    December = 11,
65
}
66
67
impl Month {
68
    /// The next month.
69
    ///
70
    /// `m`:        | `January`  | `February` | `...` | `December`
71
    /// ----------- | ---------  | ---------- | --- | ---------
72
    /// `m.succ()`: | `February` | `March`    | `...` | `January`
73
    #[inline]
74
    #[must_use]
75
0
    pub const fn succ(&self) -> Month {
76
0
        match *self {
77
0
            Month::January => Month::February,
78
0
            Month::February => Month::March,
79
0
            Month::March => Month::April,
80
0
            Month::April => Month::May,
81
0
            Month::May => Month::June,
82
0
            Month::June => Month::July,
83
0
            Month::July => Month::August,
84
0
            Month::August => Month::September,
85
0
            Month::September => Month::October,
86
0
            Month::October => Month::November,
87
0
            Month::November => Month::December,
88
0
            Month::December => Month::January,
89
        }
90
0
    }
91
92
    /// The previous month.
93
    ///
94
    /// `m`:        | `January`  | `February` | `...` | `December`
95
    /// ----------- | ---------  | ---------- | --- | ---------
96
    /// `m.pred()`: | `December` | `January`  | `...` | `November`
97
    #[inline]
98
    #[must_use]
99
0
    pub const fn pred(&self) -> Month {
100
0
        match *self {
101
0
            Month::January => Month::December,
102
0
            Month::February => Month::January,
103
0
            Month::March => Month::February,
104
0
            Month::April => Month::March,
105
0
            Month::May => Month::April,
106
0
            Month::June => Month::May,
107
0
            Month::July => Month::June,
108
0
            Month::August => Month::July,
109
0
            Month::September => Month::August,
110
0
            Month::October => Month::September,
111
0
            Month::November => Month::October,
112
0
            Month::December => Month::November,
113
        }
114
0
    }
115
116
    /// Returns a month-of-year number starting from January = 1.
117
    ///
118
    /// `m`:                     | `January` | `February` | `...` | `December`
119
    /// -------------------------| --------- | ---------- | --- | -----
120
    /// `m.number_from_month()`: | 1         | 2          | `...` | 12
121
    #[inline]
122
    #[must_use]
123
0
    pub const fn number_from_month(&self) -> u32 {
124
0
        match *self {
125
0
            Month::January => 1,
126
0
            Month::February => 2,
127
0
            Month::March => 3,
128
0
            Month::April => 4,
129
0
            Month::May => 5,
130
0
            Month::June => 6,
131
0
            Month::July => 7,
132
0
            Month::August => 8,
133
0
            Month::September => 9,
134
0
            Month::October => 10,
135
0
            Month::November => 11,
136
0
            Month::December => 12,
137
        }
138
0
    }
139
140
    /// Get the name of the month
141
    ///
142
    /// ```
143
    /// use chrono::Month;
144
    ///
145
    /// assert_eq!(Month::January.name(), "January")
146
    /// ```
147
    #[must_use]
148
0
    pub const fn name(&self) -> &'static str {
149
0
        match *self {
150
0
            Month::January => "January",
151
0
            Month::February => "February",
152
0
            Month::March => "March",
153
0
            Month::April => "April",
154
0
            Month::May => "May",
155
0
            Month::June => "June",
156
0
            Month::July => "July",
157
0
            Month::August => "August",
158
0
            Month::September => "September",
159
0
            Month::October => "October",
160
0
            Month::November => "November",
161
0
            Month::December => "December",
162
        }
163
0
    }
164
}
165
166
impl TryFrom<u8> for Month {
167
    type Error = OutOfRange;
168
169
0
    fn try_from(value: u8) -> Result<Self, Self::Error> {
170
0
        match value {
171
0
            1 => Ok(Month::January),
172
0
            2 => Ok(Month::February),
173
0
            3 => Ok(Month::March),
174
0
            4 => Ok(Month::April),
175
0
            5 => Ok(Month::May),
176
0
            6 => Ok(Month::June),
177
0
            7 => Ok(Month::July),
178
0
            8 => Ok(Month::August),
179
0
            9 => Ok(Month::September),
180
0
            10 => Ok(Month::October),
181
0
            11 => Ok(Month::November),
182
0
            12 => Ok(Month::December),
183
0
            _ => Err(OutOfRange::new()),
184
        }
185
0
    }
186
}
187
188
impl num_traits::FromPrimitive for Month {
189
    /// Returns an `Option<Month>` from a i64, assuming a 1-index, January = 1.
190
    ///
191
    /// `Month::from_i64(n: i64)`: | `1`                  | `2`                   | ... | `12`
192
    /// ---------------------------| -------------------- | --------------------- | ... | -----
193
    /// ``:                        | Some(Month::January) | Some(Month::February) | ... | Some(Month::December)
194
    #[inline]
195
0
    fn from_u64(n: u64) -> Option<Month> {
196
0
        Self::from_u32(n as u32)
197
0
    }
198
199
    #[inline]
200
0
    fn from_i64(n: i64) -> Option<Month> {
201
0
        Self::from_u32(n as u32)
202
0
    }
203
204
    #[inline]
205
0
    fn from_u32(n: u32) -> Option<Month> {
206
0
        match n {
207
0
            1 => Some(Month::January),
208
0
            2 => Some(Month::February),
209
0
            3 => Some(Month::March),
210
0
            4 => Some(Month::April),
211
0
            5 => Some(Month::May),
212
0
            6 => Some(Month::June),
213
0
            7 => Some(Month::July),
214
0
            8 => Some(Month::August),
215
0
            9 => Some(Month::September),
216
0
            10 => Some(Month::October),
217
0
            11 => Some(Month::November),
218
0
            12 => Some(Month::December),
219
0
            _ => None,
220
        }
221
0
    }
222
}
223
224
/// A duration in calendar months
225
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
226
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
227
pub struct Months(pub(crate) u32);
228
229
impl Months {
230
    /// Construct a new `Months` from a number of months
231
0
    pub const fn new(num: u32) -> Self {
232
0
        Self(num)
233
0
    }
234
235
    /// Returns the total number of months in the `Months` instance.
236
    #[inline]
237
0
    pub const fn as_u32(&self) -> u32 {
238
0
        self.0
239
0
    }
240
}
241
242
/// An error resulting from reading `<Month>` value with `FromStr`.
243
#[derive(Clone, PartialEq, Eq)]
244
pub struct ParseMonthError {
245
    pub(crate) _dummy: (),
246
}
247
248
#[cfg(feature = "std")]
249
impl std::error::Error for ParseMonthError {}
250
251
impl fmt::Display for ParseMonthError {
252
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
253
0
        write!(f, "ParseMonthError {{ .. }}")
254
0
    }
255
}
256
257
impl fmt::Debug for ParseMonthError {
258
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259
0
        write!(f, "ParseMonthError {{ .. }}")
260
0
    }
261
}
262
263
#[cfg(feature = "serde")]
264
mod 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)]
307
mod 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
}