diff --git a/Cargo.lock b/Cargo.lock index d9670e68c80..3b6ba79da76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,15 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anes" version = "0.1.6" @@ -866,6 +875,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "core_maths" version = "0.1.0" @@ -1462,6 +1477,29 @@ dependencies = [ "itoa", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu" version = "1.5.0" @@ -3246,12 +3284,12 @@ checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" [[package]] name = "temporal_rs" version = "0.0.3" -source = "git+https://github.com/boa-dev/temporal.git?rev=1e7901d07a83211e62373ab94284a7d1ada4c913#1e7901d07a83211e62373ab94284a7d1ada4c913" +source = "git+https://github.com/boa-dev/temporal.git?rev=345ad548db498a5fe596ebebfc7f0ea6214fb079#345ad548db498a5fe596ebebfc7f0ea6214fb079" dependencies = [ "bitflags 2.6.0", + "iana-time-zone", "icu_calendar", "ixdtf", - "num-bigint", "num-traits", "rustc-hash 2.0.0", "tinystr", @@ -3867,6 +3905,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 8fd218cb15f..f0d619cdb0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,7 +111,7 @@ intrusive-collections = "0.9.7" cfg-if = "1.0.0" either = "1.13.0" sys-locale = "0.3.2" -temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "1e7901d07a83211e62373ab94284a7d1ada4c913" } +temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "345ad548db498a5fe596ebebfc7f0ea6214fb079" } web-time = "1.1.0" criterion = "0.5.1" float-cmp = "0.9.0" diff --git a/core/engine/src/builtins/temporal/calendar/mod.rs b/core/engine/src/builtins/temporal/calendar/mod.rs index 665daee341f..04ff76f1310 100644 --- a/core/engine/src/builtins/temporal/calendar/mod.rs +++ b/core/engine/src/builtins/temporal/calendar/mod.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use super::extract_from_temporal_type; use crate::{js_string, Context, JsNativeError, JsObject, JsResult, JsValue}; -use temporal_rs::components::calendar::Calendar; +use temporal_rs::Calendar; // -- `Calendar` Abstract Operations -- diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 30f053f00ea..379e4f33100 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -16,9 +16,10 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use temporal_rs::{ - components::{duration::PartialDuration, Duration as InnerDuration}, options::{RelativeTo, RoundingIncrement, RoundingOptions, TemporalRoundingMode, TemporalUnit}, + partial::PartialDuration, primitive::FiniteF64, + Duration as InnerDuration, }; use super::{ diff --git a/core/engine/src/builtins/temporal/instant/mod.rs b/core/engine/src/builtins/temporal/instant/mod.rs index a5746555325..482daf4a87d 100644 --- a/core/engine/src/builtins/temporal/instant/mod.rs +++ b/core/engine/src/builtins/temporal/instant/mod.rs @@ -24,8 +24,8 @@ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use num_traits::ToPrimitive; use temporal_rs::{ - components::Instant as InnerInstant, options::{RoundingIncrement, RoundingOptions, TemporalRoundingMode}, + Instant as InnerInstant, }; use super::options::get_difference_settings; @@ -150,7 +150,7 @@ impl BuiltInConstructor for Instant { // 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception. // NOTE: temporal_rs::Instant asserts that the epochNanoseconds are valid. - let instant = InnerInstant::new(epoch_nanos.as_inner().to_i128().unwrap_or(i128::MAX))?; + let instant = InnerInstant::try_new(epoch_nanos.as_inner().to_i128().unwrap_or(i128::MAX))?; // 4. Return ? CreateTemporalInstant(epochNanoseconds, NewTarget). create_temporal_instant(instant, Some(new_target.clone()), context) } @@ -201,7 +201,7 @@ impl Instant { // 3. Return ! CreateTemporalInstant(epochNanoseconds). let nanos = epoch_nanos.as_inner().to_i128(); create_temporal_instant( - InnerInstant::new(nanos.unwrap_or(i128::MAX))?, + InnerInstant::try_new(nanos.unwrap_or(i128::MAX))?, None, context, ) diff --git a/core/engine/src/builtins/temporal/mod.rs b/core/engine/src/builtins/temporal/mod.rs index bd4128b3a3b..45a5a6ba95d 100644 --- a/core/engine/src/builtins/temporal/mod.rs +++ b/core/engine/src/builtins/temporal/mod.rs @@ -37,10 +37,7 @@ use crate::{ Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_profiler::Profiler; -use temporal_rs::{ - components::{Date as TemporalDate, ZonedDateTime as TemporalZonedDateTime}, - NS_PER_DAY, -}; +use temporal_rs::{PlainDate as TemporalDate, ZonedDateTime as TemporalZonedDateTime, NS_PER_DAY}; // TODO: Remove in favor of `temporal_rs` pub(crate) fn ns_max_instant() -> JsBigInt { diff --git a/core/engine/src/builtins/temporal/now.rs b/core/engine/src/builtins/temporal/now.rs index 45da161bc26..d0e9f4caef4 100644 --- a/core/engine/src/builtins/temporal/now.rs +++ b/core/engine/src/builtins/temporal/now.rs @@ -11,7 +11,7 @@ use crate::{ Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_profiler::Profiler; -use temporal_rs::components::tz::TimeZone; +use temporal_rs::TimeZone; use super::{ns_max_instant, ns_min_instant, time_zone::default_time_zone}; diff --git a/core/engine/src/builtins/temporal/options.rs b/core/engine/src/builtins/temporal/options.rs index 0d369ff776e..46883c52eeb 100644 --- a/core/engine/src/builtins/temporal/options.rs +++ b/core/engine/src/builtins/temporal/options.rs @@ -13,7 +13,7 @@ use crate::{ js_string, Context, JsNativeError, JsObject, JsResult, JsString, JsValue, }; use temporal_rs::options::{ - ArithmeticOverflow, CalendarName, DifferenceSettings, DurationOverflow, InstantDisambiguation, + ArithmeticOverflow, CalendarName, DifferenceSettings, Disambiguation, DurationOverflow, OffsetDisambiguation, RoundingIncrement, TemporalRoundingMode, TemporalUnit, }; @@ -113,7 +113,7 @@ fn datetime_units() -> impl Iterator { impl ParsableOptionType for TemporalUnit {} impl ParsableOptionType for ArithmeticOverflow {} impl ParsableOptionType for DurationOverflow {} -impl ParsableOptionType for InstantDisambiguation {} +impl ParsableOptionType for Disambiguation {} impl ParsableOptionType for OffsetDisambiguation {} impl ParsableOptionType for TemporalRoundingMode {} impl ParsableOptionType for CalendarName {} diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index f85cb56036f..003c6e93e3c 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -3,8 +3,6 @@ // TODO (nekevss): DOCS DOCS AND MORE DOCS -use std::str::FromStr; - use crate::{ builtins::{ options::{get_option, get_options_object}, @@ -22,13 +20,8 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use temporal_rs::{ - components::{ - calendar::{Calendar, GetTemporalCalendar}, - Date as InnerDate, DateTime, MonthCode, PartialDate, - }, - iso::IsoDateSlots, - options::ArithmeticOverflow, - TemporalFields, TinyAsciiStr, + options::ArithmeticOverflow, partial::PartialDate, Calendar, PlainDate as InnerDate, + TinyAsciiStr, }; use super::{ @@ -51,18 +44,6 @@ impl PlainDate { } } -impl IsoDateSlots for JsObject { - fn iso_date(&self) -> temporal_rs::iso::IsoDate { - self.borrow().data().inner.iso_date() - } -} - -impl GetTemporalCalendar for JsObject { - fn get_calendar(&self) -> Calendar { - self.borrow().data().inner.get_calendar() - } -} - impl BuiltInObject for PlainDate { const NAME: JsString = StaticJsStrings::PLAIN_DATE_NAME; } @@ -262,15 +243,15 @@ impl BuiltInConstructor for PlainDate { let iso_day = super::to_integer_with_truncation(args.get_or_undefined(2), context)?; let calendar_slot = to_temporal_calendar_slot_value(args.get_or_undefined(3))?; - let date = InnerDate::new( + Ok(create_temporal_date( iso_year, iso_month, iso_day, calendar_slot, - ArithmeticOverflow::Reject, - )?; - - Ok(create_temporal_date(date, Some(new_target), context)?.into()) + Some(new_target), + context, + )? + .into()) } } @@ -504,11 +485,23 @@ impl PlainDate { if let Some(date) = item.as_object().and_then(JsObject::downcast_ref::) { let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?; let _ = get_option::(&options, js_string!("overflow"), context)?; - return create_temporal_date(date.inner.clone(), None, context).map(Into::into); + return create_temporal_date( + date.inner.iso_year(), + date.inner.iso_month().into(), + date.inner.iso_day().into(), + date.inner.calendar().clone(), + None, + context, + ) + .map(Into::into); } + let resolved_date = to_temporal_date(item, options.cloned(), context)?; create_temporal_date( - to_temporal_date(item, options.cloned(), context)?, + resolved_date.iso_year(), + resolved_date.iso_month().into(), + resolved_date.iso_day().into(), + resolved_date.calendar().clone(), None, context, ) @@ -600,7 +593,16 @@ impl PlainDate { // 5. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »). // 6. Return ? AddDate(calendarRec, temporalDate, duration, options). - create_temporal_date(date.inner.add(&duration, overflow)?, None, context).map(Into::into) + let resolved_date = date.inner.add(&duration, overflow)?; + create_temporal_date( + resolved_date.iso_year(), + resolved_date.iso_month().into(), + resolved_date.iso_day().into(), + resolved_date.calendar().clone(), + None, + context, + ) + .map(Into::into) } fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { @@ -623,8 +625,16 @@ impl PlainDate { // 5. Let negatedDuration be CreateNegatedTemporalDuration(duration). // 6. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »). // 7. Return ? AddDate(calendarRec, temporalDate, negatedDuration, options). - create_temporal_date(date.inner.subtract(&duration, overflow)?, None, context) - .map(Into::into) + let resolved_date = date.inner.subtract(&duration, overflow)?; + create_temporal_date( + resolved_date.iso_year(), + resolved_date.iso_month().into(), + resolved_date.iso_day().into(), + resolved_date.calendar().clone(), + None, + context, + ) + .map(Into::into) } // 3.3.24 Temporal.PlainDate.prototype.with ( temporalDateLike [ , options ] ) @@ -659,7 +669,16 @@ impl PlainDate { let partial = to_partial_date_record(partial_object, context)?; // 10. Return ? CalendarDateFromFields(calendarRec, fields, resolvedOptions). - create_temporal_date(date.inner.with(partial, overflow)?, None, context).map(Into::into) + let resolved_date = date.inner.with(partial, overflow)?; + create_temporal_date( + resolved_date.iso_year(), + resolved_date.iso_month().into(), + resolved_date.iso_day().into(), + resolved_date.calendar().clone(), + None, + context, + ) + .map(Into::into) } /// 3.3.26 Temporal.PlainDate.prototype.withCalendar ( calendarLike ) @@ -672,8 +691,16 @@ impl PlainDate { })?; let calendar = to_temporal_calendar_slot_value(args.get_or_undefined(0))?; - - create_temporal_date(date.inner.with_calendar(calendar)?, None, context).map(Into::into) + let resolved_date = date.inner.with_calendar(calendar)?; + create_temporal_date( + resolved_date.iso_year(), + resolved_date.iso_month().into(), + resolved_date.iso_day().into(), + resolved_date.calendar().clone(), + None, + context, + ) + .map(Into::into) } fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { @@ -757,7 +784,14 @@ impl PlainDate { impl PlainDate { /// Utitily function for translating a `Temporal.PlainDate` into a `JsObject`. pub(crate) fn as_object(&self, context: &mut Context) -> JsResult { - create_temporal_date(self.inner.clone(), None, context) + create_temporal_date( + self.inner.iso_year(), + self.inner.iso_month().into(), + self.inner.iso_day().into(), + self.inner.calendar().clone(), + None, + context, + ) } } @@ -766,19 +800,16 @@ impl PlainDate { /// 3.5.3 `CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] )` pub(crate) fn create_temporal_date( - inner: InnerDate, + iso_year: i32, + iso_month: i32, + iso_day: i32, + calendar_slot: Calendar, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { - // NOTE (nekevss): The below should never trigger as `IsValidISODate` is enforced by Date. // 1. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception. - // 2. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception. - if !DateTime::validate(&inner) { - return Err(JsNativeError::range() - .with_message("Date is not within ISO date time limits.") - .into()); - } + let inner = InnerDate::try_new(iso_year, iso_month, iso_day, calendar_slot)?; // 3. If newTarget is not present, set newTarget to %Temporal.PlainDate%. let new_target = if let Some(new_target) = new_target { @@ -843,7 +874,7 @@ pub(crate) fn to_temporal_date( let _o = get_option(&options_obj, js_string!("overflow"), context)? .unwrap_or(ArithmeticOverflow::Constrain); - let date = InnerDate::from(date_time.inner().clone()); + let date = InnerDate::from(date_time.inner.clone()); // ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]). return Ok(date); @@ -867,11 +898,10 @@ pub(crate) fn to_temporal_date( .with_message("A partial date must have at least one defined field.") .into()); } - let mut fields = TemporalFields::from(partial); // g. Return ? CalendarDateFromFields(calendar, fields, options). return calendar - .date_from_fields(&mut fields, overflow) + .date_from_partial(&partial, overflow) .map_err(Into::into); } @@ -920,7 +950,8 @@ pub(crate) fn to_partial_date_record( .with_message("The monthCode field value must be a string.") .into()); }; - MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(Into::::into) + TinyAsciiStr::<4>::from_str(&month_code.to_std_string_escaped()) + .map_err(|e| JsError::from(JsNativeError::typ().with_message(e.to_string()))) }) .transpose()?; let year = partial_object @@ -943,7 +974,7 @@ pub(crate) fn to_partial_date_record( )); }; // TODO: double check if an invalid monthCode is a range or type error. - TinyAsciiStr::<16>::from_str(&era.to_std_string_escaped()) + TinyAsciiStr::<19>::from_str(&era.to_std_string_escaped()) .map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string()))) }) .transpose()?; @@ -957,3 +988,21 @@ pub(crate) fn to_partial_date_record( era_year, }) } + +impl From> for JsValue { + fn from(value: Option) -> Self { + match value { + Some(v) => v.into(), + None => JsValue::undefined(), + } + } +} + +impl From> for JsValue { + fn from(value: Option) -> Self { + match value { + Some(v) => v.into(), + None => JsValue::undefined(), + } + } +} 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 b8f965d6c78..f932103185c 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -22,13 +22,9 @@ use boa_profiler::Profiler; mod tests; use temporal_rs::{ - components::{ - calendar::{Calendar, GetTemporalCalendar}, - DateTime as InnerDateTime, PartialDateTime, Time, - }, - iso::{IsoDate, IsoDateSlots}, options::{ArithmeticOverflow, RoundingIncrement, RoundingOptions, TemporalRoundingMode}, - TemporalFields, + partial::PartialDateTime, + PlainDateTime as InnerDateTime, PlainTime, }; use super::{ @@ -55,18 +51,6 @@ impl PlainDateTime { } } -impl IsoDateSlots for JsObject { - fn iso_date(&self) -> IsoDate { - self.borrow().data().inner.iso_date() - } -} - -impl GetTemporalCalendar for JsObject { - fn get_calendar(&self) -> Calendar { - self.borrow().data().inner.get_calendar() - } -} - impl BuiltInObject for PlainDateTime { const NAME: JsString = StaticJsStrings::PLAIN_DATETIME_NAME; } @@ -1034,18 +1018,17 @@ pub(crate) fn to_temporal_datetime( } // g. Let result be ? InterpretTemporalDateTimeFields(calendarRec, fields, resolvedOptions). let overflow = get_option::(&options, js_string!("overflow"), context)?; - let date = calendar.date_from_fields( - &mut TemporalFields::from(partial_date), + let date = calendar.date_from_partial( + &partial_date, overflow.unwrap_or(ArithmeticOverflow::Constrain), )?; - let time = Time::new( + let time = PlainTime::new( partial_time.hour.unwrap_or(0), partial_time.minute.unwrap_or(0), partial_time.second.unwrap_or(0), partial_time.millisecond.unwrap_or(0), partial_time.microsecond.unwrap_or(0), partial_time.nanosecond.unwrap_or(0), - ArithmeticOverflow::Constrain, )?; return InnerDateTime::new( diff --git a/core/engine/src/builtins/temporal/plain_month_day/mod.rs b/core/engine/src/builtins/temporal/plain_month_day/mod.rs index 8eace7d6d90..34292ab5b0e 100644 --- a/core/engine/src/builtins/temporal/plain_month_day/mod.rs +++ b/core/engine/src/builtins/temporal/plain_month_day/mod.rs @@ -13,19 +13,18 @@ use crate::{ property::Attribute, realm::Realm, string::StaticJsStrings, - Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, + Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol, + JsValue, }; use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use temporal_rs::{ - components::{ - calendar::{Calendar, GetTemporalCalendar}, - DateTime, MonthDay as InnerMonthDay, - }, - iso::IsoDateSlots, options::{ArithmeticOverflow, CalendarName}, + partial::PartialDate, + PlainDateTime, PlainMonthDay as InnerMonthDay, }; +use tinystr::TinyAsciiStr; use super::{calendar::to_temporal_calendar_slot_value, DateTimeValues}; @@ -118,17 +117,6 @@ impl PlainMonthDay { Ok(month_day_to_string(inner, show_calendar)) } } -impl IsoDateSlots for JsObject { - fn iso_date(&self) -> temporal_rs::iso::IsoDate { - self.borrow().data().inner.iso_date() - } -} - -impl GetTemporalCalendar for JsObject { - fn get_calendar(&self) -> Calendar { - self.borrow().data().inner.get_calendar() - } -} impl BuiltInObject for PlainMonthDay { const NAME: JsString = StaticJsStrings::PLAIN_MD_NAME; @@ -196,9 +184,33 @@ impl BuiltInConstructor for PlainMonthDay { args: &[JsValue], context: &mut Context, ) -> JsResult { - Err(JsNativeError::range() - .with_message("Not yet implemented.") - .into()) + // 1. If NewTarget is undefined, then + if new_target.is_undefined() { + // a. Throw a TypeError exception. + return Err(JsNativeError::typ() + .with_message("NewTarget cannot be undefined when constructing a PlainYearMonth.") + .into()); + } + + let year = args.get_or_undefined(3); + let ref_year = if year.is_undefined() { + None + } else { + Some(super::to_integer_with_truncation(year, context)?) + }; + + // We can ignore 2 as the underlying temporal library handles the reference year + let m = super::to_integer_with_truncation(args.get_or_undefined(0), context)?; + let d = super::to_integer_with_truncation(args.get_or_undefined(1), context)?; + let calendar = to_temporal_calendar_slot_value(args.get_or_undefined(2))?; + let inner = InnerMonthDay::new_with_overflow( + m, + d, + calendar, + ArithmeticOverflow::Constrain, + ref_year, + )?; + create_temporal_month_day(inner, Some(new_target), context) } } @@ -214,6 +226,7 @@ fn month_day_to_string(inner: &InnerMonthDay, show_calendar: CalendarName) -> Js // 3. Let result be the string-concatenation of month and the code unit 0x002D (HYPHEN-MINUS). let mut result = format!("{month:0>2}-{day:0>2}"); + // 4. Let calendarId be monthDay.[[Calendar]].[[id]]. let calendar_id = inner.calendar().identifier(); // 5. Let calendar be monthDay.[[Calendar]]. @@ -227,12 +240,13 @@ fn month_day_to_string(inner: &InnerMonthDay, show_calendar: CalendarName) -> Js CalendarName::Critical | CalendarName::Always | CalendarName::Auto )) && !(matches!(show_calendar, CalendarName::Auto) && calendar_id == "iso8601") { + let year = inner.iso_year().to_string(); let flag = if matches!(show_calendar, CalendarName::Critical) { "!" } else { "" }; - result.push_str(&format!("[{flag}c={calendar_id}]",)); + result = format!("{year}-{result}[{flag}u-ca={calendar_id}]"); } // 8. Return result. js_string!(result).into() @@ -245,7 +259,7 @@ pub(crate) fn create_temporal_month_day( ) -> JsResult { // 1. If IsValidISODate(referenceISOYear, isoMonth, isoDay) is false, throw a RangeError exception. // 2. If ISODateTimeWithinLimits(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception. - if !DateTime::validate(&inner) { + if !PlainDateTime::validate(&inner) { return Err(JsNativeError::range() .with_message("PlainMonthDay does not hold a valid ISO date time.") .into()); @@ -301,18 +315,45 @@ fn to_temporal_month_day( } else if let Some(item_string) = item.as_string() { InnerMonthDay::from_str(item_string.to_std_string_escaped().as_str())? } else if item.is_object() { - InnerMonthDay::new( - item.get_v(js_string!("month"), context) - .expect("Month not found") - .to_i32(context) - .expect("Cannot convert month to i32"), - item.get_v(js_string!("day"), context) - .expect("Day not found") - .to_i32(context) - .expect("Cannot convert day to i32"), - calendar, - overflow, - )? + let day = item + .get_v(js_string!("day"), context) + .expect("Day not found") + .to_i32(context) + .expect("Cannot convert day to i32"); + let month = item + .get_v(js_string!("month"), context) + .expect("Month not found") + .to_i32(context) + .expect("Cannot convert month to i32"); + + let month_code = item + .get_v(js_string!("monthCode"), context) + .expect("monthCode not found"); + let resolved_month_code = if month_code.is_undefined() { + None + } else { + TinyAsciiStr::<4>::from_str( + &month_code + .to_string(context) + .expect("Cannot convert monthCode to string") + .to_std_string_escaped(), + ) + .map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string()))) + .ok() + }; + let year = item.get_v(js_string!("year"), context).map_or(1972, |val| { + val.to_i32(context).expect("Cannot convert year to i32") + }); + + let partial_date = &PartialDate { + month: Some(month), + day: Some(day), + year: Some(year), + month_code: resolved_month_code, + ..Default::default() + }; + + calendar.month_day_from_partial(partial_date, overflow)? } else { return Err(JsNativeError::typ() .with_message("item must be an object or a string") diff --git a/core/engine/src/builtins/temporal/plain_time/mod.rs b/core/engine/src/builtins/temporal/plain_time/mod.rs index 52d34cbbbb7..ffa97d54531 100644 --- a/core/engine/src/builtins/temporal/plain_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_time/mod.rs @@ -16,8 +16,9 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use temporal_rs::{ - components::{PartialTime, Time}, options::{ArithmeticOverflow, TemporalRoundingMode}, + partial::PartialTime, + PlainTime as PlainTimeInner, }; use super::{ @@ -31,7 +32,7 @@ use super::{ // Safety: Time does not contain any traceable types. #[boa_gc(unsafe_empty_trace)] pub struct PlainTime { - inner: Time, + inner: PlainTimeInner, } impl BuiltInObject for PlainTime { @@ -184,15 +185,8 @@ impl BuiltInConstructor for PlainTime { .transpose()? .unwrap_or(0); - let inner = Time::new( - hour, - minute, - second, - millisecond, - microsecond, - nanosecond, - ArithmeticOverflow::Reject, - )?; + let inner = + PlainTimeInner::new(hour, minute, second, millisecond, microsecond, nanosecond)?; // 8. Return ? CreateTemporalTime(hour, minute, second, millisecond, microsecond, nanosecond, NewTarget). create_temporal_time(inner, Some(new_target), context).map(Into::into) @@ -597,7 +591,7 @@ impl PlainTime { // ==== PlainTime Abstract Operations ==== pub(crate) fn create_temporal_time( - inner: Time, + inner: PlainTimeInner, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { @@ -633,12 +627,14 @@ pub(crate) fn create_temporal_time( Ok(obj) } +/// 4.5.3 `ToTemporalTime ( item [ , overflow ] )` pub(crate) fn to_temporal_time( value: &JsValue, overflow: Option, context: &mut Context, -) -> JsResult