Coverage Report

Created: 2026-01-17 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/isc/time.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
/*! \file */
15
16
#include <errno.h>
17
#include <inttypes.h>
18
#include <limits.h>
19
#include <stdbool.h>
20
#include <stdlib.h>
21
#include <sys/time.h> /* Required for struct timeval on some platforms. */
22
#include <syslog.h>
23
#include <time.h>
24
25
#include <isc/log.h>
26
#include <isc/overflow.h>
27
#include <isc/strerr.h>
28
#include <isc/string.h>
29
#include <isc/time.h>
30
#include <isc/tm.h>
31
#include <isc/util.h>
32
33
#if defined(CLOCK_REALTIME)
34
0
#define CLOCKSOURCE_HIRES CLOCK_REALTIME
35
#endif /* #if defined(CLOCK_REALTIME) */
36
37
#if defined(CLOCK_REALTIME_COARSE)
38
0
#define CLOCKSOURCE CLOCK_REALTIME_COARSE
39
#elif defined(CLOCK_REALTIME_FAST)
40
#define CLOCKSOURCE CLOCK_REALTIME_FAST
41
#else /* if defined(CLOCK_REALTIME_COARSE) */
42
#define CLOCKSOURCE CLOCK_REALTIME
43
#endif /* if defined(CLOCK_REALTIME_COARSE) */
44
45
#if !defined(CLOCKSOURCE_HIRES)
46
#define CLOCKSOURCE_HIRES CLOCKSOURCE
47
#endif /* #ifndef CLOCKSOURCE_HIRES */
48
49
#if !defined(UNIT_TESTING)
50
static const isc_time_t epoch = { 0, 0 };
51
const isc_time_t *const isc_time_epoch = &epoch;
52
#endif
53
54
void
55
0
isc_time_set(isc_time_t *t, unsigned int seconds, unsigned int nanoseconds) {
56
0
  REQUIRE(t != NULL);
57
0
  REQUIRE(nanoseconds < NS_PER_SEC);
58
59
0
  t->seconds = seconds;
60
0
  t->nanoseconds = nanoseconds;
61
0
}
62
63
void
64
0
isc_time_settoepoch(isc_time_t *t) {
65
0
  REQUIRE(t != NULL);
66
67
0
  t->seconds = 0;
68
0
  t->nanoseconds = 0;
69
0
}
70
71
bool
72
0
isc_time_isepoch(const isc_time_t *t) {
73
0
  REQUIRE(t != NULL);
74
0
  INSIST(t->nanoseconds < NS_PER_SEC);
75
76
0
  if (t->seconds == 0 && t->nanoseconds == 0) {
77
0
    return true;
78
0
  }
79
80
0
  return false;
81
0
}
82
83
static isc_time_t
84
0
time_now(clockid_t clock) {
85
0
  isc_time_t t;
86
0
  struct timespec ts;
87
88
0
  RUNTIME_CHECK(clock_gettime(clock, &ts) == 0);
89
0
  INSIST(ts.tv_sec >= 0 && ts.tv_nsec >= 0 &&
90
0
         ts.tv_nsec < (long)NS_PER_SEC);
91
92
  /*
93
   * Ensure the tv_sec value fits in t->seconds.
94
   */
95
0
  INSIST(sizeof(ts.tv_sec) <= sizeof(t.seconds) ||
96
0
         ((ts.tv_sec | (unsigned int)-1) ^ (unsigned int)-1) == 0U);
97
98
0
  t.seconds = ts.tv_sec;
99
0
  t.nanoseconds = ts.tv_nsec;
100
101
0
  return t;
102
0
}
103
104
isc_time_t
105
0
isc_time_now_hires(void) {
106
0
  return time_now(CLOCKSOURCE_HIRES);
107
0
}
108
109
isc_time_t
110
0
isc_time_now(void) {
111
0
  return time_now(CLOCKSOURCE);
112
0
}
113
114
isc_nanosecs_t
115
0
isc_time_monotonic(void) {
116
0
  struct timespec ts;
117
118
0
  RUNTIME_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) != -1);
