devela/phys/time/calendar/
month.rs

1// devela::phys::time::calendar::month
2//
3//!
4//
5
6use crate::{Display, FmtResult, Formatter, FromStr};
7#[allow(clippy::enum_glob_use)]
8use Month::*;
9
10#[doc = crate::TAG_TIME!()]
11/// The months of the year.
12#[repr(u8)]
13#[allow(missing_docs)]
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
15pub enum Month {
16    January = 0,
17    February,
18    March,
19    April,
20    May,
21    June,
22    July,
23    August,
24    September,
25    October,
26    November,
27    December,
28}
29
30impl Month {
31    /// The number of months in a year.
32    pub const COUNT: usize = 12;
33
34    /// Returns the length in days of the current month, taking into account
35    /// whether it's a `leap` year, for february.
36    #[allow(clippy::len_without_is_empty)]
37    pub const fn len(self, leap: bool) -> u8 {
38        match self {
39            January => 31,
40            February => 28 + leap as u8,
41            March => 31,
42            April => 30,
43            May => 31,
44            June => 30,
45            July => 31,
46            August => 31,
47            September => 30,
48            October => 31,
49            November => 30,
50            December => 31,
51        }
52    }
53
54    /// Returns the previous month.
55    pub const fn previous(self) -> Month {
56        self.previous_nth(1)
57    }
58
59    /// Returns the previous `nth` month.
60    pub const fn previous_nth(self, nth: usize) -> Month {
61        Self::from_index_unchecked(self.index().wrapping_sub(nth) % Self::COUNT)
62    }
63
64    /// Returns the next month.
65    pub const fn next(self) -> Month {
66        self.next_nth(1)
67    }
68
69    /// Returns the next `nth` month.
70    pub const fn next_nth(self, nth: usize) -> Month {
71        Self::from_index_unchecked(self.index().wrapping_add(nth) % Self::COUNT)
72    }
73
74    /* numbers */
75
76    /// Returns the Month number from `January=1` to `December=12`.
77    pub const fn number(self) -> u8 {
78        self.index() as u8 + 1
79    }
80
81    /// Returns the Month index from `January=0` to `December=11`.
82    pub const fn index(self) -> usize {
83        self as _
84    }
85
86    /// Returns a `Month` from its counting number, from `January=1` to `December=12`.
87    ///
88    /// # Errors
89    /// `if n < 1 || n > 12`
90    pub const fn from_number(n: u8) -> Result<Month, &'static str> {
91        match n {
92            1 => Ok(January),
93            2 => Ok(February),
94            3 => Ok(March),
95            4 => Ok(April),
96            5 => Ok(May),
97            6 => Ok(June),
98            7 => Ok(July),
99            8 => Ok(August),
100            9 => Ok(September),
101            10 => Ok(October),
102            11 => Ok(November),
103            12 => Ok(December),
104            _ => Err("The month number must be between 1 and 12."),
105        }
106    }
107
108    /// Returns a `Month` from its index, from `January=0` to `December=11`.
109    ///
110    /// # Errors
111    /// `if index > 11`
112    pub const fn from_index(index: usize) -> Result<Month, &'static str> {
113        match index {
114            0 => Ok(January),
115            1 => Ok(February),
116            2 => Ok(March),
117            3 => Ok(April),
118            4 => Ok(May),
119            5 => Ok(June),
120            6 => Ok(July),
121            7 => Ok(August),
122            8 => Ok(September),
123            9 => Ok(October),
124            10 => Ok(November),
125            11 => Ok(December),
126            _ => Err("The month index must be between 0 and 11."),
127        }
128    }
129
130    /// Returns a `Month` from its index, from `January=0` to `December=11`.
131    ///
132    /// # Panics
133    /// `if index > 11`
134    pub const fn from_index_unchecked(index: usize) -> Self {
135        match index {
136            0 => January,
137            1 => February,
138            2 => March,
139            3 => April,
140            4 => May,
141            5 => June,
142            6 => July,
143            7 => August,
144            8 => September,
145            9 => October,
146            10 => November,
147            11 => December,
148            _ => panic!("The month index must be between 0 and 11."),
149        }
150    }
151}
152
153/// # abbreviations & representations
154#[allow(missing_docs, non_upper_case_globals)]
155impl Month {
156    /// Returns the 3-letter abbreviated month name, in ASCII, UpperCamelCase.
157    pub const fn abbr3(self) -> &'static str {
158        match self {
159            January => "Jan",
160            February => "Feb",
161            March => "Mar",
162            April => "Apr",
163            May => "May",
164            June => "Jun",
165            July => "Jul",
166            August => "Aug",
167            September => "Sep",
168            October => "Oct",
169            November => "Nov",
170            December => "Dec",
171        }
172    }
173
174    pub const Jan: Month = Month::January;
175    pub const Feb: Month = Month::February;
176    pub const Mar: Month = Month::March;
177    pub const Apr: Month = Month::April;
178    pub const May: Month = Month::May;
179    pub const Jun: Month = Month::June;
180    pub const Jul: Month = Month::July;
181    pub const Aug: Month = Month::August;
182    pub const Sep: Month = Month::September;
183    pub const Oct: Month = Month::October;
184    pub const Nov: Month = Month::November;
185    pub const Dec: Month = Month::December;
186
187    /// Returns the 2-letter abbreviated month name, in ASCII, UPPERCASE.
188    pub const fn abbr2(self) -> &'static str {
189        match self {
190            January => "JA",
191            February => "FE",
192            March => "MR",
193            April => "AP",
194            May => "MY",
195            June => "JN",
196            July => "JL",
197            August => "AU",
198            September => "SE",
199            October => "OC",
200            November => "NV",
201            December => "DE",
202        }
203    }
204
205    pub const JA: Month = Month::January;
206    pub const FE: Month = Month::February;
207    pub const MR: Month = Month::March;
208    pub const AP: Month = Month::April;
209    pub const MY: Month = Month::May;
210    pub const JN: Month = Month::June;
211    pub const JL: Month = Month::July;
212    pub const AU: Month = Month::August;
213    pub const SE: Month = Month::September;
214    pub const OC: Month = Month::October;
215    pub const NV: Month = Month::November;
216    pub const DE: Month = Month::December;
217
218    /// Returns the 1-letter abbreviated month name, in ASCII, UPPERCASE.
219    pub const fn abbr1(self) -> &'static str {
220        match self {
221            January => "J",
222            February => "F",
223            March => "R",
224            April => "P",
225            May => "Y",
226            June => "N",
227            July => "L",
228            August => "U",
229            September => "S",
230            October => "O",
231            November => "N",
232            December => "D",
233        }
234    }
235
236    pub const J: Month = Month::January;
237    pub const F: Month = Month::February;
238    pub const R: Month = Month::March;
239    pub const P: Month = Month::April;
240    pub const Y: Month = Month::May;
241    pub const N: Month = Month::June;
242    pub const L: Month = Month::July;
243    pub const U: Month = Month::August;
244    pub const S: Month = Month::September;
245    pub const O: Month = Month::October;
246    pub const V: Month = Month::November;
247    pub const D: Month = Month::December;
248
249    /// Returns an emoji associated to each month.
250    ///
251    /// These are: 🌺, 🐉, 🍀, 🐰, 🌼, 🐟, 🌞, 🍂, 🎃, 🦉, 🍁, 🎄.
252    ///
253    /// Hibiscus, Dragon, Four Leaf Clover, Rabbit, Blossom, Fish, Sun with Face, Fallen Leaf,
254    /// Jack-O-Lantern, Owl, Maple Leaf and Christmas Tree.
255    pub const fn emoji(self) -> char {
256        match self {
257            // Hibiscus.
258            January => '🌺',
259            // Dragon.
260            February => '🐉',
261            // Four Leaf Clover.
262            March => '🍀',
263            // Rabbit.
264            April => '🐰',
265            // Blossom.
266            May => '🌼',
267            // Fish.
268            June => '🐟',
269            // Sun with Face.
270            July => '🌞',
271            // Fallen LEaf.
272            August => '🍂',
273            // Jack-O-Lantern.
274            September => '🎃',
275            // Owl.
276            October => '🦉',
277            // Maple Leaf.
278            November => '🍁',
279            // Christmas Tree.
280            December => '🎄',
281        }
282    }
283
284    /// Returns the main zodiac symbol, associated to the start of the month.
285    ///
286    /// These are: ♑, ♒, ♓, ♈, ♉, ♊, ♋, ♌, ♍, ♎, ♏, ♐.
287    ///
288    /// Capricorn, Aquarius, Pisces, Aries, Taurus, Gemini, Cancer, Leo, Virgo,
289    /// Libra, Scorpio, Sagittarius.
290    ///
291    /// # Examples
292    /// ```
293    /// # use devela::Month;
294    /// assert_eq![Month::July.zodiac_start(), '♋'];
295    /// ```
296    pub const fn zodiac_start(self) -> char {
297        match self {
298            // Capricorn
299            January => '♑',
300            // Aquarius.
301            February => '♒',
302            // Pisces.
303            March => '♓',
304            // Aries.
305            April => '♈',
306            // Taurus.
307            May => '♉',
308            // Gemini.
309            June => '♊',
310            // Cancer.
311            July => '♋',
312            // Leo.
313            August => '♌',
314            // Virgo.
315            September => '♍',
316            // Libra.
317            October => '♎',
318            // Scorpio.
319            November => '♏',
320            // Sagittarius.
321            December => '♐',
322        }
323    }
324
325    /// Returns the main zodiac name, associated to the start of the month.
326    ///
327    /// These are: Capricorn, Aquarius, Pisces, Aries, Taurus, Gemini, Cancer,
328    /// Leo, Virgo, Libra, Scorpio, Sagittarius.
329    ///
330    /// # Examples
331    /// ```
332    /// # use devela::Month;
333    /// assert_eq![Month::July.zodiac_start_name(), "Cancer"];
334    /// ```
335    pub const fn zodiac_start_name(self) -> &'static str {
336        match self {
337            January => "Capricorn",
338            February => "Aquarius",
339            March => "Pisces",
340            April => "Aries",
341            May => "Taurus",
342            June => "Gemini",
343            July => "Cancer",
344            August => "Leo",
345            September => "Virgo",
346            October => "Libra",
347            November => "Scorpio",
348            December => "Sagittarius",
349        }
350    }
351
352    /// Returns the secondary zodiac symbol, associated to the end of the month.
353    ///
354    /// These are: ♒, ♓, ♈, ♉, ♊, ♋, ♌, ♍, ♎, ♏, ♐, ♑.
355    ///
356    /// # Examples
357    /// ```
358    /// # use devela::Month;
359    /// assert_eq![Month::July.zodiac_end(), '♌'];
360    /// ```
361    pub const fn zodiac_end(self) -> char {
362        self.next().zodiac_start()
363    }
364
365    /// Returns the secondary zodiac name, associated to the end of the month.
366    ///
367    /// These are: Aquarius, Pisces, Aries, Taurus, Gemini, Cancer, Leo, Virgo,
368    /// Libra, Scorpio, Sagittarius, Capricorn.
369    ///
370    /// # Examples
371    /// ```
372    /// # use devela::Month;
373    /// assert_eq![Month::July.zodiac_end_name(), "Leo"];
374    /// ```
375    pub const fn zodiac_end_name(self) -> &'static str {
376        self.next().zodiac_start_name()
377    }
378}
379
380impl Display for Month {
381    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult<()> {
382        f.write_str(match self {
383            January => "January",
384            February => "February",
385            March => "March",
386            April => "April",
387            May => "May",
388            June => "June",
389            July => "July",
390            August => "August",
391            September => "September",
392            October => "October",
393            November => "November",
394            December => "December",
395        })
396    }
397}
398
399impl From<Month> for u8 {
400    fn from(month: Month) -> Self {
401        month as _
402    }
403}
404
405/// Returns a `Month` from a string containing either the full month name,
406/// or any of the month ASCII abbreviations.
407impl FromStr for Month {
408    type Err = &'static str;
409
410    #[rustfmt::skip]
411    fn from_str(s: &str) -> Result<Month, Self::Err> {
412        if s.eq_ignore_ascii_case("January") { Ok(January)
413        } else if s.eq_ignore_ascii_case("February") { Ok(February)
414        } else if s.eq_ignore_ascii_case("March") { Ok(March)
415        } else if s.eq_ignore_ascii_case("April") { Ok(April)
416        } else if s.eq_ignore_ascii_case("May") { Ok(May)
417        } else if s.eq_ignore_ascii_case("June") { Ok(June)
418        } else if s.eq_ignore_ascii_case("July") { Ok(July)
419        } else if s.eq_ignore_ascii_case("August") { Ok(August)
420        } else if s.eq_ignore_ascii_case("September") { Ok(September)
421        } else if s.eq_ignore_ascii_case("October") { Ok(October)
422        } else if s.eq_ignore_ascii_case("November") { Ok(November)
423        } else if s.eq_ignore_ascii_case("December") { Ok(December)
424        //
425        } else if s.eq_ignore_ascii_case("Jan") { Ok(January)
426        } else if s.eq_ignore_ascii_case("Feb") { Ok(February)
427        } else if s.eq_ignore_ascii_case("Mar") { Ok(March)
428        } else if s.eq_ignore_ascii_case("Apr") { Ok(April)
429        // } else if s.eq_ignore_ascii_case("May") { Ok(May) // repeated
430        } else if s.eq_ignore_ascii_case("Jun") { Ok(June)
431        } else if s.eq_ignore_ascii_case("Jul") { Ok(July)
432        } else if s.eq_ignore_ascii_case("Aug") { Ok(August)
433        } else if s.eq_ignore_ascii_case("Sep") { Ok(September)
434        } else if s.eq_ignore_ascii_case("Oct") { Ok(October)
435        } else if s.eq_ignore_ascii_case("Nov") { Ok(November)
436        } else if s.eq_ignore_ascii_case("Dec") { Ok(December)
437        // abbr2
438        } else if s.eq_ignore_ascii_case("JA") { Ok(January)
439        } else if s.eq_ignore_ascii_case("FE") { Ok(February)
440        } else if s.eq_ignore_ascii_case("MR") { Ok(March)
441        } else if s.eq_ignore_ascii_case("AP") { Ok(April)
442        } else if s.eq_ignore_ascii_case("MY") { Ok(May)
443        } else if s.eq_ignore_ascii_case("JN") { Ok(June)
444        } else if s.eq_ignore_ascii_case("JL") { Ok(July)
445        } else if s.eq_ignore_ascii_case("AU") { Ok(August)
446        } else if s.eq_ignore_ascii_case("SE") { Ok(September)
447        } else if s.eq_ignore_ascii_case("OC") { Ok(October)
448        } else if s.eq_ignore_ascii_case("NV") { Ok(November)
449        } else if s.eq_ignore_ascii_case("DE") { Ok(December)
450        // abbr1
451        } else if s.eq_ignore_ascii_case("J") { Ok(January)
452        } else if s.eq_ignore_ascii_case("F") { Ok(February)
453        } else if s.eq_ignore_ascii_case("R") { Ok(March)
454        } else if s.eq_ignore_ascii_case("P") { Ok(April)
455        } else if s.eq_ignore_ascii_case("Y") { Ok(May)
456        } else if s.eq_ignore_ascii_case("N") { Ok(June)
457        } else if s.eq_ignore_ascii_case("L") { Ok(July)
458        } else if s.eq_ignore_ascii_case("U") { Ok(August)
459        } else if s.eq_ignore_ascii_case("S") { Ok(September)
460        } else if s.eq_ignore_ascii_case("O") { Ok(October)
461        } else if s.eq_ignore_ascii_case("V") { Ok(November)
462        } else if s.eq_ignore_ascii_case("D") { Ok(December)
463        //
464        } else {
465            Err("Invalid month name.")
466        }
467    }
468}