Bug 1874684 - Part 11: Remove no longer needed BigInt code path in TemporalDurationTo...
[gecko.git] / js / src / builtin / temporal / Instant.cpp
blob5f30980d755af606df8898a86e28438e7b8c9d58
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/CheckedInt.h"
11 #include "mozilla/FloatingPoint.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/Span.h"
15 #include <algorithm>
16 #include <array>
17 #include <cstdlib>
18 #include <iterator>
19 #include <stddef.h>
20 #include <stdint.h>
21 #include <utility>
23 #include "jsnum.h"
24 #include "jspubtd.h"
25 #include "NamespaceImports.h"
27 #include "builtin/temporal/Calendar.h"
28 #include "builtin/temporal/Duration.h"
29 #include "builtin/temporal/Int96.h"
30 #include "builtin/temporal/PlainDateTime.h"
31 #include "builtin/temporal/Temporal.h"
32 #include "builtin/temporal/TemporalParser.h"
33 #include "builtin/temporal/TemporalRoundingMode.h"
34 #include "builtin/temporal/TemporalTypes.h"
35 #include "builtin/temporal/TemporalUnit.h"
36 #include "builtin/temporal/TimeZone.h"
37 #include "builtin/temporal/ToString.h"
38 #include "builtin/temporal/Wrapped.h"
39 #include "builtin/temporal/ZonedDateTime.h"
40 #include "gc/AllocKind.h"
41 #include "gc/Barrier.h"
42 #include "js/CallArgs.h"
43 #include "js/CallNonGenericMethod.h"
44 #include "js/Class.h"
45 #include "js/Conversions.h"
46 #include "js/ErrorReport.h"
47 #include "js/friend/ErrorMessages.h"
48 #include "js/PropertyDescriptor.h"
49 #include "js/PropertySpec.h"
50 #include "js/RootingAPI.h"
51 #include "js/TypeDecls.h"
52 #include "js/Value.h"
53 #include "vm/BigIntType.h"
54 #include "vm/BytecodeUtil.h"
55 #include "vm/GlobalObject.h"
56 #include "vm/JSAtomState.h"
57 #include "vm/JSContext.h"
58 #include "vm/JSObject.h"
59 #include "vm/PlainObject.h"
60 #include "vm/StringType.h"
62 #include "vm/JSObject-inl.h"
63 #include "vm/NativeObject-inl.h"
64 #include "vm/ObjectOperations-inl.h"
66 using namespace js;
67 using namespace js::temporal;
69 static inline bool IsInstant(Handle<Value> v) {
70 return v.isObject() && v.toObject().is<InstantObject>();
73 /**
74 * Check if the absolute value is less-or-equal to the given limit.
76 template <const auto& digits>
77 static bool AbsoluteValueIsLessOrEqual(const BigInt* bigInt) {
78 size_t length = bigInt->digitLength();
80 // Fewer digits than the limit, so definitely in range.
81 if (length < std::size(digits)) {
82 return true;
85 // More digits than the limit, so definitely out of range.
86 if (length > std::size(digits)) {
87 return false;
90 // Compare each digit when the input has the same number of digits.
91 size_t index = std::size(digits);
92 for (auto digit : digits) {
93 auto d = bigInt->digit(--index);
94 if (d < digit) {
95 return true;
97 if (d > digit) {
98 return false;
101 return true;
104 static constexpr auto NanosecondsMaxInstant() {
105 static_assert(BigInt::DigitBits == 64 || BigInt::DigitBits == 32);
107 // ±8.64 × 10^21 is the nanoseconds from epoch limit.
108 // 8.64 × 10^21 is 86_40000_00000_00000_00000 or 0x1d4_60162f51_6f000000.
109 // Return the BigInt digits of that number for fast BigInt comparisons.
110 if constexpr (BigInt::DigitBits == 64) {
111 return std::array{
112 BigInt::Digit(0x1d4),
113 BigInt::Digit(0x6016'2f51'6f00'0000),
115 } else {
116 return std::array{
117 BigInt::Digit(0x1d4),
118 BigInt::Digit(0x6016'2f51),
119 BigInt::Digit(0x6f00'0000),
125 * IsValidEpochNanoseconds ( epochNanoseconds )
127 bool js::temporal::IsValidEpochNanoseconds(const BigInt* epochNanoseconds) {
128 // Steps 1-3.
129 static constexpr auto epochLimit = NanosecondsMaxInstant();
130 return AbsoluteValueIsLessOrEqual<epochLimit>(epochNanoseconds);
133 static bool IsValidEpochMicroseconds(const BigInt* epochMicroseconds) {
134 int64_t i;
135 if (!BigInt::isInt64(epochMicroseconds, &i)) {
136 return false;
139 constexpr int64_t MicrosecondsMaxInstant = Instant::max().toMicroseconds();
140 return -MicrosecondsMaxInstant <= i && i <= MicrosecondsMaxInstant;
143 static bool IsValidEpochMilliseconds(double epochMilliseconds) {
144 MOZ_ASSERT(IsInteger(epochMilliseconds));
146 constexpr int64_t MillisecondsMaxInstant = Instant::max().toMilliseconds();
147 return std::abs(epochMilliseconds) <= double(MillisecondsMaxInstant);
150 static bool IsValidEpochSeconds(double epochSeconds) {
151 MOZ_ASSERT(IsInteger(epochSeconds));
153 constexpr int64_t SecondsMaxInstant = Instant::max().toSeconds();
154 return std::abs(epochSeconds) <= double(SecondsMaxInstant);
158 * IsValidEpochNanoseconds ( epochNanoseconds )
160 bool js::temporal::IsValidEpochInstant(const Instant& instant) {
161 MOZ_ASSERT(0 <= instant.nanoseconds && instant.nanoseconds <= 999'999'999);
163 // Steps 1-3.
164 return Instant::min() <= instant && instant <= Instant::max();
167 #ifdef DEBUG
169 * Validates a nanoseconds amount is at most as large as the difference
170 * between two valid nanoseconds from the epoch instants.
172 bool js::temporal::IsValidInstantSpan(const InstantSpan& span) {
173 MOZ_ASSERT(0 <= span.nanoseconds && span.nanoseconds <= 999'999'999);
175 // Steps 1-3.
176 return InstantSpan::min() <= span && span <= InstantSpan::max();
178 #endif
181 * Return the BigInt as a 96-bit integer. The BigInt digits must not consist of
182 * more than 96-bits.
184 static Int96 ToInt96(const BigInt* ns) {
185 static_assert(BigInt::DigitBits == 64 || BigInt::DigitBits == 32);
187 auto digits = ns->digits();
188 if constexpr (BigInt::DigitBits == 64) {
189 BigInt::Digit x = 0, y = 0;
190 switch (digits.size()) {
191 case 2:
192 y = digits[1];
193 [[fallthrough]];
194 case 1:
195 x = digits[0];
196 [[fallthrough]];
197 case 0:
198 break;
199 default:
200 MOZ_ASSERT_UNREACHABLE("unexpected digit length");
202 return Int96{
203 Int96::Digits{Int96::Digit(x), Int96::Digit(x >> 32), Int96::Digit(y)},
204 ns->isNegative()};
205 } else {
206 BigInt::Digit x = 0, y = 0, z = 0;
207 switch (digits.size()) {
208 case 3:
209 z = digits[2];
210 [[fallthrough]];
211 case 2:
212 y = digits[1];
213 [[fallthrough]];
214 case 1:
215 x = digits[0];
216 [[fallthrough]];
217 case 0:
218 break;
219 default:
220 MOZ_ASSERT_UNREACHABLE("unexpected digit length");
222 return Int96{
223 Int96::Digits{Int96::Digit(x), Int96::Digit(y), Int96::Digit(z)},
224 ns->isNegative()};
228 Instant js::temporal::ToInstant(const BigInt* epochNanoseconds) {
229 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
231 auto [seconds, nanos] =
232 ToInt96(epochNanoseconds) / ToNanoseconds(TemporalUnit::Second);
233 return {seconds, nanos};
236 static BigInt* CreateBigInt(JSContext* cx,
237 const std::array<uint32_t, 3>& digits,
238 bool negative) {
239 static_assert(BigInt::DigitBits == 64 || BigInt::DigitBits == 32);
241 if constexpr (BigInt::DigitBits == 64) {
242 uint64_t x = (uint64_t(digits[1]) << 32) | digits[0];
243 uint64_t y = digits[2];
245 size_t length = y ? 2 : x ? 1 : 0;
246 auto* result = BigInt::createUninitialized(cx, length, negative);
247 if (!result) {
248 return nullptr;
250 if (y) {
251 result->setDigit(1, y);
253 if (x) {
254 result->setDigit(0, x);
256 return result;
257 } else {
258 size_t length = digits[2] ? 3 : digits[1] ? 2 : digits[0] ? 1 : 0;
259 auto* result = BigInt::createUninitialized(cx, length, negative);
260 if (!result) {
261 return nullptr;
263 while (length--) {
264 result->setDigit(length, digits[length]);
266 return result;
270 static auto ToBigIntDigits(uint64_t seconds, uint32_t nanoseconds) {
271 // Multiplies two uint32_t values and returns the lower 32-bits. The higher
272 // 32-bits are stored in |high|.
273 auto digitMul = [](uint32_t a, uint32_t b, uint32_t* high) {
274 uint64_t result = static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
275 *high = result >> 32;
276 return static_cast<uint32_t>(result);
279 // Adds two uint32_t values and returns the result. Overflow is added to the
280 // out-param |carry|.
281 auto digitAdd = [](uint32_t a, uint32_t b, uint32_t* carry) {
282 uint32_t result = a + b;
283 *carry += static_cast<uint32_t>(result < a);
284 return result;
287 constexpr uint32_t secToNanos = ToNanoseconds(TemporalUnit::Second);
289 // uint32_t digits stored in the same order as BigInt digits, i.e. the least
290 // significant digit is stored at index zero.
291 std::array<uint32_t, 2> multiplicand = {uint32_t(seconds),
292 uint32_t(seconds >> 32)};
293 std::array<uint32_t, 3> accumulator = {nanoseconds, 0, 0};
295 // This code follows the implementation of |BigInt::multiplyAccumulate()|.
297 uint32_t carry = 0;
299 uint32_t high = 0;
300 uint32_t low = digitMul(secToNanos, multiplicand[0], &high);
302 uint32_t newCarry = 0;
303 accumulator[0] = digitAdd(accumulator[0], low, &newCarry);
304 accumulator[1] = digitAdd(high, newCarry, &carry);
307 uint32_t high = 0;
308 uint32_t low = digitMul(secToNanos, multiplicand[1], &high);
310 uint32_t newCarry = 0;
311 accumulator[1] = digitAdd(accumulator[1], low, &carry);
312 accumulator[2] = digitAdd(high, carry, &newCarry);
313 MOZ_ASSERT(newCarry == 0);
316 return accumulator;
319 template <typename T>
320 static BigInt* ToBigInt(JSContext* cx,
321 const SecondsAndNanoseconds<T>& secondsAndNanoseconds) {
322 uint64_t seconds = std::abs(secondsAndNanoseconds.seconds);
323 uint32_t nanoseconds = secondsAndNanoseconds.nanoseconds;
325 // Negative nanoseconds are represented as the difference to 1'000'000'000.
326 // Convert these back to their absolute value and adjust the seconds part
327 // accordingly.
329 // For example the nanoseconds from the epoch value |-1n| is represented as
330 // the instant {seconds: -1, nanoseconds: 999'999'999}.
331 if (secondsAndNanoseconds.seconds < 0 && nanoseconds != 0) {
332 nanoseconds = ToNanoseconds(TemporalUnit::Second) - nanoseconds;
333 seconds -= 1;
336 auto digits = ToBigIntDigits(seconds, nanoseconds);
337 return CreateBigInt(cx, digits, secondsAndNanoseconds.seconds < 0);
340 BigInt* js::temporal::ToEpochNanoseconds(JSContext* cx,
341 const Instant& instant) {
342 MOZ_ASSERT(IsValidEpochInstant(instant));
343 return ::ToBigInt(cx, instant);
347 * GetUTCEpochNanoseconds ( year, month, day, hour, minute, second, millisecond,
348 * microsecond, nanosecond [ , offsetNanoseconds ] )
350 Instant js::temporal::GetUTCEpochNanoseconds(const PlainDateTime& dateTime) {
351 auto& [date, time] = dateTime;
353 // Step 1.
354 MOZ_ASSERT(IsValidISODateTime(dateTime));
356 // Additionally ensure the date-time value can be represented as an Instant.
357 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
359 // Steps 2-5.
360 int64_t ms = MakeDate(dateTime);
362 // Propagate the input range to the compiler.
363 int32_t nanos =
364 std::clamp(time.microsecond * 1'000 + time.nanosecond, 0, 999'999);
366 // Steps 6-8.
367 return Instant::fromMilliseconds(ms) + InstantSpan{0, nanos};
371 * GetUTCEpochNanoseconds ( year, month, day, hour, minute, second, millisecond,
372 * microsecond, nanosecond [ , offsetNanoseconds ] )
374 Instant js::temporal::GetUTCEpochNanoseconds(
375 const PlainDateTime& dateTime, const InstantSpan& offsetNanoseconds) {
376 MOZ_ASSERT(offsetNanoseconds.abs() <
377 InstantSpan::fromNanoseconds(ToNanoseconds(TemporalUnit::Day)));
379 // Steps 1-6.
380 auto epochNanoseconds = GetUTCEpochNanoseconds(dateTime);
382 // Steps 7-9.
383 return epochNanoseconds - offsetNanoseconds;
387 * CompareEpochNanoseconds ( epochNanosecondsOne, epochNanosecondsTwo )
389 static int32_t CompareEpochNanoseconds(const Instant& epochNanosecondsOne,
390 const Instant& epochNanosecondsTwo) {
391 // Step 1.
392 if (epochNanosecondsOne > epochNanosecondsTwo) {
393 return 1;
396 // Step 2.
397 if (epochNanosecondsOne < epochNanosecondsTwo) {
398 return -1;
401 // Step 3.
402 return 0;
406 * CreateTemporalInstant ( epochNanoseconds [ , newTarget ] )
408 InstantObject* js::temporal::CreateTemporalInstant(JSContext* cx,
409 const Instant& instant) {
410 // Step 1.
411 MOZ_ASSERT(IsValidEpochInstant(instant));
413 // Steps 2-3.
414 auto* object = NewBuiltinClassInstance<InstantObject>(cx);
415 if (!object) {
416 return nullptr;
419 // Step 4.
420 object->setFixedSlot(InstantObject::SECONDS_SLOT,
421 NumberValue(instant.seconds));
422 object->setFixedSlot(InstantObject::NANOSECONDS_SLOT,
423 Int32Value(instant.nanoseconds));
425 // Step 5.
426 return object;
430 * CreateTemporalInstant ( epochNanoseconds [ , newTarget ] )
432 static InstantObject* CreateTemporalInstant(JSContext* cx, const CallArgs& args,
433 Handle<BigInt*> epochNanoseconds) {
434 // Step 1.
435 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
437 // Steps 2-3.
438 Rooted<JSObject*> proto(cx);
439 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Instant, &proto)) {
440 return nullptr;
443 auto* object = NewObjectWithClassProto<InstantObject>(cx, proto);
444 if (!object) {
445 return nullptr;
448 // Step 4.
449 auto instant = ToInstant(epochNanoseconds);
450 object->setFixedSlot(InstantObject::SECONDS_SLOT,
451 NumberValue(instant.seconds));
452 object->setFixedSlot(InstantObject::NANOSECONDS_SLOT,
453 Int32Value(instant.nanoseconds));
455 // Step 5.
456 return object;
460 * ToTemporalInstant ( item )
462 Wrapped<InstantObject*> js::temporal::ToTemporalInstant(JSContext* cx,
463 Handle<Value> item) {
464 // Step 1.
465 if (item.isObject()) {
466 JSObject* itemObj = &item.toObject();
468 // Step 1.a.
469 if (itemObj->canUnwrapAs<InstantObject>()) {
470 return itemObj;
474 // Steps 1.b-d and 3-6
475 Instant epochNanoseconds;
476 if (!ToTemporalInstant(cx, item, &epochNanoseconds)) {
477 return nullptr;
480 // Step 7.
481 return CreateTemporalInstant(cx, epochNanoseconds);
485 * ToTemporalInstant ( item )
487 bool js::temporal::ToTemporalInstant(JSContext* cx, Handle<Value> item,
488 Instant* result) {
489 // Step 1.
490 Rooted<Value> primitiveValue(cx, item);
491 if (item.isObject()) {
492 JSObject* itemObj = &item.toObject();
494 // Step 1.a.
495 if (auto* instant = itemObj->maybeUnwrapIf<InstantObject>()) {
496 *result = ToInstant(instant);
497 return true;
500 // Step 1.b.
501 if (auto* zonedDateTime = itemObj->maybeUnwrapIf<ZonedDateTimeObject>()) {
502 *result = ToInstant(zonedDateTime);
503 return true;
506 // Steps 1.c-d.
507 if (!ToPrimitive(cx, JSTYPE_STRING, &primitiveValue)) {
508 return false;
512 // Step 2.
513 if (!primitiveValue.isString()) {
514 // The value is always on the stack, so JSDVG_SEARCH_STACK can be used for
515 // better error reporting.
516 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK,
517 primitiveValue, nullptr, "not a string");
518 return false;
520 Rooted<JSString*> string(cx, primitiveValue.toString());
522 // Steps 3-4.
523 PlainDateTime dateTime;
524 int64_t offset;
525 if (!ParseTemporalInstantString(cx, string, &dateTime, &offset)) {
526 return false;
528 MOZ_ASSERT(std::abs(offset) < ToNanoseconds(TemporalUnit::Day));
530 // Step 6. (Reordered)
531 if (!ISODateTimeWithinLimits(dateTime)) {
532 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
533 JSMSG_TEMPORAL_INSTANT_INVALID);
534 return false;
537 // Step 5.
538 auto epochNanoseconds =
539 GetUTCEpochNanoseconds(dateTime, InstantSpan::fromNanoseconds(offset));
541 // Step 6.
542 if (!IsValidEpochInstant(epochNanoseconds)) {
543 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
544 JSMSG_TEMPORAL_INSTANT_INVALID);
545 return false;
548 // Step 7.
549 *result = epochNanoseconds;
550 return true;
554 * AddNormalizedTimeDurationToEpochNanoseconds ( d, epochNs )
556 Instant js::temporal::AddNormalizedTimeDurationToEpochNanoseconds(
557 const NormalizedTimeDuration& d, const Instant& epochNs) {
558 MOZ_ASSERT(IsValidNormalizedTimeDuration(d));
559 MOZ_ASSERT(IsValidEpochInstant(epochNs));
561 // Step 1.
562 return epochNs + d.to<InstantSpan>();
566 * AddInstant ( epochNanoseconds, hours, minutes, seconds, milliseconds,
567 * microseconds, nanoseconds )
569 bool js::temporal::AddInstant(JSContext* cx, const Instant& instant,
570 const NormalizedTimeDuration& duration,
571 Instant* result) {
572 MOZ_ASSERT(IsValidEpochInstant(instant));
573 MOZ_ASSERT(IsValidNormalizedTimeDuration(duration));
575 // Step 1.
576 auto r = AddNormalizedTimeDurationToEpochNanoseconds(duration, instant);
578 // Step 2.
579 if (!IsValidEpochInstant(r)) {
580 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
581 JSMSG_TEMPORAL_INSTANT_INVALID);
582 return false;
585 // Step 3.
586 *result = r;
587 return true;
591 * DifferenceInstant ( ns1, ns2, roundingIncrement, smallestUnit, roundingMode )
593 NormalizedTimeDuration js::temporal::DifferenceInstant(
594 const Instant& ns1, const Instant& ns2, Increment roundingIncrement,
595 TemporalUnit smallestUnit, TemporalRoundingMode roundingMode) {
596 MOZ_ASSERT(IsValidEpochInstant(ns1));
597 MOZ_ASSERT(IsValidEpochInstant(ns2));
598 MOZ_ASSERT(smallestUnit > TemporalUnit::Day);
599 MOZ_ASSERT(roundingIncrement <=
600 MaximumTemporalDurationRoundingIncrement(smallestUnit));
602 // Step 1.
603 auto diff = NormalizedTimeDurationFromEpochNanosecondsDifference(ns2, ns1);
604 MOZ_ASSERT(IsValidInstantSpan(diff.to<InstantSpan>()));
606 // Step 2.
607 if (smallestUnit == TemporalUnit::Nanosecond &&
608 roundingIncrement == Increment{1}) {
609 return diff;
612 // Steps 3-4.
613 return RoundDuration(diff, roundingIncrement, smallestUnit, roundingMode);
617 * RoundNumberToIncrementAsIfPositive ( x, increment, roundingMode )
619 static Instant RoundNumberToIncrementAsIfPositive(
620 const Instant& x, int64_t increment, TemporalRoundingMode roundingMode) {
621 MOZ_ASSERT(IsValidEpochInstant(x));
622 MOZ_ASSERT(increment > 0);
623 MOZ_ASSERT(increment <= ToNanoseconds(TemporalUnit::Day));
625 // This operation is equivalent to adjusting the rounding mode through
626 // |ToPositiveRoundingMode| and then calling |RoundNumberToIncrement|.
627 auto rounded =
628 RoundNumberToIncrement(x.toTotalNanoseconds(), 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 auto* result = CreateTemporalInstant(cx, Instant::fromSeconds(epochSeconds));
869 if (!result) {
870 return false;
872 args.rval().setObject(*result);
873 return true;
877 * Temporal.Instant.fromEpochMilliseconds ( epochMilliseconds )
879 static bool Instant_fromEpochMilliseconds(JSContext* cx, unsigned argc,
880 Value* vp) {
881 CallArgs args = CallArgsFromVp(argc, vp);
883 // Step 1.
884 double epochMilliseconds;
885 if (!JS::ToNumber(cx, args.get(0), &epochMilliseconds)) {
886 return false;
889 // Step 2.
891 // NumberToBigInt throws a RangeError for non-integral numbers.
892 if (!IsInteger(epochMilliseconds)) {
893 ToCStringBuf cbuf;
894 const char* str = NumberToCString(&cbuf, epochMilliseconds);
896 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
897 JSMSG_TEMPORAL_INSTANT_NONINTEGER, str);
898 return false;
901 // Step 3. (Not applicable)
903 // Step 4.
904 if (!IsValidEpochMilliseconds(epochMilliseconds)) {
905 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
906 JSMSG_TEMPORAL_INSTANT_INVALID);
907 return false;
910 // Step 5.
911 auto* result =
912 CreateTemporalInstant(cx, Instant::fromMilliseconds(epochMilliseconds));
913 if (!result) {
914 return false;
916 args.rval().setObject(*result);
917 return true;
921 * Temporal.Instant.fromEpochMicroseconds ( epochMicroseconds )
923 static bool Instant_fromEpochMicroseconds(JSContext* cx, unsigned argc,
924 Value* vp) {
925 CallArgs args = CallArgsFromVp(argc, vp);
927 // Step 1.
928 Rooted<BigInt*> epochMicroseconds(cx, js::ToBigInt(cx, args.get(0)));
929 if (!epochMicroseconds) {
930 return false;
933 // Step 2. (Not applicable)
935 // Step 3.
936 if (!IsValidEpochMicroseconds(epochMicroseconds)) {
937 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
938 JSMSG_TEMPORAL_INSTANT_INVALID);
939 return false;
942 int64_t i;
943 MOZ_ALWAYS_TRUE(BigInt::isInt64(epochMicroseconds, &i));
945 // Step 4.
946 auto* result = CreateTemporalInstant(cx, Instant::fromMicroseconds(i));
947 if (!result) {
948 return false;
950 args.rval().setObject(*result);
951 return true;
955 * Temporal.Instant.fromEpochNanoseconds ( epochNanoseconds )
957 static bool Instant_fromEpochNanoseconds(JSContext* cx, unsigned argc,
958 Value* vp) {
959 CallArgs args = CallArgsFromVp(argc, vp);
961 // Step 1.
962 Rooted<BigInt*> epochNanoseconds(cx, js::ToBigInt(cx, args.get(0)));
963 if (!epochNanoseconds) {
964 return false;
967 // Step 2.
968 if (!IsValidEpochNanoseconds(epochNanoseconds)) {
969 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
970 JSMSG_TEMPORAL_INSTANT_INVALID);
971 return false;
974 // Step 3.
975 auto* result = CreateTemporalInstant(cx, ToInstant(epochNanoseconds));
976 if (!result) {
977 return false;
979 args.rval().setObject(*result);
980 return true;
984 * Temporal.Instant.compare ( one, two )
986 static bool Instant_compare(JSContext* cx, unsigned argc, Value* vp) {
987 CallArgs args = CallArgsFromVp(argc, vp);
989 // Step 1.
990 Instant one;
991 if (!ToTemporalInstant(cx, args.get(0), &one)) {
992 return false;
995 // Step 2.
996 Instant two;
997 if (!ToTemporalInstant(cx, args.get(1), &two)) {
998 return false;
1001 // Step 3.
1002 args.rval().setInt32(CompareEpochNanoseconds(one, two));
1003 return true;
1007 * get Temporal.Instant.prototype.epochSeconds
1009 static bool Instant_epochSeconds(JSContext* cx, const CallArgs& args) {
1010 // Step 3.
1011 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1013 // Steps 4-5.
1014 args.rval().setNumber(instant.seconds);
1015 return true;
1019 * get Temporal.Instant.prototype.epochSeconds
1021 static bool Instant_epochSeconds(JSContext* cx, unsigned argc, Value* vp) {
1022 // Steps 1-2.
1023 CallArgs args = CallArgsFromVp(argc, vp);
1024 return CallNonGenericMethod<IsInstant, Instant_epochSeconds>(cx, args);
1028 * get Temporal.Instant.prototype.epochMilliseconds
1030 static bool Instant_epochMilliseconds(JSContext* cx, const CallArgs& args) {
1031 // Step 3.
1032 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1034 // Step 4-5.
1035 args.rval().setNumber(instant.floorToMilliseconds());
1036 return true;
1040 * get Temporal.Instant.prototype.epochMilliseconds
1042 static bool Instant_epochMilliseconds(JSContext* cx, unsigned argc, Value* vp) {
1043 // Steps 1-2.
1044 CallArgs args = CallArgsFromVp(argc, vp);
1045 return CallNonGenericMethod<IsInstant, Instant_epochMilliseconds>(cx, args);
1049 * get Temporal.Instant.prototype.epochMicroseconds
1051 static bool Instant_epochMicroseconds(JSContext* cx, const CallArgs& args) {
1052 // Step 3.
1053 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1055 // Step 4.
1056 auto* microseconds =
1057 BigInt::createFromInt64(cx, instant.floorToMicroseconds());
1058 if (!microseconds) {
1059 return false;
1062 // Step 5.
1063 args.rval().setBigInt(microseconds);
1064 return true;
1068 * get Temporal.Instant.prototype.epochMicroseconds
1070 static bool Instant_epochMicroseconds(JSContext* cx, unsigned argc, Value* vp) {
1071 // Steps 1-2.
1072 CallArgs args = CallArgsFromVp(argc, vp);
1073 return CallNonGenericMethod<IsInstant, Instant_epochMicroseconds>(cx, args);
1077 * get Temporal.Instant.prototype.epochNanoseconds
1079 static bool Instant_epochNanoseconds(JSContext* cx, const CallArgs& args) {
1080 // Step 3.
1081 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1082 auto* nanoseconds = ToEpochNanoseconds(cx, instant);
1083 if (!nanoseconds) {
1084 return false;
1087 // Step 4.
1088 args.rval().setBigInt(nanoseconds);
1089 return true;
1093 * get Temporal.Instant.prototype.epochNanoseconds
1095 static bool Instant_epochNanoseconds(JSContext* cx, unsigned argc, Value* vp) {
1096 // Steps 1-2.
1097 CallArgs args = CallArgsFromVp(argc, vp);
1098 return CallNonGenericMethod<IsInstant, Instant_epochNanoseconds>(cx, args);
1102 * Temporal.Instant.prototype.add ( temporalDurationLike )
1104 static bool Instant_add(JSContext* cx, const CallArgs& args) {
1105 return AddDurationToOrSubtractDurationFromInstant(cx, InstantDuration::Add,
1106 args);
1110 * Temporal.Instant.prototype.add ( temporalDurationLike )
1112 static bool Instant_add(JSContext* cx, unsigned argc, Value* vp) {
1113 // Steps 1-2.
1114 CallArgs args = CallArgsFromVp(argc, vp);
1115 return CallNonGenericMethod<IsInstant, Instant_add>(cx, args);
1119 * Temporal.Instant.prototype.subtract ( temporalDurationLike )
1121 static bool Instant_subtract(JSContext* cx, const CallArgs& args) {
1122 return AddDurationToOrSubtractDurationFromInstant(
1123 cx, InstantDuration::Subtract, args);
1127 * Temporal.Instant.prototype.subtract ( temporalDurationLike )
1129 static bool Instant_subtract(JSContext* cx, unsigned argc, Value* vp) {
1130 // Steps 1-2.
1131 CallArgs args = CallArgsFromVp(argc, vp);
1132 return CallNonGenericMethod<IsInstant, Instant_subtract>(cx, args);
1136 * Temporal.Instant.prototype.until ( other [ , options ] )
1138 static bool Instant_until(JSContext* cx, const CallArgs& args) {
1139 return DifferenceTemporalInstant(cx, TemporalDifference::Until, args);
1143 * Temporal.Instant.prototype.until ( other [ , options ] )
1145 static bool Instant_until(JSContext* cx, unsigned argc, Value* vp) {
1146 // Steps 1-2.
1147 CallArgs args = CallArgsFromVp(argc, vp);
1148 return CallNonGenericMethod<IsInstant, Instant_until>(cx, args);
1152 * Temporal.Instant.prototype.since ( other [ , options ] )
1154 static bool Instant_since(JSContext* cx, const CallArgs& args) {
1155 return DifferenceTemporalInstant(cx, TemporalDifference::Since, args);
1159 * Temporal.Instant.prototype.since ( other [ , options ] )
1161 static bool Instant_since(JSContext* cx, unsigned argc, Value* vp) {
1162 // Steps 1-2.
1163 CallArgs args = CallArgsFromVp(argc, vp);
1164 return CallNonGenericMethod<IsInstant, Instant_since>(cx, args);
1168 * Temporal.Instant.prototype.round ( roundTo )
1170 static bool Instant_round(JSContext* cx, const CallArgs& args) {
1171 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1173 // Steps 3-16.
1174 auto smallestUnit = TemporalUnit::Auto;
1175 auto roundingMode = TemporalRoundingMode::HalfExpand;
1176 auto roundingIncrement = Increment{1};
1177 if (args.get(0).isString()) {
1178 // Steps 4 and 6-8. (Not applicable in our implementation.)
1180 // Step 9.
1181 Rooted<JSString*> paramString(cx, args[0].toString());
1182 if (!GetTemporalUnit(cx, paramString, TemporalUnitKey::SmallestUnit,
1183 TemporalUnitGroup::Time, &smallestUnit)) {
1184 return false;
1187 // Steps 10-16. (Not applicable in our implementation.)
1188 } else {
1189 // Steps 3 and 5.
1190 Rooted<JSObject*> options(
1191 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
1192 if (!options) {
1193 return false;
1196 // Steps 6-7.
1197 if (!ToTemporalRoundingIncrement(cx, options, &roundingIncrement)) {
1198 return false;
1201 // Step 8.
1202 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
1203 return false;
1206 // Step 9.
1207 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
1208 TemporalUnitGroup::Time, &smallestUnit)) {
1209 return false;
1211 if (smallestUnit == TemporalUnit::Auto) {
1212 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1213 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
1214 return false;
1217 // Steps 10-15.
1218 uint64_t maximum = UnitsPerDay(smallestUnit);
1220 // Step 16.
1221 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
1222 true)) {
1223 return false;
1227 // Step 17.
1228 auto roundedNs = RoundTemporalInstant(instant, roundingIncrement,
1229 smallestUnit, roundingMode);
1231 // Step 18.
1232 auto* result = CreateTemporalInstant(cx, roundedNs);
1233 if (!result) {
1234 return false;
1236 args.rval().setObject(*result);
1237 return true;
1241 * Temporal.Instant.prototype.round ( options )
1243 static bool Instant_round(JSContext* cx, unsigned argc, Value* vp) {
1244 // Steps 1-2.
1245 CallArgs args = CallArgsFromVp(argc, vp);
1246 return CallNonGenericMethod<IsInstant, Instant_round>(cx, args);
1250 * Temporal.Instant.prototype.equals ( other )
1252 static bool Instant_equals(JSContext* cx, const CallArgs& args) {
1253 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1255 // Step 3.
1256 Instant other;
1257 if (!ToTemporalInstant(cx, args.get(0), &other)) {
1258 return false;
1261 // Steps 4-5.
1262 args.rval().setBoolean(instant == other);
1263 return true;
1267 * Temporal.Instant.prototype.equals ( other )
1269 static bool Instant_equals(JSContext* cx, unsigned argc, Value* vp) {
1270 // Steps 1-2.
1271 CallArgs args = CallArgsFromVp(argc, vp);
1272 return CallNonGenericMethod<IsInstant, Instant_equals>(cx, args);
1276 * Temporal.Instant.prototype.toString ( [ options ] )
1278 static bool Instant_toString(JSContext* cx, const CallArgs& args) {
1279 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1281 Rooted<TimeZoneValue> timeZone(cx);
1282 auto roundingMode = TemporalRoundingMode::Trunc;
1283 SecondsStringPrecision precision = {Precision::Auto(),
1284 TemporalUnit::Nanosecond, Increment{1}};
1285 if (args.hasDefined(0)) {
1286 // Step 3.
1287 Rooted<JSObject*> options(
1288 cx, RequireObjectArg(cx, "options", "toString", args[0]));
1289 if (!options) {
1290 return false;
1293 // Steps 4-5.
1294 auto digits = Precision::Auto();
1295 if (!ToFractionalSecondDigits(cx, options, &digits)) {
1296 return false;
1299 // Step 6.
1300 if (!ToTemporalRoundingMode(cx, options, &roundingMode)) {
1301 return false;
1304 // Step 7.
1305 auto smallestUnit = TemporalUnit::Auto;
1306 if (!GetTemporalUnit(cx, options, TemporalUnitKey::SmallestUnit,
1307 TemporalUnitGroup::Time, &smallestUnit)) {
1308 return false;
1311 // Step 8.
1312 if (smallestUnit == TemporalUnit::Hour) {
1313 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1314 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
1315 "smallestUnit");
1316 return false;
1319 // Step 9.
1320 Rooted<Value> value(cx);
1321 if (!GetProperty(cx, options, options, cx->names().timeZone, &value)) {
1322 return false;
1325 // Step 10.
1326 if (!value.isUndefined()) {
1327 if (!ToTemporalTimeZone(cx, value, &timeZone)) {
1328 return false;
1332 // Step 11.
1333 precision = ToSecondsStringPrecision(smallestUnit, digits);
1336 // Step 12.
1337 auto ns = RoundTemporalInstant(instant, precision.increment, precision.unit,
1338 roundingMode);
1340 // Step 13.
1341 Rooted<InstantObject*> roundedInstant(cx, CreateTemporalInstant(cx, ns));
1342 if (!roundedInstant) {
1343 return false;
1346 // Step 14.
1347 JSString* str = TemporalInstantToString(cx, roundedInstant, timeZone,
1348 precision.precision);
1349 if (!str) {
1350 return false;
1353 args.rval().setString(str);
1354 return true;
1358 * Temporal.Instant.prototype.toString ( [ options ] )
1360 static bool Instant_toString(JSContext* cx, unsigned argc, Value* vp) {
1361 // Steps 1-2.
1362 CallArgs args = CallArgsFromVp(argc, vp);
1363 return CallNonGenericMethod<IsInstant, Instant_toString>(cx, args);
1367 * Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] )
1369 static bool Instant_toLocaleString(JSContext* cx, const CallArgs& args) {
1370 Rooted<InstantObject*> instant(cx,
1371 &args.thisv().toObject().as<InstantObject>());
1373 // Step 3.
1374 Rooted<TimeZoneValue> timeZone(cx);
1375 JSString* str =
1376 TemporalInstantToString(cx, instant, timeZone, Precision::Auto());
1377 if (!str) {
1378 return false;
1381 args.rval().setString(str);
1382 return true;
1386 * Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] )
1388 static bool Instant_toLocaleString(JSContext* cx, unsigned argc, Value* vp) {
1389 // Steps 1-2.
1390 CallArgs args = CallArgsFromVp(argc, vp);
1391 return CallNonGenericMethod<IsInstant, Instant_toLocaleString>(cx, args);
1395 * Temporal.Instant.prototype.toJSON ( )
1397 static bool Instant_toJSON(JSContext* cx, const CallArgs& args) {
1398 Rooted<InstantObject*> instant(cx,
1399 &args.thisv().toObject().as<InstantObject>());
1401 // Step 3.
1402 Rooted<TimeZoneValue> timeZone(cx);
1403 JSString* str =
1404 TemporalInstantToString(cx, instant, timeZone, Precision::Auto());
1405 if (!str) {
1406 return false;
1409 args.rval().setString(str);
1410 return true;
1414 * Temporal.Instant.prototype.toJSON ( )
1416 static bool Instant_toJSON(JSContext* cx, unsigned argc, Value* vp) {
1417 // Steps 1-2.
1418 CallArgs args = CallArgsFromVp(argc, vp);
1419 return CallNonGenericMethod<IsInstant, Instant_toJSON>(cx, args);
1423 * Temporal.Instant.prototype.valueOf ( )
1425 static bool Instant_valueOf(JSContext* cx, unsigned argc, Value* vp) {
1426 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
1427 "Instant", "primitive type");
1428 return false;
1432 * Temporal.Instant.prototype.toZonedDateTime ( item )
1434 static bool Instant_toZonedDateTime(JSContext* cx, const CallArgs& args) {
1435 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1437 // Step 3.
1438 Rooted<JSObject*> item(
1439 cx, RequireObjectArg(cx, "item", "toZonedDateTime", args.get(0)));
1440 if (!item) {
1441 return false;
1444 // Step 4.
1445 Rooted<Value> calendarLike(cx);
1446 if (!GetProperty(cx, item, item, cx->names().calendar, &calendarLike)) {
1447 return false;
1450 // Step 5.
1451 if (calendarLike.isUndefined()) {
1452 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1453 JSMSG_TEMPORAL_MISSING_PROPERTY, "calendar");
1454 return false;
1457 // Step 6.
1458 Rooted<CalendarValue> calendar(cx);
1459 if (!ToTemporalCalendar(cx, calendarLike, &calendar)) {
1460 return false;
1463 // Step 7.
1464 Rooted<Value> timeZoneLike(cx);
1465 if (!GetProperty(cx, item, item, cx->names().timeZone, &timeZoneLike)) {
1466 return false;
1469 // Step 8.
1470 if (timeZoneLike.isUndefined()) {
1471 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1472 JSMSG_TEMPORAL_MISSING_PROPERTY, "timeZone");
1473 return false;
1476 // Step 9.
1477 Rooted<TimeZoneValue> timeZone(cx);
1478 if (!ToTemporalTimeZone(cx, timeZoneLike, &timeZone)) {
1479 return false;
1482 // Step 10.
1483 auto* result = CreateTemporalZonedDateTime(cx, instant, timeZone, calendar);
1484 if (!result) {
1485 return false;
1488 args.rval().setObject(*result);
1489 return true;
1493 * Temporal.Instant.prototype.toZonedDateTime ( item )
1495 static bool Instant_toZonedDateTime(JSContext* cx, unsigned argc, Value* vp) {
1496 // Steps 1-2.
1497 CallArgs args = CallArgsFromVp(argc, vp);
1498 return CallNonGenericMethod<IsInstant, Instant_toZonedDateTime>(cx, args);
1502 * Temporal.Instant.prototype.toZonedDateTimeISO ( item )
1504 static bool Instant_toZonedDateTimeISO(JSContext* cx, const CallArgs& args) {
1505 auto instant = ToInstant(&args.thisv().toObject().as<InstantObject>());
1507 // Step 3.
1508 Rooted<TimeZoneValue> timeZone(cx);
1509 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
1510 return false;
1513 // Step 4.
1514 Rooted<CalendarValue> calendar(cx, CalendarValue(cx->names().iso8601));
1515 auto* result = CreateTemporalZonedDateTime(cx, instant, timeZone, calendar);
1516 if (!result) {
1517 return false;
1520 args.rval().setObject(*result);
1521 return true;
1525 * Temporal.Instant.prototype.toZonedDateTimeISO ( item )
1527 static bool Instant_toZonedDateTimeISO(JSContext* cx, unsigned argc,
1528 Value* vp) {
1529 // Steps 1-2.
1530 CallArgs args = CallArgsFromVp(argc, vp);
1531 return CallNonGenericMethod<IsInstant, Instant_toZonedDateTimeISO>(cx, args);
1534 const JSClass InstantObject::class_ = {
1535 "Temporal.Instant",
1536 JSCLASS_HAS_RESERVED_SLOTS(InstantObject::SLOT_COUNT) |
1537 JSCLASS_HAS_CACHED_PROTO(JSProto_Instant),
1538 JS_NULL_CLASS_OPS,
1539 &InstantObject::classSpec_,
1542 const JSClass& InstantObject::protoClass_ = PlainObject::class_;
1544 static const JSFunctionSpec Instant_methods[] = {
1545 JS_FN("from", Instant_from, 1, 0),
1546 JS_FN("fromEpochSeconds", Instant_fromEpochSeconds, 1, 0),
1547 JS_FN("fromEpochMilliseconds", Instant_fromEpochMilliseconds, 1, 0),
1548 JS_FN("fromEpochMicroseconds", Instant_fromEpochMicroseconds, 1, 0),
1549 JS_FN("fromEpochNanoseconds", Instant_fromEpochNanoseconds, 1, 0),
1550 JS_FN("compare", Instant_compare, 2, 0),
1551 JS_FS_END,
1554 static const JSFunctionSpec Instant_prototype_methods[] = {
1555 JS_FN("add", Instant_add, 1, 0),
1556 JS_FN("subtract", Instant_subtract, 1, 0),
1557 JS_FN("until", Instant_until, 1, 0),
1558 JS_FN("since", Instant_since, 1, 0),
1559 JS_FN("round", Instant_round, 1, 0),
1560 JS_FN("equals", Instant_equals, 1, 0),
1561 JS_FN("toString", Instant_toString, 0, 0),
1562 JS_FN("toLocaleString", Instant_toLocaleString, 0, 0),
1563 JS_FN("toJSON", Instant_toJSON, 0, 0),
1564 JS_FN("valueOf", Instant_valueOf, 0, 0),
1565 JS_FN("toZonedDateTime", Instant_toZonedDateTime, 1, 0),
1566 JS_FN("toZonedDateTimeISO", Instant_toZonedDateTimeISO, 1, 0),
1567 JS_FS_END,
1570 static const JSPropertySpec Instant_prototype_properties[] = {
1571 JS_PSG("epochSeconds", Instant_epochSeconds, 0),
1572 JS_PSG("epochMilliseconds", Instant_epochMilliseconds, 0),
1573 JS_PSG("epochMicroseconds", Instant_epochMicroseconds, 0),
1574 JS_PSG("epochNanoseconds", Instant_epochNanoseconds, 0),
1575 JS_STRING_SYM_PS(toStringTag, "Temporal.Instant", JSPROP_READONLY),
1576 JS_PS_END,
1579 const ClassSpec InstantObject::classSpec_ = {
1580 GenericCreateConstructor<InstantConstructor, 1, gc::AllocKind::FUNCTION>,
1581 GenericCreatePrototype<InstantObject>,
1582 Instant_methods,
1583 nullptr,
1584 Instant_prototype_methods,
1585 Instant_prototype_properties,
1586 nullptr,
1587 ClassSpec::DontDefineConstructor,