$darkmode
Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
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 
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;
36  seconds = -seconds;
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 
165 error:
168 }
169 
170 static 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 */
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)) {
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);
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 }
int icaldurationtype_as_seconds(struct icaldurationtype dur)
Extracts the duration in integer seconds from an icaldurationtype.
Definition: icalduration.c:238
Defines the data structure for time durations.
struct icaldurationtype icaldurationtype_bad_duration(void)
Creates a bad duration (used to indicate error).
Definition: icalduration.c:283
int icaldurationtype_as_utc_seconds(struct icaldurationtype dur)
Extracts the duration in integer seconds from an icaldurationtype in UTC time.
Definition: icalduration.c:251
Common memory management routines.
Timezone handling routines.
struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
Definition: icaltime.c:366
unsigned int seconds
Definition: icalduration.h:36
int second
Definition: icaltime.h:96
unsigned int days
Definition: icalduration.h:32
Defines the data structure for representing date-times.
icaltime_t icaltime_as_timet_with_zone(const struct icaltimetype tt, const icaltimezone *zone)
Definition: icaltime.c:292
struct icaldurationtype icaldurationtype_from_seconds(int seconds)
Creates a new icaldurationtype from a duration in seconds.
Definition: icalduration.c:28
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition: icalerror.c:90
void icaltime_adjust(struct icaltimetype *tt, const int days, const int hours, const int minutes, const int seconds)
Definition: icaltime.c:755
int icaltimezone_get_utc_offset(icaltimezone *zone, const struct icaltimetype *tt, int *is_daylight)
Definition: icaltimezone.c:887
Error handling for libical.
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition: icalmemory.c:406
char * icaldurationtype_as_ical_string(struct icaldurationtype d)
Definition: icalduration.c:181
bool icaldurationtype_is_null_duration(struct icaldurationtype d)
Checks if a duration is a null duration.
Definition: icalduration.c:270
unsigned int minutes
Definition: icalduration.h:35
struct icaltimetype icalduration_extend(struct icaltimetype t, struct icaldurationtype d)
Extends a time duration.
Definition: icalduration.c:297
struct icaltimetype icaltime_null_time(void)
Definition: icaltime.c:579
char * icaldurationtype_as_ical_string_r(struct icaldurationtype d)
Definition: icalduration.c:190
struct icaldurationtype icaldurationtype_normalize(struct icaldurationtype dur)
Create a normalized duration from another duration.
Definition: icalduration.c:369
struct _icaltimezone icaltimezone
Definition: icaltimezone.h:29
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition: icalmemory.c:315
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
unsigned int hours
Definition: icalduration.h:34
struct icaldurationtype icaldurationtype_null_duration(void)
Creates a duration with zero length.
Definition: icalduration.c:261
struct icaldurationtype icalduration_from_times(struct icaltimetype t1, struct icaltimetype t2)
Creates a duration from two icaltimetype endpoints.
Definition: icalduration.c:346
icaltime_t icaltime_as_timet(const struct icaltimetype tt)
Definition: icaltime.c:259
unsigned int weeks
Definition: icalduration.h:33
struct icaldurationtype icaldurationtype_from_string(const char *str)
Creates a new icaldurationtype from a duration given as a string.
Definition: icalduration.c:43
bool icaldurationtype_is_bad_duration(struct icaldurationtype d)
Checks if a duration is a bad duration.
Definition: icalduration.c:292
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition: icalmemory.c:158
int is_daylight
Definition: icaltime.h:100