use core::fmt;
use crate::calendar::{days_from_civil, weekday_from_civil, Weekday};
use crate::date::Date;
use crate::error::{Error, Result};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Days(pub u64);
impl Days {
pub const fn new(days: u64) -> Days {
Days(days)
}
pub const fn get(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Months(pub u32);
impl Months {
pub const fn new(months: u32) -> Months {
Months(months)
}
pub const fn get(self) -> u32 {
self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IsoWeek {
year: i32,
week: u32,
}
impl IsoWeek {
pub(crate) const fn new(year: i32, week: u32) -> IsoWeek {
IsoWeek { year, week }
}
pub const fn year(self) -> i32 {
self.year
}
pub const fn week(self) -> u32 {
self.week
}
pub const fn parts(self) -> (i32, u32) {
(self.year, self.week)
}
pub fn monday(self) -> Result<Date> {
if !(1..=53).contains(&self.week) {
return Err(Error::out_of_range("iso week"));
}
let jan4 = days_from_civil(self.year, 1, 4);
let monday_week1 = jan4 - weekday_from_civil(jan4) as i64;
Date::from_days_checked(monday_week1 + (self.week as i64 - 1) * 7)
}
pub const fn first_weekday(self) -> Weekday {
Weekday::Monday
}
}
impl fmt::Display for IsoWeek {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-W{:02}", self.year, self.week)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn iso_week_monday() {
let w = IsoWeek::new(2021, 1);
assert_eq!(w.monday().unwrap(), Date::from_ymd(2021, 1, 4).unwrap());
let w = IsoWeek::new(2020, 53);
assert_eq!(w.monday().unwrap(), Date::from_ymd(2020, 12, 28).unwrap());
let w = IsoWeek::new(2026, 1);
assert_eq!(w.monday().unwrap(), Date::from_ymd(2025, 12, 29).unwrap());
assert_eq!(w.to_string(), "2026-W01");
assert!(IsoWeek::new(2024, 54).monday().is_err());
}
}