Browse Source

Updates to temporal_rs version and temporal methods (#3900)

* Updates to temporal_rs version and temporal methods

* Apply review feedback for relativeTo

* Forgot Date.prototype.toPlainDateTime

* Fix doc on toPlainDateTime
pull/3906/head
Kevin Ness 5 months ago committed by GitHub
parent
commit
5889a767d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      Cargo.lock
  2. 2
      Cargo.toml
  3. 66
      core/engine/src/builtins/temporal/duration/mod.rs
  4. 19
      core/engine/src/builtins/temporal/mod.rs
  5. 85
      core/engine/src/builtins/temporal/plain_date/mod.rs
  6. 134
      core/engine/src/builtins/temporal/plain_date_time/mod.rs
  7. 124
      core/engine/src/builtins/temporal/plain_time/mod.rs

5
Cargo.lock generated

@ -3200,8 +3200,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]] [[package]]
name = "temporal_rs" name = "temporal_rs"
version = "0.0.2" version = "0.0.3"
source = "git+https://github.com/boa-dev/temporal.git?rev=cf70b50f90865d2c00fb994b7adf8b9e1bd837b8#cf70b50f90865d2c00fb994b7adf8b9e1bd837b8" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6f351ef929946476b4107c09348c9e25ba1155ff8e3867b1914878a0fb553f"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"icu_calendar", "icu_calendar",

2
Cargo.toml

@ -115,7 +115,7 @@ intrusive-collections = "0.9.6"
cfg-if = "1.0.0" cfg-if = "1.0.0"
either = "1.13.0" either = "1.13.0"
sys-locale = "0.3.1" sys-locale = "0.3.1"
temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "cf70b50f90865d2c00fb994b7adf8b9e1bd837b8" } temporal_rs = "0.0.3"
web-time = "1.1.0" web-time = "1.1.0"
criterion = "0.5.1" criterion = "0.5.1"
float-cmp = "0.9.0" float-cmp = "0.9.0"

66
core/engine/src/builtins/temporal/duration/mod.rs

@ -180,6 +180,7 @@ impl IntrinsicObject for Duration {
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.static_method(Self::from, js_string!("from"), 1)
.method(Self::with, js_string!("with"), 1) .method(Self::with, js_string!("with"), 1)
.method(Self::negated, js_string!("negated"), 0) .method(Self::negated, js_string!("negated"), 0)
.method(Self::abs, js_string!("abs"), 0) .method(Self::abs, js_string!("abs"), 0)
@ -412,7 +413,26 @@ impl Duration {
} }
} }
// -- Duration Method implementations -- // ==== Duration methods implementations ====
impl Duration {
fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let item = args.get_or_undefined(0);
// 1. If item is an Object and item has an [[InitializedTemporalDuration]] internal slot, then
if let Some(duration) = item.as_object().and_then(JsObject::downcast_ref::<Self>) {
// a. Return ! CreateTemporalDuration(item.[[Years]], item.[[Months]], item.[[Weeks]],
// item.[[Days]], item.[[Hours]], item.[[Minutes]], item.[[Seconds]], item.[[Milliseconds]],
// item.[[Microseconds]], item.[[Nanoseconds]]).
return create_temporal_duration(duration.inner, None, context).map(Into::into);
}
// 2. Return ? ToTemporalDuration(item).
create_temporal_duration(to_temporal_duration_record(item, context)?, None, context)
.map(Into::into)
}
}
// ==== Duration.prototype method implementations ====
impl Duration { impl Duration {
/// 7.3.15 `Temporal.Duration.prototype.with ( temporalDurationLike )` /// 7.3.15 `Temporal.Duration.prototype.with ( temporalDurationLike )`
@ -581,17 +601,45 @@ impl Duration {
} }
/// 7.3.18 `Temporal.Duration.prototype.add ( other [ , options ] )` /// 7.3.18 `Temporal.Duration.prototype.add ( other [ , options ] )`
pub(crate) fn add(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { pub(crate) fn add(
Err(JsNativeError::range() this: &JsValue,
.with_message("not yet implemented.") args: &[JsValue],
.into()) context: &mut Context,
) -> JsResult<JsValue> {
// 1.Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
let duration = this
.as_object()
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("this value must be a Duration object.")
})?;
// 3. Return ? AddDurations(add, duration, other).
let other = to_temporal_duration_record(args.get_or_undefined(0), context)?;
create_temporal_duration(duration.inner.add(&other)?, None, context).map(Into::into)
} }
/// 7.3.19 `Temporal.Duration.prototype.subtract ( other [ , options ] )` /// 7.3.19 `Temporal.Duration.prototype.subtract ( other [ , options ] )`
pub(crate) fn subtract(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { pub(crate) fn subtract(
Err(JsNativeError::range() this: &JsValue,
.with_message("not yet implemented.") args: &[JsValue],
.into()) context: &mut Context,
) -> JsResult<JsValue> {
// 1.Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
let duration = this
.as_object()
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("this value must be a Duration object.")
})?;
let other = to_temporal_duration_record(args.get_or_undefined(0), context)?;
// 3. Return ? AddDurations(add, duration, other).
create_temporal_duration(duration.inner.subtract(&other)?, None, context).map(Into::into)
} }
/// 7.3.20 `Temporal.Duration.prototype.round ( roundTo )` /// 7.3.20 `Temporal.Duration.prototype.round ( roundTo )`

