summaryrefslogtreecommitdiffabout
path: root/libical/src/libical/icaltime.c
Side-by-side diff
Diffstat (limited to 'libical/src/libical/icaltime.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libical/src/libical/icaltime.c1116
1 files changed, 777 insertions, 339 deletions
diff --git a/libical/src/libical/icaltime.c b/libical/src/libical/icaltime.c
index a04ca04..3aac74e 100644
--- a/libical/src/libical/icaltime.c
+++ b/libical/src/libical/icaltime.c
@@ -26,7 +26,7 @@
======================================================================*/
#ifdef HAVE_CONFIG_H
-#include <config.h>
+#include "config.h"
#endif
#include "icaltime.h"
@@ -34,140 +34,223 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+#include <time.h>
-int snprintf(char *str, size_t n, char const *fmt, ...);
+#include "astime.h" /* Julian data handling routines */
-#ifdef ICAL_NO_LIBICAL
-#define icalerror_set_errno(x)
-#define icalerror_check_arg_rv(x,y)
-#define icalerror_check_arg_re(x,y,z)
-#else
#include "icalerror.h"
#include "icalmemory.h"
+
+#include "icaltimezone.h"
+#include "icalvalue.h"
+
+#ifdef WIN32
+#include <Windows.h>
+#define ERROR error
+#define snprintf _snprintf
+#define strcasecmp stricmp
#endif
+/*
+ * Function to convert a struct tm time specification
+ * to an ANSI time_t using the specified time zone.
+ * This is different from the standard mktime() function
+ * in that we dont want the automatic adjustments for
+ * local daylight savings time applied to the result.
+ * This function expects well-formed input.
+ */
+static time_t make_time(struct tm *tm, int tzm)
+{
+ time_t tim;
+ static int days[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 };
+ /* check that year specification within range */
-struct icaltimetype
-icaltime_from_timet(time_t tm, int is_date)
-{
- struct icaltimetype tt = icaltime_null_time();
- struct tm t;
+ if (tm->tm_year < 70 || tm->tm_year > 138)
+ return((time_t) -1);
- t = *(gmtime(&tm));
-
- if(is_date == 0){
- tt.second = t.tm_sec;
- tt.minute = t.tm_min;
- tt.hour = t.tm_hour;
- } else {
- tt.second = tt.minute =tt.hour = 0 ;
- }
+ /* check that month specification within range */
- tt.day = t.tm_mday;
- tt.month = t.tm_mon + 1;
- tt.year = t.tm_year+ 1900;
-
- tt.is_utc = 1;
- tt.is_date = is_date;
+ if (tm->tm_mon < 0 || tm->tm_mon > 11)
+ return((time_t) -1);
- return tt;
-}
+ /* check for upper bound of Jan 17, 2038 (to avoid possibility of
+ 32-bit arithmetic overflow) */
+
+ if (tm->tm_year == 138) {
+ if (tm->tm_mon > 0)
+ return((time_t) -1);
+ else if (tm->tm_mday > 17)
+ return((time_t) -1);
+ }
-/* Structure used by set_tz to hold an old value of TZ, and the new
- value, which is in memory we will have to free in unset_tz */
-struct set_tz_save {char* orig_tzid; char* new_env_str;};
+ /*
+ * calculate elapsed days since start of the epoch (midnight Jan
+ * 1st, 1970 UTC) 17 = number of leap years between 1900 and 1970
+ * (number of leap days to subtract)
+ */
-/* Temporarily change the TZ environmental variable. */
-struct set_tz_save set_tz(const char* tzid)
-{
+ tim = (tm->tm_year - 70) * 365 + ((tm->tm_year - 1) / 4) - 17;
- char *orig_tzid = 0;
- char *new_env_str;
- struct set_tz_save savetz;
- size_t tmp_sz;
+ /* add number of days elapsed in the current year */
- savetz.orig_tzid = 0;
- savetz.new_env_str = 0;
+ tim += days[tm->tm_mon];
- if(getenv("TZ") != 0){
- orig_tzid = (char*)icalmemory_strdup(getenv("TZ"));
+ /* check and adjust for leap years (the leap year check only valid
+ during the 32-bit era */
- if(orig_tzid == 0){
- icalerror_set_errno(ICAL_NEWFAILED_ERROR);
- return savetz;
- }
- }
+ if ((tm->tm_year & 3) == 0 && tm->tm_mon > 1)
+ tim += 1;
- tmp_sz =strlen(tzid)+4;
- new_env_str = (char*)malloc(tmp_sz);
+ /* elapsed days to current date */
- if(new_env_str == 0){
- icalerror_set_errno(ICAL_NEWFAILED_ERROR);
- return savetz;
- }
-
- /* Copy the TZid into a string with the form that putenv expects. */
- strcpy(new_env_str,"TZ=");
- strcpy(new_env_str+3,tzid);
+ tim += tm->tm_mday;
- putenv(new_env_str);
- /* Old value of TZ and the string we will have to free later */
- savetz.orig_tzid = orig_tzid;
- savetz.new_env_str = new_env_str;
- return savetz;
+ /* calculate elapsed hours since start of the epoch */
+
+ tim = tim * 24 + tm->tm_hour;
+
+ /* calculate elapsed minutes since start of the epoch */
+
+ tim = tim * 60 + tm->tm_min;
+
+ /* adjust per time zone specification */
+
+ tim -= tzm;
+
+ /* calculate elapsed seconds since start of the epoch */
+
+ tim = tim * 60 + tm->tm_sec;
+
+ /* return number of seconds since start of the epoch */
+
+ return(tim);
}
-void unset_tz(struct set_tz_save savetz)
+/** @brief Constructor (deprecated).
+ *
+ * Convert seconds past UNIX epoch to a timetype.
+ *
+ * @deprecated This constructor is deprecated and shouldn't be used in
+ * new software. Use icaltime_from_timet_with_zone(time_t, int,
+ * icaltimezone *) instead. In the meantime, calls to this method
+ * return a floating time, which can always be converted to a local
+ * time with an appropriate call to icaltime_convert_to_zone().
+ */
+
+struct icaltimetype
+icaltime_from_timet(const time_t tm, const int is_date)
{
- /* restore the original TZ environment */
+#ifndef NO_WARN_DEPRECATED
+ icalerror_warn("icaltime_from_timet() is DEPRECATED, use icaltime_from_timet_with_zone() instead");
+#endif
- char* orig_tzid = savetz.orig_tzid;
+ return icaltime_from_timet_with_zone(tm, is_date, 0);
+}
- if(orig_tzid!=0){
- size_t tmp_sz =strlen(orig_tzid)+4;
- char* orig_env_str = (char*)icalmemory_tmp_buffer(tmp_sz);
- if(orig_env_str == 0){
- icalerror_set_errno(ICAL_NEWFAILED_ERROR);
- return;
- }
-
- strcpy(orig_env_str,"TZ=");
- strcpy(orig_env_str+3,orig_tzid);
-#ifndef _WIN32
- setenv("TZ", savetz.orig_tzid,1 );
-#else
- putenv("TZ=MEZ");//, savetz.orig_tzid );
-#endif
- free(orig_tzid);
- } else {
-#ifdef __FreeBSD__
- unsetenv("TZ");
+/** @brief Constructor.
+ *
+ * @param tm The time
+ * @param is_date Boolean: 1 means we should treat tm as a DATE
+ * @param zone The timezone tm is in, NULL means to treat tm as a
+ * floating time
+ *
+ * Return a new icaltime instance, initialized to the given time
+ * expressed as seconds past UNIX epoch, optionally using the given
+ * timezone.
+ *
+ * If the caller specifies the is_date param as TRUE, the returned
+ * object is of DATE type, otherwise the input is meant to be of
+ * DATE-TIME type.
+ * If the zone is not specified (NULL zone param) the time is taken
+ * to be floating, that is, valid in any timezone. Note that, in
+ * addition to the uses specified in [RFC2445], this can be used
+ * when doing simple math on couples of times.
+ * If the zone is specified (UTC or otherwise), it's stored in the
+ * object and it's used as the native timezone for this object.
+ * This means that the caller can convert this time to a different
+ * target timezone with no need to store the source timezone.
+ *
+ */
+struct icaltimetype
+icaltime_from_timet_with_zone(const time_t tm, const int is_date,
+ const icaltimezone *zone)
+{
+ struct icaltimetype tt = icaltime_null_time();
+ struct tm t;
+ icaltimezone *utc_zone;
+
+ /* Convert the time_t to a struct tm. We can trust gmtime for this. */
+#ifdef HAVE_GMTIME_R
+ gmtime_r(&tm, &t);
#else
- putenv("TZ"); /* Delete from environment */
+ {
+ struct tm *t_ptr = gmtime(&tm);
+ t = *t_ptr;
+ }
#endif
- }
- if(savetz.new_env_str != 0){
- free(savetz.new_env_str);
+ tt.year = t.tm_year + 1900;
+ tt.month = t.tm_mon + 1;
+ tt.day = t.tm_mday;
+
+ if (is_date) {
+ tt.is_date = 1;
+ return tt;
}
+
+ tt.hour = t.tm_hour;
+ tt.minute = t.tm_min;
+ tt.second = t.tm_sec;
+
+ /* If it's a floating time, we don't do any conversion. */
+ if (zone == NULL) {
+ return tt;
+ }
+
+ utc_zone = icaltimezone_get_utc_timezone ();
+ tt.is_utc = (zone == utc_zone) ? 1 : 0;
+ tt.zone = zone;
+
+ return tt;
}
+/** @brief Convenience constructor.
+ *
+ * Returns the current time in the given timezone, as an icaltimetype.
+ */
+struct icaltimetype icaltime_current_time_with_zone(const icaltimezone *zone)
+{
+ return icaltime_from_timet_with_zone (time (NULL), 0, zone);
+}
-time_t icaltime_as_timet(struct icaltimetype tt)
+/** @brief Convenience constructor.
+ *
+ * Returns the current day as an icaltimetype, with is_date set.
+ */
+struct icaltimetype icaltime_today(void)
+{
+ return icaltime_from_timet_with_zone (time (NULL), 1, NULL);
+}
+
+/** @brief Return the time as seconds past the UNIX epoch
+ */
+time_t icaltime_as_timet(const struct icaltimetype tt)
{
struct tm stm;
time_t t;
- memset(&stm,0,sizeof( struct tm));
-
- if(icaltime_is_null_time(tt)) {
+ /* If the time is the special null time, return 0. */
+ if (icaltime_is_null_time(tt)) {
return 0;
}
+ /* Copy the icaltimetype to a struct tm. */
+ memset (&stm, 0, sizeof (struct tm));
+
stm.tm_sec = tt.second;
stm.tm_min = tt.minute;
stm.tm_hour = tt.hour;
@@ -176,19 +259,57 @@ time_t icaltime_as_timet(struct icaltimetype tt)
stm.tm_year = tt.year-1900;
stm.tm_isdst = -1;
- if(tt.is_utc == 1 || tt.is_date == 1){
- struct set_tz_save old_tz = set_tz("UTC");
- t = mktime(&stm);
- unset_tz(old_tz);
- } else {
- t = mktime(&stm);
- }
+ t = make_time(&stm, 0);
return t;
}
-char* icaltime_as_ical_string(struct icaltimetype tt)
+/** @brief Return the time as seconds past the UNIX epoch, using the
+ * given timezone.
+ *
+ * This convenience method combines a call to icaltime_convert() with
+ * a call to icaltime_as_timet().
+ * If the input timezone is null, no conversion is done; that is, the
+ * time is simply returned as time_t in its native timezone.
+ */
+time_t icaltime_as_timet_with_zone(const struct icaltimetype _tt,
+ const icaltimezone *zone)
+{
+ struct icaltimetype tt = _tt;
+ struct tm stm;
+ time_t t;
+
+ /* If the time is the special null time, return 0. */
+ if (icaltime_is_null_time(tt)) {
+ return 0;
+ }
+
+ if (zone != NULL) {
+ tt = icaltime_convert_to_zone(_tt, zone);
+ }
+
+ /* Copy the icaltimetype to a struct tm. */
+ memset (&stm, 0, sizeof (struct tm));
+
+ stm.tm_sec = tt.second;
+ stm.tm_min = tt.minute;
+ stm.tm_hour = tt.hour;
+ stm.tm_mday = tt.day;
+ stm.tm_mon = tt.month-1;
+ stm.tm_year = tt.year-1900;
+ stm.tm_isdst = -1;
+
+ t = make_time(&stm, 0);
+
+ return t;
+}
+
+/**
+ * Return a string represention of the time, in RFC2445 format. The
+ * string is owned by libical
+ */
+const char* icaltime_as_ical_string(const struct icaltimetype tt)
{
size_t size = 17;
char* buf = icalmemory_new_buffer(size);
@@ -212,107 +333,33 @@ char* icaltime_as_ical_string(struct icaltimetype tt)
}
-
-/* convert tt, of timezone tzid, into a utc time */
-struct icaltimetype icaltime_as_utc(struct icaltimetype tt,const char* tzid)
-{
- int tzid_offset;
-
- if(tt.is_utc == 1 || tt.is_date == 1){
- return tt;
- }
-
- tzid_offset = icaltime_utc_offset(tt,tzid);
-
- tt.second -= tzid_offset;
-
- tt.is_utc = 1;
-
- return icaltime_normalize(tt);
-}
-
-/* convert tt, a time in UTC, into a time in timezone tzid */
-struct icaltimetype icaltime_as_zone(struct icaltimetype tt,const char* tzid)
+/**
+ * Reset all of the time components to be in their normal ranges. For
+ * instance, given a time with minutes=70, the minutes will be reduces
+ * to 10, and the hour incremented. This allows the caller to do
+ * arithmetic on times without worrying about overflow or
+ * underflow.
+ *
+ * Implementation note: we call icaltime_adjust() with no adjustment.
+ */
+struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
{
- int tzid_offset;
-
- tzid_offset = icaltime_utc_offset(tt,tzid);
-
- tt.second += tzid_offset;
-
- tt.is_utc = 0;
-
- return icaltime_normalize(tt);
-
-}
-
-
-/* Return the offset of the named zone as seconds. tt is a time
- indicating the date for which you want the offset */
-int icaltime_utc_offset(struct icaltimetype ictt, const char* tzid)
-{
-
- time_t tt = icaltime_as_timet(ictt);
- time_t offset_tt;
- struct tm gtm;
- struct set_tz_save old_tz;
-
- if(tzid != 0){
- old_tz = set_tz(tzid);
- }
-
- /* Mis-interpret a UTC broken out time as local time */
- gtm = *(gmtime(&tt));
- gtm.tm_isdst = localtime(&tt)->tm_isdst;
- offset_tt = mktime(&gtm);
-
- if(tzid != 0){
- unset_tz(old_tz);
- }
-
- return tt-offset_tt;
-}
-
-
-
-/* Normalize by converting from localtime to utc and back to local
- time. This uses localtime because localtime and mktime are inverses
- of each other */
-
-struct icaltimetype icaltime_normalize(struct icaltimetype tt)
-{
- struct tm stm;
- time_t tut;
-
- memset(&stm,0,sizeof( struct tm));
-
- stm.tm_sec = tt.second;
- stm.tm_min = tt.minute;
- stm.tm_hour = tt.hour;
- stm.tm_mday = tt.day;
- stm.tm_mon = tt.month-1;
- stm.tm_year = tt.year-1900;
- stm.tm_isdst = -1; /* prevents mktime from changing hour based on
- daylight savings */
-
- tut = mktime(&stm);
-
- stm = *(localtime(&tut));
-
- tt.second = stm.tm_sec;
- tt.minute = stm.tm_min;
- tt.hour = stm.tm_hour;
- tt.day = stm.tm_mday;
- tt.month = stm.tm_mon +1;
- tt.year = stm.tm_year+1900;
-
- return tt;
+ struct icaltimetype ret = tt;
+ icaltime_adjust(&ret, 0, 0, 0, 0);
+ return ret;
}
-#ifndef ICAL_NO_LIBICAL
-#include "icalvalue.h"
+/** @brief Contructor.
+ *
+ * Create a time from an ISO format string.
+ *
+ * @todo If the given string specifies a DATE-TIME not in UTC, there
+ * is no way to know if this is a floating time or really refers to a
+ * timezone. We should probably add a new constructor:
+ * icaltime_from_string_with_zone()
+ */
struct icaltimetype icaltime_from_string(const char* str)
{
struct icaltimetype tt = icaltime_null_time();
@@ -326,189 +373,230 @@ struct icaltimetype icaltime_from_string(const char* str)
tt.is_utc = 0;
tt.is_date = 0;
} else if (size == 16) { /* UTC time, ends in 'Z'*/
+ if(str[15] != 'Z')
+ goto ERROR;
+
tt.is_utc = 1;
+ tt.zone = icaltimezone_get_utc_timezone();
tt.is_date = 0;
-
- if(str[15] != 'Z'){
- icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
- return icaltime_null_time();
- }
-
} else if (size == 8) { /* A DATE */
tt.is_utc = 1;
tt.is_date = 1;
} else { /* error */
- icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
- return icaltime_null_time();
+ goto ERROR;
}
if(tt.is_date == 1){
- sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day);
+ if (sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day) < 3)
+ goto ERROR;
} else {
char tsep;
- sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day,
- &tsep,&tt.hour,&tt.minute,&tt.second);
-
- if(tsep != 'T'){
- icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
- return icaltime_null_time();
- }
+ if (sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day,
+ &tsep,&tt.hour,&tt.minute,&tt.second) < 7)
+ goto ERROR;
+ if(tsep != 'T')
+ goto ERROR;
}
return tt;
+
+ERROR:
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ return icaltime_null_time();
}
-#endif
-char ctime_str[20];
-char* icaltime_as_ctime(struct icaltimetype t)
-{
- time_t tt;
-
- tt = icaltime_as_timet(t);
- sprintf(ctime_str,"%s",ctime(&tt));
- ctime_str[strlen(ctime_str)-1] = 0;
+/* Returns whether the specified year is a leap year. Year is the normal year,
+ e.g. 2001. */
+int
+icaltime_is_leap_year (const int year)
+{
- return ctime_str;
+ if (year <= 1752)
+ return (year % 4 == 0);
+ else
+ return ( (year % 4==0) && (year % 100 !=0 )) || (year % 400 == 0);
}
+static int _days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
-short days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
-
-short icaltime_days_in_month(short month,short year)
+int icaltime_days_in_month(const int month, const int year)
{
- int is_leap =0;
- int days = days_in_month[month];
+ int days = _days_in_month[month];
assert(month > 0);
assert(month <= 12);
- if( (year % 4 == 0 && year % 100 != 0) ||
- year % 400 == 0){
- is_leap =1;
- }
-
if( month == 2){
- days += is_leap;
+ days += icaltime_is_leap_year(year);
}
return days;
}
/* 1-> Sunday, 7->Saturday */
-short icaltime_day_of_week(struct icaltimetype t){
+int icaltime_day_of_week(const struct icaltimetype t){
+ UTinstant jt;
- time_t tt = icaltime_as_timet(t);
- struct tm *tm;
+ memset(&jt,0,sizeof(UTinstant));
- if(t.is_utc == 1 || t.is_date == 1){
- tm = gmtime(&tt);
- } else {
- tm = localtime(&tt);
- }
+ jt.year = t.year;
+ jt.month = t.month;
+ jt.day = t.day;
+ jt.i_hour = 0;
+ jt.i_minute = 0;
+ jt.i_second = 0;
+
+ juldat(&jt);
- return tm->tm_wday+1;
+ return jt.weekday + 1;
}
-/* Day of the year that the first day of the week (Sunday) is on */
-short icaltime_start_doy_of_week(struct icaltimetype t){
- time_t tt = icaltime_as_timet(t);
- time_t start_tt;
- struct tm *stm;
- int syear;
+/** Day of the year that the first day of the week (Sunday) is on.
+ *
+ * @todo Doesn't take into account different week start days.
+ */
+int icaltime_start_doy_of_week(const struct icaltimetype t){
+ UTinstant jt;
- stm = gmtime(&tt);
- syear = stm->tm_year;
+ memset(&jt,0,sizeof(UTinstant));
- start_tt = tt - stm->tm_wday*(60*60*24);
+ jt.year = t.year;
+ jt.month = t.month;
+ jt.day = t.day;
+ jt.i_hour = 0;
+ jt.i_minute = 0;
+ jt.i_second = 0;
- stm = gmtime(&start_tt);
-
- if(syear == stm->tm_year){
- return stm->tm_yday+1;
- } else {
- /* return negative to indicate that start of week is in
- previous year. */
- int is_leap = 0;
- int year = stm->tm_year;
-
- if( (year % 4 == 0 && year % 100 != 0) ||
- year % 400 == 0){
- is_leap =1;
- }
+ juldat(&jt);
+ caldat(&jt);
- return (stm->tm_yday+1)-(365+is_leap);
- }
-
+ return jt.day_of_year - jt.weekday;
}
-short icaltime_week_number(struct icaltimetype ictt)
+/**
+ * @todo Doesn't take into account the start day of the
+ * week. strftime assumes that weeks start on Monday.
+ */
+int icaltime_week_number(const struct icaltimetype ictt)
{
- char str[5];
- time_t t = icaltime_as_timet(ictt);
- int week_no;
+ UTinstant jt;
- strftime(str,5,"%V", gmtime(&t));
+ memset(&jt,0,sizeof(UTinstant));
- week_no = atoi(str);
+ jt.year = ictt.year;
+ jt.month = ictt.month;
+ jt.day = ictt.day;
+ jt.i_hour = 0;
+ jt.i_minute = 0;
+ jt.i_second = 0;
- return week_no;
+ juldat(&jt);
+ caldat(&jt);
+ return (jt.day_of_year - jt.weekday) / 7;
}
+/* The first array is for non-leap years, the second for leap years*/
+static const int days_in_year[2][13] =
+{ /* jan feb mar apr may jun jul aug sep oct nov dec */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
-short icaltime_day_of_year(struct icaltimetype t){
- time_t tt;
- struct tm *stm;
- struct set_tz_save old_tz;
-
- tt = icaltime_as_timet(t);
+/**
+ * Returns the day of the year, counting from 1 (Jan 1st).
+ */
+int icaltime_day_of_year(const struct icaltimetype t){
+ int is_leap = icaltime_is_leap_year (t.year);
- old_tz = set_tz("UTC");
- stm = localtime(&tt);
- unset_tz(old_tz);
-
- return stm->tm_yday+1;
-
+ return days_in_year[is_leap][t.month - 1] + t.day;
}
+/** @brief Contructor.
+ *
+ * Create a new time, given a day of year and a year.
+ */
/* Jan 1 is day #1, not 0 */
-struct icaltimetype icaltime_from_day_of_year(short doy, short year)
+struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year)
{
- struct tm stm;
- time_t tt;
- struct set_tz_save old_tz;
-
- /* Get the time of january 1 of this year*/
- memset(&stm,0,sizeof(struct tm));
- stm.tm_year = year-1900;
- stm.tm_mday = 1;
+ struct icaltimetype tt = icaltime_null_date();
+ int is_leap;
+ int month;
+ int doy = _doy;
+ int year = _year;
+
+ is_leap = icaltime_is_leap_year(year);
+
+ /* Zero and neg numbers represent days of the previous year */
+ if(doy <1){
+ year--;
+ is_leap = icaltime_is_leap_year(year);
+ doy += days_in_year[is_leap][12];
+ } else if(doy > days_in_year[is_leap][12]){
+ /* Move on to the next year*/
+ is_leap = icaltime_is_leap_year(year);
+ doy -= days_in_year[is_leap][12];
+ year++;
+ }
- old_tz = set_tz("UTC");
- tt = mktime(&stm);
- unset_tz(old_tz);
+ tt.year = year;
+ for (month = 11; month >= 0; month--) {
+ if (doy > days_in_year[is_leap][month]) {
+ tt.month = month + 1;
+ tt.day = doy - days_in_year[is_leap][month];
+ break;
+ }
+ }
- /* Now add in the days */
+ return tt;
+}
- doy--;
- tt += doy *60*60*24;
+/** @brief Constructor.
+ *
+ * Return a null time, which indicates no time has been set.
+ * This time represents the beginning of the epoch.
+ */
+struct icaltimetype icaltime_null_time(void)
+{
+ struct icaltimetype t;
+ memset(&t,0,sizeof(struct icaltimetype));
- return icaltime_from_timet(tt, 1);
+ return t;
}
-struct icaltimetype icaltime_null_time()
+/** @brief Constructor.
+ *
+ * Return a null date, which indicates no time has been set.
+ */
+struct icaltimetype icaltime_null_date(void)
{
struct icaltimetype t;
memset(&t,0,sizeof(struct icaltimetype));
+ t.is_date = 1;
+
+ /*
+ * Init to -1 to match what icalyacc.y used to do.
+ * Does anything depend on this?
+ */
+ t.hour = -1;
+ t.minute = -1;
+ t.second = -1;
+
return t;
}
-int icaltime_is_valid_time(struct icaltimetype t){
+/**
+ * Returns false if the time is clearly invalid, but is not null. This
+ * is usually the result of creating a new time type buy not clearing
+ * it, or setting one of the flags to an illegal value.
+ */
+int icaltime_is_valid_time(const struct icaltimetype t){
if(t.is_utc > 1 || t.is_utc < 0 ||
t.year < 0 || t.year > 3000 ||
t.is_date > 1 || t.is_date < 0){
@@ -519,7 +607,26 @@ int icaltime_is_valid_time(struct icaltimetype t){
}
-int icaltime_is_null_time(struct icaltimetype t)
+/** @brief Returns true if time is a DATE
+ */
+int icaltime_is_date(const struct icaltimetype t) {
+
+ return t.is_date;
+}
+
+/** @brief Returns true if time is relative to UTC zone
+ *
+ * @todo We should only check the zone
+ */
+int icaltime_is_utc(const struct icaltimetype t) {
+
+ return t.is_utc;
+}
+
+/**
+ * Return true if the time is null.
+ */
+int icaltime_is_null_time(const struct icaltimetype t)
{
if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){
return 1;
@@ -529,44 +636,101 @@ int icaltime_is_null_time(struct icaltimetype t)
}
-int icaltime_compare(struct icaltimetype a,struct icaltimetype b)
-{
- time_t t1 = icaltime_as_timet(a);
- time_t t2 = icaltime_as_timet(b);
-
- if (t1 > t2) {
- return 1;
- } else if (t1 < t2) {
- return -1;
- } else {
- return 0;
- }
+/**
+ * Return -1, 0, or 1 to indicate that a<b, a==b, or a>b.
+ * This calls icaltime_compare function after converting them to the utc
+ * timezone.
+ */
+int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in)
+{
+ int retval = 0;
+ struct icaltimetype a, b;
+
+ a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone());
+ b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone());
+
+ if (a.year > b.year)
+ retval = 1;
+ else if (a.year < b.year)
+ retval = -1;
+
+ else if (a.month > b.month)
+ retval = 1;
+ else if (a.month < b.month)
+ retval = -1;
+
+ else if (a.day > b.day)
+ retval = 1;
+ else if (a.day < b.day)
+ retval = -1;
+
+ /* if both are dates, we are done */
+ if (a.is_date && b.is_date)
+ return retval;
+
+ /* else, if we already found a difference, we are done */
+ else if (retval != 0)
+ return retval;
+
+ /* else, if only one is a date (and we already know the date part is equal),
+ then the other is greater */
+ else if (b.is_date)
+ retval = 1;
+ else if (a.is_date)
+ retval = -1;
+
+ else if (a.hour > b.hour)
+ retval = 1;
+ else if (a.hour < b.hour)
+ retval = -1;
+
+ else if (a.minute > b.minute)
+ retval = 1;
+ else if (a.minute < b.minute)
+ retval = -1;
+
+ else if (a.second > b.second)
+ retval = 1;
+ else if (a.second < b.second)
+ retval = -1;
+
+ return retval;
}
+/**
+ * like icaltime_compare, but only use the date parts.
+ */
+
int
-icaltime_compare_date_only (struct icaltimetype a, struct icaltimetype b)
+icaltime_compare_date_only(const struct icaltimetype a_in, const struct icaltimetype b_in)
{
- time_t t1;
- time_t t2;
-
- if (a.year == b.year && a.month == b.month && a.day == b.day)
- return 0;
-
- t1 = icaltime_as_timet (a);
- t2 = icaltime_as_timet (b);
-
- if (t1 > t2)
- return 1;
- else if (t1 < t2)
- return -1;
- else {
- /* not reached */
- assert (0);
- return 0;
- }
-}
+ int retval;
+ struct icaltimetype a, b;
+
+ a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone());
+ b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone());
+
+ if (a.year > b.year)
+ retval = 1;
+ else if (a.year < b.year)
+ retval = -1;
+
+ else if (a.month > b.month)
+ retval = 1;
+ else if (a.month < b.month)
+ retval = -1;
+
+ else if (a.day > b.day)
+ retval = 1;
+ else if (a.day < b.day)
+ retval = -1;
+ else
+ retval = 0;
+
+ return retval;
+}
/* These are defined in icalduration.c:
struct icaltimetype icaltime_add(struct icaltimetype t,
@@ -575,3 +739,277 @@ struct icaldurationtype icaltime_subtract(struct icaltimetype t1,
struct icaltimetype t2)
*/
+
+
+/** @brief Internal, shouldn't be part of the public API
+ *
+ * Adds (or subtracts) a time from a icaltimetype.
+ * NOTE: This function is exactly the same as icaltimezone_adjust_change()
+ * except for the type of the first parameter.
+ */
+void
+icaltime_adjust(struct icaltimetype *tt, const int days, const int hours,
+ const int minutes, const int seconds) {
+
+ int second, minute, hour, day;
+ int minutes_overflow, hours_overflow, days_overflow, years_overflow;
+ int days_in_month;
+
+ /* Add on the seconds. */
+ second = tt->second + seconds;
+ tt->second = second % 60;
+ minutes_overflow = second / 60;
+ if (tt->second < 0) {
+ tt->second += 60;
+ minutes_overflow--;
+ }
+
+ /* Add on the minutes. */
+ minute = tt->minute + minutes + minutes_overflow;
+ tt->minute = minute % 60;
+ hours_overflow = minute / 60;
+ if (tt->minute < 0) {
+ tt->minute += 60;
+ hours_overflow--;
+ }
+
+ /* Add on the hours. */
+ hour = tt->hour + hours + hours_overflow;
+ tt->hour = hour % 24;
+ days_overflow = hour / 24;
+ if (tt->hour < 0) {
+ tt->hour += 24;
+ days_overflow--;
+ }
+
+ /* Normalize the month. We do this before handling the day since we may
+ need to know what month it is to get the number of days in it.
+ Note that months are 1 to 12, so we have to be a bit careful. */
+ if (tt->month >= 13) {
+ years_overflow = (tt->month - 1) / 12;
+ tt->year += years_overflow;
+ tt->month -= years_overflow * 12;
+ } else if (tt->month <= 0) {
+ /* 0 to -11 is -1 year out, -12 to -23 is -2 years. */
+ years_overflow = (tt->month / 12) - 1;
+ tt->year += years_overflow;
+ tt->month -= years_overflow * 12;
+ }
+
+ /* Add on the days. */
+ day = tt->day + days + days_overflow;
+ if (day > 0) {
+ for (;;) {
+ days_in_month = icaltime_days_in_month (tt->month, tt->year);
+ if (day <= days_in_month)
+ break;
+
+ tt->month++;
+ if (tt->month >= 13) {
+ tt->year++;
+ tt->month = 1;
+ }
+
+ day -= days_in_month;
+ }
+ } else {
+ while (day <= 0) {
+ if (tt->month == 1) {
+ tt->year--;
+ tt->month = 12;
+ } else {
+ tt->month--;
+ }
+
+ day += icaltime_days_in_month (tt->month, tt->year);
+ }
+ }
+ tt->day = day;
+}
+
+/** @brief Convert time to a given timezone
+ *
+ * Convert a time from its native timezone to a given timezone.
+ *
+ * If tt is a date, the returned time is an exact
+ * copy of the input. If it's a floating time, the returned object
+ * represents the same time translated to the given timezone.
+ * Otherwise the time will be converted to the new
+ * time zone, and its native timezone set to the right timezone.
+ */
+struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt,
+ icaltimezone *zone) {
+
+ struct icaltimetype ret = tt;
+
+ /* If it's a date do nothing */
+ if (tt.is_date) {
+ return ret;
+ }
+
+ if (tt.zone == zone) {
+ return ret;
+ }
+
+ /* If it's a floating time we don't want to adjust the time */
+ if (tt.zone != NULL) {
+ icaltimezone_convert_time(&ret, tt.zone, zone);
+ }
+
+ ret.zone = zone;
+ if (zone == icaltimezone_get_utc_timezone()) {
+ ret.is_utc = 1;
+ } else {
+ ret.is_utc = 0;
+ }
+
+ return ret;
+}
+
+const icaltimezone *
+icaltime_get_timezone(const struct icaltimetype t) {
+
+ return t.zone;
+}
+
+char *
+icaltime_get_tzid(const struct icaltimetype t) {
+
+ if (t.zone != NULL) {
+ return icaltimezone_get_tzid(t.zone);
+ } else {
+ return NULL;
+ }
+}
+
+/** @brief Set the timezone
+ *
+ * Force the icaltime to be interpreted relative to another timezone.
+ * If you need to do timezone conversion, applying offset adjustments,
+ * then you should use icaltime_convert_to_timezone instead.
+ */
+struct icaltimetype
+icaltime_set_timezone(struct icaltimetype *t, const icaltimezone *zone) {
+
+ /* If it's a date do nothing */
+ if (t->is_date) {
+ return *t;
+ }
+
+ if (t->zone == zone) {
+ return *t;
+ }
+
+ t->zone = zone;
+ if (zone == icaltimezone_get_utc_timezone()) {
+ t->is_utc = 1;
+ } else {
+ t->is_utc = 0;
+ }
+
+ return *t;
+}
+
+
+/**
+ * @brief builds an icaltimespan given a start time, end time and busy value.
+ *
+ * @param dtstart The beginning time of the span, can be a date-time
+ * or just a date.
+ * @param dtend The end time of the span.
+ * @param is_busy A boolean value, 0/1.
+ * @return A span using the supplied values.
+ *
+ * returned span contains times specified in UTC.
+ */
+
+icaltime_span icaltime_span_new(struct icaltimetype dtstart,
+ struct icaltimetype dtend,
+ int is_busy)
+{
+ icaltime_span span;
+
+ span.is_busy = is_busy;
+
+ span.start = icaltime_as_timet_with_zone(dtstart,
+ icaltimezone_get_utc_timezone());
+
+ if (icaltime_is_null_time(dtend)) {
+ if (!icaltime_is_date(dtstart)) {
+ /* If dtstart is a DATE-TIME and there is no DTEND nor DURATION
+ it takes no time */
+ span.end = span.start;
+ return span;
+ } else {
+ dtend = dtstart;
+ }
+ }
+
+ span.end = icaltime_as_timet_with_zone(dtend, icaltimezone_get_utc_timezone());
+
+ if (icaltime_is_date(dtstart)) {
+ /* no time specified, go until the end of the day..*/
+ span.end += 60*60*24 - 1;
+ }
+ return span;
+}
+
+
+/** @brief Returns true if the two spans overlap
+ *
+ * @param s1 1st span to test
+ * @param s2 2nd span to test
+ * @return boolean value
+ *
+ * The result is calculated by testing if the start time of s1 is contained
+ * by the s2 span, or if the end time of s1 is contained by the s2 span.
+ *
+ * Also returns true if the spans are equal.
+ *
+ * Note, this will return false if the spans are adjacent.
+ */
+
+int icaltime_span_overlaps(icaltime_span *s1,
+ icaltime_span *s2)
+{
+ /* s1->start in s2 */
+ if (s1->start > s2->start && s1->start < s2->end)
+ return 1;
+
+ /* s1->end in s2 */
+ if (s1->end > s2->start && s1->end < s2->end)
+ return 1;
+
+ /* s2->start in s1 */
+ if (s2->start > s1->start && s2->start < s1->end)
+ return 1;
+
+ /* s2->end in s1 */
+ if (s2->end > s1->start && s2->end < s1->end)
+ return 1;
+
+ if (s1->start == s2->start && s1->end == s2->end)
+ return 1;
+
+ return 0;
+}
+
+/** @brief Returns true if the span is totally within the containing
+ * span
+ *
+ * @param s The span to test for.
+ * @param container The span to test against.
+ * @return boolean value.
+ *
+ */
+
+int icaltime_span_contains(icaltime_span *s,
+ icaltime_span *container)
+{
+
+ if ((s->start >= container->start && s->start < container->end) &&
+ (s->end <= container->end && s->end > container->start))
+ return 1;
+
+ return 0;
+}