Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/builtins/core/instant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,13 @@ impl Instant {
UtcOffsetRecordOrZ::Offset(offset) => {
let ns = offset
.fraction()
.and_then(|x| x.to_nanoseconds())
.map(|x| {
x.to_nanoseconds().ok_or(
TemporalError::range()
.with_enum(ErrorMessage::FractionalTimeMoreThanNineDigits),
)
})
.transpose()?
.unwrap_or(0);
(offset.hour() as i64 * NANOSECONDS_PER_HOUR
+ i64::from(offset.minute()) * NANOSECONDS_PER_MINUTE
Expand All @@ -317,7 +323,13 @@ impl Instant {
let time_nanoseconds = ixdtf_record
.time
.fraction
.and_then(|x| x.to_nanoseconds())
.map(|x| {
x.to_nanoseconds().ok_or(
TemporalError::range()
.with_enum(ErrorMessage::FractionalTimeMoreThanNineDigits),
)
})
.transpose()?
.unwrap_or(0);
let (millisecond, rem) = time_nanoseconds.div_rem_euclid(&1_000_000);
let (microsecond, nanosecond) = rem.div_rem_euclid(&1_000);
Expand Down
28 changes: 28 additions & 0 deletions src/builtins/core/zoned_date_time/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,34 @@ fn test_same_date_reverse_wallclock() {
})
}

#[test]
fn test_invalid_fractional_offset_digits() {
test_all_providers!(provider: {
let test = "2020-01-01T00:00:00.123456789+02:30:00.1234567890[UTC]";
let result = ZonedDateTime::from_utf8_with_provider(
test.as_bytes(),
crate::options::Disambiguation::Compatible,
crate::options::OffsetDisambiguation::Use,
&provider,
);
assert!(result.is_err(), "ZonedDateTime should be invalid");
})
}

#[test]
fn test_valid_fractional_offset_digits() {
test_all_providers!(provider: {
let test = "2020-01-01T00:00:00.123456789+02:30:00.123456789[UTC]";
let result = ZonedDateTime::from_utf8_with_provider(
test.as_bytes(),
crate::options::Disambiguation::Compatible,
crate::options::OffsetDisambiguation::Use,
&provider,
);
assert!(result.is_ok(), "ZonedDateTime should be valid");
})
}

#[test]
fn test_relativeto_back_transition() {
// intl402/Temporal/Duration/prototype/round/relativeto-dst-back-transition
Expand Down
43 changes: 43 additions & 0 deletions src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,46 @@ pub static TZ_PROVIDER: LazyLock<CompiledTzdbProvider> =

#[cfg(all(test, feature = "compiled_data"))]
pub(crate) static FS_TZ_PROVIDER: LazyLock<FsTzdbProvider> = LazyLock::new(FsTzdbProvider::default);

#[cfg(test)]
mod tests {
use super::{Instant, PlainDate, PlainDateTime};
#[test]
fn builtins_from_str_10_digit_fractions() {
// Failure case with 10 digits
let test = "2020-01-01T00:00:00.1234567890Z";
let result = Instant::from_utf8(test.as_bytes());
assert!(result.is_err(), "Instant fraction should be invalid");
let result = PlainDate::from_utf8(test.as_bytes());
assert!(result.is_err(), "PlainDate fraction should be invalid");
let result = PlainDateTime::from_utf8(test.as_bytes());
assert!(result.is_err(), "PlainDateTime fraction should be invalid");
}

#[test]
fn instant_based_10_digit_offset() {
let test = "2020-01-01T00:00:00.123456789+02:30:00.1234567890[UTC]";
let result = Instant::from_utf8(test.as_bytes());
assert!(result.is_err(), "Instant should be invalid");
}

#[test]
fn instant_based_9_digit_offset() {
let test = "2020-01-01T00:00:00.123456789+02:30:00.123456789[UTC]";
let result = Instant::from_utf8(test.as_bytes());
assert!(result.is_ok(), "Instant should be valid");
}

#[test]
fn builtin_from_str_9_digit_fractions() {
// Success case with 9 digits
let test = "2020-01-01T00:00:00.123456789Z";
let result = Instant::from_utf8(test.as_bytes());
assert!(result.is_ok(), "Instant fraction should be valid");
let test = "2020-01-01T00:00:00.123456789";
let result = PlainDate::from_utf8(test.as_bytes());
assert!(result.is_ok(), "PlainDate fraction should be valid");
let result = PlainDateTime::from_utf8(test.as_bytes());
assert!(result.is_ok(), "PlainDateTime fraction should be valid");
}
}
31 changes: 31 additions & 0 deletions src/parsed_intermediates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ impl ParsedDate {
// Assertion: PlainDate must exist on a DateTime parse.
let record = parse_record.date.temporal_unwrap()?;

// TODO: Potentially, remove this check in favor of type guarantee of a
// Temporal parser.
// NOTE: The check here is to confirm that the time component is
// valid.
let _time = parse_record
.time
.map(IsoTime::from_time_record)
.transpose()?
.unwrap_or_default();

Ok(Self { record, calendar })
}
/// Converts a UTF-8 encoded YearMonth string into a `ParsedDate`.
Expand All @@ -59,8 +69,19 @@ impl ParsedDate {
// Assertion: PlainDate must exist on a DateTime parse.
let record = parse_record.date.temporal_unwrap()?;

// TODO: Potentially, remove this check in favor of type guarantee of a
// Temporal parser.
// NOTE: The check here is to confirm that the time component is
// valid.
let _time = parse_record
.time
.map(IsoTime::from_time_record)
.transpose()?
.unwrap_or_default();

Ok(Self { record, calendar })
}

/// Converts a UTF-8 encoded MonthDay string into a `ParsedDate`.
pub fn month_day_from_utf8(s: &[u8]) -> TemporalResult<Self> {
let parse_record = parsers::parse_month_day(s)?;
Expand All @@ -70,6 +91,16 @@ impl ParsedDate {
// Assertion: PlainDate must exist on a DateTime parse.
let record = parse_record.date.temporal_unwrap()?;

// TODO: Potentially, remove this check in favor of type guarantee of a
// Temporal parser.
// NOTE: The check here is to confirm that the time component is
// valid.
let _time = parse_record
.time
.map(IsoTime::from_time_record)
.transpose()?
.unwrap_or_default();

Ok(Self { record, calendar })
}
}
Expand Down