Skip to main content

jsonld_expandable_core/
interop_chrono.rs

1//! `ToJsonValue` impls for `chrono` types — gated on the `chrono` feature.
2//!
3//! Lets fields of foreign date/time types participate in expansion without
4//! the consumer crate having to wrap them in a newtype to satisfy orphan
5//! rules. All temporal types render as their xsd-style lexical form.
6
7use crate::{JsonValue, ToJsonValue};
8use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, SecondsFormat, TimeZone};
9
10impl<V: JsonValue, Tz: TimeZone> ToJsonValue<V> for DateTime<Tz>
11where
12    Tz::Offset: ::core::fmt::Display,
13{
14    #[inline]
15    fn to_json_value(&self) -> V {
16        // `use_z: true` renders UTC as the canonical `Z` suffix instead of
17        // `+00:00`; NGSI-LD brokers expect the former.
18        V::from_string(self.to_rfc3339_opts(SecondsFormat::AutoSi, true))
19    }
20}
21
22impl<V: JsonValue> ToJsonValue<V> for NaiveDateTime {
23    #[inline]
24    fn to_json_value(&self) -> V {
25        V::from_string(self.format("%Y-%m-%dT%H:%M:%S%.f").to_string())
26    }
27}
28
29impl<V: JsonValue> ToJsonValue<V> for NaiveDate {
30    #[inline]
31    fn to_json_value(&self) -> V {
32        V::from_string(self.format("%Y-%m-%d").to_string())
33    }
34}
35
36impl<V: JsonValue> ToJsonValue<V> for NaiveTime {
37    #[inline]
38    fn to_json_value(&self) -> V {
39        V::from_string(self.format("%H:%M:%S%.f").to_string())
40    }
41}
42
43#[cfg(all(test, feature = "serde-json"))]
44#[allow(clippy::panic)]
45mod tests {
46    use crate::ToJsonValue;
47    use chrono::{TimeZone, Utc};
48
49    #[test]
50    fn utc_datetimes_render_with_z_suffix() {
51        let Some(dt) = Utc.with_ymd_and_hms(2026, 1, 2, 3, 4, 5).single() else {
52            panic!("valid timestamp");
53        };
54        let v: serde_json::Value = dt.to_json_value();
55        assert_eq!(v, serde_json::json!("2026-01-02T03:04:05Z"));
56    }
57}