LibJS: Implement Temporal.TimeZone.prototype.getInstantFor()
This commit is contained in:
parent
55e1edd51b
commit
97f6c6029f
Notes:
sideshowbarker
2024-07-18 01:38:12 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/97f6c6029ff Pull-request: https://github.com/SerenityOS/serenity/pull/10743 Reviewed-by: https://github.com/IdanHo ✅
15 changed files with 341 additions and 0 deletions
|
@ -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) \
|
||||
|
|
|
@ -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 '{}'") \
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 = {});
|
||||
|
||||
}
|
||||
|
|
|
@ -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) };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue