mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:17:34 +00:00
LibJS: Selectively display DateTimeFormat day periods as noon
This commit is contained in:
parent
32c07bc6c3
commit
b8d4f8debf
2 changed files with 68 additions and 1 deletions
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/Find.h>
|
||||||
#include <AK/IterationDecision.h>
|
#include <AK/IterationDecision.h>
|
||||||
#include <AK/NumericLimits.h>
|
#include <AK/NumericLimits.h>
|
||||||
#include <LibJS/Runtime/AbstractOperations.h>
|
#include <LibJS/Runtime/AbstractOperations.h>
|
||||||
|
@ -503,6 +504,30 @@ static Optional<StyleAndValue> find_calendar_field(StringView name, Unicode::Cal
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Optional<StringView> resolve_day_period(StringView locale, StringView calendar, Unicode::CalendarPatternStyle style, Span<PatternPartition const> pattern_parts, LocalTime local_time)
|
||||||
|
{
|
||||||
|
// Use the "noon" day period if the locale has it, but only if the time is either exactly 12:00.00 or would be displayed as such.
|
||||||
|
if (local_time.hour == 12) {
|
||||||
|
auto it = find_if(pattern_parts.begin(), pattern_parts.end(), [&](auto const& part) {
|
||||||
|
if (part.type == "minute"sv && local_time.minute != 0)
|
||||||
|
return true;
|
||||||
|
if (part.type == "second"sv && local_time.second != 0)
|
||||||
|
return true;
|
||||||
|
if (part.type == "fractionalSecondDigits"sv && local_time.millisecond != 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it == pattern_parts.end()) {
|
||||||
|
auto noon_symbol = Unicode::get_calendar_day_period_symbol(locale, calendar, style, Unicode::DayPeriod::Noon);
|
||||||
|
if (noon_symbol.has_value())
|
||||||
|
return *noon_symbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unicode::get_calendar_day_period_symbol_for_hour(locale, calendar, style, local_time.hour);
|
||||||
|
}
|
||||||
|
|
||||||
// 11.5.6 FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions ), https://tc39.es/ecma402/#sec-formatdatetimepattern
|
// 11.5.6 FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions ), https://tc39.es/ecma402/#sec-formatdatetimepattern
|
||||||
ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Vector<PatternPartition> pattern_parts, double time, Unicode::CalendarPattern const* range_format_options)
|
ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Vector<PatternPartition> pattern_parts, double time, Unicode::CalendarPattern const* range_format_options)
|
||||||
{
|
{
|
||||||
|
@ -606,7 +631,7 @@ ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(GlobalObjec
|
||||||
auto style = date_time_format.day_period();
|
auto style = date_time_format.day_period();
|
||||||
|
|
||||||
// ii. Let fv be a String value representing the day period of tm in the form given by f; the String value depends upon the implementation and the effective locale of dateTimeFormat.
|
// ii. Let fv be a String value representing the day period of tm in the form given by f; the String value depends upon the implementation and the effective locale of dateTimeFormat.
|
||||||
auto symbol = Unicode::get_calendar_day_period_symbol_for_hour(data_locale, date_time_format.calendar(), style, local_time.hour);
|
auto symbol = resolve_day_period(data_locale, date_time_format.calendar(), style, pattern_parts, local_time);
|
||||||
if (symbol.has_value())
|
if (symbol.has_value())
|
||||||
formatted_value = *symbol;
|
formatted_value = *symbol;
|
||||||
|
|
||||||
|
|
|
@ -273,6 +273,48 @@ describe("dayPeriod", () => {
|
||||||
expect(en.format(date1)).toBe("5 at night");
|
expect(en.format(date1)).toBe("5 at night");
|
||||||
expect(en.format(date2)).toBe("11 at night");
|
expect(en.format(date2)).toBe("11 at night");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("noon", () => {
|
||||||
|
const date0 = Date.UTC(2017, 11, 12, 12, 0, 0, 0);
|
||||||
|
const date1 = Date.UTC(2017, 11, 12, 12, 1, 0, 0);
|
||||||
|
const date2 = Date.UTC(2017, 11, 12, 12, 0, 1, 0);
|
||||||
|
const date3 = Date.UTC(2017, 11, 12, 12, 0, 0, 500);
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const data = [
|
||||||
|
{ minute: undefined, second: undefined, fractionalSecondDigits: undefined, en0: "12 noon", en1: "12 noon", en2: "12 noon", en3: "12 noon", ar0: "١٢ ظهرًا", ar1: "١٢ ظهرًا", ar2: "١٢ ظهرًا", ar3: "١٢ ظهرًا" },
|
||||||
|
{ minute: "numeric", second: undefined, fractionalSecondDigits: undefined, en0: "12:00 noon", en1: "12:01 in the afternoon", en2: "12:00 noon", en3: "12:00 noon", ar0: "١٢:٠٠ ظهرًا", ar1: "١٢:٠١ ظهرًا", ar2: "١٢:٠٠ ظهرًا", ar3: "١٢:٠٠ ظهرًا" },
|
||||||
|
{ minute: "numeric", second: "numeric", fractionalSecondDigits: undefined, en0: "12:00:00 noon", en1: "12:01:00 in the afternoon", en2: "12:00:01 in the afternoon", en3: "12:00:00 noon", ar0: "١٢:٠٠:٠٠ ظهرًا", ar1: "١٢:٠١:٠٠ ظهرًا", ar2: "١٢:٠٠:٠١ ظهرًا", ar3: "١٢:٠٠:٠٠ ظهرًا" },
|
||||||
|
{ minute: "numeric", second: "numeric", fractionalSecondDigits: 1, en0: "12:00:00.0 noon", en1: "12:01:00.0 in the afternoon", en2: "12:00:01.0 in the afternoon", en3: "12:00:00.5 in the afternoon", ar0: "١٢:٠٠:٠٠٫٠ ظهرًا", ar1: "١٢:٠١:٠٠٫٠ ظهرًا", ar2: "١٢:٠٠:٠١٫٠ ظهرًا", ar3: "١٢:٠٠:٠٠٫٥ ظهرًا" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// The en locale includes the "noon" fixed day period, whereas the ar locale does not.
|
||||||
|
data.forEach(d => {
|
||||||
|
const en = new Intl.DateTimeFormat("en", {
|
||||||
|
dayPeriod: "short",
|
||||||
|
timeZone: "UTC",
|
||||||
|
minute: d.minute,
|
||||||
|
second: d.second,
|
||||||
|
fractionalSecondDigits: d.fractionalSecondDigits,
|
||||||
|
});
|
||||||
|
expect(en.format(date0)).toBe(d.en0);
|
||||||
|
expect(en.format(date1)).toBe(d.en1);
|
||||||
|
expect(en.format(date2)).toBe(d.en2);
|
||||||
|
expect(en.format(date3)).toBe(d.en3);
|
||||||
|
|
||||||
|
const ar = new Intl.DateTimeFormat("ar", {
|
||||||
|
dayPeriod: "short",
|
||||||
|
timeZone: "UTC",
|
||||||
|
minute: d.minute,
|
||||||
|
second: d.second,
|
||||||
|
fractionalSecondDigits: d.fractionalSecondDigits,
|
||||||
|
});
|
||||||
|
expect(ar.format(date0)).toBe(d.ar0);
|
||||||
|
expect(ar.format(date1)).toBe(d.ar1);
|
||||||
|
expect(ar.format(date2)).toBe(d.ar2);
|
||||||
|
expect(ar.format(date3)).toBe(d.ar3);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("hour", () => {
|
describe("hour", () => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue