diff --git a/instant-xml/src/impls.rs b/instant-xml/src/impls.rs index d7a37f9..1d727ca 100644 --- a/instant-xml/src/impls.rs +++ b/instant-xml/src/impls.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use std::{any::type_name, marker::PhantomData}; #[cfg(feature = "chrono")] -use chrono::{DateTime, NaiveDate, Utc}; +use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc}; use crate::{Accumulate, Deserializer, Error, FromXml, Id, Kind, Serializer, ToXml}; @@ -669,6 +669,68 @@ impl<'xml> FromXml<'xml> for DateTime { const KIND: Kind = Kind::Scalar; } +#[cfg(feature = "chrono")] +impl ToXml for NaiveDateTime { + fn serialize( + &self, + field: Option>, + serializer: &mut Serializer, + ) -> Result<(), Error> { + let prefix = match field { + Some(id) => { + let prefix = serializer.write_start(id.name, id.ns)?; + serializer.end_start()?; + Some((prefix, id.name)) + } + None => None, + }; + + serializer.write_str(&self.format("%Y-%m-%dT%H:%M:%S%.f"))?; + if let Some((prefix, name)) = prefix { + serializer.write_close(prefix, name)?; + } + + Ok(()) + } +} + +#[cfg(feature = "chrono")] +impl<'xml> FromXml<'xml> for NaiveDateTime { + fn matches(id: Id<'_>, field: Option>) -> bool { + match field { + Some(field) => id == field, + None => false, + } + } + + fn deserialize<'cx>( + into: &mut Self::Accumulator, + field: &'static str, + deserializer: &mut Deserializer<'cx, 'xml>, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue(field)); + } + + let value = match deserializer.take_str()? { + Some(value) => value, + None => return Ok(()), + }; + + match NaiveDateTime::parse_from_str(value.as_ref(), "%Y-%m-%dT%H:%M:%S%.f") { + Ok(dt) => { + *into = Some(dt); + Ok(()) + } + _ => Err(Error::Other("invalid date/time".into())), + } + } + + type Accumulator = Option; + + const KIND: Kind = Kind::Scalar; +} + #[cfg(feature = "chrono")] impl ToXml for NaiveDate { fn serialize( diff --git a/instant-xml/tests/chrono.rs b/instant-xml/tests/chrono.rs index c1081e1..09a02df 100644 --- a/instant-xml/tests/chrono.rs +++ b/instant-xml/tests/chrono.rs @@ -1,23 +1,36 @@ #![cfg(feature = "chrono")] -use chrono::{DateTime, TimeZone, Utc}; +use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; use similar_asserts::assert_eq; use instant_xml::{from_str, to_string, FromXml, ToXml}; #[derive(Debug, Eq, PartialEq, FromXml, ToXml)] -struct Test { - dt: DateTime, +struct Test { + dt: T, } +type TestUtcDateTime = Test>; + #[test] fn datetime() { let dt = Utc.with_ymd_and_hms(2022, 11, 21, 21, 17, 23).unwrap(); let test = Test { dt }; let xml = "
2022-11-21T21:17:23+00:00
"; assert_eq!(to_string(&test).unwrap(), xml); - assert_eq!(from_str::(xml).unwrap(), test); + assert_eq!(from_str::(xml).unwrap(), test); let zulu = xml.replace("+00:00", "Z"); - assert_eq!(from_str::(&zulu).unwrap(), test); + assert_eq!(from_str::(&zulu).unwrap(), test); +} + +type TestNaiveDateTime = Test; + +#[test] +fn naive_datetime() { + let dt = NaiveDateTime::parse_from_str("2022-11-21T21:17:23", "%Y-%m-%dT%H:%M:%S").unwrap(); + let test = Test { dt }; + let xml = "
2022-11-21T21:17:23
"; + assert_eq!(to_string(&test).unwrap(), xml); + assert_eq!(from_str::(xml).unwrap(), test); }