Bug 1874684 - Part 18: Fix narrowing warnings from clang-tidy. r=allstarschh
[gecko.git] / js / src / builtin / temporal / PlainDateTime.cpp
blobf0f55e5b2a27bebed65a56bf82238d7c5724e7c1
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "builtin/temporal/PlainDateTime.h"
9 #include "mozilla/Assertions.h"
11 #include <algorithm>
12 #include <type_traits>
13 #include <utility>
15 #include "jsnum.h"
16 #include "jspubtd.h"
17 #include "NamespaceImports.h"
19 #include "builtin/temporal/Calendar.h"
20 #include "builtin/temporal/Duration.h"
21 #include "builtin/temporal/PlainDate.h"
22 #include "builtin/temporal/PlainMonthDay.h"
23 #include "builtin/temporal/PlainTime.h"
24 #include "builtin/temporal/PlainYearMonth.h"
25 #include "builtin/temporal/Temporal.h"
26 #include "builtin/temporal/TemporalFields.h"
27 #include "builtin/temporal/TemporalParser.h"
28 #include "builtin/temporal/TemporalRoundingMode.h"
29 #include "builtin/temporal/TemporalTypes.h"
30 #include "builtin/temporal/TemporalUnit.h"
31 #include "builtin/temporal/TimeZone.h"
32 #include "builtin/temporal/ToString.h"
33 #include "builtin/temporal/Wrapped.h"
34 #include "builtin/temporal/ZonedDateTime.h"
35 #include "ds/IdValuePair.h"
36 #include "gc/AllocKind.h"
37 #include "gc/Barrier.h"
38 #include "js/AllocPolicy.h"
39 #include "js/CallArgs.h"
40 #include "js/CallNonGenericMethod.h"
41 #include "js/Class.h"
42 #include "js/ErrorReport.h"
43 #include "js/friend/ErrorMessages.h"
44 #include "js/GCVector.h"
45 #include "js/Id.h"
46 #include "js/PropertyDescriptor.h"
47 #include "js/PropertySpec.h"
48 #include "js/RootingAPI.h"
49 #include "js/TypeDecls.h"
50 #include "js/Value.h"
51 #include "vm/BytecodeUtil.h"
52 #include "vm/GlobalObject.h"
53 #include "vm/JSAtomState.h"
54 #include "vm/JSContext.h"
55 #include "vm/JSObject.h"
56 #include "vm/ObjectOperations.h"
57 #include "vm/PlainObject.h"
58 #include "vm/StringType.h"
60 #include "vm/JSObject-inl.h"
61 #include "vm/NativeObject-inl.h"
63 using namespace js;
64 using namespace js::temporal;
66 static inline bool IsPlainDateTime(Handle<Value> v) {
67 return v.isObject() && v.toObject().is<PlainDateTimeObject>();
70 #ifdef DEBUG
71 /**
72 * IsValidISODateTime ( year, month, day, hour, minute, second, millisecond,
73 * microsecond, nanosecond )
75 bool js::temporal::IsValidISODateTime(const PlainDateTime& dateTime) {
76 return IsValidISODate(dateTime.date) && IsValidTime(dateTime.time);
78 #endif
80 /**
81 * IsValidISODateTime ( year, month, day, hour, minute, second, millisecond,
82 * microsecond, nanosecond )
84 static bool ThrowIfInvalidISODateTime(JSContext* cx,
85 const PlainDateTime& dateTime) {
86 return ThrowIfInvalidISODate(cx, dateTime.date) &&
87 ThrowIfInvalidTime(cx, dateTime.time);
90 /**
91 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
92 * millisecond, microsecond, nanosecond )
94 template <typename T>
95 static bool ISODateTimeWithinLimits(T year, T month, T day, T hour, T minute,
96 T second, T millisecond, T microsecond,
97 T nanosecond) {
98 static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, double>);
100 // Step 1.
101 MOZ_ASSERT(IsInteger(year));
102 MOZ_ASSERT(IsInteger(month));
103 MOZ_ASSERT(IsInteger(day));
104 MOZ_ASSERT(IsInteger(hour));
105 MOZ_ASSERT(IsInteger(minute));
106 MOZ_ASSERT(IsInteger(second));
107 MOZ_ASSERT(IsInteger(millisecond));
108 MOZ_ASSERT(IsInteger(microsecond));
109 MOZ_ASSERT(IsInteger(nanosecond));
111 MOZ_ASSERT(IsValidISODate(year, month, day));
112 MOZ_ASSERT(
113 IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond));
115 // js> new Date(-8_64000_00000_00000).toISOString()
116 // "-271821-04-20T00:00:00.000Z"
118 // js> new Date(+8_64000_00000_00000).toISOString()
119 // "+275760-09-13T00:00:00.000Z"
121 constexpr int32_t minYear = -271821;
122 constexpr int32_t maxYear = 275760;
124 // Definitely in range.
125 if (minYear < year && year < maxYear) {
126 return true;
129 // -271821 April, 20
130 if (year < 0) {
131 if (year != minYear) {
132 return false;
134 if (month != 4) {
135 return month > 4;
137 if (day != (20 - 1)) {
138 return day > (20 - 1);
140 // Needs to be past midnight on April, 19.
141 return !(hour == 0 && minute == 0 && second == 0 && millisecond == 0 &&
142 microsecond == 0 && nanosecond == 0);
145 // 275760 September, 13
146 if (year != maxYear) {
147 return false;
149 if (month != 9) {
150 return month < 9;
152 if (day > 13) {
153 return false;
155 return true;
159 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
160 * millisecond, microsecond, nanosecond )
162 template <typename T>
163 static bool ISODateTimeWithinLimits(T year, T month, T day) {
164 static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, double>);
166 MOZ_ASSERT(IsValidISODate(year, month, day));
168 // js> new Date(-8_64000_00000_00000).toISOString()
169 // "-271821-04-20T00:00:00.000Z"
171 // js> new Date(+8_64000_00000_00000).toISOString()
172 // "+275760-09-13T00:00:00.000Z"
174 constexpr int32_t minYear = -271821;
175 constexpr int32_t maxYear = 275760;
177 // ISODateTimeWithinLimits is called with hour=12 and the remaining time
178 // components set to zero. That means the maximum value is exclusive, whereas
179 // the minimum value is inclusive.
181 // FIXME: spec bug - GetUTCEpochNanoseconds when called with large |year| may
182 // cause MakeDay to return NaN, which makes MakeDate return NaN, which is
183 // unexpected in GetUTCEpochNanoseconds, step 4.
184 // https://github.com/tc39/proposal-temporal/issues/2315
186 // Definitely in range.
187 if (minYear < year && year < maxYear) {
188 return true;
191 // -271821 April, 20
192 if (year < 0) {
193 if (year != minYear) {
194 return false;
196 if (month != 4) {
197 return month > 4;
199 if (day < (20 - 1)) {
200 return false;
202 return true;
205 // 275760 September, 13
206 if (year != maxYear) {
207 return false;
209 if (month != 9) {
210 return month < 9;
212 if (day > 13) {
213 return false;
215 return true;
219 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
220 * millisecond, microsecond, nanosecond )
222 bool js::temporal::ISODateTimeWithinLimits(double year, double month,
223 double day) {
224 return ::ISODateTimeWithinLimits(year, month, day);
228 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
229 * millisecond, microsecond, nanosecond )
231 bool js::temporal::ISODateTimeWithinLimits(const PlainDateTime& dateTime) {
232 const auto& [date, time] = dateTime;
233 return ::ISODateTimeWithinLimits(date.year, date.month, date.day, time.hour,
234 time.minute, time.second, time.millisecond,
235 time.microsecond, time.nanosecond);
239 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
240 * millisecond, microsecond, nanosecond )
242 bool js::temporal::ISODateTimeWithinLimits(const PlainDate& date) {
243 return ::ISODateTimeWithinLimits(date.year, date.month, date.day);
247 * CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second,
248 * millisecond, microsecond, nanosecond, calendar [ , newTarget ] )
250 static PlainDateTimeObject* CreateTemporalDateTime(
251 JSContext* cx, const CallArgs& args, double isoYear, double isoMonth,
252 double isoDay, double hour, double minute, double second,
253 double millisecond, double microsecond, double nanosecond,
254 Handle<CalendarValue> calendar) {
255 MOZ_ASSERT(IsInteger(isoYear));
256 MOZ_ASSERT(IsInteger(isoMonth));
257 MOZ_ASSERT(IsInteger(isoDay));
258 MOZ_ASSERT(IsInteger(hour));
259 MOZ_ASSERT(IsInteger(minute));
260 MOZ_ASSERT(IsInteger(second));
261 MOZ_ASSERT(IsInteger(millisecond));
262 MOZ_ASSERT(IsInteger(microsecond));
263 MOZ_ASSERT(IsInteger(nanosecond));
265 // Step 1.
266 if (!ThrowIfInvalidISODate(cx, isoYear, isoMonth, isoDay)) {
267 return nullptr;
270 // Step 2.
271 if (!ThrowIfInvalidTime(cx, hour, minute, second, millisecond, microsecond,
272 nanosecond)) {
273 return nullptr;
276 // Step 3.
277 if (!ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, hour, minute, second,
278 millisecond, microsecond, nanosecond)) {
279 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
280 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
281 return nullptr;
284 // Steps 4-5.
285 Rooted<JSObject*> proto(cx);
286 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainDateTime,
287 &proto)) {
288 return nullptr;
291 auto* dateTime = NewObjectWithClassProto<PlainDateTimeObject>(cx, proto);
292 if (!dateTime) {
293 return nullptr;
296 // Step 6.
297 dateTime->setFixedSlot(PlainDateTimeObject::ISO_YEAR_SLOT,
298 Int32Value(int32_t(isoYear)));
300 // Step 7.
301 dateTime->setFixedSlot(PlainDateTimeObject::ISO_MONTH_SLOT,
302 Int32Value(int32_t(isoMonth)));
304 // Step 8.
305 dateTime->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT,
306 Int32Value(int32_t(isoDay)));
308 // Step 9.
309 dateTime->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT,
310 Int32Value(int32_t(hour)));
312 // Step 10.
313 dateTime->setFixedSlot(PlainDateTimeObject::ISO_MINUTE_SLOT,
314 Int32Value(int32_t(minute)));
316 // Step 11.
317 dateTime->setFixedSlot(PlainDateTimeObject::ISO_SECOND_SLOT,
318 Int32Value(int32_t(second)));
320 // Step 12.
321 dateTime->setFixedSlot(PlainDateTimeObject::ISO_MILLISECOND_SLOT,
322 Int32Value(int32_t(millisecond)));
324 // Step 13.
325 dateTime->setFixedSlot(PlainDateTimeObject::ISO_MICROSECOND_SLOT,
326 Int32Value(int32_t(microsecond)));
328 // Step 14.
329 dateTime->setFixedSlot(PlainDateTimeObject::ISO_NANOSECOND_SLOT,
330 Int32Value(int32_t(nanosecond)));
332 // Step 15.
333 dateTime->setFixedSlot(PlainDateTimeObject::CALENDAR_SLOT,
334 calendar.toValue());
336 // Step 16.
337 return dateTime;
341 * CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second,
342 * millisecond, microsecond, nanosecond, calendar [ , newTarget ] )
344 PlainDateTimeObject* js::temporal::CreateTemporalDateTime(
345 JSContext* cx, const PlainDateTime& dateTime,
346 Handle<CalendarValue> calendar) {
347 const auto& [date, time] = dateTime;
348 const auto& [isoYear, isoMonth, isoDay] = date;
349 const auto& [hour, minute, second, millisecond, microsecond, nanosecond] =
350 time;
352 // Steps 1-2.
353 if (!ThrowIfInvalidISODateTime(cx, dateTime)) {
354 return nullptr;
357 // Step 3.
358 if (!ISODateTimeWithinLimits(dateTime)) {
359 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
360 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
361 return nullptr;
364 // Steps 4-5.
365 auto* object = NewBuiltinClassInstance<PlainDateTimeObject>(cx);
366 if (!object) {
367 return nullptr;
370 // Step 6.
371 object->setFixedSlot(PlainDateTimeObject::ISO_YEAR_SLOT, Int32Value(isoYear));
373 // Step 7.
374 object->setFixedSlot(PlainDateTimeObject::ISO_MONTH_SLOT,
375 Int32Value(isoMonth));
377 // Step 8.
378 object->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT, Int32Value(isoDay));
380 // Step 9.
381 object->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT, Int32Value(hour));
383 // Step 10.
384 object->setFixedSlot(PlainDateTimeObject::ISO_MINUTE_SLOT,
385 Int32Value(minute));
387 // Step 11.
388 object->setFixedSlot(PlainDateTimeObject::ISO_SECOND_SLOT,
389 Int32Value(second));
391 // Step 12.
392 object->setFixedSlot(PlainDateTimeObject::ISO_MILLISECOND_SLOT,
393 Int32Value(millisecond));
395 // Step 13.
396 object->setFixedSlot(PlainDateTimeObject::ISO_MICROSECOND_SLOT,
397 Int32Value(microsecond));
399 // Step 14.
400 object->setFixedSlot(PlainDateTimeObject::ISO_NANOSECOND_SLOT,
401 Int32Value(nanosecond));
403 // Step 15.
404 object->setFixedSlot(PlainDateTimeObject::CALENDAR_SLOT, calendar.toValue());
406 // Step 16.
407 return object;
411 * CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second,
412 * millisecond, microsecond, nanosecond, calendar [ , newTarget ] )
414 bool js::temporal::CreateTemporalDateTime(
415 JSContext* cx, const PlainDateTime& dateTime,
416 Handle<CalendarValue> calendar,
417 MutableHandle<PlainDateTimeWithCalendar> result) {
418 // Steps 1-2.
419 if (!ThrowIfInvalidISODateTime(cx, dateTime)) {
420 return false;
423 // Step 3.
424 if (!ISODateTimeWithinLimits(dateTime)) {
425 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
426 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
427 return false;
430 result.set(PlainDateTimeWithCalendar{dateTime, calendar});
431 return true;
435 * InterpretTemporalDateTimeFields ( calendarRec, fields, options )
437 bool js::temporal::InterpretTemporalDateTimeFields(
438 JSContext* cx, Handle<CalendarRecord> calendar, Handle<PlainObject*> fields,
439 Handle<PlainObject*> options, PlainDateTime* result) {
440 // Step 1. (Not applicable in our implementation.)
442 // Step 2.
443 MOZ_ASSERT(CalendarMethodsRecordHasLookedUp(calendar,
444 CalendarMethod::DateFromFields));
446 // Step 3.
447 TemporalTimeLike timeResult;
448 if (!ToTemporalTimeRecord(cx, fields, &timeResult)) {
449 return false;
452 // Step 4.
453 auto overflow = TemporalOverflow::Constrain;
454 if (!ToTemporalOverflow(cx, options, &overflow)) {
455 return false;
458 // Steps 5-6.
459 Rooted<Value> overflowValue(cx);
460 if (overflow == TemporalOverflow::Constrain) {
461 overflowValue.setString(cx->names().constrain);
462 } else {
463 MOZ_ASSERT(overflow == TemporalOverflow::Reject);
464 overflowValue.setString(cx->names().reject);
466 if (!DefineDataProperty(cx, options, cx->names().overflow, overflowValue)) {
467 return false;
470 // Step 7.
471 auto temporalDate =
472 js::temporal::CalendarDateFromFields(cx, calendar, fields, options);
473 if (!temporalDate) {
474 return false;
476 auto date = ToPlainDate(&temporalDate.unwrap());
478 // Step 8.
479 PlainTime time;
480 if (!RegulateTime(cx, timeResult, overflow, &time)) {
481 return false;
484 // Step 9.
485 *result = {date, time};
486 return true;
490 * InterpretTemporalDateTimeFields ( calendarRec, fields, options )
492 bool js::temporal::InterpretTemporalDateTimeFields(
493 JSContext* cx, Handle<CalendarRecord> calendar, Handle<PlainObject*> fields,
494 PlainDateTime* result) {
495 // TODO: Avoid creating the options object when CalendarDateFromFields calls
496 // the built-in Calendar.prototype.dateFromFields method.
497 Rooted<PlainObject*> options(cx, NewPlainObjectWithProto(cx, nullptr));
498 if (!options) {
499 return false;
502 return InterpretTemporalDateTimeFields(cx, calendar, fields, options, result);
506 * ToTemporalDateTime ( item [ , options ] )
508 static Wrapped<PlainDateTimeObject*> ToTemporalDateTime(
509 JSContext* cx, Handle<Value> item, Handle<JSObject*> maybeOptions) {
510 // Step 1. (Not applicable)
512 // Step 2.
513 Rooted<PlainObject*> maybeResolvedOptions(cx);
514 if (maybeOptions) {
515 maybeResolvedOptions = SnapshotOwnProperties(cx, maybeOptions);
516 if (!maybeResolvedOptions) {
517 return nullptr;
521 // Steps 3-4.
522 Rooted<CalendarValue> calendar(cx);
523 PlainDateTime result;
524 if (item.isObject()) {
525 Rooted<JSObject*> itemObj(cx, &item.toObject());
527 // Step 3.a.
528 if (itemObj->canUnwrapAs<PlainDateTimeObject>()) {
529 return itemObj;
532 // Step 3.b.
533 if (auto* zonedDateTime = itemObj->maybeUnwrapIf<ZonedDateTimeObject>()) {
534 auto epochInstant = ToInstant(zonedDateTime);
535 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
536 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
538 if (!timeZone.wrap(cx)) {
539 return nullptr;
541 if (!calendar.wrap(cx)) {
542 return nullptr;
545 // Step 3.b.i.
546 if (maybeResolvedOptions) {
547 TemporalOverflow ignored;
548 if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
549 return nullptr;
553 // Steps 3.b.ii-iv.
554 return GetPlainDateTimeFor(cx, timeZone, epochInstant, calendar);
557 // Step 3.c.
558 if (auto* date = itemObj->maybeUnwrapIf<PlainDateObject>()) {
559 PlainDateTime dateTime = {ToPlainDate(date), {}};
560 Rooted<CalendarValue> calendar(cx, date->calendar());
561 if (!calendar.wrap(cx)) {
562 return nullptr;
565 // Step 3.c.i.
566 if (maybeResolvedOptions) {
567 TemporalOverflow ignored;
568 if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
569 return nullptr;
573 // Step 3.c.ii.
574 return CreateTemporalDateTime(cx, dateTime, calendar);
577 // Step 3.d.
578 if (!GetTemporalCalendarWithISODefault(cx, itemObj, &calendar)) {
579 return nullptr;
582 // Step 3.e.
583 Rooted<CalendarRecord> calendarRec(cx);
584 if (!CreateCalendarMethodsRecord(cx, calendar,
586 CalendarMethod::DateFromFields,
587 CalendarMethod::Fields,
589 &calendarRec)) {
590 return nullptr;
593 // Step 3.f.
594 JS::RootedVector<PropertyKey> fieldNames(cx);
595 if (!CalendarFields(cx, calendarRec,
596 {CalendarField::Day, CalendarField::Month,
597 CalendarField::MonthCode, CalendarField::Year},
598 &fieldNames)) {
599 return nullptr;
602 // Step 3.g.
603 if (!AppendSorted(cx, fieldNames.get(),
605 TemporalField::Hour,
606 TemporalField::Microsecond,
607 TemporalField::Millisecond,
608 TemporalField::Minute,
609 TemporalField::Nanosecond,
610 TemporalField::Second,
611 })) {
612 return nullptr;
615 // Step 3.h.
616 Rooted<PlainObject*> fields(cx,
617 PrepareTemporalFields(cx, itemObj, fieldNames));
618 if (!fields) {
619 return nullptr;
622 // Step 3.i.
623 if (maybeResolvedOptions) {
624 if (!InterpretTemporalDateTimeFields(cx, calendarRec, fields,
625 maybeResolvedOptions, &result)) {
626 return nullptr;
628 } else {
629 if (!InterpretTemporalDateTimeFields(cx, calendarRec, fields, &result)) {
630 return nullptr;
633 } else {
634 // Step 4.a.
635 if (!item.isString()) {
636 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
637 nullptr, "not a string");
638 return nullptr;
640 Rooted<JSString*> string(cx, item.toString());
642 // Step 4.b.
643 Rooted<JSString*> calendarString(cx);
644 if (!ParseTemporalDateTimeString(cx, string, &result, &calendarString)) {
645 return nullptr;
648 // Step 4.c.
649 MOZ_ASSERT(IsValidISODate(result.date));
651 // Step 4.d.
652 MOZ_ASSERT(IsValidTime(result.time));
654 // Steps 4.e-h.
655 if (calendarString) {
656 if (!ToBuiltinCalendar(cx, calendarString, &calendar)) {
657 return nullptr;
659 } else {
660 calendar.set(CalendarValue(cx->names().iso8601));
663 // Step 4.i.
664 if (maybeResolvedOptions) {
665 TemporalOverflow ignored;
666 if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
667 return nullptr;
672 // Step 5.
673 return CreateTemporalDateTime(cx, result, calendar);
677 * ToTemporalDateTime ( item [ , options ] )
679 Wrapped<PlainDateTimeObject*> js::temporal::ToTemporalDateTime(
680 JSContext* cx, Handle<Value> item) {
681 return ::ToTemporalDateTime(cx, item, nullptr);
685 * ToTemporalDateTime ( item [ , options ] )
687 bool js::temporal::ToTemporalDateTime(JSContext* cx, Handle<Value> item,
688 PlainDateTime* result) {
689 auto obj = ::ToTemporalDateTime(cx, item, nullptr);
690 if (!obj) {
691 return false;
694 *result = ToPlainDateTime(&obj.unwrap());
695 return true;
699 * ToTemporalDateTime ( item [ , options ] )
701 static bool ToTemporalDateTime(
702 JSContext* cx, Handle<Value> item,
703 MutableHandle<PlainDateTimeWithCalendar> result) {
704 Handle<JSObject*> options = nullptr;
706 auto* obj = ::ToTemporalDateTime(cx, item, options).unwrapOrNull();
707 if (!obj) {
708 return false;
711 auto dateTime = ToPlainDateTime(obj);
712 Rooted<CalendarValue> calendar(cx, obj->calendar());
713 if (!calendar.wrap(cx)) {
714 return false;
717 result.set(PlainDateTimeWithCalendar{dateTime, calendar});
718 return true;
722 * CompareISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
723 * d2, h2, min2, s2, ms2, mus2, ns2 )
725 static int32_t CompareISODateTime(const PlainDateTime& one,
726 const PlainDateTime& two) {
727 // Step 1. (Not applicable in our implementation.)
729 // Steps 2-3.
730 if (int32_t dateResult = CompareISODate(one.date, two.date)) {
731 return dateResult;
734 // Steps 4.
735 return CompareTemporalTime(one.time, two.time);
739 * AddDateTime ( year, month, day, hour, minute, second, millisecond,
740 * microsecond, nanosecond, calendarRec, years, months, weeks, days, norm,
741 * options )
743 static bool AddDateTime(JSContext* cx, const PlainDateTime& dateTime,
744 Handle<CalendarRecord> calendar,
745 const NormalizedDuration& duration,
746 Handle<JSObject*> options, PlainDateTime* result) {
747 MOZ_ASSERT(IsValidDuration(duration));
749 // Step 1.
750 MOZ_ASSERT(IsValidISODateTime(dateTime));
751 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
753 // Step 2.
754 auto timeResult = AddTime(dateTime.time, duration.time);
756 // Step 3.
757 const auto& datePart = dateTime.date;
759 // Step 4.
760 auto dateDuration = DateDuration{
761 duration.date.years,
762 duration.date.months,
763 duration.date.weeks,
764 duration.date.days + timeResult.days,
766 if (!ThrowIfInvalidDuration(cx, dateDuration)) {
767 return false;
770 // Step 5.
771 PlainDate addedDate;
772 if (!AddDate(cx, calendar, datePart, dateDuration, options, &addedDate)) {
773 return false;
776 // Step 6.
777 *result = {addedDate, timeResult.time};
778 return true;
782 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
783 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
785 static bool DifferenceISODateTime(JSContext* cx, const PlainDateTime& one,
786 const PlainDateTime& two,
787 Handle<CalendarRecord> calendar,
788 TemporalUnit largestUnit,
789 Handle<PlainObject*> maybeOptions,
790 NormalizedDuration* result) {
791 // Steps 1-2.
792 MOZ_ASSERT(IsValidISODateTime(one));
793 MOZ_ASSERT(IsValidISODateTime(two));
794 MOZ_ASSERT(ISODateTimeWithinLimits(one));
795 MOZ_ASSERT(ISODateTimeWithinLimits(two));
797 // Step 3.
798 MOZ_ASSERT_IF(
799 one.date != two.date && largestUnit < TemporalUnit::Day,
800 CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateUntil));
802 // Step 4.
803 auto timeDuration = DifferenceTime(one.time, two.time);
805 // Step 5.
806 int32_t timeSign = NormalizedTimeDurationSign(timeDuration);
808 // Step 6.
809 int32_t dateSign = CompareISODate(two.date, one.date);
811 // Step 7.
812 auto adjustedDate = one.date;
814 // Step 8.
815 if (timeSign == -dateSign) {
816 // Step 8.a.
817 adjustedDate = BalanceISODate(adjustedDate.year, adjustedDate.month,
818 adjustedDate.day - timeSign);
820 // Step 8.b.
821 if (!Add24HourDaysToNormalizedTimeDuration(cx, timeDuration, -timeSign,
822 &timeDuration)) {
823 return false;
827 MOZ_ASSERT(IsValidISODate(adjustedDate));
828 MOZ_ASSERT(ISODateTimeWithinLimits(adjustedDate));
830 // TODO: Avoid allocating CreateTemporalDate.
832 // Step 9.
833 Rooted<PlainDateObject*> date1(
834 cx, CreateTemporalDate(cx, adjustedDate, calendar.receiver()));
835 if (!date1) {
836 return false;
839 // Step 10.
840 Rooted<PlainDateObject*> date2(
841 cx, CreateTemporalDate(cx, two.date, calendar.receiver()));
842 if (!date2) {
843 return false;
846 // Step 11.
847 auto dateLargestUnit = std::min(TemporalUnit::Day, largestUnit);
849 DateDuration dateDifference;
850 if (maybeOptions) {
851 // FIXME: spec issue - this copy is no longer needed, all callers have
852 // already copied the user input object.
853 // https://github.com/tc39/proposal-temporal/issues/2525
855 // Step 12.
856 Rooted<PlainObject*> untilOptions(cx,
857 SnapshotOwnProperties(cx, maybeOptions));
858 if (!untilOptions) {
859 return false;
862 // Step 13.
863 Rooted<Value> largestUnitValue(
864 cx, StringValue(TemporalUnitToString(cx, dateLargestUnit)));
865 if (!DefineDataProperty(cx, untilOptions, cx->names().largestUnit,
866 largestUnitValue)) {
867 return false;
870 // Step 14.
871 if (!DifferenceDate(cx, calendar, date1, date2, untilOptions,
872 &dateDifference)) {
873 return false;
875 } else {
876 // Steps 12-14.
877 if (!DifferenceDate(cx, calendar, date1, date2, dateLargestUnit,
878 &dateDifference)) {
879 return false;
883 // Step 15.
884 return CreateNormalizedDurationRecord(cx, dateDifference, timeDuration,
885 result);
889 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
890 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
892 bool js::temporal::DifferenceISODateTime(JSContext* cx,
893 const PlainDateTime& one,
894 const PlainDateTime& two,
895 Handle<CalendarRecord> calendar,
896 TemporalUnit largestUnit,
897 NormalizedDuration* result) {
898 return ::DifferenceISODateTime(cx, one, two, calendar, largestUnit, nullptr,
899 result);
903 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
904 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
906 bool js::temporal::DifferenceISODateTime(
907 JSContext* cx, const PlainDateTime& one, const PlainDateTime& two,
908 Handle<CalendarRecord> calendar, TemporalUnit largestUnit,
909 Handle<PlainObject*> options, NormalizedDuration* result) {
910 return ::DifferenceISODateTime(cx, one, two, calendar, largestUnit, options,
911 result);
915 * RoundISODateTime ( year, month, day, hour, minute, second, millisecond,
916 * microsecond, nanosecond, increment, unit, roundingMode [ , dayLength ] )
918 static PlainDateTime RoundISODateTime(const PlainDateTime& dateTime,
919 Increment increment, TemporalUnit unit,
920 TemporalRoundingMode roundingMode) {
921 const auto& [date, time] = dateTime;
923 // Step 1.
924 MOZ_ASSERT(IsValidISODateTime(dateTime));
925 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
927 // Step 2. (Not applicable in our implementation.)
929 // Step 3.
930 auto roundedTime = RoundTime(time, increment, unit, roundingMode);
931 MOZ_ASSERT(0 <= roundedTime.days && roundedTime.days <= 1);
933 // Step 4.
934 auto balanceResult = BalanceISODate(date.year, date.month,
935 date.day + int32_t(roundedTime.days));
937 // Step 5.
938 return {balanceResult, roundedTime.time};
942 * DifferenceTemporalPlainDateTime ( operation, dateTime, other, options )
944 static bool DifferenceTemporalPlainDateTime(JSContext* cx,
945 TemporalDifference operation,
946 const CallArgs& args) {
947 Rooted<PlainDateTimeWithCalendar> dateTime(
948 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
950 // Step 1. (Not applicable in our implementation.)
952 // Step 2.
953 Rooted<PlainDateTimeWithCalendar> other(cx);
954 if (!::ToTemporalDateTime(cx, args.get(0), &other)) {
955 return false;
958 // Step 3.
959 if (!CalendarEqualsOrThrow(cx, dateTime.calendar(), other.calendar())) {
960 return false;
963 // Steps 4-5.
964 DifferenceSettings settings;
965 Rooted<PlainObject*> resolvedOptions(cx);
966 if (args.hasDefined(1)) {
967 Rooted<JSObject*> options(
968 cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
969 if (!options) {
970 return false;
973 // Step 4.
974 resolvedOptions = SnapshotOwnProperties(cx, options);
975 if (!resolvedOptions) {
976 return false;
979 // Step 5.
980 if (!GetDifferenceSettings(
981 cx, operation, resolvedOptions, TemporalUnitGroup::DateTime,
982 TemporalUnit::Nanosecond, TemporalUnit::Day, &settings)) {
983 return false;
985 } else {
986 // Steps 4-5.
987 settings = {
988 TemporalUnit::Nanosecond,
989 TemporalUnit::Day,
990 TemporalRoundingMode::Trunc,
991 Increment{1},
995 // Steps 6-7.
996 bool datePartsIdentical = dateTime.date() == other.date();
998 // Step 8.
999 if (datePartsIdentical && dateTime.time() == other.time()) {
1000 auto* obj = CreateTemporalDuration(cx, {});
1001 if (!obj) {
1002 return false;
1005 args.rval().setObject(*obj);
1006 return true;
1009 // Step 9.
1010 Rooted<CalendarRecord> calendar(cx);
1011 if (!CreateCalendarMethodsRecord(cx, dateTime.calendar(),
1013 CalendarMethod::DateAdd,
1014 CalendarMethod::DateUntil,
1016 &calendar)) {
1017 return false;
1020 // Step 10.
1021 NormalizedDuration diff;
1022 if (!::DifferenceISODateTime(cx, dateTime, other, calendar,
1023 settings.largestUnit, resolvedOptions, &diff)) {
1024 return false;
1027 // Step 11.
1028 bool roundingGranularityIsNoop =
1029 settings.smallestUnit == TemporalUnit::Nanosecond &&
1030 settings.roundingIncrement == Increment{1};
1032 // Steps 12-13.
1033 DateDuration balancedDate;
1034 TimeDuration balancedTime;
1035 if (!roundingGranularityIsNoop) {
1036 // Step 12.a.
1037 Rooted<PlainDateObject*> relativeTo(
1038 cx, CreateTemporalDate(cx, dateTime.date(), dateTime.calendar()));
1039 if (!relativeTo) {
1040 return false;
1043 // Steps 12.b-c.
1044 NormalizedDuration roundResult;
1045 if (!temporal::RoundDuration(cx, diff, settings.roundingIncrement,
1046 settings.smallestUnit, settings.roundingMode,
1047 relativeTo, calendar, &roundResult)) {
1048 return false;
1051 // Step 12.d.
1052 NormalizedTimeDuration withDays;
1053 if (!Add24HourDaysToNormalizedTimeDuration(
1054 cx, roundResult.time, roundResult.date.days, &withDays)) {
1055 return false;
1058 // Step 12.e.
1059 balancedTime = BalanceTimeDuration(withDays, settings.largestUnit);
1061 // Step 12.f.
1062 auto toBalance = DateDuration{
1063 roundResult.date.years,
1064 roundResult.date.months,
1065 roundResult.date.weeks,
1066 balancedTime.days,
1068 if (!temporal::BalanceDateDurationRelative(
1069 cx, toBalance, settings.largestUnit, settings.smallestUnit,
1070 relativeTo, calendar, &balancedDate)) {
1071 return false;
1073 } else {
1074 // Step 13.a.
1075 NormalizedTimeDuration withDays;
1076 if (!Add24HourDaysToNormalizedTimeDuration(cx, diff.time, diff.date.days,
1077 &withDays)) {
1078 return false;
1081 // Step 13.b.
1082 balancedTime = BalanceTimeDuration(withDays, settings.largestUnit);
1084 // Step 13.c.
1085 balancedDate = {
1086 diff.date.years,
1087 diff.date.months,
1088 diff.date.weeks,
1089 balancedTime.days,
1092 MOZ_ASSERT(IsValidDuration(balancedDate));
1094 // Step 14.
1095 Duration duration = {
1096 double(balancedDate.years), double(balancedDate.months),
1097 double(balancedDate.weeks), double(balancedDate.days),
1098 double(balancedTime.hours), double(balancedTime.minutes),
1099 double(balancedTime.seconds), double(balancedTime.milliseconds),
1100 balancedTime.microseconds, balancedTime.nanoseconds,
1102 if (operation == TemporalDifference::Since) {
1103 duration = duration.negate();
1106 auto* obj = CreateTemporalDuration(cx, duration);
1107 if (!obj) {
1108 return false;
1111 args.rval().setObject(*obj);
1112 return true;
1115 enum class PlainDateTimeDuration { Add, Subtract };
1118 * AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime,
1119 * temporalDurationLike, options )
1121 static bool AddDurationToOrSubtractDurationFromPlainDateTime(
1122 JSContext* cx, PlainDateTimeDuration operation, const CallArgs& args) {
1123 Rooted<PlainDateTimeWithCalendar> dateTime(
1124 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1126 // Step 1. (Not applicable in our implementation.)
1128 // Step 2.
1129 Duration duration;
1130 if (!ToTemporalDurationRecord(cx, args.get(0), &duration)) {
1131 return false;
1134 // Step 3.
1135 Rooted<JSObject*> options(cx);
1136 if (args.hasDefined(1)) {
1137 const char* name =
1138 operation == PlainDateTimeDuration::Add ? "add" : "subtract";
1139 options = RequireObjectArg(cx, "options", name, args[1]);
1140 } else {
1141 options = NewPlainObjectWithProto(cx, nullptr);
1143 if (!options) {
1144 return false;
1147 // Step 4.
1148 Rooted<CalendarRecord> calendar(cx);
1149 if (!CreateCalendarMethodsRecord(cx, dateTime.calendar(),
1151 CalendarMethod::DateAdd,
1153 &calendar)) {
1154 return false;
1157 // Step 5.
1158 if (operation == PlainDateTimeDuration::Subtract) {
1159 duration = duration.negate();
1161 auto normalized = CreateNormalizedDurationRecord(duration);
1163 // Step 6
1164 PlainDateTime result;
1165 if (!AddDateTime(cx, dateTime, calendar, normalized, options, &result)) {
1166 return false;
1169 // Steps 7-8.
1170 MOZ_ASSERT(IsValidISODateTime(result));
1172 // Step 9.
1173 auto* obj = CreateTemporalDateTime(cx, result, dateTime.calendar());
1174 if (!obj) {
1175 return false;
1178 args.rval().setObject(*obj);
1179 return true;
1183 * Temporal.PlainDateTime ( isoYear, isoMonth, isoDay [ , hour [ , minute [ ,
1184 * second [ , millisecond [ , microsecond [ , nanosecond [ , calendarLike ] ] ]
1185 * ] ] ] ] )
1187 static bool PlainDateTimeConstructor(JSContext* cx, unsigned argc, Value* vp) {
1188 CallArgs args = CallArgsFromVp(argc, vp);
1190 // Step 1.
1191 if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainDateTime")) {
1192 return false;
1195 // Step 2.
1196 double isoYear;
1197 if (!ToIntegerWithTruncation(cx, args.get(0), "year", &isoYear)) {
1198 return false;
1201 // Step 3.
1202 double isoMonth;
1203 if (!ToIntegerWithTruncation(cx, args.get(1), "month", &isoMonth)) {
1204 return false;
1207 // Step 4.
1208 double isoDay;
1209 if (!ToIntegerWithTruncation(cx, args.get(2), "day", &isoDay)) {
1210 return false;
1213 // Step 5.
1214 double hour = 0;
1215 if (args.hasDefined(3)) {
1216 if (!ToIntegerWithTruncation(cx, args[3], "hour", &hour)) {
1217 return false;
1221 // Step 6.
1222 double minute = 0;
1223 if (args.hasDefined(4)) {
1224 if (!ToIntegerWithTruncation(cx, args[4], "minute", &minute)) {
1225 return false;
1229 // Step 7.
1230 double second = 0;
1231 if (args.hasDefined(5)) {
1232 if (!ToIntegerWithTruncation(cx, args[5], "second", &second)) {
1233 return false;
1237 // Step 8.
1238 double millisecond = 0;
1239 if (args.hasDefined(6)) {
1240 if (!ToIntegerWithTruncation(cx, args[6], "millisecond", &millisecond)) {
1241 return false;
1245 // Step 9.
1246 double microsecond = 0;
1247 if (args.hasDefined(7)) {
1248 if (!ToIntegerWithTruncation(cx, args[7], "microsecond", &microsecond)) {
1249 return false;
1253 // Step 10.
1254 double nanosecond = 0;
1255 if (args.hasDefined(8)) {
1256 if (!ToIntegerWithTruncation(cx, args[8], "nanosecond", &nanosecond)) {
1257 return false;
1261 // Step 11.
1262 Rooted<CalendarValue> calendar(cx);
1263 if (!ToTemporalCalendarWithISODefault(cx, args.get(9), &calendar)) {
1264 return false;
1267 // Step 12.
1268 auto* temporalDateTime = CreateTemporalDateTime(
1269 cx, args, isoYear, isoMonth, isoDay, hour, minute, second, millisecond,
1270 microsecond, nanosecond, calendar);
1271 if (!temporalDateTime) {
1272 return false;
1275 args.rval().setObject(*temporalDateTime);
1276 return true;
1280 * Temporal.PlainDateTime.from ( item [ , options ] )
1282 static bool PlainDateTime_from(JSContext* cx, unsigned argc, Value* vp) {
1283 CallArgs args = CallArgsFromVp(argc, vp);
1285 // Step 1.
1286 Rooted<JSObject*> options(cx);
1287 if (args.hasDefined(1)) {
1288 options = RequireObjectArg(cx, "options", "from", args[1]);
1289 if (!options) {
1290 return false;
1294 // Step 2.
1295 if (args.get(0).isObject()) {
1296 JSObject* item = &args[0].toObject();
1297 if (auto* temporalDateTime = item->maybeUnwrapIf<PlainDateTimeObject>()) {
1298 auto dateTime = ToPlainDateTime(temporalDateTime);
1300 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1301 if (!calendar.wrap(cx)) {
1302 return false;
1305 if (options) {
1306 // Step 2.a.
1307 TemporalOverflow ignored;
1308 if (!ToTemporalOverflow(cx, options, &ignored)) {
1309 return false;
1313 // Step 2.b.
1314 auto* result = CreateTemporalDateTime(cx, dateTime, calendar);
1315 if (!result) {
1316 return false;
1319 args.rval().setObject(*result);
1320 return true;
1324 // Step 3.
1325 auto result = ToTemporalDateTime(cx, args.get(0), options);
1326 if (!result) {
1327 return false;
1330 args.rval().setObject(*result);
1331 return true;
1335 * Temporal.PlainDateTime.compare ( one, two )
1337 static bool PlainDateTime_compare(JSContext* cx, unsigned argc, Value* vp) {
1338 CallArgs args = CallArgsFromVp(argc, vp);
1340 // Step 1.
1341 PlainDateTime one;
1342 if (!ToTemporalDateTime(cx, args.get(0), &one)) {
1343 return false;
1346 // Step 2.
1347 PlainDateTime two;
1348 if (!ToTemporalDateTime(cx, args.get(1), &two)) {
1349 return false;
1352 // Step 3.
1353 args.rval().setInt32(CompareISODateTime(one, two));
1354 return true;
1358 * get Temporal.PlainDateTime.prototype.calendarId
1360 static bool PlainDateTime_calendarId(JSContext* cx, const CallArgs& args) {
1361 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1363 // Step 3.
1364 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1365 auto* calendarId = ToTemporalCalendarIdentifier(cx, calendar);
1366 if (!calendarId) {
1367 return false;
1370 args.rval().setString(calendarId);
1371 return true;
1375 * get Temporal.PlainDateTime.prototype.calendarId
1377 static bool PlainDateTime_calendarId(JSContext* cx, unsigned argc, Value* vp) {
1378 // Steps 1-2.
1379 CallArgs args = CallArgsFromVp(argc, vp);
1380 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_calendarId>(cx,
1381 args);
1385 * get Temporal.PlainDateTime.prototype.year
1387 static bool PlainDateTime_year(JSContext* cx, const CallArgs& args) {
1388 // Step 3.
1389 Rooted<PlainDateTimeObject*> dateTime(
1390 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1391 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1393 // Step 4.
1394 return CalendarYear(cx, calendar, dateTime, args.rval());
1398 * get Temporal.PlainDateTime.prototype.year
1400 static bool PlainDateTime_year(JSContext* cx, unsigned argc, Value* vp) {
1401 // Steps 1-2.
1402 CallArgs args = CallArgsFromVp(argc, vp);
1403 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_year>(cx, args);
1407 * get Temporal.PlainDateTime.prototype.month
1409 static bool PlainDateTime_month(JSContext* cx, const CallArgs& args) {
1410 // Step 3.
1411 Rooted<PlainDateTimeObject*> dateTime(
1412 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1413 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1415 // Step 4.
1416 return CalendarMonth(cx, calendar, dateTime, args.rval());
1420 * get Temporal.PlainDateTime.prototype.month
1422 static bool PlainDateTime_month(JSContext* cx, unsigned argc, Value* vp) {
1423 // Steps 1-2.
1424 CallArgs args = CallArgsFromVp(argc, vp);
1425 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_month>(cx, args);
1429 * get Temporal.PlainDateTime.prototype.monthCode
1431 static bool PlainDateTime_monthCode(JSContext* cx, const CallArgs& args) {
1432 // Step 3.
1433 Rooted<PlainDateTimeObject*> dateTime(
1434 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1435 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1437 // Step 4.
1438 return CalendarMonthCode(cx, calendar, dateTime, args.rval());
1442 * get Temporal.PlainDateTime.prototype.monthCode
1444 static bool PlainDateTime_monthCode(JSContext* cx, unsigned argc, Value* vp) {
1445 // Steps 1-2.
1446 CallArgs args = CallArgsFromVp(argc, vp);
1447 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthCode>(cx,
1448 args);
1452 * get Temporal.PlainDateTime.prototype.day
1454 static bool PlainDateTime_day(JSContext* cx, const CallArgs& args) {
1455 // Step 3.
1456 Rooted<PlainDateTimeObject*> dateTime(
1457 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1458 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1460 // Step 4.
1461 return CalendarDay(cx, calendar, dateTime, args.rval());
1465 * get Temporal.PlainDateTime.prototype.day
1467 static bool PlainDateTime_day(JSContext* cx, unsigned argc, Value* vp) {
1468 // Steps 1-2.
1469 CallArgs args = CallArgsFromVp(argc, vp);
1470 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_day>(cx, args);
1474 * get Temporal.PlainDateTime.prototype.hour
1476 static bool PlainDateTime_hour(JSContext* cx, const CallArgs& args) {
1477 // Step 3.
1478 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1479 args.rval().setInt32(dateTime->isoHour());
1480 return true;
1484 * get Temporal.PlainDateTime.prototype.hour
1486 static bool PlainDateTime_hour(JSContext* cx, unsigned argc, Value* vp) {
1487 // Steps 1-2.
1488 CallArgs args = CallArgsFromVp(argc, vp);
1489 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_hour>(cx, args);
1493 * get Temporal.PlainDateTime.prototype.minute
1495 static bool PlainDateTime_minute(JSContext* cx, const CallArgs& args) {
1496 // Step 3.
1497 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1498 args.rval().setInt32(dateTime->isoMinute());
1499 return true;
1503 * get Temporal.PlainDateTime.prototype.minute
1505 static bool PlainDateTime_minute(JSContext* cx, unsigned argc, Value* vp) {
1506 // Steps 1-2.
1507 CallArgs args = CallArgsFromVp(argc, vp);
1508 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_minute>(cx, args);
1512 * get Temporal.PlainDateTime.prototype.second
1514 static bool PlainDateTime_second(JSContext* cx, const CallArgs& args) {
1515 // Step 3.
1516 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1517 args.rval().setInt32(dateTime->isoSecond());
1518 return true;
1522 * get Temporal.PlainDateTime.prototype.second
1524 static bool PlainDateTime_second(JSContext* cx, unsigned argc, Value* vp) {
1525 // Steps 1-2.
1526 CallArgs args = CallArgsFromVp(argc, vp);
1527 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_second>(cx, args);
1531 * get Temporal.PlainDateTime.prototype.millisecond
1533 static bool PlainDateTime_millisecond(JSContext* cx, const CallArgs& args) {
1534 // Step 3.
1535 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1536 args.rval().setInt32(dateTime->isoMillisecond());
1537 return true;
1541 * get Temporal.PlainDateTime.prototype.millisecond
1543 static bool PlainDateTime_millisecond(JSContext* cx, unsigned argc, Value* vp) {
1544 // Steps 1-2.
1545 CallArgs args = CallArgsFromVp(argc, vp);
1546 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_millisecond>(cx,
1547 args);
1551 * get Temporal.PlainDateTime.prototype.microsecond
1553 static bool PlainDateTime_microsecond(JSContext* cx, const CallArgs& args) {
1554 // Step 3.
1555 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1556 args.rval().setInt32(dateTime->isoMicrosecond());
1557 return true;
1561 * get Temporal.PlainDateTime.prototype.microsecond
1563 static bool PlainDateTime_microsecond(JSContext* cx, unsigned argc, Value* vp) {
1564 // Steps 1-2.
1565 CallArgs args = CallArgsFromVp(argc, vp);
1566 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_microsecond>(cx,
1567 args);
1571 * get Temporal.PlainDateTime.prototype.nanosecond
1573 static bool PlainDateTime_nanosecond(JSContext* cx, const CallArgs& args) {
1574 // Step 3.
1575 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1576 args.rval().setInt32(dateTime->isoNanosecond());
1577 return true;
1581 * get Temporal.PlainDateTime.prototype.nanosecond
1583 static bool PlainDateTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) {
1584 // Steps 1-2.
1585 CallArgs args = CallArgsFromVp(argc, vp);
1586 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_nanosecond>(cx,
1587 args);
1591 * get Temporal.PlainDateTime.prototype.dayOfWeek
1593 static bool PlainDateTime_dayOfWeek(JSContext* cx, const CallArgs& args) {
1594 // Step 3.
1595 Rooted<PlainDateTimeObject*> dateTime(
1596 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1597 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1599 // Step 4.
1600 return CalendarDayOfWeek(cx, calendar, dateTime, args.rval());
1604 * get Temporal.PlainDateTime.prototype.dayOfWeek
1606 static bool PlainDateTime_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) {
1607 // Steps 1-2.
1608 CallArgs args = CallArgsFromVp(argc, vp);
1609 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfWeek>(cx,
1610 args);
1614 * get Temporal.PlainDateTime.prototype.dayOfYear
1616 static bool PlainDateTime_dayOfYear(JSContext* cx, const CallArgs& args) {
1617 // Step 3.
1618 Rooted<PlainDateTimeObject*> dateTime(
1619 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1620 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1622 // Step 4.
1623 return CalendarDayOfYear(cx, calendar, dateTime, args.rval());
1627 * get Temporal.PlainDateTime.prototype.dayOfYear
1629 static bool PlainDateTime_dayOfYear(JSContext* cx, unsigned argc, Value* vp) {
1630 // Steps 1-2.
1631 CallArgs args = CallArgsFromVp(argc, vp);
1632 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfYear>(cx,
1633 args);
1637 * get Temporal.PlainDateTime.prototype.weekOfYear
1639 static bool PlainDateTime_weekOfYear(JSContext* cx, const CallArgs& args) {
1640 // Step 3.
1641 Rooted<PlainDateTimeObject*> dateTime(
1642 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1643 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1645 // Step 4.
1646 return CalendarWeekOfYear(cx, calendar, dateTime, args.rval());
1650 * get Temporal.PlainDateTime.prototype.weekOfYear
1652 static bool PlainDateTime_weekOfYear(JSContext* cx, unsigned argc, Value* vp) {
1653 // Steps 1-2.
1654 CallArgs args = CallArgsFromVp(argc, vp);
1655 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_weekOfYear>(cx,
1656 args);
1660 * get Temporal.PlainDateTime.prototype.yearOfWeek
1662 static bool PlainDateTime_yearOfWeek(JSContext* cx, const CallArgs& args) {
1663 // Step 3.
1664 Rooted<PlainDateTimeObject*> dateTime(
1665 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1666 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1668 // Step 4.
1669 return CalendarYearOfWeek(cx, calendar, dateTime, args.rval());
1673 * get Temporal.PlainDateTime.prototype.yearOfWeek
1675 static bool PlainDateTime_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) {
1676 // Steps 1-2.
1677 CallArgs args = CallArgsFromVp(argc, vp);
1678 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_yearOfWeek>(cx,
1679 args);
1683 * get Temporal.PlainDateTime.prototype.daysInWeek
1685 static bool PlainDateTime_daysInWeek(JSContext* cx, const CallArgs& args) {
1686 // Step 3.
1687 Rooted<PlainDateTimeObject*> dateTime(
1688 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1689 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1691 // Step 4.
1692 return CalendarDaysInWeek(cx, calendar, dateTime, args.rval());
1696 * get Temporal.PlainDateTime.prototype.daysInWeek
1698 static bool PlainDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) {
1699 // Steps 1-2.
1700 CallArgs args = CallArgsFromVp(argc, vp);
1701 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInWeek>(cx,
1702 args);
1706 * get Temporal.PlainDateTime.prototype.daysInMonth
1708 static bool PlainDateTime_daysInMonth(JSContext* cx, const CallArgs& args) {
1709 // Step 3.
1710 Rooted<PlainDateTimeObject*> dateTime(
1711 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1712 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1714 // Step 4.
1715 return CalendarDaysInMonth(cx, calendar, dateTime, args.rval());
1719 * get Temporal.PlainDateTime.prototype.daysInMonth
1721 static bool PlainDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) {
1722 // Steps 1-2.
1723 CallArgs args = CallArgsFromVp(argc, vp);
1724 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInMonth>(cx,
1725 args);
1729 * get Temporal.PlainDateTime.prototype.daysInYear
1731 static bool PlainDateTime_daysInYear(JSContext* cx, const CallArgs& args) {
1732 // Step 3.
1733 Rooted<PlainDateTimeObject*> dateTime(
1734 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1735 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1737 // Step 4.
1738 return CalendarDaysInYear(cx, calendar, dateTime, args.rval());
1742 * get Temporal.PlainDateTime.prototype.daysInYear
1744 static bool PlainDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) {
1745 // Steps 1-2.
1746 CallArgs args = CallArgsFromVp(argc, vp);
1747 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInYear>(cx,
1748 args);
1752 * get Temporal.PlainDateTime.prototype.monthsInYear
1754 static bool PlainDateTime_monthsInYear(JSContext* cx, const CallArgs& args) {
1755 // Step 3.
1756 Rooted<PlainDateTimeObject*> dateTime(
1757 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1758 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1760 // Step 4.
1761 return CalendarMonthsInYear(cx, calendar, dateTime, args.rval());
1765 * get Temporal.PlainDateTime.prototype.monthsInYear
1767 static bool PlainDateTime_monthsInYear(JSContext* cx, unsigned argc,
1768 Value* vp) {
1769 // Steps 1-2.
1770 CallArgs args = CallArgsFromVp(argc, vp);
1771 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthsInYear>(
1772 cx, args);
1776 * get Temporal.PlainDateTime.prototype.inLeapYear
1778 static bool PlainDateTime_inLeapYear(JSContext* cx, const CallArgs& args) {
1779 // Step 3.
1780 Rooted<PlainDateTimeObject*> dateTime(
1781 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1782 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1784 // Step 4.
1785 return CalendarInLeapYear(cx, calendar, dateTime, args.rval());
1789 * get Temporal.PlainDateTime.prototype.inLeapYear
1791 static bool PlainDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) {
1792 // Steps 1-2.
1793 CallArgs args = CallArgsFromVp(argc, vp);
1794 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_inLeapYear>(cx,
1795 args);
1799 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
1801 static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
1802 Rooted<PlainDateTimeObject*> dateTime(
1803 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1805 // Step 3.
1806 Rooted<JSObject*> temporalDateTimeLike(
1807 cx, RequireObjectArg(cx, "temporalDateTimeLike", "with", args.get(0)));
1808 if (!temporalDateTimeLike) {
1809 return false;
1812 // Step 4.
1813 if (!RejectTemporalLikeObject(cx, temporalDateTimeLike)) {
1814 return false;
1817 // Step 5.
1818 Rooted<PlainObject*> resolvedOptions(cx);
1819 if (args.hasDefined(1)) {
1820 Rooted<JSObject*> options(cx,
1821 RequireObjectArg(cx, "options", "with", args[1]));
1822 if (!options) {
1823 return false;
1825 resolvedOptions = SnapshotOwnProperties(cx, options);
1826 } else {
1827 resolvedOptions = NewPlainObjectWithProto(cx, nullptr);
1829 if (!resolvedOptions) {
1830 return false;
1833 // Step 6.
1834 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
1835 Rooted<CalendarRecord> calendar(cx);
1836 if (!CreateCalendarMethodsRecord(cx, calendarValue,
1838 CalendarMethod::DateFromFields,
1839 CalendarMethod::Fields,
1840 CalendarMethod::MergeFields,
1842 &calendar)) {
1843 return false;
1846 // Step 7.
1847 JS::RootedVector<PropertyKey> fieldNames(cx);
1848 if (!CalendarFields(cx, calendar,
1849 {CalendarField::Day, CalendarField::Month,
1850 CalendarField::MonthCode, CalendarField::Year},
1851 &fieldNames)) {
1852 return false;
1855 // Step 8.
1856 Rooted<PlainObject*> fields(cx,
1857 PrepareTemporalFields(cx, dateTime, fieldNames));
1858 if (!fields) {
1859 return false;
1862 // Steps 9-14.
1863 struct TimeField {
1864 using FieldName = ImmutableTenuredPtr<PropertyName*> JSAtomState::*;
1866 FieldName name;
1867 int32_t value;
1868 } timeFields[] = {
1869 {&JSAtomState::hour, dateTime->isoHour()},
1870 {&JSAtomState::minute, dateTime->isoMinute()},
1871 {&JSAtomState::second, dateTime->isoSecond()},
1872 {&JSAtomState::millisecond, dateTime->isoMillisecond()},
1873 {&JSAtomState::microsecond, dateTime->isoMicrosecond()},
1874 {&JSAtomState::nanosecond, dateTime->isoNanosecond()},
1877 Rooted<Value> timeFieldValue(cx);
1878 for (const auto& timeField : timeFields) {
1879 Handle<PropertyName*> name = cx->names().*(timeField.name);
1880 timeFieldValue.setInt32(timeField.value);
1882 if (!DefineDataProperty(cx, fields, name, timeFieldValue)) {
1883 return false;
1887 // Step 15.
1888 if (!AppendSorted(cx, fieldNames.get(),
1890 TemporalField::Hour,
1891 TemporalField::Microsecond,
1892 TemporalField::Millisecond,
1893 TemporalField::Minute,
1894 TemporalField::Nanosecond,
1895 TemporalField::Second,
1896 })) {
1897 return false;
1900 // Step 16.
1901 Rooted<PlainObject*> partialDateTime(
1902 cx, PreparePartialTemporalFields(cx, temporalDateTimeLike, fieldNames));
1903 if (!partialDateTime) {
1904 return false;
1907 // Step 17.
1908 Rooted<JSObject*> mergedFields(
1909 cx, CalendarMergeFields(cx, calendar, fields, partialDateTime));
1910 if (!mergedFields) {
1911 return false;
1914 // Step 18.
1915 fields = PrepareTemporalFields(cx, mergedFields, fieldNames);
1916 if (!fields) {
1917 return false;
1920 // Step 19.
1921 PlainDateTime result;
1922 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, resolvedOptions,
1923 &result)) {
1924 return false;
1927 // Steps 20-21.
1928 MOZ_ASSERT(IsValidISODateTime(result));
1930 // Step 22.
1931 auto* obj = CreateTemporalDateTime(cx, result, calendar.receiver());
1932 if (!obj) {
1933 return false;
1936 args.rval().setObject(*obj);
1937 return true;
1941 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
1943 static bool PlainDateTime_with(JSContext* cx, unsigned argc, Value* vp) {
1944 // Steps 1-2.
1945 CallArgs args = CallArgsFromVp(argc, vp);
1946 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_with>(cx, args);
1950 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1952 static bool PlainDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
1953 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1954 auto date = ToPlainDate(temporalDateTime);
1955 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1957 // Step 4.
1958 PlainTime time = {};
1959 if (args.hasDefined(0)) {
1960 if (!ToTemporalTime(cx, args[0], &time)) {
1961 return false;
1965 // Steps 3 and 5.
1966 auto* obj = CreateTemporalDateTime(cx, {date, time}, calendar);
1967 if (!obj) {
1968 return false;
1971 args.rval().setObject(*obj);
1972 return true;
1976 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1978 static bool PlainDateTime_withPlainTime(JSContext* cx, unsigned argc,
1979 Value* vp) {
1980 // Steps 1-2.
1981 CallArgs args = CallArgsFromVp(argc, vp);
1982 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainTime>(
1983 cx, args);
1987 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
1989 static bool PlainDateTime_withPlainDate(JSContext* cx, const CallArgs& args) {
1990 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1991 auto time = ToPlainTime(temporalDateTime);
1992 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1994 // Step 3.
1995 Rooted<PlainDateWithCalendar> plainDate(cx);
1996 if (!ToTemporalDate(cx, args.get(0), &plainDate)) {
1997 return false;
1999 auto date = plainDate.date();
2001 // Step 4.
2002 if (!ConsolidateCalendars(cx, calendar, plainDate.calendar(), &calendar)) {
2003 return false;
2006 // Step 5.
2007 auto* obj = CreateTemporalDateTime(cx, {date, time}, calendar);
2008 if (!obj) {
2009 return false;
2012 args.rval().setObject(*obj);
2013 return true;
2017 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
2019 static bool PlainDateTime_withPlainDate(JSContext* cx, unsigned argc,
2020 Value* vp) {
2021 // Steps 1-2.
2022 CallArgs args = CallArgsFromVp(argc, vp);
2023 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainDate>(
2024 cx, args);
2028 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2030 static bool PlainDateTime_withCalendar(JSContext* cx, const CallArgs& args) {
2031 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2032 auto dateTime = ToPlainDateTime(temporalDateTime);
2034 // Step 3.
2035 Rooted<CalendarValue> calendar(cx);
2036 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) {
2037 return false;
2040 // Step 4.
2041 auto* result = CreateTemporalDateTime(cx, dateTime, calendar);
2042 if (!result) {
2043 return false;
2046 args.rval().setObject(*result);
2047 return true;
2051 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2053 static bool PlainDateTime_withCalendar(JSContext* cx, unsigned argc,
2054 Value* vp) {
2055 // Steps 1-2.
2056 CallArgs args = CallArgsFromVp(argc, vp);
2057 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withCalendar>(
2058 cx, args);
2062 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2064 static bool PlainDateTime_add(JSContext* cx, const CallArgs& args) {
2065 // Step 3.
2066 return AddDurationToOrSubtractDurationFromPlainDateTime(
2067 cx, PlainDateTimeDuration::Add, args);
2071 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2073 static bool PlainDateTime_add(JSContext* cx, unsigned argc, Value* vp) {
2074 // Steps 1-2.
2075 CallArgs args = CallArgsFromVp(argc, vp);
2076 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_add>(cx, args);
2080 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2081 * ] )
2083 static bool PlainDateTime_subtract(JSContext* cx, const CallArgs& args) {
2084 // Step 3.
2085 return AddDurationToOrSubtractDurationFromPlainDateTime(
2086 cx, PlainDateTimeDuration::Subtract, args);
2090 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2091 * ] )
2093 static bool PlainDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) {
2094 // Steps 1-2.
2095 CallArgs args = CallArgsFromVp(argc, vp);
2096 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_subtract>(cx,
2097 args);
2101 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2103 static bool PlainDateTime_until(JSContext* cx, const CallArgs& args) {
2104 // Step 3.
2105 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Until, args);
2109 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2111 static bool PlainDateTime_until(JSContext* cx, unsigned argc, Value* vp) {
2112 // Steps 1-2.
2113 CallArgs args = CallArgsFromVp(argc, vp);
2114 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_until>(cx, args);
2118 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2120 static bool PlainDateTime_since(JSContext* cx, const CallArgs& args) {
2121 // Step 3.
2122 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Since, args);
2126 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2128 static bool PlainDateTime_since(JSContext* cx, unsigned argc, Value* vp) {
2129 // Steps 1-2.
2130 CallArgs args = CallArgsFromVp(argc, vp);
2131 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_since>(cx, args);
2135 * Temporal.PlainDateTime.prototype.round ( roundTo )
2137 static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
2138 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2139 auto dateTime = ToPlainDateTime(temporalDateTime);
2140 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2142 // Steps 3-12.
2143 auto smallestUnit = TemporalUnit::Auto;
2144 auto roundingMode = TemporalRoundingMode::HalfExpand;
2145 auto roundingIncrement = Increment{1};
2146 if (args.get(0).isString()) {
2147 // Step 4. (Not applicable in our implementation.)
2149 // Step 9.
2150 Rooted<JSString*> paramString(cx, args[0].toString());
2151 if (!GetTemporalUnit(cx, paramString, TemporalUnitKey::SmallestUnit,
2152 TemporalUnitGroup::DayTime, &smallestUnit)) {
2153 return false;
2156 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
2157 smallestUnit <= TemporalUnit::Nanosecond);
2159 // Steps 6-8 and 10-12. (Implicit)
2160 } else {
2161 // Steps 3 and 5.
2162 Rooted<JSObject*> roundTo(
2163 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
2164 if (!roundTo) {
2165 return false;
2168 // Steps 6-7.
2169 if (!ToTemporalRoundingIncrement(cx, roundTo, &roundingIncrement)) {
2170 return false;
2173 // Step 8.
2174 if (!ToTemporalRoundingMode(cx, roundTo, &roundingMode)) {
2175 return false;
2178 // Step 9.
2179 if (!GetTemporalUnit(cx, roundTo, TemporalUnitKey::SmallestUnit,
2180 TemporalUnitGroup::DayTime, &smallestUnit)) {
2181 return false;
2184 if (smallestUnit == TemporalUnit::Auto) {
2185 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2186 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
2187 return false;
2190 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
2191 smallestUnit <= TemporalUnit::Nanosecond);
2193 // Steps 10-11.
2194 auto maximum = Increment{1};
2195 bool inclusive = true;
2196 if (smallestUnit > TemporalUnit::Day) {
2197 maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
2198 inclusive = false;
2201 // Step 12.
2202 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
2203 inclusive)) {
2204 return false;
2208 // Step 13.
2209 if (smallestUnit == TemporalUnit::Nanosecond &&
2210 roundingIncrement == Increment{1}) {
2211 auto* obj = CreateTemporalDateTime(cx, dateTime, calendar);
2212 if (!obj) {
2213 return false;
2216 args.rval().setObject(*obj);
2217 return true;
2220 // Step 14.
2221 auto result =
2222 RoundISODateTime(dateTime, roundingIncrement, smallestUnit, roundingMode);
2224 // Step 15.
2225 auto* obj = CreateTemporalDateTime(cx, result, calendar);
2226 if (!obj) {
2227 return false;
2230 args.rval().setObject(*obj);
2231 return true;
2235 * Temporal.PlainDateTime.prototype.round ( roundTo )
2237 static bool PlainDateTime_round(JSContext* cx, unsigned argc, Value* vp) {
2238 // Steps 1-2.
2239 CallArgs args = CallArgsFromVp(argc, vp);
2240 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_round>(cx, args);
2244 * Temporal.PlainDateTime.prototype.equals ( other )
2246 static bool PlainDateTime_equals(JSContext* cx, const CallArgs& args) {
2247 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2248 auto dateTime = ToPlainDateTime(temporalDateTime);
2249 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2251 // Step 3.
2252 Rooted<PlainDateTimeWithCalendar> other(cx);
2253 if (!::ToTemporalDateTime(cx, args.get(0), &other)) {
2254 return false;
2257 // Steps 4-13.
2258 bool equals = dateTime == other.dateTime();
2259 if (equals && !CalendarEquals(cx, calendar, other.calendar(), &equals)) {
2260 return false;
2263 args.rval().setBoolean(equals);
2264 return true;
2268 * Temporal.PlainDateTime.prototype.equals ( other )
2270 static bool PlainDateTime_equals(JSContext* cx, unsigned argc, Value* vp) {
2271 // Steps 1-2.
2272 CallArgs args = CallArgsFromVp(argc, vp);
2273 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_equals>(cx, args);
2277 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2279 static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) {
2280 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2281 auto dt = ToPlainDateTime(dateTime);
2282 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2284 SecondsStringPrecision precision = {Precision::Auto(),
2285 TemporalUnit::Nanosecond, Increment{1}};
2286 auto roundingMode = TemporalRoundingMode::Trunc;
2287 auto showCalendar = CalendarOption::Auto;
2288 if (args.hasDefined(0)) {
2289 // Step 3.
2290 Rooted<JSObject*> options(
2291 cx, RequireObjectArg(cx, "options", "toString", args[0]));
2292 if (!options) {
2293 return false;
2296 // Steps 4-5.
2297 if (!ToCalendarNameOption(cx, options, &showCalendar)) {
2298 return false;
2301 // Step 6.
2302 auto digits = Precision::Auto();
2303 if (!ToFractionalSecondDigits(cx, options, &digits)) {
2304 return false;
2307 // Step 7.
2308 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
2309 return false;
2312 // Step 8.
2313 auto smallestUnit = TemporalUnit::Auto;
2314 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
2315 TemporalUnitGroup::Time, &smallestUnit)) {
2316 return false;
2319 // Step 9.
2320 if (smallestUnit == TemporalUnit::Hour) {
2321 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2322 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
2323 "smallestUnit");
2324 return false;
2327 // Step 10.
2328 precision = ToSecondsStringPrecision(smallestUnit, digits);
2331 // Step 11.
2332 auto result =
2333 RoundISODateTime(dt, precision.increment, precision.unit, roundingMode);
2335 // Step 12.
2336 JSString* str = ::TemporalDateTimeToString(cx, result, calendar,
2337 precision.precision, showCalendar);
2338 if (!str) {
2339 return false;
2342 args.rval().setString(str);
2343 return true;
2347 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2349 static bool PlainDateTime_toString(JSContext* cx, unsigned argc, Value* vp) {
2350 // Steps 1-2.
2351 CallArgs args = CallArgsFromVp(argc, vp);
2352 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toString>(cx,
2353 args);
2357 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2359 static bool PlainDateTime_toLocaleString(JSContext* cx, const CallArgs& args) {
2360 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2361 auto dt = ToPlainDateTime(dateTime);
2362 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2364 // Step 3.
2365 JSString* str = ::TemporalDateTimeToString(
2366 cx, dt, calendar, Precision::Auto(), CalendarOption::Auto);
2367 if (!str) {
2368 return false;
2371 args.rval().setString(str);
2372 return true;
2376 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2378 static bool PlainDateTime_toLocaleString(JSContext* cx, unsigned argc,
2379 Value* vp) {
2380 // Steps 1-2.
2381 CallArgs args = CallArgsFromVp(argc, vp);
2382 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toLocaleString>(
2383 cx, args);
2387 * Temporal.PlainDateTime.prototype.toJSON ( )
2389 static bool PlainDateTime_toJSON(JSContext* cx, const CallArgs& args) {
2390 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2391 auto dt = ToPlainDateTime(dateTime);
2392 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2394 // Step 3.
2395 JSString* str = ::TemporalDateTimeToString(
2396 cx, dt, calendar, Precision::Auto(), CalendarOption::Auto);
2397 if (!str) {
2398 return false;
2401 args.rval().setString(str);
2402 return true;
2406 * Temporal.PlainDateTime.prototype.toJSON ( )
2408 static bool PlainDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) {
2409 // Steps 1-2.
2410 CallArgs args = CallArgsFromVp(argc, vp);
2411 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toJSON>(cx, args);
2415 * Temporal.PlainDateTime.prototype.valueOf ( )
2417 static bool PlainDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) {
2418 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
2419 "PlainDateTime", "primitive type");
2420 return false;
2424 * Temporal.PlainDateTime.prototype.getISOFields ( )
2426 static bool PlainDateTime_getISOFields(JSContext* cx, const CallArgs& args) {
2427 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2428 auto dateTime = ToPlainDateTime(temporalDateTime);
2429 auto calendar = temporalDateTime->calendar();
2431 // Step 3.
2432 Rooted<IdValueVector> fields(cx, IdValueVector(cx));
2434 // Step 4.
2435 if (!fields.emplaceBack(NameToId(cx->names().calendar), calendar.toValue())) {
2436 return false;
2439 // Step 5.
2440 if (!fields.emplaceBack(NameToId(cx->names().isoDay),
2441 Int32Value(dateTime.date.day))) {
2442 return false;
2445 // Step 6.
2446 if (!fields.emplaceBack(NameToId(cx->names().isoHour),
2447 Int32Value(dateTime.time.hour))) {
2448 return false;
2451 // Step 7.
2452 if (!fields.emplaceBack(NameToId(cx->names().isoMicrosecond),
2453 Int32Value(dateTime.time.microsecond))) {
2454 return false;
2457 // Step 8.
2458 if (!fields.emplaceBack(NameToId(cx->names().isoMillisecond),
2459 Int32Value(dateTime.time.millisecond))) {
2460 return false;
2463 // Step 9.
2464 if (!fields.emplaceBack(NameToId(cx->names().isoMinute),
2465 Int32Value(dateTime.time.minute))) {
2466 return false;
2469 // Step 10.
2470 if (!fields.emplaceBack(NameToId(cx->names().isoMonth),
2471 Int32Value(dateTime.date.month))) {
2472 return false;
2475 // Step 11.
2476 if (!fields.emplaceBack(NameToId(cx->names().isoNanosecond),
2477 Int32Value(dateTime.time.nanosecond))) {
2478 return false;
2481 // Step 12.
2482 if (!fields.emplaceBack(NameToId(cx->names().isoSecond),
2483 Int32Value(dateTime.time.second))) {
2484 return false;
2487 // Step 13.
2488 if (!fields.emplaceBack(NameToId(cx->names().isoYear),
2489 Int32Value(dateTime.date.year))) {
2490 return false;
2493 // Step 14.
2494 auto* obj = NewPlainObjectWithUniqueNames(cx, fields);
2495 if (!obj) {
2496 return false;
2499 args.rval().setObject(*obj);
2500 return true;
2504 * Temporal.PlainDateTime.prototype.getISOFields ( )
2506 static bool PlainDateTime_getISOFields(JSContext* cx, unsigned argc,
2507 Value* vp) {
2508 // Steps 1-2.
2509 CallArgs args = CallArgsFromVp(argc, vp);
2510 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_getISOFields>(
2511 cx, args);
2515 * Temporal.PlainDateTime.prototype.getCalendar ( )
2517 static bool PlainDateTime_getCalendar(JSContext* cx, const CallArgs& args) {
2518 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2519 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2521 // Step 3.
2522 auto* obj = ToTemporalCalendarObject(cx, calendar);
2523 if (!obj) {
2524 return false;
2527 args.rval().setObject(*obj);
2528 return true;
2532 * Temporal.PlainDateTime.prototype.getCalendar ( )
2534 static bool PlainDateTime_getCalendar(JSContext* cx, unsigned argc, Value* vp) {
2535 // Steps 1-2.
2536 CallArgs args = CallArgsFromVp(argc, vp);
2537 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_getCalendar>(cx,
2538 args);
2542 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2543 * options ] )
2545 static bool PlainDateTime_toZonedDateTime(JSContext* cx, const CallArgs& args) {
2546 Rooted<PlainDateTimeObject*> dateTime(
2547 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2548 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2550 // Step 3.
2551 Rooted<TimeZoneValue> timeZone(cx);
2552 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
2553 return false;
2556 auto disambiguation = TemporalDisambiguation::Compatible;
2557 if (args.hasDefined(1)) {
2558 // Step 4.
2559 Rooted<JSObject*> options(
2560 cx, RequireObjectArg(cx, "options", "toZonedDateTime", args[1]));
2561 if (!options) {
2562 return false;
2565 // Step 5.
2566 if (!ToTemporalDisambiguation(cx, options, &disambiguation)) {
2567 return false;
2571 // Steps 6-7.
2572 Instant instant;
2573 if (!GetInstantFor(cx, timeZone, dateTime, disambiguation, &instant)) {
2574 return false;
2577 // Step 8.
2578 auto* result = CreateTemporalZonedDateTime(cx, instant, timeZone, calendar);
2579 if (!result) {
2580 return false;
2583 args.rval().setObject(*result);
2584 return true;
2588 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2589 * options ] )
2591 static bool PlainDateTime_toZonedDateTime(JSContext* cx, unsigned argc,
2592 Value* vp) {
2593 // Steps 1-2.
2594 CallArgs args = CallArgsFromVp(argc, vp);
2595 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toZonedDateTime>(
2596 cx, args);
2600 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2602 static bool PlainDateTime_toPlainDate(JSContext* cx, const CallArgs& args) {
2603 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2604 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2606 // Step 3.
2607 auto* obj = CreateTemporalDate(cx, ToPlainDate(dateTime), calendar);
2608 if (!obj) {
2609 return false;
2612 args.rval().setObject(*obj);
2613 return true;
2617 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2619 static bool PlainDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
2620 // Steps 1-2.
2621 CallArgs args = CallArgsFromVp(argc, vp);
2622 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainDate>(cx,
2623 args);
2627 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2629 static bool PlainDateTime_toPlainYearMonth(JSContext* cx,
2630 const CallArgs& args) {
2631 Rooted<PlainDateTimeObject*> dateTime(
2632 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2633 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
2635 // Step 3.
2636 Rooted<CalendarRecord> calendar(cx);
2637 if (!CreateCalendarMethodsRecord(cx, calendarValue,
2639 CalendarMethod::Fields,
2640 CalendarMethod::YearMonthFromFields,
2642 &calendar)) {
2643 return false;
2646 // Step 4.
2647 JS::RootedVector<PropertyKey> fieldNames(cx);
2648 if (!CalendarFields(cx, calendar,
2649 {CalendarField::MonthCode, CalendarField::Year},
2650 &fieldNames)) {
2651 return false;
2654 // Step 4.
2655 Rooted<PlainObject*> fields(cx,
2656 PrepareTemporalFields(cx, dateTime, fieldNames));
2657 if (!fields) {
2658 return false;
2661 // Step 5.
2662 auto obj = CalendarYearMonthFromFields(cx, calendar, fields);
2663 if (!obj) {
2664 return false;
2667 args.rval().setObject(*obj);
2668 return true;
2672 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2674 static bool PlainDateTime_toPlainYearMonth(JSContext* cx, unsigned argc,
2675 Value* vp) {
2676 // Steps 1-2.
2677 CallArgs args = CallArgsFromVp(argc, vp);
2678 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainYearMonth>(
2679 cx, args);
2683 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2685 static bool PlainDateTime_toPlainMonthDay(JSContext* cx, const CallArgs& args) {
2686 Rooted<PlainDateTimeObject*> dateTime(
2687 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2688 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
2690 // Step 3.
2691 Rooted<CalendarRecord> calendar(cx);
2692 if (!CreateCalendarMethodsRecord(cx, calendarValue,
2694 CalendarMethod::Fields,
2695 CalendarMethod::MonthDayFromFields,
2697 &calendar)) {
2698 return false;
2701 // Step 4.
2702 JS::RootedVector<PropertyKey> fieldNames(cx);
2703 if (!CalendarFields(cx, calendar,
2704 {CalendarField::Day, CalendarField::MonthCode},
2705 &fieldNames)) {
2706 return false;
2709 // Step 5.
2710 Rooted<PlainObject*> fields(cx,
2711 PrepareTemporalFields(cx, dateTime, fieldNames));
2712 if (!fields) {
2713 return false;
2716 // Steps 6-7.
2717 auto obj = CalendarMonthDayFromFields(cx, calendar, fields);
2718 if (!obj) {
2719 return false;
2722 args.rval().setObject(*obj);
2723 return true;
2727 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2729 static bool PlainDateTime_toPlainMonthDay(JSContext* cx, unsigned argc,
2730 Value* vp) {
2731 // Steps 1-2.
2732 CallArgs args = CallArgsFromVp(argc, vp);
2733 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainMonthDay>(
2734 cx, args);
2738 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2740 static bool PlainDateTime_toPlainTime(JSContext* cx, const CallArgs& args) {
2741 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2743 // Step 3.
2744 auto* obj = CreateTemporalTime(cx, ToPlainTime(dateTime));
2745 if (!obj) {
2746 return false;
2749 args.rval().setObject(*obj);
2750 return true;
2754 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2756 static bool PlainDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) {
2757 // Steps 1-2.
2758 CallArgs args = CallArgsFromVp(argc, vp);
2759 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainTime>(cx,
2760 args);
2763 const JSClass PlainDateTimeObject::class_ = {
2764 "Temporal.PlainDateTime",
2765 JSCLASS_HAS_RESERVED_SLOTS(PlainDateTimeObject::SLOT_COUNT) |
2766 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainDateTime),
2767 JS_NULL_CLASS_OPS,
2768 &PlainDateTimeObject::classSpec_,
2771 const JSClass& PlainDateTimeObject::protoClass_ = PlainObject::class_;
2773 static const JSFunctionSpec PlainDateTime_methods[] = {
2774 JS_FN("from", PlainDateTime_from, 1, 0),
2775 JS_FN("compare", PlainDateTime_compare, 2, 0),
2776 JS_FS_END,
2779 static const JSFunctionSpec PlainDateTime_prototype_methods[] = {
2780 JS_FN("with", PlainDateTime_with, 1, 0),
2781 JS_FN("withPlainTime", PlainDateTime_withPlainTime, 0, 0),
2782 JS_FN("withPlainDate", PlainDateTime_withPlainDate, 1, 0),
2783 JS_FN("withCalendar", PlainDateTime_withCalendar, 1, 0),
2784 JS_FN("add", PlainDateTime_add, 1, 0),
2785 JS_FN("subtract", PlainDateTime_subtract, 1, 0),
2786 JS_FN("until", PlainDateTime_until, 1, 0),
2787 JS_FN("since", PlainDateTime_since, 1, 0),
2788 JS_FN("round", PlainDateTime_round, 1, 0),
2789 JS_FN("equals", PlainDateTime_equals, 1, 0),
2790 JS_FN("toString", PlainDateTime_toString, 0, 0),
2791 JS_FN("toLocaleString", PlainDateTime_toLocaleString, 0, 0),
2792 JS_FN("toJSON", PlainDateTime_toJSON, 0, 0),
2793 JS_FN("valueOf", PlainDateTime_valueOf, 0, 0),
2794 JS_FN("toZonedDateTime", PlainDateTime_toZonedDateTime, 1, 0),
2795 JS_FN("toPlainDate", PlainDateTime_toPlainDate, 0, 0),
2796 JS_FN("toPlainYearMonth", PlainDateTime_toPlainYearMonth, 0, 0),
2797 JS_FN("toPlainMonthDay", PlainDateTime_toPlainMonthDay, 0, 0),
2798 JS_FN("toPlainTime", PlainDateTime_toPlainTime, 0, 0),
2799 JS_FN("getISOFields", PlainDateTime_getISOFields, 0, 0),
2800 JS_FN("getCalendar", PlainDateTime_getCalendar, 0, 0),
2801 JS_FS_END,
2804 static const JSPropertySpec PlainDateTime_prototype_properties[] = {
2805 JS_PSG("calendarId", PlainDateTime_calendarId, 0),
2806 JS_PSG("year", PlainDateTime_year, 0),
2807 JS_PSG("month", PlainDateTime_month, 0),
2808 JS_PSG("monthCode", PlainDateTime_monthCode, 0),
2809 JS_PSG("day", PlainDateTime_day, 0),
2810 JS_PSG("hour", PlainDateTime_hour, 0),
2811 JS_PSG("minute", PlainDateTime_minute, 0),
2812 JS_PSG("second", PlainDateTime_second, 0),
2813 JS_PSG("millisecond", PlainDateTime_millisecond, 0),
2814 JS_PSG("microsecond", PlainDateTime_microsecond, 0),
2815 JS_PSG("nanosecond", PlainDateTime_nanosecond, 0),
2816 JS_PSG("dayOfWeek", PlainDateTime_dayOfWeek, 0),
2817 JS_PSG("dayOfYear", PlainDateTime_dayOfYear, 0),
2818 JS_PSG("weekOfYear", PlainDateTime_weekOfYear, 0),
2819 JS_PSG("yearOfWeek", PlainDateTime_yearOfWeek, 0),
2820 JS_PSG("daysInWeek", PlainDateTime_daysInWeek, 0),
2821 JS_PSG("daysInMonth", PlainDateTime_daysInMonth, 0),
2822 JS_PSG("daysInYear", PlainDateTime_daysInYear, 0),
2823 JS_PSG("monthsInYear", PlainDateTime_monthsInYear, 0),
2824 JS_PSG("inLeapYear", PlainDateTime_inLeapYear, 0),
2825 JS_STRING_SYM_PS(toStringTag, "Temporal.PlainDateTime", JSPROP_READONLY),
2826 JS_PS_END,
2829 const ClassSpec PlainDateTimeObject::classSpec_ = {
2830 GenericCreateConstructor<PlainDateTimeConstructor, 3,
2831 gc::AllocKind::FUNCTION>,
2832 GenericCreatePrototype<PlainDateTimeObject>,
2833 PlainDateTime_methods,
2834 nullptr,
2835 PlainDateTime_prototype_methods,
2836 PlainDateTime_prototype_properties,
2837 nullptr,
2838 ClassSpec::DontDefineConstructor,