diff options
Diffstat (limited to 'libical/src/libical/icaltime.c')
-rw-r--r-- | libical/src/libical/icaltime.c | 640 |
1 files changed, 523 insertions, 117 deletions
diff --git a/libical/src/libical/icaltime.c b/libical/src/libical/icaltime.c index 112ac7972b..8169161dee 100644 --- a/libical/src/libical/icaltime.c +++ b/libical/src/libical/icaltime.c @@ -30,47 +30,223 @@ #endif #include "icaltime.h" -#include "icalvalue.h" #include <assert.h> #include <string.h> #include <stdlib.h> #include <stdio.h> +#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" +#endif + + + +extern long int timezone; /* Global defined by libc */ struct icaltimetype -icaltime_from_timet(time_t tm, int is_date, int is_utc) +icaltime_from_timet(time_t tm, int is_date) { - struct icaltimetype tt; + struct icaltimetype tt = icaltime_null_time(); struct tm t; -#if 0 /* This is incorrect; a time_t *is* in UTC by definition. So we just ignore the flag. */ - if(is_utc == 0){ - tm += icaltime_local_utc_offset(); - } -#endif - 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 ; + } - tt.second = t.tm_sec; - tt.minute = t.tm_min; - tt.hour = t.tm_hour; tt.day = t.tm_mday; tt.month = t.tm_mon + 1; tt.year = t.tm_year+ 1900; -#if 0 - tt.is_utc = is_utc; -#endif tt.is_utc = 1; tt.is_date = is_date; return tt; } -/* Always returns time in UTC */ +char* set_tz(const char* tzid) +{ + char *tzstr = 0; + char *tmp; + + /* Put the new time zone into the environment */ + if(getenv("TZ") != 0){ + tzstr = (char*)strdup(getenv("TZ")); + + if(tzstr == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + } + + tmp = (char*)malloc(1024); + + if(tmp == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + snprintf(tmp,1024,"TZ=%s",tzid); + + /* HACK. In some libc versions, putenv gives the string to the + system and in some it gives a copy, so the following might be a + memory leak. THe linux man page says that glibc2.1.2 take + ownership ( no leak) while BSD4.4 uses a copy ( A leak ) */ + putenv(tmp); + + return tzstr; /* This will be zero if the TZ env var was not set */ +} + +void unset_tz(char* tzstr) +{ + /* restore the original environment */ + + if(tzstr!=0){ + char temp[1024]; + snprintf(temp,1024,"TZ=%s",tzstr); + putenv(temp); + free(tzstr); + } else { + putenv("TZ"); /* Delete from environment */ + } +} + time_t icaltime_as_timet(struct icaltimetype tt) { struct tm stm; + time_t t; + + memset(&stm,0,sizeof( struct tm)); + + if(icaltime_is_null_time(tt)) { + return 0; + } + + 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; + + if(tt.is_utc == 1 || tt.is_date == 1){ + char* old_tz = set_tz("UTC"); + t = mktime(&stm); + unset_tz(old_tz); + } else { + t = mktime(&stm); + } + + return t; + +} + +char* icaltime_as_ical_string(struct icaltimetype tt) +{ + size_t size = 17; + char* buf = icalmemory_new_buffer(size); + + if(tt.is_date){ + snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day); + } else { + char* fmt; + if(tt.is_utc){ + fmt = "%04d%02d%02dT%02d%02d%02dZ"; + } else { + fmt = "%04d%02d%02dT%02d%02d%02d"; + } + snprintf(buf, size,fmt,tt.year,tt.month,tt.day, + tt.hour,tt.minute,tt.second); + } + + icalmemory_add_tmp_buffer(buf); + + return buf; + +} + + +/* 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) +{ + 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; + + char *tzstr = 0; + + if(tzid != 0){ + tzstr = 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(>m); + + if(tzid != 0){ + unset_tz(tzstr); + } + + 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)); @@ -87,29 +263,71 @@ time_t icaltime_as_timet(struct icaltimetype tt) if(tt.is_utc) stm.tm_sec -= icaltime_local_utc_offset(); + tut = mktime(&stm); - return tut; + 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; } +#ifndef ICAL_NO_LIBICAL +#include "icalvalue.h" struct icaltimetype icaltime_from_string(const char* str) { - struct icaltimetype tt; - icalvalue *v = icalvalue_new_from_string(ICAL_DATETIME_VALUE,str); - - if (v == 0){ + struct icaltimetype tt = icaltime_null_time(); + int size; + + icalerror_check_arg_re(str!=0,"str",icaltime_null_time()); + + size = strlen(str); + + if(size == 15) { /* floating time */ + tt.is_utc = 0; + tt.is_date = 0; + } else if (size == 16) { /* UTC time, ends in 'Z'*/ + tt.is_utc = 1; + 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 = 0; + tt.is_date = 1; + } else { /* error */ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); return icaltime_null_time(); } - tt = icalvalue_get_datetime(v); + if(tt.is_date == 1){ + sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day); + } 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); - icalvalue_free(v); + if(tsep != 'T'){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return icaltime_null_time(); + } - return tt; - + } + + return tt; } +#endif char ctime_str[20]; char* icaltime_as_ctime(struct icaltimetype t) @@ -128,6 +346,7 @@ 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 is_leap =0; int days = days_in_month[month]; @@ -152,7 +371,11 @@ short icaltime_day_of_week(struct icaltimetype t){ time_t tt = icaltime_as_timet(t); struct tm *tm; - tm = gmtime(&tt); + if(t.is_utc == 1){ + tm = gmtime(&tt); + } else { + tm = localtime(&tt); + } return tm->tm_wday+1; } @@ -161,26 +384,50 @@ 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; stm = gmtime(&tt); + syear = stm->tm_year; start_tt = tt - stm->tm_wday*(60*60*24); 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; - return stm->tm_yday; + if( (year % 4 == 0 && year % 100 != 0) || + year % 400 == 0){ + is_leap =1; + } + + return (stm->tm_yday+1)-(365+is_leap); + } + } + + short icaltime_day_of_year(struct icaltimetype t){ time_t tt = icaltime_as_timet(t); struct tm *stm; - stm = gmtime(&tt); + if(t.is_utc==1){ + stm = gmtime(&tt); + } else { + stm = localtime(&tt); + } - return stm->tm_yday; + return stm->tm_yday+1; } +/* Jan 1 is day #1, not 0 */ struct icaltimetype icaltime_from_day_of_year(short doy, short year) { struct tm stm; @@ -195,9 +442,10 @@ struct icaltimetype icaltime_from_day_of_year(short doy, short year) /* Now add in the days */ + doy--; tt += doy *60*60*24; - return icaltime_from_timet(tt, 1, 1); + return icaltime_from_timet(tt, 1); } struct icaltimetype icaltime_null_time() @@ -255,102 +503,83 @@ icaltime_compare_date_only (struct icaltimetype a, struct icaltimetype b) } } -/* convert tt, of timezone tzid, into a utc time */ -struct icaltimetype icaltime_as_utc(struct icaltimetype tt,const char* tzid) +struct icalperiodtype icalperiodtype_from_string (const char* str) { - time_t offset, tm; - struct icaltimetype utc; + + struct icalperiodtype p, null_p; + char *s = strdup(str); + char *start, *end = s; + int old_ieaf = icalerror_errors_are_fatal; - offset = icaltime_utc_offset(tt,tzid); - tm = icaltime_as_timet(tt); + p.start = p.end = icaltime_null_time(); + p.duration = icaldurationtype_from_int(0); - tm += offset; + null_p = p; - utc = icaltime_from_timet(tm,0,0); + if(s == 0) goto error; - return utc; -} + start = s; + end = strchr(s, '/'); -/* convert tt, a time in UTC, into a time in timezone tzid */ -struct icaltimetype icaltime_as_zone(struct icaltimetype tt,const char* tzid) -{ - time_t offset, tm; - struct icaltimetype zone; + if(end == 0) goto error; - offset = icaltime_utc_offset(tt,tzid); - tm = icaltime_as_timet(tt); + *end = 0; + end++; - tm -= offset; - - zone = icaltime_from_timet(tm,0,0); + p.start = icaltime_from_string(start); - return zone; + if (icaltime_is_null_time(p.start)) goto error; + + icalerror_errors_are_fatal = 0; + p.end = icaltime_from_string(end); + icalerror_errors_are_fatal = old_ieaf; -} + if (icaltime_is_null_time(p.end)){ -/* Return the offset of the named zone as seconds. tt is a time - indicating the date for which you want the offset */ -time_t icaltime_utc_offset(struct icaltimetype tt, const char* tzid) -{ -#ifdef HAVE_TIMEZONE - extern long int timezone; -#endif - time_t now; - struct tm *stm; + p.duration = icaldurationtype_from_string(end); - char *tzstr = 0; - char *tmp; + if(icaldurationtype_as_int(p.duration) == 0) goto error; + } - /* Put the new time zone into the environment */ - if(getenv("TZ") != 0){ - tzstr = (char*)strdup(getenv("TZ")); - } + return p; - tmp = (char*)malloc(1024); - snprintf(tmp,1024,"TZ=%s",tzid); + error: + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return null_p; +} - putenv(tmp); - /* Get the offset */ +const char* icalperiodtype_as_ical_string(struct icalperiodtype p) +{ - now = icaltime_as_timet(tt); + const char* start; + const char* end; - stm = localtime(&now); /* This sets 'timezone'*/ + char *buf; + size_t buf_size = 40; + char* buf_ptr = 0; - /* restore the original environment */ + buf = (char*)icalmemory_new_buffer(buf_size); + buf_ptr = buf; + - if(tzstr!=0){ - putenv(tzstr); + start = icaltime_as_ical_string(p.start); + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, start); + + if(!icaltime_is_null_time(p.end)){ + end = icaltime_as_ical_string(p.end); } else { - putenv("TZ"); /* Delete from environment */ + end = icaldurationtype_as_ical_string(p.duration); } - -#ifdef HAVE_TIMEZONE - return timezone; -#else - return -stm->tm_gmtoff; -#endif -} -time_t icaltime_local_utc_offset() -{ - time_t now; - struct tm *stm; + icalmemory_append_char(&buf, &buf_ptr, &buf_size, '/'); - stm = localtime(&now); /* This sets 'timezone'*/ + icalmemory_append_string(&buf, &buf_ptr, &buf_size, end); -#ifdef HAVE_TIMEZONE - return timezone; -#else - return -stm->tm_gmtoff; -#endif + return buf; } - - - - - time_t icalperiodtype_duration (struct icalperiodtype period); @@ -360,20 +589,21 @@ icalperiodtype_end (struct icalperiodtype period); /* From Russel Steinthal */ -time_t icaldurationtype_as_timet(struct icaldurationtype dur) +int icaldurationtype_as_int(struct icaldurationtype dur) { - return (time_t) (dur.seconds + - (60 * dur.minutes) + - (60 * 60 * dur.hours) + - (60 * 60 * 24 * dur.days) + - (60 * 60 * 24 * 7 * dur.weeks)); + return (int)( (dur.seconds + + (60 * dur.minutes) + + (60 * 60 * dur.hours) + + (60 * 60 * 24 * dur.days) + + (60 * 60 * 24 * 7 * dur.weeks)) + * (dur.is_neg==1? -1 : 1) ) ; } /* From Seth Alves, <alves@hungry.com> */ -struct icaldurationtype icaldurationtype_from_timet(time_t t) +struct icaldurationtype icaldurationtype_from_int(int t) { struct icaldurationtype dur; - time_t used = 0; + int used = 0; dur.weeks = (t - used) / (60 * 60 * 24 * 7); used += dur.weeks * (60 * 60 * 24 * 7); @@ -385,33 +615,209 @@ struct icaldurationtype icaldurationtype_from_timet(time_t t) used += dur.minutes * (60); dur.seconds = (t - used); + dur.is_neg = t<0? 1 : 0; + return dur; } +#ifndef ICAL_NO_LIBICAL +#include "icalvalue.h" struct icaldurationtype icaldurationtype_from_string(const char* str) { - icalvalue *v = icalvalue_new_from_string(ICAL_DURATION_VALUE,str); + int i; + int begin_flag = 0; + int time_flag = 0; + int date_flag = 0; + int week_flag = 0; + int digits=-1; + int scan_size = -1; + int size = strlen(str); + char p; + struct icaldurationtype d; + + memset(&d, 0, sizeof(struct icaldurationtype)); + + for(i=0;i != size;i++){ + p = str[i]; + + switch(p) + { + case '-': { + if(i != 0 || begin_flag == 1) goto error; + + d.is_neg = 1; + break; + } + + case 'P': { + if (i != 0 && i !=1 ) goto error; + begin_flag = 1; + break; + } + + case 'T': { + time_flag = 1; + break; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + + /* HACK. Skip any more digits if the l;ast one + read has not been assigned */ + if(digits != -1){ + break; + } + + if (begin_flag == 0) goto error; + /* Get all of the digits, not one at a time */ + scan_size = sscanf((char*)(str+i),"%d",&digits); + if(scan_size == 0) goto error; + break; + } + + case 'H': { + if (time_flag == 0||week_flag == 1||d.hours !=0||digits ==-1) + goto error; + d.hours = digits; digits = -1; + break; + } + case 'M': { + if (time_flag == 0||week_flag==1||d.minutes != 0||digits ==-1) + goto error; + d.minutes = digits; digits = -1; + break; + } + case 'S': { + if (time_flag == 0||week_flag==1||d.seconds!=0||digits ==-1) + goto error; + d.seconds = digits; digits = -1; + break; + } + case 'W': { + if (time_flag==1||date_flag==1||d.weeks!=0||digits ==-1) + goto error; + week_flag = 1; + d.weeks = digits; digits = -1; + break; + } + case 'D': { + if (time_flag==1||week_flag==1||d.days!=0||digits ==-1) + goto error; + date_flag = 1; + d.days = digits; digits = -1; + break; + } + default: { + goto error; + } + + } + } + + return d; + + + error: + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + memset(&d, 0, sizeof(struct icaldurationtype)); + return d; + +} + +#define TMP_BUF_SIZE 1024 +void append_duration_segment(char** buf, char** buf_ptr, size_t* buf_size, + char* sep, unsigned int value) { + + char temp[TMP_BUF_SIZE]; + + sprintf(temp,"%d",value); + + icalmemory_append_string(buf, buf_ptr, buf_size, temp); + icalmemory_append_string(buf, buf_ptr, buf_size, sep); + +} + +char* icaldurationtype_as_ical_string(struct icaldurationtype d) +{ - if( v !=0){ - return icalvalue_get_duration(v); + char *buf, *output_line; + size_t buf_size = 256; + char* buf_ptr = 0; + int seconds; + + buf = (char*)icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + + seconds = icaldurationtype_as_int(d); + + if(seconds !=0){ + + if(d.is_neg == 1){ + icalmemory_append_char(&buf, &buf_ptr, &buf_size, '-'); + } + + icalmemory_append_char(&buf, &buf_ptr, &buf_size, 'P'); + + if (d.weeks != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "W", d.weeks); + } + + if (d.days != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "D", d.days); + } + + if (d.hours != 0 || d.minutes != 0 || d.seconds != 0) { + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "T"); + + if (d.hours != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "H", d.hours); + } + if (d.minutes != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "M", + d.minutes); + } + if (d.seconds != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "S", + d.seconds); + } + + } } else { - struct icaldurationtype dur; - memset(&dur,0,sizeof(struct icaldurationtype)); - return dur; + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "PTS0"); } + output_line = icalmemory_tmp_copy(buf); + icalmemory_free_buffer(buf); + + return output_line; + } +#endif struct icaltimetype icaltime_add(struct icaltimetype t, struct icaldurationtype d) { - time_t tt = icaltime_as_timet(t); - time_t dt = icaldurationtype_as_timet(d); + int dt = icaldurationtype_as_int(d); + + t.second += dt; - return icaltime_from_timet(tt + dt, t.is_date, t.is_utc); + t = icaltime_normalize(t); + return t; } struct icaldurationtype icaltime_subtract(struct icaltimetype t1, @@ -421,7 +827,7 @@ struct icaldurationtype icaltime_subtract(struct icaltimetype t1, time_t t1t = icaltime_as_timet(t1); time_t t2t = icaltime_as_timet(t2); - return icaldurationtype_from_timet(t1t-t2t); + return icaldurationtype_from_int(t1t-t2t); } |