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

LibJS: Implement Temporal.PlainDateTime.from()

This commit is contained in:
Linus Groh 2021-08-22 23:16:42 +01:00
parent 573587b83a
commit ad427f85ca
9 changed files with 507 additions and 2 deletions

View file

@ -560,6 +560,26 @@ Optional<TemporalDate> parse_temporal_date_string(GlobalObject& global_object, S
return TemporalDate { .year = result->year, .month = result->month, .day = result->day, .calendar = move(result->calendar) };
}
// 13.39 ParseTemporalDateTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldatetimestring
Optional<ISODateTime> parse_temporal_date_time_string(GlobalObject& global_object, String const& iso_string)
{
auto& vm = global_object.vm();
// 1. Assert: Type(isoString) is String.
// 2. If isoString does not satisfy the syntax of a TemporalDateTimeString (see 13.33), then
// a. Throw a RangeError exception.
// TODO
// 3. Let result be ? ParseISODateTime(isoString).
auto result = parse_iso_date_time(global_object, iso_string);
if (vm.exception())
return {};
// 4. Return result.
return result;
}
// 13.40 ParseTemporalDurationString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldurationstring
Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject& global_object, String const& iso_string)
{

View file

@ -73,6 +73,7 @@ Optional<ISODateTime> parse_iso_date_time(GlobalObject&, String const& iso_strin
Optional<TemporalInstant> parse_temporal_instant_string(GlobalObject&, String const& iso_string);
Optional<String> parse_temporal_calendar_string(GlobalObject&, String const& iso_string);
Optional<TemporalDate> parse_temporal_date_string(GlobalObject&, String const& iso_string);
Optional<ISODateTime> parse_temporal_date_time_string(GlobalObject&, String const& iso_string);
Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject&, String const& iso_string);
Optional<TemporalTimeZone> parse_temporal_time_zone_string(GlobalObject&, String const& iso_string);
double to_positive_integer_or_infinity(GlobalObject&, Value argument);

View file

@ -7,10 +7,15 @@
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.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/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainDateTimeConstructor.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
@ -93,6 +98,143 @@ bool iso_date_time_within_limits(GlobalObject& global_object, i32 year, u8 month
return true;
}
// 5.5.3 InterpretTemporalDateTimeFields ( calendar, fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-interprettemporaldatetimefields
Optional<ISODateTime> interpret_temporal_date_time_fields(GlobalObject& global_object, Object& calendar, Object& fields, Object& options)
{
auto& vm = global_object.vm();
Optional<TemporalTime> time_result;
// 1. Let timeResult be ? ToTemporalTimeRecord(fields).
time_result = to_temporal_time_record(global_object, fields);
if (vm.exception())
return {};
// 2. Let temporalDate be ? DateFromFields(calendar, fields, options).
auto* temporal_date = date_from_fields(global_object, calendar, fields, options);
if (vm.exception())
return {};
// 3. Let overflow be ? ToTemporalOverflow(options).
auto overflow = to_temporal_overflow(global_object, options);
if (vm.exception())
return {};
// 4. Let timeResult be ? RegulateTime(timeResult.[[Hour]], timeResult.[[Minute]], timeResult.[[Second]], timeResult.[[Millisecond]], timeResult.[[Microsecond]], timeResult.[[Nanosecond]], overflow).
time_result = regulate_time(global_object, time_result->hour, time_result->minute, time_result->second, time_result->millisecond, time_result->microsecond, time_result->nanosecond, *overflow);
if (vm.exception())
return {};
// 5. Return the Record { [[Year]]: temporalDate.[[ISOYear]], [[Month]]: temporalDate.[[ISOMonth]], [[Day]]: temporalDate.[[ISODay]], [[Hour]]: timeResult.[[Hour]], [[Minute]]: timeResult.[[Minute]], [[Second]]: timeResult.[[Second]], [[Millisecond]]: timeResult.[[Millisecond]], [[Microsecond]]: timeResult.[[Microsecond]], [[Nanosecond]]: timeResult.[[Nanosecond]] }.
return ISODateTime {
.year = temporal_date->iso_year(),
.month = temporal_date->iso_month(),
.day = temporal_date->iso_day(),
.hour = static_cast<u8>(time_result->hour),
.minute = static_cast<u8>(time_result->minute),
.second = static_cast<u8>(time_result->second),
.millisecond = static_cast<u16>(time_result->millisecond),
.microsecond = static_cast<u16>(time_result->microsecond),
.nanosecond = static_cast<u16>(time_result->nanosecond),
};
}
// 5.5.4 ToTemporalDateTime ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldatetime
PlainDateTime* to_temporal_date_time(GlobalObject& global_object, Value item, Object* options)
{
auto& vm = global_object.vm();
// 1. If options is not present, set options to ! OrdinaryObjectCreate(null).
if (!options)
options = Object::create(global_object, nullptr);
Object* calendar;
ISODateTime result;
// 2. If Type(item) is Object, then
if (item.is_object()) {
auto& item_object = item.as_object();
// a. If item has an [[InitializedTemporalDateTime]] internal slot, then
if (is<PlainDateTime>(item_object)) {
// i. Return item.
return &static_cast<PlainDateTime&>(item_object);
}
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (is<ZonedDateTime>(item_object)) {
auto& zoned_date_time = static_cast<ZonedDateTime&>(item_object);
// i. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
auto* instant = create_temporal_instant(global_object, zoned_date_time.nanoseconds());
// ii. Return ? BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], instant, item.[[Calendar]]).
return builtin_time_zone_get_plain_date_time_for(global_object, &zoned_date_time.time_zone(), *instant, zoned_date_time.calendar());
}
// c. If item has an [[InitializedTemporalDate]] internal slot, then
if (is<PlainDate>(item_object)) {
auto& plain_date = static_cast<PlainDate&>(item_object);
// i. Return ? CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], 0, 0, 0, 0, 0, 0, item.[[Calendar]]).
return create_temporal_date_time(global_object, plain_date.iso_year(), plain_date.iso_month(), plain_date.iso_day(), 0, 0, 0, 0, 0, 0, plain_date.calendar());
}
// d. Let calendar be ? GetTemporalCalendarWithISODefault(item).
calendar = get_temporal_calendar_with_iso_default(global_object, item_object);
if (vm.exception())
return {};
// e. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", "second", "year" »).
auto field_names = calendar_fields(global_object, *calendar, { "day"sv, "hour"sv, "microsecond"sv, "millisecond"sv, "minute"sv, "month"sv, "monthCode"sv, "nanosecond"sv, "second"sv, "year"sv });
if (vm.exception())
return {};
// f. Let fields be ? PrepareTemporalFields(item, fieldNames, «»).
auto* fields = prepare_temporal_fields(global_object, item_object, field_names, {});
if (vm.exception())
return {};
// g. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, options).
auto maybe_result = interpret_temporal_date_time_fields(global_object, *calendar, *fields, *options);
if (vm.exception())
return {};
result = move(*maybe_result);
}
// 3. Else,
else {
// a. Perform ? ToTemporalOverflow(options).
(void)to_temporal_overflow(global_object, *options);
if (vm.exception())
return {};
// b. Let string be ? ToString(item).
auto string = item.to_string(global_object);
if (vm.exception())
return {};
// c. Let result be ? ParseTemporalDateTimeString(string).
auto maybe_result = parse_temporal_date_time_string(global_object, string);
if (vm.exception())
return {};
result = move(*maybe_result);
// d. Assert: ! IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
VERIFY(is_valid_iso_date(result.year, result.month, result.day));
// e. Assert: ! IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
VERIFY(is_valid_time(result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
// f. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
calendar = to_temporal_calendar_with_iso_default(global_object, result.calendar.has_value() ? js_string(vm, *result.calendar) : js_undefined());
if (vm.exception())
return {};
}
// 4. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], calendar).
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);
}
// 5.5.5 BalanceISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisodatetime
ISODateTime balance_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, i64 nanosecond)
{

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/Optional.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
@ -49,6 +50,8 @@ private:
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);
Optional<ISODateTime> interpret_temporal_date_time_fields(GlobalObject&, Object& calendar, Object& fields, Object& options);
PlainDateTime* to_temporal_date_time(GlobalObject&, Value item, Object* options = nullptr);
ISODateTime balance_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, i64 nanosecond);
PlainDateTime* create_temporal_date_time(GlobalObject&, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, FunctionObject* new_target = nullptr);

