1 /* Miscellaneous time-related utilities
2 *
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4 *
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Federico Mena <federico@ximian.com>
18 * Miguel de Icaza <miguel@ximian.com>
19 * Damon Chaplin <damon@ximian.com>
20 */
21
22 #include <string.h>
23 #include <ctype.h>
24 #include "e-cal-time-util.h"
25
26 #ifdef G_OS_WIN32
27 #ifdef gmtime_r
28 #undef gmtime_r
29 #endif
30
31 /* The gmtime() in Microsoft's C library is MT-safe */
32 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
33 #endif
34
35 #define REFORMATION_DAY 639787 /* First day of the reformation, counted from 1 Jan 1 */
36 #define MISSING_DAYS 11 /* They corrected out 11 days */
37 #define THURSDAY 4 /* First day of reformation */
38 #define SATURDAY 6 /* Offset value; 1 Jan 1 was a Saturday */
39 #define ISODATE_LENGTH 17 /* 4+2+2+1+2+2+2+1 + 1 */
40
41 /* Number of days in a month, using 0 (Jan) to 11 (Dec). For leap years,
42 * add 1 to February (month 1). */
43 static const gint days_in_month[12] = {
44 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
45 };
46
47 /**************************************************************************
48 * time_t manipulation functions.
49 *
50 * NOTE: these use the Unix timezone functions like mktime() and localtime()
51 * and so should not be used in Evolution. New Evolution code should use
52 * ICalTime values rather than time_t values wherever possible.
53 **************************************************************************/
54
55 /**
56 * time_add_day:
57 * @time: A time_t value.
58 * @days: Number of days to add.
59 *
60 * Adds a day onto the time, using local time.
61 * Note that if clocks go forward due to daylight savings time, there are
62 * some non-existent local times, so the hour may be changed to make it a
63 * valid time. This also means that it may not be wise to keep calling
64 * time_add_day() to step through a certain period - if the hour gets changed
65 * to make it valid time, any further calls to time_add_day() will also return
66 * this hour, which may not be what you want.
67 *
68 * Returns: a time_t value containing @time plus the days added.
69 */
70 time_t
time_add_day(time_t time,gint days)71 time_add_day (time_t time,
72 gint days)
73 {
74 struct tm *tm;
75
76 tm = localtime (&time);
77 tm->tm_mday += days;
78 tm->tm_isdst = -1;
79
80 return mktime (tm);
81 }
82
83 /**
84 * time_add_week:
85 * @time: A time_t value.
86 * @weeks: Number of weeks to add.
87 *
88 * Adds the given number of weeks to a time value.
89 *
90 * Returns: a time_t value containing @time plus the weeks added.
91 */
92 time_t
time_add_week(time_t time,gint weeks)93 time_add_week (time_t time,
94 gint weeks)
95 {
96 return time_add_day (time, weeks * 7);
97 }
98
99 /**
100 * time_day_begin:
101 * @t: A time_t value.
102 *
103 * Returns the start of the day, according to the local time.
104 *
105 * Returns: the time corresponding to the beginning of the day.
106 */
107 time_t
time_day_begin(time_t t)108 time_day_begin (time_t t)
109 {
110 struct tm tm;
111
112 tm = *localtime (&t);
113 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
114 tm.tm_isdst = -1;
115
116 return mktime (&tm);
117 }
118
119 /**
120 * time_day_end:
121 * @t: A time_t value.
122 *
123 * Returns the end of the day, according to the local time.
124 *
125 * Returns: the time corresponding to the end of the day.
126 */
127 time_t
time_day_end(time_t t)128 time_day_end (time_t t)
129 {
130 struct tm tm;
131
132 tm = *localtime (&t);
133 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
134 tm.tm_mday++;
135 tm.tm_isdst = -1;
136
137 return mktime (&tm);
138 }
139
140 /**************************************************************************
141 * time_t manipulation functions, using timezones in libical.
142 *
143 * NOTE: these are only here to make the transition to the timezone
144 * functions easier. New code should use ICalTime values rather than
145 * time_t values wherever possible.
146 **************************************************************************/
147
148 /**
149 * time_add_day_with_zone:
150 * @time: A time_t value.
151 * @days: Number of days to add.
152 * @zone: Timezone to use.
153 *
154 * Adds or subtracts a number of days to/from the given time_t value, using
155 * the given timezone.
156 * NOTE: this function is only here to make the transition to the timezone
157 * functions easier. New code should use ICalTime values and
158 * i_cal_time_adjust() to add or subtract days, hours, minutes & seconds.
159 *
160 * Returns: a time_t value containing @time plus the days added.
161 */
162 time_t
time_add_day_with_zone(time_t time,gint days,const ICalTimezone * zone)163 time_add_day_with_zone (time_t time,
164 gint days,
165 const ICalTimezone *zone)
166 {
167 ICalTime *tt;
168 time_t res;
169
170 /* Convert to an ICalTime. */
171 tt = i_cal_time_new_from_timet_with_zone (time, FALSE, (ICalTimezone *) zone);
172
173 /* Add/subtract the number of days. */
174 i_cal_time_adjust (tt, days, 0, 0, 0);
175
176 /* Convert back to a time_t. */
177 res = i_cal_time_as_timet_with_zone (tt, (ICalTimezone *) zone);
178
179 g_object_unref (tt);
180
181 return res;
182 }
183
184 /**
185 * time_add_week_with_zone:
186 * @time: A time_t value.
187 * @weeks: Number of weeks to add.
188 * @zone: Timezone to use.
189 *
190 * Adds or subtracts a number of weeks to/from the given time_t value, using
191 * the given timezone.
192 * NOTE: this function is only here to make the transition to the timezone
193 * functions easier. New code should use ICalTime values and
194 * i_cal_time_adjust() to add or subtract days, hours, minutes & seconds.
195 *
196 * Returns: a time_t value containing @time plus the weeks added.
197 */
198 time_t
time_add_week_with_zone(time_t time,gint weeks,const ICalTimezone * zone)199 time_add_week_with_zone (time_t time,
200 gint weeks,
201 const ICalTimezone *zone)
202 {
203 return time_add_day_with_zone (time, weeks * 7, zone);
204 }
205
206 /**
207 * time_add_month_with_zone:
208 * @time: A time_t value.
209 * @months: Number of months to add.
210 * @zone: Timezone to use.
211 *
212 * Adds or subtracts a number of months to/from the given time_t value, using
213 * the given timezone.
214 *
215 * If the day would be off the end of the month (e.g. adding 1 month to
216 * 30th January, would lead to an invalid day, 30th February), it moves it
217 * down to the last day in the month, e.g. 28th Feb (or 29th in a leap year.)
218 *
219 * NOTE: this function is only here to make the transition to the timezone
220 * functions easier. New code should use ICalTime values and
221 * i_cal_time_adjust() to add or subtract days, hours, minutes & seconds.
222 *
223 * Returns: a time_t value containing @time plus the months added.
224 */
225 time_t
time_add_month_with_zone(time_t time,gint months,const ICalTimezone * zone)226 time_add_month_with_zone (time_t time,
227 gint months,
228 const ICalTimezone *zone)
229 {
230 ICalTime *tt;
231 gint day, days_in_month;
232 time_t res;
233
234 /* Convert to an ICalTime. */
235 tt = i_cal_time_new_from_timet_with_zone (time, FALSE, (ICalTimezone *) zone);
236
237 /* Add on the number of months. */
238 i_cal_time_set_month (tt, i_cal_time_get_month (tt) + months);
239
240 /* Save the day, and set it to 1, so we don't overflow into the next
241 * month. */
242 day = i_cal_time_get_day (tt);
243 i_cal_time_set_day (tt, 1);
244
245 /* Normalize it, fixing any month overflow. */
246 i_cal_time_normalize_inplace (tt);
247
248 /* If we go past the end of a month, set it to the last day. */
249 days_in_month = time_days_in_month (i_cal_time_get_year (tt), i_cal_time_get_month (tt) - 1);
250 if (day > days_in_month)
251 day = days_in_month;
252
253 i_cal_time_set_day (tt, day);
254
255 /* Convert back to a time_t. */
256 res = i_cal_time_as_timet_with_zone (tt, (ICalTimezone *) zone);
257
258 g_object_unref (tt);
259
260 return res;
261 }
262
263 /**
264 * time_year_begin_with_zone:
265 * @time: A time_t value.
266 * @zone: Timezone to use.
267 *
268 * Returns the start of the year containing the given time_t, using the given
269 * timezone.
270 * NOTE: this function is only here to make the transition to the timezone
271 * functions easier. New code should use ICalTime values and
272 * i_cal_time_adjust() to add or subtract days, hours, minutes & seconds.
273 *
274 * Returns: the beginning of the year.
275 */
276 time_t
time_year_begin_with_zone(time_t time,const ICalTimezone * zone)277 time_year_begin_with_zone (time_t time,
278 const ICalTimezone *zone)
279 {
280 ICalTime *tt;
281 time_t res;
282
283 /* Convert to an ICalTime. */
284 tt = i_cal_time_new_from_timet_with_zone (time, FALSE, (ICalTimezone *) zone);
285
286 /* Set it to the start of the year. */
287 i_cal_time_set_month (tt, 1);
288 i_cal_time_set_day (tt, 1);
289 i_cal_time_set_hour (tt, 0);
290 i_cal_time_set_minute (tt, 0);
291 i_cal_time_set_second (tt, 0);
292
293 /* Convert back to a time_t. */
294 res = i_cal_time_as_timet_with_zone (tt, (ICalTimezone *) zone);
295
296 g_object_unref (tt);
297
298 return res;
299 }
300
301 /**
302 * time_month_begin_with_zone:
303 * @time: A time_t value.
304 * @zone: Timezone to use.
305 *
306 * Returns the start of the month containing the given time_t, using the given
307 * timezone.
308 * NOTE: this function is only here to make the transition to the timezone
309 * functions easier. New code should use ICalTime values and
310 * i_cal_time_adjust() to add or subtract days, hours, minutes & seconds.
311 *
312 * Returns: the beginning of the month.
313 */
314 time_t
time_month_begin_with_zone(time_t time,const ICalTimezone * zone)315 time_month_begin_with_zone (time_t time,
316 const ICalTimezone *zone)
317 {
318 ICalTime *tt;
319 time_t res;
320
321 /* Convert to an ICalTime. */
322 tt = i_cal_time_new_from_timet_with_zone (time, FALSE, (ICalTimezone *) zone);
323
324 /* Set it to the start of the month. */
325 i_cal_time_set_day (tt, 1);
326 i_cal_time_set_hour (tt, 0);
327 i_cal_time_set_minute (tt, 0);
328 i_cal_time_set_second (tt, 0);
329
330 /* Convert back to a time_t. */
331 res = i_cal_time_as_timet_with_zone (tt, (ICalTimezone *) zone);
332
333 g_object_unref (tt);
334
335 return res;
336 }
337
338 /**
339 * time_week_begin_with_zone:
340 * @time: A time_t value.
341 * @week_start_day: Day to use as the starting of the week.
342 * @zone: Timezone to use.
343 *
344 * Returns the start of the week containing the given time_t, using the given
345 * timezone. week_start_day should use the same values as mktime(),
346 * i.e. 0 (Sun) to 6 (Sat).
347 * NOTE: this function is only here to make the transition to the timezone
348 * functions easier. New code should use ICalTime values and
349 * i_cal_time_adjust() to add or subtract days, hours, minutes & seconds.
350 *
351 * Returns: the beginning of the week.
352 */
353 time_t
time_week_begin_with_zone(time_t time,gint week_start_day,const ICalTimezone * zone)354 time_week_begin_with_zone (time_t time,
355 gint week_start_day,
356 const ICalTimezone *zone)
357 {
358 ICalTime *tt;
359 gint weekday, offset;
360 time_t res;
361
362 /* Convert to an ICalTime. */
363 tt = i_cal_time_new_from_timet_with_zone (time, FALSE, (ICalTimezone *) zone);
364
365 /* Get the weekday. */
366 weekday = time_day_of_week (i_cal_time_get_day (tt), i_cal_time_get_month (tt) - 1, i_cal_time_get_year (tt));
367
368 /* Calculate the current offset from the week start day. */
369 offset = (weekday + 7 - week_start_day) % 7;
370
371 /* Set it to the start of the month. */
372 i_cal_time_set_day (tt, i_cal_time_get_day (tt) - offset);
373 i_cal_time_set_hour (tt, 0);
374 i_cal_time_set_minute (tt, 0);
375 i_cal_time_set_second (tt, 0);
376
377 /* Normalize it, to fix any overflow. */
378 i_cal_time_normalize_inplace (tt);
379
380 /* Convert back to a time_t. */
381 res = i_cal_time_as_timet_with_zone (tt, (ICalTimezone *) zone);
382
383 g_object_unref (tt);
384
385 return res;
386 }
387
388 /**
389 * time_day_begin_with_zone:
390 * @time: A time_t value.
391 * @zone: Timezone to use.
392 *
393 * Returns the start of the day containing the given time_t, using the given
394 * timezone.
395 * NOTE: this function is only here to make the transition to the timezone
396 * functions easier. New code should use ICalTime values and
397 * i_cal_time_adjust() to add or subtract days, hours, minutes & seconds.
398 *
399 * Returns: the beginning of the day.
400 */
401 time_t
time_day_begin_with_zone(time_t time,const ICalTimezone * zone)402 time_day_begin_with_zone (time_t time,
403 const ICalTimezone *zone)
404 {
405 ICalTime *tt;
406 time_t new_time;
407
408 /* Convert to an ICalTime. */
409 tt = i_cal_time_new_from_timet_with_zone (time, FALSE, (ICalTimezone *) zone);
410
411 /* Set it to the start of the day. */
412 i_cal_time_set_hour (tt, 0);
413 i_cal_time_set_minute (tt, 0);
414 i_cal_time_set_second (tt, 0);
415
416 /* Convert back to a time_t and make sure the time is in the past. */
417 while (new_time = i_cal_time_as_timet_with_zone (tt, (ICalTimezone *) zone), new_time > time) {
418 i_cal_time_adjust (tt, 0, -1, 0, 0);
419 }
420
421 g_object_unref (tt);
422
423 return new_time;
424 }
425
426 /**
427 * time_day_end_with_zone:
428 * @time: A time_t value.
429 * @zone: Timezone to use.
430 *
431 * Returns the end of the day containing the given time_t, using the given
432 * timezone. (The end of the day is the start of the next day.)
433 * NOTE: this function is only here to make the transition to the timezone
434 * functions easier. New code should use ICalTime values and
435 * i_cal_time_adjust() to add or subtract days, hours, minutes & seconds.
436 *
437 * Returns: the end of the day.
438 */
439 time_t
time_day_end_with_zone(time_t time,const ICalTimezone * zone)440 time_day_end_with_zone (time_t time,
441 const ICalTimezone *zone)
442 {
443 ICalTime *tt;
444 time_t new_time;
445
446 /* Convert to an ICalTime. */
447 tt = i_cal_time_new_from_timet_with_zone (time, FALSE, (ICalTimezone *) zone);
448
449 /* Set it to the start of the next day. */
450 i_cal_time_set_hour (tt, 0);
451 i_cal_time_set_minute (tt, 0);
452 i_cal_time_set_second (tt, 0);
453
454 i_cal_time_adjust (tt, 1, 0, 0, 0);
455
456 /* Convert back to a time_t and make sure the time is in the future. */
457 while (new_time = i_cal_time_as_timet_with_zone (tt, (ICalTimezone *) zone), new_time <= time) {
458 i_cal_time_adjust (tt, 0, 1, 0, 0);
459 }
460
461 g_object_unref (tt);
462
463 return new_time;
464 }
465
466 /**
467 * time_to_gdate_with_zone:
468 * @date: Destination #GDate value.
469 * @time: A time value.
470 * @zone: (nullable): Desired timezone for destination @date, or %NULL if
471 * the UTC timezone is desired.
472 *
473 * Converts a time_t value to a #GDate structure using the specified timezone.
474 * This is analogous to g_date_set_time() but takes the timezone into account.
475 **/
476 void
time_to_gdate_with_zone(GDate * date,time_t time,const ICalTimezone * zone)477 time_to_gdate_with_zone (GDate *date,
478 time_t time,
479 const ICalTimezone *zone)
480 {
481 ICalTime *tt;
482
483 g_return_if_fail (date != NULL);
484 g_return_if_fail (time != -1);
485
486 tt = i_cal_time_new_from_timet_with_zone (
487 time, FALSE,
488 zone ? (ICalTimezone *) zone : i_cal_timezone_get_utc_timezone ());
489
490 g_date_set_dmy (date, i_cal_time_get_day (tt), i_cal_time_get_month (tt), i_cal_time_get_year (tt));
491
492 g_object_unref (tt);
493 }
494
495 /**************************************************************************
496 * General time functions.
497 **************************************************************************/
498
499 /**
500 * time_days_in_month:
501 * @year: The year.
502 * @month: The month.
503 *
504 * Returns the number of days in the month. Year is the normal year, e.g. 2001.
505 * Month is 0 (Jan) to 11 (Dec).
506 *
507 * Returns: number of days in the given month/year.
508 */
509 gint
time_days_in_month(gint year,gint month)510 time_days_in_month (gint year,
511 gint month)
512 {
513 gint days;
514
515 g_return_val_if_fail (year >= 1900, 0);
516 g_return_val_if_fail ((month >= 0) && (month < 12), 0);
517
518 days = days_in_month[month];
519 if (month == 1 && time_is_leap_year (year))
520 days++;
521
522 return days;
523 }
524
525 /**
526 * time_day_of_year:
527 * @day: The day.
528 * @month: The month.
529 * @year: The year.
530 *
531 * Returns the 1-based day number within the year of the specified date.
532 * Year is the normal year, e.g. 2001. Month is 0 to 11.
533 *
534 * Returns: the day of the year.
535 */
536 gint
time_day_of_year(gint day,gint month,gint year)537 time_day_of_year (gint day,
538 gint month,
539 gint year)
540 {
541 gint i;
542
543 for (i = 0; i < month; i++) {
544 day += days_in_month[i];
545
546 if (i == 1 && time_is_leap_year (year))
547 day++;
548 }
549
550 return day;
551 }
552
553 /**
554 * time_day_of_week:
555 * @day: The day.
556 * @month: The month.
557 * @year: The year.
558 *
559 * Returns the day of the week for the specified date, 0 (Sun) to 6 (Sat).
560 * For the days that were removed on the Gregorian reformation, it returns
561 * Thursday. Year is the normal year, e.g. 2001. Month is 0 to 11.
562 *
563 * Returns: the day of the week for the given date.
564 */
565 gint
time_day_of_week(gint day,gint month,gint year)566 time_day_of_week (gint day,
567 gint month,
568 gint year)
569 {
570 gint n;
571
572 n = (year - 1) * 365 + time_leap_years_up_to (year - 1)
573 + time_day_of_year (day, month, year);
574
575 if (n < REFORMATION_DAY)
576 return (n - 1 + SATURDAY) % 7;
577
578 if (n >= (REFORMATION_DAY + MISSING_DAYS))
579 return (n - 1 + SATURDAY - MISSING_DAYS) % 7;
580
581 return THURSDAY;
582 }
583
584 /**
585 * time_is_leap_year:
586 * @year: The year.
587 *
588 * Returns whether the specified year is a leap year. Year is the normal year,
589 * e.g. 2001.
590 *
591 * Returns: TRUE if the year is leap, FALSE if not.
592 */
593 gboolean
time_is_leap_year(gint year)594 time_is_leap_year (gint year)
595 {
596 if (year <= 1752)
597 return !(year % 4);
598 else
599 return (!(year % 4) && (year % 100)) || !(year % 400);
600 }
601
602 /**
603 * time_leap_years_up_to:
604 * @year: The year.
605 *
606 * Returns the number of leap years since year 1 up to (but not including) the
607 * specified year. Year is the normal year, e.g. 2001.
608 *
609 * Returns: number of leap years.
610 */
611 gint
time_leap_years_up_to(gint year)612 time_leap_years_up_to (gint year)
613 {
614 /* There is normally a leap year every 4 years, except at the turn of
615 * centuries since 1700. But there is a leap year on centuries since 1700
616 * which are divisible by 400. */
617 return (year / 4
618 - ((year > 1700) ? (year / 100 - 17) : 0)
619 + ((year > 1600) ? ((year - 1600) / 400) : 0));
620 }
621
622 /**
623 * isodate_from_time_t:
624 * @t: A time value.
625 *
626 * Creates an ISO 8601 UTC representation from a time value.
627 *
628 * Returns: String with the ISO 8601 representation of the UTC time.
629 **/
630 gchar *
isodate_from_time_t(time_t t)631 isodate_from_time_t (time_t t)
632 {
633 gchar *ret;
634 struct tm stm;
635 const gchar fmt[] = "%04d%02d%02dT%02d%02d%02dZ";
636
637 gmtime_r (&t, &stm);
638 ret = g_malloc (ISODATE_LENGTH);
639 g_snprintf (
640 ret, ISODATE_LENGTH, fmt,
641 (stm.tm_year + 1900),
642 (stm.tm_mon + 1),
643 stm.tm_mday,
644 stm.tm_hour,
645 stm.tm_min,
646 stm.tm_sec);
647
648 return ret;
649 }
650
651 /**
652 * time_from_isodate:
653 * @str: Date/time value in ISO 8601 format.
654 *
655 * Converts an ISO 8601 UTC time string into a time_t value.
656 *
657 * Returns: Time_t corresponding to the specified ISO string.
658 * Note that we only allow UTC times at present.
659 **/
660 time_t
time_from_isodate(const gchar * str)661 time_from_isodate (const gchar *str)
662 {
663 ICalTime *tt;
664 ICalTimezone *utc_zone;
665 gint len, i;
666 time_t res;
667
668 g_return_val_if_fail (str != NULL, -1);
669
670 /* yyyymmdd[Thhmmss[Z]] */
671
672 len = strlen (str);
673
674 if (!(len == 8 || len == 15 || len == 16))
675 return -1;
676
677 for (i = 0; i < len; i++)
678 if (!((i != 8 && i != 15 && isdigit (str[i]))
679 || (i == 8 && str[i] == 'T')
680 || (i == 15 && str[i] == 'Z')))
681 return -1;
682
683 #define digit_at(x,y) (x[y] - '0')
684
685 tt = i_cal_time_new_null_time ();
686
687 i_cal_time_set_year (tt, digit_at (str, 0) * 1000 +
688 digit_at (str, 1) * 100 +
689 digit_at (str, 2) * 10 +
690 digit_at (str, 3));
691
692 i_cal_time_set_month (tt, digit_at (str, 4) * 10 +
693 digit_at (str, 5));
694
695 i_cal_time_set_day (tt, digit_at (str, 6) * 10 +
696 digit_at (str, 7));
697
698 if (len > 8) {
699 i_cal_time_set_hour (tt, digit_at (str, 9) * 10 +
700 digit_at (str, 10));
701 i_cal_time_set_minute (tt, digit_at (str, 11) * 10 +
702 digit_at (str, 12));
703 i_cal_time_set_second (tt, digit_at (str, 13) * 10 +
704 digit_at (str, 14));
705 }
706
707 utc_zone = i_cal_timezone_get_utc_timezone ();
708
709 res = i_cal_time_as_timet_with_zone (tt, utc_zone);
710
711 g_object_unref (tt);
712
713 return res;
714 }
715
716 /**
717 * e_cal_util_icaltime_to_tm:
718 * @itt: An #ICalTime
719 *
720 * Converts an #ICalTime into a GLibc's struct tm.
721 *
722 * Returns: The converted time as a struct tm. All fields will be
723 * set properly except for tm.tm_yday.
724 *
725 * Since: 2.22
726 */
727 struct tm
e_cal_util_icaltime_to_tm(const ICalTime * itt)728 e_cal_util_icaltime_to_tm (const ICalTime *itt)
729 {
730 struct tm tm;
731 ICalTime *tt = (ICalTime *) itt;
732
733 memset (&tm, 0, sizeof (struct tm));
734
735 g_return_val_if_fail (itt != NULL, tm);
736
737 if (!i_cal_time_is_date (tt)) {
738 tm.tm_sec = i_cal_time_get_second (tt);
739 tm.tm_min = i_cal_time_get_minute (tt);
740 tm.tm_hour = i_cal_time_get_hour (tt);
741 }
742
743 tm.tm_mday = i_cal_time_get_day (tt);
744 tm.tm_mon = i_cal_time_get_month (tt) - 1;
745 tm.tm_year = i_cal_time_get_year (tt) - 1900;
746 tm.tm_wday = time_day_of_week (i_cal_time_get_day (tt), i_cal_time_get_month (tt) - 1, i_cal_time_get_year (tt));
747 tm.tm_isdst = -1;
748
749 return tm;
750 }
751
752 /**
753 * e_cal_util_icaltime_to_tm_with_zone:
754 * @itt: A time value.
755 * @from_zone: Source timezone.
756 * @to_zone: Destination timezone.
757 *
758 * Converts a time value from one timezone to another, and returns a struct tm
759 * representation of the time.
760 *
761 * Returns: The converted time as a struct tm. All fields will be
762 * set properly except for tm.tm_yday.
763 *
764 * Since: 2.22
765 **/
766 struct tm
e_cal_util_icaltime_to_tm_with_zone(const ICalTime * itt,const ICalTimezone * from_zone,const ICalTimezone * to_zone)767 e_cal_util_icaltime_to_tm_with_zone (const ICalTime *itt,
768 const ICalTimezone *from_zone,
769 const ICalTimezone *to_zone)
770 {
771 struct tm tm;
772 ICalTime *itt_copy;
773
774 memset (&tm, 0, sizeof (tm));
775 tm.tm_isdst = -1;
776
777 g_return_val_if_fail (itt != NULL, tm);
778
779 itt_copy = i_cal_time_clone (itt);
780
781 i_cal_time_convert_timezone (itt_copy, (ICalTimezone *) from_zone, (ICalTimezone *) to_zone);
782 tm = e_cal_util_icaltime_to_tm (itt_copy);
783 g_object_unref (itt_copy);
784
785 return tm;
786 }
787
788 /**
789 * e_cal_util_tm_to_icaltime:
790 * @tm: A struct tm.
791 * @is_date: Whether the given time is a date only or not.
792 *
793 * Converts a struct tm into an #ICalTime. Free the returned object
794 * with g_object_unref(), when no longer needed.
795 *
796 * Returns: (transfer full): The converted time as an #ICalTime.
797 *
798 * Since: 2.22
799 */
800 ICalTime *
e_cal_util_tm_to_icaltime(struct tm * tm,gboolean is_date)801 e_cal_util_tm_to_icaltime (struct tm *tm,
802 gboolean is_date)
803 {
804 ICalTime *itt;
805
806 g_return_val_if_fail (tm != NULL, NULL);
807
808 itt = i_cal_time_new_null_time ();
809
810 if (!is_date)
811 i_cal_time_set_time (itt, tm->tm_hour, tm->tm_min, tm->tm_sec);
812
813 i_cal_time_set_date (itt, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
814 i_cal_time_set_is_date (itt, is_date);
815
816 return itt;
817 }
818