1/*	$NetBSD: time.c,v 1.2 2024/08/18 20:47:16 christos Exp $	*/
2
3/*
4 * Copyright (C) 2004-2008, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1998-2001, 2003  Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/* Id */
21
22/*! \file */
23
24#include <config.h>
25
26#include <errno.h>
27#include <limits.h>
28#include <syslog.h>
29#include <time.h>
30
31#include <sys/time.h>	/* Required for struct timeval on some platforms. */
32
33#include <isc/log.h>
34#include <isc/print.h>
35#include <isc/strerror.h>
36#include <isc/string.h>
37#include <isc/time.h>
38#include <isc/util.h>
39
40#define NS_PER_S	1000000000	/*%< Nanoseconds per second. */
41#define NS_PER_US	1000		/*%< Nanoseconds per microsecond. */
42#define US_PER_S	1000000		/*%< Microseconds per second. */
43
44/*
45 * All of the INSIST()s checks of nanoseconds < NS_PER_S are for
46 * consistency checking of the type. In lieu of magic numbers, it
47 * is the best we've got.  The check is only performed on functions which
48 * need an initialized type.
49 */
50
51#ifndef ISC_FIX_TV_USEC
52#define ISC_FIX_TV_USEC 1
53#endif
54
55/*%
56 *** Intervals
57 ***/
58
59static isc_interval_t zero_interval = { 0, 0 };
60isc_interval_t *isc_interval_zero = &zero_interval;
61
62#if ISC_FIX_TV_USEC
63static inline void
64fix_tv_usec(struct timeval *tv) {
65	isc_boolean_t fixed = ISC_FALSE;
66
67	if (tv->tv_usec < 0) {
68		fixed = ISC_TRUE;
69		do {
70			tv->tv_sec -= 1;
71			tv->tv_usec += US_PER_S;
72		} while (tv->tv_usec < 0);
73	} else if (tv->tv_usec >= US_PER_S) {
74		fixed = ISC_TRUE;
75		do {
76			tv->tv_sec += 1;
77			tv->tv_usec -= US_PER_S;
78		} while (tv->tv_usec >=US_PER_S);
79	}
80	/*
81	 * Call syslog directly as was are called from the logging functions.
82	 */
83	if (fixed)
84		(void)syslog(LOG_ERR, "gettimeofday returned bad tv_usec: corrected");
85}
86#endif
87
88void
89isc_interval_set(isc_interval_t *i,
90		 unsigned int seconds, unsigned int nanoseconds)
91{
92	REQUIRE(i != NULL);
93	REQUIRE(nanoseconds < NS_PER_S);
94
95	i->seconds = seconds;
96	i->nanoseconds = nanoseconds;
97}
98
99isc_boolean_t
100isc_interval_iszero(const isc_interval_t *i) {
101	REQUIRE(i != NULL);
102	INSIST(i->nanoseconds < NS_PER_S);
103
104	if (i->seconds == 0 && i->nanoseconds == 0)
105		return (ISC_TRUE);
106
107	return (ISC_FALSE);
108}
109
110
111/***
112 *** Absolute Times
113 ***/
114
115static isc_time_t epoch = { 0, 0 };
116isc_time_t *isc_time_epoch = &epoch;
117
118void
119isc_time_set(isc_time_t *t, unsigned int seconds, unsigned int nanoseconds) {
120	REQUIRE(t != NULL);
121	REQUIRE(nanoseconds < NS_PER_S);
122
123	t->seconds = seconds;
124	t->nanoseconds = nanoseconds;
125}
126
127void
128isc_time_settoepoch(isc_time_t *t) {
129	REQUIRE(t != NULL);
130
131	t->seconds = 0;
132	t->nanoseconds = 0;
133}
134
135isc_boolean_t
136isc_time_isepoch(const isc_time_t *t) {
137	REQUIRE(t != NULL);
138	INSIST(t->nanoseconds < NS_PER_S);
139
140	if (t->seconds == 0 && t->nanoseconds == 0)
141		return (ISC_TRUE);
142
143	return (ISC_FALSE);
144}
145
146
147isc_result_t
148isc_time_now(isc_time_t *t) {
149	struct timeval tv;
150	char strbuf[ISC_STRERRORSIZE];
151
152	REQUIRE(t != NULL);
153
154	if (gettimeofday(&tv, NULL) == -1) {
155		isc__strerror(errno, strbuf, sizeof(strbuf));
156		UNEXPECTED_ERROR(__FILE__, __LINE__, "%s", strbuf);
157		return (ISC_R_UNEXPECTED);
158	}
159
160	/*
161	 * Does POSIX guarantee the signedness of tv_sec and tv_usec?  If not,
162	 * then this test will generate warnings for platforms on which it is
163	 * unsigned.  In any event, the chances of any of these problems
164	 * happening are pretty much zero, but since the libisc library ensures
165	 * certain things to be true ...
166	 */
167#if ISC_FIX_TV_USEC
168	fix_tv_usec(&tv);
169	if (tv.tv_sec < 0)
170		return (ISC_R_UNEXPECTED);
171#else
172	if (tv.tv_sec < 0 || tv.tv_usec < 0 || tv.tv_usec >= US_PER_S)
173		return (ISC_R_UNEXPECTED);
174#endif
175
176	/*
177	 * Ensure the tv_sec value fits in t->seconds.
178	 */
179	if (sizeof(tv.tv_sec) > sizeof(t->seconds) &&
180	    ((tv.tv_sec | (unsigned int)-1) ^ (unsigned int)-1) != 0U)
181		return (ISC_R_RANGE);
182
183	t->seconds = tv.tv_sec;
184	t->nanoseconds = tv.tv_usec * NS_PER_US;
185
186	return (ISC_R_SUCCESS);
187}
188
189isc_result_t
190isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i) {
191	struct timeval tv;
192	char strbuf[ISC_STRERRORSIZE];
193
194	REQUIRE(t != NULL);
195	REQUIRE(i != NULL);
196	INSIST(i->nanoseconds < NS_PER_S);
197
198	if (gettimeofday(&tv, NULL) == -1) {
199		isc__strerror(errno, strbuf, sizeof(strbuf));
200		UNEXPECTED_ERROR(__FILE__, __LINE__, "%s", strbuf);
201		return (ISC_R_UNEXPECTED);
202	}
203
204	/*
205	 * Does POSIX guarantee the signedness of tv_sec and tv_usec?  If not,
206	 * then this test will generate warnings for platforms on which it is
207	 * unsigned.  In any event, the chances of any of these problems
208	 * happening are pretty much zero, but since the libisc library ensures
209	 * certain things to be true ...
210	 */
211#if ISC_FIX_TV_USEC
212	fix_tv_usec(&tv);
213	if (tv.tv_sec < 0)
214		return (ISC_R_UNEXPECTED);
215#else
216	if (tv.tv_sec < 0 || tv.tv_usec < 0 || tv.tv_usec >= US_PER_S)
217		return (ISC_R_UNEXPECTED);
218#endif
219
220	/*
221	 * Ensure the resulting seconds value fits in the size of an
222	 * unsigned int.  (It is written this way as a slight optimization;
223	 * note that even if both values == INT_MAX, then when added
224	 * and getting another 1 added below the result is UINT_MAX.)
225	 */
226	if ((tv.tv_sec > INT_MAX || i->seconds > INT_MAX) &&
227	    ((long long)tv.tv_sec + i->seconds > UINT_MAX))
228		return (ISC_R_RANGE);
229
230	t->seconds = tv.tv_sec + i->seconds;
231	t->nanoseconds = tv.tv_usec * NS_PER_US + i->nanoseconds;
232	if (t->nanoseconds >= NS_PER_S) {
233		t->seconds++;
234		t->nanoseconds -= NS_PER_S;
235	}
236
237	return (ISC_R_SUCCESS);
238}
239
240int
241isc_time_compare(const isc_time_t *t1, const isc_time_t *t2) {
242	REQUIRE(t1 != NULL && t2 != NULL);
243	INSIST(t1->nanoseconds < NS_PER_S && t2->nanoseconds < NS_PER_S);
244
245	if (t1->seconds < t2->seconds)
246		return (-1);
247	if (t1->seconds > t2->seconds)
248		return (1);
249	if (t1->nanoseconds < t2->nanoseconds)
250		return (-1);
251	if (t1->nanoseconds > t2->nanoseconds)
252		return (1);
253	return (0);
254}
255
256isc_result_t
257isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result)
258{
259	REQUIRE(t != NULL && i != NULL && result != NULL);
260	INSIST(t->nanoseconds < NS_PER_S && i->nanoseconds < NS_PER_S);
261
262	/*
263	 * Ensure the resulting seconds value fits in the size of an
264	 * unsigned int.  (It is written this way as a slight optimization;
265	 * note that even if both values == INT_MAX, then when added
266	 * and getting another 1 added below the result is UINT_MAX.)
267	 */
268	if ((t->seconds > INT_MAX || i->seconds > INT_MAX) &&
269	    ((long long)t->seconds + i->seconds > UINT_MAX))
270		return (ISC_R_RANGE);
271
272	result->seconds = t->seconds + i->seconds;
273	result->nanoseconds = t->nanoseconds + i->nanoseconds;
274	if (result->nanoseconds >= NS_PER_S) {
275		result->seconds++;
276		result->nanoseconds -= NS_PER_S;
277	}
278
279	return (ISC_R_SUCCESS);
280}
281
282isc_result_t
283isc_time_subtract(const isc_time_t *t, const isc_interval_t *i,
284		  isc_time_t *result)
285{
286	REQUIRE(t != NULL && i != NULL && result != NULL);
287	INSIST(t->nanoseconds < NS_PER_S && i->nanoseconds < NS_PER_S);
288
289	if ((unsigned int)t->seconds < i->seconds ||
290	    ((unsigned int)t->seconds == i->seconds &&
291	     t->nanoseconds < i->nanoseconds))
292	    return (ISC_R_RANGE);
293
294	result->seconds = t->seconds - i->seconds;
295	if (t->nanoseconds >= i->nanoseconds)
296		result->nanoseconds = t->nanoseconds - i->nanoseconds;
297	else {
298		result->nanoseconds = NS_PER_S - i->nanoseconds +
299			t->nanoseconds;
300		result->seconds--;
301	}
302
303	return (ISC_R_SUCCESS);
304}
305
306isc_uint64_t
307isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2) {
308	isc_uint64_t i1, i2, i3;
309
310	REQUIRE(t1 != NULL && t2 != NULL);
311	INSIST(t1->nanoseconds < NS_PER_S && t2->nanoseconds < NS_PER_S);
312
313	i1 = (isc_uint64_t)t1->seconds * NS_PER_S + t1->nanoseconds;
314	i2 = (isc_uint64_t)t2->seconds * NS_PER_S + t2->nanoseconds;
315
316	if (i1 <= i2)
317		return (0);
318
319	i3 = i1 - i2;
320
321	/*
322	 * Convert to microseconds.
323	 */
324	i3 /= NS_PER_US;
325
326	return (i3);
327}
328
329isc_uint32_t
330isc_time_seconds(const isc_time_t *t) {
331	REQUIRE(t != NULL);
332	INSIST(t->nanoseconds < NS_PER_S);
333
334	return ((isc_uint32_t)t->seconds);
335}
336
337isc_result_t
338isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp) {
339	time_t seconds;
340
341	REQUIRE(t != NULL);
342	INSIST(t->nanoseconds < NS_PER_S);
343
344	/*
345	 * Ensure that the number of seconds represented by t->seconds
346	 * can be represented by a time_t.  Since t->seconds is an unsigned
347	 * int and since time_t is mostly opaque, this is trickier than
348	 * it seems.  (This standardized opaqueness of time_t is *very*
349	 * frustrating; time_t is not even limited to being an integral
350	 * type.)
351	 *
352	 * The mission, then, is to avoid generating any kind of warning
353	 * about "signed versus unsigned" while trying to determine if the
354	 * the unsigned int t->seconds is out range for tv_sec, which is
355	 * pretty much only true if time_t is a signed integer of the same
356	 * size as the return value of isc_time_seconds.
357	 *
358	 * If the paradox in the if clause below is true, t->seconds is out
359	 * of range for time_t.
360	 */
361	seconds = (time_t)t->seconds;
362
363	INSIST(sizeof(unsigned int) == sizeof(isc_uint32_t));
364	INSIST(sizeof(time_t) >= sizeof(isc_uint32_t));
365
366	if (t->seconds > (~0U>>1) && seconds <= (time_t)(~0U>>1))
367		return (ISC_R_RANGE);
368
369	*secondsp = seconds;
370
371	return (ISC_R_SUCCESS);
372}
373
374isc_uint32_t
375isc_time_nanoseconds(const isc_time_t *t) {
376	REQUIRE(t != NULL);
377
378	ENSURE(t->nanoseconds < NS_PER_S);
379
380	return ((isc_uint32_t)t->nanoseconds);
381}
382
383void
384isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len) {
385	time_t now;
386	unsigned int flen;
387
388	REQUIRE(len > 0);
389
390	now = (time_t) t->seconds;
391	flen = strftime(buf, len, "%d-%b-%Y %X", localtime(&now));
392	INSIST(flen < len);
393	if (flen != 0)
394		snprintf(buf + flen, len - flen,
395			 ".%03u", t->nanoseconds / 1000000);
396	else
397		snprintf(buf, len, "99-Bad-9999 99:99:99.999");
398}
399
400void
401isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len) {
402	time_t now;
403	unsigned int flen;
404
405	REQUIRE(len > 0);
406
407	now = (time_t)t->seconds;
408	flen = strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
409	INSIST(flen < len);
410}
411
412void
413isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len) {
414	time_t now;
415	unsigned int flen;
416
417	REQUIRE(len > 0);
418
419	now = (time_t)t->seconds;
420	flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
421	INSIST(flen < len);
422}
423