[gnome-calendar] event: fix timezone handling



commit 7a6a13d7e0bc4ededda326202e5493f3e403ed84
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Mon Feb 15 19:50:15 2016 -0200

    event: fix timezone handling
    
    Using libical with libecal means that the dates
    are always stored as UTF dates, and we receive the
    timezone as the ECalComponentDateTime::tzid value.
    
    This fact was net being considered when adding date
    support on GcalEvent, and was causing various issues
    when displaying dates.
    
    Fix that by properly handling timezones in GcalEvent.

 src/gcal-event.c      |  132 +++++++++++++++++++++++++++----------------------
 src/gcal-month-view.c |   10 ++--
 src/gcal-utils.c      |   35 +++++++++-----
 src/gcal-utils.h      |    5 +-
 src/gcal-year-view.c  |    5 +-
 5 files changed, 107 insertions(+), 80 deletions(-)
---
diff --git a/src/gcal-event.c b/src/gcal-event.c
index 56f50bd..937edb3 100644
--- a/src/gcal-event.c
+++ b/src/gcal-event.c
@@ -63,25 +63,69 @@ enum {
   N_PROPS
 };
 
+static GTimeZone*
+get_timezone_from_ical (ECalComponentDateTime *comp)
+{
+  GTimeZone *tz;
+
+  if (comp->value->is_date)
+    {
+      /*
+       * When loading an all day event, the timezone must not be taken into account
+       * when loading the date. Since all events in ical are UTC, we match it by
+       * setting the timezone as UTC as well.
+       */
+      tz = g_time_zone_new_utc ();
+    }
+  else if (icaltime_get_timezone (*comp->value))
+    {
+      gchar *tzid;
+      gint offset;
+
+      offset = icaltimezone_get_utc_offset ((icaltimezone*) icaltime_get_timezone (*comp->value),
+                                            comp->value, NULL);
+      tzid = format_utc_offset (offset);
+      tz = g_time_zone_new (tzid);
+
+      g_free (tzid);
+    }
+  else if (comp->tzid)
+    {
+      tz = g_time_zone_new (comp->tzid);
+    }
+  else
+    {
+      tz = g_time_zone_new_utc ();
+    }
+
+  return tz;
+}
+
 static ECalComponentDateTime*
 build_component_from_datetime (GcalEvent *self,
                                GDateTime *dt)
 {
   ECalComponentDateTime *comp_dt;
-  const gchar *tzid;
+  GDateTime *utf_dt;
 
   if (!dt)
     return NULL;
 
+  utf_dt = g_date_time_to_utc (dt);
+
   comp_dt = g_new0 (ECalComponentDateTime, 1);
   comp_dt->value = NULL;
   comp_dt->tzid = NULL;
 
-  tzid = g_date_time_get_timezone_abbreviation (dt);
-
-  comp_dt->value = datetime_to_icaltime (dt);
+  comp_dt->value = datetime_to_icaltime (utf_dt);
+  comp_dt->value->zone = NULL;
   comp_dt->value->is_date = self->all_day;
-  comp_dt->tzid = g_strdup (tzid ? tzid : "UTC");
+
+  /* All day events have no timezone */
+  if (!self->all_day)
+    comp_dt->tzid = format_utc_offset (g_date_time_get_utc_offset (dt));
+
+  g_clear_pointer (&utf_dt, g_date_time_unref);
 
   return comp_dt;
 }
@@ -138,31 +182,40 @@ gcal_event_set_component_internal (GcalEvent     *self,
       ECalComponentDateTime start;
       ECalComponentDateTime end;
       GDateTime *date_start;
+      GTimeZone *zone_start;
       GDateTime *date_end;
-      GTimeZone *tz;
+      GTimeZone *zone_end;
+      GDateTime *aux;
+      gboolean start_is_all_day, end_is_all_day;
       gchar *description;
 
       /* Setup start date */
       e_cal_component_get_dtstart (component, &start);
-      date_start = icaltime_to_datetime (start.value, &tz);
+      zone_start = get_timezone_from_ical (&start);
+      aux = icaltime_to_datetime (start.value);
+      date_start = g_date_time_to_timezone (aux, zone_start);
+      start_is_all_day = datetime_is_date (aux);
+
+      self->dt_start = g_date_time_ref (date_start);
+
+      g_clear_pointer (&aux, g_date_time_unref);
 
-      gcal_event_set_date_start (self, date_start);
+      /* The timezone of the event is the timezone of the start date */
+      self->timezone = g_time_zone_ref (zone_start);
 
       /* Setup end date */
       e_cal_component_get_dtend (component, &end);
-      date_end = icaltime_to_datetime (end.value, NULL);
+      zone_end = get_timezone_from_ical (&end);
+      aux = icaltime_to_datetime (end.value);
+      date_end = g_date_time_to_timezone (aux, zone_end);
+      end_is_all_day = datetime_is_date (aux);
 
-      gcal_event_set_date_end (self, date_end);
+      self->dt_end = g_date_time_ref (date_end);
 
-      /*
-       * Setup timezone. We use the timezone from the start
-       * date as the timezone of the event, and the end date
-       * is adjusted if necessary.
-       */
-      gcal_event_set_timezone (self, tz);
+      g_clear_pointer (&aux, g_date_time_unref);
 
       /* Setup all day */
-      gcal_event_set_all_day (self, datetime_is_date (date_start) && datetime_is_date (date_end));
+      self->all_day = start_is_all_day && end_is_all_day;
 
       /* Setup description */
       description = get_desc_from_component (component, "\n\n");
@@ -176,7 +229,9 @@ gcal_event_set_component_internal (GcalEvent     *self,
       g_object_notify (G_OBJECT (self), "summary");
 
       g_clear_pointer (&date_start, g_date_time_unref);
+      g_clear_pointer (&zone_start, g_time_zone_unref);
       g_clear_pointer (&date_end, g_date_time_unref);
+      g_clear_pointer (&zone_end, g_time_zone_unref);
       g_clear_pointer (&description, g_free);
     }
 }
