6532ff2e8f0e875cab7dbb008296fb4609779225
[claws.git] / src / plugins / vcalendar / libical / libical / icaltime.c
1 /* -*- Mode: C -*-
2   ======================================================================
3   FILE: icaltime.c
4   CREATOR: eric 02 June 2000
5   
6   $Id$
7   $Locker$
8     
9  (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
10
11  This program is free software; you can redistribute it and/or modify
12  it under the terms of either: 
13
14     The LGPL as published by the Free Software Foundation, version
15     2.1, available at: http://www.fsf.org/copyleft/lesser.html
16
17   Or:
18
19     The Mozilla Public License Version 1.0. You may obtain a copy of
20     the License at http://www.mozilla.org/MPL/
21
22  The Original Code is eric. The Initial Developer of the Original
23  Code is Eric Busboom
24
25
26  ======================================================================*/
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #if USE_PTHREAD
33 #include <pthread.h>
34 #endif
35 #include <glib.h>
36
37 #include "icaltime.h"
38 #include <assert.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42
43 #ifdef ICAL_NO_LIBICAL
44 #define icalerror_set_errno(x)
45 #define  icalerror_check_arg_rv(x,y)
46 #define  icalerror_check_arg_re(x,y,z)
47 #else
48 #include "icalerror.h"
49 #include "icalmemory.h"
50 #endif
51
52
53
54
55 struct icaltimetype 
56 icaltime_from_timet(time_t tm, int is_date)
57 {
58     struct icaltimetype tt = icaltime_null_time();
59     struct tm t, buft;
60
61     t = *(gmtime_r(&tm, &buft));
62      
63     if(is_date == 0){ 
64         tt.second = t.tm_sec;
65         tt.minute = t.tm_min;
66         tt.hour = t.tm_hour;
67     } else {
68         tt.second = tt.minute =tt.hour = 0 ;
69     }
70
71     tt.day = t.tm_mday;
72     tt.month = t.tm_mon + 1;
73     tt.year = t.tm_year+ 1900;
74     
75     tt.is_utc = 1;
76     tt.is_date = is_date; 
77
78     return tt;
79 }
80
81 /* Structure used by set_tz to hold an old value of TZ, and the new
82    value, which is in memory we will have to free in unset_tz */
83 struct set_tz_save {char* orig_tzid; char* new_env_str;};
84
85 /* Temporarily change the TZ environmental variable. */
86 struct set_tz_save set_tz(const char* tzid)
87 {
88
89     char *orig_tzid = 0;
90     char *new_env_str;
91     struct set_tz_save savetz;
92     size_t tmp_sz; 
93
94     savetz.orig_tzid = 0;
95     savetz.new_env_str = 0;
96
97     if(getenv("TZ") != 0){
98         orig_tzid = (char*)icalmemory_strdup(getenv("TZ"));
99
100         if(orig_tzid == 0){
101             icalerror_set_errno(ICAL_NEWFAILED_ERROR);
102             return savetz;
103         }
104     }
105
106     tmp_sz =strlen(tzid)+4; 
107     new_env_str = (char*)malloc(tmp_sz);
108
109     if(new_env_str == 0){
110         icalerror_set_errno(ICAL_NEWFAILED_ERROR);
111         free(orig_tzid);
112         return savetz;
113     }
114     
115     /* Copy the TZid into a string with the form that putenv expects. */
116     strcpy(new_env_str,"TZ=");
117     strcpy(new_env_str+3,tzid);
118
119     putenv(new_env_str); 
120
121     /* Old value of TZ and the string we will have to free later */
122     savetz.orig_tzid = orig_tzid;
123     savetz.new_env_str = new_env_str;
124
125     tzset();
126
127     return savetz;
128 }
129
130 void unset_tz(struct set_tz_save savetz)
131 {
132     /* restore the original TZ environment */
133
134     char* orig_tzid = savetz.orig_tzid;
135
136     if(orig_tzid!=0){   
137         size_t tmp_sz =strlen(orig_tzid)+4; 
138         char* orig_env_str = (char*)malloc(tmp_sz);
139
140         if(orig_env_str == 0){
141             icalerror_set_errno(ICAL_NEWFAILED_ERROR);
142             return;
143         }
144         
145         strcpy(orig_env_str,"TZ=");
146         strcpy(orig_env_str+3,orig_tzid);
147
148         putenv(orig_env_str);
149
150         free(orig_tzid);
151     } else {
152         g_unsetenv("TZ"); /* Delete from environment */
153     } 
154
155     if(savetz.new_env_str != 0){
156         free(savetz.new_env_str);
157     }
158     tzset();
159 }
160
161
162 time_t icaltime_as_timet(struct icaltimetype tt)
163 {
164     struct tm stm;
165     time_t t;
166
167     memset(&stm,0,sizeof( struct tm));
168
169     if(icaltime_is_null_time(tt)) {
170         return 0;
171     }
172
173     stm.tm_sec = tt.second;
174     stm.tm_min = tt.minute;
175     stm.tm_hour = tt.hour;
176     stm.tm_mday = tt.day;
177     stm.tm_mon = tt.month-1;
178     stm.tm_year = tt.year-1900;
179     stm.tm_isdst = -1;
180
181     if(tt.is_utc == 1 && tt.is_date == 0){
182         struct set_tz_save old_tz = set_tz("UTC");
183         t = mktime(&stm);
184         unset_tz(old_tz);
185     } else {
186         t = mktime(&stm);
187     }
188
189     return t;
190
191 }
192
193 char* icaltime_as_ical_string(struct icaltimetype tt)
194 {
195     size_t size = 17;
196     char* buf = icalmemory_new_buffer(size);
197
198     if(tt.is_date){
199         snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day);
200     } else {
201         char* fmt;
202         if(tt.is_utc){
203             fmt = "%04d%02d%02dT%02d%02d%02dZ";
204         } else {
205             fmt = "%04d%02d%02dT%02d%02d%02d";
206         }
207         snprintf(buf, size,fmt,tt.year,tt.month,tt.day,
208                  tt.hour,tt.minute,tt.second);
209     }
210     
211     icalmemory_add_tmp_buffer(buf);
212
213     return buf;
214
215 }
216
217
218 /* convert tt, of timezone tzid, into a utc time */
219 struct icaltimetype icaltime_as_utc(struct icaltimetype tt,const char* tzid)
220 {
221     int tzid_offset;
222
223     if(tt.is_utc == 1 || tt.is_date == 1){
224         return tt;
225     }
226
227     tzid_offset = icaltime_utc_offset(tt,tzid);
228
229     tt.second -= tzid_offset;
230
231     tt.is_utc = 1;
232
233     return icaltime_normalize(tt);
234 }
235
236 /* convert tt, a time in UTC, into a time in timezone tzid */
237 struct icaltimetype icaltime_as_zone(struct icaltimetype tt,const char* tzid)
238 {
239     int tzid_offset;
240
241     tzid_offset = icaltime_utc_offset(tt,tzid);
242
243     tt.second += tzid_offset;
244
245     tt.is_utc = 0;
246
247     return icaltime_normalize(tt);
248
249 }
250
251
252 /* Return the offset of the named zone as seconds. tt is a time
253    indicating the date for which you want the offset */
254 int icaltime_utc_offset(struct icaltimetype ictt, const char* tzid)
255 {
256
257     time_t tt = icaltime_as_timet(ictt);
258     time_t offset_tt;
259     struct tm gtm, buft1, buft2;
260     struct set_tz_save old_tz; 
261
262     if(tzid != 0){
263         old_tz = set_tz(tzid);
264     }
265  
266     /* Mis-interpret a UTC broken out time as local time */
267     gtm = *(gmtime_r(&tt, &buft1));
268     gtm.tm_isdst = localtime_r(&tt, &buft2)->tm_isdst;    
269     offset_tt = mktime(&gtm);
270     
271     if(tzid != 0){
272         unset_tz(old_tz);
273     }
274
275     return tt-offset_tt;
276 }
277
278
279
280 /* Normalize by converting from localtime to utc and back to local
281    time. This uses localtime because localtime and mktime are inverses
282    of each other */
283
284 struct icaltimetype icaltime_normalize(struct icaltimetype tt)
285 {
286     struct tm stm, buft;
287     time_t tut;
288
289     memset(&stm,0,sizeof( struct tm));
290
291     stm.tm_sec = tt.second;
292     stm.tm_min = tt.minute;
293     stm.tm_hour = tt.hour;
294     stm.tm_mday = tt.day;
295     stm.tm_mon = tt.month-1;
296     stm.tm_year = tt.year-1900;
297     stm.tm_isdst = -1; /* prevents mktime from changing hour based on
298                           daylight savings */
299
300     tut = mktime(&stm);
301
302     stm = *(localtime_r(&tut, &buft));
303
304     tt.second = stm.tm_sec;
305     tt.minute = stm.tm_min;
306     tt.hour = stm.tm_hour;
307     tt.day = stm.tm_mday;
308     tt.month = stm.tm_mon +1;
309     tt.year = stm.tm_year+1900;
310
311     return tt;
312 }
313
314
315 #ifndef ICAL_NO_LIBICAL
316 #include "icalvalue.h"
317
318 struct icaltimetype icaltime_from_string(const char* str)
319 {
320     struct icaltimetype tt = icaltime_null_time();
321     int size;
322
323     icalerror_check_arg_re(str!=0,"str",icaltime_null_time());
324
325     size = strlen(str);
326     
327     if(size == 15) { /* floating time */
328         tt.is_utc = 0;
329         tt.is_date = 0;
330     } else if (size == 16) { /* UTC time, ends in 'Z'*/
331         tt.is_utc = 1;
332         tt.is_date = 0;
333
334         if(str[15] != 'Z'){
335             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
336             return icaltime_null_time();
337         }
338             
339     } else if (size == 8) { /* A DATE */
340         tt.is_utc = 1;
341         tt.is_date = 1;
342     } else if (size == 20) { /* A shitty date by Outlook */
343         char tsep, offset_way;
344         int off_h, off_m;
345         sscanf(str,"%04d%02d%02d%c%02d%02d%02d%c%02d%02d",&tt.year,&tt.month,&tt.day,
346                &tsep,&tt.hour,&tt.minute,&tt.second, &offset_way, &off_h, &off_m);
347
348         if(tsep != 'T'){
349             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
350             return icaltime_null_time();
351         }
352         if (offset_way != '-' && offset_way != '+'){
353             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
354             return icaltime_null_time();
355         }
356         
357         /* substract offset to get utc */
358         if (offset_way == '-')
359             tt.second += 3600*off_h;
360         else 
361             tt.second -= 3600*off_h;
362         tt.is_utc = 1;
363         tt.is_date = 0;
364         return icaltime_normalize(tt);
365     } else { /* error */
366         icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
367         return icaltime_null_time();
368     }
369
370     if(tt.is_date == 1){
371         sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day);
372     } else {
373         char tsep;
374         sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day,
375                &tsep,&tt.hour,&tt.minute,&tt.second);
376
377         if(tsep != 'T'){
378             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
379             return icaltime_null_time();
380         }
381
382     }
383
384     return tt;    
385 }
386 #endif
387
388 char ctime_str[20];
389 char* icaltime_as_ctime(struct icaltimetype t)
390 {
391     time_t tt;
392     char buft[512];
393     tt = icaltime_as_timet(t);
394     sprintf(ctime_str,"%s",ctime_r(&tt, buft));
395
396     ctime_str[strlen(ctime_str)-1] = 0;
397
398     return ctime_str;
399 }
400
401
402 short days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
403
404 short icaltime_days_in_month(short month,short year)
405 {
406
407     int is_leap =0;
408     int days = days_in_month[month];
409
410     assert(month > 0);
411     assert(month <= 12);
412
413     if( (year % 4 == 0 && year % 100 != 0) ||
414         year % 400 == 0){
415         is_leap =1;
416     }
417
418     if( month == 2){
419         days += is_leap;
420     }
421
422     return days;
423 }
424
425 /* 1-> Sunday, 7->Saturday */
426 short icaltime_day_of_week(struct icaltimetype t){
427
428     time_t tt = icaltime_as_timet(t);
429     struct tm *tm, buft;
430
431     if(t.is_utc == 1){
432         tm = gmtime_r(&tt, &buft);
433     } else {
434         tm = localtime_r(&tt, &buft);
435     }
436
437     return tm->tm_wday+1;
438 }
439
440 /* Day of the year that the first day of the week (Sunday) is on  */
441 short icaltime_start_doy_of_week(struct icaltimetype t){
442     time_t tt = icaltime_as_timet(t);
443     time_t start_tt;
444     struct tm *stm, buft1, buft2;
445     int syear;
446
447     stm = gmtime_r(&tt, &buft1);
448     syear = stm->tm_year;
449
450     start_tt = tt - stm->tm_wday*(60*60*24);
451
452     stm = gmtime_r(&start_tt, &buft2);
453     
454     if(syear == stm->tm_year){
455         return stm->tm_yday+1;
456     } else {
457         /* return negative to indicate that start of week is in
458            previous year. */
459         int is_leap = 0;
460         int year = stm->tm_year;
461
462         if( (year % 4 == 0 && year % 100 != 0) ||
463             year % 400 == 0){
464             is_leap =1;
465         }
466
467         return (stm->tm_yday+1)-(365+is_leap);
468     }
469     
470 }
471
472 short icaltime_week_number(struct icaltimetype ictt)
473 {
474     char str[5];
475     time_t t = icaltime_as_timet(ictt);
476     int week_no;
477     struct tm buft;
478
479     strftime(str,5,"%V", gmtime_r(&t, &buft));
480
481     week_no = atoi(str);
482
483     return week_no;
484
485 }
486
487
488 short icaltime_day_of_year(struct icaltimetype t){
489     time_t tt = icaltime_as_timet(t);
490     struct tm *stm, buft;
491
492     if(t.is_utc==1){
493         stm = gmtime_r(&tt, &buft);
494     } else {
495         stm = localtime_r(&tt, &buft);
496     }
497
498     return stm->tm_yday+1;
499     
500 }
501
502 /* Jan 1 is day #1, not 0 */
503 struct icaltimetype icaltime_from_day_of_year(short doy,  short year)
504 {
505     struct tm stm; 
506     time_t tt;
507     struct set_tz_save old_tz = set_tz("UTC");
508
509     /* Get the time of january 1 of this year*/
510     memset(&stm,0,sizeof(struct tm)); 
511     stm.tm_year = year-1900;
512     stm.tm_mday = 1;
513
514     tt = mktime(&stm);
515     unset_tz(old_tz);
516
517
518     /* Now add in the days */
519
520     doy--;
521     tt += doy *60*60*24;
522
523     return icaltime_from_timet(tt, 1);
524 }
525
526 struct icaltimetype icaltime_null_time()
527 {
528     struct icaltimetype t;
529     memset(&t,0,sizeof(struct icaltimetype));
530
531     return t;
532 }
533
534
535 int icaltime_is_valid_time(struct icaltimetype t){
536     if(t.is_utc > 1 || t.is_utc < 0 ||
537        t.year < 0 || t.year > 3000 ||
538        t.is_date > 1 || t.is_date < 0){
539         return 0;
540     } else {
541         return 1;
542     }
543
544 }
545
546 int icaltime_is_null_time(struct icaltimetype t)
547 {
548     if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){
549         return 1;
550     }
551
552     return 0;
553
554 }
555
556 int icaltime_compare(struct icaltimetype a,struct icaltimetype b)
557 {
558     time_t t1 = icaltime_as_timet(a);
559     time_t t2 = icaltime_as_timet(b);
560
561     if (t1 > t2) { 
562         return 1; 
563     } else if (t1 < t2) { 
564         return -1;
565     } else { 
566         return 0; 
567     }
568
569 }
570
571 int
572 icaltime_compare_date_only (struct icaltimetype a, struct icaltimetype b)
573 {
574     time_t t1;
575     time_t t2;
576
577     if (a.year == b.year && a.month == b.month && a.day == b.day)
578         return 0;
579
580     t1 = icaltime_as_timet (a);
581     t2 = icaltime_as_timet (b);
582
583     if (t1 > t2) 
584         return 1; 
585     else if (t1 < t2)
586         return -1;
587     else {
588         /* not reached */
589         assert (0);
590         return 0;
591     }
592 }
593
594
595 /* These are defined in icalduration.c:
596 struct icaltimetype  icaltime_add(struct icaltimetype t,
597                                   struct icaldurationtype  d)
598 struct icaldurationtype  icaltime_subtract(struct icaltimetype t1,
599                                            struct icaltimetype t2)
600 */
601