From ce8e8c217eaa299c13365728a25fe5466969a871 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Tue, 5 Dec 2017 16:28:18 -0600 Subject: [PATCH 1/5] edit-dialog: Fix timezone for events created in month view In month view, when clicking on a day of the month, every event is initially an All Day event until the user opens the Edit Details dialog and unchecks the checkbox. This means that the event is originally created in UTC time, and needs to be explicitly switched to local time. --- src/gcal-edit-dialog.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/gcal-edit-dialog.c b/src/gcal-edit-dialog.c index 31cd0a00..01a34f05 100644 --- a/src/gcal-edit-dialog.c +++ b/src/gcal-edit-dialog.c @@ -452,6 +452,7 @@ action_button_clicked (GtkWidget *widget, GcalRecurrenceFrequency freq; GcalRecurrence *old_recur; GDateTime *start_date, *end_date; + gboolean was_all_day; gboolean all_day; gchar *note_text; @@ -468,6 +469,7 @@ action_button_clicked (GtkWidget *widget, /* Update all day */ all_day = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->all_day_check)); + was_all_day = gcal_event_get_all_day (dialog->event); gcal_event_set_all_day (dialog->event, all_day); @@ -483,7 +485,7 @@ action_button_clicked (GtkWidget *widget, /* * Update start & end dates. The dates are already translated to the current - * timezone. + * timezone (unless the event used to be all day, but no longer is). */ start_date = gcal_edit_dialog_get_date_start (dialog); end_date = gcal_edit_dialog_get_date_end (dialog); @@ -500,6 +502,23 @@ action_button_clicked (GtkWidget *widget, g_clear_pointer (&end_date, g_date_time_unref); end_date = fake_end_date; } + else if (!all_day && was_all_day) + { + /* When an all day event is changed to be not an all day event, we + * need to correct for the fact that the event's timezone was until + * now set to UTC. That means we need to change the timezone to + * localtime now, or else it will be saved incorrectly. + */ + GDateTime *localtime_date; + + localtime_date = g_date_time_to_local (start_date); + g_clear_pointer (&start_date, g_date_time_unref); + start_date = localtime_date; + + localtime_date = g_date_time_to_local (end_date); + g_clear_pointer (&end_date, g_date_time_unref); + end_date = localtime_date; + } gcal_event_set_date_start (dialog->event, start_date); gcal_event_set_date_end (dialog->event, end_date); From 1d8a1646340a044f165b1b541adffc3505391ebb Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Tue, 5 Dec 2017 16:34:14 -0600 Subject: [PATCH 2/5] event: Fix changing the timezone g_date_time_equal() corrects for timezone offsets to ensure that, e.g., 6 AM CST is treated as equal to 12 PM UTC. This means attempts to change the start and end times of events to the same time in another timezone are currently failing. We need to remove this attempt at optimization because it prevents gcal_event_set_timezone() from ever doing anything. --- src/gcal-event.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/gcal-event.c b/src/gcal-event.c index 789f67c4..dc619245 100644 --- a/src/gcal-event.c +++ b/src/gcal-event.c @@ -985,9 +985,7 @@ gcal_event_set_date_end (GcalEvent *self, { g_return_if_fail (GCAL_IS_EVENT (self)); - if (self->dt_end != dt && - (!self->dt_end || !dt || - (self->dt_end && dt && !g_date_time_equal (self->dt_end, dt)))) + if (self->dt_end != dt) { ECalComponentDateTime *component_dt, current; @@ -1042,9 +1040,7 @@ gcal_event_set_date_start (GcalEvent *self, { g_return_if_fail (GCAL_IS_EVENT (self)); - if (self->dt_start != dt && - (!self->dt_start || !dt || - (self->dt_start && dt && !g_date_time_equal (self->dt_start, dt)))) + if (self->dt_start != dt) { ECalComponentDateTime *component_dt, current; From 281ed44f1c93543d305f7d4c08c61a0066703467 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Tue, 5 Dec 2017 17:26:39 -0600 Subject: [PATCH 3/5] Revert "event: correctly store timezones" This reverts commit ce2c9552693bf549a232a9681692b5fbd2cd3560. This commit is not right. Lots of code in Calendar relies on being able to set the timezone of a GcalEvent, and this results in us always ignoring the timezone set by Calendar in preference to whatever happened to be in the ECalComponentDateTime previously. That might not be so bad, but additionally, the time is not adjusted accordingly, so this actually results in the time of events changing unexpectedly from what Calendar is trying to set. The code to explicitly store UTC times for all day events is OK (I guess) and will be restored in a subsequent commit. --- src/gcal-event.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/gcal-event.c b/src/gcal-event.c index dc619245..d9c7fe7d 100644 --- a/src/gcal-event.c +++ b/src/gcal-event.c @@ -221,11 +221,11 @@ get_timezone_from_ical (ECalComponentDateTime *comp) } static ECalComponentDateTime* -build_component_from_datetime (GcalEvent *self, - icaltimezone *current_tz, - GDateTime *dt) +build_component_from_datetime (GcalEvent *self, + GDateTime *dt) { ECalComponentDateTime *comp_dt; + gchar *tzid; if (!dt) return NULL; @@ -234,12 +234,15 @@ build_component_from_datetime (GcalEvent *self, comp_dt->value = NULL; comp_dt->tzid = NULL; + tzid = g_date_time_format (dt, "%Z"); + comp_dt->value = datetime_to_icaltime (dt); - comp_dt->value->zone = self->all_day ? icaltimezone_get_utc_timezone () : current_tz; + comp_dt->value->zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); comp_dt->value->is_date = self->all_day; - /* All day events have UTC timezone */ - comp_dt->tzid = g_strdup (self->all_day ? "UTC" : icaltimezone_get_tzid (current_tz)); + /* All day events have no timezone */ + if (!self->all_day) + comp_dt->tzid = tzid; return comp_dt; } @@ -987,25 +990,19 @@ gcal_event_set_date_end (GcalEvent *self, if (self->dt_end != dt) { - ECalComponentDateTime *component_dt, current; + ECalComponentDateTime *component_dt; g_clear_pointer (&self->dt_end, g_date_time_unref); self->dt_end = g_date_time_ref (dt); - /* Retrieve the current timezone */ - e_cal_component_get_dtstart (self->component, ¤t); - /* Setup the ECalComponent's datetime value */ - component_dt = build_component_from_datetime (self, - icaltimezone_get_builtin_timezone_from_tzid (current.tzid), - dt); + component_dt = build_component_from_datetime (self, dt); e_cal_component_set_dtend (self->component, component_dt); e_cal_component_commit_sequence (self->component); g_object_notify (G_OBJECT (self), "date-end"); - e_cal_component_free_datetime (¤t); e_cal_component_free_datetime (component_dt); g_free (component_dt); } @@ -1042,25 +1039,19 @@ gcal_event_set_date_start (GcalEvent *self, if (self->dt_start != dt) { - ECalComponentDateTime *component_dt, current; + ECalComponentDateTime *component_dt; g_clear_pointer (&self->dt_start, g_date_time_unref); self->dt_start = g_date_time_ref (dt); - /* Retrieve the current timezone */ - e_cal_component_get_dtstart (self->component, ¤t); - /* Setup the ECalComponent's datetime value */ - component_dt = build_component_from_datetime (self, - icaltimezone_get_builtin_timezone_from_tzid (current.tzid), - dt); + component_dt = build_component_from_datetime (self, dt); e_cal_component_set_dtstart (self->component, component_dt); e_cal_component_commit_sequence (self->component); g_object_notify (G_OBJECT (self), "date-start"); - e_cal_component_free_datetime (¤t); e_cal_component_free_datetime (component_dt); g_free (component_dt); } From a7304ed9b0bb3e69fc06162ffefb79d6c6ac76d3 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Tue, 5 Dec 2017 17:36:14 -0600 Subject: [PATCH 4/5] event: Restore handling of all day events This brings back the original handling of all day events that we had before I reverted the previous commit. --- src/gcal-event.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/gcal-event.c b/src/gcal-event.c index d9c7fe7d..dc606635 100644 --- a/src/gcal-event.c +++ b/src/gcal-event.c @@ -225,24 +225,28 @@ build_component_from_datetime (GcalEvent *self, GDateTime *dt) { ECalComponentDateTime *comp_dt; - gchar *tzid; if (!dt) return NULL; comp_dt = g_new0 (ECalComponentDateTime, 1); - comp_dt->value = NULL; - comp_dt->tzid = NULL; - - tzid = g_date_time_format (dt, "%Z"); - comp_dt->value = datetime_to_icaltime (dt); - comp_dt->value->zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); comp_dt->value->is_date = self->all_day; - /* All day events have no timezone */ - if (!self->all_day) - comp_dt->tzid = tzid; + if (self->all_day) + { + comp_dt->value->zone = icaltimezone_get_utc_timezone (); + comp_dt->tzid = g_strdup ("UTC"); + } + else + { + gchar *tzid; + + tzid = g_date_time_format (dt, "%Z"); + comp_dt->value->zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); + /* This transfers ownership of tzid. */ + comp_dt->tzid = tzid; + } return comp_dt; } From 0724d4092ecf047e5f3c0136c10c42e7b4b0cd29 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Tue, 5 Dec 2017 21:15:34 -0600 Subject: [PATCH 5/5] event: Save local time correctly The old code here was not setting tzid correctly. It me took hours to figure out how to do this, but it turned out to be simple once I found the right place, buried deep in the e-d-s source code. Now, ideally we would not change the timezone of the event every time we edit it. But some big changes in how GNOME Calendar tracks timezone are going to be required if we do that. In particular, I believe we would have to stop saving all day events in UTC, and give up on using GDateTime as we do now, and track timezone completely separately everywhere using icaltimezone. This is what e-d-s does, and it's probably what we should be doing too. But that would be a big change. This commit solves the basic problem, which is to ensure the event is scheduled for the right time, regardless of timezone. This finally completely fixes #168. --- src/gcal-event.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/gcal-event.c b/src/gcal-event.c index dc606635..a7aaa00a 100644 --- a/src/gcal-event.c +++ b/src/gcal-event.c @@ -240,12 +240,8 @@ build_component_from_datetime (GcalEvent *self, } else { - gchar *tzid; - - tzid = g_date_time_format (dt, "%Z"); - comp_dt->value->zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); - /* This transfers ownership of tzid. */ - comp_dt->tzid = tzid; + comp_dt->value->zone = e_cal_util_get_system_timezone (); + comp_dt->tzid = g_strdup (icaltimezone_get_tzid ((icaltimezone *) comp_dt->value->zone)); } return comp_dt;