19
core/engine/src/builtins/temporal/mod.rs

@ -34,7 +34,7 @@ use crate::{
realm::Realm, realm::Realm,
string::StaticJsStrings, string::StaticJsStrings,
value::Type, value::Type,
Context, JsBigInt, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
}; };
use boa_macros::js_str; use boa_macros::js_str;
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -250,15 +250,18 @@ pub(crate) fn to_relative_temporal_object(
) -> RelativeTemporalObjectResult { ) -> RelativeTemporalObjectResult {
let relative_to = options.get(PropertyKey::from(js_str!("relativeTo")), context)?; let relative_to = options.get(PropertyKey::from(js_str!("relativeTo")), context)?;
let plain_date = match relative_to { let plain_date = match relative_to {
JsValue::String(relative_to_str) => Some(relative_to_str.into()), JsValue::String(relative_to_str) => JsValue::from(relative_to_str),
JsValue::Object(relative_to_obj) => Some(relative_to_obj.into()), JsValue::Object(relative_to_obj) => JsValue::from(relative_to_obj),
_ => None, _ => {
} return Err(JsNativeError::typ()
.map(|plane_date| Ok::<_, JsError>(to_temporal_date(&plane_date, None, context)?.inner)) .with_message("Invalid type for converting to relativeTo object")
.transpose()?; .into())
}
};
let plain_date = to_temporal_date(&plain_date, None, context)?;
// TODO: Implement TemporalZonedDateTime conversion when ZonedDateTime is implemented // TODO: Implement TemporalZonedDateTime conversion when ZonedDateTime is implemented
Ok((plain_date, None)) Ok((Some(plain_date), None))
} }
// 13.22 `LargerOfTwoTemporalUnits ( u1, u2 )` // 13.22 `LargerOfTwoTemporalUnits ( u1, u2 )`

85
core/engine/src/builtins/temporal/plain_date/mod.rs

