use crate::util::t::{Day, Month, Year, C};
pub(crate) fn is_leap_year(year: Year) -> bool {
year % C(4) == 0 && (year % C(100) != 0 || year % C(400) == 0)
}
pub(crate) const fn is_leap_year_unranged(year: i16) -> bool {
year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
pub(crate) fn saturate_day_in_month(
year: Year,
month: Month,
day: Day,
) -> Day {
day.min(days_in_month(year, month))
}
pub(crate) fn days_in_month(year: Year, month: Month) -> Day {
if month == 2 && is_leap_year(year) {
Day::V::<29, 28, 31>()
} else {
days_in_month_common_year(month)
}
}
pub(crate) const fn days_in_month_unranged(year: i16, month: i8) -> i8 {
match month {
1 => 31,
2 if is_leap_year_unranged(year) => 29,
2 => 28,
3 => 31,
4 => 30,
5 => 31,
6 => 30,
7 => 31,
8 => 31,
9 => 30,
10 => 31,
11 => 30,
12 => 31,
_ => 0,
}
}
pub(crate) fn days_in_month_common_year(month: Month) -> Day {
const BY_MONTH: [Day; 13] = [
Day::V::<0, 28, 31>(),
Day::V::<31, 28, 31>(),
Day::V::<28, 28, 31>(),
Day::V::<31, 28, 31>(),
Day::V::<30, 28, 31>(),
Day::V::<31, 28, 31>(),
Day::V::<30, 28, 31>(),
Day::V::<31, 28, 31>(),
Day::V::<31, 28, 31>(),
Day::V::<30, 28, 31>(),
Day::V::<31, 28, 31>(),
Day::V::<30, 28, 31>(),
Day::V::<31, 28, 31>(),
];
BY_MONTH[usize::from(month.get() as u8)]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn t_is_leap_year() {
let is_leap_year = |y: i16| is_leap_year(Year::new(y).unwrap());
assert!(is_leap_year(2024));
assert!(!is_leap_year(2023));
assert!(!is_leap_year(2025));
assert!(is_leap_year(2000));
assert!(!is_leap_year(1900));
assert!(!is_leap_year(1800));
assert!(!is_leap_year(1700));
assert!(is_leap_year(1600));
assert!(is_leap_year(0));
assert!(!is_leap_year(-1));
assert!(!is_leap_year(-2));
assert!(!is_leap_year(-3));
assert!(is_leap_year(-4));
assert!(!is_leap_year(-100));
assert!(!is_leap_year(-200));
assert!(!is_leap_year(-300));
assert!(is_leap_year(400));
assert!(!is_leap_year(9999));
assert!(!is_leap_year(-9999));
}
#[test]
fn t_days_in_month() {
let days_in_month = |year: i16, month| {
days_in_month(Year::new(year).unwrap(), Month::new(month).unwrap())
};
assert_eq!(28, days_in_month(-9999, 2).get());
}
}