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

LibJS: Implement Temporal.Instant.prototype.toString()

This commit is contained in:
Linus Groh 2021-08-31 00:15:36 +01:00 committed by Andreas Kling
parent 1c65ee6edf
commit 576be0f8e7
14 changed files with 396 additions and 6 deletions

View file

@ -164,6 +164,7 @@ namespace JS {
P(fontcolor) \
P(fontsize) \
P(forEach) \
P(fractionalSecondDigits) \
P(freeze) \
P(from) \
P(fromCharCode) \

View file

@ -7,6 +7,7 @@
#include <AK/CharacterTypes.h>
#include <AK/DateTimeLexer.h>
#include <AK/Variant.h>
#include <LibJS/Runtime/IteratorOperations.h>
#include <LibJS/Runtime/PropertyName.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
@ -168,6 +169,44 @@ Value get_option(GlobalObject& global_object, Object& options, PropertyName cons
return value;
}
// 13.4 GetStringOrNumberOption ( options, property, stringValues, minimum, maximum, fallback ), https://tc39.es/proposal-temporal/#sec-getstringornumberoption
template<typename NumberType>
Optional<Variant<String, NumberType>> get_string_or_number_option(GlobalObject& global_object, Object& options, PropertyName const& property, Vector<StringView> const& string_values, NumberType minimum, NumberType maximum, Value fallback)
{
auto& vm = global_object.vm();
// 1. Assert: Type(options) is Object.
// 2. Let value be ? GetOption(options, property, « Number, String », empty, fallback).
auto value = get_option(global_object, options, property, { OptionType::Number, OptionType::String }, {}, fallback);
if (vm.exception())
return {};
// 3. If Type(value) is Number, then
if (value.is_number()) {
// a. If value < minimum or value > maximum, throw a RangeError exception.
if (value.as_double() < minimum || value.as_double() > maximum) {
vm.template throw_exception<RangeError>(global_object, ErrorType::OptionIsNotValidValue, value.as_double(), property.as_string());
return {};
}
// b. Return floor((value)).
return floor(value.as_double());
}
// 4. Assert: Type(value) is String.
VERIFY(value.is_string());
// 5. If stringValues does not contain value, throw a RangeError exception.
if (!string_values.contains_slow(value.as_string().string())) {
vm.template throw_exception<RangeError>(global_object, ErrorType::OptionIsNotValidValue, value.as_string().string(), property.as_string());
return {};
}
// 6. Return value.
return value.as_string().string();
}
// 13.6 ToTemporalOverflow ( normalizedOptions ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaloverflow
Optional<String> to_temporal_overflow(GlobalObject& global_object, Object& normalized_options)
{
@ -264,6 +303,88 @@ u64 to_temporal_rounding_increment(GlobalObject& global_object, Object& normaliz
return floored_increment;
}
// 13.16 ToSecondsStringPrecision ( normalizedOptions ), https://tc39.es/proposal-temporal/#sec-temporal-tosecondsstringprecision
Optional<SecondsStringPrecision> to_seconds_string_precision(GlobalObject& global_object, Object& normalized_options)
{
auto& vm = global_object.vm();
// Let smallestUnit be ? ToSmallestTemporalUnit(normalizedOptions, « "year", "month", "week", "day", "hour" », undefined).
auto smallest_unit = to_smallest_temporal_unit(global_object, normalized_options, { "year"sv, "month"sv, "week"sv, "day"sv, "hour"sv }, {});
if (vm.exception())
return {};
// 2. If smallestUnit is "minute", then
if (smallest_unit == "minute"sv) {
// a. Return the Record { [[Precision]]: "minute", [[Unit]]: "minute", [[Increment]]: 1 }.
return SecondsStringPrecision { .precision = String { "minute"sv }, .unit = "minute"sv, .increment = 1 };
}
// 3. If smallestUnit is "second", then
if (smallest_unit == "second"sv) {
// a. Return the Record { [[Precision]]: 0, [[Unit]]: "second", [[Increment]]: 1 }.
return SecondsStringPrecision { .precision = 0, .unit = "second"sv, .increment = 1 };
}
// 4. If smallestUnit is "millisecond", then
if (smallest_unit == "millisecond"sv) {
// a. Return the Record { [[Precision]]: 3, [[Unit]]: "millisecond", [[Increment]]: 1 }.
return SecondsStringPrecision { .precision = 3, .unit = "millisecond"sv, .increment = 1 };
}
// 5. If smallestUnit is "microsecond", then
if (smallest_unit == "microsecond"sv) {
// a. Return the Record { [[Precision]]: 6, [[Unit]]: "microsecond", [[Increment]]: 1 }.
return SecondsStringPrecision { .precision = 6, .unit = "microsecond"sv, .increment = 1 };
}
// 6. If smallestUnit is "nanosecond", then
if (smallest_unit == "nanosecond"sv) {
// a. Return the Record { [[Precision]]: 9, [[Unit]]: "nanosecond", [[Increment]]: 1 }.
return SecondsStringPrecision { .precision = 9, .unit = "nanosecond"sv, .increment = 1 };
}
// 7. Assert: smallestUnit is undefined.
VERIFY(!smallest_unit.has_value());
// 8. Let digits be ? GetStringOrNumberOption(normalizedOptions, "fractionalSecondDigits", « "auto" », 0, 9, "auto").
auto digits_variant = get_string_or_number_option<u8>(global_object, normalized_options, vm.names.fractionalSecondDigits, { "auto"sv }, 0, 9, js_string(vm, "auto"sv));
if (vm.exception())
return {};
// 9. If digits is "auto", then
if (digits_variant->has<String>()) {
VERIFY(digits_variant->get<String>() == "auto"sv);
// a. Return the Record { [[Precision]]: "auto", [[Unit]]: "nanosecond", [[Increment]]: 1 }.
return SecondsStringPrecision { .precision = String { "auto"sv }, .unit = "nanosecond"sv, .increment = 1 };
}
auto digits = digits_variant->get<u8>();
// 10. If digits is 0, then
if (digits == 0) {
// a. Return the Record { [[Precision]]: 0, [[Unit]]: "second", [[Increment]]: 1 }.
return SecondsStringPrecision { .precision = 0, .unit = "second"sv, .increment = 1 };
}
// 11. If digits is 1, 2, or 3, then
if (digits == 1 || digits == 2 || digits == 3) {
// a. Return the Record { [[Precision]]: digits, [[Unit]]: "millisecond", [[Increment]]: 10^(3 digits) }.
return SecondsStringPrecision { .precision = digits, .unit = "millisecond"sv, .increment = (u32)pow(10, 3 - digits) };
}
// 12. If digits is 4, 5, or 6, then
if (digits == 4 || digits == 5 || digits == 6) {
// a. Return the Record { [[Precision]]: digits, [[Unit]]: "microsecond", [[Increment]]: 10^(6 digits) }.
return SecondsStringPrecision { .precision = digits, .unit = "microsecond"sv, .increment = (u32)pow(10, 6 - digits) };
}
// 13. Assert: digits is 7, 8, or 9.
VERIFY(digits == 7 || digits == 8 || digits == 9);
// 14. Return the Record { [[Precision]]: digits, [[Unit]]: "nanosecond", [[Increment]]: 10^(9 digits) }.
return SecondsStringPrecision { .precision = digits, .unit = "nanosecond"sv, .increment = (u32)pow(10, 9 - digits) };
}
// https://tc39.es/proposal-temporal/#table-temporal-singular-and-plural-units
static HashMap<StringView, StringView> plural_to_singular_units = {
{ "years"sv, "year"sv },
@ -313,6 +434,56 @@ Optional<String> to_smallest_temporal_unit(GlobalObject& global_object, Object&
return smallest_unit;
}
// 13.27 FormatSecondsStringPart ( second, millisecond, microsecond, nanosecond, precision ), https://tc39.es/proposal-temporal/#sec-temporal-formatsecondsstringpart
String format_seconds_string_part(u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<String, u8> const& precision)
{
// 1. Assert: second, millisecond, microsecond and nanosecond are integers.
// Non-standard sanity check
if (precision.has<String>())
VERIFY(precision.get<String>().is_one_of("minute"sv, "auto"sv));
// 2. If precision is "minute", return "".
if (precision.has<String>() && precision.get<String>() == "minute"sv)
return String::empty();
// 3. Let secondsString be the string-concatenation of the code unit 0x003A (COLON) and second formatted as a two-digit decimal number, padded to the left with zeroes if necessary.
auto seconds_string = String::formatted(":{:02}", second);
// 4. Let fraction be millisecond × 10^6 + microsecond × 10^3 + nanosecond.
u32 fraction = millisecond * 1'000'000 + microsecond * 1'000 + nanosecond;
String fraction_string;
// 5. If precision is "auto", then
if (precision.has<String>() && precision.get<String>() == "auto"sv) {
// a. If fraction is 0, return secondsString.
if (fraction == 0)
return seconds_string;
// b. Set fraction to fraction formatted as a nine-digit decimal number, padded to the left with zeroes if necessary.
fraction_string = String::formatted("{:09}", fraction);
// c. Set fraction to the longest possible substring of fraction starting at position 0 and not ending with the code unit 0x0030 (DIGIT ZERO).
fraction_string = fraction_string.trim("0"sv, TrimMode::Right);
}
// 6. Else,
else {
// a. If precision is 0, return secondsString.
if (precision.get<u8>() == 0)
return seconds_string;
// b. Set fraction to fraction formatted as a nine-digit decimal number, padded to the left with zeroes if necessary.
fraction_string = String::formatted("{:09}", fraction);
// c. Set fraction to the substring of fraction from 0 to precision.
fraction_string = fraction_string.substring(0, precision.get<u8>());
}
// 7. Return the string-concatenation of secondsString, the code unit 0x002E (FULL STOP), and fraction.
return String::formatted("{}.{}", seconds_string, fraction_string);
}
// 13.29 ConstrainToRange ( x, minimum, maximum ), https://tc39.es/proposal-temporal/#sec-temporal-constraintorange
double constrain_to_range(double x, double minimum, double maximum)
{

View file

@ -9,6 +9,7 @@
#include <AK/Forward.h>
#include <AK/String.h>
#include <AK/Variant.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/GlobalObject.h>
@ -69,14 +70,24 @@ struct TemporalTimeZone {
Optional<String> name;
};
struct SecondsStringPrecision {
Variant<String, u8> precision;
String unit;
u32 increment;
};
MarkedValueList iterable_to_list_of_type(GlobalObject&, Value items, Vector<OptionType> const& element_types);
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);
template<typename NumberType>
Optional<Variant<String, NumberType>> get_string_or_number_option(GlobalObject&, Object& options, PropertyName const& property, Vector<StringView> const& string_values, NumberType minimum, NumberType maximum, 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<SecondsStringPrecision> to_seconds_string_precision(GlobalObject&, Object& normalized_options);
Optional<String> to_smallest_temporal_unit(GlobalObject&, Object& normalized_options, Vector<StringView> const& disallowed_units, Optional<String> fallback);
String format_seconds_string_part(u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<String, u8> const& precision);
double constrain_to_range(double x, double minimum, double maximum);
BigInt* round_number_to_increment(GlobalObject&, BigInt const&, u64 increment, String const& rounding_mode);
Optional<ISODateTime> parse_iso_date_time(GlobalObject&, String const& iso_string);

View file

@ -5,10 +5,12 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Variant.h>
#include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
@ -233,4 +235,56 @@ BigInt* round_temporal_instant(GlobalObject& global_object, BigInt const& nanose
return round_number_to_increment(global_object, nanoseconds, increment_nanoseconds, rounding_mode);
}
// 8.5.9 TemporalInstantToString ( instant, timeZone, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporalinstanttostring
Optional<String> temporal_instant_to_string(GlobalObject& global_object, Instant& instant, Value time_zone, Variant<String, u8> const& precision)
{
auto& vm = global_object.vm();
// 1. Assert: Type(instant) is Object.
// 2. Assert: instant has an [[InitializedTemporalInstant]] internal slot.
// 3. Let outputTimeZone be timeZone.
auto output_time_zone = time_zone;
// 4. If outputTimeZone is undefined, then
if (output_time_zone.is_undefined()) {
// a. Set outputTimeZone to ? CreateTemporalTimeZone("UTC").
output_time_zone = create_temporal_time_zone(global_object, "UTC"sv);
// TODO: Can this really throw...?
if (vm.exception())
return {};
}
// 5. Let isoCalendar be ! GetISO8601Calendar().
auto* iso_calendar = get_iso8601_calendar(global_object);
// 6. Let dateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(outputTimeZone, instant, isoCalendar).
auto* date_time = builtin_time_zone_get_plain_date_time_for(global_object, output_time_zone, instant, *iso_calendar);
if (vm.exception())
return {};
// 7. Let dateTimeString be ? TemporalDateTimeToString(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], undefined, precision, "never").
auto date_time_string = temporal_date_time_to_string(global_object, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), js_undefined(), precision, "never"sv);
if (vm.exception())
return {};
Optional<String> time_zone_string;
// 8. If timeZone is undefined, then
if (time_zone.is_undefined()) {
// a. Let timeZoneString be "Z".
time_zone_string = "Z"sv;
}
// 9. Else,
else {
// a. Let timeZoneString be ? BuiltinTimeZoneGetOffsetStringFor(timeZone, instant).
time_zone_string = builtin_time_zone_get_offset_string_for(global_object, time_zone, instant);
if (vm.exception())
return {};
}
// 10. Return the string-concatenation of dateTimeString and timeZoneString.
return String::formatted("{}{}", *date_time_string, *time_zone_string);
}
}

View file

@ -8,6 +8,7 @@
#pragma once
#include <AK/Optional.h>
#include <AK/Variant.h>
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/Object.h>
@ -41,5 +42,6 @@ BigInt* parse_temporal_instant(GlobalObject&, String const& iso_string);
i32 compare_epoch_nanoseconds(BigInt const&, BigInt const&);
BigInt* add_instant(GlobalObject&, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
BigInt* round_temporal_instant(GlobalObject&, BigInt const& nanoseconds, u64 increment, String const& unit, String const& rounding_mode);
Optional<String> temporal_instant_to_string(GlobalObject&, Instant&, Value time_zone, Variant<String, u8> const& precision);
}

View file

@ -11,6 +11,7 @@
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
namespace JS::Temporal {
@ -39,6 +40,7 @@ void InstantPrototype::initialize(GlobalObject& global_object)
define_native_function(vm.names.subtract, subtract, 1, attr);
define_native_function(vm.names.round, round, 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);
}
@ -280,6 +282,59 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::equals)
return Value(true);
}
// 8.3.13 Temporal.Instant.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_string)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto* instant = 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 timeZone be ? Get(options, "timeZone").
auto time_zone = options->get(vm.names.timeZone);
if (vm.exception())
return {};
// 5. If timeZone is not undefined, then
if (!time_zone.is_undefined()) {
// a. Set timeZone to ? ToTemporalTimeZone(timeZone).
time_zone = to_temporal_time_zone(global_object, time_zone);
if (vm.exception())
return {};
}
// 6. Let precision be ? ToSecondsStringPrecision(options).
auto precision = to_seconds_string_precision(global_object, *options);
if (vm.exception())
return {};
// 7. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = to_temporal_rounding_mode(global_object, *options, "trunc"sv);
if (vm.exception())
return {};
// 8. Let roundedNs be ? RoundTemporalInstant(instant.[[Nanoseconds]], precision.[[Increment]], precision.[[Unit]], roundingMode).
auto* rounded_ns = round_temporal_instant(global_object, instant->nanoseconds(), precision->increment, precision->unit, *rounding_mode);
if (vm.exception())
return {};
// 9. Let roundedInstant be ! CreateTemporalInstant(roundedNs).
auto* rounded_instant = create_temporal_instant(global_object, *rounded_ns);
// 10. Return ? TemporalInstantToString(roundedInstant, timeZone, precision.[[Precision]]).
auto string = temporal_instant_to_string(global_object, *rounded_instant, time_zone, precision->precision);
if (vm.exception())
return {};
return js_string(vm, *string);
}
// 8.3.16 Temporal.Instant.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.valueof
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::value_of)
{

View file

@ -27,6 +27,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(subtract);
JS_DECLARE_NATIVE_FUNCTION(round);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(value_of);
};

