1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 19:27:35 +00:00

LibJS: Allow string as parameter in Temporal's round() / total()

This is a normative change in the Temporal spec.

See: 1f0c586
This commit is contained in:
Linus Groh 2021-11-19 00:50:31 +00:00
parent 93ee922027
commit d0c29c9735
9 changed files with 199 additions and 89 deletions

View file

@ -288,92 +288,107 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::abs)
return TRY(create_temporal_duration(global_object, fabs(duration->years()), fabs(duration->months()), fabs(duration->weeks()), fabs(duration->days()), fabs(duration->hours()), fabs(duration->minutes()), fabs(duration->seconds()), fabs(duration->milliseconds()), fabs(duration->microseconds()), fabs(duration->nanoseconds())));
}
// 7.3.21 Temporal.Duration.prototype.total ( options ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.total
// 7.3.21 Temporal.Duration.prototype.total ( totalOf ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.total
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto* duration = TRY(typed_this_object(global_object));
// 3. If options is undefined, throw a TypeError exception.
// 3. If totalOf is undefined, throw a TypeError exception.
if (vm.argument(0).is_undefined())
return vm.throw_completion<TypeError>(global_object, ErrorType::TemporalMissingOptionsObject);
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(global_object, vm.argument(0)));
Object* total_of;
// 5. Let relativeTo be ? ToRelativeTemporalObject(options).
auto relative_to = TRY(to_relative_temporal_object(global_object, *options));
// 4. If Type(totalOf) is String, then
if (vm.argument(0).is_string()) {
// a. Let paramString be totalOf.
// 6. Let unit be ? ToTemporalDurationTotalUnit(options).
auto unit = TRY(to_temporal_duration_total_unit(global_object, *options));
// b. Set totalOf to ! OrdinaryObjectCreate(null).
total_of = Object::create(global_object, nullptr);
// 7. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], unit, relativeTo).
// c. Perform ! CreateDataPropertyOrThrow(totalOf, "unit", paramString).
MUST(total_of->create_data_property_or_throw(vm.names.unit, vm.argument(0)));
}
// 5. Else,
else {
// a. Set totalOf to ? GetOptionsObject(totalOf).
total_of = TRY(get_options_object(global_object, vm.argument(0)));
}
// 6. Let relativeTo be ? ToRelativeTemporalObject(totalOf).
auto relative_to = TRY(to_relative_temporal_object(global_object, *total_of));
// 7. Let unit be ? ToTemporalDurationTotalUnit(totalOf).
auto unit = TRY(to_temporal_duration_total_unit(global_object, *total_of));
// 8. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], unit, relativeTo).
auto unbalance_result = TRY(unbalance_duration_relative(global_object, duration->years(), duration->months(), duration->weeks(), duration->days(), unit, relative_to));
// 8. Let intermediate be undefined.
// 9. Let intermediate be undefined.
ZonedDateTime* intermediate = nullptr;
// 9. If relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
// 10. If relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (relative_to.is_object() && is<ZonedDateTime>(relative_to.as_object())) {
// a. Set intermediate to ? MoveRelativeZonedDateTime(relativeTo, unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], 0).
intermediate = TRY(move_relative_zoned_date_time(global_object, static_cast<ZonedDateTime&>(relative_to.as_object()), unbalance_result.years, unbalance_result.months, unbalance_result.weeks, 0));
}
// 10. Let balanceResult be ? BalanceDuration(unbalanceResult.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], unit, intermediate).
// 11. Let balanceResult be ? BalanceDuration(unbalanceResult.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], unit, intermediate).
auto balance_result = TRY(balance_duration(global_object, unbalance_result.days, duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), *js_bigint(vm, Crypto::SignedBigInteger::create_from(duration->nanoseconds())), unit, intermediate));
// 11. Let roundResult be ? RoundDuration(unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], balanceResult.[[Days]], balanceResult.[[Hours]], balanceResult.[[Minutes]], balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]], 1, unit, "trunc", relativeTo).
// 12. Let roundResult be ? RoundDuration(unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], balanceResult.[[Days]], balanceResult.[[Hours]], balanceResult.[[Minutes]], balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]], 1, unit, "trunc", relativeTo).
auto round_result = TRY(round_duration(global_object, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, balance_result.days, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds, 1, unit, "trunc"sv, relative_to.is_object() ? &relative_to.as_object() : nullptr));
double whole;
// 12. If unit is "year", then
// 13. If unit is "year", then
if (unit == "year"sv) {
// a. Let whole be roundResult.[[Years]].
whole = round_result.years;
}
// 13. Else if unit is "month", then
// 14. Else if unit is "month", then
else if (unit == "month"sv) {
// a. Let whole be roundResult.[[Months]].
whole = round_result.months;
}
// 14. Else if unit is "week", then
// 15. Else if unit is "week", then
else if (unit == "week"sv) {
// a. Let whole be roundResult.[[Weeks]].
whole = round_result.weeks;
}
// 15. Else if unit is "day", then
// 16. Else if unit is "day", then
else if (unit == "day"sv) {
// a. Let whole be roundResult.[[Days]].
whole = round_result.days;
}
// 16. Else if unit is "hour", then
// 17. Else if unit is "hour", then
else if (unit == "hour"sv) {
// a. Let whole be roundResult.[[Hours]].
whole = round_result.hours;
}
// 17. Else if unit is "minute", then
// 18. Else if unit is "minute", then
else if (unit == "minute"sv) {
// a. Let whole be roundResult.[[Minutes]].
whole = round_result.minutes;
}
// 18. Else if unit is "second", then
// 19. Else if unit is "second", then
else if (unit == "second"sv) {
// a. Let whole be roundResult.[[Seconds]].
whole = round_result.seconds;
}
// 19. Else if unit is "millisecond", then
// 20. Else if unit is "millisecond", then
else if (unit == "millisecond"sv) {
// a. Let whole be roundResult.[[Milliseconds]].
whole = round_result.milliseconds;
}
// 20. Else if unit is "microsecond", then
// 21. Else if unit is "microsecond", then
else if (unit == "microsecond"sv) {
// a. Let whole be roundResult.[[Microseconds]].
whole = round_result.microseconds;
}
// 21. Else,
// 22. Else,
else {
// a. Assert: unit is "nanosecond".
VERIFY(unit == "nanosecond"sv);
@ -382,7 +397,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
whole = round_result.nanoseconds;
}
// 22. Return whole + roundResult.[[Remainder]].
// 23. Return whole + roundResult.[[Remainder]].
return whole + round_result.remainder;
}

View file

@ -243,24 +243,40 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::since)
return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
}
// 8.3.11 Temporal.Instant.prototype.round ( options ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.round
// 8.3.11 Temporal.Instant.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.round
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto* instant = TRY(typed_this_object(global_object));
// 3. If options is undefined, then
// 3. If roundTo is undefined, then
if (vm.argument(0).is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(global_object, ErrorType::TemporalMissingOptionsObject);
}
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(global_object, vm.argument(0)));
Object* round_to;
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », undefined).
auto smallest_unit_value = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, {}));
// 4. If Type(roundTo) is String, then
if (vm.argument(0).is_string()) {
// a. Let paramString be roundTo.
// b. Set roundTo to ! OrdinaryObjectCreate(null).
round_to = Object::create(global_object, nullptr);
// FIXME: "_smallestUnit_" is a spec bug, see https://github.com/tc39/proposal-temporal/pull/1931
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "_smallestUnit_", paramString).
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0)));
}
// 5. Else,
else {
// a. Set roundTo to ? GetOptionsObject(roundTo).
round_to = TRY(get_options_object(global_object, vm.argument(0)));
}
// 6. Let smallestUnit be ? ToSmallestTemporalUnit(roundTo, « "year", "month", "week", "day" », undefined).
auto smallest_unit_value = TRY(to_smallest_temporal_unit(global_object, *round_to, { "year"sv, "month"sv, "week"sv, "day"sv }, {}));
// 6. If smallestUnit is undefined, throw a RangeError exception.
if (!smallest_unit_value.has_value())
@ -269,8 +285,8 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round)
// At this point smallest_unit_value can only be a string
auto& smallest_unit = *smallest_unit_value;
// 7. Let roundingMode be ? ToTemporalRoundingMode(options, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "halfExpand"));
// 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *round_to, "halfExpand"));
double maximum;
// 8. If smallestUnit is "hour", then
@ -306,8 +322,8 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round)
maximum = 86400000000000;
}
// 14. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, true).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, maximum, true));
// 14. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo, maximum, true).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *round_to, maximum, true));
// 15. Let roundedNs be ! RoundTemporalInstant(instant.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
auto* rounded_ns = round_temporal_instant(global_object, instant->nanoseconds(), rounding_increment, smallest_unit, rounding_mode);

View file

@ -506,42 +506,58 @@ JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::subtract)
return TRY(create_temporal_date_time(global_object, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, date_time->calendar()));
}
// 5.3.30 Temporal.PlainDateTime.prototype.round ( options ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.round
// 5.3.30 Temporal.PlainDateTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.round
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::round)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto* date_time = TRY(typed_this_object(global_object));
// 3. If options is undefined, then
// 3. If roundTo is undefined, then
if (vm.argument(0).is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(global_object, ErrorType::TemporalMissingOptionsObject);
}
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(global_object, vm.argument(0)));
Object* round_to;
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week" », undefined).
auto smallest_unit_value = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv }, {}));
// 4. If Type(roundTo) is String, then
if (vm.argument(0).is_string()) {
// a. Let paramString be roundTo.
// 6. If smallestUnit is undefined, throw a RangeError exception.
// b. Set roundTo to ! OrdinaryObjectCreate(null).
round_to = Object::create(global_object, nullptr);
// FIXME: "_smallestUnit_" is a spec bug, see https://github.com/tc39/proposal-temporal/pull/1931
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "_smallestUnit_", paramString).
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0)));
}
// 5. Else,
else {
// a. Set roundTo to ? GetOptionsObject(roundTo).
round_to = TRY(get_options_object(global_object, vm.argument(0)));
}
// 6. Let smallestUnit be ? ToSmallestTemporalUnit(roundTo, « "year", "month", "week" », undefined).
auto smallest_unit_value = TRY(to_smallest_temporal_unit(global_object, *round_to, { "year"sv, "month"sv, "week"sv }, {}));
// 7. If smallestUnit is undefined, throw a RangeError exception.
if (!smallest_unit_value.has_value())
return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, vm.names.undefined.as_string(), "smallestUnit");
// NOTE: At this point smallest_unit_value can only be a string
auto& smallest_unit = *smallest_unit_value;
// 7. Let roundingMode be ? ToTemporalRoundingMode(options, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "halfExpand"));
// 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *round_to, "halfExpand"));
// 8. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(options, smallestUnit).
auto rounding_increment = TRY(to_temporal_date_time_rounding_increment(global_object, *options, smallest_unit));
// 9. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(roundTo, smallestUnit).
auto rounding_increment = TRY(to_temporal_date_time_rounding_increment(global_object, *round_to, smallest_unit));
// 9. Let result be ! RoundISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode).
// 10. Let result be ! RoundISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode).
auto result = round_iso_date_time(date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), rounding_increment, smallest_unit, rounding_mode);
// 10. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], dateTime.[[Calendar]]).
// 11. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], dateTime.[[Calendar]]).
return TRY(create_temporal_date_time(global_object, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, date_time->calendar()));
}

View file

@ -250,60 +250,76 @@ JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::with)
return TRY(create_temporal_time(global_object, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
}
// 4.3.15 Temporal.PlainTime.prototype.round ( options ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.round
// 4.3.15 Temporal.PlainTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.round
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::round)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto* temporal_time = TRY(typed_this_object(global_object));
// 3. If options is undefined, then
// 3. If roundTo is undefined, then
if (vm.argument(0).is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(global_object, ErrorType::TemporalMissingOptionsObject);
}
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(global_object, vm.argument(0)));
Object* round_to;
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », undefined).
auto smallest_unit_value = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, {}));
// 4. If Type(roundTo) is String, then
if (vm.argument(0).is_string()) {
// a. Let paramString be roundTo.
// 6. If smallestUnit is undefined, throw a RangeError exception.
// b. Set roundTo to ! OrdinaryObjectCreate(null).
round_to = Object::create(global_object, nullptr);
// FIXME: "_smallestUnit_" is a spec bug, see https://github.com/tc39/proposal-temporal/pull/1931
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "_smallestUnit_", paramString).
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0)));
}
// 5. Else,
else {
// a. Set roundTo to ? GetOptionsObject(roundTo).
round_to = TRY(get_options_object(global_object, vm.argument(0)));
}
// 6. Let smallestUnit be ? ToSmallestTemporalUnit(roundTo, « "year", "month", "week", "day" », undefined).
auto smallest_unit_value = TRY(to_smallest_temporal_unit(global_object, *round_to, { "year"sv, "month"sv, "week"sv, "day"sv }, {}));
// 7. If smallestUnit is undefined, throw a RangeError exception.
if (!smallest_unit_value.has_value())
return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, vm.names.undefined.as_string(), "smallestUnit");
// NOTE: At this point smallest_unit_value can only be a string
auto& smallest_unit = *smallest_unit_value;
// 7. Let roundingMode be ? ToTemporalRoundingMode(options, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "halfExpand"));
// 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *round_to, "halfExpand"));
double maximum;
// 8. If smallestUnit is "hour", then
// 9. If smallestUnit is "hour", then
if (smallest_unit == "hour"sv) {
// a. Let maximum be 24.
maximum = 24;
}
// 9. Else if smallestUnit is "minute" or "second", then
// 10. Else if smallestUnit is "minute" or "second", then
else if (smallest_unit == "minute"sv || smallest_unit == "second"sv) {
// a. Let maximum be 60.
maximum = 60;
}
// 10. Else,
// 11. Else,
else {
// a. Let maximum be 1000.
maximum = 1000;
}
// 11. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, maximum, false));
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *round_to, maximum, false));
// 12. Let result be ! RoundTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode).
// 13. Let result be ! RoundTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode).
auto result = round_time(temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), rounding_increment, smallest_unit, rounding_mode);
// 13. Return ? CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
// 14. Return ? CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
return TRY(create_temporal_time(global_object, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
}

View file

@ -942,85 +942,101 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::subtract)
return MUST(create_temporal_zoned_date_time(global_object, *epoch_nanoseconds, time_zone, calendar));
}
// 6.3.39 Temporal.ZonedDateTime.prototype.round ( options ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.round
// 6.3.39 Temporal.ZonedDateTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.round
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::round)
{
// 1. Let zonedDateTime be the this value.
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
auto* zoned_date_time = TRY(typed_this_object(global_object));
// 3. If options is undefined, then
// 3. If roundTo is undefined, then
if (vm.argument(0).is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(global_object, ErrorType::TemporalMissingOptionsObject);
}
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(global_object, vm.argument(0)));
Object* round_to;
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week" », undefined).
auto smallest_unit_value = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv }, {}));
// 4. If Type(roundTo) is String, then
if (vm.argument(0).is_string()) {
// a. Let paramString be roundTo.
// 6. If smallestUnit is undefined, throw a RangeError exception.
// b. Set roundTo to ! OrdinaryObjectCreate(null).
round_to = Object::create(global_object, nullptr);
// FIXME: "_smallestUnit_" is a spec bug, see https://github.com/tc39/proposal-temporal/pull/1931
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "_smallestUnit_", paramString).
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0)));
}
// 5. Else,
else {
// a. Set roundTo to ? GetOptionsObject(roundTo).
round_to = TRY(get_options_object(global_object, vm.argument(0)));
}
// 6. Let smallestUnit be ? ToSmallestTemporalUnit(roundTo, « "year", "month", "week" », undefined).
auto smallest_unit_value = TRY(to_smallest_temporal_unit(global_object, *round_to, { "year"sv, "month"sv, "week"sv }, {}));
// 7. If smallestUnit is undefined, throw a RangeError exception.
if (!smallest_unit_value.has_value())
return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, vm.names.undefined.as_string(), "smallestUnit");
// NOTE: At this point smallest_unit_value can only be a string
auto& smallest_unit = *smallest_unit_value;
// 7. Let roundingMode be ? ToTemporalRoundingMode(options, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "halfExpand"sv));
// 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *round_to, "halfExpand"sv));
// 8. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(options, smallestUnit).
auto rounding_increment = TRY(to_temporal_date_time_rounding_increment(global_object, *options, smallest_unit));
// 9. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(options, smallestUnit).
auto rounding_increment = TRY(to_temporal_date_time_rounding_increment(global_object, *round_to, smallest_unit));
// 9. Let timeZone be zonedDateTime.[[TimeZone]].
// 10. Let timeZone be zonedDateTime.[[TimeZone]].
auto& time_zone = zoned_date_time->time_zone();
// 10. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]).
// 11. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]).
auto* instant = MUST(create_temporal_instant(global_object, zoned_date_time->nanoseconds()));
// 11. Let calendar be zonedDateTime.[[Calendar]].
// 12. Let calendar be zonedDateTime.[[Calendar]].
auto& calendar = zoned_date_time->calendar();
// 12. Let temporalDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar).
// 13. Let temporalDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar).
auto* temporal_date_time = TRY(builtin_time_zone_get_plain_date_time_for(global_object, &time_zone, *instant, calendar));
// 13. Let isoCalendar be ! GetISO8601Calendar().
// 14. Let isoCalendar be ! GetISO8601Calendar().
auto* iso_calendar = get_iso8601_calendar(global_object);
// 14. Let dtStart be ? CreateTemporalDateTime(temporalDateTime.[[ISOYear]], temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], 0, 0, 0, 0, 0, 0, isoCalendar).
// 15. Let dtStart be ? CreateTemporalDateTime(temporalDateTime.[[ISOYear]], temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], 0, 0, 0, 0, 0, 0, isoCalendar).
auto* dt_start = TRY(create_temporal_date_time(global_object, temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day(), 0, 0, 0, 0, 0, 0, *iso_calendar));
// 15. Let instantStart be ? BuiltinTimeZoneGetInstantFor(timeZone, dtStart, "compatible").
// 16. Let instantStart be ? BuiltinTimeZoneGetInstantFor(timeZone, dtStart, "compatible").
auto* instant_start = TRY(builtin_time_zone_get_instant_for(global_object, &time_zone, *dt_start, "compatible"sv));
// 16. Let startNs be instantStart.[[Nanoseconds]].
// 17. Let startNs be instantStart.[[Nanoseconds]].
auto& start_ns = instant_start->nanoseconds();
// 17. Let endNs be ? AddZonedDateTime(startNs, timeZone, zonedDateTime.[[Calendar]], 0, 0, 0, 1, 0, 0, 0, 0, 0, 0).
// 18. Let endNs be ? AddZonedDateTime(startNs, timeZone, zonedDateTime.[[Calendar]], 0, 0, 0, 1, 0, 0, 0, 0, 0, 0).
// TODO: Shouldn't `zonedDateTime.[[Calendar]]` be `calendar` for consistency?
auto* end_ns = TRY(add_zoned_date_time(global_object, start_ns, &time_zone, zoned_date_time->calendar(), 0, 0, 0, 1, 0, 0, 0, 0, 0, 0));
// 18. Let dayLengthNs be (endNs startNs).
// 19. Let dayLengthNs be (endNs startNs).
auto day_length_ns = end_ns->big_integer().minus(start_ns.big_integer()).to_double();
// 19. If dayLengthNs is 0, then
// 20. If dayLengthNs is 0, then
if (day_length_ns == 0) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalZonedDateTimeRoundZeroLengthDay);
}
// 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]], temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]], temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode, dayLengthNs).
// 21. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]], temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]], temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode, dayLengthNs).
auto round_result = round_iso_date_time(temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day(), temporal_date_time->iso_hour(), temporal_date_time->iso_minute(), temporal_date_time->iso_second(), temporal_date_time->iso_millisecond(), temporal_date_time->iso_microsecond(), temporal_date_time->iso_nanosecond(), rounding_increment, smallest_unit, rounding_mode, day_length_ns);
// 21. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
// 22. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(global_object, &time_zone, *instant));
// 22. Let epochNanoseconds be ? InterpretISODateTimeOffset(roundResult.[[Year]], roundResult.[[Month]], roundResult.[[Day]], roundResult.[[Hour]], roundResult.[[Minute]], roundResult.[[Second]], roundResult.[[Millisecond]], roundResult.[[Microsecond]], roundResult.[[Nanosecond]], option, offsetNanoseconds, timeZone, "compatible", "prefer", match exactly).
// 23. Let epochNanoseconds be ? InterpretISODateTimeOffset(roundResult.[[Year]], roundResult.[[Month]], roundResult.[[Day]], roundResult.[[Hour]], roundResult.[[Minute]], roundResult.[[Second]], roundResult.[[Millisecond]], roundResult.[[Microsecond]], roundResult.[[Nanosecond]], option, offsetNanoseconds, timeZone, "compatible", "prefer", match exactly).
auto* epoch_nanoseconds = TRY(interpret_iso_date_time_offset(global_object, round_result.year, round_result.month, round_result.day, round_result.hour, round_result.minute, round_result.second, round_result.millisecond, round_result.microsecond, round_result.nanosecond, OffsetBehavior::Option, offset_nanoseconds, &time_zone, "compatible"sv, "prefer"sv, MatchBehavior::MatchExactly));
// 23. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
// 24. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
return MUST(create_temporal_zoned_date_time(global_object, *epoch_nanoseconds, time_zone, calendar));
}