From 5aab1420b8a4bf8b04d82abeeaf38f4dbf52423b Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Tue, 23 Jun 2026 11:51:20 +0200 Subject: [PATCH] Fix parsing of timestamps with newer lava In the latest version of lava the dt field is created with atetime.datetime.now(datetime.UTC).isoformat() rather then datetime.datetime.utcnow().isoformat(). This causes the timezone specifier to be added (always +00:00 as the logs are UTC)'. From a Rust perspective the former is a `NaiveDateTime` (no timezone) while the later is a `DateTime`. Adjust the parsing to try both, but convert to NaiveDateTime to keep the same user API. In future potentially this could be moved to always be a DateTime to make it more clear to the end-user what the stamp is --- lava-api/src/joblog.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/lava-api/src/joblog.rs b/lava-api/src/joblog.rs index b0957e2..bae7992 100644 --- a/lava-api/src/joblog.rs +++ b/lava-api/src/joblog.rs @@ -5,7 +5,7 @@ use std::task::Poll; use std::time::Duration; use bytes::{Bytes, BytesMut}; -use chrono::NaiveDateTime; +use chrono::{DateTime, NaiveDateTime, Utc}; use futures::future::BoxFuture; use futures::stream::BoxStream; use futures::{prelude::*, ready}; @@ -218,8 +218,25 @@ pub enum JobLogLevel { Exception, } +fn job_deserialize_dt<'de, D>(d: D) -> Result +where + D: Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum DorN { + D(DateTime), + N(NaiveDateTime), + } + match DorN::deserialize(d)? { + DorN::D(d) => Ok(d.naive_utc()), + DorN::N(n) => Ok(n), + } +} + #[derive(Debug, Clone, Deserialize)] pub struct JobLogEntry { + #[serde(deserialize_with = "job_deserialize_dt")] pub dt: NaiveDateTime, pub lvl: JobLogLevel, pub ns: Option, @@ -302,3 +319,21 @@ impl Stream for JobLog<'_> { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn job_log_entry_deserialisation() { + let entry0: JobLogEntry = serde_json::from_str( + r#"{"dt": "2026-06-02T12:16:27.730458", "lvl": "results", "msg": {"definition": "lava", "case": "job", "result": "pass"}}"#, + ) + .unwrap(); + let entry1: JobLogEntry = serde_json::from_str( + r#"{"dt": "2026-06-02T12:16:27.730458+00:00", "lvl": "results", "msg": {"definition": "lava", "case": "job", "result": "pass"}}"#, + ) + .unwrap(); + assert_eq!(entry0.dt, entry1.dt); + } +}