Browse Source

Update Temporal rounding and implement additional methods (#3892)

* Impl methods and update temporal for new rounding

* implement PlainDate until and since methods

* Bump temporal-rs commit
pull/3897/head
Kevin Ness 4 months ago committed by GitHub
parent
commit
fc2a6e0996
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      Cargo.lock
  2. 2
      Cargo.toml
  3. 25
      core/engine/src/builtins/temporal/duration/mod.rs
  4. 31
      core/engine/src/builtins/temporal/instant/mod.rs
  5. 20
      core/engine/src/builtins/temporal/options.rs
  6. 135
      core/engine/src/builtins/temporal/plain_date/mod.rs
  7. 333
      core/engine/src/builtins/temporal/plain_date_time/mod.rs

2
Cargo.lock generated

@ -3201,7 +3201,7 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]] [[package]]
name = "temporal_rs" name = "temporal_rs"
version = "0.0.2" version = "0.0.2"
source = "git+https://github.com/boa-dev/temporal.git?rev=c658ac7db4701822cc179d04b56bb9c8fb7e954c#c658ac7db4701822cc179d04b56bb9c8fb7e954c" source = "git+https://github.com/boa-dev/temporal.git?rev=cf70b50f90865d2c00fb994b7adf8b9e1bd837b8#cf70b50f90865d2c00fb994b7adf8b9e1bd837b8"
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 = "c658ac7db4701822cc179d04b56bb9c8fb7e954c" } temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "cf70b50f90865d2c00fb994b7adf8b9e1bd837b8" }
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"

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

