use chrono::{DateTime, SecondsFormat, Utc};
pub fn now_utc_seconds() -> String {
Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true)
}
pub fn canonicalize(value: &str) -> Option<String> {
DateTime::parse_from_rfc3339(value).ok().map(|dt| {
dt.with_timezone(&Utc)
.to_rfc3339_opts(SecondsFormat::Secs, true)
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn canonicalizes_positive_offset_to_utc_z() {
assert_eq!(
canonicalize("2026-05-28T18:00:00+07:00").as_deref(),
Some("2026-05-28T11:00:00Z")
);
}
#[test]
fn canonicalizes_zero_offset_to_z() {
assert_eq!(
canonicalize("2026-05-28T18:00:00+00:00").as_deref(),
Some("2026-05-28T18:00:00Z")
);
}
#[test]
fn passes_through_already_canonical() {
assert_eq!(
canonicalize("2026-05-28T18:00:00Z").as_deref(),
Some("2026-05-28T18:00:00Z")
);
}
#[test]
fn rejects_non_rfc3339() {
assert_eq!(canonicalize("not-a-timestamp"), None);
}
#[test]
fn rejects_calendar_invalid_shape() {
assert_eq!(canonicalize("2026-99-99T99:99:99Z"), None);
}
}