1use core::fmt;
10
11use crate::calendar::{days_from_civil, iso_week_from_civil, weekday_from_civil, Weekday};
12use crate::date::Date;
13use crate::error::{Error, Result};
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Days(pub u64);
18
19impl Days {
20 pub const fn new(days: u64) -> Days {
22 Days(days)
23 }
24
25 pub const fn get(self) -> u64 {
27 self.0
28 }
29}
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub struct Months(pub u32);
34
35impl Months {
36 pub const fn new(months: u32) -> Months {
38 Months(months)
39 }
40
41 pub const fn get(self) -> u32 {
43 self.0
44 }
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
53pub struct IsoWeek {
54 year: i32,
55 week: u32,
56}
57
58impl IsoWeek {
59 pub(crate) const fn new(year: i32, week: u32) -> IsoWeek {
60 IsoWeek { year, week }
61 }
62
63 pub const fn year(self) -> i32 {
65 self.year
66 }
67
68 pub const fn week(self) -> u32 {
70 self.week
71 }
72
73 pub const fn parts(self) -> (i32, u32) {
75 (self.year, self.week)
76 }
77
78 pub fn monday(self) -> Result<Date> {
83 let max_week = iso_week_from_civil(self.year, 12, 28).1;
84 if self.week == 0 || self.week > max_week {
85 return Err(Error::out_of_range("iso week"));
86 }
87 let jan4 = days_from_civil(self.year, 1, 4);
89 let monday_week1 = jan4 - weekday_from_civil(jan4) as i64;
90 Date::from_days_checked(monday_week1 + (self.week as i64 - 1) * 7)
91 }
92
93 pub const fn first_weekday(self) -> Weekday {
95 Weekday::Monday
96 }
97}
98
99impl fmt::Display for IsoWeek {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(f, "{}-W{:02}", self.year, self.week)
102 }
103}
104
105#[cfg(all(test, feature = "alloc"))]
106mod tests {
107 use super::*;
108 use alloc::string::ToString;
109
110 #[test]
111 fn iso_week_monday() {
112 let w = IsoWeek::new(2021, 1);
113 assert_eq!(w.monday().unwrap(), Date::from_ymd(2021, 1, 4).unwrap());
114 let w = IsoWeek::new(2020, 53);
115 assert_eq!(w.monday().unwrap(), Date::from_ymd(2020, 12, 28).unwrap());
116 let w = IsoWeek::new(2026, 1);
117 assert_eq!(w.monday().unwrap(), Date::from_ymd(2025, 12, 29).unwrap());
118 assert_eq!(w.to_string(), "2026-W01");
119 assert!(IsoWeek::new(2024, 54).monday().is_err());
120 assert!(IsoWeek::new(2021, 53).monday().is_err());
121 }
122}