View file

@ -5,6 +5,7 @@
*/
#include <AK/Checked.h>
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
@ -28,6 +29,9 @@ void PlainDateTimeConstructor::initialize(GlobalObject& global_object)
// 5.2.1 Temporal.PlainDateTime.prototype, https://tc39.es/proposal-temporal/#sec-temporal-plaindatetime-prototype
define_direct_property(vm.names.prototype, global_object.temporal_plain_date_time_prototype(), 0);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.from, from, 1, attr);
define_direct_property(vm.names.length, Value(3), Attribute::Configurable);
}
@ -110,4 +114,31 @@ Value PlainDateTimeConstructor::construct(FunctionObject& new_target)
return create_temporal_date_time(global_object, iso_year, iso_month, iso_day, hour, minute, second, millisecond, microsecond, nanosecond, *calendar, &new_target);
}
// 5.2.2 Temporal.PlainDateTime.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.from
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimeConstructor::from)
{
auto item = vm.argument(0);
// 1. Set options to ? GetOptionsObject(options).
auto* options = get_options_object(global_object, vm.argument(1));
if (vm.exception())
return {};
// 2. If Type(item) is Object and item has an [[InitializedTemporalDateTime]] internal slot, then
if (item.is_object() && is<PlainDateTime>(item.as_object())) {
auto& plain_date_time = static_cast<PlainDateTime&>(item.as_object());
// a. Perform ? ToTemporalOverflow(options).
(void)to_temporal_overflow(global_object, *options);
if (vm.exception())
return {};
// b. Return ? CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[ISOHour]], item.[[ISOMinute]], item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]], item.[[ISONanosecond]], item.[[Calendar]]).
return create_temporal_date_time(global_object, plain_date_time.iso_year(), plain_date_time.iso_month(), plain_date_time.iso_day(), plain_date_time.iso_hour(), plain_date_time.iso_minute(), plain_date_time.iso_second(), plain_date_time.iso_millisecond(), plain_date_time.iso_microsecond(), plain_date_time.iso_nanosecond(), plain_date_time.calendar());
}
// 3. Return ? ToTemporalDateTime(item, options).
return to_temporal_date_time(global_object, item, options);
}
}

