Skip to content

Commit

Permalink
Implement Duration* and Timestamp* converters for time v0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasbb committed May 12, 2022
1 parent 088af5f commit ccad9af
Show file tree
Hide file tree
Showing 3 changed files with 308 additions and 0 deletions.
1 change: 1 addition & 0 deletions serde_with/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ rustversion = "1.0.0"
serde = {version = "1.0.122", features = ["derive"]}
serde_json = {version = "1.0.1", optional = true}
serde_with_macros = {path = "../serde_with_macros", version = "1.5.2", optional = true}
time_0_3 = {package = "time", version = "~0.3", optional = true}

[dev-dependencies]
expect-test = "1.0.0"
Expand Down
3 changes: 3 additions & 0 deletions serde_with/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ pub mod base64;
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub mod chrono;
#[cfg(feature = "time_0_3")]
#[cfg_attr(docsrs, doc(cfg(feature = "time_0_3")))]
pub mod time_0_3;
mod content;
pub mod de;
mod duplicate_key_impls;
Expand Down
304 changes: 304 additions & 0 deletions serde_with/src/time_0_3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
use crate::de::DeserializeAs;
use crate::formats::{Flexible, Format, Strict, Strictness};
use crate::ser::SerializeAs;
use crate::utils::duration::{DurationSigned, Sign};
use crate::{
DurationMicroSeconds, DurationMicroSecondsWithFrac, DurationMilliSeconds,
DurationMilliSecondsWithFrac, DurationNanoSeconds, DurationNanoSecondsWithFrac,
DurationSeconds, DurationSecondsWithFrac, TimestampMicroSeconds, TimestampMicroSecondsWithFrac,
TimestampMilliSeconds, TimestampMilliSecondsWithFrac, TimestampNanoSeconds,
TimestampNanoSecondsWithFrac, TimestampSeconds, TimestampSecondsWithFrac,
};
use serde::{de, Deserializer, Serializer};
use std::convert::TryInto;
use std::time::Duration as StdDuration;
use time_0_3::{Date, Duration, OffsetDateTime, PrimitiveDateTime, Time};

/// Create a [`PrimitiveDateTime`] for the Unix Epoch
fn unix_epoch_primitive() -> PrimitiveDateTime {
PrimitiveDateTime::new(
Date::from_ordinal_date(1970, 1).unwrap(),
Time::from_hms_nano(0, 0, 0, 0).unwrap(),
)
}

/// Convert a [`chrono::Duration`] into a [`DurationSigned`]
fn duration_into_duration_signed(dur: &Duration) -> DurationSigned {
let std_dur = StdDuration::new(
dur.whole_seconds().abs() as _,
dur.subsec_nanoseconds().abs() as _,
);

DurationSigned::with_duration(
if dur.is_positive() {
Sign::Positive
} else {
Sign::Negative
},
std_dur,
)
}

/// Convert a [`DurationSigned`] into a [`time_0_3::Duration`]
fn duration_from_duration_signed<'de, D>(sdur: DurationSigned) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
let mut dur: Duration = match sdur.duration.try_into() {
Ok(dur) => dur,
Err(msg) => {
return Err(de::Error::custom(format!(
"Duration is outside of the representable range: {}",
msg
)))
}
};
if sdur.sign.is_negative() {
dur = -dur;
}
Ok(dur)
}

macro_rules! use_duration_signed_ser {
(
$main_trait:ident $internal_trait:ident =>
{
$ty:ty; $converter:ident =>
$({
$format:ty, $strictness:ty =>
$($tbound:ident: $bound:ident $(,)?)*
})*
}
) => {
$(
impl<$($tbound ,)*> SerializeAs<$ty> for $main_trait<$format, $strictness>
where
$($tbound: $bound,)*
{
fn serialize_as<S>(source: &$ty, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let dur: DurationSigned = $converter(source);
$internal_trait::<$format, $strictness>::serialize_as(
&dur,
serializer,
)
}
}
)*
};
(
$( $main_trait:ident $internal_trait:ident, )+ => $rest:tt
) => {
$( use_duration_signed_ser!($main_trait $internal_trait => $rest); )+
};
}

fn offset_datetime_to_duration(source: &OffsetDateTime) -> DurationSigned {
duration_into_duration_signed(&(*source - OffsetDateTime::UNIX_EPOCH))
}

fn primitive_datetime_to_duration(source: &PrimitiveDateTime) -> DurationSigned {
duration_into_duration_signed(&(*source - unix_epoch_primitive()))
}

use_duration_signed_ser!(
DurationSeconds DurationSeconds,
DurationMilliSeconds DurationMilliSeconds,
DurationMicroSeconds DurationMicroSeconds,
DurationNanoSeconds DurationNanoSeconds,
=> {
Duration; duration_into_duration_signed =>
{i64, STRICTNESS => STRICTNESS: Strictness}
{f64, STRICTNESS => STRICTNESS: Strictness}
{String, STRICTNESS => STRICTNESS: Strictness}
}
);
use_duration_signed_ser!(
TimestampSeconds DurationSeconds,
TimestampMilliSeconds DurationMilliSeconds,
TimestampMicroSeconds DurationMicroSeconds,
TimestampNanoSeconds DurationNanoSeconds,
=> {
OffsetDateTime; offset_datetime_to_duration =>
{i64, STRICTNESS => STRICTNESS: Strictness}
{f64, STRICTNESS => STRICTNESS: Strictness}
{String, STRICTNESS => STRICTNESS: Strictness}
}
);
use_duration_signed_ser!(
TimestampSeconds DurationSeconds,
TimestampMilliSeconds DurationMilliSeconds,
TimestampMicroSeconds DurationMicroSeconds,
TimestampNanoSeconds DurationNanoSeconds,
=> {
PrimitiveDateTime; primitive_datetime_to_duration =>
{i64, STRICTNESS => STRICTNESS: Strictness}
{f64, STRICTNESS => STRICTNESS: Strictness}
{String, STRICTNESS => STRICTNESS: Strictness}
}
);

