Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/date/lib/duration.c
Line
Count
Source
1
/*
2
 * The MIT License (MIT)
3
 *
4
 * Copyright (c) 2015-2026 Derick Rethans
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
25
#include "timelib.h"
26
#include "timelib_private.h"
27
28
int timelib_duration_ctor_static(
29
  timelib_duration *duration,
30
  uint64_t          seconds,
31
  uint32_t          nanoseconds,
32
  bool              negative
33
7
) {
34
7
  if (seconds > MAX_DURATION_SECONDS) {
35
0
    return TIMELIB_ERROR_SECONDS_OUT_OF_RANGE;
36
0
  }
37
7
  if (nanoseconds >= NSECS_PER_SEC) {
38
0
    return TIMELIB_ERROR_NANOSECONDS_OUT_OF_RANGE;
39
0
  }
40
41
7
  duration->seconds = seconds;
42
7
  duration->nanoseconds = nanoseconds;
43
44
7
  if (seconds != 0 || nanoseconds != 0) {
45
0
    duration->negative = negative;
46
7
  } else {
47
7
    duration->negative = false;
48
7
  }
49
50
7
  return TIMELIB_ERROR_NO_ERROR;
51
7
}
52
53
timelib_duration *timelib_duration_ctor(
54
  uint64_t               seconds,
55
  uint32_t               nanoseconds,
56
  bool                   negative,
57
  int                   *error_code
58
0
) {
59
0
  timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
60
61
0
  *error_code = timelib_duration_ctor_static(tmp, seconds, nanoseconds, negative);
62
63
0
  if (*error_code != TIMELIB_ERROR_NO_ERROR) {
64
0
    free(tmp);
65
0
    return NULL;
66
0
  }
67
68
0
  return tmp;
69
0
}
70
71
timelib_duration *timelib_duration_create_from_iso8601string(
72
  const char *string,
73
  int        *error_code
74
0
) {
75
0
  timelib_time            *b = NULL, *e = NULL;
76
0
  timelib_rel_time        *p = NULL;
77
0
  int                      r = -1;
78
0
  timelib_error_container *errors;
79
0
  timelib_duration        *new_duration = NULL;
80
81
0
  timelib_strtointerval(string, strlen(string), &b, &e, &p, &r, &errors);
82
83
0
  if (errors->error_count > 0 || errors->warning_count > 0) {
84
0
    *error_code = TIMELIB_ERROR_ISO8601_DURATION_PARSE_FAILURE;
85
0
    goto free_elements;
86
0
  }
87
88
0
  if (!p) {
89
0
    *error_code = TIMELIB_ERROR_DURATION_MISSING_PERIOD;
90
0
    goto free_elements;
91
0
  }
92
93
0
  if (b != NULL || e != NULL || r != -1) {
94
0
    *error_code = TIMELIB_ERROR_DURATION_ONLY_PERIOD_ALLOWED;
95
0
    goto free_elements;
96
0
  }
97
98
0
  if (p->y > 0 || p->m >0 || p->d > 0) {
99
0
    *error_code = TIMELIB_ERROR_DURATION_DAYS_FOUND;
100
0
    goto free_elements;
101
0
  }
102
103
0
  new_duration = calloc(1, sizeof(timelib_duration));
104
105
0
  *error_code = timelib_duration_ctor_static(new_duration, p->h * 3600 + p->i * 60 + p->s, 0, false);
106
107
0
  if (*error_code != TIMELIB_ERROR_NO_ERROR) {
108
0
    free(new_duration);
109
0
    new_duration = NULL;
110
0
  }
111
112
0
free_elements:
113
0
  if (b) {
114
0
    timelib_time_dtor(b);
115
0
  }
116
0
  if (e) {
117
0
    timelib_time_dtor(e);
118
0
  }
119
0
  if (p) {
120
0
    timelib_rel_time_dtor(p);
121
0
  }
122
0
  timelib_error_container_dtor(errors);
123
124
0
  return new_duration;
125
0
}
126
127
void timelib_duration_dtor(timelib_duration *duration)
128
0
{
129
0
  free(duration);
130
0
}
131
132
static int timelib_duration_add_abs_internal(
133
  timelib_duration       *new_duration,
134
  const timelib_duration *original,
135
  const timelib_duration *additional
136
0
) {
137
0
  uint64_t seconds = original->seconds + additional->seconds;
138
0
  uint32_t nanoseconds = original->nanoseconds + additional->nanoseconds;
139
140
0
  if (nanoseconds >= NSECS_PER_SEC) {
141
0
    seconds++;
142
0
    nanoseconds -= NSECS_PER_SEC;
143
0
  }
144
145
0
  return timelib_duration_ctor_static(new_duration, seconds, nanoseconds, original->negative);
146
0
}
147
148
149
static int timelib_duration_sub_abs_internal(
150
  timelib_duration       *new_duration,
151
  const timelib_duration *original,
152
  const timelib_duration *minus
153
0
) {
154
0
  uint64_t seconds = original->seconds - minus->seconds;
155
0
  int32_t nanoseconds = (int32_t)original->nanoseconds - (int32_t)minus->nanoseconds;
156
157
0
  if (nanoseconds < 0) {
158
0
    seconds--;
159
0
    nanoseconds += NSECS_PER_SEC;
160
0
  }
161
162
0
  return timelib_duration_ctor_static(new_duration, seconds, nanoseconds, original->negative);
163
0
}
164
165
static int timelib_duration_null_abs_internal(timelib_duration *new_duration)
166
0
{
167
0
  new_duration->negative = false;
168
0
  new_duration->seconds = 0;
169
0
  new_duration->nanoseconds = 0;
170
171
0
  return TIMELIB_ERROR_NO_ERROR;
172
0
}
173
174
175
int timelib_duration_add_static(
176
  timelib_duration       *new_duration,
177
  const timelib_duration *original,
178
  const timelib_duration *additional
179
0
) {
180
0
  int c = 0;
181
182
  /* ++ / -- */
