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"
12 #include <type_traits>
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"
42 #include "js/ErrorReport.h"
43 #include "js/friend/ErrorMessages.h"
44 #include "js/GCVector.h"
46 #include "js/PropertyDescriptor.h"
47 #include "js/PropertySpec.h"
48 #include "js/RootingAPI.h"
49 #include "js/TypeDecls.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"
64 using namespace js::temporal
;
66 static inline bool IsPlainDateTime(Handle
<Value
> v
) {
67 return v
.isObject() && v
.toObject().is
<PlainDateTimeObject
>();
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
);
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
);
91 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
92 * millisecond, microsecond, nanosecond )
95 static bool ISODateTimeWithinLimits(T year
, T month
, T day
, T hour
, T minute
,
96 T second
, T millisecond
, T microsecond
,
98 static_assert(std::is_same_v
<T
, int32_t> || std::is_same_v
<T
, double>);
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
));
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
) {
131 if (year
!= minYear
) {
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
) {
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
) {
188 if (year
!= minYear
) {
194 if (day
< (20 - 1)) {
200 // 275760 September, 13
201 if (year
!= maxYear
) {
214 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
215 * millisecond, microsecond, nanosecond )
217 bool js::temporal::ISODateTimeWithinLimits(double year
, double month
,
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
));
261 if (!ThrowIfInvalidISODate(cx
, isoYear
, isoMonth
, isoDay
)) {
266 if (!ThrowIfInvalidTime(cx
, hour
, minute
, second
, millisecond
, microsecond
,
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
);
280 Rooted
<JSObject
*> proto(cx
);
281 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_PlainDateTime
,
286 auto* dateTime
= NewObjectWithClassProto
<PlainDateTimeObject
>(cx
, proto
);
292 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_YEAR_SLOT
,
293 Int32Value(int32_t(isoYear
)));
296 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_MONTH_SLOT
,
297 Int32Value(int32_t(isoMonth
)));
300 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT
,
301 Int32Value(int32_t(isoDay
)));
304 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT
,
305 Int32Value(int32_t(hour
)));
308 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_MINUTE_SLOT
,
309 Int32Value(int32_t(minute
)));
312 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_SECOND_SLOT
,
313 Int32Value(int32_t(second
)));
316 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_MILLISECOND_SLOT
,
317 Int32Value(int32_t(millisecond
)));
320 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_MICROSECOND_SLOT
,
321 Int32Value(int32_t(microsecond
)));
324 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_NANOSECOND_SLOT
,
325 Int32Value(int32_t(nanosecond
)));
328 dateTime
->setFixedSlot(PlainDateTimeObject::CALENDAR_SLOT
,
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
] =
348 if (!ThrowIfInvalidISODateTime(cx
, dateTime
)) {
353 if (!ISODateTimeWithinLimits(dateTime
)) {
354 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
355 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID
);
360 auto* object
= NewBuiltinClassInstance
<PlainDateTimeObject
>(cx
);
366 object
->setFixedSlot(PlainDateTimeObject::ISO_YEAR_SLOT
, Int32Value(isoYear
));
369 object
->setFixedSlot(PlainDateTimeObject::ISO_MONTH_SLOT
,
370 Int32Value(isoMonth
));
373 object
->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT
, Int32Value(isoDay
));
376 object
->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT
, Int32Value(hour
));
379 object
->setFixedSlot(PlainDateTimeObject::ISO_MINUTE_SLOT
,
383 object
->setFixedSlot(PlainDateTimeObject::ISO_SECOND_SLOT
,
387 object
->setFixedSlot(PlainDateTimeObject::ISO_MILLISECOND_SLOT
,
388 Int32Value(millisecond
));
391 object
->setFixedSlot(PlainDateTimeObject::ISO_MICROSECOND_SLOT
,
392 Int32Value(microsecond
));
395 object
->setFixedSlot(PlainDateTimeObject::ISO_NANOSECOND_SLOT
,
396 Int32Value(nanosecond
));
399 object
->setFixedSlot(PlainDateTimeObject::CALENDAR_SLOT
, calendar
.toValue());
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
) {
414 if (!ThrowIfInvalidISODateTime(cx
, dateTime
)) {
419 if (!ISODateTimeWithinLimits(dateTime
)) {
420 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
421 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID
);
425 result
.set(PlainDateTimeWithCalendar
{dateTime
, calendar
});
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.)
438 MOZ_ASSERT(CalendarMethodsRecordHasLookedUp(calendar
,
439 CalendarMethod::DateFromFields
));
442 TemporalTimeLike timeResult
;
443 if (!ToTemporalTimeRecord(cx
, fields
, &timeResult
)) {
448 auto overflow
= TemporalOverflow::Constrain
;
449 if (!ToTemporalOverflow(cx
, options
, &overflow
)) {
454 Rooted
<Value
> overflowValue(cx
);
455 if (overflow
== TemporalOverflow::Constrain
) {
456 overflowValue
.setString(cx
->names().constrain
);
458 MOZ_ASSERT(overflow
== TemporalOverflow::Reject
);
459 overflowValue
.setString(cx
->names().reject
);
461 if (!DefineDataProperty(cx
, options
, cx
->names().overflow
, overflowValue
)) {
467 js::temporal::CalendarDateFromFields(cx
, calendar
, fields
, options
);
471 auto date
= ToPlainDate(&temporalDate
.unwrap());
475 if (!RegulateTime(cx
, timeResult
, overflow
, &time
)) {
480 *result
= {date
, time
};
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));
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)
508 Rooted
<PlainObject
*> maybeResolvedOptions(cx
);
510 maybeResolvedOptions
= SnapshotOwnProperties(cx
, maybeOptions
);
511 if (!maybeResolvedOptions
) {
517 Rooted
<CalendarValue
> calendar(cx
);
518 PlainDateTime result
;
519 if (item
.isObject()) {
520 Rooted
<JSObject
*> itemObj(cx
, &item
.toObject());
523 if (itemObj
->canUnwrapAs
<PlainDateTimeObject
>()) {
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
)) {
536 if (!calendar
.wrap(cx
)) {
541 if (maybeResolvedOptions
) {
542 TemporalOverflow ignored
;
543 if (!ToTemporalOverflow(cx
, maybeResolvedOptions
, &ignored
)) {
549 return GetPlainDateTimeFor(cx
, timeZone
, epochInstant
, calendar
);
553 if (auto* date
= itemObj
->maybeUnwrapIf
<PlainDateObject
>()) {
554 PlainDateTime dateTime
= {ToPlainDate(date
), {}};
555 Rooted
<CalendarValue
> calendar(cx
, date
->calendar());
556 if (!calendar
.wrap(cx
)) {
561 if (maybeResolvedOptions
) {
562 TemporalOverflow ignored
;
563 if (!ToTemporalOverflow(cx
, maybeResolvedOptions
, &ignored
)) {
569 return CreateTemporalDateTime(cx
, dateTime
, calendar
);
573 if (!GetTemporalCalendarWithISODefault(cx
, itemObj
, &calendar
)) {
578 Rooted
<CalendarRecord
> calendarRec(cx
);
579 if (!CreateCalendarMethodsRecord(cx
, calendar
,
581 CalendarMethod::DateFromFields
,
582 CalendarMethod::Fields
,
589 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
590 if (!CalendarFields(cx
, calendarRec
,
591 {CalendarField::Day
, CalendarField::Month
,
592 CalendarField::MonthCode
, CalendarField::Year
},
598 if (!AppendSorted(cx
, fieldNames
.get(),
601 TemporalField::Microsecond
,
602 TemporalField::Millisecond
,
603 TemporalField::Minute
,
604 TemporalField::Nanosecond
,
605 TemporalField::Second
,
611 Rooted
<PlainObject
*> fields(cx
,
612 PrepareTemporalFields(cx
, itemObj
, fieldNames
));
618 if (maybeResolvedOptions
) {
619 if (!InterpretTemporalDateTimeFields(cx
, calendarRec
, fields
,
620 maybeResolvedOptions
, &result
)) {
624 if (!InterpretTemporalDateTimeFields(cx
, calendarRec
, fields
, &result
)) {
630 if (!item
.isString()) {
631 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, item
,
632 nullptr, "not a string");
635 Rooted
<JSString
*> string(cx
, item
.toString());
638 Rooted
<JSString
*> calendarString(cx
);
639 if (!ParseTemporalDateTimeString(cx
, string
, &result
, &calendarString
)) {
644 MOZ_ASSERT(IsValidISODate(result
.date
));
647 MOZ_ASSERT(IsValidTime(result
.time
));
650 if (calendarString
) {
651 if (!ToBuiltinCalendar(cx
, calendarString
, &calendar
)) {
655 calendar
.set(CalendarValue(cx
->names().iso8601
));
659 if (maybeResolvedOptions
) {
660 TemporalOverflow ignored
;
661 if (!ToTemporalOverflow(cx
, maybeResolvedOptions
, &ignored
)) {
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);
689 *result
= ToPlainDateTime(&obj
.unwrap());
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();
706 auto dateTime
= ToPlainDateTime(obj
);
707 Rooted
<CalendarValue
> calendar(cx
, obj
->calendar());
708 if (!calendar
.wrap(cx
)) {
712 result
.set(PlainDateTimeWithCalendar
{dateTime
, calendar
});
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.)
725 if (int32_t dateResult
= CompareISODate(one
.date
, two
.date
)) {
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,
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
));
745 MOZ_ASSERT(IsValidISODateTime(dateTime
));
746 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime
));
749 auto timeResult
= AddTime(dateTime
.time
, duration
.time
);
752 const auto& datePart
= dateTime
.date
;
755 auto dateDuration
= DateDuration
{
757 duration
.date
.months
,
759 duration
.date
.days
+ timeResult
.days
,
761 if (!ThrowIfInvalidDuration(cx
, dateDuration
)) {
767 if (!AddDate(cx
, calendar
, datePart
, dateDuration
, options
, &addedDate
)) {
772 *result
= {addedDate
, timeResult
.time
};
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
) {
787 MOZ_ASSERT(IsValidISODateTime(one
));
788 MOZ_ASSERT(IsValidISODateTime(two
));
789 MOZ_ASSERT(ISODateTimeWithinLimits(one
));
790 MOZ_ASSERT(ISODateTimeWithinLimits(two
));
794 one
.date
!= two
.date
&& largestUnit
< TemporalUnit::Day
,
795 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateUntil
));
798 auto timeDuration
= DifferenceTime(one
.time
, two
.time
);
801 int32_t timeSign
= NormalizedTimeDurationSign(timeDuration
);
804 int32_t dateSign
= CompareISODate(two
.date
, one
.date
);
807 auto adjustedDate
= one
.date
;
810 if (timeSign
== -dateSign
) {
812 adjustedDate
= BalanceISODate(adjustedDate
.year
, adjustedDate
.month
,
813 adjustedDate
.day
- timeSign
);
816 if (!Add24HourDaysToNormalizedTimeDuration(cx
, timeDuration
, -timeSign
,
822 MOZ_ASSERT(IsValidISODate(adjustedDate
));
823 MOZ_ASSERT(ISODateTimeWithinLimits(adjustedDate
));
825 // TODO: Avoid allocating CreateTemporalDate.
828 Rooted
<PlainDateObject
*> date1(
829 cx
, CreateTemporalDate(cx
, adjustedDate
, calendar
.receiver()));
835 Rooted
<PlainDateObject
*> date2(
836 cx
, CreateTemporalDate(cx
, two
.date
, calendar
.receiver()));
842 auto dateLargestUnit
= std::min(TemporalUnit::Day
, largestUnit
);
844 DateDuration dateDifference
;
848 // The spec performs an unnecessary copy operation. As an optimization, we
850 auto untilOptions
= maybeOptions
;
853 Rooted
<Value
> largestUnitValue(
854 cx
, StringValue(TemporalUnitToString(cx
, dateLargestUnit
)));
855 if (!DefineDataProperty(cx
, untilOptions
, cx
->names().largestUnit
,
861 if (!DifferenceDate(cx
, calendar
, date1
, date2
, untilOptions
,
867 if (!DifferenceDate(cx
, calendar
, date1
, date2
, dateLargestUnit
,
874 return CreateNormalizedDurationRecord(cx
, dateDifference
, timeDuration
,
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,
893 *result
= normalized
.date
;
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
,
910 *result
= normalized
.date
;
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
;
924 MOZ_ASSERT(IsValidISODateTime(dateTime
));
925 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime
));
927 // Step 2. (Not applicable in our implementation.)
930 auto roundedTime
= RoundTime(time
, increment
, unit
, roundingMode
);
931 MOZ_ASSERT(0 <= roundedTime
.days
&& roundedTime
.days
<= 1);
934 auto balanceResult
= BalanceISODate(date
.year
, date
.month
,
935 date
.day
+ int32_t(roundedTime
.days
));
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.)
953 Rooted
<PlainDateTimeWithCalendar
> other(cx
);
954 if (!::ToTemporalDateTime(cx
, args
.get(0), &other
)) {
959 if (!CalendarEqualsOrThrow(cx
, dateTime
.calendar(), other
.calendar())) {
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]));
974 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
975 if (!resolvedOptions
) {
980 if (!GetDifferenceSettings(
981 cx
, operation
, resolvedOptions
, TemporalUnitGroup::DateTime
,
982 TemporalUnit::Nanosecond
, TemporalUnit::Day
, &settings
)) {
988 TemporalUnit::Nanosecond
,
990 TemporalRoundingMode::Trunc
,
996 bool datePartsIdentical
= dateTime
.date() == other
.date();
999 if (datePartsIdentical
&& dateTime
.time() == other
.time()) {
1000 auto* obj
= CreateTemporalDuration(cx
, {});
1005 args
.rval().setObject(*obj
);
1010 Rooted
<CalendarRecord
> calendar(cx
);
1011 if (!CreateCalendarMethodsRecord(cx
, dateTime
.calendar(),
1013 CalendarMethod::DateAdd
,
1014 CalendarMethod::DateUntil
,
1021 NormalizedDuration diff
;
1022 if (!::DifferenceISODateTime(cx
, dateTime
, other
, calendar
,
1023 settings
.largestUnit
, resolvedOptions
, &diff
)) {
1028 bool roundingGranularityIsNoop
=
1029 settings
.smallestUnit
== TemporalUnit::Nanosecond
&&
1030 settings
.roundingIncrement
== Increment
{1};
1033 DateDuration balancedDate
;
1034 TimeDuration balancedTime
;
1035 if (!roundingGranularityIsNoop
) {
1037 Rooted
<PlainDateObject
*> relativeTo(
1038 cx
, CreateTemporalDate(cx
, dateTime
.date(), dateTime
.calendar()));
1044 NormalizedDuration roundResult
;
1045 if (!temporal::RoundDuration(cx
, diff
, settings
.roundingIncrement
,
1046 settings
.smallestUnit
, settings
.roundingMode
,
1047 relativeTo
, calendar
, &roundResult
)) {
1052 NormalizedTimeDuration withDays
;
1053 if (!Add24HourDaysToNormalizedTimeDuration(
1054 cx
, roundResult
.time
, roundResult
.date
.days
, &withDays
)) {
1059 balancedTime
= BalanceTimeDuration(withDays
, settings
.largestUnit
);
1062 auto toBalance
= DateDuration
{
1063 roundResult
.date
.years
,
1064 roundResult
.date
.months
,
1065 roundResult
.date
.weeks
,
1068 if (!temporal::BalanceDateDurationRelative(
1069 cx
, toBalance
, settings
.largestUnit
, settings
.smallestUnit
,
1070 relativeTo
, calendar
, &balancedDate
)) {
1075 NormalizedTimeDuration withDays
;
1076 if (!Add24HourDaysToNormalizedTimeDuration(cx
, diff
.time
, diff
.date
.days
,
1082 balancedTime
= BalanceTimeDuration(withDays
, settings
.largestUnit
);
1092 MOZ_ASSERT(IsValidDuration(balancedDate
));
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
);
1111 args
.rval().setObject(*obj
);
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.)
1130 if (!ToTemporalDurationRecord(cx
, args
.get(0), &duration
)) {
1135 Rooted
<JSObject
*> options(cx
);
1136 if (args
.hasDefined(1)) {
1138 operation
== PlainDateTimeDuration::Add
? "add" : "subtract";
1139 options
= RequireObjectArg(cx
, "options", name
, args
[1]);
1141 options
= NewPlainObjectWithProto(cx
, nullptr);
1148 Rooted
<CalendarRecord
> calendar(cx
);
1149 if (!CreateCalendarMethodsRecord(cx
, dateTime
.calendar(),
1151 CalendarMethod::DateAdd
,
1158 if (operation
== PlainDateTimeDuration::Subtract
) {
1159 duration
= duration
.negate();
1161 auto normalized
= CreateNormalizedDurationRecord(duration
);
1164 PlainDateTime result
;
1165 if (!AddDateTime(cx
, dateTime
, calendar
, normalized
, options
, &result
)) {
1170 MOZ_ASSERT(IsValidISODateTime(result
));
1173 auto* obj
= CreateTemporalDateTime(cx
, result
, dateTime
.calendar());
1178 args
.rval().setObject(*obj
);
1183 * Temporal.PlainDateTime ( isoYear, isoMonth, isoDay [ , hour [ , minute [ ,
1184 * second [ , millisecond [ , microsecond [ , nanosecond [ , calendarLike ] ] ]
1187 static bool PlainDateTimeConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1188 CallArgs args
= CallArgsFromVp(argc
, vp
);
1191 if (!ThrowIfNotConstructing(cx
, args
, "Temporal.PlainDateTime")) {
1197 if (!ToIntegerWithTruncation(cx
, args
.get(0), "year", &isoYear
)) {
1203 if (!ToIntegerWithTruncation(cx
, args
.get(1), "month", &isoMonth
)) {
1209 if (!ToIntegerWithTruncation(cx
, args
.get(2), "day", &isoDay
)) {
1215 if (args
.hasDefined(3)) {
1216 if (!ToIntegerWithTruncation(cx
, args
[3], "hour", &hour
)) {
1223 if (args
.hasDefined(4)) {
1224 if (!ToIntegerWithTruncation(cx
, args
[4], "minute", &minute
)) {
1231 if (args
.hasDefined(5)) {
1232 if (!ToIntegerWithTruncation(cx
, args
[5], "second", &second
)) {
1238 double millisecond
= 0;
1239 if (args
.hasDefined(6)) {
1240 if (!ToIntegerWithTruncation(cx
, args
[6], "millisecond", &millisecond
)) {
1246 double microsecond
= 0;
1247 if (args
.hasDefined(7)) {
1248 if (!ToIntegerWithTruncation(cx
, args
[7], "microsecond", µsecond
)) {
1254 double nanosecond
= 0;
1255 if (args
.hasDefined(8)) {
1256 if (!ToIntegerWithTruncation(cx
, args
[8], "nanosecond", &nanosecond
)) {
1262 Rooted
<CalendarValue
> calendar(cx
);
1263 if (!ToTemporalCalendarWithISODefault(cx
, args
.get(9), &calendar
)) {
1268 auto* temporalDateTime
= CreateTemporalDateTime(
1269 cx
, args
, isoYear
, isoMonth
, isoDay
, hour
, minute
, second
, millisecond
,
1270 microsecond
, nanosecond
, calendar
);
1271 if (!temporalDateTime
) {
1275 args
.rval().setObject(*temporalDateTime
);
1280 * Temporal.PlainDateTime.from ( item [ , options ] )
1282 static bool PlainDateTime_from(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1283 CallArgs args
= CallArgsFromVp(argc
, vp
);
1286 Rooted
<JSObject
*> options(cx
);
1287 if (args
.hasDefined(1)) {
1288 options
= RequireObjectArg(cx
, "options", "from", args
[1]);
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
)) {
1307 TemporalOverflow ignored
;
1308 if (!ToTemporalOverflow(cx
, options
, &ignored
)) {
1314 auto* result
= CreateTemporalDateTime(cx
, dateTime
, calendar
);
1319 args
.rval().setObject(*result
);
1325 auto result
= ToTemporalDateTime(cx
, args
.get(0), options
);
1330 args
.rval().setObject(*result
);
1335 * Temporal.PlainDateTime.compare ( one, two )
1337 static bool PlainDateTime_compare(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1338 CallArgs args
= CallArgsFromVp(argc
, vp
);
1342 if (!ToTemporalDateTime(cx
, args
.get(0), &one
)) {
1348 if (!ToTemporalDateTime(cx
, args
.get(1), &two
)) {
1353 args
.rval().setInt32(CompareISODateTime(one
, two
));
1358 * get Temporal.PlainDateTime.prototype.calendarId
1360 static bool PlainDateTime_calendarId(JSContext
* cx
, const CallArgs
& args
) {
1361 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
1364 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
1365 auto* calendarId
= ToTemporalCalendarIdentifier(cx
, calendar
);
1370 args
.rval().setString(calendarId
);
1375 * get Temporal.PlainDateTime.prototype.calendarId
1377 static bool PlainDateTime_calendarId(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1379 CallArgs args
= CallArgsFromVp(argc
, vp
);
1380 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_calendarId
>(cx
,
1385 * get Temporal.PlainDateTime.prototype.year
1387 static bool PlainDateTime_year(JSContext
* cx
, const CallArgs
& args
) {
1389 Rooted
<PlainDateTimeObject
*> dateTime(
1390 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1391 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
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
) {
1411 Rooted
<PlainDateTimeObject
*> dateTime(
1412 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1413 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
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
) {
1433 Rooted
<PlainDateTimeObject
*> dateTime(
1434 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1435 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
1446 CallArgs args
= CallArgsFromVp(argc
, vp
);
1447 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_monthCode
>(cx
,
1452 * get Temporal.PlainDateTime.prototype.day
1454 static bool PlainDateTime_day(JSContext
* cx
, const CallArgs
& args
) {
1456 Rooted
<PlainDateTimeObject
*> dateTime(
1457 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1458 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
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
) {
1478 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
1479 args
.rval().setInt32(dateTime
->isoHour());
1484 * get Temporal.PlainDateTime.prototype.hour
1486 static bool PlainDateTime_hour(JSContext
* cx
, unsigned argc
, Value
* vp
) {
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
) {
1497 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
1498 args
.rval().setInt32(dateTime
->isoMinute());
1503 * get Temporal.PlainDateTime.prototype.minute
1505 static bool PlainDateTime_minute(JSContext
* cx
, unsigned argc
, Value
* vp
) {
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
) {
1516 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
1517 args
.rval().setInt32(dateTime
->isoSecond());
1522 * get Temporal.PlainDateTime.prototype.second
1524 static bool PlainDateTime_second(JSContext
* cx
, unsigned argc
, Value
* vp
) {
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
) {
1535 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
1536 args
.rval().setInt32(dateTime
->isoMillisecond());
1541 * get Temporal.PlainDateTime.prototype.millisecond
1543 static bool PlainDateTime_millisecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1545 CallArgs args
= CallArgsFromVp(argc
, vp
);
1546 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_millisecond
>(cx
,
1551 * get Temporal.PlainDateTime.prototype.microsecond
1553 static bool PlainDateTime_microsecond(JSContext
* cx
, const CallArgs
& args
) {
1555 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
1556 args
.rval().setInt32(dateTime
->isoMicrosecond());
1561 * get Temporal.PlainDateTime.prototype.microsecond
1563 static bool PlainDateTime_microsecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1565 CallArgs args
= CallArgsFromVp(argc
, vp
);
1566 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_microsecond
>(cx
,
1571 * get Temporal.PlainDateTime.prototype.nanosecond
1573 static bool PlainDateTime_nanosecond(JSContext
* cx
, const CallArgs
& args
) {
1575 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
1576 args
.rval().setInt32(dateTime
->isoNanosecond());
1581 * get Temporal.PlainDateTime.prototype.nanosecond
1583 static bool PlainDateTime_nanosecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1585 CallArgs args
= CallArgsFromVp(argc
, vp
);
1586 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_nanosecond
>(cx
,
1591 * get Temporal.PlainDateTime.prototype.dayOfWeek
1593 static bool PlainDateTime_dayOfWeek(JSContext
* cx
, const CallArgs
& args
) {
1595 Rooted
<PlainDateTimeObject
*> dateTime(
1596 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1597 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
1608 CallArgs args
= CallArgsFromVp(argc
, vp
);
1609 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_dayOfWeek
>(cx
,
1614 * get Temporal.PlainDateTime.prototype.dayOfYear
1616 static bool PlainDateTime_dayOfYear(JSContext
* cx
, const CallArgs
& args
) {
1618 Rooted
<PlainDateTimeObject
*> dateTime(
1619 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1620 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
1631 CallArgs args
= CallArgsFromVp(argc
, vp
);
1632 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_dayOfYear
>(cx
,
1637 * get Temporal.PlainDateTime.prototype.weekOfYear
1639 static bool PlainDateTime_weekOfYear(JSContext
* cx
, const CallArgs
& args
) {
1641 Rooted
<PlainDateTimeObject
*> dateTime(
1642 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1643 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
1654 CallArgs args
= CallArgsFromVp(argc
, vp
);
1655 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_weekOfYear
>(cx
,
1660 * get Temporal.PlainDateTime.prototype.yearOfWeek
1662 static bool PlainDateTime_yearOfWeek(JSContext
* cx
, const CallArgs
& args
) {
1664 Rooted
<PlainDateTimeObject
*> dateTime(
1665 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1666 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
1677 CallArgs args
= CallArgsFromVp(argc
, vp
);
1678 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_yearOfWeek
>(cx
,
1683 * get Temporal.PlainDateTime.prototype.daysInWeek
1685 static bool PlainDateTime_daysInWeek(JSContext
* cx
, const CallArgs
& args
) {
1687 Rooted
<PlainDateTimeObject
*> dateTime(
1688 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1689 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
1700 CallArgs args
= CallArgsFromVp(argc
, vp
);
1701 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_daysInWeek
>(cx
,
1706 * get Temporal.PlainDateTime.prototype.daysInMonth
1708 static bool PlainDateTime_daysInMonth(JSContext
* cx
, const CallArgs
& args
) {
1710 Rooted
<PlainDateTimeObject
*> dateTime(
1711 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1712 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
1723 CallArgs args
= CallArgsFromVp(argc
, vp
);
1724 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_daysInMonth
>(cx
,
1729 * get Temporal.PlainDateTime.prototype.daysInYear
1731 static bool PlainDateTime_daysInYear(JSContext
* cx
, const CallArgs
& args
) {
1733 Rooted
<PlainDateTimeObject
*> dateTime(
1734 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1735 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
1746 CallArgs args
= CallArgsFromVp(argc
, vp
);
1747 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_daysInYear
>(cx
,
1752 * get Temporal.PlainDateTime.prototype.monthsInYear
1754 static bool PlainDateTime_monthsInYear(JSContext
* cx
, const CallArgs
& args
) {
1756 Rooted
<PlainDateTimeObject
*> dateTime(
1757 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1758 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
1761 return CalendarMonthsInYear(cx
, calendar
, dateTime
, args
.rval());
1765 * get Temporal.PlainDateTime.prototype.monthsInYear
1767 static bool PlainDateTime_monthsInYear(JSContext
* cx
, unsigned argc
,
1770 CallArgs args
= CallArgsFromVp(argc
, vp
);
1771 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_monthsInYear
>(
1776 * get Temporal.PlainDateTime.prototype.inLeapYear
1778 static bool PlainDateTime_inLeapYear(JSContext
* cx
, const CallArgs
& args
) {
1780 Rooted
<PlainDateTimeObject
*> dateTime(
1781 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
1782 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
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
) {
1793 CallArgs args
= CallArgsFromVp(argc
, vp
);
1794 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_inLeapYear
>(cx
,
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
>());
1806 Rooted
<JSObject
*> temporalDateTimeLike(
1807 cx
, RequireObjectArg(cx
, "temporalDateTimeLike", "with", args
.get(0)));
1808 if (!temporalDateTimeLike
) {
1811 if (!ThrowIfTemporalLikeObject(cx
, temporalDateTimeLike
)) {
1816 Rooted
<PlainObject
*> resolvedOptions(cx
);
1817 if (args
.hasDefined(1)) {
1818 Rooted
<JSObject
*> options(cx
,
1819 RequireObjectArg(cx
, "options", "with", args
[1]));
1823 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
1825 resolvedOptions
= NewPlainObjectWithProto(cx
, nullptr);
1827 if (!resolvedOptions
) {
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
,
1845 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
1846 if (!CalendarFields(cx
, calendar
,
1847 {CalendarField::Day
, CalendarField::Month
,
1848 CalendarField::MonthCode
, CalendarField::Year
},
1854 Rooted
<PlainObject
*> fields(cx
,
1855 PrepareTemporalFields(cx
, dateTime
, fieldNames
));
1862 using FieldName
= ImmutableTenuredPtr
<PropertyName
*> JSAtomState::*;
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
)) {
1886 if (!AppendSorted(cx
, fieldNames
.get(),
1888 TemporalField::Hour
,
1889 TemporalField::Microsecond
,
1890 TemporalField::Millisecond
,
1891 TemporalField::Minute
,
1892 TemporalField::Nanosecond
,
1893 TemporalField::Second
,
1899 Rooted
<PlainObject
*> partialDateTime(
1900 cx
, PreparePartialTemporalFields(cx
, temporalDateTimeLike
, fieldNames
));
1901 if (!partialDateTime
) {
1906 Rooted
<JSObject
*> mergedFields(
1907 cx
, CalendarMergeFields(cx
, calendar
, fields
, partialDateTime
));
1908 if (!mergedFields
) {
1913 fields
= PrepareTemporalFields(cx
, mergedFields
, fieldNames
);
1919 PlainDateTime result
;
1920 if (!InterpretTemporalDateTimeFields(cx
, calendar
, fields
, resolvedOptions
,
1926 MOZ_ASSERT(IsValidISODateTime(result
));
1929 auto* obj
= CreateTemporalDateTime(cx
, result
, calendar
.receiver());
1934 args
.rval().setObject(*obj
);
1939 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
1941 static bool PlainDateTime_with(JSContext
* cx
, unsigned argc
, Value
* vp
) {
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());
1956 PlainTime time
= {};
1957 if (args
.hasDefined(0)) {
1958 if (!ToTemporalTime(cx
, args
[0], &time
)) {
1964 auto* obj
= CreateTemporalDateTime(cx
, {date
, time
}, calendar
);
1969 args
.rval().setObject(*obj
);
1974 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1976 static bool PlainDateTime_withPlainTime(JSContext
* cx
, unsigned argc
,
1979 CallArgs args
= CallArgsFromVp(argc
, vp
);
1980 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_withPlainTime
>(
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());
1993 Rooted
<PlainDateWithCalendar
> plainDate(cx
);
1994 if (!ToTemporalDate(cx
, args
.get(0), &plainDate
)) {
1997 auto date
= plainDate
.date();
2000 if (!ConsolidateCalendars(cx
, calendar
, plainDate
.calendar(), &calendar
)) {
2005 auto* obj
= CreateTemporalDateTime(cx
, {date
, time
}, calendar
);
2010 args
.rval().setObject(*obj
);
2015 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
2017 static bool PlainDateTime_withPlainDate(JSContext
* cx
, unsigned argc
,
2020 CallArgs args
= CallArgsFromVp(argc
, vp
);
2021 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_withPlainDate
>(
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
);
2033 Rooted
<CalendarValue
> calendar(cx
);
2034 if (!ToTemporalCalendar(cx
, args
.get(0), &calendar
)) {
2039 auto* result
= CreateTemporalDateTime(cx
, dateTime
, calendar
);
2044 args
.rval().setObject(*result
);
2049 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2051 static bool PlainDateTime_withCalendar(JSContext
* cx
, unsigned argc
,
2054 CallArgs args
= CallArgsFromVp(argc
, vp
);
2055 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_withCalendar
>(
2060 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2062 static bool PlainDateTime_add(JSContext
* cx
, const CallArgs
& args
) {
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
) {
2073 CallArgs args
= CallArgsFromVp(argc
, vp
);
2074 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_add
>(cx
, args
);
2078 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2081 static bool PlainDateTime_subtract(JSContext
* cx
, const CallArgs
& args
) {
2083 return AddDurationToOrSubtractDurationFromPlainDateTime(
2084 cx
, PlainDateTimeDuration::Subtract
, args
);
2088 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2091 static bool PlainDateTime_subtract(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2093 CallArgs args
= CallArgsFromVp(argc
, vp
);
2094 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_subtract
>(cx
,
2099 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2101 static bool PlainDateTime_until(JSContext
* cx
, const CallArgs
& args
) {
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
) {
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
) {
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
) {
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());
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.)
2148 Rooted
<JSString
*> paramString(cx
, args
[0].toString());
2149 if (!GetTemporalUnit(cx
, paramString
, TemporalUnitKey::SmallestUnit
,
2150 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
2154 MOZ_ASSERT(TemporalUnit::Day
<= smallestUnit
&&
2155 smallestUnit
<= TemporalUnit::Nanosecond
);
2157 // Steps 6-8 and 10-12. (Implicit)
2160 Rooted
<JSObject
*> roundTo(
2161 cx
, RequireObjectArg(cx
, "roundTo", "round", args
.get(0)));
2167 if (!ToTemporalRoundingIncrement(cx
, roundTo
, &roundingIncrement
)) {
2172 if (!ToTemporalRoundingMode(cx
, roundTo
, &roundingMode
)) {
2177 if (!GetTemporalUnit(cx
, roundTo
, TemporalUnitKey::SmallestUnit
,
2178 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
2182 if (smallestUnit
== TemporalUnit::Auto
) {
2183 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2184 JSMSG_TEMPORAL_MISSING_OPTION
, "smallestUnit");
2188 MOZ_ASSERT(TemporalUnit::Day
<= smallestUnit
&&
2189 smallestUnit
<= TemporalUnit::Nanosecond
);
2192 auto maximum
= Increment
{1};
2193 bool inclusive
= true;
2194 if (smallestUnit
> TemporalUnit::Day
) {
2195 maximum
= MaximumTemporalDurationRoundingIncrement(smallestUnit
);
2200 if (!ValidateTemporalRoundingIncrement(cx
, roundingIncrement
, maximum
,
2207 if (smallestUnit
== TemporalUnit::Nanosecond
&&
2208 roundingIncrement
== Increment
{1}) {
2209 auto* obj
= CreateTemporalDateTime(cx
, dateTime
, calendar
);
2214 args
.rval().setObject(*obj
);
2220 RoundISODateTime(dateTime
, roundingIncrement
, smallestUnit
, roundingMode
);
2223 auto* obj
= CreateTemporalDateTime(cx
, result
, calendar
);
2228 args
.rval().setObject(*obj
);
2233 * Temporal.PlainDateTime.prototype.round ( roundTo )
2235 static bool PlainDateTime_round(JSContext
* cx
, unsigned argc
, Value
* vp
) {
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());
2250 Rooted
<PlainDateTimeWithCalendar
> other(cx
);
2251 if (!::ToTemporalDateTime(cx
, args
.get(0), &other
)) {
2256 bool equals
= dateTime
== other
.dateTime();
2257 if (equals
&& !CalendarEquals(cx
, calendar
, other
.calendar(), &equals
)) {
2261 args
.rval().setBoolean(equals
);
2266 * Temporal.PlainDateTime.prototype.equals ( other )
2268 static bool PlainDateTime_equals(JSContext
* cx
, unsigned argc
, Value
* vp
) {
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)) {
2288 Rooted
<JSObject
*> options(
2289 cx
, RequireObjectArg(cx
, "options", "toString", args
[0]));
2295 if (!ToCalendarNameOption(cx
, options
, &showCalendar
)) {
2300 auto digits
= Precision::Auto();
2301 if (!ToFractionalSecondDigits(cx
, options
, &digits
)) {
2306 if (!ToTemporalRoundingMode(cx
, options
, &roundingMode
)) {
2311 auto smallestUnit
= TemporalUnit::Auto
;
2312 if (!GetTemporalUnit(cx
, options
, TemporalUnitKey::SmallestUnit
,
2313 TemporalUnitGroup::Time
, &smallestUnit
)) {
2318 if (smallestUnit
== TemporalUnit::Hour
) {
2319 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2320 JSMSG_TEMPORAL_INVALID_UNIT_OPTION
, "hour",
2326 precision
= ToSecondsStringPrecision(smallestUnit
, digits
);
2331 RoundISODateTime(dt
, precision
.increment
, precision
.unit
, roundingMode
);
2334 JSString
* str
= ::TemporalDateTimeToString(cx
, result
, calendar
,
2335 precision
.precision
, showCalendar
);
2340 args
.rval().setString(str
);
2345 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2347 static bool PlainDateTime_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2349 CallArgs args
= CallArgsFromVp(argc
, vp
);
2350 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toString
>(cx
,
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());
2363 JSString
* str
= ::TemporalDateTimeToString(
2364 cx
, dt
, calendar
, Precision::Auto(), CalendarOption::Auto
);
2369 args
.rval().setString(str
);
2374 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2376 static bool PlainDateTime_toLocaleString(JSContext
* cx
, unsigned argc
,
2379 CallArgs args
= CallArgsFromVp(argc
, vp
);
2380 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toLocaleString
>(
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());
2393 JSString
* str
= ::TemporalDateTimeToString(
2394 cx
, dt
, calendar
, Precision::Auto(), CalendarOption::Auto
);
2399 args
.rval().setString(str
);
2404 * Temporal.PlainDateTime.prototype.toJSON ( )
2406 static bool PlainDateTime_toJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
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");
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();
2430 Rooted
<IdValueVector
> fields(cx
, IdValueVector(cx
));
2433 if (!fields
.emplaceBack(NameToId(cx
->names().calendar
), calendar
.toValue())) {
2438 if (!fields
.emplaceBack(NameToId(cx
->names().isoDay
),
2439 Int32Value(dateTime
.date
.day
))) {
2444 if (!fields
.emplaceBack(NameToId(cx
->names().isoHour
),
2445 Int32Value(dateTime
.time
.hour
))) {
2450 if (!fields
.emplaceBack(NameToId(cx
->names().isoMicrosecond
),
2451 Int32Value(dateTime
.time
.microsecond
))) {
2456 if (!fields
.emplaceBack(NameToId(cx
->names().isoMillisecond
),
2457 Int32Value(dateTime
.time
.millisecond
))) {
2462 if (!fields
.emplaceBack(NameToId(cx
->names().isoMinute
),
2463 Int32Value(dateTime
.time
.minute
))) {
2468 if (!fields
.emplaceBack(NameToId(cx
->names().isoMonth
),
2469 Int32Value(dateTime
.date
.month
))) {
2474 if (!fields
.emplaceBack(NameToId(cx
->names().isoNanosecond
),
2475 Int32Value(dateTime
.time
.nanosecond
))) {
2480 if (!fields
.emplaceBack(NameToId(cx
->names().isoSecond
),
2481 Int32Value(dateTime
.time
.second
))) {
2486 if (!fields
.emplaceBack(NameToId(cx
->names().isoYear
),
2487 Int32Value(dateTime
.date
.year
))) {
2492 auto* obj
= NewPlainObjectWithUniqueNames(cx
, fields
);
2497 args
.rval().setObject(*obj
);
2502 * Temporal.PlainDateTime.prototype.getISOFields ( )
2504 static bool PlainDateTime_getISOFields(JSContext
* cx
, unsigned argc
,
2507 CallArgs args
= CallArgsFromVp(argc
, vp
);
2508 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_getISOFields
>(
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());
2520 auto* obj
= ToTemporalCalendarObject(cx
, calendar
);
2525 args
.rval().setObject(*obj
);
2530 * Temporal.PlainDateTime.prototype.getCalendar ( )
2532 static bool PlainDateTime_getCalendar(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2534 CallArgs args
= CallArgsFromVp(argc
, vp
);
2535 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_getCalendar
>(cx
,
2540 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
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());
2549 Rooted
<TimeZoneValue
> timeZone(cx
);
2550 if (!ToTemporalTimeZone(cx
, args
.get(0), &timeZone
)) {
2554 auto disambiguation
= TemporalDisambiguation::Compatible
;
2555 if (args
.hasDefined(1)) {
2557 Rooted
<JSObject
*> options(
2558 cx
, RequireObjectArg(cx
, "options", "toZonedDateTime", args
[1]));
2564 if (!ToTemporalDisambiguation(cx
, options
, &disambiguation
)) {
2571 if (!GetInstantFor(cx
, timeZone
, dateTime
, disambiguation
, &instant
)) {
2576 auto* result
= CreateTemporalZonedDateTime(cx
, instant
, timeZone
, calendar
);
2581 args
.rval().setObject(*result
);
2586 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2589 static bool PlainDateTime_toZonedDateTime(JSContext
* cx
, unsigned argc
,
2592 CallArgs args
= CallArgsFromVp(argc
, vp
);
2593 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toZonedDateTime
>(
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());
2605 auto* obj
= CreateTemporalDate(cx
, ToPlainDate(dateTime
), calendar
);
2610 args
.rval().setObject(*obj
);
2615 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2617 static bool PlainDateTime_toPlainDate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2619 CallArgs args
= CallArgsFromVp(argc
, vp
);
2620 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toPlainDate
>(cx
,
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());
2634 Rooted
<CalendarRecord
> calendar(cx
);
2635 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
2637 CalendarMethod::Fields
,
2638 CalendarMethod::YearMonthFromFields
,
2645 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
2646 if (!CalendarFields(cx
, calendar
,
2647 {CalendarField::MonthCode
, CalendarField::Year
},
2653 Rooted
<PlainObject
*> fields(cx
,
2654 PrepareTemporalFields(cx
, dateTime
, fieldNames
));
2660 auto obj
= CalendarYearMonthFromFields(cx
, calendar
, fields
);
2665 args
.rval().setObject(*obj
);
2670 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2672 static bool PlainDateTime_toPlainYearMonth(JSContext
* cx
, unsigned argc
,
2675 CallArgs args
= CallArgsFromVp(argc
, vp
);
2676 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toPlainYearMonth
>(
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());
2689 Rooted
<CalendarRecord
> calendar(cx
);
2690 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
2692 CalendarMethod::Fields
,
2693 CalendarMethod::MonthDayFromFields
,
2700 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
2701 if (!CalendarFields(cx
, calendar
,
2702 {CalendarField::Day
, CalendarField::MonthCode
},
2708 Rooted
<PlainObject
*> fields(cx
,
2709 PrepareTemporalFields(cx
, dateTime
, fieldNames
));
2715 auto obj
= CalendarMonthDayFromFields(cx
, calendar
, fields
);
2720 args
.rval().setObject(*obj
);
2725 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2727 static bool PlainDateTime_toPlainMonthDay(JSContext
* cx
, unsigned argc
,
2730 CallArgs args
= CallArgsFromVp(argc
, vp
);
2731 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toPlainMonthDay
>(
2736 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2738 static bool PlainDateTime_toPlainTime(JSContext
* cx
, const CallArgs
& args
) {
2739 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2742 auto* obj
= CreateTemporalTime(cx
, ToPlainTime(dateTime
));
2747 args
.rval().setObject(*obj
);
2752 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2754 static bool PlainDateTime_toPlainTime(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2756 CallArgs args
= CallArgsFromVp(argc
, vp
);
2757 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toPlainTime
>(cx
,
2761 const JSClass
PlainDateTimeObject::class_
= {
2762 "Temporal.PlainDateTime",
2763 JSCLASS_HAS_RESERVED_SLOTS(PlainDateTimeObject::SLOT_COUNT
) |
2764 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainDateTime
),
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),
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),
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
),
2827 const ClassSpec
PlainDateTimeObject::classSpec_
= {
2828 GenericCreateConstructor
<PlainDateTimeConstructor
, 3,
2829 gc::AllocKind::FUNCTION
>,
2830 GenericCreatePrototype
<PlainDateTimeObject
>,
2831 PlainDateTime_methods
,
2833 PlainDateTime_prototype_methods
,
2834 PlainDateTime_prototype_properties
,
2836 ClassSpec::DontDefineConstructor
,