119
120
0
  isc_time_t time = {
121
0
    .seconds = ts.tv_sec,
122
0
    .nanoseconds = ts.tv_nsec,
123
0
  };
124
125
0
  return isc_nanosecs_fromtime(time);
126
0
}
127
128
isc_result_t
129
0
isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i) {
130
0
  struct timespec ts;
131
132
0
  REQUIRE(t != NULL);
133
0
  REQUIRE(i != NULL);
134
0
  INSIST(i->nanoseconds < NS_PER_SEC);
135
136
0
  if (clock_gettime(CLOCKSOURCE, &ts) == -1) {
137
0
    UNEXPECTED_SYSERROR(errno, "clock_gettime()");
138
0
    return ISC_R_UNEXPECTED;
139
0
  }
140
141
0
  if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= (long)NS_PER_SEC) {
142
0
    return ISC_R_UNEXPECTED;
143
0
  }
144
145
  /*
146
   * Ensure the resulting seconds value fits in the size of an
147
   * unsigned int.  (It is written this way as a slight optimization;
148
   * note that even if both values == INT_MAX, then when added
149
   * and getting another 1 added below the result is UINT_MAX.)
150
   */
151
0
  if ((ts.tv_sec > INT_MAX || i->seconds > INT_MAX) &&
152
0
      ((long long)ts.tv_sec + i->seconds > UINT_MAX))
153
0
  {
154
0
    return ISC_R_RANGE;
155
0
  }
156
157
0
  t->seconds = ts.tv_sec + i->seconds;
158
0
  t->nanoseconds = ts.tv_nsec + i->nanoseconds;
159
0
  if (t->nanoseconds >= NS_PER_SEC) {
160
0
    t->seconds++;
161
0
    t->nanoseconds -= NS_PER_SEC;
162
0
  }
163
164
0
  return ISC_R_SUCCESS;
