diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..7b2b22c3a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.cargo.features": "all" +} \ No newline at end of file diff --git a/tests/serde/timestamps.rs b/tests/serde/timestamps.rs index 9a93c559d..3f5c008f4 100644 --- a/tests/serde/timestamps.rs +++ b/tests/serde/timestamps.rs @@ -1,25 +1,25 @@ use serde::{Deserialize, Serialize}; -use serde_test::{assert_de_tokens_error, assert_tokens, Configure, Token}; +use serde_test::{assert_de_tokens_error, assert_de_tokens, assert_tokens, Configure, Token}; use time::macros::datetime; use time::serde::timestamp; -use time::OffsetDateTime; +use time::{OffsetDateTime,PrimitiveDateTime}; #[derive(Serialize, Deserialize, Debug, PartialEq)] -struct Test { +struct TestOffset { #[serde(with = "timestamp")] dt: OffsetDateTime, } #[test] -fn serialize_timestamp() { - let value = Test { +fn serialize_timestamp_offset() { + let value = TestOffset { dt: datetime!(2000-01-01 00:00:00 UTC), }; assert_tokens( &value.compact(), &[ Token::Struct { - name: "Test", + name: "TestOffset", len: 1, }, Token::Str("dt"), @@ -27,10 +27,10 @@ fn serialize_timestamp() { Token::StructEnd, ], ); - assert_de_tokens_error::( + assert_de_tokens_error::( &[ Token::Struct { - name: "Test", + name: "TestOffset", len: 1, }, Token::Str("dt"), @@ -40,3 +40,600 @@ fn serialize_timestamp() { "invalid type: string \"bad\", expected i64", ); } + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestPrimitive{ + #[serde(with = "timestamp")] + dt: PrimitiveDateTime, +} + + +#[test] +fn serialize_timestamp_primitive() { + let value = TestPrimitive { + dt: datetime!(2015-01-01 00:00:00), + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestPrimitive", + len: 1, + }, + Token::Str("dt"), + Token::I64(1420070400), + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestPrimitive", + len: 1, + }, + Token::Str("dt"), + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); +} + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestOffsetOption { + #[serde(with = "timestamp")] + dt: Option, +} + + +#[test] +fn serialize_timestamp_offset_option() { + let value = TestOffsetOption { + dt: Some(datetime!(1990-01-01 00:00:00 UTC)), + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestOffsetOption", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::I64(631152000), + Token::StructEnd, + ], + ); + let value = TestOffsetOption { + dt: None, + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestOffsetOption", + len: 1, + }, + Token::Str("dt"), + Token::None, + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestOffsetOption", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); + assert_de_tokens::( + &TestOffsetOption { + dt: Some(datetime!(1969-09-30 09:46:40 +0000)) + }, + &[ + Token::Struct { + name: "TestOffsetOption", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::I64(-8_000_000), + Token::StructEnd, + ], + ); +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestPrimitiveOption { + #[serde(with = "timestamp")] + dt: Option, +} + + +#[test] +fn serialize_timestamp_primitive_option() { + let value = TestPrimitiveOption { + dt: Some(datetime!(1980-01-01 00:00:00)), + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestPrimitiveOption", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::I64(315532800), + Token::StructEnd, + ], + ); + let value = TestPrimitiveOption { + dt: None, + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestPrimitiveOption", + len: 1, + }, + Token::Str("dt"), + Token::None, + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestPrimitiveOption", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); + assert_de_tokens::( + &TestPrimitiveOption { + dt: Some(datetime!(1930-04-11 02:35:12)) + }, + &[ + Token::Struct { + name: "TestPrimitiveOption", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::I64(-1253654688), + Token::StructEnd, + ], + ); +} + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestOffsetMillis { + #[serde(with = "timestamp::millis")] + dt: OffsetDateTime, +} + +#[test] +fn serialize_timestamp_offset_millis() { + let value = TestOffsetMillis { + dt: datetime!(2000-01-01 00:00:00.123 UTC), + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestOffsetMillis", + len: 1, + }, + Token::Str("dt"), + Token::I64(946684800123), + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestOffsetMillis", + len: 1, + }, + Token::Str("dt"), + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); + + assert_de_tokens::( + &TestOffsetMillis { + dt: datetime!(1930-04-11 02:35:12.123 +00) + }, + &[ + Token::Struct { + name: "TestOffsetMillis", + len: 1, + }, + Token::Str("dt"), + Token::I64(-1253654687877), + Token::StructEnd, + ], + ); +} + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestPrimitiveMillis{ + #[serde(with = "timestamp::millis")] + dt: PrimitiveDateTime, +} + + +#[test] +fn serialize_timestamp_primitive_millis() { + let value = TestPrimitiveMillis { + dt: datetime!(2015-01-01 00:00:00.123), + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestPrimitiveMillis", + len: 1, + }, + Token::Str("dt"), + Token::I64(1420070400123), + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestPrimitiveMillis", + len: 1, + }, + Token::Str("dt"), + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); + assert_de_tokens::( + &TestPrimitiveMillis { + dt: datetime!(1969-09-30 09:46:40.123) + }, + &[ + Token::Struct { + name: "TestPrimitiveMillis", + len: 1, + }, + Token::Str("dt"), + Token::I64(-7_999_999_877), + Token::StructEnd, + ], + ); +} + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestOffsetOptionMillis { + #[serde(with = "timestamp::millis")] + dt: Option, +} + + +#[test] +fn serialize_timestamp_offset_option_millis() { + let value = TestOffsetOptionMillis { + dt: Some(datetime!(1990-01-01 00:00:00.123 UTC)), + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestOffsetOptionMillis", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::I64(631152000123), + Token::StructEnd, + ], + ); + let value = TestOffsetOptionMillis { + dt: None, + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestOffsetOptionMillis", + len: 1, + }, + Token::Str("dt"), + Token::None, + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestOffsetOptionMillis", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); + assert_de_tokens::( + &TestOffsetOptionMillis { + dt: Some(datetime!(1969-09-30 09:46:40.123 +0000)) + }, + &[ + Token::Struct { + name: "TestOffsetOptionMillis", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::I64(-7_999_999_877), + Token::StructEnd, + ], + ); +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestPrimitiveOptionMillis { + #[serde(with = "timestamp::millis")] + dt: Option, +} + + +#[test] +fn serialize_timestamp_primitive_option_millis() { + let value = TestPrimitiveOptionMillis { + dt: Some(datetime!(1980-01-01 00:00:00.123)), + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestPrimitiveOptionMillis", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::I64(315532800123), + Token::StructEnd, + ], + ); + let value = TestPrimitiveOptionMillis { + dt: None, + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestPrimitiveOptionMillis", + len: 1, + }, + Token::Str("dt"), + Token::None, + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestPrimitiveOptionMillis", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); + assert_de_tokens::( + &TestPrimitiveOptionMillis { + dt: Some(datetime!(1930-04-11 02:35:12.123)) + }, + &[ + Token::Struct { + name: "TestPrimitiveOptionMillis", + len: 1, + }, + Token::Str("dt"), + Token::Some, + Token::I64(-1253654687877), + Token::StructEnd, + ], + ); +} + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestOffsetVec { + #[serde(with = "timestamp")] + dt: Vec, +} + +#[test] +fn serialize_timestamp_offset_vec() { + let value = TestOffsetVec { + dt: vec![datetime!(2000-01-01 00:00:00 UTC),datetime!(2010-01-01 00:00:00 UTC)] + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestOffsetVec", + len: 1, + }, + Token::Str("dt"), + Token::Seq{ len: Some(2) }, + Token::I64(946684800), + Token::I64(1262304000), + Token::SeqEnd, + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestOffsetVec", + len: 1, + }, + Token::Str("dt"), + Token::Seq{ len : Some(1) }, + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); +} + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestPrimitiveVec{ + #[serde(with = "timestamp")] + dt: Vec, +} + + +#[test] +fn serialize_timestamp_primitive_vec() { + let value = TestPrimitiveVec { + dt: vec![datetime!(1860-01-01 00:00:00),datetime!(1972-01-01 00:00:00)], + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestPrimitiveVec", + len: 1, + }, + Token::Str("dt"), + Token::Seq{ len: Some(2) }, + Token::I64(-3471292800), + Token::I64(63072000), + Token::SeqEnd, + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestPrimitiveVec", + len: 1, + }, + Token::Str("dt"), + Token::Seq{ len : Some(1) }, + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); +} + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestOffsetVecMillis { + #[serde(with = "timestamp::millis")] + dt: Vec, +} + +#[test] +fn serialize_timestamp_offset_vec_millis() { + let value = TestOffsetVecMillis { + dt: vec![datetime!(2000-01-01 00:00:00.123 UTC),datetime!(2010-01-01 00:00:00.123 UTC)] + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestOffsetVecMillis", + len: 1, + }, + Token::Str("dt"), + Token::Seq{ len: Some(2) }, + Token::I64(946684800123), + Token::I64(1262304000123), + Token::SeqEnd, + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestOffsetVecMillis", + len: 1, + }, + Token::Str("dt"), + Token::Seq{ len : Some(1) }, + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); +} + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct TestPrimitiveVecMillis{ + #[serde(with = "timestamp::millis")] + dt: Vec, +} + + +#[test] +fn serialize_timestamp_primitive_vec_millis() { + let value = TestPrimitiveVecMillis { + dt: vec![datetime!(1860-01-01 00:00:00.123),datetime!(1972-01-01 00:00:00.123)], + }; + assert_tokens( + &value.compact(), + &[ + Token::Struct { + name: "TestPrimitiveVecMillis", + len: 1, + }, + Token::Str("dt"), + Token::Seq{ len: Some(2) }, + Token::I64(-3471292799877), + Token::I64(63072000123), + Token::SeqEnd, + Token::StructEnd, + ], + ); + assert_de_tokens_error::( + &[ + Token::Struct { + name: "TestPrimitiveVecMillis", + len: 1, + }, + Token::Str("dt"), + Token::Seq{ len : Some(1) }, + Token::Str("bad"), + Token::StructEnd, + ], + "invalid type: string \"bad\", expected i64", + ); +} diff --git a/time-macros/src/serde_types.rs b/time-macros/src/serde_types.rs new file mode 100644 index 000000000..e69de29bb diff --git a/time/src/serde/mod.rs b/time/src/serde/mod.rs index 844d7bdef..d06cb263f 100644 --- a/time/src/serde/mod.rs +++ b/time/src/serde/mod.rs @@ -489,3 +489,150 @@ impl<'a> Deserialize<'a> for Month { } } // endregion Month + +pub trait AsWellKnown { + type IntoWellKnownError: core::fmt::Display; + type WellKnownSer<'s>: Serialize + where + Self: 's; + + fn as_well_known<'s>(&'s self) -> Result, Self::IntoWellKnownError>; + + fn fmt_err(error: Self::IntoWellKnownError) -> E { + E::custom(error) + } + + #[inline] + fn serialize_from_wellknown(&self, serializer: S) -> Result { + let wk = self.as_well_known().map_err(Self::fmt_err)?; + wk.serialize(serializer) + } +} + +impl AsWellKnown for Option +where + T: AsWellKnown, +{ + type IntoWellKnownError = T::IntoWellKnownError; + + type WellKnownSer<'s> = Option> where Self: 's, T : 's; + + #[inline] + fn as_well_known<'s>(&'s self) -> Result, Self::IntoWellKnownError> { + self.as_ref().map(T::as_well_known).transpose() + } +} + +impl AsWellKnown for [T] +where + T: AsWellKnown, +{ + type IntoWellKnownError = T::IntoWellKnownError; + + type WellKnownSer<'s> = Vec> where Self: 's, T : 's; + + #[inline] + fn as_well_known<'s>(&'s self) -> Result, Self::IntoWellKnownError> { + self.iter().map(T::as_well_known).collect::>() + } + + #[inline] + fn fmt_err(error: Self::IntoWellKnownError) -> E { + T::fmt_err(error) + } + + fn serialize_from_wellknown(&self, serializer: S) -> Result { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + + for i in self { + let tmp = i.as_well_known().map_err(T::fmt_err)?; + + serde::ser::SerializeSeq::serialize_element(&mut seq, &tmp)?; + } + + serde::ser::SerializeSeq::end(seq) + } +} + +impl AsWellKnown for Vec +where + T: AsWellKnown, +{ + type IntoWellKnownError = T::IntoWellKnownError; + + type WellKnownSer<'s> = Vec> where Self: 's, T : 's; + + #[inline] + fn as_well_known<'s>(&'s self) -> Result, Self::IntoWellKnownError> { + self.iter().map(T::as_well_known).collect::>() + } + + #[inline] + fn fmt_err(error: Self::IntoWellKnownError) -> E { + T::fmt_err(error) + } + + fn serialize_from_wellknown(&self, serializer: S) -> Result { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + + for i in self { + let tmp = i.as_well_known().map_err(T::fmt_err)?; + + serde::ser::SerializeSeq::serialize_element(&mut seq, &tmp)?; + } + + serde::ser::SerializeSeq::end(seq) + } +} + +pub trait FromWellKnown: Sized { + type FromWellKnownError: std::fmt::Display; + type WellKnownDeser<'de>: Deserialize<'de> + 'de; + + fn from_well_known<'de>( + wk: Self::WellKnownDeser<'de>, + ) -> Result; + fn fmt_err(e: Self::FromWellKnownError) -> E { + E::custom(e) + } + + #[inline] + fn deserialize_from_well_known<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result { + let wk = Self::WellKnownDeser::deserialize(deserializer)?; + Self::from_well_known(wk).map_err(Self::fmt_err) + } +} + +impl FromWellKnown for Option +where + T: FromWellKnown, +{ + type FromWellKnownError = T::FromWellKnownError; + + type WellKnownDeser<'de> = Option>; + + #[inline] + fn from_well_known<'de>( + wk: Self::WellKnownDeser<'de>, + ) -> Result { + wk.map(T::from_well_known).transpose() + } +} + +impl FromWellKnown for Vec +where + T: FromWellKnown, +{ + type FromWellKnownError = T::FromWellKnownError; + + type WellKnownDeser<'de> = Vec> where T::WellKnownDeser<'de>: 'de; + + #[inline] + fn from_well_known<'de>( + wk: Self::WellKnownDeser<'de>, + ) -> Result { + wk.into_iter().map(T::from_well_known).collect() + } +} diff --git a/time/src/serde/timestamp.rs b/time/src/serde/timestamp.rs index d86e6b933..46cdd221f 100644 --- a/time/src/serde/timestamp.rs +++ b/time/src/serde/timestamp.rs @@ -1,28 +1,180 @@ -//! Treat an [`OffsetDateTime`] as a [Unix timestamp] for the purposes of serde. +//! Treat an [`OffsetDateTime`] and [`PrimitiveDateTime`] as a [Unix timestamp] for the purposes of +//! serde. //! //! Use this module in combination with serde's [`#[with]`][with] attribute. //! //! When deserializing, the offset is assumed to be UTC. //! +//! Also works with [`Option`], and [`Option`]. +//! //! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time //! [with]: https://serde.rs/field-attrs.html#with use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use crate::OffsetDateTime; +use super::{AsWellKnown, FromWellKnown}; +use crate::{OffsetDateTime, PrimitiveDateTime}; -/// Serialize an `OffsetDateTime` as its Unix timestamp -pub fn serialize( - datetime: &OffsetDateTime, - serializer: S, -) -> Result { - datetime.unix_timestamp().serialize(serializer) +/// Serialize an [`OffsetDateTime`] and [`PrimitiveDateTime`] as its Unix timestamp +/// +/// Also works with [`Option`], and [`Option`]. +#[inline(always)] +pub fn serialize(t: &T, serializer: S) -> Result +where + T: AsWellKnown, +{ + t.serialize_from_wellknown(serializer) } /// Deserialize an `OffsetDateTime` from its Unix timestamp -pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result { - OffsetDateTime::from_unix_timestamp(<_>::deserialize(deserializer)?) - .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) +/// +/// Also works with [`Option`], and [`Option`]. +#[inline(always)] +pub fn deserialize<'a, D: Deserializer<'a>, T>(deserializer: D) -> Result +where + T: FromWellKnown, +{ + T::deserialize_from_well_known(deserializer) +} + +pub struct Timestamp; + +impl AsWellKnown for OffsetDateTime { + type IntoWellKnownError = std::convert::Infallible; + + type WellKnownSer<'s> = i64 where Self: 's; + + fn as_well_known<'s>(&'s self) -> Result, Self::IntoWellKnownError> { + Ok(self.unix_timestamp()) + } +} + +impl FromWellKnown for OffsetDateTime { + type FromWellKnownError = crate::error::ComponentRange; + + type WellKnownDeser<'de> = i64; + + fn fmt_err(e: Self::FromWellKnownError) -> E { + E::invalid_value(serde::de::Unexpected::Signed(e.value), &e) + } + + fn from_well_known<'de>( + wk: Self::WellKnownDeser<'de>, + ) -> Result { + OffsetDateTime::from_unix_timestamp(wk) + } +} + +impl AsWellKnown for PrimitiveDateTime { + type IntoWellKnownError = std::convert::Infallible; + + type WellKnownSer<'s> = i64 where Self: 's; + + fn as_well_known<'s>(&'s self) -> Result, Self::IntoWellKnownError> { + Ok(self.assume_utc().unix_timestamp()) + } +} + +impl FromWellKnown for PrimitiveDateTime { + type FromWellKnownError = crate::error::ComponentRange; + + type WellKnownDeser<'de> = i64; + + fn from_well_known<'de>( + wk: Self::WellKnownDeser<'de>, + ) -> Result { + OffsetDateTime::from_unix_timestamp(wk).map(|t| t.date().with_time(t.time())) + } +} + +// Treat an [`OffsetDateTime`] as a [Unix timestamp (milliseconds)] for the purposes of serde. +// +// Use this module in combination with serde's [`#[with]`][with] attribute. +// +// When deserializing, the offset is assumed to be UTC. +// +// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +// [with]: https://serde.rs/field-attrs.html#with +pub mod millis { + use super::*; + + /// Serialize an [`OffsetDateTime`] and [`PrimitiveDateTime`] as its Unix timestamp in + /// milliseconds + /// + /// Also works with [`Option`], and [`Option`]. + #[inline(always)] + pub fn serialize(t: &T, serializer: S) -> Result + where + T: AsWellKnown, + { + t.serialize_from_wellknown(serializer) + } + + /// Deserialize an `OffsetDateTime` from its Unix timestamp in milliseconds + /// + /// Also works with [`Option`], and [`Option`]. + #[inline(always)] + pub fn deserialize<'a, D: Deserializer<'a>, T>(deserializer: D) -> Result + where + T: FromWellKnown, + { + T::deserialize_from_well_known(deserializer) + } + + pub struct TimestampMillis; + + impl AsWellKnown for OffsetDateTime { + type IntoWellKnownError = std::convert::Infallible; + + type WellKnownSer<'s> = i64 where Self: 's; + + fn as_well_known<'s>(&'s self) -> Result, Self::IntoWellKnownError> { + Ok((self.unix_timestamp_nanos() / 1_000_000) as i64) + } + } + + impl FromWellKnown for OffsetDateTime { + type FromWellKnownError = crate::error::ComponentRange; + + type WellKnownDeser<'de> = i64; + + fn fmt_err(e: Self::FromWellKnownError) -> E { + E::invalid_value(serde::de::Unexpected::Signed(e.value), &e) + } + + fn from_well_known<'de>( + timestamp: Self::WellKnownDeser<'de>, + ) -> Result { + let secs = timestamp / 1_000; + let millis = timestamp % 1000; + + Ok(OffsetDateTime::from_unix_timestamp(secs)? + crate::Duration::milliseconds(millis)) + } + } + + impl AsWellKnown for PrimitiveDateTime { + type IntoWellKnownError = std::convert::Infallible; + + type WellKnownSer<'s> = i64 where Self: 's; + + #[inline] + fn as_well_known<'s>(&'s self) -> Result, Self::IntoWellKnownError> { + Ok((self.assume_utc().unix_timestamp_nanos() / 1_000_000) as i64) + } + } + + impl FromWellKnown for PrimitiveDateTime { + type FromWellKnownError = crate::error::ComponentRange; + + type WellKnownDeser<'de> = i64; + + fn from_well_known<'de>( + wk: Self::WellKnownDeser<'de>, + ) -> Result { + let t = >::from_well_known(wk)?; + Ok(t.date().with_time(t.time())) + } + } } /// Treat an `Option` as a [Unix timestamp] for the purposes of @@ -34,11 +186,13 @@ pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result` as its Unix timestamp + #[deprecated] pub fn serialize( option: &Option, serializer: S, @@ -49,6 +203,7 @@ pub mod option { } /// Deserialize an `Option` from its Unix timestamp + #[deprecated] pub fn deserialize<'a, D: Deserializer<'a>>( deserializer: D, ) -> Result, D::Error> {