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 5 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]]
name = "temporal_rs"
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 = [
"bitflags 2.6.0",
"icu_calendar",

2
Cargo.toml

@ -115,7 +115,7 @@ intrusive-collections = "0.9.6"
cfg-if = "1.0.0"
either = "1.13.0"
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"
criterion = "0.5.1"
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 temporal_rs::{
components::Duration as InnerDuration,
options::{RelativeTo, RoundingIncrement, TemporalRoundingMode, TemporalUnit},
options::{RelativeTo, RoundingIncrement, RoundingOptions, TemporalRoundingMode, TemporalUnit},
};
use super::{
@ -389,7 +389,7 @@ impl Duration {
// 3. Return 𝔽(! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]],
// duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]],
// 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
@ -640,10 +640,11 @@ impl Duration {
// NOTE: 6 & 7 unused in favor of `is_none()`.
// 6. Let smallestUnitPresent 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").
// 9. Let largestUnit be ? GetTemporalUnit(roundTo, "largestUnit", datetime, undefined, « "auto" »).
let largest_unit = get_temporal_unit(
options.largest_unit = get_temporal_unit(
&round_to,
js_str!("largestUnit"),
TemporalUnitGroup::DateTime,
@ -658,15 +659,15 @@ impl Duration {
super::to_relative_temporal_object(&round_to, context)?;
// 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo).
let rounding_increment =
options.increment =
get_option::<RoundingIncrement>(&round_to, js_str!("roundingIncrement"), context)?;
// 14. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
let rounding_mode =
options.rounding_mode =
get_option::<TemporalRoundingMode>(&round_to, js_str!("roundingMode"), context)?;
// 15. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", datetime, undefined).
let smallest_unit = get_temporal_unit(
options.smallest_unit = get_temporal_unit(
&round_to,
js_str!("smallestUnit"),
TemporalUnitGroup::DateTime,
@ -676,17 +677,9 @@ impl Duration {
// NOTE: execute step 21 earlier before initial values are shadowed.
// 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(
rounding_increment,
smallest_unit,
largest_unit,
rounding_mode,
options,
&RelativeTo {
date: plain_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_macros::js_str;
use boa_profiler::Profiler;
use temporal_rs::{
components::Instant as InnerInstant,
options::{RoundingIncrement, TemporalRoundingMode, TemporalUnit},
};
use temporal_rs::{components::Instant as InnerInstant, options::TemporalRoundingMode};
use super::options::get_difference_settings;
/// The `Temporal.Instant` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)]
@ -285,15 +284,9 @@ impl Instant {
let other = to_temporal_instant(args.get_or_undefined(0))?;
// Fetch the necessary options.
let options = get_options_object(args.get_or_undefined(1))?;
let mode = get_option::<TemporalRoundingMode>(&options, js_str!("roundingMode"), context)?;
let increment =
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)?;
let settings =
get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?;
let result = instant.inner.until(&other, settings)?;
create_temporal_duration(result.into(), None, context).map(Into::into)
}
@ -314,15 +307,9 @@ impl Instant {
// 3. Return ? DifferenceTemporalInstant(since, instant, other, options).
let other = to_temporal_instant(args.get_or_undefined(0))?;
let options = get_options_object(args.get_or_undefined(1))?;
let mode = get_option::<TemporalRoundingMode>(&options, js_str!("roundingMode"), context)?;
let increment =
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)?;
let settings =
get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?;
let result = instant.inner.since(&other, settings)?;
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,
Context, JsNativeError, JsObject, JsResult, JsValue,
};
use boa_macros::js_str;
use temporal_rs::options::{
ArithmeticOverflow, DurationOverflow, InstantDisambiguation, OffsetDisambiguation,
RoundingIncrement, TemporalRoundingMode, TemporalUnit,
ArithmeticOverflow, DifferenceSettings, DurationOverflow, InstantDisambiguation,
OffsetDisambiguation, RoundingIncrement, TemporalRoundingMode, TemporalUnit,
};
// TODO: Expand docs on the below options.
@ -46,6 +47,21 @@ pub(crate) fn get_temporal_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)]
#[allow(unused)]
pub(crate) enum TemporalUnitGroup {

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

@ -28,7 +28,10 @@ use temporal_rs::{
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.
#[derive(Debug, Clone, Trace, Finalize, JsData)]
@ -495,22 +498,84 @@ impl PlainDate {
.into())
}
fn get_iso_fields(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::error()
.with_message("not yet implemented.")
.into())
fn get_iso_fields(this: &JsValue, _: &[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. 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> {
Err(JsNativeError::error()
.with_message("not yet implemented.")
.into())
fn add(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. 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> {
Err(JsNativeError::error()
.with_message("not yet implemented.")
.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 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> {
@ -525,16 +590,44 @@ impl PlainDate {
.into())
}
fn until(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::error()
.with_message("not yet implemented.")
.into())
fn until(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.")
})?;
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> {
Err(JsNativeError::error()
.with_message("not yet implemented.")
.into())
fn since(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. 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> {

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

@ -3,6 +3,7 @@
use crate::{
builtins::{
options::{get_option, get_options_object},
temporal::{calendar, to_integer_with_truncation},
BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
},
@ -27,8 +28,11 @@ use temporal_rs::{
DateTime as InnerDateTime,
},
iso::{IsoDate, IsoDateSlots},
options::ArithmeticOverflow,
};
use super::to_temporal_duration_record;
/// The `Temporal.PlainDateTime` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)]
#[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerDateTime` could contain `Trace` types.
@ -267,6 +271,8 @@ impl IntrinsicObject for PlainDateTime {
None,
Attribute::CONFIGURABLE,
)
.method(Self::add, js_string!("add"), 2)
.method(Self::subtract, js_string!("subtract"), 2)
.build();
}
@ -290,7 +296,7 @@ impl BuiltInConstructor for PlainDateTime {
if new_target.is_undefined() {
// a. Throw a TypeError exception.
return Err(JsNativeError::typ()
.with_message("NewTarget cannot be undefined when contructing PlainDateTime.")
.with_message("NewTarget cannot be undefined when contructing PlainDatedt.")
.into());
};
@ -348,309 +354,320 @@ impl BuiltInConstructor for PlainDateTime {
// ==== `PlainDateTimeTime` accessor implmentations ====
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> {
let date = this
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.")
})?;
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this
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. Return 𝔽(dateTime.[[ISOHour]]).
Ok(time.inner.hour().into())
// 3. Return 𝔽(datedt.[[ISOHour]]).
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> {
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this
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. Return 𝔽(dateTime.[[ISOMinute]]).
Ok(time.inner.minute().into())
// 3. Return 𝔽(datedt.[[ISOMinute]]).
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> {
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this
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. Return 𝔽(dateTime.[[ISOSecond]]).
Ok(time.inner.second().into())
// 3. Return 𝔽(datedt.[[ISOSecond]]).
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> {
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this
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. Return 𝔽(dateTime.[[ISOMillisecond]]).
Ok(time.inner.millisecond().into())
// 3. Return 𝔽(datedt.[[ISOMillisecond]]).
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> {
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this
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. Return 𝔽(dateTime.[[ISOMicrosecond]]).
Ok(time.inner.microsecond().into())
// 3. Return 𝔽(datedt.[[ISOMicrosecond]]).
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> {
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
let time = this
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. Return 𝔽(dateTime.[[ISONanosecond]]).
Ok(time.inner.nanosecond().into())
// 3. Return 𝔽(datedt.[[ISONanosecond]]).
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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(
this: &JsValue,
_: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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(
this: &JsValue,
_: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let obj = this
let dt = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?;
let Some(date) = obj.downcast_ref::<Self>() else {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
.and_then(JsObject::downcast_ref::<Self>)
.ok_or_else(|| {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
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> {
let obj = this
let dt = this
.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 {
return Err(JsNativeError::typ()
.with_message("the this object must be a PlainDateTime object.")
.into());
};
Ok(dt.inner.in_leap_year()?.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()
};
// 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(
&new_target,
StandardConstructors::plain_date_time,

Loading…
Cancel
Save