1use time::util::is_leap_year;
12
13use super::super::{Date, Month, Weekday};
14
15use super::CalendarWeek;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
18pub struct CalendarMonth {
19 year: i32,
20 month: Month,
21}
22
23impl std::fmt::Display for CalendarMonth {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{} {}", self.month, self.year)
26 }
27}
28
29impl CalendarMonth {
30 pub(in super::super) const fn new(year: i32, month: Month) -> Self {
31 Self { year, month }
32 }
33
34 pub fn prev(&self) -> CalendarMonth {
35 let (month, year) = match self.month {
36 Month::January => (Month::December, self.year - 1),
37 _ => (self.month.previous(), self.year),
38 };
39 Self { year, month }
40 }
41
42 pub fn next(&self) -> CalendarMonth {
43 let (month, year) = match self.month {
44 Month::December => (Month::January, self.year + 1),
45 _ => (self.month.next(), self.year),
46 };
47 Self { year, month }
48 }
49
50 pub fn days(&self) -> u8 {
51 match self.month {
52 Month::April | Month::June | Month::September | Month::November => 30,
53 Month::February => {
54 if is_leap_year(self.year) {
55 29
56 } else {
57 28
58 }
59 }
60 _ => 31,
61 }
62 }
63
64 pub fn weeks(&self, starting_weekday: Weekday) -> Vec<CalendarWeek> {
65 let days = self.days();
66
67 let first_day = Date::new(1, self.month, self.year).unwrap();
68 let last_day = Date::new(days, self.month, self.year).unwrap();
69 let first_weekday = first_day.weekday();
70 let first_week = first_day.iso_week();
71 let last_week = last_day.iso_week();
72
73 let to_fill = apply_starting_weekday(first_weekday, starting_weekday);
74
75 let mut weeks = Vec::with_capacity(6);
76 let mut current_weekdays = [(0, false); 7];
77
78 let mut idx = 0;
79
80 let prev_month_days = self.prev().days();
82 for i in (0..to_fill).rev() {
83 current_weekdays[idx] = (prev_month_days - i, false);
84 idx += 1;
85 }
86
87 let mut current_week = first_week;
89 for day in 1..=days {
90 if idx == 7 {
91 weeks.push(CalendarWeek::new(
92 current_week,
93 current_weekdays,
94 starting_weekday,
95 ));
96 current_week = Date::new(day, self.month, self.year).unwrap().iso_week();
97 idx = 0;
98 }
99 current_weekdays[idx] = (day, true);
100 idx += 1;
101 }
102
103 let mut next_month_day = 1;
105 for i in idx..7 {
106 current_weekdays[i] = (next_month_day, false);
107 next_month_day += 1;
108 }
109
110 weeks.push(CalendarWeek::new(
111 last_week,
112 current_weekdays,
113 starting_weekday,
114 ));
115
116 weeks
117 }
118}
119
120fn apply_starting_weekday(weekday: Weekday, start_with: Weekday) -> u8 {
121 let raw = weekday as i8 - start_with as i8;
122 (match raw < 0 {
123 true => raw + 7,
124 false => raw,
125 }) as u8
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_calendar_weeks() {
134 let cm = CalendarMonth::new(2026, Month::January);
135 let weeks = cm.weeks(Weekday::Monday);
136 let expected = vec![
137 CalendarWeek::new(
138 1,
139 [
140 (29, false),
141 (30, false),
142 (31, false),
143 (01, true),
144 (02, true),
145 (03, true),
146 (04, true),
147 ],
148 Weekday::Monday,
149 ),
150 CalendarWeek::new(
151 2,
152 [
153 (05, true),
154 (06, true),
155 (07, true),
156 (08, true),
157 (09, true),
158 (10, true),
159 (11, true),
160 ],
161 Weekday::Monday,
162 ),
163 CalendarWeek::new(
164 3,
165 [
166 (12, true),
167 (13, true),
168 (14, true),
169 (15, true),
170 (16, true),
171 (17, true),
172 (18, true),
173 ],
174 Weekday::Monday,
175 ),
176 CalendarWeek::new(
177 4,
178 [
179 (19, true),
180 (20, true),
181 (21, true),
182 (22, true),
183 (23, true),
184 (24, true),
185 (25, true),
186 ],
187 Weekday::Monday,
188 ),
189 CalendarWeek::new(
190 5,
191 [
192 (26, true),
193 (27, true),
194 (28, true),
195 (29, true),
196 (30, true),
197 (31, true),
198 (01, false),
199 ],
200 Weekday::Monday,
201 ),
202 ];
203 assert_eq!(weeks, expected);
204
205 let cm = CalendarMonth::new(2025, Month::December);
206 let weeks = cm.weeks(Weekday::Monday);
207 let expected = vec![
208 CalendarWeek::new(
209 49,
210 [
211 (01, true),
212 (02, true),
213 (03, true),
214 (04, true),
215 (05, true),
216 (06, true),
217 (07, true),
218 ],
219 Weekday::Monday,
220 ),
221 CalendarWeek::new(
222 50,
223 [
224 (08, true),
225 (09, true),
226 (10, true),
227 (11, true),
228 (12, true),
229 (13, true),
230 (14, true),
231 ],
232 Weekday::Monday,
233 ),
234 CalendarWeek::new(
235 51,
236 [
237 (15, true),
238 (16, true),
239 (17, true),
240 (18, true),
241 (19, true),
242 (20, true),
243 (21, true),
244 ],
245 Weekday::Monday,
246 ),
247 CalendarWeek::new(
248 52,
249 [
250 (22, true),
251 (23, true),
252 (24, true),
253 (25, true),
254 (26, true),
255 (27, true),
256 (28, true),
257 ],
258 Weekday::Monday,
259 ),
260 CalendarWeek::new(
261 1,
262 [
263 (29, true),
264 (30, true),
265 (31, true),
266 (01, false),
267 (02, false),
268 (03, false),
269 (04, false),
270 ],
271 Weekday::Monday,
272 ),
273 ];
274 assert_eq!(weeks, expected);
275
276 let cm = CalendarMonth::new(2027, Month::January);
277 let weeks = cm.weeks(Weekday::Monday);
278 let expected = vec![
279 CalendarWeek::new(
280 53,
281 [
282 (28, false),
283 (29, false),
284 (30, false),
285 (31, false),
286 (01, true),
287 (02, true),
288 (03, true),
289 ],
290 Weekday::Monday,
291 ),
292 CalendarWeek::new(
293 1,
294 [
295 (04, true),
296 (05, true),
297 (06, true),
298 (07, true),
299 (08, true),
300 (09, true),
301 (10, true),
302 ],
303 Weekday::Monday,
304 ),
305 CalendarWeek::new(
306 2,
307 [
308 (11, true),
309 (12, true),
310 (13, true),
311 (14, true),
312 (15, true),
313 (16, true),
314 (17, true),
315 ],
316 Weekday::Monday,
317 ),
318 CalendarWeek::new(
319 3,
320 [
321 (18, true),
322 (19, true),
323 (20, true),
324 (21, true),
325 (22, true),
326 (23, true),
327 (24, true),
328 ],
329 Weekday::Monday,
330 ),
331 CalendarWeek::new(
332 4,
333 [
334 (25, true),
335 (26, true),
336 (27, true),
337 (28, true),
338 (29, true),
339 (30, true),
340 (31, true),
341 ],
342 Weekday::Monday,
343 ),
344 ];
345 assert_eq!(weeks, expected);
346 }
347}