use core::fmt::{self, Debug, Formatter};
use crate::calendar::Iso;
use crate::{Calendar, Date, Month, Year};
#[derive(Copy, Clone)]
pub struct YearMonth<C: Calendar = Iso> {
pub(crate) calendar: C,
pub(crate) year: i32,
pub(crate) month: u8,
}
impl YearMonth<Iso> {
pub const fn iso(year: i32, month: u8) -> crate::Result<Self> {
Iso.year_month(year, month)
}
}
impl<C: Calendar> YearMonth<C> {
pub fn of(calendar: C, year: i32, month: u8) -> crate::Result<Self> {
calendar.year_month(year, month)
}
}
impl<C: Calendar> YearMonth<C> {
#[must_use]
pub const fn calendar(self) -> C {
self.calendar
}
#[allow(unsafe_code)]
#[must_use]
pub const fn year(self) -> Year<C> {
unsafe { Year::unchecked_of(self.calendar, self.year) }
}
#[allow(unsafe_code)]
#[must_use]
pub const fn month(self) -> Month<C> {
unsafe { Month::unchecked_of(self.calendar, self.month) }
}
}
impl<C: Calendar> YearMonth<C> {
#[must_use]
pub fn days(self) -> u8 {
self.calendar.year_month_days(self.year, self.month)
}
}
impl<C: Calendar> YearMonth<C> {
pub fn with_day(self, day: u8) -> crate::Result<Date<C>> {
Date::of(self.calendar, self.year, self.month, day)
}
}
impl<C: Calendar> Debug for YearMonth<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.pad(&self.format_rfc3339())
}
}
#[cfg(test)]
mod tests {
use alloc::format;
use test_case::test_case;
use crate::Year;
#[test_case(2000)]
#[test_case(2024)]
#[test_case(2025)]
fn expect_year_month_days(year: i32) -> crate::Result<()> {
let year = Year::iso(year)?;
let days = [
31,
if year.is_leap() { 29 } else { 28 },
31,
30,
31,
30,
31,
31,
30,
31,
30,
31,
];
for month in 1..=12 {
let ym = year.with_month(month)?;
assert_eq!(ym.year(), year);
assert_eq!(ym.month().number(), month);
assert_eq!(ym.format_rfc3339(), format!("{:?}-{:02}", year, month));
assert_eq!(ym.days(), days[month as usize - 1]);
}
Ok(())
}
}