LibJS: Stub out Temporal.ZonedDateTime.prototype.getTimeZoneTransition

We will have to add facilities to determine next/previous time zone
transitions. Ideally, ICU can provide this.
This commit is contained in:
Timothy Flynn 2024-11-25 13:51:48 -05:00 committed by Andreas Kling
commit b95528d7b5
Notes: github-actions[bot] 2024-11-26 10:03:09 +00:00
8 changed files with 133 additions and 0 deletions

View file

@ -264,6 +264,7 @@ namespace JS {
P(getTime) \
P(getTimezoneOffset) \
P(getTimeZones) \
P(getTimeZoneTransition) \
P(getUint8) \
P(getUint16) \
P(getUint32) \

View file

@ -245,6 +245,21 @@ ThrowCompletionOr<ShowOffset> get_temporal_show_offset_option(VM& vm, Object con
return ShowOffset::Auto;
}
// 13.13 GetDirectionOption ( options ), https://tc39.es/proposal-temporal/#sec-temporal-getdirectionoption
ThrowCompletionOr<Direction> get_direction_option(VM& vm, Object const& options)
{
// 1. Let stringValue be ? GetOption(options, "direction", STRING, « "next", "previous" », REQUIRED).
auto string_value = TRY(get_option(vm, options, vm.names.direction, OptionType::String, { "next"sv, "previous"sv }, Required {}));
auto string_view = string_value.as_string().utf8_string_view();
// 2. If stringValue is "next", return NEXT.
if (string_view == "next"sv)
return Direction::Next;
// 3. Return PREVIOUS.
return Direction::Previous;
}
// 13.14 ValidateTemporalRoundingIncrement ( increment, dividend, inclusive ), https://tc39.es/proposal-temporal/#sec-validatetemporalroundingincrement
ThrowCompletionOr<void> validate_temporal_rounding_increment(VM& vm, u64 increment, u64 dividend, bool inclusive)
{

View file

@ -33,6 +33,11 @@ enum class DateType {
YearMonth,
};
enum class Direction {
Next,
Previous,
};
enum class Disambiguation {
Compatible,
Earlier,
@ -175,6 +180,7 @@ ThrowCompletionOr<OffsetOption> get_temporal_offset_option(VM&, Object const& op
ThrowCompletionOr<ShowTimeZoneName> get_temporal_show_time_zone_name_option(VM&, Object const& options);
ThrowCompletionOr<ShowOffset> get_temporal_show_offset_option(VM&, Object const& options);
ThrowCompletionOr<ShowCalendar> get_temporal_show_calendar_name_option(VM&, Object const& options);
ThrowCompletionOr<Direction> get_direction_option(VM&, Object const& options);
ThrowCompletionOr<void> validate_temporal_rounding_increment(VM&, u64 increment, u64 dividend, bool inclusive);
ThrowCompletionOr<Precision> get_temporal_fractional_second_digits_option(VM&, Object const& options);
SecondsStringPrecision to_seconds_string_precision_record(UnitValue, Precision);

View file

@ -75,6 +75,26 @@ ISODateTime get_iso_parts_from_epoch(Crypto::SignedBigInteger const& epoch_nanos
return combine_iso_date_and_time_record(iso_date, time);
}
// 11.1.3 GetNamedTimeZoneNextTransition ( timeZoneIdentifier, epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-getnamedtimezonenexttransition
Optional<Crypto::SignedBigInteger> get_named_time_zone_next_transition(StringView time_zone, Crypto::SignedBigInteger const& epoch_nanoseconds)
{
// FIXME: Implement this AO.
(void)time_zone;
(void)epoch_nanoseconds;
return {};
}
// 11.1.4 GetNamedTimeZonePreviousTransition ( timeZoneIdentifier, epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-getnamedtimezoneprevioustransition
Optional<Crypto::SignedBigInteger> get_named_time_zone_previous_transition(StringView time_zone, Crypto::SignedBigInteger const& epoch_nanoseconds)
{
// FIXME: Implement this AO.
(void)time_zone;
(void)epoch_nanoseconds;
return {};
}
// 11.1.5 FormatOffsetTimeZoneIdentifier ( offsetMinutes [ , style ] ), https://tc39.es/proposal-temporal/#sec-temporal-formatoffsettimezoneidentifier
String format_offset_time_zone_identifier(i64 offset_minutes, Optional<TimeStyle> style)
{

View file

@ -22,6 +22,8 @@ struct TimeZone {
};
ISODateTime get_iso_parts_from_epoch(Crypto::SignedBigInteger const& epoch_nanoseconds);
Optional<Crypto::SignedBigInteger> get_named_time_zone_next_transition(StringView time_zone, Crypto::SignedBigInteger const& epoch_nanoseconds);
Optional<Crypto::SignedBigInteger> get_named_time_zone_previous_transition(StringView time_zone, Crypto::SignedBigInteger const& epoch_nanoseconds);
String format_offset_time_zone_identifier(i64 offset_minutes, Optional<TimeStyle> = {});
String format_utc_offset_nanoseconds(i64 offset_nanoseconds);
String format_date_time_utc_offset_rounded(i64 offset_nanoseconds);

View file

@ -80,6 +80,7 @@ void ZonedDateTimePrototype::initialize(Realm& realm)
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
define_native_function(realm, vm.names.startOfDay, start_of_day, 0, attr);
define_native_function(realm, vm.names.getTimeZoneTransition, get_time_zone_transition, 1, attr);
}
// 6.3.3 get Temporal.ZonedDateTime.prototype.calendarId, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.calendarid
@ -832,4 +833,72 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::start_of_day)
return MUST(create_temporal_zoned_date_time(vm, BigInt::create(vm, move(epoch_nanoseconds)), time_zone, calendar));
}
// 6.3.46 Temporal.ZonedDateTime.prototype.getTimeZoneTransition ( directionParam ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.gettimezonetransition
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::get_time_zone_transition)
{
auto& realm = *vm.current_realm();
auto direction_param_value = vm.argument(0);
// 1. Let zonedDateTime be the this value.
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
auto zoned_date_time = TRY(typed_this_object(vm));
// 3. Let timeZone be zonedDateTime.[[TimeZone]].
auto const& time_zone = zoned_date_time->time_zone();
// 4. If directionParam is undefined, throw a TypeError exception.
if (direction_param_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "Transition direction parameter"sv);
GC::Ptr<Object> direction_param;
// 5. If directionParam is a String, then
if (direction_param_value.is_string()) {
// a. Let paramString be directionParam.
auto param_string = direction_param_value;
// b. Set directionParam to OrdinaryObjectCreate(null).
direction_param = Object::create(realm, nullptr);
// c. Perform ! CreateDataPropertyOrThrow(directionParam, "direction", paramString).
MUST(direction_param->create_data_property_or_throw(vm.names.direction, param_string));
}
// 6. Else,
else {
// a. Set directionParam to ? GetOptionsObject(directionParam).
direction_param = TRY(get_options_object(vm, direction_param_value));
}
// 7. Let direction be ? GetDirectionOption(directionParam).
auto direction = TRY(get_direction_option(vm, *direction_param));
// 8. If IsOffsetTimeZoneIdentifier(timeZone) is true, return null.
if (is_offset_time_zone_identifier(time_zone))
return js_null();
Optional<Crypto::SignedBigInteger> transition;
switch (direction) {
// 9. If direction is NEXT, then
case Direction::Next:
// a. Let transition be GetNamedTimeZoneNextTransition(timeZone, zonedDateTime.[[EpochNanoseconds]]).
transition = get_named_time_zone_next_transition(time_zone, zoned_date_time->epoch_nanoseconds()->big_integer());
break;
// 10. Else,
case Direction::Previous:
// a. Assert: direction is PREVIOUS.
// b. Let transition be GetNamedTimeZonePreviousTransition(timeZone, zonedDateTime.[[EpochNanoseconds]]).
transition = get_named_time_zone_previous_transition(time_zone, zoned_date_time->epoch_nanoseconds()->big_integer());
break;
}
// 11. If transition is null, return null.
if (!transition.has_value())
return js_null();
// 12. Return ! CreateTemporalZonedDateTime(transition, timeZone, zonedDateTime.[[Calendar]]).
return MUST(create_temporal_zoned_date_time(vm, BigInt::create(vm, transition.release_value()), time_zone, zoned_date_time->calendar()));
}
}

View file

@ -66,6 +66,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(to_json);
JS_DECLARE_NATIVE_FUNCTION(value_of);
JS_DECLARE_NATIVE_FUNCTION(start_of_day);
JS_DECLARE_NATIVE_FUNCTION(get_time_zone_transition);
};
}

View file

@ -0,0 +1,19 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.ZonedDateTime.prototype.getTimeZoneTransition).toHaveLength(1);
});
test("basic functionality", () => {
const zonedDateTime = new Temporal.ZonedDateTime(1627318123456789000n, "UTC", "iso8601");
expect(zonedDateTime.getTimeZoneTransition("next")).toBeNull();
expect(zonedDateTime.getTimeZoneTransition("previous")).toBeNull();
});
});
describe("errors", () => {
test("this value must be a Temporal.TimeZone object", () => {
expect(() => {
Temporal.ZonedDateTime.prototype.getTimeZoneTransition.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
});
});