pub fn epoch_seconds(ts: &str) -> Option<i64>Expand description
The inverse of rfc3339: 2026-08-29T10:40:00Z back to Unix seconds.
It lives beside the formatter because the two are one thing, and a repository that had
the one without the other grew a second parser every time somebody needed to compare two
recorded instants. Strict about the shape it accepts — exactly YYYY-MM-DDTHH:MM:SSZ,
which is the only shape this tool writes — because a lenient parser that guessed at a
half-recognised string would answer None for a malformed timestamp and a plausible
number for a different one, and only the first of those is safe.
None is the honest answer for anything else, and the caller’s job is to report it as
unjudgeable rather than as fresh: an episode whose evidence cannot be read as a time is
one a recovery sweep must skip and count, never one it may sweep.
use majordomus_cli::peers::{epoch_seconds, rfc3339};
use std::time::{Duration, UNIX_EPOCH};
assert_eq!(epoch_seconds("1970-01-01T00:00:00Z"), Some(0));
assert_eq!(epoch_seconds("2026-08-29T10:40:00Z"), Some(1_788_000_000));
assert_eq!(epoch_seconds("not a timestamp"), None);
assert_eq!(epoch_seconds("2026-08-29 10:40:00Z"), None, "the T is not optional");
assert_eq!(epoch_seconds("2026-08-29T10:40:00+02:00"), None, "UTC or nothing");
// and it round-trips with the formatter it is the inverse of
for secs in [0u64, 1, 1_788_000_000, 2_000_000_000] {
let text = rfc3339(UNIX_EPOCH + Duration::from_secs(secs));
assert_eq!(epoch_seconds(&text), Some(secs as i64), "{text}");
}