Fix all-day event parsing with VALUE=DATE format support
- Add special handling for date-only format (%Y%m%d) in parse_datetime_with_tz - Convert NaiveDate to midnight NaiveDateTime for all-day events - Reorder all-day detection to occur before datetime parsing - All-day events now properly display in calendar views 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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<Vec<String>, 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user