PlainTime.cpp (44667B)
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/. */ 6 7 #include "builtin/temporal/PlainTime.h" 8 9 #include "mozilla/Assertions.h" 10 11 #include <algorithm> 12 #include <cmath> 13 #include <cstdlib> 14 15 #include "jsnum.h" 16 #include "jspubtd.h" 17 #include "NamespaceImports.h" 18 19 #include "builtin/intl/DateTimeFormat.h" 20 #include "builtin/temporal/Duration.h" 21 #include "builtin/temporal/PlainDateTime.h" 22 #include "builtin/temporal/Temporal.h" 23 #include "builtin/temporal/TemporalParser.h" 24 #include "builtin/temporal/TemporalRoundingMode.h" 25 #include "builtin/temporal/TemporalTypes.h" 26 #include "builtin/temporal/TemporalUnit.h" 27 #include "builtin/temporal/TimeZone.h" 28 #include "builtin/temporal/ToString.h" 29 #include "builtin/temporal/ZonedDateTime.h" 30 #include "gc/AllocKind.h" 31 #include "gc/Barrier.h" 32 #include "js/CallArgs.h" 33 #include "js/CallNonGenericMethod.h" 34 #include "js/Class.h" 35 #include "js/ErrorReport.h" 36 #include "js/friend/ErrorMessages.h" 37 #include "js/PropertyDescriptor.h" 38 #include "js/PropertySpec.h" 39 #include "js/RootingAPI.h" 40 #include "js/Value.h" 41 #include "vm/BytecodeUtil.h" 42 #include "vm/GlobalObject.h" 43 #include "vm/JSAtomState.h" 44 #include "vm/JSContext.h" 45 #include "vm/JSObject.h" 46 #include "vm/PlainObject.h" 47 48 #include "vm/JSObject-inl.h" 49 #include "vm/NativeObject-inl.h" 50 #include "vm/ObjectOperations-inl.h" 51 52 using namespace js; 53 using namespace js::temporal; 54 55 static inline bool IsPlainTime(Handle<Value> v) { 56 return v.isObject() && v.toObject().is<PlainTimeObject>(); 57 } 58 59 #ifdef DEBUG 60 /** 61 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ) 62 */ 63 template <typename T> 64 static bool IsValidTime(T hour, T minute, T second, T millisecond, 65 T microsecond, T nanosecond) { 66 static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, double>); 67 68 MOZ_ASSERT(IsInteger(hour)); 69 MOZ_ASSERT(IsInteger(minute)); 70 MOZ_ASSERT(IsInteger(second)); 71 MOZ_ASSERT(IsInteger(millisecond)); 72 MOZ_ASSERT(IsInteger(microsecond)); 73 MOZ_ASSERT(IsInteger(nanosecond)); 74 75 // Step 1. 76 if (hour < 0 || hour > 23) { 77 return false; 78 } 79 80 // Step 2. 81 if (minute < 0 || minute > 59) { 82 return false; 83 } 84 85 // Step 3. 86 if (second < 0 || second > 59) { 87 return false; 88 } 89 90 // Step 4. 91 if (millisecond < 0 || millisecond > 999) { 92 return false; 93 } 94 95 // Step 5. 96 if (microsecond < 0 || microsecond > 999) { 97 return false; 98 } 99 100 // Step 6. 101 if (nanosecond < 0 || nanosecond > 999) { 102 return false; 103 } 104 105 // Step 7. 106 return true; 107 } 108 109 /** 110 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ) 111 */ 112 bool js::temporal::IsValidTime(const Time& time) { 113 const auto& [hour, minute, second, millisecond, microsecond, nanosecond] = 114 time; 115 return ::IsValidTime(hour, minute, second, millisecond, microsecond, 116 nanosecond); 117 } 118 119 /** 120 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ) 121 */ 122 bool js::temporal::IsValidTime(double hour, double minute, double second, 123 double millisecond, double microsecond, 124 double nanosecond) { 125 return ::IsValidTime(hour, minute, second, millisecond, microsecond, 126 nanosecond); 127 } 128 #endif 129 130 static void ReportInvalidTimeValue(JSContext* cx, const char* name, int32_t min, 131 int32_t max, double num) { 132 Int32ToCStringBuf minCbuf; 133 const char* minStr = Int32ToCString(&minCbuf, min); 134 135 Int32ToCStringBuf maxCbuf; 136 const char* maxStr = Int32ToCString(&maxCbuf, max); 137 138 ToCStringBuf numCbuf; 139 const char* numStr = NumberToCString(&numCbuf, num); 140 141 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 142 JSMSG_TEMPORAL_PLAIN_TIME_INVALID_VALUE, name, 143 minStr, maxStr, numStr); 144 } 145 146 static inline bool ThrowIfInvalidTimeValue(JSContext* cx, const char* name, 147 int32_t min, int32_t max, 148 double num) { 149 if (min <= num && num <= max) { 150 return true; 151 } 152 ReportInvalidTimeValue(cx, name, min, max, num); 153 return false; 154 } 155 156 /** 157 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ) 158 */ 159 bool js::temporal::ThrowIfInvalidTime(JSContext* cx, double hour, double minute, 160 double second, double millisecond, 161 double microsecond, double nanosecond) { 162 // Step 1. 163 MOZ_ASSERT(IsInteger(hour)); 164 MOZ_ASSERT(IsInteger(minute)); 165 MOZ_ASSERT(IsInteger(second)); 166 MOZ_ASSERT(IsInteger(millisecond)); 167 MOZ_ASSERT(IsInteger(microsecond)); 168 MOZ_ASSERT(IsInteger(nanosecond)); 169 170 // Step 2. 171 if (!ThrowIfInvalidTimeValue(cx, "hour", 0, 23, hour)) { 172 return false; 173 } 174 175 // Step 3. 176 if (!ThrowIfInvalidTimeValue(cx, "minute", 0, 59, minute)) { 177 return false; 178 } 179 180 // Step 4. 181 if (!ThrowIfInvalidTimeValue(cx, "second", 0, 59, second)) { 182 return false; 183 } 184 185 // Step 5. 186 if (!ThrowIfInvalidTimeValue(cx, "millisecond", 0, 999, millisecond)) { 187 return false; 188 } 189 190 // Step 6. 191 if (!ThrowIfInvalidTimeValue(cx, "microsecond", 0, 999, microsecond)) { 192 return false; 193 } 194 195 // Step 7. 196 if (!ThrowIfInvalidTimeValue(cx, "nanosecond", 0, 999, nanosecond)) { 197 return false; 198 } 199 200 // Step 8. 201 return true; 202 } 203 204 /** 205 * RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond, 206 * overflow ) 207 */ 208 bool js::temporal::RegulateTime(JSContext* cx, const TemporalTimeLike& time, 209 TemporalOverflow overflow, Time* result) { 210 auto [hour, minute, second, millisecond, microsecond, nanosecond] = time; 211 MOZ_ASSERT(IsInteger(hour)); 212 MOZ_ASSERT(IsInteger(minute)); 213 MOZ_ASSERT(IsInteger(second)); 214 MOZ_ASSERT(IsInteger(millisecond)); 215 MOZ_ASSERT(IsInteger(microsecond)); 216 MOZ_ASSERT(IsInteger(nanosecond)); 217 218 // Steps 1-2. 219 if (overflow == TemporalOverflow::Constrain) { 220 // Step 1.a. 221 hour = std::clamp(hour, 0.0, 23.0); 222 223 // Step 1.b. 224 minute = std::clamp(minute, 0.0, 59.0); 225 226 // Step 1.c. 227 second = std::clamp(second, 0.0, 59.0); 228 229 // Step 1.d. 230 millisecond = std::clamp(millisecond, 0.0, 999.0); 231 232 // Step 1.e. 233 microsecond = std::clamp(microsecond, 0.0, 999.0); 234 235 // Step 1.f. 236 nanosecond = std::clamp(nanosecond, 0.0, 999.0); 237 } else { 238 // Step 2.a. 239 MOZ_ASSERT(overflow == TemporalOverflow::Reject); 240 241 // Step 2.b. 242 if (!ThrowIfInvalidTime(cx, hour, minute, second, millisecond, microsecond, 243 nanosecond)) { 244 return false; 245 } 246 } 247 248 // Step 3. 249 *result = { 250 int32_t(hour), int32_t(minute), int32_t(second), 251 int32_t(millisecond), int32_t(microsecond), int32_t(nanosecond), 252 }; 253 return true; 254 } 255 256 /** 257 * CreateTemporalTime ( time [ , newTarget ] ) 258 */ 259 static PlainTimeObject* CreateTemporalTime(JSContext* cx, const CallArgs& args, 260 const Time& time) { 261 MOZ_ASSERT(IsValidTime(time)); 262 263 // Steps 1-2. 264 Rooted<JSObject*> proto(cx); 265 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainTime, 266 &proto)) { 267 return nullptr; 268 } 269 270 auto* object = NewObjectWithClassProto<PlainTimeObject>(cx, proto); 271 if (!object) { 272 return nullptr; 273 } 274 275 // Step 3. 276 auto packedTime = PackedTime::pack(time); 277 object->initFixedSlot( 278 PlainTimeObject::PACKED_TIME_SLOT, 279 DoubleValue(mozilla::BitwiseCast<double>(packedTime.value))); 280 281 // Step 4. 282 return object; 283 } 284 285 /** 286 * CreateTemporalTime ( time [ , newTarget ] ) 287 */ 288 PlainTimeObject* js::temporal::CreateTemporalTime(JSContext* cx, 289 const Time& time) { 290 MOZ_ASSERT(IsValidTime(time)); 291 292 // Steps 1-2. 293 auto* object = NewBuiltinClassInstance<PlainTimeObject>(cx); 294 if (!object) { 295 return nullptr; 296 } 297 298 // Step 3. 299 auto packedTime = PackedTime::pack(time); 300 object->initFixedSlot( 301 PlainTimeObject::PACKED_TIME_SLOT, 302 DoubleValue(mozilla::BitwiseCast<double>(packedTime.value))); 303 304 // Step 4. 305 return object; 306 } 307 308 /** 309 * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond ) 310 */ 311 template <typename IntT> 312 static TimeRecord BalanceTime(IntT hour, IntT minute, IntT second, 313 IntT millisecond, IntT microsecond, 314 IntT nanosecond) { 315 // Combined floor'ed division and modulo operation. 316 auto divmod = [](IntT dividend, int32_t divisor, int32_t* remainder) { 317 MOZ_ASSERT(divisor > 0); 318 319 IntT quotient = dividend / divisor; 320 *remainder = dividend % divisor; 321 322 // The remainder is negative, add the divisor and simulate a floor instead 323 // of trunc division. 324 if (*remainder < 0) { 325 *remainder += divisor; 326 quotient -= 1; 327 } 328 329 return quotient; 330 }; 331 332 Time time = {}; 333 334 // Steps 1-2. 335 microsecond += divmod(nanosecond, 1000, &time.nanosecond); 336 337 // Steps 3-4. 338 millisecond += divmod(microsecond, 1000, &time.microsecond); 339 340 // Steps 5-6. 341 second += divmod(millisecond, 1000, &time.millisecond); 342 343 // Steps 7-8. 344 minute += divmod(second, 60, &time.second); 345 346 // Steps 9-10. 347 hour += divmod(minute, 60, &time.minute); 348 349 // Steps 11-12. 350 int64_t days = divmod(hour, 24, &time.hour); 351 352 // Step 13. 353 MOZ_ASSERT(IsValidTime(time)); 354 return {days, time}; 355 } 356 357 /** 358 * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond ) 359 */ 360 static TimeRecord BalanceTime(int32_t hour, int32_t minute, int32_t second, 361 int32_t millisecond, int32_t microsecond, 362 int32_t nanosecond) { 363 MOZ_ASSERT(-24 < hour && hour < 2 * 24); 364 MOZ_ASSERT(-60 < minute && minute < 2 * 60); 365 MOZ_ASSERT(-60 < second && second < 2 * 60); 366 MOZ_ASSERT(-1000 < millisecond && millisecond < 2 * 1000); 367 MOZ_ASSERT(-1000 < microsecond && microsecond < 2 * 1000); 368 MOZ_ASSERT(-1000 < nanosecond && nanosecond < 2 * 1000); 369 370 return BalanceTime<int32_t>(hour, minute, second, millisecond, microsecond, 371 nanosecond); 372 } 373 374 /** 375 * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond ) 376 */ 377 TimeRecord js::temporal::BalanceTime(const Time& time, int64_t nanoseconds) { 378 MOZ_ASSERT(IsValidTime(time)); 379 MOZ_ASSERT(std::abs(nanoseconds) <= ToNanoseconds(TemporalUnit::Day)); 380 381 return ::BalanceTime<int64_t>(time.hour, time.minute, time.second, 382 time.millisecond, time.microsecond, 383 time.nanosecond + nanoseconds); 384 } 385 386 /** 387 * TimeDurationFromComponents ( hours, minutes, seconds, milliseconds, 388 * microseconds, nanoseconds ) 389 */ 390 static TimeDuration TimeDurationFromComponents(int32_t hours, int32_t minutes, 391 int32_t seconds, 392 int32_t milliseconds, 393 int32_t microseconds, 394 int32_t nanoseconds) { 395 MOZ_ASSERT(std::abs(hours) <= 23); 396 MOZ_ASSERT(std::abs(minutes) <= 59); 397 MOZ_ASSERT(std::abs(seconds) <= 59); 398 MOZ_ASSERT(std::abs(milliseconds) <= 999); 399 MOZ_ASSERT(std::abs(microseconds) <= 999); 400 MOZ_ASSERT(std::abs(nanoseconds) <= 999); 401 402 // Steps 1-5. 403 int64_t nanos = int64_t(hours); 404 nanos *= 60; 405 nanos += int64_t(minutes); 406 nanos *= 60; 407 nanos += int64_t(seconds); 408 nanos *= 1000; 409 nanos += int64_t(milliseconds); 410 nanos *= 1000; 411 nanos += int64_t(microseconds); 412 nanos *= 1000; 413 nanos += int64_t(nanoseconds); 414 MOZ_ASSERT(std::abs(nanos) < ToNanoseconds(TemporalUnit::Day)); 415 416 auto timeDuration = TimeDuration::fromNanoseconds(nanos); 417 418 // Step 6. 419 MOZ_ASSERT(IsValidTimeDuration(timeDuration)); 420 421 // Step 7. 422 return timeDuration; 423 } 424 425 /** 426 * DifferenceTime ( time1, time2 ) 427 */ 428 TimeDuration js::temporal::DifferenceTime(const Time& time1, 429 const Time& time2) { 430 MOZ_ASSERT(IsValidTime(time1)); 431 MOZ_ASSERT(IsValidTime(time2)); 432 433 // Step 1. 434 int32_t hours = time2.hour - time1.hour; 435 436 // Step 2. 437 int32_t minutes = time2.minute - time1.minute; 438 439 // Step 3. 440 int32_t seconds = time2.second - time1.second; 441 442 // Step 4. 443 int32_t milliseconds = time2.millisecond - time1.millisecond; 444 445 // Step 5. 446 int32_t microseconds = time2.microsecond - time1.microsecond; 447 448 // Step 6. 449 int32_t nanoseconds = time2.nanosecond - time1.nanosecond; 450 451 // Step 7. 452 auto result = ::TimeDurationFromComponents( 453 hours, minutes, seconds, milliseconds, microseconds, nanoseconds); 454 455 // Step 8. 456 MOZ_ASSERT(result.abs() < TimeDuration::fromDays(1)); 457 458 // Step 9. 459 return result; 460 } 461 462 /** 463 * ToTemporalTimeRecord ( temporalTimeLike [ , completeness ] ) 464 */ 465 static bool ToTemporalTimeRecord(JSContext* cx, 466 Handle<JSObject*> temporalTimeLike, 467 TemporalTimeLike* result) { 468 // Steps 1-3. (Not applicable in our implementation.) 469 470 // Step 4. 471 bool any = false; 472 473 Rooted<Value> value(cx); 474 auto getTimeProperty = [&](Handle<PropertyName*> property, const char* name, 475 double* num) { 476 if (!GetProperty(cx, temporalTimeLike, temporalTimeLike, property, 477 &value)) { 478 return false; 479 } 480 481 if (!value.isUndefined()) { 482 any = true; 483 484 if (!ToIntegerWithTruncation(cx, value, name, num)) { 485 return false; 486 } 487 } 488 return true; 489 }; 490 491 // Steps 5-6. 492 if (!getTimeProperty(cx->names().hour, "hour", &result->hour)) { 493 return false; 494 } 495 496 // Steps 7-8. 497 if (!getTimeProperty(cx->names().microsecond, "microsecond", 498 &result->microsecond)) { 499 return false; 500 } 501 502 // Steps 9-10. 503 if (!getTimeProperty(cx->names().millisecond, "millisecond", 504 &result->millisecond)) { 505 return false; 506 } 507 508 // Steps 11-12. 509 if (!getTimeProperty(cx->names().minute, "minute", &result->minute)) { 510 return false; 511 } 512 513 // Steps 13-14. 514 if (!getTimeProperty(cx->names().nanosecond, "nanosecond", 515 &result->nanosecond)) { 516 return false; 517 } 518 519 // Steps 15-16. 520 if (!getTimeProperty(cx->names().second, "second", &result->second)) { 521 return false; 522 } 523 524 // Step 17. 525 if (!any) { 526 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 527 JSMSG_TEMPORAL_PLAIN_TIME_MISSING_UNIT); 528 return false; 529 } 530 531 // Step 18. 532 return true; 533 } 534 535 struct TimeOptions { 536 TemporalOverflow overflow = TemporalOverflow::Constrain; 537 }; 538 539 /** 540 * ToTemporalTime ( item [ , options ] ) 541 */ 542 static bool ToTemporalTimeOptions(JSContext* cx, Handle<Value> options, 543 TimeOptions* result) { 544 if (options.isUndefined()) { 545 *result = {}; 546 return true; 547 } 548 549 // NOTE: |options| are only passed from `Temporal.PlainTime.from`. 550 551 Rooted<JSObject*> resolvedOptions( 552 cx, RequireObjectArg(cx, "options", "from", options)); 553 if (!resolvedOptions) { 554 return false; 555 } 556 557 auto overflow = TemporalOverflow::Constrain; 558 if (!GetTemporalOverflowOption(cx, resolvedOptions, &overflow)) { 559 return false; 560 } 561 562 *result = {overflow}; 563 return true; 564 } 565 566 /** 567 * ToTemporalTime ( item [ , options ] ) 568 */ 569 static bool ToTemporalTime(JSContext* cx, Handle<JSObject*> item, 570 Handle<Value> options, Time* result) { 571 // Step 2.a. 572 if (auto* plainTime = item->maybeUnwrapIf<PlainTimeObject>()) { 573 auto time = plainTime->time(); 574 575 // Steps 2.a.i-ii. 576 TimeOptions ignoredOptions; 577 if (!ToTemporalTimeOptions(cx, options, &ignoredOptions)) { 578 return false; 579 } 580 581 // Step 2.a.iii. 582 *result = time; 583 return true; 584 } 585 586 // Step 2.b. 587 if (auto* dateTime = item->maybeUnwrapIf<PlainDateTimeObject>()) { 588 auto time = dateTime->time(); 589 590 // Steps 2.b.i-ii. 591 TimeOptions ignoredOptions; 592 if (!ToTemporalTimeOptions(cx, options, &ignoredOptions)) { 593 return false; 594 } 595 596 // Step 2.b.iii. 597 *result = time; 598 return true; 599 } 600 601 // Step 2.c. 602 if (auto* zonedDateTime = item->maybeUnwrapIf<ZonedDateTimeObject>()) { 603 auto epochNs = zonedDateTime->epochNanoseconds(); 604 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone()); 605 606 if (!timeZone.wrap(cx)) { 607 return false; 608 } 609 610 // Steps 2.c.i. 611 ISODateTime dateTime; 612 if (!GetISODateTimeFor(cx, timeZone, epochNs, &dateTime)) { 613 return false; 614 } 615 616 // Steps 2.c.ii-iii. 617 TimeOptions ignoredOptions; 618 if (!ToTemporalTimeOptions(cx, options, &ignoredOptions)) { 619 return false; 620 } 621 622 // Step 2.c.iv. 623 *result = dateTime.time; 624 return true; 625 } 626 627 // Step 2.d. 628 TemporalTimeLike timeResult{}; 629 if (!ToTemporalTimeRecord(cx, item, &timeResult)) { 630 return false; 631 } 632 633 // Steps 2.e-f. 634 TimeOptions resolvedOptions; 635 if (!ToTemporalTimeOptions(cx, options, &resolvedOptions)) { 636 return false; 637 } 638 auto [overflow] = resolvedOptions; 639 640 // Step 2.g and 4. 641 return RegulateTime(cx, timeResult, overflow, result); 642 } 643 644 /** 645 * ToTemporalTime ( item [ , options ] ) 646 */ 647 static bool ToTemporalTime(JSContext* cx, Handle<Value> item, 648 Handle<Value> options, Time* result) { 649 // Step 1. (Not applicable in our implementation.) 650 651 // Step 2. 652 if (item.isObject()) { 653 Rooted<JSObject*> itemObj(cx, &item.toObject()); 654 return ToTemporalTime(cx, itemObj, options, result); 655 } 656 657 // Step 3.a. 658 if (!item.isString()) { 659 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item, 660 nullptr, "not a string"); 661 return false; 662 } 663 Rooted<JSString*> string(cx, item.toString()); 664 665 // Steps 3.b-e. 666 if (!ParseTemporalTimeString(cx, string, result)) { 667 return false; 668 } 669 MOZ_ASSERT(IsValidTime(*result)); 670 671 // Steps 3.f-g. 672 TimeOptions ignoredOptions; 673 if (!ToTemporalTimeOptions(cx, options, &ignoredOptions)) { 674 return false; 675 } 676 677 // Step 4. 678 return true; 679 } 680 681 /** 682 * ToTemporalTime ( item [ , options ] ) 683 */ 684 bool js::temporal::ToTemporalTime(JSContext* cx, Handle<Value> item, 685 Time* result) { 686 return ToTemporalTime(cx, item, UndefinedHandleValue, result); 687 } 688 689 /** 690 * CompareTimeRecord ( time1, time2 ) 691 */ 692 int32_t js::temporal::CompareTimeRecord(const Time& one, const Time& two) { 693 // Steps 1-2. 694 if (int32_t diff = one.hour - two.hour) { 695 return diff < 0 ? -1 : 1; 696 } 697 698 // Steps 3-4. 699 if (int32_t diff = one.minute - two.minute) { 700 return diff < 0 ? -1 : 1; 701 } 702 703 // Steps 5-6. 704 if (int32_t diff = one.second - two.second) { 705 return diff < 0 ? -1 : 1; 706 } 707 708 // Steps 7-8. 709 if (int32_t diff = one.millisecond - two.millisecond) { 710 return diff < 0 ? -1 : 1; 711 } 712 713 // Steps 9-10. 714 if (int32_t diff = one.microsecond - two.microsecond) { 715 return diff < 0 ? -1 : 1; 716 } 717 718 // Steps 11-12. 719 if (int32_t diff = one.nanosecond - two.nanosecond) { 720 return diff < 0 ? -1 : 1; 721 } 722 723 // Step 13. 724 return 0; 725 } 726 727 static int64_t TimeToNanos(const Time& time) { 728 // No overflow possible because the input is a valid time. 729 MOZ_ASSERT(IsValidTime(time)); 730 731 int64_t hour = time.hour; 732 int64_t minute = time.minute; 733 int64_t second = time.second; 734 int64_t millisecond = time.millisecond; 735 int64_t microsecond = time.microsecond; 736 int64_t nanosecond = time.nanosecond; 737 738 int64_t millis = ((hour * 60 + minute) * 60 + second) * 1000 + millisecond; 739 return (millis * 1000 + microsecond) * 1000 + nanosecond; 740 } 741 742 /** 743 * RoundTime ( time, increment, unit, roundingMode ) 744 */ 745 TimeRecord js::temporal::RoundTime(const Time& time, Increment increment, 746 TemporalUnit unit, 747 TemporalRoundingMode roundingMode) { 748 MOZ_ASSERT(IsValidTime(time)); 749 MOZ_ASSERT(unit >= TemporalUnit::Day); 750 MOZ_ASSERT_IF(unit > TemporalUnit::Day, 751 increment <= MaximumTemporalDurationRoundingIncrement(unit)); 752 MOZ_ASSERT_IF(unit == TemporalUnit::Day, increment == Increment{1}); 753 754 int32_t days = 0; 755 auto [hour, minute, second, millisecond, microsecond, nanosecond] = time; 756 757 // Steps 1-6. 758 Time quantity; 759 int32_t* result; 760 switch (unit) { 761 case TemporalUnit::Day: 762 quantity = time; 763 result = &days; 764 break; 765 case TemporalUnit::Hour: 766 quantity = time; 767 result = &hour; 768 minute = 0; 769 second = 0; 770 millisecond = 0; 771 microsecond = 0; 772 nanosecond = 0; 773 break; 774 case TemporalUnit::Minute: 775 quantity = {0, minute, second, millisecond, microsecond, nanosecond}; 776 result = &minute; 777 second = 0; 778 millisecond = 0; 779 microsecond = 0; 780 nanosecond = 0; 781 break; 782 case TemporalUnit::Second: 783 quantity = {0, 0, second, millisecond, microsecond, nanosecond}; 784 result = &second; 785 millisecond = 0; 786 microsecond = 0; 787 nanosecond = 0; 788 break; 789 case TemporalUnit::Millisecond: 790 quantity = {0, 0, 0, millisecond, microsecond, nanosecond}; 791 result = &millisecond; 792 microsecond = 0; 793 nanosecond = 0; 794 break; 795 case TemporalUnit::Microsecond: 796 quantity = {0, 0, 0, 0, microsecond, nanosecond}; 797 result = µsecond; 798 nanosecond = 0; 799 break; 800 case TemporalUnit::Nanosecond: 801 quantity = {0, 0, 0, 0, 0, nanosecond}; 802 result = &nanosecond; 803 break; 804 805 case TemporalUnit::Unset: 806 case TemporalUnit::Auto: 807 case TemporalUnit::Year: 808 case TemporalUnit::Month: 809 case TemporalUnit::Week: 810 MOZ_CRASH("unexpected temporal unit"); 811 } 812 813 int64_t quantityNs = TimeToNanos(quantity); 814 MOZ_ASSERT(0 <= quantityNs && quantityNs < ToNanoseconds(TemporalUnit::Day)); 815 816 // Step 7. 817 int64_t unitLength = ToNanoseconds(unit); 818 int64_t incrementNs = increment.value() * unitLength; 819 MOZ_ASSERT(incrementNs <= ToNanoseconds(TemporalUnit::Day), 820 "incrementNs doesn't overflow time resolution"); 821 822 // Step 8. 823 int64_t r = RoundNumberToIncrement(quantityNs, incrementNs, roundingMode) / 824 unitLength; 825 MOZ_ASSERT(r == int64_t(int32_t(r)), 826 "can't overflow when inputs are all in range"); 827 828 *result = int32_t(r); 829 830 // Step 9. 831 if (unit == TemporalUnit::Day) { 832 return {int64_t(days), {0, 0, 0, 0, 0, 0}}; 833 } 834 835 // Steps 10-16. 836 return ::BalanceTime(hour, minute, second, millisecond, microsecond, 837 nanosecond); 838 } 839 840 /** 841 * AddTime ( time, timeDuration ) 842 */ 843 TimeRecord js::temporal::AddTime(const Time& time, 844 const TimeDuration& duration) { 845 MOZ_ASSERT(IsValidTime(time)); 846 MOZ_ASSERT(IsValidTimeDuration(duration)); 847 848 auto [seconds, nanoseconds] = duration.denormalize(); 849 MOZ_ASSERT(std::abs(nanoseconds) <= 999'999'999); 850 851 // Steps 1-2. 852 return ::BalanceTime<int64_t>(time.hour, time.minute, time.second + seconds, 853 time.millisecond, time.microsecond, 854 time.nanosecond + nanoseconds); 855 } 856 857 /** 858 * DifferenceTemporalPlainTime ( operation, temporalTime, other, options ) 859 */ 860 static bool DifferenceTemporalPlainTime(JSContext* cx, 861 TemporalDifference operation, 862 const CallArgs& args) { 863 auto temporalTime = args.thisv().toObject().as<PlainTimeObject>().time(); 864 865 // Step 1. 866 Time other; 867 if (!ToTemporalTime(cx, args.get(0), &other)) { 868 return false; 869 } 870 871 // Steps 2-3. 872 DifferenceSettings settings; 873 if (args.hasDefined(1)) { 874 // Step 2. 875 Rooted<JSObject*> options( 876 cx, RequireObjectArg(cx, "options", ToName(operation), args[1])); 877 if (!options) { 878 return false; 879 } 880 881 // Step 3. 882 if (!GetDifferenceSettings(cx, operation, options, TemporalUnitGroup::Time, 883 TemporalUnit::Nanosecond, TemporalUnit::Hour, 884 &settings)) { 885 return false; 886 } 887 } else { 888 // Steps 2-3. 889 settings = { 890 TemporalUnit::Nanosecond, 891 TemporalUnit::Hour, 892 TemporalRoundingMode::Trunc, 893 Increment{1}, 894 }; 895 } 896 897 // Step 4. 898 auto timeDuration = DifferenceTime(temporalTime, other); 899 900 // Step 5. 901 timeDuration = 902 RoundTimeDuration(timeDuration, settings.roundingIncrement, 903 settings.smallestUnit, settings.roundingMode); 904 905 // Steps 6-7. 906 Duration duration; 907 if (!TemporalDurationFromInternal(cx, timeDuration, settings.largestUnit, 908 &duration)) { 909 return false; 910 } 911 912 // Step 8. 913 if (operation == TemporalDifference::Since) { 914 duration = duration.negate(); 915 } 916 917 // Step 9. 918 auto* result = CreateTemporalDuration(cx, duration); 919 if (!result) { 920 return false; 921 } 922 923 args.rval().setObject(*result); 924 return true; 925 } 926 927 /** 928 * AddDurationToTime ( operation, temporalTime, temporalDurationLike ) 929 */ 930 static bool AddDurationToTime(JSContext* cx, TemporalAddDuration operation, 931 const CallArgs& args) { 932 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 933 auto time = temporalTime->time(); 934 935 // Step 1. 936 Duration duration; 937 if (!ToTemporalDuration(cx, args.get(0), &duration)) { 938 return false; 939 } 940 941 // Step 2. 942 if (operation == TemporalAddDuration::Subtract) { 943 duration = duration.negate(); 944 } 945 946 // Step 3. (Inlined ToInternalDurationRecord) 947 auto timeDuration = TimeDurationFromComponents(duration); 948 949 // Step 4. 950 auto result = AddTime(time, timeDuration); 951 MOZ_ASSERT(IsValidTime(result.time)); 952 953 // Step 5. 954 auto* obj = CreateTemporalTime(cx, result.time); 955 if (!obj) { 956 return false; 957 } 958 959 args.rval().setObject(*obj); 960 return true; 961 } 962 963 /** 964 * Temporal.PlainTime ( [ hour [ , minute [ , second [ , millisecond [ , 965 * microsecond [ , nanosecond ] ] ] ] ] ] ) 966 */ 967 static bool PlainTimeConstructor(JSContext* cx, unsigned argc, Value* vp) { 968 CallArgs args = CallArgsFromVp(argc, vp); 969 970 // Step 1. 971 if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainTime")) { 972 return false; 973 } 974 975 // Step 2. 976 double hour = 0; 977 if (args.hasDefined(0)) { 978 if (!ToIntegerWithTruncation(cx, args[0], "hour", &hour)) { 979 return false; 980 } 981 } 982 983 // Step 3. 984 double minute = 0; 985 if (args.hasDefined(1)) { 986 if (!ToIntegerWithTruncation(cx, args[1], "minute", &minute)) { 987 return false; 988 } 989 } 990 991 // Step 4. 992 double second = 0; 993 if (args.hasDefined(2)) { 994 if (!ToIntegerWithTruncation(cx, args[2], "second", &second)) { 995 return false; 996 } 997 } 998 999 // Step 5. 1000 double millisecond = 0; 1001 if (args.hasDefined(3)) { 1002 if (!ToIntegerWithTruncation(cx, args[3], "millisecond", &millisecond)) { 1003 return false; 1004 } 1005 } 1006 1007 // Step 6. 1008 double microsecond = 0; 1009 if (args.hasDefined(4)) { 1010 if (!ToIntegerWithTruncation(cx, args[4], "microsecond", µsecond)) { 1011 return false; 1012 } 1013 } 1014 1015 // Step 7. 1016 double nanosecond = 0; 1017 if (args.hasDefined(5)) { 1018 if (!ToIntegerWithTruncation(cx, args[5], "nanosecond", &nanosecond)) { 1019 return false; 1020 } 1021 } 1022 1023 // Steps 8-9. 1024 Time time; 1025 if (!RegulateTime(cx, 1026 { 1027 hour, 1028 minute, 1029 second, 1030 millisecond, 1031 microsecond, 1032 nanosecond, 1033 }, 1034 TemporalOverflow::Reject, &time)) { 1035 return false; 1036 } 1037 1038 // Step 10. 1039 auto* temporalTime = CreateTemporalTime(cx, args, time); 1040 if (!temporalTime) { 1041 return false; 1042 } 1043 1044 args.rval().setObject(*temporalTime); 1045 return true; 1046 } 1047 1048 /** 1049 * Temporal.PlainTime.from ( item [ , options ] ) 1050 */ 1051 static bool PlainTime_from(JSContext* cx, unsigned argc, Value* vp) { 1052 CallArgs args = CallArgsFromVp(argc, vp); 1053 1054 // Step 1. 1055 Time result; 1056 if (!ToTemporalTime(cx, args.get(0), args.get(1), &result)) { 1057 return false; 1058 } 1059 MOZ_ASSERT(IsValidTime(result)); 1060 1061 auto* obj = temporal::CreateTemporalTime(cx, result); 1062 if (!obj) { 1063 return false; 1064 } 1065 1066 args.rval().setObject(*obj); 1067 return true; 1068 } 1069 1070 /** 1071 * Temporal.PlainTime.compare ( one, two ) 1072 */ 1073 static bool PlainTime_compare(JSContext* cx, unsigned argc, Value* vp) { 1074 CallArgs args = CallArgsFromVp(argc, vp); 1075 1076 // Step 1. 1077 Time one; 1078 if (!ToTemporalTime(cx, args.get(0), &one)) { 1079 return false; 1080 } 1081 1082 // Step 2. 1083 Time two; 1084 if (!ToTemporalTime(cx, args.get(1), &two)) { 1085 return false; 1086 } 1087 1088 // Step 3. 1089 args.rval().setInt32(CompareTimeRecord(one, two)); 1090 return true; 1091 } 1092 1093 /** 1094 * get Temporal.PlainTime.prototype.hour 1095 */ 1096 static bool PlainTime_hour(JSContext* cx, const CallArgs& args) { 1097 // Step 3. 1098 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1099 args.rval().setInt32(temporalTime->time().hour); 1100 return true; 1101 } 1102 1103 /** 1104 * get Temporal.PlainTime.prototype.hour 1105 */ 1106 static bool PlainTime_hour(JSContext* cx, unsigned argc, Value* vp) { 1107 // Steps 1-2. 1108 CallArgs args = CallArgsFromVp(argc, vp); 1109 return CallNonGenericMethod<IsPlainTime, PlainTime_hour>(cx, args); 1110 } 1111 1112 /** 1113 * get Temporal.PlainTime.prototype.minute 1114 */ 1115 static bool PlainTime_minute(JSContext* cx, const CallArgs& args) { 1116 // Step 3. 1117 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1118 args.rval().setInt32(temporalTime->time().minute); 1119 return true; 1120 } 1121 1122 /** 1123 * get Temporal.PlainTime.prototype.minute 1124 */ 1125 static bool PlainTime_minute(JSContext* cx, unsigned argc, Value* vp) { 1126 // Steps 1-2. 1127 CallArgs args = CallArgsFromVp(argc, vp); 1128 return CallNonGenericMethod<IsPlainTime, PlainTime_minute>(cx, args); 1129 } 1130 1131 /** 1132 * get Temporal.PlainTime.prototype.second 1133 */ 1134 static bool PlainTime_second(JSContext* cx, const CallArgs& args) { 1135 // Step 3. 1136 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1137 args.rval().setInt32(temporalTime->time().second); 1138 return true; 1139 } 1140 1141 /** 1142 * get Temporal.PlainTime.prototype.second 1143 */ 1144 static bool PlainTime_second(JSContext* cx, unsigned argc, Value* vp) { 1145 // Steps 1-2. 1146 CallArgs args = CallArgsFromVp(argc, vp); 1147 return CallNonGenericMethod<IsPlainTime, PlainTime_second>(cx, args); 1148 } 1149 1150 /** 1151 * get Temporal.PlainTime.prototype.millisecond 1152 */ 1153 static bool PlainTime_millisecond(JSContext* cx, const CallArgs& args) { 1154 // Step 3. 1155 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1156 args.rval().setInt32(temporalTime->time().millisecond); 1157 return true; 1158 } 1159 1160 /** 1161 * get Temporal.PlainTime.prototype.millisecond 1162 */ 1163 static bool PlainTime_millisecond(JSContext* cx, unsigned argc, Value* vp) { 1164 // Steps 1-2. 1165 CallArgs args = CallArgsFromVp(argc, vp); 1166 return CallNonGenericMethod<IsPlainTime, PlainTime_millisecond>(cx, args); 1167 } 1168 1169 /** 1170 * get Temporal.PlainTime.prototype.microsecond 1171 */ 1172 static bool PlainTime_microsecond(JSContext* cx, const CallArgs& args) { 1173 // Step 3. 1174 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1175 args.rval().setInt32(temporalTime->time().microsecond); 1176 return true; 1177 } 1178 1179 /** 1180 * get Temporal.PlainTime.prototype.microsecond 1181 */ 1182 static bool PlainTime_microsecond(JSContext* cx, unsigned argc, Value* vp) { 1183 // Steps 1-2. 1184 CallArgs args = CallArgsFromVp(argc, vp); 1185 return CallNonGenericMethod<IsPlainTime, PlainTime_microsecond>(cx, args); 1186 } 1187 1188 /** 1189 * get Temporal.PlainTime.prototype.nanosecond 1190 */ 1191 static bool PlainTime_nanosecond(JSContext* cx, const CallArgs& args) { 1192 // Step 3. 1193 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1194 args.rval().setInt32(temporalTime->time().nanosecond); 1195 return true; 1196 } 1197 1198 /** 1199 * get Temporal.PlainTime.prototype.nanosecond 1200 */ 1201 static bool PlainTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) { 1202 // Steps 1-2. 1203 CallArgs args = CallArgsFromVp(argc, vp); 1204 return CallNonGenericMethod<IsPlainTime, PlainTime_nanosecond>(cx, args); 1205 } 1206 1207 /** 1208 * Temporal.PlainTime.prototype.add ( temporalDurationLike ) 1209 */ 1210 static bool PlainTime_add(JSContext* cx, const CallArgs& args) { 1211 // Step 3. 1212 return AddDurationToTime(cx, TemporalAddDuration::Add, args); 1213 } 1214 1215 /** 1216 * Temporal.PlainTime.prototype.add ( temporalDurationLike ) 1217 */ 1218 static bool PlainTime_add(JSContext* cx, unsigned argc, Value* vp) { 1219 // Steps 1-2. 1220 CallArgs args = CallArgsFromVp(argc, vp); 1221 return CallNonGenericMethod<IsPlainTime, PlainTime_add>(cx, args); 1222 } 1223 1224 /** 1225 * Temporal.PlainTime.prototype.subtract ( temporalDurationLike ) 1226 */ 1227 static bool PlainTime_subtract(JSContext* cx, const CallArgs& args) { 1228 // Step 3. 1229 return AddDurationToTime(cx, TemporalAddDuration::Subtract, args); 1230 } 1231 1232 /** 1233 * Temporal.PlainTime.prototype.subtract ( temporalDurationLike ) 1234 */ 1235 static bool PlainTime_subtract(JSContext* cx, unsigned argc, Value* vp) { 1236 // Steps 1-2. 1237 CallArgs args = CallArgsFromVp(argc, vp); 1238 return CallNonGenericMethod<IsPlainTime, PlainTime_subtract>(cx, args); 1239 } 1240 1241 /** 1242 * Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] ) 1243 */ 1244 static bool PlainTime_with(JSContext* cx, const CallArgs& args) { 1245 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1246 auto time = temporalTime->time(); 1247 1248 // Step 3. 1249 Rooted<JSObject*> temporalTimeLike( 1250 cx, RequireObjectArg(cx, "temporalTimeLike", "with", args.get(0))); 1251 if (!temporalTimeLike) { 1252 return false; 1253 } 1254 if (!ThrowIfTemporalLikeObject(cx, temporalTimeLike)) { 1255 return false; 1256 } 1257 1258 // Steps 4-16. 1259 TemporalTimeLike partialTime = { 1260 double(time.hour), double(time.minute), 1261 double(time.second), double(time.millisecond), 1262 double(time.microsecond), double(time.nanosecond), 1263 }; 1264 if (!::ToTemporalTimeRecord(cx, temporalTimeLike, &partialTime)) { 1265 return false; 1266 } 1267 1268 // Steps 17-18 1269 auto overflow = TemporalOverflow::Constrain; 1270 if (args.hasDefined(1)) { 1271 // Step 17. 1272 Rooted<JSObject*> options(cx, 1273 RequireObjectArg(cx, "options", "with", args[1])); 1274 if (!options) { 1275 return false; 1276 } 1277 1278 // Step 18. 1279 if (!GetTemporalOverflowOption(cx, options, &overflow)) { 1280 return false; 1281 } 1282 } 1283 1284 // Step 19. 1285 Time result; 1286 if (!RegulateTime(cx, partialTime, overflow, &result)) { 1287 return false; 1288 } 1289 1290 // Step 20. 1291 auto* obj = CreateTemporalTime(cx, result); 1292 if (!obj) { 1293 return false; 1294 } 1295 1296 args.rval().setObject(*obj); 1297 return true; 1298 } 1299 1300 /** 1301 * Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] ) 1302 */ 1303 static bool PlainTime_with(JSContext* cx, unsigned argc, Value* vp) { 1304 // Steps 1-2. 1305 CallArgs args = CallArgsFromVp(argc, vp); 1306 return CallNonGenericMethod<IsPlainTime, PlainTime_with>(cx, args); 1307 } 1308 1309 /** 1310 * Temporal.PlainTime.prototype.until ( other [ , options ] ) 1311 */ 1312 static bool PlainTime_until(JSContext* cx, const CallArgs& args) { 1313 // Step 3. 1314 return DifferenceTemporalPlainTime(cx, TemporalDifference::Until, args); 1315 } 1316 1317 /** 1318 * Temporal.PlainTime.prototype.until ( other [ , options ] ) 1319 */ 1320 static bool PlainTime_until(JSContext* cx, unsigned argc, Value* vp) { 1321 // Steps 1-2. 1322 CallArgs args = CallArgsFromVp(argc, vp); 1323 return CallNonGenericMethod<IsPlainTime, PlainTime_until>(cx, args); 1324 } 1325 1326 /** 1327 * Temporal.PlainTime.prototype.since ( other [ , options ] ) 1328 */ 1329 static bool PlainTime_since(JSContext* cx, const CallArgs& args) { 1330 // Step 3. 1331 return DifferenceTemporalPlainTime(cx, TemporalDifference::Since, args); 1332 } 1333 1334 /** 1335 * Temporal.PlainTime.prototype.since ( other [ , options ] ) 1336 */ 1337 static bool PlainTime_since(JSContext* cx, unsigned argc, Value* vp) { 1338 // Steps 1-2. 1339 CallArgs args = CallArgsFromVp(argc, vp); 1340 return CallNonGenericMethod<IsPlainTime, PlainTime_since>(cx, args); 1341 } 1342 1343 /** 1344 * Temporal.PlainTime.prototype.round ( roundTo ) 1345 */ 1346 static bool PlainTime_round(JSContext* cx, const CallArgs& args) { 1347 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1348 auto time = temporalTime->time(); 1349 1350 // Steps 3-13. 1351 auto smallestUnit = TemporalUnit::Unset; 1352 auto roundingMode = TemporalRoundingMode::HalfExpand; 1353 auto roundingIncrement = Increment{1}; 1354 if (args.get(0).isString()) { 1355 // Step 4. (Not applicable in our implementation.) 1356 1357 // Step 9. 1358 Rooted<JSString*> paramString(cx, args[0].toString()); 1359 if (!GetTemporalUnitValuedOption( 1360 cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) { 1361 return false; 1362 } 1363 1364 // Step 10. 1365 if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, 1366 smallestUnit, TemporalUnitGroup::Time)) { 1367 return false; 1368 } 1369 1370 // Steps 6-8 and 11-13. (Implicit) 1371 } else { 1372 // Steps 3 and 5. 1373 Rooted<JSObject*> options( 1374 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0))); 1375 if (!options) { 1376 return false; 1377 } 1378 1379 // Steps 6-7. 1380 if (!GetRoundingIncrementOption(cx, options, &roundingIncrement)) { 1381 return false; 1382 } 1383 1384 // Step 8. 1385 if (!GetRoundingModeOption(cx, options, &roundingMode)) { 1386 return false; 1387 } 1388 1389 // Step 9. 1390 if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, 1391 &smallestUnit)) { 1392 return false; 1393 } 1394 1395 if (smallestUnit == TemporalUnit::Unset) { 1396 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1397 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit"); 1398 return false; 1399 } 1400 1401 // Step 10. 1402 if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, 1403 smallestUnit, TemporalUnitGroup::Time)) { 1404 return false; 1405 } 1406 1407 // Steps 11-12. 1408 auto maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit); 1409 1410 // Step 13. 1411 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum, 1412 false)) { 1413 return false; 1414 } 1415 } 1416 1417 // Step 14. 1418 auto result = RoundTime(time, roundingIncrement, smallestUnit, roundingMode); 1419 1420 // Step 15. 1421 auto* obj = CreateTemporalTime(cx, result.time); 1422 if (!obj) { 1423 return false; 1424 } 1425 1426 args.rval().setObject(*obj); 1427 return true; 1428 } 1429 1430 /** 1431 * Temporal.PlainTime.prototype.round ( roundTo ) 1432 */ 1433 static bool PlainTime_round(JSContext* cx, unsigned argc, Value* vp) { 1434 // Steps 1-2. 1435 CallArgs args = CallArgsFromVp(argc, vp); 1436 return CallNonGenericMethod<IsPlainTime, PlainTime_round>(cx, args); 1437 } 1438 1439 /** 1440 * Temporal.PlainTime.prototype.equals ( other ) 1441 */ 1442 static bool PlainTime_equals(JSContext* cx, const CallArgs& args) { 1443 auto temporalTime = args.thisv().toObject().as<PlainTimeObject>().time(); 1444 1445 // Step 3. 1446 Time other; 1447 if (!ToTemporalTime(cx, args.get(0), &other)) { 1448 return false; 1449 } 1450 1451 // Steps 4-5. 1452 args.rval().setBoolean(temporalTime == other); 1453 return true; 1454 } 1455 1456 /** 1457 * Temporal.PlainTime.prototype.equals ( other ) 1458 */ 1459 static bool PlainTime_equals(JSContext* cx, unsigned argc, Value* vp) { 1460 // Steps 1-2. 1461 CallArgs args = CallArgsFromVp(argc, vp); 1462 return CallNonGenericMethod<IsPlainTime, PlainTime_equals>(cx, args); 1463 } 1464 1465 /** 1466 * Temporal.PlainTime.prototype.toString ( [ options ] ) 1467 */ 1468 static bool PlainTime_toString(JSContext* cx, const CallArgs& args) { 1469 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1470 auto time = temporalTime->time(); 1471 1472 SecondsStringPrecision precision = {Precision::Auto(), 1473 TemporalUnit::Nanosecond, Increment{1}}; 1474 auto roundingMode = TemporalRoundingMode::Trunc; 1475 if (args.hasDefined(0)) { 1476 // Step 3. 1477 Rooted<JSObject*> options( 1478 cx, RequireObjectArg(cx, "options", "toString", args[0])); 1479 if (!options) { 1480 return false; 1481 } 1482 1483 // Steps 4-5. 1484 auto digits = Precision::Auto(); 1485 if (!GetTemporalFractionalSecondDigitsOption(cx, options, &digits)) { 1486 return false; 1487 } 1488 1489 // Step 6. 1490 if (!GetRoundingModeOption(cx, options, &roundingMode)) { 1491 return false; 1492 } 1493 1494 // Step 7. 1495 auto smallestUnit = TemporalUnit::Unset; 1496 if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, 1497 &smallestUnit)) { 1498 return false; 1499 } 1500 1501 // Step 8. 1502 if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, 1503 smallestUnit, TemporalUnitGroup::Time)) { 1504 return false; 1505 } 1506 1507 // Step 9. 1508 if (smallestUnit == TemporalUnit::Hour) { 1509 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1510 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour", 1511 "smallestUnit"); 1512 return false; 1513 } 1514 1515 // Step 10. 1516 precision = ToSecondsStringPrecision(smallestUnit, digits); 1517 } 1518 1519 // Step 11. 1520 auto roundedTime = 1521 RoundTime(time, precision.increment, precision.unit, roundingMode); 1522 1523 // Step 12. 1524 JSString* str = TimeRecordToString(cx, roundedTime.time, precision.precision); 1525 if (!str) { 1526 return false; 1527 } 1528 1529 args.rval().setString(str); 1530 return true; 1531 } 1532 1533 /** 1534 * Temporal.PlainTime.prototype.toString ( [ options ] ) 1535 */ 1536 static bool PlainTime_toString(JSContext* cx, unsigned argc, Value* vp) { 1537 // Steps 1-2. 1538 CallArgs args = CallArgsFromVp(argc, vp); 1539 return CallNonGenericMethod<IsPlainTime, PlainTime_toString>(cx, args); 1540 } 1541 1542 /** 1543 * Temporal.PlainTime.prototype.toLocaleString ( [ locales [ , options ] ] ) 1544 */ 1545 static bool PlainTime_toLocaleString(JSContext* cx, const CallArgs& args) { 1546 // Steps 3-4. 1547 return intl::TemporalObjectToLocaleString(cx, args, 1548 intl::DateTimeFormatKind::Time); 1549 } 1550 1551 /** 1552 * Temporal.PlainTime.prototype.toLocaleString ( [ locales [ , options ] ] ) 1553 */ 1554 static bool PlainTime_toLocaleString(JSContext* cx, unsigned argc, Value* vp) { 1555 // Steps 1-2. 1556 CallArgs args = CallArgsFromVp(argc, vp); 1557 return CallNonGenericMethod<IsPlainTime, PlainTime_toLocaleString>(cx, args); 1558 } 1559 1560 /** 1561 * Temporal.PlainTime.prototype.toJSON ( ) 1562 */ 1563 static bool PlainTime_toJSON(JSContext* cx, const CallArgs& args) { 1564 auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); 1565 auto time = temporalTime->time(); 1566 1567 // Step 3. 1568 JSString* str = TimeRecordToString(cx, time, Precision::Auto()); 1569 if (!str) { 1570 return false; 1571 } 1572 1573 args.rval().setString(str); 1574 return true; 1575 } 1576 1577 /** 1578 * Temporal.PlainTime.prototype.toJSON ( ) 1579 */ 1580 static bool PlainTime_toJSON(JSContext* cx, unsigned argc, Value* vp) { 1581 // Steps 1-2. 1582 CallArgs args = CallArgsFromVp(argc, vp); 1583 return CallNonGenericMethod<IsPlainTime, PlainTime_toJSON>(cx, args); 1584 } 1585 1586 /** 1587 * Temporal.PlainTime.prototype.valueOf ( ) 1588 */ 1589 static bool PlainTime_valueOf(JSContext* cx, unsigned argc, Value* vp) { 1590 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, 1591 "PlainTime", "primitive type"); 1592 return false; 1593 } 1594 1595 const JSClass PlainTimeObject::class_ = { 1596 "Temporal.PlainTime", 1597 JSCLASS_HAS_RESERVED_SLOTS(PlainTimeObject::SLOT_COUNT) | 1598 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainTime), 1599 JS_NULL_CLASS_OPS, 1600 &PlainTimeObject::classSpec_, 1601 }; 1602 1603 const JSClass& PlainTimeObject::protoClass_ = PlainObject::class_; 1604 1605 static const JSFunctionSpec PlainTime_methods[] = { 1606 JS_FN("from", PlainTime_from, 1, 0), 1607 JS_FN("compare", PlainTime_compare, 2, 0), 1608 JS_FS_END, 1609 }; 1610 1611 static const JSFunctionSpec PlainTime_prototype_methods[] = { 1612 JS_FN("add", PlainTime_add, 1, 0), 1613 JS_FN("subtract", PlainTime_subtract, 1, 0), 1614 JS_FN("with", PlainTime_with, 1, 0), 1615 JS_FN("until", PlainTime_until, 1, 0), 1616 JS_FN("since", PlainTime_since, 1, 0), 1617 JS_FN("round", PlainTime_round, 1, 0), 1618 JS_FN("equals", PlainTime_equals, 1, 0), 1619 JS_FN("toString", PlainTime_toString, 0, 0), 1620 JS_FN("toLocaleString", PlainTime_toLocaleString, 0, 0), 1621 JS_FN("toJSON", PlainTime_toJSON, 0, 0), 1622 JS_FN("valueOf", PlainTime_valueOf, 0, 0), 1623 JS_FS_END, 1624 }; 1625 1626 static const JSPropertySpec PlainTime_prototype_properties[] = { 1627 JS_PSG("hour", PlainTime_hour, 0), 1628 JS_PSG("minute", PlainTime_minute, 0), 1629 JS_PSG("second", PlainTime_second, 0), 1630 JS_PSG("millisecond", PlainTime_millisecond, 0), 1631 JS_PSG("microsecond", PlainTime_microsecond, 0), 1632 JS_PSG("nanosecond", PlainTime_nanosecond, 0), 1633 JS_STRING_SYM_PS(toStringTag, "Temporal.PlainTime", JSPROP_READONLY), 1634 JS_PS_END, 1635 }; 1636 1637 const ClassSpec PlainTimeObject::classSpec_ = { 1638 GenericCreateConstructor<PlainTimeConstructor, 0, gc::AllocKind::FUNCTION>, 1639 GenericCreatePrototype<PlainTimeObject>, 1640 PlainTime_methods, 1641 nullptr, 1642 PlainTime_prototype_methods, 1643 PlainTime_prototype_properties, 1644 nullptr, 1645 ClassSpec::DontDefineConstructor, 1646 };