165
0
}
166
167
int
168
0
isc_time_compare(const isc_time_t *t1, const isc_time_t *t2) {
169
0
  REQUIRE(t1 != NULL && t2 != NULL);
170
0
  INSIST(t1->nanoseconds < NS_PER_SEC && t2->nanoseconds < NS_PER_SEC);
171
172
0
  if (t1->seconds < t2->seconds) {
173
0
    return -1;
174
0
  }
175
0
  if (t1->seconds > t2->seconds) {
176
0
    return 1;
177
0
  }
178
0
  if (t1->nanoseconds < t2->nanoseconds) {
179
0
    return -1;
180
0
  }
181
0
  if (t1->nanoseconds > t2->nanoseconds) {
182
0
    return 1;
183
0
  }
184
0
  return 0;
185
0
}
186
187
isc_result_t
188
0
isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result) {
189
0
  REQUIRE(t != NULL && i != NULL && result != NULL);
190
0
  REQUIRE(t->nanoseconds < NS_PER_SEC && i->nanoseconds < NS_PER_SEC);
191
192
  /* Seconds */
193
0
  if (ckd_add(&result->seconds, t->seconds, i->seconds)) {
194
0
    return ISC_R_RANGE;
195
0
  }
196
197
  /* Nanoseconds */
198
0
  result->nanoseconds = t->nanoseconds + i->nanoseconds;
199
0
  if (result->nanoseconds >= NS_PER_SEC) {
200
0
    if (result->seconds == UINT_MAX) {
201
0
      return ISC_R_RANGE;
202
0
    }
203
0
    result->nanoseconds -= NS_PER_SEC;
204
0
    result->seconds++;
205
0
  }
206
207
0
  return ISC_R_SUCCESS;
208
0
}
209
210
isc_result_t
211
isc_time_subtract(const isc_time_t *t, const isc_interval_t *i,
212
0
      isc_time_t *result) {
213
0
  REQUIRE(t != NULL && i != NULL && result != NULL);
214
0
  REQUIRE(t->nanoseconds < NS_PER_SEC && i->nanoseconds < NS_PER_SEC);
215
216
  /* Seconds */
217
0
  if (ckd_sub(&result->seconds, t->seconds, i->seconds)) {
218
0
    return ISC_R_RANGE;
219
0
  }
220
221
  /* Nanoseconds */
222
0
  if (t->nanoseconds >= i->nanoseconds) {
223
0
    result->nanoseconds = t->nanoseconds - i->nanoseconds;
224
0
  } else {
225
0
    if (result->seconds == 0) {
226
0
      return ISC_R_RANGE;
227
0
    }
228
0
    result->seconds--;
229
0
    result->nanoseconds = NS_PER_SEC + t->nanoseconds -
230
0
              i->nanoseconds;
231
0
  }
232
233
0
  return ISC_R_SUCCESS;
234
0
}
235
236
uint64_t
237
0
isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2) {
238
0
  uint64_t i1, i2, i3;
239
240
0
  REQUIRE(t1 != NULL && t2 != NULL);
241
0
  INSIST(t1->nanoseconds < NS_PER_SEC && t2->nanoseconds < NS_PER_SEC);
242
243
0
  i1 = (uint64_t)t1->seconds * NS_PER_SEC + t1->nanoseconds;
244
0
  i2 = (uint64_t)t2->seconds * NS_PER_SEC + t2->nanoseconds;
245
246
0
  if (i1 <= i2) {
247
0
    return 0;
248
0
  }
249
250
0
  i3 = i1 - i2;
251
252
  /*
253
   * Convert to microseconds.
254
   */
255
0
  i3 /= NS_PER_US;
256
257
0
  return i3;
258
0
}
259
260
uint32_t
261
0
isc_time_seconds(const isc_time_t *t) {
262
0
  REQUIRE(t != NULL);
263
0
  INSIST(t->nanoseconds < NS_PER_SEC);
264
265
0
  return (uint32_t)t->seconds;
266
0
}
267
268
isc_result_t
269
0
isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp) {
270
0
  time_t seconds;
271
272
0
  REQUIRE(t != NULL);
273
0
  INSIST(t->nanoseconds < NS_PER_SEC);
274
275
  /*
276
   * Ensure that the number of seconds represented by t->seconds
277
   * can be represented by a time_t.  Since t->seconds is an
278
   * unsigned int and since time_t is mostly opaque, this is
279
   * trickier than it seems.  (This standardized opaqueness of
280
   * time_t is *very* frustrating; time_t is not even limited to
281
   * being an integral type.)
282
   *
283
   * The mission, then, is to avoid generating any kind of warning
284
   * about "signed versus unsigned" while trying to determine if
285
   * the unsigned int t->seconds is out range for tv_sec,
286
   * which is pretty much only true if time_t is a signed integer
287
   * of the same size as the return value of isc_time_seconds.
288
   *
289
   * If the paradox in the if clause below is true, t->seconds is
290
   * out of range for time_t.
291
   */
292
0
  seconds = (time_t)t->seconds;
293
294
0
  INSIST(sizeof(unsigned int) == sizeof(uint32_t));
295
0
  INSIST(sizeof(time_t) >= sizeof(uint32_t));
296
297
0
  if (t->seconds > (~0U >> 1) && seconds <= (time_t)(~0U >> 1)) {
298
0
    return ISC_R_RANGE;
299
0
  }
300
301
0
  *secondsp = seconds;
302
303
0
  return ISC_R_SUCCESS;
304
0
}
305
306
uint32_t
307
0
isc_time_nanoseconds(const isc_time_t *t) {
308
0
  REQUIRE(t != NULL);
309
310
0
  ENSURE(t->nanoseconds < NS_PER_SEC);
311
312
0
  return (uint32_t)t->nanoseconds;
313
0
}
314
315
uint32_t
316
0
isc_time_miliseconds(const isc_time_t *t) {
317
0
  REQUIRE(t != NULL);
318
0
  INSIST(t->nanoseconds < NS_PER_SEC);
319
320
0
  return (t->seconds * MS_PER_SEC) + (t->nanoseconds / NS_PER_MS);
321
0
}
322
323
void
324
0
isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len) {
325
0
  time_t now;
326
0
  unsigned int flen;
327
0
  struct tm tm;
328
329
0
  REQUIRE(t != NULL);
330
0
  INSIST(t->nanoseconds < NS_PER_SEC);
331
0
  REQUIRE(buf != NULL);
332
0
  REQUIRE(len > 0);
333
334
0
  now = (time_t)t->seconds;
335
0
  flen = strftime(buf, len, "%d-%b-%Y %X", localtime_r(&now, &tm));
336
0
  INSIST(flen < len);
337
0
  if (flen != 0) {
338
0
    snprintf(buf + flen, len - flen, ".%03u",
339
0
       t->nanoseconds / NS_PER_MS);
340
0
  } else {
341
0
    strlcpy(buf, "99-Bad-9999 99:99:99.999", len);
342
0
  }
343
0
}
344
345
void
346
0
isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len) {
347
0
  time_t now;
348
0
  unsigned int flen;
349
0
  struct tm tm;
350
351
0
  REQUIRE(t != NULL);
352
0
  INSIST(t->nanoseconds < NS_PER_SEC);
353
0
  REQUIRE(buf != NULL);
354
0
  REQUIRE(len > 0);
355
356
  /*
357
   * 5 spaces, 1 comma, 3 GMT, 2 %d, 4 %Y, 8 %H:%M:%S, 3+ %a, 3+
358
   * %b (29+)
359
   */
360
0
  now = (time_t)t->seconds;
361
0
  flen = strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT",
362
0
      gmtime_r(&now, &tm));
363
0
  INSIST(flen < len);
364
0
}
365
366
isc_result_t
367
0
isc_time_parsehttptimestamp(char *buf, isc_time_t *t) {
368
0
  struct tm t_tm;
369
0
  time_t when;
370
0
  char *p;
371
372
0
  REQUIRE(buf != NULL);
373
0
  REQUIRE(t != NULL);
374
375
0
  p = isc_tm_strptime(buf, "%a, %d %b %Y %H:%M:%S", &t_tm);
376
0
  if (p == NULL) {
377
0
    return ISC_R_UNEXPECTED;
378
0
  }
379
0
  when = isc_tm_timegm(&t_tm);
380
0
  if (when == -1) {
381
0
    return ISC_R_UNEXPECTED;
382
0
  }
383
0
  isc_time_set(t, when, 0);
384
0
  return ISC_R_SUCCESS;
385
0
}
386
387
void
388
0
isc_time_formatISO8601Lms(const isc_time_t *t, char *buf, unsigned int len) {
389
0
  time_t now;
390
0
  unsigned int flen;
391
0
  struct tm tm;
392
393
0
  REQUIRE(t != NULL);
394
0
  INSIST(t->nanoseconds < NS_PER_SEC);
395
0
  REQUIRE(buf != NULL);
396
0
  REQUIRE(len > 0);
397
398
0
  now = (time_t)t->seconds;
399
0
  flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%S", localtime_r(&now, &tm));
400
0
  INSIST(flen < len);
401
0
  if (flen > 0U && len - flen >= 6) {
402
0
    snprintf(buf + flen, len - flen, ".%03u",
403
0
       t->nanoseconds / NS_PER_MS);
404
0
  }
405
0
}
406
407
void
408
0
isc_time_formatISO8601TZms(const isc_time_t *t, char *buf, unsigned int len) {
409
0
  char strftime_buf[64] = { 0 };
410
0
  char ms_buf[8] = { 0 };
411
0
  time_t now;
412
0
  unsigned int flen;
413
0
  struct tm tm;
414
415
0
  REQUIRE(t != NULL);
416
0
  INSIST(t->nanoseconds < NS_PER_SEC);
417
0
  REQUIRE(buf != NULL);
418
0
  REQUIRE(len > 0);
419
420
0
  now = (time_t)t->seconds;
421
0
  flen = strftime(strftime_buf, len, "%Y-%m-%dT%H:%M:%S.xxx%z",
422
0
      localtime_r(&now, &tm));
423
0
  snprintf(ms_buf, sizeof(ms_buf), ".%03u", t->nanoseconds / NS_PER_MS);
424
425
0
  INSIST(flen < len);
426
0
  size_t local_date_len = sizeof("yyyy-mm-ddThh:mm:ss") - 1ul;
427
0
  size_t ms_date_len = local_date_len + 4;
428
429
0
  memmove(buf, strftime_buf, local_date_len);
430
0
  memmove(buf + local_date_len, ms_buf, 4);
431
0
  memmove(buf + ms_date_len, strftime_buf + ms_date_len, 3);
432
0
  buf[ms_date_len + 3] = ':';
433
0
  memmove(buf + ms_date_len + 4, strftime_buf + ms_date_len + 3, 3);
434
0
}
435
void
436
0
isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len) {
437
0
  time_t now;
438
0
  unsigned int flen;
439
0
  struct tm tm;
440
441
0
  REQUIRE(t != NULL);
442
0
  INSIST(t->nanoseconds < NS_PER_SEC);
443
0
  REQUIRE(buf != NULL);
444
0
  REQUIRE(len > 0);
445
446
0
  now = (time_t)t->seconds;
447
0
  flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm));
