Skip to content

Commit

Permalink
Store offset components properly
Browse files Browse the repository at this point in the history
  • Loading branch information
jhpratt committed Mar 13, 2022
1 parent 5fde5ef commit 46bf135
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 23 deletions.
14 changes: 10 additions & 4 deletions src/parsing/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,21 @@ pub(crate) fn parse_offset_hour(
pub(crate) fn parse_offset_minute(
input: &[u8],
modifiers: modifier::OffsetMinute,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<u8, 2>(modifiers.padding)(input)?
.map(|offset_minute| offset_minute as _),
)
}

/// Parse the "second" component of a `UtcOffset`.
pub(crate) fn parse_offset_second(
input: &[u8],
modifiers: modifier::OffsetSecond,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<u8, 2>(modifiers.padding)(input)?
.map(|offset_second| offset_second as _),
)
}
// endregion offset components
6 changes: 4 additions & 2 deletions src/parsing/parsable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,9 @@ impl sealed::Sealed for Rfc2822 {
}
})
.assign_value_to(&mut parsed.offset_hour);
let input = exactly_n_digits::<_, 2>(input)
let input = exactly_n_digits::<u8, 2>(input)
.ok_or(InvalidComponent("offset minute"))?
.map(|offset_minute| offset_minute as _)
.assign_value_to(&mut parsed.offset_minute);

Ok(input)
Expand Down Expand Up @@ -350,8 +351,9 @@ impl sealed::Sealed for Rfc3339 {
})
.assign_value_to(&mut parsed.offset_hour);
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<_, 2>(input)
let input = exactly_n_digits::<u8, 2>(input)
.ok_or(InvalidComponent("offset minute"))?
.map(|offset_minute: u8| offset_minute as _)
.assign_value_to(&mut parsed.offset_minute);

Ok(input)
Expand Down
111 changes: 102 additions & 9 deletions src/parsing/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ pub struct Parsed {
/// Whole hours of the UTC offset.
pub(crate) offset_hour: Option<i8>,
/// Minutes within the hour of the UTC offset.
pub(crate) offset_minute: Option<u8>,
pub(crate) offset_minute: Option<i8>,
/// Seconds within the minute of the UTC offset.
pub(crate) offset_second: Option<u8>,
pub(crate) offset_second: Option<i8>,
/// Indicates whether a leap second is permitted to be parsed. This is required by some
/// well-known formats.
pub(crate) leap_second_allowed: bool,
Expand Down Expand Up @@ -275,8 +275,28 @@ impl Parsed {
second: u8,
subsecond: u32,
offset_hour: i8,
offset_minute: u8,
offset_second: u8,
}

/// Obtain the absolute value of the offset minute.
#[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
pub const fn offset_minute(&self) -> Option<u8> {
Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
}

/// Obtain the offset minute as an `i8`.
pub const fn offset_minute_signed(&self) -> Option<i8> {
self.offset_minute
}

/// Obtain the absolute value of the offset second.
#[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
pub const fn offset_second(&self) -> Option<u8> {
Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
}

/// Obtain the offset second as an `i8`.
pub const fn offset_second_signed(&self) -> Option<i8> {
self.offset_second
}
}

Expand Down Expand Up @@ -317,8 +337,44 @@ impl Parsed {
set_second second: u8,
set_subsecond subsecond: u32,
set_offset_hour offset_hour: i8,
set_offset_minute offset_minute: u8,
set_offset_second offset_second: u8,
}

/// Set the named component.
#[deprecated(
since = "0.3.8",
note = "use `parsed.set_offset_minute_signed()` instead"
)]
pub fn set_offset_minute(&mut self, value: u8) -> Option<()> {
if value > i8::MAX as u8 {
None
} else {
self.set_offset_minute_signed(value as _)
}
}

/// Set the `offset_minute` component.
pub fn set_offset_minute_signed(&mut self, value: i8) -> Option<()> {
self.offset_minute = Some(value);
Some(())
}

/// Set the named component.
#[deprecated(
since = "0.3.8",
note = "use `parsed.set_offset_second_signed()` instead"
)]
pub fn set_offset_second(&mut self, value: u8) -> Option<()> {
if value > i8::MAX as u8 {
None
} else {
self.set_offset_second_signed(value as _)
}
}

/// Set the `offset_second` component.
pub fn set_offset_second_signed(&mut self, value: i8) -> Option<()> {
self.offset_second = Some(value);
Some(())
}
}

