1use {
30 core::{
31 fmt::{self, Debug, Display, Formatter},
32 ops::Deref,
33 str::FromStr,
34 },
35 crate::{Error, Result as CrateResult},
36};
37
38mod tests;
39
40pub (crate) const END_OF_FEBRUARY_IN_LEAP_YEARS: u8 = 29;
41pub (crate) const END_OF_FEBRUARY_IN_COMMON_YEARS: u8 = 28;
42
43const SECONDS_OF_28_DAYS: i64 = (crate::DAY * 28) as i64;
44const SECONDS_OF_29_DAYS: i64 = (crate::DAY * 29) as i64;
45const SECONDS_OF_30_DAYS: i64 = (crate::DAY * 30) as i64;
46const SECONDS_OF_31_DAYS: i64 = (crate::DAY * 31) as i64;
47
48#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Copy)]
61pub enum Month {
62
63 January,
65
66 February,
68
69 March,
71
72 April,
74
75 May,
77
78 June,
80
81 July,
83
84 August,
86
87 September,
89
90 October,
92
93 November,
95
96 December,
98
99}
100
101impl Month {
102
103 pub fn order(&self) -> u8 {
107 match self {
108 Month::January => 1,
109 Month::February => 2,
110 Month::March => 3,
111 Month::April => 4,
112 Month::May => 5,
113 Month::June => 6,
114 Month::July => 7,
115 Month::August => 8,
116 Month::September => 9,
117 Month::October => 10,
118 Month::November => 11,
119 Month::December => 12,
120 }
121 }
122
123 pub fn try_from_order(order: u8) -> CrateResult<Self> {
127 match order {
128 1 => Ok(Month::January),
129 2 => Ok(Month::February),
130 3 => Ok(Month::March),
131 4 => Ok(Month::April),
132 5 => Ok(Month::May),
133 6 => Ok(Month::June),
134 7 => Ok(Month::July),
135 8 => Ok(Month::August),
136 9 => Ok(Month::September),
137 10 => Ok(Month::October),
138 11 => Ok(Month::November),
139 12 => Ok(Month::December),
140 _ => Err(err!("Invalid month: {}", order)),
141 }
142 }
143
144 pub fn next(&self) -> Option<Self> {
146 match self {
147 Month::January => Some(Month::February),
148 Month::February => Some(Month::March),
149 Month::March => Some(Month::April),
150 Month::April => Some(Month::May),
151 Month::May => Some(Month::June),
152 Month::June => Some(Month::July),
153 Month::July => Some(Month::August),
154 Month::August => Some(Month::September),
155 Month::September => Some(Month::October),
156 Month::October => Some(Month::November),
157 Month::November => Some(Month::December),
158 Month::December => None,
159 }
160 }
161
162 pub fn wrapping_next(&self) -> Self {
166 match self {
167 Month::January => Month::February,
168 Month::February => Month::March,
169 Month::March => Month::April,
170 Month::April => Month::May,
171 Month::May => Month::June,
172 Month::June => Month::July,
173 Month::July => Month::August,
174 Month::August => Month::September,
175 Month::September => Month::October,
176 Month::October => Month::November,
177 Month::November => Month::December,
178 Month::December => Month::January,
179 }
180 }
181
182 pub fn last(&self) -> Option<Self> {
184 match self {
185 Month::January => None,
186 Month::February => Some(Month::January),
187 Month::March => Some(Month::February),
188 Month::April => Some(Month::March),
189 Month::May => Some(Month::April),
190 Month::June => Some(Month::May),
191 Month::July => Some(Month::June),
192 Month::August => Some(Month::July),
193 Month::September => Some(Month::August),
194 Month::October => Some(Month::September),
195 Month::November => Some(Month::October),
196 Month::December => Some(Month::November),
197 }
198 }
199
200 pub fn wrapping_last(&self) -> Self {
204 match self {
205 Month::January => Month::December,
206 Month::February => Month::January,
207 Month::March => Month::February,
208 Month::April => Month::March,
209 Month::May => Month::April,
210 Month::June => Month::May,
211 Month::July => Month::June,
212 Month::August => Month::July,
213 Month::September => Month::August,
214 Month::October => Month::September,
215 Month::November => Month::October,
216 Month::December => Month::November,
217 }
218 }
219
220 pub (crate) fn to_unix(&self) -> i32 {
222 match self {
223 Month::January => 0,
224 Month::February => 1,
225 Month::March => 2,
226 Month::April => 3,
227 Month::May => 4,
228 Month::June => 5,
229 Month::July => 6,
230 Month::August => 7,
231 Month::September => 8,
232 Month::October => 9,
233 Month::November => 10,
234 Month::December => 11,
235 }
236 }
237
238 #[cfg(test)]
240 #[cfg(not(windows))]
241 pub (crate) fn try_from_unix(tm_mon: i32) -> CrateResult<Self> {
242 match tm_mon {
243 0 => Ok(Month::January),
244 1 => Ok(Month::February),
245 2 => Ok(Month::March),
246 3 => Ok(Month::April),
247 4 => Ok(Month::May),
248 5 => Ok(Month::June),
249 6 => Ok(Month::July),
250 7 => Ok(Month::August),
251 8 => Ok(Month::September),
252 9 => Ok(Month::October),
253 10 => Ok(Month::November),
254 11 => Ok(Month::December),
255 _ => Err(err!("Invalid Unix month: {tm_mon}", tm_mon=tm_mon)),
256 }
257 }
258
259}
260
261impl Deref for Month {
262
263 type Target = str;
264
265 fn deref(&self) -> &Self::Target {
266 match self {
267 Month::January => "January",
268 Month::February => "February",
269 Month::March => "March",
270 Month::April => "April",
271 Month::May => "May",
272 Month::June => "June",
273 Month::July => "July",
274 Month::August => "August",
275 Month::September => "September",
276 Month::October => "October",
277 Month::November => "November",
278 Month::December => "December",
279 }
280 }
281
282}
283
284impl Display for Month {
285
286 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
287 f.write_str(self)
288 }
289
290}
291
292impl FromStr for Month {
293
294 type Err = Error;
295
296 fn from_str(s: &str) -> Result<Self, Self::Err> {
297 if s.eq_ignore_ascii_case(&*Month::January) {
298 Ok(Month::January)
299 } else if s.eq_ignore_ascii_case(&*Month::February) {
300 Ok(Month::February)
301 } else if s.eq_ignore_ascii_case(&*Month::March) {
302 Ok(Month::March)
303 } else if s.eq_ignore_ascii_case(&*Month::April) {
304 Ok(Month::April)
305 } else if s.eq_ignore_ascii_case(&*Month::May) {
306 Ok(Month::May)
307 } else if s.eq_ignore_ascii_case(&*Month::June) {
308 Ok(Month::June)
309 } else if s.eq_ignore_ascii_case(&*Month::July) {
310 Ok(Month::July)
311 } else if s.eq_ignore_ascii_case(&*Month::August) {
312 Ok(Month::August)
313 } else if s.eq_ignore_ascii_case(&*Month::September) {
314 Ok(Month::September)
315 } else if s.eq_ignore_ascii_case(&*Month::October) {
316 Ok(Month::October)
317 } else if s.eq_ignore_ascii_case(&*Month::November) {
318 Ok(Month::November)
319 } else if s.eq_ignore_ascii_case(&*Month::December) {
320 Ok(Month::December)
321 } else {
322 Err(err!("Unknown month: {:?}", s))
323 }
324 }
325
326}
327
328pub (crate) fn last_day_of_month(month: &Month, leap_year: bool) -> u8 {
330 match month {
331 Month::January => 31,
332 Month::February => if leap_year { END_OF_FEBRUARY_IN_LEAP_YEARS } else { END_OF_FEBRUARY_IN_COMMON_YEARS },
333 Month::March => 31,
334 Month::April => 30,
335 Month::May => 31,
336 Month::June => 30,
337 Month::July => 31,
338 Month::August => 31,
339 Month::September => 30,
340 Month::October => 31,
341 Month::November => 30,
342 Month::December => 31,
343 }
344}
345
346pub (crate) fn seconds_of_month(month: &Month, leap_year: bool) -> i64 {
348 match month {
349 Month::January | Month::March | Month::May | Month::July | Month::August | Month::October | Month::December => SECONDS_OF_31_DAYS,
350 Month::February => if leap_year { SECONDS_OF_29_DAYS } else { SECONDS_OF_28_DAYS },
351 Month::April | Month::June | Month::September | Month::November => SECONDS_OF_30_DAYS,
352 }
353}