diff options
Diffstat (limited to 'calendar/cal-util')
-rw-r--r-- | calendar/cal-util/cal-component.c | 39 | ||||
-rw-r--r-- | calendar/cal-util/cal-component.h | 4 | ||||
-rw-r--r-- | calendar/cal-util/cal-recur.c | 267 | ||||
-rw-r--r-- | calendar/cal-util/timeutil.c | 606 | ||||
-rw-r--r-- | calendar/cal-util/timeutil.h | 123 |
5 files changed, 695 insertions, 344 deletions
diff --git a/calendar/cal-util/cal-component.c b/calendar/cal-util/cal-component.c index 1ebc398e63..d340cea60f 100644 --- a/calendar/cal-util/cal-component.c +++ b/calendar/cal-util/cal-component.c @@ -4933,5 +4933,44 @@ cal_component_event_dates_match (CalComponent *comp1, return TRUE; } +/* Returns TRUE if the TZIDs are equivalent, i.e. both NULL or the same. */ +static gboolean +cal_component_compare_tzid (const char *tzid1, const char *tzid2) +{ + gboolean retval = TRUE; + + if (tzid1) { + if (!tzid2 || strcmp (tzid1, tzid2)) + retval = FALSE; + } else { + if (tzid2) + retval = FALSE; + } + + return retval; +} +/* Returns TRUE if the component uses the given timezones for both DTSTART + and DTEND. */ +gboolean +cal_component_compare_event_timezone (CalComponent *comp, icaltimezone *zone) +{ + CalComponentDateTime datetime; + const char *tzid; + gboolean match; + tzid = icaltimezone_get_tzid (zone); + + cal_component_get_dtstart (comp, &datetime); + match = cal_component_compare_tzid (tzid, datetime.tzid); + cal_component_free_datetime (&datetime); + if (!match) + return FALSE; + + /* FIXME: DURATION may be used instead. */ + cal_component_get_dtend (comp, &datetime); + match = cal_component_compare_tzid (tzid, datetime.tzid); + cal_component_free_datetime (&datetime); + + return match; +} diff --git a/calendar/cal-util/cal-component.h b/calendar/cal-util/cal-component.h index 2dcc06ce61..f0628cb967 100644 --- a/calendar/cal-util/cal-component.h +++ b/calendar/cal-util/cal-component.h @@ -335,6 +335,10 @@ void cal_component_set_attendee_list (CalComponent *comp, GSList *attendee_list) gboolean cal_component_event_dates_match (CalComponent *comp1, CalComponent *comp2); +/* Returns TRUE if the component uses the given timezones for both DTSTART + and DTEND. */ +gboolean cal_component_compare_event_timezone (CalComponent *comp, icaltimezone *zone); + /* Functions to free returned values */ void cal_component_free_categories_list (GSList *categ_list); diff --git a/calendar/cal-util/cal-recur.c b/calendar/cal-util/cal-recur.c index 1e21f3109c..8df567fa5c 100644 --- a/calendar/cal-util/cal-recur.c +++ b/calendar/cal-util/cal-recur.c @@ -115,8 +115,8 @@ typedef struct { int interval; - /* Specifies the end of the recurrence. No occurrences are generated - after this date. If it is 0, the event recurs forever. */ + /* Specifies the end of the recurrence, inclusive. No occurrences are + generated after this date. If it is 0, the event recurs forever. */ time_t enddate; /* WKST property - the week start day: 0 = Monday to 6 = Sunday. */ @@ -197,8 +197,13 @@ struct _CalObjTime { guint8 hour; /* 0 - 23 */ guint8 minute; /* 0 - 59 */ guint8 second; /* 0 - 59 (maybe up to 61 for leap seconds) */ - guint8 is_rdate; /* TRUE if this is an RDATE, which may have an - end or duration set. */ + guint8 flags; /* The meaning of this depends on where the + CalObjTime is used. In most cases this is + set to TRUE to indicate that this is an + RDATE with an end or a duration set. + In the exceptions code, this is set to TRUE + to indicate that this is an EXDATE with a + DATE value. */ }; /* This is what we use to represent specific recurrence dates. @@ -262,7 +267,8 @@ static void cal_recur_generate_instances_of_rule (CalComponent *comp, gpointer tz_cb_data); static CalRecurrence * cal_recur_from_icalproperty (icalproperty *prop, - gboolean exception); + gboolean exception, + icaltimezone *zone); static gint cal_recur_ical_weekday_to_weekday (enum icalrecurrencetype_weekday day); static void cal_recur_free (CalRecurrence *r); @@ -276,6 +282,7 @@ static void cal_object_compute_duration (CalObjTime *start, static gboolean generate_instances_for_chunk (CalComponent *comp, time_t comp_dtstart, + icaltimezone *zone, GSList *rrules, GSList *rdates, GSList *exrules, @@ -291,6 +298,7 @@ static gboolean generate_instances_for_chunk (CalComponent *comp, gpointer cb_data); static GArray* cal_obj_expand_recurrence (CalObjTime *event_start, + icaltimezone *zone, CalRecurrence *recur, CalObjTime *interval_start, CalObjTime *interval_end, @@ -458,11 +466,10 @@ static gint cal_obj_time_day_of_year (CalObjTime *cotime); static void cal_obj_time_find_first_week (CalObjTime *cotime, RecurData *recur_data); static void cal_object_time_from_time (CalObjTime *cotime, - time_t t); -#if 0 -static gint cal_obj_date_only_compare_func (const void *arg1, - const void *arg2); -#endif + time_t t, + icaltimezone *zone); +static gint cal_obj_date_only_compare_func (const void *arg1, + const void *arg2); @@ -722,13 +729,14 @@ cal_recur_generate_instances_of_rule (CalComponent *comp, /* Convert the interval start & end to CalObjTime. Note that if end is -1 interval_end won't be set, so don't use it! Also note that we use end - 1 since we want the interval to be - inclusive as it makes the code simpler. */ - cal_object_time_from_time (&interval_start, start); + inclusive as it makes the code simpler. We do all calculation + in the timezone of the DTSTART. */ + cal_object_time_from_time (&interval_start, start, start_zone); if (end != -1) - cal_object_time_from_time (&interval_end, end - 1); + cal_object_time_from_time (&interval_end, end - 1, start_zone); - cal_object_time_from_time (&event_start, dtstart_time); - cal_object_time_from_time (&event_end, dtend_time); + cal_object_time_from_time (&event_start, dtstart_time, start_zone); + cal_object_time_from_time (&event_end, dtend_time, start_zone); /* Calculate the duration of the event, which we use for all occurrences. We can't just subtract start from end since that may @@ -776,10 +784,11 @@ cal_recur_generate_instances_of_rule (CalComponent *comp, chunk_end.hour = 23; chunk_end.minute = 59; chunk_end.second = 61; - chunk_end.is_rdate = FALSE; + chunk_end.flags = FALSE; } if (!generate_instances_for_chunk (comp, dtstart_time, + start_zone, rrules, rdates, exrules, exdates, single_rule, @@ -820,6 +829,8 @@ array_to_list (short *array, int max_elements) /** * cal_recur_from_icalproperty: * @ir: An RRULE or EXRULE #icalproperty. + * @zone: The DTSTART timezone, used for converting the UNTIL property if it + * is given as a DATE value. * * Converts an #icalproperty to a #CalRecurrence. This should be * freed using the cal_recur_free() function. @@ -827,7 +838,8 @@ array_to_list (short *array, int max_elements) * Return value: #CalRecurrence structure. **/ static CalRecurrence * -cal_recur_from_icalproperty (icalproperty *prop, gboolean exception) +cal_recur_from_icalproperty (icalproperty *prop, gboolean exception, + icaltimezone *zone) { struct icalrecurrencetype ir; CalRecurrence *r; @@ -846,16 +858,31 @@ 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); } else { - /* FIXME: icaltime_as_timet() seems to return -1 if UNTIL isn't - set, but a simpler test would be better. */ - r->enddate = icaltime_as_timet (ir.until); - if (r->enddate == -1) + if (icaltime_is_null_time (ir.until)) { + /* If neither COUNT or UNTIL is set, the event + recurs forever. */ r->enddate = 0; - else if (ir.until.is_date) - /* FIXME: Decide what to do here. */ - r->enddate = time_add_day (r->enddate, 1) - 1; + } else if (ir.until.is_date) { + /* If UNTIL is a DATE, we stop at the end of + the day, in local time (with the DTSTART timezone). + Note that UNTIL is inclusive so we stop before + midnight. */ + ir.until.hour = 23; + ir.until.minute = 59; + ir.until.second = 59; + + r->enddate = icaltime_as_timet_with_zone (ir.until, + zone); + } else { + /* If UNTIL is a DATE-TIME, it must be in UTC. */ + icaltimezone *utc_zone; + utc_zone = icaltimezone_get_utc_timezone (); + r->enddate = icaltime_as_timet_with_zone (ir.until, + utc_zone); + } } r->week_start_day = cal_recur_ical_weekday_to_weekday (ir.week_start); @@ -984,6 +1011,7 @@ cal_recur_free (CalRecurrence *r) static gboolean generate_instances_for_chunk (CalComponent *comp, time_t comp_dtstart, + icaltimezone *zone, GSList *rrules, GSList *rdates, GSList *exrules, @@ -1003,7 +1031,7 @@ generate_instances_for_chunk (CalComponent *comp, GSList *elem; gint i; time_t start_time, end_time; - struct tm start_tm, end_tm; + struct icaltimetype start_tt, end_tt; gboolean cb_status = TRUE, rule_finished, finished = TRUE; #if 0 @@ -1041,9 +1069,9 @@ generate_instances_for_chunk (CalComponent *comp, CalRecurrence *r; prop = elem->data; - r = cal_recur_from_icalproperty (prop, FALSE); + r = cal_recur_from_icalproperty (prop, FALSE, zone); - tmp_occs = cal_obj_expand_recurrence (event_start, r, + tmp_occs = cal_obj_expand_recurrence (event_start, zone, r, chunk_start, chunk_end, &rule_finished); @@ -1058,16 +1086,26 @@ generate_instances_for_chunk (CalComponent *comp, g_array_free (tmp_occs, TRUE); } - /* Add on specific occurrence dates, flag them as RDATEs, and store - a pointer to the period in the rdate_periods array. */ + /* Add on specific RDATE occurrence dates. If they have an end time + or duration set, flag them as RDATEs, and store a pointer to the + period in the rdate_periods array. Otherwise we can just treat them + as normal occurrences. */ for (elem = rdates; elem; elem = elem->next) { CalComponentPeriod *p; CalObjRecurrenceDate rdate; - time_t t; p = elem->data; - t = icaltime_as_timet (p->start); - cal_object_time_from_time (&cotime, t); + + /* FIXME: We currently assume RDATEs are in the same timezone + as DTSTART. We should get the RDATE timezone and convert + to the DTSTART timezone first. */ + cotime.year = p->start.year; + cotime.month = p->start.month - 1; + cotime.day = p->start.day; + cotime.hour = p->start.hour; + cotime.minute = p->start.minute; + cotime.second = p->start.second; + cotime.flags = FALSE; /* If the rdate is after the current chunk we set finished to FALSE, and we skip it. */ @@ -1078,10 +1116,11 @@ generate_instances_for_chunk (CalComponent *comp, /* Check if the end date or duration is set. If it is we need to store it so we can get it later. (libical seems to set - second to -1 to denote an unset time. See icalvalue.c) */ + second to -1 to denote an unset time. See icalvalue.c) + FIXME. */ if (p->type != CAL_COMPONENT_PERIOD_DATETIME || p->u.end.second != -1) { - cotime.is_rdate = TRUE; + cotime.flags = TRUE; rdate.start = cotime; rdate.period = p; @@ -1097,9 +1136,9 @@ generate_instances_for_chunk (CalComponent *comp, CalRecurrence *r; prop = elem->data; - r = cal_recur_from_icalproperty (prop, FALSE); + r = cal_recur_from_icalproperty (prop, FALSE, zone); - tmp_occs = cal_obj_expand_recurrence (event_start, r, + tmp_occs = cal_obj_expand_recurrence (event_start, zone, r, chunk_start, chunk_end, &rule_finished); @@ -1112,16 +1151,30 @@ generate_instances_for_chunk (CalComponent *comp, /* Add on specific exception dates. */ for (elem = exdates; elem; elem = elem->next) { CalComponentDateTime *cdt; - time_t t; - - /* FIXME we should only be dealing with dates, not times too. - No, I think it is supposed to be dates & times - Damon. - I'm not sure what the semantics of just a date would be, - since the event could recur several times each day. */ cdt = elem->data; - t = icaltime_as_timet (*cdt->value); - cal_object_time_from_time (&cotime, t); + + /* FIXME: We currently assume EXDATEs are in the same timezone + as DTSTART. We should get the EXDATE timezone and convert + to the DTSTART timezone first. */ + cotime.year = cdt->value->year; + cotime.month = cdt->value->month - 1; + cotime.day = cdt->value->day; + + /* If the EXDATE has a DATE value, set the time to the start + of the day and set flags to TRUE so we know to skip all + occurrences on that date. */ + if (cdt->value->is_date) { + cotime.hour = 0; + cotime.minute = 0; + cotime.second = 0; + cotime.flags = TRUE; + } else { + cotime.hour = cdt->value->hour; + cotime.minute = cdt->value->minute; + cotime.second = cdt->value->second; + cotime.flags = FALSE; + } g_array_append_val (ex_occs, cotime); } @@ -1148,17 +1201,17 @@ generate_instances_for_chunk (CalComponent *comp, g_print ("Checking occurrence: %s\n", cal_obj_time_to_string (occ)); #endif - start_tm.tm_year = occ->year - 1900; - start_tm.tm_mon = occ->month; - start_tm.tm_mday = occ->day; - start_tm.tm_hour = occ->hour; - start_tm.tm_min = occ->minute; - start_tm.tm_sec = occ->second; - start_tm.tm_isdst = -1; - start_time = mktime (&start_tm); + start_tt.year = occ->year; + start_tt.month = occ->month + 1; + start_tt.day = occ->day; + start_tt.hour = occ->hour; + start_tt.minute = occ->minute; + start_tt.second = occ->second; + start_tt.is_daylight = -1; + start_time = icaltime_as_timet_with_zone (start_tt, zone); if (start_time == -1) { - g_warning ("mktime failed - time_t out of range?"); + g_warning ("time_t out of range"); finished = TRUE; break; } @@ -1177,7 +1230,10 @@ generate_instances_for_chunk (CalComponent *comp, continue; } - if (occ->is_rdate) { + if (occ->flags) { + /* If it is an RDATE, we see if the end date or + duration was set. If not, we use the same duration + as the original occurrence. */ if (!cal_object_get_rdate_end (occ, rdate_periods)) { cal_obj_time_add_days (occ, duration_days); cal_obj_time_add_seconds (occ, @@ -1188,17 +1244,17 @@ generate_instances_for_chunk (CalComponent *comp, cal_obj_time_add_seconds (occ, duration_seconds); } - end_tm.tm_year = occ->year - 1900; - end_tm.tm_mon = occ->month; - end_tm.tm_mday = occ->day; - end_tm.tm_hour = occ->hour; - end_tm.tm_min = occ->minute; - end_tm.tm_sec = occ->second; - end_tm.tm_isdst = -1; - end_time = mktime (&end_tm); + end_tt.year = occ->year; + end_tt.month = occ->month + 1; + end_tt.day = occ->day; + end_tt.hour = occ->hour; + end_tt.minute = occ->minute; + end_tt.second = occ->second; + end_tt.is_daylight = -1; + end_time = icaltime_as_timet_with_zone (end_tt, zone); if (end_time == -1) { - g_warning ("mktime failed - time_t out of range?"); + g_warning ("time_t out of range"); finished = TRUE; break; } @@ -1238,7 +1294,6 @@ cal_object_get_rdate_end (CalObjTime *occ, CalObjRecurrenceDate *rdate = NULL; CalComponentPeriod *p; gint lower, upper, middle, cmp = 0; - time_t t; lower = 0; upper = rdate_periods->len; @@ -1267,8 +1322,16 @@ cal_object_get_rdate_end (CalObjTime *occ, p = rdate->period; if (p->type == CAL_COMPONENT_PERIOD_DATETIME) { - t = icaltime_as_timet (p->u.end); - cal_object_time_from_time (occ, t); + /* FIXME: We currently assume RDATEs are in the same timezone + as DTSTART. We should get the RDATE timezone and convert + to the DTSTART timezone first. */ + occ->year = p->u.end.year; + occ->month = p->u.end.month - 1; + occ->day = p->u.end.day; + occ->hour = p->u.end.hour; + occ->minute = p->u.end.minute; + occ->second = p->u.end.second; + occ->flags = FALSE; } else { cal_obj_time_add_days (occ, p->u.duration.weeks * 7 + p->u.duration.days); @@ -1319,6 +1382,7 @@ cal_object_compute_duration (CalObjTime *start, after the given interval.*/ static GArray* cal_obj_expand_recurrence (CalObjTime *event_start, + icaltimezone *zone, CalRecurrence *recur, CalObjTime *interval_start, CalObjTime *interval_end, @@ -1346,7 +1410,7 @@ cal_obj_expand_recurrence (CalObjTime *event_start, /* Compute the event_end, if the recur's enddate is set. */ if (recur->enddate > 0) { cal_object_time_from_time (&event_end_cotime, - recur->enddate); + recur->enddate, zone); event_end = &event_end_cotime; /* If the enddate is before the requested interval return. */ @@ -1653,7 +1717,7 @@ static CalRecurVTable* cal_obj_get_vtable (icalrecurrencetype_frequency recur_ty vtable = &cal_obj_secondly_vtable; break; default: - g_warning ("Unknown recurrence frequenct"); + g_warning ("Unknown recurrence frequency"); vtable = NULL; } @@ -1836,15 +1900,15 @@ cal_obj_remove_exceptions (GArray *occs, && cal_obj_time_compare_func (occ, prev_occ) == 0) { keep_occ = FALSE; - /* If this occurrence is an RDATE, and the previous - occurrence in the array was kept, set the RDATE flag - of the last one, so we still use the end date - or duration. */ - if (occ->is_rdate && !current_time_is_exception) { + /* If this occurrence is an RDATE with an end or + duration set, and the previous occurrence in the + array was kept, set the RDATE flag of the last one, + so we still use the end date or duration. */ + if (occ->flags && !current_time_is_exception) { last_occ_kept = &g_array_index (occs, CalObjTime, j - 1); - last_occ_kept->is_rdate = TRUE; + last_occ_kept->flags = TRUE; } } else { /* We've found a new occurrence time. Reset the flag @@ -1857,9 +1921,14 @@ cal_obj_remove_exceptions (GArray *occs, to one that matches or follows this occurrence. */ while (ex_occ) { - cmp = cal_obj_time_compare_func (ex_occ, occ); - /* I'm pretty sure this is wrong. */ - /*cmp = cal_obj_date_only_compare_func (ex_occ, occ);*/ + /* If the exception is an EXDATE with + a DATE value, we only have to + compare the date. */ + if (ex_occ->flags) + cmp = cal_obj_date_only_compare_func (ex_occ, occ); + else + cmp = cal_obj_time_compare_func (ex_occ, occ); + if (cmp > 0) break; @@ -1886,6 +1955,10 @@ cal_obj_remove_exceptions (GArray *occs, } if (keep_occ) { + /* We are keeping this occurrence, so we move it to + the next free space, unless its position hasn't + changed (i.e. all previous occurrences were also + kept). */ if (i != j) g_array_index (occs, CalObjTime, j) = g_array_index (occs, CalObjTime, i); @@ -3465,7 +3538,7 @@ cal_obj_time_compare_func (const void *arg1, return retval; } -#if 0 + static gint cal_obj_date_only_compare_func (const void *arg1, const void *arg2) @@ -3492,7 +3565,7 @@ cal_obj_date_only_compare_func (const void *arg1, return 0; } -#endif + /* Returns the weekday of the given CalObjTime, from 0 (Mon) - 6 (Sun). */ static gint @@ -3590,23 +3663,21 @@ cal_obj_time_find_first_week (CalObjTime *cotime, static void -cal_object_time_from_time (CalObjTime *cotime, - time_t t) +cal_object_time_from_time (CalObjTime *cotime, + time_t t, + icaltimezone *zone) { - struct tm *tmp_tm; - time_t tmp_time_t; + struct icaltimetype tt; - tmp_time_t = t; - /* FIXME */ - tmp_tm = localtime (&tmp_time_t); + tt = icaltime_from_timet_with_zone (t, FALSE, zone); - cotime->year = tmp_tm->tm_year + 1900; - cotime->month = tmp_tm->tm_mon; - cotime->day = tmp_tm->tm_mday; - cotime->hour = tmp_tm->tm_hour; - cotime->minute = tmp_tm->tm_min; - cotime->second = tmp_tm->tm_sec; - cotime->is_rdate = FALSE; + cotime->year = tt.year; + cotime->month = tt.month - 1; + cotime->day = tt.day; + cotime->hour = tt.hour; + cotime->minute = tt.minute; + cotime->second = tt.second; + cotime->flags = FALSE; } @@ -3746,6 +3817,7 @@ cal_recur_get_rule_end_date (icalproperty *prop) const char *xname, *xvalue; icalvalue *value; struct icaltimetype icaltime; + icaltimezone *utc_zone; param = icalproperty_get_first_parameter (prop, ICAL_X_PARAMETER); while (param) { @@ -3758,7 +3830,9 @@ cal_recur_get_rule_end_date (icalproperty *prop) icaltime = icalvalue_get_datetime (value); icalvalue_free (value); - return icaltime_as_timet (icaltime); + utc_zone = icaltimezone_get_utc_timezone (); + return icaltime_as_timet_with_zone (icaltime, + utc_zone); } } @@ -3776,10 +3850,13 @@ cal_recur_set_rule_end_date (icalproperty *prop, { icalparameter *param; icalvalue *value; + icaltimezone *utc_zone; struct icaltimetype icaltime; const char *end_date_string, *xname; - icaltime = icaltime_from_timet (end_date, FALSE); + /* We save the value as a UTC DATE-TIME. */ + utc_zone = icaltimezone_get_utc_timezone (); + icaltime = icaltime_from_timet_with_zone (end_date, FALSE, utc_zone); value = icalvalue_new_datetime (icaltime); end_date_string = icalvalue_as_ical_string (value); icalvalue_free (value); diff --git a/calendar/cal-util/timeutil.c b/calendar/cal-util/timeutil.c index 3bb254dbf7..eb68376449 100644 --- a/calendar/cal-util/timeutil.c +++ b/calendar/cal-util/timeutil.c @@ -2,10 +2,10 @@ * * Copyright (C) 1998 The Free Software Foundation * Copyright (C) 2000 Ximian, Inc. - * Copyright (C) 2000 Ximian, Inc. * * Authors: Federico Mena <federico@ximian.com> * Miguel de Icaza <miguel@ximian.com> + * Damon Chaplin <damon@ximian.com> */ #include <string.h> @@ -16,108 +16,36 @@ -void -print_time_t (time_t t) -{ - struct tm *tm = localtime (&t); - - printf ("%d/%02d/%02d %02d:%02d:%02d", - 1900 + tm->tm_year, tm->tm_mon+1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); -} - -/** - * isodate_from_time_t: - * @t: A time value. - * - * Creates an ISO 8601 local time representation from a time value. - * - * Return value: String with the ISO 8601 representation of the local time. - **/ -char * -isodate_from_time_t (time_t t) -{ - struct tm *tm; - char isotime[40]; - - tm = gmtime (&t); - strftime (isotime, sizeof (isotime)-1, "%Y%m%dT%H%M%SZ", tm); - return g_strdup (isotime); -} - -/** - * time_from_isodate: - * @str: Date/time value in ISO 8601 format. - * - * Converts an ISO 8601 time string into a time_t value. - * - * Return value: Time_t corresponding to the specified ISO string. - * Note that we only allow UTC times at present. - **/ -time_t -time_from_isodate (const char *str) -{ - int len; - struct tm my_tm; - time_t t; - int i; - char *old_tz; - - g_return_val_if_fail (str != NULL, -1); - - /* yyyymmdd[Thhmmss[Z]] */ - - len = strlen (str); +#define REFORMATION_DAY 639787 /* First day of the reformation, counted from 1 Jan 1 */ +#define MISSING_DAYS 11 /* They corrected out 11 days */ +#define THURSDAY 4 /* First day of reformation */ +#define SATURDAY 6 /* Offset value; 1 Jan 1 was a Saturday */ - if (!(len == 8 || len == 15 || len == 16)) - return -1; - for (i = 0; i < len; i++) - if (!((i != 8 && i != 15 && isdigit (str[i])) - || (i == 8 && str[i] == 'T') - || (i == 15 && str[i] == 'Z'))) - return -1; - - memset (&my_tm, 0, sizeof (my_tm)); - -#define digit_at(x,y) (x[y] - '0') - - my_tm.tm_year = (digit_at (str, 0) * 1000 + digit_at (str, 1) * 100 + - digit_at (str, 2) * 10 + digit_at (str, 3)) - 1900; - - my_tm.tm_mon = digit_at (str, 4) * 10 + digit_at (str, 5) - 1; - my_tm.tm_mday = digit_at (str, 6) * 10 + digit_at (str, 7); - - if (len > 8) { - my_tm.tm_hour = digit_at (str, 9) * 10 + digit_at (str, 10); - my_tm.tm_min = digit_at (str, 11) * 10 + digit_at (str, 12); - my_tm.tm_sec = digit_at (str, 13) * 10 + digit_at (str, 14); - } +/* Number of days in a month, using 0 (Jan) to 11 (Dec). For leap years, + add 1 to February (month 1). */ +static const int days_in_month[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; - my_tm.tm_isdst = -1; - old_tz = set_tz ("UTC"); - t = mktime (&my_tm); - unset_tz (old_tz); - return t; -} +/************************************************************************** + * time_t manipulation functions. + * + * NOTE: these use the Unix timezone functions like mktime() and localtime() + * and so should not be used in Evolution. New Evolution code should use + * icaltimetype values rather than time_t values wherever possible. + **************************************************************************/ -time_t -time_add_minutes (time_t time, int minutes) +static void +print_time_t (time_t t) { - struct tm *tm = localtime (&time); - time_t new_time; - - tm->tm_min += minutes; - if ((new_time = mktime (tm)) == -1) { - g_message ("time_add_minutes(): mktime() could not handle " - "adding %d minutes with\n", minutes); - print_time_t (time); - printf ("\n"); - return time; - } - return new_time; + struct tm *tm = localtime (&t); + + printf ("%d/%02d/%02d %02d:%02d:%02d", + 1900 + tm->tm_year, tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); } /* Adds a day onto the time, using local time. @@ -132,9 +60,6 @@ time_add_day (time_t time, int days) { struct tm *tm = localtime (&time); time_t new_time; -#if 0 - int dst_flag = tm->tm_isdst; -#endif tm->tm_mday += days; tm->tm_isdst = -1; @@ -147,18 +72,6 @@ time_add_day (time_t time, int days) return time; } -#if 0 - /* I don't know what this is for. See also time_day_begin() and - time_day_end(). - Damon. */ - if (dst_flag > tm->tm_isdst) { - tm->tm_hour++; - new_time += 3600; - } else if (dst_flag < tm->tm_isdst) { - tm->tm_hour--; - new_time -= 3600; - } -#endif - return new_time; } @@ -197,63 +110,23 @@ time_add_month (time_t time, int months) } time_t -time_add_year (time_t time, int years) -{ - struct tm *tm = localtime (&time); - time_t new_time; - - tm->tm_year += years; - if ((new_time = mktime (tm)) == -1) { - g_message ("time_add_year(): mktime() could not handling adding %d years with\n", - years); - print_time_t (time); - printf ("\n"); - return time; - } - return new_time; -} - -/* Number of days in a month, for normal and leap years */ -static const int days_in_month[2][12] = { - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } -}; - -/* Returns whether the specified year is a leap year */ -static int -is_leap_year (int year) -{ - if (year <= 1752) - return !(year % 4); - else - return (!(year % 4) && (year % 100)) || !(year % 400); -} - -int -time_days_in_month (int year, int month) -{ - g_return_val_if_fail (year >= 1900, 0); - g_return_val_if_fail ((month >= 0) && (month < 12), 0); - - return days_in_month [is_leap_year (year)][month]; -} - -time_t -time_from_day (int year, int month, int day) +time_year_begin (time_t t) { struct tm tm; - memset (&tm, 0, sizeof (tm)); - tm.tm_year = year - 1900; - tm.tm_mon = month; - tm.tm_mday = day; + tm = *localtime (&t); + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_mon = 0; + tm.tm_mday = 1; tm.tm_isdst = -1; return mktime (&tm); } time_t -time_year_begin (time_t t) +time_month_begin (time_t t) { struct tm tm; @@ -261,32 +134,37 @@ time_year_begin (time_t t) tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; - tm.tm_mon = 0; tm.tm_mday = 1; tm.tm_isdst = -1; return mktime (&tm); } +/* Returns the start of the week. week_start_day should use the same values + as mktime(), i.e. 0 (Sun) to 6 (Sat). */ time_t -time_year_end (time_t t) +time_week_begin (time_t t, int week_start_day) { struct tm tm; + int offset; tm = *localtime (&t); + + /* Calculate the current offset from the week start day. */ + offset = (tm.tm_wday + 7 - week_start_day) % 7; + tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; - tm.tm_mon = 0; - tm.tm_mday = 1; - tm.tm_year++; + tm.tm_mday -= offset; tm.tm_isdst = -1; return mktime (&tm); } +/* Returns the start of the day, according to the local time. */ time_t -time_month_begin (time_t t) +time_day_begin (time_t t) { struct tm tm; @@ -294,99 +172,397 @@ time_month_begin (time_t t) tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; - tm.tm_mday = 1; tm.tm_isdst = -1; return mktime (&tm); } +/* Returns the end of the day, according to the local time. */ time_t -time_month_end (time_t t) +time_day_end (time_t t) { struct tm tm; tm = *localtime (&t); + tm.tm_mday++; tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; - tm.tm_mday = 1; - tm.tm_mon++; tm.tm_isdst = -1; return mktime (&tm); } -/* Returns the start of the week. week_start_day should use the same values - as mktime(), i.e. 0 (Sun) to 6 (Sat). */ + +/************************************************************************** + * time_t manipulation functions, using timezones in libical. + * + * NOTE: these are only here to make the transition to the timezone + * functions easier. New code should use icaltimetype values rather than + * time_t values wherever possible. + **************************************************************************/ + + +/* Adds or subtracts a number of days to/from the given time_t value, using + the given timezone. + NOTE: this function is only here to make the transition to the timezone + functions easier. New code should use icaltimetype values and + icaltime_adjust() to add or subtract days, hours, minutes & seconds. */ time_t -time_week_begin (time_t t, int week_start_day) +time_add_day_with_zone (time_t time, int days, icaltimezone *zone) { - struct tm tm; - int offset; + struct icaltimetype tt; - tm = *localtime (&t); + /* Convert to an icaltimetype. */ + tt = icaltime_from_timet_with_zone (time, FALSE, zone); - /* Calculate the current offset from the week start day. */ - offset = (tm.tm_wday + 7 - week_start_day) % 7; + /* Add/subtract the number of days. */ + icaltime_adjust (&tt, days, 0, 0, 0); - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - tm.tm_mday -= offset; - tm.tm_isdst = -1; + /* Convert back to a time_t. */ + return icaltime_as_timet_with_zone (tt, zone); +} - return mktime (&tm); + +/* Adds or subtracts a number of weeks to/from the given time_t value, using + the given timezone. + NOTE: this function is only here to make the transition to the timezone + functions easier. New code should use icaltimetype values and + icaltime_adjust() to add or subtract days, hours, minutes & seconds. */ +time_t +time_add_week_with_zone (time_t time, int weeks, icaltimezone *zone) +{ + return time_add_day_with_zone (time, weeks * 7, zone); } -/* Returns the end of the week. week_start_day should use the same values - as mktime(), i.e. 0 (Sun) to 6 (Sat). */ + +/* Adds or subtracts a number of months to/from the given time_t value, using + the given timezone. + NOTE: this function is only here to make the transition to the timezone + functions easier. New code should use icaltimetype values and + icaltime_adjust() to add or subtract days, hours, minutes & seconds. */ time_t -time_week_end (time_t t, int week_start_day) +time_add_month_with_zone (time_t time, int months, icaltimezone *zone) { - struct tm tm; - int offset; + struct icaltimetype tt; - tm = *localtime (&t); + /* Convert to an icaltimetype. */ + tt = icaltime_from_timet_with_zone (time, FALSE, zone); + + /* Add on the number of months. */ + tt.month += months; + + /* Normalize it, fixing any overflow. */ + tt = icaltime_normalize (tt); + + /* Convert back to a time_t. */ + return icaltime_as_timet_with_zone (tt, zone); +} + + +/* Returns the start of the year containing the given time_t, using the given + timezone. + NOTE: this function is only here to make the transition to the timezone + functions easier. New code should use icaltimetype values and + icaltime_adjust() to add or subtract days, hours, minutes & seconds. */ +time_t +time_year_begin_with_zone (time_t time, icaltimezone *zone) +{ + struct icaltimetype tt; + + /* Convert to an icaltimetype. */ + tt = icaltime_from_timet_with_zone (time, FALSE, zone); + + /* Set it to the start of the year. */ + tt.month = 1; + tt.day = 1; + tt.hour = 0; + tt.minute = 0; + tt.second = 0; + tt.is_daylight = -1; + + /* Convert back to a time_t. */ + return icaltime_as_timet_with_zone (tt, zone); +} + + +/* Returns the start of the month containing the given time_t, using the given + timezone. + NOTE: this function is only here to make the transition to the timezone + functions easier. New code should use icaltimetype values and + icaltime_adjust() to add or subtract days, hours, minutes & seconds. */ +time_t +time_month_begin_with_zone (time_t time, icaltimezone *zone) +{ + struct icaltimetype tt; + + /* Convert to an icaltimetype. */ + tt = icaltime_from_timet_with_zone (time, FALSE, zone); + + /* Set it to the start of the month. */ + tt.day = 1; + tt.hour = 0; + tt.minute = 0; + tt.second = 0; + tt.is_daylight = -1; + + /* Convert back to a time_t. */ + return icaltime_as_timet_with_zone (tt, zone); +} + + +/* Returns the start of the week containing the given time_t, using the given + timezone. week_start_day should use the same values as mktime(), + i.e. 0 (Sun) to 6 (Sat). + NOTE: this function is only here to make the transition to the timezone + functions easier. New code should use icaltimetype values and + icaltime_adjust() to add or subtract days, hours, minutes & seconds. */ +time_t +time_week_begin_with_zone (time_t time, int week_start_day, icaltimezone *zone) +{ + struct icaltimetype tt; + int weekday, offset; + + /* Convert to an icaltimetype. */ + tt = icaltime_from_timet_with_zone (time, FALSE, zone); + + /* Get the weekday. */ + weekday = time_day_of_week (tt.day, tt.month - 1, tt.year); /* Calculate the current offset from the week start day. */ - offset = (tm.tm_wday + 7 - week_start_day) % 7; + offset = (weekday + 7 - week_start_day) % 7; - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - tm.tm_mday += 7 - offset; - tm.tm_isdst = -1; + /* Set it to the start of the month. */ + tt.day -= offset; + tt.hour = 0; + tt.minute = 0; + tt.second = 0; + tt.is_daylight = -1; - return mktime (&tm); + /* Normalize it, to fix any overflow. */ + tt = icaltime_normalize (tt); + + /* Convert back to a time_t. */ + return icaltime_as_timet_with_zone (tt, zone); } -/* Returns the start of the day, according to the local time. */ + +/* Returns the start of the day containing the given time_t, using the given + timezone. + NOTE: this function is only here to make the transition to the timezone + functions easier. New code should use icaltimetype values and + icaltime_adjust() to add or subtract days, hours, minutes & seconds. */ time_t -time_day_begin (time_t t) +time_day_begin_with_zone (time_t time, icaltimezone *zone) { - struct tm tm; + struct icaltimetype tt; - tm = *localtime (&t); - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - tm.tm_isdst = -1; + /* Convert to an icaltimetype. */ + tt = icaltime_from_timet_with_zone (time, FALSE, zone); - return mktime (&tm); + /* Set it to the start of the day. */ + tt.hour = 0; + tt.minute = 0; + tt.second = 0; + tt.is_daylight = -1; + + /* Convert back to a time_t. */ + return icaltime_as_timet_with_zone (tt, zone); } -/* Returns the end of the day, according to the local time. */ + +/* Returns the end of the day containing the given time_t, using the given + timezone. (The end of the day is the start of the next day.) + NOTE: this function is only here to make the transition to the timezone + functions easier. New code should use icaltimetype values and + icaltime_adjust() to add or subtract days, hours, minutes & seconds. */ time_t -time_day_end (time_t t) +time_day_end_with_zone (time_t time, icaltimezone *zone) { - struct tm tm; + struct icaltimetype tt; - tm = *localtime (&t); - tm.tm_mday++; - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - tm.tm_isdst = -1; + /* Convert to an icaltimetype. */ + tt = icaltime_from_timet_with_zone (time, FALSE, zone); - return mktime (&tm); + /* Set it to the start of the next day. */ + tt.day++; + tt.hour = 0; + tt.minute = 0; + tt.second = 0; + tt.is_daylight = -1; + + /* Normalize it, to fix any overflow. */ + tt = icaltime_normalize (tt); + + /* Convert back to a time_t. */ + return icaltime_as_timet_with_zone (tt, zone); } + + +/************************************************************************** + * General time functions. + **************************************************************************/ + + +/* Returns the number of days in the month. Year is the normal year, e.g. 2001. + Month is 0 (Jan) to 11 (Dec). */ +int +time_days_in_month (int year, int month) +{ + int days; + + g_return_val_if_fail (year >= 1900, 0); + g_return_val_if_fail ((month >= 0) && (month < 12), 0); + + days = days_in_month[month]; + if (month == 1 && time_is_leap_year (year)) + days++; + + return days; +} + + +/* Returns the 1-based day number within the year of the specified date. + Year is the normal year, e.g. 2001. Month is 0 to 11. */ +int +time_day_of_year (int day, int month, int year) +{ + int i; + + for (i = 0; i < month; i++) { + day += days_in_month[i]; + + if (month == 1 && time_is_leap_year (year)) + day++; + } + + return day; +} + + +/* Returns the day of the week for the specified date, 0 (Sun) to 6 (Sat). + For the days that were removed on the Gregorian reformation, it returns + Thursday. Year is the normal year, e.g. 2001. Month is 0 to 11. */ +int +time_day_of_week (int day, int month, int year) +{ + int n; + + n = (year - 1) * 365 + time_leap_years_up_to (year - 1) + + time_day_of_year (day, month, year); + + if (n < REFORMATION_DAY) + return (n - 1 + SATURDAY) % 7; + + if (n >= (REFORMATION_DAY + MISSING_DAYS)) + return (n - 1 + SATURDAY - MISSING_DAYS) % 7; + + return THURSDAY; +} + + +/* Returns whether the specified year is a leap year. Year is the normal year, + e.g. 2001. */ +gboolean +time_is_leap_year (int year) +{ + if (year <= 1752) + return !(year % 4); + else + return (!(year % 4) && (year % 100)) || !(year % 400); +} + + +/* Returns the number of leap years since year 1 up to (but not including) the + specified year. Year is the normal year, e.g. 2001. */ +int +time_leap_years_up_to (int year) +{ + /* There is normally a leap year every 4 years, except at the turn of + centuries since 1700. But there is a leap year on centuries since 1700 + which are divisible by 400. */ + return (year / 4 + - ((year > 1700) ? (year / 100 - 17) : 0) + + ((year > 1600) ? ((year - 1600) / 400) : 0)); +} + + +/** + * isodate_from_time_t: + * @t: A time value. + * + * Creates an ISO 8601 UTC representation from a time value. + * + * Return value: String with the ISO 8601 representation of the UTC time. + **/ +char * +isodate_from_time_t (time_t t) +{ + struct tm *tm; + char isotime[40]; + + tm = gmtime (&t); + strftime (isotime, sizeof (isotime)-1, "%Y%m%dT%H%M%SZ", tm); + return g_strdup (isotime); +} + +/** + * time_from_isodate: + * @str: Date/time value in ISO 8601 format. + * + * Converts an ISO 8601 UTC time string into a time_t value. + * + * Return value: Time_t corresponding to the specified ISO string. + * Note that we only allow UTC times at present. + **/ +time_t +time_from_isodate (const char *str) +{ + struct icaltimetype tt = icaltime_null_time (); + icaltimezone *utc_zone; + int len, i; + + g_return_val_if_fail (str != NULL, -1); + + /* yyyymmdd[Thhmmss[Z]] */ + + len = strlen (str); + + if (!(len == 8 || len == 15 || len == 16)) + return -1; + + for (i = 0; i < len; i++) + if (!((i != 8 && i != 15 && isdigit (str[i])) + || (i == 8 && str[i] == 'T') + || (i == 15 && str[i] == 'Z'))) + return -1; + +#define digit_at(x,y) (x[y] - '0') + + tt.year = digit_at (str, 0) * 1000 + + digit_at (str, 1) * 100 + + digit_at (str, 2) * 10 + + digit_at (str, 3); + + tt.month = digit_at (str, 4) * 10 + + digit_at (str, 5); + + tt.day = digit_at (str, 6) * 10 + + digit_at (str, 7); + + if (len > 8) { + tt.hour = digit_at (str, 9) * 10 + + digit_at (str, 10); + tt.minute = digit_at (str, 11) * 10 + + digit_at (str, 12); + tt.second = digit_at (str, 13) * 10 + + digit_at (str, 14); + } + + tt.is_daylight = -1; + + utc_zone = icaltimezone_get_utc_timezone (); + + return icaltime_as_timet_with_zone (tt, utc_zone); +} + diff --git a/calendar/cal-util/timeutil.h b/calendar/cal-util/timeutil.h index b01747ecfc..3679173572 100644 --- a/calendar/cal-util/timeutil.h +++ b/calendar/cal-util/timeutil.h @@ -2,10 +2,10 @@ * * Copyright (C) 1998 The Free Software Foundation * Copyright (C) 2000 Ximian, Inc. - * Copyright (C) 2000 Ximian, Inc. * * Authors: Federico Mena <federico@ximian.com> * Miguel de Icaza <miguel@ximian.com> + * Damon Chaplin <damon@ximian.com> */ #ifndef TIMEUTIL_H @@ -14,52 +14,107 @@ #include <time.h> #include <ical.h> +#include <glib.h> -char *isodate_from_time_t (time_t t); -time_t time_from_isodate (const char *str); +/************************************************************************** + * General time functions. + **************************************************************************/ -time_t time_add_minutes (time_t time, int minutes); -time_t time_add_day (time_t time, int days); -time_t time_add_week (time_t time, int weeks); -time_t time_add_month (time_t time, int months); -time_t time_add_year (time_t time, int years); +/* Returns the number of days in the month. Year is the normal year, e.g. 2001. + Month is 0 (Jan) to 11 (Dec). */ +int time_days_in_month (int year, int month); +/* Returns the 1-based day number within the year of the specified date. + Year is the normal year, e.g. 2001. Month is 0 to 11. */ +int time_day_of_year (int day, int month, int year); -/* Returns the number of days in the specified month. Years are full years - (starting from year 1). Months are in [0, 11]. */ -int time_days_in_month (int year, int month); +/* Returns the day of the week for the specified date, 0 (Sun) to 6 (Sat). + For the days that were removed on the Gregorian reformation, it returns + Thursday. Year is the normal year, e.g. 2001. Month is 0 to 11. */ +int time_day_of_week (int day, int month, int year); -/* Converts the specified date to a time_t at the start of the specified day. - Years are full years (starting from year 1). Months are in [0, 11]. - Days are 1-based. */ -time_t time_from_day (int year, int month, int day); +/* Returns whether the specified year is a leap year. Year is the normal year, + e.g. 2001. */ +gboolean time_is_leap_year (int year); -/* For the functions below, time ranges are considered to contain the start - time, but not the end time. */ +/* Returns the number of leap years since year 1 up to (but not including) the + specified year. Year is the normal year, e.g. 2001. */ +int time_leap_years_up_to (int year); -/* These two functions take a time value and return the beginning or end of - the corresponding year, respectively. */ -time_t time_year_begin (time_t t); -time_t time_year_end (time_t t); +/* Convert to or from an ISO 8601 representation of a time, in UTC, + e.g. "20010708T183000Z". */ +char *isodate_from_time_t (time_t t); +time_t time_from_isodate (const char *str); -/* These two functions take a time value and return the beginning or end of - the corresponding month, respectively. */ -time_t time_month_begin (time_t t); -time_t time_month_end (time_t t); -/* These functions take a time value and return the beginning or end of the - corresponding week, respectively. week_start_day should use the same values +/************************************************************************** + * time_t manipulation functions. + * + * NOTE: these use the Unix timezone functions like mktime() and localtime() + * and so should not be used in Evolution. New Evolution code should use + * icaltimetype values rather than time_t values wherever possible. + **************************************************************************/ + +/* Add or subtract a number of days, weeks or months. */ +time_t time_add_day (time_t time, int days); +time_t time_add_week (time_t time, int weeks); +time_t time_add_month (time_t time, int months); + +/* Returns the beginning of the year or month. */ +time_t time_year_begin (time_t t); +time_t time_month_begin (time_t t); + +/* Returns the beginning of the week. week_start_day should use the same values as mktime(), i.e. 0 (Sun) to 6 (Sat). */ -time_t time_week_begin (time_t t, int week_start_day); -time_t time_week_end (time_t t, int week_start_day); +time_t time_week_begin (time_t t, int week_start_day); -/* These two functions take a time value and return the beginning or end of - the corresponding day, respectively. */ -time_t time_day_begin (time_t t); -time_t time_day_end (time_t t); +/* Returns the beginning or end of the day. */ +time_t time_day_begin (time_t t); +time_t time_day_end (time_t t); -void print_time_t (time_t t); + +/************************************************************************** + * time_t manipulation functions, using timezones in libical. + * + * NOTE: these are only here to make the transition to the timezone + * functions easier. New code should use icaltimetype values rather than + * time_t values wherever possible. + **************************************************************************/ + +/* Adds or subtracts a number of days to/from the given time_t value, using + the given timezone. */ +time_t time_add_day_with_zone (time_t time, int days, icaltimezone *zone); + +/* Adds or subtracts a number of weeks to/from the given time_t value, using + the given timezone. */ +time_t time_add_week_with_zone (time_t time, int weeks, icaltimezone *zone); + +/* Adds or subtracts a number of months to/from the given time_t value, using + the given timezone. */ +time_t time_add_month_with_zone (time_t time, int months, icaltimezone *zone); + +/* Returns the start of the year containing the given time_t, using the given + timezone. */ +time_t time_year_begin_with_zone (time_t time, icaltimezone *zone); + +/* Returns the start of the month containing the given time_t, using the given + timezone. */ +time_t time_month_begin_with_zone (time_t time, icaltimezone *zone); + +/* Returns the start of the week containing the given time_t, using the given + timezone. week_start_day should use the same values as mktime(), + i.e. 0 (Sun) to 6 (Sat). */ +time_t time_week_begin_with_zone (time_t time, int week_start_day, + icaltimezone *zone); + +/* Returns the start of the day containing the given time_t, using the given + timezone. */ +time_t time_day_begin_with_zone (time_t time, icaltimezone *zone); + +/* Returns the end of the day containing the given time_t, using the given + timezone. (The end of the day is the start of the next day.) */ +time_t time_day_end_with_zone (time_t time, icaltimezone *zone); #endif |