1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use chrono::{DateTime, Duration, TimeZone, Utc};
use std::fmt;
use std::str::FromStr;

use {Error, Result};

/// A UTC timestamp. Used for serialization to and from the plist date type.
#[derive(Clone, Debug, PartialEq)]
pub struct Date {
    inner: DateTime<Utc>,
}

impl Date {
    #[doc(hidden)]
    pub fn from_seconds_since_plist_epoch(timestamp: f64) -> Result<Date> {
        // Seconds since 1/1/2001 00:00:00.

        if timestamp.is_nan() {
            return Err(Error::InvalidData);
        }

        let millis = timestamp * 1_000.0;
        // Chrono's Duration can only millisecond values between ::std::i64::MIN and
        // ::std::i64::MAX.
        if millis > ::std::i64::MAX as f64 || millis < ::std::i64::MIN as f64 {
            return Err(Error::InvalidData);
        }

        let whole_millis = millis.floor();
        let submilli_nanos = ((millis - whole_millis) * 1_000_000.0).floor();

        let dur = Duration::milliseconds(whole_millis as i64);
        let dur = dur + Duration::nanoseconds(submilli_nanos as i64);

        let plist_epoch = Utc.ymd(2001, 1, 1).and_hms(0, 0, 0);
        let date = plist_epoch.checked_add_signed(dur).ok_or(Error::InvalidData)?;

        Ok(Date { inner: date })
    }
}

impl From<DateTime<Utc>> for Date {
    fn from(date: DateTime<Utc>) -> Self {
        Date { inner: date }
    }
}

impl Into<DateTime<Utc>> for Date {
    fn into(self) -> DateTime<Utc> {
        self.inner
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.inner)
    }
}

impl FromStr for Date {
    type Err = ();

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        let date = DateTime::parse_from_rfc3339(s).map_err(|_| ())?;
        Ok(Date { inner: date.with_timezone(&Utc) })
    }
}

#[cfg(feature = "serde")]
pub mod serde_impls {
    use serde_base::de::{Deserialize, Deserializer, Error, Visitor, Unexpected};
    use serde_base::ser::{Serialize, Serializer};
    use std::fmt;
    use std::str::FromStr;

    use Date;

    pub const DATE_NEWTYPE_STRUCT_NAME: &'static str = "PLIST-DATE";

    impl Serialize for Date {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where S: Serializer
        {
            let date_str = self.to_string();
            serializer.serialize_newtype_struct(DATE_NEWTYPE_STRUCT_NAME, &date_str)
        }
    }

    struct DateNewtypeVisitor;

    impl<'de> Visitor<'de> for DateNewtypeVisitor {
        type Value = Date;

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

        fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
            where D: Deserializer<'de>
        {
            deserializer.deserialize_str(DateStrVisitor)
        }
    }

    struct DateStrVisitor;

    impl<'de> Visitor<'de> for DateStrVisitor {
        type Value = Date;

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

        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where E: Error
        {
            Date::from_str(v).map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
        }
    }

    impl<'de> Deserialize<'de> for Date {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where D: Deserializer<'de>
        {
            deserializer.deserialize_newtype_struct(DATE_NEWTYPE_STRUCT_NAME, DateNewtypeVisitor)
        }
    }
}