diff --git a/components/datetime/src/combo.rs b/components/datetime/src/combo.rs index 6471ffbd1cd..5aa89dcc7d2 100644 --- a/components/datetime/src/combo.rs +++ b/components/datetime/src/combo.rs @@ -25,7 +25,7 @@ use crate::{provider::neo::*, scaffold::*}; /// use icu::datetime::fieldsets::{Combo, ET, zone::Location}; /// use icu::datetime::DateTimeFormatter; /// use icu::locale::locale; -/// use icu::timezone::ZonedDateTimeParser; +/// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper}; /// use writeable::assert_writeable_eq; /// /// // Note: Combo type can be elided, but it is shown here for demonstration @@ -35,9 +35,12 @@ use crate::{provider::neo::*, scaffold::*}; /// ) /// .unwrap(); /// -/// let zdt = ZonedDateTimeParser::new() -/// .parse_location_only("2024-10-18T15:44[America/Los_Angeles]", formatter.calendar()) -/// .unwrap(); +/// let zdt = ZonedDateTime::try_location_only_from_str( +/// "2024-10-18T15:44[America/Los_Angeles]", +/// formatter.calendar(), +/// TimeZoneIdMapper::new(), +/// ) +/// .unwrap(); /// /// assert_writeable_eq!( /// formatter.format(&zdt), @@ -52,7 +55,7 @@ use crate::{provider::neo::*, scaffold::*}; /// use icu::datetime::fieldsets::{Combo, ET, zone::Location}; /// use icu::datetime::FixedCalendarDateTimeFormatter; /// use icu::locale::locale; -/// use icu::timezone::{ZonedDateTimeParser, ZonedDateTime}; +/// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper}; /// use writeable::assert_writeable_eq; /// /// // Note: Combo type can be elided, but it is shown here for demonstration @@ -62,8 +65,7 @@ use crate::{provider::neo::*, scaffold::*}; /// ) /// .unwrap(); /// -/// let zdt = ZonedDateTimeParser::new() -/// .parse_location_only("2024-10-18T15:44[America/Los_Angeles]", Gregorian) +/// let zdt = ZonedDateTime::try_location_only_from_str("2024-10-18T15:44[America/Los_Angeles]", Gregorian, TimeZoneIdMapper::new()) /// .unwrap(); /// /// assert_writeable_eq!( @@ -79,7 +81,7 @@ use crate::{provider::neo::*, scaffold::*}; /// use icu::datetime::fieldsets::{enums::DateFieldSet, Combo, zone::GenericShort, YMD}; /// use icu::datetime::DateTimeFormatter; /// use icu::locale::locale; -/// use icu::timezone::ZonedDateTimeParser; +/// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper}; /// use writeable::assert_writeable_eq; /// /// // Note: Combo type can be elided, but it is shown here for demonstration @@ -89,9 +91,12 @@ use crate::{provider::neo::*, scaffold::*}; /// ) /// .unwrap(); /// -/// let zdt = ZonedDateTimeParser::new() -/// .parse_location_only("2024-10-18T15:44[America/Los_Angeles]", formatter.calendar()) -/// .unwrap(); +/// let zdt = ZonedDateTime::try_location_only_from_str( +/// "2024-10-18T15:44[America/Los_Angeles]", +/// formatter.calendar(), +/// TimeZoneIdMapper::new(), +/// ) +/// .unwrap(); /// /// assert_writeable_eq!( /// formatter.format(&zdt), @@ -106,7 +111,7 @@ use crate::{provider::neo::*, scaffold::*}; /// use icu::datetime::fieldsets::{T, zone::SpecificLong}; /// use icu::datetime::FixedCalendarDateTimeFormatter; /// use icu::locale::locale; -/// use icu::timezone::{ZonedDateTimeParser, ZonedDateTime}; +/// use icu::timezone::{ZonedDateTime, ZoneOffsetCalculator, TimeZoneIdMapper}; /// use writeable::assert_writeable_eq; /// /// let formatter = FixedCalendarDateTimeFormatter::try_new( @@ -115,9 +120,13 @@ use crate::{provider::neo::*, scaffold::*}; /// ) /// .unwrap(); /// -/// let zdt = ZonedDateTimeParser::new() -/// .parse("2024-10-18T15:44-0700[America/Los_Angeles]", Gregorian) -/// .unwrap(); +/// let zdt = ZonedDateTime::try_from_str( +/// "2024-10-18T15:44-0700[America/Los_Angeles]", +/// Gregorian, +/// TimeZoneIdMapper::new(), +/// &ZoneOffsetCalculator::new(), +/// ) +/// .unwrap(); /// /// assert_writeable_eq!( /// formatter.format(&zdt), diff --git a/components/datetime/src/fieldsets.rs b/components/datetime/src/fieldsets.rs index 35c76931019..ce5067cdb13 100644 --- a/components/datetime/src/fieldsets.rs +++ b/components/datetime/src/fieldsets.rs @@ -1136,7 +1136,7 @@ pub mod zone { /// /// ``` /// use icu::calendar::Date; - /// use icu::timezone::{ZonedDateTimeParser, Time, TimeZoneBcp47Id, TimeZoneInfo, UtcOffset, ZoneVariant}; + /// use icu::timezone::{Time, TimeZoneBcp47Id, TimeZoneInfo, UtcOffset, ZoneVariant}; /// use icu::calendar::Gregorian; /// use icu::datetime::FixedCalendarDateTimeFormatter; /// use icu::datetime::fieldsets::zone::{SpecificLong, SpecificShort}; diff --git a/components/datetime/src/parts.rs b/components/datetime/src/parts.rs index 6c7a69d9ea2..9ea8d7ba398 100644 --- a/components/datetime/src/parts.rs +++ b/components/datetime/src/parts.rs @@ -16,7 +16,7 @@ //! use icu::datetime::DateTimeFormatter; //! use icu::decimal::parts as decimal_parts; //! use icu::locale::locale; -//! use icu::timezone::{ZonedDateTimeParser, Time}; +//! use icu::timezone::{ZonedDateTime, Time, TimeZoneIdMapper, ZoneOffsetCalculator}; //! use writeable::assert_writeable_parts_eq; //! //! let dtf = DateTimeFormatter::try_new( @@ -25,7 +25,7 @@ //! ) //! .unwrap(); //! -//! let dtz = ZonedDateTimeParser::new().parse("2023-11-20T11:35:03.5+00:00[Europe/London]", dtf.calendar()).unwrap(); +//! let dtz = ZonedDateTime::try_from_str("2023-11-20T11:35:03.5+00:00[Europe/London]", dtf.calendar(), TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()).unwrap(); //! //! // Missing data is filled in on a best-effort basis, and an error is signaled. //! assert_writeable_parts_eq!( diff --git a/components/datetime/src/pattern/formatter.rs b/components/datetime/src/pattern/formatter.rs index d1b7e97d82e..af9daf5ee8d 100644 --- a/components/datetime/src/pattern/formatter.rs +++ b/components/datetime/src/pattern/formatter.rs @@ -157,14 +157,12 @@ where /// use icu::datetime::pattern::DateTimePattern; /// use icu::datetime::pattern::TypedDateTimeNames; /// use icu::locale::locale; - /// use icu::timezone::ZonedDateTimeParser; + /// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper, ZoneOffsetCalculator}; /// use writeable::assert_try_writeable_eq; /// - /// let mut london_winter = ZonedDateTimeParser::new() - /// .parse("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian) + /// let mut london_winter = ZonedDateTime::try_from_str("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap(); - /// let mut london_summer = ZonedDateTimeParser::new() - /// .parse("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian) + /// let mut london_summer = ZonedDateTime::try_from_str("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap(); /// /// let mut names = TypedDateTimeNames::::try_new( diff --git a/components/datetime/src/pattern/names.rs b/components/datetime/src/pattern/names.rs index b06c21f1bf4..d6c227e6e0b 100644 --- a/components/datetime/src/pattern/names.rs +++ b/components/datetime/src/pattern/names.rs @@ -433,7 +433,7 @@ size_test!( /// use icu::datetime::pattern::{DateTimePattern, PatternLoadError}; /// use icu::datetime::fieldsets::enums::CompositeFieldSet; /// use icu::locale::locale; -/// use icu::timezone::{Time, TimeZoneInfo, ZonedDateTimeParser, ZonedDateTime}; +/// use icu::timezone::{Time, TimeZoneInfo, TimeZoneIdMapper, ZonedDateTime, ZoneOffsetCalculator}; /// use icu_provider_adapters::empty::EmptyDataProvider; /// use writeable::{Part, assert_try_writeable_parts_eq}; /// @@ -451,7 +451,7 @@ size_test!( /// // The pattern string contains lots of symbols including "E", "MMM", and "a", /// // but we did not load any data! /// -/// let mut dtz = ZonedDateTimeParser::new().parse("2023-11-20T11:35:03+00:00[Europe/London]", Gregorian).unwrap(); +/// let mut dtz = ZonedDateTime::try_from_str("2023-11-20T11:35:03+00:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()).unwrap(); /// /// // Missing data is filled in on a best-effort basis, and an error is signaled. /// assert_try_writeable_parts_eq!( @@ -1404,15 +1404,13 @@ impl TypedDateTimeNames { /// use icu::datetime::pattern::DateTimePattern; /// use icu::datetime::pattern::TypedDateTimeNames; /// use icu::locale::locale; - /// use icu::timezone::ZonedDateTimeParser; + /// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper, ZoneOffsetCalculator}; /// use writeable::assert_try_writeable_eq; /// - /// let mut zone_london_winter = ZonedDateTimeParser::new() - /// .parse("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian) + /// let mut zone_london_winter = ZonedDateTime::try_from_str("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; - /// let mut zone_london_summer = ZonedDateTimeParser::new() - /// .parse("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian) + /// let mut zone_london_summer = ZonedDateTime::try_from_str("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; /// @@ -1516,11 +1514,10 @@ impl TypedDateTimeNames { /// use icu::datetime::pattern::DateTimePattern; /// use icu::datetime::pattern::TypedDateTimeNames; /// use icu::locale::locale; - /// use icu::timezone::ZonedDateTimeParser; + /// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper, ZoneOffsetCalculator}; /// use writeable::assert_try_writeable_eq; /// - /// let mut zone_london_winter = ZonedDateTimeParser::new() - /// .parse("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian) + /// let mut zone_london_winter = ZonedDateTime::try_from_str("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; /// @@ -1580,11 +1577,10 @@ impl TypedDateTimeNames { /// use icu::datetime::pattern::DateTimePattern; /// use icu::datetime::pattern::TypedDateTimeNames; /// use icu::locale::locale; - /// use icu::timezone::ZonedDateTimeParser; + /// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper, ZoneOffsetCalculator}; /// use writeable::assert_try_writeable_eq; /// - /// let mut zone_london_winter = ZonedDateTimeParser::new() - /// .parse("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian) + /// let mut zone_london_winter = ZonedDateTime::try_from_str("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; /// @@ -1644,15 +1640,13 @@ impl TypedDateTimeNames { /// use icu::datetime::pattern::DateTimePattern; /// use icu::datetime::pattern::TypedDateTimeNames; /// use icu::locale::locale; - /// use icu::timezone::ZonedDateTimeParser; + /// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper, ZoneOffsetCalculator}; /// use writeable::assert_try_writeable_eq; /// - /// let mut zone_london_winter = ZonedDateTimeParser::new() - /// .parse("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian) + /// let mut zone_london_winter = ZonedDateTime::try_from_str("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; - /// let mut zone_london_summer = ZonedDateTimeParser::new() - /// .parse("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian) + /// let mut zone_london_summer = ZonedDateTime::try_from_str("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; /// @@ -1718,15 +1712,13 @@ impl TypedDateTimeNames { /// use icu::datetime::pattern::DateTimePattern; /// use icu::datetime::pattern::TypedDateTimeNames; /// use icu::locale::locale; - /// use icu::timezone::ZonedDateTimeParser; + /// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper, ZoneOffsetCalculator}; /// use writeable::assert_try_writeable_eq; /// - /// let mut zone_london_winter = ZonedDateTimeParser::new() - /// .parse("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian) + /// let mut zone_london_winter = ZonedDateTime::try_from_str("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; - /// let mut zone_london_summer = ZonedDateTimeParser::new() - /// .parse("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian) + /// let mut zone_london_summer = ZonedDateTime::try_from_str("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; /// @@ -1792,15 +1784,13 @@ impl TypedDateTimeNames { /// use icu::datetime::pattern::DateTimePattern; /// use icu::datetime::pattern::TypedDateTimeNames; /// use icu::locale::locale; - /// use icu::timezone::ZonedDateTimeParser; + /// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper, ZoneOffsetCalculator}; /// use writeable::assert_try_writeable_eq; /// - /// let mut zone_london_winter = ZonedDateTimeParser::new() - /// .parse("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian) + /// let mut zone_london_winter = ZonedDateTime::try_from_str("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; - /// let mut zone_london_summer = ZonedDateTimeParser::new() - /// .parse("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian) + /// let mut zone_london_summer = ZonedDateTime::try_from_str("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; /// @@ -1866,15 +1856,13 @@ impl TypedDateTimeNames { /// use icu::datetime::pattern::DateTimePattern; /// use icu::datetime::pattern::TypedDateTimeNames; /// use icu::locale::locale; - /// use icu::timezone::ZonedDateTimeParser; + /// use icu::timezone::{ZonedDateTime, TimeZoneIdMapper, ZoneOffsetCalculator}; /// use writeable::assert_try_writeable_eq; /// - /// let mut zone_london_winter = ZonedDateTimeParser::new() - /// .parse("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian) + /// let mut zone_london_winter = ZonedDateTime::try_from_str("2024-01-01T00:00:00+00:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; - /// let mut zone_london_summer = ZonedDateTimeParser::new() - /// .parse("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian) + /// let mut zone_london_summer = ZonedDateTime::try_from_str("2024-07-01T00:00:00+01:00[Europe/London]", Gregorian, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap() /// .zone; /// diff --git a/components/datetime/tests/mock.rs b/components/datetime/tests/mock.rs index a824575f79a..2501db0db74 100644 --- a/components/datetime/tests/mock.rs +++ b/components/datetime/tests/mock.rs @@ -5,7 +5,9 @@ //! Some useful parsing functions for tests. use icu_calendar::Gregorian; -use icu_timezone::{models, TimeZoneInfo, ZoneVariant, ZonedDateTime, ZonedDateTimeParser}; +use icu_timezone::{ + models, TimeZoneIdMapper, TimeZoneInfo, ZoneOffsetCalculator, ZoneVariant, ZonedDateTime, +}; /// Parse a [`DateTime`] and [`TimeZoneInfo`] from a string. /// @@ -28,10 +30,15 @@ use icu_timezone::{models, TimeZoneInfo, ZoneVariant, ZonedDateTime, ZonedDateTi pub fn parse_zoned_gregorian_from_str( input: &str, ) -> ZonedDateTime> { - match ZonedDateTimeParser::new().parse(input, Gregorian) { + match ZonedDateTime::try_from_str( + input, + Gregorian, + TimeZoneIdMapper::new(), + &ZoneOffsetCalculator::new(), + ) { Ok(zdt) => zdt, Err(icu_timezone::ParseError::MismatchedTimeZoneFields) => { - match ZonedDateTimeParser::new().parse_loose(input, Gregorian) { + match ZonedDateTime::try_loose_from_str(input, Gregorian, TimeZoneIdMapper::new()) { Ok(zdt) => { ZonedDateTime { date: zdt.date, diff --git a/components/icu/examples/jiff.rs b/components/icu/examples/jiff.rs index dff564a1984..04e627299b2 100644 --- a/components/icu/examples/jiff.rs +++ b/components/icu/examples/jiff.rs @@ -6,7 +6,7 @@ use icu::{ calendar::Date, datetime::{fieldsets, DateTimeFormatter}, locale::locale, - timezone::{Time, TimeZoneIdMapper, UtcOffset, ZonedDateTime, ZonedDateTimeParser}, + timezone::{Time, TimeZoneIdMapper, UtcOffset, ZoneOffsetCalculator, ZonedDateTime}, }; fn main() -> Result<(), Box> { @@ -45,9 +45,13 @@ fn main() -> Result<(), Box> { // Alternatively, the ICU ZonedDateTime can be parsed from a serialized IXDTF string. assert_eq!( - ZonedDateTimeParser::new() - .parse(&zoned.to_string(), icu::calendar::Iso) - .unwrap(), + ZonedDateTime::try_from_str( + &zoned.to_string(), + icu::calendar::Iso, + TimeZoneIdMapper::new(), + &ZoneOffsetCalculator::new() + ) + .unwrap(), zoned_date_time ); diff --git a/components/timezone/src/ixdtf.rs b/components/timezone/src/ixdtf.rs index 62d7610f423..c45f1fdbaa8 100644 --- a/components/timezone/src/ixdtf.rs +++ b/components/timezone/src/ixdtf.rs @@ -3,14 +3,11 @@ // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use crate::{ - provider::{names::IanaToBcp47MapV3, ZoneOffsetPeriodV1}, - time_zone::models, - DateTime, InvalidOffsetError, Time, TimeZoneBcp47Id, TimeZoneIdMapper, + time_zone::models, DateTime, InvalidOffsetError, Time, TimeZoneBcp47Id, TimeZoneIdMapperBorrowed, TimeZoneInfo, UtcOffset, ZoneOffsetCalculator, ZonedDateTime, }; use core::str::FromStr; use icu_calendar::{AnyCalendarKind, AsCalendar, Date, DateError, Iso, RangeError}; -use icu_provider::prelude::*; use ixdtf::{ parsers::{ records::{ @@ -56,17 +53,17 @@ pub enum ParseError { /// # Example /// ``` /// use icu::calendar::Iso; - /// use icu::timezone::{ZonedDateTimeParser, ParseError}; + /// use icu::timezone::{ZonedDateTime, ParseError, TimeZoneIdMapper}; /// /// // This timestamp is in UTC, and requires a time zone calculation in order to display a Zurich time. /// assert_eq!( - /// ZonedDateTimeParser::new().parse_loose("2024-08-12T12:32:00Z[Europe/Zurich]", Iso).unwrap_err(), + /// ZonedDateTime::try_loose_from_str("2024-08-12T12:32:00Z[Europe/Zurich]", Iso, TimeZoneIdMapper::new()).unwrap_err(), /// ParseError::RequiresCalculation, /// ); /// /// // These timestamps are in local time - /// ZonedDateTimeParser::new().parse_loose("2024-08-12T14:32:00+02:00[Europe/Zurich]", Iso).unwrap(); - /// ZonedDateTimeParser::new().parse_loose("2024-08-12T14:32:00[Europe/Zurich]", Iso).unwrap(); + /// ZonedDateTime::try_loose_from_str("2024-08-12T14:32:00+02:00[Europe/Zurich]", Iso, TimeZoneIdMapper::new()).unwrap(); + /// ZonedDateTime::try_loose_from_str("2024-08-12T14:32:00[Europe/Zurich]", Iso, TimeZoneIdMapper::new()).unwrap(); /// ``` #[displaydoc( "A timezone calculation is required to interpret this string, which is not supported" @@ -124,52 +121,6 @@ impl UtcOffset { } } -#[derive(Debug)] -/// A parser for [`ZonedDateTime`] based on IXDTF strings. -/// -/// Any function that takes a calendar argument returns an error if the -/// string has a calendar annotation that does not match the calendar -/// argument. -/// -/// ✨ *Enabled with the `ixdtf` Cargo feature.* -pub struct ZonedDateTimeParser { - mapper: TimeZoneIdMapper, - offsets: ZoneOffsetCalculator, -} - -impl ZonedDateTimeParser { - /// Creates a new [`ZonedDateTimeParser`] from compiled data. - #[cfg(feature = "compiled_data")] - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - Self { - mapper: TimeZoneIdMapper::new().static_to_owned(), - offsets: ZoneOffsetCalculator::new(), - } - } - - icu_provider::gen_buffer_data_constructors!( - () -> error: DataError, - functions: [ - new: skip, - try_new_with_buffer_provider, - try_new_unstable, - Self - ] - ); - - #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)] - pub fn try_new_unstable

(provider: &P) -> Result - where - P: ?Sized + DataProvider + DataProvider, - { - Ok(Self { - mapper: TimeZoneIdMapper::try_new_unstable(provider)?, - offsets: ZoneOffsetCalculator::try_new_unstable(provider)?, - }) - } -} - struct Intermediate<'a> { offset: Option, is_z: bool, @@ -401,63 +352,56 @@ impl<'a> Intermediate<'a> { } } -impl ZonedDateTimeParser { +impl ZonedDateTime { /// Create a [`ZonedDateTime`] in any calendar from an IXDTF syntax string. /// /// Returns an error if the string has a calendar annotation that does not /// match the calendar argument, unless the argument is [`Iso`]. /// /// This function is "strict": the string should have only an offset and no named time zone. - pub fn parse_offset_only( - &self, - ixdtf_str: &str, - calendar: A, - ) -> Result, ParseError> { - self.parse_offset_only_from_utf8(ixdtf_str.as_bytes(), calendar) + pub fn try_offset_only_from_str(ixdtf_str: &str, calendar: A) -> Result { + Self::try_offset_only_from_utf8(ixdtf_str.as_bytes(), calendar) } /// Create a [`ZonedDateTime`] in any calendar from IXDTF syntax UTF-8 bytes. /// - /// See [`Self::parse_offset_only`]. - pub fn parse_offset_only_from_utf8( - &self, - ixdtf_str: &[u8], - calendar: A, - ) -> Result, ParseError> { + /// See [`Self:try_offset_only_from_str`](Self::try_offset_only_from_str). + pub fn try_offset_only_from_utf8(ixdtf_str: &[u8], calendar: A) -> Result { let ixdtf_record = IxdtfParser::from_utf8(ixdtf_str).parse()?; let date = Date::try_from_ixdtf_record(&ixdtf_record, calendar)?; let time = Time::try_from_ixdtf_record(&ixdtf_record)?; let zone = Intermediate::try_from_ixdtf_record(&ixdtf_record)?.offset_only()?; Ok(ZonedDateTime { date, time, zone }) } +} +impl ZonedDateTime> { /// Create a [`ZonedDateTime`] in any calendar from an IXDTF syntax string. /// /// Returns an error if the string has a calendar annotation that does not /// match the calendar argument, unless the argument is [`Iso`]. /// /// This function is "strict": the string should have only a named time zone and no offset. - pub fn parse_location_only( - &self, + pub fn try_location_only_from_str( ixdtf_str: &str, calendar: A, - ) -> Result>, ParseError> { - self.parse_location_only_from_utf8(ixdtf_str.as_bytes(), calendar) + mapper: TimeZoneIdMapperBorrowed, + ) -> Result { + Self::try_location_only_from_utf8(ixdtf_str.as_bytes(), calendar, mapper) } /// Create a [`ZonedDateTime`] in any calendar from IXDTF syntax UTF-8 bytes. /// - /// See [`Self::parse_location_only`]. - pub fn parse_location_only_from_utf8( - &self, + /// See [`Self::try_location_only_from_str`]. + pub fn try_location_only_from_utf8( ixdtf_str: &[u8], calendar: A, - ) -> Result>, ParseError> { + mapper: TimeZoneIdMapperBorrowed, + ) -> Result { let ixdtf_record = IxdtfParser::from_utf8(ixdtf_str).parse()?; let date = Date::try_from_ixdtf_record(&ixdtf_record, calendar)?; let time = Time::try_from_ixdtf_record(&ixdtf_record)?; - let zone = Intermediate::try_from_ixdtf_record(&ixdtf_record)? - .location_only(self.mapper.as_borrowed())?; + let zone = Intermediate::try_from_ixdtf_record(&ixdtf_record)?.location_only(mapper)?; Ok(ZonedDateTime { date, time, zone }) } @@ -470,31 +414,32 @@ impl ZonedDateTimeParser { /// neither. If the named time zone is missing, it is returned as Etc/Unknown. /// /// The zone variant is _not_ calculated with this function. If you need it, use - /// [`Self::parse`]. - pub fn parse_loose( - &self, + /// [`Self::try_from_str`]. + pub fn try_loose_from_str( ixdtf_str: &str, calendar: A, - ) -> Result>, ParseError> { - self.parse_loose_from_utf8(ixdtf_str.as_bytes(), calendar) + mapper: TimeZoneIdMapperBorrowed, + ) -> Result { + Self::try_loose_from_utf8(ixdtf_str.as_bytes(), calendar, mapper) } /// Create a [`ZonedDateTime`] in any calendar from IXDTF syntax UTF-8 bytes. /// - /// See [`Self::parse_loose`]. - pub fn parse_loose_from_utf8( - &self, + /// See [`Self::try_loose_from_str`]. + pub fn try_loose_from_utf8( ixdtf_str: &[u8], calendar: A, - ) -> Result>, ParseError> { + mapper: TimeZoneIdMapperBorrowed, + ) -> Result { let ixdtf_record = IxdtfParser::from_utf8(ixdtf_str).parse()?; let date = Date::try_from_ixdtf_record(&ixdtf_record, calendar)?; let time = Time::try_from_ixdtf_record(&ixdtf_record)?; - let zone = - Intermediate::try_from_ixdtf_record(&ixdtf_record)?.loose(self.mapper.as_borrowed())?; + let zone = Intermediate::try_from_ixdtf_record(&ixdtf_record)?.loose(mapper)?; Ok(ZonedDateTime { date, time, zone }) } +} +impl ZonedDateTime> { /// Create a [`ZonedDateTime`] in any calendar from an IXDTF syntax string. /// /// Returns an error if the string has a calendar annotation that does not @@ -511,13 +456,17 @@ impl ZonedDateTimeParser { /// ``` /// use icu_calendar::cal::Hebrew; /// use icu_timezone::{ - /// ZonedDateTimeParser, TimeZoneBcp47Id, TimeZoneInfo, UtcOffset, ZoneVariant, + /// ZonedDateTime, TimeZoneBcp47Id, TimeZoneInfo, UtcOffset, ZoneVariant, TimeZoneIdMapper, ZoneOffsetCalculator /// }; /// use tinystr::tinystr; /// - /// let zoneddatetime = ZonedDateTimeParser::new() - /// .parse("2024-08-08T12:08:19-05:00[America/Chicago][u-ca=hebrew]", Hebrew) - /// .unwrap(); + /// let zoneddatetime = ZonedDateTime::try_from_str( + /// "2024-08-08T12:08:19-05:00[America/Chicago][u-ca=hebrew]", + /// Hebrew, + /// TimeZoneIdMapper::new(), + /// &ZoneOffsetCalculator::new(), + /// ) + /// .unwrap(); /// /// assert_eq!(zoneddatetime.date.year().extended_year, 5784); /// assert_eq!( @@ -554,10 +503,9 @@ impl ZonedDateTimeParser { /// /// ``` /// use icu_calendar::Iso; - /// use icu_timezone::{ZonedDateTimeParser, TimeZoneInfo, UtcOffset}; + /// use icu_timezone::{ZonedDateTime, TimeZoneInfo, UtcOffset}; /// - /// let tz_from_offset = ZonedDateTimeParser::new() - /// .parse_offset_only("2024-08-08T12:08:19-05:00", Iso) + /// let tz_from_offset = ZonedDateTime::try_offset_only_from_str("2024-08-08T12:08:19-05:00", Iso) /// .unwrap(); /// /// assert_eq!( @@ -573,15 +521,13 @@ impl ZonedDateTimeParser { /// ``` /// use icu_calendar::Iso; /// use icu_timezone::{ - /// ZonedDateTimeParser, TimeZoneBcp47Id, TimeZoneInfo, UtcOffset, ZoneVariant, + /// ZonedDateTime, TimeZoneBcp47Id, TimeZoneInfo, UtcOffset, ZoneVariant, TimeZoneIdMapper /// }; /// use tinystr::tinystr; /// - /// let tz_from_offset_annotation = ZonedDateTimeParser::new() - /// .parse_offset_only("2024-08-08T12:08:19[-05:00]", Iso) + /// let tz_from_offset_annotation = ZonedDateTime::try_offset_only_from_str("2024-08-08T12:08:19[-05:00]", Iso) /// .unwrap(); - /// let tz_from_iana_annotation = ZonedDateTimeParser::new() - /// .parse_location_only("2024-08-08T12:08:19[America/Chicago]", Iso) + /// let tz_from_iana_annotation = ZonedDateTime::try_location_only_from_str("2024-08-08T12:08:19[America/Chicago]", Iso, TimeZoneIdMapper::new()) /// .unwrap(); /// /// assert_eq!( @@ -607,10 +553,10 @@ impl ZonedDateTimeParser { /// /// ``` /// use icu_calendar::Iso; - /// use icu_timezone::{TimeZoneInfo, ZonedDateTimeParser, UtcOffset, TimeZoneBcp47Id, ZoneVariant, ParseError}; + /// use icu_timezone::{TimeZoneInfo, ZonedDateTime, UtcOffset, TimeZoneBcp47Id, ZoneVariant, ParseError, TimeZoneIdMapper, ZoneOffsetCalculator}; /// use tinystr::tinystr; /// - /// let consistent_tz_from_both = ZonedDateTimeParser::new().parse("2024-08-08T12:08:19-05:00[America/Chicago]", Iso).unwrap(); + /// let consistent_tz_from_both = ZonedDateTime::try_from_str("2024-08-08T12:08:19-05:00[America/Chicago]", Iso, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()).unwrap(); /// /// /// assert_eq!(consistent_tz_from_both.zone.time_zone_id(), TimeZoneBcp47Id(tinystr!(8, "uschi"))); @@ -622,7 +568,7 @@ impl ZonedDateTimeParser { /// // time zone or the offset are wrong. /// // The only valid way to display this zoned datetime is "GMT-5", so we drop the time zone. /// assert_eq!( - /// ZonedDateTimeParser::new().parse("2024-08-08T12:08:19-05:00[America/Los_Angeles]", Iso) + /// ZonedDateTime::try_from_str("2024-08-08T12:08:19-05:00[America/Los_Angeles]", Iso, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()) /// .unwrap().zone.time_zone_id(), /// TimeZoneBcp47Id::unknown() /// ); @@ -630,7 +576,7 @@ impl ZonedDateTimeParser { /// // We don't know that America/Los_Angeles didn't use standard time (-08:00) in August, but we have a /// // name for Los Angeles at -8 (Pacific Standard Time), so this parses successfully. /// assert!( - /// ZonedDateTimeParser::new().parse("2024-08-08T12:08:19-08:00[America/Los_Angeles]", Iso).is_ok() + /// ZonedDateTime::try_from_str("2024-08-08T12:08:19-08:00[America/Los_Angeles]", Iso, TimeZoneIdMapper::new(), &ZoneOffsetCalculator::new()).is_ok() /// ); /// ``` /// @@ -641,12 +587,12 @@ impl ZonedDateTimeParser { /// ``` /// use icu_calendar::Iso; /// use icu_timezone::{ - /// ZonedDateTimeParser, ParseError, TimeZoneBcp47Id, TimeZoneInfo, UtcOffset, + /// ZonedDateTime, ParseError, TimeZoneBcp47Id, TimeZoneInfo, UtcOffset, /// }; /// use tinystr::tinystr; /// - /// let consistent_tz_from_both = ZonedDateTimeParser::new() - /// .parse_offset_only("2024-08-08T12:08:19-05:00[-05:00]", Iso) + /// let consistent_tz_from_both = ZonedDateTime:: + /// try_offset_only_from_str("2024-08-08T12:08:19-05:00[-05:00]", Iso) /// .unwrap(); /// /// assert_eq!( @@ -654,35 +600,36 @@ impl ZonedDateTimeParser { /// UtcOffset::try_from_seconds(-18000).unwrap() /// ); /// - /// let inconsistent_tz_from_both = ZonedDateTimeParser::new() - /// .parse_offset_only("2024-08-08T12:08:19-05:00[+05:00]", Iso); + /// let inconsistent_tz_from_both = ZonedDateTime:: + /// try_offset_only_from_str("2024-08-08T12:08:19-05:00[+05:00]", Iso); /// /// assert!(matches!( /// inconsistent_tz_from_both, /// Err(ParseError::InconsistentTimeZoneOffsets) /// )); /// ``` - pub fn parse( - &self, + pub fn try_from_str( ixdtf_str: &str, calendar: A, - ) -> Result>, ParseError> { - self.parse_from_utf8(ixdtf_str.as_bytes(), calendar) + mapper: TimeZoneIdMapperBorrowed, + offsets: &ZoneOffsetCalculator, + ) -> Result { + Self::try_from_utf8(ixdtf_str.as_bytes(), calendar, mapper, offsets) } /// Create a [`ZonedDateTime`] in any calendar from IXDTF syntax UTF-8 bytes. /// - /// See [`Self::parse`]. - pub fn parse_from_utf8( - &self, + /// See [`Self::try_from_str`]. + pub fn try_from_utf8( ixdtf_str: &[u8], calendar: A, - ) -> Result>, ParseError> { + mapper: TimeZoneIdMapperBorrowed, + offsets: &ZoneOffsetCalculator, + ) -> Result { let ixdtf_record = IxdtfParser::from_utf8(ixdtf_str).parse()?; let date = Date::try_from_ixdtf_record(&ixdtf_record, calendar)?; let time = Time::try_from_ixdtf_record(&ixdtf_record)?; - let zone = Intermediate::try_from_ixdtf_record(&ixdtf_record)? - .full(self.mapper.as_borrowed(), &self.offsets)?; + let zone = Intermediate::try_from_ixdtf_record(&ixdtf_record)?.full(mapper, offsets)?; Ok(ZonedDateTime { date, time, zone }) } @@ -801,8 +748,7 @@ mod test { #[test] fn max_possible_ixdtf_utc_offset() { assert_eq!( - ZonedDateTimeParser::new() - .parse_offset_only("2024-08-08T12:08:19+23:59:60.999999999", Iso) + ZonedDateTime::try_offset_only_from_str("2024-08-08T12:08:19+23:59:60.999999999", Iso) .unwrap_err(), ParseError::InvalidOffsetError ); @@ -810,18 +756,14 @@ mod test { #[test] fn zone_calculations() { - ZonedDateTimeParser::new() - .parse_offset_only("2024-08-08T12:08:19Z", Iso) - .unwrap(); + ZonedDateTime::try_offset_only_from_str("2024-08-08T12:08:19Z", Iso).unwrap(); assert_eq!( - ZonedDateTimeParser::new() - .parse_offset_only("2024-08-08T12:08:19Z[+08:00]", Iso) + ZonedDateTime::try_offset_only_from_str("2024-08-08T12:08:19Z[+08:00]", Iso) .unwrap_err(), ParseError::RequiresCalculation ); assert_eq!( - ZonedDateTimeParser::new() - .parse_offset_only("2024-08-08T12:08:19Z[Europe/Zurich]", Iso) + ZonedDateTime::try_offset_only_from_str("2024-08-08T12:08:19Z[Europe/Zurich]", Iso) .unwrap_err(), ParseError::MismatchedTimeZoneFields ); @@ -829,10 +771,23 @@ mod test { #[test] fn future_zone() { - let result = ZonedDateTimeParser::new() - .parse_loose("2024-08-08T12:08:19[Future/Zone]", Iso) - .unwrap(); + let result = ZonedDateTime::try_loose_from_str( + "2024-08-08T12:08:19[Future/Zone]", + Iso, + TimeZoneIdMapperBorrowed::new(), + ) + .unwrap(); assert_eq!(result.zone.time_zone_id(), TimeZoneBcp47Id::unknown()); assert_eq!(result.zone.offset(), None); } + + #[test] + fn lax() { + ZonedDateTime::try_location_only_from_str( + "2024-10-18T15:44[America/Los_Angeles]", + icu_calendar::cal::Gregorian, + crate::TimeZoneIdMapper::new(), + ) + .unwrap(); + } } diff --git a/components/timezone/src/lib.rs b/components/timezone/src/lib.rs index 3f37c1ca504..78a933714a2 100644 --- a/components/timezone/src/lib.rs +++ b/components/timezone/src/lib.rs @@ -114,8 +114,6 @@ mod zone_offset; #[cfg(feature = "ixdtf")] mod ixdtf; -#[cfg(feature = "ixdtf")] -pub use self::ixdtf::ZonedDateTimeParser; pub use error::InvalidOffsetError; pub use ids::{ diff --git a/ffi/capi/bindings/dart/ZonedDateTimeParser.g.dart b/ffi/capi/bindings/dart/ZonedDateTimeParser.g.dart index dc05459d3da..16f17af5b19 100644 --- a/ffi/capi/bindings/dart/ZonedDateTimeParser.g.dart +++ b/ffi/capi/bindings/dart/ZonedDateTimeParser.g.dart @@ -45,7 +45,7 @@ final class ZonedDateTimeParser implements ffi.Finalizable { /// Creates a new [`ZonedIsoDateTime`] from an IXDTF string. /// - /// See the [Rust documentation for `try_from_str`](https://docs.rs/icu/latest/icu/timezone/struct.ZonedDateTimeParser.html#method.try_from_str) for more information. + /// See the [Rust documentation for `try_from_str`](https://docs.rs/icu/latest/icu/timezone/struct.ZonedDateTime.html#method.try_from_str) for more information. /// /// Throws [CalendarParseError] on failure. ZonedIsoDateTime tryIsoFromStr(String v) { diff --git a/ffi/capi/src/zoned_datetime.rs b/ffi/capi/src/zoned_datetime.rs index 6aad97c3607..d85f2f5c7c6 100644 --- a/ffi/capi/src/zoned_datetime.rs +++ b/ffi/capi/src/zoned_datetime.rs @@ -21,7 +21,10 @@ pub mod ffi { #[diplomat::rust_link(icu::timezone::ZonedDateTimeParser, Struct)] #[diplomat::opaque] - pub struct ZonedDateTimeParser(icu_timezone::ZonedDateTimeParser); + pub struct ZonedDateTimeParser( + icu_timezone::TimeZoneIdMapper, + icu_timezone::ZoneOffsetCalculator, + ); impl ZonedDateTimeParser { /// Construct a new [`ZonedDateTimeParser`] instance using compiled data. @@ -29,7 +32,10 @@ pub mod ffi { #[diplomat::attr(auto, constructor)] #[cfg(feature = "compiled_data")] pub fn create() -> Box { - Box::new(ZonedDateTimeParser(icu_timezone::ZonedDateTimeParser::new())) + Box::new(ZonedDateTimeParser( + icu_timezone::TimeZoneIdMapper::new().static_to_owned(), + icu_timezone::ZoneOffsetCalculator::new(), + )) } /// Construct a new [`ZonedDateTimeParser`] instance using a particular data source. #[diplomat::rust_link(icu::timezone::ZonedDateTimeParser::new, FnInStruct)] @@ -39,7 +45,8 @@ pub mod ffi { provider: &DataProvider, ) -> Result, DataError> { Ok(Box::new(ZonedDateTimeParser( - icu_timezone::ZonedDateTimeParser::try_new_with_buffer_provider(provider.get()?)?, + icu_timezone::TimeZoneIdMapper::try_new_with_buffer_provider(provider.get()?)?, + icu_timezone::ZoneOffsetCalculator::try_new_with_buffer_provider(provider.get()?)?, ))) } } @@ -55,19 +62,14 @@ pub mod ffi { impl ZonedDateTimeParser { /// Creates a new [`ZonedIsoDateTime`] from an IXDTF string. - #[diplomat::rust_link(icu::timezone::ZonedDateTimeParser::try_from_str, FnInStruct)] - #[diplomat::rust_link( - icu::timezone::ZonedDateTimeParser::try_from_utf8, - FnInStruct, - hidden - )] - #[diplomat::rust_link(icu::timezone::ZonedDateTimeParser::from_str, FnInStruct, hidden)] + #[diplomat::rust_link(icu::timezone::ZonedDateTime::try_from_str, FnInStruct)] + #[diplomat::rust_link(icu::timezone::ZonedDateTime::try_from_utf8, FnInStruct, hidden)] pub fn try_iso_from_str( &self, v: &DiplomatStr, ) -> Result { let icu_timezone::ZonedDateTime { date, time, zone } = - self.0.parse_from_utf8(v, Iso)?; + icu_timezone::ZonedDateTime::try_from_utf8(v, Iso, self.0.as_borrowed(), &self.1)?; Ok(ZonedIsoDateTime { date: Box::new(IsoDate(date)), time: Box::new(Time(time)), @@ -96,7 +98,12 @@ pub mod ffi { calendar: &Calendar, ) -> Result { let icu_timezone::ZonedDateTime { date, time, zone } = - self.0.parse_from_utf8(v, calendar.0.clone())?; + icu_timezone::ZonedDateTime::try_from_utf8( + v, + calendar.0.clone(), + self.0.as_borrowed(), + &self.1, + )?; Ok(ZonedDateTime { date: Box::new(Date(date)), time: Box::new(Time(time)), diff --git a/ffi/capi/tests/missing_apis.txt b/ffi/capi/tests/missing_apis.txt index 60fb074f859..4966a100890 100644 --- a/ffi/capi/tests/missing_apis.txt +++ b/ffi/capi/tests/missing_apis.txt @@ -222,14 +222,12 @@ icu::timezone::ZoneOffsetCalculator#Struct icu::timezone::ZoneOffsetCalculator::compute_offsets_from_time_zone#FnInStruct icu::timezone::ZoneOffsetCalculator::new#FnInStruct icu::timezone::ZoneOffsets#Struct -icu::timezone::ZonedDateTimeParser::parse#FnInStruct -icu::timezone::ZonedDateTimeParser::parse_from_utf8#FnInStruct -icu::timezone::ZonedDateTimeParser::parse_location_only#FnInStruct -icu::timezone::ZonedDateTimeParser::parse_location_only_from_utf8#FnInStruct -icu::timezone::ZonedDateTimeParser::parse_loose#FnInStruct -icu::timezone::ZonedDateTimeParser::parse_loose_from_utf8#FnInStruct -icu::timezone::ZonedDateTimeParser::parse_offset_only#FnInStruct -icu::timezone::ZonedDateTimeParser::parse_offset_only_from_utf8#FnInStruct +icu::timezone::ZonedDateTime::try_location_only_from_str#FnInStruct +icu::timezone::ZonedDateTime::try_location_only_from_utf8#FnInStruct +icu::timezone::ZonedDateTime::try_loose_from_str#FnInStruct +icu::timezone::ZonedDateTime::try_loose_from_utf8#FnInStruct +icu::timezone::ZonedDateTime::try_offset_only_from_str#FnInStruct +icu::timezone::ZonedDateTime::try_offset_only_from_utf8#FnInStruct icu::timezone::models::AtTime#Enum icu::timezone::models::Base#Enum icu::timezone::models::Full#Enum