mirror of
https://github.com/RGBCube/serenity
synced 2025-07-28 00:47:45 +00:00
LibJS: Allow out-of-order date ranges to be formatted
This is a normative change to the Intl spec:
769df4b
This commit is contained in:
parent
16d189e96b
commit
415742ab98
4 changed files with 60 additions and 29 deletions
|
@ -49,7 +49,6 @@
|
|||
M(IntlInvalidTime, "Time value must be between -8.64E15 and 8.64E15") \
|
||||
M(IntlInvalidUnit, "Unit {} is not a valid time unit") \
|
||||
M(IntlStartRangeAfterEndRange, "Range start {} is greater than range end {}") \
|
||||
M(IntlStartTimeAfterEndTime, "Start time {} is after end time {}") \
|
||||
M(IntlMinimumExceedsMaximum, "Minimum value {} is larger than maximum value {}") \
|
||||
M(IntlNumberIsNaN, "{} must not be NaN") \
|
||||
M(IntlNumberIsNaNOrInfinity, "Number must not be NaN or Infinity") \
|
||||
|
|
|
@ -934,29 +934,25 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
|
|||
if (isnan(end))
|
||||
return vm.throw_completion<RangeError>(global_object, ErrorType::IntlInvalidTime);
|
||||
|
||||
// 5. If x is greater than y, throw a RangeError exception.
|
||||
if (start > end)
|
||||
return vm.throw_completion<RangeError>(global_object, ErrorType::IntlStartTimeAfterEndTime, start, end);
|
||||
|
||||
// 6. Let tm1 be ToLocalTime(x, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
|
||||
// 5. Let tm1 be ToLocalTime(x, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
|
||||
auto start_local_time = TRY(to_local_time(global_object, start, date_time_format.calendar(), date_time_format.time_zone()));
|
||||
|
||||
// 7. Let tm2 be ToLocalTime(y, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
|
||||
// 6. Let tm2 be ToLocalTime(y, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
|
||||
auto end_local_time = TRY(to_local_time(global_object, end, date_time_format.calendar(), date_time_format.time_zone()));
|
||||
|
||||
// 8. Let rangePatterns be dateTimeFormat.[[RangePatterns]].
|
||||
// 7. Let rangePatterns be dateTimeFormat.[[RangePatterns]].
|
||||
auto range_patterns = date_time_format.range_patterns();
|
||||
|
||||
// 9. Let rangePattern be undefined.
|
||||
// 8. Let rangePattern be undefined.
|
||||
Optional<Unicode::CalendarRangePattern> range_pattern;
|
||||
|
||||
// 10. Let dateFieldsPracticallyEqual be true.
|
||||
// 9. Let dateFieldsPracticallyEqual be true.
|
||||
bool date_fields_practically_equal = true;
|
||||
|
||||
// 11. Let patternContainsLargerDateField be false.
|
||||
// 10. Let patternContainsLargerDateField be false.
|
||||
bool pattern_contains_larger_date_field = false;
|
||||
|
||||
// 12. While dateFieldsPracticallyEqual is true and patternContainsLargerDateField is false, repeat for each row of Table 4 in order, except the header row:
|
||||
// 11. While dateFieldsPracticallyEqual is true and patternContainsLargerDateField is false, repeat for each row of Table 4 in order, except the header row:
|
||||
for_each_range_pattern_field(start_local_time, end_local_time, [&](auto start_value, auto end_value, auto field_name) {
|
||||
// a. Let fieldName be the name given in the Range Pattern Field column of the row.
|
||||
|
||||
|
@ -1061,7 +1057,7 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
|
|||
return IterationDecision::Break;
|
||||
});
|
||||
|
||||
// 13. If dateFieldsPracticallyEqual is true, then
|
||||
// 12. If dateFieldsPracticallyEqual is true, then
|
||||
if (date_fields_practically_equal) {
|
||||
// a. Let pattern be dateTimeFormat.[[Pattern]].
|
||||
auto const& pattern = date_time_format.pattern();
|
||||
|
@ -1083,10 +1079,10 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
|
|||
return result;
|
||||
}
|
||||
|
||||
// 14. Let result be a new empty List.
|
||||
// 13. Let result be a new empty List.
|
||||
Vector<PatternPartitionWithSource> result;
|
||||
|
||||
// 15. If rangePattern is undefined, then
|
||||
// 14. If rangePattern is undefined, then
|
||||
if (!range_pattern.has_value()) {
|
||||
// a. Let rangePattern be rangePatterns.[[Default]].
|
||||
range_pattern = Unicode::get_calendar_default_range_format(date_time_format.data_locale(), date_time_format.calendar());
|
||||
|
@ -1112,7 +1108,7 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
|
|||
// step 3 here: https://unicode.org/reports/tr35/tr35-dates.html#intervalFormats
|
||||
}
|
||||
|
||||
// 16. For each Record { [[Pattern]], [[Source]] } rangePatternPart in rangePattern.[[PatternParts]], do
|
||||
// 15. For each Record { [[Pattern]], [[Source]] } rangePatternPart in rangePattern.[[PatternParts]], do
|
||||
TRY(for_each_range_pattern_with_source(*range_pattern, [&](auto const& pattern, auto source) -> ThrowCompletionOr<void> {
|
||||
// a. Let pattern be rangePatternPart.[[Pattern]].
|
||||
// b. Let source be rangePatternPart.[[Source]].
|
||||
|
@ -1141,7 +1137,7 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
|
|||
return {};
|
||||
}));
|
||||
|
||||
// 17. Return result.
|
||||
// 16. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,12 +36,6 @@ describe("errors", () => {
|
|||
}).toThrowWithMessage(RangeError, "Time value must be between -8.64E15 and 8.64E15");
|
||||
});
|
||||
});
|
||||
|
||||
test("called with values in bad order", () => {
|
||||
expect(() => {
|
||||
Intl.DateTimeFormat().formatRange(new Date(2021), new Date(1989));
|
||||
}).toThrowWithMessage(RangeError, "Start time 2021 is after end time 1989");
|
||||
});
|
||||
});
|
||||
|
||||
const d0 = Date.UTC(1989, 0, 23, 7, 8, 9, 45);
|
||||
|
@ -157,6 +151,14 @@ describe("dateStyle", () => {
|
|||
expect(ja.formatRange(d0, d1)).toBe(d.ja);
|
||||
});
|
||||
});
|
||||
|
||||
test("dates in reverse order", () => {
|
||||
const en = new Intl.DateTimeFormat("en", { dateStyle: "full", timeZone: "UTC" });
|
||||
expect(en.formatRange(d1, d0)).toBe("Tuesday, December 7, 2021 – Monday, January 23, 1989");
|
||||
|
||||
const ja = new Intl.DateTimeFormat("ja", { dateStyle: "full", timeZone: "UTC" });
|
||||
expect(ja.formatRange(d1, d0)).toBe("2021年12月7日火曜日~1989年1月23日月曜日");
|
||||
});
|
||||
});
|
||||
|
||||
describe("timeStyle", () => {
|
||||
|
|
|
@ -36,12 +36,6 @@ describe("errors", () => {
|
|||
}).toThrowWithMessage(RangeError, "Time value must be between -8.64E15 and 8.64E15");
|
||||
});
|
||||
});
|
||||
|
||||
test("called with values in bad order", () => {
|
||||
expect(() => {
|
||||
Intl.DateTimeFormat().formatRangeToParts(new Date(2021), new Date(1989));
|
||||
}).toThrowWithMessage(RangeError, "Start time 2021 is after end time 1989");
|
||||
});
|
||||
});
|
||||
|
||||
const d0 = Date.UTC(1989, 0, 23, 7, 8, 9, 45);
|
||||
|
@ -343,6 +337,46 @@ describe("dateStyle", () => {
|
|||
{ type: "day", value: "07", source: "endRange" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("dates in reverse order", () => {
|
||||
const en = new Intl.DateTimeFormat("en", { dateStyle: "full", timeZone: "UTC" });
|
||||
expect(en.formatRangeToParts(d1, d0)).toEqual([
|
||||
{ type: "weekday", value: "Tuesday", source: "startRange" },
|
||||
{ type: "literal", value: ", ", source: "startRange" },
|
||||
{ type: "month", value: "December", source: "startRange" },
|
||||
{ type: "literal", value: " ", source: "startRange" },
|
||||
{ type: "day", value: "7", source: "startRange" },
|
||||
{ type: "literal", value: ", ", source: "startRange" },
|
||||
{ type: "year", value: "2021", source: "startRange" },
|
||||
{ type: "literal", value: " – ", source: "shared" },
|
||||
{ type: "weekday", value: "Monday", source: "endRange" },
|
||||
{ type: "literal", value: ", ", source: "endRange" },
|
||||
{ type: "month", value: "January", source: "endRange" },
|
||||
{ type: "literal", value: " ", source: "endRange" },
|
||||
{ type: "day", value: "23", source: "endRange" },
|
||||
{ type: "literal", value: ", ", source: "endRange" },
|
||||
{ type: "year", value: "1989", source: "endRange" },
|
||||
]);
|
||||
|
||||
const ja = new Intl.DateTimeFormat("ja", { dateStyle: "full", timeZone: "UTC" });
|
||||
expect(ja.formatRangeToParts(d1, d0)).toEqual([
|
||||
{ type: "year", value: "2021", source: "startRange" },
|
||||
{ type: "literal", value: "年", source: "startRange" },
|
||||
{ type: "month", value: "12", source: "startRange" },
|
||||
{ type: "literal", value: "月", source: "startRange" },
|
||||
{ type: "day", value: "7", source: "startRange" },
|
||||
{ type: "literal", value: "日", source: "startRange" },
|
||||
{ type: "weekday", value: "火曜日", source: "startRange" },
|
||||
{ type: "literal", value: "~", source: "shared" },
|
||||
{ type: "year", value: "1989", source: "endRange" },
|
||||
{ type: "literal", value: "年", source: "endRange" },
|
||||
{ type: "month", value: "1", source: "endRange" },
|
||||
{ type: "literal", value: "月", source: "endRange" },
|
||||
{ type: "day", value: "23", source: "endRange" },
|
||||
{ type: "literal", value: "日", source: "endRange" },
|
||||
{ type: "weekday", value: "月曜日", source: "endRange" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("timeStyle", () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue