1use crate::{calendar::Calendar, constants::EASTER_MONDAYS};
15use time::{
16 util::{days_in_year, days_in_year_month, is_leap_year},
17 Date, Duration, Error, Month, Weekday,
18};
19
20pub fn unpack_date(date: Date, is_orthodox: bool) -> (i32, Month, u8, Weekday, u16, u16) {
33 let y = date.year();
34 let m = date.month();
35 let d = date.day();
36
37 let wd = date.weekday();
38 let yd = date.ordinal();
39
40 let em = easter_monday(y as usize, is_orthodox);
41
42 (y, m, d, wd, yd, em)
43}
44
45fn easter_monday(year: usize, is_orthodox: bool) -> u16 {
47 EASTER_MONDAYS[usize::from(is_orthodox)][year - 1901]
48}
49
50pub fn is_weekend(date: Date) -> bool {
52 let w = date.weekday();
53
54 w == time::Weekday::Saturday || w == time::Weekday::Sunday
55}
56
57pub fn is_weekday(date: Date) -> bool {
59 !is_weekend(date)
60}
61
62pub fn get_years_in_range(start: Date, end: Date) -> Vec<i32> {
64 (start.year()..=end.year()).collect()
65}
66
67pub fn get_days_in_years_in_range(start: Date, end: Date) -> Vec<u16> {
81 get_years_in_range(start, end)
82 .iter()
83 .map(|&y| days_in_year(y))
84 .collect()
85}
86
87pub fn contains_leap_year(start: Date, end: Date) -> bool {
89 get_years_in_range(start, end)
90 .iter()
91 .any(|&y| is_leap_year(y))
92}
93
94pub fn leap_year_count(start: Date, end: Date) -> i64 {
107 get_years_in_range(start, end)
108 .iter()
109 .filter(|&y| is_leap_year(*y))
110 .collect::<Vec<&i32>>()
111 .len() as i64
112}
113
114pub fn is_last_day_of_february(date: Date) -> bool {
116 let last_day_of_feb_non_leap =
117 date.month() == Month::February && date.day() == 28 && !is_leap_year(date.year());
118 let last_day_of_feb_leap =
119 date.month() == Month::February && date.day() == 29 && is_leap_year(date.year());
120
121 last_day_of_feb_non_leap || last_day_of_feb_leap
122}
123
124pub fn next_business_day<C: Calendar>(date: Date, calendar: &C) -> Date {
126 let mut new_date = date;
127
128 while !calendar.is_business_day(new_date) {
129 new_date = new_date.next_day().unwrap();
130 }
131
132 new_date
133}
134
135pub fn previous_business_day<C: Calendar>(date: Date, calendar: &C) -> Date {
137 let mut new_date = date;
138
139 while !calendar.is_business_day(new_date) {
140 new_date = new_date.previous_day().unwrap();
141 }
142
143 new_date
144}
145
146pub fn date_sequence(start: Date, end: Date) -> Vec<Date> {
148 let mut dates = Vec::with_capacity((end - start).whole_days() as usize);
149 let mut current_date = start;
150
151 while current_date <= end {
152 dates.push(current_date);
153 current_date = current_date + Duration::days(1);
154 }
155
156 dates
157}
158
159pub fn get_first_day_of_month(year: i32, month: Month) -> Result<Weekday, Error> {
161 Ok(Date::from_calendar_date(year, month, 1)?.weekday())
162}
163
164pub fn get_last_day_of_month(year: i32, month: Month) -> Result<Weekday, Error> {
166 let date = Date::from_calendar_date(year, month, 1)?;
167
168 let last_day = date + Duration::days(days_in_year_month(year, month) as i64);
169
170 Ok(last_day.weekday())
171}
172
173pub fn get_first_monday_of_month(year: i32, month: Month) -> Result<Date, Error> {
175 let first_day_date = Date::from_calendar_date(year, month, 1)?;
176
177 match first_day_date.weekday() {
178 Weekday::Monday => Ok(first_day_date),
179 _ => Ok(first_day_date.next_occurrence(Weekday::Monday)),
180 }
181}
182
183pub fn get_first_tuesday_of_month(year: i32, month: Month) -> Result<Date, Error> {
185 let first_day_date = Date::from_calendar_date(year, month, 1)?;
186
187 match first_day_date.weekday() {
188 Weekday::Tuesday => Ok(first_day_date),
189 _ => Ok(first_day_date.next_occurrence(Weekday::Tuesday)),
190 }
191}
192
193pub fn get_first_wednesday_of_month(year: i32, month: Month) -> Result<Date, Error> {
195 let first_day_date = Date::from_calendar_date(year, month, 1)?;
196
197 match first_day_date.weekday() {
198 Weekday::Wednesday => Ok(first_day_date),
199 _ => Ok(first_day_date.next_occurrence(Weekday::Wednesday)),
200 }
201}
202
203pub fn get_first_thursday_of_month(year: i32, month: Month) -> Result<Date, Error> {
205 let first_day_date = Date::from_calendar_date(year, month, 1)?;
206
207 match first_day_date.weekday() {
208 Weekday::Thursday => Ok(first_day_date),
209 _ => Ok(first_day_date.next_occurrence(Weekday::Thursday)),
210 }
211}
212
213pub fn get_first_friday_of_month(year: i32, month: Month) -> Result<Date, Error> {
215 let first_day_date = Date::from_calendar_date(year, month, 1)?;
216
217 match first_day_date.weekday() {
218 Weekday::Friday => Ok(first_day_date),
219 _ => Ok(first_day_date.next_occurrence(Weekday::Friday)),
220 }
221}
222
223pub fn get_first_saturday_of_month(year: i32, month: Month) -> Result<Date, Error> {
225 let first_day_date = Date::from_calendar_date(year, month, 1)?;
226
227 match first_day_date.weekday() {
228 Weekday::Saturday => Ok(first_day_date),
229 _ => Ok(first_day_date.next_occurrence(Weekday::Saturday)),
230 }
231}
232
233pub fn get_first_sunday_of_month(year: i32, month: Month) -> Result<Date, Error> {
235 let first_day_date = Date::from_calendar_date(year, month, 1)?;
236
237 match first_day_date.weekday() {
238 Weekday::Sunday => Ok(first_day_date),
239 _ => Ok(first_day_date.next_occurrence(Weekday::Sunday)),
240 }
241}
242
243pub fn get_last_monday_of_month(year: i32, month: Month) -> Result<Date, Error> {
245 let days_in_month = days_in_year_month(year, month);
246 let last_day_date = Date::from_calendar_date(year, month, days_in_month)?;
247
248 match last_day_date.weekday() {
249 Weekday::Monday => Ok(last_day_date),
250 _ => Ok(last_day_date.prev_occurrence(Weekday::Monday)),
251 }
252}
253
254pub fn get_last_tuesday_of_month(year: i32, month: Month) -> Result<Date, Error> {
256 let days_in_month = days_in_year_month(year, month);
257 let last_day_date = Date::from_calendar_date(year, month, days_in_month)?;
258
259 match last_day_date.weekday() {
260 Weekday::Tuesday => Ok(last_day_date),
261 _ => Ok(last_day_date.prev_occurrence(Weekday::Tuesday)),
262 }
263}
264
265pub fn get_last_wednesday_of_month(year: i32, month: Month) -> Result<Date, Error> {
267 let days_in_month = days_in_year_month(year, month);
268 let last_day_date = Date::from_calendar_date(year, month, days_in_month)?;
269
270 match last_day_date.weekday() {
271 Weekday::Wednesday => Ok(last_day_date),
272 _ => Ok(last_day_date.prev_occurrence(Weekday::Wednesday)),
273 }
274}
275
276pub fn get_last_thursday_of_month(year: i32, month: Month) -> Result<Date, Error> {
278 let days_in_month = days_in_year_month(year, month);
279 let last_day_date = Date::from_calendar_date(year, month, days_in_month)?;
280
281 match last_day_date.weekday() {
282 Weekday::Thursday => Ok(last_day_date),
283 _ => Ok(last_day_date.prev_occurrence(Weekday::Thursday)),
284 }
285}
286
287pub fn get_last_friday_of_month(year: i32, month: Month) -> Result<Date, Error> {
289 let days_in_month = days_in_year_month(year, month);
290 let last_day_date = Date::from_calendar_date(year, month, days_in_month)?;
291
292 match last_day_date.weekday() {
293 Weekday::Friday => Ok(last_day_date),
294 _ => Ok(last_day_date.prev_occurrence(Weekday::Friday)),
295 }
296}
297
298pub fn get_last_saturday_of_month(year: i32, month: Month) -> Result<Date, Error> {
300 let days_in_month = days_in_year_month(year, month);
301 let last_day_date = Date::from_calendar_date(year, month, days_in_month)?;
302
303 match last_day_date.weekday() {
304 Weekday::Saturday => Ok(last_day_date),
305 _ => Ok(last_day_date.prev_occurrence(Weekday::Saturday)),
306 }
307}
308
309pub fn get_last_sunday_of_month(year: i32, month: Month) -> Result<Date, Error> {
311 let days_in_month = days_in_year_month(year, month);
312 let last_day_date = Date::from_calendar_date(year, month, days_in_month)?;
313
314 match last_day_date.weekday() {
315 Weekday::Sunday => Ok(last_day_date),
316 _ => Ok(last_day_date.prev_occurrence(Weekday::Sunday)),
317 }
318}
319
320#[cfg(test)]
325mod test_utilities {
326 use super::*;
327
328 #[test]
329 fn test_first_x_day_of_month() {
330 let y = 2024;
331 let m = Month::January;
332
333 assert_eq!(
334 get_first_monday_of_month(y, m).unwrap(),
335 Date::from_calendar_date(y, m, 1).unwrap()
336 );
337 assert_eq!(
338 get_first_tuesday_of_month(y, m).unwrap(),
339 Date::from_calendar_date(y, m, 2).unwrap()
340 );
341 assert_eq!(
342 get_first_wednesday_of_month(y, m).unwrap(),
343 Date::from_calendar_date(y, m, 3).unwrap()
344 );
345 assert_eq!(
346 get_first_thursday_of_month(y, m).unwrap(),
347 Date::from_calendar_date(y, m, 4).unwrap()
348 );
349 assert_eq!(
350 get_first_friday_of_month(y, m).unwrap(),
351 Date::from_calendar_date(y, m, 5).unwrap()
352 );
353 assert_eq!(
354 get_first_saturday_of_month(y, m).unwrap(),
355 Date::from_calendar_date(y, m, 6).unwrap()
356 );
357 assert_eq!(
358 get_first_sunday_of_month(y, m).unwrap(),
359 Date::from_calendar_date(y, m, 7).unwrap()
360 );
361 }
362
363 #[test]
364 fn test_last_x_day_of_month() {
365 let y = 2024;
366 let m = Month::January;
367
368 assert_eq!(
369 get_last_monday_of_month(y, m).unwrap(),
370 Date::from_calendar_date(y, m, 29).unwrap()
371 );
372 assert_eq!(
373 get_last_tuesday_of_month(y, m).unwrap(),
374 Date::from_calendar_date(y, m, 30).unwrap()
375 );
376 assert_eq!(
377 get_last_wednesday_of_month(y, m).unwrap(),
378 Date::from_calendar_date(y, m, 31).unwrap()
379 );
380 assert_eq!(
381 get_last_thursday_of_month(y, m).unwrap(),
382 Date::from_calendar_date(y, m, 25).unwrap()
383 );
384 assert_eq!(
385 get_last_friday_of_month(y, m).unwrap(),
386 Date::from_calendar_date(y, m, 26).unwrap()
387 );
388 assert_eq!(
389 get_last_saturday_of_month(y, m).unwrap(),
390 Date::from_calendar_date(y, m, 27).unwrap()
391 );
392 assert_eq!(
393 get_last_sunday_of_month(y, m).unwrap(),
394 Date::from_calendar_date(y, m, 28).unwrap()
395 );
396 }
397
398 #[test]
399 fn test_get_first_day_of_month() {
400 assert_eq!(
401 get_first_day_of_month(2024, Month::January).unwrap(),
402 Weekday::Monday
403 );
404 assert_eq!(
405 get_first_day_of_month(2024, Month::February).unwrap(),
406 Weekday::Thursday
407 );
408 assert_eq!(
409 get_first_day_of_month(2024, Month::March).unwrap(),
410 Weekday::Friday
411 );
412 assert_eq!(
413 get_first_day_of_month(2024, Month::April).unwrap(),
414 Weekday::Monday
415 );
416 assert_eq!(
417 get_first_day_of_month(2024, Month::May).unwrap(),
418 Weekday::Wednesday
419 );
420 assert_eq!(
421 get_first_day_of_month(2024, Month::June).unwrap(),
422 Weekday::Saturday
423 );
424 assert_eq!(
425 get_first_day_of_month(2024, Month::July).unwrap(),
426 Weekday::Monday
427 );
428 assert_eq!(
429 get_first_day_of_month(2024, Month::August).unwrap(),
430 Weekday::Thursday
431 );
432 assert_eq!(
433 get_first_day_of_month(2024, Month::September).unwrap(),
434 Weekday::Sunday
435 );
436 assert_eq!(
437 get_first_day_of_month(2024, Month::October).unwrap(),
438 Weekday::Tuesday
439 );
440 assert_eq!(
441 get_first_day_of_month(2024, Month::November).unwrap(),
442 Weekday::Friday
443 );
444 assert_eq!(
445 get_first_day_of_month(2024, Month::December).unwrap(),
446 Weekday::Sunday
447 );
448 }
449
450 #[test]
451 fn test_get_first_monday_of_month() {
452 assert_eq!(
453 get_first_monday_of_month(2024, Month::January).unwrap(),
454 Date::from_calendar_date(2024, Month::January, 1).unwrap()
455 );
456 assert_eq!(
457 get_first_monday_of_month(2024, Month::February).unwrap(),
458 Date::from_calendar_date(2024, Month::February, 5).unwrap()
459 );
460 assert_eq!(
461 get_first_monday_of_month(2024, Month::March).unwrap(),
462 Date::from_calendar_date(2024, Month::March, 4).unwrap()
463 );
464 assert_eq!(
465 get_first_monday_of_month(2024, Month::April).unwrap(),
466 Date::from_calendar_date(2024, Month::April, 1).unwrap()
467 );
468 assert_eq!(
469 get_first_monday_of_month(2024, Month::May).unwrap(),
470 Date::from_calendar_date(2024, Month::May, 6).unwrap()
471 );
472 assert_eq!(
473 get_first_monday_of_month(2024, Month::June).unwrap(),
474 Date::from_calendar_date(2024, Month::June, 3).unwrap()
475 );
476 assert_eq!(
477 get_first_monday_of_month(2024, Month::July).unwrap(),
478 Date::from_calendar_date(2024, Month::July, 1).unwrap()
479 );
480 assert_eq!(
481 get_first_monday_of_month(2024, Month::August).unwrap(),
482 Date::from_calendar_date(2024, Month::August, 5).unwrap()
483 );
484 assert_eq!(
485 get_first_monday_of_month(2024, Month::September).unwrap(),
486 Date::from_calendar_date(2024, Month::September, 2).unwrap()
487 );
488 assert_eq!(
489 get_first_monday_of_month(2024, Month::October).unwrap(),
490 Date::from_calendar_date(2024, Month::October, 7).unwrap()
491 );
492 assert_eq!(
493 get_first_monday_of_month(2024, Month::November).unwrap(),
494 Date::from_calendar_date(2024, Month::November, 4).unwrap()
495 );
496 assert_eq!(
497 get_first_monday_of_month(2024, Month::December).unwrap(),
498 Date::from_calendar_date(2024, Month::December, 2).unwrap()
499 );
500 }
501}