serde_millis 0.1.1

A serde wrapper that stores integer millisecond value for timestamps and durations (used similarly to serde_bytes)
Documentation
use std::fmt;
use std::time::{Duration, SystemTime, Instant, UNIX_EPOCH};
use std::marker::PhantomData;

use serde::ser::{Serialize, Serializer, Error as SerError};
use serde::de::{Deserialize, Deserializer, Error as DeError, Visitor};

use traits::{Sealed, Milliseconds};


impl Sealed for Duration {
    fn encode<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        self.as_secs().checked_mul(1000).and_then(|x| {
            x.checked_add((self.subsec_nanos()/1_000_000) as u64)
        })
        .ok_or_else(|| S::Error::custom("duration value out of range"))
        .and_then(|v| v.serialize(serializer))
    }
    fn decode<'de, D>(deserializer: D) -> Result<Self, D::Error>
        where Self: Sized,
              D: Deserializer<'de>,
    {
        let val = Deserialize::deserialize(deserializer)?;
        Ok(Duration::from_millis(val))
    }
}
impl Sealed for SystemTime {
    fn encode<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        self.duration_since(UNIX_EPOCH)
            .map_err(|_| S::Error::custom("invalid system time"))
            .and_then(|x| x.encode(serializer))
    }
    fn decode<'de, D>(deserializer: D) -> Result<Self, D::Error>
        where Self: Sized,
              D: Deserializer<'de>,
    {
        let val = Duration::decode(deserializer)?;
        Ok((UNIX_EPOCH + val))
    }
}
impl Sealed for Instant {
    fn encode<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let inow = Instant::now();
        let snow = SystemTime::now();
        if *self < inow {
            (snow - inow.duration_since(*self)).encode(serializer)
        } else {
            (snow + self.duration_since(inow)).encode(serializer)
        }
    }
    fn decode<'de, D>(deserializer: D) -> Result<Self, D::Error>
        where Self: Sized,
              D: Deserializer<'de>,
    {
        let inow = Instant::now();
        let snow = SystemTime::now();
        let stime = UNIX_EPOCH + Duration::decode(deserializer)?;
        if stime > snow {
            stime.duration_since(snow)
                .map_err(|_| D::Error::custom("instant out of range"))
                .map(|x| inow + x)
        } else {
            snow.duration_since(stime)
                .map_err(|_| D::Error::custom("instant out of range"))
                .map(|x| inow - x)
        }
    }
}

impl<T: Sealed> Sealed for Option<T> {
    fn encode<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        struct Data<'a, V: 'a>(&'a V) where V: Sealed;

        impl<'a, V: Sealed + 'a> Serialize for Data<'a, V> {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where S: Serializer
            {
                self.0.encode(serializer)
            }
        }

        match *self {
            Some(ref value) => serializer.serialize_some(&Data(value)),
            None => serializer.serialize_none(),
        }
    }
    fn decode<'de, D>(deserializer: D) -> Result<Self, D::Error>
        where Self: Sized,
              D: Deserializer<'de>,
    {
        struct OptionVisitor<T> {
            marker: PhantomData<T>,
        }

        impl<'de, T: Sealed> Visitor<'de> for OptionVisitor<T> {
            type Value = Option<T>;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
            {
                formatter.write_str("option")
            }

            #[inline]
            fn visit_unit<E>(self) -> Result<Option<T>, E>
                where E: DeError,
            {
                Ok(None)
            }

            #[inline]
            fn visit_none<E>(self) -> Result<Option<T>, E>
                where E: DeError,
            {
                Ok(None)
            }

            #[inline]
            fn visit_some<D>(self, deserializer: D)
                -> Result<Option<T>, D::Error>
                where D: Deserializer<'de>,
            {
                T::decode(deserializer).map(Some)
            }
        }

        deserializer.deserialize_option(OptionVisitor { marker: PhantomData })
    }
}

impl Milliseconds for Duration {}
impl Milliseconds for SystemTime {}
impl Milliseconds for Instant {}
impl<T: Milliseconds> Milliseconds for Option<T> {}


