From da1bae6694c99e24ef65ece502d1c2d7153e519b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= Date: Mon, 28 Sep 2026 14:55:45 +0200 Subject: [PATCH 1/2] cli: parse YANG date-and-time with fromisoformat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The strptime format rejected fractional seconds and non-positive offsets. Signed-off-by: Mattias Walström --- src/statd/python/cli_pretty/cli_pretty.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/statd/python/cli_pretty/cli_pretty.py b/src/statd/python/cli_pretty/cli_pretty.py index 4fa241ade..d43a855d3 100755 --- a/src/statd/python/cli_pretty/cli_pretty.py +++ b/src/statd/python/cli_pretty/cli_pretty.py @@ -665,9 +665,7 @@ def from_yang(cls, ydate): if not ydate: return None - date, tz = ydate.split("+") - tz = tz.replace(":", "") - return cls.strptime(f"{date}+{tz}", "%Y-%m-%dT%H:%M:%S%z") + return cls.fromisoformat(ydate) class Route: def __init__(self, data, ip): From 52f493ecb0dd22520e443edf4d592bc4da6a6296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= Date: Mon, 28 Sep 2026 14:55:45 +0200 Subject: [PATCH 2/2] statd: floor the clock offset once for interface last-change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Truncating the realtime and monotonic clocks separately before subtracting let the same stamp read one second apart on consecutive GETs. Compute the offset from full-precision timespecs and floor it once. Signed-off-by: Mattias Walström --- src/statd/iface.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/statd/iface.c b/src/statd/iface.c index 8c5e55d45..16a6be145 100644 --- a/src/statd/iface.c +++ b/src/statd/iface.c @@ -28,6 +28,16 @@ static time_t monotonic(void) return ts.tv_sec; } +/* CLOCK_REALTIME - CLOCK_MONOTONIC in whole seconds, floored */ +static time_t clock_offset(void) +{ + struct timespec rt, mono; + + clock_gettime(CLOCK_REALTIME, &rt); + clock_gettime(CLOCK_MONOTONIC, &mono); + return rt.tv_sec - mono.tv_sec - (rt.tv_nsec < mono.tv_nsec); +} + static struct iface *iface_find(struct iface_ctx *ctx, const char *name) { struct iface *l; @@ -263,12 +273,13 @@ void iface_ctx_exit(struct iface_ctx *ctx) /* Add last-change to every interface in tree whose state changed since start */ void iface_annotate(struct iface_ctx *ctx, struct lyd_node *tree) { - time_t now = time(NULL), mono = monotonic(); struct lyd_node *iface; + time_t off; if (!tree) return; + off = clock_offset(); LY_LIST_FOR(lyd_child(tree), iface) { struct iface *l; char buf[32]; @@ -277,7 +288,7 @@ void iface_annotate(struct iface_ctx *ctx, struct lyd_node *tree) if (!l || !l->changed) continue; - format_timestamp(now - (mono - l->changed), buf, sizeof(buf)); + format_timestamp(l->changed + off, buf, sizeof(buf)); if (lyd_new_term(iface, NULL, "last-change", buf, 0, NULL)) WARN("Failed adding last-change to interface %s", l->name); }