@ -18,7 +18,7 @@ use boa_macros::js_str;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use temporal_rs::{ use temporal_rs::{
components::Duration as InnerDuration, components::Duration as InnerDuration,
options::{RelativeTo, RoundingIncrement, TemporalRoundingMode, TemporalUnit}, options::{RelativeTo, RoundingIncrement, RoundingOptions, TemporalRoundingMode, TemporalUnit},
}; };
use super::{ use super::{
@ -389,7 +389,7 @@ impl Duration {
// 3. Return 𝔽(! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], // 3. Return 𝔽(! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]],
// duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], // duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]],
// duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]])). // duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]])).
Ok(duration.inner.sign().into()) Ok((duration.inner.sign() as i8).into())
} }
/// 7.3.14 get Temporal.Duration.prototype.blank /// 7.3.14 get Temporal.Duration.prototype.blank
@ -640,10 +640,11 @@ impl Duration {
// NOTE: 6 & 7 unused in favor of `is_none()`. // NOTE: 6 & 7 unused in favor of `is_none()`.
// 6. Let smallestUnitPresent be true. // 6. Let smallestUnitPresent be true.
// 7. Let largestUnitPresent be true. // 7. Let largestUnitPresent be true.
let mut options = RoundingOptions::default();
// 8. NOTE: The following steps read options and perform independent validation in alphabetical order (ToRelativeTemporalObject reads "relativeTo", ToTemporalRoundingIncrement reads "roundingIncrement" and ToTemporalRoundingMode reads "roundingMode"). // 8. NOTE: The following steps read options and perform independent validation in alphabetical order (ToRelativeTemporalObject reads "relativeTo", ToTemporalRoundingIncrement reads "roundingIncrement" and ToTemporalRoundingMode reads "roundingMode").
// 9. Let largestUnit be ? GetTemporalUnit(roundTo, "largestUnit", datetime, undefined, « "auto" »). // 9. Let largestUnit be ? GetTemporalUnit(roundTo, "largestUnit", datetime, undefined, « "auto" »).
let largest_unit = get_temporal_unit( options.largest_unit = get_temporal_unit(
&round_to, &round_to,
js_str!("largestUnit"), js_str!("largestUnit"),
TemporalUnitGroup::DateTime, TemporalUnitGroup::DateTime,
@ -658,15 +659,15 @@ impl Duration {
super::to_relative_temporal_object(&round_to, context)?; super::to_relative_temporal_object(&round_to, context)?;
// 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo). // 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo).
let rounding_increment = options.increment =
get_option::<RoundingIncrement>(&round_to, js_str!("roundingIncrement"), context)?; get_option::<RoundingIncrement>(&round_to, js_str!("roundingIncrement"), context)?;
// 14. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). // 14. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
let rounding_mode = options.rounding_mode =
get_option::<TemporalRoundingMode>(&round_to, js_str!("roundingMode"), context)?; get_option::<TemporalRoundingMode>(&round_to, js_str!("roundingMode"), context)?;
// 15. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", datetime, undefined). // 15. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", datetime, undefined).
let smallest_unit = get_temporal_unit( options.smallest_unit = get_temporal_unit(
&round_to, &round_to,
js_str!("smallestUnit"), js_str!("smallestUnit"),
TemporalUnitGroup::DateTime, TemporalUnitGroup::DateTime,
@ -676,17 +677,9 @@ impl Duration {
// NOTE: execute step 21 earlier before initial values are shadowed. // NOTE: execute step 21 earlier before initial values are shadowed.
// 21. If smallestUnitPresent is false and largestUnitPresent is false, then // 21. If smallestUnitPresent is false and largestUnitPresent is false, then
if smallest_unit.is_none() && largest_unit.is_none() {
// a. Throw a RangeError exception.
return Err(JsNativeError::range()
.with_message("smallestUnit or largestUnit must be present.")
.into());
}
let rounded_duration = duration.inner.round( let rounded_duration = duration.inner.round(
rounding_increment, options,
smallest_unit,
largest_unit,
rounding_mode,
&RelativeTo { &RelativeTo {
date: plain_relative_to.as_ref(), date: plain_relative_to.as_ref(),
zdt: zoned_relative_to.as_ref(), zdt: zoned_relative_to.as_ref(),

31
core/engine/src/builtins/temporal/instant/mod.rs

@ -21,10 +21,9 @@ use crate::{
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use boa_macros::js_str; use boa_macros::js_str;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use temporal_rs::{ use temporal_rs::{components::Instant as InnerInstant, options::TemporalRoundingMode};
components::Instant as InnerInstant,
options::{RoundingIncrement, TemporalRoundingMode, TemporalUnit}, use super::options::get_difference_settings;
};
/// The `Temporal.Instant` object. /// The `Temporal.Instant` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)] #[derive(Debug, Clone, Trace, Finalize, JsData)]
@ -285,15 +284,9 @@ impl Instant {
let other = to_temporal_instant(args.get_or_undefined(0))?; let other = to_temporal_instant(args.get_or_undefined(0))?;
// Fetch the necessary options. // Fetch the necessary options.
let options = get_options_object(args.get_or_undefined(1))?; let settings =
let mode = get_option::<TemporalRoundingMode>(&options, js_str!("roundingMode"), context)?; get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?;
let increment = let result = instant.inner.until(&other, settings)?;
get_option::<RoundingIncrement>(&options, js_str!("roundingIncrement"), context)?;
let smallest_unit = get_option::<TemporalUnit>(&options, js_str!("smallestUnit"), context)?;
let largest_unit = get_option::<TemporalUnit>(&options, js_str!("largestUnit"), context)?;
let result = instant
.inner
.until(&other, mode, increment, smallest_unit, largest_unit)?;
create_temporal_duration(result.into(), None, context).map(Into::into) create_temporal_duration(result.into(), None, context).map(Into::into)
} }
@ -314,15 +307,9 @@ impl Instant {
// 3. Return ? DifferenceTemporalInstant(since, instant, other, options). // 3. Return ? DifferenceTemporalInstant(since, instant, other, options).
let other = to_temporal_instant(args.get_or_undefined(0))?; let other = to_temporal_instant(args.get_or_undefined(0))?;
let options = get_options_object(args.get_or_undefined(1))?; let settings =
let mode = get_option::<TemporalRoundingMode>(&options, js_str!("roundingMode"), context)?; get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?;
let increment = let result = instant.inner.since(&other, settings)?;
get_option::<RoundingIncrement>(&options, js_str!("roundingIncrement"), context)?;
let smallest_unit = get_option::<TemporalUnit>(&options, js_str!("smallestUnit"), context)?;
let largest_unit = get_option::<TemporalUnit>(&options, js_str!("largestUnit"), context)?;
let result = instant
.inner
.since(&other, mode, increment, smallest_unit, largest_unit)?;
create_temporal_duration(result.into(), None, context).map(Into::into) create_temporal_duration(result.into(), None, context).map(Into::into)
} }

20
core/engine/src/builtins/temporal/options.rs

@ -13,9 +13,10 @@ use crate::{
string::JsStr, string::JsStr,
Context, JsNativeError, JsObject, JsResult, JsValue, Context, JsNativeError, JsObject, JsResult, JsValue,
}; };
use boa_macros::js_str;
use temporal_rs::options::{ use temporal_rs::options::{
ArithmeticOverflow, DurationOverflow, InstantDisambiguation, OffsetDisambiguation, ArithmeticOverflow, DifferenceSettings, DurationOverflow, InstantDisambiguation,
RoundingIncrement, TemporalRoundingMode, TemporalUnit, OffsetDisambiguation, RoundingIncrement, TemporalRoundingMode, TemporalUnit,
}; };
// TODO: Expand docs on the below options. // TODO: Expand docs on the below options.
@ -46,6 +47,21 @@ pub(crate) fn get_temporal_unit(
Ok(unit) Ok(unit)
} }
#[inline]
pub(crate) fn get_difference_settings(
options: &JsObject,
context: &mut Context,
) -> JsResult<DifferenceSettings> {
let mut settings = DifferenceSettings::default();
settings.rounding_mode =
get_option::<TemporalRoundingMode>(options, js_str!("roundingMode"), context)?;
settings.increment =
get_option::<RoundingIncrement>(options, js_str!("roundingIncrement"), context)?;
settings.smallest_unit = get_option::<TemporalUnit>(options, js_str!("smallestUnit"), context)?;
settings.largest_unit = get_option::<TemporalUnit>(options, js_str!("largestUnit"), context)?;
Ok(settings)
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[allow(unused)] #[allow(unused)]
pub(crate) enum TemporalUnitGroup { pub(crate) enum TemporalUnitGroup {

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

@ -28,7 +28,10 @@ use temporal_rs::{
options::ArithmeticOverflow, options::ArithmeticOverflow,
}; };
use super::{calendar, PlainDateTime, ZonedDateTime}; use super::{
calendar, create_temporal_duration, options::get_difference_settings,
to_temporal_duration_record, PlainDateTime, ZonedDateTime,
};
/// The `Temporal.PlainDate` object. /// The `Temporal.PlainDate` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)] #[derive(Debug, Clone, Trace, Finalize, JsData)]
@ -495,22 +498,84 @@ impl PlainDate {
.into()) .into())
} }
fn get_iso_fields(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn get_iso_fields(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::error() // 1. Let temporalDate be the this value.
.with_message("not yet implemented.") // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
.into()) 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. Let fields be OrdinaryObjectCreate(%Object.prototype%).
let fields = JsObject::with_object_proto(context.intrinsics());
// 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", temporalDate.[[Calendar]]).
fields.create_data_property_or_throw(
js_str!("calendar"),
JsString::from(date.inner.calendar().identifier()?),
context,
)?;
// 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", 𝔽(temporalDate.[[ISODay]])).
fields.create_data_property_or_throw(js_str!("isoDay"), date.inner.iso_day(), context)?;
// 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", 𝔽(temporalDate.[[ISOMonth]])).
fields.create_data_property_or_throw(
js_str!("isoMonth"),
date.inner.iso_month(),
context,
)?;
// 7. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", 𝔽(temporalDate.[[ISOYear]])).
fields.create_data_property_or_throw(js_str!("isoYear"), date.inner.iso_year(), context)?;
// 8. Return fields.
Ok(fields.into())
} }
fn add(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::error() // 1. Let temporalDate be the this value.
.with_message("not yet implemented.") // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
.into()) 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. Let duration be ? ToTemporalDuration(temporalDurationLike).
let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;
// 4. Set options to ? GetOptionsObject(options).
let options = get_options_object(args.get_or_undefined(1))?;
let overflow = get_option::<ArithmeticOverflow>(&options, js_str!("overflow"), context)?;
// 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)
} }
fn subtract(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::error() // 1. Let temporalDate be the this value.
.with_message("not yet implemented.") // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
.into()) 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. Let duration be ? ToTemporalDuration(temporalDurationLike).
let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;
// 4. Set options to ? GetOptionsObject(options).
let options = get_options_object(args.get_or_undefined(1))?;
let overflow = get_option::<ArithmeticOverflow>(&options, js_str!("overflow"), context)?;
// 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)
} }
fn with(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn with(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
@ -525,16 +590,44 @@ impl PlainDate {
.into()) .into())
} }
fn until(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::error() // 1. Let temporalDate be the this value.
.with_message("not yet implemented.") // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
.into()) 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.")
})?;
let other = to_temporal_date(args.get_or_undefined(0), None, context)?;
// 3. Return ? DifferenceTemporalPlainDate(until, temporalDate, other, options).
let options = get_options_object(args.get_or_undefined(1))?;
let settings = get_difference_settings(&options, context)?;
create_temporal_duration(date.inner.until(&other.inner, settings)?, None, context)
.map(Into::into)
} }
fn since(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::error() // 1. Let temporalDate be the this value.
.with_message("not yet implemented.") // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
.into()) 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. Return ? DifferenceTemporalPlainDate(since, temporalDate, other, options).
let other = to_temporal_date(args.get_or_undefined(0), None, context)?;
let options = get_options_object(args.get_or_undefined(1))?;
let settings = get_difference_settings(&options, context)?;
create_temporal_duration(date.inner.since(&other.inner, settings)?, None, context)
.map(Into::into)
} }
fn equals(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn equals(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {

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

@ -3,6 +3,7 @@
use crate::{ use crate::{
builtins::{ builtins::{
options::{get_option, get_options_object},
temporal::{calendar, to_integer_with_truncation}, temporal::{calendar, to_integer_with_truncation},
BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
}, },
@ -27,8 +28,11 @@ use temporal_rs::{
DateTime as InnerDateTime, DateTime as InnerDateTime,
}, },
iso::{IsoDate, IsoDateSlots}, iso::{IsoDate, IsoDateSlots},
options::ArithmeticOverflow,
}; };
use super::to_temporal_duration_record;
/// The `Temporal.PlainDateTime` object. /// The `Temporal.PlainDateTime` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)] #[derive(Debug, Clone, Trace, Finalize, JsData)]
#[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerDateTime` could contain `Trace` types. #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerDateTime` could contain `Trace` types.
@ -267,6 +271,8 @@ impl IntrinsicObject for PlainDateTime {
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.method(Self::add, js_string!("add"), 2)
.method(Self::subtract, js_string!("subtract"), 2)
.build(); .build();
} }
@ -290,7 +296,7 @@ impl BuiltInConstructor for PlainDateTime {
if new_target.is_undefined() { if new_target.is_undefined() {
// a. Throw a TypeError exception. // a. Throw a TypeError exception.
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("NewTarget cannot be undefined when contructing PlainDateTime.") .with_message("NewTarget cannot be undefined when contructing PlainDatedt.")
.into()); .into());
}; };
@ -348,309 +354,320 @@ impl BuiltInConstructor for PlainDateTime {
// ==== `PlainDateTimeTime` accessor implmentations ==== // ==== `PlainDateTimeTime` accessor implmentations ====
impl PlainDateTime { impl PlainDateTime {
/// 5.3.3 get `Temporal.PlainDateTime.prototype.calendarId` /// 5.3.3 get `Temporal.PlainDatedt.prototype.calendarId`
fn get_calendar_id(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_calendar_id(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let date = this let dt = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| { .ok_or_else(|| {
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(date.inner.calendar().identifier()?).into()) Ok(JsString::from(dt.inner.calendar().identifier()?).into())
} }
/// 5.3.4 get `Temporal.PlainDateTime.prototype.year` /// 5.3.4 get `Temporal.PlainDatedt.prototype.year`
fn get_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.year()?.into()) Ok(dt.inner.year()?.into())
} }
/// 5.3.5 get `Temporal.PlainDateTime.prototype.month` /// 5.3.5 get `Temporal.PlainDatedt.prototype.month`
fn get_month(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_month(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.month()?.into()) Ok(dt.inner.month()?.into())
} }
/// 5.3.6 get Temporal.PlainDateTime.prototype.monthCode /// 5.3.6 get Temporal.PlainDatedt.prototype.monthCode
fn get_month_code(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_month_code(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(JsString::from(date.inner.month_code()?.as_str()).into()) Ok(JsString::from(dt.inner.month_code()?.as_str()).into())
} }
/// 5.3.7 get `Temporal.PlainDateTime.prototype.day` /// 5.3.7 get `Temporal.PlainDatedt.prototype.day`
fn get_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.day()?.into()) Ok(dt.inner.day()?.into())
} }
/// 5.3.8 get `Temporal.PlainDateTime.prototype.hour` /// 5.3.8 get `Temporal.PlainDatedt.prototype.hour`
fn get_hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn get_hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
// 1. Let dateTime be the this value. // 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this let dt = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| { .ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?; })?;
// 3. Return 𝔽(dateTime.[[ISOHour]]). // 3. Return 𝔽(datedt.[[ISOHour]]).
Ok(time.inner.hour().into()) Ok(dt.inner.hour().into())
} }
/// 5.3.9 get `Temporal.PlainDateTime.prototype.minute` /// 5.3.9 get `Temporal.PlainDatedt.prototype.minute`
fn get_minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn get_minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
// 1. Let dateTime be the this value. // 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this let dt = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| { .ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?; })?;
// 3. Return 𝔽(dateTime.[[ISOMinute]]). // 3. Return 𝔽(datedt.[[ISOMinute]]).
Ok(time.inner.minute().into()) Ok(dt.inner.minute().into())
} }
/// 5.3.10 get `Temporal.PlainDateTime.prototype.second` /// 5.3.10 get `Temporal.PlainDatedt.prototype.second`
fn get_second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn get_second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
// 1. Let dateTime be the this value. // 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this let dt = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| { .ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?; })?;
// 3. Return 𝔽(dateTime.[[ISOSecond]]). // 3. Return 𝔽(datedt.[[ISOSecond]]).
Ok(time.inner.second().into()) Ok(dt.inner.second().into())
} }
/// 5.3.11 get `Temporal.PlainDateTime.prototype.millisecond` /// 5.3.11 get `Temporal.PlainDatedt.prototype.millisecond`
fn get_millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn get_millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
// 1. Let dateTime be the this value. // 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this let dt = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| { .ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?; })?;
// 3. Return 𝔽(dateTime.[[ISOMillisecond]]). // 3. Return 𝔽(datedt.[[ISOMillisecond]]).
Ok(time.inner.millisecond().into()) Ok(dt.inner.millisecond().into())
} }
/// 5.3.12 get `Temporal.PlainDateTime.prototype.microsecond` /// 5.3.12 get `Temporal.PlainDatedt.prototype.microsecond`
fn get_microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn get_microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
// 1. Let dateTime be the this value. // 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this let dt = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| { .ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?; })?;
// 3. Return 𝔽(dateTime.[[ISOMicrosecond]]). // 3. Return 𝔽(datedt.[[ISOMicrosecond]]).
Ok(time.inner.microsecond().into()) Ok(dt.inner.microsecond().into())
} }
/// 5.3.13 get `Temporal.PlainDateTime.prototype.nanosecond` /// 5.3.13 get `Temporal.PlainDatedt.prototype.nanosecond`
fn get_nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { fn get_nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
// 1. Let dateTime be the this value. // 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this let dt = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| { .ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?; })?;
// 3. Return 𝔽(dateTime.[[ISONanosecond]]). // 3. Return 𝔽(datedt.[[ISONanosecond]]).
Ok(time.inner.nanosecond().into()) Ok(dt.inner.nanosecond().into())
} }
/// 5.3.14 get `Temporal.PlainDateTime.prototype.dayOfWeek` /// 5.3.14 get `Temporal.PlainDatedt.prototype.dayOfWeek`
fn get_day_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_day_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.day_of_week()?.into()) Ok(dt.inner.day_of_week()?.into())
} }
/// 5.3.15 get `Temporal.PlainDateTime.prototype.dayOfYear` /// 5.3.15 get `Temporal.PlainDatedt.prototype.dayOfYear`
fn get_day_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_day_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.day_of_year()?.into()) Ok(dt.inner.day_of_year()?.into())
} }
/// 5.3.16 get `Temporal.PlainDateTime.prototype.weekOfYear` /// 5.3.16 get `Temporal.PlainDatedt.prototype.weekOfYear`
fn get_week_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_week_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.week_of_year()?.into()) Ok(dt.inner.week_of_year()?.into())
} }
/// 5.3.17 get `Temporal.PlainDateTime.prototype.yearOfWeek` /// 5.3.17 get `Temporal.PlainDatedt.prototype.yearOfWeek`
fn get_year_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_year_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.year_of_week()?.into()) Ok(dt.inner.year_of_week()?.into())
} }
/// 5.3.18 get `Temporal.PlainDateTime.prototype.daysInWeek` /// 5.3.18 get `Temporal.PlainDatedt.prototype.daysInWeek`
fn get_days_in_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_days_in_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.days_in_week()?.into()) Ok(dt.inner.days_in_week()?.into())
} }
/// 5.3.19 get `Temporal.PlainDateTime.prototype.daysInMonth` /// 5.3.19 get `Temporal.PlainDatedt.prototype.daysInMonth`
fn get_days_in_month( fn get_days_in_month(
this: &JsValue, this: &JsValue,
_: &[JsValue], _: &[JsValue],
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.days_in_month()?.into()) Ok(dt.inner.days_in_month()?.into())
} }
/// 5.3.20 get `Temporal.PlainDateTime.prototype.daysInYear` /// 5.3.20 get `Temporal.PlainDatedt.prototype.daysInYear`
fn get_days_in_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_days_in_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.days_in_year()?.into()) Ok(dt.inner.days_in_year()?.into())
} }
/// 5.3.21 get `Temporal.PlainDateTime.prototype.monthsInYear` /// 5.3.21 get `Temporal.PlainDatedt.prototype.monthsInYear`
fn get_months_in_year( fn get_months_in_year(
this: &JsValue, this: &JsValue,
_: &[JsValue], _: &[JsValue],
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
let Some(date) = obj.downcast_ref::<Self>() else { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
return Err(JsNativeError::typ() })?;
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(date.inner.months_in_year()?.into()) Ok(dt.inner.months_in_year()?.into())
} }
/// 5.3.22 get `Temporal.PlainDateTime.prototype.inLeapYear` /// 5.3.22 get `Temporal.PlainDatedt.prototype.inLeapYear`
fn get_in_leap_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn get_in_leap_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let obj = this let dt = this
.as_object() .as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; .and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
let Some(date) = obj.downcast_ref::<Self>() else { Ok(dt.inner.in_leap_year()?.into())
return Err(JsNativeError::typ() }
.with_message("the this object must be a PlainDateTime object.") }
.into());
}; // ==== PlainDateTime method implementations ====
impl PlainDateTime {
fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
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. Let duration be ? ToTemporalDuration(temporalDurationLike).
let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;
// 4. Set options to ? GetOptionsObject(options).
let options = get_options_object(args.get_or_undefined(1))?;
let overflow = get_option::<ArithmeticOverflow>(&options, js_str!("overflow"), context)?;
// 5. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).
// 6. Return ? AddDate(calendarRec, temporalDate, duration, options).
create_temporal_datetime(dt.inner.add(&duration, overflow)?, None, context).map(Into::into)
}
fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
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. Let duration be ? ToTemporalDuration(temporalDurationLike).
let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;
// 4. Set options to ? GetOptionsObject(options).
let options = get_options_object(args.get_or_undefined(1))?;
let overflow = get_option::<ArithmeticOverflow>(&options, js_str!("overflow"), context)?;
Ok(date.inner.in_leap_year()?.into()) // 5. Let negatedDuration be CreateNegatedTemporalDuration(duration).
// 6. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).
// 7. Return ? AddDate(calendarRec, temporalDate, negatedDuration, options).
create_temporal_datetime(dt.inner.subtract(&duration, overflow)?, None, context)
.map(Into::into)
} }
} }
@ -680,7 +697,7 @@ pub(crate) fn create_temporal_datetime(
.into() .into()
}; };
// 5. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDateTime.prototype%", « [[InitializedTemporalDateTime]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] »). // 5. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDatedt.prototype%", « [[InitializedTemporalDateTime]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] »).
let prototype = get_prototype_from_constructor( let prototype = get_prototype_from_constructor(
&new_target, &new_target,
StandardConstructors::plain_date_time, StandardConstructors::plain_date_time,

Loading…
Cancel
Save