Fix bug #3117, "full-day event shown from 01:00 to 01:00 next day"
[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         return savetz;
112     }
113     
114     /* Copy the TZid into a string with the form that putenv expects. */
115     strcpy(new_env_str,"TZ=");
116     strcpy(new_env_str+3,tzid);
117
118     putenv(new_env_str); 
119
120     /* Old value of TZ and the string we will have to free later */
121     savetz.orig_tzid = orig_tzid;
122     savetz.new_env_str = new_env_str;
123
124     tzset();
125
126     return savetz;
127 }
128
129 void unset_tz(struct set_tz_save savetz)
130 {
131     /* restore the original TZ environment */
132
133     char* orig_tzid = savetz.orig_tzid;
134
135     if(orig_tzid!=0){   
136         size_t tmp_sz =strlen(orig_tzid)+4; 
137         char* orig_env_str = (char*)malloc(tmp_sz);
138
139         if(orig_env_str == 0){
140             icalerror_set_errno(ICAL_NEWFAILED_ERROR);
141             return;
142         }
143         
144         strcpy(orig_env_str,"TZ=");
145         strcpy(orig_env_str+3,orig_tzid);
146
147         putenv(orig_env_str);
148
149         free(orig_tzid);
150     } else {
151         g_unsetenv("TZ"); /* Delete from environment */
152     } 
153
154     if(savetz.new_env_str != 0){
155         free(savetz.new_env_str);
156     }
157     tzset();
158 }
159
160
161 time_t icaltime_as_timet(struct icaltimetype tt)
162 {
163     struct tm stm;
164     time_t t;
165
166     memset(&stm,0,sizeof( struct tm));
167
168     if(icaltime_is_null_time(tt)) {
169         return 0;
170     }
171
172     stm.tm_sec = tt.second;
173     stm.tm_min = tt.minute;
174     stm.tm_hour = tt.hour;
175     stm.tm_mday = tt.day;
176     stm.tm_mon = tt.month-1;
177     stm.tm_year = tt.year-1900;
178     stm.tm_isdst = -1;
179
180     if(tt.is_utc == 1 && tt.is_date == 0){
181         struct set_tz_save old_tz = set_tz("UTC");
182         t = mktime(&stm);
183         unset_tz(old_tz);
184     } else {
185         t = mktime(&stm);
186     }
187
188     return t;
189
190 }
191
192 char* icaltime_as_ical_string(struct icaltimetype tt)
193 {
194     size_t size = 17;
195     char* buf = icalmemory_new_buffer(size);
196
197     if(tt.is_date){
198         snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day);
199     } else {
200         char* fmt;
201         if(tt.is_utc){
202             fmt = "%04d%02d%02dT%02d%02d%02dZ";
203         } else {
204             fmt = "%04d%02d%02dT%02d%02d%02d";
205         }
206         snprintf(buf, size,fmt,tt.year,tt.month,tt.day,
207                  tt.hour,tt.minute,tt.second);
208     }
209     
210     icalmemory_add_tmp_buffer(buf);
211
212     return buf;
213
214 }
215
216
217 /* convert tt, of timezone tzid, into a utc time */
218 struct icaltimetype icaltime_as_utc(struct icaltimetype tt,const char* tzid)
219 {
220     int tzid_offset;
221
222     if(tt.is_utc == 1 || tt.is_date == 1){
223         return tt;
224     }
225
226     tzid_offset = icaltime_utc_offset(tt,tzid);
227
228     tt.second -= tzid_offset;
229
230     tt.is_utc = 1;
231
232     return icaltime_normalize(tt);
233 }
234
235 /* convert tt, a time in UTC, into a time in timezone tzid */
236 struct icaltimetype icaltime_as_zone(struct icaltimetype tt,const char* tzid)
237 {
238     int tzid_offset;
239
240     tzid_offset = icaltime_utc_offset(tt,tzid);
241
242     tt.second += tzid_offset;
243
244     tt.is_utc = 0;
245
246     return icaltime_normalize(tt);
247
248 }
249
250
251 /* Return the offset of the named zone as seconds. tt is a time
252    indicating the date for which you want the offset */
253 int icaltime_utc_offset(struct icaltimetype ictt, const char* tzid)
254 {
255
256     time_t tt = icaltime_as_timet(ictt);
257     time_t offset_tt;
258     struct tm gtm, buft1, buft2;
259     struct set_tz_save old_tz; 
260
261     if(tzid != 0){
262         old_tz = set_tz(tzid);
263     }
264  
265     /* Mis-interpret a UTC broken out time as local time */
266     gtm = *(gmtime_r(&tt, &buft1));
267     gtm.tm_isdst = localtime_r(&tt, &buft2)->tm_isdst;    
268     offset_tt = mktime(&gtm);
269     
270     if(tzid != 0){
271         unset_tz(old_tz);
272     }
273
274     return tt-offset_tt;
275 }
276
277
278
279 /* Normalize by converting from localtime to utc and back to local
280    time. This uses localtime because localtime and mktime are inverses
281    of each other */
282
283 struct icaltimetype icaltime_normalize(struct icaltimetype tt)
284 {
285     struct tm stm, buft;
286     time_t tut;
287
288     memset(&stm,0,sizeof( struct tm));
289
290     stm.tm_sec = tt.second;
291     stm.tm_min = tt.minute;
292     stm.tm_hour = tt.hour;
293     stm.tm_mday = tt.day;
294     stm.tm_mon = tt.month-1;
295     stm.tm_year = tt.year-1900;
296     stm.tm_isdst = -1; /* prevents mktime from changing hour based on
297                           daylight savings */
298
299     tut = mktime(&stm);
300
301     stm = *(localtime_r(&tut, &buft));
302
303     tt.second = stm.tm_sec;
304     tt.minute = stm.tm_min;
305     tt.hour = stm.tm_hour;
306     tt.day = stm.tm_mday;
307     tt.month = stm.tm_mon +1;
308     tt.year = stm.tm_year+1900;
309
310     return tt;
311 }
312
313
314 #ifndef ICAL_NO_LIBICAL
315 #include "icalvalue.h"
316
317 struct icaltimetype icaltime_from_string(const char* str)
318 {
319     struct icaltimetype tt = icaltime_null_time();
320     int size;
321
322     icalerror_check_arg_re(str!=0,"str",icaltime_null_time());
323
324     size = strlen(str);
325     
326     if(size == 15) { /* floating time */
327         tt.is_utc = 0;
328         tt.is_date = 0;
329     } else if (size == 16) { /* UTC time, ends in 'Z'*/
330         tt.is_utc = 1;
331         tt.is_date = 0;
332
333         if(str[15] != 'Z'){
334             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
335             return icaltime_null_time();
336         }
337             
338     } else if (size == 8) { /* A DATE */
339         tt.is_utc = 1;
340         tt.is_date = 1;
341     } else if (size == 20) { /* A shitty date by Outlook */
342         char tsep, offset_way;
343         int off_h, off_m;
344         sscanf(str,"%04d%02d%02d%c%02d%02d%02d%c%02d%02d",&tt.year,&tt.month,&tt.day,
345                &tsep,&tt.hour,&tt.minute,&tt.second, &offset_way, &off_h, &off_m);
346
347         if(tsep != 'T'){
348             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
349             return icaltime_null_time();
350         }
351         if (offset_way != '-' && offset_way != '+'){
352             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
353             return icaltime_null_time();
354         }
355         
356         /* substract offset to get utc */
357         if (offset_way == '-')
358             tt.second += 3600*off_h;
359         else 
360             tt.second -= 3600*off_h;
361         tt.is_utc = 1;
362         tt.is_date = 0;
363         return icaltime_normalize(tt);
364     } else { /* error */
365         icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
366         return icaltime_null_time();
367     }
368
369     if(tt.is_date == 1){
370         sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day);
371     } else {
372         char tsep;
373         sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day,
374                &tsep,&tt.hour,&tt.minute,&tt.second);
375
376         if(tsep != 'T'){
377             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
378             return icaltime_null_time();
379         }
380
381     }
382
383     return tt;    
384 }
385 #endif
386
387 char ctime_str[20];
388 char* icaltime_as_ctime(struct icaltimetype t)
389 {
390     time_t tt;
391     char buft[512];
392     tt = icaltime_as_timet(t);
393     sprintf(ctime_str,"%s",ctime_r(&tt, buft));
394
395     ctime_str[strlen(ctime_str)-1] = 0;
396
397     return ctime_str;
398 }
399
400
401 short days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
402
403 short icaltime_days_in_month(short month,short year)
404 {
405
406     int is_leap =0;
407     int days = days_in_month[month];
408
409     assert(month > 0);
410     assert(month <= 12);
411
412     if( (year % 4 == 0 && year % 100 != 0) ||
413         year % 400 == 0){
414         is_leap =1;
415     }
416
417     if( month == 2){
418         days += is_leap;
419     }
420
421     return days;
422 }
423
424 /* 1-> Sunday, 7->Saturday */
425 short icaltime_day_of_week(struct icaltimetype t){
426
427     time_t tt = icaltime_as_timet(t);
428     struct tm *tm, buft;
429
430     if(t.is_utc == 1){
431         tm = gmtime_r(&tt, &buft);
432     } else {
433         tm = localtime_r(&tt, &buft);
434     }
435
436     return tm->tm_wday+1;
437 }
438
439 /* Day of the year that the first day of the week (Sunday) is on  */
440 short icaltime_start_doy_of_week(struct icaltimetype t){
441     time_t tt = icaltime_as_timet(t);
442     time_t start_tt;
443     struct tm *stm, buft1, buft2;
444     int syear;
445
446     stm = gmtime_r(&tt, &buft1);
447     syear = stm->tm_year;
448
449     start_tt = tt - stm->tm_wday*(60*60*24);
450
451     stm = gmtime_r(&start_tt, &buft2);
452     
453     if(syear == stm->tm_year){
454         return stm->tm_yday+1;
455     } else {
456         /* return negative to indicate that start of week is in
457            previous year. */
458         int is_leap = 0;
459         int year = stm->tm_year;
460
461         if( (year % 4 == 0 && year % 100 != 0) ||
462             year % 400 == 0){
463             is_leap =1;
464         }
465
466         return (stm->tm_yday+1)-(365+is_leap);
467     }
468     
469 }
470
471 short icaltime_week_number(struct icaltimetype ictt)
472 {
473     char str[5];
474     time_t t = icaltime_as_timet(ictt);
475     int week_no;
476     struct tm buft;
477
478     strftime(str,5,"%V", gmtime_r(&t, &buft));
479
480     week_no = atoi(str);
481
482     return week_no;
483
484 }
485
486
487 short icaltime_day_of_year(struct icaltimetype t){
488     time_t tt = icaltime_as_timet(t);
489     struct tm *stm, buft;
490
491     if(t.is_utc==1){
492         stm = gmtime_r(&tt, &buft);
493     } else {
494         stm = localtime_r(&tt, &buft);
495     }
496
497     return stm->tm_yday+1;
498     
499 }
500
501 /* Jan 1 is day #1, not 0 */
502 struct icaltimetype icaltime_from_day_of_year(short doy,  short year)
503 {
504     struct tm stm; 
505     time_t tt;
506     struct set_tz_save old_tz = set_tz("UTC");
507
508     /* Get the time of january 1 of this year*/
509     memset(&stm,0,sizeof(struct tm)); 
510     stm.tm_year = year-1900;
511     stm.tm_mday = 1;
512
513     tt = mktime(&stm);
514     unset_tz(old_tz);
515
516
517     /* Now add in the days */
518
519     doy--;
520     tt += doy *60*60*24;
521
522     return icaltime_from_timet(tt, 1);
523 }
524
525 struct icaltimetype icaltime_null_time()
526 {
527     struct icaltimetype t;
528     memset(&t,0,sizeof(struct icaltimetype));
529
530     return t;
531 }
532
533
534 int icaltime_is_valid_time(struct icaltimetype t){
535     if(t.is_utc > 1 || t.is_utc < 0 ||
536        t.year < 0 || t.year > 3000 ||
537        t.is_date > 1 || t.is_date < 0){
538         return 0;
539     } else {
540         return 1;
541     }
542
543 }
544
545 int icaltime_is_null_time(struct icaltimetype t)
546 {
547     if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){
548         return 1;
549     }
550
551     return 0;
552
553 }
554
555 int icaltime_compare(struct icaltimetype a,struct icaltimetype b)
556 {
557     time_t t1 = icaltime_as_timet(a);
558     time_t t2 = icaltime_as_timet(b);
559
560     if (t1 > t2) { 
561         return 1; 
562     } else if (t1 < t2) { 
563         return -1;
564     } else { 
565         return 0; 
566     }
567
568 }
569
570 int
571 icaltime_compare_date_only (struct icaltimetype a, struct icaltimetype b)
572 {
573     time_t t1;
574     time_t t2;
575
576     if (a.year == b.year && a.month == b.month && a.day == b.day)
577         return 0;
578
579     t1 = icaltime_as_timet (a);
580     t2 = icaltime_as_timet (b);
581
582     if (t1 > t2) 
583         return 1; 
584     else if (t1 < t2)
585         return -1;
586     else {
587         /* not reached */
588         assert (0);
589         return 0;
590     }
591 }
592
593
594 /* These are defined in icalduration.c:
595 struct icaltimetype  icaltime_add(struct icaltimetype t,
596                                   struct icaldurationtype  d)
597 struct icaldurationtype  icaltime_subtract(struct icaltimetype t1,
598                                            struct icaltimetype t2)
599 */
600