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