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