1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 17:07:46 +00:00

LibJS: Implement spec-compliant ToDateString and its underlying AOs

This commit is contained in:
Timothy Flynn 2022-01-14 13:09:18 -05:00 committed by Linus Groh
parent 62dc9958f5
commit a488ec1ad0
2 changed files with 111 additions and 0 deletions

View file

@ -6,6 +6,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Array.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
@ -13,6 +14,7 @@
#include <LibCrypto/BigInt/UnsignedBigInteger.h> #include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/BigInt.h> #include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/DatePrototype.h> #include <LibJS/Runtime/DatePrototype.h>
#include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
@ -21,9 +23,18 @@
#include <LibJS/Runtime/MarkedValueList.h> #include <LibJS/Runtime/MarkedValueList.h>
#include <LibJS/Runtime/Temporal/Instant.h> #include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibTimeZone/TimeZone.h>
#include <LibUnicode/DateTimeFormat.h>
#include <LibUnicode/Locale.h>
namespace JS { namespace JS {
// Table 62: Names of days of the week, https://tc39.es/ecma262/#sec-todatestring-day-names
static constexpr auto day_names = AK::Array { "Sun"sv, "Mon"sv, "Tue"sv, "Wed"sv, "Thu"sv, "Fri"sv, "Sat"sv };
// Table 63: Names of months of the year, https://tc39.es/ecma262/#sec-todatestring-day-names
static constexpr auto month_names = AK::Array { "Jan"sv, "Feb"sv, "Mar"sv, "Apr"sv, "May"sv, "Jun"sv, "Jul"sv, "Aug"sv, "Sep"sv, "Oct"sv, "Nov"sv, "Dec"sv };
DatePrototype::DatePrototype(GlobalObject& global_object) DatePrototype::DatePrototype(GlobalObject& global_object)
: PrototypeObject(*global_object.object_prototype()) : PrototypeObject(*global_object.object_prototype())
{ {
@ -738,6 +749,101 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_string)
return js_string(vm, move(string)); return js_string(vm, move(string));
} }
// 21.4.4.41.1 TimeString ( tv ), https://tc39.es/ecma262/#sec-timestring
String time_string(double time)
{
// 1. Let hour be the String representation of HourFromTime(tv), formatted as a two-digit decimal number, padded to the left with the code unit 0x0030 (DIGIT ZERO) if necessary.
auto hour = hour_from_time(time);
// 2. Let minute be the String representation of MinFromTime(tv), formatted as a two-digit decimal number, padded to the left with the code unit 0x0030 (DIGIT ZERO) if necessary.
auto minute = min_from_time(time);
// 3. Let second be the String representation of SecFromTime(tv), formatted as a two-digit decimal number, padded to the left with the code unit 0x0030 (DIGIT ZERO) if necessary.
auto second = sec_from_time(time);
// 4. Return the string-concatenation of hour, ":", minute, ":", second, the code unit 0x0020 (SPACE), and "GMT".
return String::formatted("{:02}:{:02}:{:02} GMT", hour, minute, second);
}
// 21.4.4.41.2 DateString ( tv ), https://tc39.es/ecma262/#sec-datestring
String date_string(double time)
{
// 1. Let weekday be the Name of the entry in Table 62 with the Number WeekDay(tv).
auto weekday = day_names[week_day(time)];
// 2. Let month be the Name of the entry in Table 63 with the Number MonthFromTime(tv).
auto month = month_names[month_from_time(time)];
// 3. Let day be the String representation of DateFromTime(tv), formatted as a two-digit decimal number, padded to the left with the code unit 0x0030 (DIGIT ZERO) if necessary.
auto day = date_from_time(time);
// 4. Let yv be YearFromTime(tv).
auto year = year_from_time(time);
// 5. If yv ≥ +0𝔽, let yearSign be the empty String; otherwise, let yearSign be "-".
auto year_sign = year >= 0 ? ""sv : "-"sv;
// 6. Let year be the String representation of abs((yv)), formatted as a decimal number.
year = abs(year);
// 7. Let paddedYear be ! StringPad(year, 4𝔽, "0", start).
// 8. Return the string-concatenation of weekday, the code unit 0x0020 (SPACE), month, the code unit 0x0020 (SPACE), day, the code unit 0x0020 (SPACE), yearSign, and paddedYear.
return String::formatted("{} {} {:02} {}{:04}", weekday, month, day, year_sign, year);
}
// 21.4.4.41.3 TimeZoneString ( tv ), https://tc39.es/ecma262/#sec-timezoneestring
String time_zone_string(double time)
{
// 1. Let offset be LocalTZA(tv, true).
auto offset = local_tza(time, true);
StringView offset_sign;
// 2. If offset ≥ +0𝔽, then
if (offset >= 0) {
// a. Let offsetSign be "+".
offset_sign = "+"sv;
// b. Let absOffset be offset.
}
// 3. Else,
else {
// a. Let offsetSign be "-".
offset_sign = "-"sv;
// b. Let absOffset be -offset.
offset *= -1;
}
// 4. Let offsetMin be the String representation of MinFromTime(absOffset), formatted as a two-digit decimal number, padded to the left with the code unit 0x0030 (DIGIT ZERO) if necessary.
auto offset_min = min_from_time(offset);
// 5. Let offsetHour be the String representation of HourFromTime(absOffset), formatted as a two-digit decimal number, padded to the left with the code unit 0x0030 (DIGIT ZERO) if necessary.
auto offset_hour = hour_from_time(offset);
// 6. Let tzName be an implementation-defined string that is either the empty String or the string-concatenation of the code unit 0x0020 (SPACE), the code unit 0x0028 (LEFT PARENTHESIS), an implementation-defined timezone name, and the code unit 0x0029 (RIGHT PARENTHESIS).
auto tz_name = TimeZone::current_time_zone();
// Most implementations seem to prefer the long-form display name of the time zone. Not super important, but we may as well match that behavior.
if (auto long_name = Unicode::get_time_zone_name(Unicode::default_locale(), tz_name, Unicode::CalendarPatternStyle::Long); long_name.has_value())
tz_name = long_name.release_value();
// 7. Return the string-concatenation of offsetSign, offsetHour, offsetMin, and tzName.
return String::formatted("{}{:02}{:02} ({})", offset_sign, offset_hour, offset_min, tz_name);
}
// 21.4.4.41.4 ToDateString ( tv ), https://tc39.es/ecma262/#sec-todatestring
String to_date_string(double time)
{
// 1. If tv is NaN, return "Invalid Date".
if (Value(time).is_nan())
return "Invalid Date"sv;
// 2. Let t be LocalTime(tv).
time = local_time(time);
// 3. Return the string-concatenation of DateString(t), the code unit 0x0020 (SPACE), TimeString(t), and TimeZoneString(tv).
return String::formatted("{} {}{}", date_string(time), time_string(time), time_zone_string(time));
}
// 14.1.1 Date.prototype.toTemporalInstant ( ), https://tc39.es/proposal-temporal/#sec-date.prototype.totemporalinstant // 14.1.1 Date.prototype.toTemporalInstant ( ), https://tc39.es/proposal-temporal/#sec-date.prototype.totemporalinstant
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_temporal_instant) JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_temporal_instant)
{ {

View file

@ -64,4 +64,9 @@ private:
JS_DECLARE_NATIVE_FUNCTION(symbol_to_primitive); JS_DECLARE_NATIVE_FUNCTION(symbol_to_primitive);
}; };
String time_string(double time);
String date_string(double time);
String time_zone_string(double time);
String to_date_string(double time);
} }