use chrono::{Datelike, NaiveDate, Days, Weekday};
pub fn get_n_first_days_of_week(start_from: NaiveDate, target_day: Weekday, count: usize) -> Vec<NaiveDate> {
let mut result = Vec::with_capacity(count);
let first_of_current_month = NaiveDate::from_ymd_opt(start_from.year(), start_from.month(), 1).unwrap();
let first_weekday = first_of_current_month.weekday().num_days_from_monday();
let target_weekday = target_day.num_days_from_monday();
let days_to_add = (target_weekday + 7 - first_weekday) % 7;
let mut current_date = first_of_current_month + Days::new(days_to_add as u64);
if current_date <= start_from {
current_date = jump_to_next_occurrence(current_date);
}
for _ in 0..count {
result.push(current_date);
current_date = jump_to_next_occurrence(current_date);
}
result
}
fn jump_to_next_occurrence(current_date: NaiveDate) -> NaiveDate {
let next_jump = current_date + Days::new(28);
if next_jump.month() == current_date.month() {
next_jump + Days::new(7)
} else {
next_jump
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_forward_looking_logic() {
let start_date = NaiveDate::from_ymd_opt(2026, 5, 12).unwrap();
let target = Weekday::Fri;
let fridays = get_n_first_days_of_week(start_date, target, 3);
assert_eq!(fridays[0], NaiveDate::from_ymd_opt(2026, 6, 5).unwrap());
assert_eq!(fridays[1], NaiveDate::from_ymd_opt(2026, 7, 3).unwrap());
}
#[test]
fn test_exact_day_start() {
let start_date = NaiveDate::from_ymd_opt(2026, 5, 1).unwrap();
let target = Weekday::Fri;
let fridays = get_n_first_days_of_week(start_date, target, 1);
assert_eq!(fridays[0], NaiveDate::from_ymd_opt(2026, 6, 5).unwrap());
}
}