public interfaces for parser are now available.

This commit is contained in:
Kang Seonghoon 2015-02-05 02:16:35 +09:00
parent 9768b57494
commit 7eb9a1a983
8 changed files with 134 additions and 8 deletions

View File

@ -11,12 +11,12 @@ 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; use offset::{Offset, FixedOffset};
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::{DelayedFormat, StrftimeItems}; use format::{parse, Parsed, ParseResult, DelayedFormat, StrftimeItems};
/// ISO 8601 combined date and time with timezone. /// ISO 8601 combined date and time with timezone.
#[derive(Clone)] #[derive(Clone)]
@ -88,6 +88,19 @@ impl<Off:Offset> DateTime<Off> {
} }
} }
impl DateTime<FixedOffset> {
/// Parses a string with the specified format string and
/// returns a new `DateTime` with a parsed `FixedOffset`.
/// See the `format::strftime` module on the supported escape sequences.
///
/// See also `Offset::datetime_from_str` which gives a local `DateTime` on specific time zone.
pub fn from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parsed.to_datetime()
}
}
impl<Off: Offset + fmt::Display> DateTime<Off> { impl<Off: Offset + fmt::Display> DateTime<Off> {
/// Formats the combined date and time in the specified format string. /// Formats the combined date and time in the specified format string.
/// See the `format::strftime` module on the supported escape sequences. /// See the `format::strftime` module on the supported escape sequences.
@ -244,7 +257,8 @@ impl<Off: Offset + fmt::Display> fmt::Display for DateTime<Off> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use {Datelike}; use super::DateTime;
use Datelike;
use duration::Duration; use duration::Duration;
use offset::{Offset, UTC, Local, FixedOffset}; use offset::{Offset, UTC, Local, FixedOffset};
@ -275,7 +289,20 @@ mod tests {
} }
#[test] #[test]
fn test_datetime_fmt_with_local() { fn test_datetime_from_str() {
let ymdhms = |&: y,m,d,h,n,s,off| FixedOffset::east(off).ymd(y,m,d).and_hms(h,n,s);
assert_eq!(DateTime::from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
Ok(ymdhms(2014, 5, 7, 12, 34, 56, 570*60))); // ignore offset
assert!(DateTime::from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset
assert!(DateTime::from_str("Fri, 09 Aug 2013 23:54:35 GMT",
"%a, %d %b %Y %H:%M:%S GMT").is_err());
assert_eq!(UTC.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT",
"%a, %d %b %Y %H:%M:%S GMT"),
Ok(UTC.ymd(2013, 8, 9).and_hms(23, 54, 35)));
}
#[test]
fn test_datetime_format_with_local() {
// if we are not around the year boundary, local and UTC date should have the same year // if we are not around the year boundary, local and UTC date should have the same year
let dt = Local::now().with_month(5).unwrap(); let dt = Local::now().with_month(5).unwrap();
assert_eq!(dt.format("%Y").to_string(), dt.with_offset(UTC).format("%Y").to_string()); assert_eq!(dt.format("%Y").to_string(), dt.with_offset(UTC).format("%Y").to_string());

View File

@ -19,7 +19,7 @@ use offset::Offset;
use naive::date::NaiveDate; use naive::date::NaiveDate;
use naive::time::NaiveTime; use naive::time::NaiveTime;
use self::parsed::Parsed; pub use self::parsed::Parsed;
pub use self::strftime::StrftimeItems; pub use self::strftime::StrftimeItems;
/// Padding characters for numeric items. /// Padding characters for numeric items.

View File

@ -491,6 +491,25 @@ impl Parsed {
LocalResult::Ambiguous(..) => Err(NOT_ENOUGH), LocalResult::Ambiguous(..) => Err(NOT_ENOUGH),
} }
} }
/// Returns a parsed timezone-aware date and time out of given fields,
/// with an additional `Offset` used to interpret and validate the local date.
///
/// This method is able to determine the combined date and time
/// from date and time fields or a single `timestamp` field, plus a time zone offset.
/// Either way those fields have to be consistent to each other.
/// If parsed fields include an UTC offset, it also has to be consistent to `offset`.
pub fn to_datetime_with_offset<Off: Offset>(&self, offset: Off) -> ParseResult<DateTime<Off>> {
let delta = offset.local_minus_utc().num_seconds();
let delta = try!(delta.to_i32().ok_or(OUT_OF_RANGE));
if self.offset.unwrap_or(delta) != delta { return Err(IMPOSSIBLE); }
let datetime = try!(self.to_naive_datetime_with_offset(delta));
match offset.from_local_datetime(&datetime) {
LocalResult::None => Err(IMPOSSIBLE),
LocalResult::Single(t) => Ok(t),
LocalResult::Ambiguous(..) => Err(NOT_ENOUGH),
}
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -200,6 +200,7 @@ pub use naive::datetime::NaiveDateTime;
pub use date::Date; pub use date::Date;
pub use time::Time; pub use time::Time;
pub use datetime::DateTime; pub use datetime::DateTime;
pub use format::{ParseError, ParseResult};
// useful throughout the codebase // useful throughout the codebase
macro_rules! try_opt { macro_rules! try_opt {

View File

@ -15,7 +15,7 @@ use div::div_mod_floor;
use duration::Duration; use duration::Duration;
use naive::time::NaiveTime; use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime; use naive::datetime::NaiveDateTime;
use format::{DelayedFormat, StrftimeItems}; use format::{parse, Parsed, ParseResult, DelayedFormat, StrftimeItems};
use self::internals::{DateImpl, Of, Mdf, YearFlags}; use self::internals::{DateImpl, Of, Mdf, YearFlags};
@ -180,6 +180,14 @@ impl NaiveDate {
Of::new(ordinal, flags)) Of::new(ordinal, flags))
} }
/// Parses a string with the specified format string and returns a new `NaiveDate`.
/// See the `format::strftime` module on the supported escape sequences.
pub fn from_str(s: &str, fmt: &str) -> ParseResult<NaiveDate> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parsed.to_naive_date()
}
/// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`. /// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`.
#[inline] #[inline]
pub fn and_time(&self, time: NaiveTime) -> NaiveDateTime { pub fn and_time(&self, time: NaiveTime) -> NaiveDateTime {
@ -830,6 +838,20 @@ mod tests {
assert_eq!(format!("{:30?}", NaiveDate::from_ymd(12345, 6, 7)), "+12345-06-07"); assert_eq!(format!("{:30?}", NaiveDate::from_ymd(12345, 6, 7)), "+12345-06-07");
} }
#[test]
fn test_date_from_str() {
let ymd = |&: y,m,d| NaiveDate::from_ymd(y,m,d);
assert_eq!(NaiveDate::from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
Ok(ymd(2014, 5, 7))); // ignore time and offset
assert_eq!(NaiveDate::from_str("2015-W06-1=2015-033", "%G-W%V-%u = %Y-%j"),
Ok(ymd(2015, 2, 2)));
assert_eq!(NaiveDate::from_str("Fri, 09 Aug 13", "%a, %d %b %y"),
Ok(ymd(2013, 8, 9)));
assert!(NaiveDate::from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err());
assert!(NaiveDate::from_str("2014-57", "%Y-%m-%d").is_err());
assert!(NaiveDate::from_str("2014", "%Y").is_err()); // insufficient
}
#[test] #[test]
fn test_date_format() { fn test_date_format() {
let d = NaiveDate::from_ymd(2012, 3, 4); let d = NaiveDate::from_ymd(2012, 3, 4);

View File

@ -15,7 +15,7 @@ use div::div_mod_floor;
use duration::Duration; use duration::Duration;
use naive::time::NaiveTime; use naive::time::NaiveTime;
use naive::date::NaiveDate; use naive::date::NaiveDate;
use format::{DelayedFormat, StrftimeItems}; use format::{parse, Parsed, ParseResult, DelayedFormat, StrftimeItems};
/// ISO 8601 combined date and time without timezone. /// ISO 8601 combined date and time without timezone.
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
@ -60,6 +60,14 @@ impl NaiveDateTime {
} }
} }
/// Parses a string with the specified format string and returns a new `NaiveDateTime`.
/// See the `format::strftime` module on the supported escape sequences.
pub fn from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parsed.to_naive_datetime_with_offset(0) // no offset adjustment
}
/// Retrieves a date component. /// Retrieves a date component.
#[inline] #[inline]
pub fn date(&self) -> NaiveDate { pub fn date(&self) -> NaiveDate {
@ -330,6 +338,22 @@ mod tests {
assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff); assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff);
} }
#[test]
fn test_datetime_from_str() {
let ymdhms = |&: y,m,d,h,n,s| NaiveDate::from_ymd(y,m,d).and_hms(h,n,s);
assert_eq!(NaiveDateTime::from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
Ok(ymdhms(2014, 5, 7, 12, 34, 56))); // ignore offset
assert_eq!(NaiveDateTime::from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S"),
Ok(ymdhms(2015, 2, 2, 0, 0, 0)));
assert_eq!(NaiveDateTime::from_str("Fri, 09 Aug 2013 23:54:35 GMT",
"%a, %d %b %Y %H:%M:%S GMT"),
Ok(ymdhms(2013, 8, 9, 23, 54, 35)));
assert!(NaiveDateTime::from_str("Sat, 09 Aug 2013 23:54:35 GMT",
"%a, %d %b %Y %H:%M:%S GMT").is_err());
assert!(NaiveDateTime::from_str("2014-5-7 12:3456", "%Y-%m-%d %H:%M:%S").is_err());
assert!(NaiveDateTime::from_str("12:34:56", "%H:%M:%S").is_err()); // insufficient
}
#[test] #[test]
fn test_datetime_format() { fn test_datetime_format() {
let dt = NaiveDate::from_ymd(2010, 9, 8).and_hms_milli(7, 6, 54, 321); let dt = NaiveDate::from_ymd(2010, 9, 8).and_hms_milli(7, 6, 54, 321);

View File

@ -14,7 +14,7 @@ use Timelike;
use div::div_mod_floor; use div::div_mod_floor;
use offset::Offset; use offset::Offset;
use duration::Duration; use duration::Duration;
use format::{DelayedFormat, StrftimeItems}; use format::{parse, Parsed, ParseResult, DelayedFormat, StrftimeItems};
/// ISO 8601 time without timezone. /// ISO 8601 time without timezone.
/// Allows for the nanosecond precision and optional leap second representation. /// Allows for the nanosecond precision and optional leap second representation.
@ -118,6 +118,14 @@ impl NaiveTime {
Some(NaiveTime { secs: secs, frac: nano }) Some(NaiveTime { secs: secs, frac: nano })
} }
/// Parses a string with the specified format string and returns a new `NaiveTime`.
/// See the `format::strftime` module on the supported escape sequences.
pub fn from_str(s: &str, fmt: &str) -> ParseResult<NaiveTime> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parsed.to_naive_time()
}
/// Formats the time in the specified format string. /// Formats the time in the specified format string.
/// See the `format::strftime` module on the supported escape sequences. /// See the `format::strftime` module on the supported escape sequences.
#[inline] #[inline]
@ -368,6 +376,16 @@ mod tests {
assert_eq!(format!("{:30}", NaiveTime::from_hms_milli(3, 5, 7, 9)), "03:05:07.009"); assert_eq!(format!("{:30}", NaiveTime::from_hms_milli(3, 5, 7, 9)), "03:05:07.009");
} }
#[test]
fn test_time_from_str() {
let hms = |&: h,m,s| NaiveTime::from_hms(h,m,s);
assert_eq!(NaiveTime::from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
Ok(hms(12, 34, 56))); // ignore date and offset
assert_eq!(NaiveTime::from_str("PM 12:59", "%P %H:%M"),
Ok(hms(12, 59, 0)));
assert!(NaiveTime::from_str("12:3456", "%H:%M:%S").is_err());
}
#[test] #[test]
fn test_time_format() { fn test_time_format() {
let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432); let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432);

View File

@ -18,6 +18,7 @@ use naive::datetime::NaiveDateTime;
use date::Date; use date::Date;
use time::Time; use time::Time;
use datetime::DateTime; use datetime::DateTime;
use format::{parse, Parsed, ParseResult, StrftimeItems};
/// The conversion result from the local time to the timezone-aware datetime types. /// The conversion result from the local time to the timezone-aware datetime types.
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
@ -291,6 +292,20 @@ pub trait Offset: Clone + fmt::Debug {
} }
} }
/// Parses a string with the specified format string and
/// returns a `DateTime` with the current offset.
/// See the `format::strftime` module on the supported escape sequences.
///
/// If the format does not include offsets, the current offset is assumed;
/// otherwise the input should have a matching UTC offset.
///
/// See also `DateTime::from_str` which gives a local `DateTime` with parsed `FixedOffset`.
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parsed.to_datetime_with_offset(self.clone())
}
/// Returns the *current* offset from UTC to the local time. /// Returns the *current* offset from UTC to the local time.
fn local_minus_utc(&self) -> Duration; fn local_minus_utc(&self) -> Duration;