@@ -583,48 +638,7 @@ gcal_event_set_all_day (GcalEvent *self,
       self->all_day = all_day;
 
       g_object_notify (G_OBJECT (self), "all-day");
-
-      /*
-       * If we just set an all day event, the hour & minute fields
-       * from the start & end date should be set to 0.
-       */
-      if (self->all_day)
-        {
-          GDateTime *dt;
-
-          /* Update start date */
-          if (self->dt_start)
-            {
-              dt = g_date_time_new (self->timezone,
-                                    g_date_time_get_year (self->dt_start),
-                                    g_date_time_get_month (self->dt_start),
-                                    g_date_time_get_day_of_month (self->dt_start),
-                                    self->all_day ? 0 : g_date_time_get_hour (self->dt_start),
-                                    self->all_day ? 0 : g_date_time_get_minute (self->dt_start),
-                                    0);
-
-              gcal_event_set_date_start (self, dt);
-
-              g_clear_pointer (&dt, g_date_time_unref);
-            }
-
-          /* Update end date */
-          if (self->dt_end)
-            {
-              dt = g_date_time_new (self->timezone,
-                                    g_date_time_get_year (self->dt_end),
-                                    g_date_time_get_month (self->dt_end),
-                                    g_date_time_get_day_of_month (self->dt_end),
-                                    self->all_day ? 0 : g_date_time_get_hour (self->dt_end),
-                                    self->all_day ? 0 : g_date_time_get_minute (self->dt_end),
-                                    0);
-
-              gcal_event_set_date_end (self, dt);
-
-              g_clear_pointer (&dt, g_date_time_unref);
-            }
-          }
-        }
+    }
 }
 
 /**
diff --git a/src/gcal-month-view.c b/src/gcal-month-view.c
index 1055ed8..a9357ff 100644
--- a/src/gcal-month-view.c
+++ b/src/gcal-month-view.c
@@ -488,6 +488,8 @@ rebuild_popover_for_day (GcalMonthView *view,
   PangoFontDescription *ofont_desc;
   gint font_height, padding_bottom;
 
+  GTimeZone *tz;
+
   priv = gcal_month_view_get_instance_private (view);
   ppriv = GCAL_SUBSCRIBER_VIEW (view)->priv;
 
@@ -499,6 +501,7 @@ rebuild_popover_for_day (GcalMonthView *view,
   gtk_container_foreach (GTK_CONTAINER (priv->events_list_box), (GtkCallback) gtk_widget_destroy, NULL);
 
   l = g_hash_table_lookup (ppriv->overflow_cells, GINT_TO_POINTER (priv->pressed_overflow_indicator));
+  tz = g_time_zone_new_local ();
 
   /* Setup the start & end dates of the events as the begin & end of day */
   if (l != NULL)
