From 4c5407da266f47a3d1c2af57d8e28e9bfb028794 Mon Sep 17 00:00:00 2001 From: guy oron Date: Mon, 29 Jun 2026 05:43:36 +0300 Subject: [PATCH] fix(tracking): parse RFC3339 timestamps in first_seen_days() Timestamps are stored via Utc::now().to_rfc3339() but first_seen_days() parsed with NaiveDateTime format "%Y-%m-%dT%H:%M:%S" which rejects the fractional seconds and timezone offset, silently falling back to Utc::now() and always returning 0 days. Use DateTime::parse_from_rfc3339() as primary parser (matching line 951 in the same file), keeping NaiveDateTime formats as fallbacks for older databases. Fixes #2710 --- src/core/tracking.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/core/tracking.rs b/src/core/tracking.rs index 359e4f6885..7fa1e64000 100644 --- a/src/core/tracking.rs +++ b/src/core/tracking.rs @@ -1105,9 +1105,15 @@ impl Tracker { }; match oldest { Some(ts) => { - let first = chrono::NaiveDateTime::parse_from_str(&ts, "%Y-%m-%dT%H:%M:%S") - .or_else(|_| chrono::NaiveDateTime::parse_from_str(&ts, "%Y-%m-%d %H:%M:%S")) - .map(|dt| dt.and_utc()) + let first = chrono::DateTime::parse_from_rfc3339(&ts) + .map(|dt| dt.with_timezone(&chrono::Utc)) + .or_else(|_| { + chrono::NaiveDateTime::parse_from_str(&ts, "%Y-%m-%dT%H:%M:%S") + .or_else(|_| { + chrono::NaiveDateTime::parse_from_str(&ts, "%Y-%m-%d %H:%M:%S") + }) + .map(|dt| dt.and_utc()) + }) .unwrap_or_else(|_| chrono::Utc::now()); let days = (chrono::Utc::now() - first).num_days(); Ok(days.max(0)) @@ -1686,4 +1692,21 @@ mod tests { "parse_failures table should be empty after reset" ); } + + #[test] + fn test_first_seen_days_parses_rfc3339() { + let tracker = Tracker::new().expect("Failed to create tracker"); + let test_cmd = format!("rtk first_seen_test_{}", std::process::id()); + tracker + .record("first_seen_cmd", &test_cmd, 100, 20, 10) + .expect("Failed to record"); + + let days = tracker + .first_seen_days() + .expect("first_seen_days should not error"); + assert!( + days >= 0, + "first_seen_days must be non-negative, got {days}" + ); + } }