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 // FIXME: spec bug - GetUTCEpochNanoseconds when called with large |year| may
182 // cause MakeDay to return NaN, which makes MakeDate return NaN, which is
183 // unexpected in GetUTCEpochNanoseconds, step 4.
184 // https://github.com/tc39/proposal-temporal/issues/2315
186 // Definitely in range.
187 if (minYear
< year
&& year
< maxYear
) {
193 if (year
!= minYear
) {
199 if (day
< (20 - 1)) {
205 // 275760 September, 13
206 if (year
!= maxYear
) {
219 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
220 * millisecond, microsecond, nanosecond )
222 bool js::temporal::ISODateTimeWithinLimits(double year
, double month
,
224 return ::ISODateTimeWithinLimits(year
, month
, day
);
228 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
229 * millisecond, microsecond, nanosecond )
231 bool js::temporal::ISODateTimeWithinLimits(const PlainDateTime
& dateTime
) {
232 const auto& [date
, time
] = dateTime
;
233 return ::ISODateTimeWithinLimits(date
.year
, date
.month
, date
.day
, time
.hour
,
234 time
.minute
, time
.second
, time
.millisecond
,
235 time
.microsecond
, time
.nanosecond
);
239 * ISODateTimeWithinLimits ( year, month, day, hour, minute, second,
240 * millisecond, microsecond, nanosecond )
242 bool js::temporal::ISODateTimeWithinLimits(const PlainDate
& date
) {
243 return ::ISODateTimeWithinLimits(date
.year
, date
.month
, date
.day
);
247 * CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second,
248 * millisecond, microsecond, nanosecond, calendar [ , newTarget ] )
250 static PlainDateTimeObject
* CreateTemporalDateTime(
251 JSContext
* cx
, const CallArgs
& args
, double isoYear
, double isoMonth
,
252 double isoDay
, double hour
, double minute
, double second
,
253 double millisecond
, double microsecond
, double nanosecond
,
254 Handle
<CalendarValue
> calendar
) {
255 MOZ_ASSERT(IsInteger(isoYear
));
256 MOZ_ASSERT(IsInteger(isoMonth
));
257 MOZ_ASSERT(IsInteger(isoDay
));
258 MOZ_ASSERT(IsInteger(hour
));
259 MOZ_ASSERT(IsInteger(minute
));
260 MOZ_ASSERT(IsInteger(second
));
261 MOZ_ASSERT(IsInteger(millisecond
));
262 MOZ_ASSERT(IsInteger(microsecond
));
263 MOZ_ASSERT(IsInteger(nanosecond
));
266 if (!ThrowIfInvalidISODate(cx
, isoYear
, isoMonth
, isoDay
)) {
271 if (!ThrowIfInvalidTime(cx
, hour
, minute
, second
, millisecond
, microsecond
,
277 if (!ISODateTimeWithinLimits(isoYear
, isoMonth
, isoDay
, hour
, minute
, second
,
278 millisecond
, microsecond
, nanosecond
)) {
279 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
280 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID
);
285 Rooted
<JSObject
*> proto(cx
);
286 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_PlainDateTime
,
291 auto* dateTime
= NewObjectWithClassProto
<PlainDateTimeObject
>(cx
, proto
);
297 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_YEAR_SLOT
,
298 Int32Value(int32_t(isoYear
)));
301 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_MONTH_SLOT
,
302 Int32Value(int32_t(isoMonth
)));
305 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT
,
306 Int32Value(int32_t(isoDay
)));
309 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT
,
310 Int32Value(int32_t(hour
)));
313 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_MINUTE_SLOT
,
314 Int32Value(int32_t(minute
)));
317 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_SECOND_SLOT
,
318 Int32Value(int32_t(second
)));
321 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_MILLISECOND_SLOT
,
322 Int32Value(int32_t(millisecond
)));
325 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_MICROSECOND_SLOT
,
326 Int32Value(int32_t(microsecond
)));
329 dateTime
->setFixedSlot(PlainDateTimeObject::ISO_NANOSECOND_SLOT
,
330 Int32Value(int32_t(nanosecond
)));
333 dateTime
->setFixedSlot(PlainDateTimeObject::CALENDAR_SLOT
,
341 * CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second,
342 * millisecond, microsecond, nanosecond, calendar [ , newTarget ] )
344 PlainDateTimeObject
* js::temporal::CreateTemporalDateTime(
345 JSContext
* cx
, const PlainDateTime
& dateTime
,
346 Handle
<CalendarValue
> calendar
) {
347 const auto& [date
, time
] = dateTime
;
348 const auto& [isoYear
, isoMonth
, isoDay
] = date
;
349 const auto& [hour
, minute
, second
, millisecond
, microsecond
, nanosecond
] =
353 if (!ThrowIfInvalidISODateTime(cx
, dateTime
)) {
358 if (!ISODateTimeWithinLimits(dateTime
)) {
359 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
360 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID
);
365 auto* object
= NewBuiltinClassInstance
<PlainDateTimeObject
>(cx
);
371 object
->setFixedSlot(PlainDateTimeObject::ISO_YEAR_SLOT
, Int32Value(isoYear
));
374 object
->setFixedSlot(PlainDateTimeObject::ISO_MONTH_SLOT
,
375 Int32Value(isoMonth
));
378 object
->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT
, Int32Value(isoDay
));
381 object
->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT
, Int32Value(hour
));
384 object
->setFixedSlot(PlainDateTimeObject::ISO_MINUTE_SLOT
,
388 object
->setFixedSlot(PlainDateTimeObject::ISO_SECOND_SLOT
,
392 object
->setFixedSlot(PlainDateTimeObject::ISO_MILLISECOND_SLOT
,
393 Int32Value(millisecond
));
396 object
->setFixedSlot(PlainDateTimeObject::ISO_MICROSECOND_SLOT
,
397 Int32Value(microsecond
));
400 object
->setFixedSlot(PlainDateTimeObject::ISO_NANOSECOND_SLOT
,
401 Int32Value(nanosecond
));
404 object
->setFixedSlot(PlainDateTimeObject::CALENDAR_SLOT
, calendar
.toValue());
411 * CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second,
412 * millisecond, microsecond, nanosecond, calendar [ , newTarget ] )
414 bool js::temporal::CreateTemporalDateTime(
415 JSContext
* cx
, const PlainDateTime
& dateTime
,
416 Handle
<CalendarValue
> calendar
,
417 MutableHandle
<PlainDateTimeWithCalendar
> result
) {
419 if (!ThrowIfInvalidISODateTime(cx
, dateTime
)) {
424 if (!ISODateTimeWithinLimits(dateTime
)) {
425 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
426 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID
);
430 result
.set(PlainDateTimeWithCalendar
{dateTime
, calendar
});
435 * InterpretTemporalDateTimeFields ( calendarRec, fields, options )
437 bool js::temporal::InterpretTemporalDateTimeFields(
438 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<PlainObject
*> fields
,
439 Handle
<PlainObject
*> options
, PlainDateTime
* result
) {
440 // Step 1. (Not applicable in our implementation.)
443 MOZ_ASSERT(CalendarMethodsRecordHasLookedUp(calendar
,
444 CalendarMethod::DateFromFields
));
447 TemporalTimeLike timeResult
;
448 if (!ToTemporalTimeRecord(cx
, fields
, &timeResult
)) {
453 auto overflow
= TemporalOverflow::Constrain
;
454 if (!ToTemporalOverflow(cx
, options
, &overflow
)) {
459 Rooted
<Value
> overflowValue(cx
);
460 if (overflow
== TemporalOverflow::Constrain
) {
461 overflowValue
.setString(cx
->names().constrain
);
463 MOZ_ASSERT(overflow
== TemporalOverflow::Reject
);
464 overflowValue
.setString(cx
->names().reject
);
466 if (!DefineDataProperty(cx
, options
, cx
->names().overflow
, overflowValue
)) {
472 js::temporal::CalendarDateFromFields(cx
, calendar
, fields
, options
);
476 auto date
= ToPlainDate(&temporalDate
.unwrap());
480 if (!RegulateTime(cx
, timeResult
, overflow
, &time
)) {
485 *result
= {date
, time
};
490 * InterpretTemporalDateTimeFields ( calendarRec, fields, options )
492 bool js::temporal::InterpretTemporalDateTimeFields(
493 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<PlainObject
*> fields
,
494 PlainDateTime
* result
) {
495 // TODO: Avoid creating the options object when CalendarDateFromFields calls
496 // the built-in Calendar.prototype.dateFromFields method.
497 Rooted
<PlainObject
*> options(cx
, NewPlainObjectWithProto(cx
, nullptr));
502 return InterpretTemporalDateTimeFields(cx
, calendar
, fields
, options
, result
);
506 * ToTemporalDateTime ( item [ , options ] )
508 static Wrapped
<PlainDateTimeObject
*> ToTemporalDateTime(
509 JSContext
* cx
, Handle
<Value
> item
, Handle
<JSObject
*> maybeOptions
) {
510 // Step 1. (Not applicable)
513 Rooted
<PlainObject
*> maybeResolvedOptions(cx
);
515 maybeResolvedOptions
= SnapshotOwnProperties(cx
, maybeOptions
);
516 if (!maybeResolvedOptions
) {
522 Rooted
<CalendarValue
> calendar(cx
);
523 PlainDateTime result
;
524 if (item
.isObject()) {
525 Rooted
<JSObject
*> itemObj(cx
, &item
.toObject());
528 if (itemObj
->canUnwrapAs
<PlainDateTimeObject
>()) {
533 if (auto* zonedDateTime
= itemObj
->maybeUnwrapIf
<ZonedDateTimeObject
>()) {
534 auto epochInstant
= ToInstant(zonedDateTime
);
535 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
536 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
538 if (!timeZone
.wrap(cx
)) {
541 if (!calendar
.wrap(cx
)) {
546 if (maybeResolvedOptions
) {
547 TemporalOverflow ignored
;
548 if (!ToTemporalOverflow(cx
, maybeResolvedOptions
, &ignored
)) {
554 return GetPlainDateTimeFor(cx
, timeZone
, epochInstant
, calendar
);
558 if (auto* date
= itemObj
->maybeUnwrapIf
<PlainDateObject
>()) {
559 PlainDateTime dateTime
= {ToPlainDate(date
), {}};
560 Rooted
<CalendarValue
> calendar(cx
, date
->calendar());
561 if (!calendar
.wrap(cx
)) {
566 if (maybeResolvedOptions
) {
567 TemporalOverflow ignored
;
568 if (!ToTemporalOverflow(cx
, maybeResolvedOptions
, &ignored
)) {
574 return CreateTemporalDateTime(cx
, dateTime
, calendar
);
578 if (!GetTemporalCalendarWithISODefault(cx
, itemObj
, &calendar
)) {
583 Rooted
<CalendarRecord
> calendarRec(cx
);
584 if (!CreateCalendarMethodsRecord(cx
, calendar
,
586 CalendarMethod::DateFromFields
,
587 CalendarMethod::Fields
,
594 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
595 if (!CalendarFields(cx
, calendarRec
,
596 {CalendarField::Day
, CalendarField::Month
,
597 CalendarField::MonthCode
, CalendarField::Year
},
603 if (!AppendSorted(cx
, fieldNames
.get(),
606 TemporalField::Microsecond
,
607 TemporalField::Millisecond
,
608 TemporalField::Minute
,
609 TemporalField::Nanosecond
,
610 TemporalField::Second
,
616 Rooted
<PlainObject
*> fields(cx
,
617 PrepareTemporalFields(cx
, itemObj
, fieldNames
));
623 if (maybeResolvedOptions
) {
624 if (!InterpretTemporalDateTimeFields(cx
, calendarRec
, fields
,
625 maybeResolvedOptions
, &result
)) {
629 if (!InterpretTemporalDateTimeFields(cx
, calendarRec
, fields
, &result
)) {
635 if (!item
.isString()) {
636 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, item
,
637 nullptr, "not a string");
640 Rooted
<JSString
*> string(cx
, item
.toString());
643 Rooted
<JSString
*> calendarString(cx
);
644 if (!ParseTemporalDateTimeString(cx
, string
, &result
, &calendarString
)) {
649 MOZ_ASSERT(IsValidISODate(result
.date
));
652 MOZ_ASSERT(IsValidTime(result
.time
));
655 if (calendarString
) {
656 if (!ToBuiltinCalendar(cx
, calendarString
, &calendar
)) {
660 calendar
.set(CalendarValue(cx
->names().iso8601
));
664 if (maybeResolvedOptions
) {
665 TemporalOverflow ignored
;
666 if (!ToTemporalOverflow(cx
, maybeResolvedOptions
, &ignored
)) {
673 return CreateTemporalDateTime(cx
, result
, calendar
);
677 * ToTemporalDateTime ( item [ , options ] )
679 Wrapped
<PlainDateTimeObject
*> js::temporal::ToTemporalDateTime(
680 JSContext
* cx
, Handle
<Value
> item
) {
681 return ::ToTemporalDateTime(cx
, item
, nullptr);
685 * ToTemporalDateTime ( item [ , options ] )
687 bool js::temporal::ToTemporalDateTime(JSContext
* cx
, Handle
<Value
> item
,
688 PlainDateTime
* result
) {
689 auto obj
= ::ToTemporalDateTime(cx
, item
, nullptr);
694 *result
= ToPlainDateTime(&obj
.unwrap());
699 * ToTemporalDateTime ( item [ , options ] )
701 static bool ToTemporalDateTime(
702 JSContext
* cx
, Handle
<Value
> item
,
703 MutableHandle
<PlainDateTimeWithCalendar
> result
) {
704 Handle
<JSObject
*> options
= nullptr;
706 auto* obj
= ::ToTemporalDateTime(cx
, item
, options
).unwrapOrNull();
711 auto dateTime
= ToPlainDateTime(obj
);
712 Rooted
<CalendarValue
> calendar(cx
, obj
->calendar());
713 if (!calendar
.wrap(cx
)) {
717 result
.set(PlainDateTimeWithCalendar
{dateTime
, calendar
});
722 * CompareISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
723 * d2, h2, min2, s2, ms2, mus2, ns2 )
725 static int32_t CompareISODateTime(const PlainDateTime
& one
,
726 const PlainDateTime
& two
) {
727 // Step 1. (Not applicable in our implementation.)
730 if (int32_t dateResult
= CompareISODate(one
.date
, two
.date
)) {
735 return CompareTemporalTime(one
.time
, two
.time
);
739 * AddDateTime ( year, month, day, hour, minute, second, millisecond,
740 * microsecond, nanosecond, calendarRec, years, months, weeks, days, norm,
743 static bool AddDateTime(JSContext
* cx
, const PlainDateTime
& dateTime
,
744 Handle
<CalendarRecord
> calendar
,
745 const NormalizedDuration
& duration
,
746 Handle
<JSObject
*> options
, PlainDateTime
* result
) {
747 MOZ_ASSERT(IsValidDuration(duration
));
750 MOZ_ASSERT(IsValidISODateTime(dateTime
));
751 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime
));
754 auto timeResult
= AddTime(dateTime
.time
, duration
.time
);
757 const auto& datePart
= dateTime
.date
;
760 auto dateDuration
= DateDuration
{
762 duration
.date
.months
,
764 duration
.date
.days
+ timeResult
.days
,
766 if (!ThrowIfInvalidDuration(cx
, dateDuration
)) {
772 if (!AddDate(cx
, calendar
, datePart
, dateDuration
, options
, &addedDate
)) {
777 *result
= {addedDate
, timeResult
.time
};
782 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
783 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
785 static bool DifferenceISODateTime(JSContext
* cx
, const PlainDateTime
& one
,
786 const PlainDateTime
& two
,
787 Handle
<CalendarRecord
> calendar
,
788 TemporalUnit largestUnit
,
789 Handle
<PlainObject
*> maybeOptions
,
790 NormalizedDuration
* result
) {
792 MOZ_ASSERT(IsValidISODateTime(one
));
793 MOZ_ASSERT(IsValidISODateTime(two
));
794 MOZ_ASSERT(ISODateTimeWithinLimits(one
));
795 MOZ_ASSERT(ISODateTimeWithinLimits(two
));
799 one
.date
!= two
.date
&& largestUnit
< TemporalUnit::Day
,
800 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateUntil
));
803 auto timeDuration
= DifferenceTime(one
.time
, two
.time
);
806 int32_t timeSign
= NormalizedTimeDurationSign(timeDuration
);
809 int32_t dateSign
= CompareISODate(two
.date
, one
.date
);
812 auto adjustedDate
= one
.date
;
815 if (timeSign
== -dateSign
) {
817 adjustedDate
= BalanceISODate(adjustedDate
.year
, adjustedDate
.month
,
818 adjustedDate
.day
- timeSign
);
821 if (!Add24HourDaysToNormalizedTimeDuration(cx
, timeDuration
, -timeSign
,
827 MOZ_ASSERT(IsValidISODate(adjustedDate
));
828 MOZ_ASSERT(ISODateTimeWithinLimits(adjustedDate
));
830 // TODO: Avoid allocating CreateTemporalDate.
833 Rooted
<PlainDateObject
*> date1(
834 cx
, CreateTemporalDate(cx
, adjustedDate
, calendar
.receiver()));
840 Rooted
<PlainDateObject
*> date2(
841 cx
, CreateTemporalDate(cx
, two
.date
, calendar
.receiver()));
847 auto dateLargestUnit
= std::min(TemporalUnit::Day
, largestUnit
);
849 DateDuration dateDifference
;
851 // FIXME: spec issue - this copy is no longer needed, all callers have
852 // already copied the user input object.
853 // https://github.com/tc39/proposal-temporal/issues/2525
856 Rooted
<PlainObject
*> untilOptions(cx
,
857 SnapshotOwnProperties(cx
, maybeOptions
));
863 Rooted
<Value
> largestUnitValue(
864 cx
, StringValue(TemporalUnitToString(cx
, dateLargestUnit
)));
865 if (!DefineDataProperty(cx
, untilOptions
, cx
->names().largestUnit
,
871 if (!DifferenceDate(cx
, calendar
, date1
, date2
, untilOptions
,
877 if (!DifferenceDate(cx
, calendar
, date1
, date2
, dateLargestUnit
,
884 return CreateNormalizedDurationRecord(cx
, dateDifference
, timeDuration
,
889 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
890 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
892 bool js::temporal::DifferenceISODateTime(JSContext
* cx
,
893 const PlainDateTime
& one
,
894 const PlainDateTime
& two
,
895 Handle
<CalendarRecord
> calendar
,
896 TemporalUnit largestUnit
,
897 NormalizedDuration
* result
) {
898 return ::DifferenceISODateTime(cx
, one
, two
, calendar
, largestUnit
, nullptr,
903 * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
904 * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
906 bool js::temporal::DifferenceISODateTime(
907 JSContext
* cx
, const PlainDateTime
& one
, const PlainDateTime
& two
,
908 Handle
<CalendarRecord
> calendar
, TemporalUnit largestUnit
,
909 Handle
<PlainObject
*> options
, NormalizedDuration
* result
) {
910 return ::DifferenceISODateTime(cx
, one
, two
, calendar
, largestUnit
, options
,
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
) {
1813 if (!RejectTemporalLikeObject(cx
, temporalDateTimeLike
)) {
1818 Rooted
<PlainObject
*> resolvedOptions(cx
);
1819 if (args
.hasDefined(1)) {
1820 Rooted
<JSObject
*> options(cx
,
1821 RequireObjectArg(cx
, "options", "with", args
[1]));
1825 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
1827 resolvedOptions
= NewPlainObjectWithProto(cx
, nullptr);
1829 if (!resolvedOptions
) {
1834 Rooted
<CalendarValue
> calendarValue(cx
, dateTime
->calendar());
1835 Rooted
<CalendarRecord
> calendar(cx
);
1836 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
1838 CalendarMethod::DateFromFields
,
1839 CalendarMethod::Fields
,
1840 CalendarMethod::MergeFields
,
1847 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
1848 if (!CalendarFields(cx
, calendar
,
1849 {CalendarField::Day
, CalendarField::Month
,
1850 CalendarField::MonthCode
, CalendarField::Year
},
1856 Rooted
<PlainObject
*> fields(cx
,
1857 PrepareTemporalFields(cx
, dateTime
, fieldNames
));
1864 using FieldName
= ImmutableTenuredPtr
<PropertyName
*> JSAtomState::*;
1869 {&JSAtomState::hour
, dateTime
->isoHour()},
1870 {&JSAtomState::minute
, dateTime
->isoMinute()},
1871 {&JSAtomState::second
, dateTime
->isoSecond()},
1872 {&JSAtomState::millisecond
, dateTime
->isoMillisecond()},
1873 {&JSAtomState::microsecond
, dateTime
->isoMicrosecond()},
1874 {&JSAtomState::nanosecond
, dateTime
->isoNanosecond()},
1877 Rooted
<Value
> timeFieldValue(cx
);
1878 for (const auto& timeField
: timeFields
) {
1879 Handle
<PropertyName
*> name
= cx
->names().*(timeField
.name
);
1880 timeFieldValue
.setInt32(timeField
.value
);
1882 if (!DefineDataProperty(cx
, fields
, name
, timeFieldValue
)) {
1888 if (!AppendSorted(cx
, fieldNames
.get(),
1890 TemporalField::Hour
,
1891 TemporalField::Microsecond
,
1892 TemporalField::Millisecond
,
1893 TemporalField::Minute
,
1894 TemporalField::Nanosecond
,
1895 TemporalField::Second
,
1901 Rooted
<PlainObject
*> partialDateTime(
1902 cx
, PreparePartialTemporalFields(cx
, temporalDateTimeLike
, fieldNames
));
1903 if (!partialDateTime
) {
1908 Rooted
<JSObject
*> mergedFields(
1909 cx
, CalendarMergeFields(cx
, calendar
, fields
, partialDateTime
));
1910 if (!mergedFields
) {
1915 fields
= PrepareTemporalFields(cx
, mergedFields
, fieldNames
);
1921 PlainDateTime result
;
1922 if (!InterpretTemporalDateTimeFields(cx
, calendar
, fields
, resolvedOptions
,
1928 MOZ_ASSERT(IsValidISODateTime(result
));
1931 auto* obj
= CreateTemporalDateTime(cx
, result
, calendar
.receiver());
1936 args
.rval().setObject(*obj
);
1941 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
1943 static bool PlainDateTime_with(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1945 CallArgs args
= CallArgsFromVp(argc
, vp
);
1946 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_with
>(cx
, args
);
1950 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1952 static bool PlainDateTime_withPlainTime(JSContext
* cx
, const CallArgs
& args
) {
1953 auto* temporalDateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
1954 auto date
= ToPlainDate(temporalDateTime
);
1955 Rooted
<CalendarValue
> calendar(cx
, temporalDateTime
->calendar());
1958 PlainTime time
= {};
1959 if (args
.hasDefined(0)) {
1960 if (!ToTemporalTime(cx
, args
[0], &time
)) {
1966 auto* obj
= CreateTemporalDateTime(cx
, {date
, time
}, calendar
);
1971 args
.rval().setObject(*obj
);
1976 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
1978 static bool PlainDateTime_withPlainTime(JSContext
* cx
, unsigned argc
,
1981 CallArgs args
= CallArgsFromVp(argc
, vp
);
1982 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_withPlainTime
>(
1987 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
1989 static bool PlainDateTime_withPlainDate(JSContext
* cx
, const CallArgs
& args
) {
1990 auto* temporalDateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
1991 auto time
= ToPlainTime(temporalDateTime
);
1992 Rooted
<CalendarValue
> calendar(cx
, temporalDateTime
->calendar());
1995 Rooted
<PlainDateWithCalendar
> plainDate(cx
);
1996 if (!ToTemporalDate(cx
, args
.get(0), &plainDate
)) {
1999 auto date
= plainDate
.date();
2002 if (!ConsolidateCalendars(cx
, calendar
, plainDate
.calendar(), &calendar
)) {
2007 auto* obj
= CreateTemporalDateTime(cx
, {date
, time
}, calendar
);
2012 args
.rval().setObject(*obj
);
2017 * Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike )
2019 static bool PlainDateTime_withPlainDate(JSContext
* cx
, unsigned argc
,
2022 CallArgs args
= CallArgsFromVp(argc
, vp
);
2023 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_withPlainDate
>(
2028 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2030 static bool PlainDateTime_withCalendar(JSContext
* cx
, const CallArgs
& args
) {
2031 auto* temporalDateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2032 auto dateTime
= ToPlainDateTime(temporalDateTime
);
2035 Rooted
<CalendarValue
> calendar(cx
);
2036 if (!ToTemporalCalendar(cx
, args
.get(0), &calendar
)) {
2041 auto* result
= CreateTemporalDateTime(cx
, dateTime
, calendar
);
2046 args
.rval().setObject(*result
);
2051 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
2053 static bool PlainDateTime_withCalendar(JSContext
* cx
, unsigned argc
,
2056 CallArgs args
= CallArgsFromVp(argc
, vp
);
2057 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_withCalendar
>(
2062 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2064 static bool PlainDateTime_add(JSContext
* cx
, const CallArgs
& args
) {
2066 return AddDurationToOrSubtractDurationFromPlainDateTime(
2067 cx
, PlainDateTimeDuration::Add
, args
);
2071 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
2073 static bool PlainDateTime_add(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2075 CallArgs args
= CallArgsFromVp(argc
, vp
);
2076 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_add
>(cx
, args
);
2080 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2083 static bool PlainDateTime_subtract(JSContext
* cx
, const CallArgs
& args
) {
2085 return AddDurationToOrSubtractDurationFromPlainDateTime(
2086 cx
, PlainDateTimeDuration::Subtract
, args
);
2090 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
2093 static bool PlainDateTime_subtract(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2095 CallArgs args
= CallArgsFromVp(argc
, vp
);
2096 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_subtract
>(cx
,
2101 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2103 static bool PlainDateTime_until(JSContext
* cx
, const CallArgs
& args
) {
2105 return DifferenceTemporalPlainDateTime(cx
, TemporalDifference::Until
, args
);
2109 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
2111 static bool PlainDateTime_until(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2113 CallArgs args
= CallArgsFromVp(argc
, vp
);
2114 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_until
>(cx
, args
);
2118 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2120 static bool PlainDateTime_since(JSContext
* cx
, const CallArgs
& args
) {
2122 return DifferenceTemporalPlainDateTime(cx
, TemporalDifference::Since
, args
);
2126 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
2128 static bool PlainDateTime_since(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2130 CallArgs args
= CallArgsFromVp(argc
, vp
);
2131 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_since
>(cx
, args
);
2135 * Temporal.PlainDateTime.prototype.round ( roundTo )
2137 static bool PlainDateTime_round(JSContext
* cx
, const CallArgs
& args
) {
2138 auto* temporalDateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2139 auto dateTime
= ToPlainDateTime(temporalDateTime
);
2140 Rooted
<CalendarValue
> calendar(cx
, temporalDateTime
->calendar());
2143 auto smallestUnit
= TemporalUnit::Auto
;
2144 auto roundingMode
= TemporalRoundingMode::HalfExpand
;
2145 auto roundingIncrement
= Increment
{1};
2146 if (args
.get(0).isString()) {
2147 // Step 4. (Not applicable in our implementation.)
2150 Rooted
<JSString
*> paramString(cx
, args
[0].toString());
2151 if (!GetTemporalUnit(cx
, paramString
, TemporalUnitKey::SmallestUnit
,
2152 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
2156 MOZ_ASSERT(TemporalUnit::Day
<= smallestUnit
&&
2157 smallestUnit
<= TemporalUnit::Nanosecond
);
2159 // Steps 6-8 and 10-12. (Implicit)
2162 Rooted
<JSObject
*> roundTo(
2163 cx
, RequireObjectArg(cx
, "roundTo", "round", args
.get(0)));
2169 if (!ToTemporalRoundingIncrement(cx
, roundTo
, &roundingIncrement
)) {
2174 if (!ToTemporalRoundingMode(cx
, roundTo
, &roundingMode
)) {
2179 if (!GetTemporalUnit(cx
, roundTo
, TemporalUnitKey::SmallestUnit
,
2180 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
2184 if (smallestUnit
== TemporalUnit::Auto
) {
2185 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2186 JSMSG_TEMPORAL_MISSING_OPTION
, "smallestUnit");
2190 MOZ_ASSERT(TemporalUnit::Day
<= smallestUnit
&&
2191 smallestUnit
<= TemporalUnit::Nanosecond
);
2194 auto maximum
= Increment
{1};
2195 bool inclusive
= true;
2196 if (smallestUnit
> TemporalUnit::Day
) {
2197 maximum
= MaximumTemporalDurationRoundingIncrement(smallestUnit
);
2202 if (!ValidateTemporalRoundingIncrement(cx
, roundingIncrement
, maximum
,
2209 if (smallestUnit
== TemporalUnit::Nanosecond
&&
2210 roundingIncrement
== Increment
{1}) {
2211 auto* obj
= CreateTemporalDateTime(cx
, dateTime
, calendar
);
2216 args
.rval().setObject(*obj
);
2222 RoundISODateTime(dateTime
, roundingIncrement
, smallestUnit
, roundingMode
);
2225 auto* obj
= CreateTemporalDateTime(cx
, result
, calendar
);
2230 args
.rval().setObject(*obj
);
2235 * Temporal.PlainDateTime.prototype.round ( roundTo )
2237 static bool PlainDateTime_round(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2239 CallArgs args
= CallArgsFromVp(argc
, vp
);
2240 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_round
>(cx
, args
);
2244 * Temporal.PlainDateTime.prototype.equals ( other )
2246 static bool PlainDateTime_equals(JSContext
* cx
, const CallArgs
& args
) {
2247 auto* temporalDateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2248 auto dateTime
= ToPlainDateTime(temporalDateTime
);
2249 Rooted
<CalendarValue
> calendar(cx
, temporalDateTime
->calendar());
2252 Rooted
<PlainDateTimeWithCalendar
> other(cx
);
2253 if (!::ToTemporalDateTime(cx
, args
.get(0), &other
)) {
2258 bool equals
= dateTime
== other
.dateTime();
2259 if (equals
&& !CalendarEquals(cx
, calendar
, other
.calendar(), &equals
)) {
2263 args
.rval().setBoolean(equals
);
2268 * Temporal.PlainDateTime.prototype.equals ( other )
2270 static bool PlainDateTime_equals(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2272 CallArgs args
= CallArgsFromVp(argc
, vp
);
2273 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_equals
>(cx
, args
);
2277 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2279 static bool PlainDateTime_toString(JSContext
* cx
, const CallArgs
& args
) {
2280 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2281 auto dt
= ToPlainDateTime(dateTime
);
2282 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
2284 SecondsStringPrecision precision
= {Precision::Auto(),
2285 TemporalUnit::Nanosecond
, Increment
{1}};
2286 auto roundingMode
= TemporalRoundingMode::Trunc
;
2287 auto showCalendar
= CalendarOption::Auto
;
2288 if (args
.hasDefined(0)) {
2290 Rooted
<JSObject
*> options(
2291 cx
, RequireObjectArg(cx
, "options", "toString", args
[0]));
2297 if (!ToCalendarNameOption(cx
, options
, &showCalendar
)) {
2302 auto digits
= Precision::Auto();
2303 if (!ToFractionalSecondDigits(cx
, options
, &digits
)) {
2308 if (!ToTemporalRoundingMode(cx
, options
, &roundingMode
)) {
2313 auto smallestUnit
= TemporalUnit::Auto
;
2314 if (!GetTemporalUnit(cx
, options
, TemporalUnitKey::SmallestUnit
,
2315 TemporalUnitGroup::Time
, &smallestUnit
)) {
2320 if (smallestUnit
== TemporalUnit::Hour
) {
2321 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2322 JSMSG_TEMPORAL_INVALID_UNIT_OPTION
, "hour",
2328 precision
= ToSecondsStringPrecision(smallestUnit
, digits
);
2333 RoundISODateTime(dt
, precision
.increment
, precision
.unit
, roundingMode
);
2336 JSString
* str
= ::TemporalDateTimeToString(cx
, result
, calendar
,
2337 precision
.precision
, showCalendar
);
2342 args
.rval().setString(str
);
2347 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
2349 static bool PlainDateTime_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2351 CallArgs args
= CallArgsFromVp(argc
, vp
);
2352 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toString
>(cx
,
2357 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2359 static bool PlainDateTime_toLocaleString(JSContext
* cx
, const CallArgs
& args
) {
2360 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2361 auto dt
= ToPlainDateTime(dateTime
);
2362 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
2365 JSString
* str
= ::TemporalDateTimeToString(
2366 cx
, dt
, calendar
, Precision::Auto(), CalendarOption::Auto
);
2371 args
.rval().setString(str
);
2376 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
2378 static bool PlainDateTime_toLocaleString(JSContext
* cx
, unsigned argc
,
2381 CallArgs args
= CallArgsFromVp(argc
, vp
);
2382 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toLocaleString
>(
2387 * Temporal.PlainDateTime.prototype.toJSON ( )
2389 static bool PlainDateTime_toJSON(JSContext
* cx
, const CallArgs
& args
) {
2390 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2391 auto dt
= ToPlainDateTime(dateTime
);
2392 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
2395 JSString
* str
= ::TemporalDateTimeToString(
2396 cx
, dt
, calendar
, Precision::Auto(), CalendarOption::Auto
);
2401 args
.rval().setString(str
);
2406 * Temporal.PlainDateTime.prototype.toJSON ( )
2408 static bool PlainDateTime_toJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2410 CallArgs args
= CallArgsFromVp(argc
, vp
);
2411 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toJSON
>(cx
, args
);
2415 * Temporal.PlainDateTime.prototype.valueOf ( )
2417 static bool PlainDateTime_valueOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2418 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_CANT_CONVERT_TO
,
2419 "PlainDateTime", "primitive type");
2424 * Temporal.PlainDateTime.prototype.getISOFields ( )
2426 static bool PlainDateTime_getISOFields(JSContext
* cx
, const CallArgs
& args
) {
2427 auto* temporalDateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2428 auto dateTime
= ToPlainDateTime(temporalDateTime
);
2429 auto calendar
= temporalDateTime
->calendar();
2432 Rooted
<IdValueVector
> fields(cx
, IdValueVector(cx
));
2435 if (!fields
.emplaceBack(NameToId(cx
->names().calendar
), calendar
.toValue())) {
2440 if (!fields
.emplaceBack(NameToId(cx
->names().isoDay
),
2441 Int32Value(dateTime
.date
.day
))) {
2446 if (!fields
.emplaceBack(NameToId(cx
->names().isoHour
),
2447 Int32Value(dateTime
.time
.hour
))) {
2452 if (!fields
.emplaceBack(NameToId(cx
->names().isoMicrosecond
),
2453 Int32Value(dateTime
.time
.microsecond
))) {
2458 if (!fields
.emplaceBack(NameToId(cx
->names().isoMillisecond
),
2459 Int32Value(dateTime
.time
.millisecond
))) {
2464 if (!fields
.emplaceBack(NameToId(cx
->names().isoMinute
),
2465 Int32Value(dateTime
.time
.minute
))) {
2470 if (!fields
.emplaceBack(NameToId(cx
->names().isoMonth
),
2471 Int32Value(dateTime
.date
.month
))) {
2476 if (!fields
.emplaceBack(NameToId(cx
->names().isoNanosecond
),
2477 Int32Value(dateTime
.time
.nanosecond
))) {
2482 if (!fields
.emplaceBack(NameToId(cx
->names().isoSecond
),
2483 Int32Value(dateTime
.time
.second
))) {
2488 if (!fields
.emplaceBack(NameToId(cx
->names().isoYear
),
2489 Int32Value(dateTime
.date
.year
))) {
2494 auto* obj
= NewPlainObjectWithUniqueNames(cx
, fields
);
2499 args
.rval().setObject(*obj
);
2504 * Temporal.PlainDateTime.prototype.getISOFields ( )
2506 static bool PlainDateTime_getISOFields(JSContext
* cx
, unsigned argc
,
2509 CallArgs args
= CallArgsFromVp(argc
, vp
);
2510 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_getISOFields
>(
2515 * Temporal.PlainDateTime.prototype.getCalendar ( )
2517 static bool PlainDateTime_getCalendar(JSContext
* cx
, const CallArgs
& args
) {
2518 auto* temporalDateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2519 Rooted
<CalendarValue
> calendar(cx
, temporalDateTime
->calendar());
2522 auto* obj
= ToTemporalCalendarObject(cx
, calendar
);
2527 args
.rval().setObject(*obj
);
2532 * Temporal.PlainDateTime.prototype.getCalendar ( )
2534 static bool PlainDateTime_getCalendar(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2536 CallArgs args
= CallArgsFromVp(argc
, vp
);
2537 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_getCalendar
>(cx
,
2542 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2545 static bool PlainDateTime_toZonedDateTime(JSContext
* cx
, const CallArgs
& args
) {
2546 Rooted
<PlainDateTimeObject
*> dateTime(
2547 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
2548 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
2551 Rooted
<TimeZoneValue
> timeZone(cx
);
2552 if (!ToTemporalTimeZone(cx
, args
.get(0), &timeZone
)) {
2556 auto disambiguation
= TemporalDisambiguation::Compatible
;
2557 if (args
.hasDefined(1)) {
2559 Rooted
<JSObject
*> options(
2560 cx
, RequireObjectArg(cx
, "options", "toZonedDateTime", args
[1]));
2566 if (!ToTemporalDisambiguation(cx
, options
, &disambiguation
)) {
2573 if (!GetInstantFor(cx
, timeZone
, dateTime
, disambiguation
, &instant
)) {
2578 auto* result
= CreateTemporalZonedDateTime(cx
, instant
, timeZone
, calendar
);
2583 args
.rval().setObject(*result
);
2588 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
2591 static bool PlainDateTime_toZonedDateTime(JSContext
* cx
, unsigned argc
,
2594 CallArgs args
= CallArgsFromVp(argc
, vp
);
2595 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toZonedDateTime
>(
2600 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2602 static bool PlainDateTime_toPlainDate(JSContext
* cx
, const CallArgs
& args
) {
2603 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2604 Rooted
<CalendarValue
> calendar(cx
, dateTime
->calendar());
2607 auto* obj
= CreateTemporalDate(cx
, ToPlainDate(dateTime
), calendar
);
2612 args
.rval().setObject(*obj
);
2617 * Temporal.PlainDateTime.prototype.toPlainDate ( )
2619 static bool PlainDateTime_toPlainDate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2621 CallArgs args
= CallArgsFromVp(argc
, vp
);
2622 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toPlainDate
>(cx
,
2627 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2629 static bool PlainDateTime_toPlainYearMonth(JSContext
* cx
,
2630 const CallArgs
& args
) {
2631 Rooted
<PlainDateTimeObject
*> dateTime(
2632 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
2633 Rooted
<CalendarValue
> calendarValue(cx
, dateTime
->calendar());
2636 Rooted
<CalendarRecord
> calendar(cx
);
2637 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
2639 CalendarMethod::Fields
,
2640 CalendarMethod::YearMonthFromFields
,
2647 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
2648 if (!CalendarFields(cx
, calendar
,
2649 {CalendarField::MonthCode
, CalendarField::Year
},
2655 Rooted
<PlainObject
*> fields(cx
,
2656 PrepareTemporalFields(cx
, dateTime
, fieldNames
));
2662 auto obj
= CalendarYearMonthFromFields(cx
, calendar
, fields
);
2667 args
.rval().setObject(*obj
);
2672 * Temporal.PlainDateTime.prototype.toPlainYearMonth ( )
2674 static bool PlainDateTime_toPlainYearMonth(JSContext
* cx
, unsigned argc
,
2677 CallArgs args
= CallArgsFromVp(argc
, vp
);
2678 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toPlainYearMonth
>(
2683 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2685 static bool PlainDateTime_toPlainMonthDay(JSContext
* cx
, const CallArgs
& args
) {
2686 Rooted
<PlainDateTimeObject
*> dateTime(
2687 cx
, &args
.thisv().toObject().as
<PlainDateTimeObject
>());
2688 Rooted
<CalendarValue
> calendarValue(cx
, dateTime
->calendar());
2691 Rooted
<CalendarRecord
> calendar(cx
);
2692 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
2694 CalendarMethod::Fields
,
2695 CalendarMethod::MonthDayFromFields
,
2702 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
2703 if (!CalendarFields(cx
, calendar
,
2704 {CalendarField::Day
, CalendarField::MonthCode
},
2710 Rooted
<PlainObject
*> fields(cx
,
2711 PrepareTemporalFields(cx
, dateTime
, fieldNames
));
2717 auto obj
= CalendarMonthDayFromFields(cx
, calendar
, fields
);
2722 args
.rval().setObject(*obj
);
2727 * Temporal.PlainDateTime.prototype.toPlainMonthDay ( )
2729 static bool PlainDateTime_toPlainMonthDay(JSContext
* cx
, unsigned argc
,
2732 CallArgs args
= CallArgsFromVp(argc
, vp
);
2733 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toPlainMonthDay
>(
2738 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2740 static bool PlainDateTime_toPlainTime(JSContext
* cx
, const CallArgs
& args
) {
2741 auto* dateTime
= &args
.thisv().toObject().as
<PlainDateTimeObject
>();
2744 auto* obj
= CreateTemporalTime(cx
, ToPlainTime(dateTime
));
2749 args
.rval().setObject(*obj
);
2754 * Temporal.PlainDateTime.prototype.toPlainTime ( )
2756 static bool PlainDateTime_toPlainTime(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2758 CallArgs args
= CallArgsFromVp(argc
, vp
);
2759 return CallNonGenericMethod
<IsPlainDateTime
, PlainDateTime_toPlainTime
>(cx
,
2763 const JSClass
PlainDateTimeObject::class_
= {
2764 "Temporal.PlainDateTime",
2765 JSCLASS_HAS_RESERVED_SLOTS(PlainDateTimeObject::SLOT_COUNT
) |
2766 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainDateTime
),
2768 &PlainDateTimeObject::classSpec_
,
2771 const JSClass
& PlainDateTimeObject::protoClass_
= PlainObject::class_
;
2773 static const JSFunctionSpec PlainDateTime_methods
[] = {
2774 JS_FN("from", PlainDateTime_from
, 1, 0),
2775 JS_FN("compare", PlainDateTime_compare
, 2, 0),
2779 static const JSFunctionSpec PlainDateTime_prototype_methods
[] = {
2780 JS_FN("with", PlainDateTime_with
, 1, 0),
2781 JS_FN("withPlainTime", PlainDateTime_withPlainTime
, 0, 0),
2782 JS_FN("withPlainDate", PlainDateTime_withPlainDate
, 1, 0),
2783 JS_FN("withCalendar", PlainDateTime_withCalendar
, 1, 0),
2784 JS_FN("add", PlainDateTime_add
, 1, 0),
2785 JS_FN("subtract", PlainDateTime_subtract
, 1, 0),
2786 JS_FN("until", PlainDateTime_until
, 1, 0),
2787 JS_FN("since", PlainDateTime_since
, 1, 0),
2788 JS_FN("round", PlainDateTime_round
, 1, 0),
2789 JS_FN("equals", PlainDateTime_equals
, 1, 0),
2790 JS_FN("toString", PlainDateTime_toString
, 0, 0),
2791 JS_FN("toLocaleString", PlainDateTime_toLocaleString
, 0, 0),
2792 JS_FN("toJSON", PlainDateTime_toJSON
, 0, 0),
2793 JS_FN("valueOf", PlainDateTime_valueOf
, 0, 0),
2794 JS_FN("toZonedDateTime", PlainDateTime_toZonedDateTime
, 1, 0),
2795 JS_FN("toPlainDate", PlainDateTime_toPlainDate
, 0, 0),
2796 JS_FN("toPlainYearMonth", PlainDateTime_toPlainYearMonth
, 0, 0),
2797 JS_FN("toPlainMonthDay", PlainDateTime_toPlainMonthDay
, 0, 0),
2798 JS_FN("toPlainTime", PlainDateTime_toPlainTime
, 0, 0),
2799 JS_FN("getISOFields", PlainDateTime_getISOFields
, 0, 0),
2800 JS_FN("getCalendar", PlainDateTime_getCalendar
, 0, 0),
2804 static const JSPropertySpec PlainDateTime_prototype_properties
[] = {
2805 JS_PSG("calendarId", PlainDateTime_calendarId
, 0),
2806 JS_PSG("year", PlainDateTime_year
, 0),
2807 JS_PSG("month", PlainDateTime_month
, 0),
2808 JS_PSG("monthCode", PlainDateTime_monthCode
, 0),
2809 JS_PSG("day", PlainDateTime_day
, 0),
2810 JS_PSG("hour", PlainDateTime_hour
, 0),
2811 JS_PSG("minute", PlainDateTime_minute
, 0),
2812 JS_PSG("second", PlainDateTime_second
, 0),
2813 JS_PSG("millisecond", PlainDateTime_millisecond
, 0),
2814 JS_PSG("microsecond", PlainDateTime_microsecond
, 0),
2815 JS_PSG("nanosecond", PlainDateTime_nanosecond
, 0),
2816 JS_PSG("dayOfWeek", PlainDateTime_dayOfWeek
, 0),
2817 JS_PSG("dayOfYear", PlainDateTime_dayOfYear
, 0),
2818 JS_PSG("weekOfYear", PlainDateTime_weekOfYear
, 0),
2819 JS_PSG("yearOfWeek", PlainDateTime_yearOfWeek
, 0),
2820 JS_PSG("daysInWeek", PlainDateTime_daysInWeek
, 0),
2821 JS_PSG("daysInMonth", PlainDateTime_daysInMonth
, 0),
2822 JS_PSG("daysInYear", PlainDateTime_daysInYear
, 0),
2823 JS_PSG("monthsInYear", PlainDateTime_monthsInYear
, 0),
2824 JS_PSG("inLeapYear", PlainDateTime_inLeapYear
, 0),
2825 JS_STRING_SYM_PS(toStringTag
, "Temporal.PlainDateTime", JSPROP_READONLY
),
2829 const ClassSpec
PlainDateTimeObject::classSpec_
= {
2830 GenericCreateConstructor
<PlainDateTimeConstructor
, 3,
2831 gc::AllocKind::FUNCTION
>,
2832 GenericCreatePrototype
<PlainDateTimeObject
>,
2833 PlainDateTime_methods
,
2835 PlainDateTime_prototype_methods
,
2836 PlainDateTime_prototype_properties
,
2838 ClassSpec::DontDefineConstructor
,