[evolution-data-server] Calendar: Add functions to clamp a VTIMEZONE component
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Calendar: Add functions to clamp a VTIMEZONE component
- Date: Thu, 18 Feb 2021 12:41:43 +0000 (UTC)
commit ad3e1ce81defa2c6147991e8bcc8727e37391a91
Author: Milan Crha <mcrha redhat com>
Date: Thu Feb 18 13:39:58 2021 +0100
Calendar: Add functions to clamp a VTIMEZONE component
Can be used to limit the data transfer for simple events where
the whole history of the timezone shifts doesn't matter.
src/calendar/libecal/e-cal-util.c | 137 +++++++++++++++++++
src/calendar/libecal/e-cal-util.h | 6 +
src/calendar/libedata-cal/e-cal-meta-backend.c | 81 ++++++++++-
tests/libecal/CMakeLists.txt | 1 +
tests/libecal/test-cal-utils.c | 182 +++++++++++++++++++++++++
5 files changed, 406 insertions(+), 1 deletion(-)
---
diff --git a/src/calendar/libecal/e-cal-util.c b/src/calendar/libecal/e-cal-util.c
index 09121eeba..d18fbbabf 100644
--- a/src/calendar/libecal/e-cal-util.c
+++ b/src/calendar/libecal/e-cal-util.c
@@ -2996,3 +2996,140 @@ e_cal_util_set_alarm_acknowledged (ECalComponent *component,
return TRUE;
}
+
+static void
+e_cal_util_clamp_vtimezone_subcomps (ICalComponent *vtimezone,
+ ICalComponentKind kind,
+ const ICalTime *from,
+ const ICalTime *to)
+{
+ ICalComponent *subcomp;
+ ICalComponent *nearest_from_comp = NULL, *nearest_to_comp = NULL;
+ ICalTime *nearest_from_time = NULL, *nearest_to_time = NULL;
+ GSList *remove = NULL, *link;
+
+ for (subcomp = i_cal_component_get_first_component (vtimezone, kind);
+ subcomp;
+ g_object_unref (subcomp), subcomp = i_cal_component_get_next_component (vtimezone, kind)) {
+ ICalTime *dtstart;
+
+ dtstart = i_cal_component_get_dtstart (subcomp);
+ if (dtstart && !i_cal_time_is_null_time (dtstart) && i_cal_time_is_valid_time (dtstart)) {
+ gint cmp;
+
+ cmp = i_cal_time_compare (dtstart, from);
+ if (cmp < 0) {
+ if (nearest_from_time) {
+ if (i_cal_time_compare (dtstart, nearest_from_time) > 0) {
+ g_clear_object (&nearest_from_time);
+ nearest_from_time = g_object_ref (dtstart);
+ remove = g_slist_prepend (remove, nearest_from_comp);
+ nearest_from_comp = g_object_ref (subcomp);
+ } else {
+ remove = g_slist_prepend (remove, g_object_ref (subcomp));
+ }
+ } else {
+ nearest_from_time = g_object_ref (dtstart);
+ nearest_from_comp = g_object_ref (subcomp);
+ }
+ } else if (cmp > 0 && to) {
+ cmp = i_cal_time_compare (to, dtstart);
+ if (cmp < 0)
+ remove = g_slist_prepend (remove, g_object_ref (subcomp));
+ }
+ }
+
+ g_clear_object (&dtstart);
+ }
+
+ g_clear_object (&nearest_from_comp);
+ g_clear_object (&nearest_from_time);
+ g_clear_object (&nearest_to_comp);
+ g_clear_object (&nearest_to_time);
+
+ for (link = remove; link; link = g_slist_next (link)) {
+ subcomp = link->data;
+
+ i_cal_component_remove_component (vtimezone, subcomp);
+ }
+
+ g_slist_free_full (remove, g_object_unref);
+}
+
+/**
+ * e_cal_util_clamp_vtimezone:
+ * @vtimezone: (inout): a VTIMEZONE component to modify
+ * @from: an #ICalTime for the minimum time
+ * @to: (nullable): until which time to clamp, or %NULL for infinity
+ *
+ * Modifies the @vtimezone to include only subcomponents influencing
+ * the passed-in time interval between @from and @to.
+ *
+ * Since: 3.40
+ **/
+void
+e_cal_util_clamp_vtimezone (ICalComponent *vtimezone,
+ const ICalTime *from,
+ const ICalTime *to)
+{
+ g_return_if_fail (I_CAL_IS_COMPONENT (vtimezone));
+ g_return_if_fail (i_cal_component_isa (vtimezone) == I_CAL_VTIMEZONE_COMPONENT);
+ g_return_if_fail (I_CAL_IS_TIME (from));
+ if (to)
+ g_return_if_fail (I_CAL_IS_TIME (to));
+
+ e_cal_util_clamp_vtimezone_subcomps (vtimezone, I_CAL_XSTANDARD_COMPONENT, from, to);
+ e_cal_util_clamp_vtimezone_subcomps (vtimezone, I_CAL_XDAYLIGHT_COMPONENT, from, to);
+}
+
+/**
+ * e_cal_util_clamp_vtimezone_by_component:
+ * @vtimezone: (inout): a VTIMEZONE component to modify
+ * @component: an #ICalComponent to read the times from
+ *
+ * Similar to e_cal_util_clamp_vtimezone(), only reads the clamp
+ * times from the @component.
+ *
+ * Since: 3.40
+ **/
+void
+e_cal_util_clamp_vtimezone_by_component (ICalComponent *vtimezone,
+ ICalComponent *component)
+{
+ ICalProperty *prop;
+ ICalTime *dtstart, *dtend = NULL;
+
+ g_return_if_fail (I_CAL_IS_COMPONENT (vtimezone));
+ g_return_if_fail (i_cal_component_isa (vtimezone) == I_CAL_VTIMEZONE_COMPONENT);
+ g_return_if_fail (I_CAL_IS_COMPONENT (component));
+
+ dtstart = i_cal_component_get_dtstart (component);
+ if (!dtstart)
+ return;
+
+ prop = i_cal_component_get_first_property (component, I_CAL_RECURRENCEID_PROPERTY);
+ if (prop) {
+ ICalTime *recurid;
+
+ recurid = i_cal_property_get_recurrenceid (prop);
+
+ dtend = i_cal_component_get_dtend (component);
+ if (dtend && i_cal_time_compare (recurid, dtend) >= 0) {
+ g_clear_object (&dtend);
+ dtend = recurid;
+ recurid = NULL;
+ }
+
+ g_clear_object (&recurid);
+ g_object_unref (prop);
+ } else if (!e_cal_util_component_has_rrules (component)) {
+ dtend = i_cal_component_get_dtend (component);
+ if (!dtend)
+ dtend = g_object_ref (dtstart);
+ }
+
+ e_cal_util_clamp_vtimezone (vtimezone, dtstart, dtend);
+
+ g_clear_object (&dtstart);
+ g_clear_object (&dtend);
+}
diff --git a/src/calendar/libecal/e-cal-util.h b/src/calendar/libecal/e-cal-util.h
index e9bb1a65a..425eef9e8 100644
--- a/src/calendar/libecal/e-cal-util.h
+++ b/src/calendar/libecal/e-cal-util.h
@@ -351,6 +351,12 @@ gboolean e_cal_util_set_alarm_acknowledged
(ECalComponent *component,
const gchar *auid,
gint64 when); /* as time_t in UTC */
+void e_cal_util_clamp_vtimezone (ICalComponent *vtimezone,
+ const ICalTime *from,
+ const ICalTime *to);
+void e_cal_util_clamp_vtimezone_by_component
+ (ICalComponent *vtimezone,
+ ICalComponent *component);
G_END_DECLS
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.c b/src/calendar/libedata-cal/e-cal-meta-backend.c
index 023a2fbe2..159e35558 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.c
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.c
@@ -3982,7 +3982,9 @@ e_cal_meta_backend_merge_instances (ECalMetaBackend *meta_backend,
{
ForeachTzidData f_data;
ICalComponent *vcalendar;
+ ICalTime *min_start = NULL, *max_end = NULL;
GSList *link, *sorted;
+ gboolean has_rrules = FALSE;
g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), NULL);
g_return_val_if_fail (instances != NULL, NULL);
@@ -3997,6 +3999,8 @@ e_cal_meta_backend_merge_instances (ECalMetaBackend *meta_backend,
for (link = sorted; link; link = g_slist_next (link)) {
ECalComponent *comp = link->data;
+ ICalProperty *prop;
+ ICalTime *tt;
if (!E_IS_CAL_COMPONENT (comp)) {
g_warn_if_reached ();
@@ -4006,11 +4010,86 @@ e_cal_meta_backend_merge_instances (ECalMetaBackend *meta_backend,
f_data.icomp = i_cal_component_clone (e_cal_component_get_icalcomponent (comp));
i_cal_component_foreach_tzid (f_data.icomp, add_timezone_cb, &f_data);
- i_cal_component_take_component (vcalendar, f_data.icomp);
+ i_cal_component_add_component (vcalendar, f_data.icomp);
+
+ has_rrules = has_rrules || e_cal_util_component_has_rrules (f_data.icomp);
+ tt = i_cal_component_get_dtstart (f_data.icomp);
+
+ if (!min_start && tt) {
+ min_start = tt;
+ tt = NULL;
+ } else if (tt && i_cal_time_compare (tt, min_start) < 0) {
+ g_clear_object (&min_start);
+ min_start = tt;
+ tt = NULL;
+ }
+
+ prop = has_rrules ? NULL : i_cal_component_get_first_property (f_data.icomp,
I_CAL_RECURRENCEID_PROPERTY);
+ if (prop) {
+ ICalTime *recurid, *dtend;
+
+ recurid = i_cal_property_get_recurrenceid (prop);
+ g_object_unref (prop);
+
+ dtend = i_cal_component_get_dtend (f_data.icomp);
+ if (dtend && i_cal_time_compare (recurid, dtend) >= 0) {
+ g_clear_object (&dtend);
+ dtend = recurid;
+ recurid = NULL;
+ }
+
+ if (max_end && dtend && i_cal_time_compare (max_end, dtend) < 0) {
+ g_clear_object (&max_end);
+ max_end = dtend;
+ dtend = NULL;
+ } else if (!max_end) {
+ max_end = dtend;
+ dtend = NULL;
+ }
+
+ g_clear_object (&recurid);
+ g_clear_object (&dtend);
+ } else if (has_rrules) {
+ g_clear_object (&max_end);
+ } else {
+ ICalTime *dtend;
+
+ dtend = i_cal_component_get_dtend (f_data.icomp);
+
+ if (!dtend)
+ dtend = tt ? g_object_ref (tt) : (min_start ? g_object_ref (min_start) :
NULL);
+
+ if (max_end && dtend && i_cal_time_compare (max_end, dtend) < 0) {
+ g_clear_object (&max_end);
+ max_end = dtend;
+ dtend = NULL;
+ } else if (!max_end) {
+ max_end = dtend;
+ dtend = NULL;
+ }
+
+ g_clear_object (&dtend);
+ }
+
+ g_clear_object (&f_data.icomp);
+ g_clear_object (&tt);
}
g_slist_free (sorted);
+ if (min_start) {
+ ICalComponent *subcomp;
+
+ for (subcomp = i_cal_component_get_first_component (vcalendar, I_CAL_VTIMEZONE_COMPONENT);
+ subcomp;
+ g_object_unref (subcomp), subcomp = i_cal_component_get_next_component (vcalendar,
I_CAL_VTIMEZONE_COMPONENT)) {
+ e_cal_util_clamp_vtimezone (subcomp, min_start, max_end);
+ }
+ }
+
+ g_clear_object (&min_start);
+ g_clear_object (&max_end);
+
return vcalendar;
}
diff --git a/tests/libecal/CMakeLists.txt b/tests/libecal/CMakeLists.txt
index 6d740442c..fc9aa5412 100644
--- a/tests/libecal/CMakeLists.txt
+++ b/tests/libecal/CMakeLists.txt
@@ -35,6 +35,7 @@ set(TESTS
test-cal-component
test-cal-recur
test-cal-reminders
+ test-cal-utils
)
foreach(_test ${TESTS})
diff --git a/tests/libecal/test-cal-utils.c b/tests/libecal/test-cal-utils.c
new file mode 100644
index 000000000..0d076a1ea
--- /dev/null
+++ b/tests/libecal/test-cal-utils.c
@@ -0,0 +1,182 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <libecal/libecal.h>
+
+#include "e-test-server-utils.h"
+
+static ETestServerClosure test_closure = { E_TEST_SERVER_CALENDAR, NULL, E_CAL_CLIENT_SOURCE_TYPE_EVENTS,
FALSE, NULL, FALSE };
+
+#define DEF_SUBCOMP(x, dt) \
+ "BEGIN:" x "\r\n" \
+ "TZNAME:NM" x "\r\n" \
+ "DTSTART:" dt "T230000\r\n" \
+ "TZOFFSETFROM:+0100\r\n" \
+ "TZOFFSETTO:+0200\r\n" \
+ "RRULE:FREQ=YEARLY;UNTIL=" dt "T220000Z;BYDAY=-1SU;BYMONTH=4\r\n" \
+ "END:" x "\r\n"
+
+#define DEF_VTIMEZONE(location, content) \
+ "BEGIN:VTIMEZONE\r\n" \
+ "TZID:/id.no.where/" location "\r\n" \
+ "X-LIC-LOCATION:" location "\r\n" \
+ content \
+ "END:VTIMEZONE\r\n"
+
+static void
+test_clamp_vtimezone (ETestServerFixture *fixture,
+ gconstpointer user_data)
+{
+ const gchar *vtimezone_str =
+ DEF_VTIMEZONE ("Some/Location",
+ DEF_SUBCOMP ("DAYLIGHT", "19810301")
+ DEF_SUBCOMP ("STANDARD", "19811001")
+ DEF_SUBCOMP ("DAYLIGHT", "19820301")
+ DEF_SUBCOMP ("STANDARD", "19821001")
+ DEF_SUBCOMP ("DAYLIGHT", "19830301")
+ DEF_SUBCOMP ("STANDARD", "19831001")
+ DEF_SUBCOMP ("DAYLIGHT", "19840301")
+ DEF_SUBCOMP ("STANDARD", "19841001")
+ DEF_SUBCOMP ("DAYLIGHT", "19850301")
+ DEF_SUBCOMP ("STANDARD", "19851001")
+ );
+ ICalComponent *comp, *vtimezone;
+ ICalTime *from, *to;
+
+ vtimezone = i_cal_component_new_from_string (vtimezone_str);
+
+ g_assert_nonnull (vtimezone);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XDAYLIGHT_COMPONENT), ==, 5);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XSTANDARD_COMPONENT), ==, 5);
+
+ from = i_cal_time_new_from_string ("19830101T000000Z");
+ to = i_cal_time_new_from_string ("19830815T000000Z");
+
+ g_assert_nonnull (from);
+ g_assert_nonnull (to);
+
+ e_cal_util_clamp_vtimezone (vtimezone, from, NULL);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XDAYLIGHT_COMPONENT), ==, 4);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XSTANDARD_COMPONENT), ==, 4);
+
+ g_object_unref (vtimezone);
+ vtimezone = i_cal_component_new_from_string (vtimezone_str);
+
+ e_cal_util_clamp_vtimezone (vtimezone, from, to);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XDAYLIGHT_COMPONENT), ==, 2);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XSTANDARD_COMPONENT), ==, 1);
+
+ g_object_unref (vtimezone);
+ vtimezone = i_cal_component_new_from_string (vtimezone_str);
+
+ comp = i_cal_component_new_from_string (
+ "BEGIN:VEVENT\r\n"
+ "UID:1\r\n"
+ "DTSTART;VALUE=DATE:19821003\r\n"
+ "END:VEVENT\r\n");
+ g_assert_nonnull (comp);
+
+ e_cal_util_clamp_vtimezone_by_component (vtimezone, comp);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XDAYLIGHT_COMPONENT), ==, 1);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XSTANDARD_COMPONENT), ==, 1);
+
+ g_object_unref (comp);
+ g_object_unref (vtimezone);
+ vtimezone = i_cal_component_new_from_string (vtimezone_str);
+
+ comp = i_cal_component_new_from_string (
+ "BEGIN:VEVENT\r\n"
+ "UID:1\r\n"
+ "DTSTART;VALUE=DATE:19820903\r\n"
+ "DTEND;VALUE=DATE:19831103\r\n"
+ "END:VEVENT\r\n");
+ g_assert_nonnull (comp);
+
+ e_cal_util_clamp_vtimezone_by_component (vtimezone, comp);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XDAYLIGHT_COMPONENT), ==, 2);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XSTANDARD_COMPONENT), ==, 3);
+
+ g_object_unref (comp);
+ g_object_unref (vtimezone);
+ vtimezone = i_cal_component_new_from_string (vtimezone_str);
+
+ comp = i_cal_component_new_from_string (
+ "BEGIN:VEVENT\r\n"
+ "UID:1\r\n"
+ "DTSTART:19820903T080000Z\r\n"
+ "DTEND:19820903T090000Z\r\n"
+ "END:VEVENT\r\n");
+ g_assert_nonnull (comp);
+
+ e_cal_util_clamp_vtimezone_by_component (vtimezone, comp);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XDAYLIGHT_COMPONENT), ==, 1);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XSTANDARD_COMPONENT), ==, 1);
+
+ g_object_unref (comp);
+ g_object_unref (vtimezone);
+ vtimezone = i_cal_component_new_from_string (vtimezone_str);
+
+ comp = i_cal_component_new_from_string (
+ "BEGIN:VEVENT\r\n"
+ "UID:1\r\n"
+ "DTSTART:19820903T080000Z\r\n"
+ "DTEND:19820903T090000Z\r\n"
+ "RRULE:FREQ=DAILY;UNTIL=19840101T010000Z\r\n"
+ "END:VEVENT\r\n");
+ g_assert_nonnull (comp);
+
+ e_cal_util_clamp_vtimezone_by_component (vtimezone, comp);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XDAYLIGHT_COMPONENT), ==, 4);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XSTANDARD_COMPONENT), ==, 5);
+
+ g_object_unref (comp);
+ g_object_unref (vtimezone);
+ vtimezone = i_cal_component_new_from_string (vtimezone_str);
+
+ comp = i_cal_component_new_from_string (
+ "BEGIN:VEVENT\r\n"
+ "UID:1\r\n"
+ "DTSTART:19821004T080000Z\r\n"
+ "DTEND:19821004T090000Z\r\n"
+ "RRULE:FREQ=DAILY;UNTIL=20000101T010000Z\r\n"
+ "RECURRENCE-ID:19841004T090000Z\r\n"
+ "END:VEVENT\r\n");
+ g_assert_nonnull (comp);
+
+ e_cal_util_clamp_vtimezone_by_component (vtimezone, comp);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XDAYLIGHT_COMPONENT), ==, 3);
+ g_assert_cmpint (i_cal_component_count_components (vtimezone, I_CAL_XSTANDARD_COMPONENT), ==, 3);
+ g_object_unref (comp);
+
+ g_object_unref (vtimezone);
+ g_object_unref (from);
+ g_object_unref (to);
+}
+
+gint
+main (gint argc,
+ gchar **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+ g_test_bug_base ("https://gitlab.gnome.org/GNOME/evolution-data-server/issues/");
+
+ g_test_add ("/ECalUtils/ClampVTIMEZONE", ETestServerFixture, &test_closure,
+ e_test_server_utils_setup, test_clamp_vtimezone, e_test_server_utils_teardown);
+
+ return e_test_server_utils_run (argc, argv);
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]