From c118a3985fd47dd86dea150d3cee593f1b36d0eb Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 7 Feb 2017 03:08:01 +0900 Subject: [PATCH] Serialization cleanup for 0.3. - Rustc-serialize now uses the same serialization format as Serde. This also means that the older format (naturally derived from the internals) is no longer supported. - Serialization support only existed for rustc-serialize has been (temporarily) removed. This affects `Date` and all individual time zone types. This does *not* affect `DateTime` as it has individual support per `Tz`. Please note that this is considered a temporary solution to avoid stabilizing diverging implementations. Their implementations will likely be reintroduced later. --- src/date.rs | 52 --------- src/datetime.rs | 143 +++++++++++++++---------- src/naive/date.rs | 178 ++++++++++--------------------- src/naive/datetime.rs | 242 ++++++++++++++++-------------------------- src/naive/time.rs | 217 +++++++++++++------------------------ src/offset/fixed.rs | 86 --------------- src/offset/local.rs | 1 - src/offset/utc.rs | 1 - 8 files changed, 311 insertions(+), 609 deletions(-) diff --git a/src/date.rs b/src/date.rs index 7b8e5f1..16374c4 100644 --- a/src/date.rs +++ b/src/date.rs @@ -398,58 +398,6 @@ impl fmt::Display for Date where Tz::Offset: fmt::Display { } } -#[cfg(feature = "rustc-serialize")] -mod rustc_serialize { - use super::Date; - use offset::TimeZone; - use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - - // TODO the current serialization format is NEVER intentionally defined. - // in the future it is likely to be redefined to more sane and reasonable format. - - impl Encodable for Date where Tz::Offset: Encodable { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("Date", 2, |s| { - try!(s.emit_struct_field("date", 0, |s| self.date.encode(s))); - try!(s.emit_struct_field("offset", 1, |s| self.offset.encode(s))); - Ok(()) - }) - } - } - - impl Decodable for Date where Tz::Offset: Decodable { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_struct("Date", 2, |d| { - let date = try!(d.read_struct_field("date", 0, Decodable::decode)); - let offset = try!(d.read_struct_field("offset", 1, Decodable::decode)); - Ok(Date::from_utc(date, offset)) - }) - } - } - - #[test] - fn test_encodable() { - use offset::utc::UTC; - use rustc_serialize::json::encode; - - assert_eq!(encode(&UTC.ymd(2014, 7, 24)).ok(), - Some(r#"{"date":{"ymdf":16501977},"offset":{}}"#.into())); - } - - #[test] - fn test_decodable() { - use offset::utc::UTC; - use rustc_serialize::json; - - let decode = |s: &str| json::decode::>(s); - - assert_eq!(decode(r#"{"date":{"ymdf":16501977},"offset":{}}"#).ok(), - Some(UTC.ymd(2014, 7, 24))); - - assert!(decode(r#"{"date":{"ymdf":0},"offset":{}}"#).is_err()); - } -} - #[cfg(test)] mod tests { use std::fmt; diff --git a/src/datetime.rs b/src/datetime.rs index ff0e58b..62bb55c 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -405,65 +405,108 @@ impl str::FromStr for DateTime { } } +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json(to_string_utc: FUTC, to_string_fixed: FFixed) + where FUTC: Fn(&DateTime) -> Result, + FFixed: Fn(&DateTime) -> Result, + E: ::std::fmt::Debug +{ + assert_eq!(to_string_utc(&UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), + Some(r#""2014-07-24T12:34:06Z""#.into())); + + assert_eq!(to_string_fixed(&FixedOffset::east(3660).ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), + Some(r#""2014-07-24T12:34:06+01:01""#.into())); + assert_eq!(to_string_fixed(&FixedOffset::east(3650).ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), + Some(r#""2014-07-24T12:34:06+01:00:50""#.into())); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json(utc_from_str: FUTC, + fixed_from_str: FFixed, + local_from_str: FLocal) + where FUTC: Fn(&str) -> Result, E>, + FFixed: Fn(&str) -> Result, E>, + FLocal: Fn(&str) -> Result, E>, + E: ::std::fmt::Debug +{ + // should check against the offset as well (the normal DateTime comparison will ignore them) + fn norm(dt: &Option>) -> Option<(&DateTime, &Tz::Offset)> { + dt.as_ref().map(|dt| (dt, dt.offset())) + } + + assert_eq!(norm(&utc_from_str(r#""2014-07-24T12:34:06Z""#).ok()), + norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); + assert_eq!(norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), + norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); + + assert_eq!(norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()), + norm(&Some(FixedOffset::east(0).ymd(2014, 7, 24).and_hms(12, 34, 6)))); + assert_eq!(norm(&fixed_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), + norm(&Some(FixedOffset::east(60*60 + 23*60).ymd(2014, 7, 24).and_hms(13, 57, 6)))); + + // we don't know the exact local offset but we can check that + // the conversion didn't change the instant itself + assert_eq!(local_from_str(r#""2014-07-24T12:34:06Z""#).unwrap(), + UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); + assert_eq!(local_from_str(r#""2014-07-24T13:57:06+01:23""#).unwrap(), + UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); + + assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); + assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::DateTime; use offset::TimeZone; + use offset::utc::UTC; + use offset::local::Local; + use offset::fixed::FixedOffset; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - // TODO the current serialization format is NEVER intentionally defined. - // in the future it is likely to be redefined to more sane and reasonable format. - - impl Encodable for DateTime where Tz::Offset: Encodable { + impl Encodable for DateTime { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("DateTime", 2, |s| { - try!(s.emit_struct_field("datetime", 0, |s| self.datetime.encode(s))); - try!(s.emit_struct_field("offset", 1, |s| self.offset.encode(s))); - Ok(()) - }) + format!("{:?}", self).encode(s) } } - impl Decodable for DateTime where Tz::Offset: Decodable { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_struct("DateTime", 2, |d| { - let datetime = try!(d.read_struct_field("datetime", 0, Decodable::decode)); - let offset = try!(d.read_struct_field("offset", 1, Decodable::decode)); - Ok(DateTime::from_utc(datetime, offset)) - }) + impl Decodable for DateTime { + fn decode(d: &mut D) -> Result, D::Error> { + match d.read_str()?.parse::>() { + Ok(dt) => Ok(dt), + Err(_) => Err(d.error("invalid date and time")), + } } } + impl Decodable for DateTime { + fn decode(d: &mut D) -> Result, D::Error> { + match d.read_str()?.parse::>() { + Ok(dt) => Ok(dt.with_timezone(&UTC)), + Err(_) => Err(d.error("invalid date and time")), + } + } + } + + impl Decodable for DateTime { + fn decode(d: &mut D) -> Result, D::Error> { + match d.read_str()?.parse::>() { + Ok(dt) => Ok(dt.with_timezone(&Local)), + Err(_) => Err(d.error("invalid date and time")), + } + } + } + + #[cfg(test)] use rustc_serialize::json; + #[test] fn test_encodable() { - use offset::utc::UTC; - use rustc_serialize::json::encode; - - assert_eq!( - encode(&UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(concat!(r#"{"datetime":{"date":{"ymdf":16501977},"#, - r#""time":{"secs":45246,"frac":0}},"#, - r#""offset":{}}"#).into())); + super::test_encodable_json(json::encode, json::encode); } #[test] fn test_decodable() { - use offset::utc::UTC; - use rustc_serialize::json; - - let decode = |s: &str| json::decode::>(s); - - assert_eq!( - decode(r#"{"datetime":{"date":{"ymdf":16501977}, - "time":{"secs":45246,"frac":0}}, - "offset":{}}"#).ok(), - Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6))); - - assert_eq!( - decode(r#"{"datetime":{"date":{"ymdf":0}, - "time":{"secs":0,"frac":0}}, - "offset":{}}"#).ok(), - None); + super::test_decodable_json(json::decode, json::decode, json::decode); } } @@ -475,14 +518,11 @@ mod serde { use offset::utc::UTC; use offset::local::Local; use offset::fixed::FixedOffset; - use std::fmt::Display; use serde::{ser, de}; // TODO not very optimized for space (binary formats would want something better) - impl ser::Serialize for DateTime - where Tz::Offset: Display - { + impl ser::Serialize for DateTime { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { @@ -537,22 +577,13 @@ mod serde { #[test] fn test_serde_serialize() { - use self::serde_json::to_string; - - assert_eq!(to_string(&UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#""2014-07-24T12:34:06Z""#.into())); + super::test_encodable_json(self::serde_json::to_string, self::serde_json::to_string); } #[test] fn test_serde_deserialize() { - use self::serde_json; - - let from_str = |s: &str| serde_json::from_str::>(s); - - assert_eq!(from_str(r#""2014-07-24T12:34:06Z""#).ok(), - Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6))); - - assert!(from_str(r#""2014-07-32T12:34:06Z""#).is_err()); + super::test_decodable_json(self::serde_json::from_str, self::serde_json::from_str, + self::serde_json::from_str); } #[test] diff --git a/src/naive/date.rs b/src/naive/date.rs index a860ef7..f8fff8f 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -136,26 +136,6 @@ impl NaiveDate { NaiveDate::from_of(year, mdf.to_of()) } - /// Makes a new `NaiveDate` from the serialized representation. - /// Used for serialization formats. - #[cfg(feature = "rustc-serialize")] - fn from_serialized(ymdf: i32) -> Option { - // check if the year flag is correct - if (ymdf & 0b1111) as u8 != YearFlags::from_year(ymdf >> 13).0 { return None; } - - // check if the ordinal is in the range - let date = NaiveDate { ymdf: ymdf }; - if !date.of().valid() { return None; } - - Some(date) - } - - /// Returns a serialized representation of this `NaiveDate`. - #[cfg(feature = "rustc-serialize")] - fn to_serialized(&self) -> i32 { - self.ymdf - } - /// Makes a new `NaiveDate` from the [calendar date](./index.html#calendar-date) /// (year, month and day). /// @@ -1497,83 +1477,84 @@ impl str::FromStr for NaiveDate { } } +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json(to_string: F) + where F: Fn(&NaiveDate) -> Result, E: ::std::fmt::Debug +{ + assert_eq!(to_string(&NaiveDate::from_ymd(2014, 7, 24)).ok(), + Some(r#""2014-07-24""#.into())); + assert_eq!(to_string(&NaiveDate::from_ymd(0, 1, 1)).ok(), + Some(r#""0000-01-01""#.into())); + assert_eq!(to_string(&NaiveDate::from_ymd(-1, 12, 31)).ok(), + Some(r#""-0001-12-31""#.into())); + assert_eq!(to_string(&MIN).ok(), + Some(r#""-262144-01-01""#.into())); + assert_eq!(to_string(&MAX).ok(), + Some(r#""+262143-12-31""#.into())); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json(from_str: F) + where F: Fn(&str) -> Result, E: ::std::fmt::Debug +{ + use std::{i32, i64}; + + assert_eq!(from_str(r#""2016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); + assert_eq!(from_str(r#""2016-7-8""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); + assert_eq!(from_str(r#""+002016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); + assert_eq!(from_str(r#""0000-01-01""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); + assert_eq!(from_str(r#""0-1-1""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); + assert_eq!(from_str(r#""-0001-12-31""#).ok(), Some(NaiveDate::from_ymd(-1, 12, 31))); + assert_eq!(from_str(r#""-262144-01-01""#).ok(), Some(MIN)); + assert_eq!(from_str(r#""+262143-12-31""#).ok(), Some(MAX)); + + // bad formats + assert!(from_str(r#""""#).is_err()); + assert!(from_str(r#""20001231""#).is_err()); + assert!(from_str(r#""2000-00-00""#).is_err()); + assert!(from_str(r#""2000-02-30""#).is_err()); + assert!(from_str(r#""2001-02-29""#).is_err()); + assert!(from_str(r#""2002-002-28""#).is_err()); + assert!(from_str(r#""yyyy-mm-dd""#).is_err()); + assert!(from_str(r#"0"#).is_err()); + assert!(from_str(r#"20.01"#).is_err()); + assert!(from_str(&i32::MIN.to_string()).is_err()); + assert!(from_str(&i32::MAX.to_string()).is_err()); + assert!(from_str(&i64::MIN.to_string()).is_err()); + assert!(from_str(&i64::MAX.to_string()).is_err()); + assert!(from_str(r#"{}"#).is_err()); + // pre-0.3.0 rustc-serialize format is now invalid + assert!(from_str(r#"{"ymdf":20}"#).is_err()); + assert!(from_str(r#"null"#).is_err()); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::NaiveDate; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - // TODO the current serialization format is NEVER intentionally defined. - // this basically follows the automatically generated implementation for those traits, - // plus manual verification steps for avoiding security problem. - // in the future it is likely to be redefined to more sane and reasonable format. - impl Encodable for NaiveDate { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - let ymdf = self.to_serialized(); - s.emit_struct("NaiveDate", 1, |s| { - try!(s.emit_struct_field("ymdf", 0, |s| ymdf.encode(s))); - Ok(()) - }) + format!("{:?}", self).encode(s) } } impl Decodable for NaiveDate { fn decode(d: &mut D) -> Result { - d.read_struct("NaiveDate", 1, |d| { - let ymdf = try!(d.read_struct_field("ymdf", 0, Decodable::decode)); - NaiveDate::from_serialized(ymdf).ok_or_else(|| d.error("invalid date")) - }) + d.read_str()?.parse().map_err(|_| d.error("invalid date")) } } + #[cfg(test)] use rustc_serialize::json; + #[test] fn test_encodable() { - use rustc_serialize::json::encode; - - assert_eq!(encode(&NaiveDate::from_ymd(2016, 7, 8)).ok(), - Some(r#"{"ymdf":16518115}"#.into())); - assert_eq!(encode(&NaiveDate::from_ymd(0, 1, 1)).ok(), - Some(r#"{"ymdf":20}"#.into())); - assert_eq!(encode(&NaiveDate::from_ymd(-1, 12, 31)).ok(), - Some(r#"{"ymdf":-2341}"#.into())); - assert_eq!(encode(&super::MIN).ok(), - Some(r#"{"ymdf":-2147483625}"#.into())); - assert_eq!(encode(&super::MAX).ok(), - Some(r#"{"ymdf":2147481311}"#.into())); + super::test_encodable_json(json::encode); } #[test] fn test_decodable() { - use rustc_serialize::json; - use std::{i32, i64}; - - let decode = |s: &str| json::decode::(s); - - assert_eq!(decode(r#"{"ymdf":16518115}"#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(decode(r#"{"ymdf":20}"#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); - assert_eq!(decode(r#"{"ymdf":-2341}"#).ok(), Some(NaiveDate::from_ymd(-1, 12, 31))); - assert_eq!(decode(r#"{"ymdf":-2147483625}"#).ok(), Some(super::MIN)); - assert_eq!(decode(r#"{"ymdf":2147481311}"#).ok(), Some(super::MAX)); - - // some extreme values and zero are always invalid - assert!(decode(r#"{"ymdf":0}"#).is_err()); - assert!(decode(r#"{"ymdf":1}"#).is_err()); - assert!(decode(r#"{"ymdf":-1}"#).is_err()); - assert!(decode(&format!(r#"{{"ymdf":{}}}"#, i32::MIN)).is_err()); - assert!(decode(&format!(r#"{{"ymdf":{}}}"#, i32::MAX)).is_err()); - assert!(decode(&format!(r#"{{"ymdf":{}}}"#, i64::MIN)).is_err()); - assert!(decode(&format!(r#"{{"ymdf":{}}}"#, i64::MAX)).is_err()); - - // bad formats - assert!(decode(r#"{"ymdf":20.01}"#).is_err()); - assert!(decode(r#"{"ymdf":"string"}"#).is_err()); - assert!(decode(r#"{"ymdf":null}"#).is_err()); - assert!(decode(r#"{}"#).is_err()); - assert!(decode(r#"{"date":20}"#).is_err()); - assert!(decode(r#"20"#).is_err()); - assert!(decode(r#""string""#).is_err()); - assert!(decode(r#""2016-07-08""#).is_err()); // :( - assert!(decode(r#"null"#).is_err()); + super::test_decodable_json(json::decode); } } @@ -1623,53 +1604,12 @@ mod serde { #[test] fn test_serde_serialize() { - use self::serde_json::to_string; - - assert_eq!(to_string(&NaiveDate::from_ymd(2014, 7, 24)).ok(), - Some(r#""2014-07-24""#.into())); - assert_eq!(to_string(&NaiveDate::from_ymd(0, 1, 1)).ok(), - Some(r#""0000-01-01""#.into())); - assert_eq!(to_string(&NaiveDate::from_ymd(-1, 12, 31)).ok(), - Some(r#""-0001-12-31""#.into())); - assert_eq!(to_string(&super::MIN).ok(), - Some(r#""-262144-01-01""#.into())); - assert_eq!(to_string(&super::MAX).ok(), - Some(r#""+262143-12-31""#.into())); + super::test_encodable_json(self::serde_json::to_string); } #[test] fn test_serde_deserialize() { - use self::serde_json; - use std::{i32, i64}; - - let from_str = |s: &str| serde_json::from_str::(s); - - assert_eq!(from_str(r#""2016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(from_str(r#""2016-7-8""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(from_str(r#""+002016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(from_str(r#""0000-01-01""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); - assert_eq!(from_str(r#""0-1-1""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); - assert_eq!(from_str(r#""-0001-12-31""#).ok(), Some(NaiveDate::from_ymd(-1, 12, 31))); - assert_eq!(from_str(r#""-262144-01-01""#).ok(), Some(super::MIN)); - assert_eq!(from_str(r#""+262143-12-31""#).ok(), Some(super::MAX)); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""20001231""#).is_err()); - assert!(from_str(r#""2000-00-00""#).is_err()); - assert!(from_str(r#""2000-02-30""#).is_err()); - assert!(from_str(r#""2001-02-29""#).is_err()); - assert!(from_str(r#""2002-002-28""#).is_err()); - assert!(from_str(r#""yyyy-mm-dd""#).is_err()); - assert!(from_str(r#"0"#).is_err()); - assert!(from_str(r#"20.01"#).is_err()); - assert!(from_str(&i32::MIN.to_string()).is_err()); - assert!(from_str(&i32::MAX.to_string()).is_err()); - assert!(from_str(&i64::MIN.to_string()).is_err()); - assert!(from_str(&i64::MAX.to_string()).is_err()); - assert!(from_str(r#"{}"#).is_err()); - assert!(from_str(r#"{"ymdf":20}"#).is_err()); // :( - assert!(from_str(r#"null"#).is_err()); + super::test_decodable_json(self::serde_json::from_str); } #[test] diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 65f7e91..b60a565 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1394,105 +1394,116 @@ impl str::FromStr for NaiveDateTime { } } +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json(to_string: F) + where F: Fn(&NaiveDateTime) -> Result, E: ::std::fmt::Debug +{ + use naive::date; + + assert_eq!( + to_string(&NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)).ok(), + Some(r#""2016-07-08T09:10:48.090""#.into())); + assert_eq!( + to_string(&NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), + Some(r#""2014-07-24T12:34:06""#.into())); + assert_eq!( + to_string(&NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000)).ok(), + Some(r#""0000-01-01T00:00:60""#.into())); + assert_eq!( + to_string(&NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7)).ok(), + Some(r#""-0001-12-31T23:59:59.000000007""#.into())); + assert_eq!( + to_string(&date::MIN.and_hms(0, 0, 0)).ok(), + Some(r#""-262144-01-01T00:00:00""#.into())); + assert_eq!( + to_string(&date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999)).ok(), + Some(r#""+262143-12-31T23:59:60.999999999""#.into())); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json(from_str: F) + where F: Fn(&str) -> Result, E: ::std::fmt::Debug +{ + use naive::date; + + assert_eq!( + from_str(r#""2016-07-08T09:10:48.090""#).ok(), + Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); + assert_eq!( + from_str(r#""2016-7-8T9:10:48.09""#).ok(), + Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); + assert_eq!( + from_str(r#""2014-07-24T12:34:06""#).ok(), + Some(NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6))); + assert_eq!( + from_str(r#""0000-01-01T00:00:60""#).ok(), + Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); + assert_eq!( + from_str(r#""0-1-1T0:0:60""#).ok(), + Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); + assert_eq!( + from_str(r#""-0001-12-31T23:59:59.000000007""#).ok(), + Some(NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7))); + assert_eq!( + from_str(r#""-262144-01-01T00:00:00""#).ok(), + Some(date::MIN.and_hms(0, 0, 0))); + assert_eq!( + from_str(r#""+262143-12-31T23:59:60.999999999""#).ok(), + Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); + assert_eq!( + from_str(r#""+262143-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored + Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); + + // bad formats + assert!(from_str(r#""""#).is_err()); + assert!(from_str(r#""2016-07-08""#).is_err()); + assert!(from_str(r#""09:10:48.090""#).is_err()); + assert!(from_str(r#""20160708T091048.090""#).is_err()); + assert!(from_str(r#""2000-00-00T00:00:00""#).is_err()); + assert!(from_str(r#""2000-02-30T00:00:00""#).is_err()); + assert!(from_str(r#""2001-02-29T00:00:00""#).is_err()); + assert!(from_str(r#""2002-02-28T24:00:00""#).is_err()); + assert!(from_str(r#""2002-02-28T23:60:00""#).is_err()); + assert!(from_str(r#""2002-02-28T23:59:61""#).is_err()); + assert!(from_str(r#""2016-07-08T09:10:48,090""#).is_err()); + assert!(from_str(r#""2016-07-08 09:10:48.090""#).is_err()); + assert!(from_str(r#""2016-007-08T09:10:48.090""#).is_err()); + assert!(from_str(r#""yyyy-mm-ddThh:mm:ss.fffffffff""#).is_err()); + assert!(from_str(r#"0"#).is_err()); + assert!(from_str(r#"20160708000000"#).is_err()); + assert!(from_str(r#"{}"#).is_err()); + // pre-0.3.0 rustc-serialize format is now invalid + assert!(from_str(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":0}}"#).is_err()); + assert!(from_str(r#"null"#).is_err()); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::NaiveDateTime; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - // TODO the current serialization format is NEVER intentionally defined. - // in the future it is likely to be redefined to more sane and reasonable format. - impl Encodable for NaiveDateTime { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("NaiveDateTime", 2, |s| { - try!(s.emit_struct_field("date", 0, |s| self.date.encode(s))); - try!(s.emit_struct_field("time", 1, |s| self.time.encode(s))); - Ok(()) - }) + format!("{:?}", self).encode(s) } } impl Decodable for NaiveDateTime { fn decode(d: &mut D) -> Result { - d.read_struct("NaiveDateTime", 2, |d| { - let date = try!(d.read_struct_field("date", 0, Decodable::decode)); - let time = try!(d.read_struct_field("time", 1, Decodable::decode)); - Ok(NaiveDateTime::new(date, time)) - }) + d.read_str()?.parse().map_err(|_| d.error("invalid date and time")) } } + #[cfg(test)] use rustc_serialize::json; + #[test] fn test_encodable() { - use naive::date::{self, NaiveDate}; - use rustc_serialize::json::encode; - - assert_eq!( - encode(&NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)).ok(), - Some(r#"{"date":{"ymdf":16518115},"time":{"secs":33048,"frac":90000000}}"#.into())); - assert_eq!( - encode(&NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#"{"date":{"ymdf":16501977},"time":{"secs":45246,"frac":0}}"#.into())); - assert_eq!( - encode(&NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000)).ok(), - Some(r#"{"date":{"ymdf":20},"time":{"secs":59,"frac":1000000000}}"#.into())); - assert_eq!( - encode(&NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7)).ok(), - Some(r#"{"date":{"ymdf":-2341},"time":{"secs":86399,"frac":7}}"#.into())); - assert_eq!( - encode(&date::MIN.and_hms(0, 0, 0)).ok(), - Some(r#"{"date":{"ymdf":-2147483625},"time":{"secs":0,"frac":0}}"#.into())); - assert_eq!( - encode(&date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999)).ok(), - Some(r#"{"date":{"ymdf":2147481311},"time":{"secs":86399,"frac":1999999999}}"#.into())); + super::test_encodable_json(json::encode); } #[test] fn test_decodable() { - use naive::date::{self, NaiveDate}; - use rustc_serialize::json; - - let decode = |s: &str| json::decode::(s); - - assert_eq!( - decode(r#"{"date":{"ymdf":16518115},"time":{"secs":33048,"frac":90000000}}"#).ok(), - Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); - assert_eq!( - decode(r#"{"time":{"frac":0,"secs":45246},"date":{"ymdf":16501977}}"#).ok(), - Some(NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6))); - assert_eq!( - decode(r#"{"date": {"ymdf": 20}, - "time": {"secs": 59, - "frac": 1000000000}}"#).ok(), - Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); - assert_eq!( - decode(r#"{"date":{"ymdf":-2341},"time":{"secs":86399,"frac":7}}"#).ok(), - Some(NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7))); - assert_eq!( - decode(r#"{"date":{"ymdf":-2147483625},"time":{"secs":0,"frac":0}}"#).ok(), - Some(date::MIN.and_hms(0, 0, 0))); - assert_eq!( - decode(r#"{"date":{"ymdf":2147481311},"time":{"secs":86399,"frac":1999999999}}"#).ok(), - Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); - - // bad formats - assert!(decode(r#"{"date":{},"time":{}}"#).is_err()); - assert!(decode(r#"{"date":{"ymdf":0},"time":{"secs":0,"frac":0}}"#).is_err()); - assert!(decode(r#"{"date":{"ymdf":20},"time":{"secs":86400,"frac":0}}"#).is_err()); - assert!(decode(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":-1}}"#).is_err()); - assert!(decode(r#"{"date":20,"time":{"secs":0,"frac":0}}"#).is_err()); - assert!(decode(r#"{"date":"2016-08-04","time":"01:02:03.456"}"#).is_err()); - assert!(decode(r#"{"date":{"ymdf":20}}"#).is_err()); - assert!(decode(r#"{"time":{"secs":0,"frac":0}}"#).is_err()); - assert!(decode(r#"{"ymdf":20}"#).is_err()); - assert!(decode(r#"{"secs":0,"frac":0}"#).is_err()); - assert!(decode(r#"{}"#).is_err()); - assert!(decode(r#"0"#).is_err()); - assert!(decode(r#"-1"#).is_err()); - assert!(decode(r#""string""#).is_err()); - assert!(decode(r#""2016-08-04T12:34:56""#).is_err()); // :( - assert!(decode(r#""2016-08-04T12:34:56.789""#).is_err()); // :( - assert!(decode(r#"null"#).is_err()); + super::test_decodable_json(json::decode); } } @@ -1542,83 +1553,12 @@ mod serde { #[test] fn test_serde_serialize() { - use naive::date::{self, NaiveDate}; - use self::serde_json::to_string; - - assert_eq!( - to_string(&NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)).ok(), - Some(r#""2016-07-08T09:10:48.090""#.into())); - assert_eq!( - to_string(&NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#""2014-07-24T12:34:06""#.into())); - assert_eq!( - to_string(&NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000)).ok(), - Some(r#""0000-01-01T00:00:60""#.into())); - assert_eq!( - to_string(&NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7)).ok(), - Some(r#""-0001-12-31T23:59:59.000000007""#.into())); - assert_eq!( - to_string(&date::MIN.and_hms(0, 0, 0)).ok(), - Some(r#""-262144-01-01T00:00:00""#.into())); - assert_eq!( - to_string(&date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999)).ok(), - Some(r#""+262143-12-31T23:59:60.999999999""#.into())); + super::test_encodable_json(self::serde_json::to_string); } #[test] fn test_serde_deserialize() { - use naive::date::{self, NaiveDate}; - - let from_str = |s: &str| serde_json::from_str::(s); - - assert_eq!( - from_str(r#""2016-07-08T09:10:48.090""#).ok(), - Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); - assert_eq!( - from_str(r#""2016-7-8T9:10:48.09""#).ok(), - Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); - assert_eq!( - from_str(r#""2014-07-24T12:34:06""#).ok(), - Some(NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6))); - assert_eq!( - from_str(r#""0000-01-01T00:00:60""#).ok(), - Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); - assert_eq!( - from_str(r#""0-1-1T0:0:60""#).ok(), - Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); - assert_eq!( - from_str(r#""-0001-12-31T23:59:59.000000007""#).ok(), - Some(NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7))); - assert_eq!( - from_str(r#""-262144-01-01T00:00:00""#).ok(), - Some(date::MIN.and_hms(0, 0, 0))); - assert_eq!( - from_str(r#""+262143-12-31T23:59:60.999999999""#).ok(), - Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); - assert_eq!( - from_str(r#""+262143-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored - Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""2016-07-08""#).is_err()); - assert!(from_str(r#""09:10:48.090""#).is_err()); - assert!(from_str(r#""20160708T091048.090""#).is_err()); - assert!(from_str(r#""2000-00-00T00:00:00""#).is_err()); - assert!(from_str(r#""2000-02-30T00:00:00""#).is_err()); - assert!(from_str(r#""2001-02-29T00:00:00""#).is_err()); - assert!(from_str(r#""2002-02-28T24:00:00""#).is_err()); - assert!(from_str(r#""2002-02-28T23:60:00""#).is_err()); - assert!(from_str(r#""2002-02-28T23:59:61""#).is_err()); - assert!(from_str(r#""2016-07-08T09:10:48,090""#).is_err()); - assert!(from_str(r#""2016-07-08 09:10:48.090""#).is_err()); - assert!(from_str(r#""2016-007-08T09:10:48.090""#).is_err()); - assert!(from_str(r#""yyyy-mm-ddThh:mm:ss.fffffffff""#).is_err()); - assert!(from_str(r#"0"#).is_err()); - assert!(from_str(r#"20160708000000"#).is_err()); - assert!(from_str(r#"{}"#).is_err()); - assert!(from_str(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":0}}"#).is_err()); // :( - assert!(from_str(r#"null"#).is_err()); + super::test_decodable_json(self::serde_json::from_str); } #[test] diff --git a/src/naive/time.rs b/src/naive/time.rs index 29a0a4b..5171358 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -178,24 +178,6 @@ pub struct NaiveTime { } impl NaiveTime { - /// Makes a new `NaiveTime` from the serialized representation. - /// Used for serialization formats. - #[cfg(feature = "rustc-serialize")] - fn from_serialized(secs: u32, frac: u32) -> Option { - // check if the values are in the range - if secs >= 86400 { return None; } - if frac >= 2_000_000_000 { return None; } - - let time = NaiveTime { secs: secs, frac: frac }; - Some(time) - } - - /// Returns a serialized representation of this `NaiveDate`. - #[cfg(feature = "rustc-serialize")] - fn to_serialized(&self) -> (u32, u32) { - (self.secs, self.frac) - } - /// Makes a new `NaiveTime` from hour, minute and second. /// /// No [leap second](./index.html#leap-second-handling) is allowed here; @@ -1285,95 +1267,99 @@ impl str::FromStr for NaiveTime { } } +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json(to_string: F) + where F: Fn(&NaiveTime) -> Result, E: ::std::fmt::Debug +{ + assert_eq!(to_string(&NaiveTime::from_hms(0, 0, 0)).ok(), + Some(r#""00:00:00""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_milli(0, 0, 0, 950)).ok(), + Some(r#""00:00:00.950""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_milli(0, 0, 59, 1_000)).ok(), + Some(r#""00:00:60""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms(0, 1, 2)).ok(), + Some(r#""00:01:02""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_nano(3, 5, 7, 98765432)).ok(), + Some(r#""03:05:07.098765432""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms(7, 8, 9)).ok(), + Some(r#""07:08:09""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_micro(12, 34, 56, 789)).ok(), + Some(r#""12:34:56.000789""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999)).ok(), + Some(r#""23:59:60.999999999""#.into())); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json(from_str: F) + where F: Fn(&str) -> Result, E: ::std::fmt::Debug +{ + assert_eq!(from_str(r#""00:00:00""#).ok(), + Some(NaiveTime::from_hms(0, 0, 0))); + assert_eq!(from_str(r#""0:0:0""#).ok(), + Some(NaiveTime::from_hms(0, 0, 0))); + assert_eq!(from_str(r#""00:00:00.950""#).ok(), + Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); + assert_eq!(from_str(r#""0:0:0.95""#).ok(), + Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); + assert_eq!(from_str(r#""00:00:60""#).ok(), + Some(NaiveTime::from_hms_milli(0, 0, 59, 1_000))); + assert_eq!(from_str(r#""00:01:02""#).ok(), + Some(NaiveTime::from_hms(0, 1, 2))); + assert_eq!(from_str(r#""03:05:07.098765432""#).ok(), + Some(NaiveTime::from_hms_nano(3, 5, 7, 98765432))); + assert_eq!(from_str(r#""07:08:09""#).ok(), + Some(NaiveTime::from_hms(7, 8, 9))); + assert_eq!(from_str(r#""12:34:56.000789""#).ok(), + Some(NaiveTime::from_hms_micro(12, 34, 56, 789))); + assert_eq!(from_str(r#""23:59:60.999999999""#).ok(), + Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); + assert_eq!(from_str(r#""23:59:60.9999999999997""#).ok(), // excess digits are ignored + Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); + + // bad formats + assert!(from_str(r#""""#).is_err()); + assert!(from_str(r#""000000""#).is_err()); + assert!(from_str(r#""00:00:61""#).is_err()); + assert!(from_str(r#""00:60:00""#).is_err()); + assert!(from_str(r#""24:00:00""#).is_err()); + assert!(from_str(r#""23:59:59,1""#).is_err()); + assert!(from_str(r#""012:34:56""#).is_err()); + assert!(from_str(r#""hh:mm:ss""#).is_err()); + assert!(from_str(r#"0"#).is_err()); + assert!(from_str(r#"86399"#).is_err()); + assert!(from_str(r#"{}"#).is_err()); + // pre-0.3.0 rustc-serialize format is now invalid + assert!(from_str(r#"{"secs":0,"frac":0}"#).is_err()); + assert!(from_str(r#"null"#).is_err()); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::NaiveTime; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - // TODO the current serialization format is NEVER intentionally defined. - // this basically follows the automatically generated implementation for those traits, - // plus manual verification steps for avoiding security problem. - // in the future it is likely to be redefined to more sane and reasonable format. - impl Encodable for NaiveTime { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - let (secs, frac) = self.to_serialized(); - s.emit_struct("NaiveTime", 2, |s| { - try!(s.emit_struct_field("secs", 0, |s| secs.encode(s))); - try!(s.emit_struct_field("frac", 1, |s| frac.encode(s))); - Ok(()) - }) + format!("{:?}", self).encode(s) } } impl Decodable for NaiveTime { fn decode(d: &mut D) -> Result { - d.read_struct("NaiveTime", 2, |d| { - let secs = try!(d.read_struct_field("secs", 0, Decodable::decode)); - let frac = try!(d.read_struct_field("frac", 1, Decodable::decode)); - NaiveTime::from_serialized(secs, frac).ok_or_else(|| d.error("invalid time")) - }) + d.read_str()?.parse().map_err(|_| d.error("invalid time")) } } + #[cfg(test)] use rustc_serialize::json; + #[test] fn test_encodable() { - use rustc_serialize::json::encode; - - assert_eq!(encode(&NaiveTime::from_hms(0, 0, 0)).ok(), - Some(r#"{"secs":0,"frac":0}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms_milli(0, 0, 0, 950)).ok(), - Some(r#"{"secs":0,"frac":950000000}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms_milli(0, 0, 59, 1_000)).ok(), - Some(r#"{"secs":59,"frac":1000000000}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms(0, 1, 2)).ok(), - Some(r#"{"secs":62,"frac":0}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms(7, 8, 9)).ok(), - Some(r#"{"secs":25689,"frac":0}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms_micro(12, 34, 56, 789)).ok(), - Some(r#"{"secs":45296,"frac":789000}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999)).ok(), - Some(r#"{"secs":86399,"frac":1999999999}"#.into())); + super::test_encodable_json(json::encode); } #[test] fn test_decodable() { - use rustc_serialize::json; - - let decode = |s: &str| json::decode::(s); - - assert_eq!(decode(r#"{"secs":0,"frac":0}"#).ok(), - Some(NaiveTime::from_hms(0, 0, 0))); - assert_eq!(decode(r#"{"frac":950000000,"secs":0}"#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); - assert_eq!(decode(r#"{"secs":59,"frac":1000000000}"#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 59, 1_000))); - assert_eq!(decode(r#"{"frac": 0, - "secs": 62}"#).ok(), - Some(NaiveTime::from_hms(0, 1, 2))); - assert_eq!(decode(r#"{"secs":25689,"frac":0}"#).ok(), - Some(NaiveTime::from_hms(7, 8, 9))); - assert_eq!(decode(r#"{"secs":45296,"frac":789000}"#).ok(), - Some(NaiveTime::from_hms_micro(12, 34, 56, 789))); - assert_eq!(decode(r#"{"secs":86399,"frac":1999999999}"#).ok(), - Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); - - // bad formats - assert!(decode(r#"{"secs":0,"frac":-1}"#).is_err()); - assert!(decode(r#"{"secs":-1,"frac":0}"#).is_err()); - assert!(decode(r#"{"secs":86400,"frac":0}"#).is_err()); - assert!(decode(r#"{"secs":0,"frac":2000000000}"#).is_err()); - assert!(decode(r#"{"secs":0}"#).is_err()); - assert!(decode(r#"{"frac":0}"#).is_err()); - assert!(decode(r#"{"secs":0.3,"frac":0}"#).is_err()); - assert!(decode(r#"{"secs":0,"frac":0.4}"#).is_err()); - assert!(decode(r#"{}"#).is_err()); - assert!(decode(r#"0"#).is_err()); - assert!(decode(r#"86399"#).is_err()); - assert!(decode(r#""string""#).is_err()); - assert!(decode(r#""12:34:56""#).is_err()); // :( - assert!(decode(r#""12:34:56.789""#).is_err()); // :( - assert!(decode(r#"null"#).is_err()); + super::test_decodable_json(json::decode); } } @@ -1424,67 +1410,12 @@ mod serde { #[test] fn test_serde_serialize() { - use self::serde_json::to_string; - - assert_eq!(to_string(&NaiveTime::from_hms(0, 0, 0)).ok(), - Some(r#""00:00:00""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_milli(0, 0, 0, 950)).ok(), - Some(r#""00:00:00.950""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_milli(0, 0, 59, 1_000)).ok(), - Some(r#""00:00:60""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms(0, 1, 2)).ok(), - Some(r#""00:01:02""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_nano(3, 5, 7, 98765432)).ok(), - Some(r#""03:05:07.098765432""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms(7, 8, 9)).ok(), - Some(r#""07:08:09""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_micro(12, 34, 56, 789)).ok(), - Some(r#""12:34:56.000789""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999)).ok(), - Some(r#""23:59:60.999999999""#.into())); + super::test_encodable_json(self::serde_json::to_string); } #[test] fn test_serde_deserialize() { - let from_str = |s: &str| serde_json::from_str::(s); - - assert_eq!(from_str(r#""00:00:00""#).ok(), - Some(NaiveTime::from_hms(0, 0, 0))); - assert_eq!(from_str(r#""0:0:0""#).ok(), - Some(NaiveTime::from_hms(0, 0, 0))); - assert_eq!(from_str(r#""00:00:00.950""#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); - assert_eq!(from_str(r#""0:0:0.95""#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); - assert_eq!(from_str(r#""00:00:60""#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 59, 1_000))); - assert_eq!(from_str(r#""00:01:02""#).ok(), - Some(NaiveTime::from_hms(0, 1, 2))); - assert_eq!(from_str(r#""03:05:07.098765432""#).ok(), - Some(NaiveTime::from_hms_nano(3, 5, 7, 98765432))); - assert_eq!(from_str(r#""07:08:09""#).ok(), - Some(NaiveTime::from_hms(7, 8, 9))); - assert_eq!(from_str(r#""12:34:56.000789""#).ok(), - Some(NaiveTime::from_hms_micro(12, 34, 56, 789))); - assert_eq!(from_str(r#""23:59:60.999999999""#).ok(), - Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); - assert_eq!(from_str(r#""23:59:60.9999999999997""#).ok(), // excess digits are ignored - Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""000000""#).is_err()); - assert!(from_str(r#""00:00:61""#).is_err()); - assert!(from_str(r#""00:60:00""#).is_err()); - assert!(from_str(r#""24:00:00""#).is_err()); - assert!(from_str(r#""23:59:59,1""#).is_err()); - assert!(from_str(r#""012:34:56""#).is_err()); - assert!(from_str(r#""hh:mm:ss""#).is_err()); - assert!(from_str(r#"0"#).is_err()); - assert!(from_str(r#"86399"#).is_err()); - assert!(from_str(r#"{}"#).is_err()); - assert!(from_str(r#"{"secs":0,"frac":0}"#).is_err()); // :( - assert!(from_str(r#"null"#).is_err()); + super::test_decodable_json(self::serde_json::from_str); } #[test] diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index 8c545d2..885741a 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -23,23 +23,6 @@ pub struct FixedOffset { } impl FixedOffset { - /// Makes a new `FixedOffset` from the serialized representation. - /// Used for serialization formats. - #[cfg(feature = "rustc-serialize")] - fn from_serialized(secs: i32) -> Option { - // check if the values are in the range - if secs <= -86400 || 86400 <= secs { return None; } - - let offset = FixedOffset { local_minus_utc: secs }; - Some(offset) - } - - /// Returns a serialized representation of this `FixedOffset`. - #[cfg(feature = "rustc-serialize")] - fn to_serialized(&self) -> i32 { - self.local_minus_utc - } - /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference. /// The negative `secs` means the Western Hemisphere. /// @@ -139,72 +122,3 @@ impl fmt::Display for FixedOffset { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) } } -#[cfg(feature = "rustc-serialize")] -mod rustc_serialize { - use super::FixedOffset; - use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - - // TODO the current serialization format is NEVER intentionally defined. - // this basically follows the automatically generated implementation for those traits, - // plus manual verification steps for avoiding security problem. - // in the future it is likely to be redefined to more sane and reasonable format. - - impl Encodable for FixedOffset { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - let secs = self.to_serialized(); - s.emit_struct("FixedOffset", 1, |s| { - try!(s.emit_struct_field("local_minus_utc", 0, |s| secs.encode(s))); - Ok(()) - }) - } - } - - impl Decodable for FixedOffset { - fn decode(d: &mut D) -> Result { - d.read_struct("FixedOffset", 1, |d| { - let secs = try!(d.read_struct_field("local_minus_utc", 0, Decodable::decode)); - FixedOffset::from_serialized(secs).ok_or_else(|| d.error("invalid offset")) - }) - } - } - - #[test] - fn test_encodable() { - use rustc_serialize::json::encode; - - assert_eq!(encode(&FixedOffset::east(0)).ok(), - Some(r#"{"local_minus_utc":0}"#.into())); - assert_eq!(encode(&FixedOffset::east(1234)).ok(), - Some(r#"{"local_minus_utc":1234}"#.into())); - assert_eq!(encode(&FixedOffset::east(86399)).ok(), - Some(r#"{"local_minus_utc":86399}"#.into())); - assert_eq!(encode(&FixedOffset::west(1234)).ok(), - Some(r#"{"local_minus_utc":-1234}"#.into())); - assert_eq!(encode(&FixedOffset::west(86399)).ok(), - Some(r#"{"local_minus_utc":-86399}"#.into())); - } - - #[test] - fn test_decodable() { - use rustc_serialize::json; - - let decode = |s: &str| json::decode::(s); - - assert_eq!(decode(r#"{"local_minus_utc":0}"#).ok(), Some(FixedOffset::east(0))); - assert_eq!(decode(r#"{"local_minus_utc": 1234}"#).ok(), Some(FixedOffset::east(1234))); - assert_eq!(decode(r#"{"local_minus_utc":86399}"#).ok(), Some(FixedOffset::east(86399))); - assert_eq!(decode(r#"{"local_minus_utc":-1234}"#).ok(), Some(FixedOffset::west(1234))); - assert_eq!(decode(r#"{"local_minus_utc":-86399}"#).ok(), Some(FixedOffset::west(86399))); - - assert!(decode(r#"{"local_minus_utc":86400}"#).is_err()); - assert!(decode(r#"{"local_minus_utc":-86400}"#).is_err()); - assert!(decode(r#"{"local_minus_utc":0.1}"#).is_err()); - assert!(decode(r#"{"local_minus_utc":null}"#).is_err()); - assert!(decode(r#"{}"#).is_err()); - assert!(decode(r#"0"#).is_err()); - assert!(decode(r#"1234"#).is_err()); - assert!(decode(r#""string""#).is_err()); - assert!(decode(r#"null"#).is_err()); - } -} - diff --git a/src/offset/local.rs b/src/offset/local.rs index d37c15f..8f8c7ca 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -80,7 +80,6 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec { /// let dt: DateTime = Local.timestamp(0, 0); /// ~~~~ #[derive(Copy, Clone)] -#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] pub struct Local; impl Local { diff --git a/src/offset/utc.rs b/src/offset/utc.rs index b1a97cc..2a0c9d4 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -33,7 +33,6 @@ use super::{TimeZone, Offset, LocalResult}; /// assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 1, 1), dt); /// ~~~~ #[derive(Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] pub struct UTC; impl UTC {