1 /* -*- Mode: C -*-
2 ======================================================================
3 FILE: icaltime.c
4 CREATOR: eric 02 June 2000
5
6 $Id: icaltime.c,v 1.71 2008-01-29 18:31:48 dothebart Exp $
7 $Locker: $
8
9 (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org>
10 http://www.softwarestudio.org
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of either:
14
15 The LGPL as published by the Free Software Foundation, version
16 2.1, available at: http://www.fsf.org/copyleft/lesser.html
17
18 Or:
19
20 The Mozilla Public License Version 1.0. You may obtain a copy of
21 the License at http://www.mozilla.org/MPL/
22
23 The Original Code is eric. The Initial Developer of the Original
24 Code is Eric Busboom
25
26
27 ======================================================================*/
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "icaltime.h"
34 #include <assert.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <time.h>
39
40 #include "astime.h" /* Julian data handling routines */
41
42 #include "icalerror.h"
43 #include "icalmemory.h"
44
45 #include "icaltimezone.h"
46 #include "icalvalue.h"
47
48 #ifdef WIN32
49 #include <windows.h>
50
51 #if defined(_MSC_VER) && (_MSC_VER < 1900)
52 #define snprintf _snprintf
53 #endif
54 #define strcasecmp stricmp
55 #endif
56
57 #ifdef WIN32
58 /* Undef the similar macro from pthread.h, it doesn't check if
59 * gmtime() returns NULL.
60 */
61 #undef gmtime_r
62
63 /* The gmtime() in Microsoft's C library is MT-safe */
64 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
65 #endif
66
67 #ifdef HAVE_PTHREAD
68 #include <pthread.h>
69 static pthread_mutex_t tzid_mutex = PTHREAD_MUTEX_INITIALIZER;
70 #endif
71
72 /*
73 * Function to convert a struct tm time specification
74 * to an ANSI time_t using the specified time zone.
75 * This is different from the standard mktime() function
76 * in that we don't want the automatic adjustments for
77 * local daylight savings time applied to the result.
78 * This function expects well-formed input.
79 */
make_time(struct tm * tm,int tzm)80 static time_t make_time(struct tm *tm, int tzm)
81 {
82 time_t tim;
83
84 static int days[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 };
85
86 /* check that year specification within range */
87
88 if (tm->tm_year < 70 || tm->tm_year > 138)
89 return((time_t) -1);
90
91 /* check that month specification within range */
92
93 if (tm->tm_mon < 0 || tm->tm_mon > 11)
94 return((time_t) -1);
95
96 /* check for upper bound of Jan 17, 2038 (to avoid possibility of
97 32-bit arithmetic overflow) */
98
99 if (tm->tm_year == 138) {
100 if (tm->tm_mon > 0)
101 return((time_t) -1);
102 else if (tm->tm_mday > 17)
103 return((time_t) -1);
104 }
105
106 /*
107 * calculate elapsed days since start of the epoch (midnight Jan
108 * 1st, 1970 UTC) 17 = number of leap years between 1900 and 1970
109 * (number of leap days to subtract)
110 */
111
112 tim = (tm->tm_year - 70) * 365 + ((tm->tm_year - 1) / 4) - 17;
113
114 /* add number of days elapsed in the current year */
115
116 tim += days[tm->tm_mon];
117
118 /* check and adjust for leap years (the leap year check only valid
119 during the 32-bit era */
120
121 if ((tm->tm_year & 3) == 0 && tm->tm_mon > 1)
122 tim += 1;
123
124 /* elapsed days to current date */
125
126 tim += tm->tm_mday;
127
128
129 /* calculate elapsed hours since start of the epoch */
130
131 tim = tim * 24 + tm->tm_hour;
132
133 /* calculate elapsed minutes since start of the epoch */
134
135 tim = tim * 60 + tm->tm_min;
136
137 /* adjust per time zone specification */
138
139 tim -= tzm;
140
141 /* calculate elapsed seconds since start of the epoch */
142
143 tim = tim * 60 + tm->tm_sec;
144
145 /* return number of seconds since start of the epoch */
146
147 return(tim);
148 }
149
150 /** @brief Constructor (deprecated).
151 *
152 * Convert seconds past UNIX epoch to a timetype.
153 *
154 * @deprecated This constructor is deprecated and shouldn't be used in
155 * new software. Use icaltime_from_timet_with_zone(time_t, int,
156 * icaltimezone *) instead. In the meantime, calls to this method
157 * return a floating time, which can always be converted to a local
158 * time with an appropriate call to icaltime_convert_to_zone().
159 */
160
161 struct icaltimetype
icaltime_from_timet(const time_t tm,const int is_date)162 icaltime_from_timet(const time_t tm, const int is_date)
163 {
164 #ifndef NO_WARN_DEPRECATED
165 icalerror_warn("icaltime_from_timet() is DEPRECATED, use icaltime_from_timet_with_zone() instead");
166 #endif
167
168 return icaltime_from_timet_with_zone(tm, is_date, 0);
169 }
170
171
172 /** @brief Constructor.
173 *
174 * @param tm The time
175 * @param is_date Boolean: 1 means we should treat tm as a DATE
176 * @param zone The timezone tm is in, NULL means to treat tm as a
177 * floating time
178 *
179 * Return a new icaltime instance, initialized to the given time
180 * expressed as seconds past UNIX epoch, optionally using the given
181 * timezone.
182 *
183 * If the caller specifies the is_date param as TRUE, the returned
184 * object is of DATE type, otherwise the input is meant to be of
185 * DATE-TIME type.
186 * If the zone is not specified (NULL zone param) the time is taken
187 * to be floating, that is, valid in any timezone. Note that, in
188 * addition to the uses specified in [RFC2445], this can be used
189 * when doing simple math on couples of times.
190 * If the zone is specified (UTC or otherwise), it's stored in the
191 * object and it's used as the native timezone for this object.
192 * This means that the caller can convert this time to a different
193 * target timezone with no need to store the source timezone.
194 *
195 */
196 struct icaltimetype
icaltime_from_timet_with_zone(const time_t tm,const int is_date,const icaltimezone * zone)197 icaltime_from_timet_with_zone(const time_t tm, const int is_date,
198 const icaltimezone *zone)
199 {
200 struct icaltimetype tt;
201 struct tm t;
202 icaltimezone *utc_zone;
203
204 utc_zone = icaltimezone_get_utc_timezone ();
205
206 /* Convert the time_t to a struct tm in UTC time. We can trust gmtime
207 for this. */
208 #ifdef HAVE_PTHREAD
209 gmtime_r (&tm, &t);
210 #else
211 t = *(gmtime (&tm));
212 #endif
213
214 tt.year = t.tm_year + 1900;
215 tt.month = t.tm_mon + 1;
216 tt.day = t.tm_mday;
217 tt.hour = t.tm_hour;
218 tt.minute = t.tm_min;
219 tt.second = t.tm_sec;
220 tt.is_date = 0;
221 tt.is_utc = (zone == utc_zone) ? 1 : 0;
222 tt.is_daylight = 0;
223 tt.zone = NULL;
224
225 /* Use our timezone functions to convert to the required timezone. */
226 icaltimezone_convert_time (&tt, utc_zone, (icaltimezone *)zone);
227
228 tt.is_date = is_date;
229
230 /* If it is a DATE value, make sure hour, minute & second are 0. */
231 if (is_date) {
232 tt.hour = 0;
233 tt.minute = 0;
234 tt.second = 0;
235 }
236
237 return tt;
238 }
239
240 /** @brief Convenience constructor.
241 *
242 * Returns the current time in the given timezone, as an icaltimetype.
243 */
icaltime_current_time_with_zone(const icaltimezone * zone)244 struct icaltimetype icaltime_current_time_with_zone(const icaltimezone *zone)
245 {
246 return icaltime_from_timet_with_zone (time (NULL), 0, zone);
247 }
248
249 /** @brief Convenience constructor.
250 *
251 * Returns the current day as an icaltimetype, with is_date set.
252 */
icaltime_today(void)253 struct icaltimetype icaltime_today(void)
254 {
255 return icaltime_from_timet_with_zone (time (NULL), 1, NULL);
256 }
257
258 /** @brief Return the time as seconds past the UNIX epoch
259 *
260 * While this function is not currently deprecated, it probably won't do
261 * what you expect, unless you know what you're doing. In particular, you
262 * should only pass an icaltime in UTC, since no conversion is done. Even
263 * in that case, it's probably better to just use
264 * icaltime_as_timet_with_zone().
265 */
icaltime_as_timet(const struct icaltimetype tt)266 time_t icaltime_as_timet(const struct icaltimetype tt)
267 {
268 struct tm stm;
269 time_t t;
270
271 /* If the time is the special null time, return 0. */
272 if (icaltime_is_null_time(tt)) {
273 return 0;
274 }
275
276 /* Copy the icaltimetype to a struct tm. */
277 memset (&stm, 0, sizeof (struct tm));
278
279 if (icaltime_is_date(tt)) {
280 stm.tm_sec = stm.tm_min = stm.tm_hour = 0;
281 } else {
282 stm.tm_sec = tt.second;
283 stm.tm_min = tt.minute;
284 stm.tm_hour = tt.hour;
285 }
286
287 stm.tm_mday = tt.day;
288 stm.tm_mon = tt.month-1;
289 stm.tm_year = tt.year-1900;
290 stm.tm_isdst = -1;
291
292 t = make_time(&stm, 0);
293
294 return t;
295
296 }
297
298
299 /* Structure used by set_tz to hold an old value of TZ, and the new
300 value, which is in memory we will have to free in unset_tz */
301 /* This will hold the last "TZ=XXX" string we used with putenv(). After we
302 call putenv() again to set a new TZ string, we can free the previous one.
303 As far as I know, no libc implementations actually free the memory used in
304 the environment variables (how could they know if it is a static string or
305 a malloc'ed string?), so we have to free it ourselves. */
306 static char* saved_tz = NULL;
307
308 /* If you use set_tz(), you must call unset_tz() some time later to restore the
309 original TZ. Pass unset_tz() the string that set_tz() returns. Call both the functions
310 locking the tzid mutex as in icaltime_as_timet_with_zone */
set_tz(const char * tzid)311 char* set_tz(const char* tzid)
312 {
313 char *old_tz, *old_tz_copy = NULL, *new_tz;
314
315 /* Get the old TZ setting and save a copy of it to return. */
316 old_tz = getenv("TZ");
317 if(old_tz){
318 old_tz_copy = (char*)malloc(strlen (old_tz) + 4);
319
320 if(old_tz_copy == 0){
321 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
322 return 0;
323 }
324
325 strcpy (old_tz_copy, "TZ=");
326 strcpy (old_tz_copy + 3, old_tz);
327 }
328
329 /* Create the new TZ string. */
330 new_tz = (char*)malloc(strlen (tzid) + 4);
331
332 if(new_tz == 0){
333 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
334 free(old_tz_copy);
335 return 0;
336 }
337
338 strcpy (new_tz, "TZ=");
339 strcpy (new_tz + 3, tzid);
340
341 /* Add the new TZ to the environment. */
342 putenv(new_tz);
343
344 /* Free any previous TZ environment string we have used in a synchronized manner. */
345
346 free (saved_tz);
347
348 /* Save a pointer to the TZ string we just set, so we can free it later. */
349 saved_tz = new_tz;
350
351 return old_tz_copy; /* This will be zero if the TZ env var was not set */
352 }
353
unset_tz(char * tzstr)354 void unset_tz(char *tzstr)
355 {
356 /* restore the original environment */
357
358 if(tzstr!=0){
359 putenv(tzstr);
360 } else {
361 /* Delete from environment. We prefer unsetenv(3) over putenv(3)
362 because the former is POSIX and behaves consistently. The later
363 does not unset the variable in some systems (like NetBSD), leaving
364 it with an empty value. This causes problems later because further
365 calls to time related functions in libc will treat times in UTC. */
366 #ifdef HAVE_UNSETENV
367 unsetenv("TZ");
368 #else
369 putenv("TZ");
370 #endif
371 }
372
373 /* Free any previous TZ environment string we have used in a synchronized manner */
374 free (saved_tz);
375
376 /* Save a pointer to the TZ string we just set, so we can free it later.
377 (This can possibly be NULL if there was no TZ to restore.) */
378 saved_tz = tzstr;
379 }
380
381 /** Return the time as seconds past the UNIX epoch, using the
382 * given timezone.
383 *
384 * This convenience method combines a call to icaltime_convert_to_zone()
385 * with a call to icaltime_as_timet().
386 * If the input timezone is null, no conversion is done; that is, the
387 * time is simply returned as time_t in its native timezone.
388 */
icaltime_as_timet_with_zone(const struct icaltimetype tt,const icaltimezone * zone)389 time_t icaltime_as_timet_with_zone(const struct icaltimetype tt,
390 const icaltimezone *zone)
391 {
392 icaltimezone *utc_zone;
393 struct tm stm;
394 time_t t;
395 char *old_tz;
396 struct icaltimetype local_tt;
397
398 utc_zone = icaltimezone_get_utc_timezone ();
399
400 /* If the time is the special null time, return 0. */
401 if (icaltime_is_null_time(tt)) {
402 return 0;
403 }
404
405 local_tt = tt;
406
407 /* Clear the is_date flag, so we can convert the time. */
408 local_tt.is_date = 0;
409
410 /* Use our timezone functions to convert to UTC. */
411 icaltimezone_convert_time (&local_tt, (icaltimezone *)zone, utc_zone);
412
413 /* Copy the icaltimetype to a struct tm. */
414 memset (&stm, 0, sizeof (struct tm));
415
416 stm.tm_sec = local_tt.second;
417 stm.tm_min = local_tt.minute;
418 stm.tm_hour = local_tt.hour;
419 stm.tm_mday = local_tt.day;
420 stm.tm_mon = local_tt.month-1;
421 stm.tm_year = local_tt.year-1900;
422 stm.tm_isdst = -1;
423 /* The functions putenv and mktime are not thread safe, inserting a lock
424 to prevent any crashes */
425
426 #ifdef HAVE_PTHREAD
427 pthread_mutex_lock (&tzid_mutex);
428 #endif
429
430 /* Set TZ to UTC and use mktime to convert to a time_t. */
431 old_tz = set_tz ("UTC");
432 #ifdef WIN32
433 tzset ();
434 #endif
435
436 t = mktime (&stm);
437 unset_tz (old_tz);
438 #ifdef WIN32
439 tzset ();
440 #endif
441
442 #ifdef HAVE_PTHREAD
443 pthread_mutex_unlock (&tzid_mutex);
444 #endif
445 return t;
446 }
447
icaltime_as_ical_string(const struct icaltimetype tt)448 const char* icaltime_as_ical_string(const struct icaltimetype tt)
449 {
450 char *buf;
451 buf = icaltime_as_ical_string_r(tt);
452 icalmemory_add_tmp_buffer(buf);
453 return buf;
454 }
455
456
457 /**
458 * Return a string represention of the time, in RFC2445 format. The
459 * string is owned by libical
460 */
icaltime_as_ical_string_r(const struct icaltimetype tt)461 char* icaltime_as_ical_string_r(const struct icaltimetype tt)
462 {
463 size_t size = 17;
464 char* buf = icalmemory_new_buffer(size);
465
466 if(tt.is_date){
467 snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day);
468 } else {
469 const char* fmt;
470 if(tt.is_utc){
471 fmt = "%04d%02d%02dT%02d%02d%02dZ";
472 } else {
473 fmt = "%04d%02d%02dT%02d%02d%02d";
474 }
475 snprintf(buf, size,fmt,tt.year,tt.month,tt.day,
476 tt.hour,tt.minute,tt.second);
477 }
478
479 return buf;
480 }
481
482
483 /**
484 * Reset all of the time components to be in their normal ranges. For
485 * instance, given a time with minutes=70, the minutes will be reduces
486 * to 10, and the hour incremented. This allows the caller to do
487 * arithmetic on times without worrying about overflow or
488 * underflow.
489 *
490 * Implementation note: we call icaltime_adjust() with no adjustment.
491 */
icaltime_normalize(const struct icaltimetype tt)492 struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
493 {
494 struct icaltimetype ret = tt;
495 icaltime_adjust(&ret, 0, 0, 0, 0);
496 return ret;
497 }
498
499
500
501 /** @brief Contructor.
502 *
503 * Create a time from an ISO format string.
504 *
505 * @todo If the given string specifies a DATE-TIME not in UTC, there
506 * is no way to know if this is a floating time or really refers to a
507 * timezone. We should probably add a new constructor:
508 * icaltime_from_string_with_zone()
509 */
icaltime_from_string(const char * str)510 struct icaltimetype icaltime_from_string(const char* str)
511 {
512 struct icaltimetype tt = icaltime_null_time();
513 int size;
514
515 icalerror_check_arg_re(str!=0,"str",icaltime_null_time());
516
517 size = strlen(str);
518
519 if ((size == 15) || (size == 19)) { /* floating time with/without separators*/
520 tt.is_utc = 0;
521 tt.is_date = 0;
522 } else if ((size == 16) || (size == 20)) { /* UTC time, ends in 'Z'*/
523 if ((str[size-1] != 'Z'))
524 goto FAIL;
525
526 tt.is_utc = 1;
527 tt.zone = icaltimezone_get_utc_timezone();
528 tt.is_date = 0;
529 } else if ((size == 8) || (size == 10)) { /* A DATE */
530 tt.is_utc = 0;
531 tt.is_date = 1;
532 } else { /* error */
533 goto FAIL;
534 }
535
536 if (tt.is_date == 1){
537 if (size == 10) {
538 char dsep1, dsep2;
539 if (sscanf(str,"%04d%c%02d%c%02d",&tt.year,&dsep1,&tt.month,&dsep2,&tt.day) < 5)
540 goto FAIL;
541 if ((dsep1 != '-') || (dsep2 != '-'))
542 goto FAIL;
543 } else if (sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day) < 3) {
544 goto FAIL;
545 }
546 } else {
547 if (size > 16 ) {
548 char dsep1, dsep2, tsep, tsep1, tsep2;
549 if (sscanf(str,"%04d%c%02d%c%02d%c%02d%c%02d%c%02d",&tt.year,&dsep1,&tt.month,&dsep2,
550 &tt.day,&tsep,&tt.hour,&tsep1,&tt.minute,&tsep2,&tt.second) < 11)
551 goto FAIL;
552
553 if((tsep != 'T') || (dsep1 != '-') || (dsep2 != '-') || (tsep1 != ':') || (tsep2 != ':'))
554 goto FAIL;
555
556 } else {
557 char tsep;
558 if (sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day,
559 &tsep,&tt.hour,&tt.minute,&tt.second) < 7)
560 goto FAIL;
561
562 if(tsep != 'T')
563 goto FAIL;
564 }
565 }
566
567 return tt;
568
569 FAIL:
570 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
571 return icaltime_null_time();
572 }
573
574
575 /* Returns whether the specified year is a leap year. Year is the normal year,
576 e.g. 2001. */
577 int
icaltime_is_leap_year(const int year)578 icaltime_is_leap_year (const int year)
579 {
580
581 if (year <= 1752)
582 return (year % 4 == 0);
583 else
584 return ( (year % 4==0) && (year % 100 !=0 )) || (year % 400 == 0);
585 }
586
587
588 int
ycaltime_days_in_year(const int year)589 ycaltime_days_in_year (const int year)
590 {
591 if (icaltime_is_leap_year (year))
592 return 366;
593 else return 365;
594 }
595
596 static int _days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
597
icaltime_days_in_month(const int month,const int year)598 int icaltime_days_in_month(const int month, const int year)
599 {
600
601 int days = _days_in_month[month];
602
603 /* The old code aborting if it was passed a parameter like BYMONTH=0
604 * Unfortunately it's not practical right now to pass an error all
605 * the way up the stack, so instead of aborting we're going to apply
606 * the GIGO principle and simply return '30 days' if we get an
607 * invalid month. Modern applications cannot tolerate crashing.
608 * assert(month > 0);
609 * assert(month <= 12);
610 */
611 if ((month < 1) || (month > 12)) {
612 return 30;
613 }
614
615 if( month == 2){
616 days += icaltime_is_leap_year(year);
617 }
618
619 return days;
620 }
621
622 /* 1-> Sunday, 7->Saturday */
icaltime_day_of_week(const struct icaltimetype t)623 int icaltime_day_of_week(const struct icaltimetype t){
624 UTinstant jt;
625
626 memset(&jt,0,sizeof(UTinstant));
627
628 jt.year = t.year;
629 jt.month = t.month;
630 jt.day = t.day;
631 jt.i_hour = 0;
632 jt.i_minute = 0;
633 jt.i_second = 0;
634
635 juldat(&jt);
636
637 return jt.weekday + 1;
638 }
639
640 /** Day of the year that the first day of the week (Sunday) is on.
641 */
icaltime_start_doy_week(const struct icaltimetype t,int fdow)642 int icaltime_start_doy_week(const struct icaltimetype t, int fdow){
643 UTinstant jt;
644 int delta;
645
646 memset(&jt,0,sizeof(UTinstant));
647
648 jt.year = t.year;
649 jt.month = t.month;
650 jt.day = t.day;
651 jt.i_hour = 0;
652 jt.i_minute = 0;
653 jt.i_second = 0;
654
655 juldat(&jt);
656 caldat(&jt);
657
658 delta = jt.weekday - (fdow - 1);
659 if (delta < 0) delta += 7;
660 return jt.day_of_year - delta;
661 }
662
663 /** Day of the year that the first day of the week (Sunday) is on.
664 *
665 * @deprecated Doesn't take into account different week start days.
666 */
icaltime_start_doy_of_week(const struct icaltimetype t)667 int icaltime_start_doy_of_week(const struct icaltimetype t){
668
669 #ifndef NO_WARN_DEPRECATED
670 icalerror_warn("icaltime_start_doy_of_week() is DEPRECATED, use\
671 icaltime_start_doy_week() instead");
672 #endif
673
674 return icaltime_start_doy_week(t, 1);
675 }
676
677 /**
678 * @todo Doesn't take into account the start day of the
679 * week. strftime assumes that weeks start on Monday.
680 */
icaltime_week_number(const struct icaltimetype ictt)681 int icaltime_week_number(const struct icaltimetype ictt)
682 {
683 UTinstant jt;
684
685 memset(&jt,0,sizeof(UTinstant));
686
687 jt.year = ictt.year;
688 jt.month = ictt.month;
689 jt.day = ictt.day;
690 jt.i_hour = 0;
691 jt.i_minute = 0;
692 jt.i_second = 0;
693
694 juldat(&jt);
695 caldat(&jt);
696
697 return (jt.day_of_year - jt.weekday) / 7;
698 }
699
700 /* The first array is for non-leap years, the second for leap years*/
701 static const int days_in_year_passed_month[2][13] =
702 { /* jan feb mar apr may jun jul aug sep oct nov dec */
703 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
704 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
705 };
706
707 /**
708 * Returns the day of the year, counting from 1 (Jan 1st).
709 */
icaltime_day_of_year(const struct icaltimetype t)710 int icaltime_day_of_year(const struct icaltimetype t){
711 int is_leap = icaltime_is_leap_year (t.year);
712
713 return days_in_year_passed_month[is_leap][t.month - 1] + t.day;
714 }
715
716 /** @brief Contructor.
717 *
718 * Create a new time, given a day of year and a year.
719 */
720 /* Jan 1 is day #1, not 0 */
icaltime_from_day_of_year(const int _doy,const int _year)721 struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year)
722 {
723 struct icaltimetype tt = icaltime_null_date();
724 int is_leap;
725 int month;
726 int doy = _doy;
727 int year = _year;
728
729 is_leap = icaltime_is_leap_year(year);
730
731 /* Zero and neg numbers represent days of the previous year */
732 if(doy <1){
733 year--;
734 is_leap = icaltime_is_leap_year(year);
735 doy += days_in_year_passed_month[is_leap][12];
736 } else if(doy > days_in_year_passed_month[is_leap][12]){
737 /* Move on to the next year*/
738 is_leap = icaltime_is_leap_year(year);
739 doy -= days_in_year_passed_month[is_leap][12];
740 year++;
741 }
742
743 tt.year = year;
744
745 for (month = 11; month >= 0; month--) {
746 if (doy > days_in_year_passed_month[is_leap][month]) {
747 tt.month = month + 1;
748 tt.day = doy - days_in_year_passed_month[is_leap][month];
749 break;
750 }
751 }
752
753 return tt;
754 }
755
756 /** @brief Constructor.
757 *
758 * Return a null time, which indicates no time has been set.
759 * This time represents the beginning of the epoch.
760 */
icaltime_null_time(void)761 struct icaltimetype icaltime_null_time(void)
762 {
763 struct icaltimetype t;
764 memset(&t,0,sizeof(struct icaltimetype));
765
766 return t;
767 }
768
769 /** @brief Constructor.
770 *
771 * Return a null date, which indicates no time has been set.
772 */
icaltime_null_date(void)773 struct icaltimetype icaltime_null_date(void)
774 {
775 struct icaltimetype t;
776 memset(&t,0,sizeof(struct icaltimetype));
777
778 t.is_date = 1;
779
780 /*
781 * Init to -1 to match what icalyacc.y used to do.
782 * Does anything depend on this?
783 */
784 t.hour = -1;
785 t.minute = -1;
786 t.second = -1;
787
788 return t;
789 }
790
791
792 /**
793 * Returns false if the time is clearly invalid, but is not null. This
794 * is usually the result of creating a new time type buy not clearing
795 * it, or setting one of the flags to an illegal value.
796 */
icaltime_is_valid_time(const struct icaltimetype t)797 int icaltime_is_valid_time(const struct icaltimetype t){
798 if(t.is_utc > 1 || t.is_utc < 0 ||
799 t.year < 0 || t.year > 3000 ||
800 t.is_date > 1 || t.is_date < 0){
801 return 0;
802 } else {
803 return 1;
804 }
805
806 }
807
808 /** @brief Returns true if time is a DATE
809 */
icaltime_is_date(const struct icaltimetype t)810 int icaltime_is_date(const struct icaltimetype t) {
811
812 return t.is_date;
813 }
814
815 /** @brief Returns true if time is relative to UTC zone
816 *
817 * @todo We should only check the zone
818 */
icaltime_is_utc(const struct icaltimetype t)819 int icaltime_is_utc(const struct icaltimetype t) {
820
821 return t.is_utc;
822 }
823
824 /**
825 * Return true if the time is null.
826 */
icaltime_is_null_time(const struct icaltimetype t)827 int icaltime_is_null_time(const struct icaltimetype t)
828 {
829 if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){
830 return 1;
831 }
832
833 return 0;
834
835 }
836
837 /**
838 * Return -1, 0, or 1 to indicate that a<b, a==b, or a>b.
839 * This calls icaltime_compare function after converting them to the utc
840 * timezone.
841 */
842
icaltime_compare(const struct icaltimetype a_in,const struct icaltimetype b_in)843 int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in)
844 {
845 struct icaltimetype a, b;
846
847 a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone());
848 b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone());
849
850 if (a.year > b.year)
851 return 1;
852 else if (a.year < b.year)
853 return -1;
854
855 else if (a.month > b.month)
856 return 1;
857 else if (a.month < b.month)
858 return -1;
859
860 else if (a.day > b.day)
861 return 1;
862 else if (a.day < b.day)
863 return -1;
864
865 /* if both are dates, we are done */
866 if (a.is_date && b.is_date)
867 return 0;
868
869 /* else, if only one is a date (and we already know the date part is equal),
870 then the other is greater */
871 else if (b.is_date)
872 return 1;
873 else if (a.is_date)
874 return -1;
875
876 else if (a.hour > b.hour)
877 return 1;
878 else if (a.hour < b.hour)
879 return -1;
880
881 else if (a.minute > b.minute)
882 return 1;
883 else if (a.minute < b.minute)
884 return -1;
885
886 else if (a.second > b.second)
887 return 1;
888 else if (a.second < b.second)
889 return -1;
890
891 return 0;
892 }
893
894 /**
895 * like icaltime_compare, but only use the date parts.
896 */
897
898 int
icaltime_compare_date_only(const struct icaltimetype a_in,const struct icaltimetype b_in)899 icaltime_compare_date_only(const struct icaltimetype a_in, const struct icaltimetype b_in)
900 {
901 struct icaltimetype a, b;
902 icaltimezone *tz = icaltimezone_get_utc_timezone();
903
904 a = icaltime_convert_to_zone(a_in, tz);
905 b = icaltime_convert_to_zone(b_in, tz);
906
907 if (a.year > b.year)
908 return 1;
909 else if (a.year < b.year)
910 return -1;
911
912 if (a.month > b.month)
913 return 1;
914 else if (a.month < b.month)
915 return -1;
916
917 if (a.day > b.day)
918 return 1;
919 else if (a.day < b.day)
920 return -1;
921
922 return 0;
923 }
924
925 /**
926 * like icaltime_compare, but only use the date parts; accepts timezone.
927 */
928
929 int
icaltime_compare_date_only_tz(const struct icaltimetype a_in,const struct icaltimetype b_in,icaltimezone * tz)930 icaltime_compare_date_only_tz(const struct icaltimetype a_in, const struct icaltimetype b_in, icaltimezone *tz)
931 {
932 struct icaltimetype a, b;
933
934 a = icaltime_convert_to_zone(a_in, tz);
935 b = icaltime_convert_to_zone(b_in, tz);
936
937 if (a.year > b.year)
938 return 1;
939 else if (a.year < b.year)
940 return -1;
941
942 if (a.month > b.month)
943 return 1;
944 else if (a.month < b.month)
945 return -1;
946
947 if (a.day > b.day)
948 return 1;
949 else if (a.day < b.day)
950 return -1;
951
952 return 0;
953 }
954
955 /* These are defined in icalduration.c:
956 struct icaltimetype icaltime_add(struct icaltimetype t,
957 struct icaldurationtype d)
958 struct icaldurationtype icaltime_subtract(struct icaltimetype t1,
959 struct icaltimetype t2)
960 */
961
962
963
964 /** @brief Internal, shouldn't be part of the public API
965 *
966 * Adds (or subtracts) a time from a icaltimetype.
967 * NOTE: This function is exactly the same as icaltimezone_adjust_change()
968 * except for the type of the first parameter.
969 */
970 void
icaltime_adjust(struct icaltimetype * tt,const int days,const int hours,const int minutes,const int seconds)971 icaltime_adjust(struct icaltimetype *tt, const int days, const int hours,
972 const int minutes, const int seconds) {
973
974 int second, minute, hour, day;
975 int minutes_overflow, hours_overflow, days_overflow = 0, years_overflow;
976 int days_in_month;
977
978 /* If we are passed a date make sure to ignore hour minute and second */
979 if (tt->is_date)
980 goto IS_DATE;
981
982 /* Add on the seconds. */
983 second = tt->second + seconds;
984 tt->second = second % 60;
985 minutes_overflow = second / 60;
986 if (tt->second < 0) {
987 tt->second += 60;
988 minutes_overflow--;
989 }
990
991 /* Add on the minutes. */
992 minute = tt->minute + minutes + minutes_overflow;
993 tt->minute = minute % 60;
994 hours_overflow = minute / 60;
995 if (tt->minute < 0) {
996 tt->minute += 60;
997 hours_overflow--;
998 }
999
1000 /* Add on the hours. */
1001 hour = tt->hour + hours + hours_overflow;
1002 tt->hour = hour % 24;
1003 days_overflow = hour / 24;
1004 if (tt->hour < 0) {
1005 tt->hour += 24;
1006 days_overflow--;
1007 }
1008
1009 IS_DATE:
1010 /* Normalize the month. We do this before handling the day since we may
1011 need to know what month it is to get the number of days in it.
1012 Note that months are 1 to 12, so we have to be a bit careful. */
1013 if (tt->month >= 13) {
1014 years_overflow = (tt->month - 1) / 12;
1015 tt->year += years_overflow;
1016 tt->month -= years_overflow * 12;
1017 } else if (tt->month <= 0) {
1018 /* 0 to -11 is -1 year out, -12 to -23 is -2 years. */
1019 years_overflow = (tt->month / 12) - 1;
1020 tt->year += years_overflow;
1021 tt->month -= years_overflow * 12;
1022 }
1023
1024 /* Add on the days. */
1025 day = tt->day + days + days_overflow;
1026 if (day > 0) {
1027 for (;;) {
1028 days_in_month = icaltime_days_in_month (tt->month, tt->year);
1029 if (day <= days_in_month)
1030 break;
1031
1032 tt->month++;
1033 if (tt->month >= 13) {
1034 tt->year++;
1035 tt->month = 1;
1036 }
1037
1038 day -= days_in_month;
1039 }
1040 } else {
1041 while (day <= 0) {
1042 if (tt->month == 1) {
1043 tt->year--;
1044 tt->month = 12;
1045 } else {
1046 tt->month--;
1047 }
1048
1049 day += icaltime_days_in_month (tt->month, tt->year);
1050 }
1051 }
1052 tt->day = day;
1053 }
1054
1055 /** @brief Convert time to a given timezone
1056 *
1057 * Convert a time from its native timezone to a given timezone.
1058 *
1059 * If tt is a date, the returned time is an exact
1060 * copy of the input. If it's a floating time, the returned object
1061 * represents the same time translated to the given timezone.
1062 * Otherwise the time will be converted to the new
1063 * time zone, and its native timezone set to the right timezone.
1064 */
icaltime_convert_to_zone(const struct icaltimetype tt,icaltimezone * zone)1065 struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt,
1066 icaltimezone *zone) {
1067
1068 struct icaltimetype ret = tt;
1069
1070 /* If it's a date do nothing */
1071 if (tt.is_date) {
1072 return ret;
1073 }
1074
1075 if (tt.zone == zone) {
1076 return ret;
1077 }
1078
1079 /* If it's a floating time we don't want to adjust the time */
1080 if (tt.zone != NULL) {
1081 icaltimezone_convert_time(&ret, (icaltimezone *)tt.zone, zone);
1082 }
1083
1084 ret.zone = zone;
1085 if (zone == icaltimezone_get_utc_timezone()) {
1086 ret.is_utc = 1;
1087 } else {
1088 ret.is_utc = 0;
1089 }
1090
1091 return ret;
1092 }
1093
1094 const icaltimezone *
icaltime_get_timezone(const struct icaltimetype t)1095 icaltime_get_timezone(const struct icaltimetype t) {
1096
1097 return t.zone;
1098 }
1099
1100 const char *
icaltime_get_tzid(const struct icaltimetype t)1101 icaltime_get_tzid(const struct icaltimetype t) {
1102
1103 if (t.zone != NULL) {
1104 return icaltimezone_get_tzid((icaltimezone *)t.zone);
1105 } else {
1106 return NULL;
1107 }
1108 }
1109
1110 /** @brief Set the timezone
1111 *
1112 * Force the icaltime to be interpreted relative to another timezone.
1113 * If you need to do timezone conversion, applying offset adjustments,
1114 * then you should use icaltime_convert_to_timezone instead.
1115 */
1116 struct icaltimetype
icaltime_set_timezone(struct icaltimetype * t,const icaltimezone * zone)1117 icaltime_set_timezone(struct icaltimetype *t, const icaltimezone *zone) {
1118
1119 /* If it's a date do nothing */
1120 if (t->is_date) {
1121 return *t;
1122 }
1123
1124 if (t->zone == zone) {
1125 return *t;
1126 }
1127
1128 t->zone = zone;
1129 if (zone == icaltimezone_get_utc_timezone()) {
1130 t->is_utc = 1;
1131 } else {
1132 t->is_utc = 0;
1133 }
1134
1135 return *t;
1136 }
1137
1138
1139 /**
1140 * @brief builds an icaltimespan given a start time, end time and busy value.
1141 *
1142 * @param dtstart The beginning time of the span, can be a date-time
1143 * or just a date.
1144 * @param dtend The end time of the span.
1145 * @param is_busy A boolean value, 0/1.
1146 * @return A span using the supplied values.
1147 *
1148 * returned span contains times specified in UTC.
1149 */
1150
icaltime_span_new(struct icaltimetype dtstart,struct icaltimetype dtend,int is_busy)1151 icaltime_span icaltime_span_new(struct icaltimetype dtstart,
1152 struct icaltimetype dtend,
1153 int is_busy)
1154 {
1155 icaltime_span span;
1156
1157 span.is_busy = is_busy;
1158
1159 span.start = icaltime_as_timet_with_zone(dtstart,
1160 dtstart.zone ? dtstart.zone : icaltimezone_get_utc_timezone());
1161
1162 if (icaltime_is_null_time(dtend)) {
1163 if (!icaltime_is_date(dtstart)) {
1164 /* If dtstart is a DATE-TIME and there is no DTEND nor DURATION
1165 it takes no time */
1166 span.end = span.start;
1167 return span;
1168 } else {
1169 dtend = dtstart;
1170 }
1171 }
1172
1173 span.end = icaltime_as_timet_with_zone(dtend,
1174 dtend.zone ? dtend.zone : icaltimezone_get_utc_timezone());
1175
1176 if (icaltime_is_date(dtstart)) {
1177 /* no time specified, go until the end of the day..*/
1178 span.end += 60*60*24 - 1;
1179 }
1180 return span;
1181 }
1182
1183
1184 /** @brief Returns true if the two spans overlap
1185 *
1186 * @param s1 1st span to test
1187 * @param s2 2nd span to test
1188 * @return boolean value
1189 *
1190 * The result is calculated by testing if the start time of s1 is contained
1191 * by the s2 span, or if the end time of s1 is contained by the s2 span.
1192 *
1193 * Also returns true if the spans are equal.
1194 *
1195 * Note, this will return false if the spans are adjacent.
1196 */
1197
icaltime_span_overlaps(icaltime_span * s1,icaltime_span * s2)1198 int icaltime_span_overlaps(icaltime_span *s1,
1199 icaltime_span *s2)
1200 {
1201 /* s1->start in s2 */
1202 if (s1->start > s2->start && s1->start < s2->end)
1203 return 1;
1204
1205 /* s1->end in s2 */
1206 if (s1->end > s2->start && s1->end < s2->end)
1207 return 1;
1208
1209 /* s2->start in s1 */
1210 if (s2->start > s1->start && s2->start < s1->end)
1211 return 1;
1212
1213 /* s2->end in s1 */
1214 if (s2->end > s1->start && s2->end < s1->end)
1215 return 1;
1216
1217 if (s1->start == s2->start && s1->end == s2->end)
1218 return 1;
1219
1220 return 0;
1221 }
1222
1223 /** @brief Returns true if the span is totally within the containing
1224 * span
1225 *
1226 * @param s The span to test for.
1227 * @param container The span to test against.
1228 * @return boolean value.
1229 *
1230 */
1231
icaltime_span_contains(icaltime_span * s,icaltime_span * container)1232 int icaltime_span_contains(icaltime_span *s,
1233 icaltime_span *container)
1234 {
1235
1236 if ((s->start >= container->start && s->start < container->end) &&
1237 (s->end <= container->end && s->end > container->start))
1238 return 1;
1239
1240 return 0;
1241 }
1242