Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / js / src / builtin / temporal / PlainDateTime.cpp
blob37ce7d56514941723f46cf468c271c427f27a1d9
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 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;
1811 if (!ThrowIfTemporalLikeObject(cx, temporalDateTimeLike)) {
1812 return false;
1815 // Step 4.
1816 Rooted<PlainObject*> resolvedOptions(cx);
1817 if (args.hasDefined(1)) {
1818 Rooted<JSObject*> options(cx,
1819 RequireObjectArg(cx, "options", "with", args[1]));
1820 if (!options) {
1821 return false;
1823 resolvedOptions = SnapshotOwnProperties(cx, options);
1824 } else {
1825 resolvedOptions = NewPlainObjectWithProto(cx, nullptr);
1827 if (!resolvedOptions) {
1828 return false;
1831 // Step 5.
1832 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
1833 Rooted<CalendarRecord> calendar(cx);
1834 if (!CreateCalendarMethodsRecord(cx, calendarValue,
1836 CalendarMethod::DateFromFields,
1837 CalendarMethod::Fields,
1838 CalendarMethod::MergeFields,
1840 &calendar)) {
1841 return false;
1844 // Step 6.
1845 JS::RootedVector<PropertyKey> fieldNames(cx);
1846 if (!CalendarFields(cx, calendar,
1847 {CalendarField::Day, CalendarField::Month,
1848 CalendarField::MonthCode, CalendarField::Year},
1849 &fieldNames)) {
1850 return false;
1853 // Step 7.
1854 Rooted<PlainObject*> fields(cx,
1855 PrepareTemporalFields(cx, dateTime, fieldNames));
1856 if (!fields) {
1857 return false;
1860 // Steps 8-13.
1861 struct TimeField {
1862 using FieldName = ImmutableTenuredPtr<PropertyName*> JSAtomState::*;
1864 FieldName name;
1865 int32_t value;
1866 } timeFields[] = {
1867 {&JSAtomState::hour, dateTime->isoHour()},
1868 {&JSAtomState::minute, dateTime->isoMinute()},
1869 {&JSAtomState::second, dateTime->isoSecond()},
1870 {&JSAtomState::millisecond, dateTime->isoMillisecond()},
1871 {&JSAtomState::microsecond, dateTime->isoMicrosecond()},
1872 {&JSAtomState::nanosecond, dateTime->isoNanosecond()},
1875 Rooted<Value> timeFieldValue(cx);
1876 for (const auto& timeField : timeFields) {
1877 Handle<PropertyName*> name = cx->names().*(timeField.name);
1878 timeFieldValue.setInt32(timeField.value);
1880 if (!DefineDataProperty(cx, fields, name, timeFieldValue)) {
1881 return false;
1885 // Step 14.
1886 if (!AppendSorted(cx, fieldNames.get(),
1888 TemporalField::Hour,
1889 TemporalField::Microsecond,
1890 TemporalField::Millisecond,
1891 TemporalField::Minute,
1892 TemporalField::Nanosecond,
1893 TemporalField::Second,
1894 })) {
1895 return false;
1898 // Step 15.
1899 Rooted<PlainObject*> partialDateTime(
1900 cx, PreparePartialTemporalFields(cx, temporalDateTimeLike, fieldNames));
1901 if (!partialDateTime) {
1902 return false;
1905 // Step 16.
1906 Rooted<JSObject*> mergedFields(
1907 cx, CalendarMergeFields(cx, calendar, fields, partialDateTime));
1908 if (!mergedFields) {
1909 return false;
1912 // Step 17.
1913 fields = PrepareTemporalFields(cx, mergedFields, fieldNames);
1914 if (!fields) {
1915 return false;
1918 // Step 18.
1919 PlainDateTime result;
1920 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, resolvedOptions,
1921 &result)) {
1922 return false;
1925 // Steps 19-20.
1926 MOZ_ASSERT(IsValidISODateTime(result));
1928 // Step 21.
1929 auto* obj = CreateTemporalDateTime(cx, result, calendar.receiver());
1930 if (!obj) {
1931 return false;
1934 args.rval().setObject(*obj);
1935 return true;
1939 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
1941 static bool PlainDateTime_with(JSContext* cx, unsigned argc, Value* vp) {
1942 // Steps 1-2.
1943 CallArgs args = CallArgsFromVp(argc, vp);
1944 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_with>(cx, args);
1948 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1950 static bool PlainDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
1951 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1952 auto date = ToPlainDate(temporalDateTime);
1953 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1955 // Step 4.
1956 PlainTime time = {};
1957 if (args.hasDefined(0)) {
1958 if (!ToTemporalTime(cx, args[0], &time)) {
1959 return false;
1963 // Steps 3 and 5.
1964 auto* obj = CreateTemporalDateTime(cx, {date, time}, calendar);
1965 if (!obj) {
1966 return false;
1969 args.rval().setObject(*obj);
1970 return true;
1974 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1976 static bool PlainDateTime_withPlainTime(JSContext* cx, unsigned argc,
1977 Value* vp) {
1978 // Steps 1-2.
1979 CallArgs args = CallArgsFromVp(argc, vp);
1980 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainTime>(
1981 cx, args);
1985 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
1987 static bool PlainDateTime_withPlainDate(JSContext* cx, const CallArgs& args) {
1988 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1989 auto time = ToPlainTime(temporalDateTime);
1990 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1992 // Step 3.
1993 Rooted<PlainDateWithCalendar> plainDate(cx);
1994 if (!ToTemporalDate(cx, args.get(0), &plainDate)) {
1995 return false;
1997 auto date = plainDate.date();
1999 // Step 4.
2000 if (!ConsolidateCalendars(cx, calendar, plainDate.calendar(), &calendar)) {
2001 return false;
2004 // Step 5.
2005 auto* obj = CreateTemporalDateTime(cx, {date, time}, calendar);
2006 if (!obj) {
2007 return false;
2010 args.rval().setObject(*obj);
2011 return true;
2015 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
2017 static bool PlainDateTime_withPlainDate(JSContext* cx, unsigned argc,
2018 Value* vp) {
2019 // Steps 1-2.
2020 CallArgs args = CallArgsFromVp(argc, vp);
2021 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainDate>(
2022 cx, args);
2026 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2028 static bool PlainDateTime_withCalendar(JSContext* cx, const CallArgs& args) {
2029 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2030 auto dateTime = ToPlainDateTime(temporalDateTime);
2032 // Step 3.
2033 Rooted<CalendarValue> calendar(cx);
2034 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) {
2035 return false;
2038 // Step 4.
2039 auto* result = CreateTemporalDateTime(cx, dateTime, calendar);
2040 if (!result) {
2041 return false;
2044 args.rval().setObject(*result);
2045 return true;
2049 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2051 static bool PlainDateTime_withCalendar(JSContext* cx, unsigned argc,
2052 Value* vp) {
2053 // Steps 1-2.
2054 CallArgs args = CallArgsFromVp(argc, vp);
2055 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withCalendar>(
2056 cx, args);
2060 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2062 static bool PlainDateTime_add(JSContext* cx, const CallArgs& args) {
2063 // Step 3.
2064 return AddDurationToOrSubtractDurationFromPlainDateTime(
2065 cx, PlainDateTimeDuration::Add, args);
2069 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2071 static bool PlainDateTime_add(JSContext* cx, unsigned argc, Value* vp) {
2072 // Steps 1-2.
2073 CallArgs args = CallArgsFromVp(argc, vp);
2074 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_add>(cx, args);
2078 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2079 * ] )
2081 static bool PlainDateTime_subtract(JSContext* cx, const CallArgs& args) {
2082 // Step 3.
2083 return AddDurationToOrSubtractDurationFromPlainDateTime(
2084 cx, PlainDateTimeDuration::Subtract, args);
2088 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2089 * ] )
2091 static bool PlainDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) {
2092 // Steps 1-2.
2093 CallArgs args = CallArgsFromVp(argc, vp);
2094 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_subtract>(cx,
2095 args);
2099 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2101 static bool PlainDateTime_until(JSContext* cx, const CallArgs& args) {
2102 // Step 3.
2103 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Until, args);
2107 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2109 static bool PlainDateTime_until(JSContext* cx, unsigned argc, Value* vp) {
2110 // Steps 1-2.
2111 CallArgs args = CallArgsFromVp(argc, vp);
2112 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_until>(cx, args);
2116 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2118 static bool PlainDateTime_since(JSContext* cx, const CallArgs& args) {
2119 // Step 3.
2120 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Since, args);
2124 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2126 static bool PlainDateTime_since(JSContext* cx, unsigned argc, Value* vp) {
2127 // Steps 1-2.
2128 CallArgs args = CallArgsFromVp(argc, vp);
2129 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_since>(cx, args);
2133 * Temporal.PlainDateTime.prototype.round ( roundTo )
2135 static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
2136 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2137 auto dateTime = ToPlainDateTime(temporalDateTime);
2138 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2140 // Steps 3-12.
2141 auto smallestUnit = TemporalUnit::Auto;
2142 auto roundingMode = TemporalRoundingMode::HalfExpand;
2143 auto roundingIncrement = Increment{1};
2144 if (args.get(0).isString()) {
2145 // Step 4. (Not applicable in our implementation.)
2147 // Step 9.
2148 Rooted<JSString*> paramString(cx, args[0].toString());
2149 if (!GetTemporalUnit(cx, paramString, TemporalUnitKey::SmallestUnit,
2150 TemporalUnitGroup::DayTime, &smallestUnit)) {
2151 return false;
2154 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
2155 smallestUnit <= TemporalUnit::Nanosecond);
2157 // Steps 6-8 and 10-12. (Implicit)
2158 } else {
2159 // Steps 3 and 5.
2160 Rooted<JSObject*> roundTo(
2161 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
2162 if (!roundTo) {
2163 return false;
2166 // Steps 6-7.
2167 if (!ToTemporalRoundingIncrement(cx, roundTo, &roundingIncrement)) {
2168 return false;
2171 // Step 8.
2172 if (!ToTemporalRoundingMode(cx, roundTo, &roundingMode)) {
2173 return false;
2176 // Step 9.
2177 if (!GetTemporalUnit(cx, roundTo, TemporalUnitKey::SmallestUnit,
2178 TemporalUnitGroup::DayTime, &smallestUnit)) {
2179 return false;
2182 if (smallestUnit == TemporalUnit::Auto) {
2183 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2184 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
2185 return false;
2188 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
2189 smallestUnit <= TemporalUnit::Nanosecond);
2191 // Steps 10-11.
2192 auto maximum = Increment{1};
2193 bool inclusive = true;
2194 if (smallestUnit > TemporalUnit::Day) {
2195 maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
2196 inclusive = false;
2199 // Step 12.
2200 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
2201 inclusive)) {
2202 return false;
2206 // Step 13.
2207 if (smallestUnit == TemporalUnit::Nanosecond &&
2208 roundingIncrement == Increment{1}) {
2209 auto* obj = CreateTemporalDateTime(cx, dateTime, calendar);
2210 if (!obj) {
2211 return false;
2214 args.rval().setObject(*obj);
2215 return true;
2218 // Step 14.
2219 auto result =
2220 RoundISODateTime(dateTime, roundingIncrement, smallestUnit, roundingMode);
2222 // Step 15.
2223 auto* obj = CreateTemporalDateTime(cx, result, calendar);
2224 if (!obj) {
2225 return false;
2228 args.rval().setObject(*obj);
2229 return true;
2233 * Temporal.PlainDateTime.prototype.round ( roundTo )
2235 static bool PlainDateTime_round(JSContext* cx, unsigned argc, Value* vp) {
2236 // Steps 1-2.
2237 CallArgs args = CallArgsFromVp(argc, vp);
2238 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_round>(cx, args);
2242 * Temporal.PlainDateTime.prototype.equals ( other )
2244 static bool PlainDateTime_equals(JSContext* cx, const CallArgs& args) {
2245 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2246 auto dateTime = ToPlainDateTime(temporalDateTime);
2247 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2249 // Step 3.
2250 Rooted<PlainDateTimeWithCalendar> other(cx);
2251 if (!::ToTemporalDateTime(cx, args.get(0), &other)) {
2252 return false;
2255 // Steps 4-13.
2256 bool equals = dateTime == other.dateTime();
2257 if (equals && !CalendarEquals(cx, calendar, other.calendar(), &equals)) {
2258 return false;
2261 args.rval().setBoolean(equals);
2262 return true;
2266 * Temporal.PlainDateTime.prototype.equals ( other )
2268 static bool PlainDateTime_equals(JSContext* cx, unsigned argc, Value* vp) {
2269 // Steps 1-2.
2270 CallArgs args = CallArgsFromVp(argc, vp);
2271 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_equals>(cx, args);
2275 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2277 static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) {
2278 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2279 auto dt = ToPlainDateTime(dateTime);
2280 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2282 SecondsStringPrecision precision = {Precision::Auto(),
2283 TemporalUnit::Nanosecond, Increment{1}};
2284 auto roundingMode = TemporalRoundingMode::Trunc;
2285 auto showCalendar = CalendarOption::Auto;
2286 if (args.hasDefined(0)) {
2287 // Step 3.
2288 Rooted<JSObject*> options(
2289 cx, RequireObjectArg(cx, "options", "toString", args[0]));
2290 if (!options) {
2291 return false;
2294 // Steps 4-5.
2295 if (!ToCalendarNameOption(cx, options, &showCalendar)) {
2296 return false;
2299 // Step 6.
2300 auto digits = Precision::Auto();
2301 if (!ToFractionalSecondDigits(cx, options, &digits)) {
2302 return false;
2305 // Step 7.
2306 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
2307 return false;
2310 // Step 8.
2311 auto smallestUnit = TemporalUnit::Auto;
2312 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
2313 TemporalUnitGroup::Time, &smallestUnit)) {
2314 return false;
2317 // Step 9.
2318 if (smallestUnit == TemporalUnit::Hour) {
2319 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2320 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
2321 "smallestUnit");
2322 return false;
2325 // Step 10.
2326 precision = ToSecondsStringPrecision(smallestUnit, digits);
2329 // Step 11.
2330 auto result =
2331 RoundISODateTime(dt, precision.increment, precision.unit, roundingMode);
2333 // Step 12.
2334 JSString* str = ::TemporalDateTimeToString(cx, result, calendar,
2335 precision.precision, showCalendar);
2336 if (!str) {
2337 return false;
2340 args.rval().setString(str);
2341 return true;
2345 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2347 static bool PlainDateTime_toString(JSContext* cx, unsigned argc, Value* vp) {
2348 // Steps 1-2.
2349 CallArgs args = CallArgsFromVp(argc, vp);
2350 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toString>(cx,
2351 args);
2355 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2357 static bool PlainDateTime_toLocaleString(JSContext* cx, const CallArgs& args) {
2358 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2359 auto dt = ToPlainDateTime(dateTime);
2360 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2362 // Step 3.
2363 JSString* str = ::TemporalDateTimeToString(
2364 cx, dt, calendar, Precision::Auto(), CalendarOption::Auto);
2365 if (!str) {
2366 return false;
2369 args.rval().setString(str);
2370 return true;
2374 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2376 static bool PlainDateTime_toLocaleString(JSContext* cx, unsigned argc,
2377 Value* vp) {
2378 // Steps 1-2.
2379 CallArgs args = CallArgsFromVp(argc, vp);
2380 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toLocaleString>(
2381 cx, args);
2385 * Temporal.PlainDateTime.prototype.toJSON ( )
2387 static bool PlainDateTime_toJSON(JSContext* cx, const CallArgs& args) {
2388 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2389 auto dt = ToPlainDateTime(dateTime);
2390 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2392 // Step 3.
2393 JSString* str = ::TemporalDateTimeToString(
2394 cx, dt, calendar, Precision::Auto(), CalendarOption::Auto);
2395 if (!str) {
2396 return false;
2399 args.rval().setString(str);
2400 return true;
2404 * Temporal.PlainDateTime.prototype.toJSON ( )
2406 static bool PlainDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) {
2407 // Steps 1-2.
2408 CallArgs args = CallArgsFromVp(argc, vp);
2409 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toJSON>(cx, args);
2413 * Temporal.PlainDateTime.prototype.valueOf ( )
2415 static bool PlainDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) {
2416 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
2417 "PlainDateTime", "primitive type");
2418 return false;
2422 * Temporal.PlainDateTime.prototype.getISOFields ( )
2424 static bool PlainDateTime_getISOFields(JSContext* cx, const CallArgs& args) {
2425 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2426 auto dateTime = ToPlainDateTime(temporalDateTime);
2427 auto calendar = temporalDateTime->calendar();
2429 // Step 3.
2430 Rooted<IdValueVector> fields(cx, IdValueVector(cx));
2432 // Step 4.
2433 if (!fields.emplaceBack(NameToId(cx->names().calendar), calendar.toValue())) {
2434 return false;
2437 // Step 5.
2438 if (!fields.emplaceBack(NameToId(cx->names().isoDay),
2439 Int32Value(dateTime.date.day))) {
2440 return false;
2443 // Step 6.
2444 if (!fields.emplaceBack(NameToId(cx->names().isoHour),
2445 Int32Value(dateTime.time.hour))) {
2446 return false;
2449 // Step 7.
2450 if (!fields.emplaceBack(NameToId(cx->names().isoMicrosecond),
2451 Int32Value(dateTime.time.microsecond))) {
2452 return false;
2455 // Step 8.
2456 if (!fields.emplaceBack(NameToId(cx->names().isoMillisecond),
2457 Int32Value(dateTime.time.millisecond))) {
2458 return false;
2461 // Step 9.
2462 if (!fields.emplaceBack(NameToId(cx->names().isoMinute),
2463 Int32Value(dateTime.time.minute))) {
2464 return false;
2467 // Step 10.
2468 if (!fields.emplaceBack(NameToId(cx->names().isoMonth),
2469 Int32Value(dateTime.date.month))) {
2470 return false;
2473 // Step 11.
2474 if (!fields.emplaceBack(NameToId(cx->names().isoNanosecond),
2475 Int32Value(dateTime.time.nanosecond))) {
2476 return false;
2479 // Step 12.
2480 if (!fields.emplaceBack(NameToId(cx->names().isoSecond),
2481 Int32Value(dateTime.time.second))) {
2482 return false;
2485 // Step 13.
2486 if (!fields.emplaceBack(NameToId(cx->names().isoYear),
2487 Int32Value(dateTime.date.year))) {
2488 return false;
2491 // Step 14.
2492 auto* obj = NewPlainObjectWithUniqueNames(cx, fields);
2493 if (!obj) {
2494 return false;
2497 args.rval().setObject(*obj);
2498 return true;
2502 * Temporal.PlainDateTime.prototype.getISOFields ( )
2504 static bool PlainDateTime_getISOFields(JSContext* cx, unsigned argc,
2505 Value* vp) {
2506 // Steps 1-2.
2507 CallArgs args = CallArgsFromVp(argc, vp);
2508 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_getISOFields>(
2509 cx, args);
2513 * Temporal.PlainDateTime.prototype.getCalendar ( )
2515 static bool PlainDateTime_getCalendar(JSContext* cx, const CallArgs& args) {
2516 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2517 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2519 // Step 3.
2520 auto* obj = ToTemporalCalendarObject(cx, calendar);
2521 if (!obj) {
2522 return false;
2525 args.rval().setObject(*obj);
2526 return true;
2530 * Temporal.PlainDateTime.prototype.getCalendar ( )
2532 static bool PlainDateTime_getCalendar(JSContext* cx, unsigned argc, Value* vp) {
2533 // Steps 1-2.
2534 CallArgs args = CallArgsFromVp(argc, vp);
2535 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_getCalendar>(cx,
2536 args);
2540 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2541 * options ] )
2543 static bool PlainDateTime_toZonedDateTime(JSContext* cx, const CallArgs& args) {
2544 Rooted<PlainDateTimeObject*> dateTime(
2545 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2546 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2548 // Step 3.
2549 Rooted<TimeZoneValue> timeZone(cx);
2550 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
2551 return false;
2554 auto disambiguation = TemporalDisambiguation::Compatible;
2555 if (args.hasDefined(1)) {
2556 // Step 4.
2557 Rooted<JSObject*> options(
2558 cx, RequireObjectArg(cx, "options", "toZonedDateTime", args[1]));
2559 if (!options) {
2560 return false;
2563 // Step 5.
2564 if (!ToTemporalDisambiguation(cx, options, &disambiguation)) {
2565 return false;
2569 // Steps 6-7.
2570 Instant instant;
2571 if (!GetInstantFor(cx, timeZone, dateTime, disambiguation, &instant)) {
2572 return false;
2575 // Step 8.
2576 auto* result = CreateTemporalZonedDateTime(cx, instant, timeZone, calendar);
2577 if (!result) {
2578 return false;
2581 args.rval().setObject(*result);
2582 return true;
2586 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2587 * options ] )
2589 static bool PlainDateTime_toZonedDateTime(JSContext* cx, unsigned argc,
2590 Value* vp) {
2591 // Steps 1-2.
2592 CallArgs args = CallArgsFromVp(argc, vp);
2593 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toZonedDateTime>(
2594 cx, args);
2598 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2600 static bool PlainDateTime_toPlainDate(JSContext* cx, const CallArgs& args) {
2601 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2602 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2604 // Step 3.
2605 auto* obj = CreateTemporalDate(cx, ToPlainDate(dateTime), calendar);
2606 if (!obj) {
2607 return false;
2610 args.rval().setObject(*obj);
2611 return true;
2615 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2617 static bool PlainDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
2618 // Steps 1-2.
2619 CallArgs args = CallArgsFromVp(argc, vp);
2620 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainDate>(cx,
2621 args);
2625 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2627 static bool PlainDateTime_toPlainYearMonth(JSContext* cx,
2628 const CallArgs& args) {
2629 Rooted<PlainDateTimeObject*> dateTime(
2630 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2631 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
2633 // Step 3.
2634 Rooted<CalendarRecord> calendar(cx);
2635 if (!CreateCalendarMethodsRecord(cx, calendarValue,
2637 CalendarMethod::Fields,
2638 CalendarMethod::YearMonthFromFields,
2640 &calendar)) {
2641 return false;
2644 // Step 4.
2645 JS::RootedVector<PropertyKey> fieldNames(cx);
2646 if (!CalendarFields(cx, calendar,
2647 {CalendarField::MonthCode, CalendarField::Year},
2648 &fieldNames)) {
2649 return false;
2652 // Step 4.
2653 Rooted<PlainObject*> fields(cx,
2654 PrepareTemporalFields(cx, dateTime, fieldNames));
2655 if (!fields) {
2656 return false;
2659 // Step 5.
2660 auto obj = CalendarYearMonthFromFields(cx, calendar, fields);
2661 if (!obj) {
2662 return false;
2665 args.rval().setObject(*obj);
2666 return true;
2670 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2672 static bool PlainDateTime_toPlainYearMonth(JSContext* cx, unsigned argc,
2673 Value* vp) {
2674 // Steps 1-2.
2675 CallArgs args = CallArgsFromVp(argc, vp);
2676 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainYearMonth>(
2677 cx, args);
2681 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2683 static bool PlainDateTime_toPlainMonthDay(JSContext* cx, const CallArgs& args) {
2684 Rooted<PlainDateTimeObject*> dateTime(
2685 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2686 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
2688 // Step 3.
2689 Rooted<CalendarRecord> calendar(cx);
2690 if (!CreateCalendarMethodsRecord(cx, calendarValue,
2692 CalendarMethod::Fields,
2693 CalendarMethod::MonthDayFromFields,
2695 &calendar)) {
2696 return false;
2699 // Step 4.
2700 JS::RootedVector<PropertyKey> fieldNames(cx);
2701 if (!CalendarFields(cx, calendar,
2702 {CalendarField::Day, CalendarField::MonthCode},
2703 &fieldNames)) {
2704 return false;
2707 // Step 5.
2708 Rooted<PlainObject*> fields(cx,
2709 PrepareTemporalFields(cx, dateTime, fieldNames));
2710 if (!fields) {
2711 return false;
2714 // Steps 6-7.
2715 auto obj = CalendarMonthDayFromFields(cx, calendar, fields);
2716 if (!obj) {
2717 return false;
2720 args.rval().setObject(*obj);
2721 return true;
2725 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2727 static bool PlainDateTime_toPlainMonthDay(JSContext* cx, unsigned argc,
2728 Value* vp) {
2729 // Steps 1-2.
2730 CallArgs args = CallArgsFromVp(argc, vp);
2731 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainMonthDay>(
2732 cx, args);
2736 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2738 static bool PlainDateTime_toPlainTime(JSContext* cx, const CallArgs& args) {
2739 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2741 // Step 3.
2742 auto* obj = CreateTemporalTime(cx, ToPlainTime(dateTime));
2743 if (!obj) {
2744 return false;
2747 args.rval().setObject(*obj);
2748 return true;
2752 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2754 static bool PlainDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) {
2755 // Steps 1-2.
2756 CallArgs args = CallArgsFromVp(argc, vp);
2757 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainTime>(cx,
2758 args);
2761 const JSClass PlainDateTimeObject::class_ = {
2762 "Temporal.PlainDateTime",
2763 JSCLASS_HAS_RESERVED_SLOTS(PlainDateTimeObject::SLOT_COUNT) |
2764 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainDateTime),
2765 JS_NULL_CLASS_OPS,
2766 &PlainDateTimeObject::classSpec_,
2769 const JSClass& PlainDateTimeObject::protoClass_ = PlainObject::class_;
2771 static const JSFunctionSpec PlainDateTime_methods[] = {
2772 JS_FN("from", PlainDateTime_from, 1, 0),
2773 JS_FN("compare", PlainDateTime_compare, 2, 0),
2774 JS_FS_END,
2777 static const JSFunctionSpec PlainDateTime_prototype_methods[] = {
2778 JS_FN("with", PlainDateTime_with, 1, 0),
2779 JS_FN("withPlainTime", PlainDateTime_withPlainTime, 0, 0),
2780 JS_FN("withPlainDate", PlainDateTime_withPlainDate, 1, 0),
2781 JS_FN("withCalendar", PlainDateTime_withCalendar, 1, 0),
2782 JS_FN("add", PlainDateTime_add, 1, 0),
2783 JS_FN("subtract", PlainDateTime_subtract, 1, 0),
2784 JS_FN("until", PlainDateTime_until, 1, 0),
2785 JS_FN("since", PlainDateTime_since, 1, 0),
2786 JS_FN("round", PlainDateTime_round, 1, 0),
2787 JS_FN("equals", PlainDateTime_equals, 1, 0),
2788 JS_FN("toString", PlainDateTime_toString, 0, 0),
2789 JS_FN("toLocaleString", PlainDateTime_toLocaleString, 0, 0),
2790 JS_FN("toJSON", PlainDateTime_toJSON, 0, 0),
2791 JS_FN("valueOf", PlainDateTime_valueOf, 0, 0),
2792 JS_FN("toZonedDateTime", PlainDateTime_toZonedDateTime, 1, 0),
2793 JS_FN("toPlainDate", PlainDateTime_toPlainDate, 0, 0),
2794 JS_FN("toPlainYearMonth", PlainDateTime_toPlainYearMonth, 0, 0),
2795 JS_FN("toPlainMonthDay", PlainDateTime_toPlainMonthDay, 0, 0),
2796 JS_FN("toPlainTime", PlainDateTime_toPlainTime, 0, 0),
2797 JS_FN("getISOFields", PlainDateTime_getISOFields, 0, 0),
2798 JS_FN("getCalendar", PlainDateTime_getCalendar, 0, 0),
2799 JS_FS_END,
2802 static const JSPropertySpec PlainDateTime_prototype_properties[] = {
2803 JS_PSG("calendarId", PlainDateTime_calendarId, 0),
2804 JS_PSG("year", PlainDateTime_year, 0),
2805 JS_PSG("month", PlainDateTime_month, 0),
2806 JS_PSG("monthCode", PlainDateTime_monthCode, 0),
2807 JS_PSG("day", PlainDateTime_day, 0),
2808 JS_PSG("hour", PlainDateTime_hour, 0),
2809 JS_PSG("minute", PlainDateTime_minute, 0),
2810 JS_PSG("second", PlainDateTime_second, 0),
2811 JS_PSG("millisecond", PlainDateTime_millisecond, 0),
2812 JS_PSG("microsecond", PlainDateTime_microsecond, 0),
2813 JS_PSG("nanosecond", PlainDateTime_nanosecond, 0),
2814 JS_PSG("dayOfWeek", PlainDateTime_dayOfWeek, 0),
2815 JS_PSG("dayOfYear", PlainDateTime_dayOfYear, 0),
2816 JS_PSG("weekOfYear", PlainDateTime_weekOfYear, 0),
2817 JS_PSG("yearOfWeek", PlainDateTime_yearOfWeek, 0),
2818 JS_PSG("daysInWeek", PlainDateTime_daysInWeek, 0),
2819 JS_PSG("daysInMonth", PlainDateTime_daysInMonth, 0),
2820 JS_PSG("daysInYear", PlainDateTime_daysInYear, 0),
2821 JS_PSG("monthsInYear", PlainDateTime_monthsInYear, 0),
2822 JS_PSG("inLeapYear", PlainDateTime_inLeapYear, 0),
2823 JS_STRING_SYM_PS(toStringTag, "Temporal.PlainDateTime", JSPROP_READONLY),
2824 JS_PS_END,
2827 const ClassSpec PlainDateTimeObject::classSpec_ = {
2828 GenericCreateConstructor<PlainDateTimeConstructor, 3,
2829 gc::AllocKind::FUNCTION>,
2830 GenericCreatePrototype<PlainDateTimeObject>,
2831 PlainDateTime_methods,
2832 nullptr,
2833 PlainDateTime_prototype_methods,
2834 PlainDateTime_prototype_properties,
2835 nullptr,
2836 ClassSpec::DontDefineConstructor,