More fixes for parsing dates in vcalendar on Windows.
[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 (g_getenv("TZ") != NULL) {
98                 orig_tzid = (char*)icalmemory_strdup(g_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         time_t t;
165
166 #ifdef G_OS_WIN32
167         GTimeZone *zone;
168         GDateTime *dt;
169
170         if (tt.is_utc == 1)
171                 zone = g_time_zone_new_utc();
172         else
173                 zone = g_time_zone_new_local();
174
175         dt = g_date_time_new(
176                                 zone,
177                                 tt.year,
178                                 tt.month,
179                                 tt.day,
180                                 tt.hour,
181                                 tt.minute,
182                                 tt.second);
183
184         /* Got to return something... */
185         if (dt == NULL)
186                 return 0;
187
188         t = g_date_time_to_unix(dt);
189
190         g_date_time_unref(dt);
191         g_time_zone_unref(zone);
192
193 #else
194     struct tm stm;
195
196     memset(&stm,0,sizeof( struct tm));
197
198     if(icaltime_is_null_time(tt)) {
199         return 0;
200     }
201
202     stm.tm_sec = tt.second;
203     stm.tm_min = tt.minute;
204     stm.tm_hour = tt.hour;
205     stm.tm_mday = tt.day;
206     stm.tm_mon = tt.month-1;
207     stm.tm_year = tt.year-1900;
208     stm.tm_isdst = -1;
209
210     if(tt.is_utc == 1 && tt.is_date == 0){
211         struct set_tz_save old_tz = set_tz("UTC");
212         t = mktime(&stm);
213         unset_tz(old_tz);
214     } else {
215         t = mktime(&stm);
216     }
217 #endif
218
219     return t;
220 }
221
222 char* icaltime_as_ical_string(struct icaltimetype tt)
223 {
224     size_t size = 17;
225     char* buf = icalmemory_new_buffer(size);
226
227     if(tt.is_date){
228         snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day);
229     } else {
230         char* fmt;
231         if(tt.is_utc){
232             fmt = "%04d%02d%02dT%02d%02d%02dZ";
233         } else {
234             fmt = "%04d%02d%02dT%02d%02d%02d";
235         }
236         snprintf(buf, size,fmt,tt.year,tt.month,tt.day,
237                  tt.hour,tt.minute,tt.second);
238     }
239     
240     icalmemory_add_tmp_buffer(buf);
241
242     return buf;
243
244 }
245
246
247 /* convert tt, of timezone tzid, into a utc time */
248 struct icaltimetype icaltime_as_utc(struct icaltimetype tt,const char* tzid)
249 {
250     int tzid_offset;
251
252     if(tt.is_utc == 1 || tt.is_date == 1){
253         return tt;
254     }
255
256     tzid_offset = icaltime_utc_offset(tt,tzid);
257
258     tt.second -= tzid_offset;
259
260     tt.is_utc = 1;
261
262     return icaltime_normalize(tt);
263 }
264
265 /* convert tt, a time in UTC, into a time in timezone tzid */
266 struct icaltimetype icaltime_as_zone(struct icaltimetype tt,const char* tzid)
267 {
268     int tzid_offset;
269
270     tzid_offset = icaltime_utc_offset(tt,tzid);
271
272     tt.second += tzid_offset;
273
274     tt.is_utc = 0;
275
276     return icaltime_normalize(tt);
277
278 }
279
280
281 /* Return the offset of the named zone as seconds. tt is a time
282    indicating the date for which you want the offset */
283 int icaltime_utc_offset(struct icaltimetype ictt, const char* tzid)
284 {
285
286     time_t tt = icaltime_as_timet(ictt);
287     time_t offset_tt;
288     struct tm gtm, buft1, buft2;
289     struct set_tz_save old_tz; 
290
291     if(tzid != 0){
292         old_tz = set_tz(tzid);
293     }
294  
295     /* Mis-interpret a UTC broken out time as local time */
296     gtm = *(gmtime_r(&tt, &buft1));
297     gtm.tm_isdst = localtime_r(&tt, &buft2)->tm_isdst;    
298     offset_tt = mktime(&gtm);
299     
300     if(tzid != 0){
301         unset_tz(old_tz);
302     }
303
304     return tt-offset_tt;
305 }
306
307
308
309 /* Normalize by converting from localtime to utc and back to local
310    time. This uses localtime because localtime and mktime are inverses
311    of each other */
312
313 struct icaltimetype icaltime_normalize(struct icaltimetype tt)
314 {
315 #ifndef G_OS_WIN32
316     struct tm stm, buft;
317     time_t tut;
318
319     memset(&stm,0,sizeof( struct tm));
320
321     stm.tm_sec = tt.second;
322     stm.tm_min = tt.minute;
323     stm.tm_hour = tt.hour;
324     stm.tm_mday = tt.day;
325     stm.tm_mon = tt.month-1;
326     stm.tm_year = tt.year-1900;
327     stm.tm_isdst = -1; /* prevents mktime from changing hour based on
328                           daylight savings */
329
330     tut = mktime(&stm);
331
332     stm = *(localtime_r(&tut, &buft));
333
334     tt.second = stm.tm_sec;
335     tt.minute = stm.tm_min;
336     tt.hour = stm.tm_hour;
337     tt.day = stm.tm_mday;
338     tt.month = stm.tm_mon +1;
339     tt.year = stm.tm_year+1900;
340 #endif
341     return tt;
342 }
343
344
345 #ifndef ICAL_NO_LIBICAL
346 #include "icalvalue.h"
347
348 struct icaltimetype icaltime_from_string(const char* str)
349 {
350     struct icaltimetype tt = icaltime_null_time();
351     int size;
352
353     icalerror_check_arg_re(str!=0,"str",icaltime_null_time());
354
355     size = strlen(str);
356     
357     if(size == 15) { /* floating time */
358         tt.is_utc = 0;
359         tt.is_date = 0;
360     } else if (size == 16) { /* UTC time, ends in 'Z'*/
361         tt.is_utc = 1;
362         tt.is_date = 0;
363
364         if(str[15] != 'Z'){
365             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
366             return icaltime_null_time();
367         }
368             
369     } else if (size == 8) { /* A DATE */
370         tt.is_utc = 1;
371         tt.is_date = 1;
372     } else if (size == 20) { /* A shitty date by Outlook */
373         char tsep, offset_way;
374         int off_h, off_m;
375         sscanf(str,"%04d%02d%02d%c%02d%02d%02d%c%02d%02d",&tt.year,&tt.month,&tt.day,
376                &tsep,&tt.hour,&tt.minute,&tt.second, &offset_way, &off_h, &off_m);
377
378         if(tsep != 'T'){
379             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
380             return icaltime_null_time();
381         }
382         if (offset_way != '-' && offset_way != '+'){
383             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
384             return icaltime_null_time();
385         }
386         
387         /* substract offset to get utc */
388         if (offset_way == '-')
389             tt.second += 3600*off_h;
390         else 
391             tt.second -= 3600*off_h;
392         tt.is_utc = 1;
393         tt.is_date = 0;
394         return icaltime_normalize(tt);
395     } else { /* error */
396         icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
397         return icaltime_null_time();
398     }
399
400     if(tt.is_date == 1){
401         sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day);
402     } else {
403         char tsep;
404         sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day,
405                &tsep,&tt.hour,&tt.minute,&tt.second);
406
407         if(tsep != 'T'){
408             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
409             return icaltime_null_time();
410         }
411
412     }
413
414     return tt;    
415 }
416 #endif
417
418 char ctime_str[20];
419 char* icaltime_as_ctime(struct icaltimetype t)
420 {
421     time_t tt;
422     char buft[512];
423     tt = icaltime_as_timet(t);
424     sprintf(ctime_str,"%s",ctime_r(&tt, buft));
425
426     ctime_str[strlen(ctime_str)-1] = 0;
427
428     return ctime_str;
429 }
430
431
432 short days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
433
434 short icaltime_days_in_month(short month,short year)
435 {
436
437     int is_leap =0;
438     int days = days_in_month[month];
439
440     assert(month > 0);
441     assert(month <= 12);
442
443     if( (year % 4 == 0 && year % 100 != 0) ||
444         year % 400 == 0){
445         is_leap =1;
446     }
447
448     if( month == 2){
449         days += is_leap;
450     }
451
452     return days;
453 }
454
455 /* 1-> Sunday, 7->Saturday */
456 short icaltime_day_of_week(struct icaltimetype t){
457
458     time_t tt = icaltime_as_timet(t);
459     struct tm *tm, buft;
460
461     if(t.is_utc == 1){
462         tm = gmtime_r(&tt, &buft);
463     } else {
464         tm = localtime_r(&tt, &buft);
465     }
466
467     return tm->tm_wday+1;
468 }
469
470 /* Day of the year that the first day of the week (Sunday) is on  */
471 short icaltime_start_doy_of_week(struct icaltimetype t){
472     time_t tt = icaltime_as_timet(t);
473     time_t start_tt;
474     struct tm *stm, buft1, buft2;
475     int syear;
476
477     stm = gmtime_r(&tt, &buft1);
478     syear = stm->tm_year;
479
480     start_tt = tt - stm->tm_wday*(60*60*24);
481
482     stm = gmtime_r(&start_tt, &buft2);
483     
484     if(syear == stm->tm_year){
485         return stm->tm_yday+1;
486     } else {
487         /* return negative to indicate that start of week is in
488            previous year. */
489         int is_leap = 0;
490         int year = stm->tm_year;
491
492         if( (year % 4 == 0 && year % 100 != 0) ||
493             year % 400 == 0){
494             is_leap =1;
495         }
496
497         return (stm->tm_yday+1)-(365+is_leap);
498     }
499     
500 }
501
502 short icaltime_week_number(struct icaltimetype ictt)
503 {
504     char str[5];
505     time_t t = icaltime_as_timet(ictt);
506     int week_no;
507     struct tm buft;
508
509     strftime(str,5,"%V", gmtime_r(&t, &buft));
510
511     week_no = atoi(str);
512
513     return week_no;
514
515 }
516
517
518 short icaltime_day_of_year(struct icaltimetype t){
519 #ifdef G_OS_WIN32
520         GTimeZone *zone;
521         GDateTime *dt;
522
523         if (t.is_utc == 1)
524                 zone = g_time_zone_new_utc();
525         else
526                 zone = g_time_zone_new_local();
527
528         dt = g_date_time_new(
529                                 zone,
530                                 t.year,
531                                 t.month,
532                                 t.day,
533                                 t.hour,
534                                 t.minute,
535                                 t.second);
536
537         /* Got to return something... */
538         if (dt == NULL)
539                 return 1;
540
541         gint doy = g_date_time_get_day_of_year(dt) - 1;
542         g_date_time_unref(dt);
543         g_time_zone_unref(zone);
544
545         return doy;
546
547 #else
548     time_t tt = icaltime_as_timet(t);
549     struct tm *stm, buft;
550
551     if(t.is_utc==1){
552         stm = gmtime_r(&tt, &buft);
553     } else {
554         stm = localtime_r(&tt, &buft);
555     }
556
557     return stm->tm_yday+1;
558 #endif
559 }
560
561 /* Jan 1 is day #1, not 0 */
562 struct icaltimetype icaltime_from_day_of_year(short doy,  short year)
563 {
564 #ifdef G_OS_WIN32
565         GDateTime *dt = g_date_time_new_utc(
566                         year,
567                         1,
568                         1,
569                         0,
570                         0,
571                         0);
572         GDateTime *dtd = g_date_time_add_days(dt, doy);
573
574         g_date_time_unref(dt);
575         struct icaltimetype t = icaltime_null_time();
576
577         t.year = g_date_time_get_year(dtd);
578         t.month = g_date_time_get_month(dtd);
579         t.day = g_date_time_get_day_of_month(dtd);
580         t.hour = g_date_time_get_hour(dtd);
581         t.minute = g_date_time_get_minute(dtd);
582         t.second = g_date_time_get_second(dtd);
583         t.is_utc = 1;
584         t.is_date = 1;
585         g_date_time_unref(dtd);
586
587         return t;
588
589 #else
590     struct tm stm; 
591     time_t tt;
592     struct set_tz_save old_tz = set_tz("UTC");
593
594     /* Get the time of january 1 of this year*/
595     memset(&stm,0,sizeof(struct tm)); 
596     stm.tm_year = year-1900;
597     stm.tm_mday = 1;
598
599     tt = mktime(&stm);
600     unset_tz(old_tz);
601
602
603     /* Now add in the days */
604
605     doy--;
606     tt += doy *60*60*24;
607
608     return icaltime_from_timet(tt, 1);
609 #endif
610 }
611
612 struct icaltimetype icaltime_null_time()
613 {
614     struct icaltimetype t;
615     memset(&t,0,sizeof(struct icaltimetype));
616
617     return t;
618 }
619
620
621 int icaltime_is_valid_time(struct icaltimetype t){
622     if(t.is_utc > 1 || t.is_utc < 0 ||
623        t.year < 0 || t.year > 3000 ||
624        t.is_date > 1 || t.is_date < 0){
625         return 0;
626     } else {
627         return 1;
628     }
629
630 }
631
632 int icaltime_is_null_time(struct icaltimetype t)
633 {
634     if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){
635         return 1;
636     }
637
638     return 0;
639
640 }
641
642 int icaltime_compare(struct icaltimetype a,struct icaltimetype b)
643 {
644     time_t t1 = icaltime_as_timet(a);
645     time_t t2 = icaltime_as_timet(b);
646
647     if (t1 > t2) { 
648         return 1; 
649     } else if (t1 < t2) { 
650         return -1;
651     } else { 
652         return 0; 
653     }
654
655 }
656
657 int
658 icaltime_compare_date_only (struct icaltimetype a, struct icaltimetype b)
659 {
660     time_t t1;
661     time_t t2;
662
663     if (a.year == b.year && a.month == b.month && a.day == b.day)
664         return 0;
665
666     t1 = icaltime_as_timet (a);
667     t2 = icaltime_as_timet (b);
668
669     if (t1 > t2) 
670         return 1; 
671     else if (t1 < t2)
672         return -1;
673     else {
674         /* not reached */
675         assert (0);
676         return 0;
677     }
678 }
679
680
681 /* These are defined in icalduration.c:
682 struct icaltimetype  icaltime_add(struct icaltimetype t,
683                                   struct icaldurationtype  d)
684 struct icaldurationtype  icaltime_subtract(struct icaltimetype t1,
685                                            struct icaltimetype t2)
686 */
687