LibJS: Implement Temporal.TimeZone.prototype.getInstantFor()

This commit is contained in:
Linus Groh 2021-11-01 14:20:06 +01:00
parent 55e1edd51b
commit 97f6c6029f
Notes: sideshowbarker 2024-07-18 01:38:12 +09:00
15 changed files with 341 additions and 0 deletions

View file

@ -133,6 +133,7 @@ namespace JS {
P(deleteProperty) \
P(deref) \
P(description) \
P(disambiguation) \
P(done) \
P(dotAll) \
P(encodeURI) \
@ -198,6 +199,7 @@ namespace JS {
P(getFloat64) \
P(getFullYear) \
P(getHours) \
P(getInstantFor) \
P(getInt8) \
P(getInt16) \
P(getInt32) \

View file

@ -196,6 +196,10 @@
M(StringRawCannotConvert, "Cannot convert property 'raw' to object from {}") \
M(StringRepeatCountMustBe, "repeat count must be a {} number") \
M(TemporalAmbiguousMonthOfPlainMonthDay, "Accessing month of PlainMonthDay is ambiguous, use monthCode instead") \
M(TemporalDisambiguatePossibleInstantsEarlierZero, "Cannot disambiguate zero possible instants with mode \"earlier\"") \
M(TemporalDisambiguatePossibleInstantsRejectMoreThanOne, "Cannot disambiguate two or more possible instants with mode \"reject\"") \
M(TemporalDisambiguatePossibleInstantsRejectZero, "Cannot disambiguate zero possible instants with mode \"reject\"") \
M(TemporalDisambiguatePossibleInstantsZero, "Cannot disambiguate zero possible instants") \
M(TemporalDuplicateCalendarField, "Duplicate calendar field '{}'") \
M(TemporalInvalidCalendar, "Invalid calendar") \
M(TemporalInvalidCalendarFieldName, "Invalid calendar field '{}'") \

View file

@ -200,6 +200,18 @@ ThrowCompletionOr<String> to_temporal_overflow(GlobalObject& global_object, Obje
return option.as_string().string();
}
// 13.7 ToTemporalDisambiguation ( normalizedOptions ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldisambiguation
ThrowCompletionOr<String> to_temporal_disambiguation(GlobalObject& global_object, Object const& normalized_options)
{
auto& vm = global_object.vm();
// 1. Return ? GetOption(normalizedOptions, "disambiguation", « String », « "compatible", "earlier", "later", "reject" », "compatible").
auto option = TRY(get_option(global_object, normalized_options, vm.names.disambiguation, { OptionType::String }, { "compatible"sv, "earlier"sv, "later"sv, "reject"sv }, js_string(vm, "compatible")));
VERIFY(option.is_string());
return option.as_string().string();
}
// 13.8 ToTemporalRoundingMode ( normalizedOptions, fallback ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalroundingmode
ThrowCompletionOr<String> to_temporal_rounding_mode(GlobalObject& global_object, Object const& normalized_options, String const& fallback)
{

View file

@ -90,6 +90,7 @@ ThrowCompletionOr<Value> get_option(GlobalObject&, Object const& options, Proper
template<typename NumberType>
ThrowCompletionOr<Variant<String, NumberType>> get_string_or_number_option(GlobalObject&, Object const& options, PropertyKey const& property, Vector<StringView> const& string_values, NumberType minimum, NumberType maximum, Value fallback);
ThrowCompletionOr<String> to_temporal_overflow(GlobalObject&, Object const& normalized_options);
ThrowCompletionOr<String> to_temporal_disambiguation(GlobalObject&, Object const& normalized_options);
ThrowCompletionOr<String> to_temporal_rounding_mode(GlobalObject&, Object const& normalized_options, String const& fallback);
ThrowCompletionOr<String> to_show_calendar_option(GlobalObject&, Object const& normalized_options);
ThrowCompletionOr<u64> to_temporal_rounding_increment(GlobalObject&, Object const& normalized_options, Optional<double> dividend, bool inclusive);

View file

@ -12,6 +12,7 @@
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
@ -135,6 +136,29 @@ ThrowCompletionOr<Object*> calendar_merge_fields(GlobalObject& global_object, Ob
return &result.as_object();
}
// 12.1.7 CalendarDateAdd ( calendar, date, duration, options [ , dateAdd ] ), https://tc39.es/proposal-temporal/#sec-temporal-calendardateadd
ThrowCompletionOr<PlainDate*> calendar_date_add(GlobalObject& global_object, Object& calendar, PlainDate& date, Duration& duration, Object* options, FunctionObject* date_add)
{
auto& vm = global_object.vm();
// 1. Assert: Type(calendar) is Object.
// 2. If dateAdd is not present, set dateAdd to ? GetMethod(calendar, "dateAdd").
if (!date_add)
date_add = TRY(Value(&calendar).get_method(global_object, vm.names.dateAdd));
// 3. Let addedDate be ? Call(dateAdd, calendar, « date, duration, options »).
auto added_date = TRY(call(global_object, date_add ?: js_undefined(), &calendar, &date, &duration, options ?: js_undefined()));
// 4. Perform ? RequireInternalSlot(addedDate, [[InitializedTemporalDate]]).
auto* added_date_object = TRY(added_date.to_object(global_object));
if (!is<PlainDate>(added_date_object))
return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObjectOfType, "Temporal.PlainDate");
// 5. Return addedDate.
return static_cast<PlainDate*>(added_date_object);
}
// 12.1.9 CalendarYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendaryear
ThrowCompletionOr<double> calendar_year(GlobalObject& global_object, Object& calendar, Object& date_like)
{

View file

@ -36,6 +36,7 @@ ThrowCompletionOr<Calendar*> get_builtin_calendar(GlobalObject&, String const& i
Calendar* get_iso8601_calendar(GlobalObject&);
ThrowCompletionOr<Vector<String>> calendar_fields(GlobalObject&, Object& calendar, Vector<StringView> const& field_names);
ThrowCompletionOr<Object*> calendar_merge_fields(GlobalObject&, Object& calendar, Object& fields, Object& additional_fields);
ThrowCompletionOr<PlainDate*> calendar_date_add(GlobalObject&, Object& calendar, PlainDate&, Duration&, Object* options, FunctionObject* date_add = nullptr);
ThrowCompletionOr<double> calendar_year(GlobalObject&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_month(GlobalObject&, Object& calendar, Object& date_like);
ThrowCompletionOr<String> calendar_month_code(GlobalObject&, Object& calendar, Object& date_like);

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -10,6 +11,7 @@
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
@ -310,4 +312,25 @@ i8 compare_iso_date_time(i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8
return compare_temporal_time(hour1, minute1, second1, millisecond1, microsecond1, nanosecond1, hour2, minute2, second2, millisecond2, microsecond2, nanosecond2);
}
// 5.5.9 AddDateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddatetime
ThrowCompletionOr<TemporalPlainDateTime> add_date_time(GlobalObject& global_object, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options)
{
// 1. Assert: year, month, day, hour, minute, second, millisecond, microsecond, and nanosecond are integers.
// 2. Let timeResult be ! AddTime(hour, minute, second, millisecond, microsecond, nanosecond, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
auto time_result = add_time(hour, minute, second, millisecond, microsecond, nanosecond, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
// 3. Let datePart be ? CreateTemporalDate(year, month, day, calendar).
auto* date_part = TRY(create_temporal_date(global_object, year, month, day, calendar));
// 4. Let dateDuration be ? CreateTemporalDuration(years, months, weeks, days + timeResult.[[Days]], 0, 0, 0, 0, 0, 0).
auto* date_duration = TRY(create_temporal_duration(global_object, years, months, weeks, days + time_result.days, 0, 0, 0, 0, 0, 0));
// 5. Let addedDate be ? CalendarDateAdd(calendar, datePart, dateDuration, options).
auto* added_date = TRY(calendar_date_add(global_object, calendar, *date_part, *date_duration, options));
// 6. Return the Record { [[Year]]: addedDate.[[ISOYear]], [[Month]]: addedDate.[[ISOMonth]], [[Day]]: addedDate.[[ISODay]], [[Hour]]: timeResult.[[Hour]], [[Minute]]: timeResult.[[Minute]], [[Second]]: timeResult.[[Second]], [[Millisecond]]: timeResult.[[Millisecond]], [[Microsecond]]: timeResult.[[Microsecond]], [[Nanosecond]]: timeResult.[[Nanosecond]] }.
return TemporalPlainDateTime { .year = added_date->iso_year(), .month = added_date->iso_month(), .day = added_date->iso_day(), .hour = time_result.hour, .minute = time_result.minute, .second = time_result.second, .millisecond = time_result.millisecond, .microsecond = time_result.microsecond, .nanosecond = time_result.nanosecond };
}
}

View file

@ -49,6 +49,19 @@ private:
Object& m_calendar; // [[Calendar]]
};
// Used by AddDateTime to temporarily hold values
struct TemporalPlainDateTime {
i32 year;
u8 month;
u8 day;
u8 hour;
u8 minute;
u8 second;
u16 millisecond;
u16 microsecond;
u16 nanosecond;
};
BigInt* get_epoch_from_iso_parts(GlobalObject&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond);
bool iso_date_time_within_limits(GlobalObject&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond);
ThrowCompletionOr<ISODateTime> interpret_temporal_date_time_fields(GlobalObject&, Object& calendar, Object& fields, Object& options);
@ -57,5 +70,6 @@ ISODateTime balance_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute
ThrowCompletionOr<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 const* new_target = nullptr);
ThrowCompletionOr<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<StringView, 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);
ThrowCompletionOr<TemporalPlainDateTime> add_date_time(GlobalObject&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options);
}

View file

@ -452,6 +452,36 @@ i8 compare_temporal_time(u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16
return 0;
}
// 4.5.12 AddTime ( hour, minute, second, millisecond, microsecond, nanosecond, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-addtime
DaysAndTime add_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
{
// 1. Assert: hour, minute, second, millisecond, microsecond, nanosecond, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds are integers.
VERIFY(hours == trunc(hours) && minutes == trunc(minutes) && seconds == trunc(seconds) && milliseconds == trunc(milliseconds) && microseconds == trunc(microseconds) && nanoseconds == trunc(nanoseconds));
// FIXME: balance_time() should probably take double arguments. In fact, pretty much every balance_foo() needed to take doubles at some point.
// 2. Let hour be hour + hours.
i64 hour_ = hour + hours;
// 3. Let minute be minute + minutes.
i64 minute_ = minute + minutes;
// 4. Let second be second + seconds.
i64 second_ = second + seconds;
// 5. Let millisecond be millisecond + milliseconds.
i64 millisecond_ = millisecond + milliseconds;
// 6. Let microsecond be microsecond + microseconds.
i64 microsecond_ = microsecond + microseconds;
// 7. Let nanosecond be nanosecond + nanoseconds.
i64 nanosecond_ = nanosecond + nanoseconds;
// 8. Return ! BalanceTime(hour, minute, second, millisecond, microsecond, nanosecond).
return balance_time(hour_, minute_, second_, millisecond_, microsecond_, nanosecond_);
}
// 4.5.13 RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, roundingMode [ , dayLengthNs ] ), https://tc39.es/proposal-temporal/#sec-temporal-roundtime
DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length_ns)
{

View file

@ -102,6 +102,7 @@ ThrowCompletionOr<PlainTime*> create_temporal_time(GlobalObject&, u8 hour, u8 mi
ThrowCompletionOr<UnregulatedTemporalTime> to_temporal_time_record(GlobalObject&, Object const& temporal_time_like);
String temporal_time_to_string(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<StringView, u8> const& precision);
i8 compare_temporal_time(u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2);
DaysAndTime add_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length_ns = {});
}

View file

@ -9,6 +9,7 @@
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorOperations.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
@ -446,4 +447,177 @@ ThrowCompletionOr<PlainDateTime*> builtin_time_zone_get_plain_date_time_for(Glob
return create_temporal_date_time(global_object, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, calendar);
}
// 11.6.14 BuiltinTimeZoneGetInstantFor ( timeZone, dateTime, disambiguation ), https://tc39.es/proposal-temporal/#sec-temporal-builtintimezonegetinstantfor
ThrowCompletionOr<Instant*> builtin_time_zone_get_instant_for(GlobalObject& global_object, Value time_zone, PlainDateTime& date_time, StringView disambiguation)
{
// 1. Assert: dateTime has an [[InitializedTemporalDateTime]] internal slot.
// 2. Let possibleInstants be ? GetPossibleInstantsFor(timeZone, dateTime).
auto possible_instants = TRY(get_possible_instants_for(global_object, time_zone, date_time));
// 3. Return ? DisambiguatePossibleInstants(possibleInstants, timeZone, dateTime, disambiguation).
return disambiguate_possible_instants(global_object, possible_instants, time_zone, date_time, disambiguation);
}
// 11.6.15 DisambiguatePossibleInstants ( possibleInstants, timeZone, dateTime, disambiguation ), https://tc39.es/proposal-temporal/#sec-temporal-disambiguatepossibleinstants
ThrowCompletionOr<Instant*> disambiguate_possible_instants(GlobalObject& global_object, Vector<Value> const& possible_instants, Value time_zone, PlainDateTime& date_time, StringView disambiguation)
{
// TODO: MarkedValueList<T> would be nice, then we could pass a Vector<Instant*> here and wouldn't need the casts...
auto& vm = global_object.vm();
// 1. Assert: dateTime has an [[InitializedTemporalDateTime]] internal slot.
// 2. Let n be possibleInstants's length.
auto n = possible_instants.size();
// 3. If n = 1, then
if (n == 1) {
// a. Return possibleInstants[0].
auto& instant = possible_instants[0];
return &static_cast<Instant&>(const_cast<Object&>(instant.as_object()));
}
// 4. If n ≠ 0, then
if (n != 0) {
// a. If disambiguation is "earlier" or "compatible", then
if (disambiguation.is_one_of("earlier"sv, "compatible"sv)) {
// i. Return possibleInstants[0].
auto& instant = possible_instants[0];
return &static_cast<Instant&>(const_cast<Object&>(instant.as_object()));
}
// b. If disambiguation is "later", then
if (disambiguation == "later"sv) {
// i. Return possibleInstants[n 1].
auto& instant = possible_instants[n - 1];
return &static_cast<Instant&>(const_cast<Object&>(instant.as_object()));
}
// c. Assert: disambiguation is "reject".
VERIFY(disambiguation == "reject"sv);
// d. Throw a RangeError exception.
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDisambiguatePossibleInstantsRejectMoreThanOne);
}
// 5. Assert: n = 0.
VERIFY(n == 0);
// 6. If disambiguation is "reject", then
if (disambiguation == "reject"sv) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDisambiguatePossibleInstantsRejectZero);
}
// 7. Let epochNanoseconds be ! GetEpochFromISOParts(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]]).
auto* epoch_nanoseconds = get_epoch_from_iso_parts(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());
// 8. Let dayBefore be ! CreateTemporalInstant(epochNanoseconds 8.64 × 10^13).
auto* day_before = MUST(create_temporal_instant(global_object, *js_bigint(vm, epoch_nanoseconds->big_integer().minus("86400000000000"_sbigint))));
// 9. Let dayAfter be ! CreateTemporalInstant(epochNanoseconds + 8.64 × 10^13).
auto* day_after = MUST(create_temporal_instant(global_object, *js_bigint(vm, epoch_nanoseconds->big_integer().plus("86400000000000"_sbigint))));
// 10. Let offsetBefore be ? GetOffsetNanosecondsFor(timeZone, dayBefore).
auto offset_before = TRY(get_offset_nanoseconds_for(global_object, time_zone, *day_before));
// 11. Let offsetAfter be ? GetOffsetNanosecondsFor(timeZone, dayAfter).
auto offset_after = TRY(get_offset_nanoseconds_for(global_object, time_zone, *day_after));
// 12. Let nanoseconds be offsetAfter offsetBefore.
auto nanoseconds = offset_after - offset_before;
// 13. If disambiguation is "earlier", then
if (disambiguation == "earlier"sv) {
TODO();
// a. Let earlier be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], dateTime.[[Calendar]], 0, 0, 0, 0, 0, 0, 0, 0, 0, nanoseconds, undefined).
auto earlier = TRY(add_date_time(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(), date_time.calendar(), 0, 0, 0, 0, 0, 0, 0, 0, 0, -nanoseconds, nullptr));
// b. Let earlierDateTime be ! CreateTemporalDateTime(earlier.[[Year]], earlier.[[Month]], earlier.[[Day]], earlier.[[Hour]], earlier.[[Minute]], earlier.[[Second]], earlier.[[Millisecond]], earlier.[[Microsecond]], earlier.[[Nanosecond]], dateTime.[[Calendar]]).
auto* earlier_date_time = MUST(create_temporal_date_time(global_object, earlier.year, earlier.month, earlier.day, earlier.hour, earlier.minute, earlier.second, earlier.millisecond, earlier.microsecond, earlier.nanosecond, date_time.calendar()));
// c. Set possibleInstants to ? GetPossibleInstantsFor(timeZone, earlierDateTime).
auto possible_instants_mvl = TRY(get_possible_instants_for(global_object, time_zone, *earlier_date_time));
// d. If possibleInstants is empty, throw a RangeError exception.
if (possible_instants_mvl.is_empty())
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDisambiguatePossibleInstantsEarlierZero);
// e. Return possibleInstants[0].
auto& instant = possible_instants_mvl[0];
return &static_cast<Instant&>(const_cast<Object&>(instant.as_object()));
}
// 14. Assert: disambiguation is "compatible" or "later".
VERIFY(disambiguation.is_one_of("compatible"sv, "later"sv));
// 15. Let later be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], dateTime.[[Calendar]], 0, 0, 0, 0, 0, 0, 0, 0, 0, nanoseconds, undefined).
auto later = TRY(add_date_time(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(), date_time.calendar(), 0, 0, 0, 0, 0, 0, 0, 0, 0, nanoseconds, nullptr));
// 16. Let laterDateTime be ! CreateTemporalDateTime(later.[[Year]], later.[[Month]], later.[[Day]], later.[[Hour]], later.[[Minute]], later.[[Second]], later.[[Millisecond]], later.[[Microsecond]], later.[[Nanosecond]], dateTime.[[Calendar]]).
auto* later_date_time = MUST(create_temporal_date_time(global_object, later.year, later.month, later.day, later.hour, later.minute, later.second, later.millisecond, later.microsecond, later.nanosecond, date_time.calendar()));
// 17. Set possibleInstants to ? GetPossibleInstantsFor(timeZone, laterDateTime).
auto possible_instants_mvl = TRY(get_possible_instants_for(global_object, time_zone, *later_date_time));
// 18. Set n to possibleInstants's length.
n = possible_instants_mvl.size();
// 19. If n = 0, throw a RangeError exception.
if (n == 0)
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDisambiguatePossibleInstantsZero);
// 20. Return possibleInstants[n 1].
auto& instant = possible_instants_mvl[n - 1];
return &static_cast<Instant&>(const_cast<Object&>(instant.as_object()));
}
// 11.6.16 GetPossibleInstantsFor ( timeZone, dateTime ), https://tc39.es/proposal-temporal/#sec-temporal-getpossibleinstantsfor
ThrowCompletionOr<MarkedValueList> get_possible_instants_for(GlobalObject& global_object, Value time_zone, PlainDateTime& date_time)
{
auto& vm = global_object.vm();
// 1. Assert: dateTime has an [[InitializedTemporalDateTime]] internal slot.
// 2. Let possibleInstants be ? Invoke(timeZone, "getPossibleInstantsFor", « dateTime »).
auto possible_instants = TRY(time_zone.invoke(global_object, vm.names.getPossibleInstantsFor, &date_time));
// 3. Let iteratorRecord be ? GetIterator(possibleInstants, sync).
auto* iterator = TRY(get_iterator(global_object, possible_instants, IteratorHint::Sync));
// 4. Let list be a new empty List.
auto list = MarkedValueList { vm.heap() };
// 5. Let next be true.
Object* next = nullptr;
// 6. Repeat, while next is not false,
do {
// a. Set next to ? IteratorStep(iteratorRecord).
next = TRY(iterator_step(global_object, *iterator));
// b. If next is not false, then
if (next) {
// i. Let nextValue be ? IteratorValue(next).
auto next_value = TRY(iterator_value(global_object, *next));
// ii. If Type(nextValue) is not Object or nextValue does not have an [[InitializedTemporalInstant]] internal slot, then
if (!next_value.is_object() || !is<Instant>(next_value.as_object())) {
// 1. Let completion be ThrowCompletion(a newly created TypeError object).
auto completion = vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObjectOfType, "Temporal.Instant");
// 2. Return ? IteratorClose(iteratorRecord, completion).
return iterator_close(*iterator, move(completion));
}
// iii. Append nextValue to the end of the List list.
list.append(next_value);
}
} while (next != nullptr);
// 7. Return list.
return { move(list) };
}
}

