dia_time/
month.rs

1/*
2==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
3
4Dia-Time
5
6Copyright (C) 2018-2022, 2024  Anonymous
7
8There are several releases over multiple years,
9they are listed as ranges, such as: "2018-2022".
10
11This program is free software: you can redistribute it and/or modify
12it under the terms of the GNU Lesser General Public License as published by
13the Free Software Foundation, either version 3 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU Lesser General Public License for more details.
20
21You should have received a copy of the GNU Lesser General Public License
22along with this program.  If not, see <https://www.gnu.org/licenses/>.
23
24::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
25*/
26
27//! # Month
28
29use {
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/// # Month
49///
50/// ## Notes
51///
52/// - The months' names are English only. That applies to implementations of [`FromStr`][trait:core/str/FromStr],
53///   [`Deref<Target=str>`][trait:core/ops/Deref]...
54/// - Implementation of `Deref<Target=str>` is same as [`Display`][trait:core/fmt/Display]'s, but it's faster because it provides references to
55///   static strings.
56///
57/// [trait:core/fmt/Display]: https://doc.rust-lang.org/core/fmt/trait.Display.html
58/// [trait:core/ops/Deref]: https://doc.rust-lang.org/core/ops/trait.Deref.html
59/// [trait:core/str/FromStr]: https://doc.rust-lang.org/core/str/trait.FromStr.html
60#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Copy)]
61pub enum Month {
62
63    /// # January,
64    January,
65
66    /// # February,
67    February,
68
69    /// # March,
70    March,
71
72    /// # April,
73    April,
74
75    /// # May,
76    May,
77
78    /// # June,
79    June,
80
81    /// # July,
82    July,
83
84    /// # August,
85    August,
86
87    /// # September,
88    September,
89
90    /// # October,
91    October,
92
93    /// # November,
94    November,
95
96    /// # December,
97    December,
98
99}
100
101impl Month {
102
103    /// # Returns ordinal number of this month
104    ///
105    /// January will be `1`, February will be `2`...
106    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    /// # Parses month from an ordinal number
124    ///
125    /// `1` will be January, `2` will be February...
126    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    /// # Gets next month
145    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    /// # Gets next month
163    ///
164    /// Next of December will be January.
165    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    /// # Gets last month
183    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    /// # Gets last month
201    ///
202    /// Last of January will be December.
203    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    /// # Converts self into Unix value
221    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    /// # Tries to convert a Unix value into self
239    #[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
328/// # Gets last day of month
329pub (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
346/// # Gets seconds of month
347pub (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}