View file

@ -23,6 +23,8 @@ public:
private:
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
};
}

View file

@ -8,6 +8,7 @@
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
@ -33,8 +34,41 @@ void PlainTime::visit_edges(Visitor& visitor)
visitor.visit(&m_calendar);
}
// 4.5.4 RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulatetime
Optional<TemporalTime> regulate_time(GlobalObject& global_object, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, StringView overflow)
{
auto& vm = global_object.vm();
// 1. Assert: hour, minute, second, millisecond, microsecond and nanosecond are integers.
// NOTE: As the spec is currently written this assertion can fail, these are either integers _or_ infinity.
// See https://github.com/tc39/proposal-temporal/issues/1672.
// 2. Assert: overflow is either "constrain" or "reject".
// NOTE: Asserted by the VERIFY_NOT_REACHED at the end
// 3. If overflow is "constrain", then
if (overflow == "constrain"sv) {
// a. Return ! ConstrainTime(hour, minute, second, millisecond, microsecond, nanosecond).
return constrain_time(hour, minute, second, millisecond, microsecond, nanosecond);
}
// 4. If overflow is "reject", then
if (overflow == "reject"sv) {
// a. If ! IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.
if (!is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond)) {
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainTime);
return {};
}
// b. Return the Record { [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }.
return TemporalTime { .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = microsecond, .nanosecond = nanosecond };
}
VERIFY_NOT_REACHED();
}
// 4.5.5 IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidtime
bool is_valid_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond)
bool is_valid_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
{
// 1. Assert: hour, minute, second, millisecond, microsecond, and nanosecond are integers.
@ -131,6 +165,33 @@ DaysAndTime balance_time(i64 hour, i64 minute, i64 second, i64 millisecond, i64
};
}
// 4.5.7 ConstrainTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-constraintime
TemporalTime constrain_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
{
// 1. Assert: hour, minute, second, millisecond, microsecond, and nanosecond are integers.
// 2. Set hour to ! ConstrainToRange(hour, 0, 23).
hour = constrain_to_range(hour, 0, 23);
// 3. Set minute to ! ConstrainToRange(minute, 0, 59).
minute = constrain_to_range(minute, 0, 59);
// 4. Set second to ! ConstrainToRange(second, 0, 59).
second = constrain_to_range(second, 0, 59);
// 5. Set millisecond to ! ConstrainToRange(millisecond, 0, 999).
millisecond = constrain_to_range(millisecond, 0, 999);
// 6. Set microsecond to ! ConstrainToRange(microsecond, 0, 999).
microsecond = constrain_to_range(microsecond, 0, 999);
// 7. Set nanosecond to ! ConstrainToRange(nanosecond, 0, 999).
nanosecond = constrain_to_range(nanosecond, 0, 999);
// 8. Return the Record { [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }.
return TemporalTime { .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = microsecond, .nanosecond = nanosecond };
}
// 4.5.8 CreateTemporalTime ( hour, minute, second, millisecond, microsecond, nanosecond [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaltime
PlainTime* create_temporal_time(GlobalObject& global_object, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, FunctionObject* new_target)
{
@ -164,4 +225,43 @@ PlainTime* create_temporal_time(GlobalObject& global_object, u8 hour, u8 minute,
return object;
}
// 4.5.9 ToTemporalTimeRecord ( temporalTimeLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimerecord
Optional<TemporalTime> to_temporal_time_record(GlobalObject& global_object, Object& temporal_time_like)
{
auto& vm = global_object.vm();
// 1. Assert: Type(temporalTimeLike) is Object.
// 2. Let result be the Record { [[Hour]]: undefined, [[Minute]]: undefined, [[Second]]: undefined, [[Millisecond]]: undefined, [[Microsecond]]: undefined, [[Nanosecond]]: undefined }.
auto result = TemporalTime {};
// 3. For each row of Table 3, except the header row, in table order, do
for (auto& [internal_slot, property] : temporal_time_like_properties<TemporalTime, double>(vm)) {
// a. Let property be the Property value of the current row.
// b. Let value be ? Get(temporalTimeLike, property).
auto value = temporal_time_like.get(property);
if (vm.exception())
return {};
// c. If value is undefined, then
if (value.is_undefined()) {
// i. Throw a TypeError exception.
vm.throw_exception<TypeError>(global_object, ErrorType::TemporalMissingRequiredProperty, property);
return {};
}
// d. Set value to ? ToIntegerOrInfinity(value).
auto value_number = value.to_integer_or_infinity(global_object);
if (vm.exception())
return {};
// e. Set result's internal slot whose name is the Internal Slot value of the current row to value.
result.*internal_slot = value_number;
}
// 4. Return result.
return result;
}
}

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/Optional.h>
#include <LibJS/Runtime/Object.h>
namespace JS::Temporal {
@ -50,8 +51,41 @@ struct DaysAndTime {
u16 nanosecond;
};
bool is_valid_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond);
struct TemporalTime {
double hour;
double minute;
double second;
double millisecond;
double microsecond;
double nanosecond;
};
// Table 3: Properties of a TemporalTimeLike, https://tc39.es/proposal-temporal/#table-temporal-temporaltimelike-properties
template<typename StructT, typename ValueT>
struct TemporalTimeLikeProperty {
ValueT StructT::*internal_slot { nullptr };
PropertyName property;
};
template<typename StructT, typename ValueT>
auto temporal_time_like_properties = [](VM& vm) {
using PropertyT = TemporalTimeLikeProperty<StructT, ValueT>;
return AK::Array {
PropertyT { &StructT::hour, vm.names.hour },
PropertyT { &StructT::microsecond, vm.names.microsecond },
PropertyT { &StructT::millisecond, vm.names.millisecond },
PropertyT { &StructT::minute, vm.names.minute },
PropertyT { &StructT::nanosecond, vm.names.nanosecond },
PropertyT { &StructT::second, vm.names.second },
};
};
Optional<TemporalTime> regulate_time(GlobalObject&, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, StringView overflow);
bool is_valid_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
DaysAndTime balance_time(i64 hour, i64 minute, i64 second, i64 millisecond, i64 microsecond, i64 nanosecond);
TemporalTime constrain_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
PlainTime* create_temporal_time(GlobalObject&, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, FunctionObject* new_target = nullptr);
Optional<TemporalTime> to_temporal_time_record(GlobalObject&, Object& temporal_time_like);
}

