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 diffNs
= tomorrowInstant
- todayInstant
;
2468 MOZ_ASSERT(IsValidInstantSpan(diffNs
));
2471 constexpr int32_t secPerHour
= 60 * 60;
2472 constexpr int64_t nsPerSec
= ToNanoseconds(TemporalUnit::Second
);
2473 constexpr double nsPerHour
= ToNanoseconds(TemporalUnit::Hour
);
2475 int64_t hours
= diffNs
.seconds
/ secPerHour
;
2476 int64_t seconds
= diffNs
.seconds
% secPerHour
;
2477 int64_t nanoseconds
= seconds
* nsPerSec
+ diffNs
.nanoseconds
;
2479 double result
= double(hours
) + double(nanoseconds
) / nsPerHour
;
2480 args
.rval().setNumber(result
);
2485 * get Temporal.ZonedDateTime.prototype.hoursInDay
2487 static bool ZonedDateTime_hoursInDay(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2489 CallArgs args
= CallArgsFromVp(argc
, vp
);
2490 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_hoursInDay
>(cx
,
2495 * get Temporal.ZonedDateTime.prototype.daysInWeek
2497 static bool ZonedDateTime_daysInWeek(JSContext
* cx
, const CallArgs
& args
) {
2498 Rooted
<ZonedDateTime
> zonedDateTime(
2499 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2502 PlainDateTime dateTime
;
2503 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2504 zonedDateTime
.instant(), &dateTime
)) {
2509 return CalendarDaysInWeek(cx
, zonedDateTime
.calendar(), dateTime
,
2514 * get Temporal.ZonedDateTime.prototype.daysInWeek
2516 static bool ZonedDateTime_daysInWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2518 CallArgs args
= CallArgsFromVp(argc
, vp
);
2519 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_daysInWeek
>(cx
,
2524 * get Temporal.ZonedDateTime.prototype.daysInMonth
2526 static bool ZonedDateTime_daysInMonth(JSContext
* cx
, const CallArgs
& args
) {
2527 Rooted
<ZonedDateTime
> zonedDateTime(
2528 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2531 PlainDateTime dateTime
;
2532 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2533 zonedDateTime
.instant(), &dateTime
)) {
2538 return CalendarDaysInMonth(cx
, zonedDateTime
.calendar(), dateTime
,
2543 * get Temporal.ZonedDateTime.prototype.daysInMonth
2545 static bool ZonedDateTime_daysInMonth(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2547 CallArgs args
= CallArgsFromVp(argc
, vp
);
2548 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_daysInMonth
>(cx
,
2553 * get Temporal.ZonedDateTime.prototype.daysInYear
2555 static bool ZonedDateTime_daysInYear(JSContext
* cx
, const CallArgs
& args
) {
2556 Rooted
<ZonedDateTime
> zonedDateTime(
2557 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2560 PlainDateTime dateTime
;
2561 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2562 zonedDateTime
.instant(), &dateTime
)) {
2567 return CalendarDaysInYear(cx
, zonedDateTime
.calendar(), dateTime
,
2572 * get Temporal.ZonedDateTime.prototype.daysInYear
2574 static bool ZonedDateTime_daysInYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2576 CallArgs args
= CallArgsFromVp(argc
, vp
);
2577 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_daysInYear
>(cx
,
2582 * get Temporal.ZonedDateTime.prototype.monthsInYear
2584 static bool ZonedDateTime_monthsInYear(JSContext
* cx
, const CallArgs
& args
) {
2585 Rooted
<ZonedDateTime
> zonedDateTime(
2586 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2589 PlainDateTime dateTime
;
2590 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2591 zonedDateTime
.instant(), &dateTime
)) {
2596 return CalendarMonthsInYear(cx
, zonedDateTime
.calendar(), dateTime
,
2601 * get Temporal.ZonedDateTime.prototype.monthsInYear
2603 static bool ZonedDateTime_monthsInYear(JSContext
* cx
, unsigned argc
,
2606 CallArgs args
= CallArgsFromVp(argc
, vp
);
2607 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_monthsInYear
>(
2612 * get Temporal.ZonedDateTime.prototype.inLeapYear
2614 static bool ZonedDateTime_inLeapYear(JSContext
* cx
, const CallArgs
& args
) {
2615 Rooted
<ZonedDateTime
> zonedDateTime(
2616 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2619 PlainDateTime dateTime
;
2620 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
2621 zonedDateTime
.instant(), &dateTime
)) {
2626 return CalendarInLeapYear(cx
, zonedDateTime
.calendar(), dateTime
,
2631 * get Temporal.ZonedDateTime.prototype.inLeapYear
2633 static bool ZonedDateTime_inLeapYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2635 CallArgs args
= CallArgsFromVp(argc
, vp
);
2636 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_inLeapYear
>(cx
,
2641 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2643 static bool ZonedDateTime_offsetNanoseconds(JSContext
* cx
,
2644 const CallArgs
& args
) {
2645 Rooted
<ZonedDateTime
> zonedDateTime(
2646 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2649 auto timeZone
= zonedDateTime
.timeZone();
2652 const auto& instant
= zonedDateTime
.instant();
2655 int64_t offsetNanoseconds
;
2656 if (!GetOffsetNanosecondsFor(cx
, timeZone
, instant
, &offsetNanoseconds
)) {
2659 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
2661 args
.rval().setNumber(offsetNanoseconds
);
2666 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2668 static bool ZonedDateTime_offsetNanoseconds(JSContext
* cx
, unsigned argc
,
2671 CallArgs args
= CallArgsFromVp(argc
, vp
);
2672 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_offsetNanoseconds
>(
2677 * get Temporal.ZonedDateTime.prototype.offset
2679 static bool ZonedDateTime_offset(JSContext
* cx
, const CallArgs
& args
) {
2680 Rooted
<ZonedDateTime
> zonedDateTime(
2681 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2684 auto timeZone
= zonedDateTime
.timeZone();
2687 const auto& instant
= zonedDateTime
.instant();
2690 JSString
* str
= GetOffsetStringFor(cx
, timeZone
, instant
);
2695 args
.rval().setString(str
);
2700 * get Temporal.ZonedDateTime.prototype.offset
2702 static bool ZonedDateTime_offset(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2704 CallArgs args
= CallArgsFromVp(argc
, vp
);
2705 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_offset
>(cx
, args
);
2709 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2712 static bool ZonedDateTime_with(JSContext
* cx
, const CallArgs
& args
) {
2713 Rooted
<ZonedDateTime
> zonedDateTime(
2714 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2717 Rooted
<JSObject
*> temporalZonedDateTimeLike(
2719 RequireObjectArg(cx
, "temporalZonedDateTimeLike", "with", args
.get(0)));
2720 if (!temporalZonedDateTimeLike
) {
2725 if (!RejectTemporalLikeObject(cx
, temporalZonedDateTimeLike
)) {
2730 Rooted
<PlainObject
*> resolvedOptions(cx
);
2731 if (args
.hasDefined(1)) {
2732 Rooted
<JSObject
*> options(cx
,
2733 RequireObjectArg(cx
, "options", "with", args
[1]));
2737 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
2739 resolvedOptions
= NewPlainObjectWithProto(cx
, nullptr);
2741 if (!resolvedOptions
) {
2746 Rooted
<CalendarRecord
> calendar(cx
);
2747 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
2749 CalendarMethod::DateFromFields
,
2750 CalendarMethod::Fields
,
2751 CalendarMethod::MergeFields
,
2758 Rooted
<TimeZoneRecord
> timeZone(cx
);
2759 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
2761 TimeZoneMethod::GetOffsetNanosecondsFor
,
2762 TimeZoneMethod::GetPossibleInstantsFor
,
2769 const auto& instant
= zonedDateTime
.instant();
2772 int64_t offsetNanoseconds
;
2773 if (!GetOffsetNanosecondsFor(cx
, timeZone
, instant
, &offsetNanoseconds
)) {
2778 Rooted
<PlainDateTimeObject
*> dateTime(
2780 GetPlainDateTimeFor(cx
, instant
, calendar
.receiver(), offsetNanoseconds
));
2786 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
2787 if (!CalendarFields(cx
, calendar
,
2788 {CalendarField::Day
, CalendarField::Month
,
2789 CalendarField::MonthCode
, CalendarField::Year
},
2795 Rooted
<PlainObject
*> fields(cx
,
2796 PrepareTemporalFields(cx
, dateTime
, fieldNames
));
2803 using FieldName
= ImmutableTenuredPtr
<PropertyName
*> JSAtomState::*;
2808 {&JSAtomState::hour
, dateTime
->isoHour()},
2809 {&JSAtomState::minute
, dateTime
->isoMinute()},
2810 {&JSAtomState::second
, dateTime
->isoSecond()},
2811 {&JSAtomState::millisecond
, dateTime
->isoMillisecond()},
2812 {&JSAtomState::microsecond
, dateTime
->isoMicrosecond()},
2813 {&JSAtomState::nanosecond
, dateTime
->isoNanosecond()},
2816 Rooted
<Value
> timeFieldValue(cx
);
2817 for (const auto& timeField
: timeFields
) {
2818 Handle
<PropertyName
*> name
= cx
->names().*(timeField
.name
);
2819 timeFieldValue
.setInt32(timeField
.value
);
2821 if (!DefineDataProperty(cx
, fields
, name
, timeFieldValue
)) {
2827 JSString
* fieldsOffset
= FormatUTCOffsetNanoseconds(cx
, offsetNanoseconds
);
2828 if (!fieldsOffset
) {
2832 timeFieldValue
.setString(fieldsOffset
);
2833 if (!DefineDataProperty(cx
, fields
, cx
->names().offset
, timeFieldValue
)) {
2838 if (!AppendSorted(cx
, fieldNames
.get(),
2840 TemporalField::Hour
,
2841 TemporalField::Microsecond
,
2842 TemporalField::Millisecond
,
2843 TemporalField::Minute
,
2844 TemporalField::Nanosecond
,
2845 TemporalField::Offset
,
2846 TemporalField::Second
,
2852 Rooted
<PlainObject
*> partialZonedDateTime(
2854 PreparePartialTemporalFields(cx
, temporalZonedDateTimeLike
, fieldNames
));
2855 if (!partialZonedDateTime
) {
2860 Rooted
<JSObject
*> mergedFields(
2861 cx
, CalendarMergeFields(cx
, calendar
, fields
, partialZonedDateTime
));
2862 if (!mergedFields
) {
2867 fields
= PrepareTemporalFields(cx
, mergedFields
, fieldNames
,
2868 {TemporalField::Offset
});
2874 auto disambiguation
= TemporalDisambiguation::Compatible
;
2875 if (!ToTemporalDisambiguation(cx
, resolvedOptions
, &disambiguation
)) {
2880 auto offset
= TemporalOffset::Prefer
;
2881 if (!ToTemporalOffset(cx
, resolvedOptions
, &offset
)) {
2886 PlainDateTime dateTimeResult
;
2887 if (!InterpretTemporalDateTimeFields(cx
, calendar
, fields
, resolvedOptions
,
2893 Rooted
<Value
> offsetString(cx
);
2894 if (!GetProperty(cx
, fields
, fields
, cx
->names().offset
, &offsetString
)) {
2899 MOZ_ASSERT(offsetString
.isString());
2902 Rooted
<JSString
*> offsetStr(cx
, offsetString
.toString());
2903 int64_t newOffsetNanoseconds
;
2904 if (!ParseDateTimeUTCOffset(cx
, offsetStr
, &newOffsetNanoseconds
)) {
2909 Instant epochNanoseconds
;
2910 if (!InterpretISODateTimeOffset(
2911 cx
, dateTimeResult
, OffsetBehaviour::Option
, newOffsetNanoseconds
,
2912 timeZone
, disambiguation
, offset
, MatchBehaviour::MatchExactly
,
2913 &epochNanoseconds
)) {
2918 auto* result
= CreateTemporalZonedDateTime(
2919 cx
, epochNanoseconds
, timeZone
.receiver(), calendar
.receiver());
2924 args
.rval().setObject(*result
);
2929 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2932 static bool ZonedDateTime_with(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2934 CallArgs args
= CallArgsFromVp(argc
, vp
);
2935 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_with
>(cx
, args
);
2939 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2941 static bool ZonedDateTime_withPlainTime(JSContext
* cx
, const CallArgs
& args
) {
2942 Rooted
<ZonedDateTime
> zonedDateTime(
2943 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
2946 PlainTime time
= {};
2947 if (args
.hasDefined(0)) {
2948 if (!ToTemporalTime(cx
, args
[0], &time
)) {
2954 Rooted
<TimeZoneRecord
> timeZone(cx
);
2955 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
2957 TimeZoneMethod::GetOffsetNanosecondsFor
,
2958 TimeZoneMethod::GetPossibleInstantsFor
,
2965 PlainDateTime plainDateTime
;
2966 if (!GetPlainDateTimeFor(cx
, timeZone
, zonedDateTime
.instant(),
2972 auto calendar
= zonedDateTime
.calendar();
2975 Rooted
<PlainDateTimeWithCalendar
> resultPlainDateTime(cx
);
2976 if (!CreateTemporalDateTime(cx
, {plainDateTime
.date
, time
}, calendar
,
2977 &resultPlainDateTime
)) {
2983 if (!GetInstantFor(cx
, timeZone
, resultPlainDateTime
,
2984 TemporalDisambiguation::Compatible
, &instant
)) {
2990 CreateTemporalZonedDateTime(cx
, instant
, timeZone
.receiver(), calendar
);
2995 args
.rval().setObject(*result
);
3000 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
3002 static bool ZonedDateTime_withPlainTime(JSContext
* cx
, unsigned argc
,
3005 CallArgs args
= CallArgsFromVp(argc
, vp
);
3006 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withPlainTime
>(
3011 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3013 static bool ZonedDateTime_withPlainDate(JSContext
* cx
, const CallArgs
& args
) {
3014 Rooted
<ZonedDateTime
> zonedDateTime(
3015 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3018 Rooted
<PlainDateWithCalendar
> plainDate(cx
);
3019 if (!ToTemporalDate(cx
, args
.get(0), &plainDate
)) {
3022 auto date
= plainDate
.date();
3025 Rooted
<TimeZoneRecord
> timeZone(cx
);
3026 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
3028 TimeZoneMethod::GetOffsetNanosecondsFor
,
3029 TimeZoneMethod::GetPossibleInstantsFor
,
3036 PlainDateTime plainDateTime
;
3037 if (!GetPlainDateTimeFor(cx
, timeZone
, zonedDateTime
.instant(),
3043 Rooted
<CalendarValue
> calendar(cx
);
3044 if (!ConsolidateCalendars(cx
, zonedDateTime
.calendar(), plainDate
.calendar(),
3050 Rooted
<PlainDateTimeWithCalendar
> resultPlainDateTime(cx
);
3051 if (!CreateTemporalDateTime(cx
, {date
, plainDateTime
.time
}, calendar
,
3052 &resultPlainDateTime
)) {
3058 if (!GetInstantFor(cx
, timeZone
, resultPlainDateTime
,
3059 TemporalDisambiguation::Compatible
, &instant
)) {
3065 CreateTemporalZonedDateTime(cx
, instant
, timeZone
.receiver(), calendar
);
3070 args
.rval().setObject(*result
);
3075 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3077 static bool ZonedDateTime_withPlainDate(JSContext
* cx
, unsigned argc
,
3080 CallArgs args
= CallArgsFromVp(argc
, vp
);
3081 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withPlainDate
>(
3086 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3088 static bool ZonedDateTime_withTimeZone(JSContext
* cx
, const CallArgs
& args
) {
3089 Rooted
<ZonedDateTime
> zonedDateTime(
3090 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3093 Rooted
<TimeZoneValue
> timeZone(cx
);
3094 if (!ToTemporalTimeZone(cx
, args
.get(0), &timeZone
)) {
3099 auto* result
= CreateTemporalZonedDateTime(
3100 cx
, zonedDateTime
.instant(), timeZone
, zonedDateTime
.calendar());
3105 args
.rval().setObject(*result
);
3110 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3112 static bool ZonedDateTime_withTimeZone(JSContext
* cx
, unsigned argc
,
3115 CallArgs args
= CallArgsFromVp(argc
, vp
);
3116 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withTimeZone
>(
3121 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3123 static bool ZonedDateTime_withCalendar(JSContext
* cx
, const CallArgs
& args
) {
3124 Rooted
<ZonedDateTime
> zonedDateTime(
3125 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3128 Rooted
<CalendarValue
> calendar(cx
);
3129 if (!ToTemporalCalendar(cx
, args
.get(0), &calendar
)) {
3134 auto* result
= CreateTemporalZonedDateTime(
3135 cx
, zonedDateTime
.instant(), zonedDateTime
.timeZone(), calendar
);
3140 args
.rval().setObject(*result
);
3145 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3147 static bool ZonedDateTime_withCalendar(JSContext
* cx
, unsigned argc
,
3150 CallArgs args
= CallArgsFromVp(argc
, vp
);
3151 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_withCalendar
>(
3156 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3158 static bool ZonedDateTime_add(JSContext
* cx
, const CallArgs
& args
) {
3159 return AddDurationToOrSubtractDurationFromZonedDateTime(
3160 cx
, ZonedDateTimeDuration::Add
, args
);
3164 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3166 static bool ZonedDateTime_add(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3168 CallArgs args
= CallArgsFromVp(argc
, vp
);
3169 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_add
>(cx
, args
);
3173 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3176 static bool ZonedDateTime_subtract(JSContext
* cx
, const CallArgs
& args
) {
3177 return AddDurationToOrSubtractDurationFromZonedDateTime(
3178 cx
, ZonedDateTimeDuration::Subtract
, args
);
3182 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3185 static bool ZonedDateTime_subtract(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3187 CallArgs args
= CallArgsFromVp(argc
, vp
);
3188 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_subtract
>(cx
,
3193 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3195 static bool ZonedDateTime_until(JSContext
* cx
, const CallArgs
& args
) {
3197 return DifferenceTemporalZonedDateTime(cx
, TemporalDifference::Until
, args
);
3201 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3203 static bool ZonedDateTime_until(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3205 CallArgs args
= CallArgsFromVp(argc
, vp
);
3206 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_until
>(cx
, args
);
3210 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3212 static bool ZonedDateTime_since(JSContext
* cx
, const CallArgs
& args
) {
3214 return DifferenceTemporalZonedDateTime(cx
, TemporalDifference::Since
, args
);
3218 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3220 static bool ZonedDateTime_since(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3222 CallArgs args
= CallArgsFromVp(argc
, vp
);
3223 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_since
>(cx
, args
);
3227 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3229 static bool ZonedDateTime_round(JSContext
* cx
, const CallArgs
& args
) {
3230 Rooted
<ZonedDateTime
> zonedDateTime(
3231 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3234 auto smallestUnit
= TemporalUnit::Auto
;
3235 auto roundingMode
= TemporalRoundingMode::HalfExpand
;
3236 auto roundingIncrement
= Increment
{1};
3237 if (args
.get(0).isString()) {
3238 // Step 4. (Not applicable in our implementation.)
3241 Rooted
<JSString
*> paramString(cx
, args
[0].toString());
3242 if (!GetTemporalUnit(cx
, paramString
, TemporalUnitKey::SmallestUnit
,
3243 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
3247 // Steps 6-8 and 10-12. (Implicit)
3250 Rooted
<JSObject
*> roundTo(
3251 cx
, RequireObjectArg(cx
, "roundTo", "round", args
.get(0)));
3257 if (!ToTemporalRoundingIncrement(cx
, roundTo
, &roundingIncrement
)) {
3262 if (!ToTemporalRoundingMode(cx
, roundTo
, &roundingMode
)) {
3267 if (!GetTemporalUnit(cx
, roundTo
, TemporalUnitKey::SmallestUnit
,
3268 TemporalUnitGroup::DayTime
, &smallestUnit
)) {
3272 if (smallestUnit
== TemporalUnit::Auto
) {
3273 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3274 JSMSG_TEMPORAL_MISSING_OPTION
, "smallestUnit");
3278 MOZ_ASSERT(TemporalUnit::Day
<= smallestUnit
&&
3279 smallestUnit
<= TemporalUnit::Nanosecond
);
3282 auto maximum
= Increment
{1};
3283 bool inclusive
= true;
3284 if (smallestUnit
> TemporalUnit::Day
) {
3285 maximum
= MaximumTemporalDurationRoundingIncrement(smallestUnit
);
3290 if (!ValidateTemporalRoundingIncrement(cx
, roundingIncrement
, maximum
,
3297 if (smallestUnit
== TemporalUnit::Nanosecond
&&
3298 roundingIncrement
== Increment
{1}) {
3300 auto* result
= CreateTemporalZonedDateTime(cx
, zonedDateTime
.instant(),
3301 zonedDateTime
.timeZone(),
3302 zonedDateTime
.calendar());
3307 args
.rval().setObject(*result
);
3312 Rooted
<TimeZoneRecord
> timeZone(cx
);
3313 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
3315 TimeZoneMethod::GetOffsetNanosecondsFor
,
3316 TimeZoneMethod::GetPossibleInstantsFor
,
3322 // Step 16. (Reordered)
3323 auto calendar
= zonedDateTime
.calendar();
3326 int64_t offsetNanoseconds
;
3327 if (!GetOffsetNanosecondsFor(cx
, timeZone
, zonedDateTime
.instant(),
3328 &offsetNanoseconds
)) {
3331 MOZ_ASSERT(std::abs(offsetNanoseconds
) < ToNanoseconds(TemporalUnit::Day
));
3334 auto temporalDateTime
=
3335 GetPlainDateTimeFor(zonedDateTime
.instant(), offsetNanoseconds
);
3338 Rooted
<CalendarValue
> isoCalendar(cx
, CalendarValue(cx
->names().iso8601
));
3339 Rooted
<PlainDateTimeWithCalendar
> dtStart(cx
);
3340 if (!CreateTemporalDateTime(cx
, {temporalDateTime
.date
, {}}, isoCalendar
,
3347 if (!GetInstantFor(cx
, timeZone
, dtStart
, TemporalDisambiguation::Compatible
,
3354 if (!AddDaysToZonedDateTime(cx
, startNs
, ToPlainDateTime(dtStart
), timeZone
,
3355 calendar
, 1, &endNs
)) {
3358 MOZ_ASSERT(IsValidEpochInstant(endNs
));
3361 auto dayLengthNs
= endNs
- startNs
;
3362 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs
));
3365 if (dayLengthNs
<= InstantSpan
{}) {
3366 JS_ReportErrorNumberASCII(
3367 cx
, GetErrorMessage
, nullptr,
3368 JSMSG_TEMPORAL_ZONED_DATE_TIME_NON_POSITIVE_DAY_LENGTH
);
3373 PlainDateTime roundResult
;
3374 if (!RoundISODateTime(cx
, temporalDateTime
, roundingIncrement
, smallestUnit
,
3375 roundingMode
, dayLengthNs
, &roundResult
)) {
3380 Instant epochNanoseconds
;
3381 if (!InterpretISODateTimeOffset(
3382 cx
, roundResult
, OffsetBehaviour::Option
, offsetNanoseconds
, timeZone
,
3383 TemporalDisambiguation::Compatible
, TemporalOffset::Prefer
,
3384 MatchBehaviour::MatchExactly
, &epochNanoseconds
)) {
3389 auto* result
= CreateTemporalZonedDateTime(cx
, epochNanoseconds
,
3390 timeZone
.receiver(), calendar
);
3395 args
.rval().setObject(*result
);
3400 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3402 static bool ZonedDateTime_round(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3404 CallArgs args
= CallArgsFromVp(argc
, vp
);
3405 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_round
>(cx
, args
);
3409 * Temporal.ZonedDateTime.prototype.equals ( other )
3411 static bool ZonedDateTime_equals(JSContext
* cx
, const CallArgs
& args
) {
3412 Rooted
<ZonedDateTime
> zonedDateTime(
3413 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3416 Rooted
<ZonedDateTime
> other(cx
);
3417 if (!ToTemporalZonedDateTime(cx
, args
.get(0), &other
)) {
3422 bool equals
= zonedDateTime
.instant() == other
.instant();
3423 if (equals
&& !TimeZoneEquals(cx
, zonedDateTime
.timeZone(), other
.timeZone(),
3427 if (equals
&& !CalendarEquals(cx
, zonedDateTime
.calendar(), other
.calendar(),
3432 args
.rval().setBoolean(equals
);
3437 * Temporal.ZonedDateTime.prototype.equals ( other )
3439 static bool ZonedDateTime_equals(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3441 CallArgs args
= CallArgsFromVp(argc
, vp
);
3442 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_equals
>(cx
, args
);
3446 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3448 static bool ZonedDateTime_toString(JSContext
* cx
, const CallArgs
& args
) {
3449 Rooted
<ZonedDateTime
> zonedDateTime(
3450 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3452 SecondsStringPrecision precision
= {Precision::Auto(),
3453 TemporalUnit::Nanosecond
, Increment
{1}};
3454 auto roundingMode
= TemporalRoundingMode::Trunc
;
3455 auto showCalendar
= CalendarOption::Auto
;
3456 auto showTimeZone
= TimeZoneNameOption::Auto
;
3457 auto showOffset
= ShowOffsetOption::Auto
;
3458 if (args
.hasDefined(0)) {
3460 Rooted
<JSObject
*> options(
3461 cx
, RequireObjectArg(cx
, "options", "toString", args
[0]));
3467 if (!ToCalendarNameOption(cx
, options
, &showCalendar
)) {
3472 auto digits
= Precision::Auto();
3473 if (!ToFractionalSecondDigits(cx
, options
, &digits
)) {
3478 if (!ToShowOffsetOption(cx
, options
, &showOffset
)) {
3483 if (!ToTemporalRoundingMode(cx
, options
, &roundingMode
)) {
3488 auto smallestUnit
= TemporalUnit::Auto
;
3489 if (!GetTemporalUnit(cx
, options
, TemporalUnitKey::SmallestUnit
,
3490 TemporalUnitGroup::Time
, &smallestUnit
)) {
3495 if (smallestUnit
== TemporalUnit::Hour
) {
3496 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3497 JSMSG_TEMPORAL_INVALID_UNIT_OPTION
, "hour",
3503 if (!ToTimeZoneNameOption(cx
, options
, &showTimeZone
)) {
3508 precision
= ToSecondsStringPrecision(smallestUnit
, digits
);
3512 JSString
* str
= TemporalZonedDateTimeToString(
3513 cx
, zonedDateTime
, precision
.precision
, showCalendar
, showTimeZone
,
3514 showOffset
, precision
.increment
, precision
.unit
, roundingMode
);
3519 args
.rval().setString(str
);
3524 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3526 static bool ZonedDateTime_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3528 CallArgs args
= CallArgsFromVp(argc
, vp
);
3529 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toString
>(cx
,
3534 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3536 static bool ZonedDateTime_toLocaleString(JSContext
* cx
, const CallArgs
& args
) {
3537 Rooted
<ZonedDateTime
> zonedDateTime(
3538 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3541 JSString
* str
= TemporalZonedDateTimeToString(
3542 cx
, zonedDateTime
, Precision::Auto(), CalendarOption::Auto
,
3543 TimeZoneNameOption::Auto
, ShowOffsetOption::Auto
);
3548 args
.rval().setString(str
);
3553 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3555 static bool ZonedDateTime_toLocaleString(JSContext
* cx
, unsigned argc
,
3558 CallArgs args
= CallArgsFromVp(argc
, vp
);
3559 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toLocaleString
>(
3564 * Temporal.ZonedDateTime.prototype.toJSON ( )
3566 static bool ZonedDateTime_toJSON(JSContext
* cx
, const CallArgs
& args
) {
3567 Rooted
<ZonedDateTime
> zonedDateTime(
3568 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3571 JSString
* str
= TemporalZonedDateTimeToString(
3572 cx
, zonedDateTime
, Precision::Auto(), CalendarOption::Auto
,
3573 TimeZoneNameOption::Auto
, ShowOffsetOption::Auto
);
3578 args
.rval().setString(str
);
3583 * Temporal.ZonedDateTime.prototype.toJSON ( )
3585 static bool ZonedDateTime_toJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3587 CallArgs args
= CallArgsFromVp(argc
, vp
);
3588 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toJSON
>(cx
, args
);
3592 * Temporal.ZonedDateTime.prototype.valueOf ( )
3594 static bool ZonedDateTime_valueOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3595 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_CANT_CONVERT_TO
,
3596 "ZonedDateTime", "primitive type");
3601 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3603 static bool ZonedDateTime_startOfDay(JSContext
* cx
, const CallArgs
& args
) {
3604 Rooted
<ZonedDateTime
> zonedDateTime(
3605 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3608 Rooted
<TimeZoneRecord
> timeZone(cx
);
3609 if (!CreateTimeZoneMethodsRecord(cx
, zonedDateTime
.timeZone(),
3611 TimeZoneMethod::GetOffsetNanosecondsFor
,
3612 TimeZoneMethod::GetPossibleInstantsFor
,
3619 auto calendar
= zonedDateTime
.calendar();
3622 const auto& instant
= zonedDateTime
.instant();
3625 PlainDateTime temporalDateTime
;
3626 if (!GetPlainDateTimeFor(cx
, timeZone
, instant
, &temporalDateTime
)) {
3631 Rooted
<PlainDateTimeWithCalendar
> startDateTime(cx
);
3632 if (!CreateTemporalDateTime(cx
, {temporalDateTime
.date
, {}}, calendar
,
3638 Instant startInstant
;
3639 if (!GetInstantFor(cx
, timeZone
, startDateTime
,
3640 TemporalDisambiguation::Compatible
, &startInstant
)) {
3645 auto* result
= CreateTemporalZonedDateTime(cx
, startInstant
,
3646 timeZone
.receiver(), calendar
);
3651 args
.rval().setObject(*result
);
3656 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3658 static bool ZonedDateTime_startOfDay(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3660 CallArgs args
= CallArgsFromVp(argc
, vp
);
3661 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_startOfDay
>(cx
,
3666 * Temporal.ZonedDateTime.prototype.toInstant ( )
3668 static bool ZonedDateTime_toInstant(JSContext
* cx
, const CallArgs
& args
) {
3669 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
3670 auto instant
= ToInstant(zonedDateTime
);
3673 auto* result
= CreateTemporalInstant(cx
, instant
);
3678 args
.rval().setObject(*result
);
3683 * Temporal.ZonedDateTime.prototype.toInstant ( )
3685 static bool ZonedDateTime_toInstant(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3687 CallArgs args
= CallArgsFromVp(argc
, vp
);
3688 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toInstant
>(cx
,
3693 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3695 static bool ZonedDateTime_toPlainDate(JSContext
* cx
, const CallArgs
& args
) {
3696 Rooted
<ZonedDateTime
> zonedDateTime(
3697 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3700 PlainDateTime temporalDateTime
;
3701 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
3702 zonedDateTime
.instant(), &temporalDateTime
)) {
3708 CreateTemporalDate(cx
, temporalDateTime
.date
, zonedDateTime
.calendar());
3713 args
.rval().setObject(*result
);
3718 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3720 static bool ZonedDateTime_toPlainDate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3722 CallArgs args
= CallArgsFromVp(argc
, vp
);
3723 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainDate
>(cx
,
3728 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3730 static bool ZonedDateTime_toPlainTime(JSContext
* cx
, const CallArgs
& args
) {
3731 Rooted
<ZonedDateTime
> zonedDateTime(
3732 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3735 PlainDateTime temporalDateTime
;
3736 if (!GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(),
3737 zonedDateTime
.instant(), &temporalDateTime
)) {
3742 auto* result
= CreateTemporalTime(cx
, temporalDateTime
.time
);
3747 args
.rval().setObject(*result
);
3752 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3754 static bool ZonedDateTime_toPlainTime(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3756 CallArgs args
= CallArgsFromVp(argc
, vp
);
3757 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainTime
>(cx
,
3762 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3764 static bool ZonedDateTime_toPlainDateTime(JSContext
* cx
, const CallArgs
& args
) {
3765 Rooted
<ZonedDateTime
> zonedDateTime(
3766 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3770 GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(), zonedDateTime
.instant(),
3771 zonedDateTime
.calendar());
3776 args
.rval().setObject(*result
);
3781 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3783 static bool ZonedDateTime_toPlainDateTime(JSContext
* cx
, unsigned argc
,
3786 CallArgs args
= CallArgsFromVp(argc
, vp
);
3787 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainDateTime
>(
3792 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3794 static bool ZonedDateTime_toPlainYearMonth(JSContext
* cx
,
3795 const CallArgs
& args
) {
3796 Rooted
<ZonedDateTime
> zonedDateTime(
3797 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3800 Rooted
<CalendarRecord
> calendar(cx
);
3801 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
3803 CalendarMethod::Fields
,
3804 CalendarMethod::YearMonthFromFields
,
3811 Rooted
<PlainDateTimeObject
*> temporalDateTime(
3813 GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(), zonedDateTime
.instant(),
3814 zonedDateTime
.calendar()));
3815 if (!temporalDateTime
) {
3820 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
3821 if (!CalendarFields(cx
, calendar
,
3822 {CalendarField::MonthCode
, CalendarField::Year
},
3828 Rooted
<PlainObject
*> fields(
3829 cx
, PrepareTemporalFields(cx
, temporalDateTime
, fieldNames
));
3835 auto result
= CalendarYearMonthFromFields(cx
, calendar
, fields
);
3840 args
.rval().setObject(*result
);
3845 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3847 static bool ZonedDateTime_toPlainYearMonth(JSContext
* cx
, unsigned argc
,
3850 CallArgs args
= CallArgsFromVp(argc
, vp
);
3851 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainYearMonth
>(
3856 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3858 static bool ZonedDateTime_toPlainMonthDay(JSContext
* cx
, const CallArgs
& args
) {
3859 Rooted
<ZonedDateTime
> zonedDateTime(
3860 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3863 Rooted
<CalendarRecord
> calendar(cx
);
3864 if (!CreateCalendarMethodsRecord(cx
, zonedDateTime
.calendar(),
3866 CalendarMethod::Fields
,
3867 CalendarMethod::MonthDayFromFields
,
3874 Rooted
<PlainDateTimeObject
*> temporalDateTime(
3876 GetPlainDateTimeFor(cx
, zonedDateTime
.timeZone(), zonedDateTime
.instant(),
3877 zonedDateTime
.calendar()));
3878 if (!temporalDateTime
) {
3883 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
3884 if (!CalendarFields(cx
, calendar
,
3885 {CalendarField::Day
, CalendarField::MonthCode
},
3891 Rooted
<PlainObject
*> fields(
3892 cx
, PrepareTemporalFields(cx
, temporalDateTime
, fieldNames
));
3898 auto result
= CalendarMonthDayFromFields(cx
, calendar
, fields
);
3903 args
.rval().setObject(*result
);
3908 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3910 static bool ZonedDateTime_toPlainMonthDay(JSContext
* cx
, unsigned argc
,
3913 CallArgs args
= CallArgsFromVp(argc
, vp
);
3914 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_toPlainMonthDay
>(
3919 * Temporal.ZonedDateTime.prototype.getISOFields ( )
3921 static bool ZonedDateTime_getISOFields(JSContext
* cx
, const CallArgs
& args
) {
3922 Rooted
<ZonedDateTime
> zonedDateTime(
3923 cx
, ZonedDateTime
{&args
.thisv().toObject().as
<ZonedDateTimeObject
>()});
3926 Rooted
<IdValueVector
> fields(cx
, IdValueVector(cx
));
3929 const auto& instant
= zonedDateTime
.instant();
3932 auto calendar
= zonedDateTime
.calendar();
3935 auto timeZone
= zonedDateTime
.timeZone();
3938 int64_t offsetNanoseconds
;
3939 if (!GetOffsetNanosecondsFor(cx
, timeZone
, instant
, &offsetNanoseconds
)) {
3944 auto temporalDateTime
= GetPlainDateTimeFor(instant
, offsetNanoseconds
);
3947 Rooted
<JSString
*> offset(cx
,
3948 FormatUTCOffsetNanoseconds(cx
, offsetNanoseconds
));
3954 if (!fields
.emplaceBack(NameToId(cx
->names().calendar
), calendar
.toValue())) {
3959 if (!fields
.emplaceBack(NameToId(cx
->names().isoDay
),
3960 Int32Value(temporalDateTime
.date
.day
))) {
3965 if (!fields
.emplaceBack(NameToId(cx
->names().isoHour
),
3966 Int32Value(temporalDateTime
.time
.hour
))) {
3971 if (!fields
.emplaceBack(NameToId(cx
->names().isoMicrosecond
),
3972 Int32Value(temporalDateTime
.time
.microsecond
))) {
3977 if (!fields
.emplaceBack(NameToId(cx
->names().isoMillisecond
),
3978 Int32Value(temporalDateTime
.time
.millisecond
))) {
3983 if (!fields
.emplaceBack(NameToId(cx
->names().isoMinute
),
3984 Int32Value(temporalDateTime
.time
.minute
))) {
3989 if (!fields
.emplaceBack(NameToId(cx
->names().isoMonth
),
3990 Int32Value(temporalDateTime
.date
.month
))) {
3995 if (!fields
.emplaceBack(NameToId(cx
->names().isoNanosecond
),
3996 Int32Value(temporalDateTime
.time
.nanosecond
))) {
4001 if (!fields
.emplaceBack(NameToId(cx
->names().isoSecond
),
4002 Int32Value(temporalDateTime
.time
.second
))) {
4007 if (!fields
.emplaceBack(NameToId(cx
->names().isoYear
),
4008 Int32Value(temporalDateTime
.date
.year
))) {
4013 if (!fields
.emplaceBack(NameToId(cx
->names().offset
), StringValue(offset
))) {
4018 if (!fields
.emplaceBack(NameToId(cx
->names().timeZone
), timeZone
.toValue())) {
4023 auto* obj
= NewPlainObjectWithUniqueNames(cx
, fields
);
4028 args
.rval().setObject(*obj
);
4033 * Temporal.ZonedDateTime.prototype.getISOFields ( )
4035 static bool ZonedDateTime_getISOFields(JSContext
* cx
, unsigned argc
,
4038 CallArgs args
= CallArgsFromVp(argc
, vp
);
4039 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_getISOFields
>(
4044 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4046 static bool ZonedDateTime_getCalendar(JSContext
* cx
, const CallArgs
& args
) {
4047 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
4048 Rooted
<CalendarValue
> calendar(cx
, zonedDateTime
->calendar());
4051 auto* obj
= ToTemporalCalendarObject(cx
, calendar
);
4056 args
.rval().setObject(*obj
);
4061 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4063 static bool ZonedDateTime_getCalendar(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4065 CallArgs args
= CallArgsFromVp(argc
, vp
);
4066 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_getCalendar
>(cx
,
4071 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4073 static bool ZonedDateTime_getTimeZone(JSContext
* cx
, const CallArgs
& args
) {
4074 auto* zonedDateTime
= &args
.thisv().toObject().as
<ZonedDateTimeObject
>();
4075 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
4078 auto* obj
= ToTemporalTimeZoneObject(cx
, timeZone
);
4083 args
.rval().setObject(*obj
);
4088 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4090 static bool ZonedDateTime_getTimeZone(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4092 CallArgs args
= CallArgsFromVp(argc
, vp
);
4093 return CallNonGenericMethod
<IsZonedDateTime
, ZonedDateTime_getTimeZone
>(cx
,
4097 const JSClass
ZonedDateTimeObject::class_
= {
4098 "Temporal.ZonedDateTime",
4099 JSCLASS_HAS_RESERVED_SLOTS(ZonedDateTimeObject::SLOT_COUNT
) |
4100 JSCLASS_HAS_CACHED_PROTO(JSProto_ZonedDateTime
),
4102 &ZonedDateTimeObject::classSpec_
,
4105 const JSClass
& ZonedDateTimeObject::protoClass_
= PlainObject::class_
;
4107 static const JSFunctionSpec ZonedDateTime_methods
[] = {
4108 JS_FN("from", ZonedDateTime_from
, 1, 0),
4109 JS_FN("compare", ZonedDateTime_compare
, 2, 0),
4113 static const JSFunctionSpec ZonedDateTime_prototype_methods
[] = {
4114 JS_FN("with", ZonedDateTime_with
, 1, 0),
4115 JS_FN("withPlainTime", ZonedDateTime_withPlainTime
, 0, 0),
4116 JS_FN("withPlainDate", ZonedDateTime_withPlainDate
, 1, 0),
4117 JS_FN("withTimeZone", ZonedDateTime_withTimeZone
, 1, 0),
4118 JS_FN("withCalendar", ZonedDateTime_withCalendar
, 1, 0),
4119 JS_FN("add", ZonedDateTime_add
, 1, 0),
4120 JS_FN("subtract", ZonedDateTime_subtract
, 1, 0),
4121 JS_FN("until", ZonedDateTime_until
, 1, 0),
4122 JS_FN("since", ZonedDateTime_since
, 1, 0),
4123 JS_FN("round", ZonedDateTime_round
, 1, 0),
4124 JS_FN("equals", ZonedDateTime_equals
, 1, 0),
4125 JS_FN("toString", ZonedDateTime_toString
, 0, 0),
4126 JS_FN("toLocaleString", ZonedDateTime_toLocaleString
, 0, 0),
4127 JS_FN("toJSON", ZonedDateTime_toJSON
, 0, 0),
4128 JS_FN("valueOf", ZonedDateTime_valueOf
, 0, 0),
4129 JS_FN("startOfDay", ZonedDateTime_startOfDay
, 0, 0),
4130 JS_FN("toInstant", ZonedDateTime_toInstant
, 0, 0),
4131 JS_FN("toPlainDate", ZonedDateTime_toPlainDate
, 0, 0),
4132 JS_FN("toPlainTime", ZonedDateTime_toPlainTime
, 0, 0),
4133 JS_FN("toPlainDateTime", ZonedDateTime_toPlainDateTime
, 0, 0),
4134 JS_FN("toPlainYearMonth", ZonedDateTime_toPlainYearMonth
, 0, 0),
4135 JS_FN("toPlainMonthDay", ZonedDateTime_toPlainMonthDay
, 0, 0),
4136 JS_FN("getISOFields", ZonedDateTime_getISOFields
, 0, 0),
4137 JS_FN("getCalendar", ZonedDateTime_getCalendar
, 0, 0),
4138 JS_FN("getTimeZone", ZonedDateTime_getTimeZone
, 0, 0),
4142 static const JSPropertySpec ZonedDateTime_prototype_properties
[] = {
4143 JS_PSG("calendarId", ZonedDateTime_calendarId
, 0),
4144 JS_PSG("timeZoneId", ZonedDateTime_timeZoneId
, 0),
4145 JS_PSG("year", ZonedDateTime_year
, 0),
4146 JS_PSG("month", ZonedDateTime_month
, 0),
4147 JS_PSG("monthCode", ZonedDateTime_monthCode
, 0),
4148 JS_PSG("day", ZonedDateTime_day
, 0),
4149 JS_PSG("hour", ZonedDateTime_hour
, 0),
4150 JS_PSG("minute", ZonedDateTime_minute
, 0),
4151 JS_PSG("second", ZonedDateTime_second
, 0),
4152 JS_PSG("millisecond", ZonedDateTime_millisecond
, 0),
4153 JS_PSG("microsecond", ZonedDateTime_microsecond
, 0),
4154 JS_PSG("nanosecond", ZonedDateTime_nanosecond
, 0),
4155 JS_PSG("epochSeconds", ZonedDateTime_epochSeconds
, 0),
4156 JS_PSG("epochMilliseconds", ZonedDateTime_epochMilliseconds
, 0),
4157 JS_PSG("epochMicroseconds", ZonedDateTime_epochMicroseconds
, 0),
4158 JS_PSG("epochNanoseconds", ZonedDateTime_epochNanoseconds
, 0),
4159 JS_PSG("dayOfWeek", ZonedDateTime_dayOfWeek
, 0),
4160 JS_PSG("dayOfYear", ZonedDateTime_dayOfYear
, 0),
4161 JS_PSG("weekOfYear", ZonedDateTime_weekOfYear
, 0),
4162 JS_PSG("yearOfWeek", ZonedDateTime_yearOfWeek
, 0),
4163 JS_PSG("hoursInDay", ZonedDateTime_hoursInDay
, 0),
4164 JS_PSG("daysInWeek", ZonedDateTime_daysInWeek
, 0),
4165 JS_PSG("daysInMonth", ZonedDateTime_daysInMonth
, 0),
4166 JS_PSG("daysInYear", ZonedDateTime_daysInYear
, 0),
4167 JS_PSG("monthsInYear", ZonedDateTime_monthsInYear
, 0),
4168 JS_PSG("inLeapYear", ZonedDateTime_inLeapYear
, 0),
4169 JS_PSG("offsetNanoseconds", ZonedDateTime_offsetNanoseconds
, 0),
4170 JS_PSG("offset", ZonedDateTime_offset
, 0),
4171 JS_STRING_SYM_PS(toStringTag
, "Temporal.ZonedDateTime", JSPROP_READONLY
),
4175 const ClassSpec
ZonedDateTimeObject::classSpec_
= {
4176 GenericCreateConstructor
<ZonedDateTimeConstructor
, 2,
4177 gc::AllocKind::FUNCTION
>,
4178 GenericCreatePrototype
<ZonedDateTimeObject
>,
4179 ZonedDateTime_methods
,
4181 ZonedDateTime_prototype_methods
,
4182 ZonedDateTime_prototype_properties
,
4184 ClassSpec::DontDefineConstructor
,