Bug 1874684 - Part 22: Compute the precise fraction in Duration.p.total and ZonedDate...
[gecko.git] / js / src / builtin / temporal / ZonedDateTime.cpp
blobdc293c4f1bc16c5e03c61405a0aea68f324e7c04
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 diff = tomorrowInstant - todayInstant;
2468 MOZ_ASSERT(IsValidInstantSpan(diff));
2470 // Step 15.
2471 constexpr auto nsPerHour = Int128{ToNanoseconds(TemporalUnit::Hour)};
2472 args.rval().setNumber(FractionToDouble(diff.toNanoseconds(), nsPerHour));
2473 return true;
2477 * get Temporal.ZonedDateTime.prototype.hoursInDay
2479 static bool ZonedDateTime_hoursInDay(JSContext* cx, unsigned argc, Value* vp) {
2480 // Steps 1-2.
2481 CallArgs args = CallArgsFromVp(argc, vp);
2482 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_hoursInDay>(cx,
2483 args);
2487 * get Temporal.ZonedDateTime.prototype.daysInWeek
2489 static bool ZonedDateTime_daysInWeek(JSContext* cx, const CallArgs& args) {
2490 Rooted<ZonedDateTime> zonedDateTime(
2491 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2493 // Steps 3-6.
2494 PlainDateTime dateTime;
2495 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2496 zonedDateTime.instant(), &dateTime)) {
2497 return false;
2500 // Step 7.
2501 return CalendarDaysInWeek(cx, zonedDateTime.calendar(), dateTime,
2502 args.rval());
2506 * get Temporal.ZonedDateTime.prototype.daysInWeek
2508 static bool ZonedDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) {
2509 // Steps 1-2.
2510 CallArgs args = CallArgsFromVp(argc, vp);
2511 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInWeek>(cx,
2512 args);
2516 * get Temporal.ZonedDateTime.prototype.daysInMonth
2518 static bool ZonedDateTime_daysInMonth(JSContext* cx, const CallArgs& args) {
2519 Rooted<ZonedDateTime> zonedDateTime(
2520 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2522 // Steps 3-6.
2523 PlainDateTime dateTime;
2524 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2525 zonedDateTime.instant(), &dateTime)) {
2526 return false;
2529 // Step 7.
2530 return CalendarDaysInMonth(cx, zonedDateTime.calendar(), dateTime,
2531 args.rval());
2535 * get Temporal.ZonedDateTime.prototype.daysInMonth
2537 static bool ZonedDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) {
2538 // Steps 1-2.
2539 CallArgs args = CallArgsFromVp(argc, vp);
2540 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInMonth>(cx,
2541 args);
2545 * get Temporal.ZonedDateTime.prototype.daysInYear
2547 static bool ZonedDateTime_daysInYear(JSContext* cx, const CallArgs& args) {
2548 Rooted<ZonedDateTime> zonedDateTime(
2549 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2551 // Steps 3-6.
2552 PlainDateTime dateTime;
2553 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2554 zonedDateTime.instant(), &dateTime)) {
2555 return false;
2558 // Step 7.
2559 return CalendarDaysInYear(cx, zonedDateTime.calendar(), dateTime,
2560 args.rval());
2564 * get Temporal.ZonedDateTime.prototype.daysInYear
2566 static bool ZonedDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) {
2567 // Steps 1-2.
2568 CallArgs args = CallArgsFromVp(argc, vp);
2569 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInYear>(cx,
2570 args);
2574 * get Temporal.ZonedDateTime.prototype.monthsInYear
2576 static bool ZonedDateTime_monthsInYear(JSContext* cx, const CallArgs& args) {
2577 Rooted<ZonedDateTime> zonedDateTime(
2578 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2580 // Steps 3-6.
2581 PlainDateTime dateTime;
2582 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2583 zonedDateTime.instant(), &dateTime)) {
2584 return false;
2587 // Step 7.
2588 return CalendarMonthsInYear(cx, zonedDateTime.calendar(), dateTime,
2589 args.rval());
2593 * get Temporal.ZonedDateTime.prototype.monthsInYear
2595 static bool ZonedDateTime_monthsInYear(JSContext* cx, unsigned argc,
2596 Value* vp) {
2597 // Steps 1-2.
2598 CallArgs args = CallArgsFromVp(argc, vp);
2599 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_monthsInYear>(
2600 cx, args);
2604 * get Temporal.ZonedDateTime.prototype.inLeapYear
2606 static bool ZonedDateTime_inLeapYear(JSContext* cx, const CallArgs& args) {
2607 Rooted<ZonedDateTime> zonedDateTime(
2608 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2610 // Steps 3-6.
2611 PlainDateTime dateTime;
2612 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
2613 zonedDateTime.instant(), &dateTime)) {
2614 return false;
2617 // Step 7.
2618 return CalendarInLeapYear(cx, zonedDateTime.calendar(), dateTime,
2619 args.rval());
2623 * get Temporal.ZonedDateTime.prototype.inLeapYear
2625 static bool ZonedDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) {
2626 // Steps 1-2.
2627 CallArgs args = CallArgsFromVp(argc, vp);
2628 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_inLeapYear>(cx,
2629 args);
2633 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2635 static bool ZonedDateTime_offsetNanoseconds(JSContext* cx,
2636 const CallArgs& args) {
2637 Rooted<ZonedDateTime> zonedDateTime(
2638 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2640 // Step 3.
2641 auto timeZone = zonedDateTime.timeZone();
2643 // Step 4.
2644 const auto& instant = zonedDateTime.instant();
2646 // Step 5.
2647 int64_t offsetNanoseconds;
2648 if (!GetOffsetNanosecondsFor(cx, timeZone, instant, &offsetNanoseconds)) {
2649 return false;
2651 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
2653 args.rval().setNumber(offsetNanoseconds);
2654 return true;
2658 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
2660 static bool ZonedDateTime_offsetNanoseconds(JSContext* cx, unsigned argc,
2661 Value* vp) {
2662 // Steps 1-2.
2663 CallArgs args = CallArgsFromVp(argc, vp);
2664 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offsetNanoseconds>(
2665 cx, args);
2669 * get Temporal.ZonedDateTime.prototype.offset
2671 static bool ZonedDateTime_offset(JSContext* cx, const CallArgs& args) {
2672 Rooted<ZonedDateTime> zonedDateTime(
2673 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2675 // Step 3.
2676 auto timeZone = zonedDateTime.timeZone();
2678 // Step 4.
2679 const auto& instant = zonedDateTime.instant();
2681 // Step 5.
2682 JSString* str = GetOffsetStringFor(cx, timeZone, instant);
2683 if (!str) {
2684 return false;
2687 args.rval().setString(str);
2688 return true;
2692 * get Temporal.ZonedDateTime.prototype.offset
2694 static bool ZonedDateTime_offset(JSContext* cx, unsigned argc, Value* vp) {
2695 // Steps 1-2.
2696 CallArgs args = CallArgsFromVp(argc, vp);
2697 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offset>(cx, args);
2701 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2702 * ] )
2704 static bool ZonedDateTime_with(JSContext* cx, const CallArgs& args) {
2705 Rooted<ZonedDateTime> zonedDateTime(
2706 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2708 // Step 3.
2709 Rooted<JSObject*> temporalZonedDateTimeLike(
2711 RequireObjectArg(cx, "temporalZonedDateTimeLike", "with", args.get(0)));
2712 if (!temporalZonedDateTimeLike) {
2713 return false;
2716 // Step 4.
2717 if (!RejectTemporalLikeObject(cx, temporalZonedDateTimeLike)) {
2718 return false;
2721 // Step 5.
2722 Rooted<PlainObject*> resolvedOptions(cx);
2723 if (args.hasDefined(1)) {
2724 Rooted<JSObject*> options(cx,
2725 RequireObjectArg(cx, "options", "with", args[1]));
2726 if (!options) {
2727 return false;
2729 resolvedOptions = SnapshotOwnProperties(cx, options);
2730 } else {
2731 resolvedOptions = NewPlainObjectWithProto(cx, nullptr);
2733 if (!resolvedOptions) {
2734 return false;
2737 // Step 6.
2738 Rooted<CalendarRecord> calendar(cx);
2739 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
2741 CalendarMethod::DateFromFields,
2742 CalendarMethod::Fields,
2743 CalendarMethod::MergeFields,
2745 &calendar)) {
2746 return false;
2749 // Step 7.
2750 Rooted<TimeZoneRecord> timeZone(cx);
2751 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
2753 TimeZoneMethod::GetOffsetNanosecondsFor,
2754 TimeZoneMethod::GetPossibleInstantsFor,
2756 &timeZone)) {
2757 return false;
2760 // Step 8.
2761 const auto& instant = zonedDateTime.instant();
2763 // Step 9.
2764 int64_t offsetNanoseconds;
2765 if (!GetOffsetNanosecondsFor(cx, timeZone, instant, &offsetNanoseconds)) {
2766 return false;
2769 // Step 10.
2770 Rooted<PlainDateTimeObject*> dateTime(
2772 GetPlainDateTimeFor(cx, instant, calendar.receiver(), offsetNanoseconds));
2773 if (!dateTime) {
2774 return false;
2777 // Step 11.
2778 JS::RootedVector<PropertyKey> fieldNames(cx);
2779 if (!CalendarFields(cx, calendar,
2780 {CalendarField::Day, CalendarField::Month,
2781 CalendarField::MonthCode, CalendarField::Year},
2782 &fieldNames)) {
2783 return false;
2786 // Step 12.
2787 Rooted<PlainObject*> fields(cx,
2788 PrepareTemporalFields(cx, dateTime, fieldNames));
2789 if (!fields) {
2790 return false;
2793 // Steps 13-18.
2794 struct TimeField {
2795 using FieldName = ImmutableTenuredPtr<PropertyName*> JSAtomState::*;
2797 FieldName name;
2798 int32_t value;
2799 } timeFields[] = {
2800 {&JSAtomState::hour, dateTime->isoHour()},
2801 {&JSAtomState::minute, dateTime->isoMinute()},
2802 {&JSAtomState::second, dateTime->isoSecond()},
2803 {&JSAtomState::millisecond, dateTime->isoMillisecond()},
2804 {&JSAtomState::microsecond, dateTime->isoMicrosecond()},
2805 {&JSAtomState::nanosecond, dateTime->isoNanosecond()},
2808 Rooted<Value> timeFieldValue(cx);
2809 for (const auto& timeField : timeFields) {
2810 Handle<PropertyName*> name = cx->names().*(timeField.name);
2811 timeFieldValue.setInt32(timeField.value);
2813 if (!DefineDataProperty(cx, fields, name, timeFieldValue)) {
2814 return false;
2818 // Step 19.
2819 JSString* fieldsOffset = FormatUTCOffsetNanoseconds(cx, offsetNanoseconds);
2820 if (!fieldsOffset) {
2821 return false;
2824 timeFieldValue.setString(fieldsOffset);
2825 if (!DefineDataProperty(cx, fields, cx->names().offset, timeFieldValue)) {
2826 return false;
2829 // Step 20.
2830 if (!AppendSorted(cx, fieldNames.get(),
2832 TemporalField::Hour,
2833 TemporalField::Microsecond,
2834 TemporalField::Millisecond,
2835 TemporalField::Minute,
2836 TemporalField::Nanosecond,
2837 TemporalField::Offset,
2838 TemporalField::Second,
2839 })) {
2840 return false;
2843 // Step 21.
2844 Rooted<PlainObject*> partialZonedDateTime(
2846 PreparePartialTemporalFields(cx, temporalZonedDateTimeLike, fieldNames));
2847 if (!partialZonedDateTime) {
2848 return false;
2851 // Step 22.
2852 Rooted<JSObject*> mergedFields(
2853 cx, CalendarMergeFields(cx, calendar, fields, partialZonedDateTime));
2854 if (!mergedFields) {
2855 return false;
2858 // Step 23.
2859 fields = PrepareTemporalFields(cx, mergedFields, fieldNames,
2860 {TemporalField::Offset});
2861 if (!fields) {
2862 return false;
2865 // Step 24-25.
2866 auto disambiguation = TemporalDisambiguation::Compatible;
2867 if (!ToTemporalDisambiguation(cx, resolvedOptions, &disambiguation)) {
2868 return false;
2871 // Step 26.
2872 auto offset = TemporalOffset::Prefer;
2873 if (!ToTemporalOffset(cx, resolvedOptions, &offset)) {
2874 return false;
2877 // Step 27.
2878 PlainDateTime dateTimeResult;
2879 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, resolvedOptions,
2880 &dateTimeResult)) {
2881 return false;
2884 // Step 28.
2885 Rooted<Value> offsetString(cx);
2886 if (!GetProperty(cx, fields, fields, cx->names().offset, &offsetString)) {
2887 return false;
2890 // Step 29.
2891 MOZ_ASSERT(offsetString.isString());
2893 // Step 30.
2894 Rooted<JSString*> offsetStr(cx, offsetString.toString());
2895 int64_t newOffsetNanoseconds;
2896 if (!ParseDateTimeUTCOffset(cx, offsetStr, &newOffsetNanoseconds)) {
2897 return false;
2900 // Step 31.
2901 Instant epochNanoseconds;
2902 if (!InterpretISODateTimeOffset(
2903 cx, dateTimeResult, OffsetBehaviour::Option, newOffsetNanoseconds,
2904 timeZone, disambiguation, offset, MatchBehaviour::MatchExactly,
2905 &epochNanoseconds)) {
2906 return false;
2909 // Step 32.
2910 auto* result = CreateTemporalZonedDateTime(
2911 cx, epochNanoseconds, timeZone.receiver(), calendar.receiver());
2912 if (!result) {
2913 return false;
2916 args.rval().setObject(*result);
2917 return true;
2921 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
2922 * ] )
2924 static bool ZonedDateTime_with(JSContext* cx, unsigned argc, Value* vp) {
2925 // Steps 1-2.
2926 CallArgs args = CallArgsFromVp(argc, vp);
2927 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_with>(cx, args);
2931 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2933 static bool ZonedDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
2934 Rooted<ZonedDateTime> zonedDateTime(
2935 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
2937 // Steps 3-4.
2938 PlainTime time = {};
2939 if (args.hasDefined(0)) {
2940 if (!ToTemporalTime(cx, args[0], &time)) {
2941 return false;
2945 // Step 5.
2946 Rooted<TimeZoneRecord> timeZone(cx);
2947 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
2949 TimeZoneMethod::GetOffsetNanosecondsFor,
2950 TimeZoneMethod::GetPossibleInstantsFor,
2952 &timeZone)) {
2953 return false;
2956 // Steps 6 and 8.
2957 PlainDateTime plainDateTime;
2958 if (!GetPlainDateTimeFor(cx, timeZone, zonedDateTime.instant(),
2959 &plainDateTime)) {
2960 return false;
2963 // Step 7.
2964 auto calendar = zonedDateTime.calendar();
2966 // Step 9.
2967 Rooted<PlainDateTimeWithCalendar> resultPlainDateTime(cx);
2968 if (!CreateTemporalDateTime(cx, {plainDateTime.date, time}, calendar,
2969 &resultPlainDateTime)) {
2970 return false;
2973 // Step 10.
2974 Instant instant;
2975 if (!GetInstantFor(cx, timeZone, resultPlainDateTime,
2976 TemporalDisambiguation::Compatible, &instant)) {
2977 return false;
2980 // Step 11.
2981 auto* result =
2982 CreateTemporalZonedDateTime(cx, instant, timeZone.receiver(), calendar);
2983 if (!result) {
2984 return false;
2987 args.rval().setObject(*result);
2988 return true;
2992 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
2994 static bool ZonedDateTime_withPlainTime(JSContext* cx, unsigned argc,
2995 Value* vp) {
2996 // Steps 1-2.
2997 CallArgs args = CallArgsFromVp(argc, vp);
2998 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withPlainTime>(
2999 cx, args);
3003 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3005 static bool ZonedDateTime_withPlainDate(JSContext* cx, const CallArgs& args) {
3006 Rooted<ZonedDateTime> zonedDateTime(
3007 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3009 // Step 3.
3010 Rooted<PlainDateWithCalendar> plainDate(cx);
3011 if (!ToTemporalDate(cx, args.get(0), &plainDate)) {
3012 return false;
3014 auto date = plainDate.date();
3016 // Step 4.
3017 Rooted<TimeZoneRecord> timeZone(cx);
3018 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
3020 TimeZoneMethod::GetOffsetNanosecondsFor,
3021 TimeZoneMethod::GetPossibleInstantsFor,
3023 &timeZone)) {
3024 return false;
3027 // Steps 5-6.
3028 PlainDateTime plainDateTime;
3029 if (!GetPlainDateTimeFor(cx, timeZone, zonedDateTime.instant(),
3030 &plainDateTime)) {
3031 return false;
3034 // Step 7.
3035 Rooted<CalendarValue> calendar(cx);
3036 if (!ConsolidateCalendars(cx, zonedDateTime.calendar(), plainDate.calendar(),
3037 &calendar)) {
3038 return false;
3041 // Step 8.
3042 Rooted<PlainDateTimeWithCalendar> resultPlainDateTime(cx);
3043 if (!CreateTemporalDateTime(cx, {date, plainDateTime.time}, calendar,
3044 &resultPlainDateTime)) {
3045 return false;
3048 // Step 9.
3049 Instant instant;
3050 if (!GetInstantFor(cx, timeZone, resultPlainDateTime,
3051 TemporalDisambiguation::Compatible, &instant)) {
3052 return false;
3055 // Step 10.
3056 auto* result =
3057 CreateTemporalZonedDateTime(cx, instant, timeZone.receiver(), calendar);
3058 if (!result) {
3059 return false;
3062 args.rval().setObject(*result);
3063 return true;
3067 * Temporal.ZonedDateTime.prototype.withPlainDate ( plainDateLike )
3069 static bool ZonedDateTime_withPlainDate(JSContext* cx, unsigned argc,
3070 Value* vp) {
3071 // Steps 1-2.
3072 CallArgs args = CallArgsFromVp(argc, vp);
3073 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withPlainDate>(
3074 cx, args);
3078 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3080 static bool ZonedDateTime_withTimeZone(JSContext* cx, const CallArgs& args) {
3081 Rooted<ZonedDateTime> zonedDateTime(
3082 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3084 // Step 3.
3085 Rooted<TimeZoneValue> timeZone(cx);
3086 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
3087 return false;
3090 // Step 4.
3091 auto* result = CreateTemporalZonedDateTime(
3092 cx, zonedDateTime.instant(), timeZone, zonedDateTime.calendar());
3093 if (!result) {
3094 return false;
3097 args.rval().setObject(*result);
3098 return true;
3102 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )
3104 static bool ZonedDateTime_withTimeZone(JSContext* cx, unsigned argc,
3105 Value* vp) {
3106 // Steps 1-2.
3107 CallArgs args = CallArgsFromVp(argc, vp);
3108 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withTimeZone>(
3109 cx, args);
3113 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3115 static bool ZonedDateTime_withCalendar(JSContext* cx, const CallArgs& args) {
3116 Rooted<ZonedDateTime> zonedDateTime(
3117 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3119 // Step 3.
3120 Rooted<CalendarValue> calendar(cx);
3121 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) {
3122 return false;
3125 // Step 4.
3126 auto* result = CreateTemporalZonedDateTime(
3127 cx, zonedDateTime.instant(), zonedDateTime.timeZone(), calendar);
3128 if (!result) {
3129 return false;
3132 args.rval().setObject(*result);
3133 return true;
3137 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )
3139 static bool ZonedDateTime_withCalendar(JSContext* cx, unsigned argc,
3140 Value* vp) {
3141 // Steps 1-2.
3142 CallArgs args = CallArgsFromVp(argc, vp);
3143 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withCalendar>(
3144 cx, args);
3148 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3150 static bool ZonedDateTime_add(JSContext* cx, const CallArgs& args) {
3151 return AddDurationToOrSubtractDurationFromZonedDateTime(
3152 cx, ZonedDateTimeDuration::Add, args);
3156 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )
3158 static bool ZonedDateTime_add(JSContext* cx, unsigned argc, Value* vp) {
3159 // Steps 1-2.
3160 CallArgs args = CallArgsFromVp(argc, vp);
3161 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_add>(cx, args);
3165 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3166 * ] )
3168 static bool ZonedDateTime_subtract(JSContext* cx, const CallArgs& args) {
3169 return AddDurationToOrSubtractDurationFromZonedDateTime(
3170 cx, ZonedDateTimeDuration::Subtract, args);
3174 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options
3175 * ] )
3177 static bool ZonedDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) {
3178 // Steps 1-2.
3179 CallArgs args = CallArgsFromVp(argc, vp);
3180 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_subtract>(cx,
3181 args);
3185 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3187 static bool ZonedDateTime_until(JSContext* cx, const CallArgs& args) {
3188 // Step 3.
3189 return DifferenceTemporalZonedDateTime(cx, TemporalDifference::Until, args);
3193 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] )
3195 static bool ZonedDateTime_until(JSContext* cx, unsigned argc, Value* vp) {
3196 // Steps 1-2.
3197 CallArgs args = CallArgsFromVp(argc, vp);
3198 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_until>(cx, args);
3202 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3204 static bool ZonedDateTime_since(JSContext* cx, const CallArgs& args) {
3205 // Step 3.
3206 return DifferenceTemporalZonedDateTime(cx, TemporalDifference::Since, args);
3210 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] )
3212 static bool ZonedDateTime_since(JSContext* cx, unsigned argc, Value* vp) {
3213 // Steps 1-2.
3214 CallArgs args = CallArgsFromVp(argc, vp);
3215 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_since>(cx, args);
3219 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3221 static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
3222 Rooted<ZonedDateTime> zonedDateTime(
3223 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3225 // Steps 3-12.
3226 auto smallestUnit = TemporalUnit::Auto;
3227 auto roundingMode = TemporalRoundingMode::HalfExpand;
3228 auto roundingIncrement = Increment{1};
3229 if (args.get(0).isString()) {
3230 // Step 4. (Not applicable in our implementation.)
3232 // Step 9.
3233 Rooted<JSString*> paramString(cx, args[0].toString());
3234 if (!GetTemporalUnit(cx, paramString, TemporalUnitKey::SmallestUnit,
3235 TemporalUnitGroup::DayTime, &smallestUnit)) {
3236 return false;
3239 // Steps 6-8 and 10-12. (Implicit)
3240 } else {
3241 // Steps 3 and 5.a
3242 Rooted<JSObject*> roundTo(
3243 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
3244 if (!roundTo) {
3245 return false;
3248 // Steps 6-7.
3249 if (!ToTemporalRoundingIncrement(cx, roundTo, &roundingIncrement)) {
3250 return false;
3253 // Step 8.
3254 if (!ToTemporalRoundingMode(cx, roundTo, &roundingMode)) {
3255 return false;
3258 // Step 9.
3259 if (!GetTemporalUnit(cx, roundTo, TemporalUnitKey::SmallestUnit,
3260 TemporalUnitGroup::DayTime, &smallestUnit)) {
3261 return false;
3264 if (smallestUnit == TemporalUnit::Auto) {
3265 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3266 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
3267 return false;
3270 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
3271 smallestUnit <= TemporalUnit::Nanosecond);
3273 // Steps 10-11.
3274 auto maximum = Increment{1};
3275 bool inclusive = true;
3276 if (smallestUnit > TemporalUnit::Day) {
3277 maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
3278 inclusive = false;
3281 // Step 12.
3282 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
3283 inclusive)) {
3284 return false;
3288 // Step 13.
3289 if (smallestUnit == TemporalUnit::Nanosecond &&
3290 roundingIncrement == Increment{1}) {
3291 // Step 13.a.
3292 auto* result = CreateTemporalZonedDateTime(cx, zonedDateTime.instant(),
3293 zonedDateTime.timeZone(),
3294 zonedDateTime.calendar());
3295 if (!result) {
3296 return false;
3299 args.rval().setObject(*result);
3300 return true;
3303 // Step 14.
3304 Rooted<TimeZoneRecord> timeZone(cx);
3305 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
3307 TimeZoneMethod::GetOffsetNanosecondsFor,
3308 TimeZoneMethod::GetPossibleInstantsFor,
3310 &timeZone)) {
3311 return false;
3314 // Step 16. (Reordered)
3315 auto calendar = zonedDateTime.calendar();
3317 // Steps 15 and 17.
3318 int64_t offsetNanoseconds;
3319 if (!GetOffsetNanosecondsFor(cx, timeZone, zonedDateTime.instant(),
3320 &offsetNanoseconds)) {
3321 return false;
3323 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
3325 // Step 18.
3326 auto temporalDateTime =
3327 GetPlainDateTimeFor(zonedDateTime.instant(), offsetNanoseconds);
3329 // Step 19.
3330 Rooted<CalendarValue> isoCalendar(cx, CalendarValue(cx->names().iso8601));
3331 Rooted<PlainDateTimeWithCalendar> dtStart(cx);
3332 if (!CreateTemporalDateTime(cx, {temporalDateTime.date, {}}, isoCalendar,
3333 &dtStart)) {
3334 return false;
3337 // Steps 20-21.
3338 Instant startNs;
3339 if (!GetInstantFor(cx, timeZone, dtStart, TemporalDisambiguation::Compatible,
3340 &startNs)) {
3341 return false;
3344 // Step 22.
3345 Instant endNs;
3346 if (!AddDaysToZonedDateTime(cx, startNs, ToPlainDateTime(dtStart), timeZone,
3347 calendar, 1, &endNs)) {
3348 return false;
3350 MOZ_ASSERT(IsValidEpochInstant(endNs));
3352 // Step 23.
3353 auto dayLengthNs = endNs - startNs;
3354 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs));
3356 // Step 24.
3357 if (dayLengthNs <= InstantSpan{}) {
3358 JS_ReportErrorNumberASCII(
3359 cx, GetErrorMessage, nullptr,
3360 JSMSG_TEMPORAL_ZONED_DATE_TIME_NON_POSITIVE_DAY_LENGTH);
3361 return false;
3364 // Step 25.
3365 PlainDateTime roundResult;
3366 if (!RoundISODateTime(cx, temporalDateTime, roundingIncrement, smallestUnit,
3367 roundingMode, dayLengthNs, &roundResult)) {
3368 return false;
3371 // Step 26.
3372 Instant epochNanoseconds;
3373 if (!InterpretISODateTimeOffset(
3374 cx, roundResult, OffsetBehaviour::Option, offsetNanoseconds, timeZone,
3375 TemporalDisambiguation::Compatible, TemporalOffset::Prefer,
3376 MatchBehaviour::MatchExactly, &epochNanoseconds)) {
3377 return false;
3380 // Step 27.
3381 auto* result = CreateTemporalZonedDateTime(cx, epochNanoseconds,
3382 timeZone.receiver(), calendar);
3383 if (!result) {
3384 return false;
3387 args.rval().setObject(*result);
3388 return true;
3392 * Temporal.ZonedDateTime.prototype.round ( roundTo )
3394 static bool ZonedDateTime_round(JSContext* cx, unsigned argc, Value* vp) {
3395 // Steps 1-2.
3396 CallArgs args = CallArgsFromVp(argc, vp);
3397 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_round>(cx, args);
3401 * Temporal.ZonedDateTime.prototype.equals ( other )
3403 static bool ZonedDateTime_equals(JSContext* cx, const CallArgs& args) {
3404 Rooted<ZonedDateTime> zonedDateTime(
3405 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3407 // Step 3.
3408 Rooted<ZonedDateTime> other(cx);
3409 if (!ToTemporalZonedDateTime(cx, args.get(0), &other)) {
3410 return false;
3413 // Steps 4-6.
3414 bool equals = zonedDateTime.instant() == other.instant();
3415 if (equals && !TimeZoneEquals(cx, zonedDateTime.timeZone(), other.timeZone(),
3416 &equals)) {
3417 return false;
3419 if (equals && !CalendarEquals(cx, zonedDateTime.calendar(), other.calendar(),
3420 &equals)) {
3421 return false;
3424 args.rval().setBoolean(equals);
3425 return true;
3429 * Temporal.ZonedDateTime.prototype.equals ( other )
3431 static bool ZonedDateTime_equals(JSContext* cx, unsigned argc, Value* vp) {
3432 // Steps 1-2.
3433 CallArgs args = CallArgsFromVp(argc, vp);
3434 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_equals>(cx, args);
3438 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3440 static bool ZonedDateTime_toString(JSContext* cx, const CallArgs& args) {
3441 Rooted<ZonedDateTime> zonedDateTime(
3442 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3444 SecondsStringPrecision precision = {Precision::Auto(),
3445 TemporalUnit::Nanosecond, Increment{1}};
3446 auto roundingMode = TemporalRoundingMode::Trunc;
3447 auto showCalendar = CalendarOption::Auto;
3448 auto showTimeZone = TimeZoneNameOption::Auto;
3449 auto showOffset = ShowOffsetOption::Auto;
3450 if (args.hasDefined(0)) {
3451 // Step 3.
3452 Rooted<JSObject*> options(
3453 cx, RequireObjectArg(cx, "options", "toString", args[0]));
3454 if (!options) {
3455 return false;
3458 // Steps 4-5.
3459 if (!ToCalendarNameOption(cx, options, &showCalendar)) {
3460 return false;
3463 // Step 6.
3464 auto digits = Precision::Auto();
3465 if (!ToFractionalSecondDigits(cx, options, &digits)) {
3466 return false;
3469 // Step 7.
3470 if (!ToShowOffsetOption(cx, options, &showOffset)) {
3471 return false;
3474 // Step 8.
3475 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
3476 return false;
3479 // Step 9.
3480 auto smallestUnit = TemporalUnit::Auto;
3481 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
3482 TemporalUnitGroup::Time, &smallestUnit)) {
3483 return false;
3486 // Step 10.
3487 if (smallestUnit == TemporalUnit::Hour) {
3488 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3489 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
3490 "smallestUnit");
3491 return false;
3494 // Step 11.
3495 if (!ToTimeZoneNameOption(cx, options, &showTimeZone)) {
3496 return false;
3499 // Step 12.
3500 precision = ToSecondsStringPrecision(smallestUnit, digits);
3503 // Step 13.
3504 JSString* str = TemporalZonedDateTimeToString(
3505 cx, zonedDateTime, precision.precision, showCalendar, showTimeZone,
3506 showOffset, precision.increment, precision.unit, roundingMode);
3507 if (!str) {
3508 return false;
3511 args.rval().setString(str);
3512 return true;
3516 * Temporal.ZonedDateTime.prototype.toString ( [ options ] )
3518 static bool ZonedDateTime_toString(JSContext* cx, unsigned argc, Value* vp) {
3519 // Steps 1-2.
3520 CallArgs args = CallArgsFromVp(argc, vp);
3521 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toString>(cx,
3522 args);
3526 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3528 static bool ZonedDateTime_toLocaleString(JSContext* cx, const CallArgs& args) {
3529 Rooted<ZonedDateTime> zonedDateTime(
3530 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3532 // Step 3.
3533 JSString* str = TemporalZonedDateTimeToString(
3534 cx, zonedDateTime, Precision::Auto(), CalendarOption::Auto,
3535 TimeZoneNameOption::Auto, ShowOffsetOption::Auto);
3536 if (!str) {
3537 return false;
3540 args.rval().setString(str);
3541 return true;
3545 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
3547 static bool ZonedDateTime_toLocaleString(JSContext* cx, unsigned argc,
3548 Value* vp) {
3549 // Steps 1-2.
3550 CallArgs args = CallArgsFromVp(argc, vp);
3551 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toLocaleString>(
3552 cx, args);
3556 * Temporal.ZonedDateTime.prototype.toJSON ( )
3558 static bool ZonedDateTime_toJSON(JSContext* cx, const CallArgs& args) {
3559 Rooted<ZonedDateTime> zonedDateTime(
3560 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3562 // Step 3.
3563 JSString* str = TemporalZonedDateTimeToString(
3564 cx, zonedDateTime, Precision::Auto(), CalendarOption::Auto,
3565 TimeZoneNameOption::Auto, ShowOffsetOption::Auto);
3566 if (!str) {
3567 return false;
3570 args.rval().setString(str);
3571 return true;
3575 * Temporal.ZonedDateTime.prototype.toJSON ( )
3577 static bool ZonedDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) {
3578 // Steps 1-2.
3579 CallArgs args = CallArgsFromVp(argc, vp);
3580 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toJSON>(cx, args);
3584 * Temporal.ZonedDateTime.prototype.valueOf ( )
3586 static bool ZonedDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) {
3587 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
3588 "ZonedDateTime", "primitive type");
3589 return false;
3593 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3595 static bool ZonedDateTime_startOfDay(JSContext* cx, const CallArgs& args) {
3596 Rooted<ZonedDateTime> zonedDateTime(
3597 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3599 // Step 3.
3600 Rooted<TimeZoneRecord> timeZone(cx);
3601 if (!CreateTimeZoneMethodsRecord(cx, zonedDateTime.timeZone(),
3603 TimeZoneMethod::GetOffsetNanosecondsFor,
3604 TimeZoneMethod::GetPossibleInstantsFor,
3606 &timeZone)) {
3607 return false;
3610 // Step 4.
3611 auto calendar = zonedDateTime.calendar();
3613 // Step 5.
3614 const auto& instant = zonedDateTime.instant();
3616 // Steps 5-6.
3617 PlainDateTime temporalDateTime;
3618 if (!GetPlainDateTimeFor(cx, timeZone, instant, &temporalDateTime)) {
3619 return false;
3622 // Step 7.
3623 Rooted<PlainDateTimeWithCalendar> startDateTime(cx);
3624 if (!CreateTemporalDateTime(cx, {temporalDateTime.date, {}}, calendar,
3625 &startDateTime)) {
3626 return false;
3629 // Step 8.
3630 Instant startInstant;
3631 if (!GetInstantFor(cx, timeZone, startDateTime,
3632 TemporalDisambiguation::Compatible, &startInstant)) {
3633 return false;
3636 // Step 9.
3637 auto* result = CreateTemporalZonedDateTime(cx, startInstant,
3638 timeZone.receiver(), calendar);
3639 if (!result) {
3640 return false;
3643 args.rval().setObject(*result);
3644 return true;
3648 * Temporal.ZonedDateTime.prototype.startOfDay ( )
3650 static bool ZonedDateTime_startOfDay(JSContext* cx, unsigned argc, Value* vp) {
3651 // Steps 1-2.
3652 CallArgs args = CallArgsFromVp(argc, vp);
3653 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_startOfDay>(cx,
3654 args);
3658 * Temporal.ZonedDateTime.prototype.toInstant ( )
3660 static bool ZonedDateTime_toInstant(JSContext* cx, const CallArgs& args) {
3661 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
3662 auto instant = ToInstant(zonedDateTime);
3664 // Step 3.
3665 auto* result = CreateTemporalInstant(cx, instant);
3666 if (!result) {
3667 return false;
3670 args.rval().setObject(*result);
3671 return true;
3675 * Temporal.ZonedDateTime.prototype.toInstant ( )
3677 static bool ZonedDateTime_toInstant(JSContext* cx, unsigned argc, Value* vp) {
3678 // Steps 1-2.
3679 CallArgs args = CallArgsFromVp(argc, vp);
3680 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toInstant>(cx,
3681 args);
3685 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3687 static bool ZonedDateTime_toPlainDate(JSContext* cx, const CallArgs& args) {
3688 Rooted<ZonedDateTime> zonedDateTime(
3689 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3691 // Steps 3-6.
3692 PlainDateTime temporalDateTime;
3693 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
3694 zonedDateTime.instant(), &temporalDateTime)) {
3695 return false;
3698 // Step 7.
3699 auto* result =
3700 CreateTemporalDate(cx, temporalDateTime.date, zonedDateTime.calendar());
3701 if (!result) {
3702 return false;
3705 args.rval().setObject(*result);
3706 return true;
3710 * Temporal.ZonedDateTime.prototype.toPlainDate ( )
3712 static bool ZonedDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
3713 // Steps 1-2.
3714 CallArgs args = CallArgsFromVp(argc, vp);
3715 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainDate>(cx,
3716 args);
3720 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3722 static bool ZonedDateTime_toPlainTime(JSContext* cx, const CallArgs& args) {
3723 Rooted<ZonedDateTime> zonedDateTime(
3724 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3726 // Steps 3-6.
3727 PlainDateTime temporalDateTime;
3728 if (!GetPlainDateTimeFor(cx, zonedDateTime.timeZone(),
3729 zonedDateTime.instant(), &temporalDateTime)) {
3730 return false;
3733 // Step 7.
3734 auto* result = CreateTemporalTime(cx, temporalDateTime.time);
3735 if (!result) {
3736 return false;
3739 args.rval().setObject(*result);
3740 return true;
3744 * Temporal.ZonedDateTime.prototype.toPlainTime ( )
3746 static bool ZonedDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) {
3747 // Steps 1-2.
3748 CallArgs args = CallArgsFromVp(argc, vp);
3749 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainTime>(cx,
3750 args);
3754 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3756 static bool ZonedDateTime_toPlainDateTime(JSContext* cx, const CallArgs& args) {
3757 Rooted<ZonedDateTime> zonedDateTime(
3758 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3760 // Steps 3-5.
3761 auto* result =
3762 GetPlainDateTimeFor(cx, zonedDateTime.timeZone(), zonedDateTime.instant(),
3763 zonedDateTime.calendar());
3764 if (!result) {
3765 return false;
3768 args.rval().setObject(*result);
3769 return true;
3773 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( )
3775 static bool ZonedDateTime_toPlainDateTime(JSContext* cx, unsigned argc,
3776 Value* vp) {
3777 // Steps 1-2.
3778 CallArgs args = CallArgsFromVp(argc, vp);
3779 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainDateTime>(
3780 cx, args);
3784 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3786 static bool ZonedDateTime_toPlainYearMonth(JSContext* cx,
3787 const CallArgs& args) {
3788 Rooted<ZonedDateTime> zonedDateTime(
3789 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3791 // Step 3.
3792 Rooted<CalendarRecord> calendar(cx);
3793 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
3795 CalendarMethod::Fields,
3796 CalendarMethod::YearMonthFromFields,
3798 &calendar)) {
3799 return false;
3802 // Steps 4-6.
3803 Rooted<PlainDateTimeObject*> temporalDateTime(
3805 GetPlainDateTimeFor(cx, zonedDateTime.timeZone(), zonedDateTime.instant(),
3806 zonedDateTime.calendar()));
3807 if (!temporalDateTime) {
3808 return false;
3811 // Step 7.
3812 JS::RootedVector<PropertyKey> fieldNames(cx);
3813 if (!CalendarFields(cx, calendar,
3814 {CalendarField::MonthCode, CalendarField::Year},
3815 &fieldNames)) {
3816 return false;
3819 // Step 8.
3820 Rooted<PlainObject*> fields(
3821 cx, PrepareTemporalFields(cx, temporalDateTime, fieldNames));
3822 if (!fields) {
3823 return false;
3826 // Steps 9-10.
3827 auto result = CalendarYearMonthFromFields(cx, calendar, fields);
3828 if (!result) {
3829 return false;
3832 args.rval().setObject(*result);
3833 return true;
3837 * Temporal.ZonedDateTime.prototype.toPlainYearMonth ( )
3839 static bool ZonedDateTime_toPlainYearMonth(JSContext* cx, unsigned argc,
3840 Value* vp) {
3841 // Steps 1-2.
3842 CallArgs args = CallArgsFromVp(argc, vp);
3843 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainYearMonth>(
3844 cx, args);
3848 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3850 static bool ZonedDateTime_toPlainMonthDay(JSContext* cx, const CallArgs& args) {
3851 Rooted<ZonedDateTime> zonedDateTime(
3852 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3854 // Step 3.
3855 Rooted<CalendarRecord> calendar(cx);
3856 if (!CreateCalendarMethodsRecord(cx, zonedDateTime.calendar(),
3858 CalendarMethod::Fields,
3859 CalendarMethod::MonthDayFromFields,
3861 &calendar)) {
3862 return false;
3865 // Steps 4-6.
3866 Rooted<PlainDateTimeObject*> temporalDateTime(
3868 GetPlainDateTimeFor(cx, zonedDateTime.timeZone(), zonedDateTime.instant(),
3869 zonedDateTime.calendar()));
3870 if (!temporalDateTime) {
3871 return false;
3874 // Step 7.
3875 JS::RootedVector<PropertyKey> fieldNames(cx);
3876 if (!CalendarFields(cx, calendar,
3877 {CalendarField::Day, CalendarField::MonthCode},
3878 &fieldNames)) {
3879 return false;
3882 // Step 8.
3883 Rooted<PlainObject*> fields(
3884 cx, PrepareTemporalFields(cx, temporalDateTime, fieldNames));
3885 if (!fields) {
3886 return false;
3889 // Steps 9-10.
3890 auto result = CalendarMonthDayFromFields(cx, calendar, fields);
3891 if (!result) {
3892 return false;
3895 args.rval().setObject(*result);
3896 return true;
3900 * Temporal.ZonedDateTime.prototype.toPlainMonthDay ( )
3902 static bool ZonedDateTime_toPlainMonthDay(JSContext* cx, unsigned argc,
3903 Value* vp) {
3904 // Steps 1-2.
3905 CallArgs args = CallArgsFromVp(argc, vp);
3906 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainMonthDay>(
3907 cx, args);
3911 * Temporal.ZonedDateTime.prototype.getISOFields ( )
3913 static bool ZonedDateTime_getISOFields(JSContext* cx, const CallArgs& args) {
3914 Rooted<ZonedDateTime> zonedDateTime(
3915 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
3917 // Step 3.
3918 Rooted<IdValueVector> fields(cx, IdValueVector(cx));
3920 // Step 4.
3921 const auto& instant = zonedDateTime.instant();
3923 // Step 5.
3924 auto calendar = zonedDateTime.calendar();
3926 // Step 6.
3927 auto timeZone = zonedDateTime.timeZone();
3929 // Step 7.
3930 int64_t offsetNanoseconds;
3931 if (!GetOffsetNanosecondsFor(cx, timeZone, instant, &offsetNanoseconds)) {
3932 return false;
3935 // Step 8.
3936 auto temporalDateTime = GetPlainDateTimeFor(instant, offsetNanoseconds);
3938 // Step 9.
3939 Rooted<JSString*> offset(cx,
3940 FormatUTCOffsetNanoseconds(cx, offsetNanoseconds));
3941 if (!offset) {
3942 return false;
3945 // Step 10.
3946 if (!fields.emplaceBack(NameToId(cx->names().calendar), calendar.toValue())) {
3947 return false;
3950 // Step 11.
3951 if (!fields.emplaceBack(NameToId(cx->names().isoDay),
3952 Int32Value(temporalDateTime.date.day))) {
3953 return false;
3956 // Step 12.
3957 if (!fields.emplaceBack(NameToId(cx->names().isoHour),
3958 Int32Value(temporalDateTime.time.hour))) {
3959 return false;
3962 // Step 13.
3963 if (!fields.emplaceBack(NameToId(cx->names().isoMicrosecond),
3964 Int32Value(temporalDateTime.time.microsecond))) {
3965 return false;
3968 // Step 14.
3969 if (!fields.emplaceBack(NameToId(cx->names().isoMillisecond),
3970 Int32Value(temporalDateTime.time.millisecond))) {
3971 return false;
3974 // Step 15.
3975 if (!fields.emplaceBack(NameToId(cx->names().isoMinute),
3976 Int32Value(temporalDateTime.time.minute))) {
3977 return false;
3980 // Step 16.
3981 if (!fields.emplaceBack(NameToId(cx->names().isoMonth),
3982 Int32Value(temporalDateTime.date.month))) {
3983 return false;
3986 // Step 17.
3987 if (!fields.emplaceBack(NameToId(cx->names().isoNanosecond),
3988 Int32Value(temporalDateTime.time.nanosecond))) {
3989 return false;
3992 // Step 18.
3993 if (!fields.emplaceBack(NameToId(cx->names().isoSecond),
3994 Int32Value(temporalDateTime.time.second))) {
3995 return false;
3998 // Step 19.
3999 if (!fields.emplaceBack(NameToId(cx->names().isoYear),
4000 Int32Value(temporalDateTime.date.year))) {
4001 return false;
4004 // Step 20.
4005 if (!fields.emplaceBack(NameToId(cx->names().offset), StringValue(offset))) {
4006 return false;
4009 // Step 21.
4010 if (!fields.emplaceBack(NameToId(cx->names().timeZone), timeZone.toValue())) {
4011 return false;
4014 // Step 22.
4015 auto* obj = NewPlainObjectWithUniqueNames(cx, fields);
4016 if (!obj) {
4017 return false;
4020 args.rval().setObject(*obj);
4021 return true;
4025 * Temporal.ZonedDateTime.prototype.getISOFields ( )
4027 static bool ZonedDateTime_getISOFields(JSContext* cx, unsigned argc,
4028 Value* vp) {
4029 // Steps 1-2.
4030 CallArgs args = CallArgsFromVp(argc, vp);
4031 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_getISOFields>(
4032 cx, args);
4036 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4038 static bool ZonedDateTime_getCalendar(JSContext* cx, const CallArgs& args) {
4039 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
4040 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
4042 // Step 3.
4043 auto* obj = ToTemporalCalendarObject(cx, calendar);
4044 if (!obj) {
4045 return false;
4048 args.rval().setObject(*obj);
4049 return true;
4053 * Temporal.ZonedDateTime.prototype.getCalendar ( )
4055 static bool ZonedDateTime_getCalendar(JSContext* cx, unsigned argc, Value* vp) {
4056 // Steps 1-2.
4057 CallArgs args = CallArgsFromVp(argc, vp);
4058 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_getCalendar>(cx,
4059 args);
4063 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4065 static bool ZonedDateTime_getTimeZone(JSContext* cx, const CallArgs& args) {
4066 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();
4067 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
4069 // Step 3.
4070 auto* obj = ToTemporalTimeZoneObject(cx, timeZone);
4071 if (!obj) {
4072 return false;
4075 args.rval().setObject(*obj);
4076 return true;
4080 * Temporal.ZonedDateTime.prototype.getTimeZone ( )
4082 static bool ZonedDateTime_getTimeZone(JSContext* cx, unsigned argc, Value* vp) {
4083 // Steps 1-2.
4084 CallArgs args = CallArgsFromVp(argc, vp);
4085 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_getTimeZone>(cx,
4086 args);
4089 const JSClass ZonedDateTimeObject::class_ = {
4090 "Temporal.ZonedDateTime",
4091 JSCLASS_HAS_RESERVED_SLOTS(ZonedDateTimeObject::SLOT_COUNT) |
4092 JSCLASS_HAS_CACHED_PROTO(JSProto_ZonedDateTime),
4093 JS_NULL_CLASS_OPS,
4094 &ZonedDateTimeObject::classSpec_,
4097 const JSClass& ZonedDateTimeObject::protoClass_ = PlainObject::class_;
4099 static const JSFunctionSpec ZonedDateTime_methods[] = {
4100 JS_FN("from", ZonedDateTime_from, 1, 0),
4101 JS_FN("compare", ZonedDateTime_compare, 2, 0),
4102 JS_FS_END,
4105 static const JSFunctionSpec ZonedDateTime_prototype_methods[] = {
4106 JS_FN("with", ZonedDateTime_with, 1, 0),
4107 JS_FN("withPlainTime", ZonedDateTime_withPlainTime, 0, 0),
4108 JS_FN("withPlainDate", ZonedDateTime_withPlainDate, 1, 0),
4109 JS_FN("withTimeZone", ZonedDateTime_withTimeZone, 1, 0),
4110 JS_FN("withCalendar", ZonedDateTime_withCalendar, 1, 0),
4111 JS_FN("add", ZonedDateTime_add, 1, 0),
4112 JS_FN("subtract", ZonedDateTime_subtract, 1, 0),
4113 JS_FN("until", ZonedDateTime_until, 1, 0),
4114 JS_FN("since", ZonedDateTime_since, 1, 0),
4115 JS_FN("round", ZonedDateTime_round, 1, 0),
4116 JS_FN("equals", ZonedDateTime_equals, 1, 0),
4117 JS_FN("toString", ZonedDateTime_toString, 0, 0),
4118 JS_FN("toLocaleString", ZonedDateTime_toLocaleString, 0, 0),
4119 JS_FN("toJSON", ZonedDateTime_toJSON, 0, 0),
4120 JS_FN("valueOf", ZonedDateTime_valueOf, 0, 0),
4121 JS_FN("startOfDay", ZonedDateTime_startOfDay, 0, 0),
4122 JS_FN("toInstant", ZonedDateTime_toInstant, 0, 0),
4123 JS_FN("toPlainDate", ZonedDateTime_toPlainDate, 0, 0),
4124 JS_FN("toPlainTime", ZonedDateTime_toPlainTime, 0, 0),
4125 JS_FN("toPlainDateTime", ZonedDateTime_toPlainDateTime, 0, 0),
4126 JS_FN("toPlainYearMonth", ZonedDateTime_toPlainYearMonth, 0, 0),
4127 JS_FN("toPlainMonthDay", ZonedDateTime_toPlainMonthDay, 0, 0),
4128 JS_FN("getISOFields", ZonedDateTime_getISOFields, 0, 0),
4129 JS_FN("getCalendar", ZonedDateTime_getCalendar, 0, 0),
4130 JS_FN("getTimeZone", ZonedDateTime_getTimeZone, 0, 0),
4131 JS_FS_END,
4134 static const JSPropertySpec ZonedDateTime_prototype_properties[] = {
4135 JS_PSG("calendarId", ZonedDateTime_calendarId, 0),
4136 JS_PSG("timeZoneId", ZonedDateTime_timeZoneId, 0),
4137 JS_PSG("year", ZonedDateTime_year, 0),
4138 JS_PSG("month", ZonedDateTime_month, 0),
4139 JS_PSG("monthCode", ZonedDateTime_monthCode, 0),
4140 JS_PSG("day", ZonedDateTime_day, 0),
4141 JS_PSG("hour", ZonedDateTime_hour, 0),
4142 JS_PSG("minute", ZonedDateTime_minute, 0),
4143 JS_PSG("second", ZonedDateTime_second, 0),
4144 JS_PSG("millisecond", ZonedDateTime_millisecond, 0),
4145 JS_PSG("microsecond", ZonedDateTime_microsecond, 0),
4146 JS_PSG("nanosecond", ZonedDateTime_nanosecond, 0),
4147 JS_PSG("epochSeconds", ZonedDateTime_epochSeconds, 0),
4148 JS_PSG("epochMilliseconds", ZonedDateTime_epochMilliseconds, 0),
4149 JS_PSG("epochMicroseconds", ZonedDateTime_epochMicroseconds, 0),
4150 JS_PSG("epochNanoseconds", ZonedDateTime_epochNanoseconds, 0),
4151 JS_PSG("dayOfWeek", ZonedDateTime_dayOfWeek, 0),
4152 JS_PSG("dayOfYear", ZonedDateTime_dayOfYear, 0),
4153 JS_PSG("weekOfYear", ZonedDateTime_weekOfYear, 0),
4154 JS_PSG("yearOfWeek", ZonedDateTime_yearOfWeek, 0),
4155 JS_PSG("hoursInDay", ZonedDateTime_hoursInDay, 0),
4156 JS_PSG("daysInWeek", ZonedDateTime_daysInWeek, 0),
4157 JS_PSG("daysInMonth", ZonedDateTime_daysInMonth, 0),
4158 JS_PSG("daysInYear", ZonedDateTime_daysInYear, 0),
4159 JS_PSG("monthsInYear", ZonedDateTime_monthsInYear, 0),
4160 JS_PSG("inLeapYear", ZonedDateTime_inLeapYear, 0),
4161 JS_PSG("offsetNanoseconds", ZonedDateTime_offsetNanoseconds, 0),
4162 JS_PSG("offset", ZonedDateTime_offset, 0),
4163 JS_STRING_SYM_PS(toStringTag, "Temporal.ZonedDateTime", JSPROP_READONLY),
4164 JS_PS_END,
4167 const ClassSpec ZonedDateTimeObject::classSpec_ = {
4168 GenericCreateConstructor<ZonedDateTimeConstructor, 2,
4169 gc::AllocKind::FUNCTION>,
4170 GenericCreatePrototype<ZonedDateTimeObject>,
4171 ZonedDateTime_methods,
4172 nullptr,
4173 ZonedDateTime_prototype_methods,
4174 ZonedDateTime_prototype_properties,
4175 nullptr,
4176 ClassSpec::DontDefineConstructor,