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/PlainTime.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/FloatingPoint.h"
11 #include "mozilla/Maybe.h"
16 #include <type_traits>
21 #include "NamespaceImports.h"
23 #include "builtin/temporal/Duration.h"
24 #include "builtin/temporal/Instant.h"
25 #include "builtin/temporal/PlainDate.h"
26 #include "builtin/temporal/PlainDateTime.h"
27 #include "builtin/temporal/Temporal.h"
28 #include "builtin/temporal/TemporalParser.h"
29 #include "builtin/temporal/TemporalRoundingMode.h"
30 #include "builtin/temporal/TemporalTypes.h"
31 #include "builtin/temporal/TemporalUnit.h"
32 #include "builtin/temporal/TimeZone.h"
33 #include "builtin/temporal/ToString.h"
34 #include "builtin/temporal/ZonedDateTime.h"
35 #include "ds/IdValuePair.h"
36 #include "gc/AllocKind.h"
37 #include "gc/Barrier.h"
38 #include "js/AllocPolicy.h"
39 #include "js/CallArgs.h"
40 #include "js/CallNonGenericMethod.h"
42 #include "js/ErrorReport.h"
43 #include "js/friend/ErrorMessages.h"
44 #include "js/PropertyDescriptor.h"
45 #include "js/PropertySpec.h"
46 #include "js/RootingAPI.h"
48 #include "vm/BytecodeUtil.h"
49 #include "vm/GlobalObject.h"
50 #include "vm/JSAtomState.h"
51 #include "vm/JSContext.h"
52 #include "vm/JSObject.h"
53 #include "vm/PlainObject.h"
54 #include "vm/StringType.h"
56 #include "vm/JSObject-inl.h"
57 #include "vm/NativeObject-inl.h"
58 #include "vm/ObjectOperations-inl.h"
61 using namespace js::temporal
;
63 static inline bool IsPlainTime(Handle
<Value
> v
) {
64 return v
.isObject() && v
.toObject().is
<PlainTimeObject
>();
69 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
72 static bool IsValidTime(T hour
, T minute
, T second
, T millisecond
,
73 T microsecond
, T nanosecond
) {
74 static_assert(std::is_same_v
<T
, int32_t> || std::is_same_v
<T
, double>);
77 MOZ_ASSERT(IsInteger(hour
));
78 MOZ_ASSERT(IsInteger(minute
));
79 MOZ_ASSERT(IsInteger(second
));
80 MOZ_ASSERT(IsInteger(millisecond
));
81 MOZ_ASSERT(IsInteger(microsecond
));
82 MOZ_ASSERT(IsInteger(nanosecond
));
85 if (hour
< 0 || hour
> 23) {
90 if (minute
< 0 || minute
> 59) {
95 if (second
< 0 || second
> 59) {
100 if (millisecond
< 0 || millisecond
> 999) {
105 if (microsecond
< 0 || microsecond
> 999) {
110 if (nanosecond
< 0 || nanosecond
> 999) {
119 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
121 bool js::temporal::IsValidTime(const PlainTime
& time
) {
122 const auto& [hour
, minute
, second
, millisecond
, microsecond
, nanosecond
] =
124 return ::IsValidTime(hour
, minute
, second
, millisecond
, microsecond
,
129 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
131 bool js::temporal::IsValidTime(double hour
, double minute
, double second
,
132 double millisecond
, double microsecond
,
134 return ::IsValidTime(hour
, minute
, second
, millisecond
, microsecond
,
139 static void ReportInvalidTimeValue(JSContext
* cx
, const char* name
, int32_t min
,
140 int32_t max
, double num
) {
141 Int32ToCStringBuf minCbuf
;
142 const char* minStr
= Int32ToCString(&minCbuf
, min
);
144 Int32ToCStringBuf maxCbuf
;
145 const char* maxStr
= Int32ToCString(&maxCbuf
, max
);
147 ToCStringBuf numCbuf
;
148 const char* numStr
= NumberToCString(&numCbuf
, num
);
150 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
151 JSMSG_TEMPORAL_PLAIN_TIME_INVALID_VALUE
, name
,
152 minStr
, maxStr
, numStr
);
155 template <typename T
>
156 static inline bool ThrowIfInvalidTimeValue(JSContext
* cx
, const char* name
,
157 int32_t min
, int32_t max
, T num
) {
158 if (min
<= num
&& num
<= max
) {
161 ReportInvalidTimeValue(cx
, name
, min
, max
, num
);
166 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
168 template <typename T
>
169 static bool ThrowIfInvalidTime(JSContext
* cx
, T hour
, T minute
, T second
,
170 T millisecond
, T microsecond
, T nanosecond
) {
171 static_assert(std::is_same_v
<T
, int32_t> || std::is_same_v
<T
, double>);
174 MOZ_ASSERT(IsInteger(hour
));
175 MOZ_ASSERT(IsInteger(minute
));
176 MOZ_ASSERT(IsInteger(second
));
177 MOZ_ASSERT(IsInteger(millisecond
));
178 MOZ_ASSERT(IsInteger(microsecond
));
179 MOZ_ASSERT(IsInteger(nanosecond
));
182 if (!ThrowIfInvalidTimeValue(cx
, "hour", 0, 23, hour
)) {
187 if (!ThrowIfInvalidTimeValue(cx
, "minute", 0, 59, minute
)) {
192 if (!ThrowIfInvalidTimeValue(cx
, "second", 0, 59, second
)) {
197 if (!ThrowIfInvalidTimeValue(cx
, "millisecond", 0, 999, millisecond
)) {
202 if (!ThrowIfInvalidTimeValue(cx
, "microsecond", 0, 999, microsecond
)) {
207 if (!ThrowIfInvalidTimeValue(cx
, "nanosecond", 0, 999, nanosecond
)) {
216 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
218 bool js::temporal::ThrowIfInvalidTime(JSContext
* cx
, const PlainTime
& time
) {
219 const auto& [hour
, minute
, second
, millisecond
, microsecond
, nanosecond
] =
221 return ::ThrowIfInvalidTime(cx
, hour
, minute
, second
, millisecond
,
222 microsecond
, nanosecond
);
226 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
228 bool js::temporal::ThrowIfInvalidTime(JSContext
* cx
, double hour
, double minute
,
229 double second
, double millisecond
,
230 double microsecond
, double nanosecond
) {
231 return ::ThrowIfInvalidTime(cx
, hour
, minute
, second
, millisecond
,
232 microsecond
, nanosecond
);
236 * ConstrainTime ( hour, minute, second, millisecond, microsecond, nanosecond )
238 static PlainTime
ConstrainTime(double hour
, double minute
, double second
,
239 double millisecond
, double microsecond
,
242 MOZ_ASSERT(IsInteger(hour
));
243 MOZ_ASSERT(IsInteger(minute
));
244 MOZ_ASSERT(IsInteger(second
));
245 MOZ_ASSERT(IsInteger(millisecond
));
246 MOZ_ASSERT(IsInteger(microsecond
));
247 MOZ_ASSERT(IsInteger(nanosecond
));
251 int32_t(std::clamp(hour
, 0.0, 23.0)),
252 int32_t(std::clamp(minute
, 0.0, 59.0)),
253 int32_t(std::clamp(second
, 0.0, 59.0)),
254 int32_t(std::clamp(millisecond
, 0.0, 999.0)),
255 int32_t(std::clamp(microsecond
, 0.0, 999.0)),
256 int32_t(std::clamp(nanosecond
, 0.0, 999.0)),
261 * RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond,
264 bool js::temporal::RegulateTime(JSContext
* cx
, const TemporalTimeLike
& time
,
265 TemporalOverflow overflow
, PlainTime
* result
) {
266 const auto& [hour
, minute
, second
, millisecond
, microsecond
, nanosecond
] =
270 MOZ_ASSERT(IsInteger(hour
));
271 MOZ_ASSERT(IsInteger(minute
));
272 MOZ_ASSERT(IsInteger(second
));
273 MOZ_ASSERT(IsInteger(millisecond
));
274 MOZ_ASSERT(IsInteger(microsecond
));
275 MOZ_ASSERT(IsInteger(nanosecond
));
277 // Step 2. (Not applicable in our implementation.)
280 if (overflow
== TemporalOverflow::Constrain
) {
281 *result
= ConstrainTime(hour
, minute
, second
, millisecond
, microsecond
,
287 MOZ_ASSERT(overflow
== TemporalOverflow::Reject
);
290 if (!ThrowIfInvalidTime(cx
, hour
, minute
, second
, millisecond
, microsecond
,
297 int32_t(hour
), int32_t(minute
), int32_t(second
),
298 int32_t(millisecond
), int32_t(microsecond
), int32_t(nanosecond
),
304 * CreateTemporalTime ( hour, minute, second, millisecond, microsecond,
305 * nanosecond [ , newTarget ] )
307 static PlainTimeObject
* CreateTemporalTime(JSContext
* cx
, const CallArgs
& args
,
308 double hour
, double minute
,
309 double second
, double millisecond
,
312 MOZ_ASSERT(IsInteger(hour
));
313 MOZ_ASSERT(IsInteger(minute
));
314 MOZ_ASSERT(IsInteger(second
));
315 MOZ_ASSERT(IsInteger(millisecond
));
316 MOZ_ASSERT(IsInteger(microsecond
));
317 MOZ_ASSERT(IsInteger(nanosecond
));
320 if (!ThrowIfInvalidTime(cx
, hour
, minute
, second
, millisecond
, microsecond
,
326 Rooted
<JSObject
*> proto(cx
);
327 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_PlainTime
,
332 auto* object
= NewObjectWithClassProto
<PlainTimeObject
>(cx
, proto
);
338 object
->setFixedSlot(PlainTimeObject::ISO_HOUR_SLOT
,
339 Int32Value(int32_t(hour
)));
342 object
->setFixedSlot(PlainTimeObject::ISO_MINUTE_SLOT
,
343 Int32Value(int32_t(minute
)));
346 object
->setFixedSlot(PlainTimeObject::ISO_SECOND_SLOT
,
347 Int32Value(int32_t(second
)));
350 object
->setFixedSlot(PlainTimeObject::ISO_MILLISECOND_SLOT
,
351 Int32Value(int32_t(millisecond
)));
354 object
->setFixedSlot(PlainTimeObject::ISO_MICROSECOND_SLOT
,
355 Int32Value(int32_t(microsecond
)));
358 object
->setFixedSlot(PlainTimeObject::ISO_NANOSECOND_SLOT
,
359 Int32Value(int32_t(nanosecond
)));
366 * CreateTemporalTime ( hour, minute, second, millisecond, microsecond,
367 * nanosecond [ , newTarget ] )
369 PlainTimeObject
* js::temporal::CreateTemporalTime(JSContext
* cx
,
370 const PlainTime
& time
) {
371 const auto& [hour
, minute
, second
, millisecond
, microsecond
, nanosecond
] =
375 if (!ThrowIfInvalidTime(cx
, time
)) {
380 auto* object
= NewBuiltinClassInstance
<PlainTimeObject
>(cx
);
386 object
->setFixedSlot(PlainTimeObject::ISO_HOUR_SLOT
, Int32Value(hour
));
389 object
->setFixedSlot(PlainTimeObject::ISO_MINUTE_SLOT
, Int32Value(minute
));
392 object
->setFixedSlot(PlainTimeObject::ISO_SECOND_SLOT
, Int32Value(second
));
395 object
->setFixedSlot(PlainTimeObject::ISO_MILLISECOND_SLOT
,
396 Int32Value(millisecond
));
399 object
->setFixedSlot(PlainTimeObject::ISO_MICROSECOND_SLOT
,
400 Int32Value(microsecond
));
403 object
->setFixedSlot(PlainTimeObject::ISO_NANOSECOND_SLOT
,
404 Int32Value(nanosecond
));
411 * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond )
413 template <typename IntT
>
414 static BalancedTime
BalanceTime(IntT hour
, IntT minute
, IntT second
,
415 IntT millisecond
, IntT microsecond
,
417 // Combined floor'ed division and modulo operation.
418 auto divmod
= [](IntT dividend
, int32_t divisor
, int32_t* remainder
) {
419 MOZ_ASSERT(divisor
> 0);
421 IntT quotient
= dividend
/ divisor
;
422 *remainder
= dividend
% divisor
;
424 // The remainder is negative, add the divisor and simulate a floor instead
425 // of trunc division.
426 if (*remainder
< 0) {
427 *remainder
+= divisor
;
437 microsecond
+= divmod(nanosecond
, 1000, &time
.nanosecond
);
440 millisecond
+= divmod(microsecond
, 1000, &time
.microsecond
);
443 second
+= divmod(millisecond
, 1000, &time
.millisecond
);
446 minute
+= divmod(second
, 60, &time
.second
);
449 hour
+= divmod(minute
, 60, &time
.minute
);
452 int32_t days
= divmod(hour
, 24, &time
.hour
);
455 MOZ_ASSERT(IsValidTime(time
));
460 * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond )
462 static BalancedTime
BalanceTime(int32_t hour
, int32_t minute
, int32_t second
,
463 int32_t millisecond
, int32_t microsecond
,
464 int32_t nanosecond
) {
465 MOZ_ASSERT(-24 < hour
&& hour
< 2 * 24);
466 MOZ_ASSERT(-60 < minute
&& minute
< 2 * 60);
467 MOZ_ASSERT(-60 < second
&& second
< 2 * 60);
468 MOZ_ASSERT(-1000 < millisecond
&& millisecond
< 2 * 1000);
469 MOZ_ASSERT(-1000 < microsecond
&& microsecond
< 2 * 1000);
470 MOZ_ASSERT(-1000 < nanosecond
&& nanosecond
< 2 * 1000);
472 return BalanceTime
<int32_t>(hour
, minute
, second
, millisecond
, microsecond
,
477 * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond )
479 BalancedTime
js::temporal::BalanceTime(const PlainTime
& time
,
480 int64_t nanoseconds
) {
481 MOZ_ASSERT(IsValidTime(time
));
482 MOZ_ASSERT(std::abs(nanoseconds
) <= 2 * ToNanoseconds(TemporalUnit::Day
));
484 return ::BalanceTime
<int64_t>(time
.hour
, time
.minute
, time
.second
,
485 time
.millisecond
, time
.microsecond
,
486 time
.nanosecond
+ nanoseconds
);
490 * DifferenceTime ( h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, ns2 )
492 NormalizedTimeDuration
js::temporal::DifferenceTime(const PlainTime
& time1
,
493 const PlainTime
& time2
) {
494 MOZ_ASSERT(IsValidTime(time1
));
495 MOZ_ASSERT(IsValidTime(time2
));
498 int32_t hours
= time2
.hour
- time1
.hour
;
501 int32_t minutes
= time2
.minute
- time1
.minute
;
504 int32_t seconds
= time2
.second
- time1
.second
;
507 int32_t milliseconds
= time2
.millisecond
- time1
.millisecond
;
510 int32_t microseconds
= time2
.microsecond
- time1
.microsecond
;
513 int32_t nanoseconds
= time2
.nanosecond
- time1
.nanosecond
;
516 auto result
= NormalizeTimeDuration(hours
, minutes
, seconds
, milliseconds
,
517 microseconds
, nanoseconds
);
520 MOZ_ASSERT(result
.abs().toTotalNanoseconds() <
521 Int128
{ToNanoseconds(TemporalUnit::Day
)});
528 * ToTemporalTime ( item [ , overflow ] )
530 static bool ToTemporalTime(JSContext
* cx
, Handle
<Value
> item
,
531 TemporalOverflow overflow
, PlainTime
* result
) {
532 // Steps 1-2. (Not applicable in our implementation.)
535 if (item
.isObject()) {
537 Rooted
<JSObject
*> itemObj(cx
, &item
.toObject());
540 if (auto* time
= itemObj
->maybeUnwrapIf
<PlainTimeObject
>()) {
541 *result
= ToPlainTime(time
);
546 if (auto* zonedDateTime
= itemObj
->maybeUnwrapIf
<ZonedDateTimeObject
>()) {
547 auto epochInstant
= ToInstant(zonedDateTime
);
548 Rooted
<TimeZoneValue
> timeZone(cx
, zonedDateTime
->timeZone());
550 if (!timeZone
.wrap(cx
)) {
555 PlainDateTime dateTime
;
556 if (!GetPlainDateTimeFor(cx
, timeZone
, epochInstant
, &dateTime
)) {
561 *result
= dateTime
.time
;
566 if (auto* dateTime
= itemObj
->maybeUnwrapIf
<PlainDateTimeObject
>()) {
567 *result
= ToPlainTime(dateTime
);
572 TemporalTimeLike timeResult
;
573 if (!ToTemporalTimeRecord(cx
, itemObj
, &timeResult
)) {
578 if (!RegulateTime(cx
, timeResult
, overflow
, result
)) {
585 if (!item
.isString()) {
586 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, item
,
587 nullptr, "not a string");
590 Rooted
<JSString
*> string(cx
, item
.toString());
593 if (!ParseTemporalTimeString(cx
, string
, result
)) {
598 MOZ_ASSERT(IsValidTime(*result
));
606 * ToTemporalTime ( item [ , overflow ] )
608 static PlainTimeObject
* ToTemporalTime(JSContext
* cx
, Handle
<Value
> item
,
609 TemporalOverflow overflow
) {
611 if (!ToTemporalTime(cx
, item
, overflow
, &time
)) {
614 MOZ_ASSERT(IsValidTime(time
));
616 return CreateTemporalTime(cx
, time
);
620 * ToTemporalTime ( item [ , overflow ] )
622 bool js::temporal::ToTemporalTime(JSContext
* cx
, Handle
<Value
> item
,
624 return ToTemporalTime(cx
, item
, TemporalOverflow::Constrain
, result
);
628 * CompareTemporalTime ( h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2,
631 int32_t js::temporal::CompareTemporalTime(const PlainTime
& one
,
632 const PlainTime
& two
) {
634 if (int32_t diff
= one
.hour
- two
.hour
) {
635 return diff
< 0 ? -1 : 1;
639 if (int32_t diff
= one
.minute
- two
.minute
) {
640 return diff
< 0 ? -1 : 1;
644 if (int32_t diff
= one
.second
- two
.second
) {
645 return diff
< 0 ? -1 : 1;
649 if (int32_t diff
= one
.millisecond
- two
.millisecond
) {
650 return diff
< 0 ? -1 : 1;
654 if (int32_t diff
= one
.microsecond
- two
.microsecond
) {
655 return diff
< 0 ? -1 : 1;
659 if (int32_t diff
= one
.nanosecond
- two
.nanosecond
) {
660 return diff
< 0 ? -1 : 1;
668 * ToTemporalTimeRecord ( temporalTimeLike [ , completeness ] )
670 static bool ToTemporalTimeRecord(JSContext
* cx
,
671 Handle
<JSObject
*> temporalTimeLike
,
672 TemporalTimeLike
* result
) {
673 // Steps 1 and 3-4. (Not applicable in our implementation.)
675 // Step 2. (Inlined call to PrepareTemporalFields.)
676 // PrepareTemporalFields, step 1. (Not applicable in our implementation.)
678 // PrepareTemporalFields, step 2.
681 // PrepareTemporalFields, steps 3-4. (Loop unrolled)
682 Rooted
<Value
> value(cx
);
683 auto getTimeProperty
= [&](Handle
<PropertyName
*> property
, const char* name
,
686 if (!GetProperty(cx
, temporalTimeLike
, temporalTimeLike
, property
,
692 if (!value
.isUndefined()) {
697 if (!ToIntegerWithTruncation(cx
, value
, name
, num
)) {
704 if (!getTimeProperty(cx
->names().hour
, "hour", &result
->hour
)) {
707 if (!getTimeProperty(cx
->names().microsecond
, "microsecond",
708 &result
->microsecond
)) {
711 if (!getTimeProperty(cx
->names().millisecond
, "millisecond",
712 &result
->millisecond
)) {
715 if (!getTimeProperty(cx
->names().minute
, "minute", &result
->minute
)) {
718 if (!getTimeProperty(cx
->names().nanosecond
, "nanosecond",
719 &result
->nanosecond
)) {
722 if (!getTimeProperty(cx
->names().second
, "second", &result
->second
)) {
726 // PrepareTemporalFields, step 5.
728 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
729 JSMSG_TEMPORAL_PLAIN_TIME_MISSING_UNIT
);
733 // Steps 5-16. (Performed implicitly in our implementation.)
740 * ToTemporalTimeRecord ( temporalTimeLike [ , completeness ] )
742 bool js::temporal::ToTemporalTimeRecord(JSContext
* cx
,
743 Handle
<JSObject
*> temporalTimeLike
,
744 TemporalTimeLike
* result
) {
745 // Step 3.a. (Set all fields to zero.)
748 // Steps 1-2 and 4-17.
749 return ::ToTemporalTimeRecord(cx
, temporalTimeLike
, result
);
752 static int64_t TimeToNanos(const PlainTime
& time
) {
753 // No overflow possible because the input is a valid time.
754 MOZ_ASSERT(IsValidTime(time
));
756 int64_t hour
= time
.hour
;
757 int64_t minute
= time
.minute
;
758 int64_t second
= time
.second
;
759 int64_t millisecond
= time
.millisecond
;
760 int64_t microsecond
= time
.microsecond
;
761 int64_t nanosecond
= time
.nanosecond
;
763 int64_t millis
= ((hour
* 60 + minute
) * 60 + second
) * 1000 + millisecond
;
764 return (millis
* 1000 + microsecond
) * 1000 + nanosecond
;
768 * RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond,
769 * increment, unit, roundingMode [ , dayLengthNs ] )
771 RoundedTime
js::temporal::RoundTime(const PlainTime
& time
, Increment increment
,
773 TemporalRoundingMode roundingMode
) {
774 MOZ_ASSERT(IsValidTime(time
));
775 MOZ_ASSERT(unit
>= TemporalUnit::Day
);
776 MOZ_ASSERT_IF(unit
> TemporalUnit::Day
,
777 increment
<= MaximumTemporalDurationRoundingIncrement(unit
));
778 MOZ_ASSERT_IF(unit
== TemporalUnit::Day
, increment
== Increment
{1});
781 auto [hour
, minute
, second
, millisecond
, microsecond
, nanosecond
] = time
;
783 // Take the same approach as used in RoundDuration() to perform exact
784 // mathematical operations without possible loss of precision.
790 case TemporalUnit::Day
:
794 case TemporalUnit::Hour
:
803 case TemporalUnit::Minute
:
804 quantity
= {0, minute
, second
, millisecond
, microsecond
, nanosecond
};
811 case TemporalUnit::Second
:
812 quantity
= {0, 0, second
, millisecond
, microsecond
, nanosecond
};
818 case TemporalUnit::Millisecond
:
819 quantity
= {0, 0, 0, millisecond
, microsecond
, nanosecond
};
820 result
= &millisecond
;
824 case TemporalUnit::Microsecond
:
825 quantity
= {0, 0, 0, 0, microsecond
, nanosecond
};
826 result
= µsecond
;
829 case TemporalUnit::Nanosecond
:
830 quantity
= {0, 0, 0, 0, 0, nanosecond
};
831 result
= &nanosecond
;
834 case TemporalUnit::Auto
:
835 case TemporalUnit::Year
:
836 case TemporalUnit::Month
:
837 case TemporalUnit::Week
:
838 MOZ_CRASH("unexpected temporal unit");
842 int64_t nanos
= TimeToNanos(quantity
);
843 MOZ_ASSERT(0 <= nanos
&& nanos
< ToNanoseconds(TemporalUnit::Day
));
845 auto r
= RoundNumberToIncrement(nanos
, ToNanoseconds(unit
), increment
,
847 MOZ_ASSERT(r
== Int128
{int32_t(r
)},
848 "can't overflow when inputs are all in range");
850 *result
= int32_t(r
);
853 if (unit
== TemporalUnit::Day
) {
854 return {int64_t(days
), {0, 0, 0, 0, 0, 0}};
859 ::BalanceTime(hour
, minute
, second
, millisecond
, microsecond
, nanosecond
);
860 return {int64_t(balanced
.days
), balanced
.time
};
864 * RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond,
865 * increment, unit, roundingMode [ , dayLengthNs ] )
867 RoundedTime
js::temporal::RoundTime(const PlainTime
& time
, Increment increment
,
869 TemporalRoundingMode roundingMode
,
870 const InstantSpan
& dayLengthNs
) {
871 MOZ_ASSERT(IsValidTime(time
));
872 MOZ_ASSERT(IsValidInstantSpan(dayLengthNs
));
873 MOZ_ASSERT(dayLengthNs
> (InstantSpan
{}));
875 if (unit
!= TemporalUnit::Day
) {
876 return RoundTime(time
, increment
, unit
, roundingMode
);
879 // Step 1. (Not applicable)
882 int64_t quantity
= TimeToNanos(time
);
883 MOZ_ASSERT(0 <= quantity
&& quantity
< ToNanoseconds(TemporalUnit::Day
));
885 // Steps 3-8. (Not applicable)
889 // When the divisor is too large, the expression `quantity / divisor` is a
890 // value near zero. Substitute |divisor| with an equivalent expression.
891 // Choose |86'400'000'000'000| which will give a similar result because
892 // |quantity| is guaranteed to be lower than |86'400'000'000'000|.
893 int64_t divisor
= int64_t(std::min(dayLengthNs
.toTotalNanoseconds(),
894 Int128
{ToNanoseconds(TemporalUnit::Day
)}));
895 MOZ_ASSERT(divisor
> 0);
896 MOZ_ASSERT(increment
== Increment
{1}, "Rounding increment for 'day' is 1");
899 RoundNumberToIncrement(quantity
, divisor
, increment
, roundingMode
);
900 MOZ_ASSERT(result
== Int128
{int64_t(result
)});
903 return {int64_t(result
), {0, 0, 0, 0, 0, 0}};
907 * AddTime ( hour, minute, second, millisecond, microsecond, nanosecond, norm )
909 AddedTime
js::temporal::AddTime(const PlainTime
& time
,
910 const NormalizedTimeDuration
& duration
) {
911 MOZ_ASSERT(IsValidTime(time
));
912 MOZ_ASSERT(IsValidNormalizedTimeDuration(duration
));
914 auto [seconds
, nanoseconds
] = duration
;
915 if (seconds
< 0 && nanoseconds
> 0) {
917 nanoseconds
-= 1'000'000'000;
919 MOZ_ASSERT(std::abs(nanoseconds
) <= 999'999'999);
922 int64_t second
= time
.second
+ seconds
;
925 int32_t nanosecond
= time
.nanosecond
+ nanoseconds
;
929 ::BalanceTime
<int64_t>(time
.hour
, time
.minute
, second
, time
.millisecond
,
930 time
.microsecond
, nanosecond
);
931 return {balanced
.days
, balanced
.time
};
935 * DifferenceTemporalPlainTime ( operation, temporalTime, other, options )
937 static bool DifferenceTemporalPlainTime(JSContext
* cx
,
938 TemporalDifference operation
,
939 const CallArgs
& args
) {
941 ToPlainTime(&args
.thisv().toObject().as
<PlainTimeObject
>());
943 // Step 1. (Not applicable in our implementation.)
947 if (!ToTemporalTime(cx
, args
.get(0), &other
)) {
952 DifferenceSettings settings
;
953 if (args
.hasDefined(1)) {
954 Rooted
<JSObject
*> options(
955 cx
, RequireObjectArg(cx
, "options", ToName(operation
), args
[1]));
961 Rooted
<PlainObject
*> resolvedOptions(cx
,
962 SnapshotOwnProperties(cx
, options
));
963 if (!resolvedOptions
) {
968 if (!GetDifferenceSettings(
969 cx
, operation
, resolvedOptions
, TemporalUnitGroup::Time
,
970 TemporalUnit::Nanosecond
, TemporalUnit::Hour
, &settings
)) {
976 TemporalUnit::Nanosecond
,
978 TemporalRoundingMode::Trunc
,
984 auto diff
= DifferenceTime(temporalTime
, other
);
987 if (settings
.smallestUnit
!= TemporalUnit::Nanosecond
||
988 settings
.roundingIncrement
!= Increment
{1}) {
990 diff
= RoundDuration(diff
, settings
.roundingIncrement
,
991 settings
.smallestUnit
, settings
.roundingMode
);
995 auto balancedDuration
= BalanceTimeDuration(diff
, settings
.largestUnit
);
998 auto duration
= balancedDuration
.toDuration();
999 if (operation
== TemporalDifference::Since
) {
1000 duration
= duration
.negate();
1003 auto* result
= CreateTemporalDuration(cx
, duration
);
1008 args
.rval().setObject(*result
);
1012 enum class PlainTimeDuration
{ Add
, Subtract
};
1015 * AddDurationToOrSubtractDurationFromPlainTime ( operation, temporalTime,
1016 * temporalDurationLike )
1018 static bool AddDurationToOrSubtractDurationFromPlainTime(
1019 JSContext
* cx
, PlainTimeDuration operation
, const CallArgs
& args
) {
1020 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1021 auto time
= ToPlainTime(temporalTime
);
1023 // Step 1. (Not applicable in our implementation.)
1027 if (!ToTemporalDurationRecord(cx
, args
.get(0), &duration
)) {
1032 if (operation
== PlainTimeDuration::Subtract
) {
1033 duration
= duration
.negate();
1035 auto timeDuration
= NormalizeTimeDuration(duration
);
1038 auto result
= AddTime(time
, timeDuration
);
1039 MOZ_ASSERT(IsValidTime(result
.time
));
1042 auto* obj
= CreateTemporalTime(cx
, result
.time
);
1047 args
.rval().setObject(*obj
);
1052 * Temporal.PlainTime ( [ hour [ , minute [ , second [ , millisecond [ ,
1053 * microsecond [ , nanosecond ] ] ] ] ] ] )
1055 static bool PlainTimeConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1056 CallArgs args
= CallArgsFromVp(argc
, vp
);
1059 if (!ThrowIfNotConstructing(cx
, args
, "Temporal.PlainTime")) {
1065 if (args
.hasDefined(0)) {
1066 if (!ToIntegerWithTruncation(cx
, args
[0], "hour", &hour
)) {
1073 if (args
.hasDefined(1)) {
1074 if (!ToIntegerWithTruncation(cx
, args
[1], "minute", &minute
)) {
1081 if (args
.hasDefined(2)) {
1082 if (!ToIntegerWithTruncation(cx
, args
[2], "second", &second
)) {
1088 double millisecond
= 0;
1089 if (args
.hasDefined(3)) {
1090 if (!ToIntegerWithTruncation(cx
, args
[3], "millisecond", &millisecond
)) {
1096 double microsecond
= 0;
1097 if (args
.hasDefined(4)) {
1098 if (!ToIntegerWithTruncation(cx
, args
[4], "microsecond", µsecond
)) {
1104 double nanosecond
= 0;
1105 if (args
.hasDefined(5)) {
1106 if (!ToIntegerWithTruncation(cx
, args
[5], "nanosecond", &nanosecond
)) {
1112 auto* temporalTime
= CreateTemporalTime(cx
, args
, hour
, minute
, second
,
1113 millisecond
, microsecond
, nanosecond
);
1114 if (!temporalTime
) {
1118 args
.rval().setObject(*temporalTime
);
1123 * Temporal.PlainTime.from ( item [ , options ] )
1125 static bool PlainTime_from(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1126 CallArgs args
= CallArgsFromVp(argc
, vp
);
1128 // Step 1. (Not applicable)
1130 auto overflow
= TemporalOverflow::Constrain
;
1131 if (args
.hasDefined(1)) {
1133 Rooted
<JSObject
*> options(cx
,
1134 RequireObjectArg(cx
, "options", "from", args
[1]));
1140 if (!ToTemporalOverflow(cx
, options
, &overflow
)) {
1146 auto* result
= ToTemporalTime(cx
, args
.get(0), overflow
);
1151 args
.rval().setObject(*result
);
1156 * Temporal.PlainTime.compare ( one, two )
1158 static bool PlainTime_compare(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1159 CallArgs args
= CallArgsFromVp(argc
, vp
);
1163 if (!ToTemporalTime(cx
, args
.get(0), &one
)) {
1169 if (!ToTemporalTime(cx
, args
.get(1), &two
)) {
1174 args
.rval().setInt32(CompareTemporalTime(one
, two
));
1179 * get Temporal.PlainTime.prototype.hour
1181 static bool PlainTime_hour(JSContext
* cx
, const CallArgs
& args
) {
1183 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1184 args
.rval().setInt32(temporalTime
->isoHour());
1189 * get Temporal.PlainTime.prototype.hour
1191 static bool PlainTime_hour(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1193 CallArgs args
= CallArgsFromVp(argc
, vp
);
1194 return CallNonGenericMethod
<IsPlainTime
, PlainTime_hour
>(cx
, args
);
1198 * get Temporal.PlainTime.prototype.minute
1200 static bool PlainTime_minute(JSContext
* cx
, const CallArgs
& args
) {
1202 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1203 args
.rval().setInt32(temporalTime
->isoMinute());
1208 * get Temporal.PlainTime.prototype.minute
1210 static bool PlainTime_minute(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1212 CallArgs args
= CallArgsFromVp(argc
, vp
);
1213 return CallNonGenericMethod
<IsPlainTime
, PlainTime_minute
>(cx
, args
);
1217 * get Temporal.PlainTime.prototype.second
1219 static bool PlainTime_second(JSContext
* cx
, const CallArgs
& args
) {
1221 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1222 args
.rval().setInt32(temporalTime
->isoSecond());
1227 * get Temporal.PlainTime.prototype.second
1229 static bool PlainTime_second(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1231 CallArgs args
= CallArgsFromVp(argc
, vp
);
1232 return CallNonGenericMethod
<IsPlainTime
, PlainTime_second
>(cx
, args
);
1236 * get Temporal.PlainTime.prototype.millisecond
1238 static bool PlainTime_millisecond(JSContext
* cx
, const CallArgs
& args
) {
1240 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1241 args
.rval().setInt32(temporalTime
->isoMillisecond());
1246 * get Temporal.PlainTime.prototype.millisecond
1248 static bool PlainTime_millisecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1250 CallArgs args
= CallArgsFromVp(argc
, vp
);
1251 return CallNonGenericMethod
<IsPlainTime
, PlainTime_millisecond
>(cx
, args
);
1255 * get Temporal.PlainTime.prototype.microsecond
1257 static bool PlainTime_microsecond(JSContext
* cx
, const CallArgs
& args
) {
1259 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1260 args
.rval().setInt32(temporalTime
->isoMicrosecond());
1265 * get Temporal.PlainTime.prototype.microsecond
1267 static bool PlainTime_microsecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1269 CallArgs args
= CallArgsFromVp(argc
, vp
);
1270 return CallNonGenericMethod
<IsPlainTime
, PlainTime_microsecond
>(cx
, args
);
1274 * get Temporal.PlainTime.prototype.nanosecond
1276 static bool PlainTime_nanosecond(JSContext
* cx
, const CallArgs
& args
) {
1278 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1279 args
.rval().setInt32(temporalTime
->isoNanosecond());
1284 * get Temporal.PlainTime.prototype.nanosecond
1286 static bool PlainTime_nanosecond(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1288 CallArgs args
= CallArgsFromVp(argc
, vp
);
1289 return CallNonGenericMethod
<IsPlainTime
, PlainTime_nanosecond
>(cx
, args
);
1293 * Temporal.PlainTime.prototype.add ( temporalDurationLike )
1295 static bool PlainTime_add(JSContext
* cx
, const CallArgs
& args
) {
1297 return AddDurationToOrSubtractDurationFromPlainTime(
1298 cx
, PlainTimeDuration::Add
, args
);
1302 * Temporal.PlainTime.prototype.add ( temporalDurationLike )
1304 static bool PlainTime_add(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1306 CallArgs args
= CallArgsFromVp(argc
, vp
);
1307 return CallNonGenericMethod
<IsPlainTime
, PlainTime_add
>(cx
, args
);
1311 * Temporal.PlainTime.prototype.subtract ( temporalDurationLike )
1313 static bool PlainTime_subtract(JSContext
* cx
, const CallArgs
& args
) {
1315 return AddDurationToOrSubtractDurationFromPlainTime(
1316 cx
, PlainTimeDuration::Subtract
, args
);
1320 * Temporal.PlainTime.prototype.subtract ( temporalDurationLike )
1322 static bool PlainTime_subtract(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1324 CallArgs args
= CallArgsFromVp(argc
, vp
);
1325 return CallNonGenericMethod
<IsPlainTime
, PlainTime_subtract
>(cx
, args
);
1329 * Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] )
1331 static bool PlainTime_with(JSContext
* cx
, const CallArgs
& args
) {
1332 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1333 auto time
= ToPlainTime(temporalTime
);
1336 Rooted
<JSObject
*> temporalTimeLike(
1337 cx
, RequireObjectArg(cx
, "temporalTimeLike", "with", args
.get(0)));
1338 if (!temporalTimeLike
) {
1343 if (!RejectTemporalLikeObject(cx
, temporalTimeLike
)) {
1347 auto overflow
= TemporalOverflow::Constrain
;
1348 if (args
.hasDefined(1)) {
1350 Rooted
<JSObject
*> options(cx
,
1351 RequireObjectArg(cx
, "options", "with", args
[1]));
1357 if (!ToTemporalOverflow(cx
, options
, &overflow
)) {
1363 TemporalTimeLike partialTime
= {
1364 double(time
.hour
), double(time
.minute
),
1365 double(time
.second
), double(time
.millisecond
),
1366 double(time
.microsecond
), double(time
.nanosecond
),
1368 if (!::ToTemporalTimeRecord(cx
, temporalTimeLike
, &partialTime
)) {
1374 if (!RegulateTime(cx
, partialTime
, overflow
, &result
)) {
1379 auto* obj
= CreateTemporalTime(cx
, result
);
1384 args
.rval().setObject(*obj
);
1389 * Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] )
1391 static bool PlainTime_with(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1393 CallArgs args
= CallArgsFromVp(argc
, vp
);
1394 return CallNonGenericMethod
<IsPlainTime
, PlainTime_with
>(cx
, args
);
1398 * Temporal.PlainTime.prototype.until ( other [ , options ] )
1400 static bool PlainTime_until(JSContext
* cx
, const CallArgs
& args
) {
1402 return DifferenceTemporalPlainTime(cx
, TemporalDifference::Until
, args
);
1406 * Temporal.PlainTime.prototype.until ( other [ , options ] )
1408 static bool PlainTime_until(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1410 CallArgs args
= CallArgsFromVp(argc
, vp
);
1411 return CallNonGenericMethod
<IsPlainTime
, PlainTime_until
>(cx
, args
);
1415 * Temporal.PlainTime.prototype.since ( other [ , options ] )
1417 static bool PlainTime_since(JSContext
* cx
, const CallArgs
& args
) {
1419 return DifferenceTemporalPlainTime(cx
, TemporalDifference::Since
, args
);
1423 * Temporal.PlainTime.prototype.since ( other [ , options ] )
1425 static bool PlainTime_since(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1427 CallArgs args
= CallArgsFromVp(argc
, vp
);
1428 return CallNonGenericMethod
<IsPlainTime
, PlainTime_since
>(cx
, args
);
1432 * Temporal.PlainTime.prototype.round ( roundTo )
1434 static bool PlainTime_round(JSContext
* cx
, const CallArgs
& args
) {
1435 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1436 auto time
= ToPlainTime(temporalTime
);
1439 auto smallestUnit
= TemporalUnit::Auto
;
1440 auto roundingMode
= TemporalRoundingMode::HalfExpand
;
1441 auto roundingIncrement
= Increment
{1};
1442 if (args
.get(0).isString()) {
1443 // Step 4. (Not applicable in our implementation.)
1446 Rooted
<JSString
*> paramString(cx
, args
[0].toString());
1447 if (!GetTemporalUnit(cx
, paramString
, TemporalUnitKey::SmallestUnit
,
1448 TemporalUnitGroup::Time
, &smallestUnit
)) {
1452 // Steps 6-8 and 10-12. (Implicit)
1455 Rooted
<JSObject
*> options(
1456 cx
, RequireObjectArg(cx
, "roundTo", "round", args
.get(0)));
1462 if (!ToTemporalRoundingIncrement(cx
, options
, &roundingIncrement
)) {
1467 if (!ToTemporalRoundingMode(cx
, options
, &roundingMode
)) {
1472 if (!GetTemporalUnit(cx
, options
, TemporalUnitKey::SmallestUnit
,
1473 TemporalUnitGroup::Time
, &smallestUnit
)) {
1477 if (smallestUnit
== TemporalUnit::Auto
) {
1478 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1479 JSMSG_TEMPORAL_MISSING_OPTION
, "smallestUnit");
1484 auto maximum
= MaximumTemporalDurationRoundingIncrement(smallestUnit
);
1487 if (!ValidateTemporalRoundingIncrement(cx
, roundingIncrement
, maximum
,
1494 auto result
= RoundTime(time
, roundingIncrement
, smallestUnit
, roundingMode
);
1497 auto* obj
= CreateTemporalTime(cx
, result
.time
);
1502 args
.rval().setObject(*obj
);
1507 * Temporal.PlainTime.prototype.round ( roundTo )
1509 static bool PlainTime_round(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1511 CallArgs args
= CallArgsFromVp(argc
, vp
);
1512 return CallNonGenericMethod
<IsPlainTime
, PlainTime_round
>(cx
, args
);
1516 * Temporal.PlainTime.prototype.equals ( other )
1518 static bool PlainTime_equals(JSContext
* cx
, const CallArgs
& args
) {
1520 ToPlainTime(&args
.thisv().toObject().as
<PlainTimeObject
>());
1524 if (!ToTemporalTime(cx
, args
.get(0), &other
)) {
1529 args
.rval().setBoolean(temporalTime
== other
);
1534 * Temporal.PlainTime.prototype.equals ( other )
1536 static bool PlainTime_equals(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1538 CallArgs args
= CallArgsFromVp(argc
, vp
);
1539 return CallNonGenericMethod
<IsPlainTime
, PlainTime_equals
>(cx
, args
);
1543 * Temporal.PlainTime.prototype.toPlainDateTime ( temporalDate )
1545 static bool PlainTime_toPlainDateTime(JSContext
* cx
, const CallArgs
& args
) {
1546 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1547 auto time
= ToPlainTime(temporalTime
);
1550 Rooted
<PlainDateWithCalendar
> plainDate(cx
);
1551 if (!ToTemporalDate(cx
, args
.get(0), &plainDate
)) {
1554 auto date
= plainDate
.date();
1555 auto calendar
= plainDate
.calendar();
1558 auto* result
= CreateTemporalDateTime(cx
, {date
, time
}, calendar
);
1563 args
.rval().setObject(*result
);
1568 * Temporal.PlainTime.prototype.toPlainDateTime ( temporalDate )
1570 static bool PlainTime_toPlainDateTime(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1572 CallArgs args
= CallArgsFromVp(argc
, vp
);
1573 return CallNonGenericMethod
<IsPlainTime
, PlainTime_toPlainDateTime
>(cx
, args
);
1577 * Temporal.PlainTime.prototype.toZonedDateTime ( item )
1579 * |item| is an options object with `plainDate` and `timeZone` properties.
1581 static bool PlainTime_toZonedDateTime(JSContext
* cx
, const CallArgs
& args
) {
1582 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1583 auto time
= ToPlainTime(temporalTime
);
1586 Rooted
<JSObject
*> itemObj(
1587 cx
, RequireObjectArg(cx
, "item", "toZonedDateTime", args
.get(0)));
1593 Rooted
<Value
> temporalDateLike(cx
);
1594 if (!GetProperty(cx
, itemObj
, args
[0], cx
->names().plainDate
,
1595 &temporalDateLike
)) {
1600 if (temporalDateLike
.isUndefined()) {
1601 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1602 JSMSG_TEMPORAL_MISSING_PROPERTY
, "plainDate");
1607 Rooted
<PlainDateWithCalendar
> plainDate(cx
);
1608 if (!ToTemporalDate(cx
, temporalDateLike
, &plainDate
)) {
1611 auto date
= plainDate
.date();
1612 auto calendar
= plainDate
.calendar();
1615 Rooted
<Value
> temporalTimeZoneLike(cx
);
1616 if (!GetProperty(cx
, itemObj
, itemObj
, cx
->names().timeZone
,
1617 &temporalTimeZoneLike
)) {
1622 if (temporalTimeZoneLike
.isUndefined()) {
1623 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1624 JSMSG_TEMPORAL_MISSING_PROPERTY
, "timeZone");
1629 Rooted
<TimeZoneValue
> timeZone(cx
);
1630 if (!ToTemporalTimeZone(cx
, temporalTimeZoneLike
, &timeZone
)) {
1635 Rooted
<PlainDateTimeWithCalendar
> temporalDateTime(cx
);
1636 if (!CreateTemporalDateTime(cx
, {date
, time
}, calendar
, &temporalDateTime
)) {
1642 if (!GetInstantFor(cx
, timeZone
, temporalDateTime
,
1643 TemporalDisambiguation::Compatible
, &instant
)) {
1648 auto* result
= CreateTemporalZonedDateTime(cx
, instant
, timeZone
, calendar
);
1653 args
.rval().setObject(*result
);
1658 * Temporal.PlainTime.prototype.toZonedDateTime ( item )
1660 static bool PlainTime_toZonedDateTime(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1662 CallArgs args
= CallArgsFromVp(argc
, vp
);
1663 return CallNonGenericMethod
<IsPlainTime
, PlainTime_toZonedDateTime
>(cx
, args
);
1667 * Temporal.PlainTime.prototype.getISOFields ( )
1669 static bool PlainTime_getISOFields(JSContext
* cx
, const CallArgs
& args
) {
1670 Rooted
<PlainTimeObject
*> temporalTime(
1671 cx
, &args
.thisv().toObject().as
<PlainTimeObject
>());
1672 auto time
= ToPlainTime(temporalTime
);
1675 Rooted
<IdValueVector
> fields(cx
, IdValueVector(cx
));
1678 if (!fields
.emplaceBack(NameToId(cx
->names().isoHour
),
1679 Int32Value(time
.hour
))) {
1684 if (!fields
.emplaceBack(NameToId(cx
->names().isoMicrosecond
),
1685 Int32Value(time
.microsecond
))) {
1690 if (!fields
.emplaceBack(NameToId(cx
->names().isoMillisecond
),
1691 Int32Value(time
.millisecond
))) {
1696 if (!fields
.emplaceBack(NameToId(cx
->names().isoMinute
),
1697 Int32Value(time
.minute
))) {
1702 if (!fields
.emplaceBack(NameToId(cx
->names().isoNanosecond
),
1703 Int32Value(time
.nanosecond
))) {
1708 if (!fields
.emplaceBack(NameToId(cx
->names().isoSecond
),
1709 Int32Value(time
.second
))) {
1714 auto* obj
= NewPlainObjectWithUniqueNames(cx
, fields
);
1719 args
.rval().setObject(*obj
);
1724 * Temporal.PlainTime.prototype.getISOFields ( )
1726 static bool PlainTime_getISOFields(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1728 CallArgs args
= CallArgsFromVp(argc
, vp
);
1729 return CallNonGenericMethod
<IsPlainTime
, PlainTime_getISOFields
>(cx
, args
);
1733 * Temporal.PlainTime.prototype.toString ( [ options ] )
1735 static bool PlainTime_toString(JSContext
* cx
, const CallArgs
& args
) {
1736 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1737 auto time
= ToPlainTime(temporalTime
);
1739 SecondsStringPrecision precision
= {Precision::Auto(),
1740 TemporalUnit::Nanosecond
, Increment
{1}};
1741 auto roundingMode
= TemporalRoundingMode::Trunc
;
1742 if (args
.hasDefined(0)) {
1744 Rooted
<JSObject
*> options(
1745 cx
, RequireObjectArg(cx
, "options", "toString", args
[0]));
1751 auto digits
= Precision::Auto();
1752 if (!ToFractionalSecondDigits(cx
, options
, &digits
)) {
1757 if (!ToTemporalRoundingMode(cx
, options
, &roundingMode
)) {
1762 auto smallestUnit
= TemporalUnit::Auto
;
1763 if (!GetTemporalUnit(cx
, options
, TemporalUnitKey::SmallestUnit
,
1764 TemporalUnitGroup::Time
, &smallestUnit
)) {
1769 if (smallestUnit
== TemporalUnit::Hour
) {
1770 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1771 JSMSG_TEMPORAL_INVALID_UNIT_OPTION
, "hour",
1777 precision
= ToSecondsStringPrecision(smallestUnit
, digits
);
1782 RoundTime(time
, precision
.increment
, precision
.unit
, roundingMode
);
1786 TemporalTimeToString(cx
, roundedTime
.time
, precision
.precision
);
1791 args
.rval().setString(str
);
1796 * Temporal.PlainTime.prototype.toString ( [ options ] )
1798 static bool PlainTime_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1800 CallArgs args
= CallArgsFromVp(argc
, vp
);
1801 return CallNonGenericMethod
<IsPlainTime
, PlainTime_toString
>(cx
, args
);
1805 * Temporal.PlainTime.prototype.toLocaleString ( [ locales [ , options ] ] )
1807 static bool PlainTime_toLocaleString(JSContext
* cx
, const CallArgs
& args
) {
1808 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1809 auto time
= ToPlainTime(temporalTime
);
1812 JSString
* str
= TemporalTimeToString(cx
, time
, Precision::Auto());
1817 args
.rval().setString(str
);
1822 * Temporal.PlainTime.prototype.toLocaleString ( [ locales [ , options ] ] )
1824 static bool PlainTime_toLocaleString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1826 CallArgs args
= CallArgsFromVp(argc
, vp
);
1827 return CallNonGenericMethod
<IsPlainTime
, PlainTime_toLocaleString
>(cx
, args
);
1831 * Temporal.PlainTime.prototype.toJSON ( )
1833 static bool PlainTime_toJSON(JSContext
* cx
, const CallArgs
& args
) {
1834 auto* temporalTime
= &args
.thisv().toObject().as
<PlainTimeObject
>();
1835 auto time
= ToPlainTime(temporalTime
);
1838 JSString
* str
= TemporalTimeToString(cx
, time
, Precision::Auto());
1843 args
.rval().setString(str
);
1848 * Temporal.PlainTime.prototype.toJSON ( )
1850 static bool PlainTime_toJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1852 CallArgs args
= CallArgsFromVp(argc
, vp
);
1853 return CallNonGenericMethod
<IsPlainTime
, PlainTime_toJSON
>(cx
, args
);
1857 * Temporal.PlainTime.prototype.valueOf ( )
1859 static bool PlainTime_valueOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1860 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_CANT_CONVERT_TO
,
1861 "PlainTime", "primitive type");
1865 const JSClass
PlainTimeObject::class_
= {
1866 "Temporal.PlainTime",
1867 JSCLASS_HAS_RESERVED_SLOTS(PlainTimeObject::SLOT_COUNT
) |
1868 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainTime
),
1870 &PlainTimeObject::classSpec_
,
1873 const JSClass
& PlainTimeObject::protoClass_
= PlainObject::class_
;
1875 static const JSFunctionSpec PlainTime_methods
[] = {
1876 JS_FN("from", PlainTime_from
, 1, 0),
1877 JS_FN("compare", PlainTime_compare
, 2, 0),
1881 static const JSFunctionSpec PlainTime_prototype_methods
[] = {
1882 JS_FN("add", PlainTime_add
, 1, 0),
1883 JS_FN("subtract", PlainTime_subtract
, 1, 0),
1884 JS_FN("with", PlainTime_with
, 1, 0),
1885 JS_FN("until", PlainTime_until
, 1, 0),
1886 JS_FN("since", PlainTime_since
, 1, 0),
1887 JS_FN("round", PlainTime_round
, 1, 0),
1888 JS_FN("equals", PlainTime_equals
, 1, 0),
1889 JS_FN("toPlainDateTime", PlainTime_toPlainDateTime
, 1, 0),
1890 JS_FN("toZonedDateTime", PlainTime_toZonedDateTime
, 1, 0),
1891 JS_FN("getISOFields", PlainTime_getISOFields
, 0, 0),
1892 JS_FN("toString", PlainTime_toString
, 0, 0),
1893 JS_FN("toLocaleString", PlainTime_toLocaleString
, 0, 0),
1894 JS_FN("toJSON", PlainTime_toJSON
, 0, 0),
1895 JS_FN("valueOf", PlainTime_valueOf
, 0, 0),
1899 static const JSPropertySpec PlainTime_prototype_properties
[] = {
1900 JS_PSG("hour", PlainTime_hour
, 0),
1901 JS_PSG("minute", PlainTime_minute
, 0),
1902 JS_PSG("second", PlainTime_second
, 0),
1903 JS_PSG("millisecond", PlainTime_millisecond
, 0),
1904 JS_PSG("microsecond", PlainTime_microsecond
, 0),
1905 JS_PSG("nanosecond", PlainTime_nanosecond
, 0),
1906 JS_STRING_SYM_PS(toStringTag
, "Temporal.PlainTime", JSPROP_READONLY
),
1910 const ClassSpec
PlainTimeObject::classSpec_
= {
1911 GenericCreateConstructor
<PlainTimeConstructor
, 0, gc::AllocKind::FUNCTION
>,
1912 GenericCreatePrototype
<PlainTimeObject
>,
1915 PlainTime_prototype_methods
,
1916 PlainTime_prototype_properties
,
1918 ClassSpec::DontDefineConstructor
,