//! Custom serialization/deserialization implementations. /// Use the well-known ISO 8601 format when serializing and deserializing an /// [`PrimitiveDateTime`][PrimitiveDateTime]. /// /// [PrimitiveDateTime]: ::time::PrimitiveDateTime pub mod iso8601 { use std::num::NonZeroU8; use serde::{ser::Error as _, Deserializer, Serialize, Serializer}; use time::{ format_description::well_known::{ iso8601::{Config, EncodedConfig, TimePrecision}, Iso8601, }, serde::iso8601, PrimitiveDateTime, UtcOffset, }; const FORMAT_CONFIG: EncodedConfig = Config::DEFAULT .set_time_precision(TimePrecision::Second { decimal_digits: NonZeroU8::new(3), }) .encode(); /// Serialize a [`PrimitiveDateTime`] using the well-known ISO 8601 format. pub fn serialize(date_time: &PrimitiveDateTime, serializer: S) -> Result where S: Serializer, { date_time .assume_utc() .format(&Iso8601::) .map_err(S::Error::custom)? .serialize(serializer) } /// Deserialize an [`PrimitiveDateTime`] from its ISO 8601 representation. pub fn deserialize<'a, D>(deserializer: D) -> Result where D: Deserializer<'a>, { iso8601::deserialize(deserializer).map(|offset_date_time| { let utc_date_time = offset_date_time.to_offset(UtcOffset::UTC); PrimitiveDateTime::new(utc_date_time.date(), utc_date_time.time()) }) } /// Use the well-known ISO 8601 format when serializing and deserializing an /// [`Option`][PrimitiveDateTime]. /// /// [PrimitiveDateTime]: ::time::PrimitiveDateTime pub mod option { use serde::Serialize; use time::format_description::well_known::Iso8601; use super::*; /// Serialize an [`Option`] using the well-known ISO 8601 format. pub fn serialize( date_time: &Option, serializer: S, ) -> Result where S: Serializer, { date_time .map(|date_time| date_time.assume_utc().format(&Iso8601::)) .transpose() .map_err(S::Error::custom)? .serialize(serializer) } /// Deserialize an [`Option`] from its ISO 8601 representation. pub fn deserialize<'a, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'a>, { iso8601::option::deserialize(deserializer).map(|option_offset_date_time| { option_offset_date_time.map(|offset_date_time| { let utc_date_time = offset_date_time.to_offset(UtcOffset::UTC); PrimitiveDateTime::new(utc_date_time.date(), utc_date_time.time()) }) }) } } /// Use the well-known ISO 8601 format which is without timezone when serializing and deserializing an /// [`Option`][PrimitiveDateTime]. /// /// [PrimitiveDateTime]: ::time::PrimitiveDateTime pub mod option_without_timezone { use serde::{de, Deserialize, Serialize}; use time::macros::format_description; use super::*; /// Serialize an [`Option`] using the well-known ISO 8601 format which is without timezone. pub fn serialize( date_time: &Option, serializer: S, ) -> Result where S: Serializer, { date_time .map(|date_time| { let format = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]"); date_time.assume_utc().format(format) }) .transpose() .map_err(S::Error::custom)? .serialize(serializer) } /// Deserialize an [`Option`] from its ISO 8601 representation. pub fn deserialize<'a, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'a>, { Option::deserialize(deserializer)? .map(|time_string| { let format = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]"); PrimitiveDateTime::parse(time_string, format).map_err(|_| { de::Error::custom(format!( "Failed to parse PrimitiveDateTime from {time_string}" )) }) }) .transpose() } } } /// Use the UNIX timestamp when serializing and deserializing an /// [`PrimitiveDateTime`][PrimitiveDateTime]. /// /// [PrimitiveDateTime]: ::time::PrimitiveDateTime pub mod timestamp { use serde::{Deserializer, Serialize, Serializer}; use time::{serde::timestamp, PrimitiveDateTime, UtcOffset}; /// Serialize a [`PrimitiveDateTime`] using UNIX timestamp. pub fn serialize(date_time: &PrimitiveDateTime, serializer: S) -> Result where S: Serializer, { date_time .assume_utc() .unix_timestamp() .serialize(serializer) } /// Deserialize an [`PrimitiveDateTime`] from UNIX timestamp. pub fn deserialize<'a, D>(deserializer: D) -> Result where D: Deserializer<'a>, { timestamp::deserialize(deserializer).map(|offset_date_time| { let utc_date_time = offset_date_time.to_offset(UtcOffset::UTC); PrimitiveDateTime::new(utc_date_time.date(), utc_date_time.time()) }) } /// Use the UNIX timestamp when serializing and deserializing an /// [`Option`][PrimitiveDateTime]. /// /// [PrimitiveDateTime]: ::time::PrimitiveDateTime pub mod option { use serde::Serialize; use super::*; /// Serialize an [`Option`] from UNIX timestamp. pub fn serialize( date_time: &Option, serializer: S, ) -> Result where S: Serializer, { date_time .map(|date_time| date_time.assume_utc().unix_timestamp()) .serialize(serializer) } /// Deserialize an [`Option`] from UNIX timestamp. pub fn deserialize<'a, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'a>, { timestamp::option::deserialize(deserializer).map(|option_offset_date_time| { option_offset_date_time.map(|offset_date_time| { let utc_date_time = offset_date_time.to_offset(UtcOffset::UTC); PrimitiveDateTime::new(utc_date_time.date(), utc_date_time.time()) }) }) } } } /// pub mod json_string { use serde::{ de::{self, Deserialize, DeserializeOwned, Deserializer}, ser::{self, Serialize, Serializer}, }; /// Serialize a type to json_string format pub fn serialize(value: &T, serializer: S) -> Result where T: Serialize, S: Serializer, { let j = serde_json::to_string(value).map_err(ser::Error::custom)?; j.serialize(serializer) } /// Deserialize a string which is in json format pub fn deserialize<'de, T, D>(deserializer: D) -> Result where T: DeserializeOwned, D: Deserializer<'de>, { let j = String::deserialize(deserializer)?; serde_json::from_str(&j).map_err(de::Error::custom) } } /// Use a custom ISO 8601 format when serializing and deserializing /// [`PrimitiveDateTime`][PrimitiveDateTime]. /// /// [PrimitiveDateTime]: ::time::PrimitiveDateTime pub mod iso8601custom { use serde::{ser::Error as _, Deserializer, Serialize, Serializer}; use time::{ format_description::well_known::{ iso8601::{Config, EncodedConfig, TimePrecision}, Iso8601, }, serde::iso8601, PrimitiveDateTime, UtcOffset, }; const FORMAT_CONFIG: EncodedConfig = Config::DEFAULT .set_time_precision(TimePrecision::Second { decimal_digits: None, }) .encode(); /// Serialize a [`PrimitiveDateTime`] using the well-known ISO 8601 format. pub fn serialize(date_time: &PrimitiveDateTime, serializer: S) -> Result where S: Serializer, { date_time .assume_utc() .format(&Iso8601::) .map_err(S::Error::custom)? .replace('T', " ") .replace('Z', "") .serialize(serializer) } /// Deserialize an [`PrimitiveDateTime`] from its ISO 8601 representation. pub fn deserialize<'a, D>(deserializer: D) -> Result where D: Deserializer<'a>, { iso8601::deserialize(deserializer).map(|offset_date_time| { let utc_date_time = offset_date_time.to_offset(UtcOffset::UTC); PrimitiveDateTime::new(utc_date_time.date(), utc_date_time.time()) }) } } #[cfg(test)] mod tests { use serde::{Deserialize, Serialize}; use serde_json::json; #[test] fn test_leap_second_parse() { #[derive(Serialize, Deserialize)] struct Try { #[serde(with = "crate::custom_serde::iso8601")] f: time::PrimitiveDateTime, } let leap_second_date_time = json!({"f": "2023-12-31T23:59:60.000Z"}); let deser = serde_json::from_value::(leap_second_date_time); assert!(deser.is_ok()) } }