// Duration/Timestamp WITH FRACTIONS
use_duration_signed_ser!(
DurationSecondsWithFrac DurationSecondsWithFrac,
DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac,
DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac,
DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac,
=> {
Duration; duration_into_duration_signed =>
{f64, STRICTNESS => STRICTNESS: Strictness}
{String, STRICTNESS => STRICTNESS: Strictness}
}
);
use_duration_signed_ser!(
TimestampSecondsWithFrac DurationSecondsWithFrac,
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
=> {
OffsetDateTime; offset_datetime_to_duration =>
{f64, STRICTNESS => STRICTNESS: Strictness}
{String, STRICTNESS => STRICTNESS: Strictness}
}
);
use_duration_signed_ser!(
TimestampSecondsWithFrac DurationSecondsWithFrac,
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
=> {
PrimitiveDateTime; primitive_datetime_to_duration =>
{f64, STRICTNESS => STRICTNESS: Strictness}
{String, STRICTNESS => STRICTNESS: Strictness}
}
);

macro_rules! use_duration_signed_de {
(
$main_trait:ident $internal_trait:ident =>
{
$ty:ty; $converter:ident =>
$({
$format:ty, $strictness:ty =>
$($tbound:ident: $bound:ident)*
})*
}
) =>{
$(
impl<'de, $($tbound,)*> DeserializeAs<'de, $ty> for $main_trait<$format, $strictness>
where
$($tbound: $bound,)*
{
fn deserialize_as<D>(deserializer: D) -> Result<$ty, D::Error>
where
D: Deserializer<'de>,
{
let dur: DurationSigned = $internal_trait::<$format, $strictness>::deserialize_as(deserializer)?;
$converter::<D>(dur)
}
}
)*
};
(
$( $main_trait:ident $internal_trait:ident, )+ => $rest:tt
) => {
$( use_duration_signed_de!($main_trait $internal_trait => $rest); )+
};
}

fn duration_to_offset_datetime<'de, D>(dur: DurationSigned) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'de>,
{
Ok(OffsetDateTime::UNIX_EPOCH + duration_from_duration_signed::<D>(dur)?)
}

fn duration_to_primitive_datetime<'de, D>(
dur: DurationSigned,
) -> Result<PrimitiveDateTime, D::Error>
where
D: Deserializer<'de>,
{
Ok(unix_epoch_primitive() + duration_from_duration_signed::<D>(dur)?)
}

// No subsecond precision
use_duration_signed_de!(
DurationSeconds DurationSeconds,
DurationMilliSeconds DurationMilliSeconds,
DurationMicroSeconds DurationMicroSeconds,
DurationNanoSeconds DurationNanoSeconds,
=> {
Duration; duration_from_duration_signed =>
{i64, Strict =>}
{f64, Strict =>}
{String, Strict =>}
{FORMAT, Flexible => FORMAT: Format}
}
);
use_duration_signed_de!(
TimestampSeconds DurationSeconds,
TimestampMilliSeconds DurationMilliSeconds,
TimestampMicroSeconds DurationMicroSeconds,
TimestampNanoSeconds DurationNanoSeconds,
=> {
OffsetDateTime; duration_to_offset_datetime =>
{i64, Strict =>}
{f64, Strict =>}
{String, Strict =>}
{FORMAT, Flexible => FORMAT: Format}
}
);
use_duration_signed_de!(
TimestampSeconds DurationSeconds,
TimestampMilliSeconds DurationMilliSeconds,
TimestampMicroSeconds DurationMicroSeconds,
TimestampNanoSeconds DurationNanoSeconds,
=> {
PrimitiveDateTime; duration_to_primitive_datetime =>
{i64, Strict =>}
{f64, Strict =>}
{String, Strict =>}
{FORMAT, Flexible => FORMAT: Format}
}
);

// Duration/Timestamp WITH FRACTIONS
use_duration_signed_de!(
DurationSecondsWithFrac DurationSecondsWithFrac,
DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac,
DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac,
DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac,
=> {
Duration; duration_from_duration_signed =>
{f64, Strict =>}
{String, Strict =>}
{FORMAT, Flexible => FORMAT: Format}
}
);
use_duration_signed_de!(
TimestampSecondsWithFrac DurationSecondsWithFrac,
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
=> {
OffsetDateTime; duration_to_offset_datetime =>
{f64, Strict =>}
{String, Strict =>}
{FORMAT, Flexible => FORMAT: Format}
}
);
use_duration_signed_de!(
TimestampSecondsWithFrac DurationSecondsWithFrac,
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
=> {
PrimitiveDateTime; duration_to_primitive_datetime =>
{f64, Strict =>}
{String, Strict =>}
{FORMAT, Flexible => FORMAT: Format}
}
);

0 comments on commit ccad9af

Please sign in to comment.