aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/cal-util
diff options
context:
space:
mode:
Diffstat (limited to 'calendar/cal-util')
-rw-r--r--calendar/cal-util/cal-component.c12
-rw-r--r--calendar/cal-util/cal-recur.c123
-rw-r--r--calendar/cal-util/cal-recur.h6
-rw-r--r--calendar/cal-util/cal-util.c15
-rw-r--r--calendar/cal-util/cal-util.h6
-rw-r--r--calendar/cal-util/test-recur.c27
-rw-r--r--calendar/cal-util/timeutil.c2
7 files changed, 144 insertions, 47 deletions
diff --git a/calendar/cal-util/cal-component.c b/calendar/cal-util/cal-component.c
index 312556fd54..fb7c30f95b 100644
--- a/calendar/cal-util/cal-component.c
+++ b/calendar/cal-util/cal-component.c
@@ -1925,6 +1925,8 @@ set_datetime (CalComponent *comp, struct datetime *datetime,
priv = comp->priv;
+ /* If we are setting the property to NULL (i.e. removing it), then
+ we remove it if it exists. */
if (!dt) {
if (datetime->prop) {
icalcomponent_remove_property (priv->icalcomp, datetime->prop);
@@ -1942,10 +1944,12 @@ set_datetime (CalComponent *comp, struct datetime *datetime,
/* If the TZID is set to "UTC", we set the is_utc flag. */
if (dt->tzid && !strcmp (dt->tzid, "UTC"))
dt->value->is_utc = 1;
+ else
+ dt->value->is_utc = 0;
- if (datetime->prop)
+ if (datetime->prop) {
(* prop_set_func) (datetime->prop, *dt->value);
- else {
+ } else {
datetime->prop = (* prop_new_func) (*dt->value);
icalcomponent_add_property (priv->icalcomp, datetime->prop);
}
@@ -1954,9 +1958,9 @@ set_datetime (CalComponent *comp, struct datetime *datetime,
if (dt->tzid && strcmp (dt->tzid, "UTC")) {
g_assert (datetime->prop != NULL);
- if (datetime->tzid_param)
+ if (datetime->tzid_param) {
icalparameter_set_tzid (datetime->tzid_param, (char *) dt->tzid);
- else {
+ } else {
datetime->tzid_param = icalparameter_new_tzid ((char *) dt->tzid);
icalproperty_add_parameter (datetime->prop, datetime->tzid_param);
}
diff --git a/calendar/cal-util/cal-recur.c b/calendar/cal-util/cal-recur.c
index 83a0bccae9..28b1be9aec 100644
--- a/calendar/cal-util/cal-recur.c
+++ b/calendar/cal-util/cal-recur.c
@@ -104,8 +104,13 @@
* spec.
*/
+/* This is the maximum year we will go up to (inclusive). Since we use time_t
+ values we can't go past 2037 anyway, and some of our VTIMEZONEs may stop
+ at 2037 as well. */
+#define MAX_YEAR 2037
+
/* Define this for some debugging output. */
-#if 0
+#if 1
#define CAL_OBJ_DEBUG 1
#endif
@@ -264,11 +269,13 @@ static void cal_recur_generate_instances_of_rule (CalComponent *comp,
CalRecurInstanceFn cb,
gpointer cb_data,
CalRecurResolveTimezoneFn tz_cb,
- gpointer tz_cb_data);
+ gpointer tz_cb_data,
+ icaltimezone *default_timezone);
static CalRecurrence * cal_recur_from_icalproperty (icalproperty *prop,
gboolean exception,
- icaltimezone *zone);
+ icaltimezone *zone,
+ gboolean convert_end_date);
static gint cal_recur_ical_weekday_to_weekday (enum icalrecurrencetype_weekday day);
static void cal_recur_free (CalRecurrence *r);
@@ -294,6 +301,7 @@ static gboolean generate_instances_for_chunk (CalComponent *comp,
CalObjTime *chunk_end,
gint duration_days,
gint duration_seconds,
+ gboolean convert_end_date,
CalRecurInstanceFn cb,
gpointer cb_data);
@@ -487,7 +495,8 @@ static gboolean cal_recur_ensure_rule_end_date_cb (CalComponent *comp,
time_t instance_start,
time_t instance_end,
gpointer data);
-static time_t cal_recur_get_rule_end_date (icalproperty *prop);
+static time_t cal_recur_get_rule_end_date (icalproperty *prop,
+ icaltimezone *default_timezone);
static void cal_recur_set_rule_end_date (icalproperty *prop,
time_t end_date);
@@ -613,7 +622,8 @@ cal_recur_generate_instances (CalComponent *comp,
CalRecurInstanceFn cb,
gpointer cb_data,
CalRecurResolveTimezoneFn tz_cb,
- gpointer tz_cb_data)
+ gpointer tz_cb_data,
+ icaltimezone *default_timezone)
{
#if 0
g_print ("In cal_recur_generate_instances comp: %p\n", comp);
@@ -621,7 +631,8 @@ cal_recur_generate_instances (CalComponent *comp,
g_print (" end : %li - %s", end, ctime (&end));
#endif
cal_recur_generate_instances_of_rule (comp, NULL, start, end,
- cb, cb_data, tz_cb, tz_cb_data);
+ cb, cb_data, tz_cb, tz_cb_data,
+ default_timezone);
}
@@ -647,7 +658,8 @@ cal_recur_generate_instances_of_rule (CalComponent *comp,
CalRecurInstanceFn cb,
gpointer cb_data,
CalRecurResolveTimezoneFn tz_cb,
- gpointer tz_cb_data)
+ gpointer tz_cb_data,
+ icaltimezone *default_timezone)
{
CalComponentDateTime dtstart, dtend;
time_t dtstart_time, dtend_time;
@@ -656,11 +668,12 @@ cal_recur_generate_instances_of_rule (CalComponent *comp,
CalObjTime interval_start, interval_end, event_start, event_end;
CalObjTime chunk_start, chunk_end;
gint days, seconds, year;
- gboolean single_rule;
+ gboolean single_rule, convert_end_date = FALSE;
icaltimezone *start_zone = NULL, *end_zone = NULL;
g_return_if_fail (comp != NULL);
g_return_if_fail (cb != NULL);
+ g_return_if_fail (tz_cb != NULL);
g_return_if_fail (start >= -1);
g_return_if_fail (end >= -1);
@@ -675,15 +688,18 @@ cal_recur_generate_instances_of_rule (CalComponent *comp,
goto out;
}
- /* FIXME: All floating times, including those from recurrence rules,
- should be converted to the current timezone, otherwise the time_t
- values may be outside the given range. This may also mean that
- we need to add a timezone argument to the IDL method. */
-
- if (dtstart.tzid && tz_cb)
+ /* For DATE-TIME values with a TZID, we use the supplied callback to
+ resolve the TZID. For DATE values and DATE-TIME values without a
+ TZID (i.e. floating times) we use the default timezone. */
+ if (dtstart.tzid && !dtstart.value->is_date) {
start_zone = (*tz_cb) (dtstart.tzid, tz_cb_data);
- if (dtend.tzid && tz_cb)
- end_zone = (*tz_cb) (dtend.tzid, tz_cb_data);
+ } else {
+ start_zone = default_timezone;
+
+ /* Flag that we need to convert the saved ENDDATE property
+ to the default timezone. */
+ convert_end_date = TRUE;
+ }
dtstart_time = icaltime_as_timet_with_zone (*dtstart.value,
start_zone);
@@ -691,8 +707,22 @@ cal_recur_generate_instances_of_rule (CalComponent *comp,
start = dtstart_time;
/* FIXME: DURATION could be used instead, couldn't it? - Damon */
- if (!dtend.value) {
+
+ /* If there is no DTEND, then use the same as the DTSTART. For
+ DATE-TIME values that means we will just have a single point in
+ time. For DATE values it means we end up with the entire day. */
+ if (!dtend.value)
*dtend.value = *dtstart.value;
+
+ if (dtend.tzid && !dtend.value->is_date) {
+ end_zone = (*tz_cb) (dtend.tzid, tz_cb_data);
+ } else {
+ end_zone = default_timezone;
+ }
+
+ /* If DTEND is a DATE value, we add 1 day to it so that it includes
+ the entire day. */
+ if (dtend.value->is_date) {
dtend.value->hour = 0;
dtend.value->minute = 0;
dtend.value->second = 0;
@@ -768,7 +798,7 @@ cal_recur_generate_instances_of_rule (CalComponent *comp,
Though this does mean that we sometimes do a lot more work than
is necessary, e.g. if COUNT is set to something quite low. */
for (year = interval_start.year;
- end == -1 || year <= interval_end.year;
+ (end == -1 || year <= interval_end.year) && year <= MAX_YEAR;
year++) {
chunk_start = interval_start;
chunk_start.year = year;
@@ -801,6 +831,7 @@ cal_recur_generate_instances_of_rule (CalComponent *comp,
start,
&chunk_start, &chunk_end,
days, seconds,
+ convert_end_date,
cb, cb_data))
break;
}
@@ -833,9 +864,13 @@ array_to_list (short *array, int max_elements)
/**
* cal_recur_from_icalproperty:
- * @ir: An RRULE or EXRULE #icalproperty.
+ * @prop: An RRULE or EXRULE #icalproperty.
+ * @exception: TRUE if this is an EXRULE rather than an RRULE.
* @zone: The DTSTART timezone, used for converting the UNTIL property if it
* is given as a DATE value.
+ * @convert_end_date: TRUE if the saved end date needs to be converted to the
+ * given @zone timezone. This is needed if the DTSTART is a DATE or floating
+ * time.
*
* Converts an #icalproperty to a #CalRecurrence. This should be
* freed using the cal_recur_free() function.
@@ -844,7 +879,7 @@ array_to_list (short *array, int max_elements)
**/
static CalRecurrence *
cal_recur_from_icalproperty (icalproperty *prop, gboolean exception,
- icaltimezone *zone)
+ icaltimezone *zone, gboolean convert_end_date)
{
struct icalrecurrencetype ir;
CalRecurrence *r;
@@ -863,8 +898,10 @@ cal_recur_from_icalproperty (icalproperty *prop, gboolean exception,
r->interval = ir.interval;
if (ir.count != 0) {
- /* If COUNT is set, we use the pre-calculated enddate. */
- r->enddate = cal_recur_get_rule_end_date (prop);
+ /* If COUNT is set, we use the pre-calculated enddate.
+ Note that this can be 0 if the RULE doesn't actually
+ generate COUNT instances. */
+ r->enddate = cal_recur_get_rule_end_date (prop, convert_end_date ? zone : NULL);
} else {
if (icaltime_is_null_time (ir.until)) {
/* If neither COUNT or UNTIL is set, the event
@@ -1033,6 +1070,7 @@ generate_instances_for_chunk (CalComponent *comp,
CalObjTime *chunk_end,
gint duration_days,
gint duration_seconds,
+ gboolean convert_end_date,
CalRecurInstanceFn cb,
gpointer cb_data)
{
@@ -1079,7 +1117,8 @@ generate_instances_for_chunk (CalComponent *comp,
CalRecurrence *r;
prop = elem->data;
- r = cal_recur_from_icalproperty (prop, FALSE, zone);
+ r = cal_recur_from_icalproperty (prop, FALSE, zone,
+ convert_end_date);
tmp_occs = cal_obj_expand_recurrence (event_start, zone, r,
chunk_start,
@@ -1146,7 +1185,8 @@ generate_instances_for_chunk (CalComponent *comp,
CalRecurrence *r;
prop = elem->data;
- r = cal_recur_from_icalproperty (prop, FALSE, zone);
+ r = cal_recur_from_icalproperty (prop, FALSE, zone,
+ convert_end_date);
tmp_occs = cal_obj_expand_recurrence (event_start, zone, r,
chunk_start,
@@ -2986,8 +3026,15 @@ cal_obj_byday_expand_monthly (RecurData *recur_data,
occ->day = time_days_in_month (occ->year,
occ->month);
last_weekday = cal_obj_time_weekday (occ);
+
+ /* This calculates the number of days to step
+ backwards from the last day of the month
+ to the weekday we want. */
offset = (last_weekday + 7 - weekday) % 7;
- offset += (week_num - 1) * 7;
+
+ /* This adds on the weeks. */
+ offset += (-week_num - 1) * 7;
+
cal_obj_time_add_days (occ, -offset);
if (occ->year == year && occ->month == month)
g_array_append_vals (new_occs, occ, 1);
@@ -3780,16 +3827,22 @@ cal_recur_ensure_rule_end_date (CalComponent *comp,
/* If refresh is FALSE, we check if the enddate is already set, and
if it is we just return. */
if (!refresh) {
- if (cal_recur_get_rule_end_date (prop) != -1)
+ if (cal_recur_get_rule_end_date (prop, NULL) != -1)
return FALSE;
}
- /* Calculate the end date. */
+ /* Calculate the end date. Note that we initialize end_date to 0, so
+ if the RULE doesn't generate COUNT instances we save a time_t of 0.
+ Also note that we use the UTC timezone as the default timezone.
+ In get_end_date() if the DTSTART is a DATE or floating time, we will
+ convert the ENDDATE to the current timezone. */
cb_data.count = rule.count;
cb_data.instances = 0;
+ cb_data.end_date = 0;
cal_recur_generate_instances_of_rule (comp, prop, -1, -1,
cal_recur_ensure_rule_end_date_cb,
- &cb_data, tz_cb, tz_cb_data);
+ &cb_data, tz_cb, tz_cb_data,
+ icaltimezone_get_utc_timezone ());
/* Store the end date in the "X-EVOLUTION-ENDDATE" parameter of the
rule. */
@@ -3820,14 +3873,19 @@ cal_recur_ensure_rule_end_date_cb (CalComponent *comp,
}
+/* If default_timezone is set, the saved ENDDATE parameter is assumed to be
+ in that timezone. This is used when the DTSTART is a DATE or floating
+ value, since the RRULE end date will change depending on the timezone that
+ it is evaluated in. */
static time_t
-cal_recur_get_rule_end_date (icalproperty *prop)
+cal_recur_get_rule_end_date (icalproperty *prop,
+ icaltimezone *default_timezone)
{
icalparameter *param;
const char *xname, *xvalue;
icalvalue *value;
struct icaltimetype icaltime;
- icaltimezone *utc_zone;
+ icaltimezone *zone;
param = icalproperty_get_first_parameter (prop, ICAL_X_PARAMETER);
while (param) {
@@ -3840,9 +3898,10 @@ cal_recur_get_rule_end_date (icalproperty *prop)
icaltime = icalvalue_get_datetime (value);
icalvalue_free (value);
- utc_zone = icaltimezone_get_utc_timezone ();
+ zone = default_timezone ? default_timezone :
+ icaltimezone_get_utc_timezone ();
return icaltime_as_timet_with_zone (icaltime,
- utc_zone);
+ zone);
}
}
diff --git a/calendar/cal-util/cal-recur.h b/calendar/cal-util/cal-recur.h
index 921a230de4..3527368cba 100644
--- a/calendar/cal-util/cal-recur.h
+++ b/calendar/cal-util/cal-recur.h
@@ -52,6 +52,9 @@ typedef icaltimezone* (* CalRecurResolveTimezoneFn) (const char *tzid,
* The tz_cb is used to resolve references to timezones. It is passed a TZID
* and should return the icaltimezone* corresponding to that TZID. We need to
* do this as we access timezones in different ways on the client & server.
+ *
+ * The default_timezone argument is used for DTSTART or DTEND properties that
+ * are DATE values or do not have a TZID (i.e. floating times).
*/
void cal_recur_generate_instances (CalComponent *comp,
time_t start,
@@ -59,7 +62,8 @@ void cal_recur_generate_instances (CalComponent *comp,
CalRecurInstanceFn cb,
gpointer cb_data,
CalRecurResolveTimezoneFn tz_cb,
- gpointer tz_cb_data);
+ gpointer tz_cb_data,
+ icaltimezone *default_timezone);
END_GNOME_DECLS
diff --git a/calendar/cal-util/cal-util.c b/calendar/cal-util/cal-util.c
index 774b9aca11..0819a56481 100644
--- a/calendar/cal-util/cal-util.c
+++ b/calendar/cal-util/cal-util.c
@@ -312,6 +312,8 @@ compare_alarm_instance (gconstpointer a, gconstpointer b)
* @end: end time
* @resolve_tzid: callback for resolving timezones
* @user_data: data to be passed to the resolve_tzid callback
+ * @default_timezone: the timezone used to resolve DATE and floating DATE-TIME
+ * values.
*
* Generates alarm instances for a calendar component. Returns the instances
* structure, or NULL if no alarm instances occurred in the specified time
@@ -322,7 +324,8 @@ cal_util_generate_alarms_for_comp (CalComponent *comp,
time_t start,
time_t end,
CalRecurResolveTimezoneFn resolve_tzid,
- gpointer user_data)
+ gpointer user_data,
+ icaltimezone *default_timezone)
{
GList *alarm_uids;
time_t alarm_start, alarm_end;
@@ -343,7 +346,8 @@ cal_util_generate_alarms_for_comp (CalComponent *comp,
cal_recur_generate_instances (comp, alarm_start, alarm_end,
add_alarm_occurrences_cb, &aod,
- resolve_tzid, user_data);
+ resolve_tzid, user_data,
+ default_timezone);
/* We add the ABSOLUTE triggers separately */
generate_absolute_triggers (comp, &aod);
@@ -369,6 +373,8 @@ cal_util_generate_alarms_for_comp (CalComponent *comp,
* @comp_alarms: list to be returned
* @resolve_tzid: callback for resolving timezones
* @user_data: data to be passed to the resolve_tzid callback
+ * @default_timezone: the timezone used to resolve DATE and floating DATE-TIME
+ * values.
*
* Iterates through all the components in the comps list and generates alarm
* instances for them; putting them in the comp_alarms list.
@@ -381,7 +387,8 @@ cal_util_generate_alarms_for_list (GList *comps,
time_t end,
GSList **comp_alarms,
CalRecurResolveTimezoneFn resolve_tzid,
- gpointer user_data)
+ gpointer user_data,
+ icaltimezone *default_timezone)
{
GList *l;
int n;
@@ -393,7 +400,7 @@ cal_util_generate_alarms_for_list (GList *comps,
CalComponentAlarms *alarms;
comp = CAL_COMPONENT (l->data);
- alarms = cal_util_generate_alarms_for_comp (comp, start, end, resolve_tzid, user_data);
+ alarms = cal_util_generate_alarms_for_comp (comp, start, end, resolve_tzid, user_data, default_timezone);
if (alarms) {
*comp_alarms = g_slist_prepend (*comp_alarms, alarms);
diff --git a/calendar/cal-util/cal-util.h b/calendar/cal-util/cal-util.h
index 9e7c7ce5e4..da16a61822 100644
--- a/calendar/cal-util/cal-util.h
+++ b/calendar/cal-util/cal-util.h
@@ -69,13 +69,15 @@ CalComponentAlarms *cal_util_generate_alarms_for_comp (CalComponent *comp,
time_t start,
time_t end,
CalRecurResolveTimezoneFn resolve_tzid,
- gpointer user_data);
+ gpointer user_data,
+ icaltimezone *default_timezone);
int cal_util_generate_alarms_for_list (GList *comps,
time_t start,
time_t end,
GSList **comp_alarms,
CalRecurResolveTimezoneFn resolve_tzid,
- gpointer user_data);
+ gpointer user_data,
+ icaltimezone *default_timezone);
icaltimezone *cal_util_resolve_tzid (const char *tzid, gpointer data);
diff --git a/calendar/cal-util/test-recur.c b/calendar/cal-util/test-recur.c
index 213017ea0e..de8cf23dc2 100644
--- a/calendar/cal-util/test-recur.c
+++ b/calendar/cal-util/test-recur.c
@@ -121,10 +121,31 @@ get_line (char *s,
}
+/* This resolves any TZIDs in the components. The VTIMEZONEs must be in the
+ file we are reading. */
+static icaltimezone*
+resolve_tzid_cb (const char *tzid,
+ gpointer user_data)
+{
+ icalcomponent *vcalendar_comp = user_data;
+
+ if (!tzid || !tzid[0])
+ return NULL;
+ else if (!strcmp (tzid, "UTC"))
+ return icaltimezone_get_utc_timezone ();
+
+ return icalcomponent_get_timezone (vcalendar_comp, tzid);
+}
+
+
static void
generate_occurrences (icalcomponent *icalcomp)
{
icalcompiter iter;
+ icaltimezone *default_timezone;
+
+ /* This is the timezone we will use for DATE and floating values. */
+ default_timezone = icaltimezone_get_utc_timezone ();
for (iter = icalcomponent_begin_component (icalcomp, ICAL_ANY_COMPONENT);
icalcompiter_deref (&iter) != NULL;
@@ -157,11 +178,13 @@ generate_occurrences (icalcomponent *icalcomp)
#if 0
cal_recur_generate_instances (comp, 982022400, 982108800,
occurrence_cb, &occurrences,
- NULL, NULL);
+ resolve_tzid_cb, icalcomp,
+ default_timezone);
#else
cal_recur_generate_instances (comp, -1, -1,
occurrence_cb, &occurrences,
- NULL, NULL);
+ resolve_tzid_cb, icalcomp,
+ default_timezone);
#endif
/* Print the component again so we can see the
diff --git a/calendar/cal-util/timeutil.c b/calendar/cal-util/timeutil.c
index aff465b8df..f960346477 100644
--- a/calendar/cal-util/timeutil.c
+++ b/calendar/cal-util/timeutil.c
@@ -367,8 +367,6 @@ time_days_in_month (int year, int month)
{
int days;
- g_print ("Year: %i Month: %i\n", year, month);
-
g_return_val_if_fail (year >= 1900, 0);
g_return_val_if_fail ((month >= 0) && (month < 12), 0);