summaryrefslogtreecommitdiffabout
path: root/libical/src/libical/icaltime.c
Unidiff
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
@@ -13,565 +13,1003 @@
13 13
14 The LGPL as published by the Free Software Foundation, version 14 The LGPL as published by the Free Software Foundation, version
15 2.1, available at: http://www.fsf.org/copyleft/lesser.html 15 2.1, available at: http://www.fsf.org/copyleft/lesser.html
16 16
17 Or: 17 Or:
18 18
19 The Mozilla Public License Version 1.0. You may obtain a copy of 19 The Mozilla Public License Version 1.0. You may obtain a copy of
20 the License at http://www.mozilla.org/MPL/ 20 the License at http://www.mozilla.org/MPL/
21 21
22 The Original Code is eric. The Initial Developer of the Original 22 The Original Code is eric. The Initial Developer of the Original
23 Code is Eric Busboom 23 Code is Eric Busboom
24 24
25 25
26 ======================================================================*/ 26 ======================================================================*/
27 27
28#ifdef HAVE_CONFIG_H 28#ifdef HAVE_CONFIG_H
29#include <config.h> 29#include "config.h"
30#endif 30#endif
31 31
32#include "icaltime.h" 32#include "icaltime.h"
33#include <assert.h> 33#include <assert.h>
34#include <string.h> 34#include <string.h>
35#include <stdlib.h> 35#include <stdlib.h>
36#include <stdio.h> 36#include <stdio.h>
37#include <time.h>
37 38
38int snprintf(char *str, size_t n, char const *fmt, ...); 39 #include "astime.h" /* Julian data handling routines */
39 40
40#ifdef ICAL_NO_LIBICAL
41#define icalerror_set_errno(x)
42#define icalerror_check_arg_rv(x,y)
43#define icalerror_check_arg_re(x,y,z)
44#else
45#include "icalerror.h" 41#include "icalerror.h"
46#include "icalmemory.h" 42#include "icalmemory.h"
43
44#include "icaltimezone.h"
45#include "icalvalue.h"
46
47#ifdef WIN32
48#include <Windows.h>
49#define ERROR error
50#define snprintf _snprintf
51#define strcasecmp stricmp
47#endif 52#endif
48 53
54/*
55 * Function to convert a struct tm time specification
56 * to an ANSI time_t using the specified time zone.
57 * This is different from the standard mktime() function
58 * in that we dont want the automatic adjustments for
59 * local daylight savings time applied to the result.
60 * This function expects well-formed input.
61 */
62static time_t make_time(struct tm *tm, int tzm)
63{
64 time_t tim;
49 65
66 static int days[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 };
50 67
68 /* check that year specification within range */
51 69
52struct icaltimetype 70 if (tm->tm_year < 70 || tm->tm_year > 138)
53icaltime_from_timet(time_t tm, int is_date) 71 return((time_t) -1);
54{
55 struct icaltimetype tt = icaltime_null_time();
56 struct tm t;
57 72
58 t = *(gmtime(&tm)); 73 /* check that month specification within range */
59
60 if(is_date == 0){
61 tt.second = t.tm_sec;
62 tt.minute = t.tm_min;
63 tt.hour = t.tm_hour;
64 } else {
65 tt.second = tt.minute =tt.hour = 0 ;
66 }
67 74
68 tt.day = t.tm_mday; 75 if (tm->tm_mon < 0 || tm->tm_mon > 11)
69 tt.month = t.tm_mon + 1; 76 return((time_t) -1);
70 tt.year = t.tm_year+ 1900;
71
72 tt.is_utc = 1;
73 tt.is_date = is_date;
74 77
75 return tt; 78 /* check for upper bound of Jan 17, 2038 (to avoid possibility of
76} 79 32-bit arithmetic overflow) */
80
81 if (tm->tm_year == 138) {
82 if (tm->tm_mon > 0)
83 return((time_t) -1);
84 else if (tm->tm_mday > 17)
85 return((time_t) -1);
86 }
77 87
78/* Structure used by set_tz to hold an old value of TZ, and the new 88 /*
79 value, which is in memory we will have to free in unset_tz */ 89 * calculate elapsed days since start of the epoch (midnight Jan
80struct set_tz_save {char* orig_tzid; char* new_env_str;}; 90 * 1st, 1970 UTC) 17 = number of leap years between 1900 and 1970
91 * (number of leap days to subtract)
92 */
81 93
82/* Temporarily change the TZ environmental variable. */ 94 tim = (tm->tm_year - 70) * 365 + ((tm->tm_year - 1) / 4) - 17;
83struct set_tz_save set_tz(const char* tzid)
84{
85 95
86 char *orig_tzid = 0; 96 /* add number of days elapsed in the current year */
87 char *new_env_str;
88 struct set_tz_save savetz;
89 size_t tmp_sz;
90 97
91 savetz.orig_tzid = 0; 98 tim += days[tm->tm_mon];
92 savetz.new_env_str = 0;
93 99
94 if(getenv("TZ") != 0){ 100 /* check and adjust for leap years (the leap year check only valid
95 orig_tzid = (char*)icalmemory_strdup(getenv("TZ")); 101 during the 32-bit era */
96 102
97 if(orig_tzid == 0){ 103 if ((tm->tm_year & 3) == 0 && tm->tm_mon > 1)
98 icalerror_set_errno(ICAL_NEWFAILED_ERROR); 104 tim += 1;
99 return savetz;
100 }
101 }
102 105
103 tmp_sz =strlen(tzid)+4; 106 /* elapsed days to current date */
104 new_env_str = (char*)malloc(tmp_sz);
105 107
106 if(new_env_str == 0){ 108 tim += tm->tm_mday;
107 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
108 return savetz;
109 }
110
111 /* Copy the TZid into a string with the form that putenv expects. */
112 strcpy(new_env_str,"TZ=");
113 strcpy(new_env_str+3,tzid);
114 109
115 putenv(new_env_str);
116 110
117 /* Old value of TZ and the string we will have to free later */ 111 /* calculate elapsed hours since start of the epoch */
118 savetz.orig_tzid = orig_tzid; 112
119 savetz.new_env_str = new_env_str; 113 tim = tim * 24 + tm->tm_hour;
120 return savetz; 114
115 /* calculate elapsed minutes since start of the epoch */
116
117 tim = tim * 60 + tm->tm_min;
118
119 /* adjust per time zone specification */
120
121 tim -= tzm;
122
123 /* calculate elapsed seconds since start of the epoch */
124
125 tim = tim * 60 + tm->tm_sec;
126
127 /* return number of seconds since start of the epoch */
128
129 return(tim);
121} 130}
122 131
123void unset_tz(struct set_tz_save savetz) 132 /**@brief Constructor (deprecated).
133 *
134 * Convert seconds past UNIX epoch to a timetype.
135 *
136 * @deprecated This constructor is deprecated and shouldn't be used in
137 * new software. Use icaltime_from_timet_with_zone(time_t, int,
138 * icaltimezone *) instead. In the meantime, calls to this method
139 * return a floating time, which can always be converted to a local
140 * time with an appropriate call to icaltime_convert_to_zone().
141 */
142
143struct icaltimetype
144icaltime_from_timet(const time_t tm, const int is_date)
124{ 145{
125 /* restore the original TZ environment */ 146#ifndef NO_WARN_DEPRECATED
147 icalerror_warn("icaltime_from_timet() is DEPRECATED, use icaltime_from_timet_with_zone() instead");
148#endif
126 149
127 char* orig_tzid = savetz.orig_tzid; 150 return icaltime_from_timet_with_zone(tm, is_date, 0);
151}
128 152
129 if(orig_tzid!=0){
130 size_t tmp_sz =strlen(orig_tzid)+4;
131 char* orig_env_str = (char*)icalmemory_tmp_buffer(tmp_sz);
132 153
133 if(orig_env_str == 0){ 154 /**@brief Constructor.
134 icalerror_set_errno(ICAL_NEWFAILED_ERROR); 155 *
135 return; 156 *@param tm The time
136 } 157 *@param is_date Boolean: 1 means we should treat tm as a DATE
137 158 *@param zone The timezone tm is in, NULL means to treat tm as a
138 strcpy(orig_env_str,"TZ="); 159 * floating time
139 strcpy(orig_env_str+3,orig_tzid); 160 *
140#ifndef _WIN32 161 *Return a new icaltime instance, initialized to the given time
141 setenv("TZ", savetz.orig_tzid,1 ); 162 *expressed as seconds past UNIX epoch, optionally using the given
142#else 163 *timezone.
143 putenv("TZ=MEZ");//, savetz.orig_tzid ); 164 *
144#endif 165 *If the caller specifies the is_date param as TRUE, the returned
145 free(orig_tzid); 166 *object is of DATE type, otherwise the input is meant to be of
146 } else { 167 *DATE-TIME type.
147#ifdef __FreeBSD__ 168 *If the zone is not specified (NULL zone param) the time is taken
148 unsetenv("TZ"); 169 *to be floating, that is, valid in any timezone. Note that, in
170 *addition to the uses specified in [RFC2445], this can be used
171 *when doing simple math on couples of times.
172 *If the zone is specified (UTC or otherwise), it's stored in the
173 *object and it's used as the native timezone for this object.
174 *This means that the caller can convert this time to a different
175 *target timezone with no need to store the source timezone.
176 *
177 */
178struct icaltimetype
179icaltime_from_timet_with_zone(const time_t tm, const int is_date,
180 const icaltimezone *zone)
181{
182 struct icaltimetype tt = icaltime_null_time();
183 struct tm t;
184 icaltimezone *utc_zone;
185
186 /* Convert the time_t to a struct tm. We can trust gmtime for this. */
187#ifdef HAVE_GMTIME_R
188 gmtime_r(&tm, &t);
149#else 189#else
150 putenv("TZ"); /* Delete from environment */ 190 {
191 struct tm *t_ptr = gmtime(&tm);
192 t = *t_ptr;
193 }
151#endif 194#endif
152 }
153 195
154 if(savetz.new_env_str != 0){ 196 tt.year = t.tm_year + 1900;
155 free(savetz.new_env_str); 197 tt.month = t.tm_mon + 1;
198 tt.day = t.tm_mday;
199
200 if (is_date) {
201 tt.is_date = 1;
202 return tt;
156 } 203 }
204
205 tt.hour = t.tm_hour;
206 tt.minute = t.tm_min;
207 tt.second = t.tm_sec;
208
209 /* If it's a floating time, we don't do any conversion. */
210 if (zone == NULL) {
211 return tt;
212 }
213
214 utc_zone = icaltimezone_get_utc_timezone ();
215 tt.is_utc = (zone == utc_zone) ? 1 : 0;
216 tt.zone = zone;
217
218 return tt;
157} 219}
158 220
221 /**@brief Convenience constructor.
222 *
223 * Returns the current time in the given timezone, as an icaltimetype.
224 */
225struct icaltimetype icaltime_current_time_with_zone(const icaltimezone *zone)
226{
227 return icaltime_from_timet_with_zone (time (NULL), 0, zone);
228}
159 229
160time_t icaltime_as_timet(struct icaltimetype tt) 230 /**@brief Convenience constructor.
231 *
232 * Returns the current day as an icaltimetype, with is_date set.
233 */
234struct icaltimetype icaltime_today(void)
235{
236 return icaltime_from_timet_with_zone (time (NULL), 1, NULL);
237}
238
239 /** @briefReturn the time as seconds past the UNIX epoch
240 */
241time_t icaltime_as_timet(const struct icaltimetype tt)
161{ 242{
162 struct tm stm; 243 struct tm stm;
163 time_t t; 244 time_t t;
164 245
165 memset(&stm,0,sizeof( struct tm)); 246 /* If the time is the special null time, return 0. */
166 247 if (icaltime_is_null_time(tt)) {
167 if(icaltime_is_null_time(tt)) {
168 return 0; 248 return 0;
169 } 249 }
170 250
251 /* Copy the icaltimetype to a struct tm. */
252 memset (&stm, 0, sizeof (struct tm));
253
171 stm.tm_sec = tt.second; 254 stm.tm_sec = tt.second;
172 stm.tm_min = tt.minute; 255 stm.tm_min = tt.minute;
173 stm.tm_hour = tt.hour; 256 stm.tm_hour = tt.hour;
174 stm.tm_mday = tt.day; 257 stm.tm_mday = tt.day;
175 stm.tm_mon = tt.month-1; 258 stm.tm_mon = tt.month-1;
176 stm.tm_year = tt.year-1900; 259 stm.tm_year = tt.year-1900;
177 stm.tm_isdst = -1; 260 stm.tm_isdst = -1;
178 261
179 if(tt.is_utc == 1 || tt.is_date == 1){ 262 t = make_time(&stm, 0);
180 struct set_tz_save old_tz = set_tz("UTC");
181 t = mktime(&stm);
182 unset_tz(old_tz);
183 } else {
184 t = mktime(&stm);
185 }
186 263
187 return t; 264 return t;
188 265
189} 266}
190 267
191char* icaltime_as_ical_string(struct icaltimetype tt) 268 /** @briefReturn the time as seconds past the UNIX epoch, using the
269 *given timezone.
270 *
271 *This convenience method combines a call to icaltime_convert() with
272 *a call to icaltime_as_timet().
273 *If the input timezone is null, no conversion is done; that is, the
274 *time is simply returned as time_t in its native timezone.
275 */
276time_t icaltime_as_timet_with_zone(const struct icaltimetype _tt,
277 const icaltimezone *zone)
278{
279 struct icaltimetype tt = _tt;
280 struct tm stm;
281 time_t t;
282
283 /* If the time is the special null time, return 0. */
284 if (icaltime_is_null_time(tt)) {
285 return 0;
286 }
287
288 if (zone != NULL) {
289 tt = icaltime_convert_to_zone(_tt, zone);
290 }
291
292 /* Copy the icaltimetype to a struct tm. */
293 memset (&stm, 0, sizeof (struct tm));
294
295 stm.tm_sec = tt.second;
296 stm.tm_min = tt.minute;
297 stm.tm_hour = tt.hour;
298 stm.tm_mday = tt.day;
299 stm.tm_mon = tt.month-1;
300 stm.tm_year = tt.year-1900;
301 stm.tm_isdst = -1;
302
303 t = make_time(&stm, 0);
304
305 return t;
306}
307
308/**
309 * Return a string represention of the time, in RFC2445 format. The
310 * string is owned by libical
311 */
312const char* icaltime_as_ical_string(const struct icaltimetype tt)
192{ 313{
193 size_t size = 17; 314 size_t size = 17;
194 char* buf = icalmemory_new_buffer(size); 315 char* buf = icalmemory_new_buffer(size);
195 316
196 if(tt.is_date){ 317 if(tt.is_date){
197 snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day); 318 snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day);
198 } else { 319 } else {
199 char* fmt; 320 char* fmt;
200 if(tt.is_utc){ 321 if(tt.is_utc){
201 fmt = "%04d%02d%02dT%02d%02d%02dZ"; 322 fmt = "%04d%02d%02dT%02d%02d%02dZ";
202 } else { 323 } else {
203 fmt = "%04d%02d%02dT%02d%02d%02d"; 324 fmt = "%04d%02d%02dT%02d%02d%02d";
204 } 325 }
205 snprintf(buf, size,fmt,tt.year,tt.month,tt.day, 326 snprintf(buf, size,fmt,tt.year,tt.month,tt.day,
206 tt.hour,tt.minute,tt.second); 327 tt.hour,tt.minute,tt.second);
207 } 328 }
208 329
209 icalmemory_add_tmp_buffer(buf); 330 icalmemory_add_tmp_buffer(buf);
210 331
211 return buf; 332 return buf;
212 333
213} 334}
214 335
215 336/**
216/* convert tt, of timezone tzid, into a utc time */ 337 *Reset all of the time components to be in their normal ranges. For
217struct icaltimetype icaltime_as_utc(struct icaltimetype tt,const char* tzid) 338 *instance, given a time with minutes=70, the minutes will be reduces
218{ 339 *to 10, and the hour incremented. This allows the caller to do
219 int tzid_offset; 340 *arithmetic on times without worrying about overflow or
220 341 *underflow.
221 if(tt.is_utc == 1 || tt.is_date == 1){ 342 *
222 return tt; 343 *Implementation note: we call icaltime_adjust() with no adjustment.
223 } 344 */
224 345struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
225 tzid_offset = icaltime_utc_offset(tt,tzid);
226
227 tt.second -= tzid_offset;
228
229 tt.is_utc = 1;
230
231 return icaltime_normalize(tt);
232}
233
234/* convert tt, a time in UTC, into a time in timezone tzid */
235struct icaltimetype icaltime_as_zone(struct icaltimetype tt,const char* tzid)
236{ 346{
237 int tzid_offset; 347 struct icaltimetype ret = tt;
238 348 icaltime_adjust(&ret, 0, 0, 0, 0);
239 tzid_offset = icaltime_utc_offset(tt,tzid); 349 return ret;
240
241 tt.second += tzid_offset;
242
243 tt.is_utc = 0;
244
245 return icaltime_normalize(tt);
246
247}
248
249
250/* Return the offset of the named zone as seconds. tt is a time
251 indicating the date for which you want the offset */
252int icaltime_utc_offset(struct icaltimetype ictt, const char* tzid)
253{
254
255 time_t tt = icaltime_as_timet(ictt);
256 time_t offset_tt;
257 struct tm gtm;
258 struct set_tz_save old_tz;
259
260 if(tzid != 0){
261 old_tz = set_tz(tzid);
262 }
263
264 /* Mis-interpret a UTC broken out time as local time */
265 gtm = *(gmtime(&tt));
266 gtm.tm_isdst = localtime(&tt)->tm_isdst;
267 offset_tt = mktime(&gtm);
268
269 if(tzid != 0){
270 unset_tz(old_tz);
271 }
272
273 return tt-offset_tt;
274}
275
276
277
278/* Normalize by converting from localtime to utc and back to local
279 time. This uses localtime because localtime and mktime are inverses
280 of each other */
281
282struct icaltimetype icaltime_normalize(struct icaltimetype tt)
283{
284 struct tm stm;
285 time_t tut;
286
287 memset(&stm,0,sizeof( struct tm));
288
289 stm.tm_sec = tt.second;
290 stm.tm_min = tt.minute;
291 stm.tm_hour = tt.hour;
292 stm.tm_mday = tt.day;
293 stm.tm_mon = tt.month-1;
294 stm.tm_year = tt.year-1900;
295 stm.tm_isdst = -1; /* prevents mktime from changing hour based on
296 daylight savings */
297
298 tut = mktime(&stm);
299
300 stm = *(localtime(&tut));
301
302 tt.second = stm.tm_sec;
303 tt.minute = stm.tm_min;
304 tt.hour = stm.tm_hour;
305 tt.day = stm.tm_mday;
306 tt.month = stm.tm_mon +1;
307 tt.year = stm.tm_year+1900;
308
309 return tt;
310} 350}
311 351
312 352
313#ifndef ICAL_NO_LIBICAL
314#include "icalvalue.h"
315 353
354 /**@brief Contructor.
355 *
356 * Create a time from an ISO format string.
357 *
358 * @todo If the given string specifies a DATE-TIME not in UTC, there
359 * is no way to know if this is a floating time or really refers to a
360 * timezone. We should probably add a new constructor:
361 * icaltime_from_string_with_zone()
362 */
316struct icaltimetype icaltime_from_string(const char* str) 363struct icaltimetype icaltime_from_string(const char* str)
317{ 364{
318 struct icaltimetype tt = icaltime_null_time(); 365 struct icaltimetype tt = icaltime_null_time();
319 int size; 366 int size;
320 367
321 icalerror_check_arg_re(str!=0,"str",icaltime_null_time()); 368 icalerror_check_arg_re(str!=0,"str",icaltime_null_time());
322 369
323 size = strlen(str); 370 size = strlen(str);
324 371
325 if(size == 15) { /* floating time */ 372 if(size == 15) { /* floating time */
326 tt.is_utc = 0; 373 tt.is_utc = 0;
327 tt.is_date = 0; 374 tt.is_date = 0;
328 } else if (size == 16) { /* UTC time, ends in 'Z'*/ 375 } else if (size == 16) { /* UTC time, ends in 'Z'*/
376 if(str[15] != 'Z')
377 goto ERROR;
378
329 tt.is_utc = 1; 379 tt.is_utc = 1;
380 tt.zone = icaltimezone_get_utc_timezone();
330 tt.is_date = 0; 381 tt.is_date = 0;
331
332 if(str[15] != 'Z'){
333 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
334 return icaltime_null_time();
335 }
336
337 } else if (size == 8) { /* A DATE */ 382 } else if (size == 8) { /* A DATE */
338 tt.is_utc = 1; 383 tt.is_utc = 1;
339 tt.is_date = 1; 384 tt.is_date = 1;
340 } else { /* error */ 385 } else { /* error */
341 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); 386 goto ERROR;
342 return icaltime_null_time();
343 } 387 }
344 388
345 if(tt.is_date == 1){ 389 if(tt.is_date == 1){
346 sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day); 390 if (sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day) < 3)
391 goto ERROR;
347 } else { 392 } else {
348 char tsep; 393 char tsep;
349 sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day, 394 if (sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day,
350 &tsep,&tt.hour,&tt.minute,&tt.second); 395 &tsep,&tt.hour,&tt.minute,&tt.second) < 7)
351 396 goto ERROR;
352 if(tsep != 'T'){
353 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
354 return icaltime_null_time();
355 }
356 397
398 if(tsep != 'T')
399 goto ERROR;
357 } 400 }
358 401
359 return tt; 402 return tt;
403
404ERROR:
405 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
406 return icaltime_null_time();
360} 407}
361#endif
362 408
363char ctime_str[20];
364char* icaltime_as_ctime(struct icaltimetype t)
365{
366 time_t tt;
367
368 tt = icaltime_as_timet(t);
369 sprintf(ctime_str,"%s",ctime(&tt));
370 409
371 ctime_str[strlen(ctime_str)-1] = 0; 410/* Returns whether the specified year is a leap year. Year is the normal year,
411 e.g. 2001. */
412int
413icaltime_is_leap_year (const int year)
414{
372 415
373 return ctime_str; 416 if (year <= 1752)
417 return (year % 4 == 0);
418 else
419 return ( (year % 4==0) && (year % 100 !=0 )) || (year % 400 == 0);
374} 420}
375 421
422static int _days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
376 423
377short days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; 424int icaltime_days_in_month(const int month, const int year)
378
379short icaltime_days_in_month(short month,short year)
380{ 425{
381 426
382 int is_leap =0; 427 int days = _days_in_month[month];
383 int days = days_in_month[month];
384 428
385 assert(month > 0); 429 assert(month > 0);
386 assert(month <= 12); 430 assert(month <= 12);
387 431
388 if( (year % 4 == 0 && year % 100 != 0) ||
389 year % 400 == 0){
390 is_leap =1;
391 }
392
393 if( month == 2){ 432 if( month == 2){
394 days += is_leap; 433 days += icaltime_is_leap_year(year);
395 } 434 }
396 435
397 return days; 436 return days;
398} 437}
399 438
400/* 1-> Sunday, 7->Saturday */ 439/* 1-> Sunday, 7->Saturday */
401short icaltime_day_of_week(struct icaltimetype t){ 440int icaltime_day_of_week(const struct icaltimetype t){
441 UTinstant jt;
402 442
403 time_t tt = icaltime_as_timet(t); 443 memset(&jt,0,sizeof(UTinstant));
404 struct tm *tm;
405 444
406 if(t.is_utc == 1 || t.is_date == 1){ 445 jt.year = t.year;
407 tm = gmtime(&tt); 446 jt.month = t.month;
408 } else { 447 jt.day = t.day;
409 tm = localtime(&tt); 448 jt.i_hour = 0;
410 } 449 jt.i_minute = 0;
450 jt.i_second = 0;
451
452 juldat(&jt);
411 453
412 return tm->tm_wday+1; 454 return jt.weekday + 1;
413} 455}
414 456
415/* Day of the year that the first day of the week (Sunday) is on */ 457/** Day of the year that the first day of the week (Sunday) is on.
416short icaltime_start_doy_of_week(struct icaltimetype t){ 458 *
417 time_t tt = icaltime_as_timet(t); 459 * @todo Doesn't take into account different week start days.
418 time_t start_tt; 460 */
419 struct tm *stm; 461int icaltime_start_doy_of_week(const struct icaltimetype t){
420 int syear; 462 UTinstant jt;
421 463
422 stm = gmtime(&tt); 464 memset(&jt,0,sizeof(UTinstant));
423 syear = stm->tm_year;
424 465
425 start_tt = tt - stm->tm_wday*(60*60*24); 466 jt.year = t.year;
467 jt.month = t.month;
468 jt.day = t.day;
469 jt.i_hour = 0;
470 jt.i_minute = 0;
471 jt.i_second = 0;
426 472
427 stm = gmtime(&start_tt); 473 juldat(&jt);
428 474 caldat(&jt);
429 if(syear == stm->tm_year){
430 return stm->tm_yday+1;
431 } else {
432 /* return negative to indicate that start of week is in
433 previous year. */
434 int is_leap = 0;
435 int year = stm->tm_year;
436
437 if( (year % 4 == 0 && year % 100 != 0) ||
438 year % 400 == 0){
439 is_leap =1;
440 }
441 475
442 return (stm->tm_yday+1)-(365+is_leap); 476 return jt.day_of_year - jt.weekday;
443 }
444
445} 477}
446 478
447short icaltime_week_number(struct icaltimetype ictt) 479/**
480 * @todo Doesn't take into account the start day of the
481 * week. strftime assumes that weeks start on Monday.
482 */
483int icaltime_week_number(const struct icaltimetype ictt)
448{ 484{
449 char str[5]; 485 UTinstant jt;
450 time_t t = icaltime_as_timet(ictt);
451 int week_no;
452 486
453 strftime(str,5,"%V", gmtime(&t)); 487 memset(&jt,0,sizeof(UTinstant));
454 488
455 week_no = atoi(str); 489 jt.year = ictt.year;
490 jt.month = ictt.month;
491 jt.day = ictt.day;
492 jt.i_hour = 0;
493 jt.i_minute = 0;
494 jt.i_second = 0;
456 495
457 return week_no; 496 juldat(&jt);
497 caldat(&jt);
458 498
499 return (jt.day_of_year - jt.weekday) / 7;
459} 500}
460 501
502/* The first array is for non-leap years, the second for leap years*/
503static const int days_in_year[2][13] =
504{ /* jan feb mar apr may jun jul aug sep oct nov dec */
505 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
506 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
507};
461 508
462short icaltime_day_of_year(struct icaltimetype t){ 509/**
463 time_t tt; 510 *Returns the day of the year, counting from 1 (Jan 1st).
464 struct tm *stm; 511 */
465 struct set_tz_save old_tz; 512int icaltime_day_of_year(const struct icaltimetype t){
466 513 int is_leap = icaltime_is_leap_year (t.year);
467 tt = icaltime_as_timet(t);
468 514
469 old_tz = set_tz("UTC"); 515 return days_in_year[is_leap][t.month - 1] + t.day;
470 stm = localtime(&tt);
471 unset_tz(old_tz);
472
473 return stm->tm_yday+1;
474
475} 516}
476 517
518 /**@brief Contructor.
519 *
520 *Create a new time, given a day of year and a year.
521 */
477/* Jan 1 is day #1, not 0 */ 522/* Jan 1 is day #1, not 0 */
478struct icaltimetype icaltime_from_day_of_year(short doy, short year) 523struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year)
479{ 524{
480 struct tm stm; 525 struct icaltimetype tt = icaltime_null_date();
481 time_t tt; 526 int is_leap;
482 struct set_tz_save old_tz; 527 int month;
483 528 int doy = _doy;
484 /* Get the time of january 1 of this year*/ 529 int year = _year;
485 memset(&stm,0,sizeof(struct tm)); 530
486 stm.tm_year = year-1900; 531 is_leap = icaltime_is_leap_year(year);
487 stm.tm_mday = 1; 532
533 /* Zero and neg numbers represent days of the previous year */
534 if(doy <1){
535 year--;
536 is_leap = icaltime_is_leap_year(year);
537 doy += days_in_year[is_leap][12];
538 } else if(doy > days_in_year[is_leap][12]){
539 /* Move on to the next year*/
540 is_leap = icaltime_is_leap_year(year);
541 doy -= days_in_year[is_leap][12];
542 year++;
543 }
488 544
489 old_tz = set_tz("UTC"); 545 tt.year = year;
490 tt = mktime(&stm);
491 unset_tz(old_tz);
492 546
547 for (month = 11; month >= 0; month--) {
548 if (doy > days_in_year[is_leap][month]) {
549 tt.month = month + 1;
550 tt.day = doy - days_in_year[is_leap][month];
551 break;
552 }
553 }
493 554
494 /* Now add in the days */ 555 return tt;
556}
495 557
496 doy--; 558 /**@brief Constructor.
497 tt += doy *60*60*24; 559 *
560 *Return a null time, which indicates no time has been set.
561 *This time represents the beginning of the epoch.
562 */
563struct icaltimetype icaltime_null_time(void)
564{
565 struct icaltimetype t;
566 memset(&t,0,sizeof(struct icaltimetype));
498 567
499 return icaltime_from_timet(tt, 1); 568 return t;
500} 569}
501 570
502struct icaltimetype icaltime_null_time() 571 /**@brief Constructor.
572 *
573 *Return a null date, which indicates no time has been set.
574 */
575struct icaltimetype icaltime_null_date(void)
503{ 576{
504 struct icaltimetype t; 577 struct icaltimetype t;
505 memset(&t,0,sizeof(struct icaltimetype)); 578 memset(&t,0,sizeof(struct icaltimetype));
506 579
580 t.is_date = 1;
581
582 /*
583 * Init to -1 to match what icalyacc.y used to do.
584 * Does anything depend on this?
585 */
586 t.hour = -1;
587 t.minute = -1;
588 t.second = -1;
589
507 return t; 590 return t;
508} 591}
509 592
510 593
511int icaltime_is_valid_time(struct icaltimetype t){ 594/**
595 *Returns false if the time is clearly invalid, but is not null. This
596 *is usually the result of creating a new time type buy not clearing
597 *it, or setting one of the flags to an illegal value.
598 */
599int icaltime_is_valid_time(const struct icaltimetype t){
512 if(t.is_utc > 1 || t.is_utc < 0 || 600 if(t.is_utc > 1 || t.is_utc < 0 ||
513 t.year < 0 || t.year > 3000 || 601 t.year < 0 || t.year > 3000 ||
514 t.is_date > 1 || t.is_date < 0){ 602 t.is_date > 1 || t.is_date < 0){
515 return 0; 603 return 0;
516 } else { 604 } else {
517 return 1; 605 return 1;
518 } 606 }
519 607
520} 608}
521 609
522int icaltime_is_null_time(struct icaltimetype t) 610 /**@brief Returns true if time is a DATE
611 */
612int icaltime_is_date(const struct icaltimetype t) {
613
614 return t.is_date;
615}
616
617 /**@brief Returns true if time is relative to UTC zone
618 *
619 *@todo We should only check the zone
620 */
621int icaltime_is_utc(const struct icaltimetype t) {
622
623 return t.is_utc;
624}
625
626/**
627 *Return true if the time is null.
628 */
629int icaltime_is_null_time(const struct icaltimetype t)
523{ 630{
524 if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){ 631 if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){
525 return 1; 632 return 1;
526 } 633 }
527 634
528 return 0; 635 return 0;
529 636
530} 637}
531 638
532int icaltime_compare(struct icaltimetype a,struct icaltimetype b) 639/**
533{ 640 *Return -1, 0, or 1 to indicate that a<b, a==b, or a>b.
534 time_t t1 = icaltime_as_timet(a); 641 * This calls icaltime_compare function after converting them to the utc
535 time_t t2 = icaltime_as_timet(b); 642 * timezone.
536 643 */
537 if (t1 > t2) {
538 return 1;
539 } else if (t1 < t2) {
540 return -1;
541 } else {
542 return 0;
543 }
544 644
645int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in)
646{
647 int retval = 0;
648 struct icaltimetype a, b;
649
650 a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone());
651 b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone());
652
653 if (a.year > b.year)
654 retval = 1;
655 else if (a.year < b.year)
656 retval = -1;
657
658 else if (a.month > b.month)
659 retval = 1;
660 else if (a.month < b.month)
661 retval = -1;
662
663 else if (a.day > b.day)
664 retval = 1;
665 else if (a.day < b.day)
666 retval = -1;
667
668 /* if both are dates, we are done */
669 if (a.is_date && b.is_date)
670 return retval;
671
672 /* else, if we already found a difference, we are done */
673 else if (retval != 0)
674 return retval;
675
676 /* else, if only one is a date (and we already know the date part is equal),
677 then the other is greater */
678 else if (b.is_date)
679 retval = 1;
680 else if (a.is_date)
681 retval = -1;
682
683 else if (a.hour > b.hour)
684 retval = 1;
685 else if (a.hour < b.hour)
686 retval = -1;
687
688 else if (a.minute > b.minute)
689 retval = 1;
690 else if (a.minute < b.minute)
691 retval = -1;
692
693 else if (a.second > b.second)
694 retval = 1;
695 else if (a.second < b.second)
696 retval = -1;
697
698 return retval;
545} 699}
546 700
701/**
702 *like icaltime_compare, but only use the date parts.
703 */
704
547int 705int
548icaltime_compare_date_only (struct icaltimetype a, struct icaltimetype b) 706icaltime_compare_date_only(const struct icaltimetype a_in, const struct icaltimetype b_in)
549{ 707{
550 time_t t1; 708 int retval;
551 time_t t2; 709 struct icaltimetype a, b;
552 710
553 if (a.year == b.year && a.month == b.month && a.day == b.day) 711 a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone());
554 return 0; 712 b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone());
555 713
556 t1 = icaltime_as_timet (a); 714 if (a.year > b.year)
557 t2 = icaltime_as_timet (b); 715 retval = 1;
558 716 else if (a.year < b.year)
559 if (t1 > t2) 717 retval = -1;
560 return 1; 718
561 else if (t1 < t2) 719 else if (a.month > b.month)
562 return -1; 720 retval = 1;
563 else { 721 else if (a.month < b.month)
564 /* not reached */ 722 retval = -1;
565 assert (0); 723
566 return 0; 724 else if (a.day > b.day)
567 } 725 retval = 1;
568} 726 else if (a.day < b.day)
727 retval = -1;
569 728
729 else
730 retval = 0;
731
732 return retval;
733}
570 734
571/* These are defined in icalduration.c: 735/* These are defined in icalduration.c:
572struct icaltimetype icaltime_add(struct icaltimetype t, 736struct icaltimetype icaltime_add(struct icaltimetype t,
573 struct icaldurationtype d) 737 struct icaldurationtype d)
574struct icaldurationtype icaltime_subtract(struct icaltimetype t1, 738struct icaldurationtype icaltime_subtract(struct icaltimetype t1,
575 struct icaltimetype t2) 739 struct icaltimetype t2)
576*/ 740*/
577 741
742
743
744 /**@brief Internal, shouldn't be part of the public API
745 *
746 *Adds (or subtracts) a time from a icaltimetype.
747 *NOTE: This function is exactly the same as icaltimezone_adjust_change()
748 *except for the type of the first parameter.
749 */
750void
751icaltime_adjust(struct icaltimetype *tt, const int days, const int hours,
752 const int minutes, const int seconds) {
753
754 int second, minute, hour, day;
755 int minutes_overflow, hours_overflow, days_overflow, years_overflow;
756 int days_in_month;
757
758 /* Add on the seconds. */
759 second = tt->second + seconds;
760 tt->second = second % 60;
761 minutes_overflow = second / 60;
762 if (tt->second < 0) {
763 tt->second += 60;
764 minutes_overflow--;
765 }
766
767 /* Add on the minutes. */
768 minute = tt->minute + minutes + minutes_overflow;
769 tt->minute = minute % 60;
770 hours_overflow = minute / 60;
771 if (tt->minute < 0) {
772 tt->minute += 60;
773 hours_overflow--;
774 }
775
776 /* Add on the hours. */
777 hour = tt->hour + hours + hours_overflow;
778 tt->hour = hour % 24;
779 days_overflow = hour / 24;
780 if (tt->hour < 0) {
781 tt->hour += 24;
782 days_overflow--;
783 }
784
785 /* Normalize the month. We do this before handling the day since we may
786 need to know what month it is to get the number of days in it.
787 Note that months are 1 to 12, so we have to be a bit careful. */
788 if (tt->month >= 13) {
789 years_overflow = (tt->month - 1) / 12;
790 tt->year += years_overflow;
791 tt->month -= years_overflow * 12;
792 } else if (tt->month <= 0) {
793 /* 0 to -11 is -1 year out, -12 to -23 is -2 years. */
794 years_overflow = (tt->month / 12) - 1;
795 tt->year += years_overflow;
796 tt->month -= years_overflow * 12;
797 }
798
799 /* Add on the days. */
800 day = tt->day + days + days_overflow;
801 if (day > 0) {
802 for (;;) {
803 days_in_month = icaltime_days_in_month (tt->month, tt->year);
804 if (day <= days_in_month)
805 break;
806
807 tt->month++;
808 if (tt->month >= 13) {
809 tt->year++;
810 tt->month = 1;
811 }
812
813 day -= days_in_month;
814 }
815 } else {
816 while (day <= 0) {
817 if (tt->month == 1) {
818 tt->year--;
819 tt->month = 12;
820 } else {
821 tt->month--;
822 }
823
824 day += icaltime_days_in_month (tt->month, tt->year);
825 }
826 }
827 tt->day = day;
828}
829
830 /**@brief Convert time to a given timezone
831 *
832 *Convert a time from its native timezone to a given timezone.
833 *
834 *If tt is a date, the returned time is an exact
835 *copy of the input. If it's a floating time, the returned object
836 *represents the same time translated to the given timezone.
837 *Otherwise the time will be converted to the new
838 *time zone, and its native timezone set to the right timezone.
839 */
840struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt,
841 icaltimezone *zone) {
842
843 struct icaltimetype ret = tt;
844
845 /* If it's a date do nothing */
846 if (tt.is_date) {
847 return ret;
848 }
849
850 if (tt.zone == zone) {
851 return ret;
852 }
853
854 /* If it's a floating time we don't want to adjust the time */
855 if (tt.zone != NULL) {
856 icaltimezone_convert_time(&ret, tt.zone, zone);
857 }
858
859 ret.zone = zone;
860 if (zone == icaltimezone_get_utc_timezone()) {
861 ret.is_utc = 1;
862 } else {
863 ret.is_utc = 0;
864 }
865
866 return ret;
867}
868
869const icaltimezone *
870icaltime_get_timezone(const struct icaltimetype t) {
871
872 return t.zone;
873}
874
875char *
876icaltime_get_tzid(const struct icaltimetype t) {
877
878 if (t.zone != NULL) {
879 return icaltimezone_get_tzid(t.zone);
880 } else {
881 return NULL;
882 }
883}
884
885 /**@brief Set the timezone
886 *
887 *Force the icaltime to be interpreted relative to another timezone.
888 *If you need to do timezone conversion, applying offset adjustments,
889 *then you should use icaltime_convert_to_timezone instead.
890 */
891struct icaltimetype
892icaltime_set_timezone(struct icaltimetype *t, const icaltimezone *zone) {
893
894 /* If it's a date do nothing */
895 if (t->is_date) {
896 return *t;
897 }
898
899 if (t->zone == zone) {
900 return *t;
901 }
902
903 t->zone = zone;
904 if (zone == icaltimezone_get_utc_timezone()) {
905 t->is_utc = 1;
906 } else {
907 t->is_utc = 0;
908 }
909
910 return *t;
911}
912
913
914/**
915 * @brief builds an icaltimespan given a start time, end time and busy value.
916 *
917 * @param dtstart The beginning time of the span, can be a date-time
918 * or just a date.
919 * @param dtend The end time of the span.
920 * @param is_busy A boolean value, 0/1.
921 * @return A span using the supplied values.
922 *
923 * returned span contains times specified in UTC.
924 */
925
926icaltime_span icaltime_span_new(struct icaltimetype dtstart,
927 struct icaltimetype dtend,
928 int is_busy)
929{
930 icaltime_span span;
931
932 span.is_busy = is_busy;
933
934 span.start = icaltime_as_timet_with_zone(dtstart,
935 icaltimezone_get_utc_timezone());
936
937 if (icaltime_is_null_time(dtend)) {
938 if (!icaltime_is_date(dtstart)) {
939 /* If dtstart is a DATE-TIME and there is no DTEND nor DURATION
940 it takes no time */
941 span.end = span.start;
942 return span;
943 } else {
944 dtend = dtstart;
945 }
946 }
947
948 span.end = icaltime_as_timet_with_zone(dtend, icaltimezone_get_utc_timezone());
949
950 if (icaltime_is_date(dtstart)) {
951 /* no time specified, go until the end of the day..*/
952 span.end += 60*60*24 - 1;
953 }
954 return span;
955}
956
957
958/** @brief Returns true if the two spans overlap
959 *
960 * @param s1 1st span to test
961 * @param s2 2nd span to test
962 * @return boolean value
963 *
964 * The result is calculated by testing if the start time of s1 is contained
965 * by the s2 span, or if the end time of s1 is contained by the s2 span.
966 *
967 * Also returns true if the spans are equal.
968 *
969 * Note, this will return false if the spans are adjacent.
970 */
971
972int icaltime_span_overlaps(icaltime_span *s1,
973 icaltime_span *s2)
974{
975 /* s1->start in s2 */
976 if (s1->start > s2->start && s1->start < s2->end)
977 return 1;
978
979 /* s1->end in s2 */
980 if (s1->end > s2->start && s1->end < s2->end)
981 return 1;
982
983 /* s2->start in s1 */
984 if (s2->start > s1->start && s2->start < s1->end)
985 return 1;
986
987 /* s2->end in s1 */
988 if (s2->end > s1->start && s2->end < s1->end)
989 return 1;
990
991 if (s1->start == s2->start && s1->end == s2->end)
992 return 1;
993
994 return 0;
995}
996
997/** @brief Returns true if the span is totally within the containing
998 * span
999 *
1000 * @param s The span to test for.
1001 * @param container The span to test against.
1002 * @return boolean value.
1003 *
1004 */
1005
1006int icaltime_span_contains(icaltime_span *s,
1007 icaltime_span *container)
1008{
1009
1010 if ((s->start >= container->start && s->start < container->end) &&
1011 (s->end <= container->end && s->end > container->start))
1012 return 1;
1013
1014 return 0;
1015}