aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/cal-util/timeutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'calendar/cal-util/timeutil.c')
-rw-r--r--calendar/cal-util/timeutil.c606
1 files changed, 391 insertions, 215 deletions
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);
+}
+