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:
@@ -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());
|
||||
|
||||
@@ -151,54 +151,41 @@ impl EventCreationData {
|
||||
Option<String>, // calendar_path
|
||||
Option<u32>, // recurrence_count
|
||||
Option<String>, // recurrence_until
|
||||
String, // timezone
|
||||
) {
|
||||
use chrono::{Local, TimeZone};
|
||||
|
||||
// Convert local date/time to UTC for backend
|
||||
let (utc_start_date, utc_start_time, utc_end_date, utc_end_time) = if self.all_day {
|
||||
// For all-day events, just use the dates as-is (no time conversion needed)
|
||||
(
|
||||
self.start_date.format("%Y-%m-%d").to_string(),
|
||||
self.start_time.format("%H:%M").to_string(),
|
||||
self.end_date.format("%Y-%m-%d").to_string(),
|
||||
self.end_time.format("%H:%M").to_string(),
|
||||
)
|
||||
// Use local date/times and timezone - no UTC conversion
|
||||
let effective_end_date = if self.end_time == NaiveTime::from_hms_opt(0, 0, 0).unwrap() {
|
||||
// If end time is midnight (00:00), treat it as beginning of next day
|
||||
self.end_date + chrono::Duration::days(1)
|
||||
} else {
|
||||
// Convert local date/time to UTC, but preserve original local dates
|
||||
let start_local = Local.from_local_datetime(&self.start_date.and_time(self.start_time)).single();
|
||||
let end_local = Local.from_local_datetime(&self.end_date.and_time(self.end_time)).single();
|
||||
|
||||
if let (Some(start_dt), Some(end_dt)) = (start_local, end_local) {
|
||||
let start_utc = start_dt.with_timezone(&chrono::Utc);
|
||||
let end_utc = end_dt.with_timezone(&chrono::Utc);
|
||||
|
||||
// IMPORTANT: Use original local dates, not UTC dates!
|
||||
// This ensures events display on the correct day regardless of timezone conversion
|
||||
(
|
||||
self.start_date.format("%Y-%m-%d").to_string(),
|
||||
start_utc.format("%H:%M").to_string(),
|
||||
self.end_date.format("%Y-%m-%d").to_string(),
|
||||
end_utc.format("%H:%M").to_string(),
|
||||
)
|
||||
} else {
|
||||
// Fallback if timezone conversion fails - use local time as-is
|
||||
web_sys::console::warn_1(&"⚠️ Failed to convert local time to UTC, using local time".into());
|
||||
(
|
||||
self.start_date.format("%Y-%m-%d").to_string(),
|
||||
self.start_time.format("%H:%M").to_string(),
|
||||
self.end_date.format("%Y-%m-%d").to_string(),
|
||||
self.end_time.format("%H:%M").to_string(),
|
||||
)
|
||||
}
|
||||
self.end_date
|
||||
};
|
||||
|
||||
// Get the local timezone
|
||||
let timezone = {
|
||||
use js_sys::Date;
|
||||
let date = 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
|
||||
};
|
||||
|
||||
let (start_date, start_time, end_date, end_time) = (
|
||||
self.start_date.format("%Y-%m-%d").to_string(),
|
||||
self.start_time.format("%H:%M").to_string(),
|
||||
effective_end_date.format("%Y-%m-%d").to_string(),
|
||||
self.end_time.format("%H:%M").to_string(),
|
||||
);
|
||||
|
||||
(
|
||||
self.title.clone(),
|
||||
self.description.clone(),
|
||||
utc_start_date,
|
||||
utc_start_time,
|
||||
utc_end_date,
|
||||
utc_end_time,
|
||||
start_date,
|
||||
start_time,
|
||||
end_date,
|
||||
end_time,
|
||||
self.location.clone(),
|
||||
self.all_day,
|
||||
format!("{:?}", self.status).to_uppercase(),
|
||||
@@ -213,6 +200,7 @@ impl EventCreationData {
|
||||
self.selected_calendar.clone(),
|
||||
self.recurrence_count,
|
||||
self.recurrence_until.map(|d| d.format("%Y-%m-%d").to_string()),
|
||||
timezone,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use chrono::{DateTime, Datelike, Duration, NaiveDate, TimeZone, Utc, Weekday};
|
||||
use chrono::{DateTime, Datelike, Duration, NaiveDate, Utc, Weekday};
|
||||
use gloo_storage::{LocalStorage, Storage};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@@ -275,14 +275,60 @@ impl CalendarService {
|
||||
grouped
|
||||
}
|
||||
|
||||
/// Convert UTC events to local timezone for display
|
||||
fn convert_utc_to_local(mut event: VEvent) -> VEvent {
|
||||
// Check if event times are in UTC (legacy events from before timezone migration)
|
||||
let is_utc_event = event.dtstart_tzid.as_ref().map_or(true, |tz| tz == "UTC");
|
||||
|
||||
if is_utc_event {
|
||||
web_sys::console::log_1(&format!(
|
||||
"🕐 Converting UTC event '{}' to local time",
|
||||
event.summary.as_deref().unwrap_or("Untitled")
|
||||
).into());
|
||||
|
||||
// Get current timezone offset (convert from UTC to local)
|
||||
let date = js_sys::Date::new_0();
|
||||
let timezone_offset_minutes = date.get_timezone_offset() as i32;
|
||||
|
||||
// Convert start time from UTC to local
|
||||
// getTimezoneOffset() returns minutes UTC is ahead of local time
|
||||
// To convert UTC to local, we subtract the offset (add negative offset)
|
||||
let local_start = event.dtstart + chrono::Duration::minutes(-timezone_offset_minutes as i64);
|
||||
event.dtstart = local_start;
|
||||
event.dtstart_tzid = None; // Clear UTC timezone indicator
|
||||
|
||||
// Convert end time if present
|
||||
if let Some(end_utc) = event.dtend {
|
||||
let local_end = end_utc + chrono::Duration::minutes(-timezone_offset_minutes as i64);
|
||||
event.dtend = Some(local_end);
|
||||
event.dtend_tzid = None; // Clear UTC timezone indicator
|
||||
}
|
||||
|
||||
// Convert created/modified times if present
|
||||
if let Some(created_utc) = event.created {
|
||||
event.created = Some(created_utc + chrono::Duration::minutes(-timezone_offset_minutes as i64));
|
||||
event.created_tzid = None;
|
||||
}
|
||||
|
||||
if let Some(modified_utc) = event.last_modified {
|
||||
event.last_modified = Some(modified_utc + chrono::Duration::minutes(-timezone_offset_minutes as i64));
|
||||
event.last_modified_tzid = None;
|
||||
}
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
|
||||
/// Expand recurring events using VEvent (RFC 5545 compliant)
|
||||
pub fn expand_recurring_events(events: Vec<VEvent>) -> Vec<VEvent> {
|
||||
let mut expanded_events = Vec::new();
|
||||
let today = chrono::Utc::now().date_naive();
|
||||
let today = chrono::Local::now().date_naive();
|
||||
let start_range = today - Duration::days(36500); // Show past 100 years (to catch any historical yearly events)
|
||||
let end_range = today + Duration::days(36500); // Show next 100 years
|
||||
|
||||
for event in events {
|
||||
// Convert UTC events to local time for proper display
|
||||
let event = Self::convert_utc_to_local(event);
|
||||
if let Some(ref rrule) = event.rrule {
|
||||
web_sys::console::log_1(
|
||||
&format!(
|
||||
@@ -372,17 +418,18 @@ impl CalendarService {
|
||||
|
||||
// Get UNTIL date if specified
|
||||
let until_date = components.get("UNTIL").and_then(|until_str| {
|
||||
// Parse UNTIL date in YYYYMMDDTHHMMSSZ format
|
||||
// Parse UNTIL date in YYYYMMDDTHHMMSSZ format (treat as local time)
|
||||
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(
|
||||
until_str.trim_end_matches('Z'),
|
||||
"%Y%m%dT%H%M%S",
|
||||
) {
|
||||
Some(chrono::Utc.from_utc_datetime(&dt))
|
||||
Some(dt)
|
||||
} else if let Ok(dt) = chrono::DateTime::parse_from_str(until_str, "%Y%m%dT%H%M%SZ") {
|
||||
Some(dt.with_timezone(&chrono::Utc))
|
||||
// Convert UTC to local (naive) time for consistency
|
||||
Some(dt.naive_utc())
|
||||
} else if let Ok(date) = chrono::NaiveDate::parse_from_str(until_str, "%Y%m%d") {
|
||||
// Handle date-only UNTIL
|
||||
Some(chrono::Utc.from_utc_datetime(&date.and_hms_opt(23, 59, 59).unwrap()))
|
||||
Some(date.and_hms_opt(23, 59, 59).unwrap())
|
||||
} else {
|
||||
web_sys::console::log_1(
|
||||
&format!("⚠️ Failed to parse UNTIL date: {}", until_str).into(),
|
||||
@@ -395,7 +442,7 @@ impl CalendarService {
|
||||
web_sys::console::log_1(&format!("📅 RRULE has UNTIL: {}", until).into());
|
||||
}
|
||||
|
||||
let start_date = base_event.dtstart.date_naive();
|
||||
let start_date = base_event.dtstart.date();
|
||||
let mut current_date = start_date;
|
||||
let mut occurrence_count = 0;
|
||||
|
||||
@@ -422,8 +469,8 @@ impl CalendarService {
|
||||
// Check if this occurrence is in the exception dates (EXDATE)
|
||||
let is_exception = base_event.exdate.iter().any(|exception_date| {
|
||||
// Compare dates ignoring sub-second precision
|
||||
let exception_naive = exception_date.naive_utc();
|
||||
let occurrence_naive = occurrence_datetime.naive_utc();
|
||||
let exception_naive = exception_date.and_utc();
|
||||
let occurrence_naive = occurrence_datetime.and_utc();
|
||||
|
||||
// Check if dates match (within a minute to handle minor time differences)
|
||||
let diff = occurrence_naive - exception_naive;
|
||||
@@ -555,7 +602,7 @@ impl CalendarService {
|
||||
interval: i32,
|
||||
start_range: NaiveDate,
|
||||
end_range: NaiveDate,
|
||||
until_date: Option<chrono::DateTime<chrono::Utc>>,
|
||||
until_date: Option<chrono::NaiveDateTime>,
|
||||
count: usize,
|
||||
) -> Vec<VEvent> {
|
||||
let mut occurrences = Vec::new();
|
||||
@@ -565,7 +612,7 @@ impl CalendarService {
|
||||
return occurrences;
|
||||
}
|
||||
|
||||
let start_date = base_event.dtstart.date_naive();
|
||||
let start_date = base_event.dtstart.date();
|
||||
|
||||
// Find the Monday of the week containing the start_date (reference week)
|
||||
let reference_week_start =
|
||||
@@ -623,8 +670,8 @@ impl CalendarService {
|
||||
|
||||
// Check if this occurrence is in the exception dates (EXDATE)
|
||||
let is_exception = base_event.exdate.iter().any(|exception_date| {
|
||||
let exception_naive = exception_date.naive_utc();
|
||||
let occurrence_naive = occurrence_datetime.naive_utc();
|
||||
let exception_naive = exception_date.and_utc();
|
||||
let occurrence_naive = occurrence_datetime.and_utc();
|
||||
let diff = occurrence_naive - exception_naive;
|
||||
let matches = diff.num_seconds().abs() < 60;
|
||||
|
||||
@@ -675,7 +722,7 @@ impl CalendarService {
|
||||
interval: i32,
|
||||
start_range: NaiveDate,
|
||||
end_range: NaiveDate,
|
||||
until_date: Option<chrono::DateTime<chrono::Utc>>,
|
||||
until_date: Option<chrono::NaiveDateTime>,
|
||||
count: usize,
|
||||
) -> Vec<VEvent> {
|
||||
let mut occurrences = Vec::new();
|
||||
@@ -691,7 +738,7 @@ impl CalendarService {
|
||||
return occurrences;
|
||||
}
|
||||
|
||||
let start_date = base_event.dtstart.date_naive();
|
||||
let start_date = base_event.dtstart.date();
|
||||
let mut current_month_start =
|
||||
NaiveDate::from_ymd_opt(start_date.year(), start_date.month(), 1).unwrap();
|
||||
let mut total_occurrences = 0;
|
||||
@@ -749,9 +796,7 @@ impl CalendarService {
|
||||
|
||||
// Check if this occurrence is in the exception dates (EXDATE)
|
||||
let is_exception = base_event.exdate.iter().any(|exception_date| {
|
||||
let exception_naive = exception_date.naive_utc();
|
||||
let occurrence_naive = occurrence_datetime.naive_utc();
|
||||
let diff = occurrence_naive - exception_naive;
|
||||
let diff = occurrence_datetime - *exception_date;
|
||||
diff.num_seconds().abs() < 60
|
||||
});
|
||||
|
||||
@@ -792,14 +837,14 @@ impl CalendarService {
|
||||
interval: i32,
|
||||
start_range: NaiveDate,
|
||||
end_range: NaiveDate,
|
||||
until_date: Option<chrono::DateTime<chrono::Utc>>,
|
||||
until_date: Option<chrono::NaiveDateTime>,
|
||||
count: usize,
|
||||
) -> Vec<VEvent> {
|
||||
let mut occurrences = Vec::new();
|
||||
|
||||
// Parse BYDAY for monthly (e.g., "1MO" = first Monday, "-1FR" = last Friday)
|
||||
if let Some((position, weekday)) = Self::parse_monthly_byday(byday) {
|
||||
let start_date = base_event.dtstart.date_naive();
|
||||
let start_date = base_event.dtstart.date();
|
||||
let mut current_month_start =
|
||||
NaiveDate::from_ymd_opt(start_date.year(), start_date.month(), 1).unwrap();
|
||||
let mut total_occurrences = 0;
|
||||
@@ -830,9 +875,7 @@ impl CalendarService {
|
||||
|
||||
// Check EXDATE
|
||||
let is_exception = base_event.exdate.iter().any(|exception_date| {
|
||||
let exception_naive = exception_date.naive_utc();
|
||||
let occurrence_naive = occurrence_datetime.naive_utc();
|
||||
let diff = occurrence_naive - exception_naive;
|
||||
let diff = occurrence_datetime - *exception_date;
|
||||
diff.num_seconds().abs() < 60
|
||||
});
|
||||
|
||||
@@ -871,7 +914,7 @@ impl CalendarService {
|
||||
interval: i32,
|
||||
start_range: NaiveDate,
|
||||
end_range: NaiveDate,
|
||||
until_date: Option<chrono::DateTime<chrono::Utc>>,
|
||||
until_date: Option<chrono::NaiveDateTime>,
|
||||
count: usize,
|
||||
) -> Vec<VEvent> {
|
||||
let mut occurrences = Vec::new();
|
||||
@@ -887,7 +930,7 @@ impl CalendarService {
|
||||
return occurrences;
|
||||
}
|
||||
|
||||
let start_date = base_event.dtstart.date_naive();
|
||||
let start_date = base_event.dtstart.date();
|
||||
let mut current_year = start_date.year();
|
||||
let mut total_occurrences = 0;
|
||||
|
||||
@@ -930,9 +973,7 @@ impl CalendarService {
|
||||
|
||||
// Check EXDATE
|
||||
let is_exception = base_event.exdate.iter().any(|exception_date| {
|
||||
let exception_naive = exception_date.naive_utc();
|
||||
let occurrence_naive = occurrence_datetime.naive_utc();
|
||||
let diff = occurrence_naive - exception_naive;
|
||||
let diff = occurrence_datetime - *exception_date;
|
||||
diff.num_seconds().abs() < 60
|
||||
});
|
||||
|
||||
@@ -1260,6 +1301,7 @@ impl CalendarService {
|
||||
recurrence_count: Option<u32>,
|
||||
recurrence_until: Option<String>,
|
||||
calendar_path: Option<String>,
|
||||
timezone: String,
|
||||
) -> Result<(), String> {
|
||||
let window = web_sys::window().ok_or("No global window exists")?;
|
||||
|
||||
@@ -1293,7 +1335,8 @@ impl CalendarService {
|
||||
"recurrence_interval": 1_u32, // Default interval
|
||||
"recurrence_end_date": recurrence_until,
|
||||
"recurrence_count": recurrence_count,
|
||||
"calendar_path": calendar_path
|
||||
"calendar_path": calendar_path,
|
||||
"timezone": timezone
|
||||
});
|
||||
let url = format!("{}/calendar/events/series/create", self.base_url);
|
||||
(body, url)
|
||||
@@ -1317,7 +1360,8 @@ impl CalendarService {
|
||||
"reminder": reminder,
|
||||
"recurrence": recurrence,
|
||||
"recurrence_days": recurrence_days,
|
||||
"calendar_path": calendar_path
|
||||
"calendar_path": calendar_path,
|
||||
"timezone": timezone
|
||||
});
|
||||
let url = format!("{}/calendar/events/create", self.base_url);
|
||||
(body, url)
|
||||
@@ -1395,9 +1439,10 @@ impl CalendarService {
|
||||
recurrence: String,
|
||||
recurrence_days: Vec<bool>,
|
||||
calendar_path: Option<String>,
|
||||
exception_dates: Vec<DateTime<Utc>>,
|
||||
exception_dates: Vec<chrono::NaiveDateTime>,
|
||||
update_action: Option<String>,
|
||||
until_date: Option<DateTime<Utc>>,
|
||||
until_date: Option<chrono::NaiveDateTime>,
|
||||
timezone: String,
|
||||
) -> Result<(), String> {
|
||||
// Forward to update_event_with_scope with default scope
|
||||
self.update_event_with_scope(
|
||||
@@ -1425,6 +1470,7 @@ impl CalendarService {
|
||||
exception_dates,
|
||||
update_action,
|
||||
until_date,
|
||||
timezone,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1452,9 +1498,10 @@ impl CalendarService {
|
||||
recurrence: String,
|
||||
recurrence_days: Vec<bool>,
|
||||
calendar_path: Option<String>,
|
||||
exception_dates: Vec<DateTime<Utc>>,
|
||||
exception_dates: Vec<chrono::NaiveDateTime>,
|
||||
update_action: Option<String>,
|
||||
until_date: Option<DateTime<Utc>>,
|
||||
until_date: Option<chrono::NaiveDateTime>,
|
||||
timezone: String,
|
||||
) -> Result<(), String> {
|
||||
let window = web_sys::window().ok_or("No global window exists")?;
|
||||
|
||||
@@ -1485,8 +1532,9 @@ impl CalendarService {
|
||||
"calendar_path": calendar_path,
|
||||
"update_action": update_action,
|
||||
"occurrence_date": null,
|
||||
"exception_dates": exception_dates.iter().map(|dt| dt.to_rfc3339()).collect::<Vec<String>>(),
|
||||
"until_date": until_date.as_ref().map(|dt| dt.to_rfc3339())
|
||||
"exception_dates": exception_dates.iter().map(|dt| dt.format("%Y-%m-%dT%H:%M:%S").to_string()).collect::<Vec<String>>(),
|
||||
"until_date": until_date.as_ref().map(|dt| dt.format("%Y-%m-%dT%H:%M:%S").to_string()),
|
||||
"timezone": timezone
|
||||
});
|
||||
let url = format!("{}/calendar/events/update", self.base_url);
|
||||
|
||||
@@ -1692,6 +1740,7 @@ impl CalendarService {
|
||||
calendar_path: Option<String>,
|
||||
update_scope: String,
|
||||
occurrence_date: Option<String>,
|
||||
timezone: String,
|
||||
) -> Result<(), String> {
|
||||
let window = web_sys::window().ok_or("No global window exists")?;
|
||||
|
||||
@@ -1723,7 +1772,8 @@ impl CalendarService {
|
||||
"recurrence_count": recurrence_count,
|
||||
"calendar_path": calendar_path,
|
||||
"update_scope": update_scope,
|
||||
"occurrence_date": occurrence_date
|
||||
"occurrence_date": occurrence_date,
|
||||
"timezone": timezone
|
||||
});
|
||||
|
||||
let url = format!("{}/calendar/events/series/update", self.base_url);
|
||||
|
||||
Reference in New Issue
Block a user