From 75b2a09a2f39ecea2386780521946d6ffe9a855a Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 18 Nov 2021 10:30:31 -0500 Subject: [PATCH] LibJS: Implement a nearly empty Intl.DateTimeFormat object This adds plumbing for the Intl.DateTimeFormat object, constructor, and prototype. Note that unlike other Intl objects, the Intl.DateTimeFormat object has a LibUnicode structure as a base. This is to prevent wild amounts of code duplication between LibUnicode, Intl.DateTimeFormat, and other not-yet-defined Intl structures, because there's 12 fields shared between them. --- Userland/Libraries/LibJS/CMakeLists.txt | 3 + Userland/Libraries/LibJS/Forward.h | 9 +- .../Libraries/LibJS/Runtime/GlobalObject.cpp | 2 + .../LibJS/Runtime/Intl/DateTimeFormat.cpp | 54 ++++++++ .../LibJS/Runtime/Intl/DateTimeFormat.h | 126 ++++++++++++++++++ .../Intl/DateTimeFormatConstructor.cpp | 50 +++++++ .../Runtime/Intl/DateTimeFormatConstructor.h | 28 ++++ .../Runtime/Intl/DateTimeFormatPrototype.cpp | 28 ++++ .../Runtime/Intl/DateTimeFormatPrototype.h | 23 ++++ .../Libraries/LibJS/Runtime/Intl/Intl.cpp | 2 + .../DateTimeFormat.@@toStringTag.js | 3 + .../Intl/DateTimeFormat/DateTimeFormat.js | 5 + 12 files changed, 329 insertions(+), 4 deletions(-) create mode 100644 Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp create mode 100644 Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h create mode 100644 Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp create mode 100644 Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.h create mode 100644 Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormatPrototype.cpp create mode 100644 Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormatPrototype.h create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Intl/DateTimeFormat/DateTimeFormat.@@toStringTag.js create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Intl/DateTimeFormat/DateTimeFormat.js diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index 8238db7e9f..086fee3fe4 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -86,6 +86,9 @@ set(SOURCES Runtime/GlobalObject.cpp Runtime/IndexedProperties.cpp Runtime/Intl/AbstractOperations.cpp + Runtime/Intl/DateTimeFormat.cpp + Runtime/Intl/DateTimeFormatConstructor.cpp + Runtime/Intl/DateTimeFormatPrototype.cpp Runtime/Intl/DisplayNames.cpp Runtime/Intl/DisplayNamesConstructor.cpp Runtime/Intl/DisplayNamesPrototype.cpp diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index 2ae6ba6e84..9ed9134cb8 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -67,10 +67,11 @@ __JS_ENUMERATE(Float32Array, float32_array, Float32ArrayPrototype, Float32ArrayConstructor, float) \ __JS_ENUMERATE(Float64Array, float64_array, Float64ArrayPrototype, Float64ArrayConstructor, double) -#define JS_ENUMERATE_INTL_OBJECTS \ - __JS_ENUMERATE(DisplayNames, display_names, DisplayNamesPrototype, DisplayNamesConstructor) \ - __JS_ENUMERATE(ListFormat, list_format, ListFormatPrototype, ListFormatConstructor) \ - __JS_ENUMERATE(Locale, locale, LocalePrototype, LocaleConstructor) \ +#define JS_ENUMERATE_INTL_OBJECTS \ + __JS_ENUMERATE(DateTimeFormat, date_time_format, DateTimeFormatPrototype, DateTimeFormatConstructor) \ + __JS_ENUMERATE(DisplayNames, display_names, DisplayNamesPrototype, DisplayNamesConstructor) \ + __JS_ENUMERATE(ListFormat, list_format, ListFormatPrototype, ListFormatConstructor) \ + __JS_ENUMERATE(Locale, locale, LocalePrototype, LocaleConstructor) \ __JS_ENUMERATE(NumberFormat, number_format, NumberFormatPrototype, NumberFormatConstructor) #define JS_ENUMERATE_TEMPORAL_OBJECTS \ diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp index 11ed075409..df4f0ef05a 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp new file mode 100644 index 0000000000..4474105dc1 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace JS::Intl { + +Vector const& DateTimeFormat::relevant_extension_keys() +{ + // 11.3.3 Internal slots, https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots + // The value of the [[RelevantExtensionKeys]] internal slot is « "ca", "hc", "nu" ». + static Vector relevant_extension_keys { "ca"sv, "hc"sv, "nu"sv }; + return relevant_extension_keys; +} + +// 11 DateTimeFormat Objects, https://tc39.es/ecma402/#datetimeformat-objects +DateTimeFormat::DateTimeFormat(Object& prototype) + : Object(prototype) +{ +} + +DateTimeFormat::Style DateTimeFormat::style_from_string(StringView style) +{ + if (style == "full"sv) + return Style::Full; + if (style == "long"sv) + return Style::Long; + if (style == "medium"sv) + return Style::Medium; + if (style == "short"sv) + return Style::Short; + VERIFY_NOT_REACHED(); +} + +StringView DateTimeFormat::style_to_string(Style style) +{ + switch (style) { + case Style::Full: + return "full"sv; + case Style::Long: + return "long"sv; + case Style::Medium: + return "medium"sv; + case Style::Short: + return "short"sv; + default: + VERIFY_NOT_REACHED(); + } +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h new file mode 100644 index 0000000000..34cb50444e --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace JS::Intl { + +class DateTimeFormat final + : public Object + , public Unicode::CalendarPattern { + JS_OBJECT(DateTimeFormat, Object); + + using Patterns = Unicode::CalendarPattern; + +public: + enum class Style { + Full, + Long, + Medium, + Short, + }; + + static Vector const& relevant_extension_keys(); // [[RelevantExtensionKeys]] + + DateTimeFormat(Object& prototype); + virtual ~DateTimeFormat() override = default; + + String const& locale() const { return m_locale; } + void set_locale(String locale) { m_locale = move(locale); } + + String const& calendar() const { return m_calendar; } + void set_calendar(String calendar) { m_calendar = move(calendar); } + + String const& numbering_system() const { return m_numbering_system; } + void set_numbering_system(String numbering_system) { m_numbering_system = move(numbering_system); } + + bool has_hour_cycle() const { return m_hour_cycle.has_value(); } + Unicode::HourCycle hour_cycle() const { return *m_hour_cycle; } + StringView hour_cycle_string() const { return Unicode::hour_cycle_to_string(*m_hour_cycle); } + void set_hour_cycle(StringView hour_cycle) { m_hour_cycle = Unicode::hour_cycle_from_string(hour_cycle); } + void set_hour_cycle(Unicode::HourCycle hour_cycle) { m_hour_cycle = hour_cycle; } + void clear_hour_cycle() { m_hour_cycle.clear(); } + + String const& time_zone() const { return m_time_zone; } + void set_time_zone(String time_zone) { m_time_zone = move(time_zone); } + + bool has_date_style() const { return m_date_style.has_value(); } + Style date_style() const { return *m_date_style; }; + StringView date_style_string() const { return style_to_string(*m_date_style); }; + void set_date_style(StringView style) { m_date_style = style_from_string(style); }; + + bool has_time_style() const { return m_time_style.has_value(); } + Style time_style() const { return *m_time_style; }; + StringView time_style_string() const { return style_to_string(*m_time_style); }; + void set_time_style(StringView style) { m_time_style = style_from_string(style); }; + + String const& pattern() const { return Patterns::pattern; }; + void set_pattern(String pattern) { Patterns::pattern = move(pattern); } + + bool has_era() const { return Patterns::era.has_value(); } + Unicode::CalendarPatternStyle era() const { return *Patterns::era; }; + StringView era_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::era); } + + bool has_year() const { return Patterns::year.has_value(); } + Unicode::CalendarPatternStyle year() const { return *Patterns::year; }; + StringView year_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::year); } + + bool has_month() const { return Patterns::month.has_value(); } + Unicode::CalendarPatternStyle month() const { return *Patterns::month; }; + StringView month_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::month); } + + bool has_weekday() const { return Patterns::weekday.has_value(); } + Unicode::CalendarPatternStyle weekday() const { return *Patterns::weekday; }; + StringView weekday_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::weekday); } + + bool has_day() const { return Patterns::day.has_value(); } + Unicode::CalendarPatternStyle day() const { return *Patterns::day; }; + StringView day_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::day); } + + bool has_day_period() const { return Patterns::day_period.has_value(); } + Unicode::CalendarPatternStyle day_period() const { return *Patterns::day_period; }; + StringView day_period_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::day_period); } + + bool has_hour() const { return Patterns::hour.has_value(); } + Unicode::CalendarPatternStyle hour() const { return *Patterns::hour; }; + StringView hour_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::hour); } + + bool has_minute() const { return Patterns::minute.has_value(); } + Unicode::CalendarPatternStyle minute() const { return *Patterns::minute; }; + StringView minute_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::minute); } + + bool has_second() const { return Patterns::second.has_value(); } + Unicode::CalendarPatternStyle second() const { return *Patterns::second; }; + StringView second_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::second); } + + bool has_fractional_second_digits() const { return Patterns::fractional_second_digits.has_value(); } + u8 fractional_second_digits() const { return *Patterns::fractional_second_digits; }; + + bool has_time_zone_name() const { return Patterns::time_zone_name.has_value(); } + Unicode::CalendarPatternStyle time_zone_name() const { return *Patterns::time_zone_name; }; + StringView time_zone_name_string() const { return Unicode::calendar_pattern_style_to_string(*Patterns::time_zone_name); } + +private: + static Style style_from_string(StringView style); + static StringView style_to_string(Style style); + + String m_locale; // [[Locale]] + String m_calendar; // [[Calendar]] + String m_numbering_system; // [[NumberingSystem]] + Optional m_hour_cycle; // [[HourCycle]] + String m_time_zone; // [[TimeZone]] + Optional