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