diff --git a/Cargo.lock b/Cargo.lock index 8bf57e178..24311680e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -537,12 +537,11 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "ixdtf" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3be3d801e2817c5311a3be4f1e1b2148dcd2b10baadb3a5eade0544a0521ac9" +checksum = "8289f7f711a1a51f80e2e368355d023042ca55d8d554fd5e953f01464c15842d" dependencies = [ "displaydoc", - "utf8_iter", ] [[package]] @@ -944,12 +943,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index a2219e97b..36a4fbf45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ icu_calendar = { version = "2.0.0", default-features = false } icu_locale = "2.0.0" rustc-hash = "2.1.0" num-traits = { version = "0.2.19", default-features = false } -ixdtf = "0.4.0" +ixdtf = "0.5.0" iana-time-zone = "0.1.63" log = "0.4.27" tzif = "0.3.0" diff --git a/src/builtins/core/instant.rs b/src/builtins/core/instant.rs index 69c9bc35f..e96664c42 100644 --- a/src/builtins/core/instant.rs +++ b/src/builtins/core/instant.rs @@ -171,14 +171,14 @@ impl Instant { let ns_offset = match ixdtf_record.offset { UtcOffsetRecordOrZ::Offset(offset) => { let ns = offset - .fraction + .fraction() .and_then(|x| x.to_nanoseconds()) .unwrap_or(0); - (offset.hour as i64 * NANOSECONDS_PER_HOUR - + i64::from(offset.minute) * NANOSECONDS_PER_MINUTE - + i64::from(offset.second) * NANOSECONDS_PER_SECOND + (offset.hour() as i64 * NANOSECONDS_PER_HOUR + + i64::from(offset.minute()) * NANOSECONDS_PER_MINUTE + + i64::from(offset.second().unwrap_or(0)) * NANOSECONDS_PER_SECOND + i64::from(ns)) - * offset.sign as i64 + * offset.sign() as i64 } UtcOffsetRecordOrZ::Z => 0, }; diff --git a/src/builtins/core/timezone.rs b/src/builtins/core/timezone.rs index 87a453504..292d581d9 100644 --- a/src/builtins/core/timezone.rs +++ b/src/builtins/core/timezone.rs @@ -1,15 +1,15 @@ //! This module implements the Temporal `TimeZone` and components. -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::{vec, vec::Vec}; -use ixdtf::parsers::records::{TimeZoneRecord, UtcOffsetRecord}; +use ixdtf::parsers::records::{MinutePrecisionOffset, TimeZoneRecord, UtcOffsetRecord}; +use ixdtf::parsers::TimeZoneParser; use num_traits::ToPrimitive; use crate::builtins::core::duration::DateDuration; use crate::parsers::{ - parse_allowed_timezone_formats, parse_identifier, parse_offset, FormattableOffset, - FormattableTime, Precision, + parse_allowed_timezone_formats, parse_identifier, FormattableOffset, FormattableTime, Precision, }; use crate::provider::{TimeZoneOffset, TimeZoneProvider}; use crate::{ @@ -28,12 +28,24 @@ const NS_IN_HOUR: i128 = 60 * 60 * 1000 * 1000 * 1000; pub struct UtcOffset(pub(crate) i16); impl UtcOffset { - pub(crate) fn from_ixdtf_record(record: UtcOffsetRecord) -> Self { + pub(crate) fn from_ixdtf_record(record: MinutePrecisionOffset) -> Self { // NOTE: ixdtf parser restricts minute/second to 0..=60 let minutes = i16::from(record.hour) * 60 + record.minute as i16; Self(minutes * i16::from(record.sign as i8)) } + pub fn from_utf8(source: &[u8]) -> TemporalResult { + let record = TimeZoneParser::from_utf8(source) + .parse_offset() + .map_err(|e| TemporalError::range().with_message(e.to_string()))?; + match record { + UtcOffsetRecord::MinutePrecision(offset) => Ok(Self::from_ixdtf_record(offset)), + _ => { + Err(TemporalError::range().with_message("offset must be a minute precision offset")) + } + } + } + pub fn to_string(&self) -> TemporalResult { let sign = if self.0 < 0 { Sign::Negative @@ -60,11 +72,7 @@ impl UtcOffset { impl core::str::FromStr for UtcOffset { type Err = TemporalError; fn from_str(s: &str) -> Result { - let mut cursor = s.chars().peekable(); - match parse_offset(&mut cursor)? { - Some(offset) => Ok(Self(offset)), - None => Err(TemporalError::range().with_message("Invalid offset")), - } + Self::from_utf8(s.as_bytes()) } } diff --git a/src/builtins/core/zoneddatetime.rs b/src/builtins/core/zoneddatetime.rs index d9eb71479..5f388e811 100644 --- a/src/builtins/core/zoneddatetime.rs +++ b/src/builtins/core/zoneddatetime.rs @@ -1190,18 +1190,18 @@ impl ZonedDateTime { let UtcOffsetRecordOrZ::Offset(offset) = record else { return (None, true); }; - let hours_in_ns = i64::from(offset.hour) * 3_600_000_000_000_i64; - let minutes_in_ns = i64::from(offset.minute) * 60_000_000_000_i64; - let seconds_in_ns = i64::from(offset.minute) * 1_000_000_000_i64; + let hours_in_ns = i64::from(offset.hour()) * 3_600_000_000_000_i64; + let minutes_in_ns = i64::from(offset.minute()) * 60_000_000_000_i64; + let seconds_in_ns = i64::from(offset.second().unwrap_or(0)) * 1_000_000_000_i64; let ns = offset - .fraction + .fraction() .and_then(|x| x.to_nanoseconds()) .unwrap_or(0); ( Some( (hours_in_ns + minutes_in_ns + seconds_in_ns + i64::from(ns)) - * i64::from(offset.sign as i8), + * i64::from(offset.sign() as i8), ), false, ) diff --git a/src/options/relative_to.rs b/src/options/relative_to.rs index 0cfee9b7d..27d8043b4 100644 --- a/src/options/relative_to.rs +++ b/src/options/relative_to.rs @@ -68,17 +68,17 @@ impl RelativeTo { let UtcOffsetRecordOrZ::Offset(offset) = record else { return (None, true); }; - let hours_in_ns = i64::from(offset.hour) * 3_600_000_000_000_i64; - let minutes_in_ns = i64::from(offset.minute) * 60_000_000_000_i64; - let seconds_in_ns = i64::from(offset.minute) * 1_000_000_000_i64; + let hours_in_ns = i64::from(offset.hour()) * 3_600_000_000_000_i64; + let minutes_in_ns = i64::from(offset.minute()) * 60_000_000_000_i64; + let seconds_in_ns = i64::from(offset.second().unwrap_or(0)) * 1_000_000_000_i64; let ns = offset - .fraction + .fraction() .and_then(|x| x.to_nanoseconds()) .unwrap_or(0); ( Some( (hours_in_ns + minutes_in_ns + seconds_in_ns + i64::from(ns)) - * i64::from(offset.sign as i8), + * i64::from(offset.sign() as i8), ), false, ) diff --git a/src/parsers.rs b/src/parsers.rs index 0fa82a9db..f255680d9 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -14,7 +14,7 @@ use writeable::{impl_display_with_writeable, LengthHint, Writeable}; mod timezone; -pub(crate) use timezone::{parse_allowed_timezone_formats, parse_identifier, parse_offset}; +pub(crate) use timezone::{parse_allowed_timezone_formats, parse_identifier}; // TODO: Move `Writeable` functionality to `ixdtf` crate diff --git a/src/parsers/timezone.rs b/src/parsers/timezone.rs index 300b8d19a..fcf3c0057 100644 --- a/src/parsers/timezone.rs +++ b/src/parsers/timezone.rs @@ -1,6 +1,9 @@ use alloc::borrow::ToOwned; use core::{iter::Peekable, str::Chars}; -use ixdtf::parsers::{records::UtcOffsetRecordOrZ, IxdtfParser}; +use ixdtf::parsers::{ + records::{UtcOffsetRecord, UtcOffsetRecordOrZ}, + IxdtfParser, +}; use crate::{builtins::timezone::UtcOffset, TemporalError, TemporalResult, TimeZone}; @@ -37,7 +40,11 @@ pub(crate) fn parse_allowed_timezone_formats(s: &str) -> Option { match offset { UtcOffsetRecordOrZ::Z => return Some(TimeZone::default()), UtcOffsetRecordOrZ::Offset(offset) => { - return Some(TimeZone::UtcOffset(UtcOffset::from_ixdtf_record(offset))) + let offset = match offset { + UtcOffsetRecord::MinutePrecision(offset) => offset, + _ => return None, + }; + return Some(TimeZone::UtcOffset(UtcOffset::from_ixdtf_record(offset))); } } } @@ -45,6 +52,7 @@ pub(crate) fn parse_allowed_timezone_formats(s: &str) -> Option { None } +// TODO: Update `ixdtf` to expose parse_time_zone_record #[inline] pub(crate) fn parse_identifier(source: &str) -> TemporalResult { let mut cursor = source.chars().peekable();