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