1 use chrono::{DateTime, FixedOffset, SecondsFormat, Utc};
2 use std::{
3     fmt,
4     time::{Duration, SystemTime, UNIX_EPOCH},
5 };
6 
7 /// A UTC timestamp used for serialization to and from the plist date type.
8 ///
9 /// Note that while this type implements `Serialize` and `Deserialize` it will behave strangely if
10 /// used with serializers from outside this crate.
11 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
12 pub struct Date {
13     inner: SystemTime,
14 }
15 
16 pub(crate) struct InfiniteOrNanDate;
17 
18 impl Date {
19     /// The unix timestamp of the plist epoch.
20     const PLIST_EPOCH_UNIX_TIMESTAMP: Duration = Duration::from_secs(978_307_200);
21 
from_rfc3339(date: &str) -> Result<Self, ()>22     pub(crate) fn from_rfc3339(date: &str) -> Result<Self, ()> {
23         let offset: DateTime<FixedOffset> = DateTime::parse_from_rfc3339(date).map_err(|_| ())?;
24         Ok(Date {
25             inner: offset.with_timezone(&Utc).into(),
26         })
27     }
28 
to_rfc3339(&self) -> String29     pub(crate) fn to_rfc3339(&self) -> String {
30         let datetime: DateTime<Utc> = self.inner.into();
31         datetime.to_rfc3339_opts(SecondsFormat::Secs, true)
32     }
33 
from_seconds_since_plist_epoch( timestamp: f64, ) -> Result<Date, InfiniteOrNanDate>34     pub(crate) fn from_seconds_since_plist_epoch(
35         timestamp: f64,
36     ) -> Result<Date, InfiniteOrNanDate> {
37         // `timestamp` is the number of seconds since the plist epoch of 1/1/2001 00:00:00.
38         let plist_epoch = UNIX_EPOCH + Date::PLIST_EPOCH_UNIX_TIMESTAMP;
39 
40         if !timestamp.is_finite() {
41             return Err(InfiniteOrNanDate);
42         }
43 
44         let is_negative = timestamp < 0.0;
45         let timestamp = timestamp.abs();
46         let seconds = timestamp.floor() as u64;
47         let subsec_nanos = (timestamp.fract() * 1e9) as u32;
48 
49         let dur_since_plist_epoch = Duration::new(seconds, subsec_nanos);
50 
51         let inner = if is_negative {
52             plist_epoch - dur_since_plist_epoch
53         } else {
54             plist_epoch + dur_since_plist_epoch
55         };
56 
57         Ok(Date { inner })
58     }
59 
to_seconds_since_plist_epoch(&self) -> f6460     pub(crate) fn to_seconds_since_plist_epoch(&self) -> f64 {
61         // needed until #![feature(duration_float)] is stabilized
62         fn as_secs_f64(d: Duration) -> f64 {
63             const NANOS_PER_SEC: f64 = 1_000_000_000.00;
64             (d.as_secs() as f64) + f64::from(d.subsec_nanos()) / NANOS_PER_SEC
65         }
66 
67         let plist_epoch = UNIX_EPOCH + Date::PLIST_EPOCH_UNIX_TIMESTAMP;
68         match self.inner.duration_since(plist_epoch) {
69             Ok(dur_since_plist_epoch) => as_secs_f64(dur_since_plist_epoch),
70             Err(err) => -as_secs_f64(err.duration()),
71         }
72     }
73 }
74 
75 impl fmt::Debug for Date {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>76     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
77         write!(f, "{}", self.to_rfc3339())
78     }
79 }
80 
81 impl From<SystemTime> for Date {
from(date: SystemTime) -> Self82     fn from(date: SystemTime) -> Self {
83         Date { inner: date }
84     }
85 }
86 
87 impl Into<SystemTime> for Date {
into(self) -> SystemTime88     fn into(self) -> SystemTime {
89         self.inner
90     }
91 }
92 
93 #[cfg(feature = "serde")]
94 pub mod serde_impls {
95     use serde::{
96         de::{Deserialize, Deserializer, Error, Unexpected, Visitor},
97         ser::{Serialize, Serializer},
98     };
99     use std::fmt;
100 
101     use crate::Date;
102 
103     pub const DATE_NEWTYPE_STRUCT_NAME: &str = "PLIST-DATE";
104 
105     impl Serialize for Date {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,106         fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107         where
108             S: Serializer,
109         {
110             let date_str = self.to_rfc3339();
111             serializer.serialize_newtype_struct(DATE_NEWTYPE_STRUCT_NAME, &date_str)
112         }
113     }
114 
115     struct DateNewtypeVisitor;
116 
117     impl<'de> Visitor<'de> for DateNewtypeVisitor {
118         type Value = Date;
119 
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result120         fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
121             formatter.write_str("a plist date newtype")
122         }
123 
visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error> where D: Deserializer<'de>,124         fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
125         where
126             D: Deserializer<'de>,
127         {
128             deserializer.deserialize_str(DateStrVisitor)
129         }
130     }
131 
132     struct DateStrVisitor;
133 
134     impl<'de> Visitor<'de> for DateStrVisitor {
135         type Value = Date;
136 
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result137         fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
138             formatter.write_str("a plist date string")
139         }
140 
visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: Error,141         fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
142         where
143             E: Error,
144         {
145             Date::from_rfc3339(v).map_err(|()| E::invalid_value(Unexpected::Str(v), &self))
146         }
147     }
148 
149     impl<'de> Deserialize<'de> for Date {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>,150         fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
151         where
152             D: Deserializer<'de>,
153         {
154             deserializer.deserialize_newtype_struct(DATE_NEWTYPE_STRUCT_NAME, DateNewtypeVisitor)
155         }
156     }
157 }
158 
159 #[cfg(test)]
160 mod testing {
161     use super::*;
162 
163     #[test]
date_roundtrip()164     fn date_roundtrip() {
165         let date_str = "1981-05-16T11:32:06Z";
166 
167         let date = Date::from_rfc3339(date_str).expect("should parse");
168 
169         let generated_str = date.to_rfc3339();
170 
171         assert_eq!(date_str, generated_str);
172     }
173 
174     #[test]
far_past_date()175     fn far_past_date() {
176         let date_str = "1920-01-01T00:00:00Z";
177         Date::from_rfc3339(date_str).expect("should parse");
178     }
179 }
180