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.
This commit is contained in:
Georges Basile Stavracas Neto
2016-02-15 20:42:47 -02:00
parent c66a1dbc6b
commit 7a6a13d7e0
5 changed files with 107 additions and 80 deletions
+73 -59
View File
@@ -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);
gcal_event_set_date_start (self, date_start);
self->dt_start = g_date_time_ref (date_start);
g_clear_pointer (&aux, g_date_time_unref);
/* 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);
}
}
}
}
}
/**
+6 -4
View File
@@ -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);
+23 -12
View File
@@ -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);
}
+3 -2
View File
@@ -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__
+2 -3
View File
@@ -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,