From 0e6d346872289d1ee71cb7b1092b5229b11dab3e Mon Sep 17 00:00:00 2001 From: Damon Chaplin Date: Wed, 11 Jul 2001 03:56:03 +0000 Subject: more timezone updates. I'm pretty much done with the calendar code now, 2001-07-10 Damon Chaplin * gui/calendar-model.c: * gui/e-calendar-table.c: * gui/e-day-view-main-item.c: * gui/e-day-view-top-item.c: * gui/e-day-view.[hc]: * gui/e-week-view.c: * gui/gnome-cal.c: * gui/print.c: * gui/dialogs/cal-prefs-dialog.c: * gui/dialogs/comp-editor-util.c: * gui/dialogs/event-page.c: * pcs/cal-backend-file.c: * pcs/query.c: * cal-util/cal-component.[hc]: * cal-util/cal-recur.c: * cal-util/timeutil.[hc]: * cal-client/cal-client.[hc]: more timezone updates. I'm pretty much done with the calendar code now, except for alarms and conduits, which Federico and JP know more about. And there are a couple of other minor things to fix. But it is still pretty buggy. svn path=/trunk/; revision=10984 --- calendar/cal-util/timeutil.c | 606 ++++++++++++++++++++++++++++--------------- 1 file changed, 391 insertions(+), 215 deletions(-) (limited to 'calendar/cal-util/timeutil.c') 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 * Miguel de Icaza + * Damon Chaplin */ #include @@ -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); +} + -- cgit v1.2.3