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