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
179mod private {
180 #[non_exhaustive]
182 #[derive(Debug, Clone, Copy)]
183 pub struct MonthMetadata;
184}
185use private::MonthMetadata;
186
187impl SmartDisplay for Month {
188 type Metadata = MonthMetadata;
189
190 #[inline]
191 fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
192 match self {
193 January => Metadata::new(7, self, MonthMetadata),
194 February => Metadata::new(8, self, MonthMetadata),
195 March => Metadata::new(5, self, MonthMetadata),
196 April => Metadata::new(5, self, MonthMetadata),
197 May => Metadata::new(3, self, MonthMetadata),
198 June => Metadata::new(4, self, MonthMetadata),
199 July => Metadata::new(4, self, MonthMetadata),
200 August => Metadata::new(6, self, MonthMetadata),
201 September => Metadata::new(9, self, MonthMetadata),
202 October => Metadata::new(7, self, MonthMetadata),
203 November => Metadata::new(8, self, MonthMetadata),
204 December => Metadata::new(8, self, MonthMetadata),
205 }
206 }
207
208 #[inline]
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 f.pad(match self {
211 January => "January",
212 February => "February",
213 March => "March",
214 April => "April",
215 May => "May",
216 June => "June",
217 July => "July",
218 August => "August",
219 September => "September",
220 October => "October",
221 November => "November",
222 December => "December",
223 })
224 }
225}
226
227impl fmt::Display for Month {
228 #[inline]
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 SmartDisplay::fmt(self, f)
231 }
232}
233
234impl FromStr for Month {
235 type Err = error::InvalidVariant;
236
237 #[inline]
238 fn from_str(s: &str) -> Result<Self, Self::Err> {
239 match s {
240 "January" => Ok(January),
241 "February" => Ok(February),
242 "March" => Ok(March),
243 "April" => Ok(April),
244 "May" => Ok(May),
245 "June" => Ok(June),
246 "July" => Ok(July),
247 "August" => Ok(August),
248 "September" => Ok(September),
249 "October" => Ok(October),
250 "November" => Ok(November),
251 "December" => Ok(December),
252 _ => Err(error::InvalidVariant),
253 }
254 }
255}
256
257impl From<Month> for u8 {
258 #[inline]
259 fn from(month: Month) -> Self {
260 month as Self
261 }
262}
263
264impl TryFrom<u8> for Month {
265 type Error = error::ComponentRange;
266
267 #[inline]
268 fn try_from(value: u8) -> Result<Self, Self::Error> {
269 match NonZero::new(value) {
270 Some(value) => Self::from_number(value),
271 None => Err(error::ComponentRange::unconditional("month")),
272 }
273 }
274}