Bug 1874684 - Part 30: Make BalanceTimeDuration a fallible operation. r=mgaudet
[gecko.git] / js / src / builtin / temporal / PlainDateTime.cpp
blob276ab148df70daf74847569c2dbc6a98429f419f
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 // Definitely in range.
182 if (minYear < year && year < maxYear) {
183 return true;
186 // -271821 April, 20
187 if (year < 0) {
188 if (year != minYear) {
189 return false;
191 if (month != 4) {
192 return month > 4;
194 if (day < (20 - 1)) {
195 return false;
197 return true;
200 // 275760 September, 13
201 if (year != maxYear) {
202 return false;
204 if (month != 9) {
205 return month < 9;
207 if (day > 13) {
208 return false;
210 return true;
214 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
215 * millisecond, microsecond, nanosecond )
217 bool js::temporal::ISODateTimeWithinLimits(double year, double month,
218 double day) {
219 return ::ISODateTimeWithinLimits(year, month, day);
223 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
224 * millisecond, microsecond, nanosecond )
226 bool js::temporal::ISODateTimeWithinLimits(const PlainDateTime& dateTime) {
227 const auto& [date, time] = dateTime;
228 return ::ISODateTimeWithinLimits(date.year, date.month, date.day, time.hour,
229 time.minute, time.second, time.millisecond,
230 time.microsecond, time.nanosecond);
234 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
235 * millisecond, microsecond, nanosecond )
237 bool js::temporal::ISODateTimeWithinLimits(const PlainDate& date) {
238 return ::ISODateTimeWithinLimits(date.year, date.month, date.day);
242 * CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second,
243 * millisecond, microsecond, nanosecond, calendar [ , newTarget ] )
245 static PlainDateTimeObject* CreateTemporalDateTime(
246 JSContext* cx, const CallArgs& args, double isoYear, double isoMonth,
247 double isoDay, double hour, double minute, double second,
248 double millisecond, double microsecond, double nanosecond,
249 Handle<CalendarValue> calendar) {
250 MOZ_ASSERT(IsInteger(isoYear));
251 MOZ_ASSERT(IsInteger(isoMonth));
252 MOZ_ASSERT(IsInteger(isoDay));
253 MOZ_ASSERT(IsInteger(hour));
254 MOZ_ASSERT(IsInteger(minute));
255 MOZ_ASSERT(IsInteger(second));
256 MOZ_ASSERT(IsInteger(millisecond));
257 MOZ_ASSERT(IsInteger(microsecond));
258 MOZ_ASSERT(IsInteger(nanosecond));
260 // Step 1.
261 if (!ThrowIfInvalidISODate(cx, isoYear, isoMonth, isoDay)) {
262 return nullptr;
265 // Step 2.
266 if (!ThrowIfInvalidTime(cx, hour, minute, second, millisecond, microsecond,
267 nanosecond)) {
268 return nullptr;
271 // Step 3.
272 if (!ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, hour, minute, second,
273 millisecond, microsecond, nanosecond)) {
274 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
275 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
276 return nullptr;
279 // Steps 4-5.
280 Rooted<JSObject*> proto(cx);
281 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainDateTime,
282 &proto)) {
283 return nullptr;
286 auto* dateTime = NewObjectWithClassProto<PlainDateTimeObject>(cx, proto);
287 if (!dateTime) {
288 return nullptr;
291 // Step 6.
292 dateTime->setFixedSlot(PlainDateTimeObject::ISO_YEAR_SLOT,
293 Int32Value(int32_t(isoYear)));
295 // Step 7.
296 dateTime->setFixedSlot(PlainDateTimeObject::ISO_MONTH_SLOT,
297 Int32Value(int32_t(isoMonth)));
299 // Step 8.
300 dateTime->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT,
301 Int32Value(int32_t(isoDay)));
303 // Step 9.
304 dateTime->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT,
305 Int32Value(int32_t(hour)));
307 // Step 10.
308 dateTime->setFixedSlot(PlainDateTimeObject::ISO_MINUTE_SLOT,
309 Int32Value(int32_t(minute)));
311 // Step 11.
312 dateTime->setFixedSlot(PlainDateTimeObject::ISO_SECOND_SLOT,
313 Int32Value(int32_t(second)));
315 // Step 12.
316 dateTime->setFixedSlot(PlainDateTimeObject::ISO_MILLISECOND_SLOT,
317 Int32Value(int32_t(millisecond)));
319 // Step 13.
320 dateTime->setFixedSlot(PlainDateTimeObject::ISO_MICROSECOND_SLOT,
321 Int32Value(int32_t(microsecond)));
323 // Step 14.
324 dateTime->setFixedSlot(PlainDateTimeObject::ISO_NANOSECOND_SLOT,
325 Int32Value(int32_t(nanosecond)));
327 // Step 15.
328 dateTime->setFixedSlot(PlainDateTimeObject::CALENDAR_SLOT,
329 calendar.toValue());
331 // Step 16.
332 return dateTime;
336 * CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second,
337 * millisecond, microsecond, nanosecond, calendar [ , newTarget ] )
339 PlainDateTimeObject* js::temporal::CreateTemporalDateTime(
340 JSContext* cx, const PlainDateTime& dateTime,
341 Handle<CalendarValue> calendar) {
342 const auto& [date, time] = dateTime;
343 const auto& [isoYear, isoMonth, isoDay] = date;
344 const auto& [hour, minute, second, millisecond, microsecond, nanosecond] =
345 time;
347 // Steps 1-2.
348 if (!ThrowIfInvalidISODateTime(cx, dateTime)) {
349 return nullptr;
352 // Step 3.
353 if (!ISODateTimeWithinLimits(dateTime)) {
354 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
355 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
356 return nullptr;
359 // Steps 4-5.
360 auto* object = NewBuiltinClassInstance<PlainDateTimeObject>(cx);
361 if (!object) {
362 return nullptr;
365 // Step 6.
366 object->setFixedSlot(PlainDateTimeObject::ISO_YEAR_SLOT, Int32Value(isoYear));
368 // Step 7.
369 object->setFixedSlot(PlainDateTimeObject::ISO_MONTH_SLOT,
370 Int32Value(isoMonth));
372 // Step 8.
373 object->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT, Int32Value(isoDay));
375 // Step 9.
376 object->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT, Int32Value(hour));
378 // Step 10.
379 object->setFixedSlot(PlainDateTimeObject::ISO_MINUTE_SLOT,
380 Int32Value(minute));
382 // Step 11.
383 object->setFixedSlot(PlainDateTimeObject::ISO_SECOND_SLOT,
384 Int32Value(second));
386 // Step 12.
387 object->setFixedSlot(PlainDateTimeObject::ISO_MILLISECOND_SLOT,
388 Int32Value(millisecond));
390 // Step 13.
391 object->setFixedSlot(PlainDateTimeObject::ISO_MICROSECOND_SLOT,
392 Int32Value(microsecond));
394 // Step 14.
395 object->setFixedSlot(PlainDateTimeObject::ISO_NANOSECOND_SLOT,
396 Int32Value(nanosecond));
398 // Step 15.
399 object->setFixedSlot(PlainDateTimeObject::CALENDAR_SLOT, calendar.toValue());
401 // Step 16.
402 return object;
406 * CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second,
407 * millisecond, microsecond, nanosecond, calendar [ , newTarget ] )
409 bool js::temporal::CreateTemporalDateTime(
410 JSContext* cx, const PlainDateTime& dateTime,
411 Handle<CalendarValue> calendar,
412 MutableHandle<PlainDateTimeWithCalendar> result) {
413 // Steps 1-2.
414 if (!ThrowIfInvalidISODateTime(cx, dateTime)) {
415 return false;
418 // Step 3.
419 if (!ISODateTimeWithinLimits(dateTime)) {
420 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
421 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
422 return false;
425 result.set(PlainDateTimeWithCalendar{dateTime, calendar});
426 return true;
430 * InterpretTemporalDateTimeFields ( calendarRec, fields, options )
432 bool js::temporal::InterpretTemporalDateTimeFields(
433 JSContext* cx, Handle<CalendarRecord> calendar, Handle<PlainObject*> fields,
434 Handle<PlainObject*> options, PlainDateTime* result) {
435 // Step 1. (Not applicable in our implementation.)
437 // Step 2.
438 MOZ_ASSERT(CalendarMethodsRecordHasLookedUp(calendar,
439 CalendarMethod::DateFromFields));
441 // Step 3.
442 TemporalTimeLike timeResult;
443 if (!ToTemporalTimeRecord(cx, fields, &timeResult)) {
444 return false;
447 // Step 4.
448 auto overflow = TemporalOverflow::Constrain;
449 if (!ToTemporalOverflow(cx, options, &overflow)) {
450 return false;
453 // Steps 5-6.
454 Rooted<Value> overflowValue(cx);
455 if (overflow == TemporalOverflow::Constrain) {
456 overflowValue.setString(cx->names().constrain);
457 } else {
458 MOZ_ASSERT(overflow == TemporalOverflow::Reject);
459 overflowValue.setString(cx->names().reject);
461 if (!DefineDataProperty(cx, options, cx->names().overflow, overflowValue)) {
462 return false;
465 // Step 7.
466 auto temporalDate =
467 js::temporal::CalendarDateFromFields(cx, calendar, fields, options);
468 if (!temporalDate) {
469 return false;
471 auto date = ToPlainDate(&temporalDate.unwrap());
473 // Step 8.
474 PlainTime time;
475 if (!RegulateTime(cx, timeResult, overflow, &time)) {
476 return false;
479 // Step 9.
480 *result = {date, time};
481 return true;
485 * InterpretTemporalDateTimeFields ( calendarRec, fields, options )
487 bool js::temporal::InterpretTemporalDateTimeFields(
488 JSContext* cx, Handle<CalendarRecord> calendar, Handle<PlainObject*> fields,
489 PlainDateTime* result) {
490 // TODO: Avoid creating the options object when CalendarDateFromFields calls
491 // the built-in Calendar.prototype.dateFromFields method.
492 Rooted<PlainObject*> options(cx, NewPlainObjectWithProto(cx, nullptr));
493 if (!options) {
494 return false;
497 return InterpretTemporalDateTimeFields(cx, calendar, fields, options, result);
501 * ToTemporalDateTime ( item [ , options ] )
503 static Wrapped<PlainDateTimeObject*> ToTemporalDateTime(
504 JSContext* cx, Handle<Value> item, Handle<JSObject*> maybeOptions) {
505 // Step 1. (Not applicable)
507 // Step 2.
508 Rooted<PlainObject*> maybeResolvedOptions(cx);
509 if (maybeOptions) {
510 maybeResolvedOptions = SnapshotOwnProperties(cx, maybeOptions);
511 if (!maybeResolvedOptions) {
512 return nullptr;
516 // Steps 3-4.
517 Rooted<CalendarValue> calendar(cx);
518 PlainDateTime result;
519 if (item.isObject()) {
520 Rooted<JSObject*> itemObj(cx, &item.toObject());
522 // Step 3.a.
523 if (itemObj->canUnwrapAs<PlainDateTimeObject>()) {
524 return itemObj;
527 // Step 3.b.
528 if (auto* zonedDateTime = itemObj->maybeUnwrapIf<ZonedDateTimeObject>()) {
529 auto epochInstant = ToInstant(zonedDateTime);
530 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
531 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
533 if (!timeZone.wrap(cx)) {
534 return nullptr;
536 if (!calendar.wrap(cx)) {
537 return nullptr;
540 // Step 3.b.i.
541 if (maybeResolvedOptions) {
542 TemporalOverflow ignored;
543 if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
544 return nullptr;
548 // Steps 3.b.ii-iv.
549 return GetPlainDateTimeFor(cx, timeZone, epochInstant, calendar);
552 // Step 3.c.
553 if (auto* date = itemObj->maybeUnwrapIf<PlainDateObject>()) {
554 PlainDateTime dateTime = {ToPlainDate(date), {}};
555 Rooted<CalendarValue> calendar(cx, date->calendar());
556 if (!calendar.wrap(cx)) {
557 return nullptr;
560 // Step 3.c.i.
561 if (maybeResolvedOptions) {
562 TemporalOverflow ignored;
563 if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
564 return nullptr;
568 // Step 3.c.ii.
569 return CreateTemporalDateTime(cx, dateTime, calendar);
572 // Step 3.d.
573 if (!GetTemporalCalendarWithISODefault(cx, itemObj, &calendar)) {
574 return nullptr;
577 // Step 3.e.
578 Rooted<CalendarRecord> calendarRec(cx);
579 if (!CreateCalendarMethodsRecord(cx, calendar,
581 CalendarMethod::DateFromFields,
582 CalendarMethod::Fields,
584 &calendarRec)) {
585 return nullptr;
588 // Step 3.f.
589 JS::RootedVector<PropertyKey> fieldNames(cx);
590 if (!CalendarFields(cx, calendarRec,
591 {CalendarField::Day, CalendarField::Month,
592 CalendarField::MonthCode, CalendarField::Year},
593 &fieldNames)) {
594 return nullptr;
597 // Step 3.g.
598 if (!AppendSorted(cx, fieldNames.get(),
600 TemporalField::Hour,
601 TemporalField::Microsecond,
602 TemporalField::Millisecond,
603 TemporalField::Minute,
604 TemporalField::Nanosecond,
605 TemporalField::Second,
606 })) {
607 return nullptr;
610 // Step 3.h.
611 Rooted<PlainObject*> fields(cx,
612 PrepareTemporalFields(cx, itemObj, fieldNames));
613 if (!fields) {
614 return nullptr;
617 // Step 3.i.
618 if (maybeResolvedOptions) {
619 if (!InterpretTemporalDateTimeFields(cx, calendarRec, fields,
620 maybeResolvedOptions, &result)) {
621 return nullptr;
623 } else {
624 if (!InterpretTemporalDateTimeFields(cx, calendarRec, fields, &result)) {
625 return nullptr;
628 } else {
629 // Step 4.a.
630 if (!item.isString()) {
631 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
632 nullptr, "not a string");
633 return nullptr;
635 Rooted<JSString*> string(cx, item.toString());
637 // Step 4.b.
638 Rooted<JSString*> calendarString(cx);
639 if (!ParseTemporalDateTimeString(cx, string, &result, &calendarString)) {
640 return nullptr;
643 // Step 4.c.
644 MOZ_ASSERT(IsValidISODate(result.date));
646 // Step 4.d.
647 MOZ_ASSERT(IsValidTime(result.time));
649 // Steps 4.e-h.
650 if (calendarString) {
651 if (!ToBuiltinCalendar(cx, calendarString, &calendar)) {
652 return nullptr;
654 } else {
655 calendar.set(CalendarValue(cx->names().iso8601));
658 // Step 4.i.
659 if (maybeResolvedOptions) {
660 TemporalOverflow ignored;
661 if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
662 return nullptr;
667 // Step 5.
668 return CreateTemporalDateTime(cx, result, calendar);
672 * ToTemporalDateTime ( item [ , options ] )
674 Wrapped<PlainDateTimeObject*> js::temporal::ToTemporalDateTime(
675 JSContext* cx, Handle<Value> item) {
676 return ::ToTemporalDateTime(cx, item, nullptr);
680 * ToTemporalDateTime ( item [ , options ] )
682 bool js::temporal::ToTemporalDateTime(JSContext* cx, Handle<Value> item,
683 PlainDateTime* result) {
684 auto obj = ::ToTemporalDateTime(cx, item, nullptr);
685 if (!obj) {
686 return false;
689 *result = ToPlainDateTime(&obj.unwrap());
690 return true;
694 * ToTemporalDateTime ( item [ , options ] )
696 static bool ToTemporalDateTime(
697 JSContext* cx, Handle<Value> item,
698 MutableHandle<PlainDateTimeWithCalendar> result) {
699 Handle<JSObject*> options = nullptr;
701 auto* obj = ::ToTemporalDateTime(cx, item, options).unwrapOrNull();
702 if (!obj) {
703 return false;
706 auto dateTime = ToPlainDateTime(obj);
707 Rooted<CalendarValue> calendar(cx, obj->calendar());
708 if (!calendar.wrap(cx)) {
709 return false;
712 result.set(PlainDateTimeWithCalendar{dateTime, calendar});
713 return true;
717 * CompareISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
718 * d2, h2, min2, s2, ms2, mus2, ns2 )
720 static int32_t CompareISODateTime(const PlainDateTime& one,
721 const PlainDateTime& two) {
722 // Step 1. (Not applicable in our implementation.)
724 // Steps 2-3.
725 if (int32_t dateResult = CompareISODate(one.date, two.date)) {
726 return dateResult;
729 // Steps 4.
730 return CompareTemporalTime(one.time, two.time);
734 * AddDateTime ( year, month, day, hour, minute, second, millisecond,
735 * microsecond, nanosecond, calendarRec, years, months, weeks, days, norm,
736 * options )
738 static bool AddDateTime(JSContext* cx, const PlainDateTime& dateTime,
739 Handle<CalendarRecord> calendar,
740 const NormalizedDuration& duration,
741 Handle<JSObject*> options, PlainDateTime* result) {
742 MOZ_ASSERT(IsValidDuration(duration));
744 // Step 1.
745 MOZ_ASSERT(IsValidISODateTime(dateTime));
746 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
748 // Step 2.
749 auto timeResult = AddTime(dateTime.time, duration.time);
751 // Step 3.
752 const auto& datePart = dateTime.date;
754 // Step 4.
755 auto dateDuration = DateDuration{
756 duration.date.years,
757 duration.date.months,
758 duration.date.weeks,
759 duration.date.days + timeResult.days,
761 if (!ThrowIfInvalidDuration(cx, dateDuration)) {
762 return false;
765 // Step 5.
766 PlainDate addedDate;
767 if (!AddDate(cx, calendar, datePart, dateDuration, options, &addedDate)) {
768 return false;
771 // Step 6.
772 *result = {addedDate, timeResult.time};
773 return true;
777 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
778 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
780 static bool DifferenceISODateTime(JSContext* cx, const PlainDateTime& one,
781 const PlainDateTime& two,
782 Handle<CalendarRecord> calendar,
783 TemporalUnit largestUnit,
784 Handle<PlainObject*> maybeOptions,
785 NormalizedDuration* result) {
786 // Steps 1-2.
787 MOZ_ASSERT(IsValidISODateTime(one));
788 MOZ_ASSERT(IsValidISODateTime(two));
789 MOZ_ASSERT(ISODateTimeWithinLimits(one));
790 MOZ_ASSERT(ISODateTimeWithinLimits(two));
792 // Step 3.
793 MOZ_ASSERT_IF(
794 one.date != two.date && largestUnit < TemporalUnit::Day,
795 CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateUntil));
797 // Step 4.
798 auto timeDuration = DifferenceTime(one.time, two.time);
800 // Step 5.
801 int32_t timeSign = NormalizedTimeDurationSign(timeDuration);
803 // Step 6.
804 int32_t dateSign = CompareISODate(two.date, one.date);
806 // Step 7.
807 auto adjustedDate = one.date;
809 // Step 8.
810 if (timeSign == -dateSign) {
811 // Step 8.a.
812 adjustedDate = BalanceISODate(adjustedDate.year, adjustedDate.month,
813 adjustedDate.day - timeSign);
815 // Step 8.b.
816 if (!Add24HourDaysToNormalizedTimeDuration(cx, timeDuration, -timeSign,
817 &timeDuration)) {
818 return false;
822 MOZ_ASSERT(IsValidISODate(adjustedDate));
823 MOZ_ASSERT(ISODateTimeWithinLimits(adjustedDate));
825 // TODO: Avoid allocating CreateTemporalDate.
827 // Step 9.
828 Rooted<PlainDateObject*> date1(
829 cx, CreateTemporalDate(cx, adjustedDate, calendar.receiver()));
830 if (!date1) {
831 return false;
834 // Step 10.
835 Rooted<PlainDateObject*> date2(
836 cx, CreateTemporalDate(cx, two.date, calendar.receiver()));
837 if (!date2) {
838 return false;
841 // Step 11.
842 auto dateLargestUnit = std::min(TemporalUnit::Day, largestUnit);
844 DateDuration dateDifference;
845 if (maybeOptions) {
846 // Step 12.
848 // The spec performs an unnecessary copy operation. As an optimization, we
849 // omit this copy.
850 auto untilOptions = maybeOptions;
852 // Step 13.
853 Rooted<Value> largestUnitValue(
854 cx, StringValue(TemporalUnitToString(cx, dateLargestUnit)));
855 if (!DefineDataProperty(cx, untilOptions, cx->names().largestUnit,
856 largestUnitValue)) {
857 return false;
860 // Step 14.
861 if (!DifferenceDate(cx, calendar, date1, date2, untilOptions,
862 &dateDifference)) {
863 return false;
865 } else {
866 // Steps 12-14.
867 if (!DifferenceDate(cx, calendar, date1, date2, dateLargestUnit,
868 &dateDifference)) {
869 return false;
873 // Step 15.
874 return CreateNormalizedDurationRecord(cx, dateDifference, timeDuration,
875 result);
879 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
880 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
882 bool js::temporal::DifferenceISODateTime(JSContext* cx,
883 const PlainDateTime& one,
884 const PlainDateTime& two,
885 Handle<CalendarRecord> calendar,
886 TemporalUnit largestUnit,
887 DateDuration* result) {
888 NormalizedDuration normalized;
889 if (!::DifferenceISODateTime(cx, one, two, calendar, largestUnit, nullptr,
890 &normalized)) {
891 return false;
893 *result = normalized.date;
894 return true;
898 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
899 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
901 bool js::temporal::DifferenceISODateTime(
902 JSContext* cx, const PlainDateTime& one, const PlainDateTime& two,
903 Handle<CalendarRecord> calendar, TemporalUnit largestUnit,
904 Handle<PlainObject*> options, DateDuration* result) {
905 NormalizedDuration normalized;
906 if (!::DifferenceISODateTime(cx, one, two, calendar, largestUnit, options,
907 &normalized)) {
908 return false;
910 *result = normalized.date;
911 return true;
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 if (!BalanceTimeDuration(cx, withDays, settings.largestUnit,
1060 &balancedTime)) {
1061 return false;
1064 // Step 12.f.
1065 auto toBalance = DateDuration{
1066 roundResult.date.years,
1067 roundResult.date.months,
1068 roundResult.date.weeks,
1069 balancedTime.days,
1071 if (!temporal::BalanceDateDurationRelative(
1072 cx, toBalance, settings.largestUnit, settings.smallestUnit,
1073 relativeTo, calendar, &balancedDate)) {
1074 return false;
1076 } else {
1077 // Step 13.a.
1078 NormalizedTimeDuration withDays;
1079 if (!Add24HourDaysToNormalizedTimeDuration(cx, diff.time, diff.date.days,
1080 &withDays)) {
1081 return false;
1084 // Step 13.b.
1085 if (!BalanceTimeDuration(cx, withDays, settings.largestUnit,
1086 &balancedTime)) {
1087 return false;
1090 // Step 13.c.
1091 balancedDate = {
1092 diff.date.years,
1093 diff.date.months,
1094 diff.date.weeks,
1095 balancedTime.days,
1098 MOZ_ASSERT(IsValidDuration(balancedDate));
1100 // Step 14.
1101 Duration duration = {
1102 double(balancedDate.years), double(balancedDate.months),
1103 double(balancedDate.weeks), double(balancedDate.days),
1104 double(balancedTime.hours), double(balancedTime.minutes),
1105 double(balancedTime.seconds), double(balancedTime.milliseconds),
1106 balancedTime.microseconds, balancedTime.nanoseconds,
1108 if (operation == TemporalDifference::Since) {
1109 duration = duration.negate();
1112 auto* obj = CreateTemporalDuration(cx, duration);
1113 if (!obj) {
1114 return false;
1117 args.rval().setObject(*obj);
1118 return true;
1121 enum class PlainDateTimeDuration { Add, Subtract };
1124 * AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime,
1125 * temporalDurationLike, options )
1127 static bool AddDurationToOrSubtractDurationFromPlainDateTime(
1128 JSContext* cx, PlainDateTimeDuration operation, const CallArgs& args) {
1129 Rooted<PlainDateTimeWithCalendar> dateTime(
1130 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1132 // Step 1. (Not applicable in our implementation.)
1134 // Step 2.
1135 Duration duration;
1136 if (!ToTemporalDurationRecord(cx, args.get(0), &duration)) {
1137 return false;
1140 // Step 3.
1141 Rooted<JSObject*> options(cx);
1142 if (args.hasDefined(1)) {
1143 const char* name =
1144 operation == PlainDateTimeDuration::Add ? "add" : "subtract";
1145 options = RequireObjectArg(cx, "options", name, args[1]);
1146 } else {
1147 options = NewPlainObjectWithProto(cx, nullptr);
1149 if (!options) {
1150 return false;
1153 // Step 4.
1154 Rooted<CalendarRecord> calendar(cx);
1155 if (!CreateCalendarMethodsRecord(cx, dateTime.calendar(),
1157 CalendarMethod::DateAdd,
1159 &calendar)) {
1160 return false;
1163 // Step 5.
1164 if (operation == PlainDateTimeDuration::Subtract) {
1165 duration = duration.negate();
1167 auto normalized = CreateNormalizedDurationRecord(duration);
1169 // Step 6
1170 PlainDateTime result;
1171 if (!AddDateTime(cx, dateTime, calendar, normalized, options, &result)) {
1172 return false;
1175 // Steps 7-8.
1176 MOZ_ASSERT(IsValidISODateTime(result));
1178 // Step 9.
1179 auto* obj = CreateTemporalDateTime(cx, result, dateTime.calendar());
1180 if (!obj) {
1181 return false;
1184 args.rval().setObject(*obj);
1185 return true;
1189 * Temporal.PlainDateTime ( isoYear, isoMonth, isoDay [ , hour [ , minute [ ,
1190 * second [ , millisecond [ , microsecond [ , nanosecond [ , calendarLike ] ] ]
1191 * ] ] ] ] )
1193 static bool PlainDateTimeConstructor(JSContext* cx, unsigned argc, Value* vp) {
1194 CallArgs args = CallArgsFromVp(argc, vp);
1196 // Step 1.
1197 if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainDateTime")) {
1198 return false;
1201 // Step 2.
1202 double isoYear;
1203 if (!ToIntegerWithTruncation(cx, args.get(0), "year", &isoYear)) {
1204 return false;
1207 // Step 3.
1208 double isoMonth;
1209 if (!ToIntegerWithTruncation(cx, args.get(1), "month", &isoMonth)) {
1210 return false;
1213 // Step 4.
1214 double isoDay;
1215 if (!ToIntegerWithTruncation(cx, args.get(2), "day", &isoDay)) {
1216 return false;
1219 // Step 5.
1220 double hour = 0;
1221 if (args.hasDefined(3)) {
1222 if (!ToIntegerWithTruncation(cx, args[3], "hour", &hour)) {
1223 return false;
1227 // Step 6.
1228 double minute = 0;
1229 if (args.hasDefined(4)) {
1230 if (!ToIntegerWithTruncation(cx, args[4], "minute", &minute)) {
1231 return false;
1235 // Step 7.
1236 double second = 0;
1237 if (args.hasDefined(5)) {
1238 if (!ToIntegerWithTruncation(cx, args[5], "second", &second)) {
1239 return false;
1243 // Step 8.
1244 double millisecond = 0;
1245 if (args.hasDefined(6)) {
1246 if (!ToIntegerWithTruncation(cx, args[6], "millisecond", &millisecond)) {
1247 return false;
1251 // Step 9.
1252 double microsecond = 0;
1253 if (args.hasDefined(7)) {
1254 if (!ToIntegerWithTruncation(cx, args[7], "microsecond", &microsecond)) {
1255 return false;
1259 // Step 10.
1260 double nanosecond = 0;
1261 if (args.hasDefined(8)) {
1262 if (!ToIntegerWithTruncation(cx, args[8], "nanosecond", &nanosecond)) {
1263 return false;
1267 // Step 11.
1268 Rooted<CalendarValue> calendar(cx);
1269 if (!ToTemporalCalendarWithISODefault(cx, args.get(9), &calendar)) {
1270 return false;
1273 // Step 12.
1274 auto* temporalDateTime = CreateTemporalDateTime(
1275 cx, args, isoYear, isoMonth, isoDay, hour, minute, second, millisecond,
1276 microsecond, nanosecond, calendar);
1277 if (!temporalDateTime) {
1278 return false;
1281 args.rval().setObject(*temporalDateTime);
1282 return true;
1286 * Temporal.PlainDateTime.from ( item [ , options ] )
1288 static bool PlainDateTime_from(JSContext* cx, unsigned argc, Value* vp) {
1289 CallArgs args = CallArgsFromVp(argc, vp);
1291 // Step 1.
1292 Rooted<JSObject*> options(cx);
1293 if (args.hasDefined(1)) {
1294 options = RequireObjectArg(cx, "options", "from", args[1]);
1295 if (!options) {
1296 return false;
1300 // Step 2.
1301 if (args.get(0).isObject()) {
1302 JSObject* item = &args[0].toObject();
1303 if (auto* temporalDateTime = item->maybeUnwrapIf<PlainDateTimeObject>()) {
1304 auto dateTime = ToPlainDateTime(temporalDateTime);
1306 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1307 if (!calendar.wrap(cx)) {
1308 return false;
1311 if (options) {
1312 // Step 2.a.
1313 TemporalOverflow ignored;
1314 if (!ToTemporalOverflow(cx, options, &ignored)) {
1315 return false;
1319 // Step 2.b.
1320 auto* result = CreateTemporalDateTime(cx, dateTime, calendar);
1321 if (!result) {
1322 return false;
1325 args.rval().setObject(*result);
1326 return true;
1330 // Step 3.
1331 auto result = ToTemporalDateTime(cx, args.get(0), options);
1332 if (!result) {
1333 return false;
1336 args.rval().setObject(*result);
1337 return true;
1341 * Temporal.PlainDateTime.compare ( one, two )
1343 static bool PlainDateTime_compare(JSContext* cx, unsigned argc, Value* vp) {
1344 CallArgs args = CallArgsFromVp(argc, vp);
1346 // Step 1.
1347 PlainDateTime one;
1348 if (!ToTemporalDateTime(cx, args.get(0), &one)) {
1349 return false;
1352 // Step 2.
1353 PlainDateTime two;
1354 if (!ToTemporalDateTime(cx, args.get(1), &two)) {
1355 return false;
1358 // Step 3.
1359 args.rval().setInt32(CompareISODateTime(one, two));
1360 return true;
1364 * get Temporal.PlainDateTime.prototype.calendarId
1366 static bool PlainDateTime_calendarId(JSContext* cx, const CallArgs& args) {
1367 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1369 // Step 3.
1370 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1371 auto* calendarId = ToTemporalCalendarIdentifier(cx, calendar);
1372 if (!calendarId) {
1373 return false;
1376 args.rval().setString(calendarId);
1377 return true;
1381 * get Temporal.PlainDateTime.prototype.calendarId
1383 static bool PlainDateTime_calendarId(JSContext* cx, unsigned argc, Value* vp) {
1384 // Steps 1-2.
1385 CallArgs args = CallArgsFromVp(argc, vp);
1386 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_calendarId>(cx,
1387 args);
1391 * get Temporal.PlainDateTime.prototype.year
1393 static bool PlainDateTime_year(JSContext* cx, const CallArgs& args) {
1394 // Step 3.
1395 Rooted<PlainDateTimeObject*> dateTime(
1396 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1397 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1399 // Step 4.
1400 return CalendarYear(cx, calendar, dateTime, args.rval());
1404 * get Temporal.PlainDateTime.prototype.year
1406 static bool PlainDateTime_year(JSContext* cx, unsigned argc, Value* vp) {
1407 // Steps 1-2.
1408 CallArgs args = CallArgsFromVp(argc, vp);
1409 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_year>(cx, args);
1413 * get Temporal.PlainDateTime.prototype.month
1415 static bool PlainDateTime_month(JSContext* cx, const CallArgs& args) {
1416 // Step 3.
1417 Rooted<PlainDateTimeObject*> dateTime(
1418 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1419 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1421 // Step 4.
1422 return CalendarMonth(cx, calendar, dateTime, args.rval());
1426 * get Temporal.PlainDateTime.prototype.month
1428 static bool PlainDateTime_month(JSContext* cx, unsigned argc, Value* vp) {
1429 // Steps 1-2.
1430 CallArgs args = CallArgsFromVp(argc, vp);
1431 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_month>(cx, args);
1435 * get Temporal.PlainDateTime.prototype.monthCode
1437 static bool PlainDateTime_monthCode(JSContext* cx, const CallArgs& args) {
1438 // Step 3.
1439 Rooted<PlainDateTimeObject*> dateTime(
1440 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1441 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1443 // Step 4.
1444 return CalendarMonthCode(cx, calendar, dateTime, args.rval());
1448 * get Temporal.PlainDateTime.prototype.monthCode
1450 static bool PlainDateTime_monthCode(JSContext* cx, unsigned argc, Value* vp) {
1451 // Steps 1-2.
1452 CallArgs args = CallArgsFromVp(argc, vp);
1453 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthCode>(cx,
1454 args);
1458 * get Temporal.PlainDateTime.prototype.day
1460 static bool PlainDateTime_day(JSContext* cx, const CallArgs& args) {
1461 // Step 3.
1462 Rooted<PlainDateTimeObject*> dateTime(
1463 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1464 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1466 // Step 4.
1467 return CalendarDay(cx, calendar, dateTime, args.rval());
1471 * get Temporal.PlainDateTime.prototype.day
1473 static bool PlainDateTime_day(JSContext* cx, unsigned argc, Value* vp) {
1474 // Steps 1-2.
1475 CallArgs args = CallArgsFromVp(argc, vp);
1476 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_day>(cx, args);
1480 * get Temporal.PlainDateTime.prototype.hour
1482 static bool PlainDateTime_hour(JSContext* cx, const CallArgs& args) {
1483 // Step 3.
1484 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1485 args.rval().setInt32(dateTime->isoHour());
1486 return true;
1490 * get Temporal.PlainDateTime.prototype.hour
1492 static bool PlainDateTime_hour(JSContext* cx, unsigned argc, Value* vp) {
1493 // Steps 1-2.
1494 CallArgs args = CallArgsFromVp(argc, vp);
1495 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_hour>(cx, args);
1499 * get Temporal.PlainDateTime.prototype.minute
1501 static bool PlainDateTime_minute(JSContext* cx, const CallArgs& args) {
1502 // Step 3.
1503 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1504 args.rval().setInt32(dateTime->isoMinute());
1505 return true;
1509 * get Temporal.PlainDateTime.prototype.minute
1511 static bool PlainDateTime_minute(JSContext* cx, unsigned argc, Value* vp) {
1512 // Steps 1-2.
1513 CallArgs args = CallArgsFromVp(argc, vp);
1514 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_minute>(cx, args);
1518 * get Temporal.PlainDateTime.prototype.second
1520 static bool PlainDateTime_second(JSContext* cx, const CallArgs& args) {
1521 // Step 3.
1522 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1523 args.rval().setInt32(dateTime->isoSecond());
1524 return true;
1528 * get Temporal.PlainDateTime.prototype.second
1530 static bool PlainDateTime_second(JSContext* cx, unsigned argc, Value* vp) {
1531 // Steps 1-2.
1532 CallArgs args = CallArgsFromVp(argc, vp);
1533 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_second>(cx, args);
1537 * get Temporal.PlainDateTime.prototype.millisecond
1539 static bool PlainDateTime_millisecond(JSContext* cx, const CallArgs& args) {
1540 // Step 3.
1541 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1542 args.rval().setInt32(dateTime->isoMillisecond());
1543 return true;
1547 * get Temporal.PlainDateTime.prototype.millisecond
1549 static bool PlainDateTime_millisecond(JSContext* cx, unsigned argc, Value* vp) {
1550 // Steps 1-2.
1551 CallArgs args = CallArgsFromVp(argc, vp);
1552 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_millisecond>(cx,
1553 args);
1557 * get Temporal.PlainDateTime.prototype.microsecond
1559 static bool PlainDateTime_microsecond(JSContext* cx, const CallArgs& args) {
1560 // Step 3.
1561 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1562 args.rval().setInt32(dateTime->isoMicrosecond());
1563 return true;
1567 * get Temporal.PlainDateTime.prototype.microsecond
1569 static bool PlainDateTime_microsecond(JSContext* cx, unsigned argc, Value* vp) {
1570 // Steps 1-2.
1571 CallArgs args = CallArgsFromVp(argc, vp);
1572 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_microsecond>(cx,
1573 args);
1577 * get Temporal.PlainDateTime.prototype.nanosecond
1579 static bool PlainDateTime_nanosecond(JSContext* cx, const CallArgs& args) {
1580 // Step 3.
1581 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1582 args.rval().setInt32(dateTime->isoNanosecond());
1583 return true;
1587 * get Temporal.PlainDateTime.prototype.nanosecond
1589 static bool PlainDateTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) {
1590 // Steps 1-2.
1591 CallArgs args = CallArgsFromVp(argc, vp);
1592 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_nanosecond>(cx,
1593 args);
1597 * get Temporal.PlainDateTime.prototype.dayOfWeek
1599 static bool PlainDateTime_dayOfWeek(JSContext* cx, const CallArgs& args) {
1600 // Step 3.
1601 Rooted<PlainDateTimeObject*> dateTime(
1602 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1603 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1605 // Step 4.
1606 return CalendarDayOfWeek(cx, calendar, dateTime, args.rval());
1610 * get Temporal.PlainDateTime.prototype.dayOfWeek
1612 static bool PlainDateTime_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) {
1613 // Steps 1-2.
1614 CallArgs args = CallArgsFromVp(argc, vp);
1615 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfWeek>(cx,
1616 args);
1620 * get Temporal.PlainDateTime.prototype.dayOfYear
1622 static bool PlainDateTime_dayOfYear(JSContext* cx, const CallArgs& args) {
1623 // Step 3.
1624 Rooted<PlainDateTimeObject*> dateTime(
1625 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1626 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1628 // Step 4.
1629 return CalendarDayOfYear(cx, calendar, dateTime, args.rval());
1633 * get Temporal.PlainDateTime.prototype.dayOfYear
1635 static bool PlainDateTime_dayOfYear(JSContext* cx, unsigned argc, Value* vp) {
1636 // Steps 1-2.
1637 CallArgs args = CallArgsFromVp(argc, vp);
1638 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfYear>(cx,
1639 args);
1643 * get Temporal.PlainDateTime.prototype.weekOfYear
1645 static bool PlainDateTime_weekOfYear(JSContext* cx, const CallArgs& args) {
1646 // Step 3.
1647 Rooted<PlainDateTimeObject*> dateTime(
1648 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1649 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1651 // Step 4.
1652 return CalendarWeekOfYear(cx, calendar, dateTime, args.rval());
1656 * get Temporal.PlainDateTime.prototype.weekOfYear
1658 static bool PlainDateTime_weekOfYear(JSContext* cx, unsigned argc, Value* vp) {
1659 // Steps 1-2.
1660 CallArgs args = CallArgsFromVp(argc, vp);
1661 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_weekOfYear>(cx,
1662 args);
1666 * get Temporal.PlainDateTime.prototype.yearOfWeek
1668 static bool PlainDateTime_yearOfWeek(JSContext* cx, const CallArgs& args) {
1669 // Step 3.
1670 Rooted<PlainDateTimeObject*> dateTime(
1671 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1672 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1674 // Step 4.
1675 return CalendarYearOfWeek(cx, calendar, dateTime, args.rval());
1679 * get Temporal.PlainDateTime.prototype.yearOfWeek
1681 static bool PlainDateTime_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) {
1682 // Steps 1-2.
1683 CallArgs args = CallArgsFromVp(argc, vp);
1684 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_yearOfWeek>(cx,
1685 args);
1689 * get Temporal.PlainDateTime.prototype.daysInWeek
1691 static bool PlainDateTime_daysInWeek(JSContext* cx, const CallArgs& args) {
1692 // Step 3.
1693 Rooted<PlainDateTimeObject*> dateTime(
1694 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1695 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1697 // Step 4.
1698 return CalendarDaysInWeek(cx, calendar, dateTime, args.rval());
1702 * get Temporal.PlainDateTime.prototype.daysInWeek
1704 static bool PlainDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) {
1705 // Steps 1-2.
1706 CallArgs args = CallArgsFromVp(argc, vp);
1707 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInWeek>(cx,
1708 args);
1712 * get Temporal.PlainDateTime.prototype.daysInMonth
1714 static bool PlainDateTime_daysInMonth(JSContext* cx, const CallArgs& args) {
1715 // Step 3.
1716 Rooted<PlainDateTimeObject*> dateTime(
1717 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1718 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1720 // Step 4.
1721 return CalendarDaysInMonth(cx, calendar, dateTime, args.rval());
1725 * get Temporal.PlainDateTime.prototype.daysInMonth
1727 static bool PlainDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) {
1728 // Steps 1-2.
1729 CallArgs args = CallArgsFromVp(argc, vp);
1730 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInMonth>(cx,
1731 args);
1735 * get Temporal.PlainDateTime.prototype.daysInYear
1737 static bool PlainDateTime_daysInYear(JSContext* cx, const CallArgs& args) {
1738 // Step 3.
1739 Rooted<PlainDateTimeObject*> dateTime(
1740 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1741 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1743 // Step 4.
1744 return CalendarDaysInYear(cx, calendar, dateTime, args.rval());
1748 * get Temporal.PlainDateTime.prototype.daysInYear
1750 static bool PlainDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) {
1751 // Steps 1-2.
1752 CallArgs args = CallArgsFromVp(argc, vp);
1753 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInYear>(cx,
1754 args);
1758 * get Temporal.PlainDateTime.prototype.monthsInYear
1760 static bool PlainDateTime_monthsInYear(JSContext* cx, const CallArgs& args) {
1761 // Step 3.
1762 Rooted<PlainDateTimeObject*> dateTime(
1763 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1764 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1766 // Step 4.
1767 return CalendarMonthsInYear(cx, calendar, dateTime, args.rval());
1771 * get Temporal.PlainDateTime.prototype.monthsInYear
1773 static bool PlainDateTime_monthsInYear(JSContext* cx, unsigned argc,
1774 Value* vp) {
1775 // Steps 1-2.
1776 CallArgs args = CallArgsFromVp(argc, vp);
1777 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthsInYear>(
1778 cx, args);
1782 * get Temporal.PlainDateTime.prototype.inLeapYear
1784 static bool PlainDateTime_inLeapYear(JSContext* cx, const CallArgs& args) {
1785 // Step 3.
1786 Rooted<PlainDateTimeObject*> dateTime(
1787 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1788 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1790 // Step 4.
1791 return CalendarInLeapYear(cx, calendar, dateTime, args.rval());
1795 * get Temporal.PlainDateTime.prototype.inLeapYear
1797 static bool PlainDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) {
1798 // Steps 1-2.
1799 CallArgs args = CallArgsFromVp(argc, vp);
1800 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_inLeapYear>(cx,
1801 args);
1805 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
1807 static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
1808 Rooted<PlainDateTimeObject*> dateTime(
1809 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1811 // Step 3.
1812 Rooted<JSObject*> temporalDateTimeLike(
1813 cx, RequireObjectArg(cx, "temporalDateTimeLike", "with", args.get(0)));
1814 if (!temporalDateTimeLike) {
1815 return false;
1817 if (!ThrowIfTemporalLikeObject(cx, temporalDateTimeLike)) {
1818 return false;
1821 // Step 4.
1822 Rooted<PlainObject*> resolvedOptions(cx);
1823 if (args.hasDefined(1)) {
1824 Rooted<JSObject*> options(cx,
1825 RequireObjectArg(cx, "options", "with", args[1]));
1826 if (!options) {
1827 return false;
1829 resolvedOptions = SnapshotOwnProperties(cx, options);
1830 } else {
1831 resolvedOptions = NewPlainObjectWithProto(cx, nullptr);
1833 if (!resolvedOptions) {
1834 return false;
1837 // Step 5.
1838 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
1839 Rooted<CalendarRecord> calendar(cx);
1840 if (!CreateCalendarMethodsRecord(cx, calendarValue,
1842 CalendarMethod::DateFromFields,
1843 CalendarMethod::Fields,
1844 CalendarMethod::MergeFields,
1846 &calendar)) {
1847 return false;
1850 // Step 6.
1851 JS::RootedVector<PropertyKey> fieldNames(cx);
1852 if (!CalendarFields(cx, calendar,
1853 {CalendarField::Day, CalendarField::Month,
1854 CalendarField::MonthCode, CalendarField::Year},
1855 &fieldNames)) {
1856 return false;
1859 // Step 7.
1860 Rooted<PlainObject*> fields(cx,
1861 PrepareTemporalFields(cx, dateTime, fieldNames));
1862 if (!fields) {
1863 return false;
1866 // Steps 8-13.
1867 struct TimeField {
1868 using FieldName = ImmutableTenuredPtr<PropertyName*> JSAtomState::*;
1870 FieldName name;
1871 int32_t value;
1872 } timeFields[] = {
1873 {&JSAtomState::hour, dateTime->isoHour()},
1874 {&JSAtomState::minute, dateTime->isoMinute()},
1875 {&JSAtomState::second, dateTime->isoSecond()},
1876 {&JSAtomState::millisecond, dateTime->isoMillisecond()},
1877 {&JSAtomState::microsecond, dateTime->isoMicrosecond()},
1878 {&JSAtomState::nanosecond, dateTime->isoNanosecond()},
1881 Rooted<Value> timeFieldValue(cx);
1882 for (const auto& timeField : timeFields) {
1883 Handle<PropertyName*> name = cx->names().*(timeField.name);
1884 timeFieldValue.setInt32(timeField.value);
1886 if (!DefineDataProperty(cx, fields, name, timeFieldValue)) {
1887 return false;
1891 // Step 14.
1892 if (!AppendSorted(cx, fieldNames.get(),
1894 TemporalField::Hour,
1895 TemporalField::Microsecond,
1896 TemporalField::Millisecond,
1897 TemporalField::Minute,
1898 TemporalField::Nanosecond,
1899 TemporalField::Second,
1900 })) {
1901 return false;
1904 // Step 15.
1905 Rooted<PlainObject*> partialDateTime(
1906 cx, PreparePartialTemporalFields(cx, temporalDateTimeLike, fieldNames));
1907 if (!partialDateTime) {
1908 return false;
1911 // Step 16.
1912 Rooted<JSObject*> mergedFields(
1913 cx, CalendarMergeFields(cx, calendar, fields, partialDateTime));
1914 if (!mergedFields) {
1915 return false;
1918 // Step 17.
1919 fields = PrepareTemporalFields(cx, mergedFields, fieldNames);
1920 if (!fields) {
1921 return false;
1924 // Step 18.
1925 PlainDateTime result;
1926 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, resolvedOptions,
1927 &result)) {
1928 return false;
1931 // Steps 19-20.
1932 MOZ_ASSERT(IsValidISODateTime(result));
1934 // Step 21.
1935 auto* obj = CreateTemporalDateTime(cx, result, calendar.receiver());
1936 if (!obj) {
1937 return false;
1940 args.rval().setObject(*obj);
1941 return true;
1945 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
1947 static bool PlainDateTime_with(JSContext* cx, unsigned argc, Value* vp) {
1948 // Steps 1-2.
1949 CallArgs args = CallArgsFromVp(argc, vp);
1950 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_with>(cx, args);
1954 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1956 static bool PlainDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
1957 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1958 auto date = ToPlainDate(temporalDateTime);
1959 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1961 // Step 4.
1962 PlainTime time = {};
1963 if (args.hasDefined(0)) {
1964 if (!ToTemporalTime(cx, args[0], &time)) {
1965 return false;
1969 // Steps 3 and 5.
1970 auto* obj = CreateTemporalDateTime(cx, {date, time}, calendar);
1971 if (!obj) {
1972 return false;
1975 args.rval().setObject(*obj);
1976 return true;
1980 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1982 static bool PlainDateTime_withPlainTime(JSContext* cx, unsigned argc,
1983 Value* vp) {
1984 // Steps 1-2.
1985 CallArgs args = CallArgsFromVp(argc, vp);
1986 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainTime>(
1987 cx, args);
1991 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
1993 static bool PlainDateTime_withPlainDate(JSContext* cx, const CallArgs& args) {
1994 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1995 auto time = ToPlainTime(temporalDateTime);
1996 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1998 // Step 3.
1999 Rooted<PlainDateWithCalendar> plainDate(cx);
2000 if (!ToTemporalDate(cx, args.get(0), &plainDate)) {
2001 return false;
2003 auto date = plainDate.date();
2005 // Step 4.
2006 if (!ConsolidateCalendars(cx, calendar, plainDate.calendar(), &calendar)) {
2007 return false;
2010 // Step 5.
2011 auto* obj = CreateTemporalDateTime(cx, {date, time}, calendar);
2012 if (!obj) {
2013 return false;
2016 args.rval().setObject(*obj);
2017 return true;
2021 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
2023 static bool PlainDateTime_withPlainDate(JSContext* cx, unsigned argc,
2024 Value* vp) {
2025 // Steps 1-2.
2026 CallArgs args = CallArgsFromVp(argc, vp);
2027 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainDate>(
2028 cx, args);
2032 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2034 static bool PlainDateTime_withCalendar(JSContext* cx, const CallArgs& args) {
2035 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2036 auto dateTime = ToPlainDateTime(temporalDateTime);
2038 // Step 3.
2039 Rooted<CalendarValue> calendar(cx);
2040 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) {
2041 return false;
2044 // Step 4.
2045 auto* result = CreateTemporalDateTime(cx, dateTime, calendar);
2046 if (!result) {
2047 return false;
2050 args.rval().setObject(*result);
2051 return true;
2055 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2057 static bool PlainDateTime_withCalendar(JSContext* cx, unsigned argc,
2058 Value* vp) {
2059 // Steps 1-2.
2060 CallArgs args = CallArgsFromVp(argc, vp);
2061 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withCalendar>(
2062 cx, args);
2066 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2068 static bool PlainDateTime_add(JSContext* cx, const CallArgs& args) {
2069 // Step 3.
2070 return AddDurationToOrSubtractDurationFromPlainDateTime(
2071 cx, PlainDateTimeDuration::Add, args);
2075 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2077 static bool PlainDateTime_add(JSContext* cx, unsigned argc, Value* vp) {
2078 // Steps 1-2.
2079 CallArgs args = CallArgsFromVp(argc, vp);
2080 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_add>(cx, args);
2084 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2085 * ] )
2087 static bool PlainDateTime_subtract(JSContext* cx, const CallArgs& args) {
2088 // Step 3.
2089 return AddDurationToOrSubtractDurationFromPlainDateTime(
2090 cx, PlainDateTimeDuration::Subtract, args);
2094 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2095 * ] )
2097 static bool PlainDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) {
2098 // Steps 1-2.
2099 CallArgs args = CallArgsFromVp(argc, vp);
2100 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_subtract>(cx,
2101 args);
2105 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2107 static bool PlainDateTime_until(JSContext* cx, const CallArgs& args) {
2108 // Step 3.
2109 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Until, args);
2113 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2115 static bool PlainDateTime_until(JSContext* cx, unsigned argc, Value* vp) {
2116 // Steps 1-2.
2117 CallArgs args = CallArgsFromVp(argc, vp);
2118 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_until>(cx, args);
2122 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2124 static bool PlainDateTime_since(JSContext* cx, const CallArgs& args) {
2125 // Step 3.
2126 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Since, args);
2130 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2132 static bool PlainDateTime_since(JSContext* cx, unsigned argc, Value* vp) {
2133 // Steps 1-2.
2134 CallArgs args = CallArgsFromVp(argc, vp);
2135 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_since>(cx, args);
2139 * Temporal.PlainDateTime.prototype.round ( roundTo )
2141 static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
2142 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2143 auto dateTime = ToPlainDateTime(temporalDateTime);
2144 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2146 // Steps 3-12.
2147 auto smallestUnit = TemporalUnit::Auto;
2148 auto roundingMode = TemporalRoundingMode::HalfExpand;
2149 auto roundingIncrement = Increment{1};
2150 if (args.get(0).isString()) {
2151 // Step 4. (Not applicable in our implementation.)
2153 // Step 9.
2154 Rooted<JSString*> paramString(cx, args[0].toString());
2155 if (!GetTemporalUnit(cx, paramString, TemporalUnitKey::SmallestUnit,
2156 TemporalUnitGroup::DayTime, &smallestUnit)) {
2157 return false;
2160 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
2161 smallestUnit <= TemporalUnit::Nanosecond);
2163 // Steps 6-8 and 10-12. (Implicit)
2164 } else {
2165 // Steps 3 and 5.
2166 Rooted<JSObject*> roundTo(
2167 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
2168 if (!roundTo) {
2169 return false;
2172 // Steps 6-7.
2173 if (!ToTemporalRoundingIncrement(cx, roundTo, &roundingIncrement)) {
2174 return false;
2177 // Step 8.
2178 if (!ToTemporalRoundingMode(cx, roundTo, &roundingMode)) {
2179 return false;
2182 // Step 9.
2183 if (!GetTemporalUnit(cx, roundTo, TemporalUnitKey::SmallestUnit,
2184 TemporalUnitGroup::DayTime, &smallestUnit)) {
2185 return false;
2188 if (smallestUnit == TemporalUnit::Auto) {
2189 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2190 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
2191 return false;
2194 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
2195 smallestUnit <= TemporalUnit::Nanosecond);
2197 // Steps 10-11.
2198 auto maximum = Increment{1};
2199 bool inclusive = true;
2200 if (smallestUnit > TemporalUnit::Day) {
2201 maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
2202 inclusive = false;
2205 // Step 12.
2206 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
2207 inclusive)) {
2208 return false;
2212 // Step 13.
2213 if (smallestUnit == TemporalUnit::Nanosecond &&
2214 roundingIncrement == Increment{1}) {
2215 auto* obj = CreateTemporalDateTime(cx, dateTime, calendar);
2216 if (!obj) {
2217 return false;
2220 args.rval().setObject(*obj);
2221 return true;
2224 // Step 14.
2225 auto result =
2226 RoundISODateTime(dateTime, roundingIncrement, smallestUnit, roundingMode);
2228 // Step 15.
2229 auto* obj = CreateTemporalDateTime(cx, result, calendar);
2230 if (!obj) {
2231 return false;
2234 args.rval().setObject(*obj);
2235 return true;
2239 * Temporal.PlainDateTime.prototype.round ( roundTo )
2241 static bool PlainDateTime_round(JSContext* cx, unsigned argc, Value* vp) {
2242 // Steps 1-2.
2243 CallArgs args = CallArgsFromVp(argc, vp);
2244 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_round>(cx, args);
2248 * Temporal.PlainDateTime.prototype.equals ( other )
2250 static bool PlainDateTime_equals(JSContext* cx, const CallArgs& args) {
2251 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2252 auto dateTime = ToPlainDateTime(temporalDateTime);
2253 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2255 // Step 3.
2256 Rooted<PlainDateTimeWithCalendar> other(cx);
2257 if (!::ToTemporalDateTime(cx, args.get(0), &other)) {
2258 return false;
2261 // Steps 4-13.
2262 bool equals = dateTime == other.dateTime();
2263 if (equals && !CalendarEquals(cx, calendar, other.calendar(), &equals)) {
2264 return false;
2267 args.rval().setBoolean(equals);
2268 return true;
2272 * Temporal.PlainDateTime.prototype.equals ( other )
2274 static bool PlainDateTime_equals(JSContext* cx, unsigned argc, Value* vp) {
2275 // Steps 1-2.
2276 CallArgs args = CallArgsFromVp(argc, vp);
2277 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_equals>(cx, args);
2281 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2283 static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) {
2284 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2285 auto dt = ToPlainDateTime(dateTime);
2286 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2288 SecondsStringPrecision precision = {Precision::Auto(),
2289 TemporalUnit::Nanosecond, Increment{1}};
2290 auto roundingMode = TemporalRoundingMode::Trunc;
2291 auto showCalendar = CalendarOption::Auto;
2292 if (args.hasDefined(0)) {
2293 // Step 3.
2294 Rooted<JSObject*> options(
2295 cx, RequireObjectArg(cx, "options", "toString", args[0]));
2296 if (!options) {
2297 return false;
2300 // Steps 4-5.
2301 if (!ToCalendarNameOption(cx, options, &showCalendar)) {
2302 return false;
2305 // Step 6.
2306 auto digits = Precision::Auto();
2307 if (!ToFractionalSecondDigits(cx, options, &digits)) {
2308 return false;
2311 // Step 7.
2312 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
2313 return false;
2316 // Step 8.
2317 auto smallestUnit = TemporalUnit::Auto;
2318 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
2319 TemporalUnitGroup::Time, &smallestUnit)) {
2320 return false;
2323 // Step 9.
2324 if (smallestUnit == TemporalUnit::Hour) {
2325 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2326 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
2327 "smallestUnit");
2328 return false;
2331 // Step 10.
2332 precision = ToSecondsStringPrecision(smallestUnit, digits);
2335 // Step 11.
2336 auto result =
2337 RoundISODateTime(dt, precision.increment, precision.unit, roundingMode);
2339 // Step 12.
2340 JSString* str = ::TemporalDateTimeToString(cx, result, calendar,
2341 precision.precision, showCalendar);
2342 if (!str) {
2343 return false;
2346 args.rval().setString(str);
2347 return true;
2351 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2353 static bool PlainDateTime_toString(JSContext* cx, unsigned argc, Value* vp) {
2354 // Steps 1-2.
2355 CallArgs args = CallArgsFromVp(argc, vp);
2356 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toString>(cx,
2357 args);
2361 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2363 static bool PlainDateTime_toLocaleString(JSContext* cx, const CallArgs& args) {
2364 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2365 auto dt = ToPlainDateTime(dateTime);
2366 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2368 // Step 3.
2369 JSString* str = ::TemporalDateTimeToString(
2370 cx, dt, calendar, Precision::Auto(), CalendarOption::Auto);
2371 if (!str) {
2372 return false;
2375 args.rval().setString(str);
2376 return true;
2380 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2382 static bool PlainDateTime_toLocaleString(JSContext* cx, unsigned argc,
2383 Value* vp) {
2384 // Steps 1-2.
2385 CallArgs args = CallArgsFromVp(argc, vp);
2386 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toLocaleString>(
2387 cx, args);
2391 * Temporal.PlainDateTime.prototype.toJSON ( )
2393 static bool PlainDateTime_toJSON(JSContext* cx, const CallArgs& args) {
2394 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2395 auto dt = ToPlainDateTime(dateTime);
2396 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2398 // Step 3.
2399 JSString* str = ::TemporalDateTimeToString(
2400 cx, dt, calendar, Precision::Auto(), CalendarOption::Auto);
2401 if (!str) {
2402 return false;
2405 args.rval().setString(str);
2406 return true;
2410 * Temporal.PlainDateTime.prototype.toJSON ( )
2412 static bool PlainDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) {
2413 // Steps 1-2.
2414 CallArgs args = CallArgsFromVp(argc, vp);
2415 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toJSON>(cx, args);
2419 * Temporal.PlainDateTime.prototype.valueOf ( )
2421 static bool PlainDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) {
2422 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
2423 "PlainDateTime", "primitive type");
2424 return false;
2428 * Temporal.PlainDateTime.prototype.getISOFields ( )
2430 static bool PlainDateTime_getISOFields(JSContext* cx, const CallArgs& args) {
2431 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2432 auto dateTime = ToPlainDateTime(temporalDateTime);
2433 auto calendar = temporalDateTime->calendar();
2435 // Step 3.
2436 Rooted<IdValueVector> fields(cx, IdValueVector(cx));
2438 // Step 4.
2439 if (!fields.emplaceBack(NameToId(cx->names().calendar), calendar.toValue())) {
2440 return false;
2443 // Step 5.
2444 if (!fields.emplaceBack(NameToId(cx->names().isoDay),
2445 Int32Value(dateTime.date.day))) {
2446 return false;
2449 // Step 6.
2450 if (!fields.emplaceBack(NameToId(cx->names().isoHour),
2451 Int32Value(dateTime.time.hour))) {
2452 return false;
2455 // Step 7.
2456 if (!fields.emplaceBack(NameToId(cx->names().isoMicrosecond),
2457 Int32Value(dateTime.time.microsecond))) {
2458 return false;
2461 // Step 8.
2462 if (!fields.emplaceBack(NameToId(cx->names().isoMillisecond),
2463 Int32Value(dateTime.time.millisecond))) {
2464 return false;
2467 // Step 9.
2468 if (!fields.emplaceBack(NameToId(cx->names().isoMinute),
2469 Int32Value(dateTime.time.minute))) {
2470 return false;
2473 // Step 10.
2474 if (!fields.emplaceBack(NameToId(cx->names().isoMonth),
2475 Int32Value(dateTime.date.month))) {
2476 return false;
2479 // Step 11.
2480 if (!fields.emplaceBack(NameToId(cx->names().isoNanosecond),
2481 Int32Value(dateTime.time.nanosecond))) {
2482 return false;
2485 // Step 12.
2486 if (!fields.emplaceBack(NameToId(cx->names().isoSecond),
2487 Int32Value(dateTime.time.second))) {
2488 return false;
2491 // Step 13.
2492 if (!fields.emplaceBack(NameToId(cx->names().isoYear),
2493 Int32Value(dateTime.date.year))) {
2494 return false;
2497 // Step 14.
2498 auto* obj = NewPlainObjectWithUniqueNames(cx, fields);
2499 if (!obj) {
2500 return false;
2503 args.rval().setObject(*obj);
2504 return true;
2508 * Temporal.PlainDateTime.prototype.getISOFields ( )
2510 static bool PlainDateTime_getISOFields(JSContext* cx, unsigned argc,
2511 Value* vp) {
2512 // Steps 1-2.
2513 CallArgs args = CallArgsFromVp(argc, vp);
2514 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_getISOFields>(
2515 cx, args);
2519 * Temporal.PlainDateTime.prototype.getCalendar ( )
2521 static bool PlainDateTime_getCalendar(JSContext* cx, const CallArgs& args) {
2522 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2523 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2525 // Step 3.
2526 auto* obj = ToTemporalCalendarObject(cx, calendar);
2527 if (!obj) {
2528 return false;
2531 args.rval().setObject(*obj);
2532 return true;
2536 * Temporal.PlainDateTime.prototype.getCalendar ( )
2538 static bool PlainDateTime_getCalendar(JSContext* cx, unsigned argc, Value* vp) {
2539 // Steps 1-2.
2540 CallArgs args = CallArgsFromVp(argc, vp);
2541 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_getCalendar>(cx,
2542 args);
2546 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2547 * options ] )
2549 static bool PlainDateTime_toZonedDateTime(JSContext* cx, const CallArgs& args) {
2550 Rooted<PlainDateTimeObject*> dateTime(
2551 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2552 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2554 // Step 3.
2555 Rooted<TimeZoneValue> timeZone(cx);
2556 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
2557 return false;
2560 auto disambiguation = TemporalDisambiguation::Compatible;
2561 if (args.hasDefined(1)) {
2562 // Step 4.
2563 Rooted<JSObject*> options(
2564 cx, RequireObjectArg(cx, "options", "toZonedDateTime", args[1]));
2565 if (!options) {
2566 return false;
2569 // Step 5.
2570 if (!ToTemporalDisambiguation(cx, options, &disambiguation)) {
2571 return false;
2575 // Steps 6-7.
2576 Instant instant;
2577 if (!GetInstantFor(cx, timeZone, dateTime, disambiguation, &instant)) {
2578 return false;
2581 // Step 8.
2582 auto* result = CreateTemporalZonedDateTime(cx, instant, timeZone, calendar);
2583 if (!result) {
2584 return false;
2587 args.rval().setObject(*result);
2588 return true;
2592 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2593 * options ] )
2595 static bool PlainDateTime_toZonedDateTime(JSContext* cx, unsigned argc,
2596 Value* vp) {
2597 // Steps 1-2.
2598 CallArgs args = CallArgsFromVp(argc, vp);
2599 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toZonedDateTime>(
2600 cx, args);
2604 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2606 static bool PlainDateTime_toPlainDate(JSContext* cx, const CallArgs& args) {
2607 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2608 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2610 // Step 3.
2611 auto* obj = CreateTemporalDate(cx, ToPlainDate(dateTime), calendar);
2612 if (!obj) {
2613 return false;
2616 args.rval().setObject(*obj);
2617 return true;
2621 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2623 static bool PlainDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
2624 // Steps 1-2.
2625 CallArgs args = CallArgsFromVp(argc, vp);
2626 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainDate>(cx,
2627 args);
2631 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2633 static bool PlainDateTime_toPlainYearMonth(JSContext* cx,
2634 const CallArgs& args) {
2635 Rooted<PlainDateTimeObject*> dateTime(
2636 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2637 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
2639 // Step 3.
2640 Rooted<CalendarRecord> calendar(cx);
2641 if (!CreateCalendarMethodsRecord(cx, calendarValue,
2643 CalendarMethod::Fields,
2644 CalendarMethod::YearMonthFromFields,
2646 &calendar)) {
2647 return false;
2650 // Step 4.
2651 JS::RootedVector<PropertyKey> fieldNames(cx);
2652 if (!CalendarFields(cx, calendar,
2653 {CalendarField::MonthCode, CalendarField::Year},
2654 &fieldNames)) {
2655 return false;
2658 // Step 4.
2659 Rooted<PlainObject*> fields(cx,
2660 PrepareTemporalFields(cx, dateTime, fieldNames));
2661 if (!fields) {
2662 return false;
2665 // Step 5.
2666 auto obj = CalendarYearMonthFromFields(cx, calendar, fields);
2667 if (!obj) {
2668 return false;
2671 args.rval().setObject(*obj);
2672 return true;
2676 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2678 static bool PlainDateTime_toPlainYearMonth(JSContext* cx, unsigned argc,
2679 Value* vp) {
2680 // Steps 1-2.
2681 CallArgs args = CallArgsFromVp(argc, vp);
2682 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainYearMonth>(
2683 cx, args);
2687 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2689 static bool PlainDateTime_toPlainMonthDay(JSContext* cx, const CallArgs& args) {
2690 Rooted<PlainDateTimeObject*> dateTime(
2691 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2692 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
2694 // Step 3.
2695 Rooted<CalendarRecord> calendar(cx);
2696 if (!CreateCalendarMethodsRecord(cx, calendarValue,
2698 CalendarMethod::Fields,
2699 CalendarMethod::MonthDayFromFields,
2701 &calendar)) {
2702 return false;
2705 // Step 4.
2706 JS::RootedVector<PropertyKey> fieldNames(cx);
2707 if (!CalendarFields(cx, calendar,
2708 {CalendarField::Day, CalendarField::MonthCode},
2709 &fieldNames)) {
2710 return false;
2713 // Step 5.
2714 Rooted<PlainObject*> fields(cx,
2715 PrepareTemporalFields(cx, dateTime, fieldNames));
2716 if (!fields) {
2717 return false;
2720 // Steps 6-7.
2721 auto obj = CalendarMonthDayFromFields(cx, calendar, fields);
2722 if (!obj) {
2723 return false;
2726 args.rval().setObject(*obj);
2727 return true;
2731 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2733 static bool PlainDateTime_toPlainMonthDay(JSContext* cx, unsigned argc,
2734 Value* vp) {
2735 // Steps 1-2.
2736 CallArgs args = CallArgsFromVp(argc, vp);
2737 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainMonthDay>(
2738 cx, args);
2742 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2744 static bool PlainDateTime_toPlainTime(JSContext* cx, const CallArgs& args) {
2745 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2747 // Step 3.
2748 auto* obj = CreateTemporalTime(cx, ToPlainTime(dateTime));
2749 if (!obj) {
2750 return false;
2753 args.rval().setObject(*obj);
2754 return true;
2758 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2760 static bool PlainDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) {
2761 // Steps 1-2.
2762 CallArgs args = CallArgsFromVp(argc, vp);
2763 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainTime>(cx,
2764 args);
2767 const JSClass PlainDateTimeObject::class_ = {
2768 "Temporal.PlainDateTime",
2769 JSCLASS_HAS_RESERVED_SLOTS(PlainDateTimeObject::SLOT_COUNT) |
2770 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainDateTime),
2771 JS_NULL_CLASS_OPS,
2772 &PlainDateTimeObject::classSpec_,
2775 const JSClass& PlainDateTimeObject::protoClass_ = PlainObject::class_;
2777 static const JSFunctionSpec PlainDateTime_methods[] = {
2778 JS_FN("from", PlainDateTime_from, 1, 0),
2779 JS_FN("compare", PlainDateTime_compare, 2, 0),
2780 JS_FS_END,
2783 static const JSFunctionSpec PlainDateTime_prototype_methods[] = {
2784 JS_FN("with", PlainDateTime_with, 1, 0),
2785 JS_FN("withPlainTime", PlainDateTime_withPlainTime, 0, 0),
2786 JS_FN("withPlainDate", PlainDateTime_withPlainDate, 1, 0),
2787 JS_FN("withCalendar", PlainDateTime_withCalendar, 1, 0),
2788 JS_FN("add", PlainDateTime_add, 1, 0),
2789 JS_FN("subtract", PlainDateTime_subtract, 1, 0),
2790 JS_FN("until", PlainDateTime_until, 1, 0),
2791 JS_FN("since", PlainDateTime_since, 1, 0),
2792 JS_FN("round", PlainDateTime_round, 1, 0),
2793 JS_FN("equals", PlainDateTime_equals, 1, 0),
2794 JS_FN("toString", PlainDateTime_toString, 0, 0),
2795 JS_FN("toLocaleString", PlainDateTime_toLocaleString, 0, 0),
2796 JS_FN("toJSON", PlainDateTime_toJSON, 0, 0),
2797 JS_FN("valueOf", PlainDateTime_valueOf, 0, 0),
2798 JS_FN("toZonedDateTime", PlainDateTime_toZonedDateTime, 1, 0),
2799 JS_FN("toPlainDate", PlainDateTime_toPlainDate, 0, 0),
2800 JS_FN("toPlainYearMonth", PlainDateTime_toPlainYearMonth, 0, 0),
2801 JS_FN("toPlainMonthDay", PlainDateTime_toPlainMonthDay, 0, 0),
2802 JS_FN("toPlainTime", PlainDateTime_toPlainTime, 0, 0),
2803 JS_FN("getISOFields", PlainDateTime_getISOFields, 0, 0),
2804 JS_FN("getCalendar", PlainDateTime_getCalendar, 0, 0),
2805 JS_FS_END,
2808 static const JSPropertySpec PlainDateTime_prototype_properties[] = {
2809 JS_PSG("calendarId", PlainDateTime_calendarId, 0),
2810 JS_PSG("year", PlainDateTime_year, 0),
2811 JS_PSG("month", PlainDateTime_month, 0),
2812 JS_PSG("monthCode", PlainDateTime_monthCode, 0),
2813 JS_PSG("day", PlainDateTime_day, 0),
2814 JS_PSG("hour", PlainDateTime_hour, 0),
2815 JS_PSG("minute", PlainDateTime_minute, 0),
2816 JS_PSG("second", PlainDateTime_second, 0),
2817 JS_PSG("millisecond", PlainDateTime_millisecond, 0),
2818 JS_PSG("microsecond", PlainDateTime_microsecond, 0),
2819 JS_PSG("nanosecond", PlainDateTime_nanosecond, 0),
2820 JS_PSG("dayOfWeek", PlainDateTime_dayOfWeek, 0),
2821 JS_PSG("dayOfYear", PlainDateTime_dayOfYear, 0),
2822 JS_PSG("weekOfYear", PlainDateTime_weekOfYear, 0),
2823 JS_PSG("yearOfWeek", PlainDateTime_yearOfWeek, 0),
2824 JS_PSG("daysInWeek", PlainDateTime_daysInWeek, 0),
2825 JS_PSG("daysInMonth", PlainDateTime_daysInMonth, 0),
2826 JS_PSG("daysInYear", PlainDateTime_daysInYear, 0),
2827 JS_PSG("monthsInYear", PlainDateTime_monthsInYear, 0),
2828 JS_PSG("inLeapYear", PlainDateTime_inLeapYear, 0),
2829 JS_STRING_SYM_PS(toStringTag, "Temporal.PlainDateTime", JSPROP_READONLY),
2830 JS_PS_END,
2833 const ClassSpec PlainDateTimeObject::classSpec_ = {
2834 GenericCreateConstructor<PlainDateTimeConstructor, 3,
2835 gc::AllocKind::FUNCTION>,
2836 GenericCreatePrototype<PlainDateTimeObject>,
2837 PlainDateTime_methods,
2838 nullptr,
2839 PlainDateTime_prototype_methods,
2840 PlainDateTime_prototype_properties,
2841 nullptr,
2842 ClassSpec::DontDefineConstructor,