Skip to content

Commit

Permalink
Build out PlainTime builtin
Browse files Browse the repository at this point in the history
  • Loading branch information
nekevss committed Jan 29, 2024
1 parent 9ffbc03 commit 67c8359
Show file tree
Hide file tree
Showing 8 changed files with 590 additions and 58 deletions.
2 changes: 1 addition & 1 deletion core/engine/src/builtins/temporal/calendar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ impl Calendar {

// 5. Set duration to ? ToTemporalDuration(duration).
let duration_like = args.get_or_undefined(1);
let duration = temporal::duration::to_temporal_duration(duration_like)?;
let duration = temporal::duration::to_temporal_duration(duration_like, context)?;

// 6. Set options to ? GetOptionsObject(options).
let options = args.get_or_undefined(2);
Expand Down
62 changes: 51 additions & 11 deletions core/engine/src/builtins/temporal/duration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,16 +584,15 @@ impl Duration {
JsNativeError::typ().with_message("this value must be a Duration object.")
})?;

let round_to = args.get_or_undefined(0);
let round_to = match round_to {
let round_to = match args.first() {
// 3. If roundTo is undefined, then
JsValue::Undefined => {
None | Some(JsValue::Undefined) => {
return Err(JsNativeError::typ()
.with_message("roundTo cannot be undefined.")
.into())
}
// 4. If Type(roundTo) is String, then
JsValue::String(rt) => {
Some(JsValue::String(rt)) => {
// a. Let paramString be roundTo.
let param_string = rt.clone();
// b. Set roundTo to OrdinaryObjectCreate(null).
Expand All @@ -607,7 +606,7 @@ impl Duration {
new_round_to
}
// 5. Else,
_ => {
Some(round_to) => {
// a. Set roundTo to ? GetOptionsObject(roundTo).
get_options_object(round_to)?
}
Expand Down Expand Up @@ -876,7 +875,10 @@ impl Duration {
// -- Duration Abstract Operations --

/// 7.5.8 `ToTemporalDuration ( item )`
pub(crate) fn to_temporal_duration(item: &JsValue) -> JsResult<InnerDuration> {
pub(crate) fn to_temporal_duration(
item: &JsValue,
context: &mut Context,
) -> JsResult<InnerDuration> {
// 1a. If Type(item) is Object
// 1b. and item has an [[InitializedTemporalDuration]] internal slot, then
if let Some(duration) = item
Expand All @@ -887,18 +889,56 @@ pub(crate) fn to_temporal_duration(item: &JsValue) -> JsResult<InnerDuration> {
}

// 2. Let result be ? ToTemporalDurationRecord(item).
let result = to_temporal_duration_record(item)?;
let result = to_temporal_duration_record(item, context)?;
// 3. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
Ok(result)
}

/// 7.5.9 `ToTemporalDurationRecord ( temporalDurationLike )`
pub(crate) fn to_temporal_duration_record(
_temporal_duration_like: &JsValue,
temporal_duration_like: &JsValue,
context: &mut Context,
) -> JsResult<InnerDuration> {
Err(JsNativeError::range()
.with_message("Duration Parsing is not yet complete.")
.into())
// 1. If Type(temporalDurationLike) is not Object, then
let JsValue::Object(duration_obj) = temporal_duration_like else {
// a. If temporalDurationLike is not a String, throw a TypeError exception.
let JsValue::String(duration_string) = temporal_duration_like else {
return Err(JsNativeError::typ()
.with_message("Invalid TemporalDurationLike value.")
.into());
};

// b. Return ? ParseTemporalDurationString(temporalDurationLike).
return duration_string
.to_std_string_escaped()
.parse::<InnerDuration>()
.map_err(Into::into);
};

// 2. If temporalDurationLike has an [[InitializedTemporalDuration]] internal slot, then
if let Some(duration) = duration_obj.downcast_ref::<Duration>() {
// a. Return ! CreateDurationRecord(temporalDurationLike.[[Years]], temporalDurationLike.[[Months]], temporalDurationLike.[[Weeks]], temporalDurationLike.[[Days]], temporalDurationLike.[[Hours]], temporalDurationLike.[[Minutes]], temporalDurationLike.[[Seconds]], temporalDurationLike.[[Milliseconds]], temporalDurationLike.[[Microseconds]], temporalDurationLike.[[Nanoseconds]]).
return Ok(duration.inner);
}

// 3. Let result be a new Duration Record with each field set to 0.
// 4. Let partial be ? ToTemporalPartialDurationRecord(temporalDurationLike).
let partial = to_temporal_partial_duration(temporal_duration_like, context)?;

// 5. If partial.[[Years]] is not undefined, set result.[[Years]] to partial.[[Years]].
// 6. If partial.[[Months]] is not undefined, set result.[[Months]] to partial.[[Months]].
// 7. If partial.[[Weeks]] is not undefined, set result.[[Weeks]] to partial.[[Weeks]].
// 8. If partial.[[Days]] is not undefined, set result.[[Days]] to partial.[[Days]].
// 9. If partial.[[Hours]] is not undefined, set result.[[Hours]] to partial.[[Hours]].
// 10. If partial.[[Minutes]] is not undefined, set result.[[Minutes]] to partial.[[Minutes]].
// 11. If partial.[[Seconds]] is not undefined, set result.[[Seconds]] to partial.[[Seconds]].
// 12. If partial.[[Milliseconds]] is not undefined, set result.[[Milliseconds]] to partial.[[Milliseconds]].
// 13. If partial.[[Microseconds]] is not undefined, set result.[[Microseconds]] to partial.[[Microseconds]].
// 14. If partial.[[Nanoseconds]] is not undefined, set result.[[Nanoseconds]] to partial.[[Nanoseconds]].
// 15. If ! IsValidDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]) is false, then
// a. Throw a RangeError exception.
// 16. Return result.
InnerDuration::from_partial(&partial).map_err(Into::into)
}

/// 7.5.14 `CreateTemporalDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds [ , newTarget ] )`
Expand Down
59 changes: 30 additions & 29 deletions core/engine/src/builtins/temporal/instant/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@ impl Instant {
})?;

// 3. Return ? AddDurationToOrSubtractDurationFromInstant(add, instant, temporalDurationLike).
let temporal_duration_like = to_temporal_duration_record(args.get_or_undefined(0))?;
let temporal_duration_like =
to_temporal_duration_record(args.get_or_undefined(0), context)?;
let result = instant.inner.add(temporal_duration_like)?;
create_temporal_instant(result, None, context)
}
Expand All @@ -258,7 +259,8 @@ impl Instant {
})?;

// 3. Return ? AddDurationToOrSubtractDurationFromInstant(subtract, instant, temporalDurationLike).
let temporal_duration_like = to_temporal_duration_record(args.get_or_undefined(0))?;
let temporal_duration_like =
to_temporal_duration_record(args.get_or_undefined(0), context)?;
let result = instant.inner.subtract(temporal_duration_like)?;
create_temporal_instant(result, None, context)
}
Expand Down Expand Up @@ -336,33 +338,32 @@ impl Instant {
JsNativeError::typ().with_message("the this object must be an instant object.")
})?;

let round_to = args.get_or_undefined(0);
// 3. If roundTo is undefined, then
if round_to.is_undefined() {
// a. Throw a TypeError exception.
return Err(JsNativeError::typ()
.with_message("roundTo cannot be undefined.")
.into());
};
// 4. If Type(roundTo) is String, then
let round_to = if round_to.is_string() {
// a. Let paramString be roundTo.
let param_string = round_to
.as_string()
.expect("roundTo is confirmed to be a string here.");
// b. Set roundTo to OrdinaryObjectCreate(null).
let new_round_to = JsObject::with_null_proto();
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit"), paramString).
new_round_to.create_data_property_or_throw(
utf16!("smallestUnit"),
param_string.clone(),
context,
)?;
new_round_to
// 5. Else,
} else {
// a. Set roundTo to ? GetOptionsObject(roundTo).
get_options_object(round_to)?
let round_to = match args.first() {
// 3. If roundTo is undefined, then
None | Some(JsValue::Undefined) => {
return Err(JsNativeError::typ()
.with_message("roundTo cannot be undefined.")
.into())
}
// 4. If Type(roundTo) is String, then
Some(JsValue::String(rt)) => {
// a. Let paramString be roundTo.
let param_string = rt.clone();
// b. Set roundTo to OrdinaryObjectCreate(null).
let new_round_to = JsObject::with_null_proto();
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
new_round_to.create_data_property_or_throw(
utf16!("smallestUnit"),
param_string,
context,
)?;
new_round_to
}
// 5. Else,
Some(round_to) => {
// a. Set roundTo to ? GetOptionsObject(roundTo).
get_options_object(round_to)?
}
};

// 6. NOTE: The following steps read options and perform independent validation in
Expand Down
6 changes: 1 addition & 5 deletions core/engine/src/builtins/temporal/plain_date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,12 +505,8 @@ pub(crate) fn create_temporal_date(
new_target: Option<&JsValue>,
context: &mut Context,
) -> JsResult<JsObject> {
// NOTE (nekevss): The below should never trigger as `IsValidISODate` is enforced by Date.
// 1. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.
if !inner.is_valid() {
return Err(JsNativeError::range()
.with_message("Date is not a valid ISO date.")
.into());
};

// 2. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
if !DateTime::<JsCustomCalendar>::validate(&inner) {
Expand Down
Loading

0 comments on commit 67c8359

Please sign in to comment.