Libical API Documentation 4.0 UNRELEASED Go to the stable 3.0 documentation
Loading...
Searching...
No Matches
icalduration.c
Go to the documentation of this file.
1/*======================================================================
2 FILE: icalduration.c
3 CREATOR: eric 02 June 2000
4
5 SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com>
6 SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
7
8 The Original Code is eric. The Initial Developer of the Original
9 Code is Eric Busboom
10======================================================================*/
11
16
17#ifdef HAVE_CONFIG_H
18#include <config.h>
19#endif
20
21#include "icalduration.h"
22#include "icalerror.h"
23#include "icalmemory.h"
24#include "icaltime.h"
25#include "icaltimezone.h"
26
27/* From Seth Alves, <alves@hungry.com> */
29{
30 struct icaldurationtype dur;
31
33
34 if (seconds < 0) {
35 dur.is_neg = 1;
37 }
38 dur.seconds = (unsigned int)seconds;
39
40 return dur;
41}
42
44{
45 int i;
46 int begin_flag = 0;
47 int time_flag = 0;
48 int date_flag = 0;
49 int digits = -1;
50 int scan_size = -1;
51 int size = (int)strlen(str);
52 char p;
53 struct icaldurationtype d;
54
55 memset(&d, 0, sizeof(struct icaldurationtype));
56
57 for (i = 0; i != size; i++) {
58 p = str[i];
59
60 switch (p) {
61 case '+': {
62 if (i != 0 || begin_flag == 1) {
63 goto error;
64 }
65 break;
66 }
67 case '-': {
68 if (i != 0 || begin_flag == 1) {
69 goto error;
70 }
71 d.is_neg = 1;
72 break;
73 }
74
75 case 'P': {
76 if (i != 0 && i != 1) {
77 goto error;
78 }
79 begin_flag = 1;
80 break;
81 }
82
83 case 'T': {
84 time_flag = 1;
85 break;
86 }
87
88 case '0':
89 case '1':
90 case '2':
91 case '3':
92 case '4':
93 case '5':
94 case '6':
95 case '7':
96 case '8':
97 case '9': {
98 /* HACK.
99 Skip any more digits if the last one read has not been assigned */
100 if (digits != -1) {
101 break;
102 }
103
104 if (begin_flag == 0) {
105 goto error;
106 }
107 /* Get all of the digits, not one at a time */
108 scan_size = sscanf(&str[i], "%10d", &digits); /*limit to 10digits.
109 increase as needed */
110 if (scan_size != 1) {
111 goto error;
112 }
113 break;
114 }
115
116 case 'H': {
117 if (time_flag == 0 || d.hours != 0 || digits == -1) {
118 goto error;
119 }
120 d.hours = (unsigned int)digits;
121 digits = -1;
122 break;
123 }
124 case 'M': {
125 if (time_flag == 0 || d.minutes != 0 || digits == -1) {
126 goto error;
127 }
128 d.minutes = (unsigned int)digits;
129 digits = -1;
130 break;
131 }
132 case 'S': {
133 if (time_flag == 0 || d.seconds != 0 || digits == -1) {
134 goto error;
135 }
136 d.seconds = (unsigned int)digits;
137 digits = -1;
138 break;
139 }
140 case 'W': {
141 if (time_flag == 1 || date_flag == 1 || d.weeks != 0 || digits == -1) {
142 goto error;
143 }
144 d.weeks = (unsigned int)digits;
145 digits = -1;
146 break;
147 }
148 case 'D': {
149 if (time_flag == 1 || d.days != 0 || digits == -1) {
150 goto error;
151 }
152 date_flag = 1;
153 d.days = (unsigned int)digits;
154 digits = -1;
155 break;
156 }
157 default: {
158 goto error;
159 }
160 }
161 }
162
163 return d;
164
165error:
168}
169
170static void append_duration_segment(char **buf, char **buf_ptr, size_t *buf_size,
171 const char *sep, unsigned int value)
172{
173 char temp[1024];
174
175 snprintf(temp, sizeof(temp), "%u", value);
176
177 icalmemory_append_string(buf, buf_ptr, buf_size, temp);
178 icalmemory_append_string(buf, buf_ptr, buf_size, sep);
179}
180
182{
183 char *buf;
184
187 return buf;
188}
189
191{
192 char *buf;
193 size_t buf_size = 256;
194 char *buf_ptr = 0;
195
196 buf = (char *)icalmemory_new_buffer(buf_size);
197 buf_ptr = buf;
198
199 if (d.weeks == 0 &&
200 d.days == 0 &&
201 d.hours == 0 &&
202 d.minutes == 0 &&
203 d.seconds == 0) {
204 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "PT0S");
205 } else {
206 if (d.is_neg == 1) {
207 icalmemory_append_char(&buf, &buf_ptr, &buf_size, '-');
208 }
209
210 icalmemory_append_char(&buf, &buf_ptr, &buf_size, 'P');
211
212 if (d.weeks != 0) {
213 append_duration_segment(&buf, &buf_ptr, &buf_size, "W", d.weeks);
214 }
215
216 if (d.days != 0) {
217 append_duration_segment(&buf, &buf_ptr, &buf_size, "D", d.days);
218 }
219
220 if (d.hours != 0 || d.minutes != 0 || d.seconds != 0) {
221 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "T");
222
223 if (d.hours != 0) {
224 append_duration_segment(&buf, &buf_ptr, &buf_size, "H", d.hours);
225 }
226 if (d.minutes != 0) {
227 append_duration_segment(&buf, &buf_ptr, &buf_size, "M", d.minutes);
228 }
229 if (d.seconds != 0) {
230 append_duration_segment(&buf, &buf_ptr, &buf_size, "S", d.seconds);
231 }
232 }
233 }
234
235 return buf;
236}
237
239{
240 if (dur.days != 0 || dur.weeks != 0) {
241 /* The length of a day is position-dependent */
243 return 0;
244 }
245 return (int)(((int)dur.seconds +
246 60 * ((int)dur.minutes +
247 60 * ((int)dur.hours))) *
248 (dur.is_neg == 1 ? -1 : 1));
249}
250
252{
253 return (int)(((int)dur.seconds +
254 60 * ((int)dur.minutes +
255 60 * ((int)dur.hours +
256 24 * ((int)dur.days +
257 7 * (int)dur.weeks)))) *
258 (dur.is_neg == 1 ? -1 : 1));
259}
260
262{
263 struct icaldurationtype d;
264
265 memset(&d, 0, sizeof(struct icaldurationtype));
266
267 return d;
268}
269
271{
273 return memcmp(&d, &n, sizeof(struct icaldurationtype)) ? false : true;
274}
275
276/* In icalvalue_new_from_string_with_error, we should not call
277 icaldurationtype_is_null_duration() to see if there is an error
278 condition. Null duration is perfectly valid for an alarm.
279 We cannot depend on the caller to check icalerrno either,
280 following the philosophy of unix errno. we set the is_neg
281 to -1 to indicate that this is a bad duration.
282*/
284{
285 struct icaldurationtype d;
286
287 memset(&d, 0, sizeof(struct icaldurationtype));
288 d.is_neg = -1;
289 return d;
290}
291
293{
294 return (d.is_neg == -1);
295}
296
298{
299 struct icaltimetype t_days;
300 if (t.is_date &&
301 (d.seconds != 0 ||
302 d.minutes != 0 ||
303 d.hours != 0)) {
304 /* We can't add time durations to dates */
305 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
306 return icaltime_null_time();
307 }
308
309 /* Days are nominal (not exact) and position-dependent */
310 if (!d.is_neg) {
311 t.day += (int)d.days;
312 t.day += (int)(d.weeks * 7);
313 } else {
314 t.day -= (int)d.days;
315 t.day -= (int)(d.weeks * 7);
316 }
317
318 t = icaltime_normalize(t);
319 t_days = t;
320
321 if (!d.is_neg) {
322 t.second += (int)d.seconds;
323 t.minute += (int)d.minutes;
324 t.hour += (int)d.hours;
325 } else {
326 t.second -= (int)d.seconds;
327 t.minute -= (int)d.minutes;
328 t.hour -= (int)d.hours;
329 }
330
331 t = icaltime_normalize(t);
332 /* RFC 5545 states:
333 * In the case of discontinuities in the time scale, such
334 * as the change from standard time to daylight time and back, the
335 * computation of the exact duration requires the subtraction or
336 * addition of the change of duration of the discontinuity.
337 */
338 icaltime_adjust(&t, 0, 0, 0,
339 /* coverity[copy_paste_error] */
340 icaltimezone_get_utc_offset((icaltimezone *)t.zone, &t, &t.is_daylight) -
341 icaltimezone_get_utc_offset((icaltimezone *)t.zone, &t_days, &t_days.is_daylight));
342
343 return t;
344}
345
347{
348 if ((!t1.is_date && t2.is_date) ||
349 (t1.is_date && !t2.is_date)) {
350 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
351 return icaldurationtype_bad_duration();
352 }
353
354 struct icaldurationtype ret;
355 if (t1.is_date) {
356 icaltime_t t1t = icaltime_as_timet(t1);
357 icaltime_t t2t = icaltime_as_timet(t2);
358 ret = icaldurationtype_null_duration();
359 ret.days = ((unsigned int)(t1t - t2t)) / (60 * 60 * 24);
360 } else {
361 icaltime_t t1t = icaltime_as_timet_with_zone(t1, t1.zone);
362 icaltime_t t2t = icaltime_as_timet_with_zone(t2, t2.zone);
363
364 ret = icaldurationtype_from_seconds((int)(t1t - t2t));
365 }
366 return ret;
367}
368
370{
372 newdur.is_neg = dur.is_neg;
373
374 // Normalize nominal duration.
375 if (dur.days % 7 == 0 && !dur.hours && !dur.minutes && !dur.seconds) {
376 newdur.weeks = dur.weeks + dur.days / 7;
377 } else {
378 newdur.days = dur.days + dur.weeks * 7;
379 }
380
381 // Normalize fixed duration.
382 unsigned ut = dur.seconds + dur.minutes * 60 + dur.hours * 60 * 60;
383 unsigned used = 0;
384 newdur.hours = (ut - used) / (60 * 60);
385 used += newdur.hours * (60 * 60);
386 newdur.minutes = (ut - used) / (60);
387 used += newdur.minutes * (60);
388 newdur.seconds = (ut - used);
389
390 return newdur;
391}
struct icaldurationtype icaldurationtype_bad_duration(void)
Creates a bad duration (used to indicate error).
struct icaldurationtype icalduration_from_times(struct icaltimetype t1, struct icaltimetype t2)
Creates a duration from two icaltimetype endpoints.
struct icaltimetype icalduration_extend(struct icaltimetype t, struct icaldurationtype d)
Extends a time duration.
bool icaldurationtype_is_bad_duration(struct icaldurationtype d)
Checks if a duration is a bad duration.
struct icaldurationtype icaldurationtype_from_seconds(int seconds)
Creates a new icaldurationtype from a duration in seconds.
int icaldurationtype_as_seconds(struct icaldurationtype dur)
Extracts the duration in integer seconds from an icaldurationtype.
int icaldurationtype_as_utc_seconds(struct icaldurationtype dur)
Extracts the duration in integer seconds from an icaldurationtype in UTC time.
char * icaldurationtype_as_ical_string_r(struct icaldurationtype d)
struct icaldurationtype icaldurationtype_from_string(const char *str)
Creates a new icaldurationtype from a duration given as a string.
bool icaldurationtype_is_null_duration(struct icaldurationtype d)
Checks if a duration is a null duration.
struct icaldurationtype icaldurationtype_null_duration(void)
Creates a duration with zero length.
char * icaldurationtype_as_ical_string(struct icaldurationtype d)
struct icaldurationtype icaldurationtype_normalize(struct icaldurationtype dur)
Create a normalized duration from another duration.
Defines the data structure for time durations.
struct icaldurationtype icaldurationtype_null_duration(void)
Creates a duration with zero length.
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition icalerror.c:90
Error handling for libical.
@ ICAL_MALFORMEDDATA_ERROR
Definition icalerror.h:56
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition icalmemory.c:363
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition icalmemory.c:313
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition icalmemory.c:404
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition icalmemory.c:156
Common memory management routines.
struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
Definition icaltime.c:366
Defines the data structure for representing date-times.
void icaltime_adjust(struct icaltimetype *tt, const int days, const int hours, const int minutes, const int seconds)
Definition icaltime.c:764
int icaltimezone_get_utc_offset(icaltimezone *zone, const struct icaltimetype *tt, int *is_daylight)
Timezone handling routines.
struct _icaltimezone icaltimezone
unsigned int weeks
unsigned int hours
unsigned int seconds
unsigned int days
unsigned int minutes
int is_daylight
Definition icaltime.h:100