Bug 1874684 - Part 21: Rename SecondsAndNanoseconds::toTotalNanoseconds. r=dminor
[gecko.git] / js / src / builtin / temporal / Instant.cpp
blob0fcb709cd430547c8e380b787de608aad4f6e175
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/Instant.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Casting.h"
11 #include "mozilla/CheckedInt.h"
12 #include "mozilla/FloatingPoint.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/Span.h"
16 #include <algorithm>
17 #include <array>
18 #include <cstdlib>
19 #include <iterator>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <utility>
24 #include "jsnum.h"
25 #include "jspubtd.h"
26 #include "NamespaceImports.h"
28 #include "builtin/temporal/Calendar.h"
29 #include "builtin/temporal/Duration.h"
30 #include "builtin/temporal/Int96.h"
31 #include "builtin/temporal/PlainDateTime.h"
32 #include "builtin/temporal/Temporal.h"
33 #include "builtin/temporal/TemporalParser.h"
34 #include "builtin/temporal/TemporalRoundingMode.h"
35 #include "builtin/temporal/TemporalTypes.h"
36 #include "builtin/temporal/TemporalUnit.h"
37 #include "builtin/temporal/TimeZone.h"
38 #include "builtin/temporal/ToString.h"
39 #include "builtin/temporal/Wrapped.h"
40 #include "builtin/temporal/ZonedDateTime.h"
41 #include "gc/AllocKind.h"
42 #include "gc/Barrier.h"
43 #include "js/CallArgs.h"
44 #include "js/CallNonGenericMethod.h"
45 #include "js/Class.h"
46 #include "js/Conversions.h"
47 #include "js/ErrorReport.h"
48 #include "js/friend/ErrorMessages.h"
49 #include "js/PropertyDescriptor.h"
50 #include "js/PropertySpec.h"
51 #include "js/RootingAPI.h"
52 #include "js/TypeDecls.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/PlainObject.h"
61 #include "vm/StringType.h"
63 #include "vm/JSObject-inl.h"
64 #include "vm/NativeObject-inl.h"
65 #include "vm/ObjectOperations-inl.h"
67 using namespace js;
68 using namespace js::temporal;
70 static inline bool IsInstant(Handle<Value> v) {
71 return v.isObject() && v.toObject().is<InstantObject>();
74 /**
75 * Check if the absolute value is less-or-equal to the given limit.
77 template <const auto& digits>
78 static bool AbsoluteValueIsLessOrEqual(const BigInt* bigInt) {
79 size_t length = bigInt->digitLength();
81 // Fewer digits than the limit, so definitely in range.
82 if (length < std::size(digits)) {
83 return true;
86 // More digits than the limit, so definitely out of range.
87 if (length > std::size(digits)) {
88 return false;
91 // Compare each digit when the input has the same number of digits.
92 size_t index = std::size(digits);
93 for (auto digit : digits) {
94 auto d = bigInt->digit(--index);
95 if (d < digit) {
96 return true;
98 if (d > digit) {
99 return false;
102 return true;
105 static constexpr auto NanosecondsMaxInstant() {
106 static_assert(BigInt::DigitBits == 64 || BigInt::DigitBits == 32);
108 // ±8.64 × 10^21 is the nanoseconds from epoch limit.
109 // 8.64 × 10^21 is 86_40000_00000_00000_00000 or 0x1d4_60162f51_6f000000.
110 // Return the BigInt digits of that number for fast BigInt comparisons.
111 if constexpr (BigInt::DigitBits == 64) {
112 return std::array{
113 BigInt::Digit(0x1d4),
114 BigInt::Digit(0x6016'2f51'6f00'0000),
116 } else {
117 return std::array{
118 BigInt::Digit(0x1d4),
119 BigInt::Digit(0x6016'2f51),
120 BigInt::Digit(0x6f00'0000),
126 * IsValidEpochNanoseconds ( epochNanoseconds )
128 bool js::temporal::IsValidEpochNanoseconds(const BigInt* epochNanoseconds) {
129 // Steps 1-3.
130 static constexpr auto epochLimit = NanosecondsMaxInstant();
131 return AbsoluteValueIsLessOrEqual<epochLimit>(epochNanoseconds);
134 static bool IsValidEpochMicroseconds(const BigInt* epochMicroseconds) {
135 int64_t i;
136 if (!BigInt::isInt64(epochMicroseconds, &i)) {
137 return false;
140 constexpr int64_t MicrosecondsMaxInstant = Instant::max().toMicroseconds();
141 return -MicrosecondsMaxInstant <= i && i <= MicrosecondsMaxInstant;
144 static bool IsValidEpochMilliseconds(double epochMilliseconds) {
145 MOZ_ASSERT(IsInteger(epochMilliseconds));
147 constexpr int64_t MillisecondsMaxInstant = Instant::max().toMilliseconds();
148 return std::abs(epochMilliseconds) <= double(MillisecondsMaxInstant);
151 static bool IsValidEpochSeconds(double epochSeconds) {
152 MOZ_ASSERT(IsInteger(epochSeconds));
154 constexpr int64_t SecondsMaxInstant = Instant::max().toSeconds();
155 return std::abs(epochSeconds) <= double(SecondsMaxInstant);
159 * IsValidEpochNanoseconds ( epochNanoseconds )
161 bool js::temporal::IsValidEpochInstant(const Instant& instant) {
162 MOZ_ASSERT(0 <= instant.nanoseconds && instant.nanoseconds <= 999'999'999);
164 // Steps 1-3.
165 return Instant::min() <= instant && instant <= Instant::max();
168 #ifdef DEBUG
170 * Validates a nanoseconds amount is at most as large as the difference
171 * between two valid nanoseconds from the epoch instants.
173 bool js::temporal::IsValidInstantSpan(const InstantSpan& span) {
174 MOZ_ASSERT(0 <= span.nanoseconds && span.nanoseconds <= 999'999'999);
176 // Steps 1-3.
177 return InstantSpan::min() <= span && span <= InstantSpan::max();
179 #endif
182 * Return the BigInt as a 96-bit integer. The BigInt digits must not consist of
183 * more than 96-bits.
185 static Int96 ToInt96(const BigInt* ns) {
186 static_assert(BigInt::DigitBits == 64 || BigInt::DigitBits == 32);
188 auto digits = ns->digits();
189 if constexpr (BigInt::DigitBits == 64) {
190 BigInt::Digit x = 0, y = 0;
191 switch (digits.size()) {
192 case 2:
193 y = digits[1];
194 [[fallthrough]];
195 case 1:
196 x = digits[0];
197 [[fallthrough]];
198 case 0:
199 break;
200 default:
201 MOZ_ASSERT_UNREACHABLE("unexpected digit length");
203 return Int96{
204 Int96::Digits{Int96::Digit(x), Int96::Digit(x >> 32), Int96::Digit(y)},
205 ns->isNegative()};
206 } else {
207 BigInt::Digit x = 0, y = 0, z = 0;
208 switch (digits.size()) {
209 case 3:
210 z = digits[2];
211 [[fallthrough]];
212 case 2:
213 y = digits[1];
214 [[fallthrough]];
215 case 1:
216 x = digits[0];
217 [[fallthrough]];
218 case 0:
219 break;
220 default:
221 MOZ_ASSERT_UNREACHABLE("unexpected digit length");
223 return Int96{
224 Int96::Digits{Int96::Digit(x), Int96::Digit(y), Int96::Digit(z)},
225 ns->isNegative()};
229 Instant js::temporal::ToInstant(const BigInt* epochNanoseconds) {
230 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
232 auto [seconds, nanos] =
233 ToInt96(epochNanoseconds) / ToNanoseconds(TemporalUnit::Second);
234 return {seconds, nanos};
237 static BigInt* CreateBigInt(JSContext* cx,
238 const std::array<uint32_t, 3>& digits,
239 bool negative) {
240 static_assert(BigInt::DigitBits == 64 || BigInt::DigitBits == 32);
242 if constexpr (BigInt::DigitBits == 64) {
243 uint64_t x = (uint64_t(digits[1]) << 32) | digits[0];
244 uint64_t y = digits[2];
246 size_t length = y ? 2 : x ? 1 : 0;
247 auto* result = BigInt::createUninitialized(cx, length, negative);
248 if (!result) {
249 return nullptr;
251 if (y) {
252 result->setDigit(1, y);
254 if (x) {
255 result->setDigit(0, x);
257 return result;
258 } else {
259 size_t length = digits[2] ? 3 : digits[1] ? 2 : digits[0] ? 1 : 0;
260 auto* result = BigInt::createUninitialized(cx, length, negative);
261 if (!result) {
262 return nullptr;
264 while (length--) {
265 result->setDigit(length, digits[length]);
267 return result;
271 static auto ToBigIntDigits(uint64_t seconds, uint32_t nanoseconds) {
272 // Multiplies two uint32_t values and returns the lower 32-bits. The higher
273 // 32-bits are stored in |high|.
274 auto digitMul = [](uint32_t a, uint32_t b, uint32_t* high) {
275 uint64_t result = static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
276 *high = result >> 32;
277 return static_cast<uint32_t>(result);
280 // Adds two uint32_t values and returns the result. Overflow is added to the
281 // out-param |carry|.
282 auto digitAdd = [](uint32_t a, uint32_t b, uint32_t* carry) {
283 uint32_t result = a + b;
284 *carry += static_cast<uint32_t>(result < a);
285 return result;
288 constexpr uint32_t secToNanos = ToNanoseconds(TemporalUnit::Second);
290 // uint32_t digits stored in the same order as BigInt digits, i.e. the least
291 // significant digit is stored at index zero.
292 std::array<uint32_t, 2> multiplicand = {uint32_t(seconds),
293 uint32_t(seconds >> 32)};
294 std::array<uint32_t, 3> accumulator = {nanoseconds, 0, 0};
296 // This code follows the implementation of |BigInt::multiplyAccumulate()|.
298 uint32_t carry = 0;
300 uint32_t high = 0;
301 uint32_t low = digitMul(secToNanos, multiplicand[0], &high);
303 uint32_t newCarry = 0;
304 accumulator[0] = digitAdd(accumulator[0], low, &newCarry);
305 accumulator[1] = digitAdd(high, newCarry, &carry);
308 uint32_t high = 0;
309 uint32_t low = digitMul(secToNanos, multiplicand[1], &high);
311 uint32_t newCarry = 0;
312 accumulator[1] = digitAdd(accumulator[1], low, &carry);
313 accumulator[2] = digitAdd(high, carry, &newCarry);
314 MOZ_ASSERT(newCarry == 0);
317 return accumulator;
320 template <typename T>
321 static BigInt* ToBigInt(JSContext* cx,
322 const SecondsAndNanoseconds<T>& secondsAndNanoseconds) {
323 uint64_t seconds = std::abs(secondsAndNanoseconds.seconds);
324 uint32_t nanoseconds = secondsAndNanoseconds.nanoseconds;
326 // Negative nanoseconds are represented as the difference to 1'000'000'000.
327 // Convert these back to their absolute value and adjust the seconds part
328 // accordingly.
330 // For example the nanoseconds from the epoch value |-1n| is represented as
331 // the instant {seconds: -1, nanoseconds: 999'999'999}.
332 if (secondsAndNanoseconds.seconds < 0 && nanoseconds != 0) {
333 nanoseconds = ToNanoseconds(TemporalUnit::Second) - nanoseconds;
334 seconds -= 1;
337 auto digits = ToBigIntDigits(seconds, nanoseconds);
338 return CreateBigInt(cx, digits, secondsAndNanoseconds.seconds < 0);
341 BigInt* js::temporal::ToEpochNanoseconds(JSContext* cx,
342 const Instant& instant) {
343 MOZ_ASSERT(IsValidEpochInstant(instant));
344 return ::ToBigInt(cx, instant);
348 * GetUTCEpochNanoseconds ( year, month, day, hour, minute, second, millisecond,
349 * microsecond, nanosecond [ , offsetNanoseconds ] )
351 Instant js::temporal::GetUTCEpochNanoseconds(const PlainDateTime& dateTime) {
352 const auto& [date, time] = dateTime;
354 // Step 1.
355 MOZ_ASSERT(IsValidISODateTime(dateTime));
357 // Additionally ensure the date-time value can be represented as an Instant.
358 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
360 // Steps 2-5.
361 int64_t ms = MakeDate(dateTime);
363 // Propagate the input range to the compiler.
364 int32_t nanos =
365 std::clamp(time.microsecond * 1'000 + time.nanosecond, 0, 999'999);
367 // Steps 6-8.
368 return Instant::fromMilliseconds(ms) + InstantSpan{0, nanos};
372 * GetUTCEpochNanoseconds ( year, month, day, hour, minute, second, millisecond,
373 * microsecond, nanosecond [ , offsetNanoseconds ] )
375 Instant js::temporal::GetUTCEpochNanoseconds(
376 const PlainDateTime& dateTime, const InstantSpan& offsetNanoseconds) {
377 MOZ_ASSERT(offsetNanoseconds.abs() <
378 InstantSpan::fromNanoseconds(ToNanoseconds(TemporalUnit::Day)));
380 // Steps 1-6.
381 auto epochNanoseconds = GetUTCEpochNanoseconds(dateTime);
383 // Steps 7-9.
384 return epochNanoseconds - offsetNanoseconds;
388 * CompareEpochNanoseconds ( epochNanosecondsOne, epochNanosecondsTwo )
390 static int32_t CompareEpochNanoseconds(const Instant& epochNanosecondsOne,
391 const Instant& epochNanosecondsTwo) {
392 // Step 1.
393 if (epochNanosecondsOne > epochNanosecondsTwo) {
394 return 1;
397 // Step 2.
398 if (epochNanosecondsOne < epochNanosecondsTwo) {
399 return -1;
402 // Step 3.
403 return 0;
407 * CreateTemporalInstant ( epochNanoseconds [ , newTarget ] )
409 InstantObject* js::temporal::CreateTemporalInstant(JSContext* cx,
410 const Instant& instant) {
411 // Step 1.
412 MOZ_ASSERT(IsValidEpochInstant(instant));
414 // Steps 2-3.
415 auto* object = NewBuiltinClassInstance<InstantObject>(cx);
416 if (!object) {
417 return nullptr;
420 // Step 4.
421 object->setFixedSlot(InstantObject::SECONDS_SLOT,
422 NumberValue(instant.seconds));
423 object->setFixedSlot(InstantObject::NANOSECONDS_SLOT,
424 Int32Value(instant.nanoseconds));
426 // Step 5.
427 return object;
431 * CreateTemporalInstant ( epochNanoseconds [ , newTarget ] )
433 static InstantObject* CreateTemporalInstant(JSContext* cx, const CallArgs& args,
434 Handle<BigInt*> epochNanoseconds) {
435 // Step 1.
436 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
438 // Steps 2-3.
439 Rooted<JSObject*> proto(cx);
440 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Instant, &proto)) {
441 return nullptr;
444 auto* object = NewObjectWithClassProto<InstantObject>(cx, proto);
445 if (!object) {
446 return nullptr;
449 // Step 4.
450 auto instant = ToInstant(epochNanoseconds);
451 object->setFixedSlot(InstantObject::SECONDS_SLOT,
452 NumberValue(instant.seconds));
453 object->setFixedSlot(InstantObject::NANOSECONDS_SLOT,
454 Int32Value(instant.nanoseconds));
456 // Step 5.
457 return object;
461 * ToTemporalInstant ( item )
463 Wrapped<InstantObject*> js::temporal::ToTemporalInstant(JSContext* cx,
464 Handle<Value> item) {
465 // Step 1.
466 if (item.isObject()) {
467 JSObject* itemObj = &item.toObject();
469 // Step 1.a.
470 if (itemObj->canUnwrapAs<InstantObject>()) {
471 return itemObj;
475 // Steps 1.b-d and 3-6
476 Instant epochNanoseconds;
477 if (!ToTemporalInstant(cx, item, &epochNanoseconds)) {
478 return nullptr;
481 // Step 7.
482 return CreateTemporalInstant(cx, epochNanoseconds);
486 * ToTemporalInstant ( item )
488 bool js::temporal::ToTemporalInstant(JSContext* cx, Handle<Value> item,
489 Instant* result) {
490 // Step 1.
491 Rooted<Value> primitiveValue(cx, item);
492 if (item.isObject()) {
493 JSObject* itemObj = &item.toObject();
495 // Step 1.a.
496 if (auto* instant = itemObj->maybeUnwrapIf<InstantObject>()) {
497 *result = ToInstant(instant);
498 return true;
501 // Step 1.b.
502 if (auto* zonedDateTime = itemObj->maybeUnwrapIf<ZonedDateTimeObject>()) {
503 *result = ToInstant(zonedDateTime);
504 return true;
507 // Steps 1.c-d.
508 if (!ToPrimitive(cx, JSTYPE_STRING, &primitiveValue)) {
509 return false;
513 // Step 2.
514 if (!primitiveValue.isString()) {
515 // The value is always on the stack, so JSDVG_SEARCH_STACK can be used for
516 // better error reporting.
517 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK,
518 primitiveValue, nullptr, "not a string");
519 return false;
521 Rooted<JSString*> string(cx, primitiveValue.toString());
523 // Steps 3-4.
524 PlainDateTime dateTime;
525 int64_t offset;
526 if (!ParseTemporalInstantString(cx, string, &dateTime, &offset)) {
527 return false;
529 MOZ_ASSERT(std::abs(offset) < ToNanoseconds(TemporalUnit::Day));
531 // Step 6. (Reordered)
532 if (!ISODateTimeWithinLimits(dateTime)) {
533 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
534 JSMSG_TEMPORAL_INSTANT_INVALID);
535 return false;
538 // Step 5.
539 auto epochNanoseconds =
540 GetUTCEpochNanoseconds(dateTime, InstantSpan::fromNanoseconds(offset));
542 // Step 6.
543 if (!IsValidEpochInstant(epochNanoseconds)) {
544 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
545 JSMSG_TEMPORAL_INSTANT_INVALID);
546 return false;
549 // Step 7.
550 *result = epochNanoseconds;
551 return true;
555 * AddNormalizedTimeDurationToEpochNanoseconds ( d, epochNs )
557 Instant js::temporal::AddNormalizedTimeDurationToEpochNanoseconds(
558 const NormalizedTimeDuration& d, const Instant& epochNs) {
559 MOZ_ASSERT(IsValidNormalizedTimeDuration(d));
560 MOZ_ASSERT(IsValidEpochInstant(epochNs));
562 // Step 1.
563 return epochNs + d.to<InstantSpan>();
567 * AddInstant ( epochNanoseconds, hours, minutes, seconds, milliseconds,
568 * microseconds, nanoseconds )
570 bool js::temporal::AddInstant(JSContext* cx, const Instant& instant,
571 const NormalizedTimeDuration& duration,
572 Instant* result) {
573 MOZ_ASSERT(IsValidEpochInstant(instant));
574 MOZ_ASSERT(IsValidNormalizedTimeDuration(duration));
576 // Step 1.
577 auto r = AddNormalizedTimeDurationToEpochNanoseconds(duration, instant);
579 // Step 2.
580 if (!IsValidEpochInstant(r)) {
581 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
582 JSMSG_TEMPORAL_INSTANT_INVALID);
583 return false;
586 // Step 3.
587 *result = r;
588 return true;
592 * DifferenceInstant ( ns1, ns2, roundingIncrement, smallestUnit, roundingMode )
594 NormalizedTimeDuration js::temporal::DifferenceInstant(
595 const Instant& ns1, const Instant& ns2, Increment roundingIncrement,
596 TemporalUnit smallestUnit, TemporalRoundingMode roundingMode) {
597 MOZ_ASSERT(IsValidEpochInstant(ns1));
598 MOZ_ASSERT(IsValidEpochInstant(ns2));
599 MOZ_ASSERT(smallestUnit > TemporalUnit::Day);
600 MOZ_ASSERT(roundingIncrement <=
601 MaximumTemporalDurationRoundingIncrement(smallestUnit));
603 // Step 1.
604 auto diff = NormalizedTimeDurationFromEpochNanosecondsDifference(ns2, ns1);
605 MOZ_ASSERT(IsValidInstantSpan(diff.to<InstantSpan>()));
607 // Step 2.
608 if (smallestUnit == TemporalUnit::Nanosecond &&
609 roundingIncrement == Increment{1}) {
610 return diff;
613 // Steps 3-4.
614 return RoundDuration(diff, roundingIncrement, smallestUnit, roundingMode);
618 * RoundNumberToIncrementAsIfPositive ( x, increment, roundingMode )
620 static Instant RoundNumberToIncrementAsIfPositive(
621 const Instant& x, int64_t increment, TemporalRoundingMode roundingMode) {
622 MOZ_ASSERT(IsValidEpochInstant(x));
623 MOZ_ASSERT(increment > 0);
624 MOZ_ASSERT(increment <= ToNanoseconds(TemporalUnit::Day));
626 // This operation is equivalent to adjusting the rounding mode through
627 // |ToPositiveRoundingMode| and then calling |RoundNumberToIncrement|.
628 auto rounded = RoundNumberToIncrement(x.toNanoseconds(), Int128{increment},
629 ToPositiveRoundingMode(roundingMode));
630 return Instant::fromNanoseconds(rounded);
634 * RoundTemporalInstant ( ns, increment, unit, roundingMode )
636 Instant js::temporal::RoundTemporalInstant(const Instant& ns,
637 Increment increment,
638 TemporalUnit unit,
639 TemporalRoundingMode roundingMode) {
640 MOZ_ASSERT(IsValidEpochInstant(ns));
641 MOZ_ASSERT(increment >= Increment::min());
642 MOZ_ASSERT(uint64_t(increment.value()) <= ToNanoseconds(TemporalUnit::Day));
643 MOZ_ASSERT(unit > TemporalUnit::Day);
645 // Steps 1-6.
646 int64_t toNanoseconds = ToNanoseconds(unit);
647 MOZ_ASSERT(
648 (increment.value() * toNanoseconds) <= ToNanoseconds(TemporalUnit::Day),
649 "increment * toNanoseconds shouldn't overflow instant resolution");
651 // Step 7.
652 return RoundNumberToIncrementAsIfPositive(
653 ns, increment.value() * toNanoseconds, roundingMode);
657 * DifferenceTemporalInstant ( operation, instant, other, options )
659 static bool DifferenceTemporalInstant(JSContext* cx,
660 TemporalDifference operation,
661 const CallArgs& args) {
662 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
664 // Step 1. (Not applicable in our implementation.)
666 // Step 2.
667 Instant other;
668 if (!ToTemporalInstant(cx, args.get(0), &other)) {
669 return false;
672 // Steps 3-4.
673 DifferenceSettings settings;
674 if (args.hasDefined(1)) {
675 Rooted<JSObject*> options(
676 cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
677 if (!options) {
678 return false;
681 // Step 3.
682 Rooted<PlainObject*> resolvedOptions(cx,
683 SnapshotOwnProperties(cx, options));
684 if (!resolvedOptions) {
685 return false;
688 // Step 4.
689 if (!GetDifferenceSettings(
690 cx, operation, resolvedOptions, TemporalUnitGroup::Time,
691 TemporalUnit::Nanosecond, TemporalUnit::Second, &settings)) {
692 return false;
694 } else {
695 // Steps 3-4.
696 settings = {
697 TemporalUnit::Nanosecond,
698 TemporalUnit::Second,
699 TemporalRoundingMode::Trunc,
700 Increment{1},
704 // Step 5.
705 auto difference =
706 DifferenceInstant(instant, other, settings.roundingIncrement,
707 settings.smallestUnit, settings.roundingMode);
709 // Step 6.
710 auto balanced = BalanceTimeDuration(difference, settings.largestUnit);
712 // Step 7.
713 auto duration = balanced.toDuration();
714 if (operation == TemporalDifference::Since) {
715 duration = duration.negate();
718 auto* obj = CreateTemporalDuration(cx, duration);
719 if (!obj) {
720 return false;
723 args.rval().setObject(*obj);
724 return true;
727 enum class InstantDuration { Add, Subtract };
730 * AddDurationToOrSubtractDurationFromInstant ( operation, instant,
731 * temporalDurationLike )
733 static bool AddDurationToOrSubtractDurationFromInstant(
734 JSContext* cx, InstantDuration operation, const CallArgs& args) {
735 auto* instant = &args.thisv().toObject().as<InstantObject>();
736 auto epochNanoseconds = ToInstant(instant);
738 // Step 1. (Not applicable in our implementation.)
740 // Step 2.
741 Duration duration;
742 if (!ToTemporalDurationRecord(cx, args.get(0), &duration)) {
743 return false;
746 // Steps 3-6.
747 if (duration.years != 0 || duration.months != 0 || duration.weeks != 0 ||
748 duration.days != 0) {
749 const char* part = duration.years != 0 ? "years"
750 : duration.months != 0 ? "months"
751 : duration.weeks != 0 ? "weeks"
752 : "days";
753 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
754 JSMSG_TEMPORAL_INSTANT_BAD_DURATION, part);
755 return false;
758 // Step 7.
759 if (operation == InstantDuration::Subtract) {
760 duration = duration.negate();
762 auto timeDuration = NormalizeTimeDuration(duration);
764 // Step 8.
765 Instant ns;
766 if (!AddInstant(cx, epochNanoseconds, timeDuration, &ns)) {
767 return false;
770 // Step 9.
771 auto* result = CreateTemporalInstant(cx, ns);
772 if (!result) {
773 return false;
776 args.rval().setObject(*result);
777 return true;
781 * Temporal.Instant ( epochNanoseconds )
783 static bool InstantConstructor(JSContext* cx, unsigned argc, Value* vp) {
784 CallArgs args = CallArgsFromVp(argc, vp);
786 // Step 1.
787 if (!ThrowIfNotConstructing(cx, args, "Temporal.Instant")) {
788 return false;
791 // Step 2.
792 Rooted<BigInt*> epochNanoseconds(cx, js::ToBigInt(cx, args.get(0)));
793 if (!epochNanoseconds) {
794 return false;
797 // Step 3.
798 if (!IsValidEpochNanoseconds(epochNanoseconds)) {
799 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
800 JSMSG_TEMPORAL_INSTANT_INVALID);
801 return false;
804 // Step 4.
805 auto* result = CreateTemporalInstant(cx, args, epochNanoseconds);
806 if (!result) {
807 return false;
810 args.rval().setObject(*result);
811 return true;
815 * Temporal.Instant.from ( item )
817 static bool Instant_from(JSContext* cx, unsigned argc, Value* vp) {
818 CallArgs args = CallArgsFromVp(argc, vp);
820 // Steps 1-2.
821 Instant epochInstant;
822 if (!ToTemporalInstant(cx, args.get(0), &epochInstant)) {
823 return false;
826 auto* result = CreateTemporalInstant(cx, epochInstant);
827 if (!result) {
828 return false;
830 args.rval().setObject(*result);
831 return true;
835 * Temporal.Instant.fromEpochSeconds ( epochSeconds )
837 static bool Instant_fromEpochSeconds(JSContext* cx, unsigned argc, Value* vp) {
838 CallArgs args = CallArgsFromVp(argc, vp);
840 // Step 1.
841 double epochSeconds;
842 if (!JS::ToNumber(cx, args.get(0), &epochSeconds)) {
843 return false;
846 // Step 2.
848 // NumberToBigInt throws a RangeError for non-integral numbers.
849 if (!IsInteger(epochSeconds)) {
850 ToCStringBuf cbuf;
851 const char* str = NumberToCString(&cbuf, epochSeconds);
853 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
854 JSMSG_TEMPORAL_INSTANT_NONINTEGER, str);
855 return false;
858 // Step 3. (Not applicable)
860 // Step 4.
861 if (!IsValidEpochSeconds(epochSeconds)) {
862 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
863 JSMSG_TEMPORAL_INSTANT_INVALID);
864 return false;
867 // Step 5.
868 int64_t seconds = mozilla::AssertedCast<int64_t>(epochSeconds);
869 auto* result = CreateTemporalInstant(cx, Instant::fromSeconds(seconds));
870 if (!result) {
871 return false;
873 args.rval().setObject(*result);
874 return true;
878 * Temporal.Instant.fromEpochMilliseconds ( epochMilliseconds )
880 static bool Instant_fromEpochMilliseconds(JSContext* cx, unsigned argc,
881 Value* vp) {
882 CallArgs args = CallArgsFromVp(argc, vp);
884 // Step 1.
885 double epochMilliseconds;
886 if (!JS::ToNumber(cx, args.get(0), &epochMilliseconds)) {
887 return false;
890 // Step 2.
892 // NumberToBigInt throws a RangeError for non-integral numbers.
893 if (!IsInteger(epochMilliseconds)) {
894 ToCStringBuf cbuf;
895 const char* str = NumberToCString(&cbuf, epochMilliseconds);
897 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
898 JSMSG_TEMPORAL_INSTANT_NONINTEGER, str);
899 return false;
902 // Step 3. (Not applicable)
904 // Step 4.
905 if (!IsValidEpochMilliseconds(epochMilliseconds)) {
906 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
907 JSMSG_TEMPORAL_INSTANT_INVALID);
908 return false;
911 // Step 5.
912 int64_t milliseconds = mozilla::AssertedCast<int64_t>(epochMilliseconds);
913 auto* result =
914 CreateTemporalInstant(cx, Instant::fromMilliseconds(milliseconds));
915 if (!result) {
916 return false;
918 args.rval().setObject(*result);
919 return true;
923 * Temporal.Instant.fromEpochMicroseconds ( epochMicroseconds )
925 static bool Instant_fromEpochMicroseconds(JSContext* cx, unsigned argc,
926 Value* vp) {
927 CallArgs args = CallArgsFromVp(argc, vp);
929 // Step 1.
930 Rooted<BigInt*> epochMicroseconds(cx, js::ToBigInt(cx, args.get(0)));
931 if (!epochMicroseconds) {
932 return false;
935 // Step 2. (Not applicable)
937 // Step 3.
938 if (!IsValidEpochMicroseconds(epochMicroseconds)) {
939 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
940 JSMSG_TEMPORAL_INSTANT_INVALID);
941 return false;
944 int64_t i;
945 MOZ_ALWAYS_TRUE(BigInt::isInt64(epochMicroseconds, &i));
947 // Step 4.
948 auto* result = CreateTemporalInstant(cx, Instant::fromMicroseconds(i));
949 if (!result) {
950 return false;
952 args.rval().setObject(*result);
953 return true;
957 * Temporal.Instant.fromEpochNanoseconds ( epochNanoseconds )
959 static bool Instant_fromEpochNanoseconds(JSContext* cx, unsigned argc,
960 Value* vp) {
961 CallArgs args = CallArgsFromVp(argc, vp);
963 // Step 1.
964 Rooted<BigInt*> epochNanoseconds(cx, js::ToBigInt(cx, args.get(0)));
965 if (!epochNanoseconds) {
966 return false;
969 // Step 2.
970 if (!IsValidEpochNanoseconds(epochNanoseconds)) {
971 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
972 JSMSG_TEMPORAL_INSTANT_INVALID);
973 return false;
976 // Step 3.
977 auto* result = CreateTemporalInstant(cx, ToInstant(epochNanoseconds));
978 if (!result) {
979 return false;
981 args.rval().setObject(*result);
982 return true;
986 * Temporal.Instant.compare ( one, two )
988 static bool Instant_compare(JSContext* cx, unsigned argc, Value* vp) {
989 CallArgs args = CallArgsFromVp(argc, vp);
991 // Step 1.
992 Instant one;
993 if (!ToTemporalInstant(cx, args.get(0), &one)) {
994 return false;
997 // Step 2.
998 Instant two;
999 if (!ToTemporalInstant(cx, args.get(1), &two)) {
1000 return false;
1003 // Step 3.
1004 args.rval().setInt32(CompareEpochNanoseconds(one, two));
1005 return true;
1009 * get Temporal.Instant.prototype.epochSeconds
1011 static bool Instant_epochSeconds(JSContext* cx, const CallArgs& args) {
1012 // Step 3.
1013 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1015 // Steps 4-5.
1016 args.rval().setNumber(instant.seconds);
1017 return true;
1021 * get Temporal.Instant.prototype.epochSeconds
1023 static bool Instant_epochSeconds(JSContext* cx, unsigned argc, Value* vp) {
1024 // Steps 1-2.
1025 CallArgs args = CallArgsFromVp(argc, vp);
1026 return CallNonGenericMethod<IsInstant, Instant_epochSeconds>(cx, args);
1030 * get Temporal.Instant.prototype.epochMilliseconds
1032 static bool Instant_epochMilliseconds(JSContext* cx, const CallArgs& args) {
1033 // Step 3.
1034 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1036 // Step 4-5.
1037 args.rval().setNumber(instant.floorToMilliseconds());
1038 return true;
1042 * get Temporal.Instant.prototype.epochMilliseconds
1044 static bool Instant_epochMilliseconds(JSContext* cx, unsigned argc, Value* vp) {
1045 // Steps 1-2.
1046 CallArgs args = CallArgsFromVp(argc, vp);
1047 return CallNonGenericMethod<IsInstant, Instant_epochMilliseconds>(cx, args);
1051 * get Temporal.Instant.prototype.epochMicroseconds
1053 static bool Instant_epochMicroseconds(JSContext* cx, const CallArgs& args) {
1054 // Step 3.
1055 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1057 // Step 4.
1058 auto* microseconds =
1059 BigInt::createFromInt64(cx, instant.floorToMicroseconds());
1060 if (!microseconds) {
1061 return false;
1064 // Step 5.
1065 args.rval().setBigInt(microseconds);
1066 return true;
1070 * get Temporal.Instant.prototype.epochMicroseconds
1072 static bool Instant_epochMicroseconds(JSContext* cx, unsigned argc, Value* vp) {
1073 // Steps 1-2.
1074 CallArgs args = CallArgsFromVp(argc, vp);
1075 return CallNonGenericMethod<IsInstant, Instant_epochMicroseconds>(cx, args);
1079 * get Temporal.Instant.prototype.epochNanoseconds
1081 static bool Instant_epochNanoseconds(JSContext* cx, const CallArgs& args) {
1082 // Step 3.
1083 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1084 auto* nanoseconds = ToEpochNanoseconds(cx, instant);
1085 if (!nanoseconds) {
1086 return false;
1089 // Step 4.
1090 args.rval().setBigInt(nanoseconds);
1091 return true;
1095 * get Temporal.Instant.prototype.epochNanoseconds
1097 static bool Instant_epochNanoseconds(JSContext* cx, unsigned argc, Value* vp) {
1098 // Steps 1-2.
1099 CallArgs args = CallArgsFromVp(argc, vp);
1100 return CallNonGenericMethod<IsInstant, Instant_epochNanoseconds>(cx, args);
1104 * Temporal.Instant.prototype.add ( temporalDurationLike )
1106 static bool Instant_add(JSContext* cx, const CallArgs& args) {
1107 return AddDurationToOrSubtractDurationFromInstant(cx, InstantDuration::Add,
1108 args);
1112 * Temporal.Instant.prototype.add ( temporalDurationLike )
1114 static bool Instant_add(JSContext* cx, unsigned argc, Value* vp) {
1115 // Steps 1-2.
1116 CallArgs args = CallArgsFromVp(argc, vp);
1117 return CallNonGenericMethod<IsInstant, Instant_add>(cx, args);
1121 * Temporal.Instant.prototype.subtract ( temporalDurationLike )
1123 static bool Instant_subtract(JSContext* cx, const CallArgs& args) {
1124 return AddDurationToOrSubtractDurationFromInstant(
1125 cx, InstantDuration::Subtract, args);
1129 * Temporal.Instant.prototype.subtract ( temporalDurationLike )
1131 static bool Instant_subtract(JSContext* cx, unsigned argc, Value* vp) {
1132 // Steps 1-2.
1133 CallArgs args = CallArgsFromVp(argc, vp);
1134 return CallNonGenericMethod<IsInstant, Instant_subtract>(cx, args);
1138 * Temporal.Instant.prototype.until ( other [ , options ] )
1140 static bool Instant_until(JSContext* cx, const CallArgs& args) {
1141 return DifferenceTemporalInstant(cx, TemporalDifference::Until, args);
1145 * Temporal.Instant.prototype.until ( other [ , options ] )
1147 static bool Instant_until(JSContext* cx, unsigned argc, Value* vp) {
1148 // Steps 1-2.
1149 CallArgs args = CallArgsFromVp(argc, vp);
1150 return CallNonGenericMethod<IsInstant, Instant_until>(cx, args);
1154 * Temporal.Instant.prototype.since ( other [ , options ] )
1156 static bool Instant_since(JSContext* cx, const CallArgs& args) {
1157 return DifferenceTemporalInstant(cx, TemporalDifference::Since, args);
1161 * Temporal.Instant.prototype.since ( other [ , options ] )
1163 static bool Instant_since(JSContext* cx, unsigned argc, Value* vp) {
1164 // Steps 1-2.
1165 CallArgs args = CallArgsFromVp(argc, vp);
1166 return CallNonGenericMethod<IsInstant, Instant_since>(cx, args);
1170 * Temporal.Instant.prototype.round ( roundTo )
1172 static bool Instant_round(JSContext* cx, const CallArgs& args) {
1173 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1175 // Steps 3-16.
1176 auto smallestUnit = TemporalUnit::Auto;
1177 auto roundingMode = TemporalRoundingMode::HalfExpand;
1178 auto roundingIncrement = Increment{1};
1179 if (args.get(0).isString()) {
1180 // Steps 4 and 6-8. (Not applicable in our implementation.)
1182 // Step 9.
1183 Rooted<JSString*> paramString(cx, args[0].toString());
1184 if (!GetTemporalUnit(cx, paramString, TemporalUnitKey::SmallestUnit,
1185 TemporalUnitGroup::Time, &smallestUnit)) {
1186 return false;
1189 // Steps 10-16. (Not applicable in our implementation.)
1190 } else {
1191 // Steps 3 and 5.
1192 Rooted<JSObject*> options(
1193 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
1194 if (!options) {
1195 return false;
1198 // Steps 6-7.
1199 if (!ToTemporalRoundingIncrement(cx, options, &roundingIncrement)) {
1200 return false;
1203 // Step 8.
1204 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
1205 return false;
1208 // Step 9.
1209 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
1210 TemporalUnitGroup::Time, &smallestUnit)) {
1211 return false;
1213 if (smallestUnit == TemporalUnit::Auto) {
1214 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1215 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
1216 return false;
1219 // Steps 10-15.
1220 int64_t maximum = UnitsPerDay(smallestUnit);
1222 // Step 16.
1223 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
1224 true)) {
1225 return false;
1229 // Step 17.
1230 auto roundedNs = RoundTemporalInstant(instant, roundingIncrement,
1231 smallestUnit, roundingMode);
1233 // Step 18.
1234 auto* result = CreateTemporalInstant(cx, roundedNs);
1235 if (!result) {
1236 return false;
1238 args.rval().setObject(*result);
1239 return true;
1243 * Temporal.Instant.prototype.round ( options )
1245 static bool Instant_round(JSContext* cx, unsigned argc, Value* vp) {
1246 // Steps 1-2.
1247 CallArgs args = CallArgsFromVp(argc, vp);
1248 return CallNonGenericMethod<IsInstant, Instant_round>(cx, args);
1252 * Temporal.Instant.prototype.equals ( other )
1254 static bool Instant_equals(JSContext* cx, const CallArgs& args) {
1255 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1257 // Step 3.
1258 Instant other;
1259 if (!ToTemporalInstant(cx, args.get(0), &other)) {
1260 return false;
1263 // Steps 4-5.
1264 args.rval().setBoolean(instant == other);
1265 return true;
1269 * Temporal.Instant.prototype.equals ( other )
1271 static bool Instant_equals(JSContext* cx, unsigned argc, Value* vp) {
1272 // Steps 1-2.
1273 CallArgs args = CallArgsFromVp(argc, vp);
1274 return CallNonGenericMethod<IsInstant, Instant_equals>(cx, args);
1278 * Temporal.Instant.prototype.toString ( [ options ] )
1280 static bool Instant_toString(JSContext* cx, const CallArgs& args) {
1281 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1283 Rooted<TimeZoneValue> timeZone(cx);
1284 auto roundingMode = TemporalRoundingMode::Trunc;
1285 SecondsStringPrecision precision = {Precision::Auto(),
1286 TemporalUnit::Nanosecond, Increment{1}};
1287 if (args.hasDefined(0)) {
1288 // Step 3.
1289 Rooted<JSObject*> options(
1290 cx, RequireObjectArg(cx, "options", "toString", args[0]));
1291 if (!options) {
1292 return false;
1295 // Steps 4-5.
1296 auto digits = Precision::Auto();
1297 if (!ToFractionalSecondDigits(cx, options, &digits)) {
1298 return false;
1301 // Step 6.
1302 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
1303 return false;
1306 // Step 7.
1307 auto smallestUnit = TemporalUnit::Auto;
1308 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
1309 TemporalUnitGroup::Time, &smallestUnit)) {
1310 return false;
1313 // Step 8.
1314 if (smallestUnit == TemporalUnit::Hour) {
1315 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1316 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
1317 "smallestUnit");
1318 return false;
1321 // Step 9.
1322 Rooted<Value> value(cx);
1323 if (!GetProperty(cx, options, options, cx->names().timeZone, &value)) {
1324 return false;
1327 // Step 10.
1328 if (!value.isUndefined()) {
1329 if (!ToTemporalTimeZone(cx, value, &timeZone)) {
1330 return false;
1334 // Step 11.
1335 precision = ToSecondsStringPrecision(smallestUnit, digits);
1338 // Step 12.
1339 auto ns = RoundTemporalInstant(instant, precision.increment, precision.unit,
1340 roundingMode);
1342 // Step 13.
1343 Rooted<InstantObject*> roundedInstant(cx, CreateTemporalInstant(cx, ns));
1344 if (!roundedInstant) {
1345 return false;
1348 // Step 14.
1349 JSString* str = TemporalInstantToString(cx, roundedInstant, timeZone,
1350 precision.precision);
1351 if (!str) {
1352 return false;
1355 args.rval().setString(str);
1356 return true;
1360 * Temporal.Instant.prototype.toString ( [ options ] )
1362 static bool Instant_toString(JSContext* cx, unsigned argc, Value* vp) {
1363 // Steps 1-2.
1364 CallArgs args = CallArgsFromVp(argc, vp);
1365 return CallNonGenericMethod<IsInstant, Instant_toString>(cx, args);
1369 * Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] )
1371 static bool Instant_toLocaleString(JSContext* cx, const CallArgs& args) {
1372 Rooted<InstantObject*> instant(cx,
1373 &args.thisv().toObject().as<InstantObject>());
1375 // Step 3.
1376 Rooted<TimeZoneValue> timeZone(cx);
1377 JSString* str =
1378 TemporalInstantToString(cx, instant, timeZone, Precision::Auto());
1379 if (!str) {
1380 return false;
1383 args.rval().setString(str);
1384 return true;
1388 * Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] )
1390 static bool Instant_toLocaleString(JSContext* cx, unsigned argc, Value* vp) {
1391 // Steps 1-2.
1392 CallArgs args = CallArgsFromVp(argc, vp);
1393 return CallNonGenericMethod<IsInstant, Instant_toLocaleString>(cx, args);
1397 * Temporal.Instant.prototype.toJSON ( )
1399 static bool Instant_toJSON(JSContext* cx, const CallArgs& args) {
1400 Rooted<InstantObject*> instant(cx,
1401 &args.thisv().toObject().as<InstantObject>());
1403 // Step 3.
1404 Rooted<TimeZoneValue> timeZone(cx);
1405 JSString* str =
1406 TemporalInstantToString(cx, instant, timeZone, Precision::Auto());
1407 if (!str) {
1408 return false;
1411 args.rval().setString(str);
1412 return true;
1416 * Temporal.Instant.prototype.toJSON ( )
1418 static bool Instant_toJSON(JSContext* cx, unsigned argc, Value* vp) {
1419 // Steps 1-2.
1420 CallArgs args = CallArgsFromVp(argc, vp);
1421 return CallNonGenericMethod<IsInstant, Instant_toJSON>(cx, args);
1425 * Temporal.Instant.prototype.valueOf ( )
1427 static bool Instant_valueOf(JSContext* cx, unsigned argc, Value* vp) {
1428 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
1429 "Instant", "primitive type");
1430 return false;
1434 * Temporal.Instant.prototype.toZonedDateTime ( item )
1436 static bool Instant_toZonedDateTime(JSContext* cx, const CallArgs& args) {
1437 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1439 // Step 3.
1440 Rooted<JSObject*> item(
1441 cx, RequireObjectArg(cx, "item", "toZonedDateTime", args.get(0)));
1442 if (!item) {
1443 return false;
1446 // Step 4.
1447 Rooted<Value> calendarLike(cx);
1448 if (!GetProperty(cx, item, item, cx->names().calendar, &calendarLike)) {
1449 return false;
1452 // Step 5.
1453 if (calendarLike.isUndefined()) {
1454 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1455 JSMSG_TEMPORAL_MISSING_PROPERTY, "calendar");
1456 return false;
1459 // Step 6.
1460 Rooted<CalendarValue> calendar(cx);
1461 if (!ToTemporalCalendar(cx, calendarLike, &calendar)) {
1462 return false;
1465 // Step 7.
1466 Rooted<Value> timeZoneLike(cx);
1467 if (!GetProperty(cx, item, item, cx->names().timeZone, &timeZoneLike)) {
1468 return false;
1471 // Step 8.
1472 if (timeZoneLike.isUndefined()) {
1473 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1474 JSMSG_TEMPORAL_MISSING_PROPERTY, "timeZone");
1475 return false;
1478 // Step 9.
1479 Rooted<TimeZoneValue> timeZone(cx);
1480 if (!ToTemporalTimeZone(cx, timeZoneLike, &timeZone)) {
1481 return false;
1484 // Step 10.
1485 auto* result = CreateTemporalZonedDateTime(cx, instant, timeZone, calendar);
1486 if (!result) {
1487 return false;
1490 args.rval().setObject(*result);
1491 return true;
1495 * Temporal.Instant.prototype.toZonedDateTime ( item )
1497 static bool Instant_toZonedDateTime(JSContext* cx, unsigned argc, Value* vp) {
1498 // Steps 1-2.
1499 CallArgs args = CallArgsFromVp(argc, vp);
1500 return CallNonGenericMethod<IsInstant, Instant_toZonedDateTime>(cx, args);
1504 * Temporal.Instant.prototype.toZonedDateTimeISO ( item )
1506 static bool Instant_toZonedDateTimeISO(JSContext* cx, const CallArgs& args) {
1507 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1509 // Step 3.
1510 Rooted<TimeZoneValue> timeZone(cx);
1511 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
1512 return false;
1515 // Step 4.
1516 Rooted<CalendarValue> calendar(cx, CalendarValue(cx->names().iso8601));
1517 auto* result = CreateTemporalZonedDateTime(cx, instant, timeZone, calendar);
1518 if (!result) {
1519 return false;
1522 args.rval().setObject(*result);
1523 return true;
1527 * Temporal.Instant.prototype.toZonedDateTimeISO ( item )
1529 static bool Instant_toZonedDateTimeISO(JSContext* cx, unsigned argc,
1530 Value* vp) {
1531 // Steps 1-2.
1532 CallArgs args = CallArgsFromVp(argc, vp);
1533 return CallNonGenericMethod<IsInstant, Instant_toZonedDateTimeISO>(cx, args);
1536 const JSClass InstantObject::class_ = {
1537 "Temporal.Instant",
1538 JSCLASS_HAS_RESERVED_SLOTS(InstantObject::SLOT_COUNT) |
1539 JSCLASS_HAS_CACHED_PROTO(JSProto_Instant),
1540 JS_NULL_CLASS_OPS,
1541 &InstantObject::classSpec_,
1544 const JSClass& InstantObject::protoClass_ = PlainObject::class_;
1546 static const JSFunctionSpec Instant_methods[] = {
1547 JS_FN("from", Instant_from, 1, 0),
1548 JS_FN("fromEpochSeconds", Instant_fromEpochSeconds, 1, 0),
1549 JS_FN("fromEpochMilliseconds", Instant_fromEpochMilliseconds, 1, 0),
1550 JS_FN("fromEpochMicroseconds", Instant_fromEpochMicroseconds, 1, 0),
1551 JS_FN("fromEpochNanoseconds", Instant_fromEpochNanoseconds, 1, 0),
1552 JS_FN("compare", Instant_compare, 2, 0),
1553 JS_FS_END,
1556 static const JSFunctionSpec Instant_prototype_methods[] = {
1557 JS_FN("add", Instant_add, 1, 0),
1558 JS_FN("subtract", Instant_subtract, 1, 0),
1559 JS_FN("until", Instant_until, 1, 0),
1560 JS_FN("since", Instant_since, 1, 0),
1561 JS_FN("round", Instant_round, 1, 0),
1562 JS_FN("equals", Instant_equals, 1, 0),
1563 JS_FN("toString", Instant_toString, 0, 0),
1564 JS_FN("toLocaleString", Instant_toLocaleString, 0, 0),
1565 JS_FN("toJSON", Instant_toJSON, 0, 0),
1566 JS_FN("valueOf", Instant_valueOf, 0, 0),
1567 JS_FN("toZonedDateTime", Instant_toZonedDateTime, 1, 0),
1568 JS_FN("toZonedDateTimeISO", Instant_toZonedDateTimeISO, 1, 0),
1569 JS_FS_END,
1572 static const JSPropertySpec Instant_prototype_properties[] = {
1573 JS_PSG("epochSeconds", Instant_epochSeconds, 0),
1574 JS_PSG("epochMilliseconds", Instant_epochMilliseconds, 0),
1575 JS_PSG("epochMicroseconds", Instant_epochMicroseconds, 0),
1576 JS_PSG("epochNanoseconds", Instant_epochNanoseconds, 0),
1577 JS_STRING_SYM_PS(toStringTag, "Temporal.Instant", JSPROP_READONLY),
1578 JS_PS_END,
1581 const ClassSpec InstantObject::classSpec_ = {
1582 GenericCreateConstructor<InstantConstructor, 1, gc::AllocKind::FUNCTION>,
1583 GenericCreatePrototype<InstantObject>,
1584 Instant_methods,
1585 nullptr,
1586 Instant_prototype_methods,
1587 Instant_prototype_properties,
1588 nullptr,
1589 ClassSpec::DontDefineConstructor,