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