Expand Down Expand Up @@ -359,8 +415,44 @@ impl Parsed {
with_second second: u8,
with_subsecond subsecond: u32,
with_offset_hour offset_hour: i8,
with_offset_minute offset_minute: u8,
with_offset_second offset_second: u8,
}

/// Set the named component and return `self`.
#[deprecated(
since = "0.3.8",
note = "use `parsed.with_offset_minute_signed()` instead"
)]
pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
if value > i8::MAX as u8 {
None
} else {
self.with_offset_minute_signed(value as _)
}
}

/// Set the `offset_minute` component and return `self`.
pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
self.offset_minute = Some(value);
Some(self)
}

/// Set the named component and return `self`.
#[deprecated(
since = "0.3.8",
note = "use `parsed.with_offset_second_signed()` instead"
)]
pub const fn with_offset_second(self, value: u8) -> Option<Self> {
if value > i8::MAX as u8 {
None
} else {
self.with_offset_second_signed(value as _)
}
}

/// Set the `offset_second` component and return `self`.
pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
self.offset_second = Some(value);
Some(self)
}
}

Expand Down Expand Up @@ -452,7 +544,8 @@ impl TryFrom<Parsed> for UtcOffset {
let hour = parsed.offset_hour.ok_or(InsufficientInformation)?;
let minute = parsed.offset_minute.unwrap_or(0);
let second = parsed.offset_second.unwrap_or(0);
Self::from_hms(hour, minute as i8, second as i8).map_err(|mut err| {

Self::from_hms(hour, minute, second).map_err(|mut err| {
// Provide the user a more accurate error.
if err.name == "hours" {
err.name = "offset hour";
Expand Down
48 changes: 40 additions & 8 deletions tests/integration/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ use time::{error, Month, Time, Weekday};
#[test]
fn getters_setters() {
macro_rules! getters_setters {
($($setter:ident $getter:ident $value:expr;)*) => {$(
let mut parsed = Parsed::new();
parsed.$setter($value);
assert_eq!(parsed.$getter(), Some($value));
($( $(#[$attr:meta])* $setter:ident $getter:ident $value:expr;)*) => {$(
$(#[$attr])*
{
let mut parsed = Parsed::new();
parsed.$setter($value);
assert_eq!(parsed.$getter(), Some($value));
}
)*};
}

Expand All @@ -34,13 +37,25 @@ fn getters_setters() {
set_second second 5;
set_subsecond subsecond 5;
set_offset_hour offset_hour 5;
set_offset_minute offset_minute 5;
set_offset_second offset_second 5;
#[allow(deprecated)] set_offset_minute offset_minute 5;
set_offset_minute_signed offset_minute_signed -5;
#[allow(deprecated)] set_offset_second offset_second 5;
set_offset_second_signed offset_second_signed -5;
}

#[allow(deprecated)]
{
let mut parsed = Parsed::new();
parsed.set_offset_minute(200);
assert_eq!(parsed.offset_minute(), None);
parsed.set_offset_second(200);
assert_eq!(parsed.offset_second(), None);
}
}

#[test]
fn builder_methods() {
#[allow(deprecated)]
let parsed = Parsed::new()
.with_year(5)
.and_then(|parsed| parsed.with_year_last_two(5))
Expand Down Expand Up @@ -91,8 +106,25 @@ fn builder_methods() {
assert_eq!(parsed.second(), Some(5));
assert_eq!(parsed.subsecond(), Some(5));
assert_eq!(parsed.offset_hour(), Some(5));
assert_eq!(parsed.offset_minute(), Some(5));
assert_eq!(parsed.offset_second(), Some(5));
#[allow(deprecated)]
{
assert_eq!(parsed.offset_minute(), Some(5));
assert_eq!(parsed.offset_second(), Some(5));
}

let parsed = Parsed::new()
.with_offset_minute_signed(-5)
.and_then(|parsed| parsed.with_offset_second_signed(-5))
.expect("all values are valid");

assert_eq!(parsed.offset_minute_signed(), Some(-5));
assert_eq!(parsed.offset_second_signed(), Some(-5));

#[allow(deprecated)]
{
assert!(Parsed::new().with_offset_minute(200).is_none());
assert!(Parsed::new().with_offset_second(200).is_none());
}
}

#[test]
Expand Down

0 comments on commit 46bf135

Please sign in to comment.