mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-02 15:34:30 +00:00
lib: add time formatting printfrr exts
Refer to docs in doc/developer for details. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
parent
2c5b4d80ef
commit
2c76ba433f
@ -219,6 +219,122 @@ Networking data types
|
||||
|
||||
:frrfmtout:`SOCK_STREAM`
|
||||
|
||||
Time/interval formats
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. frrfmt:: %pTS (struct timespec *)
|
||||
|
||||
.. frrfmt:: %pTV (struct timeval *)
|
||||
|
||||
.. frrfmt:: %pTT (time_t *)
|
||||
|
||||
Above 3 options internally result in the same code being called, support
|
||||
the same flags and produce equal output with one exception: ``%pTT``
|
||||
has no sub-second precision and the formatter will never print a
|
||||
(nonsensical) ``.000``.
|
||||
|
||||
Exactly one of ``I``, ``M`` or ``R`` must immediately follow after
|
||||
``TS``/``TV``/``TT`` to specify whether the input is an interval, monotonic
|
||||
timestamp or realtime timestamp:
|
||||
|
||||
``%pTVI``: input is an interval, not a timestamp. Print interval.
|
||||
|
||||
``%pTVIs``: input is an interval, convert to wallclock by subtracting it
|
||||
from current time (i.e. interval has passed **s**\ ince.)
|
||||
|
||||
``%pTVIu``: input is an interval, convert to wallclock by adding it to
|
||||
current time (i.e. **u**\ ntil interval has passed.)
|
||||
|
||||
``%pTVM`` - input is a timestamp on CLOCK_MONOTONIC, convert to wallclock
|
||||
time (by grabbing current CLOCK_MONOTONIC and CLOCK_REALTIME and doing the
|
||||
math) and print calendaric date.
|
||||
|
||||
``%pTVMs`` - input is a timestamp on CLOCK_MONOTONIC, print interval
|
||||
**s**\ ince that timestamp (elapsed.)
|
||||
|
||||
``%pTVMu`` - input is a timestamp on CLOCK_MONOTONIC, print interval
|
||||
**u**\ ntil that timestamp (deadline.)
|
||||
|
||||
``%pTVR`` - input is a timestamp on CLOCK_REALTIME, print calendaric date.
|
||||
|
||||
``%pTVRs`` - input is a timestamp on CLOCK_REALTIME, print interval
|
||||
**s**\ ince that timestamp.
|
||||
|
||||
``%pTVRu`` - input is a timestamp on CLOCK_REALTIME, print interval
|
||||
**u**\ ntil that timestamp.
|
||||
|
||||
``%pTVA`` - reserved for CLOCK_TAI in case a PTP implementation is
|
||||
interfaced to FRR. Not currently implemented.
|
||||
|
||||
.. note::
|
||||
|
||||
If ``%pTVRs`` or ``%pTVRu`` are used, this is generally an indication
|
||||
that a CLOCK_MONOTONIC timestamp should be used instead (or added in
|
||||
parallel.) CLOCK_REALTIME might be adjusted by NTP, PTP or similar
|
||||
procedures, causing bogus intervals to be printed.
|
||||
|
||||
``%pTVM`` on first look might be assumed to have the same problem, but
|
||||
on closer thought the assumption is always that current system time is
|
||||
correct. And since a CLOCK_MONOTONIC interval is also quite safe to
|
||||
assume to be correct, the (past) absolute timestamp to be printed from
|
||||
this can likely be correct even if it doesn't match what CLOCK_REALTIME
|
||||
would have indicated at that point in the past. This logic does,
|
||||
however, not quite work for *future* times.
|
||||
|
||||
Generally speaking, almost all use cases in FRR should (and do) use
|
||||
CLOCK_MONOTONIC (through :c:func:`monotime()`.)
|
||||
|
||||
Flags common to printing calendar times and intervals:
|
||||
|
||||
``p``: include spaces in appropriate places (depends on selected format.)
|
||||
|
||||
``%p.3TV...``: specify sub-second resolution (use with ``FMT_NSTD`` to
|
||||
suppress gcc warning.) As noted above, ``%pTT`` will never print sub-second
|
||||
digits since there are none. Only some formats support printing sub-second
|
||||
digits and the default may vary.
|
||||
|
||||
The following flags are available for printing calendar times/dates:
|
||||
|
||||
(no flag): :frrfmtout:`Sat Jan 1 00:00:00 2022` - print output from
|
||||
``ctime()``, in local time zone. Since FRR does not currently use/enable
|
||||
locale support, this is always the C locale. (Locale support getting added
|
||||
is unlikely for the time being and would likely break other things worse
|
||||
than this.)
|
||||
|
||||
``i``: :frrfmtout:`2022-01-01T00:00:00.123` - ISO8601 timestamp in local
|
||||
time zone (note there is no ``Z`` or ``+00:00`` suffix.) Defaults to
|
||||
millisecond precision.
|
||||
|
||||
``ip``: :frrfmtout:`2022-01-01 00:00:00.123` - use readable form of ISO8601
|
||||
with space instead of ``T`` separator.
|
||||
|
||||
The following flags are available for printing intervals:
|
||||
|
||||
(no flag): :frrfmtout:`9w9d09:09:09.123` - does not match any
|
||||
preexisting format; added because it does not lose precision (like ``t``)
|
||||
for longer intervals without printing huge numbers (like ``h``/``m``).
|
||||
Defaults to millisecond precision. The week/day fields are left off if
|
||||
they're zero, ``p`` adds a space after the respective letter.
|
||||
|
||||
``t``: :frrfmtout:`9w9d09h`, :frrfmtout:`9d09h09m`, :frrfmtout:`09:09:09` -
|
||||
this replaces :c:func:`frrtime_to_interval()`. ``p`` adds spaces after
|
||||
week/day/hour letters.
|
||||
|
||||
``d``: print decimal number of seconds. Defaults to millisecond precision.
|
||||
|
||||
``x`` / ``tx`` / ``dx``: Like no flag / ``t`` / ``d``, but print
|
||||
:frrfmtout:`-` for zero or negative intervals (for use with unset timers.)
|
||||
|
||||
``h``: :frrfmtout:`09:09:09`
|
||||
|
||||
``hx``: :frrfmtout:`09:09:09`, :frrfmtout:`--:--:--` - this replaces
|
||||
:c:func:`pim_time_timer_to_hhmmss()`.
|
||||
|
||||
``m``: :frrfmtout:`09:09`
|
||||
|
||||
``mx``: :frrfmtout:`09:09`, :frrfmtout:`--:--` - this replaces
|
||||
:c:func:`pim_time_timer_to_mmss()`.
|
||||
|
||||
General utility formats
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -25,6 +25,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct fbuf;
|
||||
struct printfrr_eargs;
|
||||
|
||||
#ifndef TIMESPEC_TO_TIMEVAL
|
||||
/* should be in sys/time.h on BSD & Linux libcs */
|
||||
#define TIMESPEC_TO_TIMEVAL(tv, ts) \
|
||||
@ -42,6 +45,31 @@ extern "C" {
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* Linux/glibc is sadly missing these timespec helpers */
|
||||
#ifndef timespecadd
|
||||
#define timespecadd(tsp, usp, vsp) \
|
||||
do { \
|
||||
(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
|
||||
(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
|
||||
if ((vsp)->tv_nsec >= 1000000000L) { \
|
||||
(vsp)->tv_sec++; \
|
||||
(vsp)->tv_nsec -= 1000000000L; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef timespecsub
|
||||
#define timespecsub(tsp, usp, vsp) \
|
||||
do { \
|
||||
(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
|
||||
(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
|
||||
if ((vsp)->tv_nsec < 0) { \
|
||||
(vsp)->tv_sec--; \
|
||||
(vsp)->tv_nsec += 1000000000L; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static inline time_t monotime(struct timeval *tvo)
|
||||
{
|
||||
struct timespec ts;
|
||||
@ -132,6 +160,53 @@ static inline const char *frrtime_to_interval(time_t t, char *buf,
|
||||
return buf;
|
||||
}
|
||||
|
||||
enum {
|
||||
/* n/a - input was seconds precision, don't print any fractional */
|
||||
TIMEFMT_SECONDS = (1 << 0),
|
||||
/* caller is directly invoking printfrr_time and has pre-specified
|
||||
* I/Iu/Is/M/Mu/Ms/R/Ru/Rs (for printing timers)
|
||||
*/
|
||||
TIMEFMT_PRESELECT = (1 << 1),
|
||||
/* don't print any output - this is needed for invoking printfrr_time
|
||||
* from another printfrr extensions to skip over flag characters
|
||||
*/
|
||||
TIMEFMT_SKIP = (1 << 2),
|
||||
/* use spaces in appropriate places */
|
||||
TIMEFMT_SPACE = (1 << 3),
|
||||
|
||||
/* input interpretations: */
|
||||
TIMEFMT_REALTIME = (1 << 8),
|
||||
TIMEFMT_MONOTONIC = (1 << 9),
|
||||
TIMEFMT_SINCE = (1 << 10),
|
||||
TIMEFMT_UNTIL = (1 << 11),
|
||||
|
||||
TIMEFMT_ABSOLUTE = TIMEFMT_REALTIME | TIMEFMT_MONOTONIC,
|
||||
TIMEFMT_ANCHORS = TIMEFMT_SINCE | TIMEFMT_UNTIL,
|
||||
|
||||
/* calendaric formats: */
|
||||
TIMEFMT_ISO8601 = (1 << 16),
|
||||
|
||||
/* interval formats: */
|
||||
/* 't' - use [t]raditional 3-block format */
|
||||
TIMEFMT_BASIC = (1 << 24),
|
||||
/* 'm' - select mm:ss */
|
||||
TIMEFMT_MMSS = (1 << 25),
|
||||
/* 'h' - select hh:mm:ss */
|
||||
TIMEFMT_HHMMSS = (1 << 26),
|
||||
/* 'd' - print as decimal number of seconds */
|
||||
TIMEFMT_DECIMAL = (1 << 27),
|
||||
/* 'mx'/'hx' - replace zero value with "--:--" or "--:--:--" */
|
||||
TIMEFMT_DASHES = (1 << 31),
|
||||
|
||||
/* helpers for reference */
|
||||
TIMEFMT_TIMER_DEADLINE =
|
||||
TIMEFMT_PRESELECT | TIMEFMT_MONOTONIC | TIMEFMT_UNTIL,
|
||||
TIMEFMT_TIMER_INTERVAL = TIMEFMT_PRESELECT,
|
||||
};
|
||||
|
||||
extern ssize_t printfrr_time(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
const struct timespec *ts, unsigned int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -286,6 +286,10 @@ struct va_format {
|
||||
|
||||
#pragma FRR printfrr_ext "%pSE" (char *)
|
||||
#pragma FRR printfrr_ext "%pSQ" (char *)
|
||||
|
||||
#pragma FRR printfrr_ext "%pTS" (struct timespec *)
|
||||
#pragma FRR printfrr_ext "%pTV" (struct timeval *)
|
||||
#pragma FRR printfrr_ext "%pTT" (time_t *)
|
||||
#endif
|
||||
|
||||
/* when using non-ISO-C compatible extension specifiers... */
|
||||
|
325
lib/strformat.c
325
lib/strformat.c
@ -22,8 +22,10 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "printfrr.h"
|
||||
#include "monotime.h"
|
||||
|
||||
printfrr_ext_autoreg_p("HX", printfrr_hexdump)
|
||||
static ssize_t printfrr_hexdump(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
@ -270,3 +272,326 @@ static ssize_t printfrr_quote(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
ret += bputch(buf, '"');
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t printfrr_abstime(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
const struct timespec *ts, unsigned int flags);
|
||||
static ssize_t printfrr_reltime(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
const struct timespec *ts, unsigned int flags);
|
||||
|
||||
ssize_t printfrr_time(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
const struct timespec *ts, unsigned int flags)
|
||||
{
|
||||
bool have_abs, have_anchor;
|
||||
|
||||
if (!(flags & TIMEFMT_PRESELECT)) {
|
||||
switch (ea->fmt[0]) {
|
||||
case 'I':
|
||||
/* no bit set */
|
||||
break;
|
||||
case 'M':
|
||||
flags |= TIMEFMT_MONOTONIC;
|
||||
break;
|
||||
case 'R':
|
||||
flags |= TIMEFMT_REALTIME;
|
||||
break;
|
||||
default:
|
||||
return bputs(buf,
|
||||
"{invalid time format input specifier}");
|
||||
}
|
||||
ea->fmt++;
|
||||
|
||||
if (ea->fmt[0] == 's') {
|
||||
flags |= TIMEFMT_SINCE;
|
||||
ea->fmt++;
|
||||
} else if (ea->fmt[0] == 'u') {
|
||||
flags |= TIMEFMT_UNTIL;
|
||||
ea->fmt++;
|
||||
}
|
||||
}
|
||||
|
||||
have_abs = !!(flags & TIMEFMT_ABSOLUTE);
|
||||
have_anchor = !!(flags & TIMEFMT_ANCHORS);
|
||||
|
||||
if (have_abs ^ have_anchor)
|
||||
return printfrr_abstime(buf, ea, ts, flags);
|
||||
else
|
||||
return printfrr_reltime(buf, ea, ts, flags);
|
||||
}
|
||||
|
||||
static ssize_t do_subsec(struct fbuf *buf, const struct timespec *ts,
|
||||
int precision, unsigned int flags)
|
||||
{
|
||||
unsigned long long frac;
|
||||
|
||||
if (precision <= 0 || (flags & TIMEFMT_SECONDS))
|
||||
return 0;
|
||||
|
||||
frac = ts->tv_nsec;
|
||||
if (precision > 9)
|
||||
precision = 9;
|
||||
for (int i = precision; i < 9; i++)
|
||||
frac /= 10;
|
||||
return bprintfrr(buf, ".%0*llu", precision, frac);
|
||||
}
|
||||
|
||||
static ssize_t printfrr_abstime(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
const struct timespec *ts, unsigned int flags)
|
||||
{
|
||||
struct timespec real_ts[1];
|
||||
struct tm tm;
|
||||
char cbuf[32] = ""; /* manpage says 26 for ctime_r */
|
||||
ssize_t ret = 0;
|
||||
int precision = ea->precision;
|
||||
|
||||
while (ea->fmt[0]) {
|
||||
char ch = *ea->fmt++;
|
||||
|
||||
switch (ch) {
|
||||
case 'p':
|
||||
flags |= TIMEFMT_SPACE;
|
||||
continue;
|
||||
case 'i':
|
||||
flags |= TIMEFMT_ISO8601;
|
||||
continue;
|
||||
}
|
||||
|
||||
ea->fmt--;
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & TIMEFMT_SKIP)
|
||||
return 0;
|
||||
|
||||
if (flags & TIMEFMT_REALTIME)
|
||||
*real_ts = *ts;
|
||||
else if (flags & TIMEFMT_MONOTONIC) {
|
||||
struct timespec mono_now[1];
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, real_ts);
|
||||
clock_gettime(CLOCK_MONOTONIC, mono_now);
|
||||
|
||||
timespecsub(real_ts, mono_now, real_ts);
|
||||
timespecadd(real_ts, ts, real_ts);
|
||||
} else {
|
||||
clock_gettime(CLOCK_REALTIME, real_ts);
|
||||
|
||||
if (flags & TIMEFMT_SINCE)
|
||||
timespecsub(real_ts, ts, real_ts);
|
||||
else /* flags & TIMEFMT_UNTIL */
|
||||
timespecadd(real_ts, ts, real_ts);
|
||||
}
|
||||
|
||||
localtime_r(&real_ts->tv_sec, &tm);
|
||||
|
||||
if (flags & TIMEFMT_ISO8601) {
|
||||
if (flags & TIMEFMT_SPACE)
|
||||
strftime(cbuf, sizeof(cbuf), "%Y-%m-%d %H:%M:%S", &tm);
|
||||
else
|
||||
strftime(cbuf, sizeof(cbuf), "%Y-%m-%dT%H:%M:%S", &tm);
|
||||
ret += bputs(buf, cbuf);
|
||||
|
||||
if (precision == -1)
|
||||
precision = 3;
|
||||
ret += do_subsec(buf, real_ts, precision, flags);
|
||||
} else {
|
||||
size_t len;
|
||||
|
||||
asctime_r(&tm, cbuf);
|
||||
|
||||
len = strlen(cbuf);
|
||||
if (!len)
|
||||
/* WTF. */
|
||||
return 0;
|
||||
if (cbuf[len - 1] == '\n')
|
||||
cbuf[len - 1] = '\0';
|
||||
|
||||
ret += bputs(buf, cbuf);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t printfrr_reltime(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
const struct timespec *ts, unsigned int flags)
|
||||
{
|
||||
struct timespec real_ts[1];
|
||||
ssize_t ret = 0;
|
||||
const char *space = "";
|
||||
const char *dashes = "-";
|
||||
int precision = ea->precision;
|
||||
|
||||
while (ea->fmt[0]) {
|
||||
char ch = *ea->fmt++;
|
||||
|
||||
switch (ch) {
|
||||
case 'p':
|
||||
flags |= TIMEFMT_SPACE;
|
||||
space = " ";
|
||||
continue;
|
||||
case 't':
|
||||
flags |= TIMEFMT_BASIC;
|
||||
continue;
|
||||
case 'd':
|
||||
flags |= TIMEFMT_DECIMAL;
|
||||
continue;
|
||||
case 'm':
|
||||
flags |= TIMEFMT_MMSS;
|
||||
dashes = "--:--";
|
||||
continue;
|
||||
case 'h':
|
||||
flags |= TIMEFMT_HHMMSS;
|
||||
dashes = "--:--:--";
|
||||
continue;
|
||||
case 'x':
|
||||
flags |= TIMEFMT_DASHES;
|
||||
continue;
|
||||
}
|
||||
|
||||
ea->fmt--;
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & TIMEFMT_SKIP)
|
||||
return 0;
|
||||
|
||||
if (flags & TIMEFMT_ABSOLUTE) {
|
||||
struct timespec anchor[1];
|
||||
|
||||
if (flags & TIMEFMT_REALTIME)
|
||||
clock_gettime(CLOCK_REALTIME, anchor);
|
||||
else
|
||||
clock_gettime(CLOCK_MONOTONIC, anchor);
|
||||
if (flags & TIMEFMT_UNTIL)
|
||||
timespecsub(ts, anchor, real_ts);
|
||||
else /* flags & TIMEFMT_SINCE */
|
||||
timespecsub(anchor, ts, real_ts);
|
||||
} else
|
||||
*real_ts = *ts;
|
||||
|
||||
if (real_ts->tv_sec == 0 && real_ts->tv_nsec == 0 &&
|
||||
(flags & TIMEFMT_DASHES))
|
||||
return bputs(buf, dashes);
|
||||
|
||||
if (real_ts->tv_sec < 0) {
|
||||
if (flags & TIMEFMT_DASHES)
|
||||
return bputs(buf, dashes);
|
||||
|
||||
/* -0.3s is { -1s + 700ms } */
|
||||
real_ts->tv_sec = -real_ts->tv_sec - 1;
|
||||
real_ts->tv_nsec = 1000000000L - real_ts->tv_nsec;
|
||||
if (real_ts->tv_nsec >= 1000000000L) {
|
||||
real_ts->tv_sec++;
|
||||
real_ts->tv_nsec -= 1000000000L;
|
||||
}
|
||||
|
||||
/* all formats have a - make sense in front */
|
||||
ret += bputch(buf, '-');
|
||||
}
|
||||
|
||||
if (flags & TIMEFMT_DECIMAL) {
|
||||
ret += bprintfrr(buf, "%lld", (long long)real_ts->tv_sec);
|
||||
if (precision == -1)
|
||||
precision = 3;
|
||||
ret += do_subsec(buf, real_ts, precision, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* these divisions may be slow on embedded boxes, hence only do the
|
||||
* ones we need, plus the ?: zero check to hopefully skip zeros fast
|
||||
*/
|
||||
lldiv_t min_sec = lldiv(real_ts->tv_sec, 60);
|
||||
|
||||
if (flags & TIMEFMT_MMSS) {
|
||||
ret += bprintfrr(buf, "%02lld:%02lld", min_sec.quot,
|
||||
min_sec.rem);
|
||||
ret += do_subsec(buf, real_ts, precision, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
lldiv_t hour_min = min_sec.quot ? lldiv(min_sec.quot, 60) : (lldiv_t){};
|
||||
|
||||
if (flags & TIMEFMT_HHMMSS) {
|
||||
ret += bprintfrr(buf, "%02lld:%02lld:%02lld", hour_min.quot,
|
||||
hour_min.rem, min_sec.rem);
|
||||
ret += do_subsec(buf, real_ts, precision, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
lldiv_t day_hour =
|
||||
hour_min.quot ? lldiv(hour_min.quot, 24) : (lldiv_t){};
|
||||
lldiv_t week_day =
|
||||
day_hour.quot ? lldiv(day_hour.quot, 7) : (lldiv_t){};
|
||||
|
||||
/* if sub-second precision is not supported, return */
|
||||
if (flags & TIMEFMT_BASIC) {
|
||||
/* match frrtime_to_interval (without space flag) */
|
||||
if (week_day.quot)
|
||||
ret += bprintfrr(buf, "%lldw%s%lldd%s%02lldh",
|
||||
week_day.quot, space, week_day.rem,
|
||||
space, day_hour.rem);
|
||||
else if (day_hour.quot)
|
||||
ret += bprintfrr(buf, "%lldd%s%02lldh%s%02lldm",
|
||||
day_hour.quot, space, day_hour.rem,
|
||||
space, hour_min.rem);
|
||||
else
|
||||
ret += bprintfrr(buf, "%02lld:%02lld:%02lld",
|
||||
hour_min.quot, hour_min.rem,
|
||||
min_sec.rem);
|
||||
/* no sub-seconds here */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* default format */
|
||||
if (week_day.quot)
|
||||
ret += bprintfrr(buf, "%lldw%s", week_day.quot, space);
|
||||
if (week_day.rem || week_day.quot)
|
||||
ret += bprintfrr(buf, "%lldd%s", week_day.rem, space);
|
||||
|
||||
ret += bprintfrr(buf, "%02lld:%02lld:%02lld", day_hour.rem,
|
||||
hour_min.rem, min_sec.rem);
|
||||
|
||||
if (precision == -1)
|
||||
precision = 3;
|
||||
ret += do_subsec(buf, real_ts, precision, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("TS", printfrr_ts)
|
||||
static ssize_t printfrr_ts(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
const void *vptr)
|
||||
{
|
||||
const struct timespec *ts = vptr;
|
||||
|
||||
if (!ts)
|
||||
return bputs(buf, "(null)");
|
||||
return printfrr_time(buf, ea, ts, 0);
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("TV", printfrr_tv)
|
||||
static ssize_t printfrr_tv(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
const void *vptr)
|
||||
{
|
||||
const struct timeval *tv = vptr;
|
||||
struct timespec ts;
|
||||
|
||||
if (!tv)
|
||||
return bputs(buf, "(null)");
|
||||
|
||||
ts.tv_sec = tv->tv_sec;
|
||||
ts.tv_nsec = tv->tv_usec * 1000;
|
||||
return printfrr_time(buf, ea, &ts, 0);
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("TT", printfrr_tt)
|
||||
static ssize_t printfrr_tt(struct fbuf *buf, struct printfrr_eargs *ea,
|
||||
const void *vptr)
|
||||
{
|
||||
const time_t *tt = vptr;
|
||||
struct timespec ts;
|
||||
|
||||
if (!tt)
|
||||
return bputs(buf, "(null)");
|
||||
|
||||
ts.tv_sec = *tt;
|
||||
ts.tv_nsec = 0;
|
||||
return printfrr_time(buf, ea, &ts, TIMEFMT_SECONDS);
|
||||
}
|
||||
|
@ -278,5 +278,115 @@ int main(int argc, char **argv)
|
||||
inet_pton(AF_INET6, "fe2c::34", &nh.gate.ipv6);
|
||||
printchk("fe2c::34", "%pNHcg", &nh);
|
||||
|
||||
/* time printing */
|
||||
|
||||
/* need a non-UTC timezone for testing */
|
||||
setenv("TZ", "TEST-01:00", 1);
|
||||
tzset();
|
||||
|
||||
struct timespec ts;
|
||||
struct timeval tv;
|
||||
time_t tt;
|
||||
|
||||
ts.tv_sec = tv.tv_sec = tt = 1642015880;
|
||||
ts.tv_nsec = 123456789;
|
||||
tv.tv_usec = 234567;
|
||||
|
||||
printchk("Wed Jan 12 20:31:20 2022", "%pTSR", &ts);
|
||||
printchk("Wed Jan 12 20:31:20 2022", "%pTVR", &tv);
|
||||
printchk("Wed Jan 12 20:31:20 2022", "%pTTR", &tt);
|
||||
|
||||
FMT_NSTD(printchk("Wed Jan 12 20:31:20 2022", "%.3pTSR", &ts));
|
||||
|
||||
printchk("2022-01-12T20:31:20.123", "%pTSRi", &ts);
|
||||
printchk("2022-01-12 20:31:20.123", "%pTSRip", &ts);
|
||||
printchk("2022-01-12 20:31:20.123", "%pTSRpi", &ts);
|
||||
FMT_NSTD(printchk("2022-01-12T20:31:20", "%.0pTSRi", &ts));
|
||||
FMT_NSTD(printchk("2022-01-12T20:31:20.123456789", "%.9pTSRi", &ts));
|
||||
FMT_NSTD(printchk("2022-01-12T20:31:20", "%.3pTTRi", &tt));
|
||||
|
||||
ts.tv_sec = tv.tv_sec = tt = 9 * 86400 + 12345;
|
||||
|
||||
printchk("1w 2d 03:25:45.123", "%pTSIp", &ts);
|
||||
printchk("1w2d03:25:45.123", "%pTSI", &ts);
|
||||
printchk("1w2d03:25:45.234", "%pTVI", &tv);
|
||||
printchk("1w2d03:25:45", "%pTTI", &tt);
|
||||
|
||||
printchk("1w 2d 03h", "%pTVItp", &tv);
|
||||
printchk("1w2d03h", "%pTSIt", &ts);
|
||||
|
||||
printchk("219:25:45", "%pTVIh", &tv);
|
||||
printchk("13165:45", "%pTVIm", &tv);
|
||||
|
||||
ts.tv_sec = tv.tv_sec = tt = 1 * 86400 + 12345;
|
||||
|
||||
printchk("1d 03:25:45.123", "%pTSIp", &ts);
|
||||
printchk("1d03:25:45.234", "%pTVI", &tv);
|
||||
|
||||
printchk("1d 03h 25m", "%pTVItp", &tv);
|
||||
printchk("1d03h25m", "%pTSIt", &ts);
|
||||
|
||||
printchk("98745.234", "%pTVId", &tv);
|
||||
|
||||
printchk("27:25:45", "%pTVIh", &tv);
|
||||
printchk("1645:45", "%pTVIm", &tv);
|
||||
|
||||
ts.tv_sec = tv.tv_sec = tt = 12345;
|
||||
|
||||
printchk("03:25:45.123", "%pTSIp", &ts);
|
||||
printchk("03:25:45.123", "%pTSI", &ts);
|
||||
printchk("03:25:45.234", "%pTVI", &tv);
|
||||
printchk("03:25:45", "%pTTI", &tt);
|
||||
|
||||
printchk("03:25:45", "%pTSItp", &ts);
|
||||
printchk("03:25:45", "%pTVIt", &tv);
|
||||
|
||||
printchk("12345.234", "%pTVId", &tv);
|
||||
|
||||
printchk("03:25:45", "%pTVIh", &tv);
|
||||
printchk("205:45", "%pTVIm", &tv);
|
||||
|
||||
ts.tv_sec = tv.tv_sec = tt = 0;
|
||||
|
||||
printchk("00:00:00.123", "%pTSIp", &ts);
|
||||
printchk("00:00:00.123", "%pTSI", &ts);
|
||||
printchk("00:00:00.234", "%pTVI", &tv);
|
||||
printchk("00:00:00", "%pTTI", &tt);
|
||||
|
||||
printchk("00:00:00", "%pTVItp", &tv);
|
||||
printchk("00:00:00", "%pTSIt", &ts);
|
||||
|
||||
printchk("0.234", "%pTVId", &tv);
|
||||
printchk("0.234", "%pTVIdx", &tv);
|
||||
printchk("-", "%pTTIdx", &tt);
|
||||
|
||||
printchk("00:00:00", "%pTVIhx", &tv);
|
||||
printchk("--:--:--", "%pTTIhx", &tt);
|
||||
printchk("00:00", "%pTVImx", &tv);
|
||||
printchk("--:--", "%pTTImx", &tt);
|
||||
|
||||
ts.tv_sec = tv.tv_sec = tt = -10;
|
||||
|
||||
printchk("-00:00:09.876", "%pTSIp", &ts);
|
||||
printchk("-00:00:09.876", "%pTSI", &ts);
|
||||
printchk("-00:00:09.765", "%pTVI", &tv);
|
||||
printchk("-00:00:10", "%pTTI", &tt);
|
||||
|
||||
printchk("-00:00:09", "%pTSItp", &ts);
|
||||
printchk("-00:00:09", "%pTSIt", &ts);
|
||||
printchk("-00:00:09", "%pTVIt", &tv);
|
||||
printchk("-00:00:10", "%pTTIt", &tt);
|
||||
|
||||
printchk("-9.765", "%pTVId", &tv);
|
||||
printchk("-", "%pTVIdx", &tv);
|
||||
|
||||
printchk("-00:00:09", "%pTSIh", &ts);
|
||||
printchk("--:--:--", "%pTVIhx", &tv);
|
||||
printchk("--:--:--", "%pTTIhx", &tt);
|
||||
|
||||
printchk("-00:09", "%pTSIm", &ts);
|
||||
printchk("--:--", "%pTVImx", &tv);
|
||||
printchk("--:--", "%pTTImx", &tt);
|
||||
|
||||
return !!errors;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user