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