Skip to content

Commit

Permalink
Expand size of duration records to support ECMA402 (#5996)
Browse files Browse the repository at this point in the history
This updates `IsoDurationParser` to support parsing at least the maximum
representable duration in the Temporal proposal
  • Loading branch information
jedel1043 authored Jan 15, 2025
1 parent e7030a9 commit 584bfd1
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 16 deletions.
21 changes: 12 additions & 9 deletions utils/ixdtf/src/parsers/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ pub(crate) fn parse_date_duration(cursor: &mut Cursor) -> ParserResult<DateDurat
let mut previous_unit = DateUnit::None;

while cursor.check_or(false, |ch| ch.is_ascii_digit()) {
let mut value: u32 = 0;
let mut value: u64 = 0;
while cursor.check_or(false, |ch| ch.is_ascii_digit()) {
let digit = cursor
.next_digit()?
.ok_or_else(|| ParseError::abrupt_end("DateDuration"))?;
value = value
.checked_mul(10)
.and_then(|v| v.checked_add(u32::from(digit)))
.and_then(|v| v.checked_add(u64::from(digit)))
.ok_or(ParseError::DurationValueExceededRange)?
}

Expand All @@ -81,21 +81,24 @@ pub(crate) fn parse_date_duration(cursor: &mut Cursor) -> ParserResult<DateDurat
if previous_unit > DateUnit::Year {
return Err(ParseError::DateDurationPartOrder);
}
date.years = value;
date.years =
u32::try_from(value).map_err(|_| ParseError::DurationValueExceededRange)?;
previous_unit = DateUnit::Year;
}
Some(ch) if is_month_designator(ch) => {
if previous_unit > DateUnit::Month {
return Err(ParseError::DateDurationPartOrder);
}
date.months = value;
date.months =
u32::try_from(value).map_err(|_| ParseError::DurationValueExceededRange)?;
previous_unit = DateUnit::Month;
}
Some(ch) if is_week_designator(ch) => {
if previous_unit > DateUnit::Week {
return Err(ParseError::DateDurationPartOrder);
}
date.weeks = value;
date.weeks =
u32::try_from(value).map_err(|_| ParseError::DurationValueExceededRange)?;
previous_unit = DateUnit::Week;
}
Some(ch) if is_day_designator(ch) => {
Expand Down Expand Up @@ -131,17 +134,17 @@ pub(crate) fn parse_time_duration(cursor: &mut Cursor) -> ParserResult<Option<Ti
TimeDurationDesignator,
);

let mut time: (u32, u32, u32, Option<u32>) = (0, 0, 0, None);
let mut time: (u64, u64, u64, Option<u32>) = (0, 0, 0, None);
let mut previous_unit = TimeUnit::None;
while cursor.check_or(false, |c| c.is_ascii_digit()) {
let mut value: u32 = 0;
let mut value: u64 = 0;
while cursor.check_or(false, |c| c.is_ascii_digit()) {
let digit = cursor
.next_digit()?
.ok_or_else(|| ParseError::abrupt_end("TimeDurationDigit"))?;
value = value
.checked_mul(10)
.and_then(|v| v.checked_add(u32::from(digit)))
.and_then(|v| v.checked_add(u64::from(digit)))
.ok_or(ParseError::DurationValueExceededRange)?
}

Expand Down Expand Up @@ -205,6 +208,6 @@ pub(crate) fn parse_time_duration(cursor: &mut Cursor) -> ParserResult<Option<Ti
seconds: time.2,
fraction: time.3.unwrap_or(0),
})),
TimeUnit::None => Err(ParseError::abrupt_end("TimeDUrationDesignator")),
TimeUnit::None => Err(ParseError::abrupt_end("TimeDurationDesignator")),
}
}
14 changes: 7 additions & 7 deletions utils/ixdtf/src/parsers/records.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ pub struct DateDurationRecord {
/// Weeks value.
pub weeks: u32,
/// Days value.
pub days: u32,
pub days: u64,
}

/// A `TimeDurationRecord` represents the result of parsing the time component of a Duration string.
Expand All @@ -189,27 +189,27 @@ pub enum TimeDurationRecord {
// An hours Time duration record.
Hours {
/// Hours value.
hours: u32,
hours: u64,
/// The parsed fraction value in nanoseconds.
fraction: u64,
},
// A Minutes Time duration record.
Minutes {
/// Hours value.
hours: u32,
hours: u64,
/// Minutes value.
minutes: u32,
minutes: u64,
/// The parsed fraction value in nanoseconds.
fraction: u64,
},
// A Seconds Time duration record.
Seconds {
/// Hours value.
hours: u32,
hours: u64,
/// Minutes value.
minutes: u32,
minutes: u64,
/// Seconds value.
seconds: u32,
seconds: u64,
/// The parsed fraction value in nanoseconds.
fraction: u32,
},
Expand Down
32 changes: 32 additions & 0 deletions utils/ixdtf/src/parsers/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,38 @@ fn duration_exceeds_range() {
assert_eq!(err, Err(ParseError::DurationValueExceededRange));
}

#[test]
fn maximum_duration_units() {
use crate::parsers::IsoDurationParser;

// All the values below represent the value 90,071,992,547,409,910,000,000,000
// which is the maximum representable duration in nanoseconds for ECMA402

let result = IsoDurationParser::from_str("P416999965497D").parse();
assert!(result.is_ok());

let result = IsoDurationParser::from_str("PT25019997929836H").parse();
assert!(result.is_ok());

let result = IsoDurationParser::from_str("PT1501199875790165M").parse();
assert!(result.is_ok());

let result = IsoDurationParser::from_str("PT90071992547409910S").parse();
assert!(result.is_ok());

let result = IsoDurationParser::from_str("-P416999965497D").parse();
assert!(result.is_ok());

let result = IsoDurationParser::from_str("-PT25019997929836H").parse();
assert!(result.is_ok());

let result = IsoDurationParser::from_str("-PT1501199875790165M").parse();
assert!(result.is_ok());

let result = IsoDurationParser::from_str("-PT90071992547409910S").parse();
assert!(result.is_ok());
}

#[test]
fn temporal_invalid_iso_datetime_strings() {
// NOTE: The below tests were initially pulled from test262's `argument-string-invalid`
Expand Down

0 comments on commit 584bfd1

Please sign in to comment.