fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
macro_rules! try_consume {
($e:expr) => {{ let (s_, v) = $e?;
s = s_;
v
}};
}
// an adapted RFC 2822 syntax from Section 3.3 and 4.3: // // date-time = [ day-of-week "," ] date 1*S time *S // day-of-week = *S day-name *S // day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" // date = day month year // day = *S 1*2DIGIT *S // month = 1*S month-name 1*S // month-name = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / // "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" // year = *S 2*DIGIT *S // time = time-of-day 1*S zone // time-of-day = hour ":" minute [ ":" second ] // hour = *S 2DIGIT *S // minute = *S 2DIGIT *S // second = *S 2DIGIT *S // zone = ( "+" / "-" ) 4DIGIT / // "UT" / "GMT" / ; same as +0000 // "EST" / "CST" / "MST" / "PST" / ; same as -0500 to -0800 // "EDT" / "CDT" / "MDT" / "PDT" / ; same as -0400 to -0700 // 1*(%d65-90 / %d97-122) ; same as -0000 // // some notes: // // - quoted characters can be in any mixture of lower and upper cases. // // - we do not recognize a folding white space (FWS) or comment (CFWS). // for our purposes, instead, we accept any sequence of Unicode // white space characters (denoted here to `S`). any actual RFC 2822 // parser is expected to parse FWS and/or CFWS themselves and replace // it with a single SP (`%x20`); this is legitimate. // // - two-digit year < 50 should be interpreted by adding 2000. // two-digit year >= 50 or three-digit year should be interpreted // by adding 1900. note that four-or-more-digit years less than 1000 // are *never* affected by this rule. // // - mismatching day-of-week is always an error, which is consistent to // Chrono's own rules. // // - zones can range from `-9959` to `+9959`, but `FixedOffset` does not // support offsets larger than 24 hours. this is not *that* problematic // since we do not directly go to a `DateTime` so one can recover // the offset information from `Parsed` anyway.
s = s.trim_left();
iflet Ok((s_, weekday)) = scan::short_weekday(s) { if !s_.starts_with(',') { return Err(INVALID);
}
s = &s_[1..];
parsed.set_weekday(weekday)?;
}
s = s.trim_left();
parsed.set_day(try_consume!(scan::number(s, 1, 2)))?;
s = scan::space(s)?; // mandatory
parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s))))?;
s = scan::space(s)?; // mandatory
// distinguish two- and three-digit years from four-digit years let prevlen = s.len(); letmut year = try_consume!(scan::number(s, 2, usize::MAX)); let yearlen = prevlen - s.len(); match (yearlen, year) {
(2, 0...49) => {
year += 2000;
} // 47 -> 2047, 05 -> 2005
(2, 50...99) => {
year += 1900;
} // 79 -> 1979
(3, _) => {
year += 1900;
} // 112 -> 2012, 009 -> 1909
(_, _) => {} // 1987 -> 1987, 0654 -> 0654
}
parsed.set_year(year)?;
s = scan::space(s)?; // mandatory iflet Some(offset) = try_consume!(scan::timezone_offset_2822(s)) { // only set the offset when it is definitely known (i.e. not `-0000`)
parsed.set_offset(i64::from(offset))?;
}
Ok((s, ()))
}
fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
macro_rules! try_consume {
($e:expr) => {{ let (s_, v) = $e?;
s = s_;
v
}};
}
// an adapted RFC 3339 syntax from Section 5.6: // // date-fullyear = 4DIGIT // date-month = 2DIGIT ; 01-12 // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year // time-hour = 2DIGIT ; 00-23 // time-minute = 2DIGIT ; 00-59 // time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules // time-secfrac = "." 1*DIGIT // time-numoffset = ("+" / "-") time-hour ":" time-minute // time-offset = "Z" / time-numoffset // partial-time = time-hour ":" time-minute ":" time-second [time-secfrac] // full-date = date-fullyear "-" date-month "-" date-mday // full-time = partial-time time-offset // date-time = full-date "T" full-time // // some notes: // // - quoted characters can be in any mixture of lower and upper cases. // // - it may accept any number of fractional digits for seconds. // for Chrono, this means that we should skip digits past first 9 digits. // // - unlike RFC 2822, the valid offset ranges from -23:59 to +23:59. // note that this restriction is unique to RFC 3339 and not ISO 8601. // since this is not a typical Chrono behavior, we check it earlier.
parsed.set_year(try_consume!(scan::number(s, 4, 4)))?;
s = scan::char(s, b'-')?;
parsed.set_month(try_consume!(scan::number(s, 2, 2)))?;
s = scan::char(s, b'-')?;
parsed.set_day(try_consume!(scan::number(s, 2, 2)))?;
s = match s.as_bytes().first() {
Some(&b't') | Some(&b'T') => &s[1..],
Some(_) => return Err(INVALID),
None => return Err(TOO_SHORT),
};
parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
s = scan::char(s, b':')?;
parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
s = scan::char(s, b':')?;
parsed.set_second(try_consume!(scan::number(s, 2, 2)))?; if s.starts_with('.') { let nanosecond = try_consume!(scan::nanosecond(&s[1..]));
parsed.set_nanosecond(nanosecond)?;
}
let offset = try_consume!(scan::timezone_offset_zulu(s, |s| scan::char(s, b':'))); if offset <= -86_400 || offset >= 86_400 { return Err(OUT_OF_RANGE);
}
parsed.set_offset(i64::from(offset))?;
Ok((s, ()))
}
/// Tries to parse given string into `parsed` with given formatting items. /// Returns `Ok` when the entire string has been parsed (otherwise `parsed` should not be used). /// There should be no trailing string after parsing; /// use a stray [`Item::Space`](./enum.Item.html#variant.Space) to trim whitespaces. /// /// This particular date and time parser is: /// /// - Greedy. It will consume the longest possible prefix. /// For example, `April` is always consumed entirely when the long month name is requested; /// it equally accepts `Apr`, but prefers the longer prefix in this case. /// /// - Padding-agnostic (for numeric items). /// The [`Pad`](./enum.Pad.html) field is completely ignored, /// so one can prepend any number of whitespace then any number of zeroes before numbers. /// /// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`. pubfn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()> where
I: Iterator<Item = B>,
B: Borrow<Item<'a>>,
{
parse_internal(parsed, s, items).map(|_| ()).map_err(|(_s, e)| e)
}
fn parse_internal<'a, 'b, I, B>(
parsed: &mut Parsed, mut s: &'b str,
items: I,
) -> Result<&'b str, (&'b str, ParseError)> where
I: Iterator<Item = B>,
B: Borrow<Item<'a>>,
{
macro_rules! try_consume {
($e:expr) => {{ match $e {
Ok((s_, v)) => {
s = s_;
v
}
Err(e) => return Err((s, e)),
}
}};
}
for item in items { match *item.borrow() {
Item::Literal(prefix) => { if s.len() < prefix.len() { return Err((s, TOO_SHORT));
} if !s.starts_with(prefix) { return Err((s, INVALID));
}
s = &s[prefix.len()..];
}
// for the future expansion
Internal(ref int) => match int._dummy {},
};
s = s.trim_left(); let v = if signed { if s.starts_with('-') { let v = try_consume!(scan::number(&s[1..], 1, usize::MAX)); 0i64.checked_sub(v).ok_or((s, OUT_OF_RANGE))?
} elseif s.starts_with('+') {
try_consume!(scan::number(&s[1..], 1, usize::MAX))
} else { // if there is no explicit sign, we respect the original `width`
try_consume!(scan::number(s, 1, width))
}
} else {
try_consume!(scan::number(s, 1, width))
};
set(parsed, v).map_err(|e| (s, e))?;
}
Item::Fixed(ref spec) => { usesuper::Fixed::*;
match spec {
&ShortMonthName => { let month0 = try_consume!(scan::short_month0(s));
parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?;
}
#[cfg(test)] #[test] fn test_rfc2822() { usesuper::NOT_ENOUGH; usesuper::*; use offset::FixedOffset; use DateTime;
// Test data - (input, Ok(expected result after parse and format) or Err(error code)) let testdates = [
("Tue, 20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // normal case
("Fri, 2 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // folding whitespace
("Fri, 02 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // leading zero
("20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // no day of week
("20 JAN 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // upper case month
("Tue, 20 Jan 2015 17:35 -0800", Ok("Tue, 20 Jan 2015 17:35:00 -0800")), // no second
("11 Sep 2001 09:45:00 EST", Ok("Tue, 11 Sep 2001 09:45:00 -0500")),
("30 Feb 2015 17:35:20 -0800", Err(OUT_OF_RANGE)), // bad day of month
("Tue, 20 Jan 2015", Err(TOO_SHORT)), // omitted fields
("Tue, 20 Avr 2015 17:35:20 -0800", Err(INVALID)), // bad month name
("Tue, 20 Jan 2015 25:35:20 -0800", Err(OUT_OF_RANGE)), // bad hour
("Tue, 20 Jan 2015 7:35:20 -0800", Err(INVALID)), // bad # of digits in hour
("Tue, 20 Jan 2015 17:65:20 -0800", Err(OUT_OF_RANGE)), // bad minute
("Tue, 20 Jan 2015 17:35:90 -0800", Err(OUT_OF_RANGE)), // bad second
("Tue, 20 Jan 2015 17:35:20 -0890", Err(OUT_OF_RANGE)), // bad offset
("6 Jun 1944 04:00:00Z", Err(INVALID)), // bad offset (zulu not allowed)
("Tue, 20 Jan 2015 17:35:20 HAS", Err(NOT_ENOUGH)), // bad named time zone
];
// Test against test data above for &(date, checkdate) in testdates.iter() { let d = rfc2822_to_datetime(date); // parse a date let dt = match d { // did we get a value?
Ok(dt) => Ok(fmt_rfc2822_datetime(dt)), // yes, go on
Err(e) => Err(e), // otherwise keep an error for the comparison
}; if dt != checkdate.map(|s| s.to_string()) { // check for expected result
panic!( "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}",
date, dt, checkdate
);
}
}
}
#[cfg(test)] #[test] fn parse_rfc850() { use {TimeZone, Utc};
let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT"; let dt = Utc.ymd(1994, 11, 6).and_hms(8, 49, 37);
// Check that the format is what we expect
assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str);
// Check that it parses correctly
assert_eq!(Ok(dt), Utc.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT));
// Check that the rest of the weekdays parse correctly (this test originally failed because // Sunday parsed incorrectly). let testdates = [
(Utc.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"),
];
for val in &testdates {
assert_eq!(Ok(val.0), Utc.datetime_from_str(val.1, RFC850_FMT));
}
}
#[cfg(test)] #[test] fn test_rfc3339() { usesuper::*; use offset::FixedOffset; use DateTime;
// Test data - (input, Ok(expected result after parse and format) or Err(error code)) let testdates = [
("2015-01-20T17:35:20-08:00", Ok("2015-01-20T17:35:20-08:00")), // normal case
("1944-06-06T04:04:00Z", Ok("1944-06-06T04:04:00+00:00")), // D-day
("2001-09-11T09:45:00-08:00", Ok("2001-09-11T09:45:00-08:00")),
("2015-01-20T17:35:20.001-08:00", Ok("2015-01-20T17:35:20.001-08:00")),
("2015-01-20T17:35:20.000031-08:00", Ok("2015-01-20T17:35:20.000031-08:00")),
("2015-01-20T17:35:20.000000004-08:00", Ok("2015-01-20T17:35:20.000000004-08:00")),
("2015-01-20T17:35:20.000000000452-08:00", Ok("2015-01-20T17:35:20-08:00")), // too small
("2015-02-30T17:35:20-08:00", Err(OUT_OF_RANGE)), // bad day of month
("2015-01-20T25:35:20-08:00", Err(OUT_OF_RANGE)), // bad hour
("2015-01-20T17:65:20-08:00", Err(OUT_OF_RANGE)), // bad minute
("2015-01-20T17:35:90-08:00", Err(OUT_OF_RANGE)), // bad second
("2015-01-20T17:35:20-24:00", Err(OUT_OF_RANGE)), // bad offset
];
// Test against test data above for &(date, checkdate) in testdates.iter() { let d = rfc3339_to_datetime(date); // parse a date let dt = match d { // did we get a value?
Ok(dt) => Ok(fmt_rfc3339_datetime(dt)), // yes, go on
Err(e) => Err(e), // otherwise keep an error for the comparison
}; if dt != checkdate.map(|s| s.to_string()) { // check for expected result
panic!( "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}",
date, dt, checkdate
);
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-06-24)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.