1use chrono::{Datelike, Duration, NaiveDate, Weekday};
6use std::error::Error;
7use std::result::Result;
8
9use crate::utils::dateutils::dates::{find_next_date, AggregationType, DateFreq, DatesGenerator};
10
11use crate::utils::dateutils::dates;
12
13pub type BDateFreq = DateFreq;
15
16#[derive(Debug, Clone)]
20pub struct BDatesList {
21 start_date_str: String,
22 end_date_str: String,
23 freq: DateFreq,
24}
25
26impl BDatesList {
121 pub fn new(start_date_str: String, end_date_str: String, freq: DateFreq) -> Self {
129 BDatesList {
130 start_date_str,
131 end_date_str,
132 freq,
133 }
134 }
135
136 pub fn from_n_periods(
154 start_date_str: String,
155 freq: DateFreq,
156 n_periods: usize,
157 ) -> Result<Self, Box<dyn Error>> {
158 if n_periods == 0 {
159 return Err("n_periods must be greater than 0".into());
160 }
161
162 let start_date = NaiveDate::parse_from_str(&start_date_str, "%Y-%m-%d")?;
163
164 let generator = BDatesGenerator::new(start_date, freq, n_periods)?;
166 let dates: Vec<NaiveDate> = generator.collect();
167
168 let last_date = dates
170 .last()
171 .ok_or("Generator failed to produce dates for the specified periods")?;
172
173 let end_date_str = last_date.format("%Y-%m-%d").to_string();
174
175 Ok(BDatesList {
176 start_date_str,
177 end_date_str,
178 freq,
179 })
180 }
181
182 pub fn list(&self) -> Result<Vec<NaiveDate>, Box<dyn Error>> {
190 get_bdates_list_with_freq(&self.start_date_str, &self.end_date_str, self.freq)
192 }
193
194 pub fn count(&self) -> Result<usize, Box<dyn Error>> {
200 self.list().map(|list| list.len())
202 }
203
204 pub fn groups(&self) -> Result<Vec<Vec<NaiveDate>>, Box<dyn Error>> {
214 let dates = self.list()?;
215 dates::group_dates_helper(dates, self.freq)
216 }
217
218 pub fn start_date(&self) -> Result<NaiveDate, Box<dyn Error>> {
224 NaiveDate::parse_from_str(&self.start_date_str, "%Y-%m-%d").map_err(|e| e.into())
225 }
226
227 pub fn start_date_str(&self) -> &str {
229 &self.start_date_str
230 }
231
232 pub fn end_date(&self) -> Result<NaiveDate, Box<dyn Error>> {
238 NaiveDate::parse_from_str(&self.end_date_str, "%Y-%m-%d").map_err(|e| e.into())
239 }
240
241 pub fn end_date_str(&self) -> &str {
243 &self.end_date_str
244 }
245
246 pub fn freq(&self) -> DateFreq {
248 self.freq
249 }
250
251 pub fn freq_str(&self) -> String {
253 self.freq.to_string()
254 }
255}
256
257#[derive(Debug, Clone)]
320pub struct BDatesGenerator {
321 dates_generator: DatesGenerator,
322 start_date: NaiveDate,
323 freq: DateFreq,
324 periods_remaining: usize,
325}
326
327impl BDatesGenerator {
328 pub fn new(
346 start_date: NaiveDate,
347 freq: DateFreq,
348 n_periods: usize,
349 ) -> Result<Self, Box<dyn Error>> {
350 let adj_n_periods = match freq {
352 DateFreq::Daily => n_periods + 5,
353 DateFreq::WeeklyMonday
354 | DateFreq::WeeklyFriday
355 | DateFreq::MonthStart
356 | DateFreq::MonthEnd
357 | DateFreq::QuarterStart
358 | DateFreq::QuarterEnd
359 | DateFreq::YearStart
360 | DateFreq::YearEnd => n_periods + 2,
361 };
362
363 let dates_generator = DatesGenerator::new(start_date, freq, adj_n_periods)?;
364
365 Ok(BDatesGenerator {
366 dates_generator,
367 start_date,
368 freq,
369 periods_remaining: n_periods,
370 })
371 }
372}
373
374impl Iterator for BDatesGenerator {
375 type Item = NaiveDate;
376
377 fn next(&mut self) -> Option<Self::Item> {
380 if self.periods_remaining == 0 {
382 return None;
383 }
384
385 let next_date = self.dates_generator.next()?;
387
388 let next_date = match self.freq {
389 DateFreq::Daily => {
390 let mut new_candidate = next_date.clone();
391 while !is_business_date(new_candidate) {
392 new_candidate = self.dates_generator.next()?;
393 }
394 new_candidate
395 }
396
397 DateFreq::WeeklyMonday | DateFreq::WeeklyFriday => next_date,
398 DateFreq::MonthEnd | DateFreq::QuarterEnd | DateFreq::YearEnd => {
399 let adjusted_date = iter_reverse_till_bdate(next_date);
400 if self.start_date > adjusted_date {
401 return self.next();
403 }
404 adjusted_date
405 }
406 DateFreq::MonthStart | DateFreq::QuarterStart | DateFreq::YearStart => {
407 iter_till_bdate(next_date)
409 }
410 };
411 self.periods_remaining -= 1;
413 Some(next_date)
414 }
415}
416
417pub fn is_business_date(date: NaiveDate) -> bool {
419 match date.weekday() {
420 Weekday::Sat | Weekday::Sun => false,
421 _ => true,
422 }
423}
424
425pub fn find_next_bdate(date: NaiveDate, freq: DateFreq) -> NaiveDate {
426 let next_date: NaiveDate = find_next_date(date, freq).unwrap();
427 let next_date = iter_till_bdate(next_date);
428 next_date
429}
430
431pub fn find_first_bdate_on_or_after(date: NaiveDate, freq: DateFreq) -> NaiveDate {
432 let first_date = dates::find_first_date_on_or_after(date, freq).unwrap();
434 let first_date = iter_till_bdate_by_freq(first_date, freq);
435 first_date
438}
439
440fn iter_till_bdate_by_freq(date: NaiveDate, freq: DateFreq) -> NaiveDate {
443 let agg_type = freq.agg_type();
444 let dur = match agg_type {
445 AggregationType::Start => Duration::days(1),
446 AggregationType::End => Duration::days(-1),
447 };
448 let mut current_date = date;
449 while !is_business_date(current_date) {
450 current_date = current_date + dur;
451 }
452 current_date
453}
454
455fn iter_till_bdate(date: NaiveDate) -> NaiveDate {
457 let mut current_date = date;
458 while !is_business_date(current_date) {
459 current_date = current_date + Duration::days(1);
460 }
461 current_date
462}
463
464fn iter_reverse_till_bdate(date: NaiveDate) -> NaiveDate {
466 let mut current_date = date;
467 while !is_business_date(current_date) {
468 current_date = current_date - Duration::days(1);
469 }
470 current_date
471}
472
473pub fn get_bdates_list_with_freq(
475 start_date_str: &str,
476 end_date_str: &str,
477 freq: DateFreq,
478) -> Result<Vec<NaiveDate>, Box<dyn Error>> {
479 let start_date = NaiveDate::parse_from_str(start_date_str, "%Y-%m-%d")?;
482 let end_date = NaiveDate::parse_from_str(end_date_str, "%Y-%m-%d")?;
483
484 let mut dates = dates::get_dates_list_with_freq_from_naive_date(start_date, end_date, freq)?;
485
486 match freq {
487 DateFreq::Daily => {
488 dates.retain(|date| is_business_date(*date));
489 }
490 DateFreq::WeeklyMonday | DateFreq::WeeklyFriday => {
491 }
493 _ => {
494 dates.iter_mut().for_each(|date| {
495 *date = iter_till_bdate_by_freq(*date, freq);
496 });
497 }
498 }
499
500 Ok(dates)
501}
502
503#[cfg(test)]
506mod tests {
507 use super::*;
508 use chrono::NaiveDate;
509 use std::str::FromStr;
510
511 fn date(year: i32, month: u32, day: u32) -> NaiveDate {
513 NaiveDate::from_ymd_opt(year, month, day).expect("Invalid date in test setup")
514 }
515
516 #[test]
519 fn test_date_freq_from_str() -> Result<(), Box<dyn Error>> {
520 assert_eq!(DateFreq::from_str("D")?, DateFreq::Daily);
521 assert_eq!("D".parse::<DateFreq>()?, DateFreq::Daily); assert_eq!(DateFreq::from_str("W")?, DateFreq::WeeklyMonday);
523 assert_eq!(DateFreq::from_str("M")?, DateFreq::MonthStart);
524 assert_eq!(DateFreq::from_str("Q")?, DateFreq::QuarterStart);
525
526 assert_eq!(DateFreq::from_str("Y")?, DateFreq::YearStart);
528 assert_eq!(DateFreq::from_str("A")?, DateFreq::YearStart);
529 assert_eq!(DateFreq::from_str("AS")?, DateFreq::YearStart);
530 assert_eq!(DateFreq::from_str("YS")?, DateFreq::YearStart);
531 assert_eq!("Y".parse::<DateFreq>()?, DateFreq::YearStart); assert_eq!(DateFreq::from_str("ME")?, DateFreq::MonthEnd);
534 assert_eq!(DateFreq::from_str("QE")?, DateFreq::QuarterEnd);
535 assert_eq!(DateFreq::from_str("WF")?, DateFreq::WeeklyFriday);
536 assert_eq!("WF".parse::<DateFreq>()?, DateFreq::WeeklyFriday); assert_eq!(DateFreq::from_str("YE")?, DateFreq::YearEnd);
540 assert_eq!(DateFreq::from_str("AE")?, DateFreq::YearEnd);
541
542 assert_eq!(DateFreq::from_str("WS")?, DateFreq::WeeklyMonday);
544 assert_eq!(DateFreq::from_str("MS")?, DateFreq::MonthStart);
545 assert_eq!(DateFreq::from_str("QS")?, DateFreq::QuarterStart);
546
547 assert!(DateFreq::from_str("INVALID").is_err());
549 assert!("INVALID".parse::<DateFreq>().is_err()); let err = DateFreq::from_str("INVALID").unwrap_err();
551 assert_eq!(err.to_string(), "Invalid frequency specified: INVALID");
552
553 Ok(())
554 }
555
556 #[test]
557 fn test_date_freq_to_string() {
558 assert_eq!(DateFreq::Daily.to_string(), "D");
559 assert_eq!(DateFreq::WeeklyMonday.to_string(), "W");
560 assert_eq!(DateFreq::MonthStart.to_string(), "M");
561 assert_eq!(DateFreq::QuarterStart.to_string(), "Q");
562 assert_eq!(DateFreq::YearStart.to_string(), "Y"); assert_eq!(DateFreq::MonthEnd.to_string(), "ME");
564 assert_eq!(DateFreq::QuarterEnd.to_string(), "QE");
565 assert_eq!(DateFreq::WeeklyFriday.to_string(), "WF");
566 assert_eq!(DateFreq::YearEnd.to_string(), "YE");
567 }
568
569 #[test]
570 fn test_date_freq_from_string() -> Result<(), Box<dyn Error>> {
571 assert_eq!(DateFreq::from_string("D".to_string())?, DateFreq::Daily);
572 assert!(DateFreq::from_string("INVALID".to_string()).is_err());
573 Ok(())
574 }
575
576 #[test]
577 fn test_date_freq_agg_type() {
578 assert_eq!(DateFreq::Daily.agg_type(), AggregationType::Start);
579 assert_eq!(DateFreq::WeeklyMonday.agg_type(), AggregationType::Start);
580 assert_eq!(DateFreq::MonthStart.agg_type(), AggregationType::Start);
581 assert_eq!(DateFreq::QuarterStart.agg_type(), AggregationType::Start);
582 assert_eq!(DateFreq::YearStart.agg_type(), AggregationType::Start);
583
584 assert_eq!(DateFreq::WeeklyFriday.agg_type(), AggregationType::End);
585 assert_eq!(DateFreq::MonthEnd.agg_type(), AggregationType::End);
586 assert_eq!(DateFreq::QuarterEnd.agg_type(), AggregationType::End);
587 assert_eq!(DateFreq::YearEnd.agg_type(), AggregationType::End);
588 }
589
590 #[test]
593 fn test_bdates_list_properties_new() -> Result<(), Box<dyn Error>> {
594 let start_str = "2023-01-01".to_string();
595 let end_str = "2023-12-31".to_string();
596 let freq = DateFreq::QuarterEnd;
597 let dates_list = BDatesList::new(start_str.clone(), end_str.clone(), freq);
598
599 assert_eq!(dates_list.start_date_str(), start_str);
601 assert_eq!(dates_list.end_date_str(), end_str);
603 assert_eq!(dates_list.freq(), freq);
605 assert_eq!(dates_list.freq_str(), "QE");
607
608 assert_eq!(dates_list.start_date()?, date(2023, 1, 1));
610 assert_eq!(dates_list.end_date()?, date(2023, 12, 31));
611
612 Ok(())
613 }
614
615 #[test]
616 fn test_bdates_list_properties_from_n_periods() -> Result<(), Box<dyn Error>> {
617 let start_str = "2023-01-01".to_string(); let freq = DateFreq::Daily;
619 let n_periods = 5; let dates_list = BDatesList::from_n_periods(start_str.clone(), freq, n_periods)?;
621
622 assert_eq!(dates_list.start_date_str(), start_str);
624 assert_eq!(dates_list.end_date_str(), "2023-01-06");
626 assert_eq!(dates_list.freq(), freq);
628 assert_eq!(dates_list.freq_str(), "D");
630
631 assert_eq!(dates_list.start_date()?, date(2023, 1, 1));
633 assert_eq!(dates_list.end_date()?, date(2023, 1, 6));
634
635 assert_eq!(
637 dates_list.list()?,
638 vec![
639 date(2023, 1, 2),
640 date(2023, 1, 3),
641 date(2023, 1, 4),
642 date(2023, 1, 5),
643 date(2023, 1, 6)
644 ]
645 );
646 assert_eq!(dates_list.count()?, 5);
647
648 Ok(())
649 }
650
651 #[test]
652 fn test_bdates_list_from_n_periods_zero_periods() {
653 let start_str = "2023-01-01".to_string();
654 let freq = DateFreq::Daily;
655 let n_periods = 0;
656 let result = BDatesList::from_n_periods(start_str.clone(), freq, n_periods);
657 assert!(result.is_err());
658 assert_eq!(
659 result.unwrap_err().to_string(),
660 "n_periods must be greater than 0"
661 );
662 }
663
664 #[test]
665 fn test_bdates_list_from_n_periods_invalid_start_date() {
666 let start_str = "invalid-date".to_string();
667 let freq = DateFreq::Daily;
668 let n_periods = 5;
669 let result = BDatesList::from_n_periods(start_str.clone(), freq, n_periods);
670 assert!(result.is_err());
671 assert!(result
673 .unwrap_err()
674 .to_string()
675 .contains("input contains invalid characters"));
676 }
677
678 #[test]
679 fn test_bdates_list_invalid_date_string_new() {
680 let dates_list_start_invalid = BDatesList::new(
681 "invalid-date".to_string(),
682 "2023-12-31".to_string(),
683 DateFreq::Daily,
684 );
685 assert!(dates_list_start_invalid.list().is_err());
686 assert!(dates_list_start_invalid.count().is_err());
687 assert!(dates_list_start_invalid.groups().is_err());
688 assert!(dates_list_start_invalid.start_date().is_err());
689 assert!(dates_list_start_invalid.end_date().is_ok()); let dates_list_end_invalid = BDatesList::new(
692 "2023-01-01".to_string(),
693 "invalid-date".to_string(),
694 DateFreq::Daily,
695 );
696 assert!(dates_list_end_invalid.list().is_err());
697 assert!(dates_list_end_invalid.count().is_err());
698 assert!(dates_list_end_invalid.groups().is_err());
699 assert!(dates_list_end_invalid.start_date().is_ok()); assert!(dates_list_end_invalid.end_date().is_err());
701 }
702
703 #[test]
706 fn test_bdates_list_quarterly_end_list() -> Result<(), Box<dyn Error>> {
708 let dates_list = BDatesList::new(
709 "2023-01-01".to_string(),
710 "2023-12-31".to_string(),
711 DateFreq::QuarterEnd,
712 );
713
714 let list = dates_list.list()?;
715 assert_eq!(list.len(), 4);
716 assert_eq!(
717 list,
718 vec![
719 date(2023, 3, 31),
720 date(2023, 6, 30),
721 date(2023, 9, 29),
722 date(2023, 12, 29)
723 ]
724 ); Ok(())
727 }
728
729 #[test]
730 fn test_bdates_list_weekly_monday_list() -> Result<(), Box<dyn Error>> {
732 let dates_list = BDatesList::new(
734 "2023-10-30".to_string(), "2023-11-12".to_string(), DateFreq::WeeklyMonday,
737 );
738
739 let list = dates_list.list()?;
740 assert_eq!(list.len(), 2);
745 assert_eq!(list, vec![date(2023, 10, 30), date(2023, 11, 6)]);
746
747 Ok(())
748 }
749
750 #[test]
751 fn test_bdates_list_daily_list() -> Result<(), Box<dyn Error>> {
753 let dates_list = BDatesList::new(
754 "2023-11-01".to_string(), "2023-11-05".to_string(), DateFreq::Daily,
757 );
758
759 let list = dates_list.list()?;
760 assert_eq!(list.len(), 3);
762 assert_eq!(
763 list,
764 vec![date(2023, 11, 1), date(2023, 11, 2), date(2023, 11, 3)]
765 );
766
767 Ok(())
768 }
769
770 #[test]
771 fn test_bdates_list_empty_range_list() -> Result<(), Box<dyn Error>> {
773 let dates_list = BDatesList::new(
774 "2023-12-31".to_string(),
775 "2023-01-01".to_string(), DateFreq::Daily,
777 );
778 let list = dates_list.list()?;
779 assert!(list.is_empty());
780 assert_eq!(dates_list.count()?, 0); Ok(())
783 }
784
785 #[test]
786 fn test_bdates_list_count() -> Result<(), Box<dyn Error>> {
788 let dates_list = BDatesList::new(
789 "2023-01-01".to_string(),
790 "2023-12-31".to_string(),
791 DateFreq::MonthEnd,
792 );
793 assert_eq!(dates_list.count()?, 12, "{:?}", dates_list.list()); let dates_list_weekly = BDatesList::new(
796 "2023-11-01".to_string(), "2023-11-30".to_string(), DateFreq::WeeklyFriday,
799 );
800 assert_eq!(dates_list_weekly.count()?, 4);
802
803 Ok(())
804 }
805
806 #[test]
807 fn test_bdates_list_yearly_start() -> Result<(), Box<dyn Error>> {
809 let dates_list = BDatesList::new(
810 "2023-06-01".to_string(),
811 "2025-06-01".to_string(),
812 DateFreq::YearStart,
813 );
814 assert_eq!(dates_list.list()?, vec![date(2024, 1, 1), date(2025, 1, 1)]);
819 assert_eq!(dates_list.count()?, 2);
820
821 Ok(())
822 }
823
824 #[test]
825 fn test_bdates_list_monthly_start() -> Result<(), Box<dyn Error>> {
827 let dates_list = BDatesList::new(
828 "2023-11-15".to_string(), "2024-02-15".to_string(), DateFreq::MonthStart,
831 );
832 assert_eq!(
839 dates_list.list()?,
840 vec![date(2023, 12, 1), date(2024, 1, 1), date(2024, 2, 1)]
841 );
842 assert_eq!(dates_list.count()?, 3);
843
844 Ok(())
845 }
846
847 #[test]
848 fn test_bdates_list_weekly_friday_midweek_end() -> Result<(), Box<dyn Error>> {
850 let dates_list = BDatesList::new(
851 "2023-11-01".to_string(), "2023-11-14".to_string(), DateFreq::WeeklyFriday,
854 );
855 assert_eq!(
860 dates_list.list()?,
861 vec![date(2023, 11, 3), date(2023, 11, 10)]
862 );
863 assert_eq!(dates_list.count()?, 2);
864
865 Ok(())
866 }
867
868 #[test]
871 fn test_bdates_list_groups_monthly_end() -> Result<(), Box<dyn Error>> {
873 let dates_list = BDatesList::new(
874 "2023-10-15".to_string(), "2024-01-15".to_string(), DateFreq::MonthEnd,
877 );
878
879 let groups = dates_list.groups()?;
880 assert_eq!(groups.len(), 3);
886
887 assert_eq!(groups[0], vec![date(2023, 10, 31)]); assert_eq!(groups[1], vec![date(2023, 11, 30)]); assert_eq!(groups[2], vec![date(2023, 12, 29)]); Ok(())
894 }
895
896 #[test]
897 fn test_bdates_list_groups_daily() -> Result<(), Box<dyn Error>> {
899 let dates_list = BDatesList::new(
900 "2023-11-01".to_string(), "2023-11-05".to_string(), DateFreq::Daily,
903 );
904
905 let groups = dates_list.groups()?;
906 assert_eq!(groups.len(), 3);
908
909 assert_eq!(groups[0], vec![date(2023, 11, 1)]);
911 assert_eq!(groups[1], vec![date(2023, 11, 2)]);
912 assert_eq!(groups[2], vec![date(2023, 11, 3)]);
913
914 Ok(())
915 }
916
917 #[test]
918 fn test_bdates_list_groups_weekly_friday() -> Result<(), Box<dyn Error>> {
920 let dates_list = BDatesList::new(
921 "2023-11-01".to_string(), "2023-11-15".to_string(), DateFreq::WeeklyFriday,
924 );
925
926 let groups = dates_list.groups()?;
927 assert_eq!(groups.len(), 2); assert_eq!(groups[0], vec![date(2023, 11, 3)]); assert_eq!(groups[1], vec![date(2023, 11, 10)]); Ok(())
939 }
940
941 #[test]
942 fn test_bdates_list_groups_quarterly_start_spanning_years() -> Result<(), Box<dyn Error>> {
944 let dates_list = BDatesList::new(
945 "2023-08-01".to_string(), "2024-05-01".to_string(), DateFreq::QuarterStart,
948 );
949
950 let groups = dates_list.groups()?;
951 assert_eq!(groups.len(), 3);
960
961 assert_eq!(groups[0], vec![date(2023, 10, 2)]); assert_eq!(groups[1], vec![date(2024, 1, 1)]); assert_eq!(groups[2], vec![date(2024, 4, 1)]); Ok(())
968 }
969
970 #[test]
971 fn test_bdates_list_groups_yearly_end() -> Result<(), Box<dyn Error>> {
973 let dates_list = BDatesList::new(
974 "2022-01-01".to_string(),
975 "2024-03-31".to_string(), DateFreq::YearEnd,
977 );
978
979 let groups = dates_list.groups()?;
980 assert_eq!(groups.len(), 2);
987
988 assert_eq!(groups[0], vec![date(2022, 12, 30)]); assert_eq!(groups[1], vec![date(2023, 12, 29)]); Ok(())
994 }
995
996 #[test]
997 fn test_bdates_list_groups_empty_range() -> Result<(), Box<dyn Error>> {
999 let dates_list = BDatesList::new(
1000 "2023-12-31".to_string(),
1001 "2023-01-01".to_string(), DateFreq::Daily,
1003 );
1004 let groups = dates_list.groups()?;
1005 assert!(groups.is_empty());
1006
1007 Ok(())
1008 }
1009
1010 #[test]
1013 fn test_generator_new_zero_periods() -> Result<(), Box<dyn Error>> {
1014 let start_date = date(2023, 1, 1);
1015 let freq = DateFreq::Daily;
1016 let n_periods = 0;
1017 let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1018 assert_eq!(generator.next(), None); Ok(())
1020 }
1021
1022 #[test]
1023 fn test_generator_daily() -> Result<(), Box<dyn Error>> {
1024 let start_date = date(2023, 11, 10); let freq = DateFreq::Daily;
1026 let n_periods = 4;
1027 let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1028
1029 assert_eq!(generator.next(), Some(date(2023, 11, 10))); assert_eq!(generator.next(), Some(date(2023, 11, 13))); assert_eq!(generator.next(), Some(date(2023, 11, 14))); assert_eq!(generator.next(), Some(date(2023, 11, 15))); assert_eq!(generator.next(), None); let start_date_sat = date(2023, 11, 11); let mut generator_sat = BDatesGenerator::new(start_date_sat, freq, 2)?;
1038 assert_eq!(generator_sat.next(), Some(date(2023, 11, 13))); assert_eq!(generator_sat.next(), Some(date(2023, 11, 14))); assert_eq!(generator_sat.next(), None);
1041
1042 Ok(())
1043 }
1044
1045 #[test]
1046 fn test_generator_weekly_monday() -> Result<(), Box<dyn Error>> {
1047 let start_date = date(2023, 11, 8); let freq = DateFreq::WeeklyMonday;
1049 let n_periods = 3;
1050 let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1051
1052 assert_eq!(generator.next(), Some(date(2023, 11, 13)));
1053 assert_eq!(generator.next(), Some(date(2023, 11, 20)));
1054 assert_eq!(generator.next(), Some(date(2023, 11, 27)));
1055 assert_eq!(generator.next(), None);
1056
1057 Ok(())
1058 }
1059
1060 #[test]
1061 fn test_generator_weekly_friday() -> Result<(), Box<dyn Error>> {
1062 let start_date = date(2023, 11, 11); let freq = DateFreq::WeeklyFriday;
1064 let n_periods = 3;
1065 let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1066
1067 assert_eq!(generator.next(), Some(date(2023, 11, 17)));
1068 assert_eq!(generator.next(), Some(date(2023, 11, 24)));
1069 assert_eq!(generator.next(), Some(date(2023, 12, 1)));
1070 assert_eq!(generator.next(), None);
1071
1072 Ok(())
1073 }
1074
1075 #[test]
1076 fn test_generator_month_start() -> Result<(), Box<dyn Error>> {
1077 let start_date = date(2023, 10, 15); let freq = DateFreq::MonthStart;
1079 let n_periods = 4; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1081
1082 assert_eq!(generator.next(), Some(date(2023, 11, 1)));
1083 assert_eq!(generator.next(), Some(date(2023, 12, 1)));
1084 assert_eq!(generator.next(), Some(date(2024, 1, 1)));
1085 assert_eq!(generator.next(), Some(date(2024, 2, 1)));
1086 assert_eq!(generator.next(), None);
1087
1088 Ok(())
1089 }
1090
1091 #[test]
1092 fn test_generator_month_end() -> Result<(), Box<dyn Error>> {
1093 let start_date = date(2023, 9, 30); let freq = DateFreq::MonthEnd;
1095 let n_periods = 4; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1097
1098 assert_eq!(generator.next(), Some(date(2023, 10, 31))); assert_eq!(generator.next(), Some(date(2023, 11, 30)));
1100 assert_eq!(generator.next(), Some(date(2023, 12, 29)));
1101 assert_eq!(generator.next(), Some(date(2024, 1, 31)));
1102 assert_eq!(generator.next(), None);
1103
1104 Ok(())
1105 }
1106
1107 #[test]
1108 fn test_generator_quarter_start() -> Result<(), Box<dyn Error>> {
1109 let start_date = date(2023, 8, 1); let freq = DateFreq::QuarterStart;
1111 let n_periods = 3; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1113
1114 assert_eq!(generator.next(), Some(date(2023, 10, 2))); assert_eq!(generator.next(), Some(date(2024, 1, 1)));
1116 assert_eq!(generator.next(), Some(date(2024, 4, 1)));
1117 assert_eq!(generator.next(), None);
1118
1119 Ok(())
1120 }
1121
1122 #[test]
1123 fn test_generator_quarter_end() -> Result<(), Box<dyn Error>> {
1124 let start_date = date(2023, 11, 1); let freq = DateFreq::QuarterEnd;
1126 let n_periods = 3; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1128
1129 assert_eq!(generator.next(), Some(date(2023, 12, 29))); assert_eq!(generator.next(), Some(date(2024, 3, 29))); assert_eq!(generator.next(), Some(date(2024, 6, 28))); assert_eq!(generator.next(), None);
1133
1134 Ok(())
1135 }
1136
1137 #[test]
1138 fn test_generator_year_start() -> Result<(), Box<dyn Error>> {
1139 let start_date = date(2023, 1, 1); let freq = DateFreq::YearStart;
1141 let n_periods = 3; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1143
1144 assert_eq!(generator.next(), Some(date(2023, 1, 2))); assert_eq!(generator.next(), Some(date(2024, 1, 1)));
1146 assert_eq!(generator.next(), Some(date(2025, 1, 1)));
1147 assert_eq!(generator.next(), None);
1148
1149 Ok(())
1150 }
1151
1152 #[test]
1153 fn test_generator_year_end() -> Result<(), Box<dyn Error>> {
1154 let start_date = date(2022, 12, 31); let freq = DateFreq::YearEnd;
1156 let n_periods = 3; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
1158
1159 assert_eq!(generator.next(), Some(date(2023, 12, 29))); assert_eq!(generator.next(), Some(date(2024, 12, 31)));
1161 assert_eq!(generator.next(), Some(date(2025, 12, 31)));
1162 assert_eq!(generator.next(), None);
1163
1164 Ok(())
1165 }
1166
1167 #[test]
1168 fn test_generator_collect() -> Result<(), Box<dyn Error>> {
1169 let start_date = date(2023, 11, 10); let freq = DateFreq::Daily;
1171 let n_periods = 4;
1172 let generator = BDatesGenerator::new(start_date, freq, n_periods)?; let dates: Vec<NaiveDate> = generator.collect();
1174
1175 assert_eq!(
1176 dates,
1177 vec![
1178 date(2023, 11, 10), date(2023, 11, 13), date(2023, 11, 14), date(2023, 11, 15) ]
1183 );
1184 Ok(())
1185 }
1186}