#[cfg(test)]
mod test {
    use std::time::{Duration, SystemTime, Instant, UNIX_EPOCH};

    use serde_json;

    use traits::Milliseconds;

    fn encode<V: Milliseconds>(v: V) -> String {
        #[derive(Serialize)]
        struct Data<V: Milliseconds>(#[serde(with="::funcs")]V);
        serde_json::to_string(&Data(v)).unwrap()
    }

    fn decode<V: Milliseconds>(src: &str) -> V {
        #[derive(Deserialize)]
        struct Data<V: Milliseconds>(#[serde(with="::funcs")]V);
        let data: Data<V> = serde_json::from_str(src).unwrap();
        return data.0
    }

    #[test]
    fn test_duration() {
        assert_eq!(encode(Duration::new(1, 0)), "1000");
        assert_eq!(encode(Duration::new(1234, 0)), "1234000");
        assert_eq!(encode(Duration::new(1, 2300000)), "1002");
        assert_eq!(decode::<Duration>("1002"), Duration::new(1, 2000000));
        assert_eq!(decode::<Duration>("1000"), Duration::new(1, 0));
        assert_eq!(decode::<Duration>("1234000"), Duration::new(1234, 0));
    }

    #[test]
    fn test_systemtime_past() {
        assert_eq!(encode(UNIX_EPOCH + Duration::from_millis(1511885454870)),
            "1511885454870");
        assert_eq!(decode::<SystemTime>("1511885454870"),
            UNIX_EPOCH + Duration::from_millis(1511885454870));
    }

    #[test]
    fn test_systemtime_future() {
        assert_eq!(encode(UNIX_EPOCH + Duration::from_millis(2147483647000)),
            "2147483647000");
        assert_eq!(decode::<SystemTime>("2147483647000"),
            UNIX_EPOCH + Duration::from_millis(2147483647000));
    }

    #[test]
    fn test_instant_past() {
        let early = Instant::now();
        let converted = decode::<Instant>(
            &encode(Instant::now() - Duration::from_millis(2000)));
        let late = Instant::now();
        assert!(converted >= early - Duration::from_millis(2002));
        assert!(converted <= late - Duration::from_millis(1998));
    }

    #[test]
    fn test_instant_future() {
        let early = Instant::now();
        let converted = decode::<Instant>(
            &encode(Instant::now() + Duration::from_millis(2000)));
        let late = Instant::now();
        assert!(converted >= early + Duration::from_millis(1998));
        assert!(converted <= late + Duration::from_millis(2002));
    }

    #[test]
    fn test_option() {
        assert_eq!(encode(Some(Duration::new(1, 0))), "1000");
        assert_eq!(encode(None::<Duration>), "null");
        assert_eq!(decode::<Option<Duration>>("null"), None);
        assert_eq!(decode::<Option<Duration>>("1000"),
            Some(Duration::new(1, 0)));
        assert_eq!(encode(Some(
            UNIX_EPOCH + Duration::from_millis(1511885454870))),
            "1511885454870");
        assert_eq!(encode(None::<SystemTime>), "null");
        assert_eq!(decode::<Option<SystemTime>>("null"), None);
        assert_eq!(decode::<Option<SystemTime>>("1511885454870"),
            Some(UNIX_EPOCH + Duration::from_millis(1511885454870)));

        let early = SystemTime::now();
        let converted = decode::<Option<SystemTime>>(
            &encode(Some(Instant::now())));
        let later = SystemTime::now();
        assert!(converted.unwrap() >= early - Duration::from_millis(1));
        assert!(converted.unwrap() <= later);

        let early = Instant::now();
        let converted = decode::<Option<Instant>>(
            &encode(Some(SystemTime::now())));
        let later = Instant::now();
        assert!(converted.unwrap() >= early - Duration::from_millis(1));
        assert!(converted.unwrap() <= later);

        let converted = decode::<Option<Instant>>("null");
        assert!(converted.is_none());
    }
}