View file

@ -49,6 +49,9 @@ ThrowCompletionOr<Object*> to_temporal_time_zone(GlobalObject&, Value temporal_t
ThrowCompletionOr<double> get_offset_nanoseconds_for(GlobalObject&, Value time_zone, Instant&);
ThrowCompletionOr<String> builtin_time_zone_get_offset_string_for(GlobalObject&, Value time_zone, Instant&);
ThrowCompletionOr<PlainDateTime*> builtin_time_zone_get_plain_date_time_for(GlobalObject&, Value time_zone, Instant&, Object& calendar);
ThrowCompletionOr<Instant*> builtin_time_zone_get_instant_for(GlobalObject&, Value time_zone, PlainDateTime&, StringView disambiguation);
ThrowCompletionOr<Instant*> disambiguate_possible_instants(GlobalObject&, Vector<Value> const& possible_instants, Value time_zone, PlainDateTime&, StringView disambiguation);
ThrowCompletionOr<MarkedValueList> get_possible_instants_for(GlobalObject&, Value time_zone, PlainDateTime&);
bool is_valid_time_zone_numeric_utc_offset_syntax(String const&);

View file

@ -7,6 +7,7 @@
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/Array.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/PlainDateTime.h>
@ -32,6 +33,7 @@ void TimeZonePrototype::initialize(GlobalObject& global_object)
define_native_function(vm.names.getOffsetNanosecondsFor, get_offset_nanoseconds_for, 1, attr);
define_native_function(vm.names.getOffsetStringFor, get_offset_string_for, 1, attr);
define_native_function(vm.names.getPlainDateTimeFor, get_plain_date_time_for, 1, attr);
define_native_function(vm.names.getInstantFor, get_instant_for, 1, attr);
define_native_function(vm.names.getPossibleInstantsFor, get_possible_instants_for, 1, attr);
define_native_function(vm.names.getNextTransition, get_next_transition, 1, attr);
define_native_function(vm.names.getPreviousTransition, get_previous_transition, 1, attr);
@ -101,6 +103,26 @@ JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_plain_date_time_for)
return TRY(builtin_time_zone_get_plain_date_time_for(global_object, time_zone, *instant, *calendar));
}
// 11.4.7 Temporal.TimeZone.prototype.getInstantFor ( dateTime [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getinstantfor
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_instant_for)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto* time_zone = TRY(typed_this_object(global_object));
// 3. Set dateTime to ? ToTemporalDateTime(dateTime).
auto* date_time = TRY(to_temporal_date_time(global_object, vm.argument(0)));
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(global_object, vm.argument(1)));
// 5. Let disambiguation be ? ToTemporalDisambiguation(options).
auto disambiguation = TRY(to_temporal_disambiguation(global_object, *options));
// 6. Return ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, disambiguation).
return TRY(builtin_time_zone_get_instant_for(global_object, time_zone, *date_time, disambiguation));
}
// 11.4.8 Temporal.TimeZone.prototype.getPossibleInstantsFor ( dateTime ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getpossibleinstantsfor
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_possible_instants_for)
{

View file

@ -24,6 +24,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(get_offset_nanoseconds_for);
JS_DECLARE_NATIVE_FUNCTION(get_offset_string_for);
JS_DECLARE_NATIVE_FUNCTION(get_plain_date_time_for);
JS_DECLARE_NATIVE_FUNCTION(get_instant_for);
JS_DECLARE_NATIVE_FUNCTION(get_possible_instants_for);
JS_DECLARE_NATIVE_FUNCTION(get_next_transition);
JS_DECLARE_NATIVE_FUNCTION(get_previous_transition);

View file

@ -0,0 +1,29 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.TimeZone.prototype.getInstantFor).toHaveLength(1);
});
test("basic functionality", () => {
const timeZone = new Temporal.TimeZone("UTC");
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 6, 18, 14, 47);
const instant = timeZone.getInstantFor(plainDateTime);
expect(instant).toBeInstanceOf(Temporal.Instant);
expect(instant.epochNanoseconds).toBe(1625595287000000000n);
});
test("custom offset", () => {
const timeZone = new Temporal.TimeZone("+01:30");
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 6, 18, 14, 47);
const instant = timeZone.getInstantFor(plainDateTime);
expect(instant).toBeInstanceOf(Temporal.Instant);
expect(instant.epochNanoseconds).toBe(1625589887000000000n);
});
});
describe("errors", () => {
test("this value must be a Temporal.TimeZone object", () => {
expect(() => {
Temporal.TimeZone.prototype.getInstantFor.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.TimeZone");
});
});