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