Bug 1874684 - Part 25: Editorial updates. r=dminor
[gecko.git] / js / src / builtin / temporal / PlainDateTime.cpp
blobddb01a3a1b8dcf0f7e962d5cda60197947e2dac5
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 NormalizedDuration* result) {
888 return ::DifferenceISODateTime(cx, one, two, calendar, largestUnit, nullptr,
889 result);
893 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
894 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
896 bool js::temporal::DifferenceISODateTime(
897 JSContext* cx, const PlainDateTime& one, const PlainDateTime& two,
898 Handle<CalendarRecord> calendar, TemporalUnit largestUnit,
899 Handle<PlainObject*> options, NormalizedDuration* result) {
900 return ::DifferenceISODateTime(cx, one, two, calendar, largestUnit, options,
901 result);
905 * RoundISODateTime ( year, month, day, hour, minute, second, millisecond,
906 * microsecond, nanosecond, increment, unit, roundingMode [ , dayLength ] )
908 static PlainDateTime RoundISODateTime(const PlainDateTime& dateTime,
909 Increment increment, TemporalUnit unit,
910 TemporalRoundingMode roundingMode) {
911 const auto& [date, time] = dateTime;
913 // Step 1.
914 MOZ_ASSERT(IsValidISODateTime(dateTime));
915 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
917 // Step 2. (Not applicable in our implementation.)
919 // Step 3.
920 auto roundedTime = RoundTime(time, increment, unit, roundingMode);
921 MOZ_ASSERT(0 <= roundedTime.days && roundedTime.days <= 1);
923 // Step 4.
924 auto balanceResult = BalanceISODate(date.year, date.month,
925 date.day + int32_t(roundedTime.days));
927 // Step 5.
928 return {balanceResult, roundedTime.time};
932 * DifferenceTemporalPlainDateTime ( operation, dateTime, other, options )
934 static bool DifferenceTemporalPlainDateTime(JSContext* cx,
935 TemporalDifference operation,
936 const CallArgs& args) {
937 Rooted<PlainDateTimeWithCalendar> dateTime(
938 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
940 // Step 1. (Not applicable in our implementation.)
942 // Step 2.
943 Rooted<PlainDateTimeWithCalendar> other(cx);
944 if (!::ToTemporalDateTime(cx, args.get(0), &other)) {
945 return false;
948 // Step 3.
949 if (!CalendarEqualsOrThrow(cx, dateTime.calendar(), other.calendar())) {
950 return false;
953 // Steps 4-5.
954 DifferenceSettings settings;
955 Rooted<PlainObject*> resolvedOptions(cx);
956 if (args.hasDefined(1)) {
957 Rooted<JSObject*> options(
958 cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
959 if (!options) {
960 return false;
963 // Step 4.
964 resolvedOptions = SnapshotOwnProperties(cx, options);
965 if (!resolvedOptions) {
966 return false;
969 // Step 5.
970 if (!GetDifferenceSettings(
971 cx, operation, resolvedOptions, TemporalUnitGroup::DateTime,
972 TemporalUnit::Nanosecond, TemporalUnit::Day, &settings)) {
973 return false;
975 } else {
976 // Steps 4-5.
977 settings = {
978 TemporalUnit::Nanosecond,
979 TemporalUnit::Day,
980 TemporalRoundingMode::Trunc,
981 Increment{1},
985 // Steps 6-7.
986 bool datePartsIdentical = dateTime.date() == other.date();
988 // Step 8.
989 if (datePartsIdentical && dateTime.time() == other.time()) {
990 auto* obj = CreateTemporalDuration(cx, {});
991 if (!obj) {
992 return false;
995 args.rval().setObject(*obj);
996 return true;
999 // Step 9.
1000 Rooted<CalendarRecord> calendar(cx);
1001 if (!CreateCalendarMethodsRecord(cx, dateTime.calendar(),
1003 CalendarMethod::DateAdd,
1004 CalendarMethod::DateUntil,
1006 &calendar)) {
1007 return false;
1010 // Step 10.
1011 NormalizedDuration diff;
1012 if (!::DifferenceISODateTime(cx, dateTime, other, calendar,
1013 settings.largestUnit, resolvedOptions, &diff)) {
1014 return false;
1017 // Step 11.
1018 bool roundingGranularityIsNoop =
1019 settings.smallestUnit == TemporalUnit::Nanosecond &&
1020 settings.roundingIncrement == Increment{1};
1022 // Steps 12-13.
1023 DateDuration balancedDate;
1024 TimeDuration balancedTime;
1025 if (!roundingGranularityIsNoop) {
1026 // Step 12.a.
1027 Rooted<PlainDateObject*> relativeTo(
1028 cx, CreateTemporalDate(cx, dateTime.date(), dateTime.calendar()));
1029 if (!relativeTo) {
1030 return false;
1033 // Steps 12.b-c.
1034 NormalizedDuration roundResult;
1035 if (!temporal::RoundDuration(cx, diff, settings.roundingIncrement,
1036 settings.smallestUnit, settings.roundingMode,
1037 relativeTo, calendar, &roundResult)) {
1038 return false;
1041 // Step 12.d.
1042 NormalizedTimeDuration withDays;
1043 if (!Add24HourDaysToNormalizedTimeDuration(
1044 cx, roundResult.time, roundResult.date.days, &withDays)) {
1045 return false;
1048 // Step 12.e.
1049 balancedTime = BalanceTimeDuration(withDays, settings.largestUnit);
1051 // Step 12.f.
1052 auto toBalance = DateDuration{
1053 roundResult.date.years,
1054 roundResult.date.months,
1055 roundResult.date.weeks,
1056 balancedTime.days,
1058 if (!temporal::BalanceDateDurationRelative(
1059 cx, toBalance, settings.largestUnit, settings.smallestUnit,
1060 relativeTo, calendar, &balancedDate)) {
1061 return false;
1063 } else {
1064 // Step 13.a.
1065 NormalizedTimeDuration withDays;
1066 if (!Add24HourDaysToNormalizedTimeDuration(cx, diff.time, diff.date.days,
1067 &withDays)) {
1068 return false;
1071 // Step 13.b.
1072 balancedTime = BalanceTimeDuration(withDays, settings.largestUnit);
1074 // Step 13.c.
1075 balancedDate = {
1076 diff.date.years,
1077 diff.date.months,
1078 diff.date.weeks,
1079 balancedTime.days,
1082 MOZ_ASSERT(IsValidDuration(balancedDate));
1084 // Step 14.
1085 Duration duration = {
1086 double(balancedDate.years), double(balancedDate.months),
1087 double(balancedDate.weeks), double(balancedDate.days),
1088 double(balancedTime.hours), double(balancedTime.minutes),
1089 double(balancedTime.seconds), double(balancedTime.milliseconds),
1090 balancedTime.microseconds, balancedTime.nanoseconds,
1092 if (operation == TemporalDifference::Since) {
1093 duration = duration.negate();
1096 auto* obj = CreateTemporalDuration(cx, duration);
1097 if (!obj) {
1098 return false;
1101 args.rval().setObject(*obj);
1102 return true;
1105 enum class PlainDateTimeDuration { Add, Subtract };
1108 * AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime,
1109 * temporalDurationLike, options )
1111 static bool AddDurationToOrSubtractDurationFromPlainDateTime(
1112 JSContext* cx, PlainDateTimeDuration operation, const CallArgs& args) {
1113 Rooted<PlainDateTimeWithCalendar> dateTime(
1114 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1116 // Step 1. (Not applicable in our implementation.)
1118 // Step 2.
1119 Duration duration;
1120 if (!ToTemporalDurationRecord(cx, args.get(0), &duration)) {
1121 return false;
1124 // Step 3.
1125 Rooted<JSObject*> options(cx);
1126 if (args.hasDefined(1)) {
1127 const char* name =
1128 operation == PlainDateTimeDuration::Add ? "add" : "subtract";
1129 options = RequireObjectArg(cx, "options", name, args[1]);
1130 } else {
1131 options = NewPlainObjectWithProto(cx, nullptr);
1133 if (!options) {
1134 return false;
1137 // Step 4.
1138 Rooted<CalendarRecord> calendar(cx);
1139 if (!CreateCalendarMethodsRecord(cx, dateTime.calendar(),
1141 CalendarMethod::DateAdd,
1143 &calendar)) {
1144 return false;
1147 // Step 5.
1148 if (operation == PlainDateTimeDuration::Subtract) {
1149 duration = duration.negate();
1151 auto normalized = CreateNormalizedDurationRecord(duration);
1153 // Step 6
1154 PlainDateTime result;
1155 if (!AddDateTime(cx, dateTime, calendar, normalized, options, &result)) {
1156 return false;
1159 // Steps 7-8.
1160 MOZ_ASSERT(IsValidISODateTime(result));
1162 // Step 9.
1163 auto* obj = CreateTemporalDateTime(cx, result, dateTime.calendar());
1164 if (!obj) {
1165 return false;
1168 args.rval().setObject(*obj);
1169 return true;
1173 * Temporal.PlainDateTime ( isoYear, isoMonth, isoDay [ , hour [ , minute [ ,
1174 * second [ , millisecond [ , microsecond [ , nanosecond [ , calendarLike ] ] ]
1175 * ] ] ] ] )
1177 static bool PlainDateTimeConstructor(JSContext* cx, unsigned argc, Value* vp) {
1178 CallArgs args = CallArgsFromVp(argc, vp);
1180 // Step 1.
1181 if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainDateTime")) {
1182 return false;
1185 // Step 2.
1186 double isoYear;
1187 if (!ToIntegerWithTruncation(cx, args.get(0), "year", &isoYear)) {
1188 return false;
1191 // Step 3.
1192 double isoMonth;
1193 if (!ToIntegerWithTruncation(cx, args.get(1), "month", &isoMonth)) {
1194 return false;
1197 // Step 4.
1198 double isoDay;
1199 if (!ToIntegerWithTruncation(cx, args.get(2), "day", &isoDay)) {
1200 return false;
1203 // Step 5.
1204 double hour = 0;
1205 if (args.hasDefined(3)) {
1206 if (!ToIntegerWithTruncation(cx, args[3], "hour", &hour)) {
1207 return false;
1211 // Step 6.
1212 double minute = 0;
1213 if (args.hasDefined(4)) {
1214 if (!ToIntegerWithTruncation(cx, args[4], "minute", &minute)) {
1215 return false;
1219 // Step 7.
1220 double second = 0;
1221 if (args.hasDefined(5)) {
1222 if (!ToIntegerWithTruncation(cx, args[5], "second", &second)) {
1223 return false;
1227 // Step 8.
1228 double millisecond = 0;
1229 if (args.hasDefined(6)) {
1230 if (!ToIntegerWithTruncation(cx, args[6], "millisecond", &millisecond)) {
1231 return false;
1235 // Step 9.
1236 double microsecond = 0;
1237 if (args.hasDefined(7)) {
1238 if (!ToIntegerWithTruncation(cx, args[7], "microsecond", &microsecond)) {
1239 return false;
1243 // Step 10.
1244 double nanosecond = 0;
1245 if (args.hasDefined(8)) {
1246 if (!ToIntegerWithTruncation(cx, args[8], "nanosecond", &nanosecond)) {
1247 return false;
1251 // Step 11.
1252 Rooted<CalendarValue> calendar(cx);
1253 if (!ToTemporalCalendarWithISODefault(cx, args.get(9), &calendar)) {
1254 return false;
1257 // Step 12.
1258 auto* temporalDateTime = CreateTemporalDateTime(
1259 cx, args, isoYear, isoMonth, isoDay, hour, minute, second, millisecond,
1260 microsecond, nanosecond, calendar);
1261 if (!temporalDateTime) {
1262 return false;
1265 args.rval().setObject(*temporalDateTime);
1266 return true;
1270 * Temporal.PlainDateTime.from ( item [ , options ] )
1272 static bool PlainDateTime_from(JSContext* cx, unsigned argc, Value* vp) {
1273 CallArgs args = CallArgsFromVp(argc, vp);
1275 // Step 1.
1276 Rooted<JSObject*> options(cx);
1277 if (args.hasDefined(1)) {
1278 options = RequireObjectArg(cx, "options", "from", args[1]);
1279 if (!options) {
1280 return false;
1284 // Step 2.
1285 if (args.get(0).isObject()) {
1286 JSObject* item = &args[0].toObject();
1287 if (auto* temporalDateTime = item->maybeUnwrapIf<PlainDateTimeObject>()) {
1288 auto dateTime = ToPlainDateTime(temporalDateTime);
1290 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1291 if (!calendar.wrap(cx)) {
1292 return false;
1295 if (options) {
1296 // Step 2.a.
1297 TemporalOverflow ignored;
1298 if (!ToTemporalOverflow(cx, options, &ignored)) {
1299 return false;
1303 // Step 2.b.
1304 auto* result = CreateTemporalDateTime(cx, dateTime, calendar);
1305 if (!result) {
1306 return false;
1309 args.rval().setObject(*result);
1310 return true;
1314 // Step 3.
1315 auto result = ToTemporalDateTime(cx, args.get(0), options);
1316 if (!result) {
1317 return false;
1320 args.rval().setObject(*result);
1321 return true;
1325 * Temporal.PlainDateTime.compare ( one, two )
1327 static bool PlainDateTime_compare(JSContext* cx, unsigned argc, Value* vp) {
1328 CallArgs args = CallArgsFromVp(argc, vp);
1330 // Step 1.
1331 PlainDateTime one;
1332 if (!ToTemporalDateTime(cx, args.get(0), &one)) {
1333 return false;
1336 // Step 2.
1337 PlainDateTime two;
1338 if (!ToTemporalDateTime(cx, args.get(1), &two)) {
1339 return false;
1342 // Step 3.
1343 args.rval().setInt32(CompareISODateTime(one, two));
1344 return true;
1348 * get Temporal.PlainDateTime.prototype.calendarId
1350 static bool PlainDateTime_calendarId(JSContext* cx, const CallArgs& args) {
1351 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1353 // Step 3.
1354 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1355 auto* calendarId = ToTemporalCalendarIdentifier(cx, calendar);
1356 if (!calendarId) {
1357 return false;
1360 args.rval().setString(calendarId);
1361 return true;
1365 * get Temporal.PlainDateTime.prototype.calendarId
1367 static bool PlainDateTime_calendarId(JSContext* cx, unsigned argc, Value* vp) {
1368 // Steps 1-2.
1369 CallArgs args = CallArgsFromVp(argc, vp);
1370 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_calendarId>(cx,
1371 args);
1375 * get Temporal.PlainDateTime.prototype.year
1377 static bool PlainDateTime_year(JSContext* cx, const CallArgs& args) {
1378 // Step 3.
1379 Rooted<PlainDateTimeObject*> dateTime(
1380 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1381 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1383 // Step 4.
1384 return CalendarYear(cx, calendar, dateTime, args.rval());
1388 * get Temporal.PlainDateTime.prototype.year
1390 static bool PlainDateTime_year(JSContext* cx, unsigned argc, Value* vp) {
1391 // Steps 1-2.
1392 CallArgs args = CallArgsFromVp(argc, vp);
1393 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_year>(cx, args);
1397 * get Temporal.PlainDateTime.prototype.month
1399 static bool PlainDateTime_month(JSContext* cx, const CallArgs& args) {
1400 // Step 3.
1401 Rooted<PlainDateTimeObject*> dateTime(
1402 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1403 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1405 // Step 4.
1406 return CalendarMonth(cx, calendar, dateTime, args.rval());
1410 * get Temporal.PlainDateTime.prototype.month
1412 static bool PlainDateTime_month(JSContext* cx, unsigned argc, Value* vp) {
1413 // Steps 1-2.
1414 CallArgs args = CallArgsFromVp(argc, vp);
1415 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_month>(cx, args);
1419 * get Temporal.PlainDateTime.prototype.monthCode
1421 static bool PlainDateTime_monthCode(JSContext* cx, const CallArgs& args) {
1422 // Step 3.
1423 Rooted<PlainDateTimeObject*> dateTime(
1424 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1425 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1427 // Step 4.
1428 return CalendarMonthCode(cx, calendar, dateTime, args.rval());
1432 * get Temporal.PlainDateTime.prototype.monthCode
1434 static bool PlainDateTime_monthCode(JSContext* cx, unsigned argc, Value* vp) {
1435 // Steps 1-2.
1436 CallArgs args = CallArgsFromVp(argc, vp);
1437 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthCode>(cx,
1438 args);
1442 * get Temporal.PlainDateTime.prototype.day
1444 static bool PlainDateTime_day(JSContext* cx, const CallArgs& args) {
1445 // Step 3.
1446 Rooted<PlainDateTimeObject*> dateTime(
1447 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1448 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1450 // Step 4.
1451 return CalendarDay(cx, calendar, dateTime, args.rval());
1455 * get Temporal.PlainDateTime.prototype.day
1457 static bool PlainDateTime_day(JSContext* cx, unsigned argc, Value* vp) {
1458 // Steps 1-2.
1459 CallArgs args = CallArgsFromVp(argc, vp);
1460 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_day>(cx, args);
1464 * get Temporal.PlainDateTime.prototype.hour
1466 static bool PlainDateTime_hour(JSContext* cx, const CallArgs& args) {
1467 // Step 3.
1468 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1469 args.rval().setInt32(dateTime->isoHour());
1470 return true;
1474 * get Temporal.PlainDateTime.prototype.hour
1476 static bool PlainDateTime_hour(JSContext* cx, unsigned argc, Value* vp) {
1477 // Steps 1-2.
1478 CallArgs args = CallArgsFromVp(argc, vp);
1479 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_hour>(cx, args);
1483 * get Temporal.PlainDateTime.prototype.minute
1485 static bool PlainDateTime_minute(JSContext* cx, const CallArgs& args) {
1486 // Step 3.
1487 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1488 args.rval().setInt32(dateTime->isoMinute());
1489 return true;
1493 * get Temporal.PlainDateTime.prototype.minute
1495 static bool PlainDateTime_minute(JSContext* cx, unsigned argc, Value* vp) {
1496 // Steps 1-2.
1497 CallArgs args = CallArgsFromVp(argc, vp);
1498 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_minute>(cx, args);
1502 * get Temporal.PlainDateTime.prototype.second
1504 static bool PlainDateTime_second(JSContext* cx, const CallArgs& args) {
1505 // Step 3.
1506 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1507 args.rval().setInt32(dateTime->isoSecond());
1508 return true;
1512 * get Temporal.PlainDateTime.prototype.second
1514 static bool PlainDateTime_second(JSContext* cx, unsigned argc, Value* vp) {
1515 // Steps 1-2.
1516 CallArgs args = CallArgsFromVp(argc, vp);
1517 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_second>(cx, args);
1521 * get Temporal.PlainDateTime.prototype.millisecond
1523 static bool PlainDateTime_millisecond(JSContext* cx, const CallArgs& args) {
1524 // Step 3.
1525 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1526 args.rval().setInt32(dateTime->isoMillisecond());
1527 return true;
1531 * get Temporal.PlainDateTime.prototype.millisecond
1533 static bool PlainDateTime_millisecond(JSContext* cx, unsigned argc, Value* vp) {
1534 // Steps 1-2.
1535 CallArgs args = CallArgsFromVp(argc, vp);
1536 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_millisecond>(cx,
1537 args);
1541 * get Temporal.PlainDateTime.prototype.microsecond
1543 static bool PlainDateTime_microsecond(JSContext* cx, const CallArgs& args) {
1544 // Step 3.
1545 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1546 args.rval().setInt32(dateTime->isoMicrosecond());
1547 return true;
1551 * get Temporal.PlainDateTime.prototype.microsecond
1553 static bool PlainDateTime_microsecond(JSContext* cx, unsigned argc, Value* vp) {
1554 // Steps 1-2.
1555 CallArgs args = CallArgsFromVp(argc, vp);
1556 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_microsecond>(cx,
1557 args);
1561 * get Temporal.PlainDateTime.prototype.nanosecond
1563 static bool PlainDateTime_nanosecond(JSContext* cx, const CallArgs& args) {
1564 // Step 3.
1565 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1566 args.rval().setInt32(dateTime->isoNanosecond());
1567 return true;
1571 * get Temporal.PlainDateTime.prototype.nanosecond
1573 static bool PlainDateTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) {
1574 // Steps 1-2.
1575 CallArgs args = CallArgsFromVp(argc, vp);
1576 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_nanosecond>(cx,
1577 args);
1581 * get Temporal.PlainDateTime.prototype.dayOfWeek
1583 static bool PlainDateTime_dayOfWeek(JSContext* cx, const CallArgs& args) {
1584 // Step 3.
1585 Rooted<PlainDateTimeObject*> dateTime(
1586 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1587 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1589 // Step 4.
1590 return CalendarDayOfWeek(cx, calendar, dateTime, args.rval());
1594 * get Temporal.PlainDateTime.prototype.dayOfWeek
1596 static bool PlainDateTime_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) {
1597 // Steps 1-2.
1598 CallArgs args = CallArgsFromVp(argc, vp);
1599 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfWeek>(cx,
1600 args);
1604 * get Temporal.PlainDateTime.prototype.dayOfYear
1606 static bool PlainDateTime_dayOfYear(JSContext* cx, const CallArgs& args) {
1607 // Step 3.
1608 Rooted<PlainDateTimeObject*> dateTime(
1609 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1610 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1612 // Step 4.
1613 return CalendarDayOfYear(cx, calendar, dateTime, args.rval());
1617 * get Temporal.PlainDateTime.prototype.dayOfYear
1619 static bool PlainDateTime_dayOfYear(JSContext* cx, unsigned argc, Value* vp) {
1620 // Steps 1-2.
1621 CallArgs args = CallArgsFromVp(argc, vp);
1622 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfYear>(cx,
1623 args);
1627 * get Temporal.PlainDateTime.prototype.weekOfYear
1629 static bool PlainDateTime_weekOfYear(JSContext* cx, const CallArgs& args) {
1630 // Step 3.
1631 Rooted<PlainDateTimeObject*> dateTime(
1632 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1633 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1635 // Step 4.
1636 return CalendarWeekOfYear(cx, calendar, dateTime, args.rval());
1640 * get Temporal.PlainDateTime.prototype.weekOfYear
1642 static bool PlainDateTime_weekOfYear(JSContext* cx, unsigned argc, Value* vp) {
1643 // Steps 1-2.
1644 CallArgs args = CallArgsFromVp(argc, vp);
1645 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_weekOfYear>(cx,
1646 args);
1650 * get Temporal.PlainDateTime.prototype.yearOfWeek
1652 static bool PlainDateTime_yearOfWeek(JSContext* cx, const CallArgs& args) {
1653 // Step 3.
1654 Rooted<PlainDateTimeObject*> dateTime(
1655 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1656 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1658 // Step 4.
1659 return CalendarYearOfWeek(cx, calendar, dateTime, args.rval());
1663 * get Temporal.PlainDateTime.prototype.yearOfWeek
1665 static bool PlainDateTime_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) {
1666 // Steps 1-2.
1667 CallArgs args = CallArgsFromVp(argc, vp);
1668 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_yearOfWeek>(cx,
1669 args);
1673 * get Temporal.PlainDateTime.prototype.daysInWeek
1675 static bool PlainDateTime_daysInWeek(JSContext* cx, const CallArgs& args) {
1676 // Step 3.
1677 Rooted<PlainDateTimeObject*> dateTime(
1678 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1679 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1681 // Step 4.
1682 return CalendarDaysInWeek(cx, calendar, dateTime, args.rval());
1686 * get Temporal.PlainDateTime.prototype.daysInWeek
1688 static bool PlainDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) {
1689 // Steps 1-2.
1690 CallArgs args = CallArgsFromVp(argc, vp);
1691 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInWeek>(cx,
1692 args);
1696 * get Temporal.PlainDateTime.prototype.daysInMonth
1698 static bool PlainDateTime_daysInMonth(JSContext* cx, const CallArgs& args) {
1699 // Step 3.
1700 Rooted<PlainDateTimeObject*> dateTime(
1701 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1702 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1704 // Step 4.
1705 return CalendarDaysInMonth(cx, calendar, dateTime, args.rval());
1709 * get Temporal.PlainDateTime.prototype.daysInMonth
1711 static bool PlainDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) {
1712 // Steps 1-2.
1713 CallArgs args = CallArgsFromVp(argc, vp);
1714 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInMonth>(cx,
1715 args);
1719 * get Temporal.PlainDateTime.prototype.daysInYear
1721 static bool PlainDateTime_daysInYear(JSContext* cx, const CallArgs& args) {
1722 // Step 3.
1723 Rooted<PlainDateTimeObject*> dateTime(
1724 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1725 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1727 // Step 4.
1728 return CalendarDaysInYear(cx, calendar, dateTime, args.rval());
1732 * get Temporal.PlainDateTime.prototype.daysInYear
1734 static bool PlainDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) {
1735 // Steps 1-2.
1736 CallArgs args = CallArgsFromVp(argc, vp);
1737 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInYear>(cx,
1738 args);
1742 * get Temporal.PlainDateTime.prototype.monthsInYear
1744 static bool PlainDateTime_monthsInYear(JSContext* cx, const CallArgs& args) {
1745 // Step 3.
1746 Rooted<PlainDateTimeObject*> dateTime(
1747 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1748 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1750 // Step 4.
1751 return CalendarMonthsInYear(cx, calendar, dateTime, args.rval());
1755 * get Temporal.PlainDateTime.prototype.monthsInYear
1757 static bool PlainDateTime_monthsInYear(JSContext* cx, unsigned argc,
1758 Value* vp) {
1759 // Steps 1-2.
1760 CallArgs args = CallArgsFromVp(argc, vp);
1761 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthsInYear>(
1762 cx, args);
1766 * get Temporal.PlainDateTime.prototype.inLeapYear
1768 static bool PlainDateTime_inLeapYear(JSContext* cx, const CallArgs& args) {
1769 // Step 3.
1770 Rooted<PlainDateTimeObject*> dateTime(
1771 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1772 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
1774 // Step 4.
1775 return CalendarInLeapYear(cx, calendar, dateTime, args.rval());
1779 * get Temporal.PlainDateTime.prototype.inLeapYear
1781 static bool PlainDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) {
1782 // Steps 1-2.
1783 CallArgs args = CallArgsFromVp(argc, vp);
1784 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_inLeapYear>(cx,
1785 args);
1789 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
1791 static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
1792 Rooted<PlainDateTimeObject*> dateTime(
1793 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
1795 // Step 3.
1796 Rooted<JSObject*> temporalDateTimeLike(
1797 cx, RequireObjectArg(cx, "temporalDateTimeLike", "with", args.get(0)));
1798 if (!temporalDateTimeLike) {
1799 return false;
1801 if (!ThrowIfTemporalLikeObject(cx, temporalDateTimeLike)) {
1802 return false;
1805 // Step 4.
1806 Rooted<PlainObject*> resolvedOptions(cx);
1807 if (args.hasDefined(1)) {
1808 Rooted<JSObject*> options(cx,
1809 RequireObjectArg(cx, "options", "with", args[1]));
1810 if (!options) {
1811 return false;
1813 resolvedOptions = SnapshotOwnProperties(cx, options);
1814 } else {
1815 resolvedOptions = NewPlainObjectWithProto(cx, nullptr);
1817 if (!resolvedOptions) {
1818 return false;
1821 // Step 5.
1822 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
1823 Rooted<CalendarRecord> calendar(cx);
1824 if (!CreateCalendarMethodsRecord(cx, calendarValue,
1826 CalendarMethod::DateFromFields,
1827 CalendarMethod::Fields,
1828 CalendarMethod::MergeFields,
1830 &calendar)) {
1831 return false;
1834 // Step 6.
1835 JS::RootedVector<PropertyKey> fieldNames(cx);
1836 if (!CalendarFields(cx, calendar,
1837 {CalendarField::Day, CalendarField::Month,
1838 CalendarField::MonthCode, CalendarField::Year},
1839 &fieldNames)) {
1840 return false;
1843 // Step 7.
1844 Rooted<PlainObject*> fields(cx,
1845 PrepareTemporalFields(cx, dateTime, fieldNames));
1846 if (!fields) {
1847 return false;
1850 // Steps 8-13.
1851 struct TimeField {
1852 using FieldName = ImmutableTenuredPtr<PropertyName*> JSAtomState::*;
1854 FieldName name;
1855 int32_t value;
1856 } timeFields[] = {
1857 {&JSAtomState::hour, dateTime->isoHour()},
1858 {&JSAtomState::minute, dateTime->isoMinute()},
1859 {&JSAtomState::second, dateTime->isoSecond()},
1860 {&JSAtomState::millisecond, dateTime->isoMillisecond()},
1861 {&JSAtomState::microsecond, dateTime->isoMicrosecond()},
1862 {&JSAtomState::nanosecond, dateTime->isoNanosecond()},
1865 Rooted<Value> timeFieldValue(cx);
1866 for (const auto& timeField : timeFields) {
1867 Handle<PropertyName*> name = cx->names().*(timeField.name);
1868 timeFieldValue.setInt32(timeField.value);
1870 if (!DefineDataProperty(cx, fields, name, timeFieldValue)) {
1871 return false;
1875 // Step 14.
1876 if (!AppendSorted(cx, fieldNames.get(),
1878 TemporalField::Hour,
1879 TemporalField::Microsecond,
1880 TemporalField::Millisecond,
1881 TemporalField::Minute,
1882 TemporalField::Nanosecond,
1883 TemporalField::Second,
1884 })) {
1885 return false;
1888 // Step 15.
1889 Rooted<PlainObject*> partialDateTime(
1890 cx, PreparePartialTemporalFields(cx, temporalDateTimeLike, fieldNames));
1891 if (!partialDateTime) {
1892 return false;
1895 // Step 16.
1896 Rooted<JSObject*> mergedFields(
1897 cx, CalendarMergeFields(cx, calendar, fields, partialDateTime));
1898 if (!mergedFields) {
1899 return false;
1902 // Step 17.
1903 fields = PrepareTemporalFields(cx, mergedFields, fieldNames);
1904 if (!fields) {
1905 return false;
1908 // Step 18.
1909 PlainDateTime result;
1910 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, resolvedOptions,
1911 &result)) {
1912 return false;
1915 // Steps 19-20.
1916 MOZ_ASSERT(IsValidISODateTime(result));
1918 // Step 21.
1919 auto* obj = CreateTemporalDateTime(cx, result, calendar.receiver());
1920 if (!obj) {
1921 return false;
1924 args.rval().setObject(*obj);
1925 return true;
1929 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
1931 static bool PlainDateTime_with(JSContext* cx, unsigned argc, Value* vp) {
1932 // Steps 1-2.
1933 CallArgs args = CallArgsFromVp(argc, vp);
1934 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_with>(cx, args);
1938 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1940 static bool PlainDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
1941 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1942 auto date = ToPlainDate(temporalDateTime);
1943 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1945 // Step 4.
1946 PlainTime time = {};
1947 if (args.hasDefined(0)) {
1948 if (!ToTemporalTime(cx, args[0], &time)) {
1949 return false;
1953 // Steps 3 and 5.
1954 auto* obj = CreateTemporalDateTime(cx, {date, time}, calendar);
1955 if (!obj) {
1956 return false;
1959 args.rval().setObject(*obj);
1960 return true;
1964 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1966 static bool PlainDateTime_withPlainTime(JSContext* cx, unsigned argc,
1967 Value* vp) {
1968 // Steps 1-2.
1969 CallArgs args = CallArgsFromVp(argc, vp);
1970 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainTime>(
1971 cx, args);
1975 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
1977 static bool PlainDateTime_withPlainDate(JSContext* cx, const CallArgs& args) {
1978 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
1979 auto time = ToPlainTime(temporalDateTime);
1980 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
1982 // Step 3.
1983 Rooted<PlainDateWithCalendar> plainDate(cx);
1984 if (!ToTemporalDate(cx, args.get(0), &plainDate)) {
1985 return false;
1987 auto date = plainDate.date();
1989 // Step 4.
1990 if (!ConsolidateCalendars(cx, calendar, plainDate.calendar(), &calendar)) {
1991 return false;
1994 // Step 5.
1995 auto* obj = CreateTemporalDateTime(cx, {date, time}, calendar);
1996 if (!obj) {
1997 return false;
2000 args.rval().setObject(*obj);
2001 return true;
2005 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
2007 static bool PlainDateTime_withPlainDate(JSContext* cx, unsigned argc,
2008 Value* vp) {
2009 // Steps 1-2.
2010 CallArgs args = CallArgsFromVp(argc, vp);
2011 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainDate>(
2012 cx, args);
2016 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2018 static bool PlainDateTime_withCalendar(JSContext* cx, const CallArgs& args) {
2019 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2020 auto dateTime = ToPlainDateTime(temporalDateTime);
2022 // Step 3.
2023 Rooted<CalendarValue> calendar(cx);
2024 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) {
2025 return false;
2028 // Step 4.
2029 auto* result = CreateTemporalDateTime(cx, dateTime, calendar);
2030 if (!result) {
2031 return false;
2034 args.rval().setObject(*result);
2035 return true;
2039 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2041 static bool PlainDateTime_withCalendar(JSContext* cx, unsigned argc,
2042 Value* vp) {
2043 // Steps 1-2.
2044 CallArgs args = CallArgsFromVp(argc, vp);
2045 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withCalendar>(
2046 cx, args);
2050 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2052 static bool PlainDateTime_add(JSContext* cx, const CallArgs& args) {
2053 // Step 3.
2054 return AddDurationToOrSubtractDurationFromPlainDateTime(
2055 cx, PlainDateTimeDuration::Add, args);
2059 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2061 static bool PlainDateTime_add(JSContext* cx, unsigned argc, Value* vp) {
2062 // Steps 1-2.
2063 CallArgs args = CallArgsFromVp(argc, vp);
2064 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_add>(cx, args);
2068 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2069 * ] )
2071 static bool PlainDateTime_subtract(JSContext* cx, const CallArgs& args) {
2072 // Step 3.
2073 return AddDurationToOrSubtractDurationFromPlainDateTime(
2074 cx, PlainDateTimeDuration::Subtract, args);
2078 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2079 * ] )
2081 static bool PlainDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) {
2082 // Steps 1-2.
2083 CallArgs args = CallArgsFromVp(argc, vp);
2084 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_subtract>(cx,
2085 args);
2089 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2091 static bool PlainDateTime_until(JSContext* cx, const CallArgs& args) {
2092 // Step 3.
2093 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Until, args);
2097 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2099 static bool PlainDateTime_until(JSContext* cx, unsigned argc, Value* vp) {
2100 // Steps 1-2.
2101 CallArgs args = CallArgsFromVp(argc, vp);
2102 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_until>(cx, args);
2106 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2108 static bool PlainDateTime_since(JSContext* cx, const CallArgs& args) {
2109 // Step 3.
2110 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Since, args);
2114 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2116 static bool PlainDateTime_since(JSContext* cx, unsigned argc, Value* vp) {
2117 // Steps 1-2.
2118 CallArgs args = CallArgsFromVp(argc, vp);
2119 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_since>(cx, args);
2123 * Temporal.PlainDateTime.prototype.round ( roundTo )
2125 static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
2126 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2127 auto dateTime = ToPlainDateTime(temporalDateTime);
2128 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2130 // Steps 3-12.
2131 auto smallestUnit = TemporalUnit::Auto;
2132 auto roundingMode = TemporalRoundingMode::HalfExpand;
2133 auto roundingIncrement = Increment{1};
2134 if (args.get(0).isString()) {
2135 // Step 4. (Not applicable in our implementation.)
2137 // Step 9.
2138 Rooted<JSString*> paramString(cx, args[0].toString());
2139 if (!GetTemporalUnit(cx, paramString, TemporalUnitKey::SmallestUnit,
2140 TemporalUnitGroup::DayTime, &smallestUnit)) {
2141 return false;
2144 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
2145 smallestUnit <= TemporalUnit::Nanosecond);
2147 // Steps 6-8 and 10-12. (Implicit)
2148 } else {
2149 // Steps 3 and 5.
2150 Rooted<JSObject*> roundTo(
2151 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
2152 if (!roundTo) {
2153 return false;
2156 // Steps 6-7.
2157 if (!ToTemporalRoundingIncrement(cx, roundTo, &roundingIncrement)) {
2158 return false;
2161 // Step 8.
2162 if (!ToTemporalRoundingMode(cx, roundTo, &roundingMode)) {
2163 return false;
2166 // Step 9.
2167 if (!GetTemporalUnit(cx, roundTo, TemporalUnitKey::SmallestUnit,
2168 TemporalUnitGroup::DayTime, &smallestUnit)) {
2169 return false;
2172 if (smallestUnit == TemporalUnit::Auto) {
2173 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2174 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
2175 return false;
2178 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
2179 smallestUnit <= TemporalUnit::Nanosecond);
2181 // Steps 10-11.
2182 auto maximum = Increment{1};
2183 bool inclusive = true;
2184 if (smallestUnit > TemporalUnit::Day) {
2185 maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
2186 inclusive = false;
2189 // Step 12.
2190 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
2191 inclusive)) {
2192 return false;
2196 // Step 13.
2197 if (smallestUnit == TemporalUnit::Nanosecond &&
2198 roundingIncrement == Increment{1}) {
2199 auto* obj = CreateTemporalDateTime(cx, dateTime, calendar);
2200 if (!obj) {
2201 return false;
2204 args.rval().setObject(*obj);
2205 return true;
2208 // Step 14.
2209 auto result =
2210 RoundISODateTime(dateTime, roundingIncrement, smallestUnit, roundingMode);
2212 // Step 15.
2213 auto* obj = CreateTemporalDateTime(cx, result, calendar);
2214 if (!obj) {
2215 return false;
2218 args.rval().setObject(*obj);
2219 return true;
2223 * Temporal.PlainDateTime.prototype.round ( roundTo )
2225 static bool PlainDateTime_round(JSContext* cx, unsigned argc, Value* vp) {
2226 // Steps 1-2.
2227 CallArgs args = CallArgsFromVp(argc, vp);
2228 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_round>(cx, args);
2232 * Temporal.PlainDateTime.prototype.equals ( other )
2234 static bool PlainDateTime_equals(JSContext* cx, const CallArgs& args) {
2235 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2236 auto dateTime = ToPlainDateTime(temporalDateTime);
2237 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2239 // Step 3.
2240 Rooted<PlainDateTimeWithCalendar> other(cx);
2241 if (!::ToTemporalDateTime(cx, args.get(0), &other)) {
2242 return false;
2245 // Steps 4-13.
2246 bool equals = dateTime == other.dateTime();
2247 if (equals && !CalendarEquals(cx, calendar, other.calendar(), &equals)) {
2248 return false;
2251 args.rval().setBoolean(equals);
2252 return true;
2256 * Temporal.PlainDateTime.prototype.equals ( other )
2258 static bool PlainDateTime_equals(JSContext* cx, unsigned argc, Value* vp) {
2259 // Steps 1-2.
2260 CallArgs args = CallArgsFromVp(argc, vp);
2261 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_equals>(cx, args);
2265 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2267 static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) {
2268 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2269 auto dt = ToPlainDateTime(dateTime);
2270 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2272 SecondsStringPrecision precision = {Precision::Auto(),
2273 TemporalUnit::Nanosecond, Increment{1}};
2274 auto roundingMode = TemporalRoundingMode::Trunc;
2275 auto showCalendar = CalendarOption::Auto;
2276 if (args.hasDefined(0)) {
2277 // Step 3.
2278 Rooted<JSObject*> options(
2279 cx, RequireObjectArg(cx, "options", "toString", args[0]));
2280 if (!options) {
2281 return false;
2284 // Steps 4-5.
2285 if (!ToCalendarNameOption(cx, options, &showCalendar)) {
2286 return false;
2289 // Step 6.
2290 auto digits = Precision::Auto();
2291 if (!ToFractionalSecondDigits(cx, options, &digits)) {
2292 return false;
2295 // Step 7.
2296 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
2297 return false;
2300 // Step 8.
2301 auto smallestUnit = TemporalUnit::Auto;
2302 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
2303 TemporalUnitGroup::Time, &smallestUnit)) {
2304 return false;
2307 // Step 9.
2308 if (smallestUnit == TemporalUnit::Hour) {
2309 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2310 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
2311 "smallestUnit");
2312 return false;
2315 // Step 10.
2316 precision = ToSecondsStringPrecision(smallestUnit, digits);
2319 // Step 11.
2320 auto result =
2321 RoundISODateTime(dt, precision.increment, precision.unit, roundingMode);
2323 // Step 12.
2324 JSString* str = ::TemporalDateTimeToString(cx, result, calendar,
2325 precision.precision, showCalendar);
2326 if (!str) {
2327 return false;
2330 args.rval().setString(str);
2331 return true;
2335 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2337 static bool PlainDateTime_toString(JSContext* cx, unsigned argc, Value* vp) {
2338 // Steps 1-2.
2339 CallArgs args = CallArgsFromVp(argc, vp);
2340 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toString>(cx,
2341 args);
2345 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2347 static bool PlainDateTime_toLocaleString(JSContext* cx, const CallArgs& args) {
2348 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2349 auto dt = ToPlainDateTime(dateTime);
2350 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2352 // Step 3.
2353 JSString* str = ::TemporalDateTimeToString(
2354 cx, dt, calendar, Precision::Auto(), CalendarOption::Auto);
2355 if (!str) {
2356 return false;
2359 args.rval().setString(str);
2360 return true;
2364 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2366 static bool PlainDateTime_toLocaleString(JSContext* cx, unsigned argc,
2367 Value* vp) {
2368 // Steps 1-2.
2369 CallArgs args = CallArgsFromVp(argc, vp);
2370 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toLocaleString>(
2371 cx, args);
2375 * Temporal.PlainDateTime.prototype.toJSON ( )
2377 static bool PlainDateTime_toJSON(JSContext* cx, const CallArgs& args) {
2378 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2379 auto dt = ToPlainDateTime(dateTime);
2380 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2382 // Step 3.
2383 JSString* str = ::TemporalDateTimeToString(
2384 cx, dt, calendar, Precision::Auto(), CalendarOption::Auto);
2385 if (!str) {
2386 return false;
2389 args.rval().setString(str);
2390 return true;
2394 * Temporal.PlainDateTime.prototype.toJSON ( )
2396 static bool PlainDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) {
2397 // Steps 1-2.
2398 CallArgs args = CallArgsFromVp(argc, vp);
2399 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toJSON>(cx, args);
2403 * Temporal.PlainDateTime.prototype.valueOf ( )
2405 static bool PlainDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) {
2406 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
2407 "PlainDateTime", "primitive type");
2408 return false;
2412 * Temporal.PlainDateTime.prototype.getISOFields ( )
2414 static bool PlainDateTime_getISOFields(JSContext* cx, const CallArgs& args) {
2415 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2416 auto dateTime = ToPlainDateTime(temporalDateTime);
2417 auto calendar = temporalDateTime->calendar();
2419 // Step 3.
2420 Rooted<IdValueVector> fields(cx, IdValueVector(cx));
2422 // Step 4.
2423 if (!fields.emplaceBack(NameToId(cx->names().calendar), calendar.toValue())) {
2424 return false;
2427 // Step 5.
2428 if (!fields.emplaceBack(NameToId(cx->names().isoDay),
2429 Int32Value(dateTime.date.day))) {
2430 return false;
2433 // Step 6.
2434 if (!fields.emplaceBack(NameToId(cx->names().isoHour),
2435 Int32Value(dateTime.time.hour))) {
2436 return false;
2439 // Step 7.
2440 if (!fields.emplaceBack(NameToId(cx->names().isoMicrosecond),
2441 Int32Value(dateTime.time.microsecond))) {
2442 return false;
2445 // Step 8.
2446 if (!fields.emplaceBack(NameToId(cx->names().isoMillisecond),
2447 Int32Value(dateTime.time.millisecond))) {
2448 return false;
2451 // Step 9.
2452 if (!fields.emplaceBack(NameToId(cx->names().isoMinute),
2453 Int32Value(dateTime.time.minute))) {
2454 return false;
2457 // Step 10.
2458 if (!fields.emplaceBack(NameToId(cx->names().isoMonth),
2459 Int32Value(dateTime.date.month))) {
2460 return false;
2463 // Step 11.
2464 if (!fields.emplaceBack(NameToId(cx->names().isoNanosecond),
2465 Int32Value(dateTime.time.nanosecond))) {
2466 return false;
2469 // Step 12.
2470 if (!fields.emplaceBack(NameToId(cx->names().isoSecond),
2471 Int32Value(dateTime.time.second))) {
2472 return false;
2475 // Step 13.
2476 if (!fields.emplaceBack(NameToId(cx->names().isoYear),
2477 Int32Value(dateTime.date.year))) {
2478 return false;
2481 // Step 14.
2482 auto* obj = NewPlainObjectWithUniqueNames(cx, fields);
2483 if (!obj) {
2484 return false;
2487 args.rval().setObject(*obj);
2488 return true;
2492 * Temporal.PlainDateTime.prototype.getISOFields ( )
2494 static bool PlainDateTime_getISOFields(JSContext* cx, unsigned argc,
2495 Value* vp) {
2496 // Steps 1-2.
2497 CallArgs args = CallArgsFromVp(argc, vp);
2498 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_getISOFields>(
2499 cx, args);
2503 * Temporal.PlainDateTime.prototype.getCalendar ( )
2505 static bool PlainDateTime_getCalendar(JSContext* cx, const CallArgs& args) {
2506 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2507 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
2509 // Step 3.
2510 auto* obj = ToTemporalCalendarObject(cx, calendar);
2511 if (!obj) {
2512 return false;
2515 args.rval().setObject(*obj);
2516 return true;
2520 * Temporal.PlainDateTime.prototype.getCalendar ( )
2522 static bool PlainDateTime_getCalendar(JSContext* cx, unsigned argc, Value* vp) {
2523 // Steps 1-2.
2524 CallArgs args = CallArgsFromVp(argc, vp);
2525 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_getCalendar>(cx,
2526 args);
2530 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2531 * options ] )
2533 static bool PlainDateTime_toZonedDateTime(JSContext* cx, const CallArgs& args) {
2534 Rooted<PlainDateTimeObject*> dateTime(
2535 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2536 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2538 // Step 3.
2539 Rooted<TimeZoneValue> timeZone(cx);
2540 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
2541 return false;
2544 auto disambiguation = TemporalDisambiguation::Compatible;
2545 if (args.hasDefined(1)) {
2546 // Step 4.
2547 Rooted<JSObject*> options(
2548 cx, RequireObjectArg(cx, "options", "toZonedDateTime", args[1]));
2549 if (!options) {
2550 return false;
2553 // Step 5.
2554 if (!ToTemporalDisambiguation(cx, options, &disambiguation)) {
2555 return false;
2559 // Steps 6-7.
2560 Instant instant;
2561 if (!GetInstantFor(cx, timeZone, dateTime, disambiguation, &instant)) {
2562 return false;
2565 // Step 8.
2566 auto* result = CreateTemporalZonedDateTime(cx, instant, timeZone, calendar);
2567 if (!result) {
2568 return false;
2571 args.rval().setObject(*result);
2572 return true;
2576 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2577 * options ] )
2579 static bool PlainDateTime_toZonedDateTime(JSContext* cx, unsigned argc,
2580 Value* vp) {
2581 // Steps 1-2.
2582 CallArgs args = CallArgsFromVp(argc, vp);
2583 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toZonedDateTime>(
2584 cx, args);
2588 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2590 static bool PlainDateTime_toPlainDate(JSContext* cx, const CallArgs& args) {
2591 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2592 Rooted<CalendarValue> calendar(cx, dateTime->calendar());
2594 // Step 3.
2595 auto* obj = CreateTemporalDate(cx, ToPlainDate(dateTime), calendar);
2596 if (!obj) {
2597 return false;
2600 args.rval().setObject(*obj);
2601 return true;
2605 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2607 static bool PlainDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
2608 // Steps 1-2.
2609 CallArgs args = CallArgsFromVp(argc, vp);
2610 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainDate>(cx,
2611 args);
2615 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2617 static bool PlainDateTime_toPlainYearMonth(JSContext* cx,
2618 const CallArgs& args) {
2619 Rooted<PlainDateTimeObject*> dateTime(
2620 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2621 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
2623 // Step 3.
2624 Rooted<CalendarRecord> calendar(cx);
2625 if (!CreateCalendarMethodsRecord(cx, calendarValue,
2627 CalendarMethod::Fields,
2628 CalendarMethod::YearMonthFromFields,
2630 &calendar)) {
2631 return false;
2634 // Step 4.
2635 JS::RootedVector<PropertyKey> fieldNames(cx);
2636 if (!CalendarFields(cx, calendar,
2637 {CalendarField::MonthCode, CalendarField::Year},
2638 &fieldNames)) {
2639 return false;
2642 // Step 4.
2643 Rooted<PlainObject*> fields(cx,
2644 PrepareTemporalFields(cx, dateTime, fieldNames));
2645 if (!fields) {
2646 return false;
2649 // Step 5.
2650 auto obj = CalendarYearMonthFromFields(cx, calendar, fields);
2651 if (!obj) {
2652 return false;
2655 args.rval().setObject(*obj);
2656 return true;
2660 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2662 static bool PlainDateTime_toPlainYearMonth(JSContext* cx, unsigned argc,
2663 Value* vp) {
2664 // Steps 1-2.
2665 CallArgs args = CallArgsFromVp(argc, vp);
2666 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainYearMonth>(
2667 cx, args);
2671 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2673 static bool PlainDateTime_toPlainMonthDay(JSContext* cx, const CallArgs& args) {
2674 Rooted<PlainDateTimeObject*> dateTime(
2675 cx, &args.thisv().toObject().as<PlainDateTimeObject>());
2676 Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
2678 // Step 3.
2679 Rooted<CalendarRecord> calendar(cx);
2680 if (!CreateCalendarMethodsRecord(cx, calendarValue,
2682 CalendarMethod::Fields,
2683 CalendarMethod::MonthDayFromFields,
2685 &calendar)) {
2686 return false;
2689 // Step 4.
2690 JS::RootedVector<PropertyKey> fieldNames(cx);
2691 if (!CalendarFields(cx, calendar,
2692 {CalendarField::Day, CalendarField::MonthCode},
2693 &fieldNames)) {
2694 return false;
2697 // Step 5.
2698 Rooted<PlainObject*> fields(cx,
2699 PrepareTemporalFields(cx, dateTime, fieldNames));
2700 if (!fields) {
2701 return false;
2704 // Steps 6-7.
2705 auto obj = CalendarMonthDayFromFields(cx, calendar, fields);
2706 if (!obj) {
2707 return false;
2710 args.rval().setObject(*obj);
2711 return true;
2715 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2717 static bool PlainDateTime_toPlainMonthDay(JSContext* cx, unsigned argc,
2718 Value* vp) {
2719 // Steps 1-2.
2720 CallArgs args = CallArgsFromVp(argc, vp);
2721 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainMonthDay>(
2722 cx, args);
2726 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2728 static bool PlainDateTime_toPlainTime(JSContext* cx, const CallArgs& args) {
2729 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
2731 // Step 3.
2732 auto* obj = CreateTemporalTime(cx, ToPlainTime(dateTime));
2733 if (!obj) {
2734 return false;
2737 args.rval().setObject(*obj);
2738 return true;
2742 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2744 static bool PlainDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) {
2745 // Steps 1-2.
2746 CallArgs args = CallArgsFromVp(argc, vp);
2747 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainTime>(cx,
2748 args);
2751 const JSClass PlainDateTimeObject::class_ = {
2752 "Temporal.PlainDateTime",
2753 JSCLASS_HAS_RESERVED_SLOTS(PlainDateTimeObject::SLOT_COUNT) |
2754 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainDateTime),
2755 JS_NULL_CLASS_OPS,
2756 &PlainDateTimeObject::classSpec_,
2759 const JSClass& PlainDateTimeObject::protoClass_ = PlainObject::class_;
2761 static const JSFunctionSpec PlainDateTime_methods[] = {
2762 JS_FN("from", PlainDateTime_from, 1, 0),
2763 JS_FN("compare", PlainDateTime_compare, 2, 0),
2764 JS_FS_END,
2767 static const JSFunctionSpec PlainDateTime_prototype_methods[] = {
2768 JS_FN("with", PlainDateTime_with, 1, 0),
2769 JS_FN("withPlainTime", PlainDateTime_withPlainTime, 0, 0),
2770 JS_FN("withPlainDate", PlainDateTime_withPlainDate, 1, 0),
2771 JS_FN("withCalendar", PlainDateTime_withCalendar, 1, 0),
2772 JS_FN("add", PlainDateTime_add, 1, 0),
2773 JS_FN("subtract", PlainDateTime_subtract, 1, 0),
2774 JS_FN("until", PlainDateTime_until, 1, 0),
2775 JS_FN("since", PlainDateTime_since, 1, 0),
2776 JS_FN("round", PlainDateTime_round, 1, 0),
2777 JS_FN("equals", PlainDateTime_equals, 1, 0),
2778 JS_FN("toString", PlainDateTime_toString, 0, 0),
2779 JS_FN("toLocaleString", PlainDateTime_toLocaleString, 0, 0),
2780 JS_FN("toJSON", PlainDateTime_toJSON, 0, 0),
2781 JS_FN("valueOf", PlainDateTime_valueOf, 0, 0),
2782 JS_FN("toZonedDateTime", PlainDateTime_toZonedDateTime, 1, 0),
2783 JS_FN("toPlainDate", PlainDateTime_toPlainDate, 0, 0),
2784 JS_FN("toPlainYearMonth", PlainDateTime_toPlainYearMonth, 0, 0),
2785 JS_FN("toPlainMonthDay", PlainDateTime_toPlainMonthDay, 0, 0),
2786 JS_FN("toPlainTime", PlainDateTime_toPlainTime, 0, 0),
2787 JS_FN("getISOFields", PlainDateTime_getISOFields, 0, 0),
2788 JS_FN("getCalendar", PlainDateTime_getCalendar, 0, 0),
2789 JS_FS_END,
2792 static const JSPropertySpec PlainDateTime_prototype_properties[] = {
2793 JS_PSG("calendarId", PlainDateTime_calendarId, 0),
2794 JS_PSG("year", PlainDateTime_year, 0),
2795 JS_PSG("month", PlainDateTime_month, 0),
2796 JS_PSG("monthCode", PlainDateTime_monthCode, 0),
2797 JS_PSG("day", PlainDateTime_day, 0),
2798 JS_PSG("hour", PlainDateTime_hour, 0),
2799 JS_PSG("minute", PlainDateTime_minute, 0),
2800 JS_PSG("second", PlainDateTime_second, 0),
2801 JS_PSG("millisecond", PlainDateTime_millisecond, 0),
2802 JS_PSG("microsecond", PlainDateTime_microsecond, 0),
2803 JS_PSG("nanosecond", PlainDateTime_nanosecond, 0),
2804 JS_PSG("dayOfWeek", PlainDateTime_dayOfWeek, 0),
2805 JS_PSG("dayOfYear", PlainDateTime_dayOfYear, 0),
2806 JS_PSG("weekOfYear", PlainDateTime_weekOfYear, 0),
2807 JS_PSG("yearOfWeek", PlainDateTime_yearOfWeek, 0),
2808 JS_PSG("daysInWeek", PlainDateTime_daysInWeek, 0),
2809 JS_PSG("daysInMonth", PlainDateTime_daysInMonth, 0),
2810 JS_PSG("daysInYear", PlainDateTime_daysInYear, 0),
2811 JS_PSG("monthsInYear", PlainDateTime_monthsInYear, 0),
2812 JS_PSG("inLeapYear", PlainDateTime_inLeapYear, 0),
2813 JS_STRING_SYM_PS(toStringTag, "Temporal.PlainDateTime", JSPROP_READONLY),
2814 JS_PS_END,
2817 const ClassSpec PlainDateTimeObject::classSpec_ = {
2818 GenericCreateConstructor<PlainDateTimeConstructor, 3,
2819 gc::AllocKind::FUNCTION>,
2820 GenericCreatePrototype<PlainDateTimeObject>,
2821 PlainDateTime_methods,
2822 nullptr,
2823 PlainDateTime_prototype_methods,
2824 PlainDateTime_prototype_properties,
2825 nullptr,
2826 ClassSpec::DontDefineConstructor,