mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
LibJS: Implement stringification Temporal.PlainMonthDay prototypes
This commit is contained in:
parent
bd6545f93e
commit
2487c26e1b
13 changed files with 232 additions and 1 deletions
|
@ -84,6 +84,28 @@ ThrowCompletionOr<Overflow> get_temporal_overflow_option(VM& vm, Object const& o
|
|||
return Overflow::Reject;
|
||||
}
|
||||
|
||||
// 13.10 GetTemporalShowCalendarNameOption ( options ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporalshowcalendarnameoption
|
||||
ThrowCompletionOr<ShowCalendar> get_temporal_show_calendar_name_option(VM& vm, Object const& options)
|
||||
{
|
||||
// 1. Let stringValue be ? GetOption(options, "calendarName", STRING, « "auto", "always", "never", "critical" », "auto").
|
||||
auto string_value = TRY(get_option(vm, options, vm.names.calendarName, OptionType::String, { "auto"sv, "always"sv, "never"sv, "critical"sv }, "auto"sv));
|
||||
|
||||
// 2. If stringValue is "always", return ALWAYS.
|
||||
if (string_value.as_string().utf8_string_view() == "always"sv)
|
||||
return ShowCalendar::Always;
|
||||
|
||||
// 3. If stringValue is "never", return NEVER.
|
||||
if (string_value.as_string().utf8_string_view() == "never"sv)
|
||||
return ShowCalendar::Never;
|
||||
|
||||
// 4. If stringValue is "critical", return CRITICAL.
|
||||
if (string_value.as_string().utf8_string_view() == "critical"sv)
|
||||
return ShowCalendar::Critical;
|
||||
|
||||
// 5. Return AUTO.
|
||||
return ShowCalendar::Auto;
|
||||
}
|
||||
|
||||
// 13.14 ValidateTemporalRoundingIncrement ( increment, dividend, inclusive ), https://tc39.es/proposal-temporal/#sec-validatetemporalroundingincrement
|
||||
ThrowCompletionOr<void> validate_temporal_rounding_increment(VM& vm, u64 increment, u64 dividend, bool inclusive)
|
||||
{
|
||||
|
|
|
@ -38,6 +38,13 @@ enum class Overflow {
|
|||
Reject,
|
||||
};
|
||||
|
||||
enum class ShowCalendar {
|
||||
Auto,
|
||||
Always,
|
||||
Never,
|
||||
Critical,
|
||||
};
|
||||
|
||||
enum class TimeStyle {
|
||||
Separated,
|
||||
Unseparated,
|
||||
|
@ -144,6 +151,7 @@ struct ParsedISODateTime {
|
|||
double iso_date_to_epoch_days(double year, double month, double date);
|
||||
double epoch_days_to_epoch_ms(double day, double time);
|
||||
ThrowCompletionOr<Overflow> get_temporal_overflow_option(VM&, Object const& options);
|
||||
ThrowCompletionOr<ShowCalendar> get_temporal_show_calendar_name_option(VM&, Object const& options);
|
||||
ThrowCompletionOr<void> validate_temporal_rounding_increment(VM&, u64 increment, u64 dividend, bool inclusive);
|
||||
ThrowCompletionOr<Precision> get_temporal_fractional_second_digits_option(VM&, Object const& options);
|
||||
SecondsStringPrecision to_seconds_string_precision_record(UnitValue, Precision);
|
||||
|
|
|
@ -310,6 +310,24 @@ ThrowCompletionOr<ISODate> calendar_month_day_from_fields(VM& vm, StringView cal
|
|||
return result;
|
||||
}
|
||||
|
||||
// 12.2.13 FormatCalendarAnnotation ( id, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-formatcalendarannotation
|
||||
String format_calendar_annotation(StringView id, ShowCalendar show_calendar)
|
||||
{
|
||||
// 1. If showCalendar is NEVER, return the empty String.
|
||||
if (show_calendar == ShowCalendar::Never)
|
||||
return String {};
|
||||
|
||||
// 2. If showCalendar is AUTO and id is "iso8601", return the empty String.
|
||||
if (show_calendar == ShowCalendar::Auto && id == "iso8601"sv)
|
||||
return String {};
|
||||
|
||||
// 3. If showCalendar is CRITICAL, let flag be "!"; else, let flag be the empty String.
|
||||
auto flag = show_calendar == ShowCalendar::Critical ? "!"sv : ""sv;
|
||||
|
||||
// 4. Return the string-concatenation of "[", flag, "u-ca=", id, and "]".
|
||||
return MUST(String::formatted("[{}u-ca={}]", flag, id));
|
||||
}
|
||||
|
||||
// 12.2.15 ISODaysInMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isodaysinmonth
|
||||
u8 iso_days_in_month(double year, double month)
|
||||
{
|
||||
|
|
|
@ -100,6 +100,7 @@ ThrowCompletionOr<String> canonicalize_calendar(VM&, StringView id);
|
|||
Vector<String> const& available_calendars();
|
||||
ThrowCompletionOr<CalendarFields> prepare_calendar_fields(VM&, StringView calendar, Object const& fields, CalendarFieldList calendar_field_names, CalendarFieldList non_calendar_field_names, CalendarFieldListOrPartial required_field_names);
|
||||
ThrowCompletionOr<ISODate> calendar_month_day_from_fields(VM&, StringView calendar, CalendarFields, Overflow);
|
||||
String format_calendar_annotation(StringView id, ShowCalendar);
|
||||
u8 iso_days_in_month(double year, double month);
|
||||
YearWeek iso_week_of_year(ISODate const&);
|
||||
u16 iso_day_of_year(ISODate const&);
|
||||
|
|
|
@ -84,6 +84,23 @@ bool is_valid_iso_date(double year, double month, double day)
|
|||
return true;
|
||||
}
|
||||
|
||||
// 3.5.9 PadISOYear ( y ), https://tc39.es/proposal-temporal/#sec-temporal-padisoyear
|
||||
String pad_iso_year(i32 year)
|
||||
{
|
||||
// 1. If y ≥ 0 and y ≤ 9999, then
|
||||
if (year >= 0 && year <= 9999) {
|
||||
// a. Return ToZeroPaddedDecimalString(y, 4).
|
||||
return MUST(String::formatted("{:04}", year));
|
||||
}
|
||||
|
||||
// 2. If y > 0, let yearSign be "+"; otherwise, let yearSign be "-".
|
||||
auto year_sign = year > 0 ? '+' : '-';
|
||||
|
||||
// 3. Let year be ToZeroPaddedDecimalString(abs(y), 6).
|
||||
// 4. Return the string-concatenation of yearSign and year.
|
||||
return MUST(String::formatted("{}{:06}", year_sign, abs(year)));
|
||||
}
|
||||
|
||||
// 3.5.11 ISODateWithinLimits ( isoDate ), https://tc39.es/proposal-temporal/#sec-temporal-isodatewithinlimits
|
||||
bool iso_date_within_limits(ISODate iso_date)
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@ struct ISODate {
|
|||
ISODate create_iso_date_record(double year, double month, double day);
|
||||
ThrowCompletionOr<ISODate> regulate_iso_date(VM& vm, double year, double month, double day, Overflow overflow);
|
||||
bool is_valid_iso_date(double year, double month, double day);
|
||||
String pad_iso_year(i32 year);
|
||||
bool iso_date_within_limits(ISODate);
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Intrinsics.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
|
||||
|
@ -141,4 +140,31 @@ ThrowCompletionOr<GC::Ref<PlainMonthDay>> create_temporal_month_day(VM& vm, ISOD
|
|||
return object;
|
||||
}
|
||||
|
||||
// 10.5.3 TemporalMonthDayToString ( monthDay, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporalmonthdaytostring
|
||||
String temporal_month_day_to_string(PlainMonthDay const& month_day, ShowCalendar show_calendar)
|
||||
{
|
||||
// 1. Let month be ToZeroPaddedDecimalString(monthDay.[[ISODate]].[[Month]], 2).
|
||||
// 2. Let day be ToZeroPaddedDecimalString(monthDay.[[ISODate]].[[Day]], 2).
|
||||
// 3. Let result be the string-concatenation of month, the code unit 0x002D (HYPHEN-MINUS), and day.
|
||||
auto result = MUST(String::formatted("{:02}-{:02}", month_day.iso_date().month, month_day.iso_date().day));
|
||||
|
||||
// 4. If showCalendar is one of ALWAYS or CRITICAL, or if monthDay.[[Calendar]] is not "iso8601", then
|
||||
if (show_calendar == ShowCalendar::Always || show_calendar == ShowCalendar::Critical || month_day.calendar() != "iso8601"sv) {
|
||||
// a. Let year be PadISOYear(monthDay.[[ISODate]].[[Year]]).
|
||||
auto year = pad_iso_year(month_day.iso_date().year);
|
||||
|
||||
// b. Set result to the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), and result.
|
||||
result = MUST(String::formatted("{}-{}", year, result));
|
||||
}
|
||||
|
||||
// 5. Let calendarString be FormatCalendarAnnotation(monthDay.[[Calendar]], showCalendar).
|
||||
auto calendar_string = format_calendar_annotation(month_day.calendar(), show_calendar);
|
||||
|
||||
// 6. Set result to the string-concatenation of result and calendarString.
|
||||
result = MUST(String::formatted("{}{}", result, calendar_string));
|
||||
|
||||
// 7. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <AK/String.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
@ -33,5 +34,6 @@ private:
|
|||
|
||||
ThrowCompletionOr<GC::Ref<PlainMonthDay>> to_temporal_month_day(VM&, Value item, Value options = js_undefined());
|
||||
ThrowCompletionOr<GC::Ref<PlainMonthDay>> create_temporal_month_day(VM&, ISODate, String calendar, GC::Ptr<FunctionObject> new_target = {});
|
||||
String temporal_month_day_to_string(PlainMonthDay const&, ShowCalendar);
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,11 @@ void PlainMonthDayPrototype::initialize(Realm& realm)
|
|||
define_native_accessor(realm, vm.names.calendarId, calendar_id_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.monthCode, month_code_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.day, day_getter, {}, Attribute::Configurable);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.toString, to_string, 0, attr);
|
||||
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
|
||||
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
|
||||
}
|
||||
|
||||
// 10.3.3 get Temporal.PlainMonthDay.prototype.calendarId, https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.prototype.calendarid
|
||||
|
@ -67,4 +72,44 @@ JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::day_getter)
|
|||
return calendar_iso_to_date(month_day->calendar(), month_day->iso_date()).day;
|
||||
}
|
||||
|
||||
// 10.3.8 Temporal.PlainMonthDay.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tostring
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_string)
|
||||
{
|
||||
// 1. Let monthDay be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
|
||||
auto month_day = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolved_options = TRY(get_options_object(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let showCalendar be ? GetTemporalShowCalendarNameOption(resolvedOptions).
|
||||
auto show_calendar = TRY(get_temporal_show_calendar_name_option(vm, resolved_options));
|
||||
|
||||
// 5. Return TemporalMonthDayToString(monthDay, showCalendar).
|
||||
return PrimitiveString::create(vm, temporal_month_day_to_string(month_day, show_calendar));
|
||||
}
|
||||
|
||||
// 10.3.9 Temporal.PlainMonthDay.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tolocalestring
|
||||
// NOTE: This is the minimum toLocaleString implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_locale_string)
|
||||
{
|
||||
// 1. Let monthDay be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
|
||||
auto month_day = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return TemporalMonthDayToString(monthDay, auto).
|
||||
return PrimitiveString::create(vm, temporal_month_day_to_string(month_day, ShowCalendar::Auto));
|
||||
}
|
||||
|
||||
// 10.3.10 Temporal.PlainMonthDay.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tolocalestring
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_json)
|
||||
{
|
||||
// 1. Let monthDay be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
|
||||
auto month_day = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return TemporalMonthDayToString(monthDay, auto).
|
||||
return PrimitiveString::create(vm, temporal_month_day_to_string(month_day, ShowCalendar::Auto));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(calendar_id_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(month_code_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(day_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_json);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.PlainMonthDay.prototype.toJSON).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
let plainMonthDay;
|
||||
|
||||
plainMonthDay = new Temporal.PlainMonthDay(7, 6);
|
||||
expect(plainMonthDay.toJSON()).toBe("07-06");
|
||||
|
||||
plainMonthDay = new Temporal.PlainMonthDay(7, 6, "gregory", 2021);
|
||||
expect(plainMonthDay.toJSON()).toBe("2021-07-06[u-ca=gregory]");
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainMonthDay object", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainMonthDay.prototype.toJSON.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainMonthDay");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.PlainMonthDay.prototype.toLocaleString).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
let plainMonthDay;
|
||||
|
||||
plainMonthDay = new Temporal.PlainMonthDay(7, 6);
|
||||
expect(plainMonthDay.toLocaleString()).toBe("07-06");
|
||||
|
||||
plainMonthDay = new Temporal.PlainMonthDay(7, 6, "gregory", 2021);
|
||||
expect(plainMonthDay.toLocaleString()).toBe("2021-07-06[u-ca=gregory]");
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainMonthDay object", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainMonthDay.prototype.toLocaleString.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainMonthDay");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.PlainMonthDay.prototype.toString).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
let plainMonthDay;
|
||||
|
||||
plainMonthDay = new Temporal.PlainMonthDay(7, 6);
|
||||
expect(plainMonthDay.toString()).toBe("07-06");
|
||||
expect(plainMonthDay.toString({ calendarName: "auto" })).toBe("07-06");
|
||||
expect(plainMonthDay.toString({ calendarName: "always" })).toBe("1972-07-06[u-ca=iso8601]");
|
||||
expect(plainMonthDay.toString({ calendarName: "never" })).toBe("07-06");
|
||||
expect(plainMonthDay.toString({ calendarName: "critical" })).toBe(
|
||||
"1972-07-06[!u-ca=iso8601]"
|
||||
);
|
||||
|
||||
plainMonthDay = new Temporal.PlainMonthDay(7, 6, "gregory", 2021);
|
||||
expect(plainMonthDay.toString()).toBe("2021-07-06[u-ca=gregory]");
|
||||
expect(plainMonthDay.toString({ calendarName: "auto" })).toBe("2021-07-06[u-ca=gregory]");
|
||||
expect(plainMonthDay.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=gregory]");
|
||||
expect(plainMonthDay.toString({ calendarName: "never" })).toBe("2021-07-06");
|
||||
expect(plainMonthDay.toString({ calendarName: "critical" })).toBe(
|
||||
"2021-07-06[!u-ca=gregory]"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainMonthDay object", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainMonthDay.prototype.toString.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainMonthDay");
|
||||
});
|
||||
|
||||
test("calendarName option must be one of 'auto', 'always', 'never', 'critical'", () => {
|
||||
const plainMonthDay = new Temporal.PlainMonthDay(7, 6);
|
||||
expect(() => {
|
||||
plainMonthDay.toString({ calendarName: "foo" });
|
||||
}).toThrowWithMessage(RangeError, "foo is not a valid value for option calendarName");
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue