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/ZonedDateTime.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Maybe.h"
17 #include "NamespaceImports.h"
19 #include "builtin/temporal/Calendar.h"
20 #include "builtin/temporal/Duration.h"
21 #include "builtin/temporal/Instant.h"
22 #include "builtin/temporal/Int96.h"
23 #include "builtin/temporal/PlainDate.h"
24 #include "builtin/temporal/PlainDateTime.h"
25 #include "builtin/temporal/PlainMonthDay.h"
26 #include "builtin/temporal/PlainTime.h"
27 #include "builtin/temporal/PlainYearMonth.h"
28 #include "builtin/temporal/Temporal.h"
29 #include "builtin/temporal/TemporalFields.h"
30 #include "builtin/temporal/TemporalParser.h"
31 #include "builtin/temporal/TemporalRoundingMode.h"
32 #include "builtin/temporal/TemporalTypes.h"
33 #include "builtin/temporal/TemporalUnit.h"
34 #include "builtin/temporal/TimeZone.h"
35 #include "builtin/temporal/ToString.h"
36 #include "builtin/temporal/Wrapped.h"
37 #include "ds/IdValuePair.h"
38 #include "gc/AllocKind.h"
39 #include "gc/Barrier.h"
40 #include "js/AllocPolicy.h"
41 #include "js/CallArgs.h"
42 #include "js/CallNonGenericMethod.h"
44 #include "js/ComparisonOperators.h"
45 #include "js/ErrorReport.h"
46 #include "js/friend/ErrorMessages.h"
47 #include "js/GCVector.h"
49 #include "js/Printer.h"
50 #include "js/PropertyDescriptor.h"
51 #include "js/PropertySpec.h"
52 #include "js/RootingAPI.h"
53 #include "js/TracingAPI.h"
55 #include "vm/BigIntType.h"
56 #include "vm/BytecodeUtil.h"
57 #include "vm/GlobalObject.h"
58 #include "vm/JSAtomState.h"
59 #include "vm/JSContext.h"
60 #include "vm/JSObject.h"
61 #include "vm/ObjectOperations.h"
62 #include "vm/PlainObject.h"
63 #include "vm/StringType.h"
65 #include "vm/JSContext-inl.h"
66 #include "vm/JSObject-inl.h"
67 #include "vm/NativeObject-inl.h"
68 #include "vm/ObjectOperations-inl.h"
71 using namespace js::temporal
;
73 static inline bool IsZonedDateTime(Handle
<Value
> v
) {
74 return v
.isObject() && v
.toObject().is
<ZonedDateTimeObject
>();
77 // Returns |RoundNumberToIncrement(offsetNanoseconds, 60 × 10^9, "halfExpand")|.
78 static int64_t RoundNanosecondsToMinutesIncrement(int64_t offsetNanoseconds
) {
79 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
81 constexpr int64_t increment
= ToNanoseconds(TemporalUnit::Minute
);
83 int64_t quotient
= offsetNanoseconds
/ increment
;
84 int64_t remainder
= offsetNanoseconds
% increment
;
85 if (std::abs(remainder
* 2) >= increment
) {
86 quotient
+= (offsetNanoseconds
> 0 ? 1 : -1);
88 return quotient
* increment
;
92 * InterpretISODateTimeOffset ( year, month, day, hour, minute, second,
93 * millisecond, microsecond, nanosecond, offsetBehaviour, offsetNanoseconds,
94 * timeZoneRec, disambiguation, offsetOption, matchBehaviour )
96 bool js::temporal::InterpretISODateTimeOffset(
97 JSContext
* cx
, const PlainDateTime
& dateTime
,
98 OffsetBehaviour offsetBehaviour
, int64_t offsetNanoseconds
,
99 Handle
<TimeZoneRecord
> timeZone
, TemporalDisambiguation disambiguation
,
100 TemporalOffset offsetOption
, MatchBehaviour matchBehaviour
,
102 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
105 MOZ_ASSERT(IsValidISODate(dateTime
.date
));
108 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
109 timeZone
, TimeZoneMethod::GetOffsetNanosecondsFor
));
112 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
113 timeZone
, TimeZoneMethod::GetPossibleInstantsFor
));
116 Rooted
<CalendarValue
> calendar(cx
, CalendarValue(cx
->names().iso8601
));
117 Rooted
<PlainDateTimeWithCalendar
> temporalDateTime(cx
);
118 if (!CreateTemporalDateTime(cx
, dateTime
, calendar
, &temporalDateTime
)) {
123 if (offsetBehaviour
== OffsetBehaviour::Wall
||
124 (offsetBehaviour
== OffsetBehaviour::Option
&&
125 offsetOption
== TemporalOffset::Ignore
)) {
127 return GetInstantFor(cx
, timeZone
, temporalDateTime
, disambiguation
,
132 if (offsetBehaviour
== OffsetBehaviour::Exact
||
133 (offsetBehaviour
== OffsetBehaviour::Option
&&
134 offsetOption
== TemporalOffset::Use
)) {
136 auto epochNanoseconds
= GetUTCEpochNanoseconds(
137 dateTime
, InstantSpan::fromNanoseconds(offsetNanoseconds
));
140 if (!IsValidEpochInstant(epochNanoseconds
)) {
141 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
142 JSMSG_TEMPORAL_INSTANT_INVALID
);
147 *result
= epochNanoseconds
;
152 MOZ_ASSERT(offsetBehaviour
== OffsetBehaviour::Option
);
155 MOZ_ASSERT(offsetOption
== TemporalOffset::Prefer
||
156 offsetOption
== TemporalOffset::Reject
);
159 Rooted
<InstantVector
> possibleInstants(cx
, InstantVector(cx
));
160 if (!GetPossibleInstantsFor(cx
, timeZone
, temporalDateTime
,
161 &possibleInstants
)) {
166 if (!possibleInstants
.empty()) {
168 Rooted
<Wrapped
<InstantObject
*>> candidate(cx
);
169 for (size_t i
= 0; i
< possibleInstants
.length(); i
++) {
170 candidate
= possibleInstants
[i
];
173 int64_t candidateNanoseconds
;
174 if (!GetOffsetNanosecondsFor(cx
, timeZone
, candidate
,
175 &candidateNanoseconds
)) {
178 MOZ_ASSERT(std::abs(candidateNanoseconds
) <
179 ToNanoseconds(TemporalUnit::Day
));
182 if (candidateNanoseconds
== offsetNanoseconds
) {
183 auto* unwrapped
= candidate
.unwrap(cx
);
188 *result
= ToInstant(unwrapped
);
193 if (matchBehaviour
== MatchBehaviour::MatchMinutes
) {
195 int64_t roundedCandidateNanoseconds
=
196 RoundNanosecondsToMinutesIncrement(candidateNanoseconds
);
199 if (roundedCandidateNanoseconds
== offsetNanoseconds
) {
200 auto* unwrapped
= candidate
.unwrap(cx
);
205 // Step 10.a.iii.2.a.
206 *result
= ToInstant(unwrapped
);
214 if (offsetOption
== TemporalOffset::Reject
) {
215 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
216 JSMSG_TEMPORAL_ZONED_DATE_TIME_NO_TIME_FOUND
);
221 Rooted
<Wrapped
<InstantObject
*>> instant(cx
);
222 if (!DisambiguatePossibleInstants(cx
, possibleInstants
, timeZone
,
223 ToPlainDateTime(temporalDateTime
),
224 disambiguation
, &instant
)) {
228 auto* unwrappedInstant
= instant
.unwrap(cx
);
229 if (!unwrappedInstant
) {
234 *result
= ToInstant(unwrappedInstant
);
239 * ToTemporalZonedDateTime ( item [ , options ] )
241 static bool ToTemporalZonedDateTime(JSContext
* cx
, Handle
<Value
> item
,
242 Handle
<JSObject
*> maybeOptions
,
243 MutableHandle
<ZonedDateTime
> result
) {
244 // Step 1. (Not applicable in our implementation)
247 Rooted
<PlainObject
*> maybeResolvedOptions(cx
);
249 maybeResolvedOptions
= SnapshotOwnProperties(cx
, maybeOptions
);
250 if (!maybeResolvedOptions
) {
256 auto offsetBehaviour
= OffsetBehaviour::Option
;
259 auto matchBehaviour
= MatchBehaviour::MatchExactly
;
261 // Step 7. (Reordered)
262 int64_t offsetNanoseconds
= 0;
265 Rooted
<CalendarValue
> calendar(cx
);
266 Rooted
<TimeZoneValue
> timeZone(cx
);
267 PlainDateTime dateTime
;
268 auto disambiguation
= TemporalDisambiguation::Compatible
;
269 auto offsetOption
= TemporalOffset::Reject
;
270 if (item
.isObject()) {
271 Rooted
<JSObject
*> itemObj(cx
, &item
.toObject());
274 if (auto* zonedDateTime
= itemObj
->maybeUnwrapIf
<ZonedDateTimeObject
>()) {
275 auto instant
= ToInstant(zonedDateTime
);
276 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
277 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
279 if (!timeZone
.wrap(cx
)) {
282 if (!calendar
.wrap(cx
)) {
286 result
.set(ZonedDateTime
{instant
, timeZone
, calendar
});
291 if (!GetTemporalCalendarWithISODefault(cx
, itemObj
, &calendar
)) {
296 Rooted
<CalendarRecord
> calendarRec(cx
);
297 if (!CreateCalendarMethodsRecord(cx
, calendar
,
299 CalendarMethod::DateFromFields
,
300 CalendarMethod::Fields
,
307 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
308 if (!CalendarFields(cx
, calendarRec
,
309 {CalendarField::Day
, CalendarField::Month
,
310 CalendarField::MonthCode
, CalendarField::Year
},
316 if (!AppendSorted(cx
, fieldNames
.get(),
319 TemporalField::Microsecond
,
320 TemporalField::Millisecond
,
321 TemporalField::Minute
,
322 TemporalField::Nanosecond
,
323 TemporalField::Offset
,
324 TemporalField::Second
,
325 TemporalField::TimeZone
,
331 Rooted
<PlainObject
*> fields(
332 cx
, PrepareTemporalFields(cx
, itemObj
, fieldNames
,
333 {TemporalField::TimeZone
}));
339 Rooted
<Value
> timeZoneValue(cx
);
340 if (!GetProperty(cx
, fields
, fields
, cx
->names().timeZone
,
346 if (!ToTemporalTimeZone(cx
, timeZoneValue
, &timeZone
)) {
351 Rooted
<Value
> offsetValue(cx
);
352 if (!GetProperty(cx
, fields
, fields
, cx
->names().offset
, &offsetValue
)) {
357 MOZ_ASSERT(offsetValue
.isString() || offsetValue
.isUndefined());
360 Rooted
<JSString
*> offsetString(cx
);
361 if (offsetValue
.isString()) {
362 offsetString
= offsetValue
.toString();
364 offsetBehaviour
= OffsetBehaviour::Wall
;
367 if (maybeResolvedOptions
) {
369 if (!ToTemporalDisambiguation(cx
, maybeResolvedOptions
,
375 if (!ToTemporalOffset(cx
, maybeResolvedOptions
, &offsetOption
)) {
380 if (!InterpretTemporalDateTimeFields(cx
, calendarRec
, fields
,
381 maybeResolvedOptions
, &dateTime
)) {
385 // Steps 5.l-n. (Not applicable)
388 if (!InterpretTemporalDateTimeFields(cx
, calendarRec
, fields
,
395 if (offsetBehaviour
== OffsetBehaviour::Option
) {
396 if (!ParseDateTimeUTCOffset(cx
, offsetString
, &offsetNanoseconds
)) {
402 if (!item
.isString()) {
403 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, item
,
404 nullptr, "not a string");
407 Rooted
<JSString
*> string(cx
, item
.toString());
409 // Case 1: 19700101Z[+02:00]
410 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
412 // Case 2: 19700101+00:00[+02:00]
413 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "+02:00" }
415 // Case 3: 19700101[+02:00]
416 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
418 // Case 4: 19700101Z[Europe/Berlin]
419 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
421 // Case 5: 19700101+00:00[Europe/Berlin]
422 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "Europe/Berlin" }
424 // Case 6: 19700101[Europe/Berlin]
425 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
430 int64_t timeZoneOffset
;
431 Rooted
<ParsedTimeZone
> timeZoneAnnotation(cx
);
432 Rooted
<JSString
*> calendarString(cx
);
433 if (!ParseTemporalZonedDateTimeString(
434 cx
, string
, &dateTime
, &isUTC
, &hasOffset
, &timeZoneOffset
,
435 &timeZoneAnnotation
, &calendarString
)) {
440 MOZ_ASSERT(timeZoneAnnotation
);
443 if (!ToTemporalTimeZone(cx
, timeZoneAnnotation
, &timeZone
)) {
447 // Step 6.f. (Not applicable in our implementation.)
451 offsetBehaviour
= OffsetBehaviour::Exact
;
455 else if (!hasOffset
) {
456 offsetBehaviour
= OffsetBehaviour::Wall
;
460 if (calendarString
) {
461 if (!ToBuiltinCalendar(cx
, calendarString
, &calendar
)) {
465 calendar
.set(CalendarValue(cx
->names().iso8601
));
469 matchBehaviour
= MatchBehaviour::MatchMinutes
;
471 if (maybeResolvedOptions
) {
473 if (!ToTemporalDisambiguation(cx
, maybeResolvedOptions
,
479 if (!ToTemporalOffset(cx
, maybeResolvedOptions
, &offsetOption
)) {
484 TemporalOverflow ignored
;
485 if (!ToTemporalOverflow(cx
, maybeResolvedOptions
, &ignored
)) {
491 if (offsetBehaviour
== OffsetBehaviour::Option
) {
492 MOZ_ASSERT(hasOffset
);
493 offsetNanoseconds
= timeZoneOffset
;
498 Rooted
<TimeZoneRecord
> timeZoneRec(cx
);
499 if (!CreateTimeZoneMethodsRecord(cx
, timeZone
,
501 TimeZoneMethod::GetOffsetNanosecondsFor
,
502 TimeZoneMethod::GetPossibleInstantsFor
,
509 Instant epochNanoseconds
;
510 if (!InterpretISODateTimeOffset(
511 cx
, dateTime
, offsetBehaviour
, offsetNanoseconds
, timeZoneRec
,
512 disambiguation
, offsetOption
, matchBehaviour
, &epochNanoseconds
)) {
517 result
.set(ZonedDateTime
{epochNanoseconds
, timeZone
, calendar
});
522 * ToTemporalZonedDateTime ( item [ , options ] )
524 static bool ToTemporalZonedDateTime(JSContext
* cx
, Handle
<Value
> item
,
525 MutableHandle
<ZonedDateTime
> result
) {
526 return ToTemporalZonedDateTime(cx
, item
, nullptr, result
);
530 * ToTemporalZonedDateTime ( item [ , options ] )
532 static ZonedDateTimeObject
* ToTemporalZonedDateTime(
533 JSContext
* cx
, Handle
<Value
> item
, Handle
<JSObject
*> maybeOptions
) {
534 Rooted
<ZonedDateTime
> result(cx
);
535 if (!ToTemporalZonedDateTime(cx
, item
, maybeOptions
, &result
)) {
538 return CreateTemporalZonedDateTime(cx
, result
.instant(), result
.timeZone(),
543 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
546 static ZonedDateTimeObject
* CreateTemporalZonedDateTime(
547 JSContext
* cx
, const CallArgs
& args
, Handle
<BigInt
*> epochNanoseconds
,
548 Handle
<TimeZoneValue
> timeZone
, Handle
<CalendarValue
> calendar
) {
550 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds
));
553 Rooted
<JSObject
*> proto(cx
);
554 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_ZonedDateTime
,
559 auto* obj
= NewObjectWithClassProto
<ZonedDateTimeObject
>(cx
, proto
);
565 auto instant
= ToInstant(epochNanoseconds
);
566 obj
->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT
,
567 NumberValue(instant
.seconds
));
568 obj
->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT
,
569 Int32Value(instant
.nanoseconds
));
572 obj
->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT
, timeZone
.toSlotValue());
575 obj
->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT
, calendar
.toValue());
582 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
585 ZonedDateTimeObject
* js::temporal::CreateTemporalZonedDateTime(
586 JSContext
* cx
, const Instant
& instant
, Handle
<TimeZoneValue
> timeZone
,
587 Handle
<CalendarValue
> calendar
) {
589 MOZ_ASSERT(IsValidEpochInstant(instant
));
592 auto* obj
= NewBuiltinClassInstance
<ZonedDateTimeObject
>(cx
);
598 obj
->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT
,
599 NumberValue(instant
.seconds
));
600 obj
->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT
,
601 Int32Value(instant
.nanoseconds
));
604 obj
->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT
, timeZone
.toSlotValue());
607 obj
->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT
, calendar
.toValue());
613 struct PlainDateTimeAndInstant
{
614 PlainDateTime dateTime
;
619 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
622 static bool AddDaysToZonedDateTime(JSContext
* cx
, const Instant
& instant
,
623 const PlainDateTime
& dateTime
,
624 Handle
<TimeZoneRecord
> timeZone
,
625 Handle
<CalendarValue
> calendar
, int64_t days
,
626 TemporalOverflow overflow
,
627 PlainDateTimeAndInstant
* result
) {
628 // Step 1. (Not applicable in our implementation.)
630 // Step 2. (Not applicable)
634 *result
= {dateTime
, instant
};
640 if (!AddISODate(cx
, dateTime
.date
, {0, 0, 0, days
}, overflow
, &addedDate
)) {
645 Rooted
<PlainDateTimeWithCalendar
> dateTimeResult(cx
);
646 if (!CreateTemporalDateTime(cx
, {addedDate
, dateTime
.time
}, calendar
,
652 Instant instantResult
;
653 if (!GetInstantFor(cx
, timeZone
, dateTimeResult
,
654 TemporalDisambiguation::Compatible
, &instantResult
)) {
659 *result
= {ToPlainDateTime(dateTimeResult
), instantResult
};
664 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
667 bool js::temporal::AddDaysToZonedDateTime(
668 JSContext
* cx
, const Instant
& instant
, const PlainDateTime
& dateTime
,
669 Handle
<TimeZoneRecord
> timeZone
, Handle
<CalendarValue
> calendar
,
670 int64_t days
, TemporalOverflow overflow
, Instant
* result
) {
672 PlainDateTimeAndInstant dateTimeAndInstant
;
673 if (!::AddDaysToZonedDateTime(cx
, instant
, dateTime
, timeZone
, calendar
, days
,
674 overflow
, &dateTimeAndInstant
)) {
678 *result
= dateTimeAndInstant
.instant
;
683 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
686 bool js::temporal::AddDaysToZonedDateTime(JSContext
* cx
, const Instant
& instant
,
687 const PlainDateTime
& dateTime
,
688 Handle
<TimeZoneRecord
> timeZone
,
689 Handle
<CalendarValue
> calendar
,
690 int64_t days
, Instant
* result
) {
692 auto overflow
= TemporalOverflow::Constrain
;
695 return AddDaysToZonedDateTime(cx
, instant
, dateTime
, timeZone
, calendar
, days
,
700 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
701 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
703 static bool AddZonedDateTime(JSContext
* cx
, const Instant
& epochNanoseconds
,
704 Handle
<TimeZoneRecord
> timeZone
,
705 Handle
<CalendarRecord
> calendar
,
706 const NormalizedDuration
& duration
,
707 mozilla::Maybe
<const PlainDateTime
&> dateTime
,
708 Handle
<JSObject
*> maybeOptions
, Instant
* result
) {
709 MOZ_ASSERT(IsValidEpochInstant(epochNanoseconds
));
710 MOZ_ASSERT(IsValidDuration(duration
));
713 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
714 timeZone
, TimeZoneMethod::GetPossibleInstantsFor
));
717 MOZ_ASSERT_IF(!dateTime
,
718 TimeZoneMethodsRecordHasLookedUp(
719 timeZone
, TimeZoneMethod::GetOffsetNanosecondsFor
));
721 // Steps 4-5. (Not applicable in our implementation)
724 if (duration
.date
== DateDuration
{}) {
726 return AddInstant(cx
, epochNanoseconds
, duration
.time
, result
);
729 // Step 7. (Not applicable in our implementation)
732 PlainDateTime temporalDateTime
;
735 temporalDateTime
= *dateTime
;
738 if (!GetPlainDateTimeFor(cx
, timeZone
, epochNanoseconds
,
739 &temporalDateTime
)) {
743 auto& [date
, time
] = temporalDateTime
;
746 if (duration
.date
.years
== 0 && duration
.date
.months
== 0 &&
747 duration
.date
.weeks
== 0) {
749 auto overflow
= TemporalOverflow::Constrain
;
751 if (!ToTemporalOverflow(cx
, maybeOptions
, &overflow
)) {
757 Instant intermediate
;
758 if (!AddDaysToZonedDateTime(cx
, epochNanoseconds
, temporalDateTime
,
759 timeZone
, calendar
.receiver(),
760 duration
.date
.days
, overflow
, &intermediate
)) {
765 return AddInstant(cx
, intermediate
, duration
.time
, result
);
770 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateAdd
));
773 const auto& datePart
= date
;
776 const auto& dateDuration
= duration
.date
;
781 if (!CalendarDateAdd(cx
, calendar
, datePart
, dateDuration
, maybeOptions
,
786 if (!CalendarDateAdd(cx
, calendar
, datePart
, dateDuration
, &addedDate
)) {
792 Rooted
<PlainDateTimeWithCalendar
> intermediateDateTime(cx
);
793 if (!CreateTemporalDateTime(cx
, {addedDate
, time
}, calendar
.receiver(),
794 &intermediateDateTime
)) {
799 Instant intermediateInstant
;
800 if (!GetInstantFor(cx
, timeZone
, intermediateDateTime
,
801 TemporalDisambiguation::Compatible
,
802 &intermediateInstant
)) {
807 return AddInstant(cx
, intermediateInstant
, duration
.time
, result
);
811 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
812 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
814 static bool AddZonedDateTime(JSContext
* cx
, const Instant
& epochNanoseconds
,
815 Handle
<TimeZoneRecord
> timeZone
,
816 Handle
<CalendarRecord
> calendar
,
817 const NormalizedDuration
& duration
,
818 Handle
<JSObject
*> maybeOptions
, Instant
* result
) {
819 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
, duration
,
820 mozilla::Nothing(), maybeOptions
, result
);
824 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
825 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
827 bool js::temporal::AddZonedDateTime(JSContext
* cx
,
828 const Instant
& epochNanoseconds
,
829 Handle
<TimeZoneRecord
> timeZone
,
830 Handle
<CalendarRecord
> calendar
,
831 const NormalizedDuration
& duration
,
833 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
, duration
,
834 mozilla::Nothing(), nullptr, result
);
838 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
839 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
841 bool js::temporal::AddZonedDateTime(JSContext
* cx
,
842 const Instant
& epochNanoseconds
,
843 Handle
<TimeZoneRecord
> timeZone
,
844 Handle
<CalendarRecord
> calendar
,
845 const NormalizedDuration
& duration
,
846 const PlainDateTime
& dateTime
,
848 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
, duration
,
849 mozilla::SomeRef(dateTime
), nullptr, result
);
853 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
854 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
856 bool js::temporal::AddZonedDateTime(JSContext
* cx
,
857 const Instant
& epochNanoseconds
,
858 Handle
<TimeZoneRecord
> timeZone
,
859 Handle
<CalendarRecord
> calendar
,
860 const DateDuration
& duration
,
862 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
,
863 {duration
, {}}, mozilla::Nothing(), nullptr,
868 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
869 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
871 bool js::temporal::AddZonedDateTime(JSContext
* cx
,
872 const Instant
& epochNanoseconds
,
873 Handle
<TimeZoneRecord
> timeZone
,
874 Handle
<CalendarRecord
> calendar
,
875 const DateDuration
& duration
,
876 const PlainDateTime
& dateTime
,
878 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
,
879 {duration
, {}}, mozilla::SomeRef(dateTime
), nullptr,
884 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
885 * precalculatedPlainDateTime ] )
887 static bool NormalizedTimeDurationToDays(
888 JSContext
* cx
, const NormalizedTimeDuration
& duration
,
889 Handle
<ZonedDateTime
> zonedRelativeTo
, Handle
<TimeZoneRecord
> timeZone
,
890 mozilla::Maybe
<const PlainDateTime
&> precalculatedPlainDateTime
,
891 NormalizedTimeAndDays
* result
) {
892 MOZ_ASSERT(IsValidNormalizedTimeDuration(duration
));
895 int32_t sign
= NormalizedTimeDurationSign(duration
);
899 *result
= {int64_t(0), int64_t(0), ToNanoseconds(TemporalUnit::Day
)};
904 const auto& startNs
= zonedRelativeTo
.instant();
908 if (!AddInstant(cx
, startNs
, duration
, &endNs
)) {
913 PlainDateTime startDateTime
;
914 if (!precalculatedPlainDateTime
) {
915 if (!GetPlainDateTimeFor(cx
, timeZone
, startNs
, &startDateTime
)) {
919 startDateTime
= *precalculatedPlainDateTime
;
923 PlainDateTime endDateTime
;
924 if (!GetPlainDateTimeFor(cx
, timeZone
, endNs
, &endDateTime
)) {
928 // Steps 9-10. (Not applicable in our implementation.)
931 int32_t days
= DaysUntil(startDateTime
.date
, endDateTime
.date
);
932 MOZ_ASSERT(std::abs(days
) <= 200'000'000);
935 int32_t timeSign
= CompareTemporalTime(startDateTime
.time
, endDateTime
.time
);
938 if (days
> 0 && timeSign
> 0) {
940 } else if (days
< 0 && timeSign
< 0) {
945 PlainDateTimeAndInstant relativeResult
;
946 if (!::AddDaysToZonedDateTime(cx
, startNs
, startDateTime
, timeZone
,
947 zonedRelativeTo
.calendar(), days
,
948 TemporalOverflow::Constrain
, &relativeResult
)) {
951 MOZ_ASSERT(IsValidISODateTime(relativeResult
.dateTime
));
952 MOZ_ASSERT(IsValidEpochInstant(relativeResult
.instant
));
955 if (sign
> 0 && days
> 0 && relativeResult
.instant
> endNs
) {
960 if (!::AddDaysToZonedDateTime(
961 cx
, startNs
, startDateTime
, timeZone
, zonedRelativeTo
.calendar(),
962 days
, TemporalOverflow::Constrain
, &relativeResult
)) {
965 MOZ_ASSERT(IsValidISODateTime(relativeResult
.dateTime
));
966 MOZ_ASSERT(IsValidEpochInstant(relativeResult
.instant
));
969 if (days
> 0 && relativeResult
.instant
> endNs
) {
970 JS_ReportErrorNumberASCII(
971 cx
, GetErrorMessage
, nullptr,
972 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCONSISTENT_INSTANT
);
975 MOZ_ASSERT_IF(days
> 0, relativeResult
.instant
<= endNs
);
978 MOZ_ASSERT_IF(days
== 0, relativeResult
.instant
== startNs
);
980 // Step 17. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
981 auto ns
= endNs
- relativeResult
.instant
;
982 MOZ_ASSERT(IsValidInstantSpan(ns
));
985 PlainDateTimeAndInstant oneDayFarther
;
986 if (!::AddDaysToZonedDateTime(cx
, relativeResult
.instant
,
987 relativeResult
.dateTime
, timeZone
,
988 zonedRelativeTo
.calendar(), sign
,
989 TemporalOverflow::Constrain
, &oneDayFarther
)) {
992 MOZ_ASSERT(IsValidISODateTime(oneDayFarther
.dateTime
));
993 MOZ_ASSERT(IsValidEpochInstant(oneDayFarther
.instant
));
995 // FIXME: spec issue - bad markup for |oneDayFarther|.
997 // Step 19. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
998 auto dayLengthNs
= oneDayFarther
.instant
- relativeResult
.instant
;
999 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs
));
1003 // ns = endNs - relativeResult.instant
1004 // dayLengthNs = oneDayFarther.instant - relativeResult.instant
1005 // oneDayLess = ns - dayLengthNs
1006 // = (endNs - relativeResult.instant) - (oneDayFarther.instant - relativeResult.instant)
1007 // = endNs - relativeResult.instant - oneDayFarther.instant + relativeResult.instant
1008 // = endNs - oneDayFarther.instant
1010 // |endNs| and |oneDayFarther.instant| are both valid epoch instant values,
1011 // so the difference |oneDayLess| is a valid epoch instant difference value.
1015 // Step 20. (Inlined SubtractNormalizedTimeDuration)
1016 auto oneDayLess
= ns
- dayLengthNs
;
1017 MOZ_ASSERT(IsValidInstantSpan(oneDayLess
));
1018 MOZ_ASSERT(oneDayLess
== (endNs
- oneDayFarther
.instant
));
1021 if (oneDayLess
== InstantSpan
{} ||
1022 ((oneDayLess
< InstantSpan
{}) == (sign
< 0))) {
1027 relativeResult
= oneDayFarther
;
1033 PlainDateTimeAndInstant oneDayFarther
;
1034 if (!::AddDaysToZonedDateTime(
1035 cx
, relativeResult
.instant
, relativeResult
.dateTime
, timeZone
,
1036 zonedRelativeTo
.calendar(), sign
, TemporalOverflow::Constrain
,
1040 MOZ_ASSERT(IsValidISODateTime(oneDayFarther
.dateTime
));
1041 MOZ_ASSERT(IsValidEpochInstant(oneDayFarther
.instant
));
1043 // FIXME: spec issue - bad markup for |oneDayFarther|.
1045 // Step 21.e. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
1046 dayLengthNs
= oneDayFarther
.instant
- relativeResult
.instant
;
1047 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs
));
1052 // = endNs - oneDayFarther.instant'
1053 // relativeResult.instant = oneDayFarther.instant'
1054 // dayLengthNs = oneDayFarther.instant - relativeResult.instant
1055 // = oneDayFarther.instant - oneDayFarther.instant'
1056 // oneDayLess = ns - dayLengthNs
1057 // = (endNs - oneDayFarther.instant') - (oneDayFarther.instant - oneDayFarther.instant')
1058 // = endNs - oneDayFarther.instant' - oneDayFarther.instant + oneDayFarther.instant'
1059 // = endNs - oneDayFarther.instant
1061 // Where |oneDayLess'| and |oneDayFarther.instant'| denote the variables
1062 // from before this if-statement block.
1064 // |endNs| and |oneDayFarther.instant| are both valid epoch instant values,
1065 // so the difference |oneDayLess| is a valid epoch instant difference value.
1069 // FIXME: spec issue - SubtractNormalizedTimeDuration is infallible
1072 auto oneDayLess
= ns
- dayLengthNs
;
1073 if (oneDayLess
== InstantSpan
{} ||
1074 ((oneDayLess
< InstantSpan
{}) == (sign
< 0))) {
1075 JS_ReportErrorNumberASCII(
1076 cx
, GetErrorMessage
, nullptr,
1077 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCONSISTENT_INSTANT
);
1083 if (days
< 0 && sign
> 0) {
1084 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1085 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN
,
1091 if (days
> 0 && sign
< 0) {
1092 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1093 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN
,
1098 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs
));
1099 MOZ_ASSERT(IsValidInstantSpan(ns
));
1103 if (ns
> InstantSpan
{}) {
1104 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1105 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN
,
1110 MOZ_ASSERT(ns
>= InstantSpan
{});
1114 dayLengthNs
= dayLengthNs
.abs();
1115 MOZ_ASSERT(ns
.abs() < dayLengthNs
);
1118 constexpr auto maxDayLength
= Int128
{1} << 53;
1119 auto dayLengthNanos
= dayLengthNs
.toNanoseconds();
1120 if (dayLengthNanos
>= maxDayLength
) {
1121 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1122 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN
,
1127 auto timeNanos
= ns
.toNanoseconds();
1128 MOZ_ASSERT(timeNanos
== Int128
{int64_t(timeNanos
)},
1129 "abs(ns) < dayLengthNs < 2**53 implies that |ns| fits in int64");
1132 static_assert(std::numeric_limits
<decltype(days
)>::max() <=
1133 ((int64_t(1) << 53) / (24 * 60 * 60)));
1136 *result
= {int64_t(days
), int64_t(timeNanos
), int64_t(dayLengthNanos
)};
1141 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
1142 * precalculatedPlainDateTime ] )
1144 bool js::temporal::NormalizedTimeDurationToDays(
1145 JSContext
* cx
, const NormalizedTimeDuration
& duration
,
1146 Handle
<ZonedDateTime
> zonedRelativeTo
, Handle
<TimeZoneRecord
> timeZone
,
1147 NormalizedTimeAndDays
* result
) {
1148 return ::NormalizedTimeDurationToDays(cx
, duration
, zonedRelativeTo
, timeZone
,
1149 mozilla::Nothing(), result
);
1153 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
1154 * precalculatedPlainDateTime ] )
1156 bool js::temporal::NormalizedTimeDurationToDays(
1157 JSContext
* cx
, const NormalizedTimeDuration
& duration
,
1158 Handle
<ZonedDateTime
> zonedRelativeTo
, Handle
<TimeZoneRecord
> timeZone
,
1159 const PlainDateTime
& precalculatedPlainDateTime
,
1160 NormalizedTimeAndDays
* result
) {
1161 return ::NormalizedTimeDurationToDays(
1162 cx
, duration
, zonedRelativeTo
, timeZone
,
1163 mozilla::SomeRef(precalculatedPlainDateTime
), result
);
1167 * DifferenceZonedDateTime ( ns1, ns2, timeZoneRec, calendarRec, largestUnit,
1168 * options, precalculatedPlainDateTime )
1170 static bool DifferenceZonedDateTime(
1171 JSContext
* cx
, const Instant
& ns1
, const Instant
& ns2
,
1172 Handle
<TimeZoneRecord
> timeZone
, Handle
<CalendarRecord
> calendar
,
1173 TemporalUnit largestUnit
, Handle
<PlainObject
*> maybeOptions
,
1174 mozilla::Maybe
<const PlainDateTime
&> precalculatedPlainDateTime
,
1175 NormalizedDuration
* result
) {
1176 MOZ_ASSERT(IsValidEpochInstant(ns1
));
1177 MOZ_ASSERT(IsValidEpochInstant(ns2
));
1181 *result
= CreateNormalizedDurationRecord({}, {});
1186 PlainDateTime startDateTime
;
1187 if (!precalculatedPlainDateTime
) {
1189 if (!GetPlainDateTimeFor(cx
, timeZone
, ns1
, &startDateTime
)) {
1194 startDateTime
= *precalculatedPlainDateTime
;
1198 PlainDateTime endDateTime
;
1199 if (!GetPlainDateTimeFor(cx
, timeZone
, ns2
, &endDateTime
)) {
1204 DateDuration dateDifference
;
1206 if (!DifferenceISODateTime(cx
, startDateTime
, endDateTime
, calendar
,
1207 largestUnit
, maybeOptions
, &dateDifference
)) {
1211 if (!DifferenceISODateTime(cx
, startDateTime
, endDateTime
, calendar
,
1212 largestUnit
, &dateDifference
)) {
1218 Instant intermediateNs
;
1219 if (!AddZonedDateTime(cx
, ns1
, timeZone
, calendar
,
1221 dateDifference
.years
,
1222 dateDifference
.months
,
1223 dateDifference
.weeks
,
1225 startDateTime
, &intermediateNs
)) {
1228 MOZ_ASSERT(IsValidEpochInstant(intermediateNs
));
1232 NormalizedTimeDurationFromEpochNanosecondsDifference(ns2
, intermediateNs
);
1235 Rooted
<ZonedDateTime
> intermediate(
1237 ZonedDateTime
{intermediateNs
, timeZone
.receiver(), calendar
.receiver()});
1240 NormalizedTimeAndDays timeAndDays
;
1241 if (!NormalizedTimeDurationToDays(cx
, timeDuration
, intermediate
, timeZone
,
1247 auto dateDuration
= DateDuration
{
1248 dateDifference
.years
,
1249 dateDifference
.months
,
1250 dateDifference
.weeks
,
1253 if (!ThrowIfInvalidDuration(cx
, dateDuration
)) {
1257 return CreateNormalizedDurationRecord(
1259 NormalizedTimeDuration::fromNanoseconds(timeAndDays
.time
), result
);
1263 * DifferenceZonedDateTime ( ns1, ns2, timeZoneRec, calendarRec, largestUnit,
1264 * options, precalculatedPlainDateTime )
1266 bool js::temporal::DifferenceZonedDateTime(
1267 JSContext
* cx
, const Instant
& ns1
, const Instant
& ns2
,
1268 Handle
<TimeZoneRecord
> timeZone
, Handle
<CalendarRecord
> calendar
,
1269 TemporalUnit largestUnit
, const PlainDateTime
& precalculatedPlainDateTime
,
1270 NormalizedDuration
* result
) {
1271 return ::DifferenceZonedDateTime(
1272 cx
, ns1
, ns2
, timeZone
, calendar
, largestUnit
, nullptr,
1273 mozilla::SomeRef(precalculatedPlainDateTime
), result
);
1277 * TimeZoneEquals ( one, two )
1279 static bool TimeZoneEqualsOrThrow(JSContext
* cx
, Handle
<TimeZoneValue
> one
,
1280 Handle
<TimeZoneValue
> two
) {
1282 if (one
.isObject() && two
.isObject() && one
.toObject() == two
.toObject()) {
1287 Rooted
<JSString
*> timeZoneOne(cx
, ToTemporalTimeZoneIdentifier(cx
, one
));
1293 Rooted
<JSString
*> timeZoneTwo(cx
, ToTemporalTimeZoneIdentifier(cx
, two
));
1300 if (!TimeZoneEquals(cx
, timeZoneOne
, timeZoneTwo
, &equals
)) {
1307 // Throw an error when the time zone identifiers don't match. Used when
1308 // unequal time zones throw a RangeError.
1309 if (auto charsOne
= QuoteString(cx
, timeZoneOne
)) {
1310 if (auto charsTwo
= QuoteString(cx
, timeZoneTwo
)) {
1311 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1312 JSMSG_TEMPORAL_TIMEZONE_INCOMPATIBLE
,
1313 charsOne
.get(), charsTwo
.get());
1320 * RoundISODateTime ( year, month, day, hour, minute, second, millisecond,
1321 * microsecond, nanosecond, increment, unit, roundingMode [ , dayLength ] )
1323 static bool RoundISODateTime(JSContext
* cx
, const PlainDateTime
& dateTime
,
1324 Increment increment
, TemporalUnit unit
,
1325 TemporalRoundingMode roundingMode
,
1326 const InstantSpan
& dayLength
,
1327 PlainDateTime
* result
) {
1328 MOZ_ASSERT(IsValidInstantSpan(dayLength
));
1329 MOZ_ASSERT(dayLength
> (InstantSpan
{}));
1331 const auto& [date
, time
] = dateTime
;
1334 MOZ_ASSERT(IsValidISODateTime(dateTime
));
1335 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime
));
1337 // Step 2. (Not applicable in our implementation.)
1340 auto roundedTime
= RoundTime(time
, increment
, unit
, roundingMode
, dayLength
);
1342 // |dayLength| can be as small as 1, so the number of rounded days can be as
1343 // large as the number of nanoseconds in |time|.
1344 MOZ_ASSERT(0 <= roundedTime
.days
&&
1345 roundedTime
.days
< ToNanoseconds(TemporalUnit::Day
));
1348 PlainDate balanceResult
;
1349 if (!BalanceISODate(cx
, date
.year
, date
.month
,
1350 int64_t(date
.day
) + roundedTime
.days
, &balanceResult
)) {
1355 *result
= {balanceResult
, roundedTime
.time
};
1360 * DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options )
1362 static bool DifferenceTemporalZonedDateTime(JSContext
* cx
,
1363 TemporalDifference operation
,
1364 const CallArgs
& args
) {
1365 Rooted
<ZonedDateTime
> zonedDateTime(
1366 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1368 // Step 1. (Not applicable in our implementation.)
1371 Rooted
<ZonedDateTime
> other(cx
);
1372 if (!ToTemporalZonedDateTime(cx
, args
.get(0), &other
)) {
1377 if (!CalendarEqualsOrThrow(cx
, zonedDateTime
.calendar(), other
.calendar())) {
1382 Rooted
<PlainObject
*> resolvedOptions(cx
);
1383 DifferenceSettings settings
;
1384 if (args
.hasDefined(1)) {
1385 Rooted
<JSObject
*> options(
1386 cx
, RequireObjectArg(cx
, "options", ToName(operation
), args
[1]));
1392 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
1393 if (!resolvedOptions
) {
1398 if (!GetDifferenceSettings(
1399 cx
, operation
, resolvedOptions
, TemporalUnitGroup::DateTime
,
1400 TemporalUnit::Nanosecond
, TemporalUnit::Hour
, &settings
)) {
1406 TemporalUnit::Nanosecond
,
1408 TemporalRoundingMode::Trunc
,
1414 if (settings
.largestUnit
> TemporalUnit::Day
) {
1415 MOZ_ASSERT(settings
.smallestUnit
>= settings
.largestUnit
);
1418 auto difference
= DifferenceInstant(
1419 zonedDateTime
.instant(), other
.instant(), settings
.roundingIncrement
,
1420 settings
.smallestUnit
, settings
.roundingMode
);
1423 auto balancedTime
= BalanceTimeDuration(difference
, settings
.largestUnit
);
1426 auto duration
= balancedTime
.toDuration();
1427 if (operation
== TemporalDifference::Since
) {
1428 duration
= duration
.negate();
1431 auto* result
= CreateTemporalDuration(cx
, duration
);
1436 args
.rval().setObject(*result
);
1441 if (!TimeZoneEqualsOrThrow(cx
, zonedDateTime
.timeZone(), other
.timeZone())) {
1446 if (zonedDateTime
.instant() == other
.instant()) {
1447 auto* obj
= CreateTemporalDuration(cx
, {});
1452 args
.rval().setObject(*obj
);
1457 Rooted
<TimeZoneRecord
> timeZone(cx
);
1458 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
1460 TimeZoneMethod::GetOffsetNanosecondsFor
,
1461 TimeZoneMethod::GetPossibleInstantsFor
,
1468 Rooted
<CalendarRecord
> calendar(cx
);
1469 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
1471 CalendarMethod::DateAdd
,
1472 CalendarMethod::DateUntil
,
1479 PlainDateTime precalculatedPlainDateTime
;
1480 if (!GetPlainDateTimeFor(cx
, timeZone
, zonedDateTime
.instant(),
1481 &precalculatedPlainDateTime
)) {
1486 Rooted
<PlainDateObject
*> plainRelativeTo(
1487 cx
, CreateTemporalDate(cx
, precalculatedPlainDateTime
.date
,
1488 calendar
.receiver()));
1489 if (!plainRelativeTo
) {
1494 if (resolvedOptions
) {
1495 Rooted
<Value
> largestUnitValue(
1496 cx
, StringValue(TemporalUnitToString(cx
, settings
.largestUnit
)));
1497 if (!DefineDataProperty(cx
, resolvedOptions
, cx
->names().largestUnit
,
1498 largestUnitValue
)) {
1504 NormalizedDuration difference
;
1505 if (!::DifferenceZonedDateTime(
1506 cx
, zonedDateTime
.instant(), other
.instant(), timeZone
, calendar
,
1507 settings
.largestUnit
, resolvedOptions
,
1508 mozilla::SomeRef
<const PlainDateTime
>(precalculatedPlainDateTime
),
1514 bool roundingGranularityIsNoop
=
1515 settings
.smallestUnit
== TemporalUnit::Nanosecond
&&
1516 settings
.roundingIncrement
== Increment
{1};
1519 if (!roundingGranularityIsNoop
) {
1521 NormalizedDuration roundResult
;
1522 if (!RoundDuration(cx
, difference
, settings
.roundingIncrement
,
1523 settings
.smallestUnit
, settings
.roundingMode
,
1524 plainRelativeTo
, calendar
, zonedDateTime
, timeZone
,
1525 precalculatedPlainDateTime
, &roundResult
)) {
1530 NormalizedTimeAndDays timeAndDays
;
1531 if (!NormalizedTimeDurationToDays(cx
, roundResult
.time
, zonedDateTime
,
1532 timeZone
, &timeAndDays
)) {
1537 int64_t days
= roundResult
.date
.days
+ timeAndDays
.days
;
1540 auto toAdjust
= NormalizedDuration
{
1542 roundResult
.date
.years
,
1543 roundResult
.date
.months
,
1544 roundResult
.date
.weeks
,
1547 NormalizedTimeDuration::fromNanoseconds(timeAndDays
.time
),
1549 NormalizedDuration adjustResult
;
1550 if (!AdjustRoundedDurationDays(cx
, toAdjust
, settings
.roundingIncrement
,
1551 settings
.smallestUnit
, settings
.roundingMode
,
1552 zonedDateTime
, calendar
, timeZone
,
1553 precalculatedPlainDateTime
, &adjustResult
)) {
1558 DateDuration balanceResult
;
1559 if (!temporal::BalanceDateDurationRelative(
1560 cx
, adjustResult
.date
, settings
.largestUnit
, settings
.smallestUnit
,
1561 plainRelativeTo
, calendar
, &balanceResult
)) {
1566 if (!CombineDateAndNormalizedTimeDuration(cx
, balanceResult
,
1567 adjustResult
.time
, &difference
)) {
1573 auto timeDuration
= BalanceTimeDuration(difference
.time
, TemporalUnit::Hour
);
1576 auto duration
= Duration
{
1577 double(difference
.date
.years
), double(difference
.date
.months
),
1578 double(difference
.date
.weeks
), double(difference
.date
.days
),
1579 double(timeDuration
.hours
), double(timeDuration
.minutes
),
1580 double(timeDuration
.seconds
), double(timeDuration
.milliseconds
),
1581 timeDuration
.microseconds
, timeDuration
.nanoseconds
,
1583 if (operation
== TemporalDifference::Since
) {
1584 duration
= duration
.negate();
1586 MOZ_ASSERT(IsValidDuration(duration
));
1588 auto* obj
= CreateTemporalDuration(cx
, duration
);
1593 args
.rval().setObject(*obj
);
1597 enum class ZonedDateTimeDuration
{ Add
, Subtract
};
1600 * AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime,
1601 * temporalDurationLike, options )
1603 static bool AddDurationToOrSubtractDurationFromZonedDateTime(
1604 JSContext
* cx
, ZonedDateTimeDuration operation
, const CallArgs
& args
) {
1605 Rooted
<ZonedDateTime
> zonedDateTime(
1606 cx
, &args
.thisv().toObject().as
<ZonedDateTimeObject
>());
1608 // Step 1. (Not applicable in our implementation.)
1612 if (!ToTemporalDurationRecord(cx
, args
.get(0), &duration
)) {
1617 Rooted
<JSObject
*> options(cx
);
1618 if (args
.hasDefined(1)) {
1620 operation
== ZonedDateTimeDuration::Add
? "add" : "subtract";
1621 options
= RequireObjectArg(cx
, "options", name
, args
[1]);
1623 options
= NewPlainObjectWithProto(cx
, nullptr);
1630 Rooted
<TimeZoneRecord
> timeZone(cx
);
1631 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
1633 TimeZoneMethod::GetOffsetNanosecondsFor
,
1634 TimeZoneMethod::GetPossibleInstantsFor
,
1641 Rooted
<CalendarRecord
> calendar(cx
);
1642 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
1644 CalendarMethod::DateAdd
,
1651 if (operation
== ZonedDateTimeDuration::Subtract
) {
1652 duration
= duration
.negate();
1654 auto normalized
= CreateNormalizedDurationRecord(duration
);
1657 Instant resultInstant
;
1658 if (!::AddZonedDateTime(cx
, zonedDateTime
.instant(), timeZone
, calendar
,
1659 normalized
, options
, &resultInstant
)) {
1662 MOZ_ASSERT(IsValidEpochInstant(resultInstant
));
1665 auto* result
= CreateTemporalZonedDateTime(
1666 cx
, resultInstant
, timeZone
.receiver(), calendar
.receiver());
1671 args
.rval().setObject(*result
);
1676 * Temporal.ZonedDateTime ( epochNanoseconds, timeZoneLike [ , calendarLike ] )
1678 static bool ZonedDateTimeConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1679 CallArgs args
= CallArgsFromVp(argc
, vp
);
1682 if (!ThrowIfNotConstructing(cx
, args
, "Temporal.ZonedDateTime")) {
1687 Rooted
<BigInt
*> epochNanoseconds(cx
, js::ToBigInt(cx
, args
.get(0)));
1688 if (!epochNanoseconds
) {
1693 if (!IsValidEpochNanoseconds(epochNanoseconds
)) {
1694 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1695 JSMSG_TEMPORAL_INSTANT_INVALID
);
1700 Rooted
<TimeZoneValue
> timeZone(cx
);
1701 if (!ToTemporalTimeZone(cx
, args
.get(1), &timeZone
)) {
1706 Rooted
<CalendarValue
> calendar(cx
);
1707 if (!ToTemporalCalendarWithISODefault(cx
, args
.get(2), &calendar
)) {
1712 auto* obj
= CreateTemporalZonedDateTime(cx
, args
, epochNanoseconds
, timeZone
,
1718 args
.rval().setObject(*obj
);
1723 * Temporal.ZonedDateTime.from ( item [ , options ] )
1725 static bool ZonedDateTime_from(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1726 CallArgs args
= CallArgsFromVp(argc
, vp
);
1729 Rooted
<JSObject
*> options(cx
);
1730 if (args
.hasDefined(1)) {
1731 options
= RequireObjectArg(cx
, "options", "from", args
[1]);
1738 if (args
.get(0).isObject()) {
1739 JSObject
* item
= &args
[0].toObject();
1740 if (auto* zonedDateTime
= item
->maybeUnwrapIf
<ZonedDateTimeObject
>()) {
1741 auto epochInstant
= ToInstant(zonedDateTime
);
1742 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
1743 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
1745 if (!timeZone
.wrap(cx
)) {
1748 if (!calendar
.wrap(cx
)) {
1754 TemporalDisambiguation ignoredDisambiguation
;
1755 if (!ToTemporalDisambiguation(cx
, options
, &ignoredDisambiguation
)) {
1760 TemporalOffset ignoredOffset
;
1761 if (!ToTemporalOffset(cx
, options
, &ignoredOffset
)) {
1766 TemporalOverflow ignoredOverflow
;
1767 if (!ToTemporalOverflow(cx
, options
, &ignoredOverflow
)) {
1774 CreateTemporalZonedDateTime(cx
, epochInstant
, timeZone
, calendar
);
1779 args
.rval().setObject(*result
);
1785 auto* result
= ToTemporalZonedDateTime(cx
, args
.get(0), options
);
1790 args
.rval().setObject(*result
);
1795 * Temporal.ZonedDateTime.compare ( one, two )
1797 static bool ZonedDateTime_compare(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1798 CallArgs args
= CallArgsFromVp(argc
, vp
);
1801 Rooted
<ZonedDateTime
> one(cx
);
1802 if (!ToTemporalZonedDateTime(cx
, args
.get(0), &one
)) {
1807 Rooted
<ZonedDateTime
> two(cx
);
1808 if (!ToTemporalZonedDateTime(cx
, args
.get(1), &two
)) {
1813 const auto& oneNs
= one
.instant();
1814 const auto& twoNs
= two
.instant();
1815 args
.rval().setInt32(oneNs
> twoNs
? 1 : oneNs
< twoNs
? -1 : 0);
1820 * get Temporal.ZonedDateTime.prototype.calendarId
1822 static bool ZonedDateTime_calendarId(JSContext
* cx
, const CallArgs
& args
) {
1823 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
1826 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
1827 auto* calendarId
= ToTemporalCalendarIdentifier(cx
, calendar
);
1832 args
.rval().setString(calendarId
);
1837 * get Temporal.ZonedDateTime.prototype.calendarId
1839 static bool ZonedDateTime_calendarId(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1841 CallArgs args
= CallArgsFromVp(argc
, vp
);
1842 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_calendarId
>(cx
,
1847 * get Temporal.ZonedDateTime.prototype.timeZoneId
1849 static bool ZonedDateTime_timeZoneId(JSContext
* cx
, const CallArgs
& args
) {
1850 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
1853 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
1854 auto* timeZoneId
= ToTemporalTimeZoneIdentifier(cx
, timeZone
);
1859 args
.rval().setString(timeZoneId
);
1864 * get Temporal.ZonedDateTime.prototype.timeZoneId
1866 static bool ZonedDateTime_timeZoneId(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1868 CallArgs args
= CallArgsFromVp(argc
, vp
);
1869 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_timeZoneId
>(cx
,
1874 * get Temporal.ZonedDateTime.prototype.year
1876 static bool ZonedDateTime_year(JSContext
* cx
, const CallArgs
& args
) {
1877 Rooted
<ZonedDateTime
> zonedDateTime(
1878 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1881 PlainDateTime dateTime
;
1882 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
1883 zonedDateTime
.instant(), &dateTime
)) {
1888 return CalendarYear(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
1892 * get Temporal.ZonedDateTime.prototype.year
1894 static bool ZonedDateTime_year(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1896 CallArgs args
= CallArgsFromVp(argc
, vp
);
1897 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_year
>(cx
, args
);
1901 * get Temporal.ZonedDateTime.prototype.month
1903 static bool ZonedDateTime_month(JSContext
* cx
, const CallArgs
& args
) {
1904 Rooted
<ZonedDateTime
> zonedDateTime(
1905 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1908 PlainDateTime dateTime
;
1909 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
1910 zonedDateTime
.instant(), &dateTime
)) {
1915 return CalendarMonth(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
1919 * get Temporal.ZonedDateTime.prototype.month
1921 static bool ZonedDateTime_month(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1923 CallArgs args
= CallArgsFromVp(argc
, vp
);
1924 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_month
>(cx
, args
);
1928 * get Temporal.ZonedDateTime.prototype.monthCode
1930 static bool ZonedDateTime_monthCode(JSContext
* cx
, const CallArgs
& args
) {
1931 Rooted
<ZonedDateTime
> zonedDateTime(
1932 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1935 PlainDateTime dateTime
;
1936 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
1937 zonedDateTime
.instant(), &dateTime
)) {
1942 return CalendarMonthCode(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
1946 * get Temporal.ZonedDateTime.prototype.monthCode
1948 static bool ZonedDateTime_monthCode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1950 CallArgs args
= CallArgsFromVp(argc
, vp
);
1951 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_monthCode
>(cx
,
1956 * get Temporal.ZonedDateTime.prototype.day
1958 static bool ZonedDateTime_day(JSContext
* cx
, const CallArgs
& args
) {
1959 Rooted
<ZonedDateTime
> zonedDateTime(
1960 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1962 // Step 4. (Reordered)
1963 Rooted
<CalendarRecord
> calendar(cx
);
1964 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
1966 CalendarMethod::Day
,
1973 PlainDateTime dateTime
;
1974 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
1975 zonedDateTime
.instant(), &dateTime
)) {
1980 return CalendarDay(cx
, calendar
, dateTime
, args
.rval());
1984 * get Temporal.ZonedDateTime.prototype.day
1986 static bool ZonedDateTime_day(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1988 CallArgs args
= CallArgsFromVp(argc
, vp
);
1989 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_day
>(cx
, args
);
1993 * get Temporal.ZonedDateTime.prototype.hour
1995 static bool ZonedDateTime_hour(JSContext
* cx
, const CallArgs
& args
) {
1996 Rooted
<ZonedDateTime
> zonedDateTime(
1997 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2000 PlainDateTime dateTime
;
2001 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2002 zonedDateTime
.instant(), &dateTime
)) {
2007 args
.rval().setInt32(dateTime
.time
.hour
);
2012 * get Temporal.ZonedDateTime.prototype.hour
2014 static bool ZonedDateTime_hour(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2016 CallArgs args
= CallArgsFromVp(argc
, vp
);
2017 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_hour
>(cx
, args
);
2021 * get Temporal.ZonedDateTime.prototype.minute
2023 static bool ZonedDateTime_minute(JSContext
* cx
, const CallArgs
& args
) {
2024 Rooted
<ZonedDateTime
> zonedDateTime(
2025 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2028 PlainDateTime dateTime
;
2029 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2030 zonedDateTime
.instant(), &dateTime
)) {
2035 args
.rval().setInt32(dateTime
.time
.minute
);
2040 * get Temporal.ZonedDateTime.prototype.minute
2042 static bool ZonedDateTime_minute(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2044 CallArgs args
= CallArgsFromVp(argc
, vp
);
2045 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_minute
>(cx
, args
);
2049 * get Temporal.ZonedDateTime.prototype.second
2051 static bool ZonedDateTime_second(JSContext
* cx
, const CallArgs
& args
) {
2052 Rooted
<ZonedDateTime
> zonedDateTime(
2053 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2056 PlainDateTime dateTime
;
2057 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2058 zonedDateTime
.instant(), &dateTime
)) {
2063 args
.rval().setInt32(dateTime
.time
.second
);
2068 * get Temporal.ZonedDateTime.prototype.second
2070 static bool ZonedDateTime_second(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2072 CallArgs args
= CallArgsFromVp(argc
, vp
);
2073 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_second
>(cx
, args
);
2077 * get Temporal.ZonedDateTime.prototype.millisecond
2079 static bool ZonedDateTime_millisecond(JSContext
* cx
, const CallArgs
& args
) {
2080 Rooted
<ZonedDateTime
> zonedDateTime(
2081 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2084 PlainDateTime dateTime
;
2085 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2086 zonedDateTime
.instant(), &dateTime
)) {
2091 args
.rval().setInt32(dateTime
.time
.millisecond
);
2096 * get Temporal.ZonedDateTime.prototype.millisecond
2098 static bool ZonedDateTime_millisecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2100 CallArgs args
= CallArgsFromVp(argc
, vp
);
2101 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_millisecond
>(cx
,
2106 * get Temporal.ZonedDateTime.prototype.microsecond
2108 static bool ZonedDateTime_microsecond(JSContext
* cx
, const CallArgs
& args
) {
2109 Rooted
<ZonedDateTime
> zonedDateTime(
2110 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2113 PlainDateTime dateTime
;
2114 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2115 zonedDateTime
.instant(), &dateTime
)) {
2120 args
.rval().setInt32(dateTime
.time
.microsecond
);
2125 * get Temporal.ZonedDateTime.prototype.microsecond
2127 static bool ZonedDateTime_microsecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2129 CallArgs args
= CallArgsFromVp(argc
, vp
);
2130 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_microsecond
>(cx
,
2135 * get Temporal.ZonedDateTime.prototype.nanosecond
2137 static bool ZonedDateTime_nanosecond(JSContext
* cx
, const CallArgs
& args
) {
2138 Rooted
<ZonedDateTime
> zonedDateTime(
2139 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2142 PlainDateTime dateTime
;
2143 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2144 zonedDateTime
.instant(), &dateTime
)) {
2149 args
.rval().setInt32(dateTime
.time
.nanosecond
);
2154 * get Temporal.ZonedDateTime.prototype.nanosecond
2156 static bool ZonedDateTime_nanosecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2158 CallArgs args
= CallArgsFromVp(argc
, vp
);
2159 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_nanosecond
>(cx
,
2164 * get Temporal.ZonedDateTime.prototype.epochSeconds
2166 static bool ZonedDateTime_epochSeconds(JSContext
* cx
, const CallArgs
& args
) {
2167 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
2170 auto instant
= ToInstant(zonedDateTime
);
2173 args
.rval().setNumber(instant
.seconds
);
2178 * get Temporal.ZonedDateTime.prototype.epochSeconds
2180 static bool ZonedDateTime_epochSeconds(JSContext
* cx
, unsigned argc
,
2183 CallArgs args
= CallArgsFromVp(argc
, vp
);
2184 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_epochSeconds
>(
2189 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
2191 static bool ZonedDateTime_epochMilliseconds(JSContext
* cx
,
2192 const CallArgs
& args
) {
2193 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
2196 auto instant
= ToInstant(zonedDateTime
);
2199 args
.rval().setNumber(instant
.floorToMilliseconds());
2204 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
2206 static bool ZonedDateTime_epochMilliseconds(JSContext
* cx
, unsigned argc
,
2209 CallArgs args
= CallArgsFromVp(argc
, vp
);
2210 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_epochMilliseconds
>(
2215 * get Temporal.ZonedDateTime.prototype.epochMicroseconds
2217 static bool ZonedDateTime_epochMicroseconds(JSContext
* cx
,
2218 const CallArgs
& args
) {
2219 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
2222 auto instant
= ToInstant(zonedDateTime
);
2225 auto* microseconds
=
2226 BigInt::createFromInt64(cx
, instant
.floorToMicroseconds());
2227 if (!microseconds
) {
2232 args
.rval().setBigInt(microseconds
);
2237 * get Temporal.ZonedDateTime.prototype.epochMicroseconds
2239 static bool ZonedDateTime_epochMicroseconds(JSContext
* cx
, unsigned argc
,
2242 CallArgs args
= CallArgsFromVp(argc
, vp
);
2243 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_epochMicroseconds
>(
2248 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
2250 static bool ZonedDateTime_epochNanoseconds(JSContext
* cx
,
2251 const CallArgs
& args
) {
2252 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
2255 auto* nanoseconds
= ToEpochNanoseconds(cx
, ToInstant(zonedDateTime
));
2260 args
.rval().setBigInt(nanoseconds
);
2265 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
2267 static bool ZonedDateTime_epochNanoseconds(JSContext
* cx
, unsigned argc
,
2270 CallArgs args
= CallArgsFromVp(argc
, vp
);
2271 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_epochNanoseconds
>(
2276 * get Temporal.ZonedDateTime.prototype.dayOfWeek
2278 static bool ZonedDateTime_dayOfWeek(JSContext
* cx
, const CallArgs
& args
) {
2279 Rooted
<ZonedDateTime
> zonedDateTime(
2280 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2283 PlainDateTime dateTime
;
2284 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2285 zonedDateTime
.instant(), &dateTime
)) {
2290 return CalendarDayOfWeek(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
2294 * get Temporal.ZonedDateTime.prototype.dayOfWeek
2296 static bool ZonedDateTime_dayOfWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2298 CallArgs args
= CallArgsFromVp(argc
, vp
);
2299 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_dayOfWeek
>(cx
,
2304 * get Temporal.ZonedDateTime.prototype.dayOfYear
2306 static bool ZonedDateTime_dayOfYear(JSContext
* cx
, const CallArgs
& args
) {
2307 Rooted
<ZonedDateTime
> zonedDateTime(
2308 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2311 PlainDateTime dateTime
;
2312 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2313 zonedDateTime
.instant(), &dateTime
)) {
2318 return CalendarDayOfYear(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
2322 * get Temporal.ZonedDateTime.prototype.dayOfYear
2324 static bool ZonedDateTime_dayOfYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2326 CallArgs args
= CallArgsFromVp(argc
, vp
);
2327 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_dayOfYear
>(cx
,
2332 * get Temporal.ZonedDateTime.prototype.weekOfYear
2334 static bool ZonedDateTime_weekOfYear(JSContext
* cx
, const CallArgs
& args
) {
2335 Rooted
<ZonedDateTime
> zonedDateTime(
2336 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2339 PlainDateTime dateTime
;
2340 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2341 zonedDateTime
.instant(), &dateTime
)) {
2346 return CalendarWeekOfYear(cx
, zonedDateTime
.calendar(), dateTime
,
2351 * get Temporal.ZonedDateTime.prototype.weekOfYear
2353 static bool ZonedDateTime_weekOfYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2355 CallArgs args
= CallArgsFromVp(argc
, vp
);
2356 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_weekOfYear
>(cx
,
2361 * get Temporal.ZonedDateTime.prototype.yearOfWeek
2363 static bool ZonedDateTime_yearOfWeek(JSContext
* cx
, const CallArgs
& args
) {
2364 Rooted
<ZonedDateTime
> zonedDateTime(
2365 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2368 PlainDateTime dateTime
;
2369 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2370 zonedDateTime
.instant(), &dateTime
)) {
2375 return CalendarYearOfWeek(cx
, zonedDateTime
.calendar(), dateTime
,
2380 * get Temporal.ZonedDateTime.prototype.yearOfWeek
2382 static bool ZonedDateTime_yearOfWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2384 CallArgs args
= CallArgsFromVp(argc
, vp
);
2385 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_yearOfWeek
>(cx
,
2390 * get Temporal.ZonedDateTime.prototype.hoursInDay
2392 static bool ZonedDateTime_hoursInDay(JSContext
* cx
, const CallArgs
& args
) {
2393 Rooted
<ZonedDateTime
> zonedDateTime(
2394 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2397 Rooted
<TimeZoneRecord
> timeZone(cx
);
2398 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
2400 TimeZoneMethod::GetOffsetNanosecondsFor
,
2401 TimeZoneMethod::GetPossibleInstantsFor
,
2408 const auto& instant
= zonedDateTime
.instant();
2411 PlainDateTime temporalDateTime
;
2412 if (!GetPlainDateTimeFor(cx
, timeZone
, instant
, &temporalDateTime
)) {
2417 const auto& date
= temporalDateTime
.date
;
2418 Rooted
<CalendarValue
> isoCalendar(cx
, CalendarValue(cx
->names().iso8601
));
2421 Rooted
<PlainDateTimeWithCalendar
> today(cx
);
2422 if (!CreateTemporalDateTime(cx
, {date
, {}}, isoCalendar
, &today
)) {
2427 auto tomorrowFields
= BalanceISODate(date
.year
, date
.month
, date
.day
+ 1);
2430 Rooted
<PlainDateTimeWithCalendar
> tomorrow(cx
);
2431 if (!CreateTemporalDateTime(cx
, {tomorrowFields
, {}}, isoCalendar
,
2437 Instant todayInstant
;
2438 if (!GetInstantFor(cx
, timeZone
, today
, TemporalDisambiguation::Compatible
,
2444 Instant tomorrowInstant
;
2445 if (!GetInstantFor(cx
, timeZone
, tomorrow
, TemporalDisambiguation::Compatible
,
2446 &tomorrowInstant
)) {
2451 auto diff
= tomorrowInstant
- todayInstant
;
2452 MOZ_ASSERT(IsValidInstantSpan(diff
));
2455 constexpr auto nsPerHour
= Int128
{ToNanoseconds(TemporalUnit::Hour
)};
2456 args
.rval().setNumber(FractionToDouble(diff
.toNanoseconds(), nsPerHour
));
2461 * get Temporal.ZonedDateTime.prototype.hoursInDay
2463 static bool ZonedDateTime_hoursInDay(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2465 CallArgs args
= CallArgsFromVp(argc
, vp
);
2466 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_hoursInDay
>(cx
,
2471 * get Temporal.ZonedDateTime.prototype.daysInWeek
2473 static bool ZonedDateTime_daysInWeek(JSContext
* cx
, const CallArgs
& args
) {
2474 Rooted
<ZonedDateTime
> zonedDateTime(
2475 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2478 PlainDateTime dateTime
;
2479 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2480 zonedDateTime
.instant(), &dateTime
)) {
2485 return CalendarDaysInWeek(cx
, zonedDateTime
.calendar(), dateTime
,
2490 * get Temporal.ZonedDateTime.prototype.daysInWeek
2492 static bool ZonedDateTime_daysInWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2494 CallArgs args
= CallArgsFromVp(argc
, vp
);
2495 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_daysInWeek
>(cx
,
2500 * get Temporal.ZonedDateTime.prototype.daysInMonth
2502 static bool ZonedDateTime_daysInMonth(JSContext
* cx
, const CallArgs
& args
) {
2503 Rooted
<ZonedDateTime
> zonedDateTime(
2504 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2507 PlainDateTime dateTime
;
2508 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2509 zonedDateTime
.instant(), &dateTime
)) {
2514 return CalendarDaysInMonth(cx
, zonedDateTime
.calendar(), dateTime
,
2519 * get Temporal.ZonedDateTime.prototype.daysInMonth
2521 static bool ZonedDateTime_daysInMonth(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2523 CallArgs args
= CallArgsFromVp(argc
, vp
);
2524 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_daysInMonth
>(cx
,
2529 * get Temporal.ZonedDateTime.prototype.daysInYear
2531 static bool ZonedDateTime_daysInYear(JSContext
* cx
, const CallArgs
& args
) {
2532 Rooted
<ZonedDateTime
> zonedDateTime(
2533 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2536 PlainDateTime dateTime
;
2537 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2538 zonedDateTime
.instant(), &dateTime
)) {
2543 return CalendarDaysInYear(cx
, zonedDateTime
.calendar(), dateTime
,
2548 * get Temporal.ZonedDateTime.prototype.daysInYear
2550 static bool ZonedDateTime_daysInYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2552 CallArgs args
= CallArgsFromVp(argc
, vp
);
2553 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_daysInYear
>(cx
,
2558 * get Temporal.ZonedDateTime.prototype.monthsInYear
2560 static bool ZonedDateTime_monthsInYear(JSContext
* cx
, const CallArgs
& args
) {
2561 Rooted
<ZonedDateTime
> zonedDateTime(
2562 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2565 PlainDateTime dateTime
;
2566 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2567 zonedDateTime
.instant(), &dateTime
)) {
2572 return CalendarMonthsInYear(cx
, zonedDateTime
.calendar(), dateTime
,
2577 * get Temporal.ZonedDateTime.prototype.monthsInYear
2579 static bool ZonedDateTime_monthsInYear(JSContext
* cx
, unsigned argc
,
2582 CallArgs args
= CallArgsFromVp(argc
, vp
);
2583 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_monthsInYear
>(
2588 * get Temporal.ZonedDateTime.prototype.inLeapYear
2590 static bool ZonedDateTime_inLeapYear(JSContext
* cx
, const CallArgs
& args
) {
2591 Rooted
<ZonedDateTime
> zonedDateTime(
2592 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2595 PlainDateTime dateTime
;
2596 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2597 zonedDateTime
.instant(), &dateTime
)) {
2602 return CalendarInLeapYear(cx
, zonedDateTime
.calendar(), dateTime
,
2607 * get Temporal.ZonedDateTime.prototype.inLeapYear
2609 static bool ZonedDateTime_inLeapYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2611 CallArgs args
= CallArgsFromVp(argc
, vp
);
2612 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_inLeapYear
>(cx
,
2617 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2619 static bool ZonedDateTime_offsetNanoseconds(JSContext
* cx
,
2620 const CallArgs
& args
) {
2621 Rooted
<ZonedDateTime
> zonedDateTime(
2622 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2625 auto timeZone
= zonedDateTime
.timeZone();
2628 const auto& instant
= zonedDateTime
.instant();
2631 int64_t offsetNanoseconds
;
2632 if (!GetOffsetNanosecondsFor(cx
, timeZone
, instant
, &offsetNanoseconds
)) {
2635 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
2637 args
.rval().setNumber(offsetNanoseconds
);
2642 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2644 static bool ZonedDateTime_offsetNanoseconds(JSContext
* cx
, unsigned argc
,
2647 CallArgs args
= CallArgsFromVp(argc
, vp
);
2648 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_offsetNanoseconds
>(
2653 * get Temporal.ZonedDateTime.prototype.offset
2655 static bool ZonedDateTime_offset(JSContext
* cx
, const CallArgs
& args
) {
2656 Rooted
<ZonedDateTime
> zonedDateTime(
2657 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2660 auto timeZone
= zonedDateTime
.timeZone();
2663 const auto& instant
= zonedDateTime
.instant();
2666 JSString
* str
= GetOffsetStringFor(cx
, timeZone
, instant
);
2671 args
.rval().setString(str
);
2676 * get Temporal.ZonedDateTime.prototype.offset
2678 static bool ZonedDateTime_offset(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2680 CallArgs args
= CallArgsFromVp(argc
, vp
);
2681 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_offset
>(cx
, args
);
2685 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2688 static bool ZonedDateTime_with(JSContext
* cx
, const CallArgs
& args
) {
2689 Rooted
<ZonedDateTime
> zonedDateTime(
2690 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2693 Rooted
<JSObject
*> temporalZonedDateTimeLike(
2695 RequireObjectArg(cx
, "temporalZonedDateTimeLike", "with", args
.get(0)));
2696 if (!temporalZonedDateTimeLike
) {
2699 if (!ThrowIfTemporalLikeObject(cx
, temporalZonedDateTimeLike
)) {
2704 Rooted
<PlainObject
*> resolvedOptions(cx
);
2705 if (args
.hasDefined(1)) {
2706 Rooted
<JSObject
*> options(cx
,
2707 RequireObjectArg(cx
, "options", "with", args
[1]));
2711 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
2713 resolvedOptions
= NewPlainObjectWithProto(cx
, nullptr);
2715 if (!resolvedOptions
) {
2720 Rooted
<CalendarRecord
> calendar(cx
);
2721 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
2723 CalendarMethod::DateFromFields
,
2724 CalendarMethod::Fields
,
2725 CalendarMethod::MergeFields
,
2732 Rooted
<TimeZoneRecord
> timeZone(cx
);
2733 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
2735 TimeZoneMethod::GetOffsetNanosecondsFor
,
2736 TimeZoneMethod::GetPossibleInstantsFor
,
2743 const auto& instant
= zonedDateTime
.instant();
2746 int64_t offsetNanoseconds
;
2747 if (!GetOffsetNanosecondsFor(cx
, timeZone
, instant
, &offsetNanoseconds
)) {
2752 Rooted
<PlainDateTimeObject
*> dateTime(
2754 GetPlainDateTimeFor(cx
, instant
, calendar
.receiver(), offsetNanoseconds
));
2760 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
2761 if (!CalendarFields(cx
, calendar
,
2762 {CalendarField::Day
, CalendarField::Month
,
2763 CalendarField::MonthCode
, CalendarField::Year
},
2769 Rooted
<PlainObject
*> fields(cx
,
2770 PrepareTemporalFields(cx
, dateTime
, fieldNames
));
2777 using FieldName
= ImmutableTenuredPtr
<PropertyName
*> JSAtomState::*;
2782 {&JSAtomState::hour
, dateTime
->isoHour()},
2783 {&JSAtomState::minute
, dateTime
->isoMinute()},
2784 {&JSAtomState::second
, dateTime
->isoSecond()},
2785 {&JSAtomState::millisecond
, dateTime
->isoMillisecond()},
2786 {&JSAtomState::microsecond
, dateTime
->isoMicrosecond()},
2787 {&JSAtomState::nanosecond
, dateTime
->isoNanosecond()},
2790 Rooted
<Value
> timeFieldValue(cx
);
2791 for (const auto& timeField
: timeFields
) {
2792 Handle
<PropertyName
*> name
= cx
->names().*(timeField
.name
);
2793 timeFieldValue
.setInt32(timeField
.value
);
2795 if (!DefineDataProperty(cx
, fields
, name
, timeFieldValue
)) {
2801 JSString
* fieldsOffset
= FormatUTCOffsetNanoseconds(cx
, offsetNanoseconds
);
2802 if (!fieldsOffset
) {
2806 timeFieldValue
.setString(fieldsOffset
);
2807 if (!DefineDataProperty(cx
, fields
, cx
->names().offset
, timeFieldValue
)) {
2812 if (!AppendSorted(cx
, fieldNames
.get(),
2814 TemporalField::Hour
,
2815 TemporalField::Microsecond
,
2816 TemporalField::Millisecond
,
2817 TemporalField::Minute
,
2818 TemporalField::Nanosecond
,
2819 TemporalField::Offset
,
2820 TemporalField::Second
,
2826 Rooted
<PlainObject
*> partialZonedDateTime(
2828 PreparePartialTemporalFields(cx
, temporalZonedDateTimeLike
, fieldNames
));
2829 if (!partialZonedDateTime
) {
2834 Rooted
<JSObject
*> mergedFields(
2835 cx
, CalendarMergeFields(cx
, calendar
, fields
, partialZonedDateTime
));
2836 if (!mergedFields
) {
2841 fields
= PrepareTemporalFields(cx
, mergedFields
, fieldNames
,
2842 {TemporalField::Offset
});
2848 auto disambiguation
= TemporalDisambiguation::Compatible
;
2849 if (!ToTemporalDisambiguation(cx
, resolvedOptions
, &disambiguation
)) {
2854 auto offset
= TemporalOffset::Prefer
;
2855 if (!ToTemporalOffset(cx
, resolvedOptions
, &offset
)) {
2860 PlainDateTime dateTimeResult
;
2861 if (!InterpretTemporalDateTimeFields(cx
, calendar
, fields
, resolvedOptions
,
2867 Rooted
<Value
> offsetString(cx
);
2868 if (!GetProperty(cx
, fields
, fields
, cx
->names().offset
, &offsetString
)) {
2873 MOZ_ASSERT(offsetString
.isString());
2876 Rooted
<JSString
*> offsetStr(cx
, offsetString
.toString());
2877 int64_t newOffsetNanoseconds
;
2878 if (!ParseDateTimeUTCOffset(cx
, offsetStr
, &newOffsetNanoseconds
)) {
2883 Instant epochNanoseconds
;
2884 if (!InterpretISODateTimeOffset(
2885 cx
, dateTimeResult
, OffsetBehaviour::Option
, newOffsetNanoseconds
,
2886 timeZone
, disambiguation
, offset
, MatchBehaviour::MatchExactly
,
2887 &epochNanoseconds
)) {
2892 auto* result
= CreateTemporalZonedDateTime(
2893 cx
, epochNanoseconds
, timeZone
.receiver(), calendar
.receiver());
2898 args
.rval().setObject(*result
);
2903 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2906 static bool ZonedDateTime_with(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2908 CallArgs args
= CallArgsFromVp(argc
, vp
);
2909 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_with
>(cx
, args
);
2913 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2915 static bool ZonedDateTime_withPlainTime(JSContext
* cx
, const CallArgs
& args
) {
2916 Rooted
<ZonedDateTime
> zonedDateTime(
2917 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2920 PlainTime time
= {};
2921 if (args
.hasDefined(0)) {
2922 if (!ToTemporalTime(cx
, args
[0], &time
)) {
2928 Rooted
<TimeZoneRecord
> timeZone(cx
);
2929 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
2931 TimeZoneMethod::GetOffsetNanosecondsFor
,
2932 TimeZoneMethod::GetPossibleInstantsFor
,
2939 PlainDateTime plainDateTime
;
2940 if (!GetPlainDateTimeFor(cx
, timeZone
, zonedDateTime
.instant(),
2946 auto calendar
= zonedDateTime
.calendar();
2949 Rooted
<PlainDateTimeWithCalendar
> resultPlainDateTime(cx
);
2950 if (!CreateTemporalDateTime(cx
, {plainDateTime
.date
, time
}, calendar
,
2951 &resultPlainDateTime
)) {
2957 if (!GetInstantFor(cx
, timeZone
, resultPlainDateTime
,
2958 TemporalDisambiguation::Compatible
, &instant
)) {
2964 CreateTemporalZonedDateTime(cx
, instant
, timeZone
.receiver(), calendar
);
2969 args
.rval().setObject(*result
);
2974 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2976 static bool ZonedDateTime_withPlainTime(JSContext
* cx
, unsigned argc
,
2979 CallArgs args
= CallArgsFromVp(argc
, vp
);
2980 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withPlainTime
>(
2985 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
2987 static bool ZonedDateTime_withPlainDate(JSContext
* cx
, const CallArgs
& args
) {
2988 Rooted
<ZonedDateTime
> zonedDateTime(
2989 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2992 Rooted
<PlainDateWithCalendar
> plainDate(cx
);
2993 if (!ToTemporalDate(cx
, args
.get(0), &plainDate
)) {
2996 auto date
= plainDate
.date();
2999 Rooted
<TimeZoneRecord
> timeZone(cx
);
3000 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
3002 TimeZoneMethod::GetOffsetNanosecondsFor
,
3003 TimeZoneMethod::GetPossibleInstantsFor
,
3010 PlainDateTime plainDateTime
;
3011 if (!GetPlainDateTimeFor(cx
, timeZone
, zonedDateTime
.instant(),
3017 Rooted
<CalendarValue
> calendar(cx
);
3018 if (!ConsolidateCalendars(cx
, zonedDateTime
.calendar(), plainDate
.calendar(),
3024 Rooted
<PlainDateTimeWithCalendar
> resultPlainDateTime(cx
);
3025 if (!CreateTemporalDateTime(cx
, {date
, plainDateTime
.time
}, calendar
,
3026 &resultPlainDateTime
)) {
3032 if (!GetInstantFor(cx
, timeZone
, resultPlainDateTime
,
3033 TemporalDisambiguation::Compatible
, &instant
)) {
3039 CreateTemporalZonedDateTime(cx
, instant
, timeZone
.receiver(), calendar
);
3044 args
.rval().setObject(*result
);
3049 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3051 static bool ZonedDateTime_withPlainDate(JSContext
* cx
, unsigned argc
,
3054 CallArgs args
= CallArgsFromVp(argc
, vp
);
3055 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withPlainDate
>(
3060 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3062 static bool ZonedDateTime_withTimeZone(JSContext
* cx
, const CallArgs
& args
) {
3063 Rooted
<ZonedDateTime
> zonedDateTime(
3064 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3067 Rooted
<TimeZoneValue
> timeZone(cx
);
3068 if (!ToTemporalTimeZone(cx
, args
.get(0), &timeZone
)) {
3073 auto* result
= CreateTemporalZonedDateTime(
3074 cx
, zonedDateTime
.instant(), timeZone
, zonedDateTime
.calendar());
3079 args
.rval().setObject(*result
);
3084 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3086 static bool ZonedDateTime_withTimeZone(JSContext
* cx
, unsigned argc
,
3089 CallArgs args
= CallArgsFromVp(argc
, vp
);
3090 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withTimeZone
>(
3095 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3097 static bool ZonedDateTime_withCalendar(JSContext
* cx
, const CallArgs
& args
) {
3098 Rooted
<ZonedDateTime
> zonedDateTime(
3099 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3102 Rooted
<CalendarValue
> calendar(cx
);
3103 if (!ToTemporalCalendar(cx
, args
.get(0), &calendar
)) {
3108 auto* result
= CreateTemporalZonedDateTime(
3109 cx
, zonedDateTime
.instant(), zonedDateTime
.timeZone(), calendar
);
3114 args
.rval().setObject(*result
);
3119 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3121 static bool ZonedDateTime_withCalendar(JSContext
* cx
, unsigned argc
,
3124 CallArgs args
= CallArgsFromVp(argc
, vp
);
3125 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withCalendar
>(
3130 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3132 static bool ZonedDateTime_add(JSContext
* cx
, const CallArgs
& args
) {
3133 return AddDurationToOrSubtractDurationFromZonedDateTime(
3134 cx
, ZonedDateTimeDuration::Add
, args
);
3138 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3140 static bool ZonedDateTime_add(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3142 CallArgs args
= CallArgsFromVp(argc
, vp
);
3143 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_add
>(cx
, args
);
3147 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3150 static bool ZonedDateTime_subtract(JSContext
* cx
, const CallArgs
& args
) {
3151 return AddDurationToOrSubtractDurationFromZonedDateTime(
3152 cx
, ZonedDateTimeDuration::Subtract
, args
);
3156 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3159 static bool ZonedDateTime_subtract(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3161 CallArgs args
= CallArgsFromVp(argc
, vp
);
3162 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_subtract
>(cx
,
3167 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3169 static bool ZonedDateTime_until(JSContext
* cx
, const CallArgs
& args
) {
3171 return DifferenceTemporalZonedDateTime(cx
, TemporalDifference::Until
, args
);
3175 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3177 static bool ZonedDateTime_until(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3179 CallArgs args
= CallArgsFromVp(argc
, vp
);
3180 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_until
>(cx
, args
);
3184 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3186 static bool ZonedDateTime_since(JSContext
* cx
, const CallArgs
& args
) {
3188 return DifferenceTemporalZonedDateTime(cx
, TemporalDifference::Since
, args
);
3192 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3194 static bool ZonedDateTime_since(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3196 CallArgs args
= CallArgsFromVp(argc
, vp
);
3197 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_since
>(cx
, args
);
3201 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3203 static bool ZonedDateTime_round(JSContext
* cx
, const CallArgs
& args
) {
3204 Rooted
<ZonedDateTime
> zonedDateTime(
3205 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3208 auto smallestUnit
= TemporalUnit::Auto
;
3209 auto roundingMode
= TemporalRoundingMode::HalfExpand
;
3210 auto roundingIncrement
= Increment
{1};
3211 if (args
.get(0).isString()) {
3212 // Step 4. (Not applicable in our implementation.)
3215 Rooted
<JSString
*> paramString(cx
, args
[0].toString());
3216 if (!GetTemporalUnit(cx
, paramString
, TemporalUnitKey::SmallestUnit
,
3217 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
3221 // Steps 6-8 and 10-12. (Implicit)
3224 Rooted
<JSObject
*> roundTo(
3225 cx
, RequireObjectArg(cx
, "roundTo", "round", args
.get(0)));
3231 if (!ToTemporalRoundingIncrement(cx
, roundTo
, &roundingIncrement
)) {
3236 if (!ToTemporalRoundingMode(cx
, roundTo
, &roundingMode
)) {
3241 if (!GetTemporalUnit(cx
, roundTo
, TemporalUnitKey::SmallestUnit
,
3242 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
3246 if (smallestUnit
== TemporalUnit::Auto
) {
3247 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3248 JSMSG_TEMPORAL_MISSING_OPTION
, "smallestUnit");
3252 MOZ_ASSERT(TemporalUnit::Day
<= smallestUnit
&&
3253 smallestUnit
<= TemporalUnit::Nanosecond
);
3256 auto maximum
= Increment
{1};
3257 bool inclusive
= true;
3258 if (smallestUnit
> TemporalUnit::Day
) {
3259 maximum
= MaximumTemporalDurationRoundingIncrement(smallestUnit
);
3264 if (!ValidateTemporalRoundingIncrement(cx
, roundingIncrement
, maximum
,
3271 if (smallestUnit
== TemporalUnit::Nanosecond
&&
3272 roundingIncrement
== Increment
{1}) {
3274 auto* result
= CreateTemporalZonedDateTime(cx
, zonedDateTime
.instant(),
3275 zonedDateTime
.timeZone(),
3276 zonedDateTime
.calendar());
3281 args
.rval().setObject(*result
);
3286 Rooted
<TimeZoneRecord
> timeZone(cx
);
3287 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
3289 TimeZoneMethod::GetOffsetNanosecondsFor
,
3290 TimeZoneMethod::GetPossibleInstantsFor
,
3296 // Step 16. (Reordered)
3297 auto calendar
= zonedDateTime
.calendar();
3300 int64_t offsetNanoseconds
;
3301 if (!GetOffsetNanosecondsFor(cx
, timeZone
, zonedDateTime
.instant(),
3302 &offsetNanoseconds
)) {
3305 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
3308 auto temporalDateTime
=
3309 GetPlainDateTimeFor(zonedDateTime
.instant(), offsetNanoseconds
);
3312 Rooted
<CalendarValue
> isoCalendar(cx
, CalendarValue(cx
->names().iso8601
));
3313 Rooted
<PlainDateTimeWithCalendar
> dtStart(cx
);
3314 if (!CreateTemporalDateTime(cx
, {temporalDateTime
.date
, {}}, isoCalendar
,
3321 if (!GetInstantFor(cx
, timeZone
, dtStart
, TemporalDisambiguation::Compatible
,
3328 if (!AddDaysToZonedDateTime(cx
, startNs
, ToPlainDateTime(dtStart
), timeZone
,
3329 calendar
, 1, &endNs
)) {
3332 MOZ_ASSERT(IsValidEpochInstant(endNs
));
3335 auto dayLengthNs
= endNs
- startNs
;
3336 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs
));
3339 if (dayLengthNs
<= InstantSpan
{}) {
3340 JS_ReportErrorNumberASCII(
3341 cx
, GetErrorMessage
, nullptr,
3342 JSMSG_TEMPORAL_ZONED_DATE_TIME_NON_POSITIVE_DAY_LENGTH
);
3347 PlainDateTime roundResult
;
3348 if (!RoundISODateTime(cx
, temporalDateTime
, roundingIncrement
, smallestUnit
,
3349 roundingMode
, dayLengthNs
, &roundResult
)) {
3354 Instant epochNanoseconds
;
3355 if (!InterpretISODateTimeOffset(
3356 cx
, roundResult
, OffsetBehaviour::Option
, offsetNanoseconds
, timeZone
,
3357 TemporalDisambiguation::Compatible
, TemporalOffset::Prefer
,
3358 MatchBehaviour::MatchExactly
, &epochNanoseconds
)) {
3363 auto* result
= CreateTemporalZonedDateTime(cx
, epochNanoseconds
,
3364 timeZone
.receiver(), calendar
);
3369 args
.rval().setObject(*result
);
3374 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3376 static bool ZonedDateTime_round(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3378 CallArgs args
= CallArgsFromVp(argc
, vp
);
3379 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_round
>(cx
, args
);
3383 * Temporal.ZonedDateTime.prototype.equals ( other )
3385 static bool ZonedDateTime_equals(JSContext
* cx
, const CallArgs
& args
) {
3386 Rooted
<ZonedDateTime
> zonedDateTime(
3387 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3390 Rooted
<ZonedDateTime
> other(cx
);
3391 if (!ToTemporalZonedDateTime(cx
, args
.get(0), &other
)) {
3396 bool equals
= zonedDateTime
.instant() == other
.instant();
3397 if (equals
&& !TimeZoneEquals(cx
, zonedDateTime
.timeZone(), other
.timeZone(),
3401 if (equals
&& !CalendarEquals(cx
, zonedDateTime
.calendar(), other
.calendar(),
3406 args
.rval().setBoolean(equals
);
3411 * Temporal.ZonedDateTime.prototype.equals ( other )
3413 static bool ZonedDateTime_equals(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3415 CallArgs args
= CallArgsFromVp(argc
, vp
);
3416 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_equals
>(cx
, args
);
3420 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3422 static bool ZonedDateTime_toString(JSContext
* cx
, const CallArgs
& args
) {
3423 Rooted
<ZonedDateTime
> zonedDateTime(
3424 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3426 SecondsStringPrecision precision
= {Precision::Auto(),
3427 TemporalUnit::Nanosecond
, Increment
{1}};
3428 auto roundingMode
= TemporalRoundingMode::Trunc
;
3429 auto showCalendar
= CalendarOption::Auto
;
3430 auto showTimeZone
= TimeZoneNameOption::Auto
;
3431 auto showOffset
= ShowOffsetOption::Auto
;
3432 if (args
.hasDefined(0)) {
3434 Rooted
<JSObject
*> options(
3435 cx
, RequireObjectArg(cx
, "options", "toString", args
[0]));
3441 if (!ToCalendarNameOption(cx
, options
, &showCalendar
)) {
3446 auto digits
= Precision::Auto();
3447 if (!ToFractionalSecondDigits(cx
, options
, &digits
)) {
3452 if (!ToShowOffsetOption(cx
, options
, &showOffset
)) {
3457 if (!ToTemporalRoundingMode(cx
, options
, &roundingMode
)) {
3462 auto smallestUnit
= TemporalUnit::Auto
;
3463 if (!GetTemporalUnit(cx
, options
, TemporalUnitKey::SmallestUnit
,
3464 TemporalUnitGroup::Time
, &smallestUnit
)) {
3469 if (smallestUnit
== TemporalUnit::Hour
) {
3470 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3471 JSMSG_TEMPORAL_INVALID_UNIT_OPTION
, "hour",
3477 if (!ToTimeZoneNameOption(cx
, options
, &showTimeZone
)) {
3482 precision
= ToSecondsStringPrecision(smallestUnit
, digits
);
3486 JSString
* str
= TemporalZonedDateTimeToString(
3487 cx
, zonedDateTime
, precision
.precision
, showCalendar
, showTimeZone
,
3488 showOffset
, precision
.increment
, precision
.unit
, roundingMode
);
3493 args
.rval().setString(str
);
3498 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3500 static bool ZonedDateTime_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3502 CallArgs args
= CallArgsFromVp(argc
, vp
);
3503 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toString
>(cx
,
3508 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3510 static bool ZonedDateTime_toLocaleString(JSContext
* cx
, const CallArgs
& args
) {
3511 Rooted
<ZonedDateTime
> zonedDateTime(
3512 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3515 JSString
* str
= TemporalZonedDateTimeToString(
3516 cx
, zonedDateTime
, Precision::Auto(), CalendarOption::Auto
,
3517 TimeZoneNameOption::Auto
, ShowOffsetOption::Auto
);
3522 args
.rval().setString(str
);
3527 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3529 static bool ZonedDateTime_toLocaleString(JSContext
* cx
, unsigned argc
,
3532 CallArgs args
= CallArgsFromVp(argc
, vp
);
3533 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toLocaleString
>(
3538 * Temporal.ZonedDateTime.prototype.toJSON ( )
3540 static bool ZonedDateTime_toJSON(JSContext
* cx
, const CallArgs
& args
) {
3541 Rooted
<ZonedDateTime
> zonedDateTime(
3542 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3545 JSString
* str
= TemporalZonedDateTimeToString(
3546 cx
, zonedDateTime
, Precision::Auto(), CalendarOption::Auto
,
3547 TimeZoneNameOption::Auto
, ShowOffsetOption::Auto
);
3552 args
.rval().setString(str
);
3557 * Temporal.ZonedDateTime.prototype.toJSON ( )
3559 static bool ZonedDateTime_toJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3561 CallArgs args
= CallArgsFromVp(argc
, vp
);
3562 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toJSON
>(cx
, args
);
3566 * Temporal.ZonedDateTime.prototype.valueOf ( )
3568 static bool ZonedDateTime_valueOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3569 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_CANT_CONVERT_TO
,
3570 "ZonedDateTime", "primitive type");
3575 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3577 static bool ZonedDateTime_startOfDay(JSContext
* cx
, const CallArgs
& args
) {
3578 Rooted
<ZonedDateTime
> zonedDateTime(
3579 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3582 Rooted
<TimeZoneRecord
> timeZone(cx
);
3583 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
3585 TimeZoneMethod::GetOffsetNanosecondsFor
,
3586 TimeZoneMethod::GetPossibleInstantsFor
,
3593 auto calendar
= zonedDateTime
.calendar();
3596 const auto& instant
= zonedDateTime
.instant();
3599 PlainDateTime temporalDateTime
;
3600 if (!GetPlainDateTimeFor(cx
, timeZone
, instant
, &temporalDateTime
)) {
3605 Rooted
<PlainDateTimeWithCalendar
> startDateTime(cx
);
3606 if (!CreateTemporalDateTime(cx
, {temporalDateTime
.date
, {}}, calendar
,
3612 Instant startInstant
;
3613 if (!GetInstantFor(cx
, timeZone
, startDateTime
,
3614 TemporalDisambiguation::Compatible
, &startInstant
)) {
3619 auto* result
= CreateTemporalZonedDateTime(cx
, startInstant
,
3620 timeZone
.receiver(), calendar
);
3625 args
.rval().setObject(*result
);
3630 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3632 static bool ZonedDateTime_startOfDay(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3634 CallArgs args
= CallArgsFromVp(argc
, vp
);
3635 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_startOfDay
>(cx
,
3640 * Temporal.ZonedDateTime.prototype.toInstant ( )
3642 static bool ZonedDateTime_toInstant(JSContext
* cx
, const CallArgs
& args
) {
3643 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
3644 auto instant
= ToInstant(zonedDateTime
);
3647 auto* result
= CreateTemporalInstant(cx
, instant
);
3652 args
.rval().setObject(*result
);
3657 * Temporal.ZonedDateTime.prototype.toInstant ( )
3659 static bool ZonedDateTime_toInstant(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3661 CallArgs args
= CallArgsFromVp(argc
, vp
);
3662 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toInstant
>(cx
,
3667 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3669 static bool ZonedDateTime_toPlainDate(JSContext
* cx
, const CallArgs
& args
) {
3670 Rooted
<ZonedDateTime
> zonedDateTime(
3671 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3674 PlainDateTime temporalDateTime
;
3675 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
3676 zonedDateTime
.instant(), &temporalDateTime
)) {
3682 CreateTemporalDate(cx
, temporalDateTime
.date
, zonedDateTime
.calendar());
3687 args
.rval().setObject(*result
);
3692 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3694 static bool ZonedDateTime_toPlainDate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3696 CallArgs args
= CallArgsFromVp(argc
, vp
);
3697 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainDate
>(cx
,
3702 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3704 static bool ZonedDateTime_toPlainTime(JSContext
* cx
, const CallArgs
& args
) {
3705 Rooted
<ZonedDateTime
> zonedDateTime(
3706 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3709 PlainDateTime temporalDateTime
;
3710 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
3711 zonedDateTime
.instant(), &temporalDateTime
)) {
3716 auto* result
= CreateTemporalTime(cx
, temporalDateTime
.time
);
3721 args
.rval().setObject(*result
);
3726 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3728 static bool ZonedDateTime_toPlainTime(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3730 CallArgs args
= CallArgsFromVp(argc
, vp
);
3731 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainTime
>(cx
,
3736 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3738 static bool ZonedDateTime_toPlainDateTime(JSContext
* cx
, const CallArgs
& args
) {
3739 Rooted
<ZonedDateTime
> zonedDateTime(
3740 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3744 GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(), zonedDateTime
.instant(),
3745 zonedDateTime
.calendar());
3750 args
.rval().setObject(*result
);
3755 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3757 static bool ZonedDateTime_toPlainDateTime(JSContext
* cx
, unsigned argc
,
3760 CallArgs args
= CallArgsFromVp(argc
, vp
);
3761 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainDateTime
>(
3766 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3768 static bool ZonedDateTime_toPlainYearMonth(JSContext
* cx
,
3769 const CallArgs
& args
) {
3770 Rooted
<ZonedDateTime
> zonedDateTime(
3771 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3774 Rooted
<CalendarRecord
> calendar(cx
);
3775 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
3777 CalendarMethod::Fields
,
3778 CalendarMethod::YearMonthFromFields
,
3785 Rooted
<PlainDateTimeObject
*> temporalDateTime(
3787 GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(), zonedDateTime
.instant(),
3788 zonedDateTime
.calendar()));
3789 if (!temporalDateTime
) {
3794 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
3795 if (!CalendarFields(cx
, calendar
,
3796 {CalendarField::MonthCode
, CalendarField::Year
},
3802 Rooted
<PlainObject
*> fields(
3803 cx
, PrepareTemporalFields(cx
, temporalDateTime
, fieldNames
));
3809 auto result
= CalendarYearMonthFromFields(cx
, calendar
, fields
);
3814 args
.rval().setObject(*result
);
3819 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3821 static bool ZonedDateTime_toPlainYearMonth(JSContext
* cx
, unsigned argc
,
3824 CallArgs args
= CallArgsFromVp(argc
, vp
);
3825 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainYearMonth
>(
3830 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3832 static bool ZonedDateTime_toPlainMonthDay(JSContext
* cx
, const CallArgs
& args
) {
3833 Rooted
<ZonedDateTime
> zonedDateTime(
3834 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3837 Rooted
<CalendarRecord
> calendar(cx
);
3838 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
3840 CalendarMethod::Fields
,
3841 CalendarMethod::MonthDayFromFields
,
3848 Rooted
<PlainDateTimeObject
*> temporalDateTime(
3850 GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(), zonedDateTime
.instant(),
3851 zonedDateTime
.calendar()));
3852 if (!temporalDateTime
) {
3857 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
3858 if (!CalendarFields(cx
, calendar
,
3859 {CalendarField::Day
, CalendarField::MonthCode
},
3865 Rooted
<PlainObject
*> fields(
3866 cx
, PrepareTemporalFields(cx
, temporalDateTime
, fieldNames
));
3872 auto result
= CalendarMonthDayFromFields(cx
, calendar
, fields
);
3877 args
.rval().setObject(*result
);
3882 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3884 static bool ZonedDateTime_toPlainMonthDay(JSContext
* cx
, unsigned argc
,
3887 CallArgs args
= CallArgsFromVp(argc
, vp
);
3888 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainMonthDay
>(
3893 * Temporal.ZonedDateTime.prototype.getISOFields ( )
3895 static bool ZonedDateTime_getISOFields(JSContext
* cx
, const CallArgs
& args
) {
3896 Rooted
<ZonedDateTime
> zonedDateTime(
3897 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3900 Rooted
<IdValueVector
> fields(cx
, IdValueVector(cx
));
3903 const auto& instant
= zonedDateTime
.instant();
3906 auto calendar
= zonedDateTime
.calendar();
3909 auto timeZone
= zonedDateTime
.timeZone();
3912 int64_t offsetNanoseconds
;
3913 if (!GetOffsetNanosecondsFor(cx
, timeZone
, instant
, &offsetNanoseconds
)) {
3918 auto temporalDateTime
= GetPlainDateTimeFor(instant
, offsetNanoseconds
);
3921 Rooted
<JSString
*> offset(cx
,
3922 FormatUTCOffsetNanoseconds(cx
, offsetNanoseconds
));
3928 if (!fields
.emplaceBack(NameToId(cx
->names().calendar
), calendar
.toValue())) {
3933 if (!fields
.emplaceBack(NameToId(cx
->names().isoDay
),
3934 Int32Value(temporalDateTime
.date
.day
))) {
3939 if (!fields
.emplaceBack(NameToId(cx
->names().isoHour
),
3940 Int32Value(temporalDateTime
.time
.hour
))) {
3945 if (!fields
.emplaceBack(NameToId(cx
->names().isoMicrosecond
),
3946 Int32Value(temporalDateTime
.time
.microsecond
))) {
3951 if (!fields
.emplaceBack(NameToId(cx
->names().isoMillisecond
),
3952 Int32Value(temporalDateTime
.time
.millisecond
))) {
3957 if (!fields
.emplaceBack(NameToId(cx
->names().isoMinute
),
3958 Int32Value(temporalDateTime
.time
.minute
))) {
3963 if (!fields
.emplaceBack(NameToId(cx
->names().isoMonth
),
3964 Int32Value(temporalDateTime
.date
.month
))) {
3969 if (!fields
.emplaceBack(NameToId(cx
->names().isoNanosecond
),
3970 Int32Value(temporalDateTime
.time
.nanosecond
))) {
3975 if (!fields
.emplaceBack(NameToId(cx
->names().isoSecond
),
3976 Int32Value(temporalDateTime
.time
.second
))) {
3981 if (!fields
.emplaceBack(NameToId(cx
->names().isoYear
),
3982 Int32Value(temporalDateTime
.date
.year
))) {
3987 if (!fields
.emplaceBack(NameToId(cx
->names().offset
), StringValue(offset
))) {
3992 if (!fields
.emplaceBack(NameToId(cx
->names().timeZone
), timeZone
.toValue())) {
3997 auto* obj
= NewPlainObjectWithUniqueNames(cx
, fields
);
4002 args
.rval().setObject(*obj
);
4007 * Temporal.ZonedDateTime.prototype.getISOFields ( )
4009 static bool ZonedDateTime_getISOFields(JSContext
* cx
, unsigned argc
,
4012 CallArgs args
= CallArgsFromVp(argc
, vp
);
4013 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_getISOFields
>(
4018 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4020 static bool ZonedDateTime_getCalendar(JSContext
* cx
, const CallArgs
& args
) {
4021 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
4022 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
4025 auto* obj
= ToTemporalCalendarObject(cx
, calendar
);
4030 args
.rval().setObject(*obj
);
4035 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4037 static bool ZonedDateTime_getCalendar(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4039 CallArgs args
= CallArgsFromVp(argc
, vp
);
4040 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_getCalendar
>(cx
,
4045 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4047 static bool ZonedDateTime_getTimeZone(JSContext
* cx
, const CallArgs
& args
) {
4048 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
4049 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
4052 auto* obj
= ToTemporalTimeZoneObject(cx
, timeZone
);
4057 args
.rval().setObject(*obj
);
4062 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4064 static bool ZonedDateTime_getTimeZone(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4066 CallArgs args
= CallArgsFromVp(argc
, vp
);
4067 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_getTimeZone
>(cx
,
4071 const JSClass
ZonedDateTimeObject::class_
= {
4072 "Temporal.ZonedDateTime",
4073 JSCLASS_HAS_RESERVED_SLOTS(ZonedDateTimeObject::SLOT_COUNT
) |
4074 JSCLASS_HAS_CACHED_PROTO(JSProto_ZonedDateTime
),
4076 &ZonedDateTimeObject::classSpec_
,
4079 const JSClass
& ZonedDateTimeObject::protoClass_
= PlainObject::class_
;
4081 static const JSFunctionSpec ZonedDateTime_methods
[] = {
4082 JS_FN("from", ZonedDateTime_from
, 1, 0),
4083 JS_FN("compare", ZonedDateTime_compare
, 2, 0),
4087 static const JSFunctionSpec ZonedDateTime_prototype_methods
[] = {
4088 JS_FN("with", ZonedDateTime_with
, 1, 0),
4089 JS_FN("withPlainTime", ZonedDateTime_withPlainTime
, 0, 0),
4090 JS_FN("withPlainDate", ZonedDateTime_withPlainDate
, 1, 0),
4091 JS_FN("withTimeZone", ZonedDateTime_withTimeZone
, 1, 0),
4092 JS_FN("withCalendar", ZonedDateTime_withCalendar
, 1, 0),
4093 JS_FN("add", ZonedDateTime_add
, 1, 0),
4094 JS_FN("subtract", ZonedDateTime_subtract
, 1, 0),
4095 JS_FN("until", ZonedDateTime_until
, 1, 0),
4096 JS_FN("since", ZonedDateTime_since
, 1, 0),
4097 JS_FN("round", ZonedDateTime_round
, 1, 0),
4098 JS_FN("equals", ZonedDateTime_equals
, 1, 0),
4099 JS_FN("toString", ZonedDateTime_toString
, 0, 0),
4100 JS_FN("toLocaleString", ZonedDateTime_toLocaleString
, 0, 0),
4101 JS_FN("toJSON", ZonedDateTime_toJSON
, 0, 0),
4102 JS_FN("valueOf", ZonedDateTime_valueOf
, 0, 0),
4103 JS_FN("startOfDay", ZonedDateTime_startOfDay
, 0, 0),
4104 JS_FN("toInstant", ZonedDateTime_toInstant
, 0, 0),
4105 JS_FN("toPlainDate", ZonedDateTime_toPlainDate
, 0, 0),
4106 JS_FN("toPlainTime", ZonedDateTime_toPlainTime
, 0, 0),
4107 JS_FN("toPlainDateTime", ZonedDateTime_toPlainDateTime
, 0, 0),
4108 JS_FN("toPlainYearMonth", ZonedDateTime_toPlainYearMonth
, 0, 0),
4109 JS_FN("toPlainMonthDay", ZonedDateTime_toPlainMonthDay
, 0, 0),
4110 JS_FN("getISOFields", ZonedDateTime_getISOFields
, 0, 0),
4111 JS_FN("getCalendar", ZonedDateTime_getCalendar
, 0, 0),
4112 JS_FN("getTimeZone", ZonedDateTime_getTimeZone
, 0, 0),
4116 static const JSPropertySpec ZonedDateTime_prototype_properties
[] = {
4117 JS_PSG("calendarId", ZonedDateTime_calendarId
, 0),
4118 JS_PSG("timeZoneId", ZonedDateTime_timeZoneId
, 0),
4119 JS_PSG("year", ZonedDateTime_year
, 0),
4120 JS_PSG("month", ZonedDateTime_month
, 0),
4121 JS_PSG("monthCode", ZonedDateTime_monthCode
, 0),
4122 JS_PSG("day", ZonedDateTime_day
, 0),
4123 JS_PSG("hour", ZonedDateTime_hour
, 0),
4124 JS_PSG("minute", ZonedDateTime_minute
, 0),
4125 JS_PSG("second", ZonedDateTime_second
, 0),
4126 JS_PSG("millisecond", ZonedDateTime_millisecond
, 0),
4127 JS_PSG("microsecond", ZonedDateTime_microsecond
, 0),
4128 JS_PSG("nanosecond", ZonedDateTime_nanosecond
, 0),
4129 JS_PSG("epochSeconds", ZonedDateTime_epochSeconds
, 0),
4130 JS_PSG("epochMilliseconds", ZonedDateTime_epochMilliseconds
, 0),
4131 JS_PSG("epochMicroseconds", ZonedDateTime_epochMicroseconds
, 0),
4132 JS_PSG("epochNanoseconds", ZonedDateTime_epochNanoseconds
, 0),
4133 JS_PSG("dayOfWeek", ZonedDateTime_dayOfWeek
, 0),
4134 JS_PSG("dayOfYear", ZonedDateTime_dayOfYear
, 0),
4135 JS_PSG("weekOfYear", ZonedDateTime_weekOfYear
, 0),
4136 JS_PSG("yearOfWeek", ZonedDateTime_yearOfWeek
, 0),
4137 JS_PSG("hoursInDay", ZonedDateTime_hoursInDay
, 0),
4138 JS_PSG("daysInWeek", ZonedDateTime_daysInWeek
, 0),
4139 JS_PSG("daysInMonth", ZonedDateTime_daysInMonth
, 0),
4140 JS_PSG("daysInYear", ZonedDateTime_daysInYear
, 0),
4141 JS_PSG("monthsInYear", ZonedDateTime_monthsInYear
, 0),
4142 JS_PSG("inLeapYear", ZonedDateTime_inLeapYear
, 0),
4143 JS_PSG("offsetNanoseconds", ZonedDateTime_offsetNanoseconds
, 0),
4144 JS_PSG("offset", ZonedDateTime_offset
, 0),
4145 JS_STRING_SYM_PS(toStringTag
, "Temporal.ZonedDateTime", JSPROP_READONLY
),
4149 const ClassSpec
ZonedDateTimeObject::classSpec_
= {
4150 GenericCreateConstructor
<ZonedDateTimeConstructor
, 2,
4151 gc::AllocKind::FUNCTION
>,
4152 GenericCreatePrototype
<ZonedDateTimeObject
>,
4153 ZonedDateTime_methods
,
4155 ZonedDateTime_prototype_methods
,
4156 ZonedDateTime_prototype_properties
,
4158 ClassSpec::DontDefineConstructor
,