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"
16 #include "NamespaceImports.h"
18 #include "builtin/temporal/Calendar.h"
19 #include "builtin/temporal/Duration.h"
20 #include "builtin/temporal/Instant.h"
21 #include "builtin/temporal/Int96.h"
22 #include "builtin/temporal/PlainDate.h"
23 #include "builtin/temporal/PlainDateTime.h"
24 #include "builtin/temporal/PlainMonthDay.h"
25 #include "builtin/temporal/PlainTime.h"
26 #include "builtin/temporal/PlainYearMonth.h"
27 #include "builtin/temporal/Temporal.h"
28 #include "builtin/temporal/TemporalFields.h"
29 #include "builtin/temporal/TemporalParser.h"
30 #include "builtin/temporal/TemporalRoundingMode.h"
31 #include "builtin/temporal/TemporalTypes.h"
32 #include "builtin/temporal/TemporalUnit.h"
33 #include "builtin/temporal/TimeZone.h"
34 #include "builtin/temporal/ToString.h"
35 #include "builtin/temporal/Wrapped.h"
36 #include "ds/IdValuePair.h"
37 #include "gc/AllocKind.h"
38 #include "gc/Barrier.h"
39 #include "js/AllocPolicy.h"
40 #include "js/CallArgs.h"
41 #include "js/CallNonGenericMethod.h"
43 #include "js/ComparisonOperators.h"
44 #include "js/ErrorReport.h"
45 #include "js/friend/ErrorMessages.h"
46 #include "js/GCVector.h"
48 #include "js/Printer.h"
49 #include "js/PropertyDescriptor.h"
50 #include "js/PropertySpec.h"
51 #include "js/RootingAPI.h"
52 #include "js/TracingAPI.h"
54 #include "vm/BigIntType.h"
55 #include "vm/BytecodeUtil.h"
56 #include "vm/GlobalObject.h"
57 #include "vm/JSAtomState.h"
58 #include "vm/JSContext.h"
59 #include "vm/JSObject.h"
60 #include "vm/ObjectOperations.h"
61 #include "vm/PlainObject.h"
62 #include "vm/StringType.h"
64 #include "vm/JSContext-inl.h"
65 #include "vm/JSObject-inl.h"
66 #include "vm/NativeObject-inl.h"
67 #include "vm/ObjectOperations-inl.h"
70 using namespace js::temporal
;
72 static inline bool IsZonedDateTime(Handle
<Value
> v
) {
73 return v
.isObject() && v
.toObject().is
<ZonedDateTimeObject
>();
76 // Returns |RoundNumberToIncrement(offsetNanoseconds, 60 × 10^9, "halfExpand")|.
77 static int64_t RoundNanosecondsToMinutesIncrement(int64_t offsetNanoseconds
) {
78 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
80 constexpr int64_t increment
= ToNanoseconds(TemporalUnit::Minute
);
82 int64_t quotient
= offsetNanoseconds
/ increment
;
83 int64_t remainder
= offsetNanoseconds
% increment
;
84 if (std::abs(remainder
* 2) >= increment
) {
85 quotient
+= (offsetNanoseconds
> 0 ? 1 : -1);
87 return quotient
* increment
;
91 * InterpretISODateTimeOffset ( year, month, day, hour, minute, second,
92 * millisecond, microsecond, nanosecond, offsetBehaviour, offsetNanoseconds,
93 * timeZoneRec, disambiguation, offsetOption, matchBehaviour )
95 bool js::temporal::InterpretISODateTimeOffset(
96 JSContext
* cx
, const PlainDateTime
& dateTime
,
97 OffsetBehaviour offsetBehaviour
, int64_t offsetNanoseconds
,
98 Handle
<TimeZoneRecord
> timeZone
, TemporalDisambiguation disambiguation
,
99 TemporalOffset offsetOption
, MatchBehaviour matchBehaviour
,
101 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
104 MOZ_ASSERT(IsValidISODate(dateTime
.date
));
107 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
108 timeZone
, TimeZoneMethod::GetOffsetNanosecondsFor
));
111 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
112 timeZone
, TimeZoneMethod::GetPossibleInstantsFor
));
115 Rooted
<CalendarValue
> calendar(cx
, CalendarValue(cx
->names().iso8601
));
116 Rooted
<PlainDateTimeWithCalendar
> temporalDateTime(cx
);
117 if (!CreateTemporalDateTime(cx
, dateTime
, calendar
, &temporalDateTime
)) {
122 if (offsetBehaviour
== OffsetBehaviour::Wall
||
123 offsetOption
== TemporalOffset::Ignore
) {
125 return GetInstantFor(cx
, timeZone
, temporalDateTime
, disambiguation
,
130 if (offsetBehaviour
== OffsetBehaviour::Exact
||
131 offsetOption
== TemporalOffset::Use
) {
133 auto epochNanoseconds
= GetUTCEpochNanoseconds(
134 dateTime
, InstantSpan::fromNanoseconds(offsetNanoseconds
));
137 if (!IsValidEpochInstant(epochNanoseconds
)) {
138 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
139 JSMSG_TEMPORAL_INSTANT_INVALID
);
144 *result
= epochNanoseconds
;
149 MOZ_ASSERT(offsetBehaviour
== OffsetBehaviour::Option
);
152 MOZ_ASSERT(offsetOption
== TemporalOffset::Prefer
||
153 offsetOption
== TemporalOffset::Reject
);
155 // FIXME: spec issue - duplicate assertion
158 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
159 timeZone
, TimeZoneMethod::GetPossibleInstantsFor
));
162 Rooted
<InstantVector
> possibleInstants(cx
, InstantVector(cx
));
163 if (!GetPossibleInstantsFor(cx
, timeZone
, temporalDateTime
,
164 &possibleInstants
)) {
169 if (!possibleInstants
.empty()) {
171 Rooted
<Wrapped
<InstantObject
*>> candidate(cx
);
172 for (size_t i
= 0; i
< possibleInstants
.length(); i
++) {
173 candidate
= possibleInstants
[i
];
176 int64_t candidateNanoseconds
;
177 if (!GetOffsetNanosecondsFor(cx
, timeZone
, candidate
,
178 &candidateNanoseconds
)) {
181 MOZ_ASSERT(std::abs(candidateNanoseconds
) <
182 ToNanoseconds(TemporalUnit::Day
));
185 if (candidateNanoseconds
== offsetNanoseconds
) {
186 auto* unwrapped
= candidate
.unwrap(cx
);
191 *result
= ToInstant(unwrapped
);
196 if (matchBehaviour
== MatchBehaviour::MatchMinutes
) {
198 int64_t roundedCandidateNanoseconds
=
199 RoundNanosecondsToMinutesIncrement(candidateNanoseconds
);
202 if (roundedCandidateNanoseconds
== offsetNanoseconds
) {
203 auto* unwrapped
= candidate
.unwrap(cx
);
208 // Step 11.a.iii.2.a.
209 *result
= ToInstant(unwrapped
);
217 if (offsetOption
== TemporalOffset::Reject
) {
218 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
219 JSMSG_TEMPORAL_ZONED_DATE_TIME_NO_TIME_FOUND
);
224 Rooted
<Wrapped
<InstantObject
*>> instant(cx
);
225 if (!DisambiguatePossibleInstants(cx
, possibleInstants
, timeZone
,
226 ToPlainDateTime(temporalDateTime
),
227 disambiguation
, &instant
)) {
231 auto* unwrappedInstant
= instant
.unwrap(cx
);
232 if (!unwrappedInstant
) {
237 *result
= ToInstant(unwrappedInstant
);
242 * ToTemporalZonedDateTime ( item [ , options ] )
244 static bool ToTemporalZonedDateTime(JSContext
* cx
, Handle
<Value
> item
,
245 Handle
<JSObject
*> maybeOptions
,
246 MutableHandle
<ZonedDateTime
> result
) {
247 // Step 1. (Not applicable in our implementation)
250 Rooted
<PlainObject
*> maybeResolvedOptions(cx
);
252 maybeResolvedOptions
= SnapshotOwnProperties(cx
, maybeOptions
);
253 if (!maybeResolvedOptions
) {
259 auto offsetBehaviour
= OffsetBehaviour::Option
;
262 auto matchBehaviour
= MatchBehaviour::MatchExactly
;
264 // Step 7. (Reordered)
265 int64_t offsetNanoseconds
= 0;
268 Rooted
<CalendarValue
> calendar(cx
);
269 Rooted
<TimeZoneValue
> timeZone(cx
);
270 PlainDateTime dateTime
;
271 auto disambiguation
= TemporalDisambiguation::Compatible
;
272 auto offsetOption
= TemporalOffset::Reject
;
273 if (item
.isObject()) {
274 Rooted
<JSObject
*> itemObj(cx
, &item
.toObject());
277 if (auto* zonedDateTime
= itemObj
->maybeUnwrapIf
<ZonedDateTimeObject
>()) {
278 auto instant
= ToInstant(zonedDateTime
);
279 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
280 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
282 if (!timeZone
.wrap(cx
)) {
285 if (!calendar
.wrap(cx
)) {
289 result
.set(ZonedDateTime
{instant
, timeZone
, calendar
});
294 if (!GetTemporalCalendarWithISODefault(cx
, itemObj
, &calendar
)) {
299 Rooted
<CalendarRecord
> calendarRec(cx
);
300 if (!CreateCalendarMethodsRecord(cx
, calendar
,
302 CalendarMethod::DateFromFields
,
303 CalendarMethod::Fields
,
310 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
311 if (!CalendarFields(cx
, calendarRec
,
312 {CalendarField::Day
, CalendarField::Month
,
313 CalendarField::MonthCode
, CalendarField::Year
},
319 if (!AppendSorted(cx
, fieldNames
.get(),
322 TemporalField::Microsecond
,
323 TemporalField::Millisecond
,
324 TemporalField::Minute
,
325 TemporalField::Nanosecond
,
326 TemporalField::Offset
,
327 TemporalField::Second
,
328 TemporalField::TimeZone
,
334 Rooted
<PlainObject
*> fields(
335 cx
, PrepareTemporalFields(cx
, itemObj
, fieldNames
,
336 {TemporalField::TimeZone
}));
342 Rooted
<Value
> timeZoneValue(cx
);
343 if (!GetProperty(cx
, fields
, fields
, cx
->names().timeZone
,
349 if (!ToTemporalTimeZone(cx
, timeZoneValue
, &timeZone
)) {
354 Rooted
<Value
> offsetValue(cx
);
355 if (!GetProperty(cx
, fields
, fields
, cx
->names().offset
, &offsetValue
)) {
360 MOZ_ASSERT(offsetValue
.isString() || offsetValue
.isUndefined());
363 Rooted
<JSString
*> offsetString(cx
);
364 if (offsetValue
.isString()) {
365 offsetString
= offsetValue
.toString();
367 offsetBehaviour
= OffsetBehaviour::Wall
;
370 if (maybeResolvedOptions
) {
372 if (!ToTemporalDisambiguation(cx
, maybeResolvedOptions
,
378 if (!ToTemporalOffset(cx
, maybeResolvedOptions
, &offsetOption
)) {
383 if (!InterpretTemporalDateTimeFields(cx
, calendarRec
, fields
,
384 maybeResolvedOptions
, &dateTime
)) {
388 // Steps 5.l-n. (Not applicable)
391 if (!InterpretTemporalDateTimeFields(cx
, calendarRec
, fields
,
398 if (offsetBehaviour
== OffsetBehaviour::Option
) {
399 if (!ParseDateTimeUTCOffset(cx
, offsetString
, &offsetNanoseconds
)) {
405 if (!item
.isString()) {
406 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, item
,
407 nullptr, "not a string");
410 Rooted
<JSString
*> string(cx
, item
.toString());
412 // Case 1: 19700101Z[+02:00]
413 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
415 // Case 2: 19700101+00:00[+02:00]
416 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "+02:00" }
418 // Case 3: 19700101[+02:00]
419 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
421 // Case 4: 19700101Z[Europe/Berlin]
422 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
424 // Case 5: 19700101+00:00[Europe/Berlin]
425 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "Europe/Berlin" }
427 // Case 6: 19700101[Europe/Berlin]
428 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
433 int64_t timeZoneOffset
;
434 Rooted
<ParsedTimeZone
> timeZoneString(cx
);
435 Rooted
<JSString
*> calendarString(cx
);
436 if (!ParseTemporalZonedDateTimeString(cx
, string
, &dateTime
, &isUTC
,
437 &hasOffset
, &timeZoneOffset
,
438 &timeZoneString
, &calendarString
)) {
443 MOZ_ASSERT(timeZoneString
);
446 if (!ToTemporalTimeZone(cx
, timeZoneString
, &timeZone
)) {
450 // Step 6.f. (Not applicable in our implementation.)
454 offsetBehaviour
= OffsetBehaviour::Exact
;
458 else if (!hasOffset
) {
459 offsetBehaviour
= OffsetBehaviour::Wall
;
463 if (calendarString
) {
464 if (!ToBuiltinCalendar(cx
, calendarString
, &calendar
)) {
468 calendar
.set(CalendarValue(cx
->names().iso8601
));
472 matchBehaviour
= MatchBehaviour::MatchMinutes
;
474 if (maybeResolvedOptions
) {
476 if (!ToTemporalDisambiguation(cx
, maybeResolvedOptions
,
482 if (!ToTemporalOffset(cx
, maybeResolvedOptions
, &offsetOption
)) {
487 TemporalOverflow ignored
;
488 if (!ToTemporalOverflow(cx
, maybeResolvedOptions
, &ignored
)) {
494 if (offsetBehaviour
== OffsetBehaviour::Option
) {
495 MOZ_ASSERT(hasOffset
);
496 offsetNanoseconds
= timeZoneOffset
;
501 Rooted
<TimeZoneRecord
> timeZoneRec(cx
);
502 if (!CreateTimeZoneMethodsRecord(cx
, timeZone
,
504 TimeZoneMethod::GetOffsetNanosecondsFor
,
505 TimeZoneMethod::GetPossibleInstantsFor
,
512 Instant epochNanoseconds
;
513 if (!InterpretISODateTimeOffset(
514 cx
, dateTime
, offsetBehaviour
, offsetNanoseconds
, timeZoneRec
,
515 disambiguation
, offsetOption
, matchBehaviour
, &epochNanoseconds
)) {
520 result
.set(ZonedDateTime
{epochNanoseconds
, timeZone
, calendar
});
525 * ToTemporalZonedDateTime ( item [ , options ] )
527 static bool ToTemporalZonedDateTime(JSContext
* cx
, Handle
<Value
> item
,
528 MutableHandle
<ZonedDateTime
> result
) {
529 return ToTemporalZonedDateTime(cx
, item
, nullptr, result
);
533 * ToTemporalZonedDateTime ( item [ , options ] )
535 static ZonedDateTimeObject
* ToTemporalZonedDateTime(
536 JSContext
* cx
, Handle
<Value
> item
, Handle
<JSObject
*> maybeOptions
) {
537 Rooted
<ZonedDateTime
> result(cx
);
538 if (!ToTemporalZonedDateTime(cx
, item
, maybeOptions
, &result
)) {
541 return CreateTemporalZonedDateTime(cx
, result
.instant(), result
.timeZone(),
546 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
549 static ZonedDateTimeObject
* CreateTemporalZonedDateTime(
550 JSContext
* cx
, const CallArgs
& args
, Handle
<BigInt
*> epochNanoseconds
,
551 Handle
<TimeZoneValue
> timeZone
, Handle
<CalendarValue
> calendar
) {
553 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds
));
556 Rooted
<JSObject
*> proto(cx
);
557 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_ZonedDateTime
,
562 auto* obj
= NewObjectWithClassProto
<ZonedDateTimeObject
>(cx
, proto
);
568 auto instant
= ToInstant(epochNanoseconds
);
569 obj
->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT
,
570 NumberValue(instant
.seconds
));
571 obj
->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT
,
572 Int32Value(instant
.nanoseconds
));
575 obj
->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT
, timeZone
.toSlotValue());
578 obj
->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT
, calendar
.toValue());
585 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
588 ZonedDateTimeObject
* js::temporal::CreateTemporalZonedDateTime(
589 JSContext
* cx
, const Instant
& instant
, Handle
<TimeZoneValue
> timeZone
,
590 Handle
<CalendarValue
> calendar
) {
592 MOZ_ASSERT(IsValidEpochInstant(instant
));
595 auto* obj
= NewBuiltinClassInstance
<ZonedDateTimeObject
>(cx
);
601 obj
->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT
,
602 NumberValue(instant
.seconds
));
603 obj
->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT
,
604 Int32Value(instant
.nanoseconds
));
607 obj
->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT
, timeZone
.toSlotValue());
610 obj
->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT
, calendar
.toValue());
616 struct PlainDateTimeAndInstant
{
617 PlainDateTime dateTime
;
622 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
625 static bool AddDaysToZonedDateTime(JSContext
* cx
, const Instant
& instant
,
626 const PlainDateTime
& dateTime
,
627 Handle
<TimeZoneRecord
> timeZone
,
628 Handle
<CalendarValue
> calendar
, int64_t days
,
629 TemporalOverflow overflow
,
630 PlainDateTimeAndInstant
* result
) {
631 // Step 1. (Not applicable in our implementation.)
633 // Step 2. (Not applicable)
637 *result
= {dateTime
, instant
};
643 if (!AddISODate(cx
, dateTime
.date
, {0, 0, 0, days
}, overflow
, &addedDate
)) {
648 Rooted
<PlainDateTimeWithCalendar
> dateTimeResult(cx
);
649 if (!CreateTemporalDateTime(cx
, {addedDate
, dateTime
.time
}, calendar
,
655 Instant instantResult
;
656 if (!GetInstantFor(cx
, timeZone
, dateTimeResult
,
657 TemporalDisambiguation::Compatible
, &instantResult
)) {
662 *result
= {ToPlainDateTime(dateTimeResult
), instantResult
};
667 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
670 bool js::temporal::AddDaysToZonedDateTime(
671 JSContext
* cx
, const Instant
& instant
, const PlainDateTime
& dateTime
,
672 Handle
<TimeZoneRecord
> timeZone
, Handle
<CalendarValue
> calendar
,
673 int64_t days
, TemporalOverflow overflow
, Instant
* result
) {
675 PlainDateTimeAndInstant dateTimeAndInstant
;
676 if (!::AddDaysToZonedDateTime(cx
, instant
, dateTime
, timeZone
, calendar
, days
,
677 overflow
, &dateTimeAndInstant
)) {
681 *result
= dateTimeAndInstant
.instant
;
686 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
689 bool js::temporal::AddDaysToZonedDateTime(JSContext
* cx
, const Instant
& instant
,
690 const PlainDateTime
& dateTime
,
691 Handle
<TimeZoneRecord
> timeZone
,
692 Handle
<CalendarValue
> calendar
,
693 int64_t days
, Instant
* result
) {
695 auto overflow
= TemporalOverflow::Constrain
;
698 return AddDaysToZonedDateTime(cx
, instant
, dateTime
, timeZone
, calendar
, days
,
703 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
704 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
706 static bool AddZonedDateTime(JSContext
* cx
, const Instant
& epochNanoseconds
,
707 Handle
<TimeZoneRecord
> timeZone
,
708 Handle
<CalendarRecord
> calendar
,
709 const NormalizedDuration
& duration
,
710 mozilla::Maybe
<const PlainDateTime
&> dateTime
,
711 Handle
<JSObject
*> maybeOptions
, Instant
* result
) {
712 MOZ_ASSERT(IsValidEpochInstant(epochNanoseconds
));
713 MOZ_ASSERT(IsValidDuration(duration
));
716 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
717 timeZone
, TimeZoneMethod::GetPossibleInstantsFor
));
720 MOZ_ASSERT_IF(!dateTime
,
721 TimeZoneMethodsRecordHasLookedUp(
722 timeZone
, TimeZoneMethod::GetOffsetNanosecondsFor
));
724 // Steps 4-5. (Not applicable in our implementation)
727 if (duration
.date
== DateDuration
{}) {
729 return AddInstant(cx
, epochNanoseconds
, duration
.time
, result
);
732 // Step 7. (Not applicable in our implementation)
735 PlainDateTime temporalDateTime
;
738 temporalDateTime
= *dateTime
;
741 if (!GetPlainDateTimeFor(cx
, timeZone
, epochNanoseconds
,
742 &temporalDateTime
)) {
746 auto& [date
, time
] = temporalDateTime
;
749 if (duration
.date
.years
== 0 && duration
.date
.months
== 0 &&
750 duration
.date
.weeks
== 0) {
752 auto overflow
= TemporalOverflow::Constrain
;
754 if (!ToTemporalOverflow(cx
, maybeOptions
, &overflow
)) {
760 Instant intermediate
;
761 if (!AddDaysToZonedDateTime(cx
, epochNanoseconds
, temporalDateTime
,
762 timeZone
, calendar
.receiver(),
763 duration
.date
.days
, overflow
, &intermediate
)) {
768 return AddInstant(cx
, intermediate
, duration
.time
, result
);
773 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateAdd
));
776 const auto& datePart
= date
;
779 const auto& dateDuration
= duration
.date
;
784 if (!CalendarDateAdd(cx
, calendar
, datePart
, dateDuration
, maybeOptions
,
789 if (!CalendarDateAdd(cx
, calendar
, datePart
, dateDuration
, &addedDate
)) {
795 Rooted
<PlainDateTimeWithCalendar
> intermediateDateTime(cx
);
796 if (!CreateTemporalDateTime(cx
, {addedDate
, time
}, calendar
.receiver(),
797 &intermediateDateTime
)) {
802 Instant intermediateInstant
;
803 if (!GetInstantFor(cx
, timeZone
, intermediateDateTime
,
804 TemporalDisambiguation::Compatible
,
805 &intermediateInstant
)) {
810 return AddInstant(cx
, intermediateInstant
, duration
.time
, result
);
814 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
815 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
817 static bool AddZonedDateTime(JSContext
* cx
, const Instant
& epochNanoseconds
,
818 Handle
<TimeZoneRecord
> timeZone
,
819 Handle
<CalendarRecord
> calendar
,
820 const NormalizedDuration
& duration
,
821 Handle
<JSObject
*> maybeOptions
, Instant
* result
) {
822 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
, duration
,
823 mozilla::Nothing(), maybeOptions
, result
);
827 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
828 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
830 bool js::temporal::AddZonedDateTime(JSContext
* cx
,
831 const Instant
& epochNanoseconds
,
832 Handle
<TimeZoneRecord
> timeZone
,
833 Handle
<CalendarRecord
> calendar
,
834 const NormalizedDuration
& duration
,
836 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
, duration
,
837 mozilla::Nothing(), nullptr, result
);
841 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
842 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
844 bool js::temporal::AddZonedDateTime(JSContext
* cx
,
845 const Instant
& epochNanoseconds
,
846 Handle
<TimeZoneRecord
> timeZone
,
847 Handle
<CalendarRecord
> calendar
,
848 const NormalizedDuration
& duration
,
849 const PlainDateTime
& dateTime
,
851 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
, duration
,
852 mozilla::SomeRef(dateTime
), nullptr, result
);
856 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
857 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
859 bool js::temporal::AddZonedDateTime(JSContext
* cx
,
860 const Instant
& epochNanoseconds
,
861 Handle
<TimeZoneRecord
> timeZone
,
862 Handle
<CalendarRecord
> calendar
,
863 const DateDuration
& duration
,
865 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
,
866 {duration
, {}}, mozilla::Nothing(), nullptr,
871 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
872 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
874 bool js::temporal::AddZonedDateTime(JSContext
* cx
,
875 const Instant
& epochNanoseconds
,
876 Handle
<TimeZoneRecord
> timeZone
,
877 Handle
<CalendarRecord
> calendar
,
878 const DateDuration
& duration
,
879 const PlainDateTime
& dateTime
,
881 return ::AddZonedDateTime(cx
, epochNanoseconds
, timeZone
, calendar
,
882 {duration
, {}}, mozilla::SomeRef(dateTime
), nullptr,
887 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
888 * precalculatedPlainDateTime ] )
890 static bool NormalizedTimeDurationToDays(
891 JSContext
* cx
, const NormalizedTimeDuration
& duration
,
892 Handle
<ZonedDateTime
> zonedRelativeTo
, Handle
<TimeZoneRecord
> timeZone
,
893 mozilla::Maybe
<const PlainDateTime
&> precalculatedPlainDateTime
,
894 NormalizedTimeAndDays
* result
) {
895 MOZ_ASSERT(IsValidNormalizedTimeDuration(duration
));
898 int32_t sign
= NormalizedTimeDurationSign(duration
);
902 *result
= {int64_t(0), int64_t(0), ToNanoseconds(TemporalUnit::Day
)};
907 const auto& startNs
= zonedRelativeTo
.instant();
910 auto endNs
= AddNormalizedTimeDurationToEpochNanoseconds(duration
, startNs
);
913 if (!IsValidEpochInstant(endNs
)) {
914 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
915 JSMSG_TEMPORAL_INSTANT_INVALID
);
920 PlainDateTime startDateTime
;
921 if (!precalculatedPlainDateTime
) {
922 if (!GetPlainDateTimeFor(cx
, timeZone
, startNs
, &startDateTime
)) {
926 startDateTime
= *precalculatedPlainDateTime
;
930 PlainDateTime endDateTime
;
931 if (!GetPlainDateTimeFor(cx
, timeZone
, endNs
, &endDateTime
)) {
935 // Steps 10-11. (Not applicable in our implementation.)
939 // Overflows in step 21 can be safely ignored, because they take too long to
941 int64_t days
= DaysUntil(startDateTime
.date
, endDateTime
.date
);
944 int32_t timeSign
= CompareTemporalTime(startDateTime
.time
, endDateTime
.time
);
947 if (days
> 0 && timeSign
> 0) {
949 } else if (days
< 0 && timeSign
< 0) {
954 PlainDateTimeAndInstant relativeResult
;
955 if (!::AddDaysToZonedDateTime(cx
, startNs
, startDateTime
, timeZone
,
956 zonedRelativeTo
.calendar(), days
,
957 TemporalOverflow::Constrain
, &relativeResult
)) {
960 MOZ_ASSERT(IsValidISODateTime(relativeResult
.dateTime
));
961 MOZ_ASSERT(IsValidEpochInstant(relativeResult
.instant
));
966 while (days
> 0 && relativeResult
.instant
> endNs
) {
967 // This loop can iterate indefinitely when given a specially crafted
968 // time zone object, so we need to check for interrupts.
969 if (!CheckForInterrupt(cx
)) {
977 if (!::AddDaysToZonedDateTime(
978 cx
, startNs
, startDateTime
, timeZone
, zonedRelativeTo
.calendar(),
979 days
, TemporalOverflow::Constrain
, &relativeResult
)) {
982 MOZ_ASSERT(IsValidISODateTime(relativeResult
.dateTime
));
983 MOZ_ASSERT(IsValidEpochInstant(relativeResult
.instant
));
986 MOZ_ASSERT_IF(days
> 0, relativeResult
.instant
<= endNs
);
989 MOZ_ASSERT_IF(days
== 0, relativeResult
.instant
== startNs
);
991 // Step 18. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
992 auto ns
= endNs
- relativeResult
.instant
;
993 MOZ_ASSERT(IsValidInstantSpan(ns
));
996 auto dayLengthNs
= InstantSpan
{};
998 // This loop can iterate indefinitely when given a specially crafted time
999 // zone object, so we need to check for interrupts.
1000 if (!CheckForInterrupt(cx
)) {
1005 PlainDateTimeAndInstant oneDayFarther
;
1006 if (!::AddDaysToZonedDateTime(
1007 cx
, relativeResult
.instant
, relativeResult
.dateTime
, timeZone
,
1008 zonedRelativeTo
.calendar(), sign
, TemporalOverflow::Constrain
,
1012 MOZ_ASSERT(IsValidISODateTime(oneDayFarther
.dateTime
));
1013 MOZ_ASSERT(IsValidEpochInstant(oneDayFarther
.instant
));
1015 // Step 21.b. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
1016 dayLengthNs
= oneDayFarther
.instant
- relativeResult
.instant
;
1017 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs
));
1023 // ns = endNs - relativeResult.instant
1024 // dayLengthNs = oneDayFarther.instant - relativeResult.instant
1025 // diff = ns - dayLengthNs
1026 // = (endNs - relativeResult.instant) - (oneDayFarther.instant - relativeResult.instant)
1027 // = endNs - relativeResult.instant - oneDayFarther.instant + relativeResult.instant
1028 // = endNs - oneDayFarther.instant
1030 // Second iteration:
1033 // = endNs - oneDayFarther.instant'
1034 // relativeResult.instant = oneDayFarther.instant'
1035 // dayLengthNs = oneDayFarther.instant - relativeResult.instant
1036 // = oneDayFarther.instant - oneDayFarther.instant'
1037 // diff = ns - dayLengthNs
1038 // = (endNs - oneDayFarther.instant') - (oneDayFarther.instant - oneDayFarther.instant')
1039 // = endNs - oneDayFarther.instant' - oneDayFarther.instant + oneDayFarther.instant'
1040 // = endNs - oneDayFarther.instant
1042 // Where |diff'| and |oneDayFarther.instant'| denote the variables from the
1043 // previous iteration.
1045 // This repeats for all following iterations.
1047 // |endNs| and |oneDayFarther.instant| are both valid epoch instant values,
1048 // so the difference is a valid epoch instant difference value, too.
1052 // FIXME: spec issue - SubtractNormalizedTimeDuration should be infallible
1054 // Step 21.c. (Inlined SubtractNormalizedTimeDuration)
1055 auto oneDayLess
= ns
- dayLengthNs
;
1056 MOZ_ASSERT(IsValidInstantSpan(oneDayLess
));
1057 MOZ_ASSERT(oneDayLess
== (endNs
- oneDayFarther
.instant
));
1059 if (oneDayLess
== InstantSpan
{} ||
1060 ((oneDayLess
< InstantSpan
{}) == (sign
< 0))) {
1065 relativeResult
= oneDayFarther
;
1076 if (days
< 0 && sign
> 0) {
1077 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1078 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN
,
1084 if (days
> 0 && sign
< 0) {
1085 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1086 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN
,
1091 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs
));
1092 MOZ_ASSERT(IsValidInstantSpan(ns
));
1094 // FIXME: spec issue - rewrite steps 24-25 as:
1096 // If sign = -1, then
1097 // If nanoseconds > 0, throw a RangeError.
1099 // Assert: nanoseconds ≥ 0.
1101 // https://github.com/tc39/proposal-temporal/issues/2530
1105 if (ns
> InstantSpan
{}) {
1106 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1107 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN
,
1112 MOZ_ASSERT(ns
>= InstantSpan
{});
1116 dayLengthNs
= dayLengthNs
.abs();
1117 MOZ_ASSERT(ns
.abs() < dayLengthNs
);
1120 constexpr auto maxDayLength
= Int128
{1} << 53;
1121 auto dayLengthNanos
= dayLengthNs
.toNanoseconds();
1122 if (dayLengthNanos
>= maxDayLength
) {
1123 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1124 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN
,
1129 auto timeNanos
= ns
.toNanoseconds();
1130 MOZ_ASSERT(timeNanos
== Int128
{int64_t(timeNanos
)},
1131 "abs(ns) < dayLengthNs < 2**53 implies that |ns| fits in int64");
1133 // FIXME: spec issue - restrict days to 2**53 / (24*60*60)?
1135 // Valid duration days are smaller than ⌈(2**53) / (24 * 60 * 60)⌉.
1136 static constexpr int64_t durationDays
= (int64_t(1) << 53) / (24 * 60 * 60);
1138 // NOTE: This case won't happen in practice, because the initial value of
1139 // |days| is at most ±200'000'000 and the loop can only increment resp.
1140 // decrement |days| by one.
1141 if (std::abs(days
) > durationDays
) {
1142 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1143 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN
,
1149 *result
= {days
, int64_t{timeNanos
}, int64_t(dayLengthNanos
)};
1154 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
1155 * precalculatedPlainDateTime ] )
1157 bool js::temporal::NormalizedTimeDurationToDays(
1158 JSContext
* cx
, const NormalizedTimeDuration
& duration
,
1159 Handle
<ZonedDateTime
> zonedRelativeTo
, Handle
<TimeZoneRecord
> timeZone
,
1160 NormalizedTimeAndDays
* result
) {
1161 return ::NormalizedTimeDurationToDays(cx
, duration
, zonedRelativeTo
, timeZone
,
1162 mozilla::Nothing(), result
);
1166 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
1167 * precalculatedPlainDateTime ] )
1169 bool js::temporal::NormalizedTimeDurationToDays(
1170 JSContext
* cx
, const NormalizedTimeDuration
& duration
,
1171 Handle
<ZonedDateTime
> zonedRelativeTo
, Handle
<TimeZoneRecord
> timeZone
,
1172 const PlainDateTime
& precalculatedPlainDateTime
,
1173 NormalizedTimeAndDays
* result
) {
1174 return ::NormalizedTimeDurationToDays(
1175 cx
, duration
, zonedRelativeTo
, timeZone
,
1176 mozilla::SomeRef(precalculatedPlainDateTime
), result
);
1180 * DifferenceZonedDateTime ( ns1, ns2, timeZoneRec, calendarRec, largestUnit,
1181 * options, precalculatedPlainDateTime )
1183 static bool DifferenceZonedDateTime(
1184 JSContext
* cx
, const Instant
& ns1
, const Instant
& ns2
,
1185 Handle
<TimeZoneRecord
> timeZone
, Handle
<CalendarRecord
> calendar
,
1186 TemporalUnit largestUnit
, Handle
<PlainObject
*> maybeOptions
,
1187 mozilla::Maybe
<const PlainDateTime
&> precalculatedPlainDateTime
,
1188 NormalizedDuration
* result
) {
1189 MOZ_ASSERT(IsValidEpochInstant(ns1
));
1190 MOZ_ASSERT(IsValidEpochInstant(ns2
));
1194 *result
= CreateNormalizedDurationRecord({}, {});
1199 PlainDateTime startDateTime
;
1200 if (!precalculatedPlainDateTime
) {
1202 if (!GetPlainDateTimeFor(cx
, timeZone
, ns1
, &startDateTime
)) {
1207 startDateTime
= *precalculatedPlainDateTime
;
1211 PlainDateTime endDateTime
;
1212 if (!GetPlainDateTimeFor(cx
, timeZone
, ns2
, &endDateTime
)) {
1217 NormalizedDuration dateDifference
;
1219 if (!DifferenceISODateTime(cx
, startDateTime
, endDateTime
, calendar
,
1220 largestUnit
, maybeOptions
, &dateDifference
)) {
1224 if (!DifferenceISODateTime(cx
, startDateTime
, endDateTime
, calendar
,
1225 largestUnit
, &dateDifference
)) {
1231 Instant intermediateNs
;
1232 if (!AddZonedDateTime(cx
, ns1
, timeZone
, calendar
,
1234 dateDifference
.date
.years
,
1235 dateDifference
.date
.months
,
1236 dateDifference
.date
.weeks
,
1238 startDateTime
, &intermediateNs
)) {
1241 MOZ_ASSERT(IsValidEpochInstant(intermediateNs
));
1245 NormalizedTimeDurationFromEpochNanosecondsDifference(ns2
, intermediateNs
);
1248 Rooted
<ZonedDateTime
> intermediate(
1250 ZonedDateTime
{intermediateNs
, timeZone
.receiver(), calendar
.receiver()});
1253 NormalizedTimeAndDays timeAndDays
;
1254 if (!NormalizedTimeDurationToDays(cx
, timeDuration
, intermediate
, timeZone
,
1260 auto dateDuration
= DateDuration
{
1261 dateDifference
.date
.years
,
1262 dateDifference
.date
.months
,
1263 dateDifference
.date
.weeks
,
1266 if (!ThrowIfInvalidDuration(cx
, dateDuration
)) {
1270 return CreateNormalizedDurationRecord(
1272 NormalizedTimeDuration::fromNanoseconds(timeAndDays
.time
), result
);
1276 * DifferenceZonedDateTime ( ns1, ns2, timeZoneRec, calendarRec, largestUnit,
1277 * options, precalculatedPlainDateTime )
1279 bool js::temporal::DifferenceZonedDateTime(
1280 JSContext
* cx
, const Instant
& ns1
, const Instant
& ns2
,
1281 Handle
<TimeZoneRecord
> timeZone
, Handle
<CalendarRecord
> calendar
,
1282 TemporalUnit largestUnit
, const PlainDateTime
& precalculatedPlainDateTime
,
1283 NormalizedDuration
* result
) {
1284 return ::DifferenceZonedDateTime(
1285 cx
, ns1
, ns2
, timeZone
, calendar
, largestUnit
, nullptr,
1286 mozilla::SomeRef(precalculatedPlainDateTime
), result
);
1290 * TimeZoneEquals ( one, two )
1292 static bool TimeZoneEqualsOrThrow(JSContext
* cx
, Handle
<TimeZoneValue
> one
,
1293 Handle
<TimeZoneValue
> two
) {
1295 if (one
.isObject() && two
.isObject() && one
.toObject() == two
.toObject()) {
1300 Rooted
<JSString
*> timeZoneOne(cx
, ToTemporalTimeZoneIdentifier(cx
, one
));
1306 Rooted
<JSString
*> timeZoneTwo(cx
, ToTemporalTimeZoneIdentifier(cx
, two
));
1313 if (!TimeZoneEquals(cx
, timeZoneOne
, timeZoneTwo
, &equals
)) {
1320 // Throw an error when the time zone identifiers don't match. Used when
1321 // unequal time zones throw a RangeError.
1322 if (auto charsOne
= QuoteString(cx
, timeZoneOne
)) {
1323 if (auto charsTwo
= QuoteString(cx
, timeZoneTwo
)) {
1324 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1325 JSMSG_TEMPORAL_TIMEZONE_INCOMPATIBLE
,
1326 charsOne
.get(), charsTwo
.get());
1333 * RoundISODateTime ( year, month, day, hour, minute, second, millisecond,
1334 * microsecond, nanosecond, increment, unit, roundingMode [ , dayLength ] )
1336 static bool RoundISODateTime(JSContext
* cx
, const PlainDateTime
& dateTime
,
1337 Increment increment
, TemporalUnit unit
,
1338 TemporalRoundingMode roundingMode
,
1339 const InstantSpan
& dayLength
,
1340 PlainDateTime
* result
) {
1341 MOZ_ASSERT(IsValidInstantSpan(dayLength
));
1342 MOZ_ASSERT(dayLength
> (InstantSpan
{}));
1344 const auto& [date
, time
] = dateTime
;
1347 MOZ_ASSERT(IsValidISODateTime(dateTime
));
1348 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime
));
1350 // Step 2. (Not applicable in our implementation.)
1353 auto roundedTime
= RoundTime(time
, increment
, unit
, roundingMode
, dayLength
);
1355 // |dayLength| can be as small as 1, so the number of rounded days can be as
1356 // large as the number of nanoseconds in |time|.
1357 MOZ_ASSERT(0 <= roundedTime
.days
&&
1358 roundedTime
.days
< ToNanoseconds(TemporalUnit::Day
));
1361 PlainDate balanceResult
;
1362 if (!BalanceISODate(cx
, date
.year
, date
.month
,
1363 int64_t(date
.day
) + roundedTime
.days
, &balanceResult
)) {
1368 *result
= {balanceResult
, roundedTime
.time
};
1373 * DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options )
1375 static bool DifferenceTemporalZonedDateTime(JSContext
* cx
,
1376 TemporalDifference operation
,
1377 const CallArgs
& args
) {
1378 Rooted
<ZonedDateTime
> zonedDateTime(
1379 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1381 // Step 1. (Not applicable in our implementation.)
1384 Rooted
<ZonedDateTime
> other(cx
);
1385 if (!ToTemporalZonedDateTime(cx
, args
.get(0), &other
)) {
1390 if (!CalendarEqualsOrThrow(cx
, zonedDateTime
.calendar(), other
.calendar())) {
1395 Rooted
<PlainObject
*> resolvedOptions(cx
);
1396 DifferenceSettings settings
;
1397 if (args
.hasDefined(1)) {
1398 Rooted
<JSObject
*> options(
1399 cx
, RequireObjectArg(cx
, "options", ToName(operation
), args
[1]));
1405 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
1406 if (!resolvedOptions
) {
1411 if (!GetDifferenceSettings(
1412 cx
, operation
, resolvedOptions
, TemporalUnitGroup::DateTime
,
1413 TemporalUnit::Nanosecond
, TemporalUnit::Hour
, &settings
)) {
1419 TemporalUnit::Nanosecond
,
1421 TemporalRoundingMode::Trunc
,
1427 if (settings
.largestUnit
> TemporalUnit::Day
) {
1428 MOZ_ASSERT(settings
.smallestUnit
>= settings
.largestUnit
);
1431 auto difference
= DifferenceInstant(
1432 zonedDateTime
.instant(), other
.instant(), settings
.roundingIncrement
,
1433 settings
.smallestUnit
, settings
.roundingMode
);
1436 auto balancedTime
= BalanceTimeDuration(difference
, settings
.largestUnit
);
1439 auto duration
= balancedTime
.toDuration();
1440 if (operation
== TemporalDifference::Since
) {
1441 duration
= duration
.negate();
1444 auto* result
= CreateTemporalDuration(cx
, duration
);
1449 args
.rval().setObject(*result
);
1453 // FIXME: spec issue - move this step next to the calendar validation?
1454 // https://github.com/tc39/proposal-temporal/issues/2533
1457 if (!TimeZoneEqualsOrThrow(cx
, zonedDateTime
.timeZone(), other
.timeZone())) {
1462 if (zonedDateTime
.instant() == other
.instant()) {
1463 auto* obj
= CreateTemporalDuration(cx
, {});
1468 args
.rval().setObject(*obj
);
1473 Rooted
<TimeZoneRecord
> timeZone(cx
);
1474 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
1476 TimeZoneMethod::GetOffsetNanosecondsFor
,
1477 TimeZoneMethod::GetPossibleInstantsFor
,
1484 Rooted
<CalendarRecord
> calendar(cx
);
1485 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
1487 CalendarMethod::DateAdd
,
1488 CalendarMethod::DateUntil
,
1495 PlainDateTime precalculatedPlainDateTime
;
1496 if (!GetPlainDateTimeFor(cx
, timeZone
, zonedDateTime
.instant(),
1497 &precalculatedPlainDateTime
)) {
1502 Rooted
<PlainDateObject
*> plainRelativeTo(
1503 cx
, CreateTemporalDate(cx
, precalculatedPlainDateTime
.date
,
1504 calendar
.receiver()));
1505 if (!plainRelativeTo
) {
1510 if (resolvedOptions
) {
1511 Rooted
<Value
> largestUnitValue(
1512 cx
, StringValue(TemporalUnitToString(cx
, settings
.largestUnit
)));
1513 if (!DefineDataProperty(cx
, resolvedOptions
, cx
->names().largestUnit
,
1514 largestUnitValue
)) {
1520 NormalizedDuration difference
;
1521 if (!::DifferenceZonedDateTime(
1522 cx
, zonedDateTime
.instant(), other
.instant(), timeZone
, calendar
,
1523 settings
.largestUnit
, resolvedOptions
,
1524 mozilla::SomeRef
<const PlainDateTime
>(precalculatedPlainDateTime
),
1530 bool roundingGranularityIsNoop
=
1531 settings
.smallestUnit
== TemporalUnit::Nanosecond
&&
1532 settings
.roundingIncrement
== Increment
{1};
1535 if (!roundingGranularityIsNoop
) {
1537 NormalizedDuration roundResult
;
1538 if (!RoundDuration(cx
, difference
, settings
.roundingIncrement
,
1539 settings
.smallestUnit
, settings
.roundingMode
,
1540 plainRelativeTo
, calendar
, zonedDateTime
, timeZone
,
1541 precalculatedPlainDateTime
, &roundResult
)) {
1546 NormalizedTimeAndDays timeAndDays
;
1547 if (!NormalizedTimeDurationToDays(cx
, roundResult
.time
, zonedDateTime
,
1548 timeZone
, &timeAndDays
)) {
1553 int64_t days
= roundResult
.date
.days
+ timeAndDays
.days
;
1556 auto toAdjust
= NormalizedDuration
{
1558 roundResult
.date
.years
,
1559 roundResult
.date
.months
,
1560 roundResult
.date
.weeks
,
1563 NormalizedTimeDuration::fromNanoseconds(timeAndDays
.time
),
1565 NormalizedDuration adjustResult
;
1566 if (!AdjustRoundedDurationDays(cx
, toAdjust
, settings
.roundingIncrement
,
1567 settings
.smallestUnit
, settings
.roundingMode
,
1568 zonedDateTime
, calendar
, timeZone
,
1569 precalculatedPlainDateTime
, &adjustResult
)) {
1574 DateDuration balanceResult
;
1575 if (!temporal::BalanceDateDurationRelative(
1576 cx
, adjustResult
.date
, settings
.largestUnit
, settings
.smallestUnit
,
1577 plainRelativeTo
, calendar
, &balanceResult
)) {
1582 if (!CombineDateAndNormalizedTimeDuration(cx
, balanceResult
,
1583 adjustResult
.time
, &difference
)) {
1589 auto timeDuration
= BalanceTimeDuration(difference
.time
, TemporalUnit::Hour
);
1592 auto duration
= Duration
{
1593 double(difference
.date
.years
), double(difference
.date
.months
),
1594 double(difference
.date
.weeks
), double(difference
.date
.days
),
1595 double(timeDuration
.hours
), double(timeDuration
.minutes
),
1596 double(timeDuration
.seconds
), double(timeDuration
.milliseconds
),
1597 timeDuration
.microseconds
, timeDuration
.nanoseconds
,
1599 if (operation
== TemporalDifference::Since
) {
1600 duration
= duration
.negate();
1602 MOZ_ASSERT(IsValidDuration(duration
));
1604 auto* obj
= CreateTemporalDuration(cx
, duration
);
1609 args
.rval().setObject(*obj
);
1613 enum class ZonedDateTimeDuration
{ Add
, Subtract
};
1616 * AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime,
1617 * temporalDurationLike, options )
1619 static bool AddDurationToOrSubtractDurationFromZonedDateTime(
1620 JSContext
* cx
, ZonedDateTimeDuration operation
, const CallArgs
& args
) {
1621 Rooted
<ZonedDateTime
> zonedDateTime(
1622 cx
, &args
.thisv().toObject().as
<ZonedDateTimeObject
>());
1624 // Step 1. (Not applicable in our implementation.)
1628 if (!ToTemporalDurationRecord(cx
, args
.get(0), &duration
)) {
1633 Rooted
<JSObject
*> options(cx
);
1634 if (args
.hasDefined(1)) {
1636 operation
== ZonedDateTimeDuration::Add
? "add" : "subtract";
1637 options
= RequireObjectArg(cx
, "options", name
, args
[1]);
1639 options
= NewPlainObjectWithProto(cx
, nullptr);
1646 Rooted
<TimeZoneRecord
> timeZone(cx
);
1647 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
1649 TimeZoneMethod::GetOffsetNanosecondsFor
,
1650 TimeZoneMethod::GetPossibleInstantsFor
,
1657 Rooted
<CalendarRecord
> calendar(cx
);
1658 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
1660 CalendarMethod::DateAdd
,
1667 if (operation
== ZonedDateTimeDuration::Subtract
) {
1668 duration
= duration
.negate();
1670 auto normalized
= CreateNormalizedDurationRecord(duration
);
1673 Instant resultInstant
;
1674 if (!::AddZonedDateTime(cx
, zonedDateTime
.instant(), timeZone
, calendar
,
1675 normalized
, options
, &resultInstant
)) {
1678 MOZ_ASSERT(IsValidEpochInstant(resultInstant
));
1681 auto* result
= CreateTemporalZonedDateTime(
1682 cx
, resultInstant
, timeZone
.receiver(), calendar
.receiver());
1687 args
.rval().setObject(*result
);
1692 * Temporal.ZonedDateTime ( epochNanoseconds, timeZoneLike [ , calendarLike ] )
1694 static bool ZonedDateTimeConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1695 CallArgs args
= CallArgsFromVp(argc
, vp
);
1698 if (!ThrowIfNotConstructing(cx
, args
, "Temporal.ZonedDateTime")) {
1703 Rooted
<BigInt
*> epochNanoseconds(cx
, js::ToBigInt(cx
, args
.get(0)));
1704 if (!epochNanoseconds
) {
1709 if (!IsValidEpochNanoseconds(epochNanoseconds
)) {
1710 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1711 JSMSG_TEMPORAL_INSTANT_INVALID
);
1716 Rooted
<TimeZoneValue
> timeZone(cx
);
1717 if (!ToTemporalTimeZone(cx
, args
.get(1), &timeZone
)) {
1722 Rooted
<CalendarValue
> calendar(cx
);
1723 if (!ToTemporalCalendarWithISODefault(cx
, args
.get(2), &calendar
)) {
1728 auto* obj
= CreateTemporalZonedDateTime(cx
, args
, epochNanoseconds
, timeZone
,
1734 args
.rval().setObject(*obj
);
1739 * Temporal.ZonedDateTime.from ( item [ , options ] )
1741 static bool ZonedDateTime_from(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1742 CallArgs args
= CallArgsFromVp(argc
, vp
);
1745 Rooted
<JSObject
*> options(cx
);
1746 if (args
.hasDefined(1)) {
1747 options
= RequireObjectArg(cx
, "options", "from", args
[1]);
1754 if (args
.get(0).isObject()) {
1755 JSObject
* item
= &args
[0].toObject();
1756 if (auto* zonedDateTime
= item
->maybeUnwrapIf
<ZonedDateTimeObject
>()) {
1757 auto epochInstant
= ToInstant(zonedDateTime
);
1758 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
1759 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
1761 if (!timeZone
.wrap(cx
)) {
1764 if (!calendar
.wrap(cx
)) {
1770 TemporalDisambiguation ignoredDisambiguation
;
1771 if (!ToTemporalDisambiguation(cx
, options
, &ignoredDisambiguation
)) {
1776 TemporalOffset ignoredOffset
;
1777 if (!ToTemporalOffset(cx
, options
, &ignoredOffset
)) {
1782 TemporalOverflow ignoredOverflow
;
1783 if (!ToTemporalOverflow(cx
, options
, &ignoredOverflow
)) {
1790 CreateTemporalZonedDateTime(cx
, epochInstant
, timeZone
, calendar
);
1795 args
.rval().setObject(*result
);
1801 auto* result
= ToTemporalZonedDateTime(cx
, args
.get(0), options
);
1806 args
.rval().setObject(*result
);
1811 * Temporal.ZonedDateTime.compare ( one, two )
1813 static bool ZonedDateTime_compare(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1814 CallArgs args
= CallArgsFromVp(argc
, vp
);
1817 Rooted
<ZonedDateTime
> one(cx
);
1818 if (!ToTemporalZonedDateTime(cx
, args
.get(0), &one
)) {
1823 Rooted
<ZonedDateTime
> two(cx
);
1824 if (!ToTemporalZonedDateTime(cx
, args
.get(1), &two
)) {
1829 const auto& oneNs
= one
.instant();
1830 const auto& twoNs
= two
.instant();
1831 args
.rval().setInt32(oneNs
> twoNs
? 1 : oneNs
< twoNs
? -1 : 0);
1836 * get Temporal.ZonedDateTime.prototype.calendarId
1838 static bool ZonedDateTime_calendarId(JSContext
* cx
, const CallArgs
& args
) {
1839 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
1842 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
1843 auto* calendarId
= ToTemporalCalendarIdentifier(cx
, calendar
);
1848 args
.rval().setString(calendarId
);
1853 * get Temporal.ZonedDateTime.prototype.calendarId
1855 static bool ZonedDateTime_calendarId(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1857 CallArgs args
= CallArgsFromVp(argc
, vp
);
1858 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_calendarId
>(cx
,
1863 * get Temporal.ZonedDateTime.prototype.timeZoneId
1865 static bool ZonedDateTime_timeZoneId(JSContext
* cx
, const CallArgs
& args
) {
1866 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
1869 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
1870 auto* timeZoneId
= ToTemporalTimeZoneIdentifier(cx
, timeZone
);
1875 args
.rval().setString(timeZoneId
);
1880 * get Temporal.ZonedDateTime.prototype.timeZoneId
1882 static bool ZonedDateTime_timeZoneId(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1884 CallArgs args
= CallArgsFromVp(argc
, vp
);
1885 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_timeZoneId
>(cx
,
1890 * get Temporal.ZonedDateTime.prototype.year
1892 static bool ZonedDateTime_year(JSContext
* cx
, const CallArgs
& args
) {
1893 Rooted
<ZonedDateTime
> zonedDateTime(
1894 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1897 PlainDateTime dateTime
;
1898 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
1899 zonedDateTime
.instant(), &dateTime
)) {
1904 return CalendarYear(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
1908 * get Temporal.ZonedDateTime.prototype.year
1910 static bool ZonedDateTime_year(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1912 CallArgs args
= CallArgsFromVp(argc
, vp
);
1913 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_year
>(cx
, args
);
1917 * get Temporal.ZonedDateTime.prototype.month
1919 static bool ZonedDateTime_month(JSContext
* cx
, const CallArgs
& args
) {
1920 Rooted
<ZonedDateTime
> zonedDateTime(
1921 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1924 PlainDateTime dateTime
;
1925 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
1926 zonedDateTime
.instant(), &dateTime
)) {
1931 return CalendarMonth(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
1935 * get Temporal.ZonedDateTime.prototype.month
1937 static bool ZonedDateTime_month(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1939 CallArgs args
= CallArgsFromVp(argc
, vp
);
1940 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_month
>(cx
, args
);
1944 * get Temporal.ZonedDateTime.prototype.monthCode
1946 static bool ZonedDateTime_monthCode(JSContext
* cx
, const CallArgs
& args
) {
1947 Rooted
<ZonedDateTime
> zonedDateTime(
1948 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1951 PlainDateTime dateTime
;
1952 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
1953 zonedDateTime
.instant(), &dateTime
)) {
1958 return CalendarMonthCode(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
1962 * get Temporal.ZonedDateTime.prototype.monthCode
1964 static bool ZonedDateTime_monthCode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1966 CallArgs args
= CallArgsFromVp(argc
, vp
);
1967 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_monthCode
>(cx
,
1972 * get Temporal.ZonedDateTime.prototype.day
1974 static bool ZonedDateTime_day(JSContext
* cx
, const CallArgs
& args
) {
1975 Rooted
<ZonedDateTime
> zonedDateTime(
1976 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
1978 // Step 4. (Reordered)
1979 Rooted
<CalendarRecord
> calendar(cx
);
1980 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
1982 CalendarMethod::Day
,
1989 PlainDateTime dateTime
;
1990 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
1991 zonedDateTime
.instant(), &dateTime
)) {
1996 return CalendarDay(cx
, calendar
, dateTime
, args
.rval());
2000 * get Temporal.ZonedDateTime.prototype.day
2002 static bool ZonedDateTime_day(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2004 CallArgs args
= CallArgsFromVp(argc
, vp
);
2005 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_day
>(cx
, args
);
2009 * get Temporal.ZonedDateTime.prototype.hour
2011 static bool ZonedDateTime_hour(JSContext
* cx
, const CallArgs
& args
) {
2012 Rooted
<ZonedDateTime
> zonedDateTime(
2013 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2016 PlainDateTime dateTime
;
2017 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2018 zonedDateTime
.instant(), &dateTime
)) {
2023 args
.rval().setInt32(dateTime
.time
.hour
);
2028 * get Temporal.ZonedDateTime.prototype.hour
2030 static bool ZonedDateTime_hour(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2032 CallArgs args
= CallArgsFromVp(argc
, vp
);
2033 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_hour
>(cx
, args
);
2037 * get Temporal.ZonedDateTime.prototype.minute
2039 static bool ZonedDateTime_minute(JSContext
* cx
, const CallArgs
& args
) {
2040 Rooted
<ZonedDateTime
> zonedDateTime(
2041 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2044 PlainDateTime dateTime
;
2045 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2046 zonedDateTime
.instant(), &dateTime
)) {
2051 args
.rval().setInt32(dateTime
.time
.minute
);
2056 * get Temporal.ZonedDateTime.prototype.minute
2058 static bool ZonedDateTime_minute(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2060 CallArgs args
= CallArgsFromVp(argc
, vp
);
2061 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_minute
>(cx
, args
);
2065 * get Temporal.ZonedDateTime.prototype.second
2067 static bool ZonedDateTime_second(JSContext
* cx
, const CallArgs
& args
) {
2068 Rooted
<ZonedDateTime
> zonedDateTime(
2069 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2072 PlainDateTime dateTime
;
2073 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2074 zonedDateTime
.instant(), &dateTime
)) {
2079 args
.rval().setInt32(dateTime
.time
.second
);
2084 * get Temporal.ZonedDateTime.prototype.second
2086 static bool ZonedDateTime_second(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2088 CallArgs args
= CallArgsFromVp(argc
, vp
);
2089 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_second
>(cx
, args
);
2093 * get Temporal.ZonedDateTime.prototype.millisecond
2095 static bool ZonedDateTime_millisecond(JSContext
* cx
, const CallArgs
& args
) {
2096 Rooted
<ZonedDateTime
> zonedDateTime(
2097 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2100 PlainDateTime dateTime
;
2101 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2102 zonedDateTime
.instant(), &dateTime
)) {
2107 args
.rval().setInt32(dateTime
.time
.millisecond
);
2112 * get Temporal.ZonedDateTime.prototype.millisecond
2114 static bool ZonedDateTime_millisecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2116 CallArgs args
= CallArgsFromVp(argc
, vp
);
2117 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_millisecond
>(cx
,
2122 * get Temporal.ZonedDateTime.prototype.microsecond
2124 static bool ZonedDateTime_microsecond(JSContext
* cx
, const CallArgs
& args
) {
2125 Rooted
<ZonedDateTime
> zonedDateTime(
2126 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2129 PlainDateTime dateTime
;
2130 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2131 zonedDateTime
.instant(), &dateTime
)) {
2136 args
.rval().setInt32(dateTime
.time
.microsecond
);
2141 * get Temporal.ZonedDateTime.prototype.microsecond
2143 static bool ZonedDateTime_microsecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2145 CallArgs args
= CallArgsFromVp(argc
, vp
);
2146 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_microsecond
>(cx
,
2151 * get Temporal.ZonedDateTime.prototype.nanosecond
2153 static bool ZonedDateTime_nanosecond(JSContext
* cx
, const CallArgs
& args
) {
2154 Rooted
<ZonedDateTime
> zonedDateTime(
2155 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2158 PlainDateTime dateTime
;
2159 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2160 zonedDateTime
.instant(), &dateTime
)) {
2165 args
.rval().setInt32(dateTime
.time
.nanosecond
);
2170 * get Temporal.ZonedDateTime.prototype.nanosecond
2172 static bool ZonedDateTime_nanosecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2174 CallArgs args
= CallArgsFromVp(argc
, vp
);
2175 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_nanosecond
>(cx
,
2180 * get Temporal.ZonedDateTime.prototype.epochSeconds
2182 static bool ZonedDateTime_epochSeconds(JSContext
* cx
, const CallArgs
& args
) {
2183 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
2186 auto instant
= ToInstant(zonedDateTime
);
2189 args
.rval().setNumber(instant
.seconds
);
2194 * get Temporal.ZonedDateTime.prototype.epochSeconds
2196 static bool ZonedDateTime_epochSeconds(JSContext
* cx
, unsigned argc
,
2199 CallArgs args
= CallArgsFromVp(argc
, vp
);
2200 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_epochSeconds
>(
2205 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
2207 static bool ZonedDateTime_epochMilliseconds(JSContext
* cx
,
2208 const CallArgs
& args
) {
2209 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
2212 auto instant
= ToInstant(zonedDateTime
);
2215 args
.rval().setNumber(instant
.floorToMilliseconds());
2220 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
2222 static bool ZonedDateTime_epochMilliseconds(JSContext
* cx
, unsigned argc
,
2225 CallArgs args
= CallArgsFromVp(argc
, vp
);
2226 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_epochMilliseconds
>(
2231 * get Temporal.ZonedDateTime.prototype.epochMicroseconds
2233 static bool ZonedDateTime_epochMicroseconds(JSContext
* cx
,
2234 const CallArgs
& args
) {
2235 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
2238 auto instant
= ToInstant(zonedDateTime
);
2241 auto* microseconds
=
2242 BigInt::createFromInt64(cx
, instant
.floorToMicroseconds());
2243 if (!microseconds
) {
2248 args
.rval().setBigInt(microseconds
);
2253 * get Temporal.ZonedDateTime.prototype.epochMicroseconds
2255 static bool ZonedDateTime_epochMicroseconds(JSContext
* cx
, unsigned argc
,
2258 CallArgs args
= CallArgsFromVp(argc
, vp
);
2259 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_epochMicroseconds
>(
2264 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
2266 static bool ZonedDateTime_epochNanoseconds(JSContext
* cx
,
2267 const CallArgs
& args
) {
2268 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
2271 auto* nanoseconds
= ToEpochNanoseconds(cx
, ToInstant(zonedDateTime
));
2276 args
.rval().setBigInt(nanoseconds
);
2281 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
2283 static bool ZonedDateTime_epochNanoseconds(JSContext
* cx
, unsigned argc
,
2286 CallArgs args
= CallArgsFromVp(argc
, vp
);
2287 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_epochNanoseconds
>(
2292 * get Temporal.ZonedDateTime.prototype.dayOfWeek
2294 static bool ZonedDateTime_dayOfWeek(JSContext
* cx
, const CallArgs
& args
) {
2295 Rooted
<ZonedDateTime
> zonedDateTime(
2296 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2299 PlainDateTime dateTime
;
2300 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2301 zonedDateTime
.instant(), &dateTime
)) {
2306 return CalendarDayOfWeek(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
2310 * get Temporal.ZonedDateTime.prototype.dayOfWeek
2312 static bool ZonedDateTime_dayOfWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2314 CallArgs args
= CallArgsFromVp(argc
, vp
);
2315 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_dayOfWeek
>(cx
,
2320 * get Temporal.ZonedDateTime.prototype.dayOfYear
2322 static bool ZonedDateTime_dayOfYear(JSContext
* cx
, const CallArgs
& args
) {
2323 Rooted
<ZonedDateTime
> zonedDateTime(
2324 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2327 PlainDateTime dateTime
;
2328 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2329 zonedDateTime
.instant(), &dateTime
)) {
2334 return CalendarDayOfYear(cx
, zonedDateTime
.calendar(), dateTime
, args
.rval());
2338 * get Temporal.ZonedDateTime.prototype.dayOfYear
2340 static bool ZonedDateTime_dayOfYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2342 CallArgs args
= CallArgsFromVp(argc
, vp
);
2343 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_dayOfYear
>(cx
,
2348 * get Temporal.ZonedDateTime.prototype.weekOfYear
2350 static bool ZonedDateTime_weekOfYear(JSContext
* cx
, const CallArgs
& args
) {
2351 Rooted
<ZonedDateTime
> zonedDateTime(
2352 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2355 PlainDateTime dateTime
;
2356 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2357 zonedDateTime
.instant(), &dateTime
)) {
2362 return CalendarWeekOfYear(cx
, zonedDateTime
.calendar(), dateTime
,
2367 * get Temporal.ZonedDateTime.prototype.weekOfYear
2369 static bool ZonedDateTime_weekOfYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2371 CallArgs args
= CallArgsFromVp(argc
, vp
);
2372 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_weekOfYear
>(cx
,
2377 * get Temporal.ZonedDateTime.prototype.yearOfWeek
2379 static bool ZonedDateTime_yearOfWeek(JSContext
* cx
, const CallArgs
& args
) {
2380 Rooted
<ZonedDateTime
> zonedDateTime(
2381 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2384 PlainDateTime dateTime
;
2385 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2386 zonedDateTime
.instant(), &dateTime
)) {
2391 return CalendarYearOfWeek(cx
, zonedDateTime
.calendar(), dateTime
,
2396 * get Temporal.ZonedDateTime.prototype.yearOfWeek
2398 static bool ZonedDateTime_yearOfWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2400 CallArgs args
= CallArgsFromVp(argc
, vp
);
2401 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_yearOfWeek
>(cx
,
2406 * get Temporal.ZonedDateTime.prototype.hoursInDay
2408 static bool ZonedDateTime_hoursInDay(JSContext
* cx
, const CallArgs
& args
) {
2409 Rooted
<ZonedDateTime
> zonedDateTime(
2410 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2413 Rooted
<TimeZoneRecord
> timeZone(cx
);
2414 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
2416 TimeZoneMethod::GetOffsetNanosecondsFor
,
2417 TimeZoneMethod::GetPossibleInstantsFor
,
2424 const auto& instant
= zonedDateTime
.instant();
2427 PlainDateTime temporalDateTime
;
2428 if (!GetPlainDateTimeFor(cx
, timeZone
, instant
, &temporalDateTime
)) {
2433 const auto& date
= temporalDateTime
.date
;
2434 Rooted
<CalendarValue
> isoCalendar(cx
, CalendarValue(cx
->names().iso8601
));
2437 Rooted
<PlainDateTimeWithCalendar
> today(cx
);
2438 if (!CreateTemporalDateTime(cx
, {date
, {}}, isoCalendar
, &today
)) {
2443 auto tomorrowFields
= BalanceISODate(date
.year
, date
.month
, date
.day
+ 1);
2446 Rooted
<PlainDateTimeWithCalendar
> tomorrow(cx
);
2447 if (!CreateTemporalDateTime(cx
, {tomorrowFields
, {}}, isoCalendar
,
2453 Instant todayInstant
;
2454 if (!GetInstantFor(cx
, timeZone
, today
, TemporalDisambiguation::Compatible
,
2460 Instant tomorrowInstant
;
2461 if (!GetInstantFor(cx
, timeZone
, tomorrow
, TemporalDisambiguation::Compatible
,
2462 &tomorrowInstant
)) {
2467 auto diff
= tomorrowInstant
- todayInstant
;
2468 MOZ_ASSERT(IsValidInstantSpan(diff
));
2471 constexpr auto nsPerHour
= Int128
{ToNanoseconds(TemporalUnit::Hour
)};
2472 args
.rval().setNumber(FractionToDouble(diff
.toNanoseconds(), nsPerHour
));
2477 * get Temporal.ZonedDateTime.prototype.hoursInDay
2479 static bool ZonedDateTime_hoursInDay(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2481 CallArgs args
= CallArgsFromVp(argc
, vp
);
2482 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_hoursInDay
>(cx
,
2487 * get Temporal.ZonedDateTime.prototype.daysInWeek
2489 static bool ZonedDateTime_daysInWeek(JSContext
* cx
, const CallArgs
& args
) {
2490 Rooted
<ZonedDateTime
> zonedDateTime(
2491 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2494 PlainDateTime dateTime
;
2495 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2496 zonedDateTime
.instant(), &dateTime
)) {
2501 return CalendarDaysInWeek(cx
, zonedDateTime
.calendar(), dateTime
,
2506 * get Temporal.ZonedDateTime.prototype.daysInWeek
2508 static bool ZonedDateTime_daysInWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2510 CallArgs args
= CallArgsFromVp(argc
, vp
);
2511 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_daysInWeek
>(cx
,
2516 * get Temporal.ZonedDateTime.prototype.daysInMonth
2518 static bool ZonedDateTime_daysInMonth(JSContext
* cx
, const CallArgs
& args
) {
2519 Rooted
<ZonedDateTime
> zonedDateTime(
2520 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2523 PlainDateTime dateTime
;
2524 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2525 zonedDateTime
.instant(), &dateTime
)) {
2530 return CalendarDaysInMonth(cx
, zonedDateTime
.calendar(), dateTime
,
2535 * get Temporal.ZonedDateTime.prototype.daysInMonth
2537 static bool ZonedDateTime_daysInMonth(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2539 CallArgs args
= CallArgsFromVp(argc
, vp
);
2540 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_daysInMonth
>(cx
,
2545 * get Temporal.ZonedDateTime.prototype.daysInYear
2547 static bool ZonedDateTime_daysInYear(JSContext
* cx
, const CallArgs
& args
) {
2548 Rooted
<ZonedDateTime
> zonedDateTime(
2549 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2552 PlainDateTime dateTime
;
2553 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2554 zonedDateTime
.instant(), &dateTime
)) {
2559 return CalendarDaysInYear(cx
, zonedDateTime
.calendar(), dateTime
,
2564 * get Temporal.ZonedDateTime.prototype.daysInYear
2566 static bool ZonedDateTime_daysInYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2568 CallArgs args
= CallArgsFromVp(argc
, vp
);
2569 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_daysInYear
>(cx
,
2574 * get Temporal.ZonedDateTime.prototype.monthsInYear
2576 static bool ZonedDateTime_monthsInYear(JSContext
* cx
, const CallArgs
& args
) {
2577 Rooted
<ZonedDateTime
> zonedDateTime(
2578 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2581 PlainDateTime dateTime
;
2582 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2583 zonedDateTime
.instant(), &dateTime
)) {
2588 return CalendarMonthsInYear(cx
, zonedDateTime
.calendar(), dateTime
,
2593 * get Temporal.ZonedDateTime.prototype.monthsInYear
2595 static bool ZonedDateTime_monthsInYear(JSContext
* cx
, unsigned argc
,
2598 CallArgs args
= CallArgsFromVp(argc
, vp
);
2599 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_monthsInYear
>(
2604 * get Temporal.ZonedDateTime.prototype.inLeapYear
2606 static bool ZonedDateTime_inLeapYear(JSContext
* cx
, const CallArgs
& args
) {
2607 Rooted
<ZonedDateTime
> zonedDateTime(
2608 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2611 PlainDateTime dateTime
;
2612 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2613 zonedDateTime
.instant(), &dateTime
)) {
2618 return CalendarInLeapYear(cx
, zonedDateTime
.calendar(), dateTime
,
2623 * get Temporal.ZonedDateTime.prototype.inLeapYear
2625 static bool ZonedDateTime_inLeapYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2627 CallArgs args
= CallArgsFromVp(argc
, vp
);
2628 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_inLeapYear
>(cx
,
2633 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2635 static bool ZonedDateTime_offsetNanoseconds(JSContext
* cx
,
2636 const CallArgs
& args
) {
2637 Rooted
<ZonedDateTime
> zonedDateTime(
2638 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2641 auto timeZone
= zonedDateTime
.timeZone();
2644 const auto& instant
= zonedDateTime
.instant();
2647 int64_t offsetNanoseconds
;
2648 if (!GetOffsetNanosecondsFor(cx
, timeZone
, instant
, &offsetNanoseconds
)) {
2651 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
2653 args
.rval().setNumber(offsetNanoseconds
);
2658 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2660 static bool ZonedDateTime_offsetNanoseconds(JSContext
* cx
, unsigned argc
,
2663 CallArgs args
= CallArgsFromVp(argc
, vp
);
2664 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_offsetNanoseconds
>(
2669 * get Temporal.ZonedDateTime.prototype.offset
2671 static bool ZonedDateTime_offset(JSContext
* cx
, const CallArgs
& args
) {
2672 Rooted
<ZonedDateTime
> zonedDateTime(
2673 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2676 auto timeZone
= zonedDateTime
.timeZone();
2679 const auto& instant
= zonedDateTime
.instant();
2682 JSString
* str
= GetOffsetStringFor(cx
, timeZone
, instant
);
2687 args
.rval().setString(str
);
2692 * get Temporal.ZonedDateTime.prototype.offset
2694 static bool ZonedDateTime_offset(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2696 CallArgs args
= CallArgsFromVp(argc
, vp
);
2697 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_offset
>(cx
, args
);
2701 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2704 static bool ZonedDateTime_with(JSContext
* cx
, const CallArgs
& args
) {
2705 Rooted
<ZonedDateTime
> zonedDateTime(
2706 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2709 Rooted
<JSObject
*> temporalZonedDateTimeLike(
2711 RequireObjectArg(cx
, "temporalZonedDateTimeLike", "with", args
.get(0)));
2712 if (!temporalZonedDateTimeLike
) {
2717 if (!RejectTemporalLikeObject(cx
, temporalZonedDateTimeLike
)) {
2722 Rooted
<PlainObject
*> resolvedOptions(cx
);
2723 if (args
.hasDefined(1)) {
2724 Rooted
<JSObject
*> options(cx
,
2725 RequireObjectArg(cx
, "options", "with", args
[1]));
2729 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
2731 resolvedOptions
= NewPlainObjectWithProto(cx
, nullptr);
2733 if (!resolvedOptions
) {
2738 Rooted
<CalendarRecord
> calendar(cx
);
2739 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
2741 CalendarMethod::DateFromFields
,
2742 CalendarMethod::Fields
,
2743 CalendarMethod::MergeFields
,
2750 Rooted
<TimeZoneRecord
> timeZone(cx
);
2751 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
2753 TimeZoneMethod::GetOffsetNanosecondsFor
,
2754 TimeZoneMethod::GetPossibleInstantsFor
,
2761 const auto& instant
= zonedDateTime
.instant();
2764 int64_t offsetNanoseconds
;
2765 if (!GetOffsetNanosecondsFor(cx
, timeZone
, instant
, &offsetNanoseconds
)) {
2770 Rooted
<PlainDateTimeObject
*> dateTime(
2772 GetPlainDateTimeFor(cx
, instant
, calendar
.receiver(), offsetNanoseconds
));
2778 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
2779 if (!CalendarFields(cx
, calendar
,
2780 {CalendarField::Day
, CalendarField::Month
,
2781 CalendarField::MonthCode
, CalendarField::Year
},
2787 Rooted
<PlainObject
*> fields(cx
,
2788 PrepareTemporalFields(cx
, dateTime
, fieldNames
));
2795 using FieldName
= ImmutableTenuredPtr
<PropertyName
*> JSAtomState::*;
2800 {&JSAtomState::hour
, dateTime
->isoHour()},
2801 {&JSAtomState::minute
, dateTime
->isoMinute()},
2802 {&JSAtomState::second
, dateTime
->isoSecond()},
2803 {&JSAtomState::millisecond
, dateTime
->isoMillisecond()},
2804 {&JSAtomState::microsecond
, dateTime
->isoMicrosecond()},
2805 {&JSAtomState::nanosecond
, dateTime
->isoNanosecond()},
2808 Rooted
<Value
> timeFieldValue(cx
);
2809 for (const auto& timeField
: timeFields
) {
2810 Handle
<PropertyName
*> name
= cx
->names().*(timeField
.name
);
2811 timeFieldValue
.setInt32(timeField
.value
);
2813 if (!DefineDataProperty(cx
, fields
, name
, timeFieldValue
)) {
2819 JSString
* fieldsOffset
= FormatUTCOffsetNanoseconds(cx
, offsetNanoseconds
);
2820 if (!fieldsOffset
) {
2824 timeFieldValue
.setString(fieldsOffset
);
2825 if (!DefineDataProperty(cx
, fields
, cx
->names().offset
, timeFieldValue
)) {
2830 if (!AppendSorted(cx
, fieldNames
.get(),
2832 TemporalField::Hour
,
2833 TemporalField::Microsecond
,
2834 TemporalField::Millisecond
,
2835 TemporalField::Minute
,
2836 TemporalField::Nanosecond
,
2837 TemporalField::Offset
,
2838 TemporalField::Second
,
2844 Rooted
<PlainObject
*> partialZonedDateTime(
2846 PreparePartialTemporalFields(cx
, temporalZonedDateTimeLike
, fieldNames
));
2847 if (!partialZonedDateTime
) {
2852 Rooted
<JSObject
*> mergedFields(
2853 cx
, CalendarMergeFields(cx
, calendar
, fields
, partialZonedDateTime
));
2854 if (!mergedFields
) {
2859 fields
= PrepareTemporalFields(cx
, mergedFields
, fieldNames
,
2860 {TemporalField::Offset
});
2866 auto disambiguation
= TemporalDisambiguation::Compatible
;
2867 if (!ToTemporalDisambiguation(cx
, resolvedOptions
, &disambiguation
)) {
2872 auto offset
= TemporalOffset::Prefer
;
2873 if (!ToTemporalOffset(cx
, resolvedOptions
, &offset
)) {
2878 PlainDateTime dateTimeResult
;
2879 if (!InterpretTemporalDateTimeFields(cx
, calendar
, fields
, resolvedOptions
,
2885 Rooted
<Value
> offsetString(cx
);
2886 if (!GetProperty(cx
, fields
, fields
, cx
->names().offset
, &offsetString
)) {
2891 MOZ_ASSERT(offsetString
.isString());
2894 Rooted
<JSString
*> offsetStr(cx
, offsetString
.toString());
2895 int64_t newOffsetNanoseconds
;
2896 if (!ParseDateTimeUTCOffset(cx
, offsetStr
, &newOffsetNanoseconds
)) {
2901 Instant epochNanoseconds
;
2902 if (!InterpretISODateTimeOffset(
2903 cx
, dateTimeResult
, OffsetBehaviour::Option
, newOffsetNanoseconds
,
2904 timeZone
, disambiguation
, offset
, MatchBehaviour::MatchExactly
,
2905 &epochNanoseconds
)) {
2910 auto* result
= CreateTemporalZonedDateTime(
2911 cx
, epochNanoseconds
, timeZone
.receiver(), calendar
.receiver());
2916 args
.rval().setObject(*result
);
2921 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2924 static bool ZonedDateTime_with(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2926 CallArgs args
= CallArgsFromVp(argc
, vp
);
2927 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_with
>(cx
, args
);
2931 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2933 static bool ZonedDateTime_withPlainTime(JSContext
* cx
, const CallArgs
& args
) {
2934 Rooted
<ZonedDateTime
> zonedDateTime(
2935 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2938 PlainTime time
= {};
2939 if (args
.hasDefined(0)) {
2940 if (!ToTemporalTime(cx
, args
[0], &time
)) {
2946 Rooted
<TimeZoneRecord
> timeZone(cx
);
2947 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
2949 TimeZoneMethod::GetOffsetNanosecondsFor
,
2950 TimeZoneMethod::GetPossibleInstantsFor
,
2957 PlainDateTime plainDateTime
;
2958 if (!GetPlainDateTimeFor(cx
, timeZone
, zonedDateTime
.instant(),
2964 auto calendar
= zonedDateTime
.calendar();
2967 Rooted
<PlainDateTimeWithCalendar
> resultPlainDateTime(cx
);
2968 if (!CreateTemporalDateTime(cx
, {plainDateTime
.date
, time
}, calendar
,
2969 &resultPlainDateTime
)) {
2975 if (!GetInstantFor(cx
, timeZone
, resultPlainDateTime
,
2976 TemporalDisambiguation::Compatible
, &instant
)) {
2982 CreateTemporalZonedDateTime(cx
, instant
, timeZone
.receiver(), calendar
);
2987 args
.rval().setObject(*result
);
2992 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2994 static bool ZonedDateTime_withPlainTime(JSContext
* cx
, unsigned argc
,
2997 CallArgs args
= CallArgsFromVp(argc
, vp
);
2998 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withPlainTime
>(
3003 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3005 static bool ZonedDateTime_withPlainDate(JSContext
* cx
, const CallArgs
& args
) {
3006 Rooted
<ZonedDateTime
> zonedDateTime(
3007 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3010 Rooted
<PlainDateWithCalendar
> plainDate(cx
);
3011 if (!ToTemporalDate(cx
, args
.get(0), &plainDate
)) {
3014 auto date
= plainDate
.date();
3017 Rooted
<TimeZoneRecord
> timeZone(cx
);
3018 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
3020 TimeZoneMethod::GetOffsetNanosecondsFor
,
3021 TimeZoneMethod::GetPossibleInstantsFor
,
3028 PlainDateTime plainDateTime
;
3029 if (!GetPlainDateTimeFor(cx
, timeZone
, zonedDateTime
.instant(),
3035 Rooted
<CalendarValue
> calendar(cx
);
3036 if (!ConsolidateCalendars(cx
, zonedDateTime
.calendar(), plainDate
.calendar(),
3042 Rooted
<PlainDateTimeWithCalendar
> resultPlainDateTime(cx
);
3043 if (!CreateTemporalDateTime(cx
, {date
, plainDateTime
.time
}, calendar
,
3044 &resultPlainDateTime
)) {
3050 if (!GetInstantFor(cx
, timeZone
, resultPlainDateTime
,
3051 TemporalDisambiguation::Compatible
, &instant
)) {
3057 CreateTemporalZonedDateTime(cx
, instant
, timeZone
.receiver(), calendar
);
3062 args
.rval().setObject(*result
);
3067 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3069 static bool ZonedDateTime_withPlainDate(JSContext
* cx
, unsigned argc
,
3072 CallArgs args
= CallArgsFromVp(argc
, vp
);
3073 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withPlainDate
>(
3078 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3080 static bool ZonedDateTime_withTimeZone(JSContext
* cx
, const CallArgs
& args
) {
3081 Rooted
<ZonedDateTime
> zonedDateTime(
3082 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3085 Rooted
<TimeZoneValue
> timeZone(cx
);
3086 if (!ToTemporalTimeZone(cx
, args
.get(0), &timeZone
)) {
3091 auto* result
= CreateTemporalZonedDateTime(
3092 cx
, zonedDateTime
.instant(), timeZone
, zonedDateTime
.calendar());
3097 args
.rval().setObject(*result
);
3102 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3104 static bool ZonedDateTime_withTimeZone(JSContext
* cx
, unsigned argc
,
3107 CallArgs args
= CallArgsFromVp(argc
, vp
);
3108 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withTimeZone
>(
3113 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3115 static bool ZonedDateTime_withCalendar(JSContext
* cx
, const CallArgs
& args
) {
3116 Rooted
<ZonedDateTime
> zonedDateTime(
3117 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3120 Rooted
<CalendarValue
> calendar(cx
);
3121 if (!ToTemporalCalendar(cx
, args
.get(0), &calendar
)) {
3126 auto* result
= CreateTemporalZonedDateTime(
3127 cx
, zonedDateTime
.instant(), zonedDateTime
.timeZone(), calendar
);
3132 args
.rval().setObject(*result
);
3137 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3139 static bool ZonedDateTime_withCalendar(JSContext
* cx
, unsigned argc
,
3142 CallArgs args
= CallArgsFromVp(argc
, vp
);
3143 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withCalendar
>(
3148 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3150 static bool ZonedDateTime_add(JSContext
* cx
, const CallArgs
& args
) {
3151 return AddDurationToOrSubtractDurationFromZonedDateTime(
3152 cx
, ZonedDateTimeDuration::Add
, args
);
3156 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3158 static bool ZonedDateTime_add(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3160 CallArgs args
= CallArgsFromVp(argc
, vp
);
3161 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_add
>(cx
, args
);
3165 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3168 static bool ZonedDateTime_subtract(JSContext
* cx
, const CallArgs
& args
) {
3169 return AddDurationToOrSubtractDurationFromZonedDateTime(
3170 cx
, ZonedDateTimeDuration::Subtract
, args
);
3174 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3177 static bool ZonedDateTime_subtract(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3179 CallArgs args
= CallArgsFromVp(argc
, vp
);
3180 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_subtract
>(cx
,
3185 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3187 static bool ZonedDateTime_until(JSContext
* cx
, const CallArgs
& args
) {
3189 return DifferenceTemporalZonedDateTime(cx
, TemporalDifference::Until
, args
);
3193 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3195 static bool ZonedDateTime_until(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3197 CallArgs args
= CallArgsFromVp(argc
, vp
);
3198 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_until
>(cx
, args
);
3202 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3204 static bool ZonedDateTime_since(JSContext
* cx
, const CallArgs
& args
) {
3206 return DifferenceTemporalZonedDateTime(cx
, TemporalDifference::Since
, args
);
3210 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3212 static bool ZonedDateTime_since(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3214 CallArgs args
= CallArgsFromVp(argc
, vp
);
3215 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_since
>(cx
, args
);
3219 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3221 static bool ZonedDateTime_round(JSContext
* cx
, const CallArgs
& args
) {
3222 Rooted
<ZonedDateTime
> zonedDateTime(
3223 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3226 auto smallestUnit
= TemporalUnit::Auto
;
3227 auto roundingMode
= TemporalRoundingMode::HalfExpand
;
3228 auto roundingIncrement
= Increment
{1};
3229 if (args
.get(0).isString()) {
3230 // Step 4. (Not applicable in our implementation.)
3233 Rooted
<JSString
*> paramString(cx
, args
[0].toString());
3234 if (!GetTemporalUnit(cx
, paramString
, TemporalUnitKey::SmallestUnit
,
3235 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
3239 // Steps 6-8 and 10-12. (Implicit)
3242 Rooted
<JSObject
*> roundTo(
3243 cx
, RequireObjectArg(cx
, "roundTo", "round", args
.get(0)));
3249 if (!ToTemporalRoundingIncrement(cx
, roundTo
, &roundingIncrement
)) {
3254 if (!ToTemporalRoundingMode(cx
, roundTo
, &roundingMode
)) {
3259 if (!GetTemporalUnit(cx
, roundTo
, TemporalUnitKey::SmallestUnit
,
3260 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
3264 if (smallestUnit
== TemporalUnit::Auto
) {
3265 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3266 JSMSG_TEMPORAL_MISSING_OPTION
, "smallestUnit");
3270 MOZ_ASSERT(TemporalUnit::Day
<= smallestUnit
&&
3271 smallestUnit
<= TemporalUnit::Nanosecond
);
3274 auto maximum
= Increment
{1};
3275 bool inclusive
= true;
3276 if (smallestUnit
> TemporalUnit::Day
) {
3277 maximum
= MaximumTemporalDurationRoundingIncrement(smallestUnit
);
3282 if (!ValidateTemporalRoundingIncrement(cx
, roundingIncrement
, maximum
,
3289 if (smallestUnit
== TemporalUnit::Nanosecond
&&
3290 roundingIncrement
== Increment
{1}) {
3292 auto* result
= CreateTemporalZonedDateTime(cx
, zonedDateTime
.instant(),
3293 zonedDateTime
.timeZone(),
3294 zonedDateTime
.calendar());
3299 args
.rval().setObject(*result
);
3304 Rooted
<TimeZoneRecord
> timeZone(cx
);
3305 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
3307 TimeZoneMethod::GetOffsetNanosecondsFor
,
3308 TimeZoneMethod::GetPossibleInstantsFor
,
3314 // Step 16. (Reordered)
3315 auto calendar
= zonedDateTime
.calendar();
3318 int64_t offsetNanoseconds
;
3319 if (!GetOffsetNanosecondsFor(cx
, timeZone
, zonedDateTime
.instant(),
3320 &offsetNanoseconds
)) {
3323 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
3326 auto temporalDateTime
=
3327 GetPlainDateTimeFor(zonedDateTime
.instant(), offsetNanoseconds
);
3330 Rooted
<CalendarValue
> isoCalendar(cx
, CalendarValue(cx
->names().iso8601
));
3331 Rooted
<PlainDateTimeWithCalendar
> dtStart(cx
);
3332 if (!CreateTemporalDateTime(cx
, {temporalDateTime
.date
, {}}, isoCalendar
,
3339 if (!GetInstantFor(cx
, timeZone
, dtStart
, TemporalDisambiguation::Compatible
,
3346 if (!AddDaysToZonedDateTime(cx
, startNs
, ToPlainDateTime(dtStart
), timeZone
,
3347 calendar
, 1, &endNs
)) {
3350 MOZ_ASSERT(IsValidEpochInstant(endNs
));
3353 auto dayLengthNs
= endNs
- startNs
;
3354 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs
));
3357 if (dayLengthNs
<= InstantSpan
{}) {
3358 JS_ReportErrorNumberASCII(
3359 cx
, GetErrorMessage
, nullptr,
3360 JSMSG_TEMPORAL_ZONED_DATE_TIME_NON_POSITIVE_DAY_LENGTH
);
3365 PlainDateTime roundResult
;
3366 if (!RoundISODateTime(cx
, temporalDateTime
, roundingIncrement
, smallestUnit
,
3367 roundingMode
, dayLengthNs
, &roundResult
)) {
3372 Instant epochNanoseconds
;
3373 if (!InterpretISODateTimeOffset(
3374 cx
, roundResult
, OffsetBehaviour::Option
, offsetNanoseconds
, timeZone
,
3375 TemporalDisambiguation::Compatible
, TemporalOffset::Prefer
,
3376 MatchBehaviour::MatchExactly
, &epochNanoseconds
)) {
3381 auto* result
= CreateTemporalZonedDateTime(cx
, epochNanoseconds
,
3382 timeZone
.receiver(), calendar
);
3387 args
.rval().setObject(*result
);
3392 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3394 static bool ZonedDateTime_round(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3396 CallArgs args
= CallArgsFromVp(argc
, vp
);
3397 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_round
>(cx
, args
);
3401 * Temporal.ZonedDateTime.prototype.equals ( other )
3403 static bool ZonedDateTime_equals(JSContext
* cx
, const CallArgs
& args
) {
3404 Rooted
<ZonedDateTime
> zonedDateTime(
3405 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3408 Rooted
<ZonedDateTime
> other(cx
);
3409 if (!ToTemporalZonedDateTime(cx
, args
.get(0), &other
)) {
3414 bool equals
= zonedDateTime
.instant() == other
.instant();
3415 if (equals
&& !TimeZoneEquals(cx
, zonedDateTime
.timeZone(), other
.timeZone(),
3419 if (equals
&& !CalendarEquals(cx
, zonedDateTime
.calendar(), other
.calendar(),
3424 args
.rval().setBoolean(equals
);
3429 * Temporal.ZonedDateTime.prototype.equals ( other )
3431 static bool ZonedDateTime_equals(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3433 CallArgs args
= CallArgsFromVp(argc
, vp
);
3434 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_equals
>(cx
, args
);
3438 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3440 static bool ZonedDateTime_toString(JSContext
* cx
, const CallArgs
& args
) {
3441 Rooted
<ZonedDateTime
> zonedDateTime(
3442 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3444 SecondsStringPrecision precision
= {Precision::Auto(),
3445 TemporalUnit::Nanosecond
, Increment
{1}};
3446 auto roundingMode
= TemporalRoundingMode::Trunc
;
3447 auto showCalendar
= CalendarOption::Auto
;
3448 auto showTimeZone
= TimeZoneNameOption::Auto
;
3449 auto showOffset
= ShowOffsetOption::Auto
;
3450 if (args
.hasDefined(0)) {
3452 Rooted
<JSObject
*> options(
3453 cx
, RequireObjectArg(cx
, "options", "toString", args
[0]));
3459 if (!ToCalendarNameOption(cx
, options
, &showCalendar
)) {
3464 auto digits
= Precision::Auto();
3465 if (!ToFractionalSecondDigits(cx
, options
, &digits
)) {
3470 if (!ToShowOffsetOption(cx
, options
, &showOffset
)) {
3475 if (!ToTemporalRoundingMode(cx
, options
, &roundingMode
)) {
3480 auto smallestUnit
= TemporalUnit::Auto
;
3481 if (!GetTemporalUnit(cx
, options
, TemporalUnitKey::SmallestUnit
,
3482 TemporalUnitGroup::Time
, &smallestUnit
)) {
3487 if (smallestUnit
== TemporalUnit::Hour
) {
3488 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3489 JSMSG_TEMPORAL_INVALID_UNIT_OPTION
, "hour",
3495 if (!ToTimeZoneNameOption(cx
, options
, &showTimeZone
)) {
3500 precision
= ToSecondsStringPrecision(smallestUnit
, digits
);
3504 JSString
* str
= TemporalZonedDateTimeToString(
3505 cx
, zonedDateTime
, precision
.precision
, showCalendar
, showTimeZone
,
3506 showOffset
, precision
.increment
, precision
.unit
, roundingMode
);
3511 args
.rval().setString(str
);
3516 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3518 static bool ZonedDateTime_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3520 CallArgs args
= CallArgsFromVp(argc
, vp
);
3521 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toString
>(cx
,
3526 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3528 static bool ZonedDateTime_toLocaleString(JSContext
* cx
, const CallArgs
& args
) {
3529 Rooted
<ZonedDateTime
> zonedDateTime(
3530 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3533 JSString
* str
= TemporalZonedDateTimeToString(
3534 cx
, zonedDateTime
, Precision::Auto(), CalendarOption::Auto
,
3535 TimeZoneNameOption::Auto
, ShowOffsetOption::Auto
);
3540 args
.rval().setString(str
);
3545 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3547 static bool ZonedDateTime_toLocaleString(JSContext
* cx
, unsigned argc
,
3550 CallArgs args
= CallArgsFromVp(argc
, vp
);
3551 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toLocaleString
>(
3556 * Temporal.ZonedDateTime.prototype.toJSON ( )
3558 static bool ZonedDateTime_toJSON(JSContext
* cx
, const CallArgs
& args
) {
3559 Rooted
<ZonedDateTime
> zonedDateTime(
3560 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3563 JSString
* str
= TemporalZonedDateTimeToString(
3564 cx
, zonedDateTime
, Precision::Auto(), CalendarOption::Auto
,
3565 TimeZoneNameOption::Auto
, ShowOffsetOption::Auto
);
3570 args
.rval().setString(str
);
3575 * Temporal.ZonedDateTime.prototype.toJSON ( )
3577 static bool ZonedDateTime_toJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3579 CallArgs args
= CallArgsFromVp(argc
, vp
);
3580 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toJSON
>(cx
, args
);
3584 * Temporal.ZonedDateTime.prototype.valueOf ( )
3586 static bool ZonedDateTime_valueOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3587 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_CANT_CONVERT_TO
,
3588 "ZonedDateTime", "primitive type");
3593 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3595 static bool ZonedDateTime_startOfDay(JSContext
* cx
, const CallArgs
& args
) {
3596 Rooted
<ZonedDateTime
> zonedDateTime(
3597 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3600 Rooted
<TimeZoneRecord
> timeZone(cx
);
3601 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
3603 TimeZoneMethod::GetOffsetNanosecondsFor
,
3604 TimeZoneMethod::GetPossibleInstantsFor
,
3611 auto calendar
= zonedDateTime
.calendar();
3614 const auto& instant
= zonedDateTime
.instant();
3617 PlainDateTime temporalDateTime
;
3618 if (!GetPlainDateTimeFor(cx
, timeZone
, instant
, &temporalDateTime
)) {
3623 Rooted
<PlainDateTimeWithCalendar
> startDateTime(cx
);
3624 if (!CreateTemporalDateTime(cx
, {temporalDateTime
.date
, {}}, calendar
,
3630 Instant startInstant
;
3631 if (!GetInstantFor(cx
, timeZone
, startDateTime
,
3632 TemporalDisambiguation::Compatible
, &startInstant
)) {
3637 auto* result
= CreateTemporalZonedDateTime(cx
, startInstant
,
3638 timeZone
.receiver(), calendar
);
3643 args
.rval().setObject(*result
);
3648 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3650 static bool ZonedDateTime_startOfDay(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3652 CallArgs args
= CallArgsFromVp(argc
, vp
);
3653 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_startOfDay
>(cx
,
3658 * Temporal.ZonedDateTime.prototype.toInstant ( )
3660 static bool ZonedDateTime_toInstant(JSContext
* cx
, const CallArgs
& args
) {
3661 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
3662 auto instant
= ToInstant(zonedDateTime
);
3665 auto* result
= CreateTemporalInstant(cx
, instant
);
3670 args
.rval().setObject(*result
);
3675 * Temporal.ZonedDateTime.prototype.toInstant ( )
3677 static bool ZonedDateTime_toInstant(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3679 CallArgs args
= CallArgsFromVp(argc
, vp
);
3680 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toInstant
>(cx
,
3685 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3687 static bool ZonedDateTime_toPlainDate(JSContext
* cx
, const CallArgs
& args
) {
3688 Rooted
<ZonedDateTime
> zonedDateTime(
3689 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3692 PlainDateTime temporalDateTime
;
3693 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
3694 zonedDateTime
.instant(), &temporalDateTime
)) {
3700 CreateTemporalDate(cx
, temporalDateTime
.date
, zonedDateTime
.calendar());
3705 args
.rval().setObject(*result
);
3710 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3712 static bool ZonedDateTime_toPlainDate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3714 CallArgs args
= CallArgsFromVp(argc
, vp
);
3715 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainDate
>(cx
,
3720 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3722 static bool ZonedDateTime_toPlainTime(JSContext
* cx
, const CallArgs
& args
) {
3723 Rooted
<ZonedDateTime
> zonedDateTime(
3724 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3727 PlainDateTime temporalDateTime
;
3728 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
3729 zonedDateTime
.instant(), &temporalDateTime
)) {
3734 auto* result
= CreateTemporalTime(cx
, temporalDateTime
.time
);
3739 args
.rval().setObject(*result
);
3744 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3746 static bool ZonedDateTime_toPlainTime(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3748 CallArgs args
= CallArgsFromVp(argc
, vp
);
3749 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainTime
>(cx
,
3754 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3756 static bool ZonedDateTime_toPlainDateTime(JSContext
* cx
, const CallArgs
& args
) {
3757 Rooted
<ZonedDateTime
> zonedDateTime(
3758 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3762 GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(), zonedDateTime
.instant(),
3763 zonedDateTime
.calendar());
3768 args
.rval().setObject(*result
);
3773 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3775 static bool ZonedDateTime_toPlainDateTime(JSContext
* cx
, unsigned argc
,
3778 CallArgs args
= CallArgsFromVp(argc
, vp
);
3779 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainDateTime
>(
3784 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3786 static bool ZonedDateTime_toPlainYearMonth(JSContext
* cx
,
3787 const CallArgs
& args
) {
3788 Rooted
<ZonedDateTime
> zonedDateTime(
3789 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3792 Rooted
<CalendarRecord
> calendar(cx
);
3793 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
3795 CalendarMethod::Fields
,
3796 CalendarMethod::YearMonthFromFields
,
3803 Rooted
<PlainDateTimeObject
*> temporalDateTime(
3805 GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(), zonedDateTime
.instant(),
3806 zonedDateTime
.calendar()));
3807 if (!temporalDateTime
) {
3812 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
3813 if (!CalendarFields(cx
, calendar
,
3814 {CalendarField::MonthCode
, CalendarField::Year
},
3820 Rooted
<PlainObject
*> fields(
3821 cx
, PrepareTemporalFields(cx
, temporalDateTime
, fieldNames
));
3827 auto result
= CalendarYearMonthFromFields(cx
, calendar
, fields
);
3832 args
.rval().setObject(*result
);
3837 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3839 static bool ZonedDateTime_toPlainYearMonth(JSContext
* cx
, unsigned argc
,
3842 CallArgs args
= CallArgsFromVp(argc
, vp
);
3843 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainYearMonth
>(
3848 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3850 static bool ZonedDateTime_toPlainMonthDay(JSContext
* cx
, const CallArgs
& args
) {
3851 Rooted
<ZonedDateTime
> zonedDateTime(
3852 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3855 Rooted
<CalendarRecord
> calendar(cx
);
3856 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
3858 CalendarMethod::Fields
,
3859 CalendarMethod::MonthDayFromFields
,
3866 Rooted
<PlainDateTimeObject
*> temporalDateTime(
3868 GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(), zonedDateTime
.instant(),
3869 zonedDateTime
.calendar()));
3870 if (!temporalDateTime
) {
3875 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
3876 if (!CalendarFields(cx
, calendar
,
3877 {CalendarField::Day
, CalendarField::MonthCode
},
3883 Rooted
<PlainObject
*> fields(
3884 cx
, PrepareTemporalFields(cx
, temporalDateTime
, fieldNames
));
3890 auto result
= CalendarMonthDayFromFields(cx
, calendar
, fields
);
3895 args
.rval().setObject(*result
);
3900 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3902 static bool ZonedDateTime_toPlainMonthDay(JSContext
* cx
, unsigned argc
,
3905 CallArgs args
= CallArgsFromVp(argc
, vp
);
3906 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainMonthDay
>(
3911 * Temporal.ZonedDateTime.prototype.getISOFields ( )
3913 static bool ZonedDateTime_getISOFields(JSContext
* cx
, const CallArgs
& args
) {
3914 Rooted
<ZonedDateTime
> zonedDateTime(
3915 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3918 Rooted
<IdValueVector
> fields(cx
, IdValueVector(cx
));
3921 const auto& instant
= zonedDateTime
.instant();
3924 auto calendar
= zonedDateTime
.calendar();
3927 auto timeZone
= zonedDateTime
.timeZone();
3930 int64_t offsetNanoseconds
;
3931 if (!GetOffsetNanosecondsFor(cx
, timeZone
, instant
, &offsetNanoseconds
)) {
3936 auto temporalDateTime
= GetPlainDateTimeFor(instant
, offsetNanoseconds
);
3939 Rooted
<JSString
*> offset(cx
,
3940 FormatUTCOffsetNanoseconds(cx
, offsetNanoseconds
));
3946 if (!fields
.emplaceBack(NameToId(cx
->names().calendar
), calendar
.toValue())) {
3951 if (!fields
.emplaceBack(NameToId(cx
->names().isoDay
),
3952 Int32Value(temporalDateTime
.date
.day
))) {
3957 if (!fields
.emplaceBack(NameToId(cx
->names().isoHour
),
3958 Int32Value(temporalDateTime
.time
.hour
))) {
3963 if (!fields
.emplaceBack(NameToId(cx
->names().isoMicrosecond
),
3964 Int32Value(temporalDateTime
.time
.microsecond
))) {
3969 if (!fields
.emplaceBack(NameToId(cx
->names().isoMillisecond
),
3970 Int32Value(temporalDateTime
.time
.millisecond
))) {
3975 if (!fields
.emplaceBack(NameToId(cx
->names().isoMinute
),
3976 Int32Value(temporalDateTime
.time
.minute
))) {
3981 if (!fields
.emplaceBack(NameToId(cx
->names().isoMonth
),
3982 Int32Value(temporalDateTime
.date
.month
))) {
3987 if (!fields
.emplaceBack(NameToId(cx
->names().isoNanosecond
),
3988 Int32Value(temporalDateTime
.time
.nanosecond
))) {
3993 if (!fields
.emplaceBack(NameToId(cx
->names().isoSecond
),
3994 Int32Value(temporalDateTime
.time
.second
))) {
3999 if (!fields
.emplaceBack(NameToId(cx
->names().isoYear
),
4000 Int32Value(temporalDateTime
.date
.year
))) {
4005 if (!fields
.emplaceBack(NameToId(cx
->names().offset
), StringValue(offset
))) {
4010 if (!fields
.emplaceBack(NameToId(cx
->names().timeZone
), timeZone
.toValue())) {
4015 auto* obj
= NewPlainObjectWithUniqueNames(cx
, fields
);
4020 args
.rval().setObject(*obj
);
4025 * Temporal.ZonedDateTime.prototype.getISOFields ( )
4027 static bool ZonedDateTime_getISOFields(JSContext
* cx
, unsigned argc
,
4030 CallArgs args
= CallArgsFromVp(argc
, vp
);
4031 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_getISOFields
>(
4036 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4038 static bool ZonedDateTime_getCalendar(JSContext
* cx
, const CallArgs
& args
) {
4039 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
4040 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
4043 auto* obj
= ToTemporalCalendarObject(cx
, calendar
);
4048 args
.rval().setObject(*obj
);
4053 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4055 static bool ZonedDateTime_getCalendar(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4057 CallArgs args
= CallArgsFromVp(argc
, vp
);
4058 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_getCalendar
>(cx
,
4063 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4065 static bool ZonedDateTime_getTimeZone(JSContext
* cx
, const CallArgs
& args
) {
4066 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
4067 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
4070 auto* obj
= ToTemporalTimeZoneObject(cx
, timeZone
);
4075 args
.rval().setObject(*obj
);
4080 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4082 static bool ZonedDateTime_getTimeZone(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4084 CallArgs args
= CallArgsFromVp(argc
, vp
);
4085 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_getTimeZone
>(cx
,
4089 const JSClass
ZonedDateTimeObject::class_
= {
4090 "Temporal.ZonedDateTime",
4091 JSCLASS_HAS_RESERVED_SLOTS(ZonedDateTimeObject::SLOT_COUNT
) |
4092 JSCLASS_HAS_CACHED_PROTO(JSProto_ZonedDateTime
),
4094 &ZonedDateTimeObject::classSpec_
,
4097 const JSClass
& ZonedDateTimeObject::protoClass_
= PlainObject::class_
;
4099 static const JSFunctionSpec ZonedDateTime_methods
[] = {
4100 JS_FN("from", ZonedDateTime_from
, 1, 0),
4101 JS_FN("compare", ZonedDateTime_compare
, 2, 0),
4105 static const JSFunctionSpec ZonedDateTime_prototype_methods
[] = {
4106 JS_FN("with", ZonedDateTime_with
, 1, 0),
4107 JS_FN("withPlainTime", ZonedDateTime_withPlainTime
, 0, 0),
4108 JS_FN("withPlainDate", ZonedDateTime_withPlainDate
, 1, 0),
4109 JS_FN("withTimeZone", ZonedDateTime_withTimeZone
, 1, 0),
4110 JS_FN("withCalendar", ZonedDateTime_withCalendar
, 1, 0),
4111 JS_FN("add", ZonedDateTime_add
, 1, 0),
4112 JS_FN("subtract", ZonedDateTime_subtract
, 1, 0),
4113 JS_FN("until", ZonedDateTime_until
, 1, 0),
4114 JS_FN("since", ZonedDateTime_since
, 1, 0),
4115 JS_FN("round", ZonedDateTime_round
, 1, 0),
4116 JS_FN("equals", ZonedDateTime_equals
, 1, 0),
4117 JS_FN("toString", ZonedDateTime_toString
, 0, 0),
4118 JS_FN("toLocaleString", ZonedDateTime_toLocaleString
, 0, 0),
4119 JS_FN("toJSON", ZonedDateTime_toJSON
, 0, 0),
4120 JS_FN("valueOf", ZonedDateTime_valueOf
, 0, 0),
4121 JS_FN("startOfDay", ZonedDateTime_startOfDay
, 0, 0),
4122 JS_FN("toInstant", ZonedDateTime_toInstant
, 0, 0),
4123 JS_FN("toPlainDate", ZonedDateTime_toPlainDate
, 0, 0),
4124 JS_FN("toPlainTime", ZonedDateTime_toPlainTime
, 0, 0),
4125 JS_FN("toPlainDateTime", ZonedDateTime_toPlainDateTime
, 0, 0),
4126 JS_FN("toPlainYearMonth", ZonedDateTime_toPlainYearMonth
, 0, 0),
4127 JS_FN("toPlainMonthDay", ZonedDateTime_toPlainMonthDay
, 0, 0),
4128 JS_FN("getISOFields", ZonedDateTime_getISOFields
, 0, 0),
4129 JS_FN("getCalendar", ZonedDateTime_getCalendar
, 0, 0),
4130 JS_FN("getTimeZone", ZonedDateTime_getTimeZone
, 0, 0),
4134 static const JSPropertySpec ZonedDateTime_prototype_properties
[] = {
4135 JS_PSG("calendarId", ZonedDateTime_calendarId
, 0),
4136 JS_PSG("timeZoneId", ZonedDateTime_timeZoneId
, 0),
4137 JS_PSG("year", ZonedDateTime_year
, 0),
4138 JS_PSG("month", ZonedDateTime_month
, 0),
4139 JS_PSG("monthCode", ZonedDateTime_monthCode
, 0),
4140 JS_PSG("day", ZonedDateTime_day
, 0),
4141 JS_PSG("hour", ZonedDateTime_hour
, 0),
4142 JS_PSG("minute", ZonedDateTime_minute
, 0),
4143 JS_PSG("second", ZonedDateTime_second
, 0),
4144 JS_PSG("millisecond", ZonedDateTime_millisecond
, 0),
4145 JS_PSG("microsecond", ZonedDateTime_microsecond
, 0),
4146 JS_PSG("nanosecond", ZonedDateTime_nanosecond
, 0),
4147 JS_PSG("epochSeconds", ZonedDateTime_epochSeconds
, 0),
4148 JS_PSG("epochMilliseconds", ZonedDateTime_epochMilliseconds
, 0),
4149 JS_PSG("epochMicroseconds", ZonedDateTime_epochMicroseconds
, 0),
4150 JS_PSG("epochNanoseconds", ZonedDateTime_epochNanoseconds
, 0),
4151 JS_PSG("dayOfWeek", ZonedDateTime_dayOfWeek
, 0),
4152 JS_PSG("dayOfYear", ZonedDateTime_dayOfYear
, 0),
4153 JS_PSG("weekOfYear", ZonedDateTime_weekOfYear
, 0),
4154 JS_PSG("yearOfWeek", ZonedDateTime_yearOfWeek
, 0),
4155 JS_PSG("hoursInDay", ZonedDateTime_hoursInDay
, 0),
4156 JS_PSG("daysInWeek", ZonedDateTime_daysInWeek
, 0),
4157 JS_PSG("daysInMonth", ZonedDateTime_daysInMonth
, 0),
4158 JS_PSG("daysInYear", ZonedDateTime_daysInYear
, 0),
4159 JS_PSG("monthsInYear", ZonedDateTime_monthsInYear
, 0),
4160 JS_PSG("inLeapYear", ZonedDateTime_inLeapYear
, 0),
4161 JS_PSG("offsetNanoseconds", ZonedDateTime_offsetNanoseconds
, 0),
4162 JS_PSG("offset", ZonedDateTime_offset
, 0),
4163 JS_STRING_SYM_PS(toStringTag
, "Temporal.ZonedDateTime", JSPROP_READONLY
),
4167 const ClassSpec
ZonedDateTimeObject::classSpec_
= {
4168 GenericCreateConstructor
<ZonedDateTimeConstructor
, 2,
4169 gc::AllocKind::FUNCTION
>,
4170 GenericCreatePrototype
<ZonedDateTimeObject
>,
4171 ZonedDateTime_methods
,
4173 ZonedDateTime_prototype_methods
,
4174 ZonedDateTime_prototype_properties
,
4176 ClassSpec::DontDefineConstructor
,