time/
month.rs

1//! The `Month` enum and its associated `impl`s.
2
3use core::fmt;
4use core::num::NonZeroU8;
5use core::str::FromStr;
6
7use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay};
8
9use self::Month::*;
10use crate::{error, hint, util};
11
12/// Months of the year.
13#[repr(u8)]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum Month {
16    #[allow(missing_docs)]
17    January = 1,
18    #[allow(missing_docs)]
19    February = 2,
20    #[allow(missing_docs)]
21    March = 3,
22    #[allow(missing_docs)]
23    April = 4,
24    #[allow(missing_docs)]
25    May = 5,
26    #[allow(missing_docs)]
27    June = 6,
28    #[allow(missing_docs)]
29    July = 7,
30    #[allow(missing_docs)]
31    August = 8,
32    #[allow(missing_docs)]
33    September = 9,
34    #[allow(missing_docs)]
35    October = 10,
36    #[allow(missing_docs)]
37    November = 11,
38    #[allow(missing_docs)]
39    December = 12,
40}
41
42impl Month {
43    /// Create a `Month` from its numerical value.
44    pub(crate) const fn from_number(n: NonZeroU8) -> Result<Self, error::ComponentRange> {
45        match n.get() {
46            1 => Ok(January),
47            2 => Ok(February),
48            3 => Ok(March),
49            4 => Ok(April),
50            5 => Ok(May),
51            6 => Ok(June),
52            7 => Ok(July),
53            8 => Ok(August),
54            9 => Ok(September),
55            10 => Ok(October),
56            11 => Ok(November),
57            12 => Ok(December),
58            n => Err(error::ComponentRange {
59                name: "month",
60                minimum: 1,
61                maximum: 12,
62                value: n as _,
63                conditional_message: None,
64            }),
65        }
66    }
67
68    /// Get the number of days in the month of a given year.
69    ///
70    /// ```rust
71    /// # use time::Month;
72    /// assert_eq!(Month::February.length(2020), 29);
73    /// ```
74    pub const fn length(self, year: i32) -> u8 {
75        let val = self as u8;
76        if hint::unlikely(val == 2) {
77            if util::is_leap_year(year) {
78                29
79            } else {
80                28
81            }
82        } else {
83            30 | val ^ (val >> 3)
84        }
85    }
86
87    /// Get the previous month.
88    ///
89    /// ```rust
90    /// # use time::Month;
91    /// assert_eq!(Month::January.previous(), Month::December);
92    /// ```
93    pub const fn previous(self) -> Self {
94        match self {
95            January => December,
96            February => January,
97            March => February,
98            April => March,
99            May => April,
100            June => May,
101            July => June,
102            August => July,
103            September => August,
104            October => September,
105            November => October,
106            December => November,
107        }
108    }
109
110    /// Get the next month.
111    ///
112    /// ```rust
113    /// # use time::Month;
114    /// assert_eq!(Month::January.next(), Month::February);
115    /// ```
116    pub const fn next(self) -> Self {
117        match self {
118            January => February,
119            February => March,
120            March => April,
121            April => May,
122            May => June,
123            June => July,
124            July => August,
125            August => September,
126            September => October,
127            October => November,
128            November => December,
129            December => January,
130        }
131    }
132
133    /// Get n-th next month.
134    ///
135    /// ```rust
136    /// # use time::Month;
137    /// assert_eq!(Month::January.nth_next(4), Month::May);
138    /// assert_eq!(Month::July.nth_next(9), Month::April);
139    /// ```
140    pub const fn nth_next(self, n: u8) -> Self {
141        match (self as u8 - 1 + n % 12) % 12 {
142            0 => January,
143            1 => February,
144            2 => March,
145            3 => April,
146            4 => May,
147            5 => June,
148            6 => July,
149            7 => August,
150            8 => September,
151            9 => October,
152            10 => November,
153            val => {
154                debug_assert!(val == 11);
155                December
156            }
157        }
158    }
159
160    /// Get n-th previous month.
161    ///
162    /// ```rust
163    /// # use time::Month;
164    /// assert_eq!(Month::January.nth_prev(4), Month::September);
165    /// assert_eq!(Month::July.nth_prev(9), Month::October);
166    /// ```
167    pub const fn nth_prev(self, n: u8) -> Self {
168        match self as i8 - 1 - (n % 12) as i8 {
169            1 | -11 => February,
170            2 | -10 => March,
171            3 | -9 => April,
172            4 | -8 => May,
173            5 | -7 => June,
174            6 | -6 => July,
175            7 | -5 => August,
176            8 | -4 => September,
177            9 | -3 => October,
178            10 | -2 => November,
179            11 | -1 => December,
180            val => {
181                debug_assert!(val == 0);
182                January
183            }
184        }
185    }
186}
187
188mod private {
189    #[non_exhaustive]
190    #[derive(Debug, Clone, Copy)]
191    pub struct MonthMetadata;
192}
193use private::MonthMetadata;
194
195impl SmartDisplay for Month {
196    type Metadata = MonthMetadata;
197
198    fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
199        match self {
200            January => Metadata::new(7, self, MonthMetadata),
201            February => Metadata::new(8, self, MonthMetadata),
202            March => Metadata::new(5, self, MonthMetadata),
203            April => Metadata::new(5, self, MonthMetadata),
204            May => Metadata::new(3, self, MonthMetadata),
205            June => Metadata::new(4, self, MonthMetadata),
206            July => Metadata::new(4, self, MonthMetadata),
207            August => Metadata::new(6, self, MonthMetadata),
208            September => Metadata::new(9, self, MonthMetadata),
209            October => Metadata::new(7, self, MonthMetadata),
210            November => Metadata::new(8, self, MonthMetadata),
211            December => Metadata::new(8, self, MonthMetadata),
212        }
213    }
214
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        f.pad(match self {
217            January => "January",
218            February => "February",
219            March => "March",
220            April => "April",
221            May => "May",
222            June => "June",
223            July => "July",
224            August => "August",
225            September => "September",
226            October => "October",
227            November => "November",
228            December => "December",
229        })
230    }
231}
232
233impl fmt::Display for Month {
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        SmartDisplay::fmt(self, f)
236    }
237}
238
239impl FromStr for Month {
240    type Err = error::InvalidVariant;
241
242    fn from_str(s: &str) -> Result<Self, Self::Err> {
243        match s {
244            "January" => Ok(January),
245            "February" => Ok(February),
246            "March" => Ok(March),
247            "April" => Ok(April),
248            "May" => Ok(May),
249            "June" => Ok(June),
250            "July" => Ok(July),
251            "August" => Ok(August),
252            "September" => Ok(September),
253            "October" => Ok(October),
254            "November" => Ok(November),
255            "December" => Ok(December),
256            _ => Err(error::InvalidVariant),
257        }
258    }
259}
260
261impl From<Month> for u8 {
262    fn from(month: Month) -> Self {
263        month as _
264    }
265}
266
267impl TryFrom<u8> for Month {
268    type Error = error::ComponentRange;
269
270    fn try_from(value: u8) -> Result<Self, Self::Error> {
271        match NonZeroU8::new(value) {
272            Some(value) => Self::from_number(value),
273            None => Err(error::ComponentRange {
274                name: "month",
275                minimum: 1,
276                maximum: 12,
277                value: 0,
278                conditional_message: None,
279            }),
280        }
281    }
282}