summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibJS/CMakeLists.txt2
-rw-r--r--Userland/Libraries/LibJS/Forward.h9
-rw-r--r--Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorTypes.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/GlobalObject.cpp2
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp21
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp94
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h4
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp58
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h24
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp96
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.h28
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp23
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.h22
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp28
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp2
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.js53
19 files changed, 465 insertions, 5 deletions
diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt
index 3a75eb8f3e..53c3ecd4b4 100644
--- a/Userland/Libraries/LibJS/CMakeLists.txt
+++ b/Userland/Libraries/LibJS/CMakeLists.txt
@@ -135,6 +135,8 @@ set(SOURCES
Runtime/Temporal/InstantPrototype.cpp
Runtime/Temporal/Now.cpp
Runtime/Temporal/PlainDate.cpp
+ Runtime/Temporal/PlainDateConstructor.cpp
+ Runtime/Temporal/PlainDatePrototype.cpp
Runtime/Temporal/PlainDateTime.cpp
Runtime/Temporal/PlainTime.cpp
Runtime/Temporal/Temporal.cpp
diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h
index 977c8960ed..62c59cab96 100644
--- a/Userland/Libraries/LibJS/Forward.h
+++ b/Userland/Libraries/LibJS/Forward.h
@@ -76,10 +76,11 @@
__JS_ENUMERATE(Float32Array, float32_array, Float32ArrayPrototype, Float32ArrayConstructor, float) \
__JS_ENUMERATE(Float64Array, float64_array, Float64ArrayPrototype, Float64ArrayConstructor, double)
-#define JS_ENUMERATE_TEMPORAL_OBJECTS \
- __JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \
- __JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
- __JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
+#define JS_ENUMERATE_TEMPORAL_OBJECTS \
+ __JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \
+ __JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
+ __JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
+ __JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \
__JS_ENUMERATE(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor)
#define JS_ENUMERATE_ITERATOR_PROTOTYPES \
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
index 23de5d001d..f8976b6b06 100644
--- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
+++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
@@ -78,6 +78,7 @@ namespace JS {
P(buffer) \
P(byteLength) \
P(byteOffset) \
+ P(calendar) \
P(call) \
P(callee) \
P(caller) \
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
index 9f21032df3..08d9e6e69a 100644
--- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
+++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
@@ -172,6 +172,7 @@
M(TemporalInvalidDurationPropertyValue, "Invalid value for duration property '{}': must be an integer, got {}") \
M(TemporalInvalidEpochNanoseconds, "Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17") \
M(TemporalInvalidISODate, "Invalid ISO date") \
+ M(TemporalInvalidPlainDate, "Invalid plain date") \
M(TemporalInvalidTime, "Invalid time") \
M(TemporalInvalidTimeZoneName, "Invalid time zone name") \
M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
index 3a1fad31d9..1e3c12402a 100644
--- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
+++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
@@ -75,6 +75,8 @@
#include <LibJS/Runtime/Temporal/DurationPrototype.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
+#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
+#include <LibJS/Runtime/Temporal/PlainDatePrototype.h>
#include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/TimeZonePrototype.h>
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp
index dafc0302a8..1e0e76b025 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp
@@ -423,6 +423,27 @@ Optional<TemporalInstant> parse_temporal_instant_string(GlobalObject& global_obj
return TemporalInstant { .year = result->year, .month = result->month, .day = result->day, .hour = result->hour, .minute = result->minute, .second = result->second, .millisecond = result->millisecond, .microsecond = result->microsecond, .nanosecond = result->nanosecond, .time_zone_offset = move(time_zone_result->offset) };
}
+// 13.37 ParseTemporalCalendarString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalcalendarstring
+String parse_temporal_calendar_string([[maybe_unused]] GlobalObject& global_object, [[maybe_unused]] String const& iso_string)
+{
+ // 1. Assert: Type(isoString) is String.
+
+ // 2. If isoString does not satisfy the syntax of a TemporalCalendarString (see 13.33), then
+ // a. Throw a RangeError exception.
+ // 3. Let id be the part of isoString produced by the CalendarName production, or undefined if not present.
+ Optional<StringView> id_part;
+ TODO();
+
+ // 4. If id is undefined, then
+ if (!id_part.has_value()) {
+ // a. Return "iso8601".
+ return "iso8601";
+ }
+
+ // 5. Return id.
+ return id_part.value();
+}
+
// 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)
{
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h
index c02ccf196b..d7667ce951 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h
@@ -60,6 +60,7 @@ Optional<String> to_smallest_temporal_unit(GlobalObject&, Object& normalized_opt
BigInt* round_number_to_increment(GlobalObject&, BigInt const&, u64 increment, String const& rounding_mode);
Optional<ISODateTime> parse_iso_date_time(GlobalObject&, String const& iso_string);
Optional<TemporalInstant> parse_temporal_instant_string(GlobalObject&, String const& iso_string);
+String parse_temporal_calendar_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);
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp
index aa53db94bb..f839eedd64 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp
@@ -6,8 +6,10 @@
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
+#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Value.h>
namespace JS::Temporal {
@@ -53,6 +55,98 @@ bool is_builtin_calendar(String const& identifier)
return true;
}
+// 12.1.3 GetBuiltinCalendar ( id )
+Calendar* get_builtin_calendar(GlobalObject& global_object, String const& identifier)
+{
+ auto& vm = global_object.vm();
+
+ // 1. If ! IsBuiltinCalendar(id) is false, throw a RangeError exception.
+ if (!is_builtin_calendar(identifier)) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidCalendarIdentifier, identifier);
+ return {};
+ }
+
+ // 2. Return ? Construct(%Temporal.Calendar%, ยซ id ยป).
+ MarkedValueList arguments(vm.heap());
+ arguments.append(js_string(vm, identifier));
+ auto calendar = vm.construct(*global_object.temporal_calendar_constructor(), *global_object.temporal_calendar_constructor(), move(arguments));
+ if (vm.exception())
+ return {};
+ return static_cast<Calendar*>(&calendar.as_object());
+}
+
+// 12.1.4 GetISO8601Calendar ( )
+Calendar* get_iso8601_calendar(GlobalObject& global_object)
+{
+ // 1. Return ? GetBuiltinCalendar("iso8601").
+ return get_builtin_calendar(global_object, "iso8601");
+}
+
+// 12.1.21 ToTemporalCalendar ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendar
+Object* to_temporal_calendar(GlobalObject& global_object, Value temporal_calendar_like)
+{
+ auto& vm = global_object.vm();
+
+ // 1. If Type(temporalCalendarLike) is Object, then
+ if (temporal_calendar_like.is_object()) {
+ auto& temporal_calendar_like_object = temporal_calendar_like.as_object();
+ // a. If temporalCalendarLike has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ // TODO: The rest of the Temporal built-ins
+ if (is<PlainDate>(temporal_calendar_like_object)) {
+ // i. Return temporalCalendarLike.[[Calendar]].
+ return &static_cast<PlainDate&>(temporal_calendar_like_object).calendar();
+ }
+
+ // b. If ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
+ auto has_property = temporal_calendar_like_object.has_property(vm.names.calendar);
+ if (vm.exception())
+ return {};
+ if (!has_property)
+ return &temporal_calendar_like_object;
+
+ // c. Set temporalCalendarLike to ? Get(temporalCalendarLike, "calendar").
+ temporal_calendar_like = temporal_calendar_like_object.get(vm.names.calendar);
+ if (vm.exception())
+ return {};
+ // d. If Type(temporalCalendarLike) is Object and ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
+ if (temporal_calendar_like.is_object()) {
+ has_property = temporal_calendar_like.as_object().has_property(vm.names.calendar);
+ if (vm.exception())
+ return {};
+ if (!has_property)
+ return &temporal_calendar_like.as_object();
+ }
+ }
+
+ // 2. Let identifier be ? ToString(temporalCalendarLike).
+ auto identifier = temporal_calendar_like.to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ // 3. If ! IsBuiltinCalendar(identifier) is false, then
+ if (!is_builtin_calendar(identifier)) {
+ // a. Let identifier be ? ParseTemporalCalendarString(identifier).
+ identifier = parse_temporal_calendar_string(global_object, identifier);
+ if (vm.exception())
+ return {};
+ }
+
+ // 4. Return ? CreateTemporalCalendar(identifier).
+ return create_temporal_calendar(global_object, identifier);
+}
+
+// 12.1.22 ToTemporalCalendarWithISODefault ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendarwithisodefault
+Object* to_temporal_calendar_with_iso_default(GlobalObject& global_object, Value temporal_calendar_like)
+{
+ // 1. If temporalCalendarLike is undefined, then
+ if (temporal_calendar_like.is_undefined()) {
+ // a. Return ? GetISO8601Calendar().
+ return get_iso8601_calendar(global_object);
+ }
+ // 2. Return ? ToTemporalCalendar(temporalCalendarLike).
+ return to_temporal_calendar(global_object, temporal_calendar_like);
+}
+
// 12.1.30 IsISOLeapYear ( year ), https://tc39.es/proposal-temporal/#sec-temporal-isisoleapyear
bool is_iso_leap_year(i32 year)
{
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h
index e7b8ba1839..d729fdba92 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h
@@ -30,6 +30,10 @@ private:
Calendar* create_temporal_calendar(GlobalObject&, String const& identifier, FunctionObject* new_target = nullptr);
bool is_builtin_calendar(String const& identifier);
+Calendar* get_builtin_calendar(GlobalObject&, String const& identifier);
+Calendar* get_iso8601_calendar(GlobalObject&);
+Object* to_temporal_calendar(GlobalObject&, Value);
+Object* to_temporal_calendar_with_iso_default(GlobalObject&, Value);
bool is_iso_leap_year(i32 year);
i32 iso_days_in_month(i32 year, i32 month);
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp
index bf18c9a5c2..efe78699f8 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp
@@ -4,12 +4,68 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include <LibJS/Runtime/AbstractOperations.h>
+#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
-#include <LibJS/Runtime/Value.h>
+#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
+#include <LibJS/Runtime/Temporal/PlainDateTime.h>
namespace JS::Temporal {
+// 3 Temporal.PlainDate Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-objects
+PlainDate::PlainDate(i32 year, i32 month, i32 day, Object& calendar, Object& prototype)
+ : Object(prototype)
+ , m_iso_year(year)
+ , m_iso_month(month)
+ , m_iso_day(day)
+ , m_calendar(calendar)
+{
+}
+
+void PlainDate::visit_edges(Visitor& visitor)
+{
+ visitor.visit(&m_calendar);
+}
+
+// 3.5.1 CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldate
+PlainDate* create_temporal_date(GlobalObject& global_object, i32 iso_year, i32 iso_month, i32 iso_day, Object& calendar, FunctionObject* new_target)
+{
+ auto& vm = global_object.vm();
+
+ // 1. Assert: isoYear is an integer.
+ // 2. Assert: isoMonth is an integer.
+ // 3. Assert: isoDay is an integer.
+ // 4. Assert: Type(calendar) is Object.
+
+ // 5. If ! IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.
+ if (!is_valid_iso_date(iso_year, iso_month, iso_day)) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
+ return {};
+ }
+
+ // 6. If ! ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
+ if (!iso_date_time_within_limits(global_object, iso_year, iso_month, iso_day, 12, 0, 0, 0, 0, 0)) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
+ return {};
+ }
+
+ // 7. If newTarget is not present, set it to %Temporal.PlainDate%.
+ if (!new_target)
+ new_target = global_object.temporal_plain_date_constructor();
+
+ // 8. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDate.prototype%", ยซ [[InitializedTemporalDate]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] ยป).
+ // 9. Set object.[[ISOYear]] to isoYear.
+ // 10. Set object.[[ISOMonth]] to isoMonth.
+ // 11. Set object.[[ISODay]] to isoDay.
+ // 12. Set object.[[Calendar]] to calendar.
+ auto* object = ordinary_create_from_constructor<PlainDate>(global_object, *new_target, &GlobalObject::temporal_plain_date_prototype, iso_year, iso_month, iso_day, calendar);
+ if (vm.exception())
+ return {};
+
+ return object;
+}
+
// 3.5.5 IsValidISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidisodate
bool is_valid_iso_date(i32 year, i32 month, i32 day)
{
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h
index 8471958743..fde86d4bd3 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h
@@ -10,6 +10,30 @@
namespace JS::Temporal {
+class PlainDate final : public Object {
+ JS_OBJECT(PlainDate, Object);
+
+public:
+ explicit PlainDate(i32 iso_year, i32 iso_month, i32 iso_day, Object& calendar, Object& prototype);
+ virtual ~PlainDate() override = default;
+
+ [[nodiscard]] i32 iso_year() const { return m_iso_year; }
+ [[nodiscard]] i32 iso_month() const { return m_iso_month; }
+ [[nodiscard]] i32 iso_day() const { return m_iso_day; }
+ [[nodiscard]] Object const& calendar() const { return m_calendar; }
+ [[nodiscard]] Object& calendar() { return m_calendar; }
+
+private:
+ virtual void visit_edges(Visitor&) override;
+
+ // 3.4 Properties of Temporal.PlainDate Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-plaindate-instances
+ i32 m_iso_year { 0 }; // [[ISOYear]]
+ i32 m_iso_month { 1 }; // [[ISOMonth]]
+ i32 m_iso_day { 1 }; // [[ISODay]]
+ Object& m_calendar; // [[Calendar]]
+};
+
+PlainDate* create_temporal_date(GlobalObject&, i32 iso_year, i32 iso_month, i32 iso_day, Object& calendar, FunctionObject* new_target);
bool is_valid_iso_date(i32 year, i32 month, i32 day);
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp
new file mode 100644
index 0000000000..73e7545467
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Checked.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Temporal/Calendar.h>
+#include <LibJS/Runtime/Temporal/PlainDate.h>
+#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
+
+namespace JS::Temporal {
+
+// 3.1 The Temporal.PlainDate Constructor, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-constructor
+PlainDateConstructor::PlainDateConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.PlainDate.as_string(), *global_object.function_prototype())
+{
+}
+
+void PlainDateConstructor::initialize(GlobalObject& global_object)
+{
+ NativeFunction::initialize(global_object);
+
+ auto& vm = this->vm();
+
+ // 3.2.1 Temporal.PlainDate.prototype, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-prototype
+ define_direct_property(vm.names.prototype, global_object.temporal_plain_date_prototype(), 0);
+
+ define_direct_property(vm.names.length, Value(0), Attribute::Configurable);
+}
+
+// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate
+Value PlainDateConstructor::call()
+{
+ auto& vm = this->vm();
+
+ // 1. If NewTarget is undefined, throw a TypeError exception.
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, "Temporal.PlainDate");
+ return {};
+}
+
+// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate
+Value PlainDateConstructor::construct(FunctionObject& new_target)
+{
+ auto& vm = this->vm();
+ auto& global_object = this->global_object();
+
+ // 2. Let y be ? ToIntegerOrInfinity(isoYear).
+ auto y = vm.argument(0).to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ // 3. If y is +โˆž or -โˆž, throw a RangeError exception.
+ if (Value(y).is_infinity()) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
+ return {};
+ }
+
+ // 4. Let m be ? ToIntegerOrInfinity(isoMonth).
+ auto m = vm.argument(1).to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ // 5. If m is +โˆž or -โˆž, throw a RangeError exception.
+ if (Value(m).is_infinity()) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
+ return {};
+ }
+
+ // 6. Let d be ? ToIntegerOrInfinity(isoDay).
+ auto d = vm.argument(2).to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ // 7. If d is +โˆž or -โˆž, throw a RangeError exception.
+ if (Value(d).is_infinity()) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
+ return {};
+ }
+
+ // 8. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
+ auto* calendar = to_temporal_calendar_with_iso_default(global_object, vm.argument(3));
+ if (vm.exception())
+ return {};
+
+ // IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
+ // This does not change the exposed behaviour as the call to CreateTemporalDate will immediately check that these values are valid
+ // ISO values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31) all of which are subsets of this check.
+ if (!AK::is_within_range<i32>(y) || !AK::is_within_range<i32>(m) || !AK::is_within_range<i32>(d)) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
+ return {};
+ }
+
+ // 9. Return ? CreateTemporalDate(y, m, d, calendar, NewTarget).
+ return create_temporal_date(global_object, y, m, d, *calendar, &new_target);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.h
new file mode 100644
index 0000000000..830330412d
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/NativeFunction.h>
+
+namespace JS::Temporal {
+
+class PlainDateConstructor final : public NativeFunction {
+ JS_OBJECT(PlainDateConstructor, NativeFunction);
+
+public:
+ explicit PlainDateConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~PlainDateConstructor() override = default;
+
+ virtual Value call() override;
+ virtual Value construct(FunctionObject& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp
new file mode 100644
index 0000000000..354719d72b
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Temporal/PlainDatePrototype.h>
+
+namespace JS::Temporal {
+
+// 3.3 Properties of the Temporal.PlainDate Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-plaindate-prototype-object
+PlainDatePrototype::PlainDatePrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void PlainDatePrototype::initialize(GlobalObject& global_object)
+{
+ Object::initialize(global_object);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.h
new file mode 100644
index 0000000000..88c5fbd4b9
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+
+namespace JS::Temporal {
+
+class PlainDatePrototype final : public Object {
+ JS_OBJECT(PlainDatePrototype, Object);
+
+public:
+ explicit PlainDatePrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~PlainDatePrototype() override = default;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp
index 3bb99eded6..b047d0b19c 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp
@@ -41,4 +41,32 @@ BigInt* get_epoch_from_iso_parts(GlobalObject& global_object, i32 year, i32 mont
return js_bigint(vm.heap(), Crypto::SignedBigInteger::create_from(static_cast<i64>(ms.as_double())).multiplied_by(Crypto::UnsignedBigInteger { 1'000'000 }).plus(Crypto::SignedBigInteger::create_from((i64)microsecond * 1000)).plus(Crypto::SignedBigInteger(nanosecond)));
}
+// -864 * 10^19 - 864 * 10^14
+const auto DATETIME_NANOSECONDS_MIN = "-8640086400000000000000"_sbigint;
+// +864 * 10^19 + 864 * 10^14
+const auto DATETIME_NANOSECONDS_MAX = "8640086400000000000000"_sbigint;
+
+// 5.5.2 ISODateTimeWithinLimits ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-isodatetimewithinlimits
+bool iso_date_time_within_limits(GlobalObject& global_object, i32 year, i32 month, i32 day, i32 hour, i32 minute, i32 second, i32 millisecond, i32 microsecond, i32 nanosecond)
+{
+ // 1. Assert: year, month, day, hour, minute, second, millisecond, microsecond, and nanosecond are integers.
+
+ // 2. Let ns be ! GetEpochFromISOParts(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond).
+ auto ns = get_epoch_from_iso_parts(global_object, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
+
+ // 3. If ns โ‰ค -8.64 ร— 10^21 - 8.64 ร— 10^16, then
+ if (ns->big_integer() <= DATETIME_NANOSECONDS_MIN) {
+ // a. Return false.
+ return false;
+ }
+
+ // 4. If ns โ‰ฅ 8.64 ร— 10^21 + 8.64 ร— 10^16, then
+ if (ns->big_integer() >= DATETIME_NANOSECONDS_MAX) {
+ // a. Return false.
+ return false;
+ }
+ // 5. Return true.
+ return true;
+}
+
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h
index 77f4b0a46c..f5b6452464 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h
@@ -11,5 +11,6 @@
namespace JS::Temporal {
BigInt* get_epoch_from_iso_parts(GlobalObject&, i32 year, i32 month, i32 day, i32 hour, i32 minute, i32 second, i32 millisecond, i32 microsecond, i32 nanosecond);
+bool iso_date_time_within_limits(GlobalObject&, i32 year, i32 month, i32 day, i32 hour, i32 minute, i32 second, i32 millisecond, i32 microsecond, i32 nanosecond);
}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp
index 34c0ecda94..4fa31a57d9 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp
@@ -9,6 +9,7 @@
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/Now.h>
+#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
#include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
@@ -31,6 +32,7 @@ void Temporal::initialize(GlobalObject& global_object)
define_direct_property(vm.names.Calendar, global_object.temporal_calendar_constructor(), attr);
define_direct_property(vm.names.Duration, global_object.temporal_duration_constructor(), attr);
define_direct_property(vm.names.Instant, global_object.temporal_instant_constructor(), attr);
+ define_direct_property(vm.names.PlainDate, global_object.temporal_plain_date_constructor(), attr);
define_direct_property(vm.names.TimeZone, global_object.temporal_time_zone_constructor(), attr);
}
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.js
new file mode 100644
index 0000000000..70e4399119
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.js
@@ -0,0 +1,53 @@
+describe("errors", () => {
+ test("called without new", () => {
+ expect(() => {
+ Temporal.PlainDate();
+ }).toThrowWithMessage(
+ TypeError,
+ "Temporal.PlainDate constructor must be called with 'new'"
+ );
+ });
+
+ test("cannot pass Infinity", () => {
+ expect(() => {
+ new Temporal.PlainDate(Infinity);
+ }).toThrowWithMessage(RangeError, "Invalid plain date");
+ expect(() => {
+ new Temporal.PlainDate(0, Infinity);
+ }).toThrowWithMessage(RangeError, "Invalid plain date");
+ expect(() => {
+ new Temporal.PlainDate(0, 0, Infinity);
+ }).toThrowWithMessage(RangeError, "Invalid plain date");
+ expect(() => {
+ new Temporal.PlainDate(-Infinity);
+ }).toThrowWithMessage(RangeError, "Invalid plain date");
+ expect(() => {
+ new Temporal.PlainDate(0, -Infinity);
+ }).toThrowWithMessage(RangeError, "Invalid plain date");
+ expect(() => {
+ new Temporal.PlainDate(0, 0, -Infinity);
+ }).toThrowWithMessage(RangeError, "Invalid plain date");
+ });
+
+ test("cannot pass invalid ISO month/day", () => {
+ expect(() => {
+ new Temporal.PlainDate(0, 0, 1);
+ }).toThrowWithMessage(RangeError, "Invalid plain date");
+ expect(() => {
+ new Temporal.PlainDate(0, 1, 0);
+ }).toThrowWithMessage(RangeError, "Invalid plain date");
+ });
+});
+
+describe("normal behavior", () => {
+ test("length is 0", () => {
+ expect(Temporal.PlainDate).toHaveLength(0);
+ });
+
+ test("basic functionality", () => {
+ const plainDate = new Temporal.PlainDate(2021, 7, 19);
+ expect(typeof plainDate).toBe("object");
+ expect(plainDate).toBeInstanceOf(Temporal.PlainDate);
+ expect(Object.getPrototypeOf(plainDate)).toBe(Temporal.PlainDate.prototype);
+ });
+});