Skip to content

Commit

Permalink
Support encoding -00:00 in Parsed::offset
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Jun 12, 2023
1 parent 57da55e commit 09a2926
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 21 additions & 5 deletions src/format/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,20 @@ pub struct Parsed {
pub timestamp: Option<i64>,

/// 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<i32>,

/// A dummy field to make this type not fully destructible (required for API stability).
// TODO: Change this to `#[non_exhaustive]` (on the enum) with the next breaking release.
_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]
Expand Down Expand Up @@ -617,7 +624,12 @@ impl Parsed {

/// Returns a parsed fixed time zone offset out of given fields.
pub fn to_fixed_offset(&self) -> ParseResult<FixedOffset> {
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.
Expand All @@ -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),
Expand Down

0 comments on commit 09a2926

Please sign in to comment.