448
0
  INSIST(flen < len);
449
0
}
450
451
void
452
0
isc_time_formatISO8601ms(const isc_time_t *t, char *buf, unsigned int len) {
453
0
  time_t now;
454
0
  unsigned int flen;
455
0
  struct tm tm;
456
457
0
  REQUIRE(t != NULL);
458
0
  INSIST(t->nanoseconds < NS_PER_SEC);
459
0
  REQUIRE(buf != NULL);
460
0
  REQUIRE(len > 0);
461
462
0
  now = (time_t)t->seconds;
463
0
  flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm));
464
0
  INSIST(flen < len);
465
0
  if (flen > 0U && len - flen >= 5) {
466
0
    flen -= 1; /* rewind one character (Z) */
467
0
    snprintf(buf + flen, len - flen, ".%03uZ",
468
0
       t->nanoseconds / NS_PER_MS);
469
0
  }
470
0
}
471
472
void
473
0
isc_time_formatISO8601us(const isc_time_t *t, char *buf, unsigned int len) {
474
0
  time_t now;
475
0
  unsigned int flen;
476
0
  struct tm tm;
477
478
0
  REQUIRE(t != NULL);
479
0
  INSIST(t->nanoseconds < NS_PER_SEC);
480
0
  REQUIRE(buf != NULL);
481
0
  REQUIRE(len > 0);
482
483
0
  now = (time_t)t->seconds;
484
0
  flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm));
485
0
  INSIST(flen < len);
486
0
  if (flen > 0U && len - flen >= 5) {
487
0
    flen -= 1; /* rewind one character (Z) */
488
0
    snprintf(buf + flen, len - flen, ".%06uZ",
489
0
       t->nanoseconds / NS_PER_US);
490
0
  }
491
0
}
492
493
void
494
isc_time_formatshorttimestamp(const isc_time_t *t, char *buf,
495
0
            unsigned int len) {
496
0
  time_t now;
497
0
  unsigned int flen;
498
0
  struct tm tm;
499
500
0
  REQUIRE(t != NULL);
501
0
  INSIST(t->nanoseconds < NS_PER_SEC);
502
0
  REQUIRE(buf != NULL);
503
0
  REQUIRE(len > 0);
504
505
0
  now = (time_t)t->seconds;
506
0
  flen = strftime(buf, len, "%Y%m%d%H%M%S", gmtime_r(&now, &tm));
507
0
  INSIST(flen < len);
508
0
  if (flen > 0U && len - flen >= 5) {
509
0
    snprintf(buf + flen, len - flen, "%03u",
510
0
       t->nanoseconds / NS_PER_MS);
511
0
  }
512
0
}