/src/libical/src/libical/icaltime.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*====================================================================== |
2 | | FILE: icaltime.c |
3 | | CREATOR: eric 02 June 2000 |
4 | | |
5 | | SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com> |
6 | | |
7 | | The timegm code is Copyright (c) 2001-2006, NLnet Labs. All rights reserved. |
8 | | |
9 | | SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0 |
10 | | |
11 | | The Original Code is eric. The Initial Developer of the Original |
12 | | Code is Eric Busboom |
13 | | |
14 | | The timegm code in this file is copyrighted by NLNET LABS 2001-2006, |
15 | | according to the following conditions: |
16 | | |
17 | | * Redistributions of source code must retain the above copyright notice, |
18 | | this list of conditions and the following disclaimer. |
19 | | |
20 | | * Redistributions in binary form must reproduce the above copyright notice, |
21 | | this list of conditions and the following disclaimer in the documentation |
22 | | * and/or other materials provided with the distribution. |
23 | | |
24 | | * Neither the name of the NLNET LABS nor the names of its contributors may |
25 | | be used to endorse or promote products derived from this software without |
26 | | specific prior written permission. |
27 | | |
28 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
29 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
30 | | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
31 | | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE |
32 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
33 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
34 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
35 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
36 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
37 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
38 | | POSSIBILITY OF SUCH DAMAGE. |
39 | | ======================================================================*/ |
40 | | |
41 | | #ifdef HAVE_CONFIG_H |
42 | | #include <config.h> |
43 | | #endif |
44 | | |
45 | | #include "icaltime.h" |
46 | | #include "astime.h" |
47 | | #include "icalerror.h" |
48 | | #include "icalmemory.h" |
49 | | #include "icaltimezone.h" |
50 | | |
51 | | #include <ctype.h> |
52 | | #include <stdlib.h> |
53 | | |
54 | | /* The first array is for non-leap years, the second for leap years*/ |
55 | | static const int days_in_year_passed_month[2][13] = { |
56 | | /* jan feb mar apr may jun jul aug sep oct nov dec */ |
57 | | {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, |
58 | | {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} |
59 | | }; |
60 | | |
61 | | static int icaltime_leap_days(int y1, int y2) |
62 | 0 | { |
63 | 0 | --y1; |
64 | 0 | --y2; |
65 | 0 | return (y2 / 4 - y1 / 4) - (y2 / 100 - y1 / 100) + (y2 / 400 - y1 / 400); |
66 | 0 | } |
67 | | |
68 | | /* |
69 | | * Code adapted from Python 2.4.1 sources (Lib/calendar.py). |
70 | | */ |
71 | | static icaltime_t icaltime_timegm(const struct tm *tm) |
72 | 0 | { |
73 | 0 | int year; |
74 | 0 | icaltime_t days; |
75 | 0 | icaltime_t hours; |
76 | 0 | icaltime_t minutes; |
77 | 0 | icaltime_t seconds; |
78 | |
|
79 | 0 | year = 1900 + tm->tm_year; |
80 | 0 | days = (icaltime_t)(365 * (year - 1970) + icaltime_leap_days(1970, year)); |
81 | 0 | days += days_in_year_passed_month[0][tm->tm_mon]; |
82 | |
|
83 | 0 | if (tm->tm_mon > 1 && icaltime_is_leap_year(year)) |
84 | 0 | ++days; |
85 | |
|
86 | 0 | days += tm->tm_mday - 1; |
87 | 0 | hours = days * 24 + tm->tm_hour; |
88 | 0 | minutes = hours * 60 + tm->tm_min; |
89 | 0 | seconds = minutes * 60 + tm->tm_sec; |
90 | |
|
91 | 0 | return seconds; |
92 | 0 | } |
93 | | |
94 | | /* |
95 | | * Function to convert a struct tm time specification |
96 | | * to an ANSI-compatible icaltime_t using the specified time zone. |
97 | | * This is different from the standard mktime() function |
98 | | * in that we don't want the automatic adjustments for |
99 | | * local daylight savings time applied to the result. |
100 | | * This function expects well-formed input. |
101 | | */ |
102 | | static icaltime_t make_time(struct tm *tm, int tzm) |
103 | 0 | { |
104 | 0 | icaltime_t tim; |
105 | 0 | int febs; |
106 | |
|
107 | 0 | static const int days[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }; |
108 | | |
109 | | /* check that month specification within range */ |
110 | |
|
111 | 0 | if (tm->tm_mon < 0 || tm->tm_mon > 11) |
112 | 0 | return ((icaltime_t) - 1); |
113 | | |
114 | 0 | if (tm->tm_year < 2) |
115 | 0 | return ((icaltime_t)-1); |
116 | | |
117 | | #if (SIZEOF_ICALTIME_T == 4) |
118 | | /* check that year specification within range */ |
119 | | |
120 | | if (tm->tm_year > 138) |
121 | | return ((icaltime_t) - 1); |
122 | | |
123 | | /* check for upper bound of Jan 17, 2038 (to avoid possibility of |
124 | | 32-bit arithmetic overflow) */ |
125 | | |
126 | | if (tm->tm_year == 138) { |
127 | | if (tm->tm_mon > 0) { |
128 | | return ((icaltime_t) - 1); |
129 | | } else if (tm->tm_mday > 17) { |
130 | | return ((icaltime_t) - 1); |
131 | | } |
132 | | } |
133 | | #else |
134 | | /* We don't support years >= 10000, because the function has not been tested at this range. */ |
135 | 0 | if (tm->tm_year >= 8100) { |
136 | 0 | return ((icaltime_t)-1); |
137 | 0 | } |
138 | 0 | #endif /* SIZEOF_ICALTIME_T */ |
139 | | |
140 | | /* |
141 | | * calculate elapsed days since start of the epoch (midnight Jan |
142 | | * 1st, 1970 UTC) 17 = number of leap years between 1900 and 1970 |
143 | | * (number of leap days to subtract) |
144 | | */ |
145 | | |
146 | 0 | tim = (icaltime_t) ((tm->tm_year - 70) * 365 + ((tm->tm_year - 1) / 4) - 17); |
147 | | |
148 | | /* adjust: no leap days every 100 years, except every 400 years. */ |
149 | |
|
150 | 0 | febs = (tm->tm_year - 100) - ((tm->tm_mon <= 1) ? 1 : 0); |
151 | 0 | tim -= febs / 100; |
152 | 0 | tim += febs / 400; |
153 | | |
154 | | /* add number of days elapsed in the current year */ |
155 | |
|
156 | 0 | tim += days[tm->tm_mon]; |
157 | | |
158 | | /* check and adjust for leap years */ |
159 | |
|
160 | 0 | if ((tm->tm_year & 3) == 0 && tm->tm_mon > 1) |
161 | 0 | tim += 1; |
162 | | |
163 | | /* elapsed days to current date */ |
164 | |
|
165 | 0 | tim += tm->tm_mday; |
166 | | |
167 | | /* calculate elapsed hours since start of the epoch */ |
168 | |
|
169 | 0 | tim = tim * 24 + tm->tm_hour; |
170 | | |
171 | | /* calculate elapsed minutes since start of the epoch */ |
172 | |
|
173 | 0 | tim = tim * 60 + tm->tm_min; |
174 | | |
175 | | /* adjust per time zone specification */ |
176 | |
|
177 | 0 | tim -= tzm; |
178 | | |
179 | | /* calculate elapsed seconds since start of the epoch */ |
180 | |
|
181 | 0 | tim = tim * 60 + tm->tm_sec; |
182 | | |
183 | | /* return number of seconds since start of the epoch */ |
184 | |
|
185 | 0 | return (tim); |
186 | 0 | } |
187 | | |
188 | | struct icaltimetype icaltime_from_timet_with_zone(const icaltime_t tm, const int is_date, |
189 | | const icaltimezone *zone) |
190 | 0 | { |
191 | 0 | struct icaltimetype tt; |
192 | 0 | struct tm t; |
193 | 0 | icaltimezone *utc_zone; |
194 | |
|
195 | 0 | utc_zone = icaltimezone_get_utc_timezone(); |
196 | | |
197 | | /* Convert the icaltime_t to a struct tm in UTC time. We can trust gmtime for this. */ |
198 | 0 | if (!icalgmtime_r(&tm, &t)) |
199 | 0 | return is_date ? icaltime_null_date () : icaltime_null_time (); |
200 | | |
201 | 0 | tt.year = t.tm_year + 1900; |
202 | 0 | tt.month = t.tm_mon + 1; |
203 | 0 | tt.day = t.tm_mday; |
204 | 0 | tt.hour = t.tm_hour; |
205 | 0 | tt.minute = t.tm_min; |
206 | 0 | tt.second = t.tm_sec; |
207 | 0 | tt.is_date = 0; |
208 | 0 | tt.is_daylight = 0; |
209 | 0 | tt.zone = (zone == NULL) ? NULL : utc_zone; |
210 | | |
211 | | /* Use our timezone functions to convert to the required timezone. */ |
212 | 0 | icaltimezone_convert_time(&tt, utc_zone, (icaltimezone *) zone); |
213 | |
|
214 | 0 | tt.is_date = is_date; |
215 | | |
216 | | /* If it is a DATE value, make sure hour, minute & second are 0. */ |
217 | 0 | if (is_date) { |
218 | 0 | tt.hour = 0; |
219 | 0 | tt.minute = 0; |
220 | 0 | tt.second = 0; |
221 | 0 | } |
222 | |
|
223 | 0 | return tt; |
224 | 0 | } |
225 | | |
226 | | struct icaltimetype icaltime_current_time_with_zone(const icaltimezone *zone) |
227 | 0 | { |
228 | 0 | return icaltime_from_timet_with_zone(icaltime(NULL), 0, zone); |
229 | 0 | } |
230 | | |
231 | | struct icaltimetype icaltime_today(void) |
232 | 0 | { |
233 | 0 | return icaltime_from_timet_with_zone(icaltime(NULL), 1, NULL); |
234 | 0 | } |
235 | | |
236 | | icaltime_t icaltime_as_timet(const struct icaltimetype tt) |
237 | 0 | { |
238 | 0 | struct tm stm; |
239 | 0 | icaltime_t t; |
240 | | |
241 | | /* If the time is the special null time, return 0. */ |
242 | 0 | if (icaltime_is_null_time(tt)) { |
243 | 0 | return 0; |
244 | 0 | } |
245 | | |
246 | | /* Copy the icaltimetype to a struct tm. */ |
247 | 0 | memset(&stm, 0, sizeof(struct tm)); |
248 | |
|
249 | 0 | if (icaltime_is_date(tt)) { |
250 | 0 | stm.tm_sec = stm.tm_min = stm.tm_hour = 0; |
251 | 0 | } else { |
252 | 0 | stm.tm_sec = tt.second; |
253 | 0 | stm.tm_min = tt.minute; |
254 | 0 | stm.tm_hour = tt.hour; |
255 | 0 | } |
256 | |
|
257 | 0 | stm.tm_mday = tt.day; |
258 | 0 | stm.tm_mon = tt.month - 1; |
259 | 0 | stm.tm_year = tt.year - 1900; |
260 | 0 | stm.tm_isdst = -1; |
261 | |
|
262 | 0 | t = make_time(&stm, 0); |
263 | |
|
264 | 0 | return t; |
265 | 0 | } |
266 | | |
267 | | icaltime_t icaltime_as_timet_with_zone(const struct icaltimetype tt, const icaltimezone *zone) |
268 | 0 | { |
269 | 0 | icaltimezone *utc_zone; |
270 | 0 | struct tm stm; |
271 | 0 | icaltime_t t; |
272 | 0 | struct icaltimetype local_tt; |
273 | |
|
274 | 0 | utc_zone = icaltimezone_get_utc_timezone(); |
275 | | |
276 | | /* If the time is the special null time, return 0. */ |
277 | 0 | if (icaltime_is_null_time(tt)) { |
278 | 0 | return 0; |
279 | 0 | } |
280 | | |
281 | 0 | local_tt = tt; |
282 | | |
283 | | /* Clear the is_date flag, so we can convert the time. */ |
284 | 0 | local_tt.is_date = 0; |
285 | | |
286 | | /* Use our timezone functions to convert to UTC. */ |
287 | 0 | icaltimezone_convert_time(&local_tt, (icaltimezone *) zone, utc_zone); |
288 | | |
289 | | /* Copy the icaltimetype to a struct tm. */ |
290 | 0 | memset(&stm, 0, sizeof(struct tm)); |
291 | |
|
292 | 0 | stm.tm_sec = local_tt.second; |
293 | 0 | stm.tm_min = local_tt.minute; |
294 | 0 | stm.tm_hour = local_tt.hour; |
295 | 0 | stm.tm_mday = local_tt.day; |
296 | 0 | stm.tm_mon = local_tt.month - 1; |
297 | 0 | stm.tm_year = local_tt.year - 1900; |
298 | 0 | stm.tm_isdst = -1; |
299 | |
|
300 | 0 | t = icaltime_timegm(&stm); |
301 | |
|
302 | 0 | return t; |
303 | 0 | } |
304 | | |
305 | | const char *icaltime_as_ical_string(const struct icaltimetype tt) |
306 | 0 | { |
307 | 0 | char *buf; |
308 | |
|
309 | 0 | buf = icaltime_as_ical_string_r(tt); |
310 | 0 | icalmemory_add_tmp_buffer(buf); |
311 | 0 | return buf; |
312 | 0 | } |
313 | | |
314 | | char *icaltime_as_ical_string_r(const struct icaltimetype tt) |
315 | 1.83k | { |
316 | 1.83k | size_t size = 17; |
317 | 1.83k | char *buf = icalmemory_new_buffer(size); |
318 | | |
319 | 1.83k | if (tt.is_date) { |
320 | 1.83k | snprintf(buf, size, "%04d%02d%02d", tt.year, tt.month, tt.day); |
321 | 1.83k | } else { |
322 | 0 | const char *fmt; |
323 | |
|
324 | 0 | if (icaltime_is_utc(tt)) { |
325 | 0 | fmt = "%04d%02d%02dT%02d%02d%02dZ"; |
326 | 0 | } else { |
327 | 0 | fmt = "%04d%02d%02dT%02d%02d%02d"; |
328 | 0 | } |
329 | 0 | snprintf(buf, size, fmt, tt.year, tt.month, tt.day, tt.hour, tt.minute, tt.second); |
330 | 0 | } |
331 | | |
332 | 1.83k | return buf; |
333 | 1.83k | } |
334 | | |
335 | | /* Implementation note: we call icaltime_adjust() with no adjustment. */ |
336 | | struct icaltimetype icaltime_normalize(const struct icaltimetype tt) |
337 | 0 | { |
338 | 0 | struct icaltimetype ret = tt; |
339 | |
|
340 | 0 | icaltime_adjust(&ret, 0, 0, 0, 0); |
341 | 0 | return ret; |
342 | 0 | } |
343 | | |
344 | | struct icaltimetype icaltime_from_string(const char *str) |
345 | 13.8k | { |
346 | 13.8k | struct icaltimetype tt = icaltime_null_time(); |
347 | 13.8k | size_t size; |
348 | | |
349 | 13.8k | icalerror_check_arg_re(str != 0, "str", icaltime_null_time()); |
350 | | |
351 | 13.8k | size = strlen(str); |
352 | | |
353 | 13.8k | if ((size == 15) || (size == 19)) { /* floating time with/without separators */ |
354 | 2.41k | tt.is_date = 0; |
355 | 11.4k | } else if ((size == 16) || (size == 20)) { /* UTC time, ends in 'Z' */ |
356 | 892 | if ((str[size-1] != 'Z')) |
357 | 576 | goto FAIL; |
358 | | |
359 | 316 | tt.zone = icaltimezone_get_utc_timezone(); |
360 | 316 | tt.is_date = 0; |
361 | 10.5k | } else if ((size == 8) || (size == 10)) { /* A DATE */ |
362 | 5.29k | tt.is_date = 1; |
363 | 5.29k | } else { /* error */ |
364 | 5.26k | goto FAIL; |
365 | 5.26k | } |
366 | | |
367 | 8.02k | if (tt.is_date == 1) { |
368 | 5.29k | if (size == 10) { |
369 | 1.09k | char dsep1, dsep2; |
370 | | |
371 | 1.09k | if (sscanf(str, "%04d%c%02d%c%02d", |
372 | 1.09k | &tt.year, &dsep1, &tt.month, &dsep2, &tt.day) < 5) { |
373 | 225 | goto FAIL; |
374 | 225 | } |
375 | | |
376 | 866 | if ((dsep1 != '-') || (dsep2 != '-')) { |
377 | 514 | goto FAIL; |
378 | 514 | } |
379 | 4.20k | } else if (sscanf(str, "%04d%02d%02d", &tt.year, &tt.month, &tt.day) < 3) { |
380 | 404 | goto FAIL; |
381 | 404 | } |
382 | 5.29k | } else { |
383 | 2.72k | if (size > 16) { |
384 | 1.67k | char dsep1, dsep2, tsep, tsep1, tsep2; |
385 | | |
386 | 1.67k | if (sscanf(str, "%04d%c%02d%c%02d%c%02d%c%02d%c%02d", |
387 | 1.67k | &tt.year, &dsep1, &tt.month, &dsep2, &tt.day, &tsep, |
388 | 1.67k | &tt.hour, &tsep1, &tt.minute, &tsep2, &tt.second) < 11) { |
389 | 420 | goto FAIL; |
390 | 420 | } |
391 | | |
392 | 1.25k | if ((tsep != 'T') || |
393 | 1.25k | (dsep1 != '-') || (dsep2 != '-') || |
394 | 1.25k | (tsep1 != ':') || (tsep2 != ':')) { |
395 | 1.04k | goto FAIL; |
396 | 1.04k | } |
397 | | |
398 | 1.25k | } else { |
399 | 1.05k | char tsep; |
400 | | |
401 | 1.05k | if (sscanf(str, "%04d%02d%02d%c%02d%02d%02d", |
402 | 1.05k | &tt.year, &tt.month, &tt.day, &tsep, |
403 | 1.05k | &tt.hour, &tt.minute, &tt.second) < 7) { |
404 | 529 | goto FAIL; |
405 | 529 | } |
406 | | |
407 | 521 | if (tsep != 'T') { |
408 | 227 | goto FAIL; |
409 | 227 | } |
410 | 521 | } |
411 | 2.72k | } |
412 | | |
413 | 4.65k | return tt; |
414 | | |
415 | 9.20k | FAIL: |
416 | 9.20k | icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); |
417 | 9.20k | return icaltime_null_time(); |
418 | 8.02k | } |
419 | | |
420 | | int icaltime_is_leap_year(const int year) |
421 | 0 | { |
422 | 0 | if (year <= 1752) { |
423 | 0 | return (year % 4 == 0); |
424 | 0 | } else { |
425 | 0 | return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); |
426 | 0 | } |
427 | 0 | } |
428 | | |
429 | | int icaltime_days_in_year(const int year) |
430 | 0 | { |
431 | 0 | if (icaltime_is_leap_year(year)) { |
432 | 0 | return 366; |
433 | 0 | } else { |
434 | 0 | return 365; |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | static const int _days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
439 | | |
440 | | int icaltime_days_in_month(const int month, const int year) |
441 | 0 | { |
442 | 0 | int days; |
443 | | |
444 | | /* The old code aborting if it was passed a parameter like BYMONTH=0 |
445 | | * Unfortunately it's not practical right now to pass an error all |
446 | | * the way up the stack, so instead of aborting we're going to apply |
447 | | * the GIGO principle and simply return '30 days' if we get an |
448 | | * invalid month. Modern applications cannot tolerate crashing. |
449 | | * assert(month > 0); |
450 | | * assert(month <= 12); |
451 | | */ |
452 | 0 | if ((month < 1) || (month > 12)) { |
453 | 0 | return 30; |
454 | 0 | } |
455 | | |
456 | 0 | days = _days_in_month[month]; |
457 | |
|
458 | 0 | if (month == 2) { |
459 | 0 | days += icaltime_is_leap_year(year); |
460 | 0 | } |
461 | |
|
462 | 0 | return days; |
463 | 0 | } |
464 | | |
465 | | /* 1-> Sunday, 7->Saturday */ |
466 | | int icaltime_day_of_week(const struct icaltimetype t) |
467 | 0 | { |
468 | 0 | UTinstantInt jt; |
469 | |
|
470 | 0 | memset(&jt, 0, sizeof(UTinstantInt)); |
471 | |
|
472 | 0 | jt.year = t.year; |
473 | 0 | jt.month = t.month; |
474 | 0 | jt.day = t.day; |
475 | |
|
476 | 0 | juldat_int(&jt); |
477 | |
|
478 | 0 | return jt.weekday + 1; |
479 | 0 | } |
480 | | |
481 | | int icaltime_start_doy_week(const struct icaltimetype t, int fdow) |
482 | 0 | { |
483 | 0 | UTinstantInt jt; |
484 | 0 | int delta; |
485 | |
|
486 | 0 | memset(&jt, 0, sizeof(UTinstantInt)); |
487 | |
|
488 | 0 | jt.year = t.year; |
489 | 0 | jt.month = t.month; |
490 | 0 | jt.day = t.day; |
491 | |
|
492 | 0 | juldat_int(&jt); |
493 | 0 | caldat_int(&jt); |
494 | |
|
495 | 0 | delta = jt.weekday - (fdow - 1); |
496 | 0 | if (delta < 0) { |
497 | 0 | delta += 7; |
498 | 0 | } |
499 | 0 | return jt.day_of_year - delta; |
500 | 0 | } |
501 | | |
502 | | int icaltime_week_number(const struct icaltimetype ictt) |
503 | 0 | { |
504 | 0 | UTinstantInt jt; |
505 | |
|
506 | 0 | memset(&jt, 0, sizeof(UTinstantInt)); |
507 | |
|
508 | 0 | jt.year = ictt.year; |
509 | 0 | jt.month = ictt.month; |
510 | 0 | jt.day = ictt.day; |
511 | |
|
512 | 0 | juldat_int(&jt); |
513 | 0 | caldat_int(&jt); |
514 | |
|
515 | 0 | return (jt.day_of_year - jt.weekday) / 7; |
516 | 0 | } |
517 | | |
518 | | int icaltime_day_of_year(const struct icaltimetype t) |
519 | 0 | { |
520 | 0 | int is_leap = icaltime_is_leap_year(t.year); |
521 | |
|
522 | 0 | return days_in_year_passed_month[is_leap][t.month - 1] + t.day; |
523 | 0 | } |
524 | | |
525 | | struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year) |
526 | 0 | { |
527 | 0 | struct icaltimetype tt = icaltime_null_date(); |
528 | 0 | int is_leap; |
529 | 0 | int month; |
530 | 0 | int doy = _doy; |
531 | 0 | int year = _year; |
532 | |
|
533 | 0 | is_leap = icaltime_is_leap_year(year); |
534 | | |
535 | | /* Zero and neg numbers represent days of the previous year */ |
536 | 0 | if (doy < 1) { |
537 | 0 | year--; |
538 | 0 | is_leap = icaltime_is_leap_year(year); |
539 | 0 | doy += days_in_year_passed_month[is_leap][12]; |
540 | 0 | } else if (doy > days_in_year_passed_month[is_leap][12]) { |
541 | | /* Move on to the next year */ |
542 | 0 | is_leap = icaltime_is_leap_year(year); |
543 | 0 | doy -= days_in_year_passed_month[is_leap][12]; |
544 | 0 | year++; |
545 | 0 | } |
546 | |
|
547 | 0 | tt.year = year; |
548 | |
|
549 | 0 | for (month = 11; month >= 0; month--) { |
550 | 0 | if (doy > days_in_year_passed_month[is_leap][month]) { |
551 | 0 | tt.month = month + 1; |
552 | 0 | tt.day = doy - days_in_year_passed_month[is_leap][month]; |
553 | 0 | break; |
554 | 0 | } |
555 | 0 | } |
556 | |
|
557 | 0 | return tt; |
558 | 0 | } |
559 | | |
560 | | struct icaltimetype icaltime_null_time(void) |
561 | 110k | { |
562 | 110k | struct icaltimetype t; |
563 | | |
564 | 110k | memset(&t, 0, sizeof(struct icaltimetype)); |
565 | | |
566 | 110k | return t; |
567 | 110k | } |
568 | | |
569 | | struct icaltimetype icaltime_null_date(void) |
570 | 951 | { |
571 | 951 | struct icaltimetype t; |
572 | | |
573 | 951 | memset(&t, 0, sizeof(struct icaltimetype)); |
574 | | |
575 | 951 | t.is_date = 1; |
576 | | |
577 | | /* |
578 | | * Init to -1 to match what icalyacc.y used to do. |
579 | | * Does anything depend on this? |
580 | | */ |
581 | 951 | t.hour = -1; |
582 | 951 | t.minute = -1; |
583 | 951 | t.second = -1; |
584 | | |
585 | 951 | return t; |
586 | 951 | } |
587 | | |
588 | | int icaltime_is_valid_time(const struct icaltimetype t) |
589 | 0 | { |
590 | 0 | if (t.year < 0 || t.year > 3000 || t.is_date > 1 || t.is_date < 0) { |
591 | 0 | return 0; |
592 | 0 | } else { |
593 | 0 | return 1; |
594 | 0 | } |
595 | 0 | } |
596 | | |
597 | | int icaltime_is_date(const struct icaltimetype t) |
598 | 2.09k | { |
599 | 2.09k | return t.is_date; |
600 | 2.09k | } |
601 | | |
602 | | int icaltime_is_utc(const struct icaltimetype t) |
603 | 1.08k | { |
604 | 1.08k | return t.zone == icaltimezone_get_utc_timezone(); |
605 | 1.08k | } |
606 | | |
607 | | int icaltime_is_null_time(const struct icaltimetype t) |
608 | 26.0k | { |
609 | 26.0k | if (t.second + t.minute + t.hour + t.day + t.month + t.year == 0) { |
610 | 17.9k | return 1; |
611 | 17.9k | } |
612 | | |
613 | 8.07k | return 0; |
614 | 26.0k | } |
615 | | |
616 | | int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in) |
617 | 10.0k | { |
618 | 10.0k | struct icaltimetype a, b; |
619 | | |
620 | | /* We only need to perform time zone conversion if times aren't in the same time zone |
621 | | or neither of them is floating (zone equals NULL) */ |
622 | 10.0k | if (a_in.zone != b_in.zone && a_in.zone != NULL && b_in.zone != NULL) { |
623 | 0 | a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone()); |
624 | 0 | b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone()); |
625 | 10.0k | } else { |
626 | 10.0k | a = a_in; |
627 | 10.0k | b = b_in; |
628 | 10.0k | } |
629 | | |
630 | 10.0k | if (a.year > b.year) { |
631 | 0 | return 1; |
632 | 10.0k | } else if (a.year < b.year) { |
633 | 0 | return -1; |
634 | 10.0k | } else if (a.month > b.month) { |
635 | 0 | return 1; |
636 | 10.0k | } else if (a.month < b.month) { |
637 | 0 | return -1; |
638 | 10.0k | } else if (a.day > b.day) { |
639 | 0 | return 1; |
640 | 10.0k | } else if (a.day < b.day) { |
641 | 0 | return -1; |
642 | 0 | } |
643 | | |
644 | | /* if both are dates, we are done */ |
645 | 10.0k | if (a.is_date && b.is_date) { |
646 | 0 | return 0; |
647 | | |
648 | | /* else, if only one is a date (and we already know the date part is equal), |
649 | | then the other is greater */ |
650 | 10.0k | } else if (b.is_date) { |
651 | 0 | return 1; |
652 | 10.0k | } else if (a.is_date) { |
653 | 0 | return -1; |
654 | 10.0k | } else if (a.hour > b.hour) { |
655 | 0 | return 1; |
656 | 10.0k | } else if (a.hour < b.hour) { |
657 | 0 | return -1; |
658 | 10.0k | } else if (a.minute > b.minute) { |
659 | 0 | return 1; |
660 | 10.0k | } else if (a.minute < b.minute) { |
661 | 0 | return -1; |
662 | 10.0k | } else if (a.second > b.second) { |
663 | 0 | return 1; |
664 | 10.0k | } else if (a.second < b.second) { |
665 | 0 | return -1; |
666 | 0 | } |
667 | | |
668 | 10.0k | return 0; |
669 | 10.0k | } |
670 | | |
671 | | int icaltime_compare_date_only(const struct icaltimetype a_in, |
672 | | const struct icaltimetype b_in) |
673 | 0 | { |
674 | 0 | struct icaltimetype a, b; |
675 | 0 | icaltimezone *tz = icaltimezone_get_utc_timezone(); |
676 | |
|
677 | 0 | a = icaltime_convert_to_zone(a_in, tz); |
678 | 0 | b = icaltime_convert_to_zone(b_in, tz); |
679 | |
|
680 | 0 | if (a.year > b.year) { |
681 | 0 | return 1; |
682 | 0 | } else if (a.year < b.year) { |
683 | 0 | return -1; |
684 | 0 | } |
685 | | |
686 | 0 | if (a.month > b.month) { |
687 | 0 | return 1; |
688 | 0 | } else if (a.month < b.month) { |
689 | 0 | return -1; |
690 | 0 | } |
691 | | |
692 | 0 | if (a.day > b.day) { |
693 | 0 | return 1; |
694 | 0 | } else if (a.day < b.day) { |
695 | 0 | return -1; |
696 | 0 | } |
697 | | |
698 | 0 | return 0; |
699 | 0 | } |
700 | | |
701 | | int icaltime_compare_date_only_tz(const struct icaltimetype a_in, |
702 | | const struct icaltimetype b_in, |
703 | | icaltimezone *tz) |
704 | 0 | { |
705 | 0 | struct icaltimetype a, b; |
706 | |
|
707 | 0 | a = icaltime_convert_to_zone(a_in, tz); |
708 | 0 | b = icaltime_convert_to_zone(b_in, tz); |
709 | |
|
710 | 0 | if (a.year > b.year) { |
711 | 0 | return 1; |
712 | 0 | } else if (a.year < b.year) { |
713 | 0 | return -1; |
714 | 0 | } |
715 | | |
716 | 0 | if (a.month > b.month) { |
717 | 0 | return 1; |
718 | 0 | } else if (a.month < b.month) { |
719 | 0 | return -1; |
720 | 0 | } |
721 | | |
722 | 0 | if (a.day > b.day) { |
723 | 0 | return 1; |
724 | 0 | } else if (a.day < b.day) { |
725 | 0 | return -1; |
726 | 0 | } |
727 | | |
728 | 0 | return 0; |
729 | 0 | } |
730 | | |
731 | | /* These are defined in icalduration.c: |
732 | | struct icaltimetype icaltime_add(struct icaltimetype t, |
733 | | struct icaldurationtype d) |
734 | | struct icaldurationtype icaltime_subtract(struct icaltimetype t1, |
735 | | struct icaltimetype t2) |
736 | | */ |
737 | | |
738 | | void icaltime_adjust(struct icaltimetype *tt, |
739 | | const int days, const int hours, |
740 | | const int minutes, const int seconds) |
741 | 0 | { |
742 | 0 | int second, minute, hour, day; |
743 | 0 | int minutes_overflow, hours_overflow, days_overflow = 0, years_overflow; |
744 | 0 | int days_in_month; |
745 | | |
746 | | /* If we are passed a date make sure to ignore hour minute and second */ |
747 | 0 | if (tt->is_date) |
748 | 0 | goto IS_DATE; |
749 | | |
750 | | /* Add on the seconds. */ |
751 | 0 | second = tt->second + seconds; |
752 | 0 | tt->second = second % 60; |
753 | 0 | minutes_overflow = second / 60; |
754 | 0 | if (tt->second < 0) { |
755 | 0 | tt->second += 60; |
756 | 0 | minutes_overflow--; |
757 | 0 | } |
758 | | |
759 | | /* Add on the minutes. */ |
760 | 0 | minute = tt->minute + minutes + minutes_overflow; |
761 | 0 | tt->minute = minute % 60; |
762 | 0 | hours_overflow = minute / 60; |
763 | 0 | if (tt->minute < 0) { |
764 | 0 | tt->minute += 60; |
765 | 0 | hours_overflow--; |
766 | 0 | } |
767 | | |
768 | | /* Add on the hours. */ |
769 | 0 | hour = tt->hour + hours + hours_overflow; |
770 | 0 | tt->hour = hour % 24; |
771 | 0 | days_overflow = hour / 24; |
772 | 0 | if (tt->hour < 0) { |
773 | 0 | tt->hour += 24; |
774 | 0 | days_overflow--; |
775 | 0 | } |
776 | |
|
777 | 0 | IS_DATE: |
778 | | /* Normalize the month. We do this before handling the day since we may |
779 | | need to know what month it is to get the number of days in it. |
780 | | Note that months are 1 to 12, so we have to be a bit careful. */ |
781 | 0 | if (tt->month >= 13) { |
782 | 0 | years_overflow = (tt->month - 1) / 12; |
783 | 0 | tt->year += years_overflow; |
784 | 0 | tt->month -= years_overflow * 12; |
785 | 0 | } else if (tt->month <= 0) { |
786 | | /* 0 to -11 is -1 year out, -12 to -23 is -2 years. */ |
787 | 0 | years_overflow = (tt->month / 12) - 1; |
788 | 0 | tt->year += years_overflow; |
789 | 0 | tt->month -= years_overflow * 12; |
790 | 0 | } |
791 | | |
792 | | /* Add on the days. */ |
793 | 0 | day = tt->day + days + days_overflow; |
794 | 0 | if (day > 0) { |
795 | 0 | for (;;) { |
796 | 0 | days_in_month = icaltime_days_in_month(tt->month, tt->year); |
797 | 0 | if (day <= days_in_month) |
798 | 0 | break; |
799 | | |
800 | 0 | tt->month++; |
801 | 0 | if (tt->month >= 13) { |
802 | 0 | tt->year++; |
803 | 0 | tt->month = 1; |
804 | 0 | } |
805 | |
|
806 | 0 | day -= days_in_month; |
807 | 0 | } |
808 | 0 | } else { |
809 | 0 | while (day <= 0) { |
810 | 0 | if (tt->month == 1) { |
811 | 0 | tt->year--; |
812 | 0 | tt->month = 12; |
813 | 0 | } else { |
814 | 0 | tt->month--; |
815 | 0 | } |
816 | |
|
817 | 0 | day += icaltime_days_in_month(tt->month, tt->year); |
818 | 0 | } |
819 | 0 | } |
820 | 0 | tt->day = day; |
821 | 0 | } |
822 | | |
823 | | struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt, icaltimezone *zone) |
824 | 0 | { |
825 | 0 | struct icaltimetype ret = tt; |
826 | | |
827 | | /* If it's a date do nothing */ |
828 | 0 | if (tt.is_date) { |
829 | 0 | return ret; |
830 | 0 | } |
831 | | |
832 | 0 | if (tt.zone == zone) { |
833 | 0 | return ret; |
834 | 0 | } |
835 | | |
836 | | /* If it's a floating time we don't want to adjust the time */ |
837 | 0 | if (tt.zone != NULL) { |
838 | 0 | icaltimezone *from_zone = (icaltimezone *) tt.zone; |
839 | |
|
840 | 0 | if (!from_zone) { |
841 | 0 | from_zone = icaltimezone_get_utc_timezone(); |
842 | 0 | } |
843 | |
|
844 | 0 | icaltimezone_convert_time(&ret, from_zone, zone); |
845 | 0 | } |
846 | |
|
847 | 0 | ret.zone = zone; |
848 | |
|
849 | 0 | return ret; |
850 | 0 | } |
851 | | |
852 | | const icaltimezone *icaltime_get_timezone(const struct icaltimetype t) |
853 | 0 | { |
854 | 0 | return t.zone; |
855 | 0 | } |
856 | | |
857 | | const char *icaltime_get_tzid(const struct icaltimetype t) |
858 | 0 | { |
859 | 0 | if (t.zone != NULL) { |
860 | 0 | return icaltimezone_get_tzid((icaltimezone *) t.zone); |
861 | 0 | } else { |
862 | 0 | return NULL; |
863 | 0 | } |
864 | 0 | } |
865 | | |
866 | | struct icaltimetype icaltime_set_timezone(struct icaltimetype *t, const icaltimezone *zone) |
867 | 0 | { |
868 | | /* If it's a date do nothing */ |
869 | 0 | if (t->is_date) { |
870 | 0 | return *t; |
871 | 0 | } |
872 | | |
873 | 0 | if (t->zone == zone) { |
874 | 0 | return *t; |
875 | 0 | } |
876 | | |
877 | 0 | t->zone = zone; |
878 | |
|
879 | 0 | return *t; |
880 | 0 | } |
881 | | |
882 | | icaltime_span icaltime_span_new(struct icaltimetype dtstart, struct icaltimetype dtend, int is_busy) |
883 | 0 | { |
884 | 0 | icaltime_span span; |
885 | |
|
886 | 0 | span.is_busy = is_busy; |
887 | |
|
888 | 0 | span.start = icaltime_as_timet_with_zone(dtstart, |
889 | 0 | dtstart.zone ? dtstart. |
890 | 0 | zone : icaltimezone_get_utc_timezone()); |
891 | |
|
892 | 0 | if (icaltime_is_null_time(dtend)) { |
893 | 0 | if (!icaltime_is_date(dtstart)) { |
894 | | /* If dtstart is a DATE-TIME and there is no DTEND nor DURATION |
895 | | it takes no time */ |
896 | 0 | span.end = span.start; |
897 | 0 | return span; |
898 | 0 | } else { |
899 | 0 | dtend = dtstart; |
900 | 0 | } |
901 | 0 | } |
902 | | |
903 | 0 | span.end = icaltime_as_timet_with_zone(dtend, |
904 | 0 | dtend.zone ? dtend. |
905 | 0 | zone : icaltimezone_get_utc_timezone()); |
906 | |
|
907 | 0 | if (icaltime_is_date(dtstart)) { |
908 | | /* no time specified, go until the end of the day.. */ |
909 | 0 | span.end += 60 * 60 * 24 - 1; |
910 | 0 | } |
911 | 0 | return span; |
912 | 0 | } |
913 | | |
914 | | int icaltime_span_overlaps(icaltime_span *s1, icaltime_span *s2) |
915 | 0 | { |
916 | | /* s1->start in s2 */ |
917 | 0 | if (s1->start > s2->start && s1->start < s2->end) |
918 | 0 | return 1; |
919 | | |
920 | | /* s1->end in s2 */ |
921 | 0 | if (s1->end > s2->start && s1->end < s2->end) |
922 | 0 | return 1; |
923 | | |
924 | | /* s2->start in s1 */ |
925 | 0 | if (s2->start > s1->start && s2->start < s1->end) |
926 | 0 | return 1; |
927 | | |
928 | | /* s2->end in s1 */ |
929 | 0 | if (s2->end > s1->start && s2->end < s1->end) |
930 | 0 | return 1; |
931 | | |
932 | 0 | if (s1->start == s2->start && s1->end == s2->end) |
933 | 0 | return 1; |
934 | | |
935 | 0 | return 0; |
936 | 0 | } |
937 | | |
938 | | int icaltime_span_contains(icaltime_span *s, icaltime_span *container) |
939 | 0 | { |
940 | 0 | if ((s->start >= container->start && s->start < container->end) && |
941 | 0 | (s->end <= container->end && s->end > container->start)) { |
942 | 0 | return 1; |
943 | 0 | } |
944 | | |
945 | 0 | return 0; |
946 | 0 | } |