Skip to content

Commit

Permalink
Update Temporal rounding and implement additional methods (#3892)
Browse files Browse the repository at this point in the history
* Impl methods and update temporal for new rounding

* implement PlainDate until and since methods

* Bump temporal-rs commit
  • Loading branch information
nekevss authored Jul 7, 2024
1 parent 8e76836 commit fc2a6e0
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 221 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
25 changes: 9 additions & 16 deletions core/engine/src/builtins/temporal/duration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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(),
Expand Down
31 changes: 9 additions & 22 deletions core/engine/src/builtins/temporal/instant/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)
}

Expand All @@ -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)
}

Expand Down
20 changes: 18 additions & 2 deletions core/engine/src/builtins/temporal/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down
135 changes: 114 additions & 21 deletions core/engine/src/builtins/temporal/plain_date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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> {
Expand All @@ -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> {
Expand Down
Loading

0 comments on commit fc2a6e0

Please sign in to comment.