Fix timezone handling for drag-and-drop and recurring event updates

- Fix double timezone conversion in drag-and-drop that caused 4-hour time shifts
- Frontend now sends local times instead of UTC to backend for proper conversion
- Add missing timezone parameter to update_series method to fix recurring event updates
- Update both event modal and drag-and-drop paths to include timezone information
- Maintain RFC 5545 compliance with proper timezone conversion in backend

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-09-13 20:56:18 -04:00
parent 890940fe31
commit acc5ced551
6 changed files with 387 additions and 158 deletions

View File

@@ -759,6 +759,7 @@ pub fn App() -> Html {
params.17, // calendar_path
scope,
event_data_for_update.occurrence_date.map(|d| d.format("%Y-%m-%d").to_string()), // occurrence_date
params.20, // timezone
)
.await
} else {
@@ -789,6 +790,7 @@ pub fn App() -> Html {
vec![], // exception_dates - empty for simple updates
None, // update_action - None for regular updates
None, // until_date - None for regular updates
params.20, // timezone
)
.await
};
@@ -875,6 +877,7 @@ pub fn App() -> Html {
params.18, // recurrence_count
params.19, // recurrence_until
params.17, // calendar_path
params.20, // timezone
)
.await;
match create_result {
@@ -915,7 +918,7 @@ pub fn App() -> Html {
chrono::NaiveDateTime,
chrono::NaiveDateTime,
bool,
Option<chrono::DateTime<chrono::Utc>>,
Option<chrono::NaiveDateTime>,
Option<String>,
Option<String>,
)| {
@@ -954,30 +957,13 @@ pub fn App() -> Html {
String::new()
};
// Convert local naive datetime to UTC before sending to backend
use chrono::TimeZone;
let local_tz = chrono::Local;
let start_utc = local_tz.from_local_datetime(&new_start)
.single()
.unwrap_or_else(|| {
// Fallback for ambiguous times (DST transitions)
local_tz.from_local_datetime(&new_start).earliest().unwrap()
})
.with_timezone(&chrono::Utc);
let end_utc = local_tz.from_local_datetime(&new_end)
.single()
.unwrap_or_else(|| {
// Fallback for ambiguous times (DST transitions)
local_tz.from_local_datetime(&new_end).earliest().unwrap()
})
.with_timezone(&chrono::Utc);
let start_date = start_utc.format("%Y-%m-%d").to_string();
let start_time = start_utc.format("%H:%M").to_string();
let end_date = end_utc.format("%Y-%m-%d").to_string();
let end_time = end_utc.format("%H:%M").to_string();
// Send local times to backend, which will handle timezone conversion
let start_date = new_start.format("%Y-%m-%d").to_string();
let start_time = new_start.format("%H:%M").to_string();
let end_date = new_end.format("%Y-%m-%d").to_string();
let end_time = new_end.format("%H:%M").to_string();
// Convert existing event data to string formats for the API
let status_str = match original_event.status {
@@ -1062,6 +1048,14 @@ pub fn App() -> Html {
original_event.calendar_path.clone(),
scope.clone(),
occurrence_date,
{
// Get timezone offset
let date = js_sys::Date::new_0();
let timezone_offset = date.get_timezone_offset(); // Minutes from UTC
let hours = -(timezone_offset as i32) / 60; // Convert to hours, negate for proper sign
let minutes = (timezone_offset as i32).abs() % 60;
format!("{:+03}:{:02}", hours, minutes) // Format as +05:00 or -04:00
},
)
.await,
)
@@ -1113,6 +1107,14 @@ pub fn App() -> Html {
Some("this_and_future".to_string())
},
until_date,
{
// Get timezone offset
let date = js_sys::Date::new_0();
let timezone_offset = date.get_timezone_offset(); // Minutes from UTC
let hours = -(timezone_offset as i32) / 60; // Convert to hours, negate for proper sign
let minutes = (timezone_offset as i32).abs() % 60;
format!("{:+03}:{:02}", hours, minutes) // Format as +05:00 or -04:00
},
)
.await
};
@@ -1597,7 +1599,7 @@ pub fn App() -> Html {
};
// Get the occurrence date from the clicked event
let occurrence_date = Some(event.dtstart.date_naive().format("%Y-%m-%d").to_string());
let occurrence_date = Some(event.dtstart.date().format("%Y-%m-%d").to_string());
web_sys::console::log_1(&format!("🔄 Delete action: {}", action_str).into());
web_sys::console::log_1(&format!("🔄 Event UID: {}", event.uid).into());