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 _ => Err(error::ComponentRange::unconditional("month")),
60 }
61 }
62
63 #[inline]
70 pub const fn length(self, year: i32) -> u8 {
71 util::days_in_month(self, year)
72 }
73
74 #[inline]
81 pub const fn previous(self) -> Self {
82 match self {
83 January => December,
84 February => January,
85 March => February,
86 April => March,
87 May => April,
88 June => May,
89 July => June,
90 August => July,
91 September => August,
92 October => September,
93 November => October,
94 December => November,
95 }
96 }
97
98 #[inline]
105 pub const fn next(self) -> Self {
106 match self {
107 January => February,
108 February => March,
109 March => April,
110 April => May,
111 May => June,
112 June => July,
113 July => August,
114 August => September,
115 September => October,
116 October => November,
117 November => December,
118 December => January,
119 }
120 }
121
122 #[inline]
130 pub const fn nth_next(self, n: u8) -> Self {
131 match (self as u8 - 1 + n % 12) % 12 {
132 0 => January,
133 1 => February,
134 2 => March,
135 3 => April,
136 4 => May,
137 5 => June,
138 6 => July,
139 7 => August,
140 8 => September,
141 9 => October,
142 10 => November,
143 val => {
144 debug_assert!(val == 11);
145 December
146 }
147 }
148 }
149
150 #[inline]
158 pub const fn nth_prev(self, n: u8) -> Self {
159 match self as i8 - 1 - (n % 12).cast_signed() {
160 1 | -11 => February,
161 2 | -10 => March,
162 3 | -9 => April,
163 4 | -8 => May,
164 5 | -7 => June,
165 6 | -6 => July,
166 7 | -5 => August,
167 8 | -4 => September,
168 9 | -3 => October,
169 10 | -2 => November,
170 11 | -1 => December,
171 val => {
172 debug_assert!(val == 0);
173 January
174 }
175 }
176 }
177}
178
179impl SmartDisplay for Month {
180 type Metadata = ();
181
182 #[inline]
183 fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
184 match self {
185 January => Metadata::new(7, self, ()),
186 February => Metadata::new(8, self, ()),
187 March => Metadata::new(5, self, ()),
188 April => Metadata::new(5, self, ()),
189 May => Metadata::new(3, self, ()),
190 June => Metadata::new(4, self, ()),
191 July => Metadata::new(4, self, ()),
192 August => Metadata::new(6, self, ()),
193 September => Metadata::new(9, self, ()),
194 October => Metadata::new(7, self, ()),
195 November => Metadata::new(8, self, ()),
196 December => Metadata::new(8, self, ()),
197 }
198 }
199
200 #[inline]
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 f.pad(match self {
203 January => "January",
204 February => "February",
205 March => "March",
206 April => "April",
207 May => "May",
208 June => "June",
209 July => "July",
210 August => "August",
211 September => "September",
212 October => "October",
213 November => "November",
214 December => "December",
215 })
216 }
217}
218
219impl fmt::Display for Month {
220 #[inline]
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 SmartDisplay::fmt(self, f)
223 }
224}
225
226impl FromStr for Month {
227 type Err = error::InvalidVariant;
228
229 #[inline]
230 fn from_str(s: &str) -> Result<Self, Self::Err> {
231 match s {
232 "January" => Ok(January),
233 "February" => Ok(February),
234 "March" => Ok(March),
235 "April" => Ok(April),
236 "May" => Ok(May),
237 "June" => Ok(June),
238 "July" => Ok(July),
239 "August" => Ok(August),
240 "September" => Ok(September),
241 "October" => Ok(October),
242 "November" => Ok(November),
243 "December" => Ok(December),
244 _ => Err(error::InvalidVariant),
245 }
246 }
247}
248
249impl From<Month> for u8 {
250 #[inline]
251 fn from(month: Month) -> Self {
252 month as Self
253 }
254}
255
256impl TryFrom<u8> for Month {
257 type Error = error::ComponentRange;
258
259 #[inline]
260 fn try_from(value: u8) -> Result<Self, Self::Error> {
261 match NonZero::new(value) {
262 Some(value) => Self::from_number(value),
263 None => Err(error::ComponentRange::unconditional("month")),
264 }
265 }
266}