xref: /netbsd-src/external/bsd/ntp/dist/libntp/lib/isc/unix/time.c (revision 8e33eff89e26cf71871ead62f0d5063e1313c33a)
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 
59 static isc_interval_t zero_interval = { 0, 0 };
60 isc_interval_t *isc_interval_zero = &zero_interval;
61 
62 #if ISC_FIX_TV_USEC
63 static inline void
64 fix_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 
88 void
89 isc_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 
99 isc_boolean_t
100 isc_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 
115 static isc_time_t epoch = { 0, 0 };
116 isc_time_t *isc_time_epoch = &epoch;
117 
118 void
119 isc_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 
127 void
128 isc_time_settoepoch(isc_time_t *t) {
129 	REQUIRE(t != NULL);
130 
131 	t->seconds = 0;
132 	t->nanoseconds = 0;
133 }
134 
135 isc_boolean_t
136 isc_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 
147 isc_result_t
148 isc_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 
189 isc_result_t
190 isc_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 
240 int
241 isc_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 
256 isc_result_t
257 isc_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 
282 isc_result_t
283 isc_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 
306 isc_uint64_t
307 isc_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 
329 isc_uint32_t
330 isc_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 
337 isc_result_t
338 isc_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 
374 isc_uint32_t
375 isc_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 
383 void
384 isc_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 
400 void
401 isc_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 
412 void
413 isc_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