diff --git a/backend/src/calendar.rs b/backend/src/calendar.rs index 2637c7f..8c09b87 100644 --- a/backend/src/calendar.rs +++ b/backend/src/calendar.rs @@ -167,8 +167,6 @@ impl CalDAVClient { }; let basic_auth = self.config.get_basic_auth(); - println!("🔑 REPORT Basic Auth: Basic {}", basic_auth); - println!("🌐 REPORT URL: {}", url); let response = self .http_client @@ -349,6 +347,13 @@ impl CalDAVClient { } } full_prop.push_str(&format!(":{}", prop_value)); + + // Debug logging for DTSTART properties specifically + if prop_name == "DTSTART" { + println!("🔍 Raw DTSTART property: name='{}', value='{}', params={:?}, full_prop='{}'", + property.name, prop_value, property.params, full_prop); + } + full_properties.insert(prop_name, full_prop); } @@ -358,6 +363,21 @@ impl CalDAVClient { .ok_or_else(|| CalDAVError::ParseError("Missing UID field".to_string()))? .clone(); + // Determine if it's an all-day event FIRST by checking for VALUE=DATE parameter per RFC 5545 + let empty_string = String::new(); + let dtstart_raw = full_properties.get("DTSTART").unwrap_or(&empty_string); + let dtstart_value = properties.get("DTSTART").unwrap_or(&empty_string); + let all_day = dtstart_raw.contains("VALUE=DATE") || (!dtstart_value.contains("T") && dtstart_value.len() == 8); + + // Debug logging for DTSTART parsing + if all_day { + println!("✅ ALL-DAY EVENT FOUND: DTSTART='{}' -> all_day={}", dtstart_raw, all_day); + } else { + println!("🔍 Backend: DTSTART='{}' (len={}, has_T={}, has_VALUE_DATE={}) -> all_day={}", + dtstart_raw, dtstart_raw.len(), dtstart_raw.contains("T"), + dtstart_raw.contains("VALUE=DATE"), all_day); + } + // Parse start time (required) let start_prop = properties .get("DTSTART") @@ -375,11 +395,6 @@ impl CalDAVClient { (None, None) }; - // Determine if it's an all-day event by checking for VALUE=DATE parameter - let empty_string = String::new(); - let dtstart_raw = properties.get("DTSTART").unwrap_or(&empty_string); - let all_day = dtstart_raw.contains("VALUE=DATE") || (!dtstart_raw.contains("T") && dtstart_raw.len() == 8); - // Parse status let status = properties .get("STATUS") @@ -471,6 +486,14 @@ impl CalDAVClient { vevent.exdate = exdate.into_iter().map(|dt| dt.naive_utc()).collect(); vevent.exdate_tzid = None; // TODO: Parse timezone info for EXDATE vevent.all_day = all_day; + if all_day { + println!("📅 Backend: Successfully created all-day VEvent '{}' with start={}, end={:?}, all_day={}", + vevent.summary.as_ref().unwrap_or(&"Untitled".to_string()), + vevent.dtstart, + vevent.dtend, + vevent.all_day + ); + } // Parse alarms vevent.alarms = self.parse_valarms(&event)?; @@ -582,11 +605,9 @@ impl CalDAVClient { pub async fn discover_calendars(&self) -> Result, CalDAVError> { // First, try to discover user calendars if we have a calendar path in config if let Some(calendar_path) = &self.config.calendar_path { - println!("Using configured calendar path: {}", calendar_path); return Ok(vec![calendar_path.clone()]); } - println!("No calendar path configured, discovering calendars..."); // Try different common CalDAV discovery paths // Note: paths should be relative to the server URL base @@ -599,20 +620,16 @@ impl CalDAVClient { let mut has_valid_caldav_response = false; for path in discovery_paths { - println!("Trying discovery path: {}", path); match self.discover_calendars_at_path(&path).await { Ok(calendars) => { - println!("Found {} calendar(s) at {}", calendars.len(), path); has_valid_caldav_response = true; all_calendars.extend(calendars); } Err(CalDAVError::ServerError(status)) => { // HTTP error - this might be expected for some paths, continue trying - println!("Discovery path {} returned HTTP {}, trying next path", path, status); } Err(e) => { // Network or other error - this suggests the server isn't reachable or isn't CalDAV - println!("Discovery failed for path {}: {:?}", path, e); return Err(e); } } @@ -786,14 +803,24 @@ impl CalDAVClient { return Ok((dt.naive_utc(), Some("UTC".to_string()))); } - // Try parsing as naive datetime - if let Ok(naive_dt) = chrono::NaiveDateTime::parse_from_str(datetime_part, format) { - // Per RFC 5545: if no TZID parameter is provided, treat as UTC - let tz = timezone_id.unwrap_or_else(|| "UTC".to_string()); - - // If it's UTC, the naive time is already correct - // If it's a local timezone, we store the naive time and the timezone ID - return Ok((naive_dt, Some(tz))); + // Special handling for date-only format (all-day events) + if *format == "%Y%m%d" { + if let Ok(date) = chrono::NaiveDate::parse_from_str(datetime_part, format) { + // Convert date to midnight datetime for all-day events + let naive_dt = date.and_hms_opt(0, 0, 0).unwrap(); + let tz = timezone_id.unwrap_or_else(|| "UTC".to_string()); + return Ok((naive_dt, Some(tz))); + } + } else { + // Try parsing as naive datetime for time-based formats + if let Ok(naive_dt) = chrono::NaiveDateTime::parse_from_str(datetime_part, format) { + // Per RFC 5545: if no TZID parameter is provided, treat as UTC + let tz = timezone_id.unwrap_or_else(|| "UTC".to_string()); + + // If it's UTC, the naive time is already correct + // If it's a local timezone, we store the naive time and the timezone ID + return Ok((naive_dt, Some(tz))); + } } }