mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 02:12:09 -05:00
LibJS: Implement Temporal.PlainDate.prototype.toString()
This commit is contained in:
parent
310192f918
commit
402f04c2fc
10 changed files with 164 additions and 0 deletions
|
@ -80,6 +80,7 @@ namespace JS {
|
|||
P(byteLength) \
|
||||
P(byteOffset) \
|
||||
P(calendar) \
|
||||
P(calendarName) \
|
||||
P(call) \
|
||||
P(callee) \
|
||||
P(caller) \
|
||||
|
|
|
@ -195,6 +195,20 @@ Optional<String> to_temporal_rounding_mode(GlobalObject& global_object, Object&
|
|||
return option.as_string().string();
|
||||
}
|
||||
|
||||
// 13.11 ToShowCalendarOption ( normalizedOptions ), https://tc39.es/proposal-temporal/#sec-temporal-toshowcalendaroption
|
||||
Optional<String> to_show_calendar_option(GlobalObject& global_object, Object& normalized_options)
|
||||
{
|
||||
auto& vm = global_object.vm();
|
||||
|
||||
// 1. Return ? GetOption(normalizedOptions, "calendarName", « String », « "auto", "always", "never" », "auto").
|
||||
auto option = get_option(global_object, normalized_options, vm.names.calendarName, { OptionType::String }, { "auto"sv, "always"sv, "never"sv }, js_string(vm, "auto"sv));
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
VERIFY(option.is_string());
|
||||
return option.as_string().string();
|
||||
}
|
||||
|
||||
// 13.14 ToTemporalRoundingIncrement ( normalizedOptions, dividend, inclusive ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalroundingincrement
|
||||
u64 to_temporal_rounding_increment(GlobalObject& global_object, Object& normalized_options, Optional<double> dividend, bool inclusive)
|
||||
{
|
||||
|
|
|
@ -64,6 +64,7 @@ Object* get_options_object(GlobalObject&, Value options);
|
|||
Value get_option(GlobalObject&, Object& options, PropertyName const& property, Vector<OptionType> const& types, Vector<StringView> const& values, Value fallback);
|
||||
Optional<String> to_temporal_overflow(GlobalObject&, Object& normalized_options);
|
||||
Optional<String> to_temporal_rounding_mode(GlobalObject&, Object& normalized_options, String const& fallback);
|
||||
Optional<String> to_show_calendar_option(GlobalObject&, Object& normalized_options);
|
||||
u64 to_temporal_rounding_increment(GlobalObject&, Object& normalized_options, Optional<double> dividend, bool inclusive);
|
||||
Optional<String> to_smallest_temporal_unit(GlobalObject&, Object& normalized_options, Vector<StringView> const& disallowed_units, Optional<String> fallback);
|
||||
double constrain_to_range(double x, double minimum, double maximum);
|
||||
|
|
|
@ -477,6 +477,24 @@ PlainMonthDay* month_day_from_fields(GlobalObject& global_object, Object& calend
|
|||
return static_cast<PlainMonthDay*>(month_day_object);
|
||||
}
|
||||
|
||||
// 12.1.27 FormatCalendarAnnotation ( id, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-formatcalendarannotation
|
||||
String format_calendar_annotation(StringView id, StringView show_calendar)
|
||||
{
|
||||
// 1. Assert: showCalendar is "auto", "always", or "never".
|
||||
VERIFY(show_calendar == "auto"sv || show_calendar == "always"sv || show_calendar == "never"sv);
|
||||
|
||||
// 2. If showCalendar is "never", return the empty String.
|
||||
if (show_calendar == "never"sv)
|
||||
return String::empty();
|
||||
|
||||
// 3. If showCalendar is "auto" and id is "iso8601", return the empty String.
|
||||
if (show_calendar == "auto"sv && id == "iso8601"sv)
|
||||
return String::empty();
|
||||
|
||||
// 4. Return the string-concatenation of "[u-ca=", id, and "]".
|
||||
return String::formatted("[u-ca={}]", id);
|
||||
}
|
||||
|
||||
// 12.1.28 CalendarEquals ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-calendarequals
|
||||
bool calendar_equals(GlobalObject& global_object, Object& one, Object& two)
|
||||
{
|
||||
|
|
|
@ -52,6 +52,7 @@ Object* get_temporal_calendar_with_iso_default(GlobalObject&, Object&);
|
|||
PlainDate* date_from_fields(GlobalObject&, Object& calendar, Object& fields, Object& options);
|
||||
PlainYearMonth* year_month_from_fields(GlobalObject&, Object& calendar, Object& fields, Object* options = nullptr);
|
||||
PlainMonthDay* month_day_from_fields(GlobalObject& global_object, Object& calendar, Object& fields, Object* options = nullptr);
|
||||
String format_calendar_annotation(StringView id, StringView show_calendar);
|
||||
bool calendar_equals(GlobalObject&, Object& one, Object& two);
|
||||
Object* consolidate_calendars(GlobalObject&, Object& one, Object& two);
|
||||
bool is_iso_leap_year(i32 year);
|
||||
|
|
|
@ -332,6 +332,53 @@ ISODate balance_iso_date(double year_, double month_, double day)
|
|||
return ISODate { .year = year, .month = static_cast<u8>(month), .day = static_cast<u8>(day) };
|
||||
}
|
||||
|
||||
// 3.5.7 PadISOYear ( y ), https://tc39.es/proposal-temporal/#sec-temporal-padisoyear
|
||||
String pad_iso_year(i32 y)
|
||||
{
|
||||
// 1. Assert: y is an integer.
|
||||
|
||||
// 2. If y > 999 and y ≤ 9999, then
|
||||
if (y > 999 && y <= 9999) {
|
||||
// a. Return y formatted as a four-digit decimal number.
|
||||
return String::number(y);
|
||||
}
|
||||
// 3. If y ≥ 0, let yearSign be "+"; otherwise, let yearSign be "-".
|
||||
auto year_sign = y >= 0 ? '+' : '-';
|
||||
|
||||
// 4. Let year be abs(y), formatted as a six-digit decimal number, padded to the left with zeroes as necessary.
|
||||
// 5. Return the string-concatenation of yearSign and year.
|
||||
return String::formatted("{}{:06}", year_sign, abs(y));
|
||||
}
|
||||
|
||||
// 3.5.8 TemporalDateToString ( temporalDate, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldatetostring
|
||||
Optional<String> temporal_date_to_string(GlobalObject& global_object, PlainDate& temporal_date, StringView show_calendar)
|
||||
{
|
||||
auto& vm = global_object.vm();
|
||||
|
||||
// 1. Assert: Type(temporalDate) is Object.
|
||||
// 2. Assert: temporalDate has an [[InitializedTemporalDate]] internal slot.
|
||||
|
||||
// 3. Let year be ! PadISOYear(temporalDate.[[ISOYear]]).
|
||||
auto year = pad_iso_year(temporal_date.iso_year());
|
||||
|
||||
// 4. Let month be temporalDate.[[ISOMonth]] formatted as a two-digit decimal number, padded to the left with a zero if necessary.
|
||||
auto month = String::formatted("{:02}", temporal_date.iso_month());
|
||||
|
||||
// 5. Let day be temporalDate.[[ISODay]] formatted as a two-digit decimal number, padded to the left with a zero if necessary.
|
||||
auto day = String::formatted("{:02}", temporal_date.iso_day());
|
||||
|
||||
// 6. Let calendarID be ? ToString(temporalDate.[[Calendar]]).
|
||||
auto calendar_id = Value(&temporal_date.calendar()).to_string(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 7. Let calendar be ! FormatCalendarAnnotation(calendarID, showCalendar).
|
||||
auto calendar = format_calendar_annotation(calendar_id, show_calendar);
|
||||
|
||||
// 8. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, and calendar.
|
||||
return String::formatted("{}-{}-{}{}", year, month, day, calendar);
|
||||
}
|
||||
|
||||
// 3.5.10 CompareISODate ( y1, m1, d1, y2, m2, d2 ), https://tc39.es/proposal-temporal/#sec-temporal-compareisodate
|
||||
i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2)
|
||||
{
|
||||
|
|
|
@ -45,6 +45,8 @@ PlainDate* to_temporal_date(GlobalObject&, Value item, Object* options = nullptr
|
|||
Optional<ISODate> regulate_iso_date(GlobalObject&, double year, double month, double day, String const& overflow);
|
||||
bool is_valid_iso_date(i32 year, u8 month, u8 day);
|
||||
ISODate balance_iso_date(double year, double month, double day);
|
||||
String pad_iso_year(i32 y);
|
||||
Optional<String> temporal_date_to_string(GlobalObject&, PlainDate&, StringView show_calendar);
|
||||
i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2);
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ void PlainDatePrototype::initialize(GlobalObject& global_object)
|
|||
define_native_function(vm.names.getISOFields, get_iso_fields, 0, attr);
|
||||
define_native_function(vm.names.withCalendar, with_calendar, 1, attr);
|
||||
define_native_function(vm.names.equals, equals, 1, attr);
|
||||
define_native_function(vm.names.toString, to_string, 0, attr);
|
||||
define_native_function(vm.names.valueOf, value_of, 0, attr);
|
||||
}
|
||||
|
||||
|
@ -397,6 +398,33 @@ JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::equals)
|
|||
return Value(calendar_equals(global_object, temporal_date->calendar(), other->calendar()));
|
||||
}
|
||||
|
||||
// 3.3.28 Temporal.PlainDate.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tostring
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::to_string)
|
||||
{
|
||||
// 1. Let temporalDate be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
|
||||
auto* temporal_date = typed_this(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 3. Set options to ? GetOptionsObject(options).
|
||||
auto* options = get_options_object(global_object, vm.argument(0));
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 4. Let showCalendar be ? ToShowCalendarOption(options).
|
||||
auto show_calendar = to_show_calendar_option(global_object, *options);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 5. Return ? TemporalDateToString(temporalDate, showCalendar).
|
||||
auto string = temporal_date_to_string(global_object, *temporal_date, *show_calendar);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
return js_string(vm, *string);
|
||||
}
|
||||
|
||||
// 3.3.31 Temporal.PlainDate.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.valueof
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::value_of)
|
||||
{
|
||||
|
|
|
@ -37,6 +37,7 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(get_iso_fields);
|
||||
JS_DECLARE_NATIVE_FUNCTION(with_calendar);
|
||||
JS_DECLARE_NATIVE_FUNCTION(equals);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(value_of);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.PlainDate.prototype.toString).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
let plainDate;
|
||||
|
||||
plainDate = new Temporal.PlainDate(2021, 7, 6);
|
||||
expect(plainDate.toString()).toBe("2021-07-06");
|
||||
expect(plainDate.toString({ calendarName: "auto" })).toBe("2021-07-06");
|
||||
expect(plainDate.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=iso8601]");
|
||||
expect(plainDate.toString({ calendarName: "never" })).toBe("2021-07-06");
|
||||
|
||||
plainDate = new Temporal.PlainDate(2021, 7, 6, { toString: () => "foo" });
|
||||
expect(plainDate.toString()).toBe("2021-07-06[u-ca=foo]");
|
||||
expect(plainDate.toString({ calendarName: "auto" })).toBe("2021-07-06[u-ca=foo]");
|
||||
expect(plainDate.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=foo]");
|
||||
expect(plainDate.toString({ calendarName: "never" })).toBe("2021-07-06");
|
||||
|
||||
plainDate = new Temporal.PlainDate(0, 1, 1);
|
||||
expect(plainDate.toString()).toBe("+000000-01-01");
|
||||
|
||||
plainDate = new Temporal.PlainDate(999, 1, 1);
|
||||
expect(plainDate.toString()).toBe("+000999-01-01");
|
||||
|
||||
plainDate = new Temporal.PlainDate(12345, 1, 1);
|
||||
expect(plainDate.toString()).toBe("+012345-01-01");
|
||||
|
||||
plainDate = new Temporal.PlainDate(123456, 1, 1);
|
||||
expect(plainDate.toString()).toBe("+123456-01-01");
|
||||
|
||||
plainDate = new Temporal.PlainDate(-12345, 1, 1);
|
||||
expect(plainDate.toString()).toBe("-012345-01-01");
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainDate object", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainDate.prototype.toString.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not a Temporal.PlainDate");
|
||||
});
|
||||
|
||||
test("calendarName option must be one of 'auto', 'always', 'never'", () => {
|
||||
const plainDate = new Temporal.PlainDate(2021, 7, 6);
|
||||
expect(() => {
|
||||
plainDate.toString({ calendarName: "foo" });
|
||||
}).toThrowWithMessage(RangeError, "foo is not a valid value for option calendarName");
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue