From a773159016cb1d003de8d0ad6b183d593c0b2024 Mon Sep 17 00:00:00 2001 From: Connor Johnstone Date: Wed, 3 Sep 2025 11:36:28 -0400 Subject: [PATCH] Fix recurring event filtering for month view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously filtered events by start date only, which excluded recurring events that started in previous months/years but have instances in the current month. New logic: - Non-recurring events: filter by exact month match (unchanged) - Recurring events: include if they could have instances in requested month - Check event start date is before/during month - Parse RRULE UNTIL date to exclude expired recurring events - Let frontend handle proper RRULE expansion 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- backend/src/handlers/events.rs | 50 ++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/backend/src/handlers/events.rs b/backend/src/handlers/events.rs index ba9ca2d..0010720 100644 --- a/backend/src/handlers/events.rs +++ b/backend/src/handlers/events.rs @@ -76,10 +76,54 @@ pub async fn get_calendar_events( // If year and month are specified, filter events if let (Some(year), Some(month)) = (params.year, params.month) { + let target_date = chrono::NaiveDate::from_ymd_opt(year, month, 1).unwrap(); + let month_start = target_date; + let month_end = if month == 12 { + chrono::NaiveDate::from_ymd_opt(year + 1, 1, 1).unwrap() + } else { + chrono::NaiveDate::from_ymd_opt(year, month + 1, 1).unwrap() + } - chrono::Duration::days(1); + all_events.retain(|event| { - let event_year = event.dtstart.year(); - let event_month = event.dtstart.month(); - event_year == year && event_month == month + let event_date = event.dtstart.date_naive(); + + // For non-recurring events, check if the event date is within the month + if event.rrule.is_none() || event.rrule.as_ref().unwrap().is_empty() { + let event_year = event.dtstart.year(); + let event_month = event.dtstart.month(); + return event_year == year && event_month == month; + } + + // For recurring events, check if they could have instances in this month + // Include if: + // 1. The event starts before or during the requested month + // 2. The event doesn't have an UNTIL date, OR the UNTIL date is after the month start + if event_date > month_end { + // Event starts after the requested month + return false; + } + + // Check UNTIL date in RRULE if present + if let Some(ref rrule) = event.rrule { + if let Some(until_pos) = rrule.find("UNTIL=") { + let until_part = &rrule[until_pos + 6..]; + let until_end = until_part.find(';').unwrap_or(until_part.len()); + let until_str = &until_part[..until_end]; + + // Try to parse UNTIL date (format: YYYYMMDDTHHMMSSZ or YYYYMMDD) + if until_str.len() >= 8 { + if let Ok(until_date) = chrono::NaiveDate::parse_from_str(&until_str[..8], "%Y%m%d") { + if until_date < month_start { + // Recurring event ended before the requested month + return false; + } + } + } + } + } + + // Include the recurring event - the frontend will do proper expansion + true }); }