From 4cf8f8fff49c5fcf160fd1c602479e7cfb4de1ab Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 28 May 2023 08:06:36 +0100 Subject: [PATCH 1/3] Add FromStr for FixedOffset Closes #314 --- src/format/mod.rs | 12 +++++++++++- src/format/scan.rs | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index 7c9c3b7dd0..39627999ea 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -47,8 +47,9 @@ use std::error::Error; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::naive::{NaiveDate, NaiveTime}; +use crate::offset::FixedOffset; #[cfg(any(feature = "alloc", feature = "std", test))] -use crate::offset::{FixedOffset, Offset}; +use crate::offset::Offset; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::{Datelike, Timelike}; use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday}; @@ -991,6 +992,15 @@ impl FromStr for Weekday { } } +/// Parsing a `str` into a `FixedOffset` uses the format [`%z`](./format/strftime/index.html). +impl FromStr for FixedOffset { + type Err = ParseError; + fn from_str(s: &str) -> Result { + let (_, offset) = scan::timezone_offset(s, scan::colon_or_space)?; + Self::east_opt(offset).ok_or(OUT_OF_RANGE) + } +} + /// Formats single formatting item #[cfg(feature = "unstable-locales")] #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] diff --git a/src/format/scan.rs b/src/format/scan.rs index 2273dec7d6..00e06c7c13 100644 --- a/src/format/scan.rs +++ b/src/format/scan.rs @@ -197,7 +197,7 @@ pub(super) fn space(s: &str) -> ParseResult<&str> { } /// Consumes any number (including zero) of colon or spaces. -pub(super) fn colon_or_space(s: &str) -> ParseResult<&str> { +pub(crate) fn colon_or_space(s: &str) -> ParseResult<&str> { Ok(s.trim_start_matches(|c: char| c == ':' || c.is_whitespace())) } @@ -205,7 +205,7 @@ pub(super) fn colon_or_space(s: &str) -> ParseResult<&str> { /// /// The additional `colon` may be used to parse a mandatory or optional `:` /// between hours and minutes, and should return either a new suffix or `Err` when parsing fails. -pub(super) fn timezone_offset(s: &str, consume_colon: F) -> ParseResult<(&str, i32)> +pub(crate) fn timezone_offset(s: &str, consume_colon: F) -> ParseResult<(&str, i32)> where F: FnMut(&str) -> ParseResult<&str>, { From 5ad4be8d695f288c951576780c2930c01b837245 Mon Sep 17 00:00:00 2001 From: Mike Cronce Date: Wed, 28 Jun 2023 11:30:27 -0400 Subject: [PATCH 2/3] src: Address review feedback --- src/format/mod.rs | 16 +++------------- src/offset/fixed.rs | 13 +++++++++++++ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index 39627999ea..9591fa4d16 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -47,9 +47,8 @@ use std::error::Error; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::naive::{NaiveDate, NaiveTime}; -use crate::offset::FixedOffset; #[cfg(any(feature = "alloc", feature = "std", test))] -use crate::offset::Offset; +use crate::offset::{FixedOffset, Offset}; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::{Datelike, Timelike}; use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday}; @@ -421,7 +420,7 @@ impl Error for ParseError { } // to be used in this module and submodules -const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange); +pub(crate) const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange); const IMPOSSIBLE: ParseError = ParseError(ParseErrorKind::Impossible); const NOT_ENOUGH: ParseError = ParseError(ParseErrorKind::NotEnough); const INVALID: ParseError = ParseError(ParseErrorKind::Invalid); @@ -839,7 +838,7 @@ mod parsed; // due to the size of parsing routines, they are in separate modules. mod parse; -mod scan; +pub(crate) mod scan; pub mod strftime; @@ -992,15 +991,6 @@ impl FromStr for Weekday { } } -/// Parsing a `str` into a `FixedOffset` uses the format [`%z`](./format/strftime/index.html). -impl FromStr for FixedOffset { - type Err = ParseError; - fn from_str(s: &str) -> Result { - let (_, offset) = scan::timezone_offset(s, scan::colon_or_space)?; - Self::east_opt(offset).ok_or(OUT_OF_RANGE) - } -} - /// Formats single formatting item #[cfg(feature = "unstable-locales")] #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index 246d6666ea..a6bc8d15fe 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -5,14 +5,18 @@ use core::fmt; use core::ops::{Add, Sub}; +use core::str::FromStr; #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize, Serialize}; use super::{LocalResult, Offset, TimeZone}; +use crate::format::scan; +use crate::format::OUT_OF_RANGE; use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; use crate::oldtime::Duration as OldDuration; use crate::DateTime; +use crate::ParseError; use crate::Timelike; /// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59. @@ -113,6 +117,15 @@ impl FixedOffset { } } +/// Parsing a `str` into a `FixedOffset` uses the format [`%z`](crate::format::strftime). +impl FromStr for FixedOffset { + type Err = ParseError; + fn from_str(s: &str) -> Result { + let (_, offset) = scan::timezone_offset(s, scan::colon_or_space)?; + Self::east_opt(offset).ok_or(OUT_OF_RANGE) + } +} + impl TimeZone for FixedOffset { type Offset = FixedOffset; From 58efae49f2a08c96953a62eed3ed73352fb7563f Mon Sep 17 00:00:00 2001 From: Mike Cronce Date: Wed, 28 Jun 2023 11:35:54 -0400 Subject: [PATCH 3/3] src/offset/fixed.rs: Add tests for FixedOffset::from_str() --- src/offset/fixed.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index a6bc8d15fe..fcf445fd97 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -259,6 +259,7 @@ impl Sub for DateTime { mod tests { use super::FixedOffset; use crate::offset::TimeZone; + use std::str::FromStr; #[test] fn test_date_extreme_offset() { @@ -305,4 +306,14 @@ mod tests { "2012-03-04T05:06:07-23:59:59".to_string() ); } + + #[test] + fn test_parse_offset() { + let offset = FixedOffset::from_str("-0500").unwrap(); + assert_eq!(offset.local_minus_utc, -5 * 3600); + let offset = FixedOffset::from_str("-08:00").unwrap(); + assert_eq!(offset.local_minus_utc, -8 * 3600); + let offset = FixedOffset::from_str("+06:30").unwrap(); + assert_eq!(offset.local_minus_utc, (6 * 3600) + 1800); + } }