diff options
author | Linus Groh <mail@linusgroh.de> | 2022-06-09 22:42:42 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-06-10 12:39:11 +0200 |
commit | 4c77575ec50c62c0265a53a0cd7d8b151a18e4a8 (patch) | |
tree | 807f01db54ed399e119ab3bbb191ecd30c117b5b | |
parent | b9beb2b120e47a3c446c70f26251c40b8e5ea01e (diff) | |
download | serenity-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
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` ); } }); |