View file

@ -302,6 +302,34 @@ PlainDateTime* create_temporal_date_time(GlobalObject& global_object, i32 iso_ye
return object;
}
// 5.5.7 TemporalDateTimeToString ( isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond, calendar, precision, showCalendar ), , https://tc39.es/proposal-temporal/#sec-temporal-temporaldatetimetostring
Optional<String> temporal_date_time_to_string(GlobalObject& global_object, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Value calendar, Variant<String, u8> const& precision, StringView show_calendar)
{
auto& vm = global_object.vm();
// 1. Assert: isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, and nanosecond are integers.
// 2. Let year be ! PadISOYear(isoYear).
// 3. Let month be isoMonth formatted as a two-digit decimal number, padded to the left with a zero if necessary.
// 4. Let day be isoDay formatted as a two-digit decimal number, padded to the left with a zero if necessary.
// 5. Let hour be hour formatted as a two-digit decimal number, padded to the left with a zero if necessary.
// 6. Let minute be minute formatted as a two-digit decimal number, padded to the left with a zero if necessary.
// 7. Let seconds be ! FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision).
auto seconds = format_seconds_string_part(second, millisecond, microsecond, nanosecond, precision);
// 8. Let calendarID be ? ToString(calendar).
auto calendar_id = calendar.to_string(global_object);
if (vm.exception())
return {};
// 9. Let calendarString be ! FormatCalendarAnnotation(calendarID, showCalendar).
auto calendar_string = format_calendar_annotation(calendar_id, show_calendar);
// 10. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, 0x0054 (LATIN CAPITAL LETTER T), hour, the code unit 0x003A (COLON), minute, seconds, and calendarString.
return String::formatted("{}-{:02}-{:02}T{:02}:{:02}{}{}", pad_iso_year(iso_year), iso_month, iso_day, hour, minute, seconds, calendar_string);
}
// 5.5.8 CompareISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2 ),https://tc39.es/proposal-temporal/#sec-temporal-compareisodatetime
i8 compare_iso_date_time(i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, i32 year2, u8 month2, u8 day2, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2)
{

View file

@ -8,6 +8,7 @@
#pragma once
#include <AK/Optional.h>
#include <AK/Variant.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
@ -54,6 +55,7 @@ Optional<ISODateTime> interpret_temporal_date_time_fields(GlobalObject&, Object&
PlainDateTime* to_temporal_date_time(GlobalObject&, Value item, Object* options = nullptr);
ISODateTime balance_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, i64 nanosecond);
PlainDateTime* create_temporal_date_time(GlobalObject&, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, FunctionObject* new_target = nullptr);
Optional<String> temporal_date_time_to_string(GlobalObject&, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Value calendar, Variant<String, u8> const& precision, StringView show_calendar);
i8 compare_iso_date_time(i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, i32 year2, u8 month2, u8 day2, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2);
}

