use chrono::{Datelike, Duration, NaiveDate, Weekday};
use std::error::Error;
use std::result::Result;
use crate::utils::dateutils::dates::{find_next_date, AggregationType, DateFreq, DatesGenerator};
use crate::utils::dateutils::dates;
pub type BDateFreq = DateFreq;
#[derive(Debug, Clone)]
pub struct BDatesList {
start_date_str: String,
end_date_str: String,
freq: DateFreq,
}
impl BDatesList {
pub fn new(start_date_str: String, end_date_str: String, freq: DateFreq) -> Self {
BDatesList {
start_date_str,
end_date_str,
freq,
}
}
pub fn from_n_periods(
start_date_str: String,
freq: DateFreq,
n_periods: usize,
) -> Result<Self, Box<dyn Error>> {
if n_periods == 0 {
return Err("n_periods must be greater than 0".into());
}
let start_date = NaiveDate::parse_from_str(&start_date_str, "%Y-%m-%d")?;
let generator = BDatesGenerator::new(start_date, freq, n_periods)?;
let dates: Vec<NaiveDate> = generator.collect();
let last_date = dates
.last()
.ok_or("Generator failed to produce dates for the specified periods")?;
let end_date_str = last_date.format("%Y-%m-%d").to_string();
Ok(BDatesList {
start_date_str,
end_date_str,
freq,
})
}
pub fn list(&self) -> Result<Vec<NaiveDate>, Box<dyn Error>> {
get_bdates_list_with_freq(&self.start_date_str, &self.end_date_str, self.freq)
}
pub fn count(&self) -> Result<usize, Box<dyn Error>> {
self.list().map(|list| list.len())
}
pub fn groups(&self) -> Result<Vec<Vec<NaiveDate>>, Box<dyn Error>> {
let dates = self.list()?;
dates::group_dates_helper(dates, self.freq)
}
pub fn start_date(&self) -> Result<NaiveDate, Box<dyn Error>> {
NaiveDate::parse_from_str(&self.start_date_str, "%Y-%m-%d").map_err(|e| e.into())
}
pub fn start_date_str(&self) -> &str {
&self.start_date_str
}
pub fn end_date(&self) -> Result<NaiveDate, Box<dyn Error>> {
NaiveDate::parse_from_str(&self.end_date_str, "%Y-%m-%d").map_err(|e| e.into())
}
pub fn end_date_str(&self) -> &str {
&self.end_date_str
}
pub fn freq(&self) -> DateFreq {
self.freq
}
pub fn freq_str(&self) -> String {
self.freq.to_string()
}
}
#[derive(Debug, Clone)]
pub struct BDatesGenerator {
dates_generator: DatesGenerator,
start_date: NaiveDate,
freq: DateFreq,
periods_remaining: usize,
}
impl BDatesGenerator {
pub fn new(
start_date: NaiveDate,
freq: DateFreq,
n_periods: usize,
) -> Result<Self, Box<dyn Error>> {
let adj_n_periods = match freq {
DateFreq::Daily => n_periods + 5,
DateFreq::WeeklyMonday
| DateFreq::WeeklyFriday
| DateFreq::MonthStart
| DateFreq::MonthEnd
| DateFreq::QuarterStart
| DateFreq::QuarterEnd
| DateFreq::YearStart
| DateFreq::YearEnd => n_periods + 2,
};
let dates_generator = DatesGenerator::new(start_date, freq, adj_n_periods)?;
Ok(BDatesGenerator {
dates_generator,
start_date,
freq,
periods_remaining: n_periods,
})
}
}
impl Iterator for BDatesGenerator {
type Item = NaiveDate;
fn next(&mut self) -> Option<Self::Item> {
if self.periods_remaining == 0 {
return None;
}
let next_date = self.dates_generator.next()?;
let next_date = match self.freq {
DateFreq::Daily => {
let mut new_candidate = next_date.clone();
while !is_business_date(new_candidate) {
new_candidate = self.dates_generator.next()?;
}
new_candidate
}
DateFreq::WeeklyMonday | DateFreq::WeeklyFriday => next_date,
DateFreq::MonthEnd | DateFreq::QuarterEnd | DateFreq::YearEnd => {
let adjusted_date = iter_reverse_till_bdate(next_date);
if self.start_date > adjusted_date {
return self.next();
}
adjusted_date
}
DateFreq::MonthStart | DateFreq::QuarterStart | DateFreq::YearStart => {
iter_till_bdate(next_date)
}
};
self.periods_remaining -= 1;
Some(next_date)
}
}
pub fn is_business_date(date: NaiveDate) -> bool {
match date.weekday() {
Weekday::Sat | Weekday::Sun => false,
_ => true,
}
}
pub fn find_next_bdate(date: NaiveDate, freq: DateFreq) -> NaiveDate {
let next_date: NaiveDate = find_next_date(date, freq).unwrap();
let next_date = iter_till_bdate(next_date);
next_date
}
pub fn find_first_bdate_on_or_after(date: NaiveDate, freq: DateFreq) -> NaiveDate {
let first_date = dates::find_first_date_on_or_after(date, freq).unwrap();
let first_date = iter_till_bdate_by_freq(first_date, freq);
first_date
}
fn iter_till_bdate_by_freq(date: NaiveDate, freq: DateFreq) -> NaiveDate {
let agg_type = freq.agg_type();
let dur = match agg_type {
AggregationType::Start => Duration::days(1),
AggregationType::End => Duration::days(-1),
};
let mut current_date = date;
while !is_business_date(current_date) {
current_date = current_date + dur;
}
current_date
}
fn iter_till_bdate(date: NaiveDate) -> NaiveDate {
let mut current_date = date;
while !is_business_date(current_date) {
current_date = current_date + Duration::days(1);
}
current_date
}
fn iter_reverse_till_bdate(date: NaiveDate) -> NaiveDate {
let mut current_date = date;
while !is_business_date(current_date) {
current_date = current_date - Duration::days(1);
}
current_date
}
pub fn get_bdates_list_with_freq(
start_date_str: &str,
end_date_str: &str,
freq: DateFreq,
) -> Result<Vec<NaiveDate>, Box<dyn Error>> {
let start_date = NaiveDate::parse_from_str(start_date_str, "%Y-%m-%d")?;
let end_date = NaiveDate::parse_from_str(end_date_str, "%Y-%m-%d")?;
let mut dates = dates::get_dates_list_with_freq_from_naive_date(start_date, end_date, freq)?;
match freq {
DateFreq::Daily => {
dates.retain(|date| is_business_date(*date));
}
DateFreq::WeeklyMonday | DateFreq::WeeklyFriday => {
}
_ => {
dates.iter_mut().for_each(|date| {
*date = iter_till_bdate_by_freq(*date, freq);
});
}
}
Ok(dates)
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::NaiveDate;
use std::str::FromStr;
fn date(year: i32, month: u32, day: u32) -> NaiveDate {
NaiveDate::from_ymd_opt(year, month, day).expect("Invalid date in test setup")
}
#[test]
fn test_date_freq_from_str() -> Result<(), Box<dyn Error>> {
assert_eq!(DateFreq::from_str("D")?, DateFreq::Daily);
assert_eq!("D".parse::<DateFreq>()?, DateFreq::Daily); assert_eq!(DateFreq::from_str("W")?, DateFreq::WeeklyMonday);
assert_eq!(DateFreq::from_str("M")?, DateFreq::MonthStart);
assert_eq!(DateFreq::from_str("Q")?, DateFreq::QuarterStart);
assert_eq!(DateFreq::from_str("Y")?, DateFreq::YearStart);
assert_eq!(DateFreq::from_str("A")?, DateFreq::YearStart);
assert_eq!(DateFreq::from_str("AS")?, DateFreq::YearStart);
assert_eq!(DateFreq::from_str("YS")?, DateFreq::YearStart);
assert_eq!("Y".parse::<DateFreq>()?, DateFreq::YearStart);
assert_eq!(DateFreq::from_str("ME")?, DateFreq::MonthEnd);
assert_eq!(DateFreq::from_str("QE")?, DateFreq::QuarterEnd);
assert_eq!(DateFreq::from_str("WF")?, DateFreq::WeeklyFriday);
assert_eq!("WF".parse::<DateFreq>()?, DateFreq::WeeklyFriday);
assert_eq!(DateFreq::from_str("YE")?, DateFreq::YearEnd);
assert_eq!(DateFreq::from_str("AE")?, DateFreq::YearEnd);
assert_eq!(DateFreq::from_str("WS")?, DateFreq::WeeklyMonday);
assert_eq!(DateFreq::from_str("MS")?, DateFreq::MonthStart);
assert_eq!(DateFreq::from_str("QS")?, DateFreq::QuarterStart);
assert!(DateFreq::from_str("INVALID").is_err());
assert!("INVALID".parse::<DateFreq>().is_err()); let err = DateFreq::from_str("INVALID").unwrap_err();
assert_eq!(err.to_string(), "Invalid frequency specified: INVALID");
Ok(())
}
#[test]
fn test_date_freq_to_string() {
assert_eq!(DateFreq::Daily.to_string(), "D");
assert_eq!(DateFreq::WeeklyMonday.to_string(), "W");
assert_eq!(DateFreq::MonthStart.to_string(), "M");
assert_eq!(DateFreq::QuarterStart.to_string(), "Q");
assert_eq!(DateFreq::YearStart.to_string(), "Y"); assert_eq!(DateFreq::MonthEnd.to_string(), "ME");
assert_eq!(DateFreq::QuarterEnd.to_string(), "QE");
assert_eq!(DateFreq::WeeklyFriday.to_string(), "WF");
assert_eq!(DateFreq::YearEnd.to_string(), "YE");
}
#[test]
fn test_date_freq_from_string() -> Result<(), Box<dyn Error>> {
assert_eq!(DateFreq::from_string("D".to_string())?, DateFreq::Daily);
assert!(DateFreq::from_string("INVALID".to_string()).is_err());
Ok(())
}
#[test]
fn test_date_freq_agg_type() {
assert_eq!(DateFreq::Daily.agg_type(), AggregationType::Start);
assert_eq!(DateFreq::WeeklyMonday.agg_type(), AggregationType::Start);
assert_eq!(DateFreq::MonthStart.agg_type(), AggregationType::Start);
assert_eq!(DateFreq::QuarterStart.agg_type(), AggregationType::Start);
assert_eq!(DateFreq::YearStart.agg_type(), AggregationType::Start);
assert_eq!(DateFreq::WeeklyFriday.agg_type(), AggregationType::End);
assert_eq!(DateFreq::MonthEnd.agg_type(), AggregationType::End);
assert_eq!(DateFreq::QuarterEnd.agg_type(), AggregationType::End);
assert_eq!(DateFreq::YearEnd.agg_type(), AggregationType::End);
}
#[test]
fn test_bdates_list_properties_new() -> Result<(), Box<dyn Error>> {
let start_str = "2023-01-01".to_string();
let end_str = "2023-12-31".to_string();
let freq = DateFreq::QuarterEnd;
let dates_list = BDatesList::new(start_str.clone(), end_str.clone(), freq);
assert_eq!(dates_list.start_date_str(), start_str);
assert_eq!(dates_list.end_date_str(), end_str);
assert_eq!(dates_list.freq(), freq);
assert_eq!(dates_list.freq_str(), "QE");
assert_eq!(dates_list.start_date()?, date(2023, 1, 1));
assert_eq!(dates_list.end_date()?, date(2023, 12, 31));
Ok(())
}
#[test]
fn test_bdates_list_properties_from_n_periods() -> Result<(), Box<dyn Error>> {
let start_str = "2023-01-01".to_string(); let freq = DateFreq::Daily;
let n_periods = 5; let dates_list = BDatesList::from_n_periods(start_str.clone(), freq, n_periods)?;
assert_eq!(dates_list.start_date_str(), start_str);
assert_eq!(dates_list.end_date_str(), "2023-01-06");
assert_eq!(dates_list.freq(), freq);
assert_eq!(dates_list.freq_str(), "D");
assert_eq!(dates_list.start_date()?, date(2023, 1, 1));
assert_eq!(dates_list.end_date()?, date(2023, 1, 6));
assert_eq!(
dates_list.list()?,
vec![
date(2023, 1, 2),
date(2023, 1, 3),
date(2023, 1, 4),
date(2023, 1, 5),
date(2023, 1, 6)
]
);
assert_eq!(dates_list.count()?, 5);
Ok(())
}
#[test]
fn test_bdates_list_from_n_periods_zero_periods() {
let start_str = "2023-01-01".to_string();
let freq = DateFreq::Daily;
let n_periods = 0;
let result = BDatesList::from_n_periods(start_str.clone(), freq, n_periods);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"n_periods must be greater than 0"
);
}
#[test]
fn test_bdates_list_from_n_periods_invalid_start_date() {
let start_str = "invalid-date".to_string();
let freq = DateFreq::Daily;
let n_periods = 5;
let result = BDatesList::from_n_periods(start_str.clone(), freq, n_periods);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("input contains invalid characters"));
}
#[test]
fn test_bdates_list_invalid_date_string_new() {
let dates_list_start_invalid = BDatesList::new(
"invalid-date".to_string(),
"2023-12-31".to_string(),
DateFreq::Daily,
);
assert!(dates_list_start_invalid.list().is_err());
assert!(dates_list_start_invalid.count().is_err());
assert!(dates_list_start_invalid.groups().is_err());
assert!(dates_list_start_invalid.start_date().is_err());
assert!(dates_list_start_invalid.end_date().is_ok());
let dates_list_end_invalid = BDatesList::new(
"2023-01-01".to_string(),
"invalid-date".to_string(),
DateFreq::Daily,
);
assert!(dates_list_end_invalid.list().is_err());
assert!(dates_list_end_invalid.count().is_err());
assert!(dates_list_end_invalid.groups().is_err());
assert!(dates_list_end_invalid.start_date().is_ok()); assert!(dates_list_end_invalid.end_date().is_err());
}
#[test]
fn test_bdates_list_quarterly_end_list() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-01-01".to_string(),
"2023-12-31".to_string(),
DateFreq::QuarterEnd,
);
let list = dates_list.list()?;
assert_eq!(list.len(), 4);
assert_eq!(
list,
vec![
date(2023, 3, 31),
date(2023, 6, 30),
date(2023, 9, 29),
date(2023, 12, 29)
]
);
Ok(())
}
#[test]
fn test_bdates_list_weekly_monday_list() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-10-30".to_string(), "2023-11-12".to_string(), DateFreq::WeeklyMonday,
);
let list = dates_list.list()?;
assert_eq!(list.len(), 2);
assert_eq!(list, vec![date(2023, 10, 30), date(2023, 11, 6)]);
Ok(())
}
#[test]
fn test_bdates_list_daily_list() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-11-01".to_string(), "2023-11-05".to_string(), DateFreq::Daily,
);
let list = dates_list.list()?;
assert_eq!(list.len(), 3);
assert_eq!(
list,
vec![date(2023, 11, 1), date(2023, 11, 2), date(2023, 11, 3)]
);
Ok(())
}
#[test]
fn test_bdates_list_empty_range_list() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-12-31".to_string(),
"2023-01-01".to_string(), DateFreq::Daily,
);
let list = dates_list.list()?;
assert!(list.is_empty());
assert_eq!(dates_list.count()?, 0);
Ok(())
}
#[test]
fn test_bdates_list_count() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-01-01".to_string(),
"2023-12-31".to_string(),
DateFreq::MonthEnd,
);
assert_eq!(dates_list.count()?, 12, "{:?}", dates_list.list());
let dates_list_weekly = BDatesList::new(
"2023-11-01".to_string(), "2023-11-30".to_string(), DateFreq::WeeklyFriday,
);
assert_eq!(dates_list_weekly.count()?, 4);
Ok(())
}
#[test]
fn test_bdates_list_yearly_start() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-06-01".to_string(),
"2025-06-01".to_string(),
DateFreq::YearStart,
);
assert_eq!(dates_list.list()?, vec![date(2024, 1, 1), date(2025, 1, 1)]);
assert_eq!(dates_list.count()?, 2);
Ok(())
}
#[test]
fn test_bdates_list_monthly_start() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-11-15".to_string(), "2024-02-15".to_string(), DateFreq::MonthStart,
);
assert_eq!(
dates_list.list()?,
vec![date(2023, 12, 1), date(2024, 1, 1), date(2024, 2, 1)]
);
assert_eq!(dates_list.count()?, 3);
Ok(())
}
#[test]
fn test_bdates_list_weekly_friday_midweek_end() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-11-01".to_string(), "2023-11-14".to_string(), DateFreq::WeeklyFriday,
);
assert_eq!(
dates_list.list()?,
vec![date(2023, 11, 3), date(2023, 11, 10)]
);
assert_eq!(dates_list.count()?, 2);
Ok(())
}
#[test]
fn test_bdates_list_groups_monthly_end() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-10-15".to_string(), "2024-01-15".to_string(), DateFreq::MonthEnd,
);
let groups = dates_list.groups()?;
assert_eq!(groups.len(), 3);
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(())
}
#[test]
fn test_bdates_list_groups_daily() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-11-01".to_string(), "2023-11-05".to_string(), DateFreq::Daily,
);
let groups = dates_list.groups()?;
assert_eq!(groups.len(), 3);
assert_eq!(groups[0], vec![date(2023, 11, 1)]);
assert_eq!(groups[1], vec![date(2023, 11, 2)]);
assert_eq!(groups[2], vec![date(2023, 11, 3)]);
Ok(())
}
#[test]
fn test_bdates_list_groups_weekly_friday() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-11-01".to_string(), "2023-11-15".to_string(), DateFreq::WeeklyFriday,
);
let groups = dates_list.groups()?;
assert_eq!(groups.len(), 2);
assert_eq!(groups[0], vec![date(2023, 11, 3)]); assert_eq!(groups[1], vec![date(2023, 11, 10)]);
Ok(())
}
#[test]
fn test_bdates_list_groups_quarterly_start_spanning_years() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-08-01".to_string(), "2024-05-01".to_string(), DateFreq::QuarterStart,
);
let groups = dates_list.groups()?;
assert_eq!(groups.len(), 3);
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(())
}
#[test]
fn test_bdates_list_groups_yearly_end() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2022-01-01".to_string(),
"2024-03-31".to_string(), DateFreq::YearEnd,
);
let groups = dates_list.groups()?;
assert_eq!(groups.len(), 2);
assert_eq!(groups[0], vec![date(2022, 12, 30)]); assert_eq!(groups[1], vec![date(2023, 12, 29)]);
Ok(())
}
#[test]
fn test_bdates_list_groups_empty_range() -> Result<(), Box<dyn Error>> {
let dates_list = BDatesList::new(
"2023-12-31".to_string(),
"2023-01-01".to_string(), DateFreq::Daily,
);
let groups = dates_list.groups()?;
assert!(groups.is_empty());
Ok(())
}
#[test]
fn test_generator_new_zero_periods() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 1, 1);
let freq = DateFreq::Daily;
let n_periods = 0;
let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
assert_eq!(generator.next(), None); Ok(())
}
#[test]
fn test_generator_daily() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 11, 10); let freq = DateFreq::Daily;
let n_periods = 4;
let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
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)?;
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);
Ok(())
}
#[test]
fn test_generator_weekly_monday() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 11, 8); let freq = DateFreq::WeeklyMonday;
let n_periods = 3;
let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
assert_eq!(generator.next(), Some(date(2023, 11, 13)));
assert_eq!(generator.next(), Some(date(2023, 11, 20)));
assert_eq!(generator.next(), Some(date(2023, 11, 27)));
assert_eq!(generator.next(), None);
Ok(())
}
#[test]
fn test_generator_weekly_friday() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 11, 11); let freq = DateFreq::WeeklyFriday;
let n_periods = 3;
let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
assert_eq!(generator.next(), Some(date(2023, 11, 17)));
assert_eq!(generator.next(), Some(date(2023, 11, 24)));
assert_eq!(generator.next(), Some(date(2023, 12, 1)));
assert_eq!(generator.next(), None);
Ok(())
}
#[test]
fn test_generator_month_start() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 10, 15); let freq = DateFreq::MonthStart;
let n_periods = 4; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
assert_eq!(generator.next(), Some(date(2023, 11, 1)));
assert_eq!(generator.next(), Some(date(2023, 12, 1)));
assert_eq!(generator.next(), Some(date(2024, 1, 1)));
assert_eq!(generator.next(), Some(date(2024, 2, 1)));
assert_eq!(generator.next(), None);
Ok(())
}
#[test]
fn test_generator_month_end() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 9, 30); let freq = DateFreq::MonthEnd;
let n_periods = 4; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
assert_eq!(generator.next(), Some(date(2023, 10, 31))); assert_eq!(generator.next(), Some(date(2023, 11, 30)));
assert_eq!(generator.next(), Some(date(2023, 12, 29)));
assert_eq!(generator.next(), Some(date(2024, 1, 31)));
assert_eq!(generator.next(), None);
Ok(())
}
#[test]
fn test_generator_quarter_start() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 8, 1); let freq = DateFreq::QuarterStart;
let n_periods = 3; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
assert_eq!(generator.next(), Some(date(2023, 10, 2))); assert_eq!(generator.next(), Some(date(2024, 1, 1)));
assert_eq!(generator.next(), Some(date(2024, 4, 1)));
assert_eq!(generator.next(), None);
Ok(())
}
#[test]
fn test_generator_quarter_end() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 11, 1); let freq = DateFreq::QuarterEnd;
let n_periods = 3; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
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);
Ok(())
}
#[test]
fn test_generator_year_start() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 1, 1); let freq = DateFreq::YearStart;
let n_periods = 3; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
assert_eq!(generator.next(), Some(date(2023, 1, 2))); assert_eq!(generator.next(), Some(date(2024, 1, 1)));
assert_eq!(generator.next(), Some(date(2025, 1, 1)));
assert_eq!(generator.next(), None);
Ok(())
}
#[test]
fn test_generator_year_end() -> Result<(), Box<dyn Error>> {
let start_date = date(2022, 12, 31); let freq = DateFreq::YearEnd;
let n_periods = 3; let mut generator = BDatesGenerator::new(start_date, freq, n_periods)?;
assert_eq!(generator.next(), Some(date(2023, 12, 29))); assert_eq!(generator.next(), Some(date(2024, 12, 31)));
assert_eq!(generator.next(), Some(date(2025, 12, 31)));
assert_eq!(generator.next(), None);
Ok(())
}
#[test]
fn test_generator_collect() -> Result<(), Box<dyn Error>> {
let start_date = date(2023, 11, 10); let freq = DateFreq::Daily;
let n_periods = 4;
let generator = BDatesGenerator::new(start_date, freq, n_periods)?; let dates: Vec<NaiveDate> = generator.collect();
assert_eq!(
dates,
vec![
date(2023, 11, 10), date(2023, 11, 13), date(2023, 11, 14), date(2023, 11, 15) ]
);
Ok(())
}
}