/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 | | } |