implemented `FromStr` for `DateTime<FixedOffset/UTC>`.

This commit is contained in:
Kang Seonghoon 2015-02-19 00:00:33 +09:00
parent 76b0873722
commit 46996e35e1
2 changed files with 79 additions and 11 deletions

View File

@ -6,17 +6,18 @@
* ISO 8601 date and time. * ISO 8601 date and time.
*/ */
use std::{fmt, hash}; use std::{str, fmt, hash};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
use {Weekday, Timelike, Datelike}; use {Weekday, Timelike, Datelike};
use offset::{Offset, FixedOffset}; use offset::{Offset, FixedOffset, UTC};
use duration::Duration; use duration::Duration;
use naive::datetime::NaiveDateTime; use naive::datetime::NaiveDateTime;
use time::Time; use time::Time;
use date::Date; use date::Date;
use format::{parse, Item, Parsed, ParseResult, DelayedFormat, StrftimeItems}; use format::{Item, Numeric, Pad, Fixed};
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
/// ISO 8601 combined date and time with timezone. /// ISO 8601 combined date and time with timezone.
#[derive(Clone)] #[derive(Clone)]
@ -261,6 +262,45 @@ impl<Off: Offset + fmt::Display> fmt::Display for DateTime<Off> {
} }
} }
impl str::FromStr for DateTime<FixedOffset> {
type Err = ParseError;
fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &'static [Item<'static>] = &[
Item::Space(""), Item::Numeric(Numeric::Year, Pad::Zero),
Item::Space(""), Item::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero),
Item::Space(""), Item::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Day, Pad::Zero),
Item::Space(""), Item::Literal("T"), // XXX shouldn't this be case-insensitive?
Item::Space(""), Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Space(""), Item::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Space(""), Item::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero),
Item::Fixed(Fixed::Nanosecond),
Item::Space(""), Item::Fixed(Fixed::TimezoneOffsetZ),
Item::Space(""),
];
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
parsed.to_datetime()
}
}
impl str::FromStr for DateTime<UTC> {
type Err = ParseError;
fn from_str(s: &str) -> ParseResult<DateTime<UTC>> {
// we parse non-UTC time zones then convert them into UTC
let dt: DateTime<FixedOffset> = try!(s.parse());
Ok(dt.with_offset(UTC))
}
}
// TODO: FromStr for DateTime<Local> is quite hard without a new offset design
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::DateTime; use super::DateTime;
@ -294,6 +334,21 @@ mod tests {
assert!(*EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).offset() != EST); assert!(*EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).offset() != EST);
} }
#[test]
fn test_datetime_from_str() {
assert_eq!("2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
assert_eq!("2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(),
Ok(FixedOffset::west(10 * 3600).ymd(2015, 2, 18).and_hms_milli(13, 16, 9, 150)));
assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err());
assert_eq!("2015-2-18T23:16:9.15Z".parse::<DateTime<UTC>>(),
Ok(UTC.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
assert_eq!("2015-2-18T13:16:9.15-10:00".parse::<DateTime<UTC>>(),
Ok(UTC.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
assert!("2015-2-18T23:16:9.15".parse::<DateTime<UTC>>().is_err());
}
#[test] #[test]
fn test_datetime_parse_from_str() { fn test_datetime_parse_from_str() {
let ymdhms = |&: y,m,d,h,n,s,off| FixedOffset::east(off).ymd(y,m,d).and_hms(h,n,s); let ymdhms = |&: y,m,d,h,n,s,off| FixedOffset::east(off).ymd(y,m,d).and_hms(h,n,s);

View File

@ -146,14 +146,19 @@ assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC");
assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z"); assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
~~~~ ~~~~
Parsing can be done with two methods: Parsing can be done with three methods:
- `DateTime::parse_from_str` parses a date and time with offsets and 1. The standard `FromStr` trait (and `parse` method on a string) can be used for
parsing `DateTime<FixedOffset>` and `DateTime<UTC>` values.
This parses what the `{:?}` (`std::fmt::Debug`) format specifier prints,
and requires the offset to be present.
2. `DateTime::parse_from_str` parses a date and time with offsets and
returns `DateTime<FixedOffset>`. returns `DateTime<FixedOffset>`.
This should be used when the offset is a part of input and the caller cannot guess that. This should be used when the offset is a part of input and the caller cannot guess that.
It *cannot* be used when the offset can be missing. It *cannot* be used when the offset can be missing.
- `Offset::datetime_from_str` is similar but returns `DateTime` of given offset. 3. `Offset::datetime_from_str` is similar but returns `DateTime` of given offset.
When the explicit offset is missing from the input, it simply uses given offset. When the explicit offset is missing from the input, it simply uses given offset.
It issues an error when the input contains an explicit offset different from the current offset. It issues an error when the input contains an explicit offset different from the current offset.
@ -163,8 +168,16 @@ More detailed control over the parsing process is available via `format` module.
use chrono::{UTC, Offset, DateTime}; use chrono::{UTC, Offset, DateTime};
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
// method 1
assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<UTC>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<UTC>>(), Ok(dt.clone()));
// method 2
assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone())); assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone())); assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
// method 3
assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00",
"%Y-%m-%d %H:%M:%S %z").map(|dt| dt.with_offset(UTC)), Ok(dt)); "%Y-%m-%d %H:%M:%S %z").map(|dt| dt.with_offset(UTC)), Ok(dt));