View file

@ -416,12 +416,12 @@ double get_offset_nanoseconds_for(GlobalObject& global_object, Value time_zone,
}
// 11.6.12 BuiltinTimeZoneGetOffsetStringFor ( timeZone, instant ), https://tc39.es/proposal-temporal/#sec-temporal-builtintimezonegetoffsetstringfor
Optional<String> builtin_time_zone_get_offset_string_for(GlobalObject& global_object, Object& time_zone, Instant& instant)
Optional<String> builtin_time_zone_get_offset_string_for(GlobalObject& global_object, Value time_zone, Instant& instant)
{
auto& vm = global_object.vm();
// 1. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_nanoseconds = get_offset_nanoseconds_for(global_object, &time_zone, instant);
auto offset_nanoseconds = get_offset_nanoseconds_for(global_object, time_zone, instant);
if (vm.exception())
return {};

View file

@ -43,7 +43,7 @@ double parse_time_zone_offset_string(GlobalObject&, String const&);
String format_time_zone_offset_string(double offset_nanoseconds);
Object* to_temporal_time_zone(GlobalObject&, Value temporal_time_zone_like);
double get_offset_nanoseconds_for(GlobalObject&, Value time_zone, Instant&);
Optional<String> builtin_time_zone_get_offset_string_for(GlobalObject&, Object& time_zone, Instant&);
Optional<String> builtin_time_zone_get_offset_string_for(GlobalObject&, Value time_zone, Instant&);
PlainDateTime* builtin_time_zone_get_plain_date_time_for(GlobalObject&, Value time_zone, Instant&, Object& calendar);
bool is_valid_time_zone_numeric_utc_offset_syntax(String const&);

View file

@ -98,7 +98,7 @@ JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_offset_string_for)
return {};
// 4. Return ? BuiltinTimeZoneGetOffsetStringFor(timeZone, instant).
auto offset_string = builtin_time_zone_get_offset_string_for(global_object, *time_zone, *instant);
auto offset_string = builtin_time_zone_get_offset_string_for(global_object, time_zone, *instant);
if (vm.exception())
return {};
return js_string(vm, move(*offset_string));

