From 87f86ebc9876ea7963c34d5db2440976ccf629ab Mon Sep 17 00:00:00 2001 From: nekevss Date: Sat, 11 Nov 2023 22:33:40 -0500 Subject: [PATCH] Add dyn Any context and some small changes --- boa_engine/Cargo.toml | 2 +- .../src/builtins/temporal/calendar/mod.rs | 54 ++-- .../src/builtins/temporal/calendar/object.rs | 112 +++++-- boa_temporal/Cargo.toml | 4 + boa_temporal/README.md | 8 +- boa_temporal/src/calendar.rs | 221 +++++++++---- boa_temporal/src/calendar/iso.rs | 42 ++- boa_temporal/src/date.rs | 165 ++++++---- boa_temporal/src/duration.rs | 295 ++++++++++-------- 9 files changed, 586 insertions(+), 317 deletions(-) diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index cad29f6eff0..ba571ff4002 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -62,7 +62,7 @@ boa_profiler.workspace = true boa_macros.workspace = true boa_ast.workspace = true boa_parser.workspace = true -boa_temporal.workspace = true +boa_temporal = { workspace = true, features = ["context"] } serde = { workspace = true, features = ["derive", "rc"] } serde_json.workspace = true rand = "0.8.5" diff --git a/boa_engine/src/builtins/temporal/calendar/mod.rs b/boa_engine/src/builtins/temporal/calendar/mod.rs index 2ba39e612dd..a60c22d6ad7 100644 --- a/boa_engine/src/builtins/temporal/calendar/mod.rs +++ b/boa_engine/src/builtins/temporal/calendar/mod.rs @@ -159,7 +159,7 @@ impl Calendar { create_temporal_calendar(slot, None, context) } - fn get_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + fn get_id(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { let o = this.as_object().ok_or_else(|| { JsNativeError::typ().with_message("this value of Calendar must be an object.") })?; @@ -174,7 +174,7 @@ impl Calendar { CalendarSlot::Protocol(proto) => proto.clone(), }; - Ok(JsString::from(protocol.identifier()?.as_str()).into()) + Ok(JsString::from(protocol.identifier(context)?.as_str()).into()) } /// 15.8.2.1 `Temporal.Calendar.prototype.dateFromFields ( fields [ , options ] )` - Supercedes 12.5.4 @@ -217,7 +217,7 @@ impl Calendar { ]); // 6. If calendar.[[Identifier]] is "iso8601", then - let mut fields = if protocol.identifier()?.as_str() == "iso8601" { + let mut fields = if protocol.identifier(context)?.as_str() == "iso8601" { // a. Set fields to ? PrepareTemporalFields(fields, relevantFieldNames, « "year", "day" »). let mut required_fields = Vec::from([js_string!("year"), js_string!("day")]); fields::prepare_temporal_fields( @@ -257,7 +257,7 @@ impl Calendar { // a. Perform ? CalendarResolveFields(calendar.[[Identifier]], fields, date). // b. Let result be ? CalendarDateToISO(calendar.[[Identifier]], fields, overflow). - let result = protocol.date_from_fields(&mut fields, overflow)?; + let result = protocol.date_from_fields(&mut fields, overflow, context)?; create_temporal_date(result, None, context).map(Into::into) } @@ -296,7 +296,7 @@ impl Calendar { ]); // 6. Set fields to ? PrepareTemporalFields(fields, « "month", "monthCode", "year" », « "year" »). - let mut fields = if protocol.identifier()?.as_str() == "iso8601" { + let mut fields = if protocol.identifier(context)?.as_str() == "iso8601" { // a. Set fields to ? PrepareTemporalFields(fields, relevantFieldNames, « "year" »). let mut required_fields = Vec::from([js_string!("year")]); fields::prepare_temporal_fields( @@ -333,7 +333,7 @@ impl Calendar { let overflow = get_option::(&options, utf16!("overflow"), context)? .unwrap_or(ArithmeticOverflow::Constrain); - let result = protocol.year_month_from_fields(&mut fields, overflow)?; + let result = protocol.year_month_from_fields(&mut fields, overflow, context)?; create_temporal_year_month(result, None, context) } @@ -377,7 +377,7 @@ impl Calendar { ]); // 6. If calendar.[[Identifier]] is "iso8601", then - let mut fields = if protocol.identifier()?.as_str() == "iso8601" { + let mut fields = if protocol.identifier(context)?.as_str() == "iso8601" { // a. Set fields to ? PrepareTemporalFields(fields, relevantFieldNames, « "day" »). let mut required_fields = Vec::from([js_string!("day")]); fields::prepare_temporal_fields( @@ -409,7 +409,7 @@ impl Calendar { let overflow = get_option(&options, utf16!("overflow"), context)? .unwrap_or(ArithmeticOverflow::Constrain); - let result = protocol.month_day_from_fields(&mut fields, overflow)?; + let result = protocol.month_day_from_fields(&mut fields, overflow, context)?; create_temporal_month_day(result, None, context) } @@ -451,7 +451,7 @@ impl Calendar { // 8. Let balanceResult be ? BalanceTimeDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "day"). duration.balance_time_duration(TemporalUnit::Day)?; - let result = protocol.date_add(&date.inner, &duration, overflow)?; + let result = protocol.date_add(&date.inner, &duration, overflow, context)?; create_temporal_date(result, None, context).map(Into::into) } @@ -493,7 +493,7 @@ impl Calendar { )? .unwrap_or(TemporalUnit::Day); - let result = protocol.date_until(&one.inner, &two.inner, largest_unit)?; + let result = protocol.date_until(&one.inner, &two.inner, largest_unit, context)?; create_temporal_duration(result, None, context).map(Into::into) } @@ -516,7 +516,7 @@ impl Calendar { let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; let result = protocol - .era(&date_like)? + .era(&date_like, context)? .map_or(JsValue::undefined(), |r| JsString::from(r.as_str()).into()); Ok(result) @@ -540,7 +540,7 @@ impl Calendar { let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; let result = protocol - .era_year(&date_like)? + .era_year(&date_like, context)? .map_or(JsValue::undefined(), JsValue::from); Ok(result) @@ -563,7 +563,7 @@ impl Calendar { let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - let result = protocol.year(&date_like)?; + let result = protocol.year(&date_like, context)?; Ok(result.into()) } @@ -590,7 +590,7 @@ impl Calendar { // 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then // 4.a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). - let result = protocol.month(&date_like)?; + let result = protocol.month(&date_like, context)?; Ok(result.into()) } @@ -612,7 +612,7 @@ impl Calendar { let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - let result = protocol.month_code(&date_like)?; + let result = protocol.month_code(&date_like, context)?; Ok(JsString::from(result.as_str()).into()) } @@ -634,7 +634,7 @@ impl Calendar { let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - let result = protocol.day(&date_like)?; + let result = protocol.day(&date_like, context)?; Ok(result.into()) } @@ -659,7 +659,7 @@ impl Calendar { // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - let result = protocol.day_of_week(&CalendarDateLike::Date(date.inner.clone()))?; + let result = protocol.day_of_week(&CalendarDateLike::Date(date.inner.clone()), context)?; Ok(result.into()) } @@ -683,7 +683,7 @@ impl Calendar { // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - let result = protocol.day_of_year(&CalendarDateLike::Date(date.inner.clone()))?; + let result = protocol.day_of_year(&CalendarDateLike::Date(date.inner.clone()), context)?; Ok(result.into()) } @@ -706,7 +706,7 @@ impl Calendar { // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - let result = protocol.week_of_year(&CalendarDateLike::Date(date.inner.clone()))?; + let result = protocol.week_of_year(&CalendarDateLike::Date(date.inner.clone()), context)?; Ok(result.into()) } @@ -729,7 +729,7 @@ impl Calendar { // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - let result = protocol.year_of_week(&CalendarDateLike::Date(date.inner.clone()))?; + let result = protocol.year_of_week(&CalendarDateLike::Date(date.inner.clone()), context)?; Ok(result.into()) } @@ -752,7 +752,7 @@ impl Calendar { // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - let result = protocol.days_in_week(&CalendarDateLike::Date(date.inner.clone()))?; + let result = protocol.days_in_week(&CalendarDateLike::Date(date.inner.clone()), context)?; Ok(result.into()) } @@ -774,7 +774,7 @@ impl Calendar { let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - let result = protocol.days_in_month(&date_like)?; + let result = protocol.days_in_month(&date_like, context)?; Ok(result.into()) } @@ -795,7 +795,7 @@ impl Calendar { }; let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - let result = protocol.days_in_year(&date_like)?; + let result = protocol.days_in_year(&date_like, context)?; Ok(result.into()) } @@ -821,7 +821,7 @@ impl Calendar { let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - let result = protocol.months_in_year(&date_like)?; + let result = protocol.months_in_year(&date_like, context)?; Ok(result.into()) } @@ -843,7 +843,7 @@ impl Calendar { let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - let result = protocol.in_leap_year(&date_like)?; + let result = protocol.in_leap_year(&date_like, context)?; Ok(result.into()) } @@ -916,7 +916,7 @@ impl Calendar { // 7. Let result be fieldNames. // 8. If calendar.[[Identifier]] is not "iso8601", then - if protocol.identifier()?.as_str() != "iso8601" { + if protocol.identifier(context)?.as_str() != "iso8601" { // a. NOTE: Every built-in calendar preserves all input field names in output. // b. Let extraFieldDescriptors be CalendarFieldDescriptors(calendar.[[Identifier]], fieldNames). let extended_fields = @@ -1160,7 +1160,7 @@ pub(crate) fn to_temporal_calendar_slot_value( } // Types: Box <- UserCalendar - let protocol = Box::new(CustomRuntimeCalendar::new(calendar_like, context)); + let protocol = Box::new(CustomRuntimeCalendar::new(calendar_like)); // c. Return temporalCalendarLike. return Ok(CalendarSlot::Protocol(protocol)); } diff --git a/boa_engine/src/builtins/temporal/calendar/object.rs b/boa_engine/src/builtins/temporal/calendar/object.rs index 90368afa06d..0d3a7a09523 100644 --- a/boa_engine/src/builtins/temporal/calendar/object.rs +++ b/boa_engine/src/builtins/temporal/calendar/object.rs @@ -1,11 +1,12 @@ //! Boa's implementation of a user-defined Anonymous Calendar. use crate::{ - builtins::temporal::{plain_date, plain_date_time, plain_month_day, plain_year_month}, + builtins::temporal::{plain_date, plain_month_day, plain_year_month}, object::ObjectKind, property::PropertyKey, Context, JsObject, JsValue, }; +use std::any::Any; use boa_macros::utf16; use boa_temporal::{ @@ -28,14 +29,12 @@ use boa_temporal::{ #[derive(Debug, Clone)] pub(crate) struct CustomRuntimeCalendar { calendar: JsObject, - ctx: *mut Context, } impl CustomRuntimeCalendar { - pub(crate) fn new(calendar: &JsObject, context: &mut Context) -> Self { + pub(crate) fn new(calendar: &JsObject) -> Self { Self { calendar: calendar.clone(), - ctx: context, } } } @@ -45,10 +44,13 @@ impl CalendarProtocol for CustomRuntimeCalendar { &self, fields: &mut TemporalFields, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult { // Safety: Context lives for the life of the program and execution, so // this should, in theory, be valid. - let context = unsafe { &mut *self.ctx }; + let context = context + .downcast_mut::() + .expect("Context was not provided for a CustomCalendar."); let method = self .calendar @@ -80,6 +82,7 @@ impl CalendarProtocol for CustomRuntimeCalendar { &self, fields: &mut TemporalFields, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult { todo!() } @@ -88,6 +91,7 @@ impl CalendarProtocol for CustomRuntimeCalendar { &self, fields: &mut TemporalFields, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult { todo!() } @@ -97,6 +101,7 @@ impl CalendarProtocol for CustomRuntimeCalendar { date: &TemporalDate, duration: &Duration, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult { todo!() } @@ -106,55 +111,90 @@ impl CalendarProtocol for CustomRuntimeCalendar { one: &TemporalDate, two: &TemporalDate, largest_unit: boa_temporal::options::TemporalUnit, + context: &mut dyn Any, ) -> TemporalResult { todo!() } // TODO: Determine validity of below errors. - fn era(&self, date_like: &CalendarDateLike) -> TemporalResult> { + fn era( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult> { Err(TemporalError::range().with_message("Objects do not implement era")) } - fn era_year(&self, date_like: &CalendarDateLike) -> TemporalResult> { + fn era_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult> { Err(TemporalError::range().with_message("Objects do not implement eraYear.")) } - fn year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn year(&self, date_like: &CalendarDateLike, context: &mut dyn Any) -> TemporalResult { todo!() } - fn month(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn month(&self, date_like: &CalendarDateLike, context: &mut dyn Any) -> TemporalResult { todo!() } - fn month_code(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn month_code( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { todo!() } - fn day(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn day(&self, date_like: &CalendarDateLike, context: &mut dyn Any) -> TemporalResult { todo!() } - fn day_of_week(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn day_of_week( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { todo!() } - fn day_of_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn day_of_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { todo!() } - fn week_of_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn week_of_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { todo!() } - fn year_of_week(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn year_of_week( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { todo!() } - fn days_in_week(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn days_in_week( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { // Safety: Context lives for the lifetime of the program's execution, so // this should, in theory, be safe memory to access. - let context = unsafe { &mut *self.ctx }; + let context = context + .downcast_mut::() + .expect("Context was not provided for a CustomCalendar."); let date_like = date_like_to_object(date_like, context)?; @@ -176,10 +216,16 @@ impl CalendarProtocol for CustomRuntimeCalendar { Ok(integral) } - fn days_in_month(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn days_in_month( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { // Safety: Context lives for the lifetime of the program's execution, so // this should, in theory, be safe memory to access. - let context = unsafe { &mut *self.ctx }; + let context = context + .downcast_mut::() + .expect("Context was not provided for a CustomCalendar."); let date_like = date_like_to_object(date_like, context)?; @@ -200,10 +246,16 @@ impl CalendarProtocol for CustomRuntimeCalendar { Ok(integral) } - fn days_in_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn days_in_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { // Safety: Context lives for the lifetime of the program's execution, so // this should, in theory, be safe memory to access. - let context = unsafe { &mut *self.ctx }; + let context = context + .downcast_mut::() + .expect("Context was not provided for a CustomCalendar."); let date_like = date_like_to_object(date_like, context)?; @@ -225,11 +277,19 @@ impl CalendarProtocol for CustomRuntimeCalendar { Ok(integral) } - fn months_in_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn months_in_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { todo!() } - fn in_leap_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn in_leap_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { todo!() } @@ -254,10 +314,12 @@ impl CalendarProtocol for CustomRuntimeCalendar { todo!() } - fn identifier(&self) -> TemporalResult { + fn identifier(&self, context: &mut dyn Any) -> TemporalResult { // Safety: Context lives for the lifetime of the program's execution, so // this should, in theory, be safe memory to access. - let context = unsafe { &mut *self.ctx }; + let context = context + .downcast_mut::() + .expect("Context was not provided for a CustomCalendar."); let identifier = self .calendar diff --git a/boa_temporal/Cargo.toml b/boa_temporal/Cargo.toml index b93d0f3bf45..c9894970afd 100644 --- a/boa_temporal/Cargo.toml +++ b/boa_temporal/Cargo.toml @@ -19,3 +19,7 @@ bitflags.workspace = true rustc-hash = { workspace = true, features = ["std"] } num-bigint = { workspace = true, features = ["serde"] } num-traits.workspace = true + + +[features] +context = [] diff --git a/boa_temporal/README.md b/boa_temporal/README.md index 4fc79874203..2211542e0f1 100644 --- a/boa_temporal/README.md +++ b/boa_temporal/README.md @@ -1,5 +1,11 @@ +# Temporal in Rust -# Temporal Crate +Provides a standard API for working with dates and time. + +IMPORTANT NOTE: The Temporal Proposal is still in Stage 3. As such, this crate should be viewed +as highly experimental until the proposal has been completely standardized and released. + +## Goal The intended goal of this crate is to provide an engine agnostic implementation of `ECMAScript`'s Temporal algorithms. diff --git a/boa_temporal/src/calendar.rs b/boa_temporal/src/calendar.rs index b2892bce818..8bac53ada46 100644 --- a/boa_temporal/src/calendar.rs +++ b/boa_temporal/src/calendar.rs @@ -7,7 +7,7 @@ //! it is up to implementers to implement an engine specific impl for handling JavaScript //! objects that may return true on `ImplementsCalendarProtocol`. -use std::str::FromStr; +use std::{any::Any, str::FromStr}; use crate::{ date::TemporalDate, @@ -162,18 +162,21 @@ pub trait CalendarProtocol: CalendarProtocolClone { &self, fields: &mut TemporalFields, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult; /// Creates a `Temporal.PlainYearMonth` object from the provided fields. fn year_month_from_fields( &self, fields: &mut TemporalFields, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult; /// Creates a `Temporal.PlainMonthDay` object from the provided fields. fn month_day_from_fields( &self, fields: &mut TemporalFields, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult; /// Returns a `Temporal.PlainDate` based off an added date. fn date_add( @@ -181,6 +184,7 @@ pub trait CalendarProtocol: CalendarProtocolClone { date: &TemporalDate, duration: &Duration, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult; /// Returns a `Temporal.Duration` representing the duration between two dates. fn date_until( @@ -188,38 +192,87 @@ pub trait CalendarProtocol: CalendarProtocolClone { one: &TemporalDate, two: &TemporalDate, largest_unit: TemporalUnit, + context: &mut dyn Any, ) -> TemporalResult; /// Returns the era for a given `temporaldatelike`. - fn era(&self, date_like: &CalendarDateLike) -> TemporalResult>; + fn era( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult>; /// Returns the era year for a given `temporaldatelike` - fn era_year(&self, date_like: &CalendarDateLike) -> TemporalResult>; + fn era_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult>; /// Returns the `year` for a given `temporaldatelike` - fn year(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn year(&self, date_like: &CalendarDateLike, context: &mut dyn Any) -> TemporalResult; /// Returns the `month` for a given `temporaldatelike` - fn month(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn month(&self, date_like: &CalendarDateLike, context: &mut dyn Any) -> TemporalResult; // Note: Best practice would probably be to switch to a MonthCode enum after extraction. /// Returns the `monthCode` for a given `temporaldatelike` - fn month_code(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn month_code( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Returns the `day` for a given `temporaldatelike` - fn day(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn day(&self, date_like: &CalendarDateLike, context: &mut dyn Any) -> TemporalResult; /// Returns a value representing the day of the week for a date. - fn day_of_week(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn day_of_week( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Returns a value representing the day of the year for a given calendar. - fn day_of_year(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn day_of_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Returns a value representing the week of the year for a given calendar. - fn week_of_year(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn week_of_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Returns the year of a given week. - fn year_of_week(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn year_of_week( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Returns the days in a week for a given calendar. - fn days_in_week(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn days_in_week( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Returns the days in a month for a given calendar. - fn days_in_month(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn days_in_month( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Returns the days in a year for a given calendar. - fn days_in_year(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn days_in_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Returns the months in a year for a given calendar. - fn months_in_year(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn months_in_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Returns whether a value is within a leap year according to the designated calendar. - fn in_leap_year(&self, date_like: &CalendarDateLike) -> TemporalResult; + fn in_leap_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult; /// Resolve the `TemporalFields` for the implemented Calendar fn resolve_fields( &self, @@ -231,12 +284,16 @@ pub trait CalendarProtocol: CalendarProtocolClone { /// Return the fields to ignore for this Calendar based on provided keys. fn field_keys_to_ignore(&self, additional_keys: Vec) -> Vec; /// Debug name - fn identifier(&self) -> TemporalResult; + fn identifier(&self, context: &mut dyn Any) -> TemporalResult; } impl core::fmt::Debug for dyn CalendarProtocol { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.identifier().unwrap_or_default().as_str()) + write!( + f, + "{}", + self.identifier(&mut ()).unwrap_or_default().as_str() + ) } } @@ -279,13 +336,14 @@ impl CalendarSlot { date: &TemporalDate, duration: &Duration, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.date_add(date, duration, overflow) + protocol.date_add(date, duration, overflow, context) } - Self::Protocol(protocol) => protocol.date_add(date, duration, overflow), + Self::Protocol(protocol) => protocol.date_add(date, duration, overflow, context), } } @@ -297,182 +355,223 @@ impl CalendarSlot { one: &TemporalDate, two: &TemporalDate, largest_unit: TemporalUnit, + context: &mut dyn Any, ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.date_until(one, two, largest_unit) + protocol.date_until(one, two, largest_unit, context) } - Self::Protocol(protocol) => protocol.date_until(one, two, largest_unit), + Self::Protocol(protocol) => protocol.date_until(one, two, largest_unit, context), } } /// `CalendarYear` /// /// TODO: More docs. - pub fn year(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn year(&self, date_like: &CalendarDateLike, context: &mut dyn Any) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.year(date_like) + protocol.year(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.year(date_like), + Self::Protocol(protocol) => protocol.year(date_like, context), } } /// `CalendarMonth` /// /// TODO: More docs. - pub fn month(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn month(&self, date_like: &CalendarDateLike, context: &mut dyn Any) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.month(date_like) + protocol.month(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.month(date_like), + Self::Protocol(protocol) => protocol.month(date_like, context), } } /// `CalendarMonthCode` /// /// TODO: More docs. - pub fn month_code(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn month_code( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.month_code(date_like) + protocol.month_code(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.month_code(date_like), + Self::Protocol(protocol) => protocol.month_code(date_like, context), } } /// `CalendarDay` /// /// TODO: More docs. - pub fn day(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn day(&self, date_like: &CalendarDateLike, context: &mut dyn Any) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.day(date_like) + protocol.day(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.day(date_like), + Self::Protocol(protocol) => protocol.day(date_like, context), } } /// `CalendarDayOfWeek` /// /// TODO: More docs. - pub fn day_of_week(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn day_of_week( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.day_of_week(date_like) + protocol.day_of_week(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.day_of_week(date_like), + Self::Protocol(protocol) => protocol.day_of_week(date_like, context), } } /// `CalendarDayOfYear` /// /// TODO: More docs. - pub fn day_of_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn day_of_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.day_of_year(date_like) + protocol.day_of_year(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.day_of_year(date_like), + Self::Protocol(protocol) => protocol.day_of_year(date_like, context), } } /// `CalendarWeekOfYear` /// /// TODO: More docs. - pub fn week_of_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn week_of_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.week_of_year(date_like) + protocol.week_of_year(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.week_of_year(date_like), + Self::Protocol(protocol) => protocol.week_of_year(date_like, context), } } /// `CalendarYearOfWeek` /// /// TODO: More docs. - pub fn year_of_week(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn year_of_week( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.year_of_week(date_like) + protocol.year_of_week(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.year_of_week(date_like), + Self::Protocol(protocol) => protocol.year_of_week(date_like, context), } } /// `CalendarDaysInWeek` /// /// TODO: More docs. - pub fn days_in_week(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn days_in_week( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.days_in_week(date_like) + protocol.days_in_week(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.days_in_week(date_like), + Self::Protocol(protocol) => protocol.days_in_week(date_like, context), } } /// `CalendarDaysInMonth` /// /// TODO: More docs. - pub fn days_in_month(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn days_in_month( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.days_in_month(date_like) + protocol.days_in_month(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.days_in_month(date_like), + Self::Protocol(protocol) => protocol.days_in_month(date_like, context), } } /// `CalendarDaysInYear` /// /// TODO: More docs. - pub fn days_in_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn days_in_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.days_in_year(date_like) + protocol.days_in_year(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.days_in_year(date_like), + Self::Protocol(protocol) => protocol.days_in_year(date_like, context), } } /// `CalendarMonthsInYear` /// /// TODO: More docs. - pub fn months_in_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn months_in_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.months_in_year(date_like) + protocol.months_in_year(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.months_in_year(date_like), + Self::Protocol(protocol) => protocol.months_in_year(date_like, context), } } /// `CalendarInLeapYear` /// /// TODO: More docs. - pub fn in_leap_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + pub fn in_leap_year( + &self, + date_like: &CalendarDateLike, + context: &mut dyn Any, + ) -> TemporalResult { match self { Self::Identifier(id) => { let protocol = AvailableCalendars::from_str(&id)?.to_protocol(); - protocol.in_leap_year(date_like) + protocol.in_leap_year(date_like, &mut ()) } - Self::Protocol(protocol) => protocol.in_leap_year(date_like), + Self::Protocol(protocol) => protocol.in_leap_year(date_like, context), } } } diff --git a/boa_temporal/src/calendar/iso.rs b/boa_temporal/src/calendar/iso.rs index cb02415262f..30cfe11a4e6 100644 --- a/boa_temporal/src/calendar/iso.rs +++ b/boa_temporal/src/calendar/iso.rs @@ -11,6 +11,7 @@ use crate::{ year_month::TemporalYearMonth, TemporalResult, }; +use std::any::Any; use tinystr::{tinystr, TinyStr4, TinyStr8}; @@ -31,6 +32,7 @@ impl CalendarProtocol for IsoCalendar { &self, fields: &mut TemporalFields, overflow: ArithmeticOverflow, + _: &mut dyn Any, ) -> TemporalResult { // NOTE: we are in ISO by default here. // a. Perform ? ISOResolveMonth(fields). @@ -54,6 +56,7 @@ impl CalendarProtocol for IsoCalendar { &self, fields: &mut TemporalFields, overflow: ArithmeticOverflow, + _: &mut dyn Any, ) -> TemporalResult { // 9. If calendar.[[Identifier]] is "iso8601", then // a. Perform ? ISOResolveMonth(fields). @@ -77,6 +80,7 @@ impl CalendarProtocol for IsoCalendar { &self, fields: &mut TemporalFields, overflow: ArithmeticOverflow, + _: &mut dyn Any, ) -> TemporalResult { // 8. Perform ? ISOResolveMonth(fields). fields.iso_resolve_month()?; @@ -100,6 +104,7 @@ impl CalendarProtocol for IsoCalendar { _date: &TemporalDate, _duration: &Duration, _overflow: ArithmeticOverflow, + _: &mut dyn Any, ) -> TemporalResult { // TODO: Not stable on `ICU4X`. Implement once completed. Err(TemporalError::range() @@ -118,6 +123,7 @@ impl CalendarProtocol for IsoCalendar { _one: &TemporalDate, _two: &TemporalDate, _largest_unit: TemporalUnit, + _: &mut dyn Any, ) -> TemporalResult { // TODO: Not stable on `ICU4X`. Implement once completed. Err(TemporalError::range() @@ -129,52 +135,56 @@ impl CalendarProtocol for IsoCalendar { } /// `Temporal.Calendar.prototype.era( dateLike )` for iso8601 calendar. - fn era(&self, _: &CalendarDateLike) -> TemporalResult> { + fn era(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult> { // Returns undefined on iso8601. Ok(None) } /// `Temporal.Calendar.prototype.eraYear( dateLike )` for iso8601 calendar. - fn era_year(&self, _: &CalendarDateLike) -> TemporalResult> { + fn era_year(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult> { // Returns undefined on iso8601. Ok(None) } /// Returns the `year` for the `Iso` calendar. - fn year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn year(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { Ok(date_like.as_iso_date().year()) } /// Returns the `month` for the `Iso` calendar. - fn month(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn month(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { Ok(date_like.as_iso_date().month()) } /// Returns the `monthCode` for the `Iso` calendar. - fn month_code(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn month_code( + &self, + date_like: &CalendarDateLike, + _: &mut dyn Any, + ) -> TemporalResult { let date = date_like.as_iso_date().as_icu4x()?; Ok(date.month().code.0) } /// Returns the `day` for the `Iso` calendar. - fn day(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn day(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { Ok(date_like.as_iso_date().day()) } /// Returns the `dayOfWeek` for the `Iso` calendar. - fn day_of_week(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn day_of_week(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { let date = date_like.as_iso_date().as_icu4x()?; Ok(date.day_of_week() as i32) } /// Returns the `dayOfYear` for the `Iso` calendar. - fn day_of_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn day_of_year(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { let date = date_like.as_iso_date().as_icu4x()?; Ok(i32::from(date.day_of_year_info().day_of_year)) } /// Returns the `weekOfYear` for the `Iso` calendar. - fn week_of_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn week_of_year(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { let date = date_like.as_iso_date().as_icu4x()?; let week_calculator = WeekCalculator::default(); @@ -187,7 +197,7 @@ impl CalendarProtocol for IsoCalendar { } /// Returns the `yearOfWeek` for the `Iso` calendar. - fn year_of_week(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn year_of_week(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { let date = date_like.as_iso_date().as_icu4x()?; let week_calculator = WeekCalculator::default(); @@ -206,29 +216,29 @@ impl CalendarProtocol for IsoCalendar { } /// Returns the `daysInWeek` value for the `Iso` calendar. - fn days_in_week(&self, _: &CalendarDateLike) -> TemporalResult { + fn days_in_week(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { Ok(7) } /// Returns the `daysInMonth` value for the `Iso` calendar. - fn days_in_month(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn days_in_month(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { let date = date_like.as_iso_date().as_icu4x()?; Ok(i32::from(date.days_in_month())) } /// Returns the `daysInYear` value for the `Iso` calendar. - fn days_in_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn days_in_year(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { let date = date_like.as_iso_date().as_icu4x()?; Ok(i32::from(date.days_in_year())) } /// Return the amount of months in an ISO Calendar. - fn months_in_year(&self, _: &CalendarDateLike) -> TemporalResult { + fn months_in_year(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { Ok(12) } /// Returns whether provided date is in a leap year according to this calendar. - fn in_leap_year(&self, date_like: &CalendarDateLike) -> TemporalResult { + fn in_leap_year(&self, date_like: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { // `ICU4X`'s `CalendarArithmetic` is currently private. Ok(utils::mathematical_days_in_year(date_like.as_iso_date().year()) == 366) } @@ -268,7 +278,7 @@ impl CalendarProtocol for IsoCalendar { // NOTE: This is currently not a name that is compliant with // the Temporal proposal. For debugging purposes only. /// Returns the debug name. - fn identifier(&self) -> TemporalResult { + fn identifier(&self, _: &mut dyn Any) -> TemporalResult { Ok("iso8601".to_string()) } } diff --git a/boa_temporal/src/date.rs b/boa_temporal/src/date.rs index 286a1bbc588..0f07040b308 100644 --- a/boa_temporal/src/date.rs +++ b/boa_temporal/src/date.rs @@ -8,6 +8,7 @@ use crate::{ options::{ArithmeticOverflow, TemporalUnit}, TemporalResult, }; +use std::any::Any; /// The `Temporal.PlainDate` equivalent #[derive(Debug, Default, Clone)] @@ -23,6 +24,80 @@ impl TemporalDate { pub(crate) fn new_unchecked(iso: IsoDate, calendar: CalendarSlot) -> Self { Self { iso, calendar } } + + #[inline] + /// `DifferenceDate` + pub(crate) fn diff_date( + &self, + other: &Self, + largest_unit: TemporalUnit, + context: &mut dyn Any, + ) -> TemporalResult { + if self.iso.year() == other.iso.year() + && self.iso.month() == other.iso.month() + && self.iso.day() == other.iso.day() + { + return Ok(Duration::default()); + } + + if largest_unit == TemporalUnit::Day { + let days = self.days_until(other); + return Ok(Duration::from_date_duration(DateDuration::new( + 0f64, + 0f64, + 0f64, + f64::from(days), + ))); + } + + self.calendar() + .date_until(self, other, largest_unit, context) + } + + #[inline] + /// Internal `AddDate` function + pub(crate) fn add_date( + &self, + duration: &Duration, + overflow: ArithmeticOverflow, + context: &mut dyn Any, + ) -> TemporalResult { + // 1. If options is not present, set options to undefined. + // 2. If duration.[[Years]] ≠ 0, or duration.[[Months]] ≠ 0, or duration.[[Weeks]] ≠ 0, then + if duration.date().years() != 0.0 + || duration.date().months() != 0.0 + || duration.date().weeks() != 0.0 + { + // a. If dateAdd is not present, then + // i. Set dateAdd to unused. + // ii. If calendar is an Object, set dateAdd to ? GetMethod(calendar, "dateAdd"). + // b. Return ? CalendarDateAdd(calendar, plainDate, duration, options, dateAdd). + return self.calendar().date_add(&self, duration, overflow, context); + } + + // 3. Let overflow be ? ToTemporalOverflow(options). + // 4. Let days be ? BalanceTimeDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "day").[[Days]]. + let (days, _) = duration.balance_time_duration(TemporalUnit::Day)?; + + // 5. Let result be ? AddISODate(plainDate.[[ISOYear]], plainDate.[[ISOMonth]], plainDate.[[ISODay]], 0, 0, 0, days, overflow). + let result = self + .iso + .add_iso_date(&DateDuration::new(0f64, 0f64, 0f64, days), overflow)?; + + Ok(Self::new_unchecked(result, self.calendar().clone())) + } + + #[inline] + /// Returns a new moved date and the days associated with that adjustment + pub(crate) fn move_relative_date( + &self, + duration: &Duration, + context: &mut dyn Any, + ) -> TemporalResult<(TemporalDate, f64)> { + let new_date = self.add_date(duration, ArithmeticOverflow::Constrain, context)?; + let days = f64::from(self.days_until(&new_date)); + Ok((new_date, days)) + } } // ==== Public API ==== @@ -86,72 +161,56 @@ impl TemporalDate { } #[inline] - /// Returns a new moved date and the days associated with that adjustment - pub fn move_relative_date(&self, duration: &Duration) -> TemporalResult<(TemporalDate, f64)> { - let new_date = self.add_to_date(duration, ArithmeticOverflow::Constrain)?; - let days = f64::from(self.days_until(&new_date)); - Ok((new_date, days)) + /// `DaysUntil` + /// + /// Calculates the epoch days between two `TemporalDate`s + pub fn days_until(&self, other: &Self) -> i32 { + other.iso.to_epoch_days() - self.iso.to_epoch_days() } +} +// ==== Context based API ==== + +impl TemporalDate { + #[cfg(feature = "context")] #[inline] - /// `AddDate` pub fn add_to_date( &self, duration: &Duration, overflow: ArithmeticOverflow, + context: &mut dyn Any, ) -> TemporalResult { - // 1. If options is not present, set options to undefined. - // 2. If duration.[[Years]] ≠ 0, or duration.[[Months]] ≠ 0, or duration.[[Weeks]] ≠ 0, then - if duration.date().years() != 0.0 - || duration.date().months() != 0.0 - || duration.date().weeks() != 0.0 - { - // a. If dateAdd is not present, then - // i. Set dateAdd to unused. - // ii. If calendar is an Object, set dateAdd to ? GetMethod(calendar, "dateAdd"). - // b. Return ? CalendarDateAdd(calendar, plainDate, duration, options, dateAdd). - return self.calendar().date_add(&self, duration, overflow); - } - - // 3. Let overflow be ? ToTemporalOverflow(options). - // 4. Let days be ? BalanceTimeDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "day").[[Days]]. - let (days, _) = duration.balance_time_duration(TemporalUnit::Day)?; - - // 5. Let result be ? AddISODate(plainDate.[[ISOYear]], plainDate.[[ISOMonth]], plainDate.[[ISODay]], 0, 0, 0, days, overflow). - let result = self - .iso - .add_iso_date(&DateDuration::new(0f64, 0f64, 0f64, days), overflow)?; - - Ok(Self::new_unchecked(result, self.calendar().clone())) + self.add_date(duration, overflow, context) } - /// `DifferenceDate` - pub fn diff_date(&self, other: &Self, largest_unit: TemporalUnit) -> TemporalResult { - if self.iso.year() == other.iso.year() - && self.iso.month() == other.iso.month() - && self.iso.day() == other.iso.day() - { - return Ok(Duration::default()); - } - - if largest_unit == TemporalUnit::Day { - let days = self.days_until(other); - return Ok(Duration::from_date_duration(DateDuration::new( - 0f64, - 0f64, - 0f64, - f64::from(days), - ))); - } + #[cfg(not(feature = "context"))] + #[inline] + pub fn add_to_date( + &self, + duration: &Duration, + overflow: ArithmeticOverflow, + ) -> TemporalResult { + self.add_date(duration, overflow, &mut ()) + } - self.calendar().date_until(self, other, largest_unit) + #[cfg(feature = "context")] + #[inline] + pub fn difference_date( + &self, + duration: &Duration, + overflow: ArithmeticOverflow, + context: &mut dyn Any, + ) -> TemporalResult { + self.add_date(duration, overflow, context) } + #[cfg(not(feature = "context"))] #[inline] - /// `DaysUntil` - /// - /// Calculates the epoch days between two `TemporalDate`s - pub fn days_until(&self, other: &Self) -> i32 { - other.iso.to_epoch_days() - self.iso.to_epoch_days() + pub fn difference_date( + &self, + other: &Self, + largest_unit: TemporalUnit, + ) -> TemporalResult { + self.diff_date(other, largest_unit, &mut ()) } } diff --git a/boa_temporal/src/duration.rs b/boa_temporal/src/duration.rs index c1264c0101d..3d5d1edbc2d 100644 --- a/boa_temporal/src/duration.rs +++ b/boa_temporal/src/duration.rs @@ -10,6 +10,7 @@ use crate::{ zoneddatetime::TemporalZonedDateTime, TemporalError, TemporalResult, NS_PER_DAY, }; +use std::any::Any; // ==== `DateDuration` ==== @@ -243,12 +244,42 @@ pub struct Duration { time: TimeDuration, } +// NOTE(nekevss): Structure of the below is going to be a little convoluted, +// but intended to section everything based on the below +// +// Notation - [section](sub-section(s)). +// +// Sections: +// - Creation (private/public) +// - Getters/Setters +// - Methods (private/public/feature) +// + +// ==== Private Creation methods ==== + impl Duration { /// Creates a new `Duration` from a `DateDuration` and `TimeDuration`. pub(crate) const fn new_unchecked(date: DateDuration, time: TimeDuration) -> Self { Self { date, time } } + /// Utility function to create a year duration. + pub(crate) fn one_year(year_value: f64) -> Self { + Self::from_date_duration(DateDuration::new(year_value, 0f64, 0f64, 0f64)) + } + + /// Utility function to create a month duration. + pub(crate) fn one_month(month_value: f64) -> Self { + Self::from_date_duration(DateDuration::new(0f64, month_value, 0f64, 0f64)) + } + + /// Utility function to create a week duration. + pub(crate) fn one_week(week_value: f64) -> Self { + Self::from_date_duration(DateDuration::new(0f64, 0f64, week_value, 0f64)) + } +} + +impl Duration { /// Creates a new validated `Duration`. #[allow(clippy::too_many_arguments)] pub fn new( @@ -306,28 +337,15 @@ impl Duration { } } - /// Utility function to create a one year duration. - pub(crate) fn one_year(year_value: f64) -> Self { - Self::from_date_duration(DateDuration::new(year_value, 0f64, 0f64, 0f64)) - } - - /// Utility function to create a one month duration. - pub(crate) fn one_month(month_value: f64) -> Self { - Self::from_date_duration(DateDuration::new(0f64, month_value, 0f64, 0f64)) - } - - /// Utility function to create a one week duration. - pub(crate) fn one_week(week_value: f64) -> Self { - Self::from_date_duration(DateDuration::new(0f64, 0f64, week_value, 0f64)) - } - - /// Utility function to return if the Durations values are within their valid ranges. + /// Return if the Durations values are within their valid ranges. #[inline] pub fn is_time_within_range(&self) -> bool { self.time.is_within_range() } } +// ==== Public `Duration` Getters/Setters ==== + impl Duration { /// Returns a reference to the inner `TimeDuration` pub fn time(&self) -> &TimeDuration { @@ -423,12 +441,12 @@ impl Iterator for DurationIter<'_> { 1 => Some(self.duration.date.months()), 2 => Some(self.duration.date.weeks()), 3 => Some(self.duration.date.days()), - 4 => Some(self.duration.time.hours), - 5 => Some(self.duration.time.minutes), - 6 => Some(self.duration.time.seconds), - 7 => Some(self.duration.time.milliseconds), - 8 => Some(self.duration.time.microseconds), - 9 => Some(self.duration.time.nanoseconds), + 4 => Some(self.duration.time.hours()), + 5 => Some(self.duration.time.minutes()), + 6 => Some(self.duration.time.seconds()), + 7 => Some(self.duration.time.milliseconds()), + 8 => Some(self.duration.time.microseconds()), + 9 => Some(self.duration.time.nanoseconds()), _ => None, }; self.index += 1; @@ -436,56 +454,16 @@ impl Iterator for DurationIter<'_> { } } -// ==== Duration method ==== - -impl Duration { - /// Returns the absolute value of `Duration`. - pub fn abs(&self) -> Self { - Self { - date: DateDuration::new( - self.date.years.abs(), - self.date.months.abs(), - self.date.weeks.abs(), - self.date.days.abs(), - ), - time: TimeDuration::new( - self.time.hours.abs(), - self.time.minutes.abs(), - self.time.seconds.abs(), - self.time.milliseconds.abs(), - self.time.microseconds.abs(), - self.time.nanoseconds.abs(), - ), - } - } -} +// ==== Private Duration methods ==== impl Duration { /// Returns the duration time values as a vec - fn time_values(&self) -> Vec { + pub(crate) fn time_values(&self) -> Vec { let mut values = Vec::from([self.date.days]); values.extend(self.time.into_iter()); values } - /// 7.5.10 `DurationSign ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds )` - /// - /// Determines the sign for the current self. - pub fn duration_sign(&self) -> i32 { - // 1. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do - for v in self { - // a. If v < 0, return -1. - if v < 0_f64 { - return -1; - // b. If v > 0, return 1. - } else if v > 0_f64 { - return 1; - } - } - // 2. Return 0. - 0 - } - /// 7.5.11 `IsValidDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds )` /// /// Checks if the current `DurationRecord` is a valid self. @@ -511,30 +489,8 @@ impl Duration { true } - /// 7.5.12 `DefaultTemporalLargestUnit ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds )` - pub fn default_temporal_largest_unit(&self) -> TemporalUnit { - for (index, value) in self.into_iter().enumerate() { - if value != 0.0 { - match index { - 0 => return TemporalUnit::Year, - 1 => return TemporalUnit::Month, - 2 => return TemporalUnit::Week, - 3 => return TemporalUnit::Day, - 4 => return TemporalUnit::Hour, - 5 => return TemporalUnit::Minute, - 6 => return TemporalUnit::Second, - 7 => return TemporalUnit::Millisecond, - 8 => return TemporalUnit::Microsecond, - _ => {} - } - } - } - - TemporalUnit::Nanosecond - } - /// 7.5.17 `TotalDurationNanoseconds ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, offsetShift )` - fn total_duration_nanoseconds(&self, offset_shift: f64) -> f64 { + pub(crate) fn total_duration_nanoseconds(&self, offset_shift: f64) -> f64 { let nanoseconds = if self.date.days == 0_f64 { self.time.nanoseconds } else { @@ -551,26 +507,6 @@ impl Duration { .mul_add(1_000_f64, nanoseconds) } - /// Abstract Operation 7.5.17 `BalanceTimeDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit )` - pub fn balance_time_duration( - &self, - largest_unit: TemporalUnit, - ) -> TemporalResult<(f64, TimeDuration)> { - // 1. Let balanceResult be ? BalancePossiblyInfiniteDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit, relativeTo). - let balance_result = self.balance_possibly_infinite_time_duration(largest_unit)?; - - // 2. If balanceResult is positive overflow or negative overflow, then - let Some(result) = balance_result else { - // a. Throw a RangeError exception. - return Err(TemporalError::range() - .with_message("duration overflowed viable range.") - .into()); - }; - // 3. Else, - // a. Return balanceResult. - Ok(result) - } - /// Abstract Operation 7.5.18 `BalancePossiblyInfiniteDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit )` pub(crate) fn balance_possibly_infinite_time_duration( &self, @@ -727,13 +663,13 @@ impl Duration { Ok(Some((result_days, result))) } - // TODO: move to DateDuration. /// 7.5.21 UnbalanceDateDurationRelative ( years, months, weeks, days, largestUnit, plainRelativeTo )` #[allow(dead_code)] - pub fn unbalance_duration_relative( + pub(crate) fn unbalance_duration_relative( &self, largest_unit: TemporalUnit, plain_relative_to: Option<&TemporalDate>, + context: &mut dyn Any, ) -> TemporalResult { // 1. Let allZero be false. // 2. If years = 0, and months = 0, and weeks = 0, and days = 0, set allZero to true. @@ -798,6 +734,7 @@ impl Duration { &plain_relative_to, &one_year, ArithmeticOverflow::Constrain, + context, )?; // ii. Let untilOptions be OrdinaryObjectCreate(null). @@ -807,6 +744,7 @@ impl Duration { &plain_relative_to, &new_relative_to, TemporalUnit::Month, + context, )?; // v. Let oneYearMonths be untilResult.[[Months]]. @@ -858,7 +796,7 @@ impl Duration { // e. Repeat, while years ≠ 0, while years != 0f64 { // i. Let moveResult be ? MoveRelativeDate(calendar, plainRelativeTo, oneYear, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_year)?; + let move_result = plain_relative_to.move_relative_date(&one_year, context)?; // ii. Set plainRelativeTo to moveResult.[[RelativeTo]]. plain_relative_to = move_result.0; @@ -872,7 +810,7 @@ impl Duration { // f. Repeat, while months ≠ 0, while months != 0f64 { // i. Let moveResult be ? MoveRelativeDate(calendar, plainRelativeTo, oneMonth, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_month)?; + let move_result = plain_relative_to.move_relative_date(&one_month, context)?; // ii. Set plainRelativeTo to moveResult.[[RelativeTo]]. plain_relative_to = move_result.0; // iii. Set days to days + moveResult.[[Days]]. @@ -911,7 +849,7 @@ impl Duration { // a. Let moveResult be ? MoveRelativeDate(calendar, plainRelativeTo, oneYear, dateAdd). while years != 0f64 { // a. Let moveResult be ? MoveRelativeDate(calendar, plainRelativeTo, oneYear, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_year)?; + let move_result = plain_relative_to.move_relative_date(&one_year, context)?; // b. Set plainRelativeTo to moveResult.[[RelativeTo]]. plain_relative_to = move_result.0; @@ -925,7 +863,7 @@ impl Duration { // 18. Repeat, while months ≠ 0, while months != 0f64 { // a. Let moveResult be ? MoveRelativeDate(calendar, plainRelativeTo, oneMonth, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_month)?; + let move_result = plain_relative_to.move_relative_date(&one_month, context)?; // b. Set plainRelativeTo to moveResult.[[RelativeTo]]. plain_relative_to = move_result.0; // c. Set days to days +moveResult.[[Days]]. @@ -938,7 +876,7 @@ impl Duration { // 19. Repeat, while weeks ≠ 0, while weeks != 0f64 { // a. Let moveResult be ? MoveRelativeDate(calendar, plainRelativeTo, oneWeek, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_week)?; + let move_result = plain_relative_to.move_relative_date(&one_week, context)?; // b. Set plainRelativeTo to moveResult.[[RelativeTo]]. plain_relative_to = move_result.0; // c. Set days to days + moveResult.[[Days]]. @@ -958,6 +896,7 @@ impl Duration { &self, largest_unit: TemporalUnit, plain_relative_to: Option<&TemporalDate>, + context: &mut dyn Any, ) -> TemporalResult { let mut result = DateDuration::from(self.date); @@ -1010,7 +949,7 @@ impl Duration { // d. Let newRelativeTo be moveResult.[[RelativeTo]]. // e. Let oneYearDays be moveResult.[[Days]]. let (mut new_relative_to, mut one_year_days) = - plain_relative_to.move_relative_date(&one_year)?; + plain_relative_to.move_relative_date(&one_year, context)?; // f. Repeat, while abs(days) ≥ abs(oneYearDays), while result.days().abs() >= one_year_days.abs() { @@ -1023,7 +962,7 @@ impl Duration { // iii. Set relativeTo to newRelativeTo. plain_relative_to = new_relative_to; // iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_year)?; + let move_result = plain_relative_to.move_relative_date(&one_year, context)?; // v. Set newRelativeTo to moveResult.[[RelativeTo]]. new_relative_to = move_result.0; @@ -1035,7 +974,7 @@ impl Duration { // h. Set newRelativeTo to moveResult.[[RelativeTo]]. // i. Let oneMonthDays be moveResult.[[Days]]. let (mut new_relative_to, mut one_month_days) = - plain_relative_to.move_relative_date(&one_month)?; + plain_relative_to.move_relative_date(&one_month, context)?; // j. Repeat, while abs(days) ≥ abs(oneMonthDays), while result.days().abs() >= one_month_days.abs() { @@ -1047,7 +986,7 @@ impl Duration { // iii. Set relativeTo to newRelativeTo. plain_relative_to = new_relative_to; // iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_month)?; + let move_result = plain_relative_to.move_relative_date(&one_month, context)?; // v. Set newRelativeTo to moveResult.[[RelativeTo]]. new_relative_to = move_result.0; @@ -1060,6 +999,7 @@ impl Duration { &plain_relative_to, &one_year, ArithmeticOverflow::Constrain, + context, )?; // l. If calendar is an Object, then @@ -1074,6 +1014,7 @@ impl Duration { &plain_relative_to, &new_relative_to, TemporalUnit::Month, + context, )?; // q. Let oneYearMonths be untilResult.[[Months]]. @@ -1094,6 +1035,7 @@ impl Duration { &plain_relative_to, &one_year, ArithmeticOverflow::Constrain, + context, )?; // v. Set untilOptions to OrdinaryObjectCreate(null). @@ -1103,6 +1045,7 @@ impl Duration { &plain_relative_to, &new_relative_to, TemporalUnit::Month, + context, )?; // viii. Set oneYearMonths to untilResult.[[Months]]. @@ -1120,7 +1063,7 @@ impl Duration { // d. Let newRelativeTo be moveResult.[[RelativeTo]]. // e. Let oneMonthDays be moveResult.[[Days]]. let (mut new_relative_to, mut one_month_days) = - plain_relative_to.move_relative_date(&one_month)?; + plain_relative_to.move_relative_date(&one_month, context)?; // f. Repeat, while abs(days) ≥ abs(oneMonthDays), while result.days().abs() >= one_month_days.abs() { @@ -1134,7 +1077,7 @@ impl Duration { plain_relative_to = new_relative_to; // iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_month)?; + let move_result = plain_relative_to.move_relative_date(&one_month, context)?; // v. Set newRelativeTo to moveResult.[[RelativeTo]]. new_relative_to = move_result.0; @@ -1154,7 +1097,7 @@ impl Duration { // e. Let newRelativeTo be moveResult.[[RelativeTo]]. // f. Let oneWeekDays be moveResult.[[Days]]. let (mut new_relative_to, mut one_week_days) = - plain_relative_to.move_relative_date(&one_week)?; + plain_relative_to.move_relative_date(&one_week, context)?; // g. Repeat, while abs(days) ≥ abs(oneWeekDays), while result.days().abs() >= one_week_days.abs() { @@ -1165,7 +1108,7 @@ impl Duration { // iii. Set relativeTo to newRelativeTo. plain_relative_to = new_relative_to; // iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_week)?; + let move_result = plain_relative_to.move_relative_date(&one_week, context)?; // v. Set newRelativeTo to moveResult.[[RelativeTo]]. new_relative_to = move_result.0; @@ -1195,6 +1138,7 @@ impl Duration { Option<&TemporalZonedDateTime>, Option<&TemporalDateTime>, ), + context: &mut dyn Any, ) -> TemporalResult<(Self, f64)> { let mut result = Duration::new_unchecked(unbalance_date_duration, self.time); @@ -1283,7 +1227,7 @@ impl Duration { // e. Let yearsLater be ? AddDate(calendar, plainRelativeTo, yearsDuration, undefined, dateAdd). let years_later = plain_relative_to - .add_to_date(&years_duration, ArithmeticOverflow::Constrain)?; + .add_date(&years_duration, ArithmeticOverflow::Constrain, context)?; // f. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0). let years_months_weeks = Self::new_unchecked( @@ -1298,7 +1242,7 @@ impl Duration { // g. Let yearsMonthsWeeksLater be ? AddDate(calendar, plainRelativeTo, yearsMonthsWeeks, undefined, dateAdd). let years_months_weeks_later = plain_relative_to - .add_to_date(&years_months_weeks, ArithmeticOverflow::Constrain)?; + .add_date(&years_months_weeks, ArithmeticOverflow::Constrain, context)?; // h. Let monthsWeeksInDays be DaysUntil(yearsLater, yearsMonthsWeeksLater). let months_weeks_in_days = years_later.days_until(&years_months_weeks_later); @@ -1322,7 +1266,7 @@ impl Duration { // n. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "year"). // o. Let timePassed be ? DifferenceDate(calendar, plainRelativeTo, wholeDaysLater, untilOptions). let time_passed = - plain_relative_to.diff_date(&whole_days_later, TemporalUnit::Year)?; + plain_relative_to.diff_date(&whole_days_later, TemporalUnit::Year, context)?; // p. Let yearsPassed be timePassed.[[Years]]. let years_passed = time_passed.date.years(); @@ -1337,7 +1281,7 @@ impl Duration { // t. Set plainRelativeTo to moveResult.[[RelativeTo]]. // u. Let daysPassed be moveResult.[[Days]]. let (plain_relative_to, days_passed) = - plain_relative_to.move_relative_date(&years_duration)?; + plain_relative_to.move_relative_date(&years_duration, context)?; // v. Set fractionalDays to fractionalDays - daysPassed. frac_days -= days_passed; @@ -1350,7 +1294,8 @@ impl Duration { // y. Set moveResult to ? MoveRelativeDate(calendar, plainRelativeTo, oneYear, dateAdd). // z. Let oneYearDays be moveResult.[[Days]]. - let (_, one_year_days) = plain_relative_to.move_relative_date(&one_year)?; + let (_, one_year_days) = + plain_relative_to.move_relative_date(&one_year, context)?; // aa. Let fractionalYears be years + fractionalDays / abs(oneYearDays). let frac_years = result.date.years() + (frac_days / one_year_days.abs()); @@ -1389,7 +1334,7 @@ impl Duration { // e. Let yearsMonthsLater be ? AddDate(calendar, plainRelativeTo, yearsMonths, undefined, dateAdd). let years_months_later = - plain_relative_to.add_to_date(&years_months, ArithmeticOverflow::Constrain)?; + plain_relative_to.add_date(&years_months, ArithmeticOverflow::Constrain, context)?; // f. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0). let years_months_weeks = Self::from_date_duration(DateDuration::new( @@ -1401,7 +1346,7 @@ impl Duration { // g. Let yearsMonthsWeeksLater be ? AddDate(calendar, plainRelativeTo, yearsMonthsWeeks, undefined, dateAdd). let years_months_weeks_later = plain_relative_to - .add_to_date(&years_months_weeks, ArithmeticOverflow::Constrain)?; + .add_date(&years_months_weeks, ArithmeticOverflow::Constrain, context)?; // h. Let weeksInDays be DaysUntil(yearsMonthsLater, yearsMonthsWeeksLater). let weeks_in_days = years_months_later.days_until(&years_months_weeks_later); @@ -1422,7 +1367,7 @@ impl Duration { // n. Set plainRelativeTo to moveResult.[[RelativeTo]]. // o. Let oneMonthDays be moveResult.[[Days]]. let (mut plain_relative_to, mut one_month_days) = - plain_relative_to.move_relative_date(&one_month)?; + plain_relative_to.move_relative_date(&one_month, context)?; // p. Repeat, while abs(fractionalDays) ≥ abs(oneMonthDays), while frac_days.abs() >= one_month_days.abs() { @@ -1433,7 +1378,7 @@ impl Duration { frac_days -= one_month_days; // iii. Set moveResult to ? MoveRelativeDate(calendar, plainRelativeTo, oneMonth, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_month)?; + let move_result = plain_relative_to.move_relative_date(&one_month, context)?; // iv. Set plainRelativeTo to moveResult.[[RelativeTo]]. plain_relative_to = move_result.0; @@ -1475,7 +1420,7 @@ impl Duration { // g. Set plainRelativeTo to moveResult.[[RelativeTo]]. // h. Let oneWeekDays be moveResult.[[Days]]. let (mut plain_relative_to, mut one_week_days) = - plain_relative_to.move_relative_date(&one_week)?; + plain_relative_to.move_relative_date(&one_week, context)?; // i. Repeat, while abs(fractionalDays) ≥ abs(oneWeekDays), while frac_days.abs() >= one_week_days.abs() { @@ -1486,7 +1431,7 @@ impl Duration { frac_days -= one_week_days; // iii. Set moveResult to ? MoveRelativeDate(calendar, plainRelativeTo, oneWeek, dateAdd). - let move_result = plain_relative_to.move_relative_date(&one_week)?; + let move_result = plain_relative_to.move_relative_date(&one_week, context)?; // iv. Set plainRelativeTo to moveResult.[[RelativeTo]]. plain_relative_to = move_result.0; @@ -1620,3 +1565,87 @@ impl Duration { Ok((result, total)) } } + +// ==== Public Duration methods ==== + +impl Duration { + /// Returns the absolute value of `Duration`. + pub fn abs(&self) -> Self { + Self { + date: DateDuration::new( + self.date.years.abs(), + self.date.months.abs(), + self.date.weeks.abs(), + self.date.days.abs(), + ), + time: TimeDuration::new( + self.time.hours.abs(), + self.time.minutes.abs(), + self.time.seconds.abs(), + self.time.milliseconds.abs(), + self.time.microseconds.abs(), + self.time.nanoseconds.abs(), + ), + } + } + + /// 7.5.10 `DurationSign ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds )` + /// + /// Determines the sign for the current self. + pub fn duration_sign(&self) -> i32 { + // 1. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do + for v in self { + // a. If v < 0, return -1. + if v < 0_f64 { + return -1; + // b. If v > 0, return 1. + } else if v > 0_f64 { + return 1; + } + } + // 2. Return 0. + 0 + } + + /// 7.5.12 `DefaultTemporalLargestUnit ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds )` + pub fn default_temporal_largest_unit(&self) -> TemporalUnit { + for (index, value) in self.into_iter().enumerate() { + if value != 0.0 { + match index { + 0 => return TemporalUnit::Year, + 1 => return TemporalUnit::Month, + 2 => return TemporalUnit::Week, + 3 => return TemporalUnit::Day, + 4 => return TemporalUnit::Hour, + 5 => return TemporalUnit::Minute, + 6 => return TemporalUnit::Second, + 7 => return TemporalUnit::Millisecond, + 8 => return TemporalUnit::Microsecond, + _ => {} + } + } + } + + TemporalUnit::Nanosecond + } + + /// Abstract Operation 7.5.17 `BalanceTimeDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit )` + pub fn balance_time_duration( + &self, + largest_unit: TemporalUnit, + ) -> TemporalResult<(f64, TimeDuration)> { + // 1. Let balanceResult be ? BalancePossiblyInfiniteDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit, relativeTo). + let balance_result = self.balance_possibly_infinite_time_duration(largest_unit)?; + + // 2. If balanceResult is positive overflow or negative overflow, then + let Some(result) = balance_result else { + // a. Throw a RangeError exception. + return Err(TemporalError::range() + .with_message("duration overflowed viable range.") + .into()); + }; + // 3. Else, + // a. Return balanceResult. + Ok(result) + } +}