Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add serde functions for optional microsecond timestamps #533

Merged
merged 1 commit into from
Oct 25, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
164 changes: 161 additions & 3 deletions src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,10 @@ pub mod serde {
#[derive(Debug)]
pub struct NanoSecondsTimestampVisitor;

#[doc(hidden)]
#[derive(Debug)]
pub struct MicroSecondsTimestampVisitor;

#[doc(hidden)]
#[derive(Debug)]
pub struct MilliSecondsTimestampVisitor;
Expand Down Expand Up @@ -1412,7 +1416,7 @@ pub mod serde {
use offset::TimeZone;
use {DateTime, Utc};

use super::serde_from;
use super::{serde_from, MicroSecondsTimestampVisitor};

/// Serialize a UTC datetime into an integer number of microseconds since the epoch
///
Expand Down Expand Up @@ -1489,8 +1493,6 @@ pub mod serde {
Ok(try!(d.deserialize_i64(MicroSecondsTimestampVisitor)))
}

struct MicroSecondsTimestampVisitor;

impl<'de> de::Visitor<'de> for MicroSecondsTimestampVisitor {
type Value = DateTime<Utc>;

Expand Down Expand Up @@ -1525,6 +1527,162 @@ pub mod serde {
}
}

/// Ser/de to/from optional timestamps in microseconds
///
/// Intended for use with `serde`'s `with` attribute.
///
/// # Example:
///
/// ```rust
/// # // We mark this ignored so that we can test on 1.13 (which does not
/// # // support custom derive), and run tests with --ignored on beta and
/// # // nightly to actually trigger these.
/// #
/// # #[macro_use] extern crate serde_derive;
/// # #[macro_use] extern crate serde_json;
/// # extern crate chrono;
/// # use chrono::{TimeZone, DateTime, Utc};
/// use chrono::serde::ts_microseconds_option;
/// #[derive(Deserialize, Serialize)]
/// struct S {
/// #[serde(with = "ts_microseconds_option")]
/// time: Option<DateTime<Utc>>
/// }
///
/// # fn example() -> Result<S, serde_json::Error> {
/// let time = Some(Utc.ymd(2018, 5, 17).and_hms_micro(02, 04, 59, 918355));
/// let my_s = S {
/// time: time.clone(),
/// };
///
/// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
/// let my_s: S = serde_json::from_str(&as_string)?;
/// assert_eq!(my_s.time, time);
/// # Ok(my_s)
/// # }
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_microseconds_option {
use core::fmt;
use serdelib::{de, ser};

use {DateTime, Utc};

use super::MicroSecondsTimestampVisitor;

/// Serialize a UTC datetime into an integer number of microseconds since the epoch or none
///
/// Intended for use with `serde`s `serialize_with` attribute.
///
/// # Example:
///
/// ```rust
/// # // We mark this ignored so that we can test on 1.13 (which does not
/// # // support custom derive), and run tests with --ignored on beta and
/// # // nightly to actually trigger these.
/// #
/// # #[macro_use] extern crate serde_derive;
/// # #[macro_use] extern crate serde_json;
/// # extern crate chrono;
/// # use chrono::{TimeZone, DateTime, Utc};
/// use chrono::serde::ts_microseconds_option::serialize as to_micro_tsopt;
/// #[derive(Serialize)]
/// struct S {
/// #[serde(serialize_with = "to_micro_tsopt")]
/// time: Option<DateTime<Utc>>
/// }
///
/// # fn example() -> Result<String, serde_json::Error> {
/// let my_s = S {
/// time: Some(Utc.ymd(2018, 5, 17).and_hms_micro(02, 04, 59, 918355)),
/// };
/// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
/// # Ok(as_string)
/// # }
/// # fn main() { example().unwrap(); }
/// ```
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
match *opt {
Some(ref dt) => serializer.serialize_some(&dt.timestamp_micros()),
None => serializer.serialize_none(),
}
}

/// Deserialize a `DateTime` from a microsecond timestamp or none
///
/// Intended for use with `serde`s `deserialize_with` attribute.
///
/// # Example:
///
/// ```rust
/// # // We mark this ignored so that we can test on 1.13 (which does not
/// # // support custom derive), and run tests with --ignored on beta and
/// # // nightly to actually trigger these.
/// #
/// # #[macro_use] extern crate serde_derive;
/// # #[macro_use] extern crate serde_json;
/// # extern crate chrono;
/// # use chrono::{DateTime, Utc};
/// use chrono::serde::ts_microseconds_option::deserialize as from_micro_tsopt;
/// #[derive(Deserialize)]
/// struct S {
/// #[serde(deserialize_with = "from_micro_tsopt")]
/// time: Option<DateTime<Utc>>
/// }
///
/// # fn example() -> Result<S, serde_json::Error> {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?;
/// # Ok(my_s)
/// # }
/// # fn main() { example().unwrap(); }
/// ```
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: de::Deserializer<'de>,
{
Ok(d.deserialize_option(OptionMicroSecondsTimestampVisitor)?)
}

struct OptionMicroSecondsTimestampVisitor;

impl<'de> de::Visitor<'de> for OptionMicroSecondsTimestampVisitor {
type Value = Option<DateTime<Utc>>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a unix timestamp in microseconds or none")
}

/// Deserialize a timestamp in seconds since the epoch
fn visit_some<D>(self, d: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: de::Deserializer<'de>,
{
d.deserialize_i64(MicroSecondsTimestampVisitor).map(Some)
}

/// Deserialize a timestamp in seconds since the epoch
fn visit_none<E>(self) -> Result<Option<DateTime<Utc>>, E>
where
E: de::Error,
{
Ok(None)
}

/// Deserialize a timestamp in seconds since the epoch
fn visit_unit<E>(self) -> Result<Option<DateTime<Utc>>, E>
where
E: de::Error,
{
Ok(None)
}
}
}

/// Ser/de to/from timestamps in nanoseconds
///
/// Intended for use with `serde`'s `with` attribute.
Expand Down