The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
time.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 4ae54fffc741e1c5ab585723f2eeb77f5573d010 $
19 *
20 * @brief Platform independent time functions
21 * @file lib/util/time.c
22 *
23 * @copyright 2016-2019 Alan DeKok (aland@freeradius.org)
24 * @copyright 2019-2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25 */
26RCSID("$Id: 4ae54fffc741e1c5ab585723f2eeb77f5573d010 $")
27
28#include <freeradius-devel/autoconf.h>
29#include <freeradius-devel/util/time.h>
30#include <freeradius-devel/util/skip.h>
31
32int64_t const fr_time_multiplier_by_res[] = {
33 [FR_TIME_RES_NSEC] = 1,
38 [FR_TIME_RES_MIN] = (int64_t)NSEC * 60,
39 [FR_TIME_RES_HOUR] = (int64_t)NSEC * 3600,
40 [FR_TIME_RES_DAY] = (int64_t)NSEC * 86400,
41 [FR_TIME_RES_WEEK] = (int64_t)NSEC * 86400 * 7,
44};
45
47 { L("microseconds"), FR_TIME_RES_USEC },
48 { L("us"), FR_TIME_RES_USEC },
49
50 { L("nanoseconds"), FR_TIME_RES_NSEC },
51 { L("ns"), FR_TIME_RES_NSEC },
52
53 { L("milliseconds"), FR_TIME_RES_MSEC },
54 { L("ms"), FR_TIME_RES_MSEC },
55
56 { L("centiseconds"), FR_TIME_RES_CSEC },
57 { L("cs"), FR_TIME_RES_CSEC },
58
59 { L("seconds"), FR_TIME_RES_SEC },
60 { L("s"), FR_TIME_RES_SEC },
61
62 { L("minutes"), FR_TIME_RES_MIN },
63 { L("m"), FR_TIME_RES_MIN },
64
65 { L("hours"), FR_TIME_RES_HOUR },
66 { L("h"), FR_TIME_RES_HOUR },
67
68 { L("days"), FR_TIME_RES_DAY },
69 { L("d"), FR_TIME_RES_DAY },
70
71 { L("weeks"), FR_TIME_RES_WEEK },
72 { L("w"), FR_TIME_RES_WEEK },
73
74 /*
75 * These use special values FR_TIME_DUR_MONTH and FR_TIME_DUR_YEAR
76 */
77 { L("months"), FR_TIME_RES_MONTH },
78 { L("M"), FR_TIME_RES_MONTH },
79
80 { L("years"), FR_TIME_RES_YEAR },
81 { L("y"), FR_TIME_RES_YEAR },
82
83};
85
86int64_t fr_time_epoch; //!< monotonic clock at boot, i.e. our epoch
87_Atomic int64_t fr_time_monotonic_to_realtime; //!< difference between the two clocks
88
89static char const *tz_names[2] = { NULL, NULL }; //!< normal, DST, from localtime_r(), tm_zone
90static long gmtoff[2] = {0, 0}; //!< from localtime_r(), tm_gmtoff
91static bool isdst = false; //!< from localtime_r(), tm_is_dst
92
93/*
94 * fr_time_to_sec() returns `int64_t`, which is then assigned to a local `time_t`.
95 *
96 * POSIX.1-2024 requires that the width of `time_t` is at least
97 * 64 bits, so we check that here.
98 */
99static_assert(sizeof(time_t) == sizeof(int64_t), "time_t is not 64-bits");
100
101/** Get a new fr_time_monotonic_to_realtime value
102 *
103 * Should be done regularly to adjust for changes in system time.
104 *
105 * @return
106 * - 0 on success.
107 * - -1 on failure.
108 */
110{
111 struct tm tm;
112 time_t now;
113
114 /*
115 * fr_time_monotonic_to_realtime is the difference in nano
116 *
117 * So to convert a realtime timeval to fr_time we just subtract fr_time_monotonic_to_realtime from the timeval,
118 * which leaves the number of nanoseconds elapsed since our epoch.
119 */
120 struct timespec ts_realtime, ts_monotime;
121
122 /*
123 * Call these consecutively to minimise drift...
124 */
125 if (clock_gettime(CLOCK_REALTIME, &ts_realtime) < 0) return -1;
126 if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts_monotime) < 0) return -1;
127
132
133 now = ts_realtime.tv_sec;
134
135 /*
136 * Get local time zone name, daylight savings, and GMT
137 * offsets.
138 */
139 (void) localtime_r(&now, &tm);
140
141 isdst = (tm.tm_isdst != 0);
142 tz_names[isdst] = tm.tm_zone;
143 gmtoff[isdst] = tm.tm_gmtoff * NSEC; /* they store seconds, we store nanoseconds */
144
145 return 0;
146}
147
148/** Initialize the local time.
149 *
150 * MUST be called when the program starts. MUST NOT be called after
151 * that.
152 *
153 * @return
154 * - <0 on error
155 * - 0 on success
156 */
158{
159 struct timespec ts;
160
161 tzset(); /* Populate timezone, daylight and tzname globals */
162
163 if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) < 0) return -1;
165
166 return fr_time_sync();
167}
168
169/** Return time delta from the time zone.
170 *
171 * Returns the delta between UTC and the timezone specified by tz
172 *
173 * @param[in] tz time zone name
174 * @param[out] delta the time delta
175 * @return
176 * - 0 converted OK
177 * - <0 on error
178 *
179 * @note This function ONLY handles a limited number of time
180 * zones: local and gmt. It is impossible in general to parse
181 * arbitrary time zone strings, as there are duplicates.
182 */
184{
185 *delta = fr_time_delta_wrap(0);
186
187 if ((strcmp(tz, "UTC") == 0) ||
188 (strcmp(tz, "GMT") == 0)) {
189 return 0;
190 }
191
192 /*
193 * Our local time zone OR time zone with daylight savings.
194 */
195 if (tz_names[0] && (strcmp(tz, tz_names[0]) == 0)) {
196 *delta = fr_time_delta_wrap(gmtoff[0]);
197 return 0;
198 }
199
200 if (tz_names[1] && (strcmp(tz, tz_names[1]) == 0)) {
201 *delta = fr_time_delta_wrap(gmtoff[1]);
202 return 0;
203 }
204
205 return -1;
206}
207
208/** Create fr_time_delta_t from a string
209 *
210 * @param[out] out Where to write fr_time_delta_t
211 * @param[in] in String to parse.
212 * @param[in] hint scale for the parsing. Default is "seconds".
213 * @param[in] no_trailing asserts that there should be a terminal sequence
214 * after the time delta. Allows us to produce
215 * better errors.
216 * @param[in] tt terminal sequences.
217 * @return
218 * - >= 0 on success.
219 * - <0 on failure.
220 */
222 bool no_trailing, fr_sbuff_term_t const *tt)
223{
224 fr_sbuff_t our_in = FR_SBUFF(in);
225 int64_t integer = 0; /* Whole units */
226 double f = 0.0;
227 fr_time_res_t res;
228 bool do_float;
229 bool negative;
230 fr_sbuff_err_t sberr;
231 bool overflow;
232 size_t match_len;
233
234 negative = fr_sbuff_is_char(&our_in, '-');
235 do_float = false;
236
237 if (fr_sbuff_is_char(&our_in, '.')) goto is_float;
238
239 /*
240 * Look for:
241 *
242 * <integer>[scale]
243 */
244 if ((sberr = fr_sbuff_out(&integer, &our_in)) < 0) {
245 char const *err;
246
247 num_error:
248 if (sberr != FR_SBUFF_ERR_NOT_FOUND) {
249 err = fr_table_str_by_value(sbuff_err_table, sberr, "<INVALID>");
250 } else {
251 err = "Invalid text, input should be a number";
252 }
253
254 fr_strerror_printf("Failed parsing time_delta: %s", err);
255 FR_SBUFF_ERROR_RETURN(&our_in);
256 }
257
258 /*
259 * hh:mm:ss
260 */
261 if (fr_sbuff_next_if_char(&our_in, ':')) goto do_timestamp;
262
263 /*
264 * If it's a fractional thing, then just parse it as a double.
265 *
266 * <float>[scale]
267 */
268 if (fr_sbuff_is_char(&our_in, '.')) {
269 our_in = FR_SBUFF(in);
270
271 is_float:
272 if ((sberr = fr_sbuff_out(&f, &our_in)) < 0) goto num_error;
273
274 do_float = true;
275 }
276
277 /*
278 * Now look for the time resolution.
279 */
281
282 if (fr_sbuff_is_terminal(&our_in, tt)) {
283 if (match_len == 0) res = hint;
284
285 } else if (no_trailing) {
286 fail_trailing_data:
287 /* Got a qualifier but there is more text after it. */
288 if (res != FR_TIME_RES_INVALID) {
289 fr_strerror_const("Trailing data after time_delta");
290 FR_SBUFF_ERROR_RETURN(&our_in);
291 }
292
293 fr_strerror_const("Invalid precision qualifier for time_delta");
294 FR_SBUFF_ERROR_RETURN(&our_in);
295
296 } else if (match_len == 0) {
297 /*
298 * There is trailing data, but we don't care about it. Ensure that we have a time resolution.
299 */
300 res = hint;
301 }
302
304
305 /*
306 * For floating point numbers, we pre-multiply by the time resolution, and then override the time
307 * resolution to indicate that no further scaling is necessary.
308 *
309 * We check for overflow prior to multiplication, as doubles have ~53 bits of precision, while
310 * int64_t has 64 bits of precision. That way the comparison is more likely to be accurate.
311 */
312 if (do_float) {
313 if (f < ((double) INT64_MIN) / (double) fr_time_multiplier_by_res[res]) goto fail_overflow;
314 /*
315 * INT64_MAX rounds up to 2^63 as a double, so the upper check is >=. A value
316 * equal to the bound would scale to 2^63, which does not fit in int64_t.
317 */
318 if (f >= ((double) INT64_MAX) / (double) fr_time_multiplier_by_res[res]) goto fail_overflow;
319
320 f *= (double) fr_time_multiplier_by_res[res];
321 res = FR_TIME_RES_NSEC;
322 integer = (int64_t) f;
323 }
324
325 /*
326 * We have a valid time scale. Let's use that.
327 */
328 *out = fr_time_delta_from_integer(&overflow, integer, res);
329 if (overflow) {
330 fail_overflow:
331 fr_strerror_printf("time_delta would %s", negative ? "underflow" : "overflow");
332 fr_sbuff_set_to_start(&our_in);
333 FR_SBUFF_ERROR_RETURN(&our_in);
334
335 }
336 FR_SBUFF_SET_RETURN(in, &our_in);
337
338do_timestamp:
339 res = hint;
340
341 /*
342 * We allow 2^15 hours, but much less than that in seconds/
343 */
344 if (integer < 0) {
345 if (integer < INT16_MIN) goto fail_overflow;
346 } else {
347 if (integer > INT16_MAX) goto fail_overflow;
348 }
349
350 /*
351 * It's a timestamp format
352 *
353 * [hours:]minutes:seconds
354 */
355 {
356 uint64_t hours, minutes, seconds;
358
359 fr_sbuff_marker(&m1, &our_in);
360
361 if ((sberr = fr_sbuff_out(&seconds, &our_in)) < 0) goto num_error;
362
363 /*
364 * minutes:seconds
365 */
366 if (!fr_sbuff_next_if_char(&our_in, ':')) {
367 hours = 0;
368 minutes = negative ? -integer : integer;
369
370 } else {
371 /*
372 * hours:minutes:seconds
373 *
374 * The second number we read is the minutes,
375 * and the seconds are the third number, read
376 * below. For the mm:ss form the second number
377 * is already the seconds, so it must NOT be
378 * re-read here.
379 */
380 hours = negative ? -integer : integer;
381 minutes = seconds;
382
383 if ((sberr = fr_sbuff_out(&seconds, &our_in)) < 0) goto num_error;
384 }
385
386 if (minutes >= 60) {
387 fr_strerror_printf("minutes component of time_delta is too large");
389 }
390
391 if (seconds >= 60) {
392 fr_strerror_printf("seconds component of time_delta is too large");
394 }
395
396 if (no_trailing && !fr_sbuff_is_terminal(&our_in, tt)) goto fail_trailing_data;
397
398 /*
399 * Add all the components together...
400 */
401 if (!fr_add(&integer, ((hours * 60) * 60) + (minutes * 60), seconds)) goto fail_overflow;
402
403 /*
404 * We can't have more than 64K hours plus a bit,
405 * which limits the size of the integer that we
406 * return.
407 */
408 fr_assert(integer < ((int64_t) UINT16_MAX) * 3600 + 3600 + 60);
409
410 /*
411 * Flip the sign back to negative
412 */
413 if (negative) integer = -(integer);
414 }
415
416 *out = fr_time_delta_from_sec(integer);
417 FR_SBUFF_SET_RETURN(in, &our_in);
418}
419
420/** Create fr_time_delta_t from a string
421 *
422 * @param[out] out Where to write fr_time_delta_t
423 * @param[in] in String to parse.
424 * @param[in] inlen Length of string.
425 * @param[in] hint scale for the parsing. Default is "seconds"
426 * @return
427 * - >0 on success.
428 * - <0 on failure.
429 */
431{
432 fr_slen_t slen;
433
434 if (!*in) {
435 fr_strerror_const("Empty input is invalid");
436 return -1;
437 }
438
439 slen = fr_time_delta_from_substr(out, &FR_SBUFF_IN(in, inlen), hint, true, NULL);
440 if (slen < 0) return slen;
441 if (slen != (fr_slen_t)inlen) {
442 fr_strerror_const("trailing data after time_delta"); /* Shouldn't happen with no_trailing */
443 return -(inlen + 1);
444 }
445 return slen;
446}
447
448/** Print fr_time_delta_t to a string with an appropriate suffix
449 *
450 * @param[out] out Where to write the string version of the time delta.
451 * @param[in] delta to print.
452 * @param[in] res to print resolution with.
453 * @param[in] is_unsigned whether the value should be printed unsigned.
454 * @return
455 * - >0 the number of bytes written to out.
456 * - <0 how many additional bytes would have been required.
457 */
459{
460 fr_sbuff_t our_out = FR_SBUFF(out);
461 char *q;
462 char *start;
463 int64_t lhs = 0;
464 uint64_t rhs = 0;
465
466/*
467 * The % operator can return a _signed_ value. This macro is
468 * correct for both positive and negative inputs.
469 */
470#define MOD(a,b) ((((a) < 0) ? -(uint64_t)(a) : (uint64_t)(a)) % (b))
471
472 lhs = fr_time_delta_to_integer(delta, res);
474
475 if (!is_unsigned) {
476 /*
477 * 0 is unsigned, but we want to print
478 * "-0.1" if necessary.
479 */
480 if ((lhs == 0) && fr_time_delta_isneg(delta)) {
481 FR_SBUFF_IN_CHAR_RETURN(&our_out, '-');
482 }
483
484 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%" PRIi64 ".%09" PRIu64, lhs, rhs);
485 } else {
486 if (fr_time_delta_isneg(delta)) lhs = rhs = 0;
487
488 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%" PRIu64 ".%09" PRIu64, lhs, rhs);
489 }
490 /*
491 * If the sprintf wrote nothing there's nothing to trim.
492 * (Shouldn't happen for a non-zero format like %lu.%09lu, but
493 * guarding keeps us from walking behind the buffer if the sbuff
494 * ran out of room and no bytes were written.)
495 */
496 if (fr_sbuff_current(&our_out) == fr_sbuff_start(&our_out)) FR_SBUFF_SET_RETURN(out, &our_out);
497
498 q = fr_sbuff_current(&our_out) - 1;
499 start = fr_sbuff_start(&our_out);
500
501 /*
502 * Truncate trailing zeros. Don't walk past the start of the
503 * buffer - a bare "0" has no trailing zeros to strip.
504 */
505 while ((q > start) && (*q == '0')) *(q--) = '\0';
506
507 /*
508 * If there's nothing after the decimal point,
509 * truncate the decimal point. i.e. Don't print
510 * "5."
511 */
512 if (*q == '.') {
513 *q = '\0';
514 } else {
515 q++; /* to account for q-- above */
516 }
517
519}
520
521DIAG_OFF(format-nonliteral)
522/** Copy a time string (local timezone) to an sbuff
523 *
524 * @note This function will attempt to extend the sbuff by double the length of
525 * the fmt string. It is recommended to either pre-extend the sbuff before
526 * calling this function, or avoid using format specifiers that expand to
527 * character strings longer than 4 bytes.
528 *
529 * @param[in] out Where to write the formatted time string.
530 * @param[in] time Internal server time to convert to wallclock
531 * time and copy out as formatted string.
532 * @param[in] fmt Time format string.
533 * @return
534 * - >0 the number of bytes written to the sbuff.
535 * - 0 if there's insufficient space in the sbuff.
536 */
538{
539 struct tm tm;
540 time_t utime = fr_time_to_sec(time);
541 size_t len;
542
543 localtime_r(&utime, &tm);
544
545 len = strftime(fr_sbuff_current(out), fr_sbuff_extend_lowat(NULL, out, strlen(fmt) * 2), fmt, &tm);
546 if (len == 0) return 0;
547
548 return fr_sbuff_advance(out, len);
549}
550
551/** Copy a time string (UTC) to an sbuff
552 *
553 * @note This function will attempt to extend the sbuff by double the length of
554 * the fmt string. It is recommended to either pre-extend the sbuff before
555 * calling this function, or avoid using format specifiers that expand to
556 * character strings longer than 4 bytes.
557 *
558 * @param[in] out Where to write the formatted time string.
559 * @param[in] time Internal server time to convert to wallclock
560 * time and copy out as formatted string.
561 * @param[in] fmt Time format string.
562 * @return
563 * - >0 the number of bytes written to the sbuff.
564 * - 0 if there's insufficient space in the sbuff.
565 */
567{
568 struct tm tm;
569 time_t utime = fr_time_to_sec(time);
570 size_t len;
571
572 gmtime_r(&utime, &tm);
573
574 len = strftime(fr_sbuff_current(out), fr_sbuff_extend_lowat(NULL, out, strlen(fmt) * 2), fmt, &tm);
575 if (len == 0) return 0;
576
577 return fr_sbuff_advance(out, len);
578}
579DIAG_ON(format-nonliteral)
580
582{
583 fr_time_delta_t delay;
584
585 if (fr_time_gteq(start, end)) {
586 delay = fr_time_delta_wrap(0);
587 } else {
588 delay = fr_time_sub(end, start);
589 }
590
591 if (fr_time_delta_lt(delay, fr_time_delta_wrap(1000))) { /* microseconds */
592 elapsed->array[0]++;
593
594 } else if (fr_time_delta_lt(delay, fr_time_delta_wrap(10000))) {
595 elapsed->array[1]++;
596
597 } else if (fr_time_delta_lt(delay, fr_time_delta_wrap(100000))) {
598 elapsed->array[2]++;
599
600 } else if (fr_time_delta_lt(delay, fr_time_delta_wrap(1000000))) { /* milliseconds */
601 elapsed->array[3]++;
602
603 } else if (fr_time_delta_lt(delay, fr_time_delta_wrap(10000000))) {
604 elapsed->array[4]++;
605
606 } else if (fr_time_delta_lt(delay, fr_time_delta_wrap(100000000))) {
607 elapsed->array[5]++;
608
609 } else if (fr_time_delta_lt(delay, fr_time_delta_wrap(1000000000))) { /* seconds */
610 elapsed->array[6]++;
611
612 } else { /* tens of seconds or more */
613 elapsed->array[7]++;
614
615 }
616}
617
618static const char *names[8] = {
619 "1us", "10us", "100us",
620 "1ms", "10ms", "100ms",
621 "1s", "10s"
622};
623
624static char const *tab_string = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
625
626void fr_time_elapsed_fprint(FILE *fp, fr_time_elapsed_t const *elapsed, char const *prefix, int tab_offset)
627{
628 int i;
629 size_t prefix_len;
630
631 if (!prefix) prefix = "elapsed";
632
633 prefix_len = strlen(prefix);
634
635 for (i = 0; i < 8; i++) {
636 size_t len;
637
638 if (!elapsed->array[i]) continue;
639
640 len = prefix_len + 1 + strlen(names[i]);
641
642 if (len >= (size_t) (tab_offset * 8)) {
643 fprintf(fp, "%s.%s %" PRIu64 "\n",
644 prefix, names[i], elapsed->array[i]);
645
646 } else {
647 int tabs;
648
649 tabs = (int)((tab_offset * 8) - len);
650 if ((tabs & 0x07) != 0) tabs += 7;
651 tabs >>= 3;
652
653 fprintf(fp, "%s.%s%.*s%" PRIu64 "\n",
654 prefix, names[i], tabs, tab_string, elapsed->array[i]);
655 }
656 }
657}
658
659/*
660 * Based on https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
661 */
663{
664 static const uint16_t month_yday[12] = {0, 31, 59, 90, 120, 151,
665 181, 212, 243, 273, 304, 334};
666
667 uint32_t year_adj;
668 uint32_t febs;
669 uint32_t leap_days;
670 uint32_t days;
671
672 /* Prevent crash if tm->tm_mon is invalid - seen in clusterfuzz */
673 if (unlikely(tm->tm_mon >= (__typeof__(tm->tm_mon))NUM_ELEMENTS(month_yday))) return fr_unix_time_min();
674
675 if (unlikely(tm->tm_year > 10000)) return fr_unix_time_min();
676
677 year_adj = tm->tm_year + 4800 + 1900; /* Ensure positive year, multiple of 400. */
678 febs = year_adj - (tm->tm_mon < 2 ? 1 : 0); /* Februaries since base. tm_mon is 0 - 11 */
679 leap_days = 1 + (febs / 4) - (febs / 100) + (febs / 400);
680
681 days = 365 * year_adj + leap_days + month_yday[tm->tm_mon] + tm->tm_mday - 1;
682
683#define CHECK(_x, _max) if ((tm->tm_ ## _x < 0) || (tm->tm_ ## _x >= _max)) tm->tm_ ## _x = _max - 1
684
685 CHECK(sec, 60);
686 CHECK(min, 60);
687 CHECK(hour, 24);
688 CHECK(mday, 32);
689 CHECK(mon, 12);
690 CHECK(year, 3000);
691 CHECK(wday, 7);
692 CHECK(mon, 12);
693 CHECK(yday, 366);
694 /* don't check gmtoff, it can be negative */
695
696 /*
697 * 2472692 adjusts the days for Unix epoch. It is calculated as
698 * (365.2425 * (4800 + 1970))
699 *
700 * We REMOVE the time zone offset in order to get internal unix times in UTC.
701 */
702 return fr_unix_time_from_sec((((days - 2472692) * 86400) + (tm->tm_hour * 3600) +
703 (tm->tm_min * 60) + tm->tm_sec) - tm->tm_gmtoff);
704}
705
706/** Scale an input time to NSEC, clamping it at max / min.
707 *
708 * @param t input time / time delta
709 * @param hint time resolution hint
710 * @return
711 * - INT64_MIN on underflow
712 * - 0 on invalid hint
713 * - INT64_MAX on overflow
714 * - otherwise a valid number, multiplied by the relevant scale,
715 * so that the result is in nanoseconds.
716 */
717int64_t fr_time_scale(int64_t t, fr_time_res_t hint)
718{
719 int64_t scale;
720
721 switch (hint) {
722 case FR_TIME_RES_SEC:
723 scale = NSEC;
724 break;
725
726 case FR_TIME_RES_MSEC:
727 scale = 1000000;
728 break;
729
730 case FR_TIME_RES_USEC:
731 scale = 1000;
732 break;
733
734 case FR_TIME_RES_NSEC:
735 return t;
736
737 default:
738 return 0;
739 }
740
741 if (t < 0) {
742 if (t < (INT64_MIN / scale)) {
743 return INT64_MIN;
744 }
745 } else if (t > 0) {
746 if (t > (INT64_MAX / scale)) {
747 return INT64_MAX;
748 }
749 }
750
751 return t * scale;
752}
753
754
755/*
756 * Sort of strtok/strsep function.
757 */
758static char *mystrtok(char **ptr, char const *sep)
759{
760 char *res;
761
762 if (**ptr == '\0') return NULL;
763
764 while (**ptr && strchr(sep, **ptr)) (*ptr)++;
765
766 if (**ptr == '\0') return NULL;
767
768 res = *ptr;
769 while (**ptr && strchr(sep, **ptr) == NULL) (*ptr)++;
770
771 if (**ptr != '\0') *(*ptr)++ = '\0';
772
773 return res;
774}
775
776/*
777 * Helper function to get a 2-digit date. With a maximum value,
778 * and a terminating character.
779 */
780static int get_part(char **str, int *date, int min, int max, char term, char const *name)
781{
782 char *p = *str;
783
784 if (!isdigit((uint8_t) *p) || !isdigit((uint8_t) p[1])) return -1;
785 *date = (p[0] - '0') * 10 + (p[1] - '0');
786
787 if (*date < min) {
788 fr_strerror_printf("Invalid %s (too small)", name);
789 return -1;
790 }
791
792 if (*date > max) {
793 fr_strerror_printf("Invalid %s (too large)", name);
794 return -1;
795 }
796
797 p += 2;
798 if (!term) {
799 *str = p;
800 return 0;
801 }
802
803 if (*p != term) {
804 fr_strerror_printf("Expected '%c' after %s, got '%c'",
805 term, name, *p);
806 return -1;
807 }
808 p++;
809
810 *str = p;
811 return 0;
812}
813
814static char const *months[] = {
815 "jan", "feb", "mar", "apr", "may", "jun",
816 "jul", "aug", "sep", "oct", "nov", "dec" };
817
818
819/** Convert string in various formats to a fr_unix_time_t
820 *
821 * @param date_str input date string.
822 * @param date time_t to write result to.
823 * @param[in] hint scale for the parsing. Default is "seconds"
824 * @return
825 * - 0 on success.
826 * - -1 on failure.
827 */
828int fr_unix_time_from_str(fr_unix_time_t *date, char const *date_str, fr_time_res_t hint)
829{
830 int i;
831 int64_t tmp;
832 struct tm *tm, s_tm;
833 char buf[64];
834 char *p;
835 char *f[4];
836 char *tail = NULL;
837 unsigned long l;
838 fr_time_delta_t gmt_delta = fr_time_delta_wrap(0);
839
840 if (!*date_str) {
841 fr_strerror_const("Empty input is invalid");
842 return -1;
843 }
844
845 /*
846 * Test for unix timestamp, which is just a number and
847 * nothing else.
848 */
849 tmp = strtoul(date_str, &tail, 10);
850 if (*tail == '\0') {
851 *date = fr_unix_time_from_nsec(fr_time_scale(tmp, hint));
852 return 0;
853 }
854
855 tm = &s_tm;
856 memset(tm, 0, sizeof(*tm));
857 tm->tm_isdst = -1; /* don't know, and don't care about DST */
858
859 /*
860 * Check for RFC 3339 dates. Note that we only support
861 * dates in a ~1000 year period. If the server is being
862 * used after 3000AD, someone can patch it then.
863 *
864 * %Y-%m-%dT%H:%M:%S
865 * [.%d] sub-seconds
866 * Z | (+/-)%H:%M time zone offset
867 *
868 */
869 if ((tmp > 1900) && (tmp < 3000) && *tail == '-') {
870 unsigned long subseconds;
871 int tz, tz_hour, tz_min;
872
873 p = tail + 1;
874 s_tm.tm_year = (int) (tmp - 1900); /* 'struct tm' starts years in 1900 */
875
876 if (get_part(&p, &s_tm.tm_mon, 1, 12, '-', "month") < 0) return -1;
877 s_tm.tm_mon--; /* ISO is 1..12, where 'struct tm' is 0..11 */
878
879 if (get_part(&p, &s_tm.tm_mday, 1, 31, 'T', "day") < 0) return -1;
880 if (get_part(&p, &s_tm.tm_hour, 0, 23, ':', "hour") < 0) return -1;
881 if (get_part(&p, &s_tm.tm_min, 0, 59, ':', "minute") < 0) return -1;
882 if (get_part(&p, &s_tm.tm_sec, 0, 60, '\0', "seconds") < 0) return -1;
883
884 if (*p == '.') {
885 p++;
886 subseconds = strtoul(p, &tail, 10);
887 if (subseconds > NSEC) {
888 fr_strerror_const("Invalid nanosecond specifier");
889 return -1;
890 }
891
892 /*
893 * Scale subseconds to nanoseconds by how
894 * many digits were parsed/
895 */
896 if ((tail - p) < 9) {
897 for (i = 0; i < 9 - (tail -p); i++) {
898 subseconds *= 10;
899 }
900 }
901
902 p = tail;
903 } else {
904 subseconds = 0;
905 }
906
907 /*
908 * Time zone is GMT. Leave well enough
909 * alone.
910 */
911 if (*p == 'Z') {
912 if (p[1] != '\0') {
913 fr_strerror_printf("Unexpected text '%c' after time zone", p[1]);
914 return -1;
915 }
916 tz = 0;
917 goto done;
918 }
919
920 if ((*p != '+') && (*p != '-')) {
921 fr_strerror_printf("Invalid time zone specifier '%c'", *p);
922 return -1;
923 }
924 tail = p; /* remember sign for later */
925 p++;
926
927 if (get_part(&p, &tz_hour, 0, 23, ':', "hour in time zone") < 0) return -1;
928 if (get_part(&p, &tz_min, 0, 59, '\0', "minute in time zone") < 0) return -1;
929
930 if (*p != '\0') {
931 fr_strerror_printf("Unexpected text '%c' after time zone", *p);
932 return -1;
933 }
934
935 /*
936 * We set the time zone, but the timegm()
937 * function ignores it. Note also that mktime()
938 * ignores it too, and treats the time zone as
939 * local.
940 *
941 * We can't store this value in s_tm.gtmoff,
942 * because the timegm() function helpfully zeros
943 * it out.
944 *
945 * So insyead of using stupid C library
946 * functions, we just roll our own.
947 */
948 tz = tz_hour * 3600 + tz_min * 60;
949 if (*tail == '-') tz *= -1;
950
951 done:
952 /*
953 * Set the gmt offset correctly, as
954 * fr_unix_time_from_tm() will do the correction
955 * to remove the time zone.
956 */
957 tm->tm_gmtoff = tz;
959 return 0;
960 }
961
962 /*
963 * Try to parse dates via locale-specific names,
964 * using the same format string as strftime().
965 *
966 * If that fails, then we fall back to our parsing
967 * routine, which is much more forgiving.
968 */
969
970#ifdef __APPLE__
971 /*
972 * OSX "man strptime" says it only accepts the local time zone, and GMT.
973 *
974 * However, when printing dates via strftime(), it prints
975 * "UTC" instead of "GMT". So... we have to fix it up
976 * for stupid nonsense.
977 */
978 {
979 char const *tz = strstr(date_str, "UTC");
980 if (tz) {
981 char *my_str;
982
983 my_str = talloc_strdup(NULL, date_str);
984 if (my_str) {
985 p = my_str + (tz - date_str);
986 memcpy(p, "GMT", 3);
987
988 p = strptime(my_str, "%b %e %Y %H:%M:%S %Z", tm);
989 if (p && (*p == '\0')) {
990 talloc_free(my_str);
991 *date = fr_unix_time_from_tm(tm);
992 return 0;
993 }
994 talloc_free(my_str);
995 }
996 }
997 }
998#endif
999
1000 p = strptime(date_str, "%b %e %Y %H:%M:%S %Z", tm);
1001 if (p && (*p == '\0')) {
1002 *date = fr_unix_time_from_tm(tm);
1003 return 0;
1004 }
1005
1006 strlcpy(buf, date_str, sizeof(buf));
1007
1008 p = buf;
1009 f[0] = mystrtok(&p, " \t");
1010 f[1] = mystrtok(&p, " \t");
1011 f[2] = mystrtok(&p, " \t");
1012 f[3] = mystrtok(&p, " \t"); /* may, or may not, be present */
1013 if (!f[0] || !f[1] || !f[2]) {
1014 fr_strerror_const("Too few fields");
1015 return -1;
1016 }
1017
1018 /*
1019 * Try to parse the time zone. If it's GMT / UTC or a
1020 * local time zone we're OK.
1021 *
1022 * Otherwise, ignore errors and assume GMT.
1023 */
1024 if (*p != '\0') {
1026 (void) fr_time_delta_from_time_zone(p, &gmt_delta);
1027 }
1028
1029 /*
1030 * The time has a colon, where nothing else does.
1031 * So if we find it, bubble it to the back of the list.
1032 */
1033 if (f[3]) {
1034 for (i = 0; i < 3; i++) {
1035 if (strchr(f[i], ':')) {
1036 p = f[3];
1037 f[3] = f[i];
1038 f[i] = p;
1039 break;
1040 }
1041 }
1042 }
1043
1044 /*
1045 * The month is text, which allows us to find it easily.
1046 */
1047 tm->tm_mon = 12;
1048 for (i = 0; i < 3; i++) {
1049 if (isalpha((uint8_t) *f[i])) {
1050 int j;
1051
1052 /*
1053 * Bubble the month to the front of the list
1054 */
1055 p = f[0];
1056 f[0] = f[i];
1057 f[i] = p;
1058
1059 for (j = 0; j < 12; j++) {
1060 if (strncasecmp(months[j], f[0], 3) == 0) {
1061 tm->tm_mon = j;
1062 break;
1063 }
1064 }
1065 }
1066 }
1067
1068 /* month not found? */
1069 if (tm->tm_mon == 12) {
1070 fr_strerror_const("No month found");
1071 return -1;
1072 }
1073
1074 /*
1075 * Check for invalid text, or invalid trailing text.
1076 */
1077 l = strtoul(f[1], &tail, 10);
1078 if ((l > INT_MAX) || (*tail != '\0')) {
1079 fr_strerror_const("Invalid year string");
1080 return -1;
1081 }
1082 tm->tm_year = (int)l;
1083
1084 l = strtoul(f[2], &tail, 10);
1085 if ((l > INT_MAX) || (*tail != '\0')) {
1086 fr_strerror_const("Invalid day of month string");
1087 return -1;
1088 }
1089 tm->tm_mday = (int)l;
1090
1091 if (tm->tm_year >= 1900) {
1092 tm->tm_year -= 1900;
1093
1094 } else {
1095 /*
1096 * We can't use 2-digit years any more, they make it
1097 * impossible to tell what's the day, and what's the year.
1098 */
1099 if (tm->tm_mday < 1900) {
1100 fr_strerror_const("Invalid year < 1900");
1101 return -1;
1102 }
1103
1104 /*
1105 * Swap the year and the day.
1106 */
1107 i = tm->tm_year;
1108 tm->tm_year = tm->tm_mday - 1900;
1109 tm->tm_mday = i;
1110 }
1111
1112 if (tm->tm_year > 10000) {
1113 fr_strerror_const("Invalid value for year");
1114 return -1;
1115 }
1116
1117 /*
1118 * If the day is out of range, die.
1119 */
1120 if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
1121 fr_strerror_const("Invalid value for day of month");
1122 return -1;
1123 }
1124
1125 /*
1126 * There may be %H:%M:%S. Parse it in a hacky way.
1127 */
1128 if (f[3]) {
1129 f[0] = f[3]; /* HH */
1130 f[1] = strchr(f[0], ':'); /* find : separator */
1131 if (!f[1]) {
1132 fr_strerror_const("No ':' after hour");
1133 return -1;
1134 }
1135
1136 *(f[1]++) = '\0'; /* nuke it, and point to MM:SS */
1137
1138 f[2] = strchr(f[1], ':'); /* find : separator */
1139 if (f[2]) {
1140 *(f[2]++) = '\0'; /* nuke it, and point to SS */
1141 tm->tm_sec = atoi(f[2]);
1142 } /* else leave it as zero */
1143
1144 tm->tm_hour = atoi(f[0]);
1145 tm->tm_min = atoi(f[1]);
1146 }
1147
1148 *date = fr_unix_time_add(fr_unix_time_from_tm(tm), gmt_delta);
1149
1150 return 0;
1151}
1152
1153/** Convert unix time to string
1154 *
1155 * @param[out] out Where to write the string.
1156 * @param[in] time to convert.
1157 * @param[in] res What base resolution to print the time as.
1158 * @param[in] utc If true, use UTC, otherwise local time.
1159 * @return
1160 * - 0 on success.
1161 * - -1 on failure.
1162 */
1164{
1165 fr_sbuff_t our_out = FR_SBUFF(out);
1166 int64_t subseconds;
1167 time_t t;
1168 struct tm s_tm;
1169 size_t len;
1170 char buf[128];
1171
1172 t = fr_unix_time_to_sec(time);
1173 if (utc) {
1174 (void) gmtime_r(&t, &s_tm);
1175 } else {
1176 (void) localtime_r(&t, &s_tm);
1177 }
1178
1179 len = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &s_tm);
1180 FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, buf, len);
1181 subseconds = fr_unix_time_unwrap(time) % NSEC;
1182
1183 /*
1184 * Use RFC 3339 format, which is a
1185 * profile of ISO8601. The ISO standard
1186 * allows a much more complex set of date
1187 * formats. The RFC is much stricter.
1188 */
1189 switch (res) {
1191 case FR_TIME_RES_YEAR:
1192 case FR_TIME_RES_MONTH:
1193 case FR_TIME_RES_WEEK:
1194 case FR_TIME_RES_DAY:
1195 case FR_TIME_RES_HOUR:
1196 case FR_TIME_RES_MIN:
1197 case FR_TIME_RES_SEC:
1198 break;
1199
1200 case FR_TIME_RES_CSEC:
1201 subseconds /= (NSEC / CSEC);
1202 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, ".%02" PRIi64, subseconds);
1203 break;
1204
1205 case FR_TIME_RES_MSEC:
1206 subseconds /= (NSEC / MSEC);
1207 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, ".%03" PRIi64, subseconds);
1208 break;
1209
1210 case FR_TIME_RES_USEC:
1211 subseconds /= (NSEC / USEC);
1212 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, ".%06" PRIi64, subseconds);
1213 break;
1214
1215 case FR_TIME_RES_NSEC:
1216 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, ".%09" PRIi64, subseconds);
1217 break;
1218 }
1219
1220 /*
1221 * And time zone.
1222 */
1223 if (s_tm.tm_gmtoff != 0) {
1224 int hours, minutes;
1225
1226 hours = s_tm.tm_gmtoff / 3600;
1227 minutes = (s_tm.tm_gmtoff / 60) % 60;
1228
1229 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%+03d:%02u", hours, minutes);
1230 } else {
1231 FR_SBUFF_IN_CHAR_RETURN(&our_out, 'Z');
1232 }
1233
1234 FR_SBUFF_SET_RETURN(out, &our_out);
1235}
1236
1237/** Get the offset to gmt.
1238 *
1239 */
1244
1245/** Whether or not we're daylight savings.
1246 *
1247 */
1249{
1250 return isdst;
1251}
static int const char * fmt
Definition acutest.h:638
static uint8_t overflow[100]
#define RCSID(id)
Definition build.h:603
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define DIAG_ON(_x)
Definition build.h:576
#define unlikely(_x)
Definition build.h:486
#define NUM_ELEMENTS(_t)
Definition build.h:437
#define DIAG_OFF(_x)
Definition build.h:575
static const char * tabs
Definition command.c:1589
static size_t min(size_t x, size_t y)
Definition dbuff.c:66
talloc_free(hp)
#define fr_add(_out, _a, _b)
Adds two integers.
Definition math.h:190
unsigned short uint16_t
unsigned int uint32_t
fr_sbuff_err_t
@ FR_SBUFF_ERR_NOT_FOUND
unsigned char uint8_t
ssize_t fr_slen_t
unsigned long int size_t
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:35
struct tm * gmtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:205
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:162
#define fr_assert(_expr)
Definition rad_assert.h:37
static bool done
Definition radclient.c:80
static char const * name
fr_table_num_ordered_t const sbuff_err_table[]
Definition sbuff.c:43
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2311
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2247
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define FR_SBUFF_IN_SPRINTF_RETURN(...)
#define FR_SBUFF(_sbuff_or_marker)
#define FR_SBUFF_IN_BSTRNCPY_RETURN(...)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_out(_out, _in)
#define fr_sbuff_extend_lowat(_status, _sbuff_or_marker, _lowat)
Set of terminal elements.
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition skip.h:36
@ memory_order_release
Definition stdatomic.h:130
#define _Atomic(T)
Definition stdatomic.h:77
#define atomic_store_explicit(object, desired, order)
Definition stdatomic.h:314
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static char * mystrtok(char **ptr, char const *sep)
Definition time.c:758
void fr_time_elapsed_update(fr_time_elapsed_t *elapsed, fr_time_t start, fr_time_t end)
Definition time.c:581
fr_unix_time_t fr_unix_time_from_tm(struct tm *tm)
Definition time.c:662
static char const * tab_string
Definition time.c:624
static const char * names[8]
Definition time.c:618
int fr_time_sync(void)
Get a new fr_time_monotonic_to_realtime value.
Definition time.c:109
static int get_part(char **str, int *date, int min, int max, char term, char const *name)
Definition time.c:780
static long gmtoff[2]
from localtime_r(), tm_gmtoff
Definition time.c:90
fr_table_num_ordered_t const fr_time_precision_table[]
Definition time.c:46
size_t fr_time_strftime_local(fr_sbuff_t *out, fr_time_t time, char const *fmt)
Copy a time string (local timezone) to an sbuff.
Definition time.c:537
#define CHECK(_x, _max)
fr_slen_t fr_time_delta_from_substr(fr_time_delta_t *out, fr_sbuff_t *in, fr_time_res_t hint, bool no_trailing, fr_sbuff_term_t const *tt)
Create fr_time_delta_t from a string.
Definition time.c:221
int fr_time_delta_from_time_zone(char const *tz, fr_time_delta_t *delta)
Return time delta from the time zone.
Definition time.c:183
fr_slen_t fr_time_delta_from_str(fr_time_delta_t *out, char const *in, size_t inlen, fr_time_res_t hint)
Create fr_time_delta_t from a string.
Definition time.c:430
bool fr_time_is_dst(void)
Whether or not we're daylight savings.
Definition time.c:1248
void fr_time_elapsed_fprint(FILE *fp, fr_time_elapsed_t const *elapsed, char const *prefix, int tab_offset)
Definition time.c:626
_Atomic int64_t fr_time_monotonic_to_realtime
difference between the two clocks
Definition time.c:87
int fr_unix_time_from_str(fr_unix_time_t *date, char const *date_str, fr_time_res_t hint)
Convert string in various formats to a fr_unix_time_t.
Definition time.c:828
int64_t fr_time_scale(int64_t t, fr_time_res_t hint)
Scale an input time to NSEC, clamping it at max / min.
Definition time.c:717
#define MOD(a, b)
static char const * months[]
Definition time.c:814
fr_time_delta_t fr_time_gmtoff(void)
Get the offset to gmt.
Definition time.c:1240
fr_slen_t fr_time_delta_to_str(fr_sbuff_t *out, fr_time_delta_t delta, fr_time_res_t res, bool is_unsigned)
Print fr_time_delta_t to a string with an appropriate suffix.
Definition time.c:458
size_t fr_time_precision_table_len
Definition time.c:84
static char const * tz_names[2]
normal, DST, from localtime_r(), tm_zone
Definition time.c:89
int fr_time_start(void)
Initialize the local time.
Definition time.c:157
static bool isdst
from localtime_r(), tm_is_dst
Definition time.c:91
fr_slen_t fr_unix_time_to_str(fr_sbuff_t *out, fr_unix_time_t time, fr_time_res_t res, bool utc)
Convert unix time to string.
Definition time.c:1163
int64_t fr_time_epoch
monotonic clock at boot, i.e. our epoch
Definition time.c:86
size_t fr_time_strftime_utc(fr_sbuff_t *out, fr_time_t time, char const *fmt)
Copy a time string (UTC) to an sbuff.
Definition time.c:566
int64_t const fr_time_multiplier_by_res[]
Definition time.c:32
static fr_time_delta_t fr_time_delta_from_integer(bool *overflow, int64_t integer, fr_time_res_t res)
Definition time.h:557
#define MSEC
Definition time.h:381
static int64_t fr_time_delta_to_integer(fr_time_delta_t delta, fr_time_res_t res)
Definition time.h:636
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
Definition time.h:749
#define fr_time_gteq(_a, _b)
Definition time.h:238
static fr_unix_time_t fr_unix_time_from_nsec(int64_t nsec)
Definition time.h:423
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition time.h:154
#define fr_time_delta_isneg(_a)
Definition time.h:291
#define fr_time_delta_lt(_a, _b)
Definition time.h:285
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:599
static int64_t fr_unix_time_to_sec(fr_unix_time_t delta)
Definition time.h:506
#define fr_time_delta_wrap(_time)
Definition time.h:152
#define fr_unix_time_min()
Definition time.h:159
static fr_unix_time_t fr_unix_time_from_sec(int64_t sec)
Definition time.h:449
fr_time_res_t
The base resolution for print parse operations.
Definition time.h:48
@ FR_TIME_RES_MONTH
Definition time.h:55
@ FR_TIME_RES_MSEC
Definition time.h:58
@ FR_TIME_RES_WEEK
Definition time.h:54
@ FR_TIME_RES_MIN
Definition time.h:51
@ FR_TIME_RES_CSEC
Definition time.h:57
@ FR_TIME_RES_HOUR
Definition time.h:52
@ FR_TIME_RES_YEAR
Definition time.h:56
@ FR_TIME_RES_DAY
Definition time.h:53
@ FR_TIME_RES_NSEC
Definition time.h:60
@ FR_TIME_RES_USEC
Definition time.h:59
@ FR_TIME_RES_SEC
Definition time.h:50
@ FR_TIME_RES_INVALID
Definition time.h:49
#define NSEC
Definition time.h:379
static uint64_t fr_unix_time_unwrap(fr_unix_time_t time)
Definition time.h:161
#define CLOCK_MONOTONIC_RAW
Definition time.h:968
uint64_t array[8]
100ns to 100s
Definition time.h:376
#define USEC
Definition time.h:380
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
#define FR_TIME_DUR_MONTH
Definition time.h:394
#define CSEC
Definition time.h:382
#define fr_unix_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:324
static fr_time_delta_t fr_time_delta_from_timespec(struct timespec const *ts)
Definition time.h:623
#define FR_TIME_DUR_YEAR
Definition time.h:393
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
"Unix" time.
Definition time.h:95
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_sbuff_err_t char size_t fr_sbuff_t * in
Definition value.h:1061
static fr_sbuff_err_t char size_t fr_sbuff_t size_t max
Definition value.h:1061
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061