View file

@ -0,0 +1,172 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.PlainDateTime.from).toHaveLength(1);
});
test("PlainDate instance argument", () => {
const plainDate = new Temporal.PlainDate(2021, 7, 6);
const plainDateTime = Temporal.PlainDateTime.from(plainDate);
expect(plainDateTime.year).toBe(2021);
expect(plainDateTime.month).toBe(7);
expect(plainDateTime.day).toBe(6);
expect(plainDateTime.hour).toBe(0);
expect(plainDateTime.minute).toBe(0);
expect(plainDateTime.second).toBe(0);
expect(plainDateTime.millisecond).toBe(0);
expect(plainDateTime.microsecond).toBe(0);
expect(plainDateTime.nanosecond).toBe(0);
});
test("PlainDateTime instance argument", () => {
const plainDateTime_ = new Temporal.PlainDateTime(2021, 7, 6, 18, 14, 47);
const plainDateTime = Temporal.PlainDateTime.from(plainDateTime_);
expect(plainDateTime).not.toBe(plainDateTime_);
expect(plainDateTime.year).toBe(2021);
expect(plainDateTime.month).toBe(7);
expect(plainDateTime.day).toBe(6);
expect(plainDateTime.hour).toBe(18);
expect(plainDateTime.minute).toBe(14);
expect(plainDateTime.second).toBe(47);
expect(plainDateTime.millisecond).toBe(0);
expect(plainDateTime.microsecond).toBe(0);
expect(plainDateTime.nanosecond).toBe(0);
});
test("ZonedDateTime instance argument", () => {
const timeZone = new Temporal.TimeZone("UTC");
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
const plainDateTime = Temporal.PlainDateTime.from(zonedDateTime);
expect(plainDateTime.year).toBe(2021);
expect(plainDateTime.month).toBe(7);
expect(plainDateTime.day).toBe(6);
expect(plainDateTime.hour).toBe(23);
expect(plainDateTime.minute).toBe(42);
expect(plainDateTime.second).toBe(1);
expect(plainDateTime.millisecond).toBe(0);
expect(plainDateTime.microsecond).toBe(0);
expect(plainDateTime.nanosecond).toBe(0);
});
test("fields object argument", () => {
const object = {
year: 2021,
month: 7,
day: 6,
hour: 23,
minute: 42,
second: 1,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const plainDateTime = Temporal.PlainDateTime.from(object);
expect(plainDateTime.year).toBe(2021);
expect(plainDateTime.month).toBe(7);
expect(plainDateTime.day).toBe(6);
expect(plainDateTime.hour).toBe(23);
expect(plainDateTime.minute).toBe(42);
expect(plainDateTime.second).toBe(1);
expect(plainDateTime.millisecond).toBe(0);
expect(plainDateTime.microsecond).toBe(0);
expect(plainDateTime.nanosecond).toBe(0);
});
test("with 'constrain' overflow option", () => {
const object = {
year: 0,
month: 1,
day: 1,
hour: 24,
minute: 60,
second: 60,
millisecond: 1000,
microsecond: 1000,
nanosecond: 1000,
};
const plainDateTime = Temporal.PlainDateTime.from(object, { overflow: "constrain" });
expect(plainDateTime.year).toBe(0);
expect(plainDateTime.month).toBe(1);
expect(plainDateTime.day).toBe(1);
expect(plainDateTime.hour).toBe(23);
expect(plainDateTime.minute).toBe(59);
expect(plainDateTime.second).toBe(59);
expect(plainDateTime.millisecond).toBe(999);
expect(plainDateTime.microsecond).toBe(999);
expect(plainDateTime.nanosecond).toBe(999);
});
// Un-skip once ParseISODateTime & ParseTemporalDateString are implemented
test.skip("PlainDateTime string argument", () => {
const plainDateTime = Temporal.PlainDateTime.from("2021-07-06T23:42:01Z");
expect(plainDateTime.year).toBe(2021);
expect(plainDateTime.month).toBe(7);
expect(plainDateTime.day).toBe(6);
expect(plainDateTime.hour).toBe(23);
expect(plainDateTime.minute).toBe(42);
expect(plainDateTime.second).toBe(1);
expect(plainDateTime.millisecond).toBe(0);
expect(plainDateTime.microsecond).toBe(0);
expect(plainDateTime.nanosecond).toBe(0);
});
});
describe("errors", () => {
test("missing fields", () => {
expect(() => {
Temporal.PlainDateTime.from({});
}).toThrowWithMessage(TypeError, "Required property year is missing or undefined");
expect(() => {
Temporal.PlainDateTime.from({ year: 0 });
}).toThrowWithMessage(TypeError, "Required property month is missing or undefined");
expect(() => {
Temporal.PlainDateTime.from({ year: 0, month: 1 });
}).toThrowWithMessage(TypeError, "Required property day is missing or undefined");
});
test("with 'reject' overflow option", () => {
const values = [
[{ year: 1234567, month: 1, day: 1 }, "Invalid plain date"],
[{ year: 0, month: 13, day: 1 }, "Invalid plain date"],
[{ year: 0, month: 1, day: 32 }, "Invalid plain date"],
[{ year: 0, month: 1, day: 1, hour: 24 }, "Invalid plain time"],
[{ year: 0, month: 1, day: 1, hour: 0, minute: 60 }, "Invalid plain time"],
[{ year: 0, month: 1, day: 1, hour: 0, minute: 0, second: 60 }, "Invalid plain time"],
[
{ year: 0, month: 1, day: 1, hour: 0, minute: 0, second: 0, millisecond: 1000 },
"Invalid plain time",
],
[
{
year: 0,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 1000,
},
"Invalid plain time",
],
[
{
year: 0,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 1000,
},
"Invalid plain time",
],
];
for (const [object, error] of values) {
expect(() => {
Temporal.PlainDateTime.from(object, { overflow: "reject" });
}).toThrowWithMessage(RangeError, error);
}
});
});