@ -29,8 +29,8 @@ use temporal_rs::{
}; };
use super::{ use super::{
calendar, create_temporal_duration, options::get_difference_settings, calendar, create_temporal_datetime, create_temporal_duration, options::get_difference_settings,
to_temporal_duration_record, PlainDateTime, ZonedDateTime, to_temporal_duration_record, to_temporal_time, PlainDateTime, ZonedDateTime,
}; };
/// The `Temporal.PlainDate` object. /// The `Temporal.PlainDate` object.
@ -212,6 +212,7 @@ impl IntrinsicObject for PlainDate {
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.static_method(Self::from, js_string!("from"), 2)
.method(Self::to_plain_year_month, js_string!("toPlainYearMonth"), 0) .method(Self::to_plain_year_month, js_string!("toPlainYearMonth"), 0)
.method(Self::to_plain_month_day, js_string!("toPlainMonthDay"), 0) .method(Self::to_plain_month_day, js_string!("toPlainMonthDay"), 0)
.method(Self::get_iso_fields, js_string!("getISOFields"), 0) .method(Self::get_iso_fields, js_string!("getISOFields"), 0)
@ -222,6 +223,7 @@ impl IntrinsicObject for PlainDate {
.method(Self::until, js_string!("until"), 2) .method(Self::until, js_string!("until"), 2)
.method(Self::since, js_string!("since"), 2) .method(Self::since, js_string!("since"), 2)
.method(Self::equals, js_string!("equals"), 1) .method(Self::equals, js_string!("equals"), 1)
.method(Self::to_plain_datetime, js_string!("toPlainDateTime"), 1)
.build(); .build();
} }
@ -276,7 +278,7 @@ impl PlainDate {
JsNativeError::typ().with_message("the this object must be a PlainDate object.") JsNativeError::typ().with_message("the this object must be a PlainDate object.")
})?; })?;
Ok(JsString::from(date.inner.calendar().identifier()?).into()) Ok(JsString::from(date.inner.calendar().identifier()).into())
} }
/// 3.3.4 get `Temporal.PlainDate.prototype.year` /// 3.3.4 get `Temporal.PlainDate.prototype.year`
@ -483,6 +485,28 @@ impl PlainDate {
} }
} }
// ==== `PlainDate` method implementations ====
impl PlainDate {
fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let item = args.get_or_undefined(0);
let options = args.get(1);
if let Some(date) = item.as_object().and_then(JsObject::downcast_ref::<Self>) {
let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?;
let _ = get_option::<ArithmeticOverflow>(&options, js_str!("overflow"), context)?;
return create_temporal_date(date.inner.clone(), None, context).map(Into::into);
}
create_temporal_date(
to_temporal_date(item, options.cloned(), context)?,
None,
context,
)
.map(Into::into)
}
}
// ==== `PlainDate.prototype` method implementation ==== // ==== `PlainDate.prototype` method implementation ====
impl PlainDate { impl PlainDate {
@ -514,7 +538,7 @@ impl PlainDate {
// 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", temporalDate.[[Calendar]]). // 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", temporalDate.[[Calendar]]).
fields.create_data_property_or_throw( fields.create_data_property_or_throw(
js_str!("calendar"), js_str!("calendar"),
JsString::from(date.inner.calendar().identifier()?), JsString::from(date.inner.calendar().identifier()),
context, context,
)?; )?;
// 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", 𝔽(temporalDate.[[ISODay]])). // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", 𝔽(temporalDate.[[ISODay]])).
@ -606,8 +630,7 @@ impl PlainDate {
let options = get_options_object(args.get_or_undefined(1))?; let options = get_options_object(args.get_or_undefined(1))?;
let settings = get_difference_settings(&options, context)?; let settings = get_difference_settings(&options, context)?;
create_temporal_duration(date.inner.until(&other.inner, settings)?, None, context) create_temporal_duration(date.inner.until(&other, settings)?, None, context).map(Into::into)
.map(Into::into)
} }
fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
@ -626,14 +649,44 @@ impl PlainDate {
let options = get_options_object(args.get_or_undefined(1))?; let options = get_options_object(args.get_or_undefined(1))?;
let settings = get_difference_settings(&options, context)?; let settings = get_difference_settings(&options, context)?;
create_temporal_duration(date.inner.since(&other.inner, settings)?, None, context) create_temporal_duration(date.inner.since(&other, settings)?, None, context).map(Into::into)
.map(Into::into)
} }
fn equals(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::error() let date = this
.with_message("not yet implemented.") .as_object()
.into()) .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDate object.")
})?;
let other = to_temporal_date(args.get_or_undefined(0), None, context)?;
Ok((date.inner == other).into())
}
/// 3.3.30 `Temporal.PlainDate.prototype.toPlainDateTime ( [ temporalTime ] )`
fn to_plain_datetime(
this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
let date = this
.as_object()
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDate object.")
})?;
// 3. Set temporalTime to ? ToTemporalTimeOrMidnight(temporalTime).
let time = args
.first()
.map(|v| to_temporal_time(v, None, context))
.transpose()?;
// 4. Return ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], temporalDate.[[Calendar]]).
create_temporal_datetime(date.inner.to_date_time(time)?, None, context).map(Into::into)
} }
} }
@ -699,7 +752,7 @@ pub(crate) fn to_temporal_date(
item: &JsValue, item: &JsValue,
options: Option<JsValue>, options: Option<JsValue>,
context: &mut Context, context: &mut Context,
) -> JsResult<PlainDate> { ) -> JsResult<InnerDate> {
// 1. If options is not present, set options to undefined. // 1. If options is not present, set options to undefined.
let options = options.unwrap_or(JsValue::undefined()); let options = options.unwrap_or(JsValue::undefined());
@ -711,7 +764,7 @@ pub(crate) fn to_temporal_date(
if let Some(object) = item.as_object() { if let Some(object) = item.as_object() {
// a. If item has an [[InitializedTemporalDate]] internal slot, then // a. If item has an [[InitializedTemporalDate]] internal slot, then
if let Some(date) = object.downcast_ref::<PlainDate>() { if let Some(date) = object.downcast_ref::<PlainDate>() {
return Ok(PlainDate::new(date.inner.clone())); return Ok(date.inner.clone());
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
} else if let Some(data) = object.downcast_ref::<ZonedDateTime>() { } else if let Some(data) = object.downcast_ref::<ZonedDateTime>() {
return Err(JsNativeError::range() return Err(JsNativeError::range()
@ -731,7 +784,7 @@ pub(crate) fn to_temporal_date(
let date = InnerDate::from_datetime(date_time.inner()); let date = InnerDate::from_datetime(date_time.inner());
// ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]). // ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]).
return Ok(PlainDate::new(date)); return Ok(date);
} }
// d. Let calendar be ? GetTemporalCalendarSlotValueWithISODefault(item). // d. Let calendar be ? GetTemporalCalendarSlotValueWithISODefault(item).
@ -763,5 +816,5 @@ pub(crate) fn to_temporal_date(
.parse::<InnerDate>() .parse::<InnerDate>()
.map_err(|err| JsNativeError::range().with_message(err.to_string()))?; .map_err(|err| JsNativeError::range().with_message(err.to_string()))?;
Ok(PlainDate::new(result)) Ok(result)
} }

134
core/engine/src/builtins/temporal/plain_date_time/mod.rs

@ -31,7 +31,7 @@ use temporal_rs::{
options::ArithmeticOverflow, options::ArithmeticOverflow,
}; };
use super::to_temporal_duration_record; use super::{to_temporal_duration_record, PlainDate, ZonedDateTime};
/// The `Temporal.PlainDateTime` object. /// The `Temporal.PlainDateTime` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)] #[derive(Debug, Clone, Trace, Finalize, JsData)]
@ -271,6 +271,7 @@ impl IntrinsicObject for PlainDateTime {
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.static_method(Self::from, js_string!("from"), 2)
.method(Self::add, js_string!("add"), 2) .method(Self::add, js_string!("add"), 2)
.method(Self::subtract, js_string!("subtract"), 2) .method(Self::subtract, js_string!("subtract"), 2)
.build(); .build();
@ -363,7 +364,7 @@ impl PlainDateTime {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?; })?;
Ok(JsString::from(dt.inner.calendar().identifier()?).into()) Ok(JsString::from(dt.inner.calendar().identifier()).into())
} }
/// 5.3.4 get `Temporal.PlainDatedt.prototype.year` /// 5.3.4 get `Temporal.PlainDatedt.prototype.year`
@ -621,7 +622,33 @@ impl PlainDateTime {
} }
} }
// ==== PlainDateTime method implementations ==== // ==== PlainDateTime method implemenations ====
impl PlainDateTime {
fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let item = args.get_or_undefined(0);
// 1. Set options to ? GetOptionsObject(options).
let options = args.get(1);
// 2. If item is an Object and item has an [[InitializedTemporalDateTime]] internal slot, then
let dt = if let Some(pdt) = item.as_object().and_then(JsObject::downcast_ref::<Self>) {
// a. Perform ? GetTemporalOverflowOption(options).
let options = get_options_object(args.get_or_undefined(1))?;
let _ = get_option::<ArithmeticOverflow>(&options, js_str!("overflow"), context)?;
// b. Return ! CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]],
// item.[[ISODay]], item.[[ISOHour]], item.[[ISOMinute]], item.[[ISOSecond]],
// item.[[ISOMillisecond]], item.[[ISOMicrosecond]], item.[[ISONanosecond]],
// item.[[Calendar]]).
pdt.inner.clone()
} else {
to_temporal_datetime(item, options.cloned(), context)?
};
// 3. Return ? ToTemporalDateTime(item, options).
create_temporal_datetime(dt, None, context).map(Into::into)
}
}
// ==== PlainDateTime.prototype method implementations ====
impl PlainDateTime { impl PlainDateTime {
fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
@ -669,6 +696,30 @@ impl PlainDateTime {
create_temporal_datetime(dt.inner.subtract(&duration, overflow)?, None, context) create_temporal_datetime(dt.inner.subtract(&duration, overflow)?, None, context)
.map(Into::into) .map(Into::into)
} }
fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let dt = this
.as_object()
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
// 3. Set other to ? ToTemporalDateTime(other).
let other = to_temporal_datetime(args.get_or_undefined(0), None, context)?;
// 4. Let result be CompareISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]],
// dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]],
// dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]],
// dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]],
// other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]],
// other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
// 5. If result is not 0, return false.
// 6. Return ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]).
Ok((dt.inner == other).into())
}
} }
// ==== `PlainDateTime` Abstract Operations` ==== // ==== `PlainDateTime` Abstract Operations` ====
@ -719,3 +770,80 @@ pub(crate) fn create_temporal_datetime(
// 16. Return object. // 16. Return object.
Ok(obj) Ok(obj)
} }
pub(crate) fn to_temporal_datetime(
value: &JsValue,
options: Option<JsValue>,
context: &mut Context,
) -> JsResult<InnerDateTime> {
// 1. If options is not present, set options to undefined.
let options = get_options_object(&options.unwrap_or(JsValue::undefined()))?;
// 2. Let resolvedOptions be ? SnapshotOwnProperties(! GetOptionsObject(options), null).
// 3. If item is an Object, then
if let Some(object) = value.as_object() {
// a. If item has an [[InitializedTemporalDateTime]] internal slot, then
if let Some(dt) = object.downcast_ref::<PlainDateTime>() {
// i. Return item.
return Ok(dt.inner.clone());
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
} else if let Some(_zdt) = object.downcast_ref::<ZonedDateTime>() {
// i. Perform ? GetTemporalOverflowOption(resolvedOptions).
let _ = get_option::<ArithmeticOverflow>(&options, js_str!("overflow"), context)?;
// ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
// iii. Let timeZoneRec be ? CreateTimeZoneMethodsRecord(item.[[TimeZone]], « get-offset-nanoseconds-for »).
// iv. Return ? GetPlainDateTimeFor(timeZoneRec, instant, item.[[Calendar]]).
return Err(JsNativeError::range()
.with_message("Not yet implemented.")
.into());
// c. If item has an [[InitializedTemporalDate]] internal slot, then
} else if let Some(date) = object.downcast_ref::<PlainDate>() {
// i. Perform ? GetTemporalOverflowOption(resolvedOptions).
let _ = get_option::<ArithmeticOverflow>(&options, js_str!("overflow"), context)?;
// ii. Return ? CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], 0, 0, 0, 0, 0, 0, item.[[Calendar]]).
return Ok(InnerDateTime::new(
date.inner.iso_year(),
date.inner.iso_month().into(),
date.inner.iso_day().into(),
0,
0,
0,
0,
0,
0,
date.inner.calendar().clone(),
)?);
}
// d. Let calendar be ? GetTemporalCalendarSlotValueWithISODefault(item).
// e. Let calendarRec be ? CreateCalendarMethodsRecord(calendar, « date-from-fields, fields »).
// f. Let fields be ? PrepareCalendarFields(calendarRec, item, « "day", "month",
// "monthCode", "year" », « "hour", "microsecond", "millisecond", "minute",
// "nanosecond", "second" », «»).
// g. Let result be ? InterpretTemporalDateTimeFields(calendarRec, fields, resolvedOptions).
// TODO: Implement d-g.
return Err(JsNativeError::range()
.with_message("Not yet implemented.")
.into());
}
// 4. Else,
// a. If item is not a String, throw a TypeError exception.
let Some(string) = value.as_string() else {
return Err(JsNativeError::typ()
.with_message("Cannot convert unrecognized value to PlainDateTime.")
.into());
};
// b. Let result be ? ParseTemporalDateTimeString(item).
// c. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
// d. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]],
// result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
// e. Let calendar be result.[[Calendar]].
// f. If calendar is empty, set calendar to "iso8601".
// g. If IsBuiltinCalendar(calendar) is false, throw a RangeError exception.
// h. Set calendar to CanonicalizeUValue("ca", calendar).
let date = string.to_std_string_escaped().parse::<InnerDateTime>()?;
// i. Perform ? GetTemporalOverflowOption(resolvedOptions).
let _ = get_option::<ArithmeticOverflow>(&options, js_str!("overflow"), context)?;
// 5. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]],
// result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]],
// result.[[Microsecond]], result.[[Nanosecond]], calendar).
Ok(date)
}

