Bug 1874684 - Part 21: Rename SecondsAndNanoseconds::toTotalNanoseconds. r=dminor
[gecko.git] / js / src / builtin / temporal / ZonedDateTime.cpp
blob7272dd664365c95b852548ba9470d52f8bafad6b
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 <utility>
15 #include "jspubtd.h"
16 #include "NamespaceImports.h"
18 #include "builtin/temporal/Calendar.h"
19 #include "builtin/temporal/Duration.h"
20 #include "builtin/temporal/Instant.h"
21 #include "builtin/temporal/Int96.h"
22 #include "builtin/temporal/PlainDate.h"
23 #include "builtin/temporal/PlainDateTime.h"
24 #include "builtin/temporal/PlainMonthDay.h"
25 #include "builtin/temporal/PlainTime.h"
26 #include "builtin/temporal/PlainYearMonth.h"
27 #include "builtin/temporal/Temporal.h"
28 #include "builtin/temporal/TemporalFields.h"
29 #include "builtin/temporal/TemporalParser.h"
30 #include "builtin/temporal/TemporalRoundingMode.h"
31 #include "builtin/temporal/TemporalTypes.h"
32 #include "builtin/temporal/TemporalUnit.h"
33 #include "builtin/temporal/TimeZone.h"
34 #include "builtin/temporal/ToString.h"
35 #include "builtin/temporal/Wrapped.h"
36 #include "ds/IdValuePair.h"
37 #include "gc/AllocKind.h"
38 #include "gc/Barrier.h"
39 #include "js/AllocPolicy.h"
40 #include "js/CallArgs.h"
41 #include "js/CallNonGenericMethod.h"
42 #include "js/Class.h"
43 #include "js/ComparisonOperators.h"
44 #include "js/ErrorReport.h"
45 #include "js/friend/ErrorMessages.h"
46 #include "js/GCVector.h"
47 #include "js/Id.h"
48 #include "js/Printer.h"
49 #include "js/PropertyDescriptor.h"
50 #include "js/PropertySpec.h"
51 #include "js/RootingAPI.h"
52 #include "js/TracingAPI.h"
53 #include "js/Value.h"
54 #include "vm/BigIntType.h"
55 #include "vm/BytecodeUtil.h"
56 #include "vm/GlobalObject.h"
57 #include "vm/JSAtomState.h"
58 #include "vm/JSContext.h"
59 #include "vm/JSObject.h"
60 #include "vm/ObjectOperations.h"
61 #include "vm/PlainObject.h"
62 #include "vm/StringType.h"
64 #include "vm/JSContext-inl.h"
65 #include "vm/JSObject-inl.h"
66 #include "vm/NativeObject-inl.h"
67 #include "vm/ObjectOperations-inl.h"
69 using namespace js;
70 using namespace js::temporal;
72 static inline bool IsZonedDateTime(Handle<Value> v) {
73 return v.isObject() && v.toObject().is<ZonedDateTimeObject>();
76 // Returns |RoundNumberToIncrement(offsetNanoseconds, 60 × 10^9, "halfExpand")|.
77 static int64_t RoundNanosecondsToMinutesIncrement(int64_t offsetNanoseconds) {
78 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
80 constexpr int64_t increment = ToNanoseconds(TemporalUnit::Minute);
82 int64_t quotient = offsetNanoseconds / increment;
83 int64_t remainder = offsetNanoseconds % increment;
84 if (std::abs(remainder * 2) >= increment) {
85 quotient += (offsetNanoseconds > 0 ? 1 : -1);
87 return quotient * increment;
90 /**
91 * InterpretISODateTimeOffset ( year, month, day, hour, minute, second,
92 * millisecond, microsecond, nanosecond, offsetBehaviour, offsetNanoseconds,
93 * timeZoneRec, disambiguation, offsetOption, matchBehaviour )
95 bool js::temporal::InterpretISODateTimeOffset(
96 JSContext* cx, const PlainDateTime& dateTime,
97 OffsetBehaviour offsetBehaviour, int64_t offsetNanoseconds,
98 Handle<TimeZoneRecord> timeZone, TemporalDisambiguation disambiguation,
99 TemporalOffset offsetOption, MatchBehaviour matchBehaviour,
100 Instant* result) {
101 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
103 // Step 1.
104 MOZ_ASSERT(IsValidISODate(dateTime.date));
106 // Step 2.
107 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
108 timeZone, TimeZoneMethod::GetOffsetNanosecondsFor));
110 // Step 3.
111 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
112 timeZone, TimeZoneMethod::GetPossibleInstantsFor));
114 // Step 4.
115 Rooted<CalendarValue> calendar(cx, CalendarValue(cx->names().iso8601));
116 Rooted<PlainDateTimeWithCalendar> temporalDateTime(cx);
117 if (!CreateTemporalDateTime(cx, dateTime, calendar, &temporalDateTime)) {
118 return false;
121 // Step 5.
122 if (offsetBehaviour == OffsetBehaviour::Wall ||
123 offsetOption == TemporalOffset::Ignore) {
124 // Steps 5.a-b.
125 return GetInstantFor(cx, timeZone, temporalDateTime, disambiguation,
126 result);
129 // Step 6.
130 if (offsetBehaviour == OffsetBehaviour::Exact ||
131 offsetOption == TemporalOffset::Use) {
132 // Step 6.a.
133 auto epochNanoseconds = GetUTCEpochNanoseconds(
134 dateTime, InstantSpan::fromNanoseconds(offsetNanoseconds));
136 // Step 6.b.
137 if (!IsValidEpochInstant(epochNanoseconds)) {
138 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
139 JSMSG_TEMPORAL_INSTANT_INVALID);
140 return false;
143 // Step 6.c.
144 *result = epochNanoseconds;
145 return true;
148 // Step 7.
149 MOZ_ASSERT(offsetBehaviour == OffsetBehaviour::Option);
151 // Step 8.
152 MOZ_ASSERT(offsetOption == TemporalOffset::Prefer ||
153 offsetOption == TemporalOffset::Reject);
155 // FIXME: spec issue - duplicate assertion
157 // Step 9.
158 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
159 timeZone, TimeZoneMethod::GetPossibleInstantsFor));
161 // Step 10.
162 Rooted<InstantVector> possibleInstants(cx, InstantVector(cx));
163 if (!GetPossibleInstantsFor(cx, timeZone, temporalDateTime,
164 &possibleInstants)) {
165 return false;
168 // Step 11.
169 if (!possibleInstants.empty()) {
170 // Step 11.a.
171 Rooted<Wrapped<InstantObject*>> candidate(cx);
172 for (size_t i = 0; i < possibleInstants.length(); i++) {
173 candidate = possibleInstants[i];
175 // Step 11.a.i.
176 int64_t candidateNanoseconds;
177 if (!GetOffsetNanosecondsFor(cx, timeZone, candidate,
178 &candidateNanoseconds)) {
179 return false;
181 MOZ_ASSERT(std::abs(candidateNanoseconds) <
182 ToNanoseconds(TemporalUnit::Day));
184 // Step 11.a.ii.
185 if (candidateNanoseconds == offsetNanoseconds) {
186 auto* unwrapped = candidate.unwrap(cx);
187 if (!unwrapped) {
188 return false;
191 *result = ToInstant(unwrapped);
192 return true;
195 // Step 11.a.iii.
196 if (matchBehaviour == MatchBehaviour::MatchMinutes) {
197 // Step 11.a.iii.1.
198 int64_t roundedCandidateNanoseconds =
199 RoundNanosecondsToMinutesIncrement(candidateNanoseconds);
201 // Step 11.a.iii.2.
202 if (roundedCandidateNanoseconds == offsetNanoseconds) {
203 auto* unwrapped = candidate.unwrap(cx);
204 if (!unwrapped) {
205 return false;
208 // Step 11.a.iii.2.a.
209 *result = ToInstant(unwrapped);
210 return true;
216 // Step 12.
217 if (offsetOption == TemporalOffset::Reject) {
218 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
219 JSMSG_TEMPORAL_ZONED_DATE_TIME_NO_TIME_FOUND);
220 return false;
223 // Step 13.
224 Rooted<Wrapped<InstantObject*>> instant(cx);
225 if (!DisambiguatePossibleInstants(cx, possibleInstants, timeZone,
226 ToPlainDateTime(temporalDateTime),
227 disambiguation, &instant)) {
228 return false;
231 auto* unwrappedInstant = instant.unwrap(cx);
232 if (!unwrappedInstant) {
233 return false;
236 // Step 14.
237 *result = ToInstant(unwrappedInstant);
238 return true;
242 * ToTemporalZonedDateTime ( item [ , options ] )
244 static bool ToTemporalZonedDateTime(JSContext* cx, Handle<Value> item,
245 Handle<JSObject*> maybeOptions,
246 MutableHandle<ZonedDateTime> result) {
247 // Step 1. (Not applicable in our implementation)
249 // Step 2.
250 Rooted<PlainObject*> maybeResolvedOptions(cx);
251 if (maybeOptions) {
252 maybeResolvedOptions = SnapshotOwnProperties(cx, maybeOptions);
253 if (!maybeResolvedOptions) {
254 return false;
258 // Step 3.
259 auto offsetBehaviour = OffsetBehaviour::Option;
261 // Step 4.
262 auto matchBehaviour = MatchBehaviour::MatchExactly;
264 // Step 7. (Reordered)
265 int64_t offsetNanoseconds = 0;
267 // Step 5.
268 Rooted<CalendarValue> calendar(cx);
269 Rooted<TimeZoneValue> timeZone(cx);
270 PlainDateTime dateTime;
271 auto disambiguation = TemporalDisambiguation::Compatible;
272 auto offsetOption = TemporalOffset::Reject;
273 if (item.isObject()) {
274 Rooted<JSObject*> itemObj(cx, &item.toObject());
276 // Step 5.a.
277 if (auto* zonedDateTime = itemObj->maybeUnwrapIf<ZonedDateTimeObject>()) {
278 auto instant = ToInstant(zonedDateTime);
279 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
280 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
282 if (!timeZone.wrap(cx)) {
283 return false;
285 if (!calendar.wrap(cx)) {
286 return false;
289 result.set(ZonedDateTime{instant, timeZone, calendar});
290 return true;
293 // Step 5.b.
294 if (!GetTemporalCalendarWithISODefault(cx, itemObj, &calendar)) {
295 return false;
298 // Step 5.c.
299 Rooted<CalendarRecord> calendarRec(cx);
300 if (!CreateCalendarMethodsRecord(cx, calendar,
302 CalendarMethod::DateFromFields,
303 CalendarMethod::Fields,
305 &calendarRec)) {
306 return false;
309 // Step 5.d.
310 JS::RootedVector<PropertyKey> fieldNames(cx);
311 if (!CalendarFields(cx, calendarRec,
312 {CalendarField::Day, CalendarField::Month,
313 CalendarField::MonthCode, CalendarField::Year},
314 &fieldNames)) {
315 return false;
318 // Step 5.e.
319 if (!AppendSorted(cx, fieldNames.get(),
321 TemporalField::Hour,
322 TemporalField::Microsecond,
323 TemporalField::Millisecond,
324 TemporalField::Minute,
325 TemporalField::Nanosecond,
326 TemporalField::Offset,
327 TemporalField::Second,
328 TemporalField::TimeZone,
329 })) {
330 return false;
333 // Step 5.f.
334 Rooted<PlainObject*> fields(
335 cx, PrepareTemporalFields(cx, itemObj, fieldNames,
336 {TemporalField::TimeZone}));
337 if (!fields) {
338 return false;
341 // Step 5.g.
342 Rooted<Value> timeZoneValue(cx);
343 if (!GetProperty(cx, fields, fields, cx->names().timeZone,
344 &timeZoneValue)) {
345 return false;
348 // Step 5.h.
349 if (!ToTemporalTimeZone(cx, timeZoneValue, &timeZone)) {
350 return false;
353 // Step 5.i.
354 Rooted<Value> offsetValue(cx);
355 if (!GetProperty(cx, fields, fields, cx->names().offset, &offsetValue)) {
356 return false;
359 // Step 5.j.
360 MOZ_ASSERT(offsetValue.isString() || offsetValue.isUndefined());
362 // Step 5.k.
363 Rooted<JSString*> offsetString(cx);
364 if (offsetValue.isString()) {
365 offsetString = offsetValue.toString();
366 } else {
367 offsetBehaviour = OffsetBehaviour::Wall;
370 if (maybeResolvedOptions) {
371 // Steps 5.l-m.
372 if (!ToTemporalDisambiguation(cx, maybeResolvedOptions,
373 &disambiguation)) {
374 return false;
377 // Step 5.n.
378 if (!ToTemporalOffset(cx, maybeResolvedOptions, &offsetOption)) {
379 return false;
382 // Step 5.o.
383 if (!InterpretTemporalDateTimeFields(cx, calendarRec, fields,
384 maybeResolvedOptions, &dateTime)) {
385 return false;
387 } else {
388 // Steps 5.l-n. (Not applicable)
390 // Step 5.o.
391 if (!InterpretTemporalDateTimeFields(cx, calendarRec, fields,
392 &dateTime)) {
393 return false;
397 // Step 8.
398 if (offsetBehaviour == OffsetBehaviour::Option) {
399 if (!ParseDateTimeUTCOffset(cx, offsetString, &offsetNanoseconds)) {
400 return false;
403 } else {
404 // Step 6.a.
405 if (!item.isString()) {
406 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
407 nullptr, "not a string");
408 return false;
410 Rooted<JSString*> string(cx, item.toString());
412 // Case 1: 19700101Z[+02:00]
413 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
415 // Case 2: 19700101+00:00[+02:00]
416 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "+02:00" }
418 // Case 3: 19700101[+02:00]
419 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
421 // Case 4: 19700101Z[Europe/Berlin]
422 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
424 // Case 5: 19700101+00:00[Europe/Berlin]
425 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "Europe/Berlin" }
427 // Case 6: 19700101[Europe/Berlin]
428 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
430 // Steps 6.b-c.
431 bool isUTC;
432 bool hasOffset;
433 int64_t timeZoneOffset;
434 Rooted<ParsedTimeZone> timeZoneString(cx);
435 Rooted<JSString*> calendarString(cx);
436 if (!ParseTemporalZonedDateTimeString(cx, string, &dateTime, &isUTC,
437 &hasOffset, &timeZoneOffset,
438 &timeZoneString, &calendarString)) {
439 return false;
442 // Step 6.d.
443 MOZ_ASSERT(timeZoneString);
445 // Step 6.e.
446 if (!ToTemporalTimeZone(cx, timeZoneString, &timeZone)) {
447 return false;
450 // Step 6.f. (Not applicable in our implementation.)
452 // Step 6.g.
453 if (isUTC) {
454 offsetBehaviour = OffsetBehaviour::Exact;
457 // Step 6.h.
458 else if (!hasOffset) {
459 offsetBehaviour = OffsetBehaviour::Wall;
462 // Steps 6.i-l.
463 if (calendarString) {
464 if (!ToBuiltinCalendar(cx, calendarString, &calendar)) {
465 return false;
467 } else {
468 calendar.set(CalendarValue(cx->names().iso8601));
471 // Step 6.m.
472 matchBehaviour = MatchBehaviour::MatchMinutes;
474 if (maybeResolvedOptions) {
475 // Step 6.n.
476 if (!ToTemporalDisambiguation(cx, maybeResolvedOptions,
477 &disambiguation)) {
478 return false;
481 // Step 6.o.
482 if (!ToTemporalOffset(cx, maybeResolvedOptions, &offsetOption)) {
483 return false;
486 // Step 6.p.
487 TemporalOverflow ignored;
488 if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
489 return false;
493 // Step 8.
494 if (offsetBehaviour == OffsetBehaviour::Option) {
495 MOZ_ASSERT(hasOffset);
496 offsetNanoseconds = timeZoneOffset;
500 // Step 9.
501 Rooted<TimeZoneRecord> timeZoneRec(cx);
502 if (!CreateTimeZoneMethodsRecord(cx, timeZone,
504 TimeZoneMethod::GetOffsetNanosecondsFor,
505 TimeZoneMethod::GetPossibleInstantsFor,
507 &timeZoneRec)) {
508 return false;
511 // Step 10.
512 Instant epochNanoseconds;
513 if (!InterpretISODateTimeOffset(
514 cx, dateTime, offsetBehaviour, offsetNanoseconds, timeZoneRec,
515 disambiguation, offsetOption, matchBehaviour, &epochNanoseconds)) {
516 return false;
519 // Step 11.
520 result.set(ZonedDateTime{epochNanoseconds, timeZone, calendar});
521 return true;
525 * ToTemporalZonedDateTime ( item [ , options ] )
527 static bool ToTemporalZonedDateTime(JSContext* cx, Handle<Value> item,
528 MutableHandle<ZonedDateTime> result) {
529 return ToTemporalZonedDateTime(cx, item, nullptr, result);
533 * ToTemporalZonedDateTime ( item [ , options ] )
535 static ZonedDateTimeObject* ToTemporalZonedDateTime(
536 JSContext* cx, Handle<Value> item, Handle<JSObject*> maybeOptions) {
537 Rooted<ZonedDateTime> result(cx);
538 if (!ToTemporalZonedDateTime(cx, item, maybeOptions, &result)) {
539 return nullptr;
541 return CreateTemporalZonedDateTime(cx, result.instant(), result.timeZone(),
542 result.calendar());
546 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
547 * newTarget ] )
549 static ZonedDateTimeObject* CreateTemporalZonedDateTime(
550 JSContext* cx, const CallArgs& args, Handle<BigInt*> epochNanoseconds,
551 Handle<TimeZoneValue> timeZone, Handle<CalendarValue> calendar) {
552 // Step 1.
553 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
555 // Steps 3-4.
556 Rooted<JSObject*> proto(cx);
557 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ZonedDateTime,
558 &proto)) {
559 return nullptr;
562 auto* obj = NewObjectWithClassProto<ZonedDateTimeObject>(cx, proto);
563 if (!obj) {
564 return nullptr;
567 // Step 4.
568 auto instant = ToInstant(epochNanoseconds);
569 obj->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT,
570 NumberValue(instant.seconds));
571 obj->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT,
572 Int32Value(instant.nanoseconds));
574 // Step 5.
575 obj->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT, timeZone.toSlotValue());
577 // Step 6.
578 obj->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT, calendar.toValue());
580 // Step 7.
581 return obj;
585 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
586 * newTarget ] )
588 ZonedDateTimeObject* js::temporal::CreateTemporalZonedDateTime(
589 JSContext* cx, const Instant& instant, Handle<TimeZoneValue> timeZone,
590 Handle<CalendarValue> calendar) {
591 // Step 1.
592 MOZ_ASSERT(IsValidEpochInstant(instant));
594 // Steps 2-3.
595 auto* obj = NewBuiltinClassInstance<ZonedDateTimeObject>(cx);
596 if (!obj) {
597 return nullptr;
600 // Step 4.
601 obj->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT,
602 NumberValue(instant.seconds));
603 obj->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT,
604 Int32Value(instant.nanoseconds));
606 // Step 5.
607 obj->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT, timeZone.toSlotValue());
609 // Step 6.
610 obj->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT, calendar.toValue());
612 // Step 7.
613 return obj;
616 struct PlainDateTimeAndInstant {
617 PlainDateTime dateTime;
618 Instant instant;
622 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
623 * overflow ] )
625 static bool AddDaysToZonedDateTime(JSContext* cx, const Instant& instant,
626 const PlainDateTime& dateTime,
627 Handle<TimeZoneRecord> timeZone,
628 Handle<CalendarValue> calendar, int64_t days,
629 TemporalOverflow overflow,
630 PlainDateTimeAndInstant* result) {
631 // Step 1. (Not applicable in our implementation.)
633 // Step 2. (Not applicable)
635 // Step 3.
636 if (days == 0) {
637 *result = {dateTime, instant};
638 return true;
641 // Step 4.
642 PlainDate addedDate;
643 if (!AddISODate(cx, dateTime.date, {0, 0, 0, days}, overflow, &addedDate)) {
644 return false;
647 // Step 5.
648 Rooted<PlainDateTimeWithCalendar> dateTimeResult(cx);
649 if (!CreateTemporalDateTime(cx, {addedDate, dateTime.time}, calendar,
650 &dateTimeResult)) {
651 return false;
654 // Step 6.
655 Instant instantResult;
656 if (!GetInstantFor(cx, timeZone, dateTimeResult,
657 TemporalDisambiguation::Compatible, &instantResult)) {
658 return false;
661 // Step 7.
662 *result = {ToPlainDateTime(dateTimeResult), instantResult};
663 return true;
667 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
668 * overflow ] )
670 bool js::temporal::AddDaysToZonedDateTime(
671 JSContext* cx, const Instant& instant, const PlainDateTime& dateTime,
672 Handle<TimeZoneRecord> timeZone, Handle<CalendarValue> calendar,
673 int64_t days, TemporalOverflow overflow, Instant* result) {
674 // Steps 1-7.
675 PlainDateTimeAndInstant dateTimeAndInstant;
676 if (!::AddDaysToZonedDateTime(cx, instant, dateTime, timeZone, calendar, days,
677 overflow, &dateTimeAndInstant)) {
678 return false;
681 *result = dateTimeAndInstant.instant;
682 return true;
686 * AddDaysToZonedDateTime ( instant, dateTime, timeZoneRec, calendar, days [ ,
687 * overflow ] )
689 bool js::temporal::AddDaysToZonedDateTime(JSContext* cx, const Instant& instant,
690 const PlainDateTime& dateTime,
691 Handle<TimeZoneRecord> timeZone,
692 Handle<CalendarValue> calendar,
693 int64_t days, Instant* result) {
694 // Step 2.
695 auto overflow = TemporalOverflow::Constrain;
697 // Steps 1 and 3-7.
698 return AddDaysToZonedDateTime(cx, instant, dateTime, timeZone, calendar, days,
699 overflow, result);
703 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
704 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
706 static bool AddZonedDateTime(JSContext* cx, const Instant& epochNanoseconds,
707 Handle<TimeZoneRecord> timeZone,
708 Handle<CalendarRecord> calendar,
709 const NormalizedDuration& duration,
710 mozilla::Maybe<const PlainDateTime&> dateTime,
711 Handle<JSObject*> maybeOptions, Instant* result) {
712 MOZ_ASSERT(IsValidEpochInstant(epochNanoseconds));
713 MOZ_ASSERT(IsValidDuration(duration));
715 // Step 1.
716 MOZ_ASSERT(TimeZoneMethodsRecordHasLookedUp(
717 timeZone, TimeZoneMethod::GetPossibleInstantsFor));
719 // Steps 2-3.
720 MOZ_ASSERT_IF(!dateTime,
721 TimeZoneMethodsRecordHasLookedUp(
722 timeZone, TimeZoneMethod::GetOffsetNanosecondsFor));
724 // Steps 4-5. (Not applicable in our implementation)
726 // Step 6.
727 if (duration.date == DateDuration{}) {
728 // Step 6.a.
729 return AddInstant(cx, epochNanoseconds, duration.time, result);
732 // Step 7. (Not applicable in our implementation)
734 // Steps 8-9.
735 PlainDateTime temporalDateTime;
736 if (dateTime) {
737 // Step 8.a.
738 temporalDateTime = *dateTime;
739 } else {
740 // Step 9.a.
741 if (!GetPlainDateTimeFor(cx, timeZone, epochNanoseconds,
742 &temporalDateTime)) {
743 return false;
746 auto& [date, time] = temporalDateTime;
748 // Step 10.
749 if (duration.date.years == 0 && duration.date.months == 0 &&
750 duration.date.weeks == 0) {
751 // Step 10.a.
752 auto overflow = TemporalOverflow::Constrain;
753 if (maybeOptions) {
754 if (!ToTemporalOverflow(cx, maybeOptions, &overflow)) {
755 return false;
759 // Step 10.b.
760 Instant intermediate;
761 if (!AddDaysToZonedDateTime(cx, epochNanoseconds, temporalDateTime,
762 timeZone, calendar.receiver(),
763 duration.date.days, overflow, &intermediate)) {
764 return false;
767 // Step 10.c.
768 return AddInstant(cx, intermediate, duration.time, result);
771 // Step 11.
772 MOZ_ASSERT(
773 CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateAdd));
775 // Step 12.
776 const auto& datePart = date;
778 // Step 13.
779 const auto& dateDuration = duration.date;
781 // Step 14.
782 PlainDate addedDate;
783 if (maybeOptions) {
784 if (!CalendarDateAdd(cx, calendar, datePart, dateDuration, maybeOptions,
785 &addedDate)) {
786 return false;
788 } else {
789 if (!CalendarDateAdd(cx, calendar, datePart, dateDuration, &addedDate)) {
790 return false;
794 // Step 15.
795 Rooted<PlainDateTimeWithCalendar> intermediateDateTime(cx);
796 if (!CreateTemporalDateTime(cx, {addedDate, time}, calendar.receiver(),
797 &intermediateDateTime)) {
798 return false;
801 // Step 16.
802 Instant intermediateInstant;
803 if (!GetInstantFor(cx, timeZone, intermediateDateTime,
804 TemporalDisambiguation::Compatible,
805 &intermediateInstant)) {
806 return false;
809 // Step 17.
810 return AddInstant(cx, intermediateInstant, duration.time, result);
814 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
815 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
817 static bool AddZonedDateTime(JSContext* cx, const Instant& epochNanoseconds,
818 Handle<TimeZoneRecord> timeZone,
819 Handle<CalendarRecord> calendar,
820 const NormalizedDuration& duration,
821 Handle<JSObject*> maybeOptions, Instant* result) {
822 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar, duration,
823 mozilla::Nothing(), maybeOptions, result);
827 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
828 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
830 bool js::temporal::AddZonedDateTime(JSContext* cx,
831 const Instant& epochNanoseconds,
832 Handle<TimeZoneRecord> timeZone,
833 Handle<CalendarRecord> calendar,
834 const NormalizedDuration& duration,
835 Instant* result) {
836 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar, duration,
837 mozilla::Nothing(), nullptr, result);
841 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
842 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
844 bool js::temporal::AddZonedDateTime(JSContext* cx,
845 const Instant& epochNanoseconds,
846 Handle<TimeZoneRecord> timeZone,
847 Handle<CalendarRecord> calendar,
848 const NormalizedDuration& duration,
849 const PlainDateTime& dateTime,
850 Instant* result) {
851 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar, duration,
852 mozilla::SomeRef(dateTime), nullptr, result);
856 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
857 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
859 bool js::temporal::AddZonedDateTime(JSContext* cx,
860 const Instant& epochNanoseconds,
861 Handle<TimeZoneRecord> timeZone,
862 Handle<CalendarRecord> calendar,
863 const DateDuration& duration,
864 Instant* result) {
865 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar,
866 {duration, {}}, mozilla::Nothing(), nullptr,
867 result);
871 * AddZonedDateTime ( epochNanoseconds, timeZoneRec, calendarRec, years, months,
872 * weeks, days, norm [ , precalculatedPlainDateTime [ , options ] ] )
874 bool js::temporal::AddZonedDateTime(JSContext* cx,
875 const Instant& epochNanoseconds,
876 Handle<TimeZoneRecord> timeZone,
877 Handle<CalendarRecord> calendar,
878 const DateDuration& duration,
879 const PlainDateTime& dateTime,
880 Instant* result) {
881 return ::AddZonedDateTime(cx, epochNanoseconds, timeZone, calendar,
882 {duration, {}}, mozilla::SomeRef(dateTime), nullptr,
883 result);
887 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
888 * precalculatedPlainDateTime ] )
890 static bool NormalizedTimeDurationToDays(
891 JSContext* cx, const NormalizedTimeDuration& duration,
892 Handle<ZonedDateTime> zonedRelativeTo, Handle<TimeZoneRecord> timeZone,
893 mozilla::Maybe<const PlainDateTime&> precalculatedPlainDateTime,
894 NormalizedTimeAndDays* result) {
895 MOZ_ASSERT(IsValidNormalizedTimeDuration(duration));
897 // Step 1.
898 int32_t sign = NormalizedTimeDurationSign(duration);
900 // Step 2.
901 if (sign == 0) {
902 *result = {int64_t(0), int64_t(0), ToNanoseconds(TemporalUnit::Day)};
903 return true;
906 // Step 3.
907 const auto& startNs = zonedRelativeTo.instant();
909 // Step 5.
910 auto endNs = AddNormalizedTimeDurationToEpochNanoseconds(duration, startNs);
912 // Step 6.
913 if (!IsValidEpochInstant(endNs)) {
914 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
915 JSMSG_TEMPORAL_INSTANT_INVALID);
916 return false;
919 // Steps 4 and 8.
920 PlainDateTime startDateTime;
921 if (!precalculatedPlainDateTime) {
922 if (!GetPlainDateTimeFor(cx, timeZone, startNs, &startDateTime)) {
923 return false;
925 } else {
926 startDateTime = *precalculatedPlainDateTime;
929 // Steps 7 and 9.
930 PlainDateTime endDateTime;
931 if (!GetPlainDateTimeFor(cx, timeZone, endNs, &endDateTime)) {
932 return false;
935 // Steps 10-11. (Not applicable in our implementation.)
937 // Step 12.
939 // Overflows in step 21 can be safely ignored, because they take too long to
940 // happen for int64.
941 int64_t days = DaysUntil(startDateTime.date, endDateTime.date);
943 // Step 13.
944 int32_t timeSign = CompareTemporalTime(startDateTime.time, endDateTime.time);
946 // Steps 14-15.
947 if (days > 0 && timeSign > 0) {
948 days -= 1;
949 } else if (days < 0 && timeSign < 0) {
950 days += 1;
953 // Step 16.
954 PlainDateTimeAndInstant relativeResult;
955 if (!::AddDaysToZonedDateTime(cx, startNs, startDateTime, timeZone,
956 zonedRelativeTo.calendar(), days,
957 TemporalOverflow::Constrain, &relativeResult)) {
958 return false;
960 MOZ_ASSERT(IsValidISODateTime(relativeResult.dateTime));
961 MOZ_ASSERT(IsValidEpochInstant(relativeResult.instant));
963 // Step 17.
964 if (sign > 0) {
965 // Step 17.a.
966 while (days > 0 && relativeResult.instant > endNs) {
967 // This loop can iterate indefinitely when given a specially crafted
968 // time zone object, so we need to check for interrupts.
969 if (!CheckForInterrupt(cx)) {
970 return false;
973 // Step 17.a.i.
974 days -= 1;
976 // Step 17.a.ii.
977 if (!::AddDaysToZonedDateTime(
978 cx, startNs, startDateTime, timeZone, zonedRelativeTo.calendar(),
979 days, TemporalOverflow::Constrain, &relativeResult)) {
980 return false;
982 MOZ_ASSERT(IsValidISODateTime(relativeResult.dateTime));
983 MOZ_ASSERT(IsValidEpochInstant(relativeResult.instant));
986 MOZ_ASSERT_IF(days > 0, relativeResult.instant <= endNs);
989 MOZ_ASSERT_IF(days == 0, relativeResult.instant == startNs);
991 // Step 18. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
992 auto ns = endNs - relativeResult.instant;
993 MOZ_ASSERT(IsValidInstantSpan(ns));
995 // Steps 19-21.
996 auto dayLengthNs = InstantSpan{};
997 while (true) {
998 // This loop can iterate indefinitely when given a specially crafted time
999 // zone object, so we need to check for interrupts.
1000 if (!CheckForInterrupt(cx)) {
1001 return false;
1004 // Step 21.a.
1005 PlainDateTimeAndInstant oneDayFarther;
1006 if (!::AddDaysToZonedDateTime(
1007 cx, relativeResult.instant, relativeResult.dateTime, timeZone,
1008 zonedRelativeTo.calendar(), sign, TemporalOverflow::Constrain,
1009 &oneDayFarther)) {
1010 return false;
1012 MOZ_ASSERT(IsValidISODateTime(oneDayFarther.dateTime));
1013 MOZ_ASSERT(IsValidEpochInstant(oneDayFarther.instant));
1015 // Step 21.b. (Inlined NormalizedTimeDurationFromEpochNanosecondsDifference)
1016 dayLengthNs = oneDayFarther.instant - relativeResult.instant;
1017 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs));
1019 // clang-format off
1021 // First iteration:
1023 // ns = endNs - relativeResult.instant
1024 // dayLengthNs = oneDayFarther.instant - relativeResult.instant
1025 // diff = ns - dayLengthNs
1026 // = (endNs - relativeResult.instant) - (oneDayFarther.instant - relativeResult.instant)
1027 // = endNs - relativeResult.instant - oneDayFarther.instant + relativeResult.instant
1028 // = endNs - oneDayFarther.instant
1030 // Second iteration:
1032 // ns = diff'
1033 // = endNs - oneDayFarther.instant'
1034 // relativeResult.instant = oneDayFarther.instant'
1035 // dayLengthNs = oneDayFarther.instant - relativeResult.instant
1036 // = oneDayFarther.instant - oneDayFarther.instant'
1037 // diff = ns - dayLengthNs
1038 // = (endNs - oneDayFarther.instant') - (oneDayFarther.instant - oneDayFarther.instant')
1039 // = endNs - oneDayFarther.instant' - oneDayFarther.instant + oneDayFarther.instant'
1040 // = endNs - oneDayFarther.instant
1042 // Where |diff'| and |oneDayFarther.instant'| denote the variables from the
1043 // previous iteration.
1045 // This repeats for all following iterations.
1047 // |endNs| and |oneDayFarther.instant| are both valid epoch instant values,
1048 // so the difference is a valid epoch instant difference value, too.
1050 // clang-format on
1052 // FIXME: spec issue - SubtractNormalizedTimeDuration should be infallible
1054 // Step 21.c. (Inlined SubtractNormalizedTimeDuration)
1055 auto oneDayLess = ns - dayLengthNs;
1056 MOZ_ASSERT(IsValidInstantSpan(oneDayLess));
1057 MOZ_ASSERT(oneDayLess == (endNs - oneDayFarther.instant));
1059 if (oneDayLess == InstantSpan{} ||
1060 ((oneDayLess < InstantSpan{}) == (sign < 0))) {
1061 // Step 21.c.i.
1062 ns = oneDayLess;
1064 // Step 21.c.ii.
1065 relativeResult = oneDayFarther;
1067 // Step 21.c.iii.
1068 days += sign;
1069 } else {
1070 // Step 21.d.
1071 break;
1075 // Step 22.
1076 if (days < 0 && sign > 0) {
1077 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1078 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN,
1079 "days");
1080 return false;
1083 // Step 23.
1084 if (days > 0 && sign < 0) {
1085 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1086 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN,
1087 "days");
1088 return false;
1091 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs));
1092 MOZ_ASSERT(IsValidInstantSpan(ns));
1094 // FIXME: spec issue - rewrite steps 24-25 as:
1096 // If sign = -1, then
1097 // If nanoseconds > 0, throw a RangeError.
1098 // Else,
1099 // Assert: nanoseconds ≥ 0.
1101 // https://github.com/tc39/proposal-temporal/issues/2530
1103 // Steps 24-25.
1104 if (sign < 0) {
1105 if (ns > InstantSpan{}) {
1106 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1107 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN,
1108 "nanoseconds");
1109 return false;
1111 } else {
1112 MOZ_ASSERT(ns >= InstantSpan{});
1115 // Steps 26-27.
1116 dayLengthNs = dayLengthNs.abs();
1117 MOZ_ASSERT(ns.abs() < dayLengthNs);
1119 // Step 28.
1120 constexpr auto maxDayLength = Int128{1} << 53;
1121 auto dayLengthNanos = dayLengthNs.toNanoseconds();
1122 if (dayLengthNanos >= maxDayLength) {
1123 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1124 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN,
1125 "days");
1126 return false;
1129 auto timeNanos = ns.toNanoseconds();
1130 MOZ_ASSERT(timeNanos == Int128{int64_t(timeNanos)},
1131 "abs(ns) < dayLengthNs < 2**53 implies that |ns| fits in int64");
1133 // FIXME: spec issue - restrict days to 2**53 / (24*60*60)?
1135 // Valid duration days are smaller than ⌈(2**53) / (24 * 60 * 60)⌉.
1136 static constexpr int64_t durationDays = (int64_t(1) << 53) / (24 * 60 * 60);
1138 // NOTE: This case won't happen in practice, because the initial value of
1139 // |days| is at most ±200'000'000 and the loop can only increment resp.
1140 // decrement |days| by one.
1141 if (std::abs(days) > durationDays) {
1142 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1143 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN,
1144 "days");
1145 return false;
1148 // Step 29.
1149 *result = {days, int64_t{timeNanos}, int64_t(dayLengthNanos)};
1150 return true;
1154 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
1155 * precalculatedPlainDateTime ] )
1157 bool js::temporal::NormalizedTimeDurationToDays(
1158 JSContext* cx, const NormalizedTimeDuration& duration,
1159 Handle<ZonedDateTime> zonedRelativeTo, Handle<TimeZoneRecord> timeZone,
1160 NormalizedTimeAndDays* result) {
1161 return ::NormalizedTimeDurationToDays(cx, duration, zonedRelativeTo, timeZone,
1162 mozilla::Nothing(), result);
1166 * NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ ,
1167 * precalculatedPlainDateTime ] )
1169 bool js::temporal::NormalizedTimeDurationToDays(
1170 JSContext* cx, const NormalizedTimeDuration& duration,
1171 Handle<ZonedDateTime> zonedRelativeTo, Handle<TimeZoneRecord> timeZone,
1172 const PlainDateTime& precalculatedPlainDateTime,
1173 NormalizedTimeAndDays* result) {
1174 return ::NormalizedTimeDurationToDays(
1175 cx, duration, zonedRelativeTo, timeZone,
1176 mozilla::SomeRef(precalculatedPlainDateTime), result);
1180 * DifferenceZonedDateTime ( ns1, ns2, timeZoneRec, calendarRec, largestUnit,
1181 * options, precalculatedPlainDateTime )
1183 static bool DifferenceZonedDateTime(
1184 JSContext* cx, const Instant& ns1, const Instant& ns2,
1185 Handle<TimeZoneRecord> timeZone, Handle<CalendarRecord> calendar,
1186 TemporalUnit largestUnit, Handle<PlainObject*> maybeOptions,
1187 mozilla::Maybe<const PlainDateTime&> precalculatedPlainDateTime,
1188 NormalizedDuration* result) {
1189 MOZ_ASSERT(IsValidEpochInstant(ns1));
1190 MOZ_ASSERT(IsValidEpochInstant(ns2));
1192 // Steps 1.
1193 if (ns1 == ns2) {
1194 *result = CreateNormalizedDurationRecord({}, {});
1195 return true;
1198 // Steps 2-3.
1199 PlainDateTime startDateTime;
1200 if (!precalculatedPlainDateTime) {
1201 // Steps 2.a-b.
1202 if (!GetPlainDateTimeFor(cx, timeZone, ns1, &startDateTime)) {
1203 return false;
1205 } else {
1206 // Step 3.a.
1207 startDateTime = *precalculatedPlainDateTime;
1210 // Steps 4-5.
1211 PlainDateTime endDateTime;
1212 if (!GetPlainDateTimeFor(cx, timeZone, ns2, &endDateTime)) {
1213 return false;
1216 // Step 6.
1217 NormalizedDuration dateDifference;
1218 if (maybeOptions) {
1219 if (!DifferenceISODateTime(cx, startDateTime, endDateTime, calendar,
1220 largestUnit, maybeOptions, &dateDifference)) {
1221 return false;
1223 } else {
1224 if (!DifferenceISODateTime(cx, startDateTime, endDateTime, calendar,
1225 largestUnit, &dateDifference)) {
1226 return false;
1230 // Step 7.
1231 Instant intermediateNs;
1232 if (!AddZonedDateTime(cx, ns1, timeZone, calendar,
1233 DateDuration{
1234 dateDifference.date.years,
1235 dateDifference.date.months,
1236 dateDifference.date.weeks,
1238 startDateTime, &intermediateNs)) {
1239 return false;
1241 MOZ_ASSERT(IsValidEpochInstant(intermediateNs));
1243 // Step 8.
1244 auto timeDuration =
1245 NormalizedTimeDurationFromEpochNanosecondsDifference(ns2, intermediateNs);
1247 // Step 9.
1248 Rooted<ZonedDateTime> intermediate(
1250 ZonedDateTime{intermediateNs, timeZone.receiver(), calendar.receiver()});
1252 // Step 10.
1253 NormalizedTimeAndDays timeAndDays;
1254 if (!NormalizedTimeDurationToDays(cx, timeDuration, intermediate, timeZone,
1255 &timeAndDays)) {
1256 return false;
1259 // Step 11.
1260 auto dateDuration = DateDuration{
1261 dateDifference.date.years,
1262 dateDifference.date.months,
1263 dateDifference.date.weeks,
1264 timeAndDays.days,
1266 if (!ThrowIfInvalidDuration(cx, dateDuration)) {
1267 return false;
1270 return CreateNormalizedDurationRecord(
1271 cx, dateDuration,
1272 NormalizedTimeDuration::fromNanoseconds(timeAndDays.time), result);
1276 * DifferenceZonedDateTime ( ns1, ns2, timeZoneRec, calendarRec, largestUnit,
1277 * options, precalculatedPlainDateTime )
1279 bool js::temporal::DifferenceZonedDateTime(
1280 JSContext* cx, const Instant& ns1, const Instant& ns2,
1281 Handle<TimeZoneRecord> timeZone, Handle<CalendarRecord> calendar,
1282 TemporalUnit largestUnit, const PlainDateTime& precalculatedPlainDateTime,
1283 NormalizedDuration* result) {
1284 return ::DifferenceZonedDateTime(
1285 cx, ns1, ns2, timeZone, calendar, largestUnit, nullptr,
1286 mozilla::SomeRef(precalculatedPlainDateTime), result);
1290 * TimeZoneEquals ( one, two )
1292 static bool TimeZoneEqualsOrThrow(JSContext* cx, Handle<TimeZoneValue> one,
1293 Handle<TimeZoneValue> two) {
1294 // Step 1.
1295 if (one.isObject() && two.isObject() && one.toObject() == two.toObject()) {
1296 return true;
1299 // Step 2.
1300 Rooted<JSString*> timeZoneOne(cx, ToTemporalTimeZoneIdentifier(cx, one));
1301 if (!timeZoneOne) {
1302 return false;
1305 // Step 3.
1306 Rooted<JSString*> timeZoneTwo(cx, ToTemporalTimeZoneIdentifier(cx, two));
1307 if (!timeZoneTwo) {
1308 return false;
1311 // Steps 4-9.
1312 bool equals;
1313 if (!TimeZoneEquals(cx, timeZoneOne, timeZoneTwo, &equals)) {
1314 return false;
1316 if (equals) {
1317 return true;
1320 // Throw an error when the time zone identifiers don't match. Used when
1321 // unequal time zones throw a RangeError.
1322 if (auto charsOne = QuoteString(cx, timeZoneOne)) {
1323 if (auto charsTwo = QuoteString(cx, timeZoneTwo)) {
1324 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1325 JSMSG_TEMPORAL_TIMEZONE_INCOMPATIBLE,
1326 charsOne.get(), charsTwo.get());
1329 return false;
1333 * RoundISODateTime ( year, month, day, hour, minute, second, millisecond,
1334 * microsecond, nanosecond, increment, unit, roundingMode [ , dayLength ] )
1336 static bool RoundISODateTime(JSContext* cx, const PlainDateTime& dateTime,
1337 Increment increment, TemporalUnit unit,
1338 TemporalRoundingMode roundingMode,
1339 const InstantSpan& dayLength,
1340 PlainDateTime* result) {
1341 MOZ_ASSERT(IsValidInstantSpan(dayLength));
1342 MOZ_ASSERT(dayLength > (InstantSpan{}));
1344 const auto& [date, time] = dateTime;
1346 // Step 1.
1347 MOZ_ASSERT(IsValidISODateTime(dateTime));
1348 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
1350 // Step 2. (Not applicable in our implementation.)
1352 // Step 3.
1353 auto roundedTime = RoundTime(time, increment, unit, roundingMode, dayLength);
1355 // |dayLength| can be as small as 1, so the number of rounded days can be as
1356 // large as the number of nanoseconds in |time|.
1357 MOZ_ASSERT(0 <= roundedTime.days &&
1358 roundedTime.days < ToNanoseconds(TemporalUnit::Day));
1360 // Step 4.
1361 PlainDate balanceResult;
1362 if (!BalanceISODate(cx, date.year, date.month,
1363 int64_t(date.day) + roundedTime.days, &balanceResult)) {
1364 return false;
1367 // Step 5.
1368 *result = {balanceResult, roundedTime.time};
1369 return true;
1373 * DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options )
1375 static bool DifferenceTemporalZonedDateTime(JSContext* cx,
1376 TemporalDifference operation,
1377 const CallArgs& args) {
1378 Rooted<ZonedDateTime> zonedDateTime(
1379 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1381 // Step 1. (Not applicable in our implementation.)
1383 // Step 2.
1384 Rooted<ZonedDateTime> other(cx);
1385 if (!ToTemporalZonedDateTime(cx, args.get(0), &other)) {
1386 return false;
1389 // Step 3.
1390 if (!CalendarEqualsOrThrow(cx, zonedDateTime.calendar(), other.calendar())) {
1391 return false;
1394 // Steps 4-5.
1395 Rooted<PlainObject*> resolvedOptions(cx);
1396 DifferenceSettings settings;
1397 if (args.hasDefined(1)) {
1398 Rooted<JSObject*> options(
1399 cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
1400 if (!options) {
1401 return false;
1404 // Step 4.
1405 resolvedOptions = SnapshotOwnProperties(cx, options);
1406 if (!resolvedOptions) {
1407 return false;
1410 // Step 5.
1411 if (!GetDifferenceSettings(
1412 cx, operation, resolvedOptions, TemporalUnitGroup::DateTime,
1413 TemporalUnit::Nanosecond, TemporalUnit::Hour, &settings)) {
1414 return false;
1416 } else {
1417 // Steps 4-5.
1418 settings = {
1419 TemporalUnit::Nanosecond,
1420 TemporalUnit::Hour,
1421 TemporalRoundingMode::Trunc,
1422 Increment{1},
1426 // Step 6.
1427 if (settings.largestUnit > TemporalUnit::Day) {
1428 MOZ_ASSERT(settings.smallestUnit >= settings.largestUnit);
1430 // Step 6.a.
1431 auto difference = DifferenceInstant(
1432 zonedDateTime.instant(), other.instant(), settings.roundingIncrement,
1433 settings.smallestUnit, settings.roundingMode);
1435 // Step 6.b.
1436 auto balancedTime = BalanceTimeDuration(difference, settings.largestUnit);
1438 // Step 6.c.
1439 auto duration = balancedTime.toDuration();
1440 if (operation == TemporalDifference::Since) {
1441 duration = duration.negate();
1444 auto* result = CreateTemporalDuration(cx, duration);
1445 if (!result) {
1446 return false;
1449 args.rval().setObject(*result);
1450 return true;
1453 // FIXME: spec issue - move this step next to the calendar validation?
1454 // https://github.com/tc39/proposal-temporal/issues/2533
1456 // Step 7.
1457 if (!TimeZoneEqualsOrThrow(cx, zonedDateTime.timeZone(), other.timeZone())) {
1458 return false;
1461 // Step 8.
1462 if (zonedDateTime.instant() == other.instant()) {
1463 auto* obj = CreateTemporalDuration(cx, {});
1464 if (!obj) {
1465 return false;
1468 args.rval().setObject(*obj);
1469 return true;
1472 // Step 9.
1473 Rooted<TimeZoneRecord> timeZone(cx);
1474 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
1476 TimeZoneMethod::GetOffsetNanosecondsFor,
1477 TimeZoneMethod::GetPossibleInstantsFor,
1479 &timeZone)) {
1480 return false;
1483 // Step 10.
1484 Rooted<CalendarRecord> calendar(cx);
1485 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
1487 CalendarMethod::DateAdd,
1488 CalendarMethod::DateUntil,
1490 &calendar)) {
1491 return false;
1494 // Steps 11-12.
1495 PlainDateTime precalculatedPlainDateTime;
1496 if (!GetPlainDateTimeFor(cx, timeZone, zonedDateTime.instant(),
1497 &precalculatedPlainDateTime)) {
1498 return false;
1501 // Step 13.
1502 Rooted<PlainDateObject*> plainRelativeTo(
1503 cx, CreateTemporalDate(cx, precalculatedPlainDateTime.date,
1504 calendar.receiver()));
1505 if (!plainRelativeTo) {
1506 return false;
1509 // Step 14.
1510 if (resolvedOptions) {
1511 Rooted<Value> largestUnitValue(
1512 cx, StringValue(TemporalUnitToString(cx, settings.largestUnit)));
1513 if (!DefineDataProperty(cx, resolvedOptions, cx->names().largestUnit,
1514 largestUnitValue)) {
1515 return false;
1519 // Step 15.
1520 NormalizedDuration difference;
1521 if (!::DifferenceZonedDateTime(
1522 cx, zonedDateTime.instant(), other.instant(), timeZone, calendar,
1523 settings.largestUnit, resolvedOptions,
1524 mozilla::SomeRef<const PlainDateTime>(precalculatedPlainDateTime),
1525 &difference)) {
1526 return false;
1529 // Step 16.
1530 bool roundingGranularityIsNoop =
1531 settings.smallestUnit == TemporalUnit::Nanosecond &&
1532 settings.roundingIncrement == Increment{1};
1534 // Step 17.
1535 if (!roundingGranularityIsNoop) {
1536 // Steps 17.a-b.
1537 NormalizedDuration roundResult;
1538 if (!RoundDuration(cx, difference, settings.roundingIncrement,
1539 settings.smallestUnit, settings.roundingMode,
1540 plainRelativeTo, calendar, zonedDateTime, timeZone,
1541 precalculatedPlainDateTime, &roundResult)) {
1542 return false;
1545 // Step 17.c.
1546 NormalizedTimeAndDays timeAndDays;
1547 if (!NormalizedTimeDurationToDays(cx, roundResult.time, zonedDateTime,
1548 timeZone, &timeAndDays)) {
1549 return false;
1552 // Step 17.d.
1553 int64_t days = roundResult.date.days + timeAndDays.days;
1555 // Step 17.e.
1556 auto toAdjust = NormalizedDuration{
1558 roundResult.date.years,
1559 roundResult.date.months,
1560 roundResult.date.weeks,
1561 days,
1563 NormalizedTimeDuration::fromNanoseconds(timeAndDays.time),
1565 NormalizedDuration adjustResult;
1566 if (!AdjustRoundedDurationDays(cx, toAdjust, settings.roundingIncrement,
1567 settings.smallestUnit, settings.roundingMode,
1568 zonedDateTime, calendar, timeZone,
1569 precalculatedPlainDateTime, &adjustResult)) {
1570 return false;
1573 // Step 17.f.
1574 DateDuration balanceResult;
1575 if (!temporal::BalanceDateDurationRelative(
1576 cx, adjustResult.date, settings.largestUnit, settings.smallestUnit,
1577 plainRelativeTo, calendar, &balanceResult)) {
1578 return false;
1581 // Step 17.g.
1582 if (!CombineDateAndNormalizedTimeDuration(cx, balanceResult,
1583 adjustResult.time, &difference)) {
1584 return false;
1588 // Step 18.
1589 auto timeDuration = BalanceTimeDuration(difference.time, TemporalUnit::Hour);
1591 // Step 19.
1592 auto duration = Duration{
1593 double(difference.date.years), double(difference.date.months),
1594 double(difference.date.weeks), double(difference.date.days),
1595 double(timeDuration.hours), double(timeDuration.minutes),
1596 double(timeDuration.seconds), double(timeDuration.milliseconds),
1597 timeDuration.microseconds, timeDuration.nanoseconds,
1599 if (operation == TemporalDifference::Since) {
1600 duration = duration.negate();
1602 MOZ_ASSERT(IsValidDuration(duration));
1604 auto* obj = CreateTemporalDuration(cx, duration);
1605 if (!obj) {
1606 return false;
1609 args.rval().setObject(*obj);
1610 return true;
1613 enum class ZonedDateTimeDuration { Add, Subtract };
1616 * AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime,
1617 * temporalDurationLike, options )
1619 static bool AddDurationToOrSubtractDurationFromZonedDateTime(
1620 JSContext* cx, ZonedDateTimeDuration operation, const CallArgs& args) {
1621 Rooted<ZonedDateTime> zonedDateTime(
1622 cx, &args.thisv().toObject().as<ZonedDateTimeObject>());
1624 // Step 1. (Not applicable in our implementation.)
1626 // Step 2.
1627 Duration duration;
1628 if (!ToTemporalDurationRecord(cx, args.get(0), &duration)) {
1629 return false;
1632 // Step 3.
1633 Rooted<JSObject*> options(cx);
1634 if (args.hasDefined(1)) {
1635 const char* name =
1636 operation == ZonedDateTimeDuration::Add ? "add" : "subtract";
1637 options = RequireObjectArg(cx, "options", name, args[1]);
1638 } else {
1639 options = NewPlainObjectWithProto(cx, nullptr);
1641 if (!options) {
1642 return false;
1645 // Step 4.
1646 Rooted<TimeZoneRecord> timeZone(cx);
1647 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
1649 TimeZoneMethod::GetOffsetNanosecondsFor,
1650 TimeZoneMethod::GetPossibleInstantsFor,
1652 &timeZone)) {
1653 return false;
1656 // Step 5.
1657 Rooted<CalendarRecord> calendar(cx);
1658 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
1660 CalendarMethod::DateAdd,
1662 &calendar)) {
1663 return false;
1666 // Step 6.
1667 if (operation == ZonedDateTimeDuration::Subtract) {
1668 duration = duration.negate();
1670 auto normalized = CreateNormalizedDurationRecord(duration);
1672 // Step 7.
1673 Instant resultInstant;
1674 if (!::AddZonedDateTime(cx, zonedDateTime.instant(), timeZone, calendar,
1675 normalized, options, &resultInstant)) {
1676 return false;
1678 MOZ_ASSERT(IsValidEpochInstant(resultInstant));
1680 // Step 8.
1681 auto* result = CreateTemporalZonedDateTime(
1682 cx, resultInstant, timeZone.receiver(), calendar.receiver());
1683 if (!result) {
1684 return false;
1687 args.rval().setObject(*result);
1688 return true;
1692 * Temporal.ZonedDateTime ( epochNanoseconds, timeZoneLike [ , calendarLike ] )
1694 static bool ZonedDateTimeConstructor(JSContext* cx, unsigned argc, Value* vp) {
1695 CallArgs args = CallArgsFromVp(argc, vp);
1697 // Step 1.
1698 if (!ThrowIfNotConstructing(cx, args, "Temporal.ZonedDateTime")) {
1699 return false;
1702 // Step 2.
1703 Rooted<BigInt*> epochNanoseconds(cx, js::ToBigInt(cx, args.get(0)));
1704 if (!epochNanoseconds) {
1705 return false;
1708 // Step 3.
1709 if (!IsValidEpochNanoseconds(epochNanoseconds)) {
1710 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1711 JSMSG_TEMPORAL_INSTANT_INVALID);
1712 return false;
1715 // Step 4.
1716 Rooted<TimeZoneValue> timeZone(cx);
1717 if (!ToTemporalTimeZone(cx, args.get(1), &timeZone)) {
1718 return false;
1721 // Step 5.
1722 Rooted<CalendarValue> calendar(cx);
1723 if (!ToTemporalCalendarWithISODefault(cx, args.get(2), &calendar)) {
1724 return false;
1727 // Step 6.
1728 auto* obj = CreateTemporalZonedDateTime(cx, args, epochNanoseconds, timeZone,
1729 calendar);
1730 if (!obj) {
1731 return false;
1734 args.rval().setObject(*obj);
1735 return true;
1739 * Temporal.ZonedDateTime.from ( item [ , options ] )
1741 static bool ZonedDateTime_from(JSContext* cx, unsigned argc, Value* vp) {
1742 CallArgs args = CallArgsFromVp(argc, vp);
1744 // Step 1.
1745 Rooted<JSObject*> options(cx);
1746 if (args.hasDefined(1)) {
1747 options = RequireObjectArg(cx, "options", "from", args[1]);
1748 if (!options) {
1749 return false;
1753 // Step 2.
1754 if (args.get(0).isObject()) {
1755 JSObject* item = &args[0].toObject();
1756 if (auto* zonedDateTime = item->maybeUnwrapIf<ZonedDateTimeObject>()) {
1757 auto epochInstant = ToInstant(zonedDateTime);
1758 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
1759 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
1761 if (!timeZone.wrap(cx)) {
1762 return false;
1764 if (!calendar.wrap(cx)) {
1765 return false;
1768 if (options) {
1769 // Steps 2.a-b.
1770 TemporalDisambiguation ignoredDisambiguation;
1771 if (!ToTemporalDisambiguation(cx, options, &ignoredDisambiguation)) {
1772 return false;
1775 // Step 2.c.
1776 TemporalOffset ignoredOffset;
1777 if (!ToTemporalOffset(cx, options, &ignoredOffset)) {
1778 return false;
1781 // Step 2.d.
1782 TemporalOverflow ignoredOverflow;
1783 if (!ToTemporalOverflow(cx, options, &ignoredOverflow)) {
1784 return false;
1788 // Step 2.e.
1789 auto* result =
1790 CreateTemporalZonedDateTime(cx, epochInstant, timeZone, calendar);
1791 if (!result) {
1792 return false;
1795 args.rval().setObject(*result);
1796 return true;
1800 // Step 3.
1801 auto* result = ToTemporalZonedDateTime(cx, args.get(0), options);
1802 if (!result) {
1803 return false;
1806 args.rval().setObject(*result);
1807 return true;
1811 * Temporal.ZonedDateTime.compare ( one, two )
1813 static bool ZonedDateTime_compare(JSContext* cx, unsigned argc, Value* vp) {
1814 CallArgs args = CallArgsFromVp(argc, vp);
1816 // Step 1.
1817 Rooted<ZonedDateTime> one(cx);
1818 if (!ToTemporalZonedDateTime(cx, args.get(0), &one)) {
1819 return false;
1822 // Step 2.
1823 Rooted<ZonedDateTime> two(cx);
1824 if (!ToTemporalZonedDateTime(cx, args.get(1), &two)) {
1825 return false;
1828 // Step 3.
1829 const auto& oneNs = one.instant();
1830 const auto& twoNs = two.instant();
1831 args.rval().setInt32(oneNs > twoNs ? 1 : oneNs < twoNs ? -1 : 0);
1832 return true;
1836 * get Temporal.ZonedDateTime.prototype.calendarId
1838 static bool ZonedDateTime_calendarId(JSContext* cx, const CallArgs& args) {
1839 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
1841 // Step 3.
1842 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
1843 auto* calendarId = ToTemporalCalendarIdentifier(cx, calendar);
1844 if (!calendarId) {
1845 return false;
1848 args.rval().setString(calendarId);
1849 return true;
1853 * get Temporal.ZonedDateTime.prototype.calendarId
1855 static bool ZonedDateTime_calendarId(JSContext* cx, unsigned argc, Value* vp) {
1856 // Steps 1-2.
1857 CallArgs args = CallArgsFromVp(argc, vp);
1858 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_calendarId>(cx,
1859 args);
1863 * get Temporal.ZonedDateTime.prototype.timeZoneId
1865 static bool ZonedDateTime_timeZoneId(JSContext* cx, const CallArgs& args) {
1866 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
1868 // Step 3.
1869 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
1870 auto* timeZoneId = ToTemporalTimeZoneIdentifier(cx, timeZone);
1871 if (!timeZoneId) {
1872 return false;
1875 args.rval().setString(timeZoneId);
1876 return true;
1880 * get Temporal.ZonedDateTime.prototype.timeZoneId
1882 static bool ZonedDateTime_timeZoneId(JSContext* cx, unsigned argc, Value* vp) {
1883 // Steps 1-2.
1884 CallArgs args = CallArgsFromVp(argc, vp);
1885 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_timeZoneId>(cx,
1886 args);
1890 * get Temporal.ZonedDateTime.prototype.year
1892 static bool ZonedDateTime_year(JSContext* cx, const CallArgs& args) {
1893 Rooted<ZonedDateTime> zonedDateTime(
1894 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1896 // Steps 3-6.
1897 PlainDateTime dateTime;
1898 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
1899 zonedDateTime.instant(), &dateTime)) {
1900 return false;
1903 // Step 7.
1904 return CalendarYear(cx, zonedDateTime.calendar(), dateTime, args.rval());
1908 * get Temporal.ZonedDateTime.prototype.year
1910 static bool ZonedDateTime_year(JSContext* cx, unsigned argc, Value* vp) {
1911 // Steps 1-2.
1912 CallArgs args = CallArgsFromVp(argc, vp);
1913 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_year>(cx, args);
1917 * get Temporal.ZonedDateTime.prototype.month
1919 static bool ZonedDateTime_month(JSContext* cx, const CallArgs& args) {
1920 Rooted<ZonedDateTime> zonedDateTime(
1921 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1923 // Steps 3-6.
1924 PlainDateTime dateTime;
1925 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
1926 zonedDateTime.instant(), &dateTime)) {
1927 return false;
1930 // Step 7.
1931 return CalendarMonth(cx, zonedDateTime.calendar(), dateTime, args.rval());
1935 * get Temporal.ZonedDateTime.prototype.month
1937 static bool ZonedDateTime_month(JSContext* cx, unsigned argc, Value* vp) {
1938 // Steps 1-2.
1939 CallArgs args = CallArgsFromVp(argc, vp);
1940 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_month>(cx, args);
1944 * get Temporal.ZonedDateTime.prototype.monthCode
1946 static bool ZonedDateTime_monthCode(JSContext* cx, const CallArgs& args) {
1947 Rooted<ZonedDateTime> zonedDateTime(
1948 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1950 // Steps 3-6.
1951 PlainDateTime dateTime;
1952 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
1953 zonedDateTime.instant(), &dateTime)) {
1954 return false;
1957 // Step 7.
1958 return CalendarMonthCode(cx, zonedDateTime.calendar(), dateTime, args.rval());
1962 * get Temporal.ZonedDateTime.prototype.monthCode
1964 static bool ZonedDateTime_monthCode(JSContext* cx, unsigned argc, Value* vp) {
1965 // Steps 1-2.
1966 CallArgs args = CallArgsFromVp(argc, vp);
1967 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_monthCode>(cx,
1968 args);
1972 * get Temporal.ZonedDateTime.prototype.day
1974 static bool ZonedDateTime_day(JSContext* cx, const CallArgs& args) {
1975 Rooted<ZonedDateTime> zonedDateTime(
1976 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
1978 // Step 4. (Reordered)
1979 Rooted<CalendarRecord> calendar(cx);
1980 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
1982 CalendarMethod::Day,
1984 &calendar)) {
1985 return false;
1988 // Steps 3 and 5-6.
1989 PlainDateTime dateTime;
1990 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
1991 zonedDateTime.instant(), &dateTime)) {
1992 return false;
1995 // Step 7.
1996 return CalendarDay(cx, calendar, dateTime, args.rval());
2000 * get Temporal.ZonedDateTime.prototype.day
2002 static bool ZonedDateTime_day(JSContext* cx, unsigned argc, Value* vp) {
2003 // Steps 1-2.
2004 CallArgs args = CallArgsFromVp(argc, vp);
2005 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_day>(cx, args);
2009 * get Temporal.ZonedDateTime.prototype.hour
2011 static bool ZonedDateTime_hour(JSContext* cx, const CallArgs& args) {
2012 Rooted<ZonedDateTime> zonedDateTime(
2013 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2015 // Steps 3-6.
2016 PlainDateTime dateTime;
2017 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2018 zonedDateTime.instant(), &dateTime)) {
2019 return false;
2022 // Step 7.
2023 args.rval().setInt32(dateTime.time.hour);
2024 return true;
2028 * get Temporal.ZonedDateTime.prototype.hour
2030 static bool ZonedDateTime_hour(JSContext* cx, unsigned argc, Value* vp) {
2031 // Steps 1-2.
2032 CallArgs args = CallArgsFromVp(argc, vp);
2033 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_hour>(cx, args);
2037 * get Temporal.ZonedDateTime.prototype.minute
2039 static bool ZonedDateTime_minute(JSContext* cx, const CallArgs& args) {
2040 Rooted<ZonedDateTime> zonedDateTime(
2041 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2043 // Steps 3-6.
2044 PlainDateTime dateTime;
2045 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2046 zonedDateTime.instant(), &dateTime)) {
2047 return false;
2050 // Step 7.
2051 args.rval().setInt32(dateTime.time.minute);
2052 return true;
2056 * get Temporal.ZonedDateTime.prototype.minute
2058 static bool ZonedDateTime_minute(JSContext* cx, unsigned argc, Value* vp) {
2059 // Steps 1-2.
2060 CallArgs args = CallArgsFromVp(argc, vp);
2061 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_minute>(cx, args);
2065 * get Temporal.ZonedDateTime.prototype.second
2067 static bool ZonedDateTime_second(JSContext* cx, const CallArgs& args) {
2068 Rooted<ZonedDateTime> zonedDateTime(
2069 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2071 // Steps 3-6.
2072 PlainDateTime dateTime;
2073 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2074 zonedDateTime.instant(), &dateTime)) {
2075 return false;
2078 // Step 7.
2079 args.rval().setInt32(dateTime.time.second);
2080 return true;
2084 * get Temporal.ZonedDateTime.prototype.second
2086 static bool ZonedDateTime_second(JSContext* cx, unsigned argc, Value* vp) {
2087 // Steps 1-2.
2088 CallArgs args = CallArgsFromVp(argc, vp);
2089 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_second>(cx, args);
2093 * get Temporal.ZonedDateTime.prototype.millisecond
2095 static bool ZonedDateTime_millisecond(JSContext* cx, const CallArgs& args) {
2096 Rooted<ZonedDateTime> zonedDateTime(
2097 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2099 // Steps 3-6.
2100 PlainDateTime dateTime;
2101 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2102 zonedDateTime.instant(), &dateTime)) {
2103 return false;
2106 // Step 7.
2107 args.rval().setInt32(dateTime.time.millisecond);
2108 return true;
2112 * get Temporal.ZonedDateTime.prototype.millisecond
2114 static bool ZonedDateTime_millisecond(JSContext* cx, unsigned argc, Value* vp) {
2115 // Steps 1-2.
2116 CallArgs args = CallArgsFromVp(argc, vp);
2117 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_millisecond>(cx,
2118 args);
2122 * get Temporal.ZonedDateTime.prototype.microsecond
2124 static bool ZonedDateTime_microsecond(JSContext* cx, const CallArgs& args) {
2125 Rooted<ZonedDateTime> zonedDateTime(
2126 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2128 // Steps 3-6.
2129 PlainDateTime dateTime;
2130 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2131 zonedDateTime.instant(), &dateTime)) {
2132 return false;
2135 // Step 7.
2136 args.rval().setInt32(dateTime.time.microsecond);
2137 return true;
2141 * get Temporal.ZonedDateTime.prototype.microsecond
2143 static bool ZonedDateTime_microsecond(JSContext* cx, unsigned argc, Value* vp) {
2144 // Steps 1-2.
2145 CallArgs args = CallArgsFromVp(argc, vp);
2146 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_microsecond>(cx,
2147 args);
2151 * get Temporal.ZonedDateTime.prototype.nanosecond
2153 static bool ZonedDateTime_nanosecond(JSContext* cx, const CallArgs& args) {
2154 Rooted<ZonedDateTime> zonedDateTime(
2155 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2157 // Steps 3-6.
2158 PlainDateTime dateTime;
2159 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2160 zonedDateTime.instant(), &dateTime)) {
2161 return false;
2164 // Step 7.
2165 args.rval().setInt32(dateTime.time.nanosecond);
2166 return true;
2170 * get Temporal.ZonedDateTime.prototype.nanosecond
2172 static bool ZonedDateTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) {
2173 // Steps 1-2.
2174 CallArgs args = CallArgsFromVp(argc, vp);
2175 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_nanosecond>(cx,
2176 args);
2180 * get Temporal.ZonedDateTime.prototype.epochSeconds
2182 static bool ZonedDateTime_epochSeconds(JSContext* cx, const CallArgs& args) {
2183 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
2185 // Step 3.
2186 auto instant = ToInstant(zonedDateTime);
2188 // Steps 4-5.
2189 args.rval().setNumber(instant.seconds);
2190 return true;
2194 * get Temporal.ZonedDateTime.prototype.epochSeconds
2196 static bool ZonedDateTime_epochSeconds(JSContext* cx, unsigned argc,
2197 Value* vp) {
2198 // Steps 1-2.
2199 CallArgs args = CallArgsFromVp(argc, vp);
2200 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochSeconds>(
2201 cx, args);
2205 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
2207 static bool ZonedDateTime_epochMilliseconds(JSContext* cx,
2208 const CallArgs& args) {
2209 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
2211 // Step 3.
2212 auto instant = ToInstant(zonedDateTime);
2214 // Steps 4-5.
2215 args.rval().setNumber(instant.floorToMilliseconds());
2216 return true;
2220 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
2222 static bool ZonedDateTime_epochMilliseconds(JSContext* cx, unsigned argc,
2223 Value* vp) {
2224 // Steps 1-2.
2225 CallArgs args = CallArgsFromVp(argc, vp);
2226 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochMilliseconds>(
2227 cx, args);
2231 * get Temporal.ZonedDateTime.prototype.epochMicroseconds
2233 static bool ZonedDateTime_epochMicroseconds(JSContext* cx,
2234 const CallArgs& args) {
2235 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
2237 // Step 3.
2238 auto instant = ToInstant(zonedDateTime);
2240 // Step 4.
2241 auto* microseconds =
2242 BigInt::createFromInt64(cx, instant.floorToMicroseconds());
2243 if (!microseconds) {
2244 return false;
2247 // Step 5.
2248 args.rval().setBigInt(microseconds);
2249 return true;
2253 * get Temporal.ZonedDateTime.prototype.epochMicroseconds
2255 static bool ZonedDateTime_epochMicroseconds(JSContext* cx, unsigned argc,
2256 Value* vp) {
2257 // Steps 1-2.
2258 CallArgs args = CallArgsFromVp(argc, vp);
2259 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochMicroseconds>(
2260 cx, args);
2264 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
2266 static bool ZonedDateTime_epochNanoseconds(JSContext* cx,
2267 const CallArgs& args) {
2268 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
2270 // Step 3.
2271 auto* nanoseconds = ToEpochNanoseconds(cx, ToInstant(zonedDateTime));
2272 if (!nanoseconds) {
2273 return false;
2276 args.rval().setBigInt(nanoseconds);
2277 return true;
2281 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
2283 static bool ZonedDateTime_epochNanoseconds(JSContext* cx, unsigned argc,
2284 Value* vp) {
2285 // Steps 1-2.
2286 CallArgs args = CallArgsFromVp(argc, vp);
2287 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochNanoseconds>(
2288 cx, args);
2292 * get Temporal.ZonedDateTime.prototype.dayOfWeek
2294 static bool ZonedDateTime_dayOfWeek(JSContext* cx, const CallArgs& args) {
2295 Rooted<ZonedDateTime> zonedDateTime(
2296 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2298 // Steps 3-6.
2299 PlainDateTime dateTime;
2300 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2301 zonedDateTime.instant(), &dateTime)) {
2302 return false;
2305 // Step 7.
2306 return CalendarDayOfWeek(cx, zonedDateTime.calendar(), dateTime, args.rval());
2310 * get Temporal.ZonedDateTime.prototype.dayOfWeek
2312 static bool ZonedDateTime_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) {
2313 // Steps 1-2.
2314 CallArgs args = CallArgsFromVp(argc, vp);
2315 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_dayOfWeek>(cx,
2316 args);
2320 * get Temporal.ZonedDateTime.prototype.dayOfYear
2322 static bool ZonedDateTime_dayOfYear(JSContext* cx, const CallArgs& args) {
2323 Rooted<ZonedDateTime> zonedDateTime(
2324 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2326 // Steps 3-6.
2327 PlainDateTime dateTime;
2328 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2329 zonedDateTime.instant(), &dateTime)) {
2330 return false;
2333 // Step 7.
2334 return CalendarDayOfYear(cx, zonedDateTime.calendar(), dateTime, args.rval());
2338 * get Temporal.ZonedDateTime.prototype.dayOfYear
2340 static bool ZonedDateTime_dayOfYear(JSContext* cx, unsigned argc, Value* vp) {
2341 // Steps 1-2.
2342 CallArgs args = CallArgsFromVp(argc, vp);
2343 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_dayOfYear>(cx,
2344 args);
2348 * get Temporal.ZonedDateTime.prototype.weekOfYear
2350 static bool ZonedDateTime_weekOfYear(JSContext* cx, const CallArgs& args) {
2351 Rooted<ZonedDateTime> zonedDateTime(
2352 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2354 // Steps 3-6.
2355 PlainDateTime dateTime;
2356 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2357 zonedDateTime.instant(), &dateTime)) {
2358 return false;
2361 // Step 7.
2362 return CalendarWeekOfYear(cx, zonedDateTime.calendar(), dateTime,
2363 args.rval());
2367 * get Temporal.ZonedDateTime.prototype.weekOfYear
2369 static bool ZonedDateTime_weekOfYear(JSContext* cx, unsigned argc, Value* vp) {
2370 // Steps 1-2.
2371 CallArgs args = CallArgsFromVp(argc, vp);
2372 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_weekOfYear>(cx,
2373 args);
2377 * get Temporal.ZonedDateTime.prototype.yearOfWeek
2379 static bool ZonedDateTime_yearOfWeek(JSContext* cx, const CallArgs& args) {
2380 Rooted<ZonedDateTime> zonedDateTime(
2381 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2383 // Steps 3-6.
2384 PlainDateTime dateTime;
2385 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2386 zonedDateTime.instant(), &dateTime)) {
2387 return false;
2390 // Step 7.
2391 return CalendarYearOfWeek(cx, zonedDateTime.calendar(), dateTime,
2392 args.rval());
2396 * get Temporal.ZonedDateTime.prototype.yearOfWeek
2398 static bool ZonedDateTime_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) {
2399 // Steps 1-2.
2400 CallArgs args = CallArgsFromVp(argc, vp);
2401 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_yearOfWeek>(cx,
2402 args);
2406 * get Temporal.ZonedDateTime.prototype.hoursInDay
2408 static bool ZonedDateTime_hoursInDay(JSContext* cx, const CallArgs& args) {
2409 Rooted<ZonedDateTime> zonedDateTime(
2410 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2412 // Step 3.
2413 Rooted<TimeZoneRecord> timeZone(cx);
2414 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
2416 TimeZoneMethod::GetOffsetNanosecondsFor,
2417 TimeZoneMethod::GetPossibleInstantsFor,
2419 &timeZone)) {
2420 return false;
2423 // Step 4.
2424 const auto& instant = zonedDateTime.instant();
2426 // Step 5.
2427 PlainDateTime temporalDateTime;
2428 if (!GetPlainDateTimeFor(cx, timeZone, instant, &temporalDateTime)) {
2429 return false;
2432 // Steps 6-8.
2433 const auto& date = temporalDateTime.date;
2434 Rooted<CalendarValue> isoCalendar(cx, CalendarValue(cx->names().iso8601));
2436 // Step 9.
2437 Rooted<PlainDateTimeWithCalendar> today(cx);
2438 if (!CreateTemporalDateTime(cx, {date, {}}, isoCalendar, &today)) {
2439 return false;
2442 // Step 10.
2443 auto tomorrowFields = BalanceISODate(date.year, date.month, date.day + 1);
2445 // Step 11.
2446 Rooted<PlainDateTimeWithCalendar> tomorrow(cx);
2447 if (!CreateTemporalDateTime(cx, {tomorrowFields, {}}, isoCalendar,
2448 &tomorrow)) {
2449 return false;
2452 // Step 12.
2453 Instant todayInstant;
2454 if (!GetInstantFor(cx, timeZone, today, TemporalDisambiguation::Compatible,
2455 &todayInstant)) {
2456 return false;
2459 // Step 13.
2460 Instant tomorrowInstant;
2461 if (!GetInstantFor(cx, timeZone, tomorrow, TemporalDisambiguation::Compatible,
2462 &tomorrowInstant)) {
2463 return false;
2466 // Step 14.
2467 auto diffNs = tomorrowInstant - todayInstant;
2468 MOZ_ASSERT(IsValidInstantSpan(diffNs));
2470 // Step 15.
2471 constexpr int32_t secPerHour = 60 * 60;
2472 constexpr int64_t nsPerSec = ToNanoseconds(TemporalUnit::Second);
2473 constexpr double nsPerHour = ToNanoseconds(TemporalUnit::Hour);
2475 int64_t hours = diffNs.seconds / secPerHour;
2476 int64_t seconds = diffNs.seconds % secPerHour;
2477 int64_t nanoseconds = seconds * nsPerSec + diffNs.nanoseconds;
2479 double result = double(hours) + double(nanoseconds) / nsPerHour;
2480 args.rval().setNumber(result);
2481 return true;
2485 * get Temporal.ZonedDateTime.prototype.hoursInDay
2487 static bool ZonedDateTime_hoursInDay(JSContext* cx, unsigned argc, Value* vp) {
2488 // Steps 1-2.
2489 CallArgs args = CallArgsFromVp(argc, vp);
2490 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_hoursInDay>(cx,
2491 args);
2495 * get Temporal.ZonedDateTime.prototype.daysInWeek
2497 static bool ZonedDateTime_daysInWeek(JSContext* cx, const CallArgs& args) {
2498 Rooted<ZonedDateTime> zonedDateTime(
2499 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2501 // Steps 3-6.
2502 PlainDateTime dateTime;
2503 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2504 zonedDateTime.instant(), &dateTime)) {
2505 return false;
2508 // Step 7.
2509 return CalendarDaysInWeek(cx, zonedDateTime.calendar(), dateTime,
2510 args.rval());
2514 * get Temporal.ZonedDateTime.prototype.daysInWeek
2516 static bool ZonedDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) {
2517 // Steps 1-2.
2518 CallArgs args = CallArgsFromVp(argc, vp);
2519 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInWeek>(cx,
2520 args);
2524 * get Temporal.ZonedDateTime.prototype.daysInMonth
2526 static bool ZonedDateTime_daysInMonth(JSContext* cx, const CallArgs& args) {
2527 Rooted<ZonedDateTime> zonedDateTime(
2528 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2530 // Steps 3-6.
2531 PlainDateTime dateTime;
2532 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2533 zonedDateTime.instant(), &dateTime)) {
2534 return false;
2537 // Step 7.
2538 return CalendarDaysInMonth(cx, zonedDateTime.calendar(), dateTime,
2539 args.rval());
2543 * get Temporal.ZonedDateTime.prototype.daysInMonth
2545 static bool ZonedDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) {
2546 // Steps 1-2.
2547 CallArgs args = CallArgsFromVp(argc, vp);
2548 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInMonth>(cx,
2549 args);
2553 * get Temporal.ZonedDateTime.prototype.daysInYear
2555 static bool ZonedDateTime_daysInYear(JSContext* cx, const CallArgs& args) {
2556 Rooted<ZonedDateTime> zonedDateTime(
2557 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2559 // Steps 3-6.
2560 PlainDateTime dateTime;
2561 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2562 zonedDateTime.instant(), &dateTime)) {
2563 return false;
2566 // Step 7.
2567 return CalendarDaysInYear(cx, zonedDateTime.calendar(), dateTime,
2568 args.rval());
2572 * get Temporal.ZonedDateTime.prototype.daysInYear
2574 static bool ZonedDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) {
2575 // Steps 1-2.
2576 CallArgs args = CallArgsFromVp(argc, vp);
2577 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInYear>(cx,
2578 args);
2582 * get Temporal.ZonedDateTime.prototype.monthsInYear
2584 static bool ZonedDateTime_monthsInYear(JSContext* cx, const CallArgs& args) {
2585 Rooted<ZonedDateTime> zonedDateTime(
2586 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2588 // Steps 3-6.
2589 PlainDateTime dateTime;
2590 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2591 zonedDateTime.instant(), &dateTime)) {
2592 return false;
2595 // Step 7.
2596 return CalendarMonthsInYear(cx, zonedDateTime.calendar(), dateTime,
2597 args.rval());
2601 * get Temporal.ZonedDateTime.prototype.monthsInYear
2603 static bool ZonedDateTime_monthsInYear(JSContext* cx, unsigned argc,
2604 Value* vp) {
2605 // Steps 1-2.
2606 CallArgs args = CallArgsFromVp(argc, vp);
2607 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_monthsInYear>(
2608 cx, args);
2612 * get Temporal.ZonedDateTime.prototype.inLeapYear
2614 static bool ZonedDateTime_inLeapYear(JSContext* cx, const CallArgs& args) {
2615 Rooted<ZonedDateTime> zonedDateTime(
2616 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2618 // Steps 3-6.
2619 PlainDateTime dateTime;
2620 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2621 zonedDateTime.instant(), &dateTime)) {
2622 return false;
2625 // Step 7.
2626 return CalendarInLeapYear(cx, zonedDateTime.calendar(), dateTime,
2627 args.rval());
2631 * get Temporal.ZonedDateTime.prototype.inLeapYear
2633 static bool ZonedDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) {
2634 // Steps 1-2.
2635 CallArgs args = CallArgsFromVp(argc, vp);
2636 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_inLeapYear>(cx,
2637 args);
2641 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2643 static bool ZonedDateTime_offsetNanoseconds(JSContext* cx,
2644 const CallArgs& args) {
2645 Rooted<ZonedDateTime> zonedDateTime(
2646 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2648 // Step 3.
2649 auto timeZone = zonedDateTime.timeZone();
2651 // Step 4.
2652 const auto& instant = zonedDateTime.instant();
2654 // Step 5.
2655 int64_t offsetNanoseconds;
2656 if (!GetOffsetNanosecondsFor(cx, timeZone, instant, &offsetNanoseconds)) {
2657 return false;
2659 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
2661 args.rval().setNumber(offsetNanoseconds);
2662 return true;
2666 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2668 static bool ZonedDateTime_offsetNanoseconds(JSContext* cx, unsigned argc,
2669 Value* vp) {
2670 // Steps 1-2.
2671 CallArgs args = CallArgsFromVp(argc, vp);
2672 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offsetNanoseconds>(
2673 cx, args);
2677 * get Temporal.ZonedDateTime.prototype.offset
2679 static bool ZonedDateTime_offset(JSContext* cx, const CallArgs& args) {
2680 Rooted<ZonedDateTime> zonedDateTime(
2681 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2683 // Step 3.
2684 auto timeZone = zonedDateTime.timeZone();
2686 // Step 4.
2687 const auto& instant = zonedDateTime.instant();
2689 // Step 5.
2690 JSString* str = GetOffsetStringFor(cx, timeZone, instant);
2691 if (!str) {
2692 return false;
2695 args.rval().setString(str);
2696 return true;
2700 * get Temporal.ZonedDateTime.prototype.offset
2702 static bool ZonedDateTime_offset(JSContext* cx, unsigned argc, Value* vp) {
2703 // Steps 1-2.
2704 CallArgs args = CallArgsFromVp(argc, vp);
2705 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offset>(cx, args);
2709 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2710 * ] )
2712 static bool ZonedDateTime_with(JSContext* cx, const CallArgs& args) {
2713 Rooted<ZonedDateTime> zonedDateTime(
2714 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2716 // Step 3.
2717 Rooted<JSObject*> temporalZonedDateTimeLike(
2719 RequireObjectArg(cx, "temporalZonedDateTimeLike", "with", args.get(0)));
2720 if (!temporalZonedDateTimeLike) {
2721 return false;
2724 // Step 4.
2725 if (!RejectTemporalLikeObject(cx, temporalZonedDateTimeLike)) {
2726 return false;
2729 // Step 5.
2730 Rooted<PlainObject*> resolvedOptions(cx);
2731 if (args.hasDefined(1)) {
2732 Rooted<JSObject*> options(cx,
2733 RequireObjectArg(cx, "options", "with", args[1]));
2734 if (!options) {
2735 return false;
2737 resolvedOptions = SnapshotOwnProperties(cx, options);
2738 } else {
2739 resolvedOptions = NewPlainObjectWithProto(cx, nullptr);
2741 if (!resolvedOptions) {
2742 return false;
2745 // Step 6.
2746 Rooted<CalendarRecord> calendar(cx);
2747 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
2749 CalendarMethod::DateFromFields,
2750 CalendarMethod::Fields,
2751 CalendarMethod::MergeFields,
2753 &calendar)) {
2754 return false;
2757 // Step 7.
2758 Rooted<TimeZoneRecord> timeZone(cx);
2759 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
2761 TimeZoneMethod::GetOffsetNanosecondsFor,
2762 TimeZoneMethod::GetPossibleInstantsFor,
2764 &timeZone)) {
2765 return false;
2768 // Step 8.
2769 const auto& instant = zonedDateTime.instant();
2771 // Step 9.
2772 int64_t offsetNanoseconds;
2773 if (!GetOffsetNanosecondsFor(cx, timeZone, instant, &offsetNanoseconds)) {
2774 return false;
2777 // Step 10.
2778 Rooted<PlainDateTimeObject*> dateTime(
2780 GetPlainDateTimeFor(cx, instant, calendar.receiver(), offsetNanoseconds));
2781 if (!dateTime) {
2782 return false;
2785 // Step 11.
2786 JS::RootedVector<PropertyKey> fieldNames(cx);
2787 if (!CalendarFields(cx, calendar,
2788 {CalendarField::Day, CalendarField::Month,
2789 CalendarField::MonthCode, CalendarField::Year},
2790 &fieldNames)) {
2791 return false;
2794 // Step 12.
2795 Rooted<PlainObject*> fields(cx,
2796 PrepareTemporalFields(cx, dateTime, fieldNames));
2797 if (!fields) {
2798 return false;
2801 // Steps 13-18.
2802 struct TimeField {
2803 using FieldName = ImmutableTenuredPtr<PropertyName*> JSAtomState::*;
2805 FieldName name;
2806 int32_t value;
2807 } timeFields[] = {
2808 {&JSAtomState::hour, dateTime->isoHour()},
2809 {&JSAtomState::minute, dateTime->isoMinute()},
2810 {&JSAtomState::second, dateTime->isoSecond()},
2811 {&JSAtomState::millisecond, dateTime->isoMillisecond()},
2812 {&JSAtomState::microsecond, dateTime->isoMicrosecond()},
2813 {&JSAtomState::nanosecond, dateTime->isoNanosecond()},
2816 Rooted<Value> timeFieldValue(cx);
2817 for (const auto& timeField : timeFields) {
2818 Handle<PropertyName*> name = cx->names().*(timeField.name);
2819 timeFieldValue.setInt32(timeField.value);
2821 if (!DefineDataProperty(cx, fields, name, timeFieldValue)) {
2822 return false;
2826 // Step 19.
2827 JSString* fieldsOffset = FormatUTCOffsetNanoseconds(cx, offsetNanoseconds);
2828 if (!fieldsOffset) {
2829 return false;
2832 timeFieldValue.setString(fieldsOffset);
2833 if (!DefineDataProperty(cx, fields, cx->names().offset, timeFieldValue)) {
2834 return false;
2837 // Step 20.
2838 if (!AppendSorted(cx, fieldNames.get(),
2840 TemporalField::Hour,
2841 TemporalField::Microsecond,
2842 TemporalField::Millisecond,
2843 TemporalField::Minute,
2844 TemporalField::Nanosecond,
2845 TemporalField::Offset,
2846 TemporalField::Second,
2847 })) {
2848 return false;
2851 // Step 21.
2852 Rooted<PlainObject*> partialZonedDateTime(
2854 PreparePartialTemporalFields(cx, temporalZonedDateTimeLike, fieldNames));
2855 if (!partialZonedDateTime) {
2856 return false;
2859 // Step 22.
2860 Rooted<JSObject*> mergedFields(
2861 cx, CalendarMergeFields(cx, calendar, fields, partialZonedDateTime));
2862 if (!mergedFields) {
2863 return false;
2866 // Step 23.
2867 fields = PrepareTemporalFields(cx, mergedFields, fieldNames,
2868 {TemporalField::Offset});
2869 if (!fields) {
2870 return false;
2873 // Step 24-25.
2874 auto disambiguation = TemporalDisambiguation::Compatible;
2875 if (!ToTemporalDisambiguation(cx, resolvedOptions, &disambiguation)) {
2876 return false;
2879 // Step 26.
2880 auto offset = TemporalOffset::Prefer;
2881 if (!ToTemporalOffset(cx, resolvedOptions, &offset)) {
2882 return false;
2885 // Step 27.
2886 PlainDateTime dateTimeResult;
2887 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, resolvedOptions,
2888 &dateTimeResult)) {
2889 return false;
2892 // Step 28.
2893 Rooted<Value> offsetString(cx);
2894 if (!GetProperty(cx, fields, fields, cx->names().offset, &offsetString)) {
2895 return false;
2898 // Step 29.
2899 MOZ_ASSERT(offsetString.isString());
2901 // Step 30.
2902 Rooted<JSString*> offsetStr(cx, offsetString.toString());
2903 int64_t newOffsetNanoseconds;
2904 if (!ParseDateTimeUTCOffset(cx, offsetStr, &newOffsetNanoseconds)) {
2905 return false;
2908 // Step 31.
2909 Instant epochNanoseconds;
2910 if (!InterpretISODateTimeOffset(
2911 cx, dateTimeResult, OffsetBehaviour::Option, newOffsetNanoseconds,
2912 timeZone, disambiguation, offset, MatchBehaviour::MatchExactly,
2913 &epochNanoseconds)) {
2914 return false;
2917 // Step 32.
2918 auto* result = CreateTemporalZonedDateTime(
2919 cx, epochNanoseconds, timeZone.receiver(), calendar.receiver());
2920 if (!result) {
2921 return false;
2924 args.rval().setObject(*result);
2925 return true;
2929 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2930 * ] )
2932 static bool ZonedDateTime_with(JSContext* cx, unsigned argc, Value* vp) {
2933 // Steps 1-2.
2934 CallArgs args = CallArgsFromVp(argc, vp);
2935 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_with>(cx, args);
2939 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2941 static bool ZonedDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
2942 Rooted<ZonedDateTime> zonedDateTime(
2943 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2945 // Steps 3-4.
2946 PlainTime time = {};
2947 if (args.hasDefined(0)) {
2948 if (!ToTemporalTime(cx, args[0], &time)) {
2949 return false;
2953 // Step 5.
2954 Rooted<TimeZoneRecord> timeZone(cx);
2955 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
2957 TimeZoneMethod::GetOffsetNanosecondsFor,
2958 TimeZoneMethod::GetPossibleInstantsFor,
2960 &timeZone)) {
2961 return false;
2964 // Steps 6 and 8.
2965 PlainDateTime plainDateTime;
2966 if (!GetPlainDateTimeFor(cx, timeZone, zonedDateTime.instant(),
2967 &plainDateTime)) {
2968 return false;
2971 // Step 7.
2972 auto calendar = zonedDateTime.calendar();
2974 // Step 9.
2975 Rooted<PlainDateTimeWithCalendar> resultPlainDateTime(cx);
2976 if (!CreateTemporalDateTime(cx, {plainDateTime.date, time}, calendar,
2977 &resultPlainDateTime)) {
2978 return false;
2981 // Step 10.
2982 Instant instant;
2983 if (!GetInstantFor(cx, timeZone, resultPlainDateTime,
2984 TemporalDisambiguation::Compatible, &instant)) {
2985 return false;
2988 // Step 11.
2989 auto* result =
2990 CreateTemporalZonedDateTime(cx, instant, timeZone.receiver(), calendar);
2991 if (!result) {
2992 return false;
2995 args.rval().setObject(*result);
2996 return true;
3000 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
3002 static bool ZonedDateTime_withPlainTime(JSContext* cx, unsigned argc,
3003 Value* vp) {
3004 // Steps 1-2.
3005 CallArgs args = CallArgsFromVp(argc, vp);
3006 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withPlainTime>(
3007 cx, args);
3011 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3013 static bool ZonedDateTime_withPlainDate(JSContext* cx, const CallArgs& args) {
3014 Rooted<ZonedDateTime> zonedDateTime(
3015 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3017 // Step 3.
3018 Rooted<PlainDateWithCalendar> plainDate(cx);
3019 if (!ToTemporalDate(cx, args.get(0), &plainDate)) {
3020 return false;
3022 auto date = plainDate.date();
3024 // Step 4.
3025 Rooted<TimeZoneRecord> timeZone(cx);
3026 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
3028 TimeZoneMethod::GetOffsetNanosecondsFor,
3029 TimeZoneMethod::GetPossibleInstantsFor,
3031 &timeZone)) {
3032 return false;
3035 // Steps 5-6.
3036 PlainDateTime plainDateTime;
3037 if (!GetPlainDateTimeFor(cx, timeZone, zonedDateTime.instant(),
3038 &plainDateTime)) {
3039 return false;
3042 // Step 7.
3043 Rooted<CalendarValue> calendar(cx);
3044 if (!ConsolidateCalendars(cx, zonedDateTime.calendar(), plainDate.calendar(),
3045 &calendar)) {
3046 return false;
3049 // Step 8.
3050 Rooted<PlainDateTimeWithCalendar> resultPlainDateTime(cx);
3051 if (!CreateTemporalDateTime(cx, {date, plainDateTime.time}, calendar,
3052 &resultPlainDateTime)) {
3053 return false;
3056 // Step 9.
3057 Instant instant;
3058 if (!GetInstantFor(cx, timeZone, resultPlainDateTime,
3059 TemporalDisambiguation::Compatible, &instant)) {
3060 return false;
3063 // Step 10.
3064 auto* result =
3065 CreateTemporalZonedDateTime(cx, instant, timeZone.receiver(), calendar);
3066 if (!result) {
3067 return false;
3070 args.rval().setObject(*result);
3071 return true;
3075 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3077 static bool ZonedDateTime_withPlainDate(JSContext* cx, unsigned argc,
3078 Value* vp) {
3079 // Steps 1-2.
3080 CallArgs args = CallArgsFromVp(argc, vp);
3081 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withPlainDate>(
3082 cx, args);
3086 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3088 static bool ZonedDateTime_withTimeZone(JSContext* cx, const CallArgs& args) {
3089 Rooted<ZonedDateTime> zonedDateTime(
3090 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3092 // Step 3.
3093 Rooted<TimeZoneValue> timeZone(cx);
3094 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
3095 return false;
3098 // Step 4.
3099 auto* result = CreateTemporalZonedDateTime(
3100 cx, zonedDateTime.instant(), timeZone, zonedDateTime.calendar());
3101 if (!result) {
3102 return false;
3105 args.rval().setObject(*result);
3106 return true;
3110 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3112 static bool ZonedDateTime_withTimeZone(JSContext* cx, unsigned argc,
3113 Value* vp) {
3114 // Steps 1-2.
3115 CallArgs args = CallArgsFromVp(argc, vp);
3116 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withTimeZone>(
3117 cx, args);
3121 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3123 static bool ZonedDateTime_withCalendar(JSContext* cx, const CallArgs& args) {
3124 Rooted<ZonedDateTime> zonedDateTime(
3125 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3127 // Step 3.
3128 Rooted<CalendarValue> calendar(cx);
3129 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) {
3130 return false;
3133 // Step 4.
3134 auto* result = CreateTemporalZonedDateTime(
3135 cx, zonedDateTime.instant(), zonedDateTime.timeZone(), calendar);
3136 if (!result) {
3137 return false;
3140 args.rval().setObject(*result);
3141 return true;
3145 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3147 static bool ZonedDateTime_withCalendar(JSContext* cx, unsigned argc,
3148 Value* vp) {
3149 // Steps 1-2.
3150 CallArgs args = CallArgsFromVp(argc, vp);
3151 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withCalendar>(
3152 cx, args);
3156 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3158 static bool ZonedDateTime_add(JSContext* cx, const CallArgs& args) {
3159 return AddDurationToOrSubtractDurationFromZonedDateTime(
3160 cx, ZonedDateTimeDuration::Add, args);
3164 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3166 static bool ZonedDateTime_add(JSContext* cx, unsigned argc, Value* vp) {
3167 // Steps 1-2.
3168 CallArgs args = CallArgsFromVp(argc, vp);
3169 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_add>(cx, args);
3173 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3174 * ] )
3176 static bool ZonedDateTime_subtract(JSContext* cx, const CallArgs& args) {
3177 return AddDurationToOrSubtractDurationFromZonedDateTime(
3178 cx, ZonedDateTimeDuration::Subtract, args);
3182 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3183 * ] )
3185 static bool ZonedDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) {
3186 // Steps 1-2.
3187 CallArgs args = CallArgsFromVp(argc, vp);
3188 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_subtract>(cx,
3189 args);
3193 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3195 static bool ZonedDateTime_until(JSContext* cx, const CallArgs& args) {
3196 // Step 3.
3197 return DifferenceTemporalZonedDateTime(cx, TemporalDifference::Until, args);
3201 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3203 static bool ZonedDateTime_until(JSContext* cx, unsigned argc, Value* vp) {
3204 // Steps 1-2.
3205 CallArgs args = CallArgsFromVp(argc, vp);
3206 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_until>(cx, args);
3210 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3212 static bool ZonedDateTime_since(JSContext* cx, const CallArgs& args) {
3213 // Step 3.
3214 return DifferenceTemporalZonedDateTime(cx, TemporalDifference::Since, args);
3218 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3220 static bool ZonedDateTime_since(JSContext* cx, unsigned argc, Value* vp) {
3221 // Steps 1-2.
3222 CallArgs args = CallArgsFromVp(argc, vp);
3223 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_since>(cx, args);
3227 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3229 static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
3230 Rooted<ZonedDateTime> zonedDateTime(
3231 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3233 // Steps 3-12.
3234 auto smallestUnit = TemporalUnit::Auto;
3235 auto roundingMode = TemporalRoundingMode::HalfExpand;
3236 auto roundingIncrement = Increment{1};
3237 if (args.get(0).isString()) {
3238 // Step 4. (Not applicable in our implementation.)
3240 // Step 9.
3241 Rooted<JSString*> paramString(cx, args[0].toString());
3242 if (!GetTemporalUnit(cx, paramString, TemporalUnitKey::SmallestUnit,
3243 TemporalUnitGroup::DayTime, &smallestUnit)) {
3244 return false;
3247 // Steps 6-8 and 10-12. (Implicit)
3248 } else {
3249 // Steps 3 and 5.a
3250 Rooted<JSObject*> roundTo(
3251 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
3252 if (!roundTo) {
3253 return false;
3256 // Steps 6-7.
3257 if (!ToTemporalRoundingIncrement(cx, roundTo, &roundingIncrement)) {
3258 return false;
3261 // Step 8.
3262 if (!ToTemporalRoundingMode(cx, roundTo, &roundingMode)) {
3263 return false;
3266 // Step 9.
3267 if (!GetTemporalUnit(cx, roundTo, TemporalUnitKey::SmallestUnit,
3268 TemporalUnitGroup::DayTime, &smallestUnit)) {
3269 return false;
3272 if (smallestUnit == TemporalUnit::Auto) {
3273 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3274 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
3275 return false;
3278 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
3279 smallestUnit <= TemporalUnit::Nanosecond);
3281 // Steps 10-11.
3282 auto maximum = Increment{1};
3283 bool inclusive = true;
3284 if (smallestUnit > TemporalUnit::Day) {
3285 maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
3286 inclusive = false;
3289 // Step 12.
3290 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
3291 inclusive)) {
3292 return false;
3296 // Step 13.
3297 if (smallestUnit == TemporalUnit::Nanosecond &&
3298 roundingIncrement == Increment{1}) {
3299 // Step 13.a.
3300 auto* result = CreateTemporalZonedDateTime(cx, zonedDateTime.instant(),
3301 zonedDateTime.timeZone(),
3302 zonedDateTime.calendar());
3303 if (!result) {
3304 return false;
3307 args.rval().setObject(*result);
3308 return true;
3311 // Step 14.
3312 Rooted<TimeZoneRecord> timeZone(cx);
3313 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
3315 TimeZoneMethod::GetOffsetNanosecondsFor,
3316 TimeZoneMethod::GetPossibleInstantsFor,
3318 &timeZone)) {
3319 return false;
3322 // Step 16. (Reordered)
3323 auto calendar = zonedDateTime.calendar();
3325 // Steps 15 and 17.
3326 int64_t offsetNanoseconds;
3327 if (!GetOffsetNanosecondsFor(cx, timeZone, zonedDateTime.instant(),
3328 &offsetNanoseconds)) {
3329 return false;
3331 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
3333 // Step 18.
3334 auto temporalDateTime =
3335 GetPlainDateTimeFor(zonedDateTime.instant(), offsetNanoseconds);
3337 // Step 19.
3338 Rooted<CalendarValue> isoCalendar(cx, CalendarValue(cx->names().iso8601));
3339 Rooted<PlainDateTimeWithCalendar> dtStart(cx);
3340 if (!CreateTemporalDateTime(cx, {temporalDateTime.date, {}}, isoCalendar,
3341 &dtStart)) {
3342 return false;
3345 // Steps 20-21.
3346 Instant startNs;
3347 if (!GetInstantFor(cx, timeZone, dtStart, TemporalDisambiguation::Compatible,
3348 &startNs)) {
3349 return false;
3352 // Step 22.
3353 Instant endNs;
3354 if (!AddDaysToZonedDateTime(cx, startNs, ToPlainDateTime(dtStart), timeZone,
3355 calendar, 1, &endNs)) {
3356 return false;
3358 MOZ_ASSERT(IsValidEpochInstant(endNs));
3360 // Step 23.
3361 auto dayLengthNs = endNs - startNs;
3362 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs));
3364 // Step 24.
3365 if (dayLengthNs <= InstantSpan{}) {
3366 JS_ReportErrorNumberASCII(
3367 cx, GetErrorMessage, nullptr,
3368 JSMSG_TEMPORAL_ZONED_DATE_TIME_NON_POSITIVE_DAY_LENGTH);
3369 return false;
3372 // Step 25.
3373 PlainDateTime roundResult;
3374 if (!RoundISODateTime(cx, temporalDateTime, roundingIncrement, smallestUnit,
3375 roundingMode, dayLengthNs, &roundResult)) {
3376 return false;
3379 // Step 26.
3380 Instant epochNanoseconds;
3381 if (!InterpretISODateTimeOffset(
3382 cx, roundResult, OffsetBehaviour::Option, offsetNanoseconds, timeZone,
3383 TemporalDisambiguation::Compatible, TemporalOffset::Prefer,
3384 MatchBehaviour::MatchExactly, &epochNanoseconds)) {
3385 return false;
3388 // Step 27.
3389 auto* result = CreateTemporalZonedDateTime(cx, epochNanoseconds,
3390 timeZone.receiver(), calendar);
3391 if (!result) {
3392 return false;
3395 args.rval().setObject(*result);
3396 return true;
3400 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3402 static bool ZonedDateTime_round(JSContext* cx, unsigned argc, Value* vp) {
3403 // Steps 1-2.
3404 CallArgs args = CallArgsFromVp(argc, vp);
3405 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_round>(cx, args);
3409 * Temporal.ZonedDateTime.prototype.equals ( other )
3411 static bool ZonedDateTime_equals(JSContext* cx, const CallArgs& args) {
3412 Rooted<ZonedDateTime> zonedDateTime(
3413 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3415 // Step 3.
3416 Rooted<ZonedDateTime> other(cx);
3417 if (!ToTemporalZonedDateTime(cx, args.get(0), &other)) {
3418 return false;
3421 // Steps 4-6.
3422 bool equals = zonedDateTime.instant() == other.instant();
3423 if (equals && !TimeZoneEquals(cx, zonedDateTime.timeZone(), other.timeZone(),
3424 &equals)) {
3425 return false;
3427 if (equals && !CalendarEquals(cx, zonedDateTime.calendar(), other.calendar(),
3428 &equals)) {
3429 return false;
3432 args.rval().setBoolean(equals);
3433 return true;
3437 * Temporal.ZonedDateTime.prototype.equals ( other )
3439 static bool ZonedDateTime_equals(JSContext* cx, unsigned argc, Value* vp) {
3440 // Steps 1-2.
3441 CallArgs args = CallArgsFromVp(argc, vp);
3442 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_equals>(cx, args);
3446 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3448 static bool ZonedDateTime_toString(JSContext* cx, const CallArgs& args) {
3449 Rooted<ZonedDateTime> zonedDateTime(
3450 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3452 SecondsStringPrecision precision = {Precision::Auto(),
3453 TemporalUnit::Nanosecond, Increment{1}};
3454 auto roundingMode = TemporalRoundingMode::Trunc;
3455 auto showCalendar = CalendarOption::Auto;
3456 auto showTimeZone = TimeZoneNameOption::Auto;
3457 auto showOffset = ShowOffsetOption::Auto;
3458 if (args.hasDefined(0)) {
3459 // Step 3.
3460 Rooted<JSObject*> options(
3461 cx, RequireObjectArg(cx, "options", "toString", args[0]));
3462 if (!options) {
3463 return false;
3466 // Steps 4-5.
3467 if (!ToCalendarNameOption(cx, options, &showCalendar)) {
3468 return false;
3471 // Step 6.
3472 auto digits = Precision::Auto();
3473 if (!ToFractionalSecondDigits(cx, options, &digits)) {
3474 return false;
3477 // Step 7.
3478 if (!ToShowOffsetOption(cx, options, &showOffset)) {
3479 return false;
3482 // Step 8.
3483 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
3484 return false;
3487 // Step 9.
3488 auto smallestUnit = TemporalUnit::Auto;
3489 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
3490 TemporalUnitGroup::Time, &smallestUnit)) {
3491 return false;
3494 // Step 10.
3495 if (smallestUnit == TemporalUnit::Hour) {
3496 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3497 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
3498 "smallestUnit");
3499 return false;
3502 // Step 11.
3503 if (!ToTimeZoneNameOption(cx, options, &showTimeZone)) {
3504 return false;
3507 // Step 12.
3508 precision = ToSecondsStringPrecision(smallestUnit, digits);
3511 // Step 13.
3512 JSString* str = TemporalZonedDateTimeToString(
3513 cx, zonedDateTime, precision.precision, showCalendar, showTimeZone,
3514 showOffset, precision.increment, precision.unit, roundingMode);
3515 if (!str) {
3516 return false;
3519 args.rval().setString(str);
3520 return true;
3524 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3526 static bool ZonedDateTime_toString(JSContext* cx, unsigned argc, Value* vp) {
3527 // Steps 1-2.
3528 CallArgs args = CallArgsFromVp(argc, vp);
3529 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toString>(cx,
3530 args);
3534 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3536 static bool ZonedDateTime_toLocaleString(JSContext* cx, const CallArgs& args) {
3537 Rooted<ZonedDateTime> zonedDateTime(
3538 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3540 // Step 3.
3541 JSString* str = TemporalZonedDateTimeToString(
3542 cx, zonedDateTime, Precision::Auto(), CalendarOption::Auto,
3543 TimeZoneNameOption::Auto, ShowOffsetOption::Auto);
3544 if (!str) {
3545 return false;
3548 args.rval().setString(str);
3549 return true;
3553 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3555 static bool ZonedDateTime_toLocaleString(JSContext* cx, unsigned argc,
3556 Value* vp) {
3557 // Steps 1-2.
3558 CallArgs args = CallArgsFromVp(argc, vp);
3559 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toLocaleString>(
3560 cx, args);
3564 * Temporal.ZonedDateTime.prototype.toJSON ( )
3566 static bool ZonedDateTime_toJSON(JSContext* cx, const CallArgs& args) {
3567 Rooted<ZonedDateTime> zonedDateTime(
3568 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3570 // Step 3.
3571 JSString* str = TemporalZonedDateTimeToString(
3572 cx, zonedDateTime, Precision::Auto(), CalendarOption::Auto,
3573 TimeZoneNameOption::Auto, ShowOffsetOption::Auto);
3574 if (!str) {
3575 return false;
3578 args.rval().setString(str);
3579 return true;
3583 * Temporal.ZonedDateTime.prototype.toJSON ( )
3585 static bool ZonedDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) {
3586 // Steps 1-2.
3587 CallArgs args = CallArgsFromVp(argc, vp);
3588 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toJSON>(cx, args);
3592 * Temporal.ZonedDateTime.prototype.valueOf ( )
3594 static bool ZonedDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) {
3595 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
3596 "ZonedDateTime", "primitive type");
3597 return false;
3601 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3603 static bool ZonedDateTime_startOfDay(JSContext* cx, const CallArgs& args) {
3604 Rooted<ZonedDateTime> zonedDateTime(
3605 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3607 // Step 3.
3608 Rooted<TimeZoneRecord> timeZone(cx);
3609 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
3611 TimeZoneMethod::GetOffsetNanosecondsFor,
3612 TimeZoneMethod::GetPossibleInstantsFor,
3614 &timeZone)) {
3615 return false;
3618 // Step 4.
3619 auto calendar = zonedDateTime.calendar();
3621 // Step 5.
3622 const auto& instant = zonedDateTime.instant();
3624 // Steps 5-6.
3625 PlainDateTime temporalDateTime;
3626 if (!GetPlainDateTimeFor(cx, timeZone, instant, &temporalDateTime)) {
3627 return false;
3630 // Step 7.
3631 Rooted<PlainDateTimeWithCalendar> startDateTime(cx);
3632 if (!CreateTemporalDateTime(cx, {temporalDateTime.date, {}}, calendar,
3633 &startDateTime)) {
3634 return false;
3637 // Step 8.
3638 Instant startInstant;
3639 if (!GetInstantFor(cx, timeZone, startDateTime,
3640 TemporalDisambiguation::Compatible, &startInstant)) {
3641 return false;
3644 // Step 9.
3645 auto* result = CreateTemporalZonedDateTime(cx, startInstant,
3646 timeZone.receiver(), calendar);
3647 if (!result) {
3648 return false;
3651 args.rval().setObject(*result);
3652 return true;
3656 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3658 static bool ZonedDateTime_startOfDay(JSContext* cx, unsigned argc, Value* vp) {
3659 // Steps 1-2.
3660 CallArgs args = CallArgsFromVp(argc, vp);
3661 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_startOfDay>(cx,
3662 args);
3666 * Temporal.ZonedDateTime.prototype.toInstant ( )
3668 static bool ZonedDateTime_toInstant(JSContext* cx, const CallArgs& args) {
3669 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
3670 auto instant = ToInstant(zonedDateTime);
3672 // Step 3.
3673 auto* result = CreateTemporalInstant(cx, instant);
3674 if (!result) {
3675 return false;
3678 args.rval().setObject(*result);
3679 return true;
3683 * Temporal.ZonedDateTime.prototype.toInstant ( )
3685 static bool ZonedDateTime_toInstant(JSContext* cx, unsigned argc, Value* vp) {
3686 // Steps 1-2.
3687 CallArgs args = CallArgsFromVp(argc, vp);
3688 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toInstant>(cx,
3689 args);
3693 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3695 static bool ZonedDateTime_toPlainDate(JSContext* cx, const CallArgs& args) {
3696 Rooted<ZonedDateTime> zonedDateTime(
3697 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3699 // Steps 3-6.
3700 PlainDateTime temporalDateTime;
3701 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
3702 zonedDateTime.instant(), &temporalDateTime)) {
3703 return false;
3706 // Step 7.
3707 auto* result =
3708 CreateTemporalDate(cx, temporalDateTime.date, zonedDateTime.calendar());
3709 if (!result) {
3710 return false;
3713 args.rval().setObject(*result);
3714 return true;
3718 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3720 static bool ZonedDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
3721 // Steps 1-2.
3722 CallArgs args = CallArgsFromVp(argc, vp);
3723 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainDate>(cx,
3724 args);
3728 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3730 static bool ZonedDateTime_toPlainTime(JSContext* cx, const CallArgs& args) {
3731 Rooted<ZonedDateTime> zonedDateTime(
3732 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3734 // Steps 3-6.
3735 PlainDateTime temporalDateTime;
3736 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
3737 zonedDateTime.instant(), &temporalDateTime)) {
3738 return false;
3741 // Step 7.
3742 auto* result = CreateTemporalTime(cx, temporalDateTime.time);
3743 if (!result) {
3744 return false;
3747 args.rval().setObject(*result);
3748 return true;
3752 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3754 static bool ZonedDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) {
3755 // Steps 1-2.
3756 CallArgs args = CallArgsFromVp(argc, vp);
3757 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainTime>(cx,
3758 args);
3762 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3764 static bool ZonedDateTime_toPlainDateTime(JSContext* cx, const CallArgs& args) {
3765 Rooted<ZonedDateTime> zonedDateTime(
3766 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3768 // Steps 3-5.
3769 auto* result =
3770 GetPlainDateTimeFor(cx, zonedDateTime.timeZone(), zonedDateTime.instant(),
3771 zonedDateTime.calendar());
3772 if (!result) {
3773 return false;
3776 args.rval().setObject(*result);
3777 return true;
3781 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3783 static bool ZonedDateTime_toPlainDateTime(JSContext* cx, unsigned argc,
3784 Value* vp) {
3785 // Steps 1-2.
3786 CallArgs args = CallArgsFromVp(argc, vp);
3787 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainDateTime>(
3788 cx, args);
3792 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3794 static bool ZonedDateTime_toPlainYearMonth(JSContext* cx,
3795 const CallArgs& args) {
3796 Rooted<ZonedDateTime> zonedDateTime(
3797 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3799 // Step 3.
3800 Rooted<CalendarRecord> calendar(cx);
3801 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
3803 CalendarMethod::Fields,
3804 CalendarMethod::YearMonthFromFields,
3806 &calendar)) {
3807 return false;
3810 // Steps 4-6.
3811 Rooted<PlainDateTimeObject*> temporalDateTime(
3813 GetPlainDateTimeFor(cx, zonedDateTime.timeZone(), zonedDateTime.instant(),
3814 zonedDateTime.calendar()));
3815 if (!temporalDateTime) {
3816 return false;
3819 // Step 7.
3820 JS::RootedVector<PropertyKey> fieldNames(cx);
3821 if (!CalendarFields(cx, calendar,
3822 {CalendarField::MonthCode, CalendarField::Year},
3823 &fieldNames)) {
3824 return false;
3827 // Step 8.
3828 Rooted<PlainObject*> fields(
3829 cx, PrepareTemporalFields(cx, temporalDateTime, fieldNames));
3830 if (!fields) {
3831 return false;
3834 // Steps 9-10.
3835 auto result = CalendarYearMonthFromFields(cx, calendar, fields);
3836 if (!result) {
3837 return false;
3840 args.rval().setObject(*result);
3841 return true;
3845 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3847 static bool ZonedDateTime_toPlainYearMonth(JSContext* cx, unsigned argc,
3848 Value* vp) {
3849 // Steps 1-2.
3850 CallArgs args = CallArgsFromVp(argc, vp);
3851 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainYearMonth>(
3852 cx, args);
3856 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3858 static bool ZonedDateTime_toPlainMonthDay(JSContext* cx, const CallArgs& args) {
3859 Rooted<ZonedDateTime> zonedDateTime(
3860 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3862 // Step 3.
3863 Rooted<CalendarRecord> calendar(cx);
3864 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
3866 CalendarMethod::Fields,
3867 CalendarMethod::MonthDayFromFields,
3869 &calendar)) {
3870 return false;
3873 // Steps 4-6.
3874 Rooted<PlainDateTimeObject*> temporalDateTime(
3876 GetPlainDateTimeFor(cx, zonedDateTime.timeZone(), zonedDateTime.instant(),
3877 zonedDateTime.calendar()));
3878 if (!temporalDateTime) {
3879 return false;
3882 // Step 7.
3883 JS::RootedVector<PropertyKey> fieldNames(cx);
3884 if (!CalendarFields(cx, calendar,
3885 {CalendarField::Day, CalendarField::MonthCode},
3886 &fieldNames)) {
3887 return false;
3890 // Step 8.
3891 Rooted<PlainObject*> fields(
3892 cx, PrepareTemporalFields(cx, temporalDateTime, fieldNames));
3893 if (!fields) {
3894 return false;
3897 // Steps 9-10.
3898 auto result = CalendarMonthDayFromFields(cx, calendar, fields);
3899 if (!result) {
3900 return false;
3903 args.rval().setObject(*result);
3904 return true;
3908 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3910 static bool ZonedDateTime_toPlainMonthDay(JSContext* cx, unsigned argc,
3911 Value* vp) {
3912 // Steps 1-2.
3913 CallArgs args = CallArgsFromVp(argc, vp);
3914 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainMonthDay>(
3915 cx, args);
3919 * Temporal.ZonedDateTime.prototype.getISOFields ( )
3921 static bool ZonedDateTime_getISOFields(JSContext* cx, const CallArgs& args) {
3922 Rooted<ZonedDateTime> zonedDateTime(
3923 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3925 // Step 3.
3926 Rooted<IdValueVector> fields(cx, IdValueVector(cx));
3928 // Step 4.
3929 const auto& instant = zonedDateTime.instant();
3931 // Step 5.
3932 auto calendar = zonedDateTime.calendar();
3934 // Step 6.
3935 auto timeZone = zonedDateTime.timeZone();
3937 // Step 7.
3938 int64_t offsetNanoseconds;
3939 if (!GetOffsetNanosecondsFor(cx, timeZone, instant, &offsetNanoseconds)) {
3940 return false;
3943 // Step 8.
3944 auto temporalDateTime = GetPlainDateTimeFor(instant, offsetNanoseconds);
3946 // Step 9.
3947 Rooted<JSString*> offset(cx,
3948 FormatUTCOffsetNanoseconds(cx, offsetNanoseconds));
3949 if (!offset) {
3950 return false;
3953 // Step 10.
3954 if (!fields.emplaceBack(NameToId(cx->names().calendar), calendar.toValue())) {
3955 return false;
3958 // Step 11.
3959 if (!fields.emplaceBack(NameToId(cx->names().isoDay),
3960 Int32Value(temporalDateTime.date.day))) {
3961 return false;
3964 // Step 12.
3965 if (!fields.emplaceBack(NameToId(cx->names().isoHour),
3966 Int32Value(temporalDateTime.time.hour))) {
3967 return false;
3970 // Step 13.
3971 if (!fields.emplaceBack(NameToId(cx->names().isoMicrosecond),
3972 Int32Value(temporalDateTime.time.microsecond))) {
3973 return false;
3976 // Step 14.
3977 if (!fields.emplaceBack(NameToId(cx->names().isoMillisecond),
3978 Int32Value(temporalDateTime.time.millisecond))) {
3979 return false;
3982 // Step 15.
3983 if (!fields.emplaceBack(NameToId(cx->names().isoMinute),
3984 Int32Value(temporalDateTime.time.minute))) {
3985 return false;
3988 // Step 16.
3989 if (!fields.emplaceBack(NameToId(cx->names().isoMonth),
3990 Int32Value(temporalDateTime.date.month))) {
3991 return false;
3994 // Step 17.
3995 if (!fields.emplaceBack(NameToId(cx->names().isoNanosecond),
3996 Int32Value(temporalDateTime.time.nanosecond))) {
3997 return false;
4000 // Step 18.
4001 if (!fields.emplaceBack(NameToId(cx->names().isoSecond),
4002 Int32Value(temporalDateTime.time.second))) {
4003 return false;
4006 // Step 19.
4007 if (!fields.emplaceBack(NameToId(cx->names().isoYear),
4008 Int32Value(temporalDateTime.date.year))) {
4009 return false;
4012 // Step 20.
4013 if (!fields.emplaceBack(NameToId(cx->names().offset), StringValue(offset))) {
4014 return false;
4017 // Step 21.
4018 if (!fields.emplaceBack(NameToId(cx->names().timeZone), timeZone.toValue())) {
4019 return false;
4022 // Step 22.
4023 auto* obj = NewPlainObjectWithUniqueNames(cx, fields);
4024 if (!obj) {
4025 return false;
4028 args.rval().setObject(*obj);
4029 return true;
4033 * Temporal.ZonedDateTime.prototype.getISOFields ( )
4035 static bool ZonedDateTime_getISOFields(JSContext* cx, unsigned argc,
4036 Value* vp) {
4037 // Steps 1-2.
4038 CallArgs args = CallArgsFromVp(argc, vp);
4039 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_getISOFields>(
4040 cx, args);
4044 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4046 static bool ZonedDateTime_getCalendar(JSContext* cx, const CallArgs& args) {
4047 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
4048 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
4050 // Step 3.
4051 auto* obj = ToTemporalCalendarObject(cx, calendar);
4052 if (!obj) {
4053 return false;
4056 args.rval().setObject(*obj);
4057 return true;
4061 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4063 static bool ZonedDateTime_getCalendar(JSContext* cx, unsigned argc, Value* vp) {
4064 // Steps 1-2.
4065 CallArgs args = CallArgsFromVp(argc, vp);
4066 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_getCalendar>(cx,
4067 args);
4071 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4073 static bool ZonedDateTime_getTimeZone(JSContext* cx, const CallArgs& args) {
4074 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
4075 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
4077 // Step 3.
4078 auto* obj = ToTemporalTimeZoneObject(cx, timeZone);
4079 if (!obj) {
4080 return false;
4083 args.rval().setObject(*obj);
4084 return true;
4088 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4090 static bool ZonedDateTime_getTimeZone(JSContext* cx, unsigned argc, Value* vp) {
4091 // Steps 1-2.
4092 CallArgs args = CallArgsFromVp(argc, vp);
4093 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_getTimeZone>(cx,
4094 args);
4097 const JSClass ZonedDateTimeObject::class_ = {
4098 "Temporal.ZonedDateTime",
4099 JSCLASS_HAS_RESERVED_SLOTS(ZonedDateTimeObject::SLOT_COUNT) |
4100 JSCLASS_HAS_CACHED_PROTO(JSProto_ZonedDateTime),
4101 JS_NULL_CLASS_OPS,
4102 &ZonedDateTimeObject::classSpec_,
4105 const JSClass& ZonedDateTimeObject::protoClass_ = PlainObject::class_;
4107 static const JSFunctionSpec ZonedDateTime_methods[] = {
4108 JS_FN("from", ZonedDateTime_from, 1, 0),
4109 JS_FN("compare", ZonedDateTime_compare, 2, 0),
4110 JS_FS_END,
4113 static const JSFunctionSpec ZonedDateTime_prototype_methods[] = {
4114 JS_FN("with", ZonedDateTime_with, 1, 0),
4115 JS_FN("withPlainTime", ZonedDateTime_withPlainTime, 0, 0),
4116 JS_FN("withPlainDate", ZonedDateTime_withPlainDate, 1, 0),
4117 JS_FN("withTimeZone", ZonedDateTime_withTimeZone, 1, 0),
4118 JS_FN("withCalendar", ZonedDateTime_withCalendar, 1, 0),
4119 JS_FN("add", ZonedDateTime_add, 1, 0),
4120 JS_FN("subtract", ZonedDateTime_subtract, 1, 0),
4121 JS_FN("until", ZonedDateTime_until, 1, 0),
4122 JS_FN("since", ZonedDateTime_since, 1, 0),
4123 JS_FN("round", ZonedDateTime_round, 1, 0),
4124 JS_FN("equals", ZonedDateTime_equals, 1, 0),
4125 JS_FN("toString", ZonedDateTime_toString, 0, 0),
4126 JS_FN("toLocaleString", ZonedDateTime_toLocaleString, 0, 0),
4127 JS_FN("toJSON", ZonedDateTime_toJSON, 0, 0),
4128 JS_FN("valueOf", ZonedDateTime_valueOf, 0, 0),
4129 JS_FN("startOfDay", ZonedDateTime_startOfDay, 0, 0),
4130 JS_FN("toInstant", ZonedDateTime_toInstant, 0, 0),
4131 JS_FN("toPlainDate", ZonedDateTime_toPlainDate, 0, 0),
4132 JS_FN("toPlainTime", ZonedDateTime_toPlainTime, 0, 0),
4133 JS_FN("toPlainDateTime", ZonedDateTime_toPlainDateTime, 0, 0),
4134 JS_FN("toPlainYearMonth", ZonedDateTime_toPlainYearMonth, 0, 0),
4135 JS_FN("toPlainMonthDay", ZonedDateTime_toPlainMonthDay, 0, 0),
4136 JS_FN("getISOFields", ZonedDateTime_getISOFields, 0, 0),
4137 JS_FN("getCalendar", ZonedDateTime_getCalendar, 0, 0),
4138 JS_FN("getTimeZone", ZonedDateTime_getTimeZone, 0, 0),
4139 JS_FS_END,
4142 static const JSPropertySpec ZonedDateTime_prototype_properties[] = {
4143 JS_PSG("calendarId", ZonedDateTime_calendarId, 0),
4144 JS_PSG("timeZoneId", ZonedDateTime_timeZoneId, 0),
4145 JS_PSG("year", ZonedDateTime_year, 0),
4146 JS_PSG("month", ZonedDateTime_month, 0),
4147 JS_PSG("monthCode", ZonedDateTime_monthCode, 0),
4148 JS_PSG("day", ZonedDateTime_day, 0),
4149 JS_PSG("hour", ZonedDateTime_hour, 0),
4150 JS_PSG("minute", ZonedDateTime_minute, 0),
4151 JS_PSG("second", ZonedDateTime_second, 0),
4152 JS_PSG("millisecond", ZonedDateTime_millisecond, 0),
4153 JS_PSG("microsecond", ZonedDateTime_microsecond, 0),
4154 JS_PSG("nanosecond", ZonedDateTime_nanosecond, 0),
4155 JS_PSG("epochSeconds", ZonedDateTime_epochSeconds, 0),
4156 JS_PSG("epochMilliseconds", ZonedDateTime_epochMilliseconds, 0),
4157 JS_PSG("epochMicroseconds", ZonedDateTime_epochMicroseconds, 0),
4158 JS_PSG("epochNanoseconds", ZonedDateTime_epochNanoseconds, 0),
4159 JS_PSG("dayOfWeek", ZonedDateTime_dayOfWeek, 0),
4160 JS_PSG("dayOfYear", ZonedDateTime_dayOfYear, 0),
4161 JS_PSG("weekOfYear", ZonedDateTime_weekOfYear, 0),
4162 JS_PSG("yearOfWeek", ZonedDateTime_yearOfWeek, 0),
4163 JS_PSG("hoursInDay", ZonedDateTime_hoursInDay, 0),
4164 JS_PSG("daysInWeek", ZonedDateTime_daysInWeek, 0),
4165 JS_PSG("daysInMonth", ZonedDateTime_daysInMonth, 0),
4166 JS_PSG("daysInYear", ZonedDateTime_daysInYear, 0),
4167 JS_PSG("monthsInYear", ZonedDateTime_monthsInYear, 0),
4168 JS_PSG("inLeapYear", ZonedDateTime_inLeapYear, 0),
4169 JS_PSG("offsetNanoseconds", ZonedDateTime_offsetNanoseconds, 0),
4170 JS_PSG("offset", ZonedDateTime_offset, 0),
4171 JS_STRING_SYM_PS(toStringTag, "Temporal.ZonedDateTime", JSPROP_READONLY),
4172 JS_PS_END,
4175 const ClassSpec ZonedDateTimeObject::classSpec_ = {
4176 GenericCreateConstructor<ZonedDateTimeConstructor, 2,
4177 gc::AllocKind::FUNCTION>,
4178 GenericCreatePrototype<ZonedDateTimeObject>,
4179 ZonedDateTime_methods,
4180 nullptr,
4181 ZonedDateTime_prototype_methods,
4182 ZonedDateTime_prototype_properties,
4183 nullptr,
4184 ClassSpec::DontDefineConstructor,