#[cfg(any(test, feature = "std"))]
use std::time::{Duration, SystemTime};
use serde_core::{
Deserializer,
de::{self},
ser::Serializer
};
pub(crate) const DATE_MAGIC: &str = "{312dedf6-9f6a-436f-aeb9-d3defff0494c}";
pub mod as_date {
use serde_core::{Deserializer, Serializer};
use super::AsDate;
pub fn serialize<S: Serializer, T: AsDate>(date: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
{
date.serialize_as_date(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>, T: AsDate>(deserializer: D) -> Result<T, D::Error> {
T::deserialize_from_date(deserializer)
}
}
#[diagnostic::on_unimplemented(
message = "{Self} is not a date-ish type",
note = "valid date-ish types are SystemTime, chrono::DateTime (with `chrono` feature), or jiff::Timestamp (with `jiff` feature)"
)]
pub trait AsDate {
fn serialize_as_date<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer;
fn deserialize_from_date<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
where
Self: Sized;
}
#[cfg(any(test, feature = "std"))]
impl AsDate for SystemTime {
fn serialize_as_date<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
{
let dur = self
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|e| <S::Error as serde_core::ser::Error>::custom(e.to_string()))?;
serializer.serialize_newtype_struct(DATE_MAGIC, &((dur.as_secs() as f64) * 1000. + (dur.subsec_nanos() as f64) / 1_000_000.))
}
fn deserialize_from_date<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
where
Self: Sized
{
let t = deserializer.deserialize_f64(F64Extractor)?;
let millis = t.trunc();
let nanos = (t - millis) * 1_000_000.;
Ok(SystemTime::UNIX_EPOCH
.checked_add(Duration::from_nanos_u128((millis as u128 * 1_000_000) + nanos as u128))
.ok_or_else(|| <D::Error as de::Error>::custom("date cannot be represented as SystemTime"))?)
}
}
#[cfg(feature = "jiff-0_2")]
impl AsDate for jiff_0_2::Timestamp {
fn serialize_as_date<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
{
serializer.serialize_newtype_struct(DATE_MAGIC, &((self.as_nanosecond() as f64) / 1_000_000.))
}
fn deserialize_from_date<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
where
Self: Sized
{
let t = deserializer.deserialize_f64(F64Extractor)?;
let millis = t.trunc();
let nanos = (t - millis) * 1_000_000.;
Ok(jiff_0_2::Timestamp::from_nanosecond((millis as i128 * 1_000_000) + nanos as i128)
.map_err(|e| <D::Error as de::Error>::custom(format!("date cannot be represented as jiff::Timestamp: {e}")))?)
}
}
#[cfg(feature = "chrono-0_4")]
impl AsDate for chrono_0_4::DateTime<chrono_0_4::Utc> {
fn serialize_as_date<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
{
serializer.serialize_newtype_struct(DATE_MAGIC, &((self.timestamp_millis() as f64) + (self.timestamp_subsec_nanos() as f64) / 1_000_000.))
}
fn deserialize_from_date<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
where
Self: Sized
{
let t_sec = deserializer.deserialize_f64(F64Extractor)? / 1000.;
let secs = t_sec.trunc();
let nanos = (t_sec - secs) * 1_000_000_000.;
Ok(chrono_0_4::DateTime::from_timestamp(secs as i64, nanos as u32)
.ok_or_else(|| <D::Error as de::Error>::custom(format!("date cannot be represented as chrono::DateTime")))?)
}
}
struct F64Extractor;
impl de::Visitor<'_> for F64Extractor {
type Value = f64;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(formatter, "date")
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: de::Error
{
Ok(v)
}
}