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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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