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