diff options
Diffstat (limited to 'calendar/cal-util')
-rw-r--r-- | calendar/cal-util/cal-component.c | 12 | ||||
-rw-r--r-- | calendar/cal-util/cal-recur.c | 123 | ||||
-rw-r--r-- | calendar/cal-util/cal-recur.h | 6 | ||||
-rw-r--r-- | calendar/cal-util/cal-util.c | 15 | ||||
-rw-r--r-- | calendar/cal-util/cal-util.h | 6 | ||||
-rw-r--r-- | calendar/cal-util/test-recur.c | 27 | ||||
-rw-r--r-- | calendar/cal-util/timeutil.c | 2 |
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); |