use {
core::{
fmt::{self, Debug, Display, Formatter},
ops::Deref,
str::FromStr,
},
crate::{Error, Result as CrateResult},
};
mod tests;
pub (crate) const END_OF_FEBRUARY_IN_LEAP_YEARS: u8 = 29;
pub (crate) const END_OF_FEBRUARY_IN_COMMON_YEARS: u8 = 28;
const SECONDS_OF_28_DAYS: i64 = (crate::DAY * 28) as i64;
const SECONDS_OF_29_DAYS: i64 = (crate::DAY * 29) as i64;
const SECONDS_OF_30_DAYS: i64 = (crate::DAY * 30) as i64;
const SECONDS_OF_31_DAYS: i64 = (crate::DAY * 31) as i64;
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Copy)]
pub enum Month {
January,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December,
}
impl Month {
pub fn order(&self) -> u8 {
match self {
Month::January => 1,
Month::February => 2,
Month::March => 3,
Month::April => 4,
Month::May => 5,
Month::June => 6,
Month::July => 7,
Month::August => 8,
Month::September => 9,
Month::October => 10,
Month::November => 11,
Month::December => 12,
}
}
pub fn try_from_order(order: u8) -> CrateResult<Self> {
match order {
1 => Ok(Month::January),
2 => Ok(Month::February),
3 => Ok(Month::March),
4 => Ok(Month::April),
5 => Ok(Month::May),
6 => Ok(Month::June),
7 => Ok(Month::July),
8 => Ok(Month::August),
9 => Ok(Month::September),
10 => Ok(Month::October),
11 => Ok(Month::November),
12 => Ok(Month::December),
_ => Err(err!("Invalid month: {}", order)),
}
}
pub fn next(&self) -> Option<Self> {
match self {
Month::January => Some(Month::February),
Month::February => Some(Month::March),
Month::March => Some(Month::April),
Month::April => Some(Month::May),
Month::May => Some(Month::June),
Month::June => Some(Month::July),
Month::July => Some(Month::August),
Month::August => Some(Month::September),
Month::September => Some(Month::October),
Month::October => Some(Month::November),
Month::November => Some(Month::December),
Month::December => None,
}
}
pub fn wrapping_next(&self) -> Self {
match self {
Month::January => Month::February,
Month::February => Month::March,
Month::March => Month::April,
Month::April => Month::May,
Month::May => Month::June,
Month::June => Month::July,
Month::July => Month::August,
Month::August => Month::September,
Month::September => Month::October,
Month::October => Month::November,
Month::November => Month::December,
Month::December => Month::January,
}
}
pub fn last(&self) -> Option<Self> {
match self {
Month::January => None,
Month::February => Some(Month::January),
Month::March => Some(Month::February),
Month::April => Some(Month::March),
Month::May => Some(Month::April),
Month::June => Some(Month::May),
Month::July => Some(Month::June),
Month::August => Some(Month::July),
Month::September => Some(Month::August),
Month::October => Some(Month::September),
Month::November => Some(Month::October),
Month::December => Some(Month::November),
}
}
pub fn wrapping_last(&self) -> Self {
match self {
Month::January => Month::December,
Month::February => Month::January,
Month::March => Month::February,
Month::April => Month::March,
Month::May => Month::April,
Month::June => Month::May,
Month::July => Month::June,
Month::August => Month::July,
Month::September => Month::August,
Month::October => Month::September,
Month::November => Month::October,
Month::December => Month::November,
}
}
pub (crate) fn to_unix(&self) -> i32 {
match self {
Month::January => 0,
Month::February => 1,
Month::March => 2,
Month::April => 3,
Month::May => 4,
Month::June => 5,
Month::July => 6,
Month::August => 7,
Month::September => 8,
Month::October => 9,
Month::November => 10,
Month::December => 11,
}
}
#[cfg(test)]
#[cfg(not(windows))]
pub (crate) fn try_from_unix(tm_mon: i32) -> CrateResult<Self> {
match tm_mon {
0 => Ok(Month::January),
1 => Ok(Month::February),
2 => Ok(Month::March),
3 => Ok(Month::April),
4 => Ok(Month::May),
5 => Ok(Month::June),
6 => Ok(Month::July),
7 => Ok(Month::August),
8 => Ok(Month::September),
9 => Ok(Month::October),
10 => Ok(Month::November),
11 => Ok(Month::December),
_ => Err(err!("Invalid Unix month: {tm_mon}", tm_mon=tm_mon)),
}
}
}
impl Deref for Month {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
Month::January => "January",
Month::February => "February",
Month::March => "March",
Month::April => "April",
Month::May => "May",
Month::June => "June",
Month::July => "July",
Month::August => "August",
Month::September => "September",
Month::October => "October",
Month::November => "November",
Month::December => "December",
}
}
}
impl Display for Month {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_str(self)
}
}
impl FromStr for Month {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.eq_ignore_ascii_case(&*Month::January) {
Ok(Month::January)
} else if s.eq_ignore_ascii_case(&*Month::February) {
Ok(Month::February)
} else if s.eq_ignore_ascii_case(&*Month::March) {
Ok(Month::March)
} else if s.eq_ignore_ascii_case(&*Month::April) {
Ok(Month::April)
} else if s.eq_ignore_ascii_case(&*Month::May) {
Ok(Month::May)
} else if s.eq_ignore_ascii_case(&*Month::June) {
Ok(Month::June)
} else if s.eq_ignore_ascii_case(&*Month::July) {
Ok(Month::July)
} else if s.eq_ignore_ascii_case(&*Month::August) {
Ok(Month::August)
} else if s.eq_ignore_ascii_case(&*Month::September) {
Ok(Month::September)
} else if s.eq_ignore_ascii_case(&*Month::October) {
Ok(Month::October)
} else if s.eq_ignore_ascii_case(&*Month::November) {
Ok(Month::November)
} else if s.eq_ignore_ascii_case(&*Month::December) {
Ok(Month::December)
} else {
Err(err!("Unknown month: {:?}", s))
}
}
}
pub (crate) fn last_day_of_month(month: &Month, leap_year: bool) -> u8 {
match month {
Month::January => 31,
Month::February => if leap_year { END_OF_FEBRUARY_IN_LEAP_YEARS } else { END_OF_FEBRUARY_IN_COMMON_YEARS },
Month::March => 31,
Month::April => 30,
Month::May => 31,
Month::June => 30,
Month::July => 31,
Month::August => 31,
Month::September => 30,
Month::October => 31,
Month::November => 30,
Month::December => 31,
}
}
pub (crate) fn seconds_of_month(month: &Month, leap_year: bool) -> i64 {
match month {
Month::January | Month::March | Month::May | Month::July | Month::August | Month::October | Month::December => SECONDS_OF_31_DAYS,
Month::February => if leap_year { SECONDS_OF_29_DAYS } else { SECONDS_OF_28_DAYS },
Month::April | Month::June | Month::September | Month::November => SECONDS_OF_30_DAYS,
}
}