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<Tz>` and all individual
  time zone types. This does *not* affect `DateTime<Tz>` 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.
This commit is contained in:
Kang Seonghoon 2017-02-07 03:08:01 +09:00
parent c63ef14734
commit c118a3985f
No known key found for this signature in database
GPG Key ID: 82440FABA6709020
8 changed files with 311 additions and 609 deletions

View File

@ -398,58 +398,6 @@ impl<Tz: TimeZone> fmt::Display for Date<Tz> 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<Tz: TimeZone> Encodable for Date<Tz> where Tz::Offset: Encodable {
fn encode<S: Encoder>(&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<Tz: TimeZone> Decodable for Date<Tz> where Tz::Offset: Decodable {
fn decode<D: Decoder>(d: &mut D) -> Result<Date<Tz>, 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::<Date<UTC>>(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)] #[cfg(test)]
mod tests { mod tests {
use std::fmt; use std::fmt;

View File

@ -405,65 +405,108 @@ impl str::FromStr for DateTime<Local> {
} }
} }
#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
fn test_encodable_json<FUTC, FFixed, E>(to_string_utc: FUTC, to_string_fixed: FFixed)
where FUTC: Fn(&DateTime<UTC>) -> Result<String, E>,
FFixed: Fn(&DateTime<FixedOffset>) -> Result<String, E>,
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<FUTC, FFixed, FLocal, E>(utc_from_str: FUTC,
fixed_from_str: FFixed,
local_from_str: FLocal)
where FUTC: Fn(&str) -> Result<DateTime<UTC>, E>,
FFixed: Fn(&str) -> Result<DateTime<FixedOffset>, E>,
FLocal: Fn(&str) -> Result<DateTime<Local>, E>,
E: ::std::fmt::Debug
{
// should check against the offset as well (the normal DateTime comparison will ignore them)
fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &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")] #[cfg(feature = "rustc-serialize")]
mod rustc_serialize { mod rustc_serialize {
use super::DateTime; use super::DateTime;
use offset::TimeZone; use offset::TimeZone;
use offset::utc::UTC;
use offset::local::Local;
use offset::fixed::FixedOffset;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
// TODO the current serialization format is NEVER intentionally defined. impl<Tz: TimeZone> Encodable for DateTime<Tz> {
// in the future it is likely to be redefined to more sane and reasonable format.
impl<Tz: TimeZone> Encodable for DateTime<Tz> where Tz::Offset: Encodable {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("DateTime", 2, |s| { format!("{:?}", self).encode(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(())
})
} }
} }
impl<Tz: TimeZone> Decodable for DateTime<Tz> where Tz::Offset: Decodable { impl Decodable for DateTime<FixedOffset> {
fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<Tz>, D::Error> { fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<FixedOffset>, D::Error> {
d.read_struct("DateTime", 2, |d| { match d.read_str()?.parse::<DateTime<FixedOffset>>() {
let datetime = try!(d.read_struct_field("datetime", 0, Decodable::decode)); Ok(dt) => Ok(dt),
let offset = try!(d.read_struct_field("offset", 1, Decodable::decode)); Err(_) => Err(d.error("invalid date and time")),
Ok(DateTime::from_utc(datetime, offset)) }
})
} }
} }
impl Decodable for DateTime<UTC> {
fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<UTC>, D::Error> {
match d.read_str()?.parse::<DateTime<FixedOffset>>() {
Ok(dt) => Ok(dt.with_timezone(&UTC)),
Err(_) => Err(d.error("invalid date and time")),
}
}
}
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(test)] use rustc_serialize::json;
#[test] #[test]
fn test_encodable() { fn test_encodable() {
use offset::utc::UTC; super::test_encodable_json(json::encode, json::encode);
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()));
} }
#[test] #[test]
fn test_decodable() { fn test_decodable() {
use offset::utc::UTC; super::test_decodable_json(json::decode, json::decode, json::decode);
use rustc_serialize::json;
let decode = |s: &str| json::decode::<DateTime<UTC>>(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);
} }
} }
@ -475,14 +518,11 @@ mod serde {
use offset::utc::UTC; use offset::utc::UTC;
use offset::local::Local; use offset::local::Local;
use offset::fixed::FixedOffset; use offset::fixed::FixedOffset;
use std::fmt::Display;
use serde::{ser, de}; use serde::{ser, de};
// TODO not very optimized for space (binary formats would want something better) // TODO not very optimized for space (binary formats would want something better)
impl<Tz: TimeZone> ser::Serialize for DateTime<Tz> impl<Tz: TimeZone> ser::Serialize for DateTime<Tz> {
where Tz::Offset: Display
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer where S: ser::Serializer
{ {
@ -537,22 +577,13 @@ mod serde {
#[test] #[test]
fn test_serde_serialize() { fn test_serde_serialize() {
use self::serde_json::to_string; super::test_encodable_json(self::serde_json::to_string, 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()));
} }
#[test] #[test]
fn test_serde_deserialize() { fn test_serde_deserialize() {
use self::serde_json; super::test_decodable_json(self::serde_json::from_str, self::serde_json::from_str,
self::serde_json::from_str);
let from_str = |s: &str| serde_json::from_str::<DateTime<UTC>>(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());
} }
#[test] #[test]

View File

@ -136,26 +136,6 @@ impl NaiveDate {
NaiveDate::from_of(year, mdf.to_of()) 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<NaiveDate> {
// 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) /// Makes a new `NaiveDate` from the [calendar date](./index.html#calendar-date)
/// (year, month and day). /// (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<F, E>(to_string: F)
where F: Fn(&NaiveDate) -> Result<String, E>, 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<F, E>(from_str: F)
where F: Fn(&str) -> Result<NaiveDate, E>, 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")] #[cfg(feature = "rustc-serialize")]
mod rustc_serialize { mod rustc_serialize {
use super::NaiveDate; use super::NaiveDate;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; 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 { impl Encodable for NaiveDate {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
let ymdf = self.to_serialized(); format!("{:?}", self).encode(s)
s.emit_struct("NaiveDate", 1, |s| {
try!(s.emit_struct_field("ymdf", 0, |s| ymdf.encode(s)));
Ok(())
})
} }
} }
impl Decodable for NaiveDate { impl Decodable for NaiveDate {
fn decode<D: Decoder>(d: &mut D) -> Result<NaiveDate, D::Error> { fn decode<D: Decoder>(d: &mut D) -> Result<NaiveDate, D::Error> {
d.read_struct("NaiveDate", 1, |d| { d.read_str()?.parse().map_err(|_| d.error("invalid date"))
let ymdf = try!(d.read_struct_field("ymdf", 0, Decodable::decode));
NaiveDate::from_serialized(ymdf).ok_or_else(|| d.error("invalid date"))
})
} }
} }
#[cfg(test)] use rustc_serialize::json;
#[test] #[test]
fn test_encodable() { fn test_encodable() {
use rustc_serialize::json::encode; super::test_encodable_json(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()));
} }
#[test] #[test]
fn test_decodable() { fn test_decodable() {
use rustc_serialize::json; super::test_decodable_json(json::decode);
use std::{i32, i64};
let decode = |s: &str| json::decode::<NaiveDate>(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());
} }
} }
@ -1623,53 +1604,12 @@ mod serde {
#[test] #[test]
fn test_serde_serialize() { fn test_serde_serialize() {
use self::serde_json::to_string; super::test_encodable_json(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()));
} }
#[test] #[test]
fn test_serde_deserialize() { fn test_serde_deserialize() {
use self::serde_json; super::test_decodable_json(self::serde_json::from_str);
use std::{i32, i64};
let from_str = |s: &str| serde_json::from_str::<NaiveDate>(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());
} }
#[test] #[test]

View File

@ -1394,105 +1394,116 @@ impl str::FromStr for NaiveDateTime {
} }
} }
#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
fn test_encodable_json<F, E>(to_string: F)
where F: Fn(&NaiveDateTime) -> Result<String, E>, 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<F, E>(from_str: F)
where F: Fn(&str) -> Result<NaiveDateTime, E>, 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")] #[cfg(feature = "rustc-serialize")]
mod rustc_serialize { mod rustc_serialize {
use super::NaiveDateTime; use super::NaiveDateTime;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; 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 { impl Encodable for NaiveDateTime {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("NaiveDateTime", 2, |s| { format!("{:?}", self).encode(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(())
})
} }
} }
impl Decodable for NaiveDateTime { impl Decodable for NaiveDateTime {
fn decode<D: Decoder>(d: &mut D) -> Result<NaiveDateTime, D::Error> { fn decode<D: Decoder>(d: &mut D) -> Result<NaiveDateTime, D::Error> {
d.read_struct("NaiveDateTime", 2, |d| { d.read_str()?.parse().map_err(|_| d.error("invalid date and time"))
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))
})
} }
} }
#[cfg(test)] use rustc_serialize::json;
#[test] #[test]
fn test_encodable() { fn test_encodable() {
use naive::date::{self, NaiveDate}; super::test_encodable_json(json::encode);
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()));
} }
#[test] #[test]
fn test_decodable() { fn test_decodable() {
use naive::date::{self, NaiveDate}; super::test_decodable_json(json::decode);
use rustc_serialize::json;
let decode = |s: &str| json::decode::<NaiveDateTime>(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());
} }
} }
@ -1542,83 +1553,12 @@ mod serde {
#[test] #[test]
fn test_serde_serialize() { fn test_serde_serialize() {
use naive::date::{self, NaiveDate}; super::test_encodable_json(self::serde_json::to_string);
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()));
} }
#[test] #[test]
fn test_serde_deserialize() { fn test_serde_deserialize() {
use naive::date::{self, NaiveDate}; super::test_decodable_json(self::serde_json::from_str);
let from_str = |s: &str| serde_json::from_str::<NaiveDateTime>(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());
} }
#[test] #[test]

View File

@ -178,24 +178,6 @@ pub struct NaiveTime {
} }
impl 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<NaiveTime> {
// 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. /// Makes a new `NaiveTime` from hour, minute and second.
/// ///
/// No [leap second](./index.html#leap-second-handling) is allowed here; /// 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<F, E>(to_string: F)
where F: Fn(&NaiveTime) -> Result<String, E>, 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<F, E>(from_str: F)
where F: Fn(&str) -> Result<NaiveTime, E>, 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")] #[cfg(feature = "rustc-serialize")]
mod rustc_serialize { mod rustc_serialize {
use super::NaiveTime; use super::NaiveTime;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; 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 { impl Encodable for NaiveTime {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
let (secs, frac) = self.to_serialized(); format!("{:?}", self).encode(s)
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(())
})
} }
} }
impl Decodable for NaiveTime { impl Decodable for NaiveTime {
fn decode<D: Decoder>(d: &mut D) -> Result<NaiveTime, D::Error> { fn decode<D: Decoder>(d: &mut D) -> Result<NaiveTime, D::Error> {
d.read_struct("NaiveTime", 2, |d| { d.read_str()?.parse().map_err(|_| d.error("invalid time"))
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"))
})
} }
} }
#[cfg(test)] use rustc_serialize::json;
#[test] #[test]
fn test_encodable() { fn test_encodable() {
use rustc_serialize::json::encode; super::test_encodable_json(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()));
} }
#[test] #[test]
fn test_decodable() { fn test_decodable() {
use rustc_serialize::json; super::test_decodable_json(json::decode);
let decode = |s: &str| json::decode::<NaiveTime>(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());
} }
} }
@ -1424,67 +1410,12 @@ mod serde {
#[test] #[test]
fn test_serde_serialize() { fn test_serde_serialize() {
use self::serde_json::to_string; super::test_encodable_json(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()));
} }
#[test] #[test]
fn test_serde_deserialize() { fn test_serde_deserialize() {
let from_str = |s: &str| serde_json::from_str::<NaiveTime>(s); super::test_decodable_json(self::serde_json::from_str);
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());
} }
#[test] #[test]

View File

@ -23,23 +23,6 @@ pub struct FixedOffset {
} }
impl 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<FixedOffset> {
// 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. /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
/// The negative `secs` means the Western Hemisphere. /// 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) } 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<S: Encoder>(&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: Decoder>(d: &mut D) -> Result<FixedOffset, D::Error> {
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::<FixedOffset>(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());
}
}

View File

@ -80,7 +80,6 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec {
/// let dt: DateTime<Local> = Local.timestamp(0, 0); /// let dt: DateTime<Local> = Local.timestamp(0, 0);
/// ~~~~ /// ~~~~
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
pub struct Local; pub struct Local;
impl Local { impl Local {

View File

@ -33,7 +33,6 @@ use super::{TimeZone, Offset, LocalResult};
/// assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 1, 1), dt); /// assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 1, 1), dt);
/// ~~~~ /// ~~~~
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
pub struct UTC; pub struct UTC;
impl UTC { impl UTC {