hyperhive/hive-sh4re/src/wire_time.rs

74 lines
2.5 KiB
Rust

//! Timestamp conventions for the wire types: fields are
//! `chrono::DateTime<Utc>` (serde serializes them as RFC 3339 UTC `Z`
//! strings, e.g. `2026-07-02T18:30:00Z`), while sqlite storage and
//! agent-facing *input* args stay unix-epoch seconds (`i64`). This
//! module owns the two conversions at those boundaries.
use chrono::{DateTime, Utc};
/// Convert unix-epoch seconds (the sqlite column / input-arg form)
/// into the wire timestamp type. Out-of-range values (never produced
/// by our clocks) clamp to the epoch rather than erroring — the db
/// read path must not fail on a weird row.
#[must_use]
pub fn from_secs(secs: i64) -> DateTime<Utc> {
DateTime::<Utc>::from_timestamp(secs, 0).unwrap_or_default()
}
// `now_unix()` (a hand-rolled-`SystemTime`-math-turned-`chrono` helper
// that every store used to call for its `created_at` / `sent_at` / …
// stamps) has been removed — every call site now uses
// `chrono::Utc::now()` directly, either as a `DateTime<Utc>` struct
// field or via `.timestamp()` for the ephemeral i64-typed (sqlite
// bind / cutoff arithmetic) call sites. hivectl's own separate
// `now_unix()` copy in `dag_progress.rs` is unrelated and untouched.
#[cfg(test)]
mod tests {
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use super::from_secs;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Row {
at: DateTime<Utc>,
#[serde(default, skip_serializing_if = "Option::is_none")]
maybe_at: Option<DateTime<Utc>>,
}
#[test]
fn serializes_as_rfc3339_z() {
let json = serde_json::to_string(&Row {
at: from_secs(1_751_480_000),
maybe_at: None,
})
.unwrap();
assert_eq!(json, r#"{"at":"2025-07-02T18:13:20Z"}"#);
}
#[test]
fn round_trips_and_serializes_some() {
let row = Row {
at: from_secs(0),
maybe_at: Some(from_secs(1_751_480_000)),
};
let json = serde_json::to_string(&row).unwrap();
assert_eq!(
json,
r#"{"at":"1970-01-01T00:00:00Z","maybe_at":"2025-07-02T18:13:20Z"}"#
);
assert_eq!(serde_json::from_str::<Row>(&json).unwrap(), row);
}
#[test]
fn deserializes_offset_form_normalized_to_utc() {
let row: Row = serde_json::from_str(r#"{"at":"2025-07-02T20:13:20+02:00"}"#).unwrap();
assert_eq!(row.at, from_secs(1_751_480_000));
}
#[test]
fn from_secs_clamps_out_of_range_to_epoch() {
assert_eq!(from_secs(i64::MAX), from_secs(0));
}
}