@@ -506,9 +509,7 @@ rebuild_popover_for_day (GcalMonthView *view,
       GDateTime *current_date;
       GDateTime *dt_start;
       GDateTime *dt_end;
-      GTimeZone *tz;
-
-      current_date = icaltime_to_datetime (priv->date, &tz);
+      current_date = icaltime_to_datetime (priv->date);
       dt_start = g_date_time_new (tz,
                                   g_date_time_get_year (current_date),
                                   g_date_time_get_month (current_date),
@@ -536,9 +537,10 @@ rebuild_popover_for_day (GcalMonthView *view,
       g_clear_pointer (&current_date, g_date_time_unref);
       g_clear_pointer (&dt_start, g_date_time_unref);
       g_clear_pointer (&dt_end, g_date_time_unref);
-      g_clear_pointer (&tz, g_time_zone_unref);
     }
 
+  g_clear_pointer (&tz, g_time_zone_unref);
+
   /* placement calculation */
   start_grid_y = get_start_grid_y (GTK_WIDGET (view));
   shown_rows = ceil ((priv->days_delay + icaltime_days_in_month (priv->date->month, priv->date->year)) / 
7.0);
diff --git a/src/gcal-utils.c b/src/gcal-utils.c
index 552de36..9ac6a28 100644
--- a/src/gcal-utils.c
+++ b/src/gcal-utils.c
@@ -110,8 +110,7 @@ datetime_to_icaltime (GDateTime *dt)
 }
 
 GDateTime*
-icaltime_to_datetime (const icaltimetype  *date,
-                      GTimeZone          **timezone)
+icaltime_to_datetime (const icaltimetype  *date)
 {
   GDateTime *dt;
   GTimeZone *tz;
@@ -125,10 +124,7 @@ icaltime_to_datetime (const icaltimetype  *date,
                         date->is_date ? 0 : date->minute,
                         date->is_date ? 0 : date->second);
 
-  if (timezone)
-    *timezone = tz;
-  else
-    g_clear_pointer (&tz, g_time_zone_unref);
+  g_clear_pointer (&tz, g_time_zone_unref);
 
   return dt;
 }
@@ -427,15 +423,11 @@ build_component_from_details (const gchar        *summary,
   ECalComponent *event;
   ECalComponentDateTime dt;
   ECalComponentText summ;
-  GTimeZone *tz;
-
-  tz = g_time_zone_new_local ();
 
   event = e_cal_component_new ();
   e_cal_component_set_new_vtype (event, E_CAL_COMPONENT_EVENT);
 
   dt.value = (icaltimetype*) initial_date;
-  dt.tzid = g_time_zone_get_abbreviation (tz, 0);
   e_cal_component_set_dtstart (event, &dt);
 
   if (final_date != NULL)
@@ -458,8 +450,6 @@ build_component_from_details (const gchar        *summary,
 
   e_cal_component_commit_sequence (event);
 
-  g_clear_pointer (&tz, g_time_zone_unref);
-
   return event;
 }
 
@@ -785,3 +775,24 @@ get_source_parent_name_color (GcalManager  *manager,
       *color = gdk_rgba_to_string (&c);
     }
 }
+
+gchar*
+format_utc_offset (gint offset)
+{
+  const char *sign = "+";
+  gint hours, minutes, seconds;
+
+  if (offset < 0) {
+      offset = -offset;
+      sign = "-";
+  }
+
+  hours = offset / 3600;
+  minutes = (offset % 3600) / 60;
+  seconds = offset % 60;
+
+  if (seconds > 0)
+    return g_strdup_printf ("%s%02i%02i%02i", sign, hours, minutes, seconds);
+  else
+    return g_strdup_printf ("%s%02i%02i", sign, hours, minutes);
+}
diff --git a/src/gcal-utils.h b/src/gcal-utils.h
index 303e1a0..0ac27fc 100644
--- a/src/gcal-utils.h
+++ b/src/gcal-utils.h
@@ -59,8 +59,7 @@ icaltimetype*   datetime_to_icaltime                            (GDateTime
 
 gboolean        datetime_is_date                                (GDateTime             *dt);
 
-GDateTime*      icaltime_to_datetime                            (const icaltimetype    *date,
-                                                                 GTimeZone            **timezone);
+GDateTime*      icaltime_to_datetime                            (const icaltimetype    *date);
 
 icaltimetype*   gcal_dup_icaltime                               (const icaltimetype    *date);
 
@@ -122,4 +121,6 @@ void            get_source_parent_name_color                    (GcalManager
                                                                  gchar                **name,
                                                                  gchar                **color);
 
+gchar*          format_utc_offset                               (gint                   offset);
+
 #endif // __GCAL_UTILS_H__
diff --git a/src/gcal-year-view.c b/src/gcal-year-view.c
index f202d9b..3870a59 100644
--- a/src/gcal-year-view.c
+++ b/src/gcal-year-view.c
@@ -272,7 +272,6 @@ add_event_to_day_array (GcalYearView  *year_view,
 
   GDateTime *dt_start, *dt_end;
   GDateTime *date, *second_date;
-  GTimeZone *tz;
 
   gint i;
 
@@ -284,8 +283,8 @@ add_event_to_day_array (GcalYearView  *year_view,
   dt_end = gcal_event_get_date_end (event);
 
   /* normalize date on each new event */
-  date = icaltime_to_datetime (year_view->start_selected_date, &tz);
-  second_date = g_date_time_new (tz,
+  date = icaltime_to_datetime (year_view->start_selected_date);
+  second_date = g_date_time_new (gcal_event_get_timezone (event),
                                  g_date_time_get_year (date),
                                  g_date_time_get_month (date),
                                  g_date_time_get_day_of_month (date) + 1,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]