183
0
  if (original->negative == additional->negative) {
184
0
    return timelib_duration_add_abs_internal(new_duration, original, additional);
185
0
  }
186
187
  /* +- / -+ */
188
0
  c = timelib_duration_abs_compare(original, additional);
189
190
0
  switch (c)
191
0
  {
192
0
    case -1:
193
      /* additional has the larger value */
194
0
      return timelib_duration_sub_abs_internal(new_duration, additional, original);
195
196
0
    case 0:
197
0
      return timelib_duration_null_abs_internal(new_duration);
198
199
0
    case 1:
200
      /* original has the larger value */
201
0
      return timelib_duration_sub_abs_internal(new_duration, original, additional);
202
0
  }
203
204
  /* Should not be reachable due to semantics of timelib_duration_abs_compare() */
205
0
  return TIMELIB_ERROR_NO_ERROR;
206
0
}
207
208
timelib_duration *timelib_duration_add(
209
  const timelib_duration *original,
210
  const timelib_duration *additional,
211
  int                    *error_code
212
0
) {
213
0
  timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
214
215
0
  *error_code = timelib_duration_add_static(tmp, original, additional);
216
217
0
  if (*error_code != TIMELIB_ERROR_NO_ERROR) {
218
0
    free(tmp);
219
0
    return NULL;
220
0
  }
221
222
0
  return tmp;
223
0
}
224
225
226
int timelib_duration_sub_static(
227
  timelib_duration       *new_duration,
228
  const timelib_duration *original,
229
  const timelib_duration *minus
230
0
) {
231
0
  int c = 0;
232
233
  /* +- / -+ */
234
0
  if (original->negative != minus->negative) {
235
0
    return timelib_duration_add_abs_internal(new_duration, original, minus);
236
0
  }
237
238
  /* ++ / -- */
239
0
  c = timelib_duration_abs_compare(original, minus);
240
241
0
  switch (c)
242
0
  {
243
0
    case -1: {
244
      /* minus has the larger value */
245
0
      timelib_duration tmp_duration;
246
247
0
      int result = timelib_duration_sub_abs_internal(&tmp_duration, minus, original);
248
0
      if (result != TIMELIB_ERROR_NO_ERROR) {
249
0
        return result;
250
0
      }
251
252
0
      return timelib_duration_negate_static(new_duration, &tmp_duration);
253
0
    }
254
255
0
    case 0:
256
0
      return timelib_duration_null_abs_internal(new_duration);
257
258
0
    case 1:
259
      /* original has the larger value */
260
0
      return timelib_duration_sub_abs_internal(new_duration, original, minus);
261
0
  }
262
263
  /* Should not be reachable due to semantics of comparisons above */
264
0
  return TIMELIB_ERROR_NO_ERROR;
265
0
}
266
267
timelib_duration *timelib_duration_sub(
268
  const timelib_duration *original,
269
  const timelib_duration *minus,
270
  int                    *error_code
271
0
) {
272
0
  timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
273
274
0
  *error_code = timelib_duration_sub_static(tmp, original, minus);
275
276
0
  if (*error_code != TIMELIB_ERROR_NO_ERROR) {
277
0
    free(tmp);
278
0
    return NULL;
279
0
  }
280
281
0
  return tmp;
282
0
}
283
284
int timelib_duration_mul_static(
285
  timelib_duration       *new_duration,
286
  const timelib_duration *original,
287
  uint64_t                factor
288
0
) {
289
0
  uint64_t seconds, extra_seconds, nanoseconds;
290
291
0
  if (factor == 0) {
292
0
    return timelib_duration_null_abs_internal(new_duration);
293
0
  }
294
295
0
  if (original->seconds > UINT64_MAX / factor) {
296
0
    return TIMELIB_ERROR_OVERFLOW;
297
0
  }
298
299
0
  seconds = original->seconds * factor;
300
301
  /* Calculate the number of whole seconds in the nanoseconds product.
302
   *
303
   * extra_seconds is guaranteed to be smaller than factor, because
304
   * original->nanoseconds is smaller than NSECS_PER_SEC. */
305
0
  extra_seconds = original->nanoseconds * (factor / NSECS_PER_SEC);
306
307
  /* This cannot overflow either, because NSECS_PER_SEC * NSECS_PER_SEC fits uint64_t.
308
   *
309
   * (nanoseconds * factor) % NSECS_PER_SEC is mathematically equivalent
310
   * to (nanoseconds * (factor % NSECS_PER_SEC)) % NSECS_PER_SEC. */
311
0
  nanoseconds = original->nanoseconds * (factor % NSECS_PER_SEC);
312
0
  extra_seconds += nanoseconds / NSECS_PER_SEC;
313
0
  nanoseconds %= NSECS_PER_SEC;
314
315
0
  if (UINT64_MAX - extra_seconds < seconds) {
316
0
    return TIMELIB_ERROR_OVERFLOW;
317
0
  }
318
319
0
  seconds += extra_seconds;
320
321
0
  return timelib_duration_ctor_static(new_duration, seconds, nanoseconds, original->negative);
322
0
}
323
324
timelib_duration *timelib_duration_mul(
325
  const timelib_duration *original,
326
  uint64_t                factor,
327
  int                    *error_code
328
0
) {
329
0
  timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
330
331
0
  *error_code = timelib_duration_mul_static(tmp, original, factor);
332
333
0
  if (*error_code != TIMELIB_ERROR_NO_ERROR) {
334
0
    free(tmp);
335
0
    return NULL;
336
0
  }
337
338
0
  return tmp;
339
0
}
340
341
int timelib_duration_div_static(
342
  timelib_duration       *new_duration,
343
  const timelib_duration *original,
344
  uint64_t                divisor
345
0
) {
346
0
  uint64_t seconds;
347
0
  uint64_t nanoseconds;
348
349
0
  if (divisor < 1) {
350
0
    return TIMELIB_ERROR_DIVISION_BY_ZERO;
351
0
  }
352
353
0
  seconds = original->seconds / divisor;
354
0
  nanoseconds = original->nanoseconds + ((original->seconds % divisor) * NSECS_PER_SEC);
355
0
  nanoseconds /= divisor;
356
357
0
  return timelib_duration_ctor_static(new_duration, seconds, nanoseconds, original->negative);
358
0
}
359
360
timelib_duration *timelib_duration_div(
361
  const timelib_duration *original,
362
  uint64_t                divisor,
363
  int                    *error_code
364
0
) {
365
0
  timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
366
367
0
  *error_code = timelib_duration_div_static(tmp, original, divisor);
368
369
0
  if (*error_code != TIMELIB_ERROR_NO_ERROR) {
370
0
    free(tmp);
371
0
    return NULL;
372
0
  }
373
374
0
  return tmp;
375
0
}
376
377
int timelib_duration_negate_static(timelib_duration *new_duration, const timelib_duration *original)
378
0
{
379
0
  return timelib_duration_ctor_static(new_duration, original->seconds, original->nanoseconds, !original->negative);
380
0
}
381
382
timelib_duration *timelib_duration_negate(const timelib_duration *original, int *error_code)
383
0
{
384
0
  timelib_duration *tmp = calloc(1, sizeof(timelib_duration));
385
386
0
  *error_code = timelib_duration_negate_static(tmp, original);
387
388
0
  if (*error_code != TIMELIB_ERROR_NO_ERROR) {
389
0
    free(tmp);
390
0
    return NULL;
391
0
  }
392
393
0
  return tmp;
394
0
}
395
396
int timelib_duration_abs_compare(const timelib_duration *one, const timelib_duration *two)
397
0
{
398
0
  if (one->seconds < two->seconds) {
399
0
    return -1;
400
0
  }
401
0
  if (one->seconds > two->seconds) {
402
0
    return 1;
403
0
  }
404
405
0
  if (one->nanoseconds < two->nanoseconds) {
406
0
    return -1;
407
0
  }
408
0
  if (one->nanoseconds > two->nanoseconds) {
409
0
    return 1;
410
0
  }
411
412
0
  return 0;
413
0
}
414
415
int timelib_duration_compare(const timelib_duration *one, const timelib_duration *two)
416
0
{
417
0
  if (one->negative && !two->negative) {
418
0
    return -1;
419
0
  }
420
0
  if (!one->negative && two->negative) {
421
0
    return 1;
422
0
  }
423
424
0
  if (one->negative && two->negative) {
425
0
    return timelib_duration_abs_compare(two, one);
426
0
  }
427
428
0
  return timelib_duration_abs_compare(one, two);
429
0
}