| use super::DateTime; |
| use crate::format::SecondsFormat; |
| #[cfg(feature = "clock")] |
| use crate::offset::Local; |
| use crate::offset::{FixedOffset, LocalResult, TimeZone, Utc}; |
| use core::fmt; |
| use core::ops::Deref; |
| use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; |
| |
| impl<Tz: TimeZone> Encodable for DateTime<Tz> { |
| fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { |
| self.to_rfc3339_opts(SecondsFormat::AutoSi, true).encode(s) |
| } |
| } |
| |
| // lik? function to convert a LocalResult into a serde-ish Result |
| fn from<T, D>(me: LocalResult<T>, d: &mut D) -> Result<T, D::Error> |
| where |
| D: Decoder, |
| T: fmt::Display, |
| { |
| match me { |
| LocalResult::None => Err(d.error("value is not a legal timestamp")), |
| LocalResult::Ambiguous(..) => Err(d.error("value is an ambiguous timestamp")), |
| LocalResult::Single(val) => Ok(val), |
| } |
| } |
| |
| impl Decodable for DateTime<FixedOffset> { |
| fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<FixedOffset>, D::Error> { |
| d.read_str()?.parse::<DateTime<FixedOffset>>().map_err(|_| d.error("invalid date and time")) |
| } |
| } |
| |
| #[allow(deprecated)] |
| impl Decodable for TsSeconds<FixedOffset> { |
| #[allow(deprecated)] |
| fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<FixedOffset>, D::Error> { |
| from(FixedOffset::east_opt(0).unwrap().timestamp_opt(d.read_i64()?, 0), d).map(TsSeconds) |
| } |
| } |
| |
| impl Decodable for DateTime<Utc> { |
| fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<Utc>, D::Error> { |
| d.read_str()? |
| .parse::<DateTime<FixedOffset>>() |
| .map(|dt| dt.with_timezone(&Utc)) |
| .map_err(|_| d.error("invalid date and time")) |
| } |
| } |
| |
| /// A [`DateTime`] that can be deserialized from a timestamp |
| /// |
| /// A timestamp here is seconds since the epoch |
| #[derive(Debug)] |
| pub struct TsSeconds<Tz: TimeZone>(DateTime<Tz>); |
| |
| #[allow(deprecated)] |
| impl<Tz: TimeZone> From<TsSeconds<Tz>> for DateTime<Tz> { |
| /// Pull the inner `DateTime<Tz>` out |
| #[allow(deprecated)] |
| fn from(obj: TsSeconds<Tz>) -> DateTime<Tz> { |
| obj.0 |
| } |
| } |
| |
| #[allow(deprecated)] |
| impl<Tz: TimeZone> Deref for TsSeconds<Tz> { |
| type Target = DateTime<Tz>; |
| |
| fn deref(&self) -> &Self::Target { |
| &self.0 |
| } |
| } |
| |
| #[allow(deprecated)] |
| impl Decodable for TsSeconds<Utc> { |
| fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<Utc>, D::Error> { |
| from(Utc.timestamp_opt(d.read_i64()?, 0), d).map(TsSeconds) |
| } |
| } |
| |
| #[cfg(feature = "clock")] |
| impl Decodable for DateTime<Local> { |
| fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<Local>, D::Error> { |
| match d.read_str()?.parse::<DateTime<FixedOffset>>() { |
| Ok(dt) => Ok(dt.with_timezone(&Local)), |
| Err(_) => Err(d.error("invalid date and time")), |
| } |
| } |
| } |
| |
| #[cfg(feature = "clock")] |
| #[allow(deprecated)] |
| impl Decodable for TsSeconds<Local> { |
| #[allow(deprecated)] |
| fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<Local>, D::Error> { |
| from(Utc.timestamp_opt(d.read_i64()?, 0), d).map(|dt| TsSeconds(dt.with_timezone(&Local))) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use crate::datetime::test_encodable_json; |
| use crate::datetime::{test_decodable_json, test_decodable_json_timestamps}; |
| use rustc_serialize::json; |
| |
| #[test] |
| fn test_encodable() { |
| test_encodable_json(json::encode, json::encode); |
| } |
| |
| #[cfg(feature = "clock")] |
| #[test] |
| fn test_decodable() { |
| test_decodable_json(json::decode, json::decode, json::decode); |
| } |
| |
| #[cfg(feature = "clock")] |
| #[test] |
| fn test_decodable_timestamps() { |
| test_decodable_json_timestamps(json::decode, json::decode, json::decode); |
| } |
| } |