124
core/engine/src/builtins/temporal/plain_time/mod.rs

@ -23,7 +23,7 @@ use temporal_rs::{
use super::{ use super::{
options::{get_temporal_unit, TemporalUnitGroup}, options::{get_temporal_unit, TemporalUnitGroup},
to_integer_with_truncation, to_temporal_duration_record, to_integer_with_truncation, to_temporal_duration_record, PlainDateTime, ZonedDateTime,
}; };
/// The `Temporal.PlainTime` object. /// The `Temporal.PlainTime` object.
@ -75,7 +75,7 @@ impl IntrinsicObject for PlainTime {
js_string!("hour"), js_string!("hour"),
Some(get_hour), Some(get_hour),
None, None,
Attribute::default(), Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
js_string!("minute"), js_string!("minute"),
@ -107,9 +107,11 @@ impl IntrinsicObject for PlainTime {
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.static_method(Self::from, js_string!("from"), 2)
.method(Self::add, js_string!("add"), 1) .method(Self::add, js_string!("add"), 1)
.method(Self::subtract, js_string!("subtract"), 1) .method(Self::subtract, js_string!("subtract"), 1)
.method(Self::round, js_string!("round"), 1) .method(Self::round, js_string!("round"), 1)
.method(Self::equals, js_string!("equals"), 1)
.method(Self::get_iso_fields, js_string!("getISOFields"), 0) .method(Self::get_iso_fields, js_string!("getISOFields"), 0)
.method(Self::value_of, js_string!("valueOf"), 0) .method(Self::value_of, js_string!("valueOf"), 0)
.build(); .build();
@ -287,6 +289,36 @@ impl PlainTime {
// ==== PlainTime method implementations ==== // ==== PlainTime method implementations ====
impl PlainTime {
fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let item = args.get_or_undefined(0);
// 1. Set options to ? GetOptionsObject(options).
// 2. Let overflow be ? GetTemporalOverflowOption(options).
let overflow = get_option::<ArithmeticOverflow>(
&get_options_object(args.get_or_undefined(1))?,
js_str!("overflow"),
context,
)?;
// 3. If item is an Object and item has an [[InitializedTemporalTime]] internal slot, then
let time = if let Some(time) = item
.as_object()
.and_then(JsObject::downcast_ref::<PlainTime>)
{
// a. Return ! CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]],
// item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]],
// item.[[ISONanosecond]]).
time.inner
} else {
to_temporal_time(item, overflow, context)?
};
// 4. Return ? ToTemporalTime(item, overflow).
create_temporal_time(time, None, context).map(Into::into)
}
}
// ==== PlainTime.prototype method implementations ====
impl PlainTime { impl PlainTime {
/// 4.3.9 Temporal.PlainTime.prototype.add ( temporalDurationLike ) /// 4.3.9 Temporal.PlainTime.prototype.add ( temporalDurationLike )
fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
@ -394,6 +426,29 @@ impl PlainTime {
create_temporal_time(result, None, context).map(Into::into) create_temporal_time(result, None, context).map(Into::into)
} }
/// 4.3.15 Temporal.PlainTime.prototype.equals ( other )
fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
let time = this
.as_object()
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainTime object.")
})?;
// 3. Set other to ? ToTemporalTime(other).
let other = to_temporal_time(args.get_or_undefined(0), None, context)?;
// 4. If temporalTime.[[ISOHour]] ≠ other.[[ISOHour]], return false.
// 5. If temporalTime.[[ISOMinute]] ≠ other.[[ISOMinute]], return false.
// 6. If temporalTime.[[ISOSecond]] ≠ other.[[ISOSecond]], return false.
// 7. If temporalTime.[[ISOMillisecond]] ≠ other.[[ISOMillisecond]], return false.
// 8. If temporalTime.[[ISOMicrosecond]] ≠ other.[[ISOMicrosecond]], return false.
// 9. If temporalTime.[[ISONanosecond]] ≠ other.[[ISONanosecond]], return false.
// 10. Return true.
Ok((time.inner == other).into())
}
/// 4.3.18 Temporal.PlainTime.prototype.getISOFields ( ) /// 4.3.18 Temporal.PlainTime.prototype.getISOFields ( )
fn get_iso_fields(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_iso_fields(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
// 1. Let temporalTime be the this value. // 1. Let temporalTime be the this value.
@ -484,3 +539,68 @@ pub(crate) fn create_temporal_time(
// 10. Return object. // 10. Return object.
Ok(obj) Ok(obj)
} }
pub(crate) fn to_temporal_time(
value: &JsValue,
_overflow: Option<ArithmeticOverflow>,
_context: &mut Context,
) -> JsResult<Time> {
// 1.If overflow is not present, set overflow to "constrain".
// 2. If item is an Object, then
match value {
JsValue::Object(object) => {
// a. If item has an [[InitializedTemporalTime]] internal slot, then
if let Some(time) = object.downcast_ref::<PlainTime>() {
// i. Return item.
return Ok(time.inner);
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
} else if let Some(_zdt) = object.downcast_ref::<ZonedDateTime>() {
// i. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
// ii. Let timeZoneRec be ? CreateTimeZoneMethodsRecord(item.[[TimeZone]], « get-offset-nanoseconds-for »).
// iii. Let plainDateTime be ? GetPlainDateTimeFor(timeZoneRec, instant, item.[[Calendar]]).
// iv. Return ! CreateTemporalTime(plainDateTime.[[ISOHour]], plainDateTime.[[ISOMinute]],
// plainDateTime.[[ISOSecond]], plainDateTime.[[ISOMillisecond]], plainDateTime.[[ISOMicrosecond]],
// plainDateTime.[[ISONanosecond]]).
return Err(JsNativeError::range()
.with_message("Not yet implemented.")
.into());
// c. If item has an [[InitializedTemporalDateTime]] internal slot, then
} else if let Some(dt) = object.downcast_ref::<PlainDateTime>() {
// i. Return ! CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]],
// item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]],
// item.[[ISONanosecond]]).
return Ok(Time::new(
dt.inner.hour().into(),
dt.inner.minute().into(),
dt.inner.second().into(),
dt.inner.millisecond().into(),
dt.inner.microsecond().into(),
dt.inner.nanosecond().into(),
ArithmeticOverflow::Reject,
)?);
}
// d. Let result be ? ToTemporalTimeRecord(item).
// e. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]],
// result.[[Second]], result.[[Millisecond]], result.[[Microsecond]],
// result.[[Nanosecond]], overflow).
Err(JsNativeError::range()
.with_message("Not yet implemented.")
.into())
}
// 3. Else,
JsValue::String(_str) => {
// b. Let result be ? ParseTemporalTimeString(item).
// c. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
// TODO: Add time parsing to `temporal_rs`
Err(JsNativeError::typ()
.with_message("Invalid value for converting to PlainTime.")
.into())
}
// a. If item is not a String, throw a TypeError exception.
_ => Err(JsNativeError::typ()
.with_message("Invalid value for converting to PlainTime.")
.into()),
}
// 4. Return ! CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
}

Loading…
Cancel
Save