Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / js / src / builtin / temporal / ZonedDateTime.cpp
blobcef487499726fc5bcdee5aaf160860aa84acf4ed
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"
12 #include <cstdlib>
13 #include <limits>
14 #include <utility>
16 #include "jspubtd.h"
17 #include "NamespaceImports.h"
19 #include "builtin/temporal/Calendar.h"
20 #include "builtin/temporal/Duration.h"
21 #include "builtin/temporal/Instant.h"
22 #include "builtin/temporal/Int96.h"
23 #include "builtin/temporal/PlainDate.h"
24 #include "builtin/temporal/PlainDateTime.h"
25 #include "builtin/temporal/PlainMonthDay.h"
26 #include "builtin/temporal/PlainTime.h"
27 #include "builtin/temporal/PlainYearMonth.h"
28 #include "builtin/temporal/Temporal.h"
29 #include "builtin/temporal/TemporalFields.h"
30 #include "builtin/temporal/TemporalParser.h"
31 #include "builtin/temporal/TemporalRoundingMode.h"
32 #include "builtin/temporal/TemporalTypes.h"
33 #include "builtin/temporal/TemporalUnit.h"
34 #include "builtin/temporal/TimeZone.h"
35 #include "builtin/temporal/ToString.h"
36 #include "builtin/temporal/Wrapped.h"
37 #include "ds/IdValuePair.h"
38 #include "gc/AllocKind.h"
39 #include "gc/Barrier.h"
40 #include "js/AllocPolicy.h"
41 #include "js/CallArgs.h"
42 #include "js/CallNonGenericMethod.h"
43 #include "js/Class.h"
44 #include "js/ComparisonOperators.h"
45 #include "js/ErrorReport.h"
46 #include "js/friend/ErrorMessages.h"
47 #include "js/GCVector.h"
48 #include "js/Id.h"
49 #include "js/Printer.h"
50 #include "js/PropertyDescriptor.h"
51 #include "js/PropertySpec.h"
52 #include "js/RootingAPI.h"
53 #include "js/TracingAPI.h"
54 #include "js/Value.h"
55 #include "vm/BigIntType.h"
56 #include "vm/BytecodeUtil.h"
57 #include "vm/GlobalObject.h"
58 #include "vm/JSAtomState.h"
59 #include "vm/JSContext.h"
60 #include "vm/JSObject.h"
61 #include "vm/ObjectOperations.h"
62 #include "vm/PlainObject.h"
63 #include "vm/StringType.h"
65 #include "vm/JSContext-inl.h"
66 #include "vm/JSObject-inl.h"
67 #include "vm/NativeObject-inl.h"
68 #include "vm/ObjectOperations-inl.h"
70 using namespace js;
71 using namespace js::temporal;
73 static inline bool IsZonedDateTime(Handle<Value> v) {
74 return v.isObject() && v.toObject().is<ZonedDateTimeObject>();
77 // Returns |RoundNumberToIncrement(offsetNanoseconds, 60 × 10^9, "halfExpand")|.
78 static int64_t RoundNanosecondsToMinutesIncrement(int64_t offsetNanoseconds) {
79 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
81 constexpr int64_t increment = ToNanoseconds(TemporalUnit::Minute);
83 int64_t quotient = offsetNanoseconds / increment;
84 int64_t remainder = offsetNanoseconds % increment;
85 if (std::abs(remainder * 2) >= increment) {
86 quotient += (offsetNanoseconds > 0 ? 1 : -1);
88 return quotient * increment;
91 /**
92 * InterpretISODateTimeOffset ( year, month, day, hour, minute, second,
93 * millisecond, microsecond, nanosecond, offsetBehaviour, offsetNanoseconds,
94 * timeZoneRec, disambiguation, offsetOption, matchBehaviour )
96 bool js::temporal::InterpretISODateTimeOffset(
97 JSContext* cx, const PlainDateTime& dateTime,
98 OffsetBehaviour offsetBehaviour, int64_t offsetNanoseconds,
99 Handle<TimeZoneRecord> timeZone, TemporalDisambiguation disambiguation,
100 TemporalOffset offsetOption, MatchBehaviour matchBehaviour,
101 Instant* result) {
102 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
104 // Step 1.
105 MOZ_ASSERT(IsValidISODate(dateTime.date));
107 // Step 2.
108 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
109 timeZone, TimeZoneMethod::GetOffsetNanosecondsFor));
111 // Step 3.
112 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
113 timeZone, TimeZoneMethod::GetPossibleInstantsFor));
115 // Step 4.
116 Rooted<CalendarValue> calendar(cx, CalendarValue(cx->names().iso8601));
117 Rooted<PlainDateTimeWithCalendar> temporalDateTime(cx);
118 if (!CreateTemporalDateTime(cx, dateTime, calendar, &temporalDateTime)) {
119 return false;
122 // Step 5.
123 if (offsetBehaviour == OffsetBehaviour::Wall ||
124 (offsetBehaviour == OffsetBehaviour::Option &&
125 offsetOption == TemporalOffset::Ignore)) {
126 // Steps 5.a-b.
127 return GetInstantFor(cx, timeZone, temporalDateTime, disambiguation,
128 result);
131 // Step 6.
132 if (offsetBehaviour == OffsetBehaviour::Exact ||
133 (offsetBehaviour == OffsetBehaviour::Option &&
134 offsetOption == TemporalOffset::Use)) {
135 // Step 6.a.
136 auto epochNanoseconds = GetUTCEpochNanoseconds(
137 dateTime, InstantSpan::fromNanoseconds(offsetNanoseconds));
139 // Step 6.b.
140 if (!IsValidEpochInstant(epochNanoseconds)) {
141 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
142 JSMSG_TEMPORAL_INSTANT_INVALID);
143 return false;
146 // Step 6.c.
147 *result = epochNanoseconds;
148 return true;
151 // Step 7.
152 MOZ_ASSERT(offsetBehaviour == OffsetBehaviour::Option);
154 // Step 8.
155 MOZ_ASSERT(offsetOption == TemporalOffset::Prefer ||
156 offsetOption == TemporalOffset::Reject);
158 // Step 9.
159 Rooted<InstantVector> possibleInstants(cx, InstantVector(cx));
160 if (!GetPossibleInstantsFor(cx, timeZone, temporalDateTime,
161 &possibleInstants)) {
162 return false;
165 // Step 10.
166 if (!possibleInstants.empty()) {
167 // Step 10.a.
168 Rooted<Wrapped<InstantObject*>> candidate(cx);
169 for (size_t i = 0; i < possibleInstants.length(); i++) {
170 candidate = possibleInstants[i];
172 // Step 10.a.i.
173 int64_t candidateNanoseconds;
174 if (!GetOffsetNanosecondsFor(cx, timeZone, candidate,
175 &candidateNanoseconds)) {
176 return false;
178 MOZ_ASSERT(std::abs(candidateNanoseconds) <
179 ToNanoseconds(TemporalUnit::Day));
181 // Step 10.a.ii.
182 if (candidateNanoseconds == offsetNanoseconds) {
183 auto* unwrapped = candidate.unwrap(cx);
184 if (!unwrapped) {
185 return false;
188 *result = ToInstant(unwrapped);
189 return true;
192 // Step 10.a.iii.
193 if (matchBehaviour == MatchBehaviour::MatchMinutes) {
194 // Step 10.a.iii.1.
195 int64_t roundedCandidateNanoseconds =
196 RoundNanosecondsToMinutesIncrement(candidateNanoseconds);
198 // Step 10.a.iii.2.
199 if (roundedCandidateNanoseconds == offsetNanoseconds) {
200 auto* unwrapped = candidate.unwrap(cx);
201 if (!unwrapped) {
202 return false;
205 // Step 10.a.iii.2.a.
206 *result = ToInstant(unwrapped);
207 return true;
213 // Step 11.
214 if (offsetOption == TemporalOffset::Reject) {
215 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
216 JSMSG_TEMPORAL_ZONED_DATE_TIME_NO_TIME_FOUND);
217 return false;
220 // Step 12.
221 Rooted<Wrapped<InstantObject*>> instant(cx);
222 if (!DisambiguatePossibleInstants(cx, possibleInstants, timeZone,
223 ToPlainDateTime(temporalDateTime),
224 disambiguation, &instant)) {
225 return false;
228 auto* unwrappedInstant = instant.unwrap(cx);
229 if (!unwrappedInstant) {
230 return false;
233 // Step 13.
234 *result = ToInstant(unwrappedInstant);
235 return true;
239 * ToTemporalZonedDateTime ( item [ , options ] )
241 static bool ToTemporalZonedDateTime(JSContext* cx, Handle<Value> item,
242 Handle<JSObject*> maybeOptions,
243 MutableHandle<ZonedDateTime> result) {
244 // Step 1. (Not applicable in our implementation)
246 // Step 2.
247 Rooted<PlainObject*> maybeResolvedOptions(cx);
248 if (maybeOptions) {
249 maybeResolvedOptions = SnapshotOwnProperties(cx, maybeOptions);
250 if (!maybeResolvedOptions) {
251 return false;
255 // Step 3.
256 auto offsetBehaviour = OffsetBehaviour::Option;
258 // Step 4.
259 auto matchBehaviour = MatchBehaviour::MatchExactly;
261 // Step 7. (Reordered)
262 int64_t offsetNanoseconds = 0;
264 // Step 5.
265 Rooted<CalendarValue> calendar(cx);
266 Rooted<TimeZoneValue> timeZone(cx);
267 PlainDateTime dateTime;
268 auto disambiguation = TemporalDisambiguation::Compatible;
269 auto offsetOption = TemporalOffset::Reject;
270 if (item.isObject()) {
271 Rooted<JSObject*> itemObj(cx, &item.toObject());
273 // Step 5.a.
274 if (auto* zonedDateTime = itemObj->maybeUnwrapIf<ZonedDateTimeObject>()) {
275 auto instant = ToInstant(zonedDateTime);
276 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
277 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
279 if (!timeZone.wrap(cx)) {
280 return false;
282 if (!calendar.wrap(cx)) {
283 return false;
286 result.set(ZonedDateTime{instant, timeZone, calendar});
287 return true;
290 // Step 5.b.
291 if (!GetTemporalCalendarWithISODefault(cx, itemObj, &calendar)) {
292 return false;
295 // Step 5.c.
296 Rooted<CalendarRecord> calendarRec(cx);
297 if (!CreateCalendarMethodsRecord(cx, calendar,
299 CalendarMethod::DateFromFields,
300 CalendarMethod::Fields,
302 &calendarRec)) {
303 return false;
306 // Step 5.d.
307 JS::RootedVector<PropertyKey> fieldNames(cx);
308 if (!CalendarFields(cx, calendarRec,
309 {CalendarField::Day, CalendarField::Month,
310 CalendarField::MonthCode, CalendarField::Year},
311 &fieldNames)) {
312 return false;
315 // Step 5.e.
316 if (!AppendSorted(cx, fieldNames.get(),
318 TemporalField::Hour,
319 TemporalField::Microsecond,
320 TemporalField::Millisecond,
321 TemporalField::Minute,
322 TemporalField::Nanosecond,
323 TemporalField::Offset,
324 TemporalField::Second,
325 TemporalField::TimeZone,
326 })) {
327 return false;
330 // Step 5.f.
331 Rooted<PlainObject*> fields(
332 cx, PrepareTemporalFields(cx, itemObj, fieldNames,
333 {TemporalField::TimeZone}));
334 if (!fields) {
335 return false;
338 // Step 5.g.
339 Rooted<Value> timeZoneValue(cx);
340 if (!GetProperty(cx, fields, fields, cx->names().timeZone,
341 &timeZoneValue)) {
342 return false;
345 // Step 5.h.
346 if (!ToTemporalTimeZone(cx, timeZoneValue, &timeZone)) {
347 return false;
350 // Step 5.i.
351 Rooted<Value> offsetValue(cx);
352 if (!GetProperty(cx, fields, fields, cx->names().offset, &offsetValue)) {
353 return false;
356 // Step 5.j.
357 MOZ_ASSERT(offsetValue.isString() || offsetValue.isUndefined());
359 // Step 5.k.
360 Rooted<JSString*> offsetString(cx);
361 if (offsetValue.isString()) {
362 offsetString = offsetValue.toString();
363 } else {
364 offsetBehaviour = OffsetBehaviour::Wall;
367 if (maybeResolvedOptions) {
368 // Steps 5.l-m.
369 if (!ToTemporalDisambiguation(cx, maybeResolvedOptions,
370 &disambiguation)) {
371 return false;
374 // Step 5.n.
375 if (!ToTemporalOffset(cx, maybeResolvedOptions, &offsetOption)) {
376 return false;
379 // Step 5.o.
380 if (!InterpretTemporalDateTimeFields(cx, calendarRec, fields,
381 maybeResolvedOptions, &dateTime)) {
382 return false;
384 } else {
385 // Steps 5.l-n. (Not applicable)
387 // Step 5.o.
388 if (!InterpretTemporalDateTimeFields(cx, calendarRec, fields,
389 &dateTime)) {
390 return false;
394 // Step 8.
395 if (offsetBehaviour == OffsetBehaviour::Option) {
396 if (!ParseDateTimeUTCOffset(cx, offsetString, &offsetNanoseconds)) {
397 return false;
400 } else {
401 // Step 6.a.
402 if (!item.isString()) {
403 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
404 nullptr, "not a string");
405 return false;
407 Rooted<JSString*> string(cx, item.toString());
409 // Case 1: 19700101Z[+02:00]
410 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
412 // Case 2: 19700101+00:00[+02:00]
413 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "+02:00" }
415 // Case 3: 19700101[+02:00]
416 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
418 // Case 4: 19700101Z[Europe/Berlin]
419 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
421 // Case 5: 19700101+00:00[Europe/Berlin]
422 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "Europe/Berlin" }
424 // Case 6: 19700101[Europe/Berlin]
425 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
427 // Steps 6.b-c.
428 bool isUTC;
429 bool hasOffset;
430 int64_t timeZoneOffset;
431 Rooted<ParsedTimeZone> timeZoneAnnotation(cx);
432 Rooted<JSString*> calendarString(cx);
433 if (!ParseTemporalZonedDateTimeString(
434 cx, string, &dateTime, &isUTC, &hasOffset, &timeZoneOffset,
435 &timeZoneAnnotation, &calendarString)) {
436 return false;
439 // Step 6.d.
440 MOZ_ASSERT(timeZoneAnnotation);
442 // Step 6.e.
443 if (!ToTemporalTimeZone(cx, timeZoneAnnotation, &timeZone)) {
444 return false;
447 // Step 6.f. (Not applicable in our implementation.)
449 // Step 6.g.
450 if (isUTC) {
451 offsetBehaviour = OffsetBehaviour::Exact;
454 // Step 6.h.
455 else if (!hasOffset) {
456 offsetBehaviour = OffsetBehaviour::Wall;
459 // Steps 6.i-l.
460 if (calendarString) {
461 if (!ToBuiltinCalendar(cx, calendarString, &calendar)) {
462 return false;
464 } else {
465 calendar.set(CalendarValue(cx->names().iso8601));
468 // Step 6.m.
469 matchBehaviour = MatchBehaviour::MatchMinutes;
471 if (maybeResolvedOptions) {
472 // Step 6.n.
473 if (!ToTemporalDisambiguation(cx, maybeResolvedOptions,
474 &disambiguation)) {
475 return false;
478 // Step 6.o.
479 if (!ToTemporalOffset(cx, maybeResolvedOptions, &offsetOption)) {
480 return false;
483 // Step 6.p.
484 TemporalOverflow ignored;
485 if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
486 return false;
490 // Step 8.
491 if (offsetBehaviour == OffsetBehaviour::Option) {
492 MOZ_ASSERT(hasOffset);
493 offsetNanoseconds = timeZoneOffset;
497 // Step 9.
498 Rooted<TimeZoneRecord> timeZoneRec(cx);
499 if (!CreateTimeZoneMethodsRecord(cx, timeZone,
501 TimeZoneMethod::GetOffsetNanosecondsFor,
502 TimeZoneMethod::GetPossibleInstantsFor,
504 &timeZoneRec)) {
505 return false;
508 // Step 10.
509 Instant epochNanoseconds;
510 if (!InterpretISODateTimeOffset(
511 cx, dateTime, offsetBehaviour, offsetNanoseconds, timeZoneRec,
512 disambiguation, offsetOption, matchBehaviour, &epochNanoseconds)) {
513 return false;
516 // Step 11.
517 result.set(ZonedDateTime{epochNanoseconds, timeZone, calendar});
518 return true;
522 * ToTemporalZonedDateTime ( item [ , options ] )
524 static bool ToTemporalZonedDateTime(JSContext* cx, Handle<Value> item,
525 MutableHandle<ZonedDateTime> result) {
526 return ToTemporalZonedDateTime(cx, item, nullptr, result);
530 * ToTemporalZonedDateTime ( item [ , options ] )
532 static ZonedDateTimeObject* ToTemporalZonedDateTime(
533 JSContext* cx, Handle<Value> item, Handle<JSObject*> maybeOptions) {
534 Rooted<ZonedDateTime> result(cx);
535 if (!ToTemporalZonedDateTime(cx, item, maybeOptions, &result)) {
536 return nullptr;
538 return CreateTemporalZonedDateTime(cx, result.instant(), result.timeZone(),
539 result.calendar());
543 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
544 * newTarget ] )
546 static ZonedDateTimeObject* CreateTemporalZonedDateTime(
547 JSContext* cx, const CallArgs& args, Handle<BigInt*> epochNanoseconds,
548 Handle<TimeZoneValue> timeZone, Handle<CalendarValue> calendar) {
549 // Step 1.
550 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
552 // Steps 3-4.
553 Rooted<JSObject*> proto(cx);
554 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ZonedDateTime,
555 &proto)) {
556 return nullptr;
559 auto* obj = NewObjectWithClassProto<ZonedDateTimeObject>(cx, proto);
560 if (!obj) {
561 return nullptr;
564 // Step 4.
565 auto instant = ToInstant(epochNanoseconds);
566 obj->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT,
567 NumberValue(instant.seconds));
568 obj->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT,
569 Int32Value(instant.nanoseconds));
571 // Step 5.
572 obj->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT, timeZone.toSlotValue());
574 // Step 6.
575 obj->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT, calendar.toValue());
577 // Step 7.
578 return obj;
582 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
583 * newTarget ] )
585 ZonedDateTimeObject* js::temporal::CreateTemporalZonedDateTime(
586 JSContext* cx, const Instant& instant, Handle<TimeZoneValue> timeZone,
587 Handle<CalendarValue> calendar) {
588 // Step 1.
589 MOZ_ASSERT(IsValidEpochInstant(instant));
591 // Steps 2-3.
592 auto* obj = NewBuiltinClassInstance<ZonedDateTimeObject>(cx);
593 if (!obj) {
594 return nullptr;
597 // Step 4.
598 obj->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT,
599 NumberValue(instant.seconds));
600 obj->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT,
601 Int32Value(instant.nanoseconds));
603 // Step 5.
604 obj->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT, timeZone.toSlotValue());
606 // Step 6.
607 obj->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT, calendar.toValue());
609 // Step 7.
610 return obj;
613 struct PlainDateTimeAndInstant {
614 PlainDateTime dateTime;
615 Instant instant;
619 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
620 * overflow ] )
622 static bool AddDaysToZonedDateTime(JSContext* cx, const Instant& instant,
623 const PlainDateTime& dateTime,
624 Handle<TimeZoneRecord> timeZone,
625 Handle<CalendarValue> calendar, int64_t days,
626 TemporalOverflow overflow,
627 PlainDateTimeAndInstant* result) {
628 // Step 1. (Not applicable in our implementation.)
630 // Step 2. (Not applicable)
632 // Step 3.
633 if (days == 0) {
634 *result = {dateTime, instant};
635 return true;
638 // Step 4.
639 PlainDate addedDate;
640 if (!AddISODate(cx, dateTime.date, {0, 0, 0, days}, overflow, &addedDate)) {
641 return false;
644 // Step 5.
645 Rooted<PlainDateTimeWithCalendar> dateTimeResult(cx);
646 if (!CreateTemporalDateTime(cx, {addedDate, dateTime.time}, calendar,
647 &dateTimeResult)) {
648 return false;
651 // Step 6.
652 Instant instantResult;
653 if (!GetInstantFor(cx, timeZone, dateTimeResult,
654 TemporalDisambiguation::Compatible, &instantResult)) {
655 return false;
658 // Step 7.
659 *result = {ToPlainDateTime(dateTimeResult), instantResult};
660 return true;
664 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
665 * overflow ] )
667 bool js::temporal::AddDaysToZonedDateTime(
668 JSContext* cx, const Instant& instant, const PlainDateTime& dateTime,
669 Handle<TimeZoneRecord> timeZone, Handle<CalendarValue> calendar,
670 int64_t days, TemporalOverflow overflow, Instant* result) {
671 // Steps 1-7.
672 PlainDateTimeAndInstant dateTimeAndInstant;
673 if (!::AddDaysToZonedDateTime(cx, instant, dateTime, timeZone, calendar, days,
674 overflow, &dateTimeAndInstant)) {
675 return false;
678 *result = dateTimeAndInstant.instant;
679 return true;
683 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
684 * overflow ] )
686 bool js::temporal::AddDaysToZonedDateTime(JSContext* cx, const Instant& instant,
687 const PlainDateTime& dateTime,
688 Handle<TimeZoneRecord> timeZone,
689 Handle<CalendarValue> calendar,
690 int64_t days, Instant* result) {
691 // Step 2.
692 auto overflow = TemporalOverflow::Constrain;
694 // Steps 1 and 3-7.
695 return AddDaysToZonedDateTime(cx, instant, dateTime, timeZone, calendar, days,
696 overflow, result);
700 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
701 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
703 static bool AddZonedDateTime(JSContext* cx, const Instant& epochNanoseconds,
704 Handle<TimeZoneRecord> timeZone,
705 Handle<CalendarRecord> calendar,
706 const NormalizedDuration& duration,
707 mozilla::Maybe<const PlainDateTime&> dateTime,
708 Handle<JSObject*> maybeOptions, Instant* result) {
709 MOZ_ASSERT(IsValidEpochInstant(epochNanoseconds));
710 MOZ_ASSERT(IsValidDuration(duration));
712 // Step 1.
713 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
714 timeZone, TimeZoneMethod::GetPossibleInstantsFor));
716 // Steps 2-3.
717 MOZ_ASSERT_IF(!dateTime,
718 TimeZoneMethodsRecordHasLookedUp(
719 timeZone, TimeZoneMethod::GetOffsetNanosecondsFor));
721 // Steps 4-5. (Not applicable in our implementation)
723 // Step 6.
724 if (duration.date == DateDuration{}) {
725 // Step 6.a.
726 return AddInstant(cx, epochNanoseconds, duration.time, result);
729 // Step 7. (Not applicable in our implementation)
731 // Steps 8-9.
732 PlainDateTime temporalDateTime;
733 if (dateTime) {
734 // Step 8.a.
735 temporalDateTime = *dateTime;
736 } else {
737 // Step 9.a.
738 if (!GetPlainDateTimeFor(cx, timeZone, epochNanoseconds,
739 &temporalDateTime)) {
740 return false;
743 auto& [date, time] = temporalDateTime;
745 // Step 10.
746 if (duration.date.years == 0 && duration.date.months == 0 &&
747 duration.date.weeks == 0) {
748 // Step 10.a.
749 auto overflow = TemporalOverflow::Constrain;
750 if (maybeOptions) {
751 if (!ToTemporalOverflow(cx, maybeOptions, &overflow)) {
752 return false;
756 // Step 10.b.
757 Instant intermediate;
758 if (!AddDaysToZonedDateTime(cx, epochNanoseconds, temporalDateTime,
759 timeZone, calendar.receiver(),
760 duration.date.days, overflow, &intermediate)) {
761 return false;
764 // Step 10.c.
765 return AddInstant(cx, intermediate, duration.time, result);
768 // Step 11.
769 MOZ_ASSERT(
770 CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateAdd));
772 // Step 12.
773 const auto& datePart = date;
775 // Step 13.
776 const auto& dateDuration = duration.date;
778 // Step 14.
779 PlainDate addedDate;
780 if (maybeOptions) {
781 if (!CalendarDateAdd(cx, calendar, datePart, dateDuration, maybeOptions,
782 &addedDate)) {
783 return false;
785 } else {
786 if (!CalendarDateAdd(cx, calendar, datePart, dateDuration, &addedDate)) {
787 return false;
791 // Step 15.
792 Rooted<PlainDateTimeWithCalendar> intermediateDateTime(cx);
793 if (!CreateTemporalDateTime(cx, {addedDate, time}, calendar.receiver(),
794 &intermediateDateTime)) {
795 return false;
798 // Step 16.
799 Instant intermediateInstant;
800 if (!GetInstantFor(cx, timeZone, intermediateDateTime,
801 TemporalDisambiguation::Compatible,
802 &intermediateInstant)) {
803 return false;
806 // Step 17.
807 return AddInstant(cx, intermediateInstant, duration.time, result);
811 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
812 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
814 static bool AddZonedDateTime(JSContext* cx, const Instant& epochNanoseconds,
815 Handle<TimeZoneRecord> timeZone,
816 Handle<CalendarRecord> calendar,
817 const NormalizedDuration& duration,
818 Handle<JSObject*> maybeOptions, Instant* result) {
819 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar, duration,
820 mozilla::Nothing(), maybeOptions, result);
824 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
825 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
827 bool js::temporal::AddZonedDateTime(JSContext* cx,
828 const Instant& epochNanoseconds,
829 Handle<TimeZoneRecord> timeZone,
830 Handle<CalendarRecord> calendar,
831 const NormalizedDuration& duration,
832 Instant* result) {
833 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar, duration,
834 mozilla::Nothing(), nullptr, result);
838 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
839 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
841 bool js::temporal::AddZonedDateTime(JSContext* cx,
842 const Instant& epochNanoseconds,
843 Handle<TimeZoneRecord> timeZone,
844 Handle<CalendarRecord> calendar,
845 const NormalizedDuration& duration,
846 const PlainDateTime& dateTime,
847 Instant* result) {
848 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar, duration,
849 mozilla::SomeRef(dateTime), nullptr, result);
853 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
854 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
856 bool js::temporal::AddZonedDateTime(JSContext* cx,
857 const Instant& epochNanoseconds,
858 Handle<TimeZoneRecord> timeZone,
859 Handle<CalendarRecord> calendar,
860 const DateDuration& duration,
861 Instant* result) {
862 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar,
863 {duration, {}}, mozilla::Nothing(), nullptr,
864 result);
868 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
869 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
871 bool js::temporal::AddZonedDateTime(JSContext* cx,
872 const Instant& epochNanoseconds,
873 Handle<TimeZoneRecord> timeZone,
874 Handle<CalendarRecord> calendar,
875 const DateDuration& duration,
876 const PlainDateTime& dateTime,
877 Instant* result) {
878 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar,
879 {duration, {}}, mozilla::SomeRef(dateTime), nullptr,
880 result);
884 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
885 * precalculatedPlainDateTime ] )
887 static bool NormalizedTimeDurationToDays(
888 JSContext* cx, const NormalizedTimeDuration& duration,
889 Handle<ZonedDateTime> zonedRelativeTo, Handle<TimeZoneRecord> timeZone,
890 mozilla::Maybe<const PlainDateTime&> precalculatedPlainDateTime,
891 NormalizedTimeAndDays* result) {
892 MOZ_ASSERT(IsValidNormalizedTimeDuration(duration));
894 // Step 1.
895 int32_t sign = NormalizedTimeDurationSign(duration);
897 // Step 2.
898 if (sign == 0) {
899 *result = {int64_t(0), int64_t(0), ToNanoseconds(TemporalUnit::Day)};
900 return true;
903 // Step 3.
904 const auto& startNs = zonedRelativeTo.instant();
906 // Step 5.
907 Instant endNs;
908 if (!AddInstant(cx, startNs, duration, &endNs)) {
909 return false;
912 // Steps 4 and 7.
913 PlainDateTime startDateTime;
914 if (!precalculatedPlainDateTime) {
915 if (!GetPlainDateTimeFor(cx, timeZone, startNs, &startDateTime)) {
916 return false;
918 } else {
919 startDateTime = *precalculatedPlainDateTime;
922 // Steps 6 and 8.
923 PlainDateTime endDateTime;
924 if (!GetPlainDateTimeFor(cx, timeZone, endNs, &endDateTime)) {
925 return false;
928 // Steps 9-10. (Not applicable in our implementation.)
930 // Step 11.
931 int32_t days = DaysUntil(startDateTime.date, endDateTime.date);
932 MOZ_ASSERT(std::abs(days) <= 200'000'000);
934 // Step 12.
935 int32_t timeSign = CompareTemporalTime(startDateTime.time, endDateTime.time);
937 // Steps 13-14.
938 if (days > 0 && timeSign > 0) {
939 days -= 1;
940 } else if (days < 0 && timeSign < 0) {
941 days += 1;
944 // Step 15.
945 PlainDateTimeAndInstant relativeResult;
946 if (!::AddDaysToZonedDateTime(cx, startNs, startDateTime, timeZone,
947 zonedRelativeTo.calendar(), days,
948 TemporalOverflow::Constrain, &relativeResult)) {
949 return false;
951 MOZ_ASSERT(IsValidISODateTime(relativeResult.dateTime));
952 MOZ_ASSERT(IsValidEpochInstant(relativeResult.instant));
954 // Step 16.
955 if (sign > 0 && days > 0 && relativeResult.instant > endNs) {
956 // Step 16.a.
957 days -= 1;
959 // Step 16.b.
960 if (!::AddDaysToZonedDateTime(
961 cx, startNs, startDateTime, timeZone, zonedRelativeTo.calendar(),
962 days, TemporalOverflow::Constrain, &relativeResult)) {
963 return false;
965 MOZ_ASSERT(IsValidISODateTime(relativeResult.dateTime));
966 MOZ_ASSERT(IsValidEpochInstant(relativeResult.instant));
968 // Step 16.c.
969 if (days > 0 && relativeResult.instant > endNs) {
970 JS_ReportErrorNumberASCII(
971 cx, GetErrorMessage, nullptr,
972 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCONSISTENT_INSTANT);
973 return false;
975 MOZ_ASSERT_IF(days > 0, relativeResult.instant <= endNs);
978 MOZ_ASSERT_IF(days == 0, relativeResult.instant == startNs);
980 // Step 17. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
981 auto ns = endNs - relativeResult.instant;
982 MOZ_ASSERT(IsValidInstantSpan(ns));
984 // Step 18.
985 PlainDateTimeAndInstant oneDayFarther;
986 if (!::AddDaysToZonedDateTime(cx, relativeResult.instant,
987 relativeResult.dateTime, timeZone,
988 zonedRelativeTo.calendar(), sign,
989 TemporalOverflow::Constrain, &oneDayFarther)) {
990 return false;
992 MOZ_ASSERT(IsValidISODateTime(oneDayFarther.dateTime));
993 MOZ_ASSERT(IsValidEpochInstant(oneDayFarther.instant));
995 // FIXME: spec issue - bad markup for |oneDayFarther|.
997 // Step 19. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
998 auto dayLengthNs = oneDayFarther.instant - relativeResult.instant;
999 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs));
1001 // clang-format off
1003 // ns = endNs - relativeResult.instant
1004 // dayLengthNs = oneDayFarther.instant - relativeResult.instant
1005 // oneDayLess = ns - dayLengthNs
1006 // = (endNs - relativeResult.instant) - (oneDayFarther.instant - relativeResult.instant)
1007 // = endNs - relativeResult.instant - oneDayFarther.instant + relativeResult.instant
1008 // = endNs - oneDayFarther.instant
1010 // |endNs| and |oneDayFarther.instant| are both valid epoch instant values,
1011 // so the difference |oneDayLess| is a valid epoch instant difference value.
1013 // clang-format on
1015 // Step 20. (Inlined SubtractNormalizedTimeDuration)
1016 auto oneDayLess = ns - dayLengthNs;
1017 MOZ_ASSERT(IsValidInstantSpan(oneDayLess));
1018 MOZ_ASSERT(oneDayLess == (endNs - oneDayFarther.instant));
1020 // Step 21.
1021 if (oneDayLess == InstantSpan{} ||
1022 ((oneDayLess < InstantSpan{}) == (sign < 0))) {
1023 // Step 21.a.
1024 ns = oneDayLess;
1026 // Step 21.b.
1027 relativeResult = oneDayFarther;
1029 // Step 21.c.
1030 days += sign;
1032 // Step 21.d.
1033 PlainDateTimeAndInstant oneDayFarther;
1034 if (!::AddDaysToZonedDateTime(
1035 cx, relativeResult.instant, relativeResult.dateTime, timeZone,
1036 zonedRelativeTo.calendar(), sign, TemporalOverflow::Constrain,
1037 &oneDayFarther)) {
1038 return false;
1040 MOZ_ASSERT(IsValidISODateTime(oneDayFarther.dateTime));
1041 MOZ_ASSERT(IsValidEpochInstant(oneDayFarther.instant));
1043 // FIXME: spec issue - bad markup for |oneDayFarther|.
1045 // Step 21.e. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
1046 dayLengthNs = oneDayFarther.instant - relativeResult.instant;
1047 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs));
1049 // clang-format off
1051 // ns = oneDayLess'
1052 // = endNs - oneDayFarther.instant'
1053 // relativeResult.instant = oneDayFarther.instant'
1054 // dayLengthNs = oneDayFarther.instant - relativeResult.instant
1055 // = oneDayFarther.instant - oneDayFarther.instant'
1056 // oneDayLess = ns - dayLengthNs
1057 // = (endNs - oneDayFarther.instant') - (oneDayFarther.instant - oneDayFarther.instant')
1058 // = endNs - oneDayFarther.instant' - oneDayFarther.instant + oneDayFarther.instant'
1059 // = endNs - oneDayFarther.instant
1061 // Where |oneDayLess'| and |oneDayFarther.instant'| denote the variables
1062 // from before this if-statement block.
1064 // |endNs| and |oneDayFarther.instant| are both valid epoch instant values,
1065 // so the difference |oneDayLess| is a valid epoch instant difference value.
1067 // clang-format on
1069 // FIXME: spec issue - SubtractNormalizedTimeDuration is infallible
1071 // Step 21.f.
1072 auto oneDayLess = ns - dayLengthNs;
1073 if (oneDayLess == InstantSpan{} ||
1074 ((oneDayLess < InstantSpan{}) == (sign < 0))) {
1075 JS_ReportErrorNumberASCII(
1076 cx, GetErrorMessage, nullptr,
1077 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCONSISTENT_INSTANT);
1078 return false;
1082 // Step 22.
1083 if (days < 0 && sign > 0) {
1084 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1085 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN,
1086 "days");
1087 return false;
1090 // Step 23.
1091 if (days > 0 && sign < 0) {
1092 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1093 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN,
1094 "days");
1095 return false;
1098 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs));
1099 MOZ_ASSERT(IsValidInstantSpan(ns));
1101 // Steps 24-25.
1102 if (sign < 0) {
1103 if (ns > InstantSpan{}) {
1104 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1105 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN,
1106 "nanoseconds");
1107 return false;
1109 } else {
1110 MOZ_ASSERT(ns >= InstantSpan{});
1113 // Steps 26-27.
1114 dayLengthNs = dayLengthNs.abs();
1115 MOZ_ASSERT(ns.abs() < dayLengthNs);
1117 // Step 28.
1118 constexpr auto maxDayLength = Int128{1} << 53;
1119 auto dayLengthNanos = dayLengthNs.toNanoseconds();
1120 if (dayLengthNanos >= maxDayLength) {
1121 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1122 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN,
1123 "days");
1124 return false;
1127 auto timeNanos = ns.toNanoseconds();
1128 MOZ_ASSERT(timeNanos == Int128{int64_t(timeNanos)},
1129 "abs(ns) < dayLengthNs < 2**53 implies that |ns| fits in int64");
1131 // Step 29.
1132 static_assert(std::numeric_limits<decltype(days)>::max() <=
1133 ((int64_t(1) << 53) / (24 * 60 * 60)));
1135 // Step 30.
1136 *result = {int64_t(days), int64_t(timeNanos), int64_t(dayLengthNanos)};
1137 return true;
1141 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
1142 * precalculatedPlainDateTime ] )
1144 bool js::temporal::NormalizedTimeDurationToDays(
1145 JSContext* cx, const NormalizedTimeDuration& duration,
1146 Handle<ZonedDateTime> zonedRelativeTo, Handle<TimeZoneRecord> timeZone,
1147 NormalizedTimeAndDays* result) {
1148 return ::NormalizedTimeDurationToDays(cx, duration, zonedRelativeTo, timeZone,
1149 mozilla::Nothing(), result);
1153 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
1154 * precalculatedPlainDateTime ] )
1156 bool js::temporal::NormalizedTimeDurationToDays(
1157 JSContext* cx, const NormalizedTimeDuration& duration,
1158 Handle<ZonedDateTime> zonedRelativeTo, Handle<TimeZoneRecord> timeZone,
1159 const PlainDateTime& precalculatedPlainDateTime,
1160 NormalizedTimeAndDays* result) {
1161 return ::NormalizedTimeDurationToDays(
1162 cx, duration, zonedRelativeTo, timeZone,
1163 mozilla::SomeRef(precalculatedPlainDateTime), result);
1167 * DifferenceZonedDateTime ( ns1, ns2, timeZoneRec, calendarRec, largestUnit,
1168 * options, precalculatedPlainDateTime )
1170 static bool DifferenceZonedDateTime(
1171 JSContext* cx, const Instant& ns1, const Instant& ns2,
1172 Handle<TimeZoneRecord> timeZone, Handle<CalendarRecord> calendar,
1173 TemporalUnit largestUnit, Handle<PlainObject*> maybeOptions,
1174 mozilla::Maybe<const PlainDateTime&> precalculatedPlainDateTime,
1175 NormalizedDuration* result) {
1176 MOZ_ASSERT(IsValidEpochInstant(ns1));
1177 MOZ_ASSERT(IsValidEpochInstant(ns2));
1179 // Steps 1.
1180 if (ns1 == ns2) {
1181 *result = CreateNormalizedDurationRecord({}, {});
1182 return true;
1185 // Steps 2-3.
1186 PlainDateTime startDateTime;
1187 if (!precalculatedPlainDateTime) {
1188 // Steps 2.a-b.
1189 if (!GetPlainDateTimeFor(cx, timeZone, ns1, &startDateTime)) {
1190 return false;
1192 } else {
1193 // Step 3.a.
1194 startDateTime = *precalculatedPlainDateTime;
1197 // Steps 4-5.
1198 PlainDateTime endDateTime;
1199 if (!GetPlainDateTimeFor(cx, timeZone, ns2, &endDateTime)) {
1200 return false;
1203 // Step 6.
1204 DateDuration dateDifference;
1205 if (maybeOptions) {
1206 if (!DifferenceISODateTime(cx, startDateTime, endDateTime, calendar,
1207 largestUnit, maybeOptions, &dateDifference)) {
1208 return false;
1210 } else {
1211 if (!DifferenceISODateTime(cx, startDateTime, endDateTime, calendar,
1212 largestUnit, &dateDifference)) {
1213 return false;
1217 // Step 7.
1218 Instant intermediateNs;
1219 if (!AddZonedDateTime(cx, ns1, timeZone, calendar,
1220 DateDuration{
1221 dateDifference.years,
1222 dateDifference.months,
1223 dateDifference.weeks,
1225 startDateTime, &intermediateNs)) {
1226 return false;
1228 MOZ_ASSERT(IsValidEpochInstant(intermediateNs));
1230 // Step 8.
1231 auto timeDuration =
1232 NormalizedTimeDurationFromEpochNanosecondsDifference(ns2, intermediateNs);
1234 // Step 9.
1235 Rooted<ZonedDateTime> intermediate(
1237 ZonedDateTime{intermediateNs, timeZone.receiver(), calendar.receiver()});
1239 // Step 10.
1240 NormalizedTimeAndDays timeAndDays;
1241 if (!NormalizedTimeDurationToDays(cx, timeDuration, intermediate, timeZone,
1242 &timeAndDays)) {
1243 return false;
1246 // Step 11.
1247 auto dateDuration = DateDuration{
1248 dateDifference.years,
1249 dateDifference.months,
1250 dateDifference.weeks,
1251 timeAndDays.days,
1253 if (!ThrowIfInvalidDuration(cx, dateDuration)) {
1254 return false;
1257 return CreateNormalizedDurationRecord(
1258 cx, dateDuration,
1259 NormalizedTimeDuration::fromNanoseconds(timeAndDays.time), result);
1263 * DifferenceZonedDateTime ( ns1, ns2, timeZoneRec, calendarRec, largestUnit,
1264 * options, precalculatedPlainDateTime )
1266 bool js::temporal::DifferenceZonedDateTime(
1267 JSContext* cx, const Instant& ns1, const Instant& ns2,
1268 Handle<TimeZoneRecord> timeZone, Handle<CalendarRecord> calendar,
1269 TemporalUnit largestUnit, const PlainDateTime& precalculatedPlainDateTime,
1270 NormalizedDuration* result) {
1271 return ::DifferenceZonedDateTime(
1272 cx, ns1, ns2, timeZone, calendar, largestUnit, nullptr,
1273 mozilla::SomeRef(precalculatedPlainDateTime), result);
1277 * TimeZoneEquals ( one, two )
1279 static bool TimeZoneEqualsOrThrow(JSContext* cx, Handle<TimeZoneValue> one,
1280 Handle<TimeZoneValue> two) {
1281 // Step 1.
1282 if (one.isObject() && two.isObject() && one.toObject() == two.toObject()) {
1283 return true;
1286 // Step 2.
1287 Rooted<JSString*> timeZoneOne(cx, ToTemporalTimeZoneIdentifier(cx, one));
1288 if (!timeZoneOne) {
1289 return false;
1292 // Step 3.
1293 Rooted<JSString*> timeZoneTwo(cx, ToTemporalTimeZoneIdentifier(cx, two));
1294 if (!timeZoneTwo) {
1295 return false;
1298 // Steps 4-9.
1299 bool equals;
1300 if (!TimeZoneEquals(cx, timeZoneOne, timeZoneTwo, &equals)) {
1301 return false;
1303 if (equals) {
1304 return true;
1307 // Throw an error when the time zone identifiers don't match. Used when
1308 // unequal time zones throw a RangeError.
1309 if (auto charsOne = QuoteString(cx, timeZoneOne)) {
1310 if (auto charsTwo = QuoteString(cx, timeZoneTwo)) {
1311 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1312 JSMSG_TEMPORAL_TIMEZONE_INCOMPATIBLE,
1313 charsOne.get(), charsTwo.get());
1316 return false;
1320 * RoundISODateTime ( year, month, day, hour, minute, second, millisecond,
1321 * microsecond, nanosecond, increment, unit, roundingMode [ , dayLength ] )
1323 static bool RoundISODateTime(JSContext* cx, const PlainDateTime& dateTime,
1324 Increment increment, TemporalUnit unit,
1325 TemporalRoundingMode roundingMode,
1326 const InstantSpan& dayLength,
1327 PlainDateTime* result) {
1328 MOZ_ASSERT(IsValidInstantSpan(dayLength));
1329 MOZ_ASSERT(dayLength > (InstantSpan{}));
1331 const auto& [date, time] = dateTime;
1333 // Step 1.
1334 MOZ_ASSERT(IsValidISODateTime(dateTime));
1335 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
1337 // Step 2. (Not applicable in our implementation.)
1339 // Step 3.
1340 auto roundedTime = RoundTime(time, increment, unit, roundingMode, dayLength);
1342 // |dayLength| can be as small as 1, so the number of rounded days can be as
1343 // large as the number of nanoseconds in |time|.
1344 MOZ_ASSERT(0 <= roundedTime.days &&
1345 roundedTime.days < ToNanoseconds(TemporalUnit::Day));
1347 // Step 4.
1348 PlainDate balanceResult;
1349 if (!BalanceISODate(cx, date.year, date.month,
1350 int64_t(date.day) + roundedTime.days, &balanceResult)) {
1351 return false;
1354 // Step 5.
1355 *result = {balanceResult, roundedTime.time};
1356 return true;
1360 * DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options )
1362 static bool DifferenceTemporalZonedDateTime(JSContext* cx,
1363 TemporalDifference operation,
1364 const CallArgs& args) {
1365 Rooted<ZonedDateTime> zonedDateTime(
1366 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1368 // Step 1. (Not applicable in our implementation.)
1370 // Step 2.
1371 Rooted<ZonedDateTime> other(cx);
1372 if (!ToTemporalZonedDateTime(cx, args.get(0), &other)) {
1373 return false;
1376 // Step 3.
1377 if (!CalendarEqualsOrThrow(cx, zonedDateTime.calendar(), other.calendar())) {
1378 return false;
1381 // Steps 4-5.
1382 Rooted<PlainObject*> resolvedOptions(cx);
1383 DifferenceSettings settings;
1384 if (args.hasDefined(1)) {
1385 Rooted<JSObject*> options(
1386 cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
1387 if (!options) {
1388 return false;
1391 // Step 4.
1392 resolvedOptions = SnapshotOwnProperties(cx, options);
1393 if (!resolvedOptions) {
1394 return false;
1397 // Step 5.
1398 if (!GetDifferenceSettings(
1399 cx, operation, resolvedOptions, TemporalUnitGroup::DateTime,
1400 TemporalUnit::Nanosecond, TemporalUnit::Hour, &settings)) {
1401 return false;
1403 } else {
1404 // Steps 4-5.
1405 settings = {
1406 TemporalUnit::Nanosecond,
1407 TemporalUnit::Hour,
1408 TemporalRoundingMode::Trunc,
1409 Increment{1},
1413 // Step 6.
1414 if (settings.largestUnit > TemporalUnit::Day) {
1415 MOZ_ASSERT(settings.smallestUnit >= settings.largestUnit);
1417 // Step 6.a.
1418 auto difference = DifferenceInstant(
1419 zonedDateTime.instant(), other.instant(), settings.roundingIncrement,
1420 settings.smallestUnit, settings.roundingMode);
1422 // Step 6.b.
1423 auto balancedTime = BalanceTimeDuration(difference, settings.largestUnit);
1425 // Step 6.c.
1426 auto duration = balancedTime.toDuration();
1427 if (operation == TemporalDifference::Since) {
1428 duration = duration.negate();
1431 auto* result = CreateTemporalDuration(cx, duration);
1432 if (!result) {
1433 return false;
1436 args.rval().setObject(*result);
1437 return true;
1440 // Steps 7-8.
1441 if (!TimeZoneEqualsOrThrow(cx, zonedDateTime.timeZone(), other.timeZone())) {
1442 return false;
1445 // Step 9.
1446 if (zonedDateTime.instant() == other.instant()) {
1447 auto* obj = CreateTemporalDuration(cx, {});
1448 if (!obj) {
1449 return false;
1452 args.rval().setObject(*obj);
1453 return true;
1456 // Step 10.
1457 Rooted<TimeZoneRecord> timeZone(cx);
1458 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
1460 TimeZoneMethod::GetOffsetNanosecondsFor,
1461 TimeZoneMethod::GetPossibleInstantsFor,
1463 &timeZone)) {
1464 return false;
1467 // Step 11.
1468 Rooted<CalendarRecord> calendar(cx);
1469 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
1471 CalendarMethod::DateAdd,
1472 CalendarMethod::DateUntil,
1474 &calendar)) {
1475 return false;
1478 // Steps 12-13.
1479 PlainDateTime precalculatedPlainDateTime;
1480 if (!GetPlainDateTimeFor(cx, timeZone, zonedDateTime.instant(),
1481 &precalculatedPlainDateTime)) {
1482 return false;
1485 // Step 14.
1486 Rooted<PlainDateObject*> plainRelativeTo(
1487 cx, CreateTemporalDate(cx, precalculatedPlainDateTime.date,
1488 calendar.receiver()));
1489 if (!plainRelativeTo) {
1490 return false;
1493 // Step 15.
1494 if (resolvedOptions) {
1495 Rooted<Value> largestUnitValue(
1496 cx, StringValue(TemporalUnitToString(cx, settings.largestUnit)));
1497 if (!DefineDataProperty(cx, resolvedOptions, cx->names().largestUnit,
1498 largestUnitValue)) {
1499 return false;
1503 // Step 16.
1504 NormalizedDuration difference;
1505 if (!::DifferenceZonedDateTime(
1506 cx, zonedDateTime.instant(), other.instant(), timeZone, calendar,
1507 settings.largestUnit, resolvedOptions,
1508 mozilla::SomeRef<const PlainDateTime>(precalculatedPlainDateTime),
1509 &difference)) {
1510 return false;
1513 // Step 17.
1514 bool roundingGranularityIsNoop =
1515 settings.smallestUnit == TemporalUnit::Nanosecond &&
1516 settings.roundingIncrement == Increment{1};
1518 // Step 18.
1519 if (!roundingGranularityIsNoop) {
1520 // Steps 18.a-b.
1521 NormalizedDuration roundResult;
1522 if (!RoundDuration(cx, difference, settings.roundingIncrement,
1523 settings.smallestUnit, settings.roundingMode,
1524 plainRelativeTo, calendar, zonedDateTime, timeZone,
1525 precalculatedPlainDateTime, &roundResult)) {
1526 return false;
1529 // Step 18.c.
1530 NormalizedTimeAndDays timeAndDays;
1531 if (!NormalizedTimeDurationToDays(cx, roundResult.time, zonedDateTime,
1532 timeZone, &timeAndDays)) {
1533 return false;
1536 // Step 18.d.
1537 int64_t days = roundResult.date.days + timeAndDays.days;
1539 // Step 18.e.
1540 auto toAdjust = NormalizedDuration{
1542 roundResult.date.years,
1543 roundResult.date.months,
1544 roundResult.date.weeks,
1545 days,
1547 NormalizedTimeDuration::fromNanoseconds(timeAndDays.time),
1549 NormalizedDuration adjustResult;
1550 if (!AdjustRoundedDurationDays(cx, toAdjust, settings.roundingIncrement,
1551 settings.smallestUnit, settings.roundingMode,
1552 zonedDateTime, calendar, timeZone,
1553 precalculatedPlainDateTime, &adjustResult)) {
1554 return false;
1557 // Step 18.f.
1558 DateDuration balanceResult;
1559 if (!temporal::BalanceDateDurationRelative(
1560 cx, adjustResult.date, settings.largestUnit, settings.smallestUnit,
1561 plainRelativeTo, calendar, &balanceResult)) {
1562 return false;
1565 // Step 18.g.
1566 if (!CombineDateAndNormalizedTimeDuration(cx, balanceResult,
1567 adjustResult.time, &difference)) {
1568 return false;
1572 // Step 19.
1573 auto timeDuration = BalanceTimeDuration(difference.time, TemporalUnit::Hour);
1575 // Step 20.
1576 auto duration = Duration{
1577 double(difference.date.years), double(difference.date.months),
1578 double(difference.date.weeks), double(difference.date.days),
1579 double(timeDuration.hours), double(timeDuration.minutes),
1580 double(timeDuration.seconds), double(timeDuration.milliseconds),
1581 timeDuration.microseconds, timeDuration.nanoseconds,
1583 if (operation == TemporalDifference::Since) {
1584 duration = duration.negate();
1586 MOZ_ASSERT(IsValidDuration(duration));
1588 auto* obj = CreateTemporalDuration(cx, duration);
1589 if (!obj) {
1590 return false;
1593 args.rval().setObject(*obj);
1594 return true;
1597 enum class ZonedDateTimeDuration { Add, Subtract };
1600 * AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime,
1601 * temporalDurationLike, options )
1603 static bool AddDurationToOrSubtractDurationFromZonedDateTime(
1604 JSContext* cx, ZonedDateTimeDuration operation, const CallArgs& args) {
1605 Rooted<ZonedDateTime> zonedDateTime(
1606 cx, &args.thisv().toObject().as<ZonedDateTimeObject>());
1608 // Step 1. (Not applicable in our implementation.)
1610 // Step 2.
1611 Duration duration;
1612 if (!ToTemporalDurationRecord(cx, args.get(0), &duration)) {
1613 return false;
1616 // Step 3.
1617 Rooted<JSObject*> options(cx);
1618 if (args.hasDefined(1)) {
1619 const char* name =
1620 operation == ZonedDateTimeDuration::Add ? "add" : "subtract";
1621 options = RequireObjectArg(cx, "options", name, args[1]);
1622 } else {
1623 options = NewPlainObjectWithProto(cx, nullptr);
1625 if (!options) {
1626 return false;
1629 // Step 4.
1630 Rooted<TimeZoneRecord> timeZone(cx);
1631 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
1633 TimeZoneMethod::GetOffsetNanosecondsFor,
1634 TimeZoneMethod::GetPossibleInstantsFor,
1636 &timeZone)) {
1637 return false;
1640 // Step 5.
1641 Rooted<CalendarRecord> calendar(cx);
1642 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
1644 CalendarMethod::DateAdd,
1646 &calendar)) {
1647 return false;
1650 // Step 6.
1651 if (operation == ZonedDateTimeDuration::Subtract) {
1652 duration = duration.negate();
1654 auto normalized = CreateNormalizedDurationRecord(duration);
1656 // Step 7.
1657 Instant resultInstant;
1658 if (!::AddZonedDateTime(cx, zonedDateTime.instant(), timeZone, calendar,
1659 normalized, options, &resultInstant)) {
1660 return false;
1662 MOZ_ASSERT(IsValidEpochInstant(resultInstant));
1664 // Step 8.
1665 auto* result = CreateTemporalZonedDateTime(
1666 cx, resultInstant, timeZone.receiver(), calendar.receiver());
1667 if (!result) {
1668 return false;
1671 args.rval().setObject(*result);
1672 return true;
1676 * Temporal.ZonedDateTime ( epochNanoseconds, timeZoneLike [ , calendarLike ] )
1678 static bool ZonedDateTimeConstructor(JSContext* cx, unsigned argc, Value* vp) {
1679 CallArgs args = CallArgsFromVp(argc, vp);
1681 // Step 1.
1682 if (!ThrowIfNotConstructing(cx, args, "Temporal.ZonedDateTime")) {
1683 return false;
1686 // Step 2.
1687 Rooted<BigInt*> epochNanoseconds(cx, js::ToBigInt(cx, args.get(0)));
1688 if (!epochNanoseconds) {
1689 return false;
1692 // Step 3.
1693 if (!IsValidEpochNanoseconds(epochNanoseconds)) {
1694 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1695 JSMSG_TEMPORAL_INSTANT_INVALID);
1696 return false;
1699 // Step 4.
1700 Rooted<TimeZoneValue> timeZone(cx);
1701 if (!ToTemporalTimeZone(cx, args.get(1), &timeZone)) {
1702 return false;
1705 // Step 5.
1706 Rooted<CalendarValue> calendar(cx);
1707 if (!ToTemporalCalendarWithISODefault(cx, args.get(2), &calendar)) {
1708 return false;
1711 // Step 6.
1712 auto* obj = CreateTemporalZonedDateTime(cx, args, epochNanoseconds, timeZone,
1713 calendar);
1714 if (!obj) {
1715 return false;
1718 args.rval().setObject(*obj);
1719 return true;
1723 * Temporal.ZonedDateTime.from ( item [ , options ] )
1725 static bool ZonedDateTime_from(JSContext* cx, unsigned argc, Value* vp) {
1726 CallArgs args = CallArgsFromVp(argc, vp);
1728 // Step 1.
1729 Rooted<JSObject*> options(cx);
1730 if (args.hasDefined(1)) {
1731 options = RequireObjectArg(cx, "options", "from", args[1]);
1732 if (!options) {
1733 return false;
1737 // Step 2.
1738 if (args.get(0).isObject()) {
1739 JSObject* item = &args[0].toObject();
1740 if (auto* zonedDateTime = item->maybeUnwrapIf<ZonedDateTimeObject>()) {
1741 auto epochInstant = ToInstant(zonedDateTime);
1742 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
1743 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
1745 if (!timeZone.wrap(cx)) {
1746 return false;
1748 if (!calendar.wrap(cx)) {
1749 return false;
1752 if (options) {
1753 // Steps 2.a-b.
1754 TemporalDisambiguation ignoredDisambiguation;
1755 if (!ToTemporalDisambiguation(cx, options, &ignoredDisambiguation)) {
1756 return false;
1759 // Step 2.c.
1760 TemporalOffset ignoredOffset;
1761 if (!ToTemporalOffset(cx, options, &ignoredOffset)) {
1762 return false;
1765 // Step 2.d.
1766 TemporalOverflow ignoredOverflow;
1767 if (!ToTemporalOverflow(cx, options, &ignoredOverflow)) {
1768 return false;
1772 // Step 2.e.
1773 auto* result =
1774 CreateTemporalZonedDateTime(cx, epochInstant, timeZone, calendar);
1775 if (!result) {
1776 return false;
1779 args.rval().setObject(*result);
1780 return true;
1784 // Step 3.
1785 auto* result = ToTemporalZonedDateTime(cx, args.get(0), options);
1786 if (!result) {
1787 return false;
1790 args.rval().setObject(*result);
1791 return true;
1795 * Temporal.ZonedDateTime.compare ( one, two )
1797 static bool ZonedDateTime_compare(JSContext* cx, unsigned argc, Value* vp) {
1798 CallArgs args = CallArgsFromVp(argc, vp);
1800 // Step 1.
1801 Rooted<ZonedDateTime> one(cx);
1802 if (!ToTemporalZonedDateTime(cx, args.get(0), &one)) {
1803 return false;
1806 // Step 2.
1807 Rooted<ZonedDateTime> two(cx);
1808 if (!ToTemporalZonedDateTime(cx, args.get(1), &two)) {
1809 return false;
1812 // Step 3.
1813 const auto& oneNs = one.instant();
1814 const auto& twoNs = two.instant();
1815 args.rval().setInt32(oneNs > twoNs ? 1 : oneNs < twoNs ? -1 : 0);
1816 return true;
1820 * get Temporal.ZonedDateTime.prototype.calendarId
1822 static bool ZonedDateTime_calendarId(JSContext* cx, const CallArgs& args) {
1823 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
1825 // Step 3.
1826 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
1827 auto* calendarId = ToTemporalCalendarIdentifier(cx, calendar);
1828 if (!calendarId) {
1829 return false;
1832 args.rval().setString(calendarId);
1833 return true;
1837 * get Temporal.ZonedDateTime.prototype.calendarId
1839 static bool ZonedDateTime_calendarId(JSContext* cx, unsigned argc, Value* vp) {
1840 // Steps 1-2.
1841 CallArgs args = CallArgsFromVp(argc, vp);
1842 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_calendarId>(cx,
1843 args);
1847 * get Temporal.ZonedDateTime.prototype.timeZoneId
1849 static bool ZonedDateTime_timeZoneId(JSContext* cx, const CallArgs& args) {
1850 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
1852 // Step 3.
1853 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
1854 auto* timeZoneId = ToTemporalTimeZoneIdentifier(cx, timeZone);
1855 if (!timeZoneId) {
1856 return false;
1859 args.rval().setString(timeZoneId);
1860 return true;
1864 * get Temporal.ZonedDateTime.prototype.timeZoneId
1866 static bool ZonedDateTime_timeZoneId(JSContext* cx, unsigned argc, Value* vp) {
1867 // Steps 1-2.
1868 CallArgs args = CallArgsFromVp(argc, vp);
1869 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_timeZoneId>(cx,
1870 args);
1874 * get Temporal.ZonedDateTime.prototype.year
1876 static bool ZonedDateTime_year(JSContext* cx, const CallArgs& args) {
1877 Rooted<ZonedDateTime> zonedDateTime(
1878 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1880 // Steps 3-6.
1881 PlainDateTime dateTime;
1882 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
1883 zonedDateTime.instant(), &dateTime)) {
1884 return false;
1887 // Step 7.
1888 return CalendarYear(cx, zonedDateTime.calendar(), dateTime, args.rval());
1892 * get Temporal.ZonedDateTime.prototype.year
1894 static bool ZonedDateTime_year(JSContext* cx, unsigned argc, Value* vp) {
1895 // Steps 1-2.
1896 CallArgs args = CallArgsFromVp(argc, vp);
1897 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_year>(cx, args);
1901 * get Temporal.ZonedDateTime.prototype.month
1903 static bool ZonedDateTime_month(JSContext* cx, const CallArgs& args) {
1904 Rooted<ZonedDateTime> zonedDateTime(
1905 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1907 // Steps 3-6.
1908 PlainDateTime dateTime;
1909 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
1910 zonedDateTime.instant(), &dateTime)) {
1911 return false;
1914 // Step 7.
1915 return CalendarMonth(cx, zonedDateTime.calendar(), dateTime, args.rval());
1919 * get Temporal.ZonedDateTime.prototype.month
1921 static bool ZonedDateTime_month(JSContext* cx, unsigned argc, Value* vp) {
1922 // Steps 1-2.
1923 CallArgs args = CallArgsFromVp(argc, vp);
1924 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_month>(cx, args);
1928 * get Temporal.ZonedDateTime.prototype.monthCode
1930 static bool ZonedDateTime_monthCode(JSContext* cx, const CallArgs& args) {
1931 Rooted<ZonedDateTime> zonedDateTime(
1932 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1934 // Steps 3-6.
1935 PlainDateTime dateTime;
1936 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
1937 zonedDateTime.instant(), &dateTime)) {
1938 return false;
1941 // Step 7.
1942 return CalendarMonthCode(cx, zonedDateTime.calendar(), dateTime, args.rval());
1946 * get Temporal.ZonedDateTime.prototype.monthCode
1948 static bool ZonedDateTime_monthCode(JSContext* cx, unsigned argc, Value* vp) {
1949 // Steps 1-2.
1950 CallArgs args = CallArgsFromVp(argc, vp);
1951 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_monthCode>(cx,
1952 args);
1956 * get Temporal.ZonedDateTime.prototype.day
1958 static bool ZonedDateTime_day(JSContext* cx, const CallArgs& args) {
1959 Rooted<ZonedDateTime> zonedDateTime(
1960 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1962 // Step 4. (Reordered)
1963 Rooted<CalendarRecord> calendar(cx);
1964 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
1966 CalendarMethod::Day,
1968 &calendar)) {
1969 return false;
1972 // Steps 3 and 5-6.
1973 PlainDateTime dateTime;
1974 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
1975 zonedDateTime.instant(), &dateTime)) {
1976 return false;
1979 // Step 7.
1980 return CalendarDay(cx, calendar, dateTime, args.rval());
1984 * get Temporal.ZonedDateTime.prototype.day
1986 static bool ZonedDateTime_day(JSContext* cx, unsigned argc, Value* vp) {
1987 // Steps 1-2.
1988 CallArgs args = CallArgsFromVp(argc, vp);
1989 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_day>(cx, args);
1993 * get Temporal.ZonedDateTime.prototype.hour
1995 static bool ZonedDateTime_hour(JSContext* cx, const CallArgs& args) {
1996 Rooted<ZonedDateTime> zonedDateTime(
1997 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1999 // Steps 3-6.
2000 PlainDateTime dateTime;
2001 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2002 zonedDateTime.instant(), &dateTime)) {
2003 return false;
2006 // Step 7.
2007 args.rval().setInt32(dateTime.time.hour);
2008 return true;
2012 * get Temporal.ZonedDateTime.prototype.hour
2014 static bool ZonedDateTime_hour(JSContext* cx, unsigned argc, Value* vp) {
2015 // Steps 1-2.
2016 CallArgs args = CallArgsFromVp(argc, vp);
2017 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_hour>(cx, args);
2021 * get Temporal.ZonedDateTime.prototype.minute
2023 static bool ZonedDateTime_minute(JSContext* cx, const CallArgs& args) {
2024 Rooted<ZonedDateTime> zonedDateTime(
2025 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2027 // Steps 3-6.
2028 PlainDateTime dateTime;
2029 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2030 zonedDateTime.instant(), &dateTime)) {
2031 return false;
2034 // Step 7.
2035 args.rval().setInt32(dateTime.time.minute);
2036 return true;
2040 * get Temporal.ZonedDateTime.prototype.minute
2042 static bool ZonedDateTime_minute(JSContext* cx, unsigned argc, Value* vp) {
2043 // Steps 1-2.
2044 CallArgs args = CallArgsFromVp(argc, vp);
2045 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_minute>(cx, args);
2049 * get Temporal.ZonedDateTime.prototype.second
2051 static bool ZonedDateTime_second(JSContext* cx, const CallArgs& args) {
2052 Rooted<ZonedDateTime> zonedDateTime(
2053 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2055 // Steps 3-6.
2056 PlainDateTime dateTime;
2057 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2058 zonedDateTime.instant(), &dateTime)) {
2059 return false;
2062 // Step 7.
2063 args.rval().setInt32(dateTime.time.second);
2064 return true;
2068 * get Temporal.ZonedDateTime.prototype.second
2070 static bool ZonedDateTime_second(JSContext* cx, unsigned argc, Value* vp) {
2071 // Steps 1-2.
2072 CallArgs args = CallArgsFromVp(argc, vp);
2073 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_second>(cx, args);
2077 * get Temporal.ZonedDateTime.prototype.millisecond
2079 static bool ZonedDateTime_millisecond(JSContext* cx, const CallArgs& args) {
2080 Rooted<ZonedDateTime> zonedDateTime(
2081 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2083 // Steps 3-6.
2084 PlainDateTime dateTime;
2085 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2086 zonedDateTime.instant(), &dateTime)) {
2087 return false;
2090 // Step 7.
2091 args.rval().setInt32(dateTime.time.millisecond);
2092 return true;
2096 * get Temporal.ZonedDateTime.prototype.millisecond
2098 static bool ZonedDateTime_millisecond(JSContext* cx, unsigned argc, Value* vp) {
2099 // Steps 1-2.
2100 CallArgs args = CallArgsFromVp(argc, vp);
2101 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_millisecond>(cx,
2102 args);
2106 * get Temporal.ZonedDateTime.prototype.microsecond
2108 static bool ZonedDateTime_microsecond(JSContext* cx, const CallArgs& args) {
2109 Rooted<ZonedDateTime> zonedDateTime(
2110 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2112 // Steps 3-6.
2113 PlainDateTime dateTime;
2114 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2115 zonedDateTime.instant(), &dateTime)) {
2116 return false;
2119 // Step 7.
2120 args.rval().setInt32(dateTime.time.microsecond);
2121 return true;
2125 * get Temporal.ZonedDateTime.prototype.microsecond
2127 static bool ZonedDateTime_microsecond(JSContext* cx, unsigned argc, Value* vp) {
2128 // Steps 1-2.
2129 CallArgs args = CallArgsFromVp(argc, vp);
2130 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_microsecond>(cx,
2131 args);
2135 * get Temporal.ZonedDateTime.prototype.nanosecond
2137 static bool ZonedDateTime_nanosecond(JSContext* cx, const CallArgs& args) {
2138 Rooted<ZonedDateTime> zonedDateTime(
2139 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2141 // Steps 3-6.
2142 PlainDateTime dateTime;
2143 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2144 zonedDateTime.instant(), &dateTime)) {
2145 return false;
2148 // Step 7.
2149 args.rval().setInt32(dateTime.time.nanosecond);
2150 return true;
2154 * get Temporal.ZonedDateTime.prototype.nanosecond
2156 static bool ZonedDateTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) {
2157 // Steps 1-2.
2158 CallArgs args = CallArgsFromVp(argc, vp);
2159 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_nanosecond>(cx,
2160 args);
2164 * get Temporal.ZonedDateTime.prototype.epochSeconds
2166 static bool ZonedDateTime_epochSeconds(JSContext* cx, const CallArgs& args) {
2167 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
2169 // Step 3.
2170 auto instant = ToInstant(zonedDateTime);
2172 // Steps 4-5.
2173 args.rval().setNumber(instant.seconds);
2174 return true;
2178 * get Temporal.ZonedDateTime.prototype.epochSeconds
2180 static bool ZonedDateTime_epochSeconds(JSContext* cx, unsigned argc,
2181 Value* vp) {
2182 // Steps 1-2.
2183 CallArgs args = CallArgsFromVp(argc, vp);
2184 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochSeconds>(
2185 cx, args);
2189 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
2191 static bool ZonedDateTime_epochMilliseconds(JSContext* cx,
2192 const CallArgs& args) {
2193 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
2195 // Step 3.
2196 auto instant = ToInstant(zonedDateTime);
2198 // Steps 4-5.
2199 args.rval().setNumber(instant.floorToMilliseconds());
2200 return true;
2204 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
2206 static bool ZonedDateTime_epochMilliseconds(JSContext* cx, unsigned argc,
2207 Value* vp) {
2208 // Steps 1-2.
2209 CallArgs args = CallArgsFromVp(argc, vp);
2210 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochMilliseconds>(
2211 cx, args);
2215 * get Temporal.ZonedDateTime.prototype.epochMicroseconds
2217 static bool ZonedDateTime_epochMicroseconds(JSContext* cx,
2218 const CallArgs& args) {
2219 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
2221 // Step 3.
2222 auto instant = ToInstant(zonedDateTime);
2224 // Step 4.
2225 auto* microseconds =
2226 BigInt::createFromInt64(cx, instant.floorToMicroseconds());
2227 if (!microseconds) {
2228 return false;
2231 // Step 5.
2232 args.rval().setBigInt(microseconds);
2233 return true;
2237 * get Temporal.ZonedDateTime.prototype.epochMicroseconds
2239 static bool ZonedDateTime_epochMicroseconds(JSContext* cx, unsigned argc,
2240 Value* vp) {
2241 // Steps 1-2.
2242 CallArgs args = CallArgsFromVp(argc, vp);
2243 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochMicroseconds>(
2244 cx, args);
2248 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
2250 static bool ZonedDateTime_epochNanoseconds(JSContext* cx,
2251 const CallArgs& args) {
2252 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
2254 // Step 3.
2255 auto* nanoseconds = ToEpochNanoseconds(cx, ToInstant(zonedDateTime));
2256 if (!nanoseconds) {
2257 return false;
2260 args.rval().setBigInt(nanoseconds);
2261 return true;
2265 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
2267 static bool ZonedDateTime_epochNanoseconds(JSContext* cx, unsigned argc,
2268 Value* vp) {
2269 // Steps 1-2.
2270 CallArgs args = CallArgsFromVp(argc, vp);
2271 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochNanoseconds>(
2272 cx, args);
2276 * get Temporal.ZonedDateTime.prototype.dayOfWeek
2278 static bool ZonedDateTime_dayOfWeek(JSContext* cx, const CallArgs& args) {
2279 Rooted<ZonedDateTime> zonedDateTime(
2280 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2282 // Steps 3-6.
2283 PlainDateTime dateTime;
2284 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2285 zonedDateTime.instant(), &dateTime)) {
2286 return false;
2289 // Step 7.
2290 return CalendarDayOfWeek(cx, zonedDateTime.calendar(), dateTime, args.rval());
2294 * get Temporal.ZonedDateTime.prototype.dayOfWeek
2296 static bool ZonedDateTime_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) {
2297 // Steps 1-2.
2298 CallArgs args = CallArgsFromVp(argc, vp);
2299 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_dayOfWeek>(cx,
2300 args);
2304 * get Temporal.ZonedDateTime.prototype.dayOfYear
2306 static bool ZonedDateTime_dayOfYear(JSContext* cx, const CallArgs& args) {
2307 Rooted<ZonedDateTime> zonedDateTime(
2308 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2310 // Steps 3-6.
2311 PlainDateTime dateTime;
2312 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2313 zonedDateTime.instant(), &dateTime)) {
2314 return false;
2317 // Step 7.
2318 return CalendarDayOfYear(cx, zonedDateTime.calendar(), dateTime, args.rval());
2322 * get Temporal.ZonedDateTime.prototype.dayOfYear
2324 static bool ZonedDateTime_dayOfYear(JSContext* cx, unsigned argc, Value* vp) {
2325 // Steps 1-2.
2326 CallArgs args = CallArgsFromVp(argc, vp);
2327 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_dayOfYear>(cx,
2328 args);
2332 * get Temporal.ZonedDateTime.prototype.weekOfYear
2334 static bool ZonedDateTime_weekOfYear(JSContext* cx, const CallArgs& args) {
2335 Rooted<ZonedDateTime> zonedDateTime(
2336 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2338 // Steps 3-6.
2339 PlainDateTime dateTime;
2340 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2341 zonedDateTime.instant(), &dateTime)) {
2342 return false;
2345 // Step 7.
2346 return CalendarWeekOfYear(cx, zonedDateTime.calendar(), dateTime,
2347 args.rval());
2351 * get Temporal.ZonedDateTime.prototype.weekOfYear
2353 static bool ZonedDateTime_weekOfYear(JSContext* cx, unsigned argc, Value* vp) {
2354 // Steps 1-2.
2355 CallArgs args = CallArgsFromVp(argc, vp);
2356 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_weekOfYear>(cx,
2357 args);
2361 * get Temporal.ZonedDateTime.prototype.yearOfWeek
2363 static bool ZonedDateTime_yearOfWeek(JSContext* cx, const CallArgs& args) {
2364 Rooted<ZonedDateTime> zonedDateTime(
2365 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2367 // Steps 3-6.
2368 PlainDateTime dateTime;
2369 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2370 zonedDateTime.instant(), &dateTime)) {
2371 return false;
2374 // Step 7.
2375 return CalendarYearOfWeek(cx, zonedDateTime.calendar(), dateTime,
2376 args.rval());
2380 * get Temporal.ZonedDateTime.prototype.yearOfWeek
2382 static bool ZonedDateTime_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) {
2383 // Steps 1-2.
2384 CallArgs args = CallArgsFromVp(argc, vp);
2385 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_yearOfWeek>(cx,
2386 args);
2390 * get Temporal.ZonedDateTime.prototype.hoursInDay
2392 static bool ZonedDateTime_hoursInDay(JSContext* cx, const CallArgs& args) {
2393 Rooted<ZonedDateTime> zonedDateTime(
2394 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2396 // Step 3.
2397 Rooted<TimeZoneRecord> timeZone(cx);
2398 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
2400 TimeZoneMethod::GetOffsetNanosecondsFor,
2401 TimeZoneMethod::GetPossibleInstantsFor,
2403 &timeZone)) {
2404 return false;
2407 // Step 4.
2408 const auto& instant = zonedDateTime.instant();
2410 // Step 5.
2411 PlainDateTime temporalDateTime;
2412 if (!GetPlainDateTimeFor(cx, timeZone, instant, &temporalDateTime)) {
2413 return false;
2416 // Steps 6-8.
2417 const auto& date = temporalDateTime.date;
2418 Rooted<CalendarValue> isoCalendar(cx, CalendarValue(cx->names().iso8601));
2420 // Step 9.
2421 Rooted<PlainDateTimeWithCalendar> today(cx);
2422 if (!CreateTemporalDateTime(cx, {date, {}}, isoCalendar, &today)) {
2423 return false;
2426 // Step 10.
2427 auto tomorrowFields = BalanceISODate(date.year, date.month, date.day + 1);
2429 // Step 11.
2430 Rooted<PlainDateTimeWithCalendar> tomorrow(cx);
2431 if (!CreateTemporalDateTime(cx, {tomorrowFields, {}}, isoCalendar,
2432 &tomorrow)) {
2433 return false;
2436 // Step 12.
2437 Instant todayInstant;
2438 if (!GetInstantFor(cx, timeZone, today, TemporalDisambiguation::Compatible,
2439 &todayInstant)) {
2440 return false;
2443 // Step 13.
2444 Instant tomorrowInstant;
2445 if (!GetInstantFor(cx, timeZone, tomorrow, TemporalDisambiguation::Compatible,
2446 &tomorrowInstant)) {
2447 return false;
2450 // Step 14.
2451 auto diff = tomorrowInstant - todayInstant;
2452 MOZ_ASSERT(IsValidInstantSpan(diff));
2454 // Step 15.
2455 constexpr auto nsPerHour = Int128{ToNanoseconds(TemporalUnit::Hour)};
2456 args.rval().setNumber(FractionToDouble(diff.toNanoseconds(), nsPerHour));
2457 return true;
2461 * get Temporal.ZonedDateTime.prototype.hoursInDay
2463 static bool ZonedDateTime_hoursInDay(JSContext* cx, unsigned argc, Value* vp) {
2464 // Steps 1-2.
2465 CallArgs args = CallArgsFromVp(argc, vp);
2466 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_hoursInDay>(cx,
2467 args);
2471 * get Temporal.ZonedDateTime.prototype.daysInWeek
2473 static bool ZonedDateTime_daysInWeek(JSContext* cx, const CallArgs& args) {
2474 Rooted<ZonedDateTime> zonedDateTime(
2475 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2477 // Steps 3-6.
2478 PlainDateTime dateTime;
2479 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2480 zonedDateTime.instant(), &dateTime)) {
2481 return false;
2484 // Step 7.
2485 return CalendarDaysInWeek(cx, zonedDateTime.calendar(), dateTime,
2486 args.rval());
2490 * get Temporal.ZonedDateTime.prototype.daysInWeek
2492 static bool ZonedDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) {
2493 // Steps 1-2.
2494 CallArgs args = CallArgsFromVp(argc, vp);
2495 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInWeek>(cx,
2496 args);
2500 * get Temporal.ZonedDateTime.prototype.daysInMonth
2502 static bool ZonedDateTime_daysInMonth(JSContext* cx, const CallArgs& args) {
2503 Rooted<ZonedDateTime> zonedDateTime(
2504 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2506 // Steps 3-6.
2507 PlainDateTime dateTime;
2508 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2509 zonedDateTime.instant(), &dateTime)) {
2510 return false;
2513 // Step 7.
2514 return CalendarDaysInMonth(cx, zonedDateTime.calendar(), dateTime,
2515 args.rval());
2519 * get Temporal.ZonedDateTime.prototype.daysInMonth
2521 static bool ZonedDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) {
2522 // Steps 1-2.
2523 CallArgs args = CallArgsFromVp(argc, vp);
2524 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInMonth>(cx,
2525 args);
2529 * get Temporal.ZonedDateTime.prototype.daysInYear
2531 static bool ZonedDateTime_daysInYear(JSContext* cx, const CallArgs& args) {
2532 Rooted<ZonedDateTime> zonedDateTime(
2533 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2535 // Steps 3-6.
2536 PlainDateTime dateTime;
2537 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2538 zonedDateTime.instant(), &dateTime)) {
2539 return false;
2542 // Step 7.
2543 return CalendarDaysInYear(cx, zonedDateTime.calendar(), dateTime,
2544 args.rval());
2548 * get Temporal.ZonedDateTime.prototype.daysInYear
2550 static bool ZonedDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) {
2551 // Steps 1-2.
2552 CallArgs args = CallArgsFromVp(argc, vp);
2553 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInYear>(cx,
2554 args);
2558 * get Temporal.ZonedDateTime.prototype.monthsInYear
2560 static bool ZonedDateTime_monthsInYear(JSContext* cx, const CallArgs& args) {
2561 Rooted<ZonedDateTime> zonedDateTime(
2562 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2564 // Steps 3-6.
2565 PlainDateTime dateTime;
2566 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2567 zonedDateTime.instant(), &dateTime)) {
2568 return false;
2571 // Step 7.
2572 return CalendarMonthsInYear(cx, zonedDateTime.calendar(), dateTime,
2573 args.rval());
2577 * get Temporal.ZonedDateTime.prototype.monthsInYear
2579 static bool ZonedDateTime_monthsInYear(JSContext* cx, unsigned argc,
2580 Value* vp) {
2581 // Steps 1-2.
2582 CallArgs args = CallArgsFromVp(argc, vp);
2583 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_monthsInYear>(
2584 cx, args);
2588 * get Temporal.ZonedDateTime.prototype.inLeapYear
2590 static bool ZonedDateTime_inLeapYear(JSContext* cx, const CallArgs& args) {
2591 Rooted<ZonedDateTime> zonedDateTime(
2592 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2594 // Steps 3-6.
2595 PlainDateTime dateTime;
2596 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2597 zonedDateTime.instant(), &dateTime)) {
2598 return false;
2601 // Step 7.
2602 return CalendarInLeapYear(cx, zonedDateTime.calendar(), dateTime,
2603 args.rval());
2607 * get Temporal.ZonedDateTime.prototype.inLeapYear
2609 static bool ZonedDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) {
2610 // Steps 1-2.
2611 CallArgs args = CallArgsFromVp(argc, vp);
2612 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_inLeapYear>(cx,
2613 args);
2617 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2619 static bool ZonedDateTime_offsetNanoseconds(JSContext* cx,
2620 const CallArgs& args) {
2621 Rooted<ZonedDateTime> zonedDateTime(
2622 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2624 // Step 3.
2625 auto timeZone = zonedDateTime.timeZone();
2627 // Step 4.
2628 const auto& instant = zonedDateTime.instant();
2630 // Step 5.
2631 int64_t offsetNanoseconds;
2632 if (!GetOffsetNanosecondsFor(cx, timeZone, instant, &offsetNanoseconds)) {
2633 return false;
2635 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
2637 args.rval().setNumber(offsetNanoseconds);
2638 return true;
2642 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2644 static bool ZonedDateTime_offsetNanoseconds(JSContext* cx, unsigned argc,
2645 Value* vp) {
2646 // Steps 1-2.
2647 CallArgs args = CallArgsFromVp(argc, vp);
2648 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offsetNanoseconds>(
2649 cx, args);
2653 * get Temporal.ZonedDateTime.prototype.offset
2655 static bool ZonedDateTime_offset(JSContext* cx, const CallArgs& args) {
2656 Rooted<ZonedDateTime> zonedDateTime(
2657 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2659 // Step 3.
2660 auto timeZone = zonedDateTime.timeZone();
2662 // Step 4.
2663 const auto& instant = zonedDateTime.instant();
2665 // Step 5.
2666 JSString* str = GetOffsetStringFor(cx, timeZone, instant);
2667 if (!str) {
2668 return false;
2671 args.rval().setString(str);
2672 return true;
2676 * get Temporal.ZonedDateTime.prototype.offset
2678 static bool ZonedDateTime_offset(JSContext* cx, unsigned argc, Value* vp) {
2679 // Steps 1-2.
2680 CallArgs args = CallArgsFromVp(argc, vp);
2681 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offset>(cx, args);
2685 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2686 * ] )
2688 static bool ZonedDateTime_with(JSContext* cx, const CallArgs& args) {
2689 Rooted<ZonedDateTime> zonedDateTime(
2690 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2692 // Step 3.
2693 Rooted<JSObject*> temporalZonedDateTimeLike(
2695 RequireObjectArg(cx, "temporalZonedDateTimeLike", "with", args.get(0)));
2696 if (!temporalZonedDateTimeLike) {
2697 return false;
2699 if (!ThrowIfTemporalLikeObject(cx, temporalZonedDateTimeLike)) {
2700 return false;
2703 // Step 4.
2704 Rooted<PlainObject*> resolvedOptions(cx);
2705 if (args.hasDefined(1)) {
2706 Rooted<JSObject*> options(cx,
2707 RequireObjectArg(cx, "options", "with", args[1]));
2708 if (!options) {
2709 return false;
2711 resolvedOptions = SnapshotOwnProperties(cx, options);
2712 } else {
2713 resolvedOptions = NewPlainObjectWithProto(cx, nullptr);
2715 if (!resolvedOptions) {
2716 return false;
2719 // Step 5.
2720 Rooted<CalendarRecord> calendar(cx);
2721 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
2723 CalendarMethod::DateFromFields,
2724 CalendarMethod::Fields,
2725 CalendarMethod::MergeFields,
2727 &calendar)) {
2728 return false;
2731 // Step 6.
2732 Rooted<TimeZoneRecord> timeZone(cx);
2733 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
2735 TimeZoneMethod::GetOffsetNanosecondsFor,
2736 TimeZoneMethod::GetPossibleInstantsFor,
2738 &timeZone)) {
2739 return false;
2742 // Step 7.
2743 const auto& instant = zonedDateTime.instant();
2745 // Step 8.
2746 int64_t offsetNanoseconds;
2747 if (!GetOffsetNanosecondsFor(cx, timeZone, instant, &offsetNanoseconds)) {
2748 return false;
2751 // Step 9.
2752 Rooted<PlainDateTimeObject*> dateTime(
2754 GetPlainDateTimeFor(cx, instant, calendar.receiver(), offsetNanoseconds));
2755 if (!dateTime) {
2756 return false;
2759 // Step 10.
2760 JS::RootedVector<PropertyKey> fieldNames(cx);
2761 if (!CalendarFields(cx, calendar,
2762 {CalendarField::Day, CalendarField::Month,
2763 CalendarField::MonthCode, CalendarField::Year},
2764 &fieldNames)) {
2765 return false;
2768 // Step 11.
2769 Rooted<PlainObject*> fields(cx,
2770 PrepareTemporalFields(cx, dateTime, fieldNames));
2771 if (!fields) {
2772 return false;
2775 // Steps 12-17.
2776 struct TimeField {
2777 using FieldName = ImmutableTenuredPtr<PropertyName*> JSAtomState::*;
2779 FieldName name;
2780 int32_t value;
2781 } timeFields[] = {
2782 {&JSAtomState::hour, dateTime->isoHour()},
2783 {&JSAtomState::minute, dateTime->isoMinute()},
2784 {&JSAtomState::second, dateTime->isoSecond()},
2785 {&JSAtomState::millisecond, dateTime->isoMillisecond()},
2786 {&JSAtomState::microsecond, dateTime->isoMicrosecond()},
2787 {&JSAtomState::nanosecond, dateTime->isoNanosecond()},
2790 Rooted<Value> timeFieldValue(cx);
2791 for (const auto& timeField : timeFields) {
2792 Handle<PropertyName*> name = cx->names().*(timeField.name);
2793 timeFieldValue.setInt32(timeField.value);
2795 if (!DefineDataProperty(cx, fields, name, timeFieldValue)) {
2796 return false;
2800 // Step 18.
2801 JSString* fieldsOffset = FormatUTCOffsetNanoseconds(cx, offsetNanoseconds);
2802 if (!fieldsOffset) {
2803 return false;
2806 timeFieldValue.setString(fieldsOffset);
2807 if (!DefineDataProperty(cx, fields, cx->names().offset, timeFieldValue)) {
2808 return false;
2811 // Step 19.
2812 if (!AppendSorted(cx, fieldNames.get(),
2814 TemporalField::Hour,
2815 TemporalField::Microsecond,
2816 TemporalField::Millisecond,
2817 TemporalField::Minute,
2818 TemporalField::Nanosecond,
2819 TemporalField::Offset,
2820 TemporalField::Second,
2821 })) {
2822 return false;
2825 // Step 20.
2826 Rooted<PlainObject*> partialZonedDateTime(
2828 PreparePartialTemporalFields(cx, temporalZonedDateTimeLike, fieldNames));
2829 if (!partialZonedDateTime) {
2830 return false;
2833 // Step 21.
2834 Rooted<JSObject*> mergedFields(
2835 cx, CalendarMergeFields(cx, calendar, fields, partialZonedDateTime));
2836 if (!mergedFields) {
2837 return false;
2840 // Step 22.
2841 fields = PrepareTemporalFields(cx, mergedFields, fieldNames,
2842 {TemporalField::Offset});
2843 if (!fields) {
2844 return false;
2847 // Step 23-24.
2848 auto disambiguation = TemporalDisambiguation::Compatible;
2849 if (!ToTemporalDisambiguation(cx, resolvedOptions, &disambiguation)) {
2850 return false;
2853 // Step 25.
2854 auto offset = TemporalOffset::Prefer;
2855 if (!ToTemporalOffset(cx, resolvedOptions, &offset)) {
2856 return false;
2859 // Step 26.
2860 PlainDateTime dateTimeResult;
2861 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, resolvedOptions,
2862 &dateTimeResult)) {
2863 return false;
2866 // Step 27.
2867 Rooted<Value> offsetString(cx);
2868 if (!GetProperty(cx, fields, fields, cx->names().offset, &offsetString)) {
2869 return false;
2872 // Step 28.
2873 MOZ_ASSERT(offsetString.isString());
2875 // Step 29.
2876 Rooted<JSString*> offsetStr(cx, offsetString.toString());
2877 int64_t newOffsetNanoseconds;
2878 if (!ParseDateTimeUTCOffset(cx, offsetStr, &newOffsetNanoseconds)) {
2879 return false;
2882 // Step 30.
2883 Instant epochNanoseconds;
2884 if (!InterpretISODateTimeOffset(
2885 cx, dateTimeResult, OffsetBehaviour::Option, newOffsetNanoseconds,
2886 timeZone, disambiguation, offset, MatchBehaviour::MatchExactly,
2887 &epochNanoseconds)) {
2888 return false;
2891 // Step 31.
2892 auto* result = CreateTemporalZonedDateTime(
2893 cx, epochNanoseconds, timeZone.receiver(), calendar.receiver());
2894 if (!result) {
2895 return false;
2898 args.rval().setObject(*result);
2899 return true;
2903 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2904 * ] )
2906 static bool ZonedDateTime_with(JSContext* cx, unsigned argc, Value* vp) {
2907 // Steps 1-2.
2908 CallArgs args = CallArgsFromVp(argc, vp);
2909 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_with>(cx, args);
2913 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2915 static bool ZonedDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
2916 Rooted<ZonedDateTime> zonedDateTime(
2917 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2919 // Steps 3-4.
2920 PlainTime time = {};
2921 if (args.hasDefined(0)) {
2922 if (!ToTemporalTime(cx, args[0], &time)) {
2923 return false;
2927 // Step 5.
2928 Rooted<TimeZoneRecord> timeZone(cx);
2929 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
2931 TimeZoneMethod::GetOffsetNanosecondsFor,
2932 TimeZoneMethod::GetPossibleInstantsFor,
2934 &timeZone)) {
2935 return false;
2938 // Steps 6 and 8.
2939 PlainDateTime plainDateTime;
2940 if (!GetPlainDateTimeFor(cx, timeZone, zonedDateTime.instant(),
2941 &plainDateTime)) {
2942 return false;
2945 // Step 7.
2946 auto calendar = zonedDateTime.calendar();
2948 // Step 9.
2949 Rooted<PlainDateTimeWithCalendar> resultPlainDateTime(cx);
2950 if (!CreateTemporalDateTime(cx, {plainDateTime.date, time}, calendar,
2951 &resultPlainDateTime)) {
2952 return false;
2955 // Step 10.
2956 Instant instant;
2957 if (!GetInstantFor(cx, timeZone, resultPlainDateTime,
2958 TemporalDisambiguation::Compatible, &instant)) {
2959 return false;
2962 // Step 11.
2963 auto* result =
2964 CreateTemporalZonedDateTime(cx, instant, timeZone.receiver(), calendar);
2965 if (!result) {
2966 return false;
2969 args.rval().setObject(*result);
2970 return true;
2974 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2976 static bool ZonedDateTime_withPlainTime(JSContext* cx, unsigned argc,
2977 Value* vp) {
2978 // Steps 1-2.
2979 CallArgs args = CallArgsFromVp(argc, vp);
2980 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withPlainTime>(
2981 cx, args);
2985 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
2987 static bool ZonedDateTime_withPlainDate(JSContext* cx, const CallArgs& args) {
2988 Rooted<ZonedDateTime> zonedDateTime(
2989 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2991 // Step 3.
2992 Rooted<PlainDateWithCalendar> plainDate(cx);
2993 if (!ToTemporalDate(cx, args.get(0), &plainDate)) {
2994 return false;
2996 auto date = plainDate.date();
2998 // Step 4.
2999 Rooted<TimeZoneRecord> timeZone(cx);
3000 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
3002 TimeZoneMethod::GetOffsetNanosecondsFor,
3003 TimeZoneMethod::GetPossibleInstantsFor,
3005 &timeZone)) {
3006 return false;
3009 // Steps 5-6.
3010 PlainDateTime plainDateTime;
3011 if (!GetPlainDateTimeFor(cx, timeZone, zonedDateTime.instant(),
3012 &plainDateTime)) {
3013 return false;
3016 // Step 7.
3017 Rooted<CalendarValue> calendar(cx);
3018 if (!ConsolidateCalendars(cx, zonedDateTime.calendar(), plainDate.calendar(),
3019 &calendar)) {
3020 return false;
3023 // Step 8.
3024 Rooted<PlainDateTimeWithCalendar> resultPlainDateTime(cx);
3025 if (!CreateTemporalDateTime(cx, {date, plainDateTime.time}, calendar,
3026 &resultPlainDateTime)) {
3027 return false;
3030 // Step 9.
3031 Instant instant;
3032 if (!GetInstantFor(cx, timeZone, resultPlainDateTime,
3033 TemporalDisambiguation::Compatible, &instant)) {
3034 return false;
3037 // Step 10.
3038 auto* result =
3039 CreateTemporalZonedDateTime(cx, instant, timeZone.receiver(), calendar);
3040 if (!result) {
3041 return false;
3044 args.rval().setObject(*result);
3045 return true;
3049 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3051 static bool ZonedDateTime_withPlainDate(JSContext* cx, unsigned argc,
3052 Value* vp) {
3053 // Steps 1-2.
3054 CallArgs args = CallArgsFromVp(argc, vp);
3055 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withPlainDate>(
3056 cx, args);
3060 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3062 static bool ZonedDateTime_withTimeZone(JSContext* cx, const CallArgs& args) {
3063 Rooted<ZonedDateTime> zonedDateTime(
3064 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3066 // Step 3.
3067 Rooted<TimeZoneValue> timeZone(cx);
3068 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
3069 return false;
3072 // Step 4.
3073 auto* result = CreateTemporalZonedDateTime(
3074 cx, zonedDateTime.instant(), timeZone, zonedDateTime.calendar());
3075 if (!result) {
3076 return false;
3079 args.rval().setObject(*result);
3080 return true;
3084 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3086 static bool ZonedDateTime_withTimeZone(JSContext* cx, unsigned argc,
3087 Value* vp) {
3088 // Steps 1-2.
3089 CallArgs args = CallArgsFromVp(argc, vp);
3090 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withTimeZone>(
3091 cx, args);
3095 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3097 static bool ZonedDateTime_withCalendar(JSContext* cx, const CallArgs& args) {
3098 Rooted<ZonedDateTime> zonedDateTime(
3099 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3101 // Step 3.
3102 Rooted<CalendarValue> calendar(cx);
3103 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) {
3104 return false;
3107 // Step 4.
3108 auto* result = CreateTemporalZonedDateTime(
3109 cx, zonedDateTime.instant(), zonedDateTime.timeZone(), calendar);
3110 if (!result) {
3111 return false;
3114 args.rval().setObject(*result);
3115 return true;
3119 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3121 static bool ZonedDateTime_withCalendar(JSContext* cx, unsigned argc,
3122 Value* vp) {
3123 // Steps 1-2.
3124 CallArgs args = CallArgsFromVp(argc, vp);
3125 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withCalendar>(
3126 cx, args);
3130 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3132 static bool ZonedDateTime_add(JSContext* cx, const CallArgs& args) {
3133 return AddDurationToOrSubtractDurationFromZonedDateTime(
3134 cx, ZonedDateTimeDuration::Add, args);
3138 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3140 static bool ZonedDateTime_add(JSContext* cx, unsigned argc, Value* vp) {
3141 // Steps 1-2.
3142 CallArgs args = CallArgsFromVp(argc, vp);
3143 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_add>(cx, args);
3147 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3148 * ] )
3150 static bool ZonedDateTime_subtract(JSContext* cx, const CallArgs& args) {
3151 return AddDurationToOrSubtractDurationFromZonedDateTime(
3152 cx, ZonedDateTimeDuration::Subtract, args);
3156 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3157 * ] )
3159 static bool ZonedDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) {
3160 // Steps 1-2.
3161 CallArgs args = CallArgsFromVp(argc, vp);
3162 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_subtract>(cx,
3163 args);
3167 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3169 static bool ZonedDateTime_until(JSContext* cx, const CallArgs& args) {
3170 // Step 3.
3171 return DifferenceTemporalZonedDateTime(cx, TemporalDifference::Until, args);
3175 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3177 static bool ZonedDateTime_until(JSContext* cx, unsigned argc, Value* vp) {
3178 // Steps 1-2.
3179 CallArgs args = CallArgsFromVp(argc, vp);
3180 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_until>(cx, args);
3184 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3186 static bool ZonedDateTime_since(JSContext* cx, const CallArgs& args) {
3187 // Step 3.
3188 return DifferenceTemporalZonedDateTime(cx, TemporalDifference::Since, args);
3192 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3194 static bool ZonedDateTime_since(JSContext* cx, unsigned argc, Value* vp) {
3195 // Steps 1-2.
3196 CallArgs args = CallArgsFromVp(argc, vp);
3197 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_since>(cx, args);
3201 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3203 static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
3204 Rooted<ZonedDateTime> zonedDateTime(
3205 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3207 // Steps 3-12.
3208 auto smallestUnit = TemporalUnit::Auto;
3209 auto roundingMode = TemporalRoundingMode::HalfExpand;
3210 auto roundingIncrement = Increment{1};
3211 if (args.get(0).isString()) {
3212 // Step 4. (Not applicable in our implementation.)
3214 // Step 9.
3215 Rooted<JSString*> paramString(cx, args[0].toString());
3216 if (!GetTemporalUnit(cx, paramString, TemporalUnitKey::SmallestUnit,
3217 TemporalUnitGroup::DayTime, &smallestUnit)) {
3218 return false;
3221 // Steps 6-8 and 10-12. (Implicit)
3222 } else {
3223 // Steps 3 and 5.a
3224 Rooted<JSObject*> roundTo(
3225 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
3226 if (!roundTo) {
3227 return false;
3230 // Steps 6-7.
3231 if (!ToTemporalRoundingIncrement(cx, roundTo, &roundingIncrement)) {
3232 return false;
3235 // Step 8.
3236 if (!ToTemporalRoundingMode(cx, roundTo, &roundingMode)) {
3237 return false;
3240 // Step 9.
3241 if (!GetTemporalUnit(cx, roundTo, TemporalUnitKey::SmallestUnit,
3242 TemporalUnitGroup::DayTime, &smallestUnit)) {
3243 return false;
3246 if (smallestUnit == TemporalUnit::Auto) {
3247 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3248 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
3249 return false;
3252 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
3253 smallestUnit <= TemporalUnit::Nanosecond);
3255 // Steps 10-11.
3256 auto maximum = Increment{1};
3257 bool inclusive = true;
3258 if (smallestUnit > TemporalUnit::Day) {
3259 maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
3260 inclusive = false;
3263 // Step 12.
3264 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
3265 inclusive)) {
3266 return false;
3270 // Step 13.
3271 if (smallestUnit == TemporalUnit::Nanosecond &&
3272 roundingIncrement == Increment{1}) {
3273 // Step 13.a.
3274 auto* result = CreateTemporalZonedDateTime(cx, zonedDateTime.instant(),
3275 zonedDateTime.timeZone(),
3276 zonedDateTime.calendar());
3277 if (!result) {
3278 return false;
3281 args.rval().setObject(*result);
3282 return true;
3285 // Step 14.
3286 Rooted<TimeZoneRecord> timeZone(cx);
3287 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
3289 TimeZoneMethod::GetOffsetNanosecondsFor,
3290 TimeZoneMethod::GetPossibleInstantsFor,
3292 &timeZone)) {
3293 return false;
3296 // Step 16. (Reordered)
3297 auto calendar = zonedDateTime.calendar();
3299 // Steps 15 and 17.
3300 int64_t offsetNanoseconds;
3301 if (!GetOffsetNanosecondsFor(cx, timeZone, zonedDateTime.instant(),
3302 &offsetNanoseconds)) {
3303 return false;
3305 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
3307 // Step 18.
3308 auto temporalDateTime =
3309 GetPlainDateTimeFor(zonedDateTime.instant(), offsetNanoseconds);
3311 // Step 19.
3312 Rooted<CalendarValue> isoCalendar(cx, CalendarValue(cx->names().iso8601));
3313 Rooted<PlainDateTimeWithCalendar> dtStart(cx);
3314 if (!CreateTemporalDateTime(cx, {temporalDateTime.date, {}}, isoCalendar,
3315 &dtStart)) {
3316 return false;
3319 // Steps 20-21.
3320 Instant startNs;
3321 if (!GetInstantFor(cx, timeZone, dtStart, TemporalDisambiguation::Compatible,
3322 &startNs)) {
3323 return false;
3326 // Step 22.
3327 Instant endNs;
3328 if (!AddDaysToZonedDateTime(cx, startNs, ToPlainDateTime(dtStart), timeZone,
3329 calendar, 1, &endNs)) {
3330 return false;
3332 MOZ_ASSERT(IsValidEpochInstant(endNs));
3334 // Step 23.
3335 auto dayLengthNs = endNs - startNs;
3336 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs));
3338 // Step 24.
3339 if (dayLengthNs <= InstantSpan{}) {
3340 JS_ReportErrorNumberASCII(
3341 cx, GetErrorMessage, nullptr,
3342 JSMSG_TEMPORAL_ZONED_DATE_TIME_NON_POSITIVE_DAY_LENGTH);
3343 return false;
3346 // Step 25.
3347 PlainDateTime roundResult;
3348 if (!RoundISODateTime(cx, temporalDateTime, roundingIncrement, smallestUnit,
3349 roundingMode, dayLengthNs, &roundResult)) {
3350 return false;
3353 // Step 26.
3354 Instant epochNanoseconds;
3355 if (!InterpretISODateTimeOffset(
3356 cx, roundResult, OffsetBehaviour::Option, offsetNanoseconds, timeZone,
3357 TemporalDisambiguation::Compatible, TemporalOffset::Prefer,
3358 MatchBehaviour::MatchExactly, &epochNanoseconds)) {
3359 return false;
3362 // Step 27.
3363 auto* result = CreateTemporalZonedDateTime(cx, epochNanoseconds,
3364 timeZone.receiver(), calendar);
3365 if (!result) {
3366 return false;
3369 args.rval().setObject(*result);
3370 return true;
3374 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3376 static bool ZonedDateTime_round(JSContext* cx, unsigned argc, Value* vp) {
3377 // Steps 1-2.
3378 CallArgs args = CallArgsFromVp(argc, vp);
3379 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_round>(cx, args);
3383 * Temporal.ZonedDateTime.prototype.equals ( other )
3385 static bool ZonedDateTime_equals(JSContext* cx, const CallArgs& args) {
3386 Rooted<ZonedDateTime> zonedDateTime(
3387 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3389 // Step 3.
3390 Rooted<ZonedDateTime> other(cx);
3391 if (!ToTemporalZonedDateTime(cx, args.get(0), &other)) {
3392 return false;
3395 // Steps 4-6.
3396 bool equals = zonedDateTime.instant() == other.instant();
3397 if (equals && !TimeZoneEquals(cx, zonedDateTime.timeZone(), other.timeZone(),
3398 &equals)) {
3399 return false;
3401 if (equals && !CalendarEquals(cx, zonedDateTime.calendar(), other.calendar(),
3402 &equals)) {
3403 return false;
3406 args.rval().setBoolean(equals);
3407 return true;
3411 * Temporal.ZonedDateTime.prototype.equals ( other )
3413 static bool ZonedDateTime_equals(JSContext* cx, unsigned argc, Value* vp) {
3414 // Steps 1-2.
3415 CallArgs args = CallArgsFromVp(argc, vp);
3416 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_equals>(cx, args);
3420 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3422 static bool ZonedDateTime_toString(JSContext* cx, const CallArgs& args) {
3423 Rooted<ZonedDateTime> zonedDateTime(
3424 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3426 SecondsStringPrecision precision = {Precision::Auto(),
3427 TemporalUnit::Nanosecond, Increment{1}};
3428 auto roundingMode = TemporalRoundingMode::Trunc;
3429 auto showCalendar = CalendarOption::Auto;
3430 auto showTimeZone = TimeZoneNameOption::Auto;
3431 auto showOffset = ShowOffsetOption::Auto;
3432 if (args.hasDefined(0)) {
3433 // Step 3.
3434 Rooted<JSObject*> options(
3435 cx, RequireObjectArg(cx, "options", "toString", args[0]));
3436 if (!options) {
3437 return false;
3440 // Steps 4-5.
3441 if (!ToCalendarNameOption(cx, options, &showCalendar)) {
3442 return false;
3445 // Step 6.
3446 auto digits = Precision::Auto();
3447 if (!ToFractionalSecondDigits(cx, options, &digits)) {
3448 return false;
3451 // Step 7.
3452 if (!ToShowOffsetOption(cx, options, &showOffset)) {
3453 return false;
3456 // Step 8.
3457 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
3458 return false;
3461 // Step 9.
3462 auto smallestUnit = TemporalUnit::Auto;
3463 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
3464 TemporalUnitGroup::Time, &smallestUnit)) {
3465 return false;
3468 // Step 10.
3469 if (smallestUnit == TemporalUnit::Hour) {
3470 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3471 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
3472 "smallestUnit");
3473 return false;
3476 // Step 11.
3477 if (!ToTimeZoneNameOption(cx, options, &showTimeZone)) {
3478 return false;
3481 // Step 12.
3482 precision = ToSecondsStringPrecision(smallestUnit, digits);
3485 // Step 13.
3486 JSString* str = TemporalZonedDateTimeToString(
3487 cx, zonedDateTime, precision.precision, showCalendar, showTimeZone,
3488 showOffset, precision.increment, precision.unit, roundingMode);
3489 if (!str) {
3490 return false;
3493 args.rval().setString(str);
3494 return true;
3498 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3500 static bool ZonedDateTime_toString(JSContext* cx, unsigned argc, Value* vp) {
3501 // Steps 1-2.
3502 CallArgs args = CallArgsFromVp(argc, vp);
3503 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toString>(cx,
3504 args);
3508 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3510 static bool ZonedDateTime_toLocaleString(JSContext* cx, const CallArgs& args) {
3511 Rooted<ZonedDateTime> zonedDateTime(
3512 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3514 // Step 3.
3515 JSString* str = TemporalZonedDateTimeToString(
3516 cx, zonedDateTime, Precision::Auto(), CalendarOption::Auto,
3517 TimeZoneNameOption::Auto, ShowOffsetOption::Auto);
3518 if (!str) {
3519 return false;
3522 args.rval().setString(str);
3523 return true;
3527 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3529 static bool ZonedDateTime_toLocaleString(JSContext* cx, unsigned argc,
3530 Value* vp) {
3531 // Steps 1-2.
3532 CallArgs args = CallArgsFromVp(argc, vp);
3533 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toLocaleString>(
3534 cx, args);
3538 * Temporal.ZonedDateTime.prototype.toJSON ( )
3540 static bool ZonedDateTime_toJSON(JSContext* cx, const CallArgs& args) {
3541 Rooted<ZonedDateTime> zonedDateTime(
3542 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3544 // Step 3.
3545 JSString* str = TemporalZonedDateTimeToString(
3546 cx, zonedDateTime, Precision::Auto(), CalendarOption::Auto,
3547 TimeZoneNameOption::Auto, ShowOffsetOption::Auto);
3548 if (!str) {
3549 return false;
3552 args.rval().setString(str);
3553 return true;
3557 * Temporal.ZonedDateTime.prototype.toJSON ( )
3559 static bool ZonedDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) {
3560 // Steps 1-2.
3561 CallArgs args = CallArgsFromVp(argc, vp);
3562 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toJSON>(cx, args);
3566 * Temporal.ZonedDateTime.prototype.valueOf ( )
3568 static bool ZonedDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) {
3569 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
3570 "ZonedDateTime", "primitive type");
3571 return false;
3575 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3577 static bool ZonedDateTime_startOfDay(JSContext* cx, const CallArgs& args) {
3578 Rooted<ZonedDateTime> zonedDateTime(
3579 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3581 // Step 3.
3582 Rooted<TimeZoneRecord> timeZone(cx);
3583 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
3585 TimeZoneMethod::GetOffsetNanosecondsFor,
3586 TimeZoneMethod::GetPossibleInstantsFor,
3588 &timeZone)) {
3589 return false;
3592 // Step 4.
3593 auto calendar = zonedDateTime.calendar();
3595 // Step 5.
3596 const auto& instant = zonedDateTime.instant();
3598 // Steps 5-6.
3599 PlainDateTime temporalDateTime;
3600 if (!GetPlainDateTimeFor(cx, timeZone, instant, &temporalDateTime)) {
3601 return false;
3604 // Step 7.
3605 Rooted<PlainDateTimeWithCalendar> startDateTime(cx);
3606 if (!CreateTemporalDateTime(cx, {temporalDateTime.date, {}}, calendar,
3607 &startDateTime)) {
3608 return false;
3611 // Step 8.
3612 Instant startInstant;
3613 if (!GetInstantFor(cx, timeZone, startDateTime,
3614 TemporalDisambiguation::Compatible, &startInstant)) {
3615 return false;
3618 // Step 9.
3619 auto* result = CreateTemporalZonedDateTime(cx, startInstant,
3620 timeZone.receiver(), calendar);
3621 if (!result) {
3622 return false;
3625 args.rval().setObject(*result);
3626 return true;
3630 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3632 static bool ZonedDateTime_startOfDay(JSContext* cx, unsigned argc, Value* vp) {
3633 // Steps 1-2.
3634 CallArgs args = CallArgsFromVp(argc, vp);
3635 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_startOfDay>(cx,
3636 args);
3640 * Temporal.ZonedDateTime.prototype.toInstant ( )
3642 static bool ZonedDateTime_toInstant(JSContext* cx, const CallArgs& args) {
3643 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
3644 auto instant = ToInstant(zonedDateTime);
3646 // Step 3.
3647 auto* result = CreateTemporalInstant(cx, instant);
3648 if (!result) {
3649 return false;
3652 args.rval().setObject(*result);
3653 return true;
3657 * Temporal.ZonedDateTime.prototype.toInstant ( )
3659 static bool ZonedDateTime_toInstant(JSContext* cx, unsigned argc, Value* vp) {
3660 // Steps 1-2.
3661 CallArgs args = CallArgsFromVp(argc, vp);
3662 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toInstant>(cx,
3663 args);
3667 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3669 static bool ZonedDateTime_toPlainDate(JSContext* cx, const CallArgs& args) {
3670 Rooted<ZonedDateTime> zonedDateTime(
3671 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3673 // Steps 3-6.
3674 PlainDateTime temporalDateTime;
3675 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
3676 zonedDateTime.instant(), &temporalDateTime)) {
3677 return false;
3680 // Step 7.
3681 auto* result =
3682 CreateTemporalDate(cx, temporalDateTime.date, zonedDateTime.calendar());
3683 if (!result) {
3684 return false;
3687 args.rval().setObject(*result);
3688 return true;
3692 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3694 static bool ZonedDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
3695 // Steps 1-2.
3696 CallArgs args = CallArgsFromVp(argc, vp);
3697 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainDate>(cx,
3698 args);
3702 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3704 static bool ZonedDateTime_toPlainTime(JSContext* cx, const CallArgs& args) {
3705 Rooted<ZonedDateTime> zonedDateTime(
3706 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3708 // Steps 3-6.
3709 PlainDateTime temporalDateTime;
3710 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
3711 zonedDateTime.instant(), &temporalDateTime)) {
3712 return false;
3715 // Step 7.
3716 auto* result = CreateTemporalTime(cx, temporalDateTime.time);
3717 if (!result) {
3718 return false;
3721 args.rval().setObject(*result);
3722 return true;
3726 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3728 static bool ZonedDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) {
3729 // Steps 1-2.
3730 CallArgs args = CallArgsFromVp(argc, vp);
3731 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainTime>(cx,
3732 args);
3736 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3738 static bool ZonedDateTime_toPlainDateTime(JSContext* cx, const CallArgs& args) {
3739 Rooted<ZonedDateTime> zonedDateTime(
3740 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3742 // Steps 3-5.
3743 auto* result =
3744 GetPlainDateTimeFor(cx, zonedDateTime.timeZone(), zonedDateTime.instant(),
3745 zonedDateTime.calendar());
3746 if (!result) {
3747 return false;
3750 args.rval().setObject(*result);
3751 return true;
3755 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3757 static bool ZonedDateTime_toPlainDateTime(JSContext* cx, unsigned argc,
3758 Value* vp) {
3759 // Steps 1-2.
3760 CallArgs args = CallArgsFromVp(argc, vp);
3761 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainDateTime>(
3762 cx, args);
3766 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3768 static bool ZonedDateTime_toPlainYearMonth(JSContext* cx,
3769 const CallArgs& args) {
3770 Rooted<ZonedDateTime> zonedDateTime(
3771 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3773 // Step 3.
3774 Rooted<CalendarRecord> calendar(cx);
3775 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
3777 CalendarMethod::Fields,
3778 CalendarMethod::YearMonthFromFields,
3780 &calendar)) {
3781 return false;
3784 // Steps 4-6.
3785 Rooted<PlainDateTimeObject*> temporalDateTime(
3787 GetPlainDateTimeFor(cx, zonedDateTime.timeZone(), zonedDateTime.instant(),
3788 zonedDateTime.calendar()));
3789 if (!temporalDateTime) {
3790 return false;
3793 // Step 7.
3794 JS::RootedVector<PropertyKey> fieldNames(cx);
3795 if (!CalendarFields(cx, calendar,
3796 {CalendarField::MonthCode, CalendarField::Year},
3797 &fieldNames)) {
3798 return false;
3801 // Step 8.
3802 Rooted<PlainObject*> fields(
3803 cx, PrepareTemporalFields(cx, temporalDateTime, fieldNames));
3804 if (!fields) {
3805 return false;
3808 // Steps 9-10.
3809 auto result = CalendarYearMonthFromFields(cx, calendar, fields);
3810 if (!result) {
3811 return false;
3814 args.rval().setObject(*result);
3815 return true;
3819 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3821 static bool ZonedDateTime_toPlainYearMonth(JSContext* cx, unsigned argc,
3822 Value* vp) {
3823 // Steps 1-2.
3824 CallArgs args = CallArgsFromVp(argc, vp);
3825 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainYearMonth>(
3826 cx, args);
3830 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3832 static bool ZonedDateTime_toPlainMonthDay(JSContext* cx, const CallArgs& args) {
3833 Rooted<ZonedDateTime> zonedDateTime(
3834 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3836 // Step 3.
3837 Rooted<CalendarRecord> calendar(cx);
3838 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
3840 CalendarMethod::Fields,
3841 CalendarMethod::MonthDayFromFields,
3843 &calendar)) {
3844 return false;
3847 // Steps 4-6.
3848 Rooted<PlainDateTimeObject*> temporalDateTime(
3850 GetPlainDateTimeFor(cx, zonedDateTime.timeZone(), zonedDateTime.instant(),
3851 zonedDateTime.calendar()));
3852 if (!temporalDateTime) {
3853 return false;
3856 // Step 7.
3857 JS::RootedVector<PropertyKey> fieldNames(cx);
3858 if (!CalendarFields(cx, calendar,
3859 {CalendarField::Day, CalendarField::MonthCode},
3860 &fieldNames)) {
3861 return false;
3864 // Step 8.
3865 Rooted<PlainObject*> fields(
3866 cx, PrepareTemporalFields(cx, temporalDateTime, fieldNames));
3867 if (!fields) {
3868 return false;
3871 // Steps 9-10.
3872 auto result = CalendarMonthDayFromFields(cx, calendar, fields);
3873 if (!result) {
3874 return false;
3877 args.rval().setObject(*result);
3878 return true;
3882 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3884 static bool ZonedDateTime_toPlainMonthDay(JSContext* cx, unsigned argc,
3885 Value* vp) {
3886 // Steps 1-2.
3887 CallArgs args = CallArgsFromVp(argc, vp);
3888 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainMonthDay>(
3889 cx, args);
3893 * Temporal.ZonedDateTime.prototype.getISOFields ( )
3895 static bool ZonedDateTime_getISOFields(JSContext* cx, const CallArgs& args) {
3896 Rooted<ZonedDateTime> zonedDateTime(
3897 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3899 // Step 3.
3900 Rooted<IdValueVector> fields(cx, IdValueVector(cx));
3902 // Step 4.
3903 const auto& instant = zonedDateTime.instant();
3905 // Step 5.
3906 auto calendar = zonedDateTime.calendar();
3908 // Step 6.
3909 auto timeZone = zonedDateTime.timeZone();
3911 // Step 7.
3912 int64_t offsetNanoseconds;
3913 if (!GetOffsetNanosecondsFor(cx, timeZone, instant, &offsetNanoseconds)) {
3914 return false;
3917 // Step 8.
3918 auto temporalDateTime = GetPlainDateTimeFor(instant, offsetNanoseconds);
3920 // Step 9.
3921 Rooted<JSString*> offset(cx,
3922 FormatUTCOffsetNanoseconds(cx, offsetNanoseconds));
3923 if (!offset) {
3924 return false;
3927 // Step 10.
3928 if (!fields.emplaceBack(NameToId(cx->names().calendar), calendar.toValue())) {
3929 return false;
3932 // Step 11.
3933 if (!fields.emplaceBack(NameToId(cx->names().isoDay),
3934 Int32Value(temporalDateTime.date.day))) {
3935 return false;
3938 // Step 12.
3939 if (!fields.emplaceBack(NameToId(cx->names().isoHour),
3940 Int32Value(temporalDateTime.time.hour))) {
3941 return false;
3944 // Step 13.
3945 if (!fields.emplaceBack(NameToId(cx->names().isoMicrosecond),
3946 Int32Value(temporalDateTime.time.microsecond))) {
3947 return false;
3950 // Step 14.
3951 if (!fields.emplaceBack(NameToId(cx->names().isoMillisecond),
3952 Int32Value(temporalDateTime.time.millisecond))) {
3953 return false;
3956 // Step 15.
3957 if (!fields.emplaceBack(NameToId(cx->names().isoMinute),
3958 Int32Value(temporalDateTime.time.minute))) {
3959 return false;
3962 // Step 16.
3963 if (!fields.emplaceBack(NameToId(cx->names().isoMonth),
3964 Int32Value(temporalDateTime.date.month))) {
3965 return false;
3968 // Step 17.
3969 if (!fields.emplaceBack(NameToId(cx->names().isoNanosecond),
3970 Int32Value(temporalDateTime.time.nanosecond))) {
3971 return false;
3974 // Step 18.
3975 if (!fields.emplaceBack(NameToId(cx->names().isoSecond),
3976 Int32Value(temporalDateTime.time.second))) {
3977 return false;
3980 // Step 19.
3981 if (!fields.emplaceBack(NameToId(cx->names().isoYear),
3982 Int32Value(temporalDateTime.date.year))) {
3983 return false;
3986 // Step 20.
3987 if (!fields.emplaceBack(NameToId(cx->names().offset), StringValue(offset))) {
3988 return false;
3991 // Step 21.
3992 if (!fields.emplaceBack(NameToId(cx->names().timeZone), timeZone.toValue())) {
3993 return false;
3996 // Step 22.
3997 auto* obj = NewPlainObjectWithUniqueNames(cx, fields);
3998 if (!obj) {
3999 return false;
4002 args.rval().setObject(*obj);
4003 return true;
4007 * Temporal.ZonedDateTime.prototype.getISOFields ( )
4009 static bool ZonedDateTime_getISOFields(JSContext* cx, unsigned argc,
4010 Value* vp) {
4011 // Steps 1-2.
4012 CallArgs args = CallArgsFromVp(argc, vp);
4013 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_getISOFields>(
4014 cx, args);
4018 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4020 static bool ZonedDateTime_getCalendar(JSContext* cx, const CallArgs& args) {
4021 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
4022 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
4024 // Step 3.
4025 auto* obj = ToTemporalCalendarObject(cx, calendar);
4026 if (!obj) {
4027 return false;
4030 args.rval().setObject(*obj);
4031 return true;
4035 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4037 static bool ZonedDateTime_getCalendar(JSContext* cx, unsigned argc, Value* vp) {
4038 // Steps 1-2.
4039 CallArgs args = CallArgsFromVp(argc, vp);
4040 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_getCalendar>(cx,
4041 args);
4045 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4047 static bool ZonedDateTime_getTimeZone(JSContext* cx, const CallArgs& args) {
4048 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
4049 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
4051 // Step 3.
4052 auto* obj = ToTemporalTimeZoneObject(cx, timeZone);
4053 if (!obj) {
4054 return false;
4057 args.rval().setObject(*obj);
4058 return true;
4062 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4064 static bool ZonedDateTime_getTimeZone(JSContext* cx, unsigned argc, Value* vp) {
4065 // Steps 1-2.
4066 CallArgs args = CallArgsFromVp(argc, vp);
4067 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_getTimeZone>(cx,
4068 args);
4071 const JSClass ZonedDateTimeObject::class_ = {
4072 "Temporal.ZonedDateTime",
4073 JSCLASS_HAS_RESERVED_SLOTS(ZonedDateTimeObject::SLOT_COUNT) |
4074 JSCLASS_HAS_CACHED_PROTO(JSProto_ZonedDateTime),
4075 JS_NULL_CLASS_OPS,
4076 &ZonedDateTimeObject::classSpec_,
4079 const JSClass& ZonedDateTimeObject::protoClass_ = PlainObject::class_;
4081 static const JSFunctionSpec ZonedDateTime_methods[] = {
4082 JS_FN("from", ZonedDateTime_from, 1, 0),
4083 JS_FN("compare", ZonedDateTime_compare, 2, 0),
4084 JS_FS_END,
4087 static const JSFunctionSpec ZonedDateTime_prototype_methods[] = {
4088 JS_FN("with", ZonedDateTime_with, 1, 0),
4089 JS_FN("withPlainTime", ZonedDateTime_withPlainTime, 0, 0),
4090 JS_FN("withPlainDate", ZonedDateTime_withPlainDate, 1, 0),
4091 JS_FN("withTimeZone", ZonedDateTime_withTimeZone, 1, 0),
4092 JS_FN("withCalendar", ZonedDateTime_withCalendar, 1, 0),
4093 JS_FN("add", ZonedDateTime_add, 1, 0),
4094 JS_FN("subtract", ZonedDateTime_subtract, 1, 0),
4095 JS_FN("until", ZonedDateTime_until, 1, 0),
4096 JS_FN("since", ZonedDateTime_since, 1, 0),
4097 JS_FN("round", ZonedDateTime_round, 1, 0),
4098 JS_FN("equals", ZonedDateTime_equals, 1, 0),
4099 JS_FN("toString", ZonedDateTime_toString, 0, 0),
4100 JS_FN("toLocaleString", ZonedDateTime_toLocaleString, 0, 0),
4101 JS_FN("toJSON", ZonedDateTime_toJSON, 0, 0),
4102 JS_FN("valueOf", ZonedDateTime_valueOf, 0, 0),
4103 JS_FN("startOfDay", ZonedDateTime_startOfDay, 0, 0),
4104 JS_FN("toInstant", ZonedDateTime_toInstant, 0, 0),
4105 JS_FN("toPlainDate", ZonedDateTime_toPlainDate, 0, 0),
4106 JS_FN("toPlainTime", ZonedDateTime_toPlainTime, 0, 0),
4107 JS_FN("toPlainDateTime", ZonedDateTime_toPlainDateTime, 0, 0),
4108 JS_FN("toPlainYearMonth", ZonedDateTime_toPlainYearMonth, 0, 0),
4109 JS_FN("toPlainMonthDay", ZonedDateTime_toPlainMonthDay, 0, 0),
4110 JS_FN("getISOFields", ZonedDateTime_getISOFields, 0, 0),
4111 JS_FN("getCalendar", ZonedDateTime_getCalendar, 0, 0),
4112 JS_FN("getTimeZone", ZonedDateTime_getTimeZone, 0, 0),
4113 JS_FS_END,
4116 static const JSPropertySpec ZonedDateTime_prototype_properties[] = {
4117 JS_PSG("calendarId", ZonedDateTime_calendarId, 0),
4118 JS_PSG("timeZoneId", ZonedDateTime_timeZoneId, 0),
4119 JS_PSG("year", ZonedDateTime_year, 0),
4120 JS_PSG("month", ZonedDateTime_month, 0),
4121 JS_PSG("monthCode", ZonedDateTime_monthCode, 0),
4122 JS_PSG("day", ZonedDateTime_day, 0),
4123 JS_PSG("hour", ZonedDateTime_hour, 0),
4124 JS_PSG("minute", ZonedDateTime_minute, 0),
4125 JS_PSG("second", ZonedDateTime_second, 0),
4126 JS_PSG("millisecond", ZonedDateTime_millisecond, 0),
4127 JS_PSG("microsecond", ZonedDateTime_microsecond, 0),
4128 JS_PSG("nanosecond", ZonedDateTime_nanosecond, 0),
4129 JS_PSG("epochSeconds", ZonedDateTime_epochSeconds, 0),
4130 JS_PSG("epochMilliseconds", ZonedDateTime_epochMilliseconds, 0),
4131 JS_PSG("epochMicroseconds", ZonedDateTime_epochMicroseconds, 0),
4132 JS_PSG("epochNanoseconds", ZonedDateTime_epochNanoseconds, 0),
4133 JS_PSG("dayOfWeek", ZonedDateTime_dayOfWeek, 0),
4134 JS_PSG("dayOfYear", ZonedDateTime_dayOfYear, 0),
4135 JS_PSG("weekOfYear", ZonedDateTime_weekOfYear, 0),
4136 JS_PSG("yearOfWeek", ZonedDateTime_yearOfWeek, 0),
4137 JS_PSG("hoursInDay", ZonedDateTime_hoursInDay, 0),
4138 JS_PSG("daysInWeek", ZonedDateTime_daysInWeek, 0),
4139 JS_PSG("daysInMonth", ZonedDateTime_daysInMonth, 0),
4140 JS_PSG("daysInYear", ZonedDateTime_daysInYear, 0),
4141 JS_PSG("monthsInYear", ZonedDateTime_monthsInYear, 0),
4142 JS_PSG("inLeapYear", ZonedDateTime_inLeapYear, 0),
4143 JS_PSG("offsetNanoseconds", ZonedDateTime_offsetNanoseconds, 0),
4144 JS_PSG("offset", ZonedDateTime_offset, 0),
4145 JS_STRING_SYM_PS(toStringTag, "Temporal.ZonedDateTime", JSPROP_READONLY),
4146 JS_PS_END,
4149 const ClassSpec ZonedDateTimeObject::classSpec_ = {
4150 GenericCreateConstructor<ZonedDateTimeConstructor, 2,
4151 gc::AllocKind::FUNCTION>,
4152 GenericCreatePrototype<ZonedDateTimeObject>,
4153 ZonedDateTime_methods,
4154 nullptr,
4155 ZonedDateTime_prototype_methods,
4156 ZonedDateTime_prototype_properties,
4157 nullptr,
4158 ClassSpec::DontDefineConstructor,