LibJS: Implement Temporal.ZonedDateTime.prototype.with*

Includes:

Temporal.ZonedDateTime.prototype.with
Temporal.ZonedDateTime.prototype.withPlainTime
Temporal.ZonedDateTime.prototype.withCalendar
Temporal.ZonedDateTime.prototype.withTimeZone
This commit is contained in:
Timothy Flynn 2024-11-25 13:29:01 -05:00 committed by Andreas Kling
parent f2ab9e1aa9
commit 3d0f384e01
Notes: github-actions[bot] 2024-11-26 10:03:27 +00:00
6 changed files with 427 additions and 0 deletions

View file

@ -6,11 +6,13 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/Temporal/Calendar.h> #include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Duration.h> #include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/Instant.h> #include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/PlainDate.h> #include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h> #include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/TimeZone.h> #include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimePrototype.h> #include <LibJS/Runtime/Temporal/ZonedDateTimePrototype.h>
@ -63,6 +65,10 @@ void ZonedDateTimePrototype::initialize(Realm& realm)
define_native_accessor(realm, vm.names.offset, offset_getter, {}, Attribute::Configurable); define_native_accessor(realm, vm.names.offset, offset_getter, {}, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable; u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.with, with, 1, attr);
define_native_function(realm, vm.names.withPlainTime, with_plain_time, 0, attr);
define_native_function(realm, vm.names.withTimeZone, with_time_zone, 1, attr);
define_native_function(realm, vm.names.withCalendar, with_calendar, 1, attr);
define_native_function(realm, vm.names.add, add, 1, attr); define_native_function(realm, vm.names.add, add, 1, attr);
define_native_function(realm, vm.names.subtract, subtract, 1, attr); define_native_function(realm, vm.names.subtract, subtract, 1, attr);
define_native_function(realm, vm.names.until, until, 1, attr); define_native_function(realm, vm.names.until, until, 1, attr);
@ -349,6 +355,165 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::offset_getter)
return PrimitiveString::create(vm, format_utc_offset_nanoseconds(offset_nanoseconds)); return PrimitiveString::create(vm, format_utc_offset_nanoseconds(offset_nanoseconds));
} }
// 6.3.31 Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.with
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::with)
{
auto temporal_zoned_date_time_like = vm.argument(0);
auto options = vm.argument(1);
// 1. Let zonedDateTime be the this value.
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
auto zoned_date_time = TRY(typed_this_object(vm));
// 3. If ? IsPartialTemporalObject(temporalZonedDateTimeLike) is false, throw a TypeError exception.
if (!TRY(is_partial_temporal_object(vm, temporal_zoned_date_time_like)))
return vm.throw_completion<TypeError>(ErrorType::TemporalObjectMustBePartialTemporalObject);
// 4. Let epochNs be zonedDateTime.[[EpochNanoseconds]].
auto const& epoch_nanoseconds = zoned_date_time->epoch_nanoseconds()->big_integer();
// 5. Let timeZone be zonedDateTime.[[TimeZone]].
auto const& time_zone = zoned_date_time->time_zone();
// 6. Let calendar be zonedDateTime.[[Calendar]].
auto const& calendar = zoned_date_time->calendar();
// 7. Let offsetNanoseconds be GetOffsetNanosecondsFor(timeZone, epochNs).
auto offset_nanoseconds = get_offset_nanoseconds_for(time_zone, epoch_nanoseconds);
// 8. Let isoDateTime be GetISODateTimeFor(timeZone, epochNs).
auto iso_date_time = get_iso_date_time_for(time_zone, epoch_nanoseconds);
// 9. Let fields be ISODateToFields(calendar, isoDateTime.[[ISODate]], DATE).
auto fields = iso_date_to_fields(calendar, iso_date_time.iso_date, DateType::Date);
// 10. Set fields.[[Hour]] to isoDateTime.[[Time]].[[Hour]].
fields.hour = iso_date_time.time.hour;
// 11. Set fields.[[Minute]] to isoDateTime.[[Time]].[[Minute]].
fields.minute = iso_date_time.time.minute;
// 12. Set fields.[[Second]] to isoDateTime.[[Time]].[[Second]].
fields.second = iso_date_time.time.second;
// 13. Set fields.[[Millisecond]] to isoDateTime.[[Time]].[[Millisecond]].
fields.millisecond = iso_date_time.time.millisecond;
// 14. Set fields.[[Microsecond]] to isoDateTime.[[Time]].[[Microsecond]].
fields.microsecond = iso_date_time.time.microsecond;
// 15. Set fields.[[Nanosecond]] to isoDateTime.[[Time]].[[Nanosecond]].
fields.nanosecond = iso_date_time.time.nanosecond;
// 16. Set fields.[[OffsetString]] to FormatUTCOffsetNanoseconds(offsetNanoseconds).
fields.offset = format_utc_offset_nanoseconds(offset_nanoseconds);
// 17. Let partialZonedDateTime be ? PrepareCalendarFields(calendar, temporalZonedDateTimeLike, « YEAR, MONTH, MONTH-CODE, DAY », « HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND, OFFSET », PARTIAL).
static constexpr auto calendar_field_names = to_array({ CalendarField::Year, CalendarField::Month, CalendarField::MonthCode, CalendarField::Day });
static constexpr auto non_calendar_field_names = to_array({ CalendarField::Hour, CalendarField::Minute, CalendarField::Second, CalendarField::Millisecond, CalendarField::Microsecond, CalendarField::Nanosecond, CalendarField::Offset });
auto partial_zoned_date_time = TRY(prepare_calendar_fields(vm, calendar, temporal_zoned_date_time_like.as_object(), calendar_field_names, non_calendar_field_names, Partial {}));
// 18. Set fields to CalendarMergeFields(calendar, fields, partialZonedDateTime).
fields = calendar_merge_fields(calendar, fields, partial_zoned_date_time);
// 19. Let resolvedOptions be ? GetOptionsObject(options).
auto resolved_options = TRY(get_options_object(vm, options));
// 20. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).
auto disambiguation = TRY(get_temporal_disambiguation_option(vm, resolved_options));
// 21. Let offset be ? GetTemporalOffsetOption(resolvedOptions, PREFER).
auto offset = TRY(get_temporal_offset_option(vm, resolved_options, OffsetOption::Prefer));
// 22. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
auto overflow = TRY(get_temporal_overflow_option(vm, resolved_options));
// 23. Let dateTimeResult be ? InterpretTemporalDateTimeFields(calendar, fields, overflow).
auto date_time_result = TRY(interpret_temporal_date_time_fields(vm, calendar, fields, overflow));
// 24. Let newOffsetNanoseconds be ! ParseDateTimeUTCOffset(fields.[[OffsetString]]).
auto new_offset_nanoseconds = parse_date_time_utc_offset(*fields.offset);
// 25. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[ISODate]], dateTimeResult.[[Time]], OPTION, newOffsetNanoseconds, timeZone, disambiguation, offset, MATCH-EXACTLY).
auto new_epoch_nanoseconds = TRY(interpret_iso_date_time_offset(vm, date_time_result.iso_date, date_time_result.time, OffsetBehavior::Option, new_offset_nanoseconds, time_zone, disambiguation, offset, MatchBehavior::MatchExactly));
// 26. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
return MUST(create_temporal_zoned_date_time(vm, BigInt::create(vm, move(new_epoch_nanoseconds)), time_zone, calendar));
}
// 6.3.32 Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withplaintime
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::with_plain_time)
{
auto plain_time_like = 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. Let calendar be zonedDateTime.[[Calendar]].
auto const& calendar = zoned_date_time->calendar();
// 5. Let isoDateTime be GetISODateTimeFor(timeZone, zonedDateTime.[[EpochNanoseconds]]).
auto iso_date_time = get_iso_date_time_for(time_zone, zoned_date_time->epoch_nanoseconds()->big_integer());
Crypto::SignedBigInteger epoch_nanoseconds;
// 6. If plainTimeLike is undefined, then
if (plain_time_like.is_undefined()) {
// a. Let epochNs be ? GetStartOfDay(timeZone, isoDateTime.[[ISODate]]).
epoch_nanoseconds = TRY(get_start_of_day(vm, time_zone, iso_date_time.iso_date));
}
// 7. Else,
else {
// a. Let plainTime be ? ToTemporalTime(plainTimeLike).
auto plain_time = TRY(to_temporal_time(vm, plain_time_like));
// b. Let resultISODateTime be CombineISODateAndTimeRecord(isoDateTime.[[ISODate]], plainTime.[[Time]]).
auto result_iso_date_time = combine_iso_date_and_time_record(iso_date_time.iso_date, plain_time->time());
// c. Let epochNs be ? GetEpochNanosecondsFor(timeZone, resultISODateTime, COMPATIBLE).
epoch_nanoseconds = TRY(get_epoch_nanoseconds_for(vm, time_zone, result_iso_date_time, Disambiguation::Compatible));
}
// 8. Return ! CreateTemporalZonedDateTime(epochNs, timeZone, calendar).
return MUST(create_temporal_zoned_date_time(vm, BigInt::create(vm, move(epoch_nanoseconds)), time_zone, calendar));
}
// 6.3.33 Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withtimezone
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::with_time_zone)
{
auto time_zone_like = 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 ? ToTemporalTimeZoneIdentifier(timeZoneLike).
auto time_zone = TRY(to_temporal_time_zone_identifier(vm, time_zone_like));
// 4. Return ! CreateTemporalZonedDateTime(zonedDateTime.[[EpochNanoseconds]], timeZone, zonedDateTime.[[Calendar]]).
return MUST(create_temporal_zoned_date_time(vm, zoned_date_time->epoch_nanoseconds(), move(time_zone), zoned_date_time->calendar()));
}
// 6.3.34 Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withcalendar
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::with_calendar)
{
auto calendar_like = 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 calendar be ? ToTemporalCalendarIdentifier(calendarLike).
auto calendar = TRY(to_temporal_calendar_identifier(vm, calendar_like));
// 4. Return ! CreateTemporalZonedDateTime(zonedDateTime.[[EpochNanoseconds]], zonedDateTime.[[TimeZone]], calendar).
return MUST(create_temporal_zoned_date_time(vm, zoned_date_time->epoch_nanoseconds(), zoned_date_time->time_zone(), move(calendar)));
}
// 6.3.35 Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.add // 6.3.35 Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.add
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::add) JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::add)
{ {

View file

@ -51,6 +51,10 @@ private:
JS_DECLARE_NATIVE_FUNCTION(in_leap_year_getter); JS_DECLARE_NATIVE_FUNCTION(in_leap_year_getter);
JS_DECLARE_NATIVE_FUNCTION(offset_nanoseconds_getter); JS_DECLARE_NATIVE_FUNCTION(offset_nanoseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(offset_getter); JS_DECLARE_NATIVE_FUNCTION(offset_getter);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(with_plain_time);
JS_DECLARE_NATIVE_FUNCTION(with_time_zone);
JS_DECLARE_NATIVE_FUNCTION(with_calendar);
JS_DECLARE_NATIVE_FUNCTION(add); JS_DECLARE_NATIVE_FUNCTION(add);
JS_DECLARE_NATIVE_FUNCTION(subtract); JS_DECLARE_NATIVE_FUNCTION(subtract);
JS_DECLARE_NATIVE_FUNCTION(until); JS_DECLARE_NATIVE_FUNCTION(until);

View file

@ -0,0 +1,81 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.ZonedDateTime.prototype.with).toHaveLength(1);
});
test("basic functionality", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC");
const values = [
[{ year: 2021 }, 1609459200000000000n],
[{ year: 2021, month: 7 }, 1625097600000000000n],
[{ year: 2021, month: 7, day: 6 }, 1625529600000000000n],
[{ year: 2021, monthCode: "M07", day: 6 }, 1625529600000000000n],
[{ hour: 18, minute: 14, second: 47 }, 65687000000000n],
[
{
year: 2021,
month: 7,
day: 6,
hour: 18,
minute: 14,
second: 47,
millisecond: 123,
microsecond: 456,
nanosecond: 789,
},
1625595287123456789n,
],
];
for (const [arg, epochNanoseconds] of values) {
const expected = new Temporal.ZonedDateTime(epochNanoseconds, "UTC");
expect(zonedDateTime.with(arg).equals(expected)).toBeTrue();
}
// Supplying the same values doesn't change the date/time, but still creates a new object
const zonedDateTimeLike = {
year: zonedDateTime.year,
month: zonedDateTime.month,
day: zonedDateTime.day,
hour: zonedDateTime.hour,
minute: zonedDateTime.minute,
second: zonedDateTime.second,
millisecond: zonedDateTime.millisecond,
microsecond: zonedDateTime.microsecond,
nanosecond: zonedDateTime.nanosecond,
};
expect(zonedDateTime.with(zonedDateTimeLike)).not.toBe(zonedDateTime);
expect(zonedDateTime.with(zonedDateTimeLike).equals(zonedDateTime)).toBeTrue();
});
});
describe("errors", () => {
test("this value must be a Temporal.ZonedDateTime object", () => {
expect(() => {
Temporal.ZonedDateTime.prototype.with.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
});
test("argument must be an object", () => {
expect(() => {
new Temporal.ZonedDateTime(0n, "UTC").with("foo");
}).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
expect(() => {
new Temporal.ZonedDateTime(0n, "UTC").with(42);
}).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
});
test("argument must have one of 'day', 'hour', 'microsecond', 'millisecond', 'minute', 'month', 'monthCode', 'nanosecond', 'second', 'year'", () => {
expect(() => {
new Temporal.ZonedDateTime(0n, "UTC").with({});
}).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
});
test("argument must not have 'calendar' or 'timeZone'", () => {
expect(() => {
new Temporal.ZonedDateTime(0n, "UTC").with({ calendar: {} });
}).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
expect(() => {
new Temporal.ZonedDateTime(0n, "UTC").with({ timeZone: {} });
}).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
});
});

View file

@ -0,0 +1,30 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.ZonedDateTime.prototype.withCalendar).toHaveLength(1);
});
test("basic functionality", () => {
const object = {};
const zonedDateTime = new Temporal.ZonedDateTime(1n, "UTC", "gregory");
expect(zonedDateTime.calendarId).toBe("gregory");
const withCalendarZonedDateTime = zonedDateTime.withCalendar("iso8601");
expect(withCalendarZonedDateTime.calendarId).toBe("iso8601");
});
});
describe("errors", () => {
test("this value must be a Temporal.ZonedDateTime object", () => {
expect(() => {
Temporal.ZonedDateTime.prototype.withCalendar.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
});
test("from invalid calendar identifier", () => {
const zonedDateTime = new Temporal.ZonedDateTime(1n, "UTC", "iso8601");
expect(() => {
zonedDateTime.withCalendar("iso8602foobar");
}).toThrowWithMessage(RangeError, "Invalid calendar identifier 'iso8602foobar'");
});
});

View file

@ -0,0 +1,118 @@
describe("correct behavior", () => {
test("length is 0", () => {
expect(Temporal.ZonedDateTime.prototype.withPlainTime).toHaveLength(0);
});
function checkCommonExpectedResults(withPlainTimeZonedDateTime) {
expect(withPlainTimeZonedDateTime.epochNanoseconds).toBe(1636064604200300400n);
expect(withPlainTimeZonedDateTime.epochMilliseconds).toBe(1636064604200);
expect(withPlainTimeZonedDateTime.year).toBe(2021);
expect(withPlainTimeZonedDateTime.month).toBe(11);
expect(withPlainTimeZonedDateTime.monthCode).toBe("M11");
expect(withPlainTimeZonedDateTime.day).toBe(4);
expect(withPlainTimeZonedDateTime.hour).toBe(22);
expect(withPlainTimeZonedDateTime.minute).toBe(23);
expect(withPlainTimeZonedDateTime.second).toBe(24);
expect(withPlainTimeZonedDateTime.millisecond).toBe(200);
expect(withPlainTimeZonedDateTime.microsecond).toBe(300);
expect(withPlainTimeZonedDateTime.nanosecond).toBe(400);
expect(withPlainTimeZonedDateTime.dayOfWeek).toBe(4);
expect(withPlainTimeZonedDateTime.dayOfYear).toBe(308);
expect(withPlainTimeZonedDateTime.weekOfYear).toBe(44);
expect(withPlainTimeZonedDateTime.hoursInDay).toBe(24);
expect(withPlainTimeZonedDateTime.daysInWeek).toBe(7);
expect(withPlainTimeZonedDateTime.daysInYear).toBe(365);
expect(withPlainTimeZonedDateTime.monthsInYear).toBe(12);
expect(withPlainTimeZonedDateTime.inLeapYear).toBeFalse();
expect(withPlainTimeZonedDateTime.offset).toBe("+00:00");
expect(withPlainTimeZonedDateTime.offsetNanoseconds).toBe(0);
}
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 11, 4, 21, 16, 56, 100, 200, 300);
const zonedDateTime = plainDateTime.toZonedDateTime("UTC");
const plainTime = new Temporal.PlainTime(22, 23, 24, 200, 300, 400);
const withPlainTimeZonedDateTime = zonedDateTime.withPlainTime(plainTime);
checkCommonExpectedResults(withPlainTimeZonedDateTime);
});
test("plain time-like object", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 11, 4, 21, 16, 56, 100, 200, 300);
const zonedDateTime = plainDateTime.toZonedDateTime("UTC");
const plainTimeLike = {
hour: 22,
minute: 23,
second: 24,
millisecond: 200,
microsecond: 300,
nanosecond: 400,
};
const withPlainTimeZonedDateTime = zonedDateTime.withPlainTime(plainTimeLike);
checkCommonExpectedResults(withPlainTimeZonedDateTime);
});
// FIXME: Enable this test when we have ZonedDateTime.prototype.startOfDay
// test("passing no parameters is the equivalent of using startOfDay", () => {
// const plainDateTime = new Temporal.PlainDateTime(2021, 11, 4, 21, 16, 56, 100, 200, 300);
// const zonedDateTime = plainDateTime.toZonedDateTime("UTC");
// const startOfDayZonedDateTime = zonedDateTime.startOfDay();
// const withPlainTimeZonedDateTime = zonedDateTime.withPlainTime();
// expect(startOfDayZonedDateTime.epochNanoseconds).toBe(
// withPlainTimeZonedDateTime.epochNanoseconds
// );
// expect(startOfDayZonedDateTime.epochMilliseconds).toBe(
// withPlainTimeZonedDateTime.epochMilliseconds
// );
// expect(startOfDayZonedDateTime.year).toBe(withPlainTimeZonedDateTime.year);
// expect(startOfDayZonedDateTime.month).toBe(withPlainTimeZonedDateTime.month);
// expect(startOfDayZonedDateTime.monthCode).toBe(withPlainTimeZonedDateTime.monthCode);
// expect(startOfDayZonedDateTime.day).toBe(withPlainTimeZonedDateTime.day);
// expect(startOfDayZonedDateTime.hour).toBe(withPlainTimeZonedDateTime.hour);
// expect(startOfDayZonedDateTime.minute).toBe(withPlainTimeZonedDateTime.minute);
// expect(startOfDayZonedDateTime.second).toBe(withPlainTimeZonedDateTime.second);
// expect(startOfDayZonedDateTime.millisecond).toBe(withPlainTimeZonedDateTime.millisecond);
// expect(startOfDayZonedDateTime.microsecond).toBe(withPlainTimeZonedDateTime.microsecond);
// expect(startOfDayZonedDateTime.nanosecond).toBe(withPlainTimeZonedDateTime.nanosecond);
// expect(startOfDayZonedDateTime.dayOfWeek).toBe(withPlainTimeZonedDateTime.dayOfWeek);
// expect(startOfDayZonedDateTime.dayOfYear).toBe(withPlainTimeZonedDateTime.dayOfYear);
// expect(startOfDayZonedDateTime.weekOfYear).toBe(withPlainTimeZonedDateTime.weekOfYear);
// expect(startOfDayZonedDateTime.hoursInDay).toBe(withPlainTimeZonedDateTime.hoursInDay);
// expect(startOfDayZonedDateTime.daysInWeek).toBe(withPlainTimeZonedDateTime.daysInWeek);
// expect(startOfDayZonedDateTime.daysInYear).toBe(withPlainTimeZonedDateTime.daysInYear);
// expect(startOfDayZonedDateTime.monthsInYear).toBe(withPlainTimeZonedDateTime.monthsInYear);
// expect(startOfDayZonedDateTime.inLeapYear).toBe(withPlainTimeZonedDateTime.inLeapYear);
// expect(startOfDayZonedDateTime.offset).toBe(withPlainTimeZonedDateTime.offset);
// expect(startOfDayZonedDateTime.offsetNanoseconds).toBe(
// withPlainTimeZonedDateTime.offsetNanoseconds
// );
// });
test("from plain time string", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 11, 4, 21, 16, 56, 100, 200, 300);
const zonedDateTime = plainDateTime.toZonedDateTime("UTC");
const withPlainTimeZonedDateTime = zonedDateTime.withPlainTime("22:23:24.200300400");
checkCommonExpectedResults(withPlainTimeZonedDateTime);
});
});
describe("errors", () => {
test("this value must be a Temporal.ZonedDateTime object", () => {
expect(() => {
Temporal.ZonedDateTime.prototype.withPlainTime.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
});
test("invalid plain time-like object", () => {
expect(() => {
new Temporal.ZonedDateTime(1n, "UTC").withPlainTime({});
}).toThrowWithMessage(TypeError, "Invalid time");
expect(() => {
new Temporal.ZonedDateTime(1n, "UTC").withPlainTime({ foo: 1, bar: 2 });
}).toThrowWithMessage(TypeError, "Invalid time");
});
});

View file

@ -0,0 +1,29 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.ZonedDateTime.prototype.withTimeZone).toHaveLength(1);
});
test("basic functionality", () => {
const zonedDateTime = new Temporal.ZonedDateTime(1n, "America/New_York");
expect(zonedDateTime.timeZoneId).toBe("America/New_York");
const withTimeZoneZonedDateTime = zonedDateTime.withTimeZone("UTC");
expect(withTimeZoneZonedDateTime.timeZoneId).toBe("UTC");
});
});
describe("errors", () => {
test("this value must be a Temporal.ZonedDateTime object", () => {
expect(() => {
Temporal.ZonedDateTime.prototype.withTimeZone.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
});
test("from invalid time zone string", () => {
const zonedDateTime = new Temporal.ZonedDateTime(1n, "UTC");
expect(() => {
zonedDateTime.withTimeZone("UTCfoobar");
}).toThrowWithMessage(RangeError, "Invalid time zone name 'UTCfoobar'");
});
});