summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2022-06-09 22:42:42 +0100
committerAndreas Kling <kling@serenityos.org>2022-06-10 12:39:11 +0200
commit4c77575ec50c62c0265a53a0cd7d8b151a18e4a8 (patch)
tree807f01db54ed399e119ab3bbb191ecd30c117b5b
parentb9beb2b120e47a3c446c70f26251c40b8e5ea01e (diff)
downloadserenity-4c77575ec50c62c0265a53a0cd7d8b151a18e4a8.zip
LibJS: Simplify Temporal unit AOs
This is an editorial change in the Temporal spec. See: https://github.com/tc39/proposal-temporal/commit/1b3d018
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp326
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h17
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.cpp12
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp23
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp35
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp4
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp22
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp35
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTimePrototype.cpp23
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp35
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp27
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp54
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp39
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp49
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.since.js4
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.until.js4
16 files changed, 333 insertions, 376 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp
index 4e515f5114..6c827ce39b 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp
@@ -389,8 +389,12 @@ ThrowCompletionOr<SecondsStringPrecision> to_seconds_string_precision(GlobalObje
{
auto& vm = global_object.vm();
- // Let smallestUnit be ? ToSmallestTemporalUnit(normalizedOptions, « "year", "month", "week", "day", "hour" », undefined).
- auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, normalized_options, { "year"sv, "month"sv, "week"sv, "day"sv, "hour"sv }, {}));
+ // 1. Let smallestUnit be ? GetTemporalUnit(normalizedOptions, "smallestUnit", time, undefined).
+ auto smallest_unit = TRY(get_temporal_unit(global_object, normalized_options, vm.names.smallestUnit, UnitGroup::Time, Optional<StringView> {}));
+
+ // 2. If smallestUnit is "hour", throw a RangeError exception.
+ if (smallest_unit == "hour"sv)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, *smallest_unit, "smallestUnit"sv);
// 2. If smallestUnit is "minute", then
if (smallest_unit == "minute"sv) {
@@ -462,121 +466,117 @@ ThrowCompletionOr<SecondsStringPrecision> to_seconds_string_precision(GlobalObje
return SecondsStringPrecision { .precision = digits, .unit = "nanosecond"sv, .increment = (u32)pow(10, 9 - digits) };
}
-// https://tc39.es/proposal-temporal/#table-temporal-singular-and-plural-units
-static HashMap<StringView, StringView> plural_to_singular_units = {
- { "years"sv, "year"sv },
- { "months"sv, "month"sv },
- { "weeks"sv, "week"sv },
- { "days"sv, "day"sv },
- { "hours"sv, "hour"sv },
- { "minutes"sv, "minute"sv },
- { "seconds"sv, "second"sv },
- { "milliseconds"sv, "millisecond"sv },
- { "microseconds"sv, "microsecond"sv },
- { "nanoseconds"sv, "nanosecond"sv }
+struct TemporalUnit {
+ StringView singular;
+ StringView plural;
+ UnitGroup category;
+};
+
+// https://tc39.es/proposal-temporal/#table-temporal-units
+static Vector<TemporalUnit> temporal_units = {
+ { "year"sv, "years"sv, UnitGroup::Date },
+ { "month"sv, "months"sv, UnitGroup::Date },
+ { "week"sv, "weeks"sv, UnitGroup::Date },
+ { "day"sv, "days"sv, UnitGroup::Date },
+ { "hour"sv, "hours"sv, UnitGroup::Time },
+ { "minute"sv, "minutes"sv, UnitGroup::Time },
+ { "second"sv, "seconds"sv, UnitGroup::Time },
+ { "millisecond"sv, "milliseconds"sv, UnitGroup::Time },
+ { "microsecond"sv, "microseconds"sv, UnitGroup::Time },
+ { "nanosecond"sv, "nanoseconds"sv, UnitGroup::Time }
};
-// 13.16 ToLargestTemporalUnit ( normalizedOptions, disallowedUnits, fallback [ , autoValue ] ), https://tc39.es/proposal-temporal/#sec-temporal-tolargesttemporalunit
-ThrowCompletionOr<Optional<String>> to_largest_temporal_unit(GlobalObject& global_object, Object const& normalized_options, Vector<StringView> const& disallowed_units, Optional<String> fallback, Optional<String> auto_value)
+// 13.16 GetTemporalUnit ( normalizedOptions, key, unitGroup, default [ , extraValues ] ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporalunit
+ThrowCompletionOr<Optional<String>> get_temporal_unit(GlobalObject& global_object, Object const& normalized_options, PropertyKey const& key, UnitGroup unit_group, Variant<TemporalUnitRequired, Optional<StringView>> const& default_, Vector<StringView> const& extra_values)
{
auto& vm = global_object.vm();
- // 1. Assert: disallowedUnits does not contain fallback.
- // 2. Assert: disallowedUnits does not contain "auto".
- // 3. Assert: autoValue is not present or fallback is "auto".
- VERIFY(!auto_value.has_value() || fallback == "auto"sv);
- // 4. Assert: autoValue is not present or disallowedUnits does not contain autoValue.
-
- // 5. Let largestUnit be ? GetOption(normalizedOptions, "largestUnit", « String », « "auto", "year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds", "microsecond", "microseconds", "nanosecond", "nanoseconds" », fallback).
- auto largest_unit_value = TRY(get_option(global_object, normalized_options, vm.names.largestUnit, { OptionType::String }, { "auto"sv, "year"sv, "years"sv, "month"sv, "months"sv, "week"sv, "weeks"sv, "day"sv, "days"sv, "hour"sv, "hours"sv, "minute"sv, "minutes"sv, "second"sv, "seconds"sv, "millisecond"sv, "milliseconds"sv, "microsecond"sv, "microseconds"sv, "nanosecond"sv, "nanoseconds"sv }, fallback.has_value() ? js_string(vm, *fallback) : js_undefined()));
+ // 1. Let singularNames be a new empty List.
+ Vector<StringView> singular_names;
- // OPTIMIZATION: We skip the following string-only checks for the fallback to tidy up the code a bit
- if (largest_unit_value.is_undefined())
- return Optional<String> {};
- VERIFY(largest_unit_value.is_string());
- auto largest_unit = largest_unit_value.as_string().string();
+ // 2. For each row of Table 13, except the header row, in table order, do
+ for (auto const& row : temporal_units) {
+ // a. Let unit be the value in the Singular column of the row.
+ auto unit = row.singular;
- // 6. If largestUnit is "auto" and autoValue is present, then
- if (largest_unit == "auto"sv && auto_value.has_value()) {
- // a. Return autoValue.
- return *auto_value;
+ // b. If the Category column of the row is date and unitGroup is date or datetime, append unit to singularNames.
+ if (row.category == UnitGroup::Date && (unit_group == UnitGroup::Date || unit_group == UnitGroup::DateTime))
+ singular_names.append(unit);
+ // c. Else if the Category column of the row is time and unitGroup is time or datetime, append unit to singularNames.
+ else if (row.category == UnitGroup::Time && (unit_group == UnitGroup::Time || unit_group == UnitGroup::DateTime))
+ singular_names.append(unit);
}
- // 7. If largestUnit is in the Plural column of Table 12, then
- if (auto singular_unit = plural_to_singular_units.get(largest_unit); singular_unit.has_value()) {
- // a. Set largestUnit to the corresponding Singular value of the same row.
- largest_unit = singular_unit.value();
+ // 3. If extraValues is present, then
+ if (!extra_values.is_empty()) {
+ // a. Set singularNames to the list-concatenation of singularNames and extraValues.
+ singular_names.extend(extra_values);
}
- // 8. If disallowedUnits contains largestUnit, then
- if (disallowed_units.contains_slow(largest_unit)) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, largest_unit, vm.names.largestUnit.as_string());
- }
+ Value default_value;
- // 9. Return largestUnit.
- return largest_unit;
-}
-
-// 13.17 ToSmallestTemporalUnit ( normalizedOptions, disallowedUnits, fallback ), https://tc39.es/proposal-temporal/#sec-temporal-tosmallesttemporalunit
-ThrowCompletionOr<Optional<String>> to_smallest_temporal_unit(GlobalObject& global_object, Object const& normalized_options, Vector<StringView> const& disallowed_units, Optional<String> fallback)
-{
- auto& vm = global_object.vm();
+ // 4. If default is required, then
+ if (default_.has<TemporalUnitRequired>()) {
+ // a. Let defaultValue be undefined.
+ default_value = js_undefined();
+ }
+ // 5. Else,
+ else {
+ auto default_string = default_.get<Optional<StringView>>();
- // 1. Assert: disallowedUnits does not contain fallback.
+ // a. Let defaultValue be default.
+ default_value = default_string.has_value() ? js_string(vm, *default_string) : js_undefined();
- // 2. Let smallestUnit be ? GetOption(normalizedOptions, "smallestUnit", « String », « "year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds", "microsecond", "microseconds", "nanosecond", "nanoseconds" », fallback).
- auto smallest_unit_value = TRY(get_option(global_object, normalized_options, vm.names.smallestUnit, { OptionType::String }, { "year"sv, "years"sv, "month"sv, "months"sv, "week"sv, "weeks"sv, "day"sv, "days"sv, "hour"sv, "hours"sv, "minute"sv, "minutes"sv, "second"sv, "seconds"sv, "millisecond"sv, "milliseconds"sv, "microsecond"sv, "microseconds"sv, "nanosecond"sv, "nanoseconds"sv }, fallback.has_value() ? js_string(vm, *fallback) : js_undefined()));
+ // b. If defaultValue is not undefined and singularNames does not contain defaultValue, then
+ if (default_string.has_value() && !singular_names.contains_slow(*default_string)) {
+ // i. Append defaultValue to singularNames.
+ singular_names.append(*default_string);
+ }
+ }
- // OPTIMIZATION: We skip the following string-only checks for the fallback to tidy up the code a bit
- if (smallest_unit_value.is_undefined())
- return Optional<String> {};
- VERIFY(smallest_unit_value.is_string());
- auto smallest_unit = smallest_unit_value.as_string().string();
+ // 6. Let allowedValues be a copy of singularNames.
+ auto allowed_values = singular_names;
- // 3. If smallestUnit is in the Plural column of Table 12, then
- if (auto singular_unit = plural_to_singular_units.get(smallest_unit); singular_unit.has_value()) {
- // a. Set smallestUnit to the corresponding Singular value of the same row.
- smallest_unit = singular_unit.value();
- }
+ // 7. For each element singularName of singularNames, do
+ for (auto const& singular_name : singular_names) {
+ for (auto const& row : temporal_units) {
+ // a. If singularName is listed in the Singular column of Table 13, then
+ if (singular_name == row.singular) {
+ // i. Let pluralName be the value in the Plural column of the corresponding row.
+ auto plural_name = row.plural;
- // 4. If disallowedUnits contains smallestUnit, then
- if (disallowed_units.contains_slow(smallest_unit)) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, smallest_unit, vm.names.smallestUnit.as_string());
+ // ii. Append pluralName to allowedValues.
+ allowed_values.append(plural_name);
+ }
+ }
}
- // 5. Return smallestUnit.
- return smallest_unit;
-}
+ // 8. NOTE: For each singular Temporal unit name that is contained within allowedValues, the corresponding plural name is also contained within it.
-// 13.18 ToTemporalDurationTotalUnit ( normalizedOptions ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldurationtotalunit
-ThrowCompletionOr<String> to_temporal_duration_total_unit(GlobalObject& global_object, Object const& normalized_options)
-{
- auto& vm = global_object.vm();
-
- // 1. Let unit be ? GetOption(normalizedOptions, "unit", « String », « "year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds", "microsecond", "microseconds", "nanosecond", "nanoseconds" », undefined).
- auto unit_value = TRY(get_option(global_object, normalized_options, vm.names.unit, { OptionType::String }, { "year"sv, "years"sv, "month"sv, "months"sv, "week"sv, "weeks"sv, "day"sv, "days"sv, "hour"sv, "hours"sv, "minute"sv, "minutes"sv, "second"sv, "seconds"sv, "millisecond"sv, "milliseconds"sv, "microsecond"sv, "microseconds"sv, "nanosecond"sv, "nanoseconds"sv }, js_undefined()));
+ // 9. Let value be ? GetOption(normalizedOptions, key, « String », allowedValues, defaultValue).
+ auto option_value = TRY(get_option(global_object, normalized_options, key, { OptionType::String }, allowed_values, default_value));
- // 2. If unit is undefined, then
- if (unit_value.is_undefined()) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::IsUndefined, "unit option value"sv);
- }
+ // 10. If value is undefined and default is required, throw a RangeError exception.
+ if (option_value.is_undefined() && default_.has<TemporalUnitRequired>())
+ return vm.throw_completion<RangeError>(global_object, ErrorType::IsUndefined, String::formatted("{} option value", key.as_string()));
- auto unit = unit_value.as_string().string();
+ Optional<String> value = option_value.is_undefined()
+ ? Optional<String> {}
+ : option_value.as_string().string();
- // 3. If unit is in the Plural column of Table 12, then
- if (auto singular_unit = plural_to_singular_units.get(unit); singular_unit.has_value()) {
- // a. Set unit to the corresponding Singular value of the same row.
- unit = *singular_unit;
+ // 11. If value is listed in the Plural column of Table 13, then
+ for (auto const& row : temporal_units) {
+ if (row.plural == value) {
+ // a. Set value to the value in the Singular column of the corresponding row.
+ value = row.singular;
+ }
}
- // 4. Return unit.
- return unit;
+ // 12. Return value.
+ return value;
}
-// 13.20 ToRelativeTemporalObject ( options ), https://tc39.es/proposal-temporal/#sec-temporal-torelativetemporalobject
+// 13.17 ToRelativeTemporalObject ( options ), https://tc39.es/proposal-temporal/#sec-temporal-torelativetemporalobject
ThrowCompletionOr<Value> to_relative_temporal_object(GlobalObject& global_object, Object const& options)
{
auto& vm = global_object.vm();
@@ -746,95 +746,28 @@ ThrowCompletionOr<Value> to_relative_temporal_object(GlobalObject& global_object
return TRY(create_temporal_date(global_object, result.year, result.month, result.day, *calendar));
}
-// 13.21 ValidateTemporalUnitRange ( largestUnit, smallestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-validatetemporalunitrange
-ThrowCompletionOr<void> validate_temporal_unit_range(GlobalObject& global_object, StringView largest_unit, StringView smallest_unit)
+// 13.18 LargerOfTwoTemporalUnits ( u1, u2 ), https://tc39.es/proposal-temporal/#sec-temporal-largeroftwotemporalunits
+StringView larger_of_two_temporal_units(StringView unit1, StringView unit2)
{
- auto& vm = global_object.vm();
+ // 1. Assert: Both u1 and u2 are listed in the Singular column of Table 13.
- // 1. If smallestUnit is "year" and largestUnit is not "year", then
- if (smallest_unit == "year"sv && largest_unit != "year"sv) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, smallest_unit, largest_unit);
- }
- // 2. If smallestUnit is "month" and largestUnit is not "year" or "month", then
- if (smallest_unit == "month"sv && !largest_unit.is_one_of("year"sv, "month"sv)) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, smallest_unit, largest_unit);
- }
- // 3. If smallestUnit is "week" and largestUnit is not one of "year", "month", or "week", then
- if (smallest_unit == "week"sv && !largest_unit.is_one_of("year"sv, "month"sv, "week"sv)) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, smallest_unit, largest_unit);
- }
- // 4. If smallestUnit is "day" and largestUnit is not one of "year", "month", "week", or "day", then
- if (smallest_unit == "day"sv && !largest_unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, smallest_unit, largest_unit);
- }
- // 5. If smallestUnit is "hour" and largestUnit is not one of "year", "month", "week", "day", or "hour", then
- if (smallest_unit == "hour"sv && !largest_unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv, "hour"sv)) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, smallest_unit, largest_unit);
- }
- // 6. If smallestUnit is "minute" and largestUnit is "second", "millisecond", "microsecond", or "nanosecond", then
- if (smallest_unit == "minute"sv && largest_unit.is_one_of("second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv)) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, smallest_unit, largest_unit);
- }
- // 7. If smallestUnit is "second" and largestUnit is "millisecond", "microsecond", or "nanosecond", then
- if (smallest_unit == "second"sv && largest_unit.is_one_of("millisecond"sv, "microsecond"sv, "nanosecond"sv)) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, smallest_unit, largest_unit);
- }
- // 8. If smallestUnit is "millisecond" and largestUnit is "microsecond" or "nanosecond", then
- if (smallest_unit == "millisecond"sv && largest_unit.is_one_of("microsecond"sv, "nanosecond"sv)) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, smallest_unit, largest_unit);
- }
- // 9. If smallestUnit is "microsecond" and largestUnit is "nanosecond", then
- if (smallest_unit == "microsecond"sv && largest_unit == "nanosecond"sv) {
- // a. Throw a RangeError exception.
- return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, smallest_unit, largest_unit);
- }
+ // 2. For each row of Table 13, except the header row, in table order, do
+ for (auto const& row : temporal_units) {
+ // a. Let unit be the value in the Singular column of the row.
+ auto unit = row.singular;
- return {};
-}
+ // b. If SameValue(u1, unit) is true, return unit.
+ if (unit1 == unit)
+ return unit;
-// 13.22 LargerOfTwoTemporalUnits ( u1, u2 ), https://tc39.es/proposal-temporal/#sec-temporal-largeroftwotemporalunits
-StringView larger_of_two_temporal_units(StringView unit1, StringView unit2)
-{
- // 1. If either u1 or u2 is "year", return "year".
- if (unit1 == "year"sv || unit2 == "year"sv)
- return "year"sv;
- // 2. If either u1 or u2 is "month", return "month".
- if (unit1 == "month"sv || unit2 == "month"sv)
- return "month"sv;
- // 3. If either u1 or u2 is "week", return "week".
- if (unit1 == "week"sv || unit2 == "week"sv)
- return "week"sv;
- // 4. If either u1 or u2 is "day", return "day".
- if (unit1 == "day"sv || unit2 == "day"sv)
- return "day"sv;
- // 5. If either u1 or u2 is "hour", return "hour".
- if (unit1 == "hour"sv || unit2 == "hour"sv)
- return "hour"sv;
- // 6. If either u1 or u2 is "minute", return "minute".
- if (unit1 == "minute"sv || unit2 == "minute"sv)
- return "minute"sv;
- // 7. If either u1 or u2 is "second", return "second".
- if (unit1 == "second"sv || unit2 == "second"sv)
- return "second"sv;
- // 8. If either u1 or u2 is "millisecond", return "millisecond".
- if (unit1 == "millisecond"sv || unit2 == "millisecond"sv)
- return "millisecond"sv;
- // 9. If either u1 or u2 is "microsecond", return "microsecond".
- if (unit1 == "microsecond"sv || unit2 == "microsecond"sv)
- return "microsecond"sv;
- // 10. Return "nanosecond".
- return "nanosecond"sv;
+ // c. If SameValue(u2, unit) is true, return unit.
+ if (unit2 == unit)
+ return unit;
+ }
+ VERIFY_NOT_REACHED();
}
-// 13.23 MergeLargestUnitOption ( options, largestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-mergelargestunitoption
+// 13.19 MergeLargestUnitOption ( options, largestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-mergelargestunitoption
ThrowCompletionOr<Object*> merge_largest_unit_option(GlobalObject& global_object, Object const* options, String largest_unit)
{
auto& vm = global_object.vm();
@@ -867,7 +800,7 @@ ThrowCompletionOr<Object*> merge_largest_unit_option(GlobalObject& global_object
return merged;
}
-// 13.24 MaximumTemporalDurationRoundingIncrement ( unit ), https://tc39.es/proposal-temporal/#sec-temporal-maximumtemporaldurationroundingincrement
+// 13.20 MaximumTemporalDurationRoundingIncrement ( unit ), https://tc39.es/proposal-temporal/#sec-temporal-maximumtemporaldurationroundingincrement
Optional<u16> maximum_temporal_duration_rounding_increment(StringView unit)
{
// 1. If unit is "year", "month", "week", or "day", then
@@ -895,7 +828,7 @@ Optional<u16> maximum_temporal_duration_rounding_increment(StringView unit)
return 1000;
}
-// 13.25 RejectObjectWithCalendarOrTimeZone ( object ), https://tc39.es/proposal-temporal/#sec-temporal-rejectobjectwithcalendarortimezone
+// 13.21 RejectObjectWithCalendarOrTimeZone ( object ), https://tc39.es/proposal-temporal/#sec-temporal-rejectobjectwithcalendarortimezone
ThrowCompletionOr<void> reject_object_with_calendar_or_time_zone(GlobalObject& global_object, Object& object)
{
auto& vm = global_object.vm();
@@ -929,7 +862,7 @@ ThrowCompletionOr<void> reject_object_with_calendar_or_time_zone(GlobalObject& g
return {};
}
-// 13.26 FormatSecondsStringPart ( second, millisecond, microsecond, nanosecond, precision ), https://tc39.es/proposal-temporal/#sec-temporal-formatsecondsstringpart
+// 13.22 FormatSecondsStringPart ( second, millisecond, microsecond, nanosecond, precision ), https://tc39.es/proposal-temporal/#sec-temporal-formatsecondsstringpart
String format_seconds_string_part(u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<StringView, u8> const& precision)
{
// 1. Assert: second, millisecond, microsecond and nanosecond are integers.
@@ -979,7 +912,7 @@ String format_seconds_string_part(u8 second, u16 millisecond, u16 microsecond, u
return String::formatted("{}.{}", seconds_string, fraction_string);
}
-// 13.28 GetUnsignedRoundingMode ( roundingMode, isNegative ), https://tc39.es/proposal-temporal/#sec-temporal-getunsignedroundingmode
+// 13.24 GetUnsignedRoundingMode ( roundingMode, isNegative ), https://tc39.es/proposal-temporal/#sec-temporal-getunsignedroundingmode
UnsignedRoundingMode get_unsigned_rounding_mode(StringView rounding_mode, bool is_negative)
{
// 1. If isNegative is true, return the specification type in the third column of Table 14 where the first column is roundingMode and the second column is "negative".
@@ -1033,7 +966,7 @@ UnsignedRoundingMode get_unsigned_rounding_mode(StringView rounding_mode, bool i
// it uses mathematical values which can be arbitrarily (but not infinitely) large.
// Incidentally V8's Temporal implementation does the same :^)
-// 13.29 ApplyUnsignedRoundingMode ( x, r1, r2, unsignedRoundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-applyunsignedroundingmode
+// 13.25 ApplyUnsignedRoundingMode ( x, r1, r2, unsignedRoundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-applyunsignedroundingmode
double apply_unsigned_rounding_mode(double x, double r1, double r2, Optional<UnsignedRoundingMode> const& unsigned_rounding_mode)
{
// 1. If x is equal to r1, return r1.
@@ -1093,7 +1026,7 @@ double apply_unsigned_rounding_mode(double x, double r1, double r2, Optional<Uns
return r2;
}
-// 13.29 ApplyUnsignedRoundingMode ( x, r1, r2, unsignedRoundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-applyunsignedroundingmode
+// 13.25 ApplyUnsignedRoundingMode ( x, r1, r2, unsignedRoundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-applyunsignedroundingmode
Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResult const& x, Crypto::SignedBigInteger const& r1, Crypto::SignedBigInteger const& r2, Optional<UnsignedRoundingMode> const& unsigned_rounding_mode, Crypto::UnsignedBigInteger const& increment)
{
// 1. If x is equal to r1, return r1.
@@ -1153,7 +1086,7 @@ Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResu
return r2;
}
-// 13.30 RoundNumberToIncrement ( x, increment, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement
+// 13.26 RoundNumberToIncrement ( x, increment, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement
double round_number_to_increment(double x, u64 increment, StringView rounding_mode)
{
VERIFY(rounding_mode == "ceil"sv || rounding_mode == "floor"sv || rounding_mode == "trunc"sv || rounding_mode == "halfExpand"sv);
@@ -1199,7 +1132,7 @@ double round_number_to_increment(double x, u64 increment, StringView rounding_mo
return rounded * static_cast<double>(increment);
}
-// 13.30 RoundNumberToIncrement ( x, increment, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement
+// 13.26 RoundNumberToIncrement ( x, increment, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const& x, u64 increment, StringView rounding_mode)
{
VERIFY(rounding_mode == "ceil"sv || rounding_mode == "floor"sv || rounding_mode == "trunc"sv || rounding_mode == "halfExpand"sv);
@@ -1254,7 +1187,7 @@ Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger cons
return rounded.multiplied_by(increment_big_int);
}
-// 13.32 ParseISODateTime ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parseisodatetime
+// 13.28 ParseISODateTime ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parseisodatetime
ThrowCompletionOr<ISODateTime> parse_iso_date_time(GlobalObject& global_object, ParseResult const& parse_result)
{
auto& vm = global_object.vm();
@@ -1401,7 +1334,7 @@ ThrowCompletionOr<ISODateTime> parse_iso_date_time(GlobalObject& global_object,
return ISODateTime { .year = year_mv, .month = month_mv, .day = day_mv, .hour = hour_mv, .minute = minute_mv, .second = second_mv, .millisecond = millisecond_mv, .microsecond = microsecond_mv, .nanosecond = nanosecond_mv, .calendar = move(calendar_val) };
}
-// 13.33 ParseTemporalInstantString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalinstantstring
+// 13.29 ParseTemporalInstantString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalinstantstring
ThrowCompletionOr<TemporalInstant> parse_temporal_instant_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1433,7 +1366,7 @@ ThrowCompletionOr<TemporalInstant> parse_temporal_instant_string(GlobalObject& g
return TemporalInstant { .year = result.year, .month = result.month, .day = result.day, .hour = result.hour, .minute = result.minute, .second = result.second, .millisecond = result.millisecond, .microsecond = result.microsecond, .nanosecond = result.nanosecond, .time_zone_offset = move(offset_string) };
}
-// 13.34 ParseTemporalZonedDateTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalzoneddatetimestring
+// 13.30 ParseTemporalZonedDateTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalzoneddatetimestring
ThrowCompletionOr<TemporalZonedDateTime> parse_temporal_zoned_date_time_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1455,7 +1388,7 @@ ThrowCompletionOr<TemporalZonedDateTime> parse_temporal_zoned_date_time_string(G
return TemporalZonedDateTime { .date_time = move(result), .time_zone = move(time_zone_result) };
}
-// 13.35 ParseTemporalCalendarString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalcalendarstring
+// 13.31 ParseTemporalCalendarString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalcalendarstring
ThrowCompletionOr<String> parse_temporal_calendar_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1480,7 +1413,7 @@ ThrowCompletionOr<String> parse_temporal_calendar_string(GlobalObject& global_ob
return id.value();
}
-// 13.36 ParseTemporalDateString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldatestring
+// 13.32 ParseTemporalDateString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldatestring
ThrowCompletionOr<TemporalDate> parse_temporal_date_string(GlobalObject& global_object, String const& iso_string)
{
// 1. Let parts be ? ParseTemporalDateTimeString(isoString).
@@ -1490,7 +1423,7 @@ ThrowCompletionOr<TemporalDate> parse_temporal_date_string(GlobalObject& global_
return TemporalDate { .year = parts.year, .month = parts.month, .day = parts.day, .calendar = move(parts.calendar) };
}
-// 13.37 ParseTemporalDateTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldatetimestring
+// 13.33 ParseTemporalDateTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldatetimestring
ThrowCompletionOr<ISODateTime> parse_temporal_date_time_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1510,7 +1443,7 @@ ThrowCompletionOr<ISODateTime> parse_temporal_date_time_string(GlobalObject& glo
return parse_iso_date_time(global_object, *parse_result);
}
-// 13.38 ParseTemporalDurationString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldurationstring
+// 13.34 ParseTemporalDurationString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldurationstring
ThrowCompletionOr<DurationRecord> parse_temporal_duration_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1650,7 +1583,7 @@ ThrowCompletionOr<DurationRecord> parse_temporal_duration_string(GlobalObject& g
return create_duration_record(global_object, years * factor, months * factor, weeks * factor, days * factor, hours * factor, floor(minutes) * factor, floor(seconds) * factor, floor(milliseconds) * factor, floor(microseconds) * factor, floor(nanoseconds) * factor);
}
-// 13.39 ParseTemporalMonthDayString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalmonthdaystring
+// 13.35 ParseTemporalMonthDayString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalmonthdaystring
ThrowCompletionOr<TemporalMonthDay> parse_temporal_month_day_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1682,7 +1615,7 @@ ThrowCompletionOr<TemporalMonthDay> parse_temporal_month_day_string(GlobalObject
return TemporalMonthDay { .year = year, .month = result.month, .day = result.day, .calendar = move(result.calendar) };
}
-// 13.40 ParseTemporalRelativeToString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalrelativetostring
+// 13.36 ParseTemporalRelativeToString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalrelativetostring
ThrowCompletionOr<TemporalZonedDateTime> parse_temporal_relative_to_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1727,7 +1660,7 @@ ThrowCompletionOr<TemporalZonedDateTime> parse_temporal_relative_to_string(Globa
return TemporalZonedDateTime { .date_time = move(result), .time_zone = { .z = z, .offset_string = move(offset_string), .name = move(time_zone) } };
}
-// 13.41 ParseTemporalTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimestring
+// 13.37 ParseTemporalTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimestring
ThrowCompletionOr<TemporalTime> parse_temporal_time_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1750,7 +1683,7 @@ ThrowCompletionOr<TemporalTime> parse_temporal_time_string(GlobalObject& global_
return TemporalTime { .hour = result.hour, .minute = result.minute, .second = result.second, .millisecond = result.millisecond, .microsecond = result.microsecond, .nanosecond = result.nanosecond, .calendar = move(result.calendar) };
}
-// 13.42 ParseTemporalTimeZoneString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimezonestring
+// 13.38 ParseTemporalTimeZoneString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimezonestring
ThrowCompletionOr<TemporalTimeZone> parse_temporal_time_zone_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1789,7 +1722,7 @@ ThrowCompletionOr<TemporalTimeZone> parse_temporal_time_zone_string(GlobalObject
return TemporalTimeZone { .z = false, .offset_string = Optional<String>(move(offset_string)), .name = Optional<String>(move(name)) };
}
-// 13.43 ParseTemporalYearMonthString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalyearmonthstring
+// 13.39 ParseTemporalYearMonthString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalyearmonthstring
ThrowCompletionOr<TemporalYearMonth> parse_temporal_year_month_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
@@ -1812,7 +1745,7 @@ ThrowCompletionOr<TemporalYearMonth> parse_temporal_year_month_string(GlobalObje
return TemporalYearMonth { .year = result.year, .month = result.month, .day = result.day, .calendar = move(result.calendar) };
}
-// 13.44 ToPositiveInteger ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-topositiveinteger
+// 13.40 ToPositiveInteger ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-topositiveinteger
ThrowCompletionOr<double> to_positive_integer(GlobalObject& global_object, Value argument)
{
auto& vm = global_object.vm();
@@ -1830,7 +1763,7 @@ ThrowCompletionOr<double> to_positive_integer(GlobalObject& global_object, Value
return integer;
}
-// 13.47 PrepareTemporalFields ( fields, fieldNames, requiredFields ), https://tc39.es/proposal-temporal/#sec-temporal-preparetemporalfields
+// 13.43 PrepareTemporalFields ( fields, fieldNames, requiredFields ), https://tc39.es/proposal-temporal/#sec-temporal-preparetemporalfields
ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject& global_object, Object const& fields, Vector<String> const& field_names, Vector<StringView> const& required_fields)
{
auto& vm = global_object.vm();
@@ -1879,7 +1812,7 @@ ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject& global_object,
return result;
}
-// 13.48 PreparePartialTemporalFields ( fields, fieldNames ), https://tc39.es/proposal-temporal/#sec-temporal-preparepartialtemporalfields
+// 13.44 PreparePartialTemporalFields ( fields, fieldNames ), https://tc39.es/proposal-temporal/#sec-temporal-preparepartialtemporalfields
ThrowCompletionOr<Object*> prepare_partial_temporal_fields(GlobalObject& global_object, Object const& fields, Vector<String> const& field_names)
{
auto& vm = global_object.vm();
@@ -1924,5 +1857,4 @@ ThrowCompletionOr<Object*> prepare_partial_temporal_fields(GlobalObject& global_
// 5. Return result.
return result;
}
-
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h
index 4217814adc..911a1c99cf 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h
@@ -41,6 +41,12 @@ enum class OptionType {
Number
};
+enum class UnitGroup {
+ Date,
+ Time,
+ DateTime,
+};
+
struct ISODateTime {
i32 year;
u8 month;
@@ -115,6 +121,8 @@ struct SecondsStringPrecision {
u32 increment;
};
+struct TemporalUnitRequired { };
+
ThrowCompletionOr<MarkedVector<Value>> iterable_to_list_of_type(GlobalObject&, Value items, Vector<OptionType> const& element_types);
ThrowCompletionOr<Object*> get_options_object(GlobalObject&, Value options);
ThrowCompletionOr<Value> get_option(GlobalObject&, Object const& options, PropertyKey const& property, Vector<OptionType> const& types, Vector<StringView> const& values, Value fallback);
@@ -131,11 +139,8 @@ ThrowCompletionOr<String> to_show_offset_option(GlobalObject&, Object const& nor
ThrowCompletionOr<u64> to_temporal_rounding_increment(GlobalObject&, Object const& normalized_options, Optional<double> dividend, bool inclusive);
ThrowCompletionOr<u64> to_temporal_date_time_rounding_increment(GlobalObject&, Object const& normalized_options, StringView smallest_unit);
ThrowCompletionOr<SecondsStringPrecision> to_seconds_string_precision(GlobalObject&, Object const& normalized_options);
-ThrowCompletionOr<Optional<String>> to_largest_temporal_unit(GlobalObject&, Object const& normalized_options, Vector<StringView> const& disallowed_units, Optional<String> fallback, Optional<String> auto_value = {});
-ThrowCompletionOr<Optional<String>> to_smallest_temporal_unit(GlobalObject&, Object const& normalized_options, Vector<StringView> const& disallowed_units, Optional<String> fallback);
-ThrowCompletionOr<String> to_temporal_duration_total_unit(GlobalObject& global_object, Object const& normalized_options);
+ThrowCompletionOr<Optional<String>> get_temporal_unit(GlobalObject&, Object const& normalized_options, PropertyKey const&, UnitGroup, Variant<TemporalUnitRequired, Optional<StringView>> const& default_, Vector<StringView> const& extra_values = {});
ThrowCompletionOr<Value> to_relative_temporal_object(GlobalObject&, Object const& options);
-ThrowCompletionOr<void> validate_temporal_unit_range(GlobalObject&, StringView largest_unit, StringView smallest_unit);
StringView larger_of_two_temporal_units(StringView, StringView);
ThrowCompletionOr<Object*> merge_largest_unit_option(GlobalObject&, Object const* options, String largest_unit);
Optional<u16> maximum_temporal_duration_rounding_increment(StringView unit);
@@ -164,7 +169,7 @@ ThrowCompletionOr<double> to_positive_integer(GlobalObject&, Value argument);
ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject&, Object const& fields, Vector<String> const& field_names, Vector<StringView> const& required_fields);
ThrowCompletionOr<Object*> prepare_partial_temporal_fields(GlobalObject&, Object const& fields, Vector<String> const& field_names);
-// 13.45 ToIntegerThrowOnInfinity ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-tointegerthrowoninfinity
+// 13.41 ToIntegerThrowOnInfinity ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-tointegerthrowoninfinity
template<typename... Args>
ThrowCompletionOr<double> to_integer_throw_on_infinity(GlobalObject& global_object, Value argument, ErrorType error_type, Args... args)
{
@@ -183,7 +188,7 @@ ThrowCompletionOr<double> to_integer_throw_on_infinity(GlobalObject& global_obje
return integer;
}
-// 13.46 ToIntegerWithoutRounding ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-tointegerwithoutrounding
+// 13.42 ToIntegerWithoutRounding ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-tointegerwithoutrounding
template<typename... Args>
ThrowCompletionOr<double> to_integer_without_rounding(GlobalObject& global_object, Value argument, ErrorType error_type, Args... args)
{
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.cpp
index 9171a48cef..4f9f22db96 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.cpp
@@ -205,13 +205,17 @@ JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::date_until)
// 6. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(global_object, vm.argument(2)));
- // 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" », "auto", "day").
- auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv }, "auto"sv, "day"sv));
+ // 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", date, "auto").
+ auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::Date, { "auto"sv }));
- // 8. Let result be DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], largestUnit).
+ // 8. If largestUnit is "auto", set largestUnit to "day".
+ if (largest_unit == "auto")
+ largest_unit = "day"sv;
+
+ // 9. Let result be DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], largestUnit).
auto result = difference_iso_date(global_object, one->iso_year(), one->iso_month(), one->iso_day(), two->iso_year(), two->iso_month(), two->iso_day(), *largest_unit);
- // 9. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
+ // 10. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(global_object, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0));
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp
index 9853d7c198..7ce28068ac 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp
@@ -356,8 +356,8 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
// 7. Let largestUnitPresent be true.
bool largest_unit_present = true;
- // 8. Let smallestUnit be ? ToSmallestTemporalUnit(roundTo, « », undefined).
- auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *round_to, {}, {}));
+ // 8. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", datetime, undefined).
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *round_to, vm.names.smallestUnit, UnitGroup::DateTime, Optional<StringView> {}));
// 9. If smallestUnit is undefined, then
if (!smallest_unit.has_value()) {
@@ -374,8 +374,8 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
// 11. Set defaultLargestUnit to ! LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit).
default_largest_unit = larger_of_two_temporal_units(default_largest_unit, *smallest_unit);
- // 12. Let largestUnit be ? ToLargestTemporalUnit(roundTo, « », undefined).
- auto largest_unit = TRY(to_largest_temporal_unit(global_object, *round_to, {}, {}));
+ // 12. Let largestUnit be ? GetTemporalUnit(roundTo, "largestUnit", datetime, undefined, « "auto" »).
+ auto largest_unit = TRY(get_temporal_unit(global_object, *round_to, vm.names.largestUnit, UnitGroup::DateTime, Optional<StringView> {}, { "auto"sv }));
// 13. If largestUnit is undefined, then
if (!largest_unit.has_value()) {
@@ -397,8 +397,9 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalMissingUnits);
}
- // 16. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
- TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
+ // 16. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
+ if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 17. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *round_to, "halfExpand"sv));
@@ -473,11 +474,11 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
// 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));
+ // 7. Let unit be ? GetTemporalUnit(totalOf, "unit", datetime, required).
+ auto unit = TRY(get_temporal_unit(global_object, *total_of, vm.names.unit, UnitGroup::DateTime, TemporalUnitRequired {}));
// 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));
+ auto unbalance_result = TRY(unbalance_duration_relative(global_object, duration->years(), duration->months(), duration->weeks(), duration->days(), *unit, relative_to));
// 9. Let intermediate be undefined.
ZonedDateTime* intermediate = nullptr;
@@ -489,10 +490,10 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
}
// 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(), Crypto::SignedBigInteger::create_from(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(), Crypto::SignedBigInteger::create_from(duration->nanoseconds()), *unit, intermediate));
// 12. Let roundRecord 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_record = 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));
+ auto round_record = 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));
// 13. Let roundResult be roundRecord.[[DurationRecord]].
auto& round_result = round_record.duration_record;
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp
index 7cd6375fc2..7d630165e2 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp
@@ -286,6 +286,8 @@ ThrowCompletionOr<String> temporal_instant_to_string(GlobalObject& global_object
// 8.5.10 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
ThrowCompletionOr<Duration*> difference_temporal_instant(GlobalObject& global_object, DifferenceOperation operation, Instant const& instant, Value other_value, Value options_value)
{
+ auto& vm = global_object.vm();
+
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
@@ -295,42 +297,47 @@ ThrowCompletionOr<Duration*> difference_temporal_instant(GlobalObject& global_ob
// 3. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(global_object, options_value));
- // 4. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond").
- auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv));
+ // 4. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", time, "nanosecond").
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::Time, { "nanosecond"sv }));
// 5. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("second", smallestUnit).
auto default_largest_unit = larger_of_two_temporal_units("second"sv, *smallest_unit);
- // 6. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", defaultLargestUnit).
- auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, default_largest_unit));
+ // 6. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", time, "auto").
+ auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::Time, { "auto"sv }));
+
+ // 7. If largestUnit is "auto", set largestUnit to defaultLargestUnit.
+ if (largest_unit == "auto"sv)
+ largest_unit = default_largest_unit;
- // 7. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
- TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
+ // 8. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
+ if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
- // 8. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
+ // 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
- // 9. If operation is since, then
+ // 10. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
- // 10. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
+ // 11. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
- // 11. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
+ // 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, *maximum, false));
- // 12. Let roundedNs be ! DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
+ // 13. Let roundedNs be ! DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
auto* rounded_ns = difference_instant(global_object, instant.nanoseconds(), other->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
- // 13. Assert: The following steps cannot fail due to overflow in the Number domain because abs(roundedNs) ≤ 2 × nsMaxInstant.
+ // 14. Assert: The following steps cannot fail due to overflow in the Number domain because abs(roundedNs) ≤ 2 × nsMaxInstant.
- // 14. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs, largestUnit).
+ // 15. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs, largestUnit).
auto result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, rounded_ns->big_integer(), *largest_unit));
- // 15. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
+ // 16. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp
index 54c0ee6ba6..4a398050e5 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp
@@ -203,8 +203,8 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round)
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. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, required).
+ auto smallest_unit_value = TRY(get_temporal_unit(global_object, *round_to, vm.names.smallestUnit, UnitGroup::Time, TemporalUnitRequired {}));
// 6. If smallestUnit is undefined, throw a RangeError exception.
if (!smallest_unit_value.has_value())
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp
index 297e91a115..e4bb99e8fb 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp
@@ -510,20 +510,22 @@ ThrowCompletionOr<Duration*> difference_temporal_plain_date(GlobalObject& global
// 4. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(global_object, options_value));
- // 5. Let disallowedUnits be « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ».
- auto disallowed_units = Vector<StringView> { "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv };
+ // 5. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", date, "day").
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::Date, { "day"sv }));
- // 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "day").
- auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "day"sv));
-
- // 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit).
+ // 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit).
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
- // 8. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", defaultLargestUnit).
- auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, default_largest_unit));
+ // 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", date, "auto").
+ auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::Date, { "auto"sv }));
+
+ // 8. If largestUnit is "auto", set largestUnit to defaultLargestUnit.
+ if (largest_unit == "auto"sv)
+ largest_unit = default_largest_unit;
- // 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
- TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
+ // 9. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
+ if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp
index 6bd22b1bbe..3541a0e945 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp
@@ -409,46 +409,51 @@ ThrowCompletionOr<Duration*> difference_temporal_plain_date_time(GlobalObject& g
// 4. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(global_object, options_value));
- // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond").
- auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv));
+ // 5. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", datetime, "nanosecond").
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::DateTime, { "nanosecond"sv }));
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit).
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
- // 7. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit).
- auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit));
+ // 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", datetime, "auto").
+ auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::DateTime, { "auto"sv }));
- // 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
- TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
+ // 8. If largestUnit is "auto", set largestUnit to defaultLargestUnit.
+ if (largest_unit == "auto"sv)
+ largest_unit = default_largest_unit;
- // 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
+ // 9. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
+ if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
+
+ // 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
- // 10. If operation is since, then
+ // 11. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
- // 11. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
+ // 12. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
- // 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
+ // 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
- // 13. Let diff be ? DifferenceISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], dateTime.[[Calendar]], largestUnit, options).
+ // 14. Let diff be ? DifferenceISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], dateTime.[[Calendar]], largestUnit, options).
auto diff = TRY(difference_iso_date_time(global_object, 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(), other->iso_year(), other->iso_month(), other->iso_day(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond(), date_time.calendar(), *largest_unit, options));
- // 14. Let relativeTo be ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
+ // 15. Let relativeTo be ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
auto* relative_to = MUST(create_temporal_date(global_object, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.calendar()));
- // 15. Let roundResult be (? RoundDuration(diff.[[Years]], diff.[[Months]], diff.[[Weeks]], diff.[[Days]], diff.[[Hours]], diff.[[Minutes]], diff.[[Seconds]], diff.[[Milliseconds]], diff.[[Microseconds]], diff.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo)).[[DurationRecord]].
+ // 16. Let roundResult be (? RoundDuration(diff.[[Years]], diff.[[Months]], diff.[[Weeks]], diff.[[Days]], diff.[[Hours]], diff.[[Minutes]], diff.[[Seconds]], diff.[[Milliseconds]], diff.[[Microseconds]], diff.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo)).[[DurationRecord]].
auto round_result = TRY(round_duration(global_object, diff.years, diff.months, diff.weeks, diff.days, diff.hours, diff.minutes, diff.seconds, diff.milliseconds, diff.microseconds, diff.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, relative_to)).duration_record;
- // 16. Let result be ? BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], largestUnit).
+ // 17. Let result be ? BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], largestUnit).
auto result = MUST(balance_duration(global_object, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger::create_from((i64)round_result.nanoseconds), *largest_unit));
- // 17. Return ! CreateTemporalDuration(sign × roundResult.[[Years]], sign × roundResult.[[Months]], sign × roundResult.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
+ // 18. Return ! CreateTemporalDuration(sign × roundResult.[[Years]], sign × roundResult.[[Months]], sign × roundResult.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(global_object, sign * round_result.years, sign * round_result.months, sign * round_result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTimePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTimePrototype.cpp
index d864b2b6e3..9203d510c2 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTimePrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTimePrototype.cpp
@@ -545,26 +545,19 @@ JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::round)
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 }, {}));
+ // 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, required, « "day" »).
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *round_to, vm.names.smallestUnit, UnitGroup::Time, TemporalUnitRequired {}, { "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;
-
- // 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
+ // 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *round_to, "halfExpand"));
- // 9. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(roundTo, smallestUnit).
- auto rounding_increment = TRY(to_temporal_date_time_rounding_increment(global_object, *round_to, smallest_unit));
+ // 8. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(roundTo, smallestUnit).
+ auto rounding_increment = TRY(to_temporal_date_time_rounding_increment(global_object, *round_to, *smallest_unit));
- // 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);
+ // 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).
+ 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);
- // 11. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], dateTime.[[Calendar]]).
+ // 10. 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()));
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp
index 0c17238d79..df8b6011d4 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp
@@ -622,6 +622,8 @@ DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 micro
// 4.5.14 DifferenceTemporalPlainTime ( operation, temporalTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaintime
ThrowCompletionOr<Duration*> difference_temporal_plain_time(GlobalObject& global_object, DifferenceOperation operation, PlainTime const& temporal_time, Value other_value, Value options_value)
{
+ auto& vm = global_object.vm();
+
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
@@ -631,40 +633,45 @@ ThrowCompletionOr<Duration*> difference_temporal_plain_time(GlobalObject& global
// 3. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(global_object, options_value));
- // 4. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond").
- auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv));
+ // 4. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", time, "nanosecond").
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::Time, { "nanosecond"sv }));
+
+ // 5. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", time, "auto").
+ auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::Time, { "auto"sv }));
- // 5. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", "hour").
- auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, "hour"sv));
+ // 6. If largestUnit is "auto", set largestUnit to "hour".
+ if (largest_unit == "auto"sv)
+ largest_unit = "hour"sv;
- // 6. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
- TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
+ // 7. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
+ if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
- // 7. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
+ // 8. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
- // 8. If operation is since, then
+ // 9. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
- // 9. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
+ // 10. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
- // 10. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
+ // 11. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
- // 11. Let result be ! DifferenceTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
+ // 12. Let result be ! DifferenceTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
auto result = difference_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(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond());
- // 12. Set result to (? RoundDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode)).[[DurationRecord]].
+ // 13. Set result to (? RoundDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode)).[[DurationRecord]].
auto rounded_result = TRY(round_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode)).duration_record;
- // 13. Set result to ? BalanceDuration(0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], largestUnit).
+ // 14. Set result to ? BalanceDuration(0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], largestUnit).
result = MUST(balance_duration(global_object, 0, rounded_result.hours, rounded_result.minutes, rounded_result.seconds, rounded_result.milliseconds, rounded_result.microseconds, Crypto::SignedBigInteger { (i32)rounded_result.nanoseconds }, *largest_unit));
- // 14. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
+ // 15. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp
index 2776c2ad71..8a200328d6 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp
@@ -293,44 +293,37 @@ JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::round)
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. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, required).
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *round_to, vm.names.smallestUnit, UnitGroup::Time, TemporalUnitRequired {}));
- // 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;
-
- // 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
+ // 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *round_to, "halfExpand"));
double maximum;
- // 9. If smallestUnit is "hour", then
+ // 8. If smallestUnit is "hour", then
if (smallest_unit == "hour"sv) {
// a. Let maximum be 24.
maximum = 24;
}
- // 10. Else if smallestUnit is "minute" or "second", then
+ // 9. 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;
}
- // 11. Else,
+ // 10. Else,
else {
// a. Let maximum be 1000.
maximum = 1000;
}
- // 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo, maximum, false).
+ // 11. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *round_to, maximum, false));
- // 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);
+ // 12. 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);
- // 14. Return ? CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
+ // 13. 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));
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp
index 5fa5f0b553..dce0575d85 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp
@@ -278,66 +278,76 @@ ThrowCompletionOr<Duration*> difference_temporal_plain_year_month(GlobalObject&
// 5. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(global_object, options_value));
- // 6. Let disallowedUnits be « "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ».
- auto disallowed_units = Vector<StringView> { "week"sv, "day"sv, "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv };
+ // 6. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", date, "month").
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::Date, { "month"sv }));
- // 7. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "month").
- auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "month"sv));
+ // 7. If smallestUnit is "week" or "day", throw a RangeError exception.
+ if (smallest_unit == "week"sv || smallest_unit == "day"sv)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, *smallest_unit, "smallestUnit"sv);
- // 8. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", "year").
- auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, "year"sv));
+ // 8. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", date, "auto").
+ auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::Date, { "auto"sv }));
- // 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
- TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
+ // 9. If largestUnit is "week" or "day", throw a RangeError exception.
+ if (largest_unit == "week"sv || largest_unit == "day"sv)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, *largest_unit, "largestUnit"sv);
- // 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
+ // 10. If largestUnit is "auto", set largestUnit to "year".
+ if (largest_unit == "auto"sv)
+ largest_unit = "year"sv;
+
+ // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
+ if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
+
+ // 12. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
- // 11. If operation is since, then
+ // 13. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
- // 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
+ // 14. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false));
- // 13. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
+ // 15. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv }));
- // 14. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»).
+ // 16. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»).
auto* other_fields = TRY(prepare_temporal_fields(global_object, *other, field_names, {}));
- // 15. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽).
+ // 17. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽).
MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1)));
- // 16. Let otherDate be ? CalendarDateFromFields(calendar, otherFields).
+ // 18. Let otherDate be ? CalendarDateFromFields(calendar, otherFields).
auto* other_date = TRY(calendar_date_from_fields(global_object, calendar, *other_fields));
- // 17. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
+ // 19. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
auto* this_fields = TRY(prepare_temporal_fields(global_object, year_month, field_names, {}));
- // 18. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽).
+ // 20. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽).
MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1)));
- // 19. Let thisDate be ? CalendarDateFromFields(calendar, thisFields).
+ // 21. Let thisDate be ? CalendarDateFromFields(calendar, thisFields).
auto* this_date = TRY(calendar_date_from_fields(global_object, calendar, *this_fields));
- // 20. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
+ // 22. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit));
- // 21. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions).
+ // 23. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions).
auto* duration = TRY(calendar_date_until(global_object, calendar, this_date, other_date, *until_options));
auto result = DurationRecord { duration->years(), duration->months(), 0, 0, 0, 0, 0, 0, 0, 0 };
- // 22. If smallestUnit is not "month" or roundingIncrement ≠ 1, then
+ // 24. If smallestUnit is not "month" or roundingIncrement ≠ 1, then
if (smallest_unit != "month"sv || rounding_increment != 1) {
// a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, thisDate)).[[DurationRecord]].
result = TRY(round_duration(global_object, result.years, result.months, 0, 0, 0, 0, 0, 0, 0, 0, rounding_increment, *smallest_unit, rounding_mode, this_date)).duration_record;
}
- // 23. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
+ // 25. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(global_object, sign * result.years, sign * result.months, 0, 0, 0, 0, 0, 0, 0, 0));
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp
index 343b6d9d60..f4e4f0d2e8 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp
@@ -557,34 +557,39 @@ ThrowCompletionOr<Duration*> difference_temporal_zoned_date_time(GlobalObject& g
// 4. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(global_object, options_value));
- // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond").
- auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv));
+ // 5. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", datetime, "nanosecond").
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::DateTime, { "nanosecond"sv }));
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("hour", smallestUnit).
auto default_largest_unit = larger_of_two_temporal_units("hour"sv, *smallest_unit);
- // 7. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit).
- auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit));
+ // 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", datetime, "auto").
+ auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::DateTime, { "auto"sv }));
- // 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
- TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
+ // 8. If largestUnit is "auto", set largestUnit to defaultLargestUnit.
+ if (largest_unit == "auto"sv)
+ largest_unit = default_largest_unit;
- // 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
+ // 9. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
+ if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
+ return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
+
+ // 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
- // 10. If operation is since, then
+ // 11. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
- // 11. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
+ // 12. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
- // 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
+ // 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
- // 13. If largestUnit is not one of "year", "month", "week", or "day", then
+ // 14. If largestUnit is not one of "year", "month", "week", or "day", then
if (!largest_unit->is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
// a. Let differenceNs be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
auto* difference_ns = difference_instant(global_object, zoned_date_time.nanoseconds(), other->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
@@ -598,25 +603,25 @@ ThrowCompletionOr<Duration*> difference_temporal_zoned_date_time(GlobalObject& g
return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * balance_result.hours, sign * balance_result.minutes, sign * balance_result.seconds, sign * balance_result.milliseconds, sign * balance_result.microseconds, sign * balance_result.nanoseconds));
}
- // 14. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
+ // 15. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
if (!TRY(time_zone_equals(global_object, zoned_date_time.time_zone(), other->time_zone()))) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentTimeZones);
}
- // 15. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
+ // 16. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit));
- // 16. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], largestUnit, untilOptions).
+ // 17. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], largestUnit, untilOptions).
auto difference = TRY(difference_zoned_date_time(global_object, zoned_date_time.nanoseconds(), other->nanoseconds(), zoned_date_time.time_zone(), zoned_date_time.calendar(), *largest_unit, until_options));
- // 17. Let roundResult be (? RoundDuration(difference.[[Years]], difference.[[Months]], difference.[[Weeks]], difference.[[Days]], difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], difference.[[Milliseconds]], difference.[[Microseconds]], difference.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime)).[[DurationRecord]].
+ // 18. Let roundResult be (? RoundDuration(difference.[[Years]], difference.[[Months]], difference.[[Weeks]], difference.[[Days]], difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], difference.[[Milliseconds]], difference.[[Microseconds]], difference.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime)).[[DurationRecord]].
auto round_result = TRY(round_duration(global_object, difference.years, difference.months, difference.weeks, difference.days, difference.hours, difference.minutes, difference.seconds, difference.milliseconds, difference.microseconds, difference.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, &zoned_date_time)).duration_record;
- // 18. Let result be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime).
+ // 19. Let result be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime).
auto result = TRY(adjust_rounded_duration_days(global_object, round_result.years, round_result.months, round_result.weeks, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, round_result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, &zoned_date_time));
- // 19. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
+ // 20. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(global_object, sign * result.years, sign * result.months, sign * result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp
index ff806a551b..8742ded879 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp
@@ -979,69 +979,62 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::round)
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 }, {}));
+ // 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, required, « "day" »).
+ auto smallest_unit = TRY(get_temporal_unit(global_object, *round_to, vm.names.smallestUnit, UnitGroup::Time, TemporalUnitRequired {}, { "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;
-
- // 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
+ // 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *round_to, "halfExpand"sv));
- // 9. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(options, smallestUnit).
- auto rounding_increment = TRY(to_temporal_date_time_rounding_increment(global_object, *round_to, smallest_unit));
+ // 8. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(options, smallestUnit).
+ auto rounding_increment = TRY(to_temporal_date_time_rounding_increment(global_object, *round_to, *smallest_unit));
- // 10. Let timeZone be zonedDateTime.[[TimeZone]].
+ // 9. Let timeZone be zonedDateTime.[[TimeZone]].
auto& time_zone = zoned_date_time->time_zone();
- // 11. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]).
+ // 10. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]).
auto* instant = MUST(create_temporal_instant(global_object, zoned_date_time->nanoseconds()));
- // 12. Let calendar be zonedDateTime.[[Calendar]].
+ // 11. Let calendar be zonedDateTime.[[Calendar]].
auto& calendar = zoned_date_time->calendar();
- // 13. Let temporalDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar).
+ // 12. 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));
- // 14. Let isoCalendar be ! GetISO8601Calendar().
+ // 13. Let isoCalendar be ! GetISO8601Calendar().
auto* iso_calendar = get_iso8601_calendar(global_object);
- // 15. Let dtStart be ? CreateTemporalDateTime(temporalDateTime.[[ISOYear]], temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], 0, 0, 0, 0, 0, 0, isoCalendar).
+ // 14. 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));
- // 16. Let instantStart be ? BuiltinTimeZoneGetInstantFor(timeZone, dtStart, "compatible").
+ // 15. 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));
- // 17. Let startNs be instantStart.[[Nanoseconds]].
+ // 16. Let startNs be instantStart.[[Nanoseconds]].
auto& start_ns = instant_start->nanoseconds();
- // 18. Let endNs be ? AddZonedDateTime(startNs, timeZone, zonedDateTime.[[Calendar]], 0, 0, 0, 1, 0, 0, 0, 0, 0, 0).
+ // 17. 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));
- // 19. Let dayLengthNs be ℝ(endNs - startNs).
+ // 18. Let dayLengthNs be ℝ(endNs - startNs).
auto day_length_ns = end_ns->big_integer().minus(start_ns.big_integer()).to_double();
- // 20. If dayLengthNs is 0, then
+ // 19. If dayLengthNs is 0, then
if (day_length_ns == 0) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalZonedDateTimeRoundZeroLengthDay);
}
- // 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);
+ // 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).
+ 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);
- // 22. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
+ // 21. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(global_object, &time_zone, *instant));
- // 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).
+ // 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).
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));
- // 24. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
+ // 23. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
return MUST(create_temporal_zoned_date_time(global_object, *epoch_nanoseconds, time_zone, calendar));
}
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.since.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.since.js
index cc83eeb8d1..2eeaeb2412 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.since.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.since.js
@@ -175,7 +175,7 @@ describe("errors", () => {
dateOne.since(dateTwo, pluralSmallestUnitOptions);
}).toThrowWithMessage(
RangeError,
- `${smallestUnit} is not a valid value for option smallestUnit`
+ `${smallestUnit}s is not a valid value for option smallestUnit`
);
}
@@ -194,7 +194,7 @@ describe("errors", () => {
dateOne.since(dateTwo, pluralLargestUnitOptions);
}).toThrowWithMessage(
RangeError,
- `${largestUnit} is not a valid value for option largestUnit`
+ `${largestUnit}s is not a valid value for option largestUnit`
);
}
});
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.until.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.until.js
index 34368074b9..a83be66f73 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.until.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.until.js
@@ -170,7 +170,7 @@ describe("errors", () => {
dateOne.until(dateTwo, pluralSmallestUnitOptions);
}).toThrowWithMessage(
RangeError,
- `${smallestUnit} is not a valid value for option smallestUnit`
+ `${smallestUnit}s is not a valid value for option smallestUnit`
);
}
@@ -189,7 +189,7 @@ describe("errors", () => {
dateOne.until(dateTwo, pluralLargestUnitOptions);
}).toThrowWithMessage(
RangeError,
- `${largestUnit} is not a valid value for option largestUnit`
+ `${largestUnit}s is not a valid value for option largestUnit`
);
}
});