Libical API Documentation 4.0 STABLE VERSION [Visit the v3.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 //NOLINTBEGIN(bugprone-unchecked-string-to-number-conversion)
108 /* Get all of the digits, not one at a time */
109 scan_size = sscanf(&str[i], "%10d", &digits); /*limit to 10digits.
110 increase as needed */
111 //NOLINTEND(bugprone-unchecked-string-to-number-conversion)
112
113 if (scan_size != 1) {
114 goto error;
115 }
116 break;
117 }
118
119 case 'H': {
120 if (time_flag == 0 || d.hours != 0 || digits == -1) {
121 goto error;
122 }
123 d.hours = (unsigned int)digits;
124 digits = -1;
125 break;
126 }
127 case 'M': {
128 if (time_flag == 0 || d.minutes != 0 || digits == -1) {
129 goto error;
130 }
131 d.minutes = (unsigned int)digits;
132 digits = -1;
133 break;
134 }
135 case 'S': {
136 if (time_flag == 0 || d.seconds != 0 || digits == -1) {
137 goto error;
138 }
139 d.seconds = (unsigned int)digits;
140 digits = -1;
141 break;
142 }
143 case 'W': {
144 if (time_flag == 1 || date_flag == 1 || d.weeks != 0 || digits == -1) {
145 goto error;
146 }
147 d.weeks = (unsigned int)digits;
148 digits = -1;
149 break;
150 }
151 case 'D': {
152 if (time_flag == 1 || d.days != 0 || digits == -1) {
153 goto error;
154 }
155 date_flag = 1;
156 d.days = (unsigned int)digits;
157 digits = -1;
158 break;
159 }
160 default: {
161 goto error;
162 }
163 }
164 }
165
166 return d;
167
168error:
171}
172
173static void append_duration_segment(char **buf, char **buf_ptr, size_t *buf_size,
174 const char *sep, unsigned int value)
175{
176 char temp[1024];
177
178 snprintf(temp, sizeof(temp), "%u", value);
179
180 icalmemory_append_string(buf, buf_ptr, buf_size, temp);
181 icalmemory_append_string(buf, buf_ptr, buf_size, sep);
182}
183
185{
186 char *buf;
187
190 return buf;
191}
192
194{
195 char *buf;
196 size_t buf_size = 256;
197 char *buf_ptr = 0;
198
199 buf = (char *)icalmemory_new_buffer(buf_size);
200 buf_ptr = buf;
201
202 if (d.weeks == 0 &&
203 d.days == 0 &&
204 d.hours == 0 &&
205 d.minutes == 0 &&
206 d.seconds == 0) {
207 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "PT0S");
208 } else {
209 if (d.is_neg == 1) {
210 icalmemory_append_char(&buf, &buf_ptr, &buf_size, '-');
211 }
212
213 icalmemory_append_char(&buf, &buf_ptr, &buf_size, 'P');
214
215 if (d.weeks != 0) {
216 append_duration_segment(&buf, &buf_ptr, &buf_size, "W", d.weeks);
217 }
218
219 if (d.days != 0) {
220 append_duration_segment(&buf, &buf_ptr, &buf_size, "D", d.days);
221 }
222
223 if (d.hours != 0 || d.minutes != 0 || d.seconds != 0) {
224 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "T");
225
226 if (d.hours != 0) {
227 append_duration_segment(&buf, &buf_ptr, &buf_size, "H", d.hours);
228 }
229 if (d.minutes != 0) {
230 append_duration_segment(&buf, &buf_ptr, &buf_size, "M", d.minutes);
231 }
232 if (d.seconds != 0) {
233 append_duration_segment(&buf, &buf_ptr, &buf_size, "S", d.seconds);
234 }
235 }
236 }
237
238 return buf;
239}
240
242{
243 if (dur.days != 0 || dur.weeks != 0) {
244 /* The length of a day is position-dependent */
246 return 0;
247 }
248 return (int)(((int)dur.seconds +
249 60 * ((int)dur.minutes +
250 60 * ((int)dur.hours))) *
251 (dur.is_neg == 1 ? -1 : 1));
252}
253
255{
256 return (int)(((int)dur.seconds +
257 60 * ((int)dur.minutes +
258 60 * ((int)dur.hours +
259 24 * ((int)dur.days +
260 7 * (int)dur.weeks)))) *
261 (dur.is_neg == 1 ? -1 : 1));
262}
263
265{
266 struct icaldurationtype d;
267
268 memset(&d, 0, sizeof(struct icaldurationtype));
269
270 return d;
271}
272
274{
276 return memcmp(&d, &n, sizeof(struct icaldurationtype)) == 0;
277}
278
279/* In icalvalue_new_from_string_with_error, we should not call
280 icaldurationtype_is_null_duration() to see if there is an error
281 condition. Null duration is perfectly valid for an alarm.
282 We cannot depend on the caller to check icalerrno either,
283 following the philosophy of unix errno. we set the is_neg
284 to -1 to indicate that this is a bad duration.
285*/
287{
288 struct icaldurationtype d;
289
290 memset(&d, 0, sizeof(struct icaldurationtype));
291 d.is_neg = -1;
292 return d;
293}
294
296{
297 return (d.is_neg == -1);
298}
299
301{
302 struct icaltimetype t_days;
303 if (t.is_date &&
304 (d.seconds != 0 ||
305 d.minutes != 0 ||
306 d.hours != 0)) {
307 /* We can't add time durations to dates */
308 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
309 return icaltime_null_time();
310 }
311
312 /* Days are nominal (not exact) and position-dependent */
313 if (!d.is_neg) {
314 t.day += (int)d.days;
315 t.day += (int)(d.weeks * 7);
316 } else {
317 t.day -= (int)d.days;
318 t.day -= (int)(d.weeks * 7);
319 }
320
321 t = icaltime_normalize(t);
322 t_days = t;
323
324 if (!d.is_neg) {
325 t.second += (int)d.seconds;
326 t.minute += (int)d.minutes;
327 t.hour += (int)d.hours;
328 } else {
329 t.second -= (int)d.seconds;
330 t.minute -= (int)d.minutes;
331 t.hour -= (int)d.hours;
332 }
333
334 t = icaltime_normalize(t);
335 /* RFC 5545 states:
336 * In the case of discontinuities in the time scale, such
337 * as the change from standard time to daylight time and back, the
338 * computation of the exact duration requires the subtraction or
339 * addition of the change of duration of the discontinuity.
340 */
341 icaltime_adjust(&t, 0, 0, 0,
342 /* coverity[copy_paste_error] */
343 icaltimezone_get_utc_offset((icaltimezone *)t.zone, &t, &t.is_daylight) -
344 icaltimezone_get_utc_offset((icaltimezone *)t.zone, &t_days, &t_days.is_daylight));
345
346 return t;
347}
348
350{
351 if ((!t1.is_date && t2.is_date) ||
352 (t1.is_date && !t2.is_date)) {
353 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
354 return icaldurationtype_bad_duration();
355 }
356
357 struct icaldurationtype ret;
358 if (t1.is_date) {
359 icaltime_t t1t = icaltime_as_timet(t1);
360 icaltime_t t2t = icaltime_as_timet(t2);
361 ret = icaldurationtype_null_duration();
362 ret.days = ((unsigned int)(t1t - t2t)) / (60 * 60 * 24);
363 } else {
364 icaltime_t t1t = icaltime_as_timet_with_zone(t1, t1.zone);
365 icaltime_t t2t = icaltime_as_timet_with_zone(t2, t2.zone);
366
367 ret = icaldurationtype_from_seconds((int)(t1t - t2t));
368 }
369 return ret;
370}
371
373{
375 newdur.is_neg = dur.is_neg;
376
377 // Normalize nominal duration.
378 if (dur.days % 7 == 0 && !dur.hours && !dur.minutes && !dur.seconds) {
379 newdur.weeks = dur.weeks + dur.days / 7;
380 } else {
381 newdur.days = dur.days + dur.weeks * 7;
382 }
383
384 // Normalize fixed duration.
385 unsigned ut = dur.seconds + dur.minutes * 60 + dur.hours * 60 * 60;
386 unsigned used = 0;
387 newdur.hours = (ut - used) / (60 * 60);
388 used += newdur.hours * (60 * 60);
389 newdur.minutes = (ut - used) / 60;
390 used += newdur.minutes * 60;
391 newdur.seconds = (ut - used);
392
393 return newdur;
394}
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.
const char * str
Definition icalenums.c:29
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:59
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition icalmemory.c:365
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition icalmemory.c:315
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition icalmemory.c:406
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition icalmemory.c:158
Common memory management routines.
struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
Definition icaltime.c:371
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:769
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