diff --git a/Cargo.toml b/Cargo.toml index 7ad0c51245d..782a7625064 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,7 +125,7 @@ cfg-if = "1.0.4" either = "1.15.0" sys-locale = "0.3.2" timezone_provider = { version = "0.1.0" } -temporal_rs = { version = "0.1.0", default-features = false } +temporal_rs = { version = "0.1.1", default-features = false } web-time = "1.1.0" criterion = "0.7.0" float-cmp = "0.10.0" diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 5dc8f76c9ac..fad6b6515ba 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -616,7 +616,10 @@ impl Duration { // 4. Let relativeToRecord be ? GetTemporalRelativeToOption(resolvedOptions). let relative_to = get_relative_to_option(&options, context)?; - Ok((one.compare_with_provider(&two, relative_to, context.tz_provider())? as i8).into()) + Ok( + (one.compare_with_provider(&two, relative_to, context.timezone_provider())? as i8) + .into(), + ) } } @@ -962,10 +965,11 @@ impl Duration { // NOTE: execute step 21 earlier before initial values are shadowed. // 21. If smallestUnitPresent is false and largestUnitPresent is false, then - let rounded_duration = - duration - .inner - .round_with_provider(options, relative_to, context.tz_provider())?; + let rounded_duration = duration.inner.round_with_provider( + options, + relative_to, + context.timezone_provider(), + )?; create_temporal_duration(rounded_duration, None, context).map(Into::into) } @@ -1042,7 +1046,7 @@ impl Duration { Ok(duration .inner - .total_with_provider(unit, relative_to, context.tz_provider())? + .total_with_provider(unit, relative_to, context.timezone_provider())? .as_inner() .into()) } diff --git a/core/engine/src/builtins/temporal/instant/mod.rs b/core/engine/src/builtins/temporal/instant/mod.rs index 7fc49579733..736fe88cc88 100644 --- a/core/engine/src/builtins/temporal/instant/mod.rs +++ b/core/engine/src/builtins/temporal/instant/mod.rs @@ -649,7 +649,7 @@ impl Instant { let ixdtf = instant.inner.to_ixdtf_string_with_provider( timezone, options, - context.tz_provider(), + context.timezone_provider(), )?; Ok(JsString::from(ixdtf).into()) @@ -678,7 +678,7 @@ impl Instant { let ixdtf = instant.inner.to_ixdtf_string_with_provider( None, ToStringRoundingOptions::default(), - context.tz_provider(), + context.timezone_provider(), )?; Ok(JsString::from(ixdtf).into()) } @@ -705,7 +705,7 @@ impl Instant { let ixdtf = instant.inner.to_ixdtf_string_with_provider( None, ToStringRoundingOptions::default(), - context.tz_provider(), + context.timezone_provider(), )?; Ok(JsString::from(ixdtf).into()) } @@ -758,7 +758,7 @@ impl Instant { // 4. Return ! CreateTemporalZonedDateTime(instant.[[EpochNanoseconds]], timeZone, "iso8601"). let zdt = instant .inner - .to_zoned_date_time_iso_with_provider(timezone, context.tz_provider())?; + .to_zoned_date_time_iso_with_provider(timezone, context.timezone_provider())?; create_temporal_zoneddatetime(zdt, None, context).map(Into::into) } } diff --git a/core/engine/src/builtins/temporal/mod.rs b/core/engine/src/builtins/temporal/mod.rs index 698f62cda65..7ae6451d484 100644 --- a/core/engine/src/builtins/temporal/mod.rs +++ b/core/engine/src/builtins/temporal/mod.rs @@ -224,7 +224,7 @@ pub(crate) fn get_relative_to_option( None, None, None, - context.tz_provider(), + context.timezone_provider(), )?; return Ok(Some(RelativeTo::ZonedDateTime(zdt))); } @@ -238,7 +238,7 @@ pub(crate) fn get_relative_to_option( // Steps 7-12 are handled by temporal_rs Ok(Some(RelativeTo::try_from_str_with_provider( &relative_to_str.to_std_string_escaped(), - context.tz_provider(), + context.timezone_provider(), )?)) } diff --git a/core/engine/src/builtins/temporal/now.rs b/core/engine/src/builtins/temporal/now.rs index b9ac9015171..5fd881e8981 100644 --- a/core/engine/src/builtins/temporal/now.rs +++ b/core/engine/src/builtins/temporal/now.rs @@ -81,8 +81,8 @@ impl Now { // TODO: this should be optimized once system time zone is in context // 1. Return ! SystemTimeZone(). let context: &Context = context; - let time_zone = context.get_system_time_zone(context.tz_provider())?; - Ok(JsString::from(time_zone.identifier_with_provider(context.tz_provider())?).into()) + let time_zone = context.get_system_time_zone(context.timezone_provider())?; + Ok(JsString::from(time_zone.identifier_with_provider(context.timezone_provider())?).into()) } /// 2.2.2 `Temporal.Now.instant()` @@ -125,7 +125,8 @@ impl Now { let now: InnerNow<&Context> = InnerNow::new(context); - let datetime = now.plain_date_time_iso_with_provider(time_zone, context.tz_provider())?; + let datetime = + now.plain_date_time_iso_with_provider(time_zone, context.timezone_provider())?; create_temporal_datetime(datetime, None, context).map(Into::into) } @@ -152,7 +153,7 @@ impl Now { .transpose()?; let now: InnerNow<&Context> = InnerNow::new(context); - let zdt = now.zoned_date_time_iso_with_provider(time_zone, context.tz_provider())?; + let zdt = now.zoned_date_time_iso_with_provider(time_zone, context.timezone_provider())?; create_temporal_zoneddatetime(zdt, None, context).map(Into::into) } @@ -176,7 +177,7 @@ impl Now { let now: InnerNow<&Context> = InnerNow::new(context); - let pd = now.plain_date_iso_with_provider(time_zone, context.tz_provider())?; + let pd = now.plain_date_iso_with_provider(time_zone, context.timezone_provider())?; create_temporal_date(pd, None, context).map(Into::into) } @@ -200,7 +201,7 @@ impl Now { let now: InnerNow<&Context> = InnerNow::new(context); - let pt = now.plain_time_with_provider(time_zone, context.tz_provider())?; + let pt = now.plain_time_with_provider(time_zone, context.timezone_provider())?; create_temporal_time(pt, None, context).map(Into::into) } } diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index 46006d0466c..e3a220ca78e 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -1174,9 +1174,11 @@ impl PlainDate { (to_temporal_timezone_identifier(item, context)?, None) }; - let result = - date.inner - .to_zoned_date_time_with_provider(timezone, time, context.tz_provider())?; + let result = date.inner.to_zoned_date_time_with_provider( + timezone, + time, + context.timezone_provider(), + )?; // 7. Return ! CreateTemporalZonedDateTime(epochNs, timeZone, temporalDate.[[Calendar]]). create_temporal_zoneddatetime(result, None, context).map(Into::into) diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index 5d949749652..9a7bc594742 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -1553,7 +1553,7 @@ impl PlainDateTime { let result = dt.inner.to_zoned_date_time_with_provider( timezone, disambiguation, - context.tz_provider(), + context.timezone_provider(), )?; create_temporal_zoneddatetime(result, None, context).map(Into::into) } diff --git a/core/engine/src/builtins/temporal/zoneddatetime/mod.rs b/core/engine/src/builtins/temporal/zoneddatetime/mod.rs index 6f0eaf524f0..f5c707408c7 100644 --- a/core/engine/src/builtins/temporal/zoneddatetime/mod.rs +++ b/core/engine/src/builtins/temporal/zoneddatetime/mod.rs @@ -431,7 +431,7 @@ impl BuiltInConstructor for ZonedDateTime { // a. Set timeZone to FormatOffsetTimeZoneIdentifier(timeZoneParse.[[OffsetMinutes]]). let timezone = TimeZone::try_from_identifier_str_with_provider( &timezone_str.to_std_string_escaped(), - context.tz_provider(), + context.timezone_provider(), )?; // 8. If calendar is undefined, set calendar to "iso8601". @@ -454,7 +454,7 @@ impl BuiltInConstructor for ZonedDateTime { epoch_nanos.to_i128(), timezone, calendar, - context.tz_provider(), + context.timezone_provider(), )?; // 11. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar, NewTarget). @@ -511,7 +511,7 @@ impl ZonedDateTime { Ok(JsString::from( zdt.inner .time_zone() - .identifier_with_provider(context.tz_provider())?, + .identifier_with_provider(context.timezone_provider())?, ) .into()) } @@ -955,7 +955,7 @@ impl ZonedDateTime { Ok(zdt .inner - .hours_in_day_with_provider(context.tz_provider())? + .hours_in_day_with_provider(context.timezone_provider())? .into()) } @@ -1228,7 +1228,7 @@ impl ZonedDateTime { disambiguation, offset, overflow, - context.tz_provider(), + context.timezone_provider(), )?; create_temporal_zoneddatetime(result, None, context).map(Into::into) } @@ -1264,7 +1264,7 @@ impl ZonedDateTime { let inner = zdt .inner - .with_plain_time_and_provider(time, context.tz_provider())?; + .with_plain_time_and_provider(time, context.timezone_provider())?; create_temporal_zoneddatetime(inner, None, context).map(Into::into) } @@ -1292,7 +1292,7 @@ impl ZonedDateTime { let inner = zdt .inner - .with_time_zone_with_provider(timezone, context.tz_provider())?; + .with_time_zone_with_provider(timezone, context.timezone_provider())?; create_temporal_zoneddatetime(inner, None, context).map(Into::into) } @@ -1347,9 +1347,9 @@ impl ZonedDateTime { let options = get_options_object(args.get_or_undefined(1))?; let overflow = get_option::(&options, js_string!("overflow"), context)?; - let result = zdt - .inner - .add_with_provider(&duration, overflow, context.tz_provider())?; + let result = + zdt.inner + .add_with_provider(&duration, overflow, context.timezone_provider())?; create_temporal_zoneddatetime(result, None, context).map(Into::into) } @@ -1380,7 +1380,7 @@ impl ZonedDateTime { let result = zdt.inner - .subtract_with_provider(&duration, overflow, context.tz_provider())?; + .subtract_with_provider(&duration, overflow, context.timezone_provider())?; create_temporal_zoneddatetime(result, None, context).map(Into::into) } @@ -1409,9 +1409,9 @@ impl ZonedDateTime { let options = get_options_object(args.get_or_undefined(1))?; let settings = get_difference_settings(&options, context)?; - let result = zdt - .inner - .until_with_provider(&other, settings, context.tz_provider())?; + let result = + zdt.inner + .until_with_provider(&other, settings, context.timezone_provider())?; create_temporal_duration(result, None, context).map(Into::into) } @@ -1440,9 +1440,9 @@ impl ZonedDateTime { let options = get_options_object(args.get_or_undefined(1))?; let settings = get_difference_settings(&options, context)?; - let result = zdt - .inner - .since_with_provider(&other, settings, context.tz_provider())?; + let result = + zdt.inner + .since_with_provider(&other, settings, context.timezone_provider())?; create_temporal_duration(result, None, context).map(Into::into) } @@ -1521,7 +1521,7 @@ impl ZonedDateTime { let result = zdt .inner - .round_with_provider(options, context.tz_provider())?; + .round_with_provider(options, context.timezone_provider())?; create_temporal_zoneddatetime(result, None, context).map(Into::into) } @@ -1548,7 +1548,7 @@ impl ZonedDateTime { let other = to_temporal_zoneddatetime(args.get_or_undefined(0), None, context)?; Ok(zdt .inner - .equals_with_provider(&other, context.tz_provider())? + .equals_with_provider(&other, context.timezone_provider())? .into()) } @@ -1598,7 +1598,7 @@ impl ZonedDateTime { display_timezone, show_calendar, options, - context.tz_provider(), + context.timezone_provider(), )?; Ok(JsString::from(ixdtf).into()) @@ -1628,7 +1628,7 @@ impl ZonedDateTime { DisplayTimeZone::Auto, DisplayCalendar::Auto, ToStringRoundingOptions::default(), - context.tz_provider(), + context.timezone_provider(), )?; Ok(JsString::from(ixdtf).into()) @@ -1657,7 +1657,7 @@ impl ZonedDateTime { DisplayTimeZone::Auto, DisplayCalendar::Auto, ToStringRoundingOptions::default(), - context.tz_provider(), + context.timezone_provider(), )?; Ok(JsString::from(ixdtf).into()) @@ -1700,7 +1700,7 @@ impl ZonedDateTime { let new = zdt .inner - .start_of_day_with_provider(context.tz_provider())?; + .start_of_day_with_provider(context.timezone_provider())?; create_temporal_zoneddatetime(new, None, context).map(Into::into) } @@ -1767,7 +1767,7 @@ impl ZonedDateTime { // Step 8-12 let result = zdt .inner - .get_time_zone_transition_with_provider(direction, context.tz_provider())?; + .get_time_zone_transition_with_provider(direction, context.timezone_provider())?; match result { Some(zdt) => create_temporal_zoneddatetime(zdt, None, context).map(Into::into), @@ -1964,7 +1964,7 @@ pub(crate) fn to_temporal_zoneddatetime( overflow, disambiguation, offset_option, - context.tz_provider(), + context.timezone_provider(), )?) } JsVariant::String(zdt_source) => { @@ -2003,7 +2003,7 @@ pub(crate) fn to_temporal_zoneddatetime( zdt_source.to_std_string_escaped().as_bytes(), disambiguation, offset_option, - context.tz_provider(), + context.timezone_provider(), )?) } // 5. Else, @@ -2044,7 +2044,7 @@ pub(crate) fn to_temporal_timezone_identifier( // 9. Return timeZoneIdentifierRecord.[[Identifier]]. let timezone = TimeZone::try_from_str_with_provider( &tz_string.to_std_string_escaped(), - context.tz_provider(), + context.timezone_provider(), )?; Ok(timezone) diff --git a/core/engine/src/context/mod.rs b/core/engine/src/context/mod.rs index db8de29600a..19294ac3b49 100644 --- a/core/engine/src/context/mod.rs +++ b/core/engine/src/context/mod.rs @@ -108,7 +108,7 @@ pub struct Context { can_block: bool, #[cfg(feature = "temporal")] - tz_provider: CompiledTzdbProvider, + timezone_provider: Box, /// Intl data provider. #[cfg(feature = "intl")] @@ -149,6 +149,10 @@ impl std::fmt::Debug for Context { #[cfg(feature = "intl")] debug.field("intl_provider", &self.intl_provider); + // TODO: Support TimeZoneProvider debug names + #[cfg(feature = "temporal")] + debug.field("timezone_provider", &"TimeZoneProvider"); + debug.finish_non_exhaustive() } } @@ -893,8 +897,8 @@ impl Context { /// Get the Time Zone Provider #[cfg(feature = "temporal")] - pub(crate) fn tz_provider(&self) -> &impl TimeZoneProvider { - &self.tz_provider + pub(crate) fn timezone_provider(&self) -> &dyn TimeZoneProvider { + self.timezone_provider.as_ref() } } @@ -912,6 +916,8 @@ pub struct ContextBuilder { can_block: bool, #[cfg(feature = "intl")] icu: Option, + #[cfg(feature = "temporal")] + timezone_provider: Option>, #[cfg(feature = "fuzz")] instructions_remaining: usize, } @@ -945,6 +951,12 @@ impl std::fmt::Debug for ContextBuilder { #[cfg(feature = "intl")] out.field("icu", &self.icu); + #[cfg(feature = "temporal")] + out.field( + "timezone_provider", + &self.timezone_provider.as_ref().map(|_| "TimeZoneProvider"), + ); + #[cfg(feature = "fuzz")] out.field("instructions_remaining", &self.instructions_remaining); @@ -1005,6 +1017,21 @@ impl ContextBuilder { Ok(self) } + /// Set the [`timezone_provider::provider::TimeZoneProvider`] that should be used to source + /// time zone data. + /// + /// ## Default + /// + /// If no time zone provider is provided, a compiled time zone provider will be used + /// which includes the time zone data in the binary. This may increase binary sizes + /// by up to 200 Kb. + #[cfg(feature = "temporal")] + #[must_use] + pub fn timezone_provider(mut self, provider: T) -> Self { + self.timezone_provider = Some(Box::new(provider)); + self + } + /// Initializes the [`HostHooks`] for the context. /// /// [`Host Hooks`]: https://tc39.es/ecma262/#sec-host-hooks-summary @@ -1104,7 +1131,11 @@ impl ContextBuilder { vm, strict: false, #[cfg(feature = "temporal")] - tz_provider: CompiledTzdbProvider::default(), + timezone_provider: if let Some(provider) = self.timezone_provider { + provider + } else { + Box::new(CompiledTzdbProvider::default()) + }, #[cfg(feature = "intl")] intl_provider: if let Some(icu) = self.icu { icu