use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Clone, Default, Copy)]
pub struct DateTimeWrapper(DateTime<Utc>);
impl DateTimeWrapper {
#[must_use]
pub const fn new(dt: DateTime<Utc>) -> Self {
Self(dt)
}
#[must_use]
pub const fn inner(&self) -> DateTime<Utc> {
self.0
}
}
const STRICT_FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.3fZ";
fn serialize_utc<S>(dt: DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[cfg(feature = "datetime_serialize_rfc3339")]
{
use chrono::SecondsFormat;
let s = dt.to_rfc3339_opts(SecondsFormat::Millis, true);
serializer.serialize_str(&s)
}
#[cfg(not(feature = "datetime_serialize_rfc3339"))]
{
use alloc::format;
let s = format!("{}", dt.format(STRICT_FORMAT));
serializer.serialize_str(&s)
}
}
fn parse_utc(s: &str) -> Result<DateTime<Utc>, alloc::string::String> {
DateTime::parse_from_rfc3339(s)
.map(|dt| dt.with_timezone(&Utc))
.map_err(|e| alloc::format!("{e}"))
}
pub mod date_time {
use super::{DateTimeWrapper, parse_utc, serialize_utc};
use alloc::string::String;
use serde::{self, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(date: &DateTimeWrapper, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_utc(date.inner(), serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTimeWrapper, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
parse_utc(&s)
.map(DateTimeWrapper::new)
.map_err(serde::de::Error::custom)
}
}
pub mod date_time_optional {
use super::{DateTimeWrapper, parse_utc, serialize_utc};
use alloc::string::String;
use serde::{self, Deserialize, Deserializer, Serializer};
#[allow(clippy::ref_option)]
pub fn serialize<S>(date: &Option<DateTimeWrapper>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match date {
Some(date) => serialize_utc(date.inner(), serializer),
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DateTimeWrapper>, D::Error>
where
D: Deserializer<'de>,
{
let opt: Option<String> = Option::deserialize(deserializer)?;
match opt {
Some(s) => parse_utc(&s)
.map(|dt| Some(DateTimeWrapper::new(dt)))
.map_err(serde::de::Error::custom),
None => Ok(None),
}
}
}