aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--calendar/cal-util/cal-recur.c172
-rw-r--r--calendar/cal-util/test-recur.c12
2 files changed, 128 insertions, 56 deletions
diff --git a/calendar/cal-util/cal-recur.c b/calendar/cal-util/cal-recur.c
index 5dbb851cec..99e4991323 100644
--- a/calendar/cal-util/cal-recur.c
+++ b/calendar/cal-util/cal-recur.c
@@ -144,8 +144,9 @@ typedef struct _RecurData RecurData;
struct _RecurData {
CalRecurrence *recur;
- /* This is used for the WEEKLY frequency. */
- gint weekday;
+ /* This is used for the WEEKLY frequency. It is the offset from the
+ week_start_day. */
+ gint weekday_offset;
/* This is used for fast lookup in BYMONTH filtering. */
guint8 months[12];
@@ -432,6 +433,8 @@ static gint cal_obj_time_compare (CalObjTime *cotime1,
CalObjTimeComparison type);
static gint cal_obj_time_weekday (CalObjTime *cotime,
CalRecurrence *recur);
+static gint cal_obj_time_weekday_offset (CalObjTime *cotime,
+ CalRecurrence *recur);
static gint cal_obj_time_day_of_year (CalObjTime *cotime);
static void cal_obj_time_find_first_week (CalObjTime *cotime,
RecurData *recur_data);
@@ -458,7 +461,8 @@ static void cal_recur_set_rule_end_date (icalproperty *prop,
#ifdef CAL_OBJ_DEBUG
-static char* cal_obj_time_to_string (CalObjTime *cotime);
+static char* cal_obj_time_to_string (CalObjTime *cotime,
+ RecurData *recur_data);
#endif
@@ -620,6 +624,8 @@ cal_recur_generate_instances_of_rule (CalComponent *comp,
g_return_if_fail (comp != NULL);
g_return_if_fail (cb != NULL);
+ g_return_if_fail (start >= -1);
+ g_return_if_fail (end >= -1);
/* Get dtstart, dtend, recurrences, and exceptions */
@@ -802,6 +808,9 @@ cal_recur_from_icalproperty (icalproperty *prop, gboolean exception)
r->enddate = icaltime_as_timet (ir.until);
if (r->enddate == -1)
r->enddate = 0;
+ else if (ir.until.is_date)
+ /* FIXME: Decide what to do here. */
+ r->enddate = time_add_day (r->enddate, 1) - 1;
}
r->week_start_day = cal_recur_ical_weekday_to_weekday (ir.week_start);
@@ -1593,7 +1602,8 @@ cal_obj_initialize_recur_data (RecurData *recur_data,
/* Set the weekday, used for the WEEKLY frequency and the BYWEEKNO
modifier. */
- recur_data->weekday = cal_obj_time_weekday (event_start, recur);
+ recur_data->weekday_offset = cal_obj_time_weekday_offset (event_start,
+ recur);
/* Create an array of months from bymonths for fast lookup. */
elem = recur->bymonth;
@@ -1968,7 +1978,7 @@ cal_obj_weekly_find_start_position (CalObjTime *event_start,
{
GDate event_start_date, interval_start_date;
guint32 event_start_julian, interval_start_julian;
- gint interval_start_weekday;
+ gint interval_start_weekday_offset;
CalObjTime week_start;
if (event_end && cal_obj_time_compare (event_end, interval_start,
@@ -1992,12 +2002,11 @@ cal_obj_weekly_find_start_position (CalObjTime *event_start,
/* Calculate the start of the weeks corresponding to the event start
and interval start. */
event_start_julian = g_date_julian (&event_start_date);
- event_start_julian -= recur_data->weekday;
+ event_start_julian -= recur_data->weekday_offset;
interval_start_julian = g_date_julian (&interval_start_date);
- interval_start_weekday = cal_obj_time_weekday (interval_start,
- recur_data->recur);
- interval_start_julian -= interval_start_weekday;
+ interval_start_weekday_offset = cal_obj_time_weekday_offset (interval_start, recur_data->recur);
+ interval_start_julian -= interval_start_weekday_offset;
/* We want to find the first full week using the recurrence interval
that intersects the given interval dates. */
@@ -2009,7 +2018,7 @@ cal_obj_weekly_find_start_position (CalObjTime *event_start,
}
week_start = *cotime;
- cal_obj_time_add_days (&week_start, -recur_data->weekday);
+ cal_obj_time_add_days (&week_start, -recur_data->weekday_offset);
if (event_end && cal_obj_time_compare (&week_start, event_end,
CALOBJ_DAY) > 0)
@@ -2035,14 +2044,26 @@ cal_obj_weekly_find_next_position (CalObjTime *cotime,
/* Return TRUE if the start of this week is after the event finishes
or is after the end of the required interval. */
week_start = *cotime;
- cal_obj_time_add_days (&week_start, -recur_data->weekday);
+ cal_obj_time_add_days (&week_start, -recur_data->weekday_offset);
+
+#ifdef CAL_OBJ_DEBUG
+ g_print ("Next day: %s\n",
+ cal_obj_time_to_string (cotime, recur_data));
+ g_print ("Week Start: %s\n",
+ cal_obj_time_to_string (&week_start, recur_data));
+#endif
if (event_end && cal_obj_time_compare (&week_start, event_end,
CALOBJ_DAY) > 0)
return TRUE;
if (interval_end && cal_obj_time_compare (&week_start, interval_end,
- CALOBJ_DAY) > 0)
+ CALOBJ_DAY) > 0) {
+#ifdef CAL_OBJ_DEBUG
+ g_print ("Interval end reached: %s\n",
+ cal_obj_time_to_string (interval_end, recur_data));
+#endif
return TRUE;
+ }
return FALSE;
}
@@ -2702,6 +2723,7 @@ cal_obj_byday_expand_yearly (RecurData *recur_data,
year = occ->year;
if (week_num == 0) {
+ /* Expand to every Mon/Tue/etc. in the year. */
occ->month = 0;
occ->day = 1;
first_weekday = cal_obj_time_weekday (occ, recur_data->recur);
@@ -2714,6 +2736,7 @@ cal_obj_byday_expand_yearly (RecurData *recur_data,
}
} else if (week_num > 0) {
+ /* Add the nth Mon/Tue/etc. in the year. */
occ->month = 0;
occ->day = 1;
first_weekday = cal_obj_time_weekday (occ, recur_data->recur);
@@ -2724,6 +2747,7 @@ cal_obj_byday_expand_yearly (RecurData *recur_data,
g_array_append_vals (new_occs, occ, 1);
} else {
+ /* Add the -nth Mon/Tue/etc. in the year. */
occ->month = 11;
occ->day = 31;
last_weekday = cal_obj_time_weekday (occ, recur_data->recur);
@@ -2778,6 +2802,7 @@ cal_obj_byday_expand_monthly (RecurData *recur_data,
year = occ->year;
month = occ->month;
if (week_num == 0) {
+ /* Expand to every Mon/Tue/etc. in the month.*/
occ->day = 1;
first_weekday = cal_obj_time_weekday (occ, recur_data->recur);
offset = (weekday + 7 - first_weekday) % 7;
@@ -2790,6 +2815,7 @@ cal_obj_byday_expand_monthly (RecurData *recur_data,
}
} else if (week_num > 0) {
+ /* Add the nth Mon/Tue/etc. in the month. */
occ->day = 1;
first_weekday = cal_obj_time_weekday (occ, recur_data->recur);
offset = (weekday + 7 - first_weekday) % 7;
@@ -2799,6 +2825,7 @@ cal_obj_byday_expand_monthly (RecurData *recur_data,
g_array_append_vals (new_occs, occ, 1);
} else {
+ /* Add the -nth Mon/Tue/etc. in the month. */
occ->day = time_days_in_month (occ->year,
occ->month);
last_weekday = cal_obj_time_weekday (occ, recur_data->recur);
@@ -2831,8 +2858,7 @@ cal_obj_byday_expand_weekly (RecurData *recur_data,
CalObjTime *occ;
GList *elem;
gint len, i, weekday, week_num;
- gint current_weekday;
- gint day_of_week, new_day_of_week, days_to_add;
+ gint weekday_offset, new_weekday_offset;
/* If BYDAY has not been specified, or the array is empty, just
return the array. */
@@ -2849,16 +2875,16 @@ cal_obj_byday_expand_weekly (RecurData *recur_data,
while (elem) {
weekday = GPOINTER_TO_INT (elem->data);
elem = elem->next;
+
+ /* FIXME: Currently we just ignore this, but maybe we
+ should skip all elements where week_num != 0.
+ The spec isn't clear about this. */
week_num = GPOINTER_TO_INT (elem->data);
elem = elem->next;
- current_weekday = cal_obj_time_weekday (occ, recur_data->recur);
- day_of_week = (current_weekday + 7
- - recur_data->recur->week_start_day) % 7;
- new_day_of_week = (weekday + 7
- - recur_data->recur->week_start_day) % 7;
- days_to_add = new_day_of_week - day_of_week;
- cal_obj_time_add_days (occ, days_to_add);
+ weekday_offset = cal_obj_time_weekday_offset (occ, recur_data->recur);
+ new_weekday_offset = (weekday + 7 - recur_data->recur->week_start_day) % 7;
+ cal_obj_time_add_days (occ, new_weekday_offset - weekday_offset);
g_array_append_vals (new_occs, occ, 1);
}
}
@@ -3105,32 +3131,41 @@ cal_obj_bysecond_filter (RecurData *recur_data,
-/* Adds a positive number of months to the given CalObjTime, updating the year
- appropriately so we end up with a valid month. Note that the day may be
- invalid. */
+/* Adds a positive or negative number of months to the given CalObjTime,
+ updating the year appropriately so we end up with a valid month.
+ Note that the day may be invalid, e.g. 30th Feb. */
static void
cal_obj_time_add_months (CalObjTime *cotime,
gint months)
{
- guint month;
+ guint month, years;
/* We use a guint to avoid overflow on the guint8. */
month = cotime->month + months;
- cotime->year += month / 12;
cotime->month = month % 12;
+ if (month > 0) {
+ cotime->year += month / 12;
+ } else {
+ years = month / 12;
+ if (cotime->month != 0) {
+ cotime->month += 12;
+ years -= 1;
+ }
+ cotime->year += years;
+ }
}
-/* Adds a positive number of days to the given CalObjTime, updating the month
- and year appropriately so we end up with a valid day. */
+/* Adds a positive or negative number of days to the given CalObjTime,
+ updating the month and year appropriately so we end up with a valid day. */
static void
cal_obj_time_add_days (CalObjTime *cotime,
gint days)
{
- guint day, days_in_month;
+ gint day, days_in_month;
/* We use a guint to avoid overflow on the guint8. */
- day = (guint) cotime->day;
+ day = cotime->day;
day += days;
if (days >= 0) {
@@ -3161,7 +3196,6 @@ cal_obj_time_add_days (CalObjTime *cotime,
days_in_month = time_days_in_month (cotime->year,
cotime->month);
-
day += days_in_month;
}
@@ -3170,8 +3204,9 @@ cal_obj_time_add_days (CalObjTime *cotime,
}
-/* Adds a positive number of hours to the given CalObjTime, updating the day,
- month & year appropriately so we end up with a valid time. */
+/* Adds a positive or negative number of hours to the given CalObjTime,
+ updating the day, month & year appropriately so we end up with a valid
+ time. */
static void
cal_obj_time_add_hours (CalObjTime *cotime,
gint hours)
@@ -3195,8 +3230,8 @@ cal_obj_time_add_hours (CalObjTime *cotime,
}
-/* Adds a positive number of minutes to the given CalObjTime, updating the
- rest of the CalObjTime appropriately. */
+/* Adds a positive or negative number of minutes to the given CalObjTime,
+ updating the rest of the CalObjTime appropriately. */
static void
cal_obj_time_add_minutes (CalObjTime *cotime,
gint minutes)
@@ -3220,8 +3255,8 @@ cal_obj_time_add_minutes (CalObjTime *cotime,
}
-/* Adds a positive number of seconds to the given CalObjTime, updating the
- rest of the CalObjTime appropriately. */
+/* Adds a positive or negative number of seconds to the given CalObjTime,
+ updating the rest of the CalObjTime appropriately. */
static void
cal_obj_time_add_seconds (CalObjTime *cotime,
gint seconds)
@@ -3349,8 +3384,8 @@ cal_obj_time_compare_func (const void *arg1,
retval = 0;
#if 0
- g_print ("%s - ", cal_obj_time_to_string (cotime1));
- g_print ("%s : %i\n", cal_obj_time_to_string (cotime2), retval);
+ g_print ("%s - ", cal_obj_time_to_string (cotime1, NULL));
+ g_print ("%s : %i\n", cal_obj_time_to_string (cotime2, NULL), retval);
#endif
return retval;
@@ -3383,8 +3418,7 @@ cal_obj_date_only_compare_func (const void *arg1,
return 0;
}
-/* Returns the weekday of the given CalObjTime, from 0 - 6. The week start
- day is Monday by default, but can be set in the recurrence rule. */
+/* Returns the weekday of the given CalObjTime, from 0 (Mon) - 6 (Sun). */
static gint
cal_obj_time_weekday (CalObjTime *cotime,
CalRecurrence *recur)
@@ -3398,13 +3432,32 @@ cal_obj_time_weekday (CalObjTime *cotime,
/* This results in a value of 0 (Monday) - 6 (Sunday). */
weekday = g_date_weekday (&date) - 1;
+ return weekday;
+}
+
+
+/* Returns the weekday of the given CalObjTime, from 0 - 6. The week start
+ day is Monday by default, but can be set in the recurrence rule. */
+static gint
+cal_obj_time_weekday_offset (CalObjTime *cotime,
+ CalRecurrence *recur)
+{
+ GDate date;
+ gint weekday, offset;
+
+ g_date_clear (&date, 1);
+ g_date_set_dmy (&date, cotime->day, cotime->month + 1, cotime->year);
+
+ /* This results in a value of 0 (Monday) - 6 (Sunday). */
+ weekday = g_date_weekday (&date) - 1;
+
/* This calculates the offset of our day from the start of the week.
We just add on a week (to avoid any possible negative values) and
then subtract the specified week start day, then convert it into a
value from 0-6. */
- weekday = (weekday + 7 - recur->week_start_day) % 7;
+ offset = (weekday + 7 - recur->week_start_day) % 7;
- return weekday;
+ return offset;
}
@@ -3431,27 +3484,28 @@ cal_obj_time_find_first_week (CalObjTime *cotime,
RecurData *recur_data)
{
GDate date;
- gint weekday, week_start_day, offset;
+ gint weekday, week_start_day, first_full_week_start_offset, offset;
- /* Find out the weekday of the 1st of the year. */
+ /* Find out the weekday of the 1st of the year, 0 (Mon) - 6 (Sun). */
g_date_clear (&date, 1);
g_date_set_dmy (&date, 1, 1, cotime->year);
-
- /* This results in a value of 0 (Monday) - 6 (Sunday). */
weekday = g_date_weekday (&date) - 1;
- /* Calculate the first day of the year that starts a new week. */
+ /* Calculate the first day of the year that starts a new week, i.e. the
+ first week_start_day after weekday, using 0 = 1st Jan.
+ e.g. if the 1st Jan is a Tuesday (1) and week_start_day is a
+ Monday (0), the result will be (0 + 7 - 1) % 7 = 6 (7th Jan). */
week_start_day = recur_data->recur->week_start_day;
- offset = (week_start_day + 7 - weekday) % 7;
+ first_full_week_start_offset = (week_start_day + 7 - weekday) % 7;
/* Now see if we have to move backwards 1 week, i.e. if the week
starts on or after Jan 5th (since the previous week has 4 days in
this year and so will be the first week of the year). */
- if (offset >= 4)
- offset -= 7;
+ if (first_full_week_start_offset >= 4)
+ first_full_week_start_offset -= 7;
- /* Now move to the required day. */
- offset += (recur_data->weekday + 7 - week_start_day) % 7;
+ /* Now add the days to get to the event's weekday. */
+ offset = first_full_week_start_offset + recur_data->weekday_offset;
/* Now move the cotime to the appropriate day. */
cotime->month = 0;
@@ -3484,11 +3538,19 @@ cal_object_time_from_time (CalObjTime *cotime,
buffer so beware. */
#ifdef CAL_OBJ_DEBUG
static char*
-cal_obj_time_to_string (CalObjTime *cotime)
+cal_obj_time_to_string (CalObjTime *cotime,
+ RecurData *recur_data)
{
static char buffer[20];
+ char *weekdays[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
+ " " };
+ gint weekday = 7;
+
+ if (recur_data)
+ weekday = cal_obj_time_weekday (cotime, recur_data->recur);
- sprintf (buffer, "%02i/%02i/%04i %02i:%02i:%02i",
+ sprintf (buffer, "%s %02i/%02i/%04i %02i:%02i:%02i",
+ weekdays[weekday],
cotime->day, cotime->month + 1, cotime->year,
cotime->hour, cotime->minute, cotime->second);
return buffer;
diff --git a/calendar/cal-util/test-recur.c b/calendar/cal-util/test-recur.c
index 2c94b28921..2f1320ca86 100644
--- a/calendar/cal-util/test-recur.c
+++ b/calendar/cal-util/test-recur.c
@@ -24,7 +24,12 @@
/*
* This tests the recurrence rule expansion functions.
- * FIXME: Needs to be updated to just use the public interface.
+ *
+ * NOTE: currently it starts from the event start date and continues
+ * until all recurrence rules/dates end or we reach MAX_OCCURRENCES
+ * occurrences. So it does not test generating occurrences for a specific
+ * interval. A nice addition might be to do this automatically and compare
+ * the results from the complete set to ensure they match.
*/
#include <config.h>
@@ -144,8 +149,13 @@ generate_occurrences (icalcomponent *icalcomp)
g_print ("%s\n\n", icalcomponent_as_ical_string (tmp_icalcomp));
occurrences = 0;
+#if 0
+ cal_recur_generate_instances (comp, 972950400, 976492800,
+ occurrence_cb, &occurrences);
+#else
cal_recur_generate_instances (comp, -1, -1,
occurrence_cb, &occurrences);
+#endif
g_print ("%s\n\n", icalcomponent_as_ical_string (tmp_icalcomp));
}