View file

@ -700,7 +700,7 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::offset_getter)
auto* instant = create_temporal_instant(global_object, zoned_date_time->nanoseconds());
// 4. Return ? BuiltinTimeZoneGetOffsetStringFor(zonedDateTime.[[TimeZone]], instant).
auto offset_string = builtin_time_zone_get_offset_string_for(global_object, zoned_date_time->time_zone(), *instant);
auto offset_string = builtin_time_zone_get_offset_string_for(global_object, &zoned_date_time->time_zone(), *instant);
if (vm.exception())
return {};
return js_string(vm, move(*offset_string));
@ -955,7 +955,7 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::get_iso_fields)
return {};
// 8. Let offset be ? BuiltinTimeZoneGetOffsetStringFor(timeZone, instant).
auto maybe_offset = builtin_time_zone_get_offset_string_for(global_object, time_zone, *instant);
auto maybe_offset = builtin_time_zone_get_offset_string_for(global_object, &time_zone, *instant);
if (vm.exception())
return {};
auto offset = move(*maybe_offset);

View file

@ -0,0 +1,65 @@
describe("correct behavior", () => {
test("length is 0", () => {
expect(Temporal.Instant.prototype.toString).toHaveLength(0);
});
test("basic functionality", () => {
const instant = new Temporal.Instant(1625614921123456789n);
expect(instant.toString()).toBe("2021-07-06T23:42:01.123456789Z");
});
test("timeZone option", () => {
const instant = new Temporal.Instant(1625614921123456789n);
const options = { timeZone: new Temporal.TimeZone("+01:30") };
expect(instant.toString(options)).toBe("2021-07-07T01:12:01.123456789+01:30");
});
test("fractionalSecondDigits option", () => {
const instant = new Temporal.Instant(1625614921123456000n);
const values = [
["auto", "2021-07-06T23:42:01.123456Z"],
[0, "2021-07-06T23:42:01Z"],
[1, "2021-07-06T23:42:01.1Z"],
[2, "2021-07-06T23:42:01.12Z"],
[3, "2021-07-06T23:42:01.123Z"],
[4, "2021-07-06T23:42:01.1234Z"],
[5, "2021-07-06T23:42:01.12345Z"],
[6, "2021-07-06T23:42:01.123456Z"],
[7, "2021-07-06T23:42:01.1234560Z"],
[8, "2021-07-06T23:42:01.12345600Z"],
[9, "2021-07-06T23:42:01.123456000Z"],
];
for (const [fractionalSecondDigits, expected] of values) {
const options = { fractionalSecondDigits };
expect(instant.toString(options)).toBe(expected);
}
// Ignored when smallestUnit is given
expect(instant.toString({ smallestUnit: "minute", fractionalSecondDigits: 9 })).toBe(
"2021-07-06T23:42Z"
);
});
test("smallestUnit option", () => {
const instant = new Temporal.Instant(1625614921123456789n);
const values = [
["minute", "2021-07-06T23:42Z"],
["second", "2021-07-06T23:42:01Z"],
["millisecond", "2021-07-06T23:42:01.123Z"],
["microsecond", "2021-07-06T23:42:01.123456Z"],
["nanosecond", "2021-07-06T23:42:01.123456789Z"],
];
for (const [smallestUnit, expected] of values) {
const options = { smallestUnit };
expect(instant.toString(options)).toBe(expected);
}
});
});
describe("errors", () => {
test("this value must be a Temporal.Instant object", () => {
expect(() => {
Temporal.Instant.prototype.toString.call("foo");
}).toThrowWithMessage(TypeError, "Not a Temporal.Instant");
});
});