Skip to content

Commit

Permalink
Add timestamp serde support via with-annotation (#258)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkroening authored May 12, 2020
1 parent f6e9d32 commit 58cfa11
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 9 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ Versioning].

---

## 0.2.16 [2020-05-12]

### Added

`OffsetDateTime`s can now be represented as Unix timestamps with serde. To do
this, you can use the `time::serde::timestamp` and
`time::serde::timestamp::option` modules.

## 0.2.15 [2020-05-04]

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "time"
version = "0.2.15"
version = "0.2.16"
authors = ["Jacob Pratt <the.z.cuber@gmail.com>"]
edition = "2018"
repository = "https://github.com/time-rs/time"
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ mod primitive_date_time;
mod rand;
#[cfg(serde)]
#[allow(missing_copy_implementations, missing_debug_implementations)]
mod serde;
pub mod serde;
/// The `Sign` struct and its associated `impl`s.
mod sign;
/// The `Time` struct and its associated `impl`s.
Expand Down
17 changes: 10 additions & 7 deletions src/serde/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Types with guaranteed stable serde representations.
//!
//! This allows for the ability to change the internal structure of a type while
//! maintaining backwards compatibility.
//!
//! Strings are avoided where possible to allow for optimal representations in
//! various binary forms.
//! Differential formats for serde.
// Types with guaranteed stable serde representations.
//
// This allows for the ability to change the internal structure of a type while
// maintaining backwards compatibility.
//
// Strings are avoided where possible to allow for optimal representations in
// various binary forms.

#![allow(clippy::missing_docs_in_private_items)]

Expand All @@ -15,6 +17,7 @@ mod duration;
mod primitive_date_time;
mod sign;
mod time;
pub mod timestamp;
mod utc_offset;
mod weekday;

Expand Down
99 changes: 99 additions & 0 deletions src/serde/timestamp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//! Treat an [`OffsetDateTime`] as a [Unix timestamp] for the purposes of serde.
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! When deserializing, the offset is assumed to be UTC.
//!
//! ```rust,ignore
//! use serde_json::json;
//!
//! #[derive(Serialize, Deserialize)]
//! struct S {
//! #[serde(with = "time::serde::timestamp")]
//! datetime: OffsetDateTime,
//! }
//!
//! let s = S {
//! datetime: date!(2019-01-01).midnight().assume_utc(),
//! };
//! let v = json!({ "datetime": 1_546_300_800 });
//! assert_eq!(v, serde_json::to_value(&s)?);
//! assert_eq!(s, serde_json::from_value(v)?);
//! ```
//!
//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
//! [with]: https://serde.rs/field-attrs.html#with
use crate::OffsetDateTime;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
struct Wrapper(i64);

pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
) -> Result<S::Ok, S::Error> {
Wrapper(datetime.timestamp()).serialize(serializer)
}

#[allow(single_use_lifetimes)]
pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
Wrapper::deserialize(deserializer)
.map(|Wrapper(timestamp)| timestamp)
.map(OffsetDateTime::from_unix_timestamp)
}

/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] for the purposes of
/// serde.
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// When deserializing, the offset is assumed to be UTC.
///
/// ```rust,ignore
/// use serde_json::json;
///
/// #[derive(Serialize, Deserialize)]
/// struct S {
/// #[serde(with = "time::serde::timestamp::option")]
/// datetime: Option<OffsetDateTime>,
/// }
///
/// let s = S {
/// datetime: Some(date!(2019-01-01).midnight().assume_utc()),
/// };
/// let v = json!({ "datetime": 1_546_300_800 });
/// assert_eq!(v, serde_json::to_value(&s)?);
/// assert_eq!(s, serde_json::from_value(v)?);
///
/// let s = S { datetime: None };
/// let v = json!({ "datetime": null });
/// assert_eq!(v, serde_json::to_value(&s)?);
/// assert_eq!(s, serde_json::from_value(v)?);
/// ```
///
/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
/// [with]: https://serde.rs/field-attrs.html#with
pub mod option {
use super::*;

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
struct Wrapper(#[serde(with = "super")] OffsetDateTime);

pub fn serialize<S: Serializer>(
option: &Option<OffsetDateTime>,
serializer: S,
) -> Result<S::Ok, S::Error> {
option.map(Wrapper).serialize(serializer)
}

#[allow(single_use_lifetimes)]
pub fn deserialize<'a, D: Deserializer<'a>>(
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {
Option::deserialize(deserializer).map(|opt| opt.map(|Wrapper(datetime)| datetime))
}
}

0 comments on commit 58cfa11

Please sign in to comment.