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

Support for std::time::Instant #1375

Closed
kleimkuhler opened this issue Sep 6, 2018 · 3 comments
Closed

Support for std::time::Instant #1375

kleimkuhler opened this issue Sep 6, 2018 · 3 comments
Labels

Comments

@kleimkuhler
Copy link

I ran into a case where I was looking to serialize a struct that contained a field of type std::time::Instant.

This is currently not supported by serde so I thought I would take the opportunity and try to add an implementation for it. I looked at #476 and #949 as starting points.

I think the main issue with an Instant implementation stems from the following:

Instants are opaque types that can only be compared to one another. There is no method to get "the number of seconds" from an instant. Instead, it only allows measuring the duration between two instants (or comparing two instants).

Duration is composed of a whole number of seconds and a fractional part represented in nanoseconds. SystemTime is able to anchor off of UNIX_EPOCH. Therefore, there are ways to obtain the underlying data representing these structs.

Since there is nothing similar for Instant, and it can only be compared with other Instants, is an implementation for this currently a dead end? I would love to be able to tackle this, but I am not sure if serialization is possible since there is no way to obtain the underlying data representation.

@dtolnay
Copy link
Member

dtolnay commented Sep 9, 2018

If there is no way to obtain some form of serializable underlying data from a type, then there is no way it can be serialized. Or in the reverse situation if there is no way to instantiate a type from some deserializable underlying data, it isn't possible to deserialize the type.

In this case it looks like Instant is carefully designed to expose only what is necessary for the use case described in the docs, a monotonically nondecreasing clock that can be compared relative to other instants. I am closing this issue because the API is not sufficient to enable Instant to be serialized or deserialized.

@dtolnay dtolnay closed this as completed Sep 9, 2018
@dtolnay
Copy link
Member

dtolnay commented Sep 9, 2018

As a hacky approximate workaround you could serialize an approximation of an Instant in the following way. Note that this provides no upper bound on how wrong the value might be after serializing and deserializing. It might be within a few microseconds of the original value most of the time but wrong by several hours other times, especially if the system time skips around.

(The real fix is still to use a different type.)

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use std::time::Instant;

#[derive(Serialize, Deserialize, Debug)]
struct S {
    #[serde(with = "approx_instant")]
    time: Instant,
}

mod approx_instant {
    use std::time::{Instant, SystemTime};
    use serde::{Serialize, Serializer, Deserialize, Deserializer, de::Error};

    pub fn serialize<S>(instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let system_now = SystemTime::now();
        let instant_now = Instant::now();
        let approx = system_now - (instant_now - *instant);
        approx.serialize(serializer)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Instant, D::Error>
    where
        D: Deserializer<'de>,
    {
        let de = SystemTime::deserialize(deserializer)?;
        let system_now = SystemTime::now();
        let instant_now = Instant::now();
        let duration = system_now.duration_since(de).map_err(Error::custom)?;
        let approx = instant_now - duration;
        Ok(approx)
    }
}

fn main() {
    let s = S { time: Instant::now() };
    println!("before: {:?}\n", s);

    let j = serde_json::to_string(&s).unwrap();
    println!("json: {}\n", j);

    let s: S = serde_json::from_str(&j).unwrap();
    println!("after: {:?}\n", s);
}

@dtolnay dtolnay added the support label Nov 9, 2018
@Athorus
Copy link

Athorus commented Sep 17, 2019

If you are only interested by elapsed time, you can serialize and deserialize like this :

use std::time::{Duration, Instant};

use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::Error;

pub fn serialize<S>(instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let duration = instant.elapsed();
    duration.serialize(serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Instant, D::Error>
where
    D: Deserializer<'de>,
{
    let duration = Duration::deserialize(deserializer)?;
    let now = Instant::now();
    let instant = now.checked_sub(duration).ok_or_else(|| Error::custom("Erreur checked_add"))?;
    Ok(instant)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

3 participants