From 09a2926bc8b8c60725748c3d3462e5b82fb34d63 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 19 May 2023 15:42:42 +0200 Subject: [PATCH] Support encoding `-00:00` in `Parsed::offset` --- src/format/mod.rs | 2 +- src/format/parsed.rs | 26 +++++++++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index 6b66ee49ae..147d929b71 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -57,7 +57,7 @@ use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday}; pub(crate) mod locales; pub use parse::{parse, parse_and_remainder}; -pub use parsed::Parsed; +pub use parsed::{Parsed, NO_OFFSET_INFO}; /// L10n locales. #[cfg(feature = "unstable-locales")] pub use pure_rust_locales::Locale; diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 809a5ae782..740a9f1413 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -101,6 +101,9 @@ pub struct Parsed { pub timestamp: Option, /// Offset from the local time to UTC, in seconds. + /// + /// Use the constant [`NO_OFFSET_INFO`] to indicate there is an offset of `00:00`, but that its + /// relation to local time is unknown (`-00:00` in RFC 3339 and 2822). pub offset: Option, /// A dummy field to make this type not fully destructible (required for API stability). @@ -108,6 +111,10 @@ pub struct Parsed { _dummy: (), } +/// Constant to be used with [`Parsed::set_offset`] to indicate there is an offset of `00:00`, but +/// that its relation to local time is unknown (`-00:00` in RFC 3339 and 2822). +pub const NO_OFFSET_INFO: i32 = i32::MIN; + /// Checks if `old` is either empty or has the same value as `new` (i.e. "consistent"), /// and if it is empty, set `old` to `new` as well. #[inline] @@ -617,7 +624,12 @@ impl Parsed { /// Returns a parsed fixed time zone offset out of given fields. pub fn to_fixed_offset(&self) -> ParseResult { - self.offset.and_then(FixedOffset::east_opt).ok_or(OUT_OF_RANGE) + let offset = self.offset.ok_or(NOT_ENOUGH)?; + if offset == NO_OFFSET_INFO { + Ok(FixedOffset::OFFSET_UNKNOWN) + } else { + FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE) + } } /// Returns a parsed timezone-aware date and time out of given fields. @@ -633,15 +645,19 @@ impl Parsed { (None, Some(_)) => 0, // UNIX timestamp may assume 0 offset (None, None) => return Err(NOT_ENOUGH), }; - let datetime = self.to_naive_datetime_with_offset(offset)?; - let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?; + let (offset_i32, fixed_offset) = if offset == NO_OFFSET_INFO { + (0, FixedOffset::OFFSET_UNKNOWN) + } else { + (offset, FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?) + }; + let datetime = self.to_naive_datetime_with_offset(offset_i32)?; // this is used to prevent an overflow when calling FixedOffset::from_local_datetime datetime - .checked_sub_signed(OldDuration::seconds(i64::from(offset.local_minus_utc()))) + .checked_sub_signed(OldDuration::seconds(i64::from(offset_i32))) .ok_or(OUT_OF_RANGE)?; - match offset.from_local_datetime(&datetime) { + match fixed_offset.from_local_datetime(&datetime) { LocalResult::None => Err(IMPOSSIBLE), LocalResult::Single(t) => Ok(t), LocalResult::Ambiguous(..) => Err(NOT_ENOUGH),