From f4c42b4f143844256e41bd2ebb4c30fd41c4d2ff Mon Sep 17 00:00:00 2001 From: Aleksandr Bashurov <45628798+abashurov@users.noreply.github.com> Date: Fri, 21 Aug 2020 22:56:10 +0700 Subject: [PATCH] Skip IANA TZ names when parsing %Z specifier (#473) --- CHANGELOG.md | 5 +++++ src/format/parse.rs | 12 ++++++++++-- src/format/scan.rs | 6 ++++++ src/format/strftime.rs | 11 ++++++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51829535e2..7068bc963e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ Versions with only mechanical changes will be omitted from the following list. ## 0.4.16 (unreleased) +### Features + +* Add %Z specifier to the `FromStr`, similar to the glibc strptime + (does not set the offset from the timezone name) + ## 0.4.15 ### Fixes diff --git a/src/format/parse.rs b/src/format/parse.rs index e784eac0f9..2fce8277b1 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -407,7 +407,9 @@ where parsed.set_nanosecond(nano).map_err(|e| (s, e))?; } - &TimezoneName => return Err((s, BAD_FORMAT)), + &TimezoneName => { + try_consume!(scan::timezone_name_skip(s)); + } &TimezoneOffsetColon | &TimezoneOffset => { let offset = try_consume!(scan::timezone_offset( @@ -752,7 +754,7 @@ fn test_parse() { check!("z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0); check!("+12:00", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60); check!("+12", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60); - check!("???", [fix!(TimezoneName)]; BAD_FORMAT); // not allowed + check!("CEST 5", [fix!(TimezoneName), lit!(" "), num!(Day)]; day: 5); // some practical examples check!("2015-02-04T14:37:05+09:00", @@ -771,6 +773,12 @@ fn test_parse() { num!(Minute), lit!(":"), num!(Second), sp!(" "), lit!("GMT")]; year: 2013, month: 6, day: 10, weekday: Weekday::Mon, hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37); + check!("Sun Aug 02 13:39:15 CEST 2020", + [fix!(ShortWeekdayName), sp!(" "), fix!(ShortMonthName), sp!(" "), + num!(Day), sp!(" "), num!(Hour), lit!(":"), num!(Minute), lit!(":"), + num!(Second), sp!(" "), fix!(TimezoneName), sp!(" "), num!(Year)]; + year: 2020, month: 8, day: 2, weekday: Weekday::Sun, + hour_div_12: 1, hour_mod_12: 1, minute: 39, second: 15); check!("20060102150405", [num!(Year), num!(Month), num!(Day), num!(Hour), num!(Minute), num!(Second)]; year: 2006, month: 1, day: 2, hour_div_12: 1, hour_mod_12: 3, minute: 4, second: 5); diff --git a/src/format/scan.rs b/src/format/scan.rs index aef1de8c8e..0efb1ee3d1 100644 --- a/src/format/scan.rs +++ b/src/format/scan.rs @@ -342,3 +342,9 @@ pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option)> { Ok((s_, Some(offset))) } } + +/// Tries to consume everyting until next whitespace-like symbol. +/// Does not provide any offset information from the consumed data. +pub fn timezone_name_skip(s: &str) -> ParseResult<(&str, ())> { + Ok((s.trim_left_matches(|c: char| !c.is_whitespace()), ())) +} diff --git a/src/format/strftime.rs b/src/format/strftime.rs index 0ca2ca9204..93820a2329 100644 --- a/src/format/strftime.rs +++ b/src/format/strftime.rs @@ -68,7 +68,7 @@ The following specifiers are available both to formatting and parsing. | `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same as `%I:%M:%S %p`. | | | | | | | | **TIME ZONE SPECIFIERS:** | -| `%Z` | `ACST` | *Formatting only:* Local time zone name. | +| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. [^9] | | `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). | | `%:z` | `+09:30` | Same as `%z` but with a colon. | | `%#z` | `+09` | *Parsing only:* Same as `%z` but allows minutes to be missing or present. | @@ -157,6 +157,15 @@ Notes: and parsing `07`, `070000` etc. will yield the same. Note that they can read nothing if the fractional part is zero. +[^9]: `%Z`: + Offset will not be populated from the parsed data, nor will it be validated. + Timezone is completely ignored. Similar to the glibc `strptime` treatment of + this format code. +
+
+ It is not possible to reliably convert from an abbreviation to an offset, + for example CDT can mean either Central Daylight Time (North America) or + China Daylight Time. */ #[cfg(feature = "unstable-locales")]