PlainDateTime.cpp (66045B)
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/PlainDateTime.h" 8 9 #include "mozilla/Assertions.h" 10 #include "mozilla/Casting.h" 11 12 #include <algorithm> 13 14 #include "jspubtd.h" 15 #include "NamespaceImports.h" 16 17 #include "builtin/intl/DateTimeFormat.h" 18 #include "builtin/temporal/Calendar.h" 19 #include "builtin/temporal/CalendarFields.h" 20 #include "builtin/temporal/Duration.h" 21 #include "builtin/temporal/Instant.h" 22 #include "builtin/temporal/PlainDate.h" 23 #include "builtin/temporal/PlainMonthDay.h" 24 #include "builtin/temporal/PlainTime.h" 25 #include "builtin/temporal/PlainYearMonth.h" 26 #include "builtin/temporal/Temporal.h" 27 #include "builtin/temporal/TemporalParser.h" 28 #include "builtin/temporal/TemporalRoundingMode.h" 29 #include "builtin/temporal/TemporalTypes.h" 30 #include "builtin/temporal/TemporalUnit.h" 31 #include "builtin/temporal/TimeZone.h" 32 #include "builtin/temporal/ToString.h" 33 #include "builtin/temporal/ZonedDateTime.h" 34 #include "gc/AllocKind.h" 35 #include "gc/Barrier.h" 36 #include "gc/GCEnum.h" 37 #include "js/CallArgs.h" 38 #include "js/CallNonGenericMethod.h" 39 #include "js/Class.h" 40 #include "js/ErrorReport.h" 41 #include "js/friend/ErrorMessages.h" 42 #include "js/PropertyDescriptor.h" 43 #include "js/PropertySpec.h" 44 #include "js/RootingAPI.h" 45 #include "js/TypeDecls.h" 46 #include "js/Value.h" 47 #include "vm/BytecodeUtil.h" 48 #include "vm/GlobalObject.h" 49 #include "vm/JSAtomState.h" 50 #include "vm/JSContext.h" 51 #include "vm/JSObject.h" 52 #include "vm/PlainObject.h" 53 #include "vm/StringType.h" 54 55 #include "vm/JSObject-inl.h" 56 #include "vm/NativeObject-inl.h" 57 58 using namespace js; 59 using namespace js::temporal; 60 61 static inline bool IsPlainDateTime(Handle<Value> v) { 62 return v.isObject() && v.toObject().is<PlainDateTimeObject>(); 63 } 64 65 #ifdef DEBUG 66 /** 67 * IsValidISODate ( year, month, day ) 68 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ) 69 */ 70 bool js::temporal::IsValidISODateTime(const ISODateTime& isoDateTime) { 71 return IsValidISODate(isoDateTime.date) && IsValidTime(isoDateTime.time); 72 } 73 #endif 74 75 /** 76 * ISODateTimeWithinLimits ( isoDateTime ) 77 */ 78 bool js::temporal::ISODateTimeWithinLimits(const ISODateTime& isoDateTime) { 79 MOZ_ASSERT(IsValidISODateTime(isoDateTime)); 80 81 constexpr auto min = ISODate::min(); 82 constexpr auto max = ISODate::max(); 83 84 const auto& year = isoDateTime.date.year; 85 86 // Fast-path when the input is definitely in range. 87 if (min.year < year && year < max.year) { 88 return true; 89 } 90 91 // Check |isoDateTime| is within the valid limits. 92 if (year < 0) { 93 if (isoDateTime.date != min) { 94 return isoDateTime.date > min; 95 } 96 97 // Needs to be past midnight. 98 return isoDateTime.time != Time{}; 99 } 100 return isoDateTime.date <= max; 101 } 102 103 /** 104 * CreateTemporalDateTime ( isoDateTime, calendar [ , newTarget ] ) 105 */ 106 static PlainDateTimeObject* CreateTemporalDateTime( 107 JSContext* cx, const CallArgs& args, const ISODateTime& isoDateTime, 108 Handle<CalendarValue> calendar) { 109 MOZ_ASSERT(IsValidISODateTime(isoDateTime)); 110 111 // Step 1. 112 if (!ISODateTimeWithinLimits(isoDateTime)) { 113 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 114 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 115 return nullptr; 116 } 117 118 // Steps 2-3. 119 Rooted<JSObject*> proto(cx); 120 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainDateTime, 121 &proto)) { 122 return nullptr; 123 } 124 125 auto* object = NewObjectWithClassProto<PlainDateTimeObject>(cx, proto); 126 if (!object) { 127 return nullptr; 128 } 129 130 // Step 4. 131 auto packedDate = PackedDate::pack(isoDateTime.date); 132 auto packedTime = PackedTime::pack(isoDateTime.time); 133 object->initFixedSlot(PlainDateTimeObject::PACKED_DATE_SLOT, 134 PrivateUint32Value(packedDate.value)); 135 object->initFixedSlot( 136 PlainDateTimeObject::PACKED_TIME_SLOT, 137 DoubleValue(mozilla::BitwiseCast<double>(packedTime.value))); 138 139 // Step 5. 140 object->initFixedSlot(PlainDateTimeObject::CALENDAR_SLOT, 141 calendar.toSlotValue()); 142 143 // Step 6. 144 return object; 145 } 146 147 /** 148 * CreateTemporalDateTime ( isoDateTime, calendar [ , newTarget ] ) 149 */ 150 PlainDateTimeObject* js::temporal::CreateTemporalDateTime( 151 JSContext* cx, const ISODateTime& isoDateTime, 152 Handle<CalendarValue> calendar) { 153 MOZ_ASSERT(IsValidISODateTime(isoDateTime)); 154 155 // Step 1. 156 if (!ISODateTimeWithinLimits(isoDateTime)) { 157 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 158 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 159 return nullptr; 160 } 161 162 // Steps 2-3. 163 auto* object = NewBuiltinClassInstance<PlainDateTimeObject>(cx); 164 if (!object) { 165 return nullptr; 166 } 167 168 // Step 4. 169 auto packedDate = PackedDate::pack(isoDateTime.date); 170 auto packedTime = PackedTime::pack(isoDateTime.time); 171 object->initFixedSlot(PlainDateTimeObject::PACKED_DATE_SLOT, 172 PrivateUint32Value(packedDate.value)); 173 object->initFixedSlot( 174 PlainDateTimeObject::PACKED_TIME_SLOT, 175 DoubleValue(mozilla::BitwiseCast<double>(packedTime.value))); 176 177 // Step 5. 178 object->initFixedSlot(PlainDateTimeObject::CALENDAR_SLOT, 179 calendar.toSlotValue()); 180 181 // Step 6. 182 return object; 183 } 184 185 /** 186 * CreateTemporalDateTime ( isoDateTime, calendar [ , newTarget ] ) 187 */ 188 static PlainDateTimeObject* CreateTemporalDateTime( 189 JSContext* cx, Handle<PlainDateTime> dateTime) { 190 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime)); 191 return CreateTemporalDateTime(cx, dateTime, dateTime.calendar()); 192 } 193 194 /** 195 * CreateTemporalDateTime ( isoDateTime, calendar [ , newTarget ] ) 196 */ 197 static bool CreateTemporalDateTime(JSContext* cx, const ISODateTime& dateTime, 198 Handle<CalendarValue> calendar, 199 MutableHandle<PlainDateTime> result) { 200 MOZ_ASSERT(IsValidISODateTime(dateTime)); 201 202 // Step 1. 203 if (!ISODateTimeWithinLimits(dateTime)) { 204 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 205 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 206 return false; 207 } 208 209 // Step 2-6. 210 result.set(PlainDateTime{dateTime, calendar}); 211 return true; 212 } 213 214 /** 215 * InterpretTemporalDateTimeFields ( calendar, fields, overflow ) 216 */ 217 bool js::temporal::InterpretTemporalDateTimeFields( 218 JSContext* cx, Handle<CalendarValue> calendar, 219 Handle<CalendarFields> fields, TemporalOverflow overflow, 220 ISODateTime* result) { 221 // Step 1. 222 Rooted<PlainDate> temporalDate(cx); 223 if (!CalendarDateFromFields(cx, calendar, fields, overflow, &temporalDate)) { 224 return false; 225 } 226 227 // Step 2. 228 auto timeLike = TemporalTimeLike{ 229 fields.hour(), fields.minute(), fields.second(), 230 fields.millisecond(), fields.microsecond(), fields.nanosecond(), 231 }; 232 Time time; 233 if (!RegulateTime(cx, timeLike, overflow, &time)) { 234 return false; 235 } 236 237 // Step 3. 238 *result = {temporalDate.date(), time}; 239 return true; 240 } 241 242 struct DateTimeOptions { 243 TemporalOverflow overflow = TemporalOverflow::Constrain; 244 }; 245 246 /** 247 * ToTemporalDateTime ( item [ , options ] ) 248 */ 249 static bool ToTemporalDateTimeOptions(JSContext* cx, Handle<Value> options, 250 DateTimeOptions* result) { 251 if (options.isUndefined()) { 252 *result = {}; 253 return true; 254 } 255 256 // NOTE: |options| are only passed from `Temporal.PlainDateTime.from`. 257 258 Rooted<JSObject*> resolvedOptions( 259 cx, RequireObjectArg(cx, "options", "from", options)); 260 if (!resolvedOptions) { 261 return false; 262 } 263 264 auto overflow = TemporalOverflow::Constrain; 265 if (!GetTemporalOverflowOption(cx, resolvedOptions, &overflow)) { 266 return false; 267 } 268 269 *result = {overflow}; 270 return true; 271 } 272 273 /** 274 * ToTemporalDateTime ( item [ , options ] ) 275 */ 276 static bool ToTemporalDateTime(JSContext* cx, Handle<JSObject*> item, 277 Handle<Value> options, 278 MutableHandle<PlainDateTime> result) { 279 // Step 1. (Not applicable in our implementation.) 280 281 // Step 2.a. 282 if (auto* plainDateTime = item->maybeUnwrapIf<PlainDateTimeObject>()) { 283 auto dateTime = plainDateTime->dateTime(); 284 Rooted<CalendarValue> calendar(cx, plainDateTime->calendar()); 285 if (!calendar.wrap(cx)) { 286 return false; 287 } 288 289 // Steps 2.a.i-ii. 290 DateTimeOptions ignoredOptions; 291 if (!ToTemporalDateTimeOptions(cx, options, &ignoredOptions)) { 292 return false; 293 } 294 295 // Step 2.a.iii. 296 result.set(PlainDateTime{dateTime, calendar}); 297 return true; 298 } 299 300 // Step 2.b. 301 if (auto* zonedDateTime = item->maybeUnwrapIf<ZonedDateTimeObject>()) { 302 auto epochNs = zonedDateTime->epochNanoseconds(); 303 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone()); 304 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar()); 305 306 if (!timeZone.wrap(cx)) { 307 return false; 308 } 309 if (!calendar.wrap(cx)) { 310 return false; 311 } 312 313 // Step 2.b.i. 314 ISODateTime dateTime; 315 if (!GetISODateTimeFor(cx, timeZone, epochNs, &dateTime)) { 316 return false; 317 } 318 319 // Steps 2.b.ii-iii. 320 DateTimeOptions ignoredOptions; 321 if (!ToTemporalDateTimeOptions(cx, options, &ignoredOptions)) { 322 return false; 323 } 324 325 // Step 2.b.iv. 326 result.set(PlainDateTime{dateTime, calendar}); 327 return true; 328 } 329 330 // Step 2.c. 331 if (auto* plainDate = item->maybeUnwrapIf<PlainDateObject>()) { 332 auto date = plainDate->date(); 333 Rooted<CalendarValue> calendar(cx, plainDate->calendar()); 334 if (!calendar.wrap(cx)) { 335 return false; 336 } 337 338 // Steps 2.c.i-ii. 339 DateTimeOptions ignoredOptions; 340 if (!ToTemporalDateTimeOptions(cx, options, &ignoredOptions)) { 341 return false; 342 } 343 344 // Steps 2.c.iii-iv. 345 return CreateTemporalDateTime(cx, ISODateTime{date}, calendar, result); 346 } 347 348 // Step 2.d. 349 Rooted<CalendarValue> calendar(cx); 350 if (!GetTemporalCalendarWithISODefault(cx, item, &calendar)) { 351 return false; 352 } 353 354 // Step 2.e. 355 Rooted<CalendarFields> fields(cx); 356 if (!PrepareCalendarFields(cx, calendar, item, 357 { 358 CalendarField::Year, 359 CalendarField::Month, 360 CalendarField::MonthCode, 361 CalendarField::Day, 362 CalendarField::Hour, 363 CalendarField::Minute, 364 CalendarField::Second, 365 CalendarField::Millisecond, 366 CalendarField::Microsecond, 367 CalendarField::Nanosecond, 368 }, 369 &fields)) { 370 return false; 371 } 372 373 // Steps 2.f-g. 374 DateTimeOptions resolvedOptions; 375 if (!ToTemporalDateTimeOptions(cx, options, &resolvedOptions)) { 376 return false; 377 } 378 auto [overflow] = resolvedOptions; 379 380 // Step 2.h. 381 ISODateTime dateTime; 382 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, overflow, 383 &dateTime)) { 384 return false; 385 } 386 387 // Step 2.i. 388 return CreateTemporalDateTime(cx, dateTime, calendar, result); 389 } 390 391 /** 392 * ToTemporalDateTime ( item [ , options ] ) 393 */ 394 static bool ToTemporalDateTime(JSContext* cx, Handle<Value> item, 395 Handle<Value> options, 396 MutableHandle<PlainDateTime> result) { 397 // Step 1. (Not applicable) 398 399 // Step 2. 400 if (item.isObject()) { 401 Rooted<JSObject*> itemObj(cx, &item.toObject()); 402 return ToTemporalDateTime(cx, itemObj, options, result); 403 } 404 405 // Step 3. 406 if (!item.isString()) { 407 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item, 408 nullptr, "not a string"); 409 return false; 410 } 411 Rooted<JSString*> string(cx, item.toString()); 412 413 // Steps 4-5. 414 ISODateTime dateTime; 415 Rooted<JSString*> calendarString(cx); 416 if (!ParseTemporalDateTimeString(cx, string, &dateTime, &calendarString)) { 417 return false; 418 } 419 MOZ_ASSERT(IsValidISODateTime(dateTime)); 420 421 // Steps 6-8. 422 Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601)); 423 if (calendarString) { 424 if (!CanonicalizeCalendar(cx, calendarString, &calendar)) { 425 return false; 426 } 427 } 428 429 // Steps 9-10. 430 DateTimeOptions ignoredOptions; 431 if (!ToTemporalDateTimeOptions(cx, options, &ignoredOptions)) { 432 return false; 433 } 434 435 // Steps 11-13. 436 return CreateTemporalDateTime(cx, dateTime, calendar, result); 437 } 438 439 /** 440 * ToTemporalDateTime ( item [ , options ] ) 441 */ 442 static bool ToTemporalDateTime(JSContext* cx, Handle<Value> item, 443 MutableHandle<PlainDateTime> result) { 444 return ToTemporalDateTime(cx, item, UndefinedHandleValue, result); 445 } 446 447 /** 448 * CompareISODateTime ( isoDateTime1, isoDateTime2 ) 449 */ 450 static int32_t CompareISODateTime(const ISODateTime& isoDateTime1, 451 const ISODateTime& isoDateTime2) { 452 // Steps 1-2. 453 if (int32_t dateResult = 454 CompareISODate(isoDateTime1.date, isoDateTime2.date)) { 455 return dateResult; 456 } 457 458 // Steps 3. 459 return CompareTimeRecord(isoDateTime1.time, isoDateTime2.time); 460 } 461 462 /** 463 * Add24HourDaysToTimeDuration ( d, days ) 464 */ 465 static TimeDuration Add24HourDaysToTimeDuration(const TimeDuration& d, 466 int32_t days) { 467 // Step 1. 468 auto result = d + TimeDuration::fromDays(days); 469 470 // Step 2. 471 MOZ_ASSERT(result.abs() <= TimeDuration::max()); 472 473 // Step 3. 474 return result; 475 } 476 477 /** 478 * DifferenceISODateTime ( isoDateTime1, isoDateTime2, calendar, largestUnit ) 479 */ 480 static bool DifferenceISODateTime(JSContext* cx, 481 const ISODateTime& isoDateTime1, 482 const ISODateTime& isoDateTime2, 483 Handle<CalendarValue> calendar, 484 TemporalUnit largestUnit, 485 InternalDuration* result) { 486 MOZ_ASSERT(isoDateTime1 != isoDateTime2, 487 "fast-path for same date-time case handled in caller"); 488 489 // Steps 1-2. 490 MOZ_ASSERT(IsValidISODateTime(isoDateTime1)); 491 MOZ_ASSERT(IsValidISODateTime(isoDateTime2)); 492 MOZ_ASSERT(ISODateTimeWithinLimits(isoDateTime1)); 493 MOZ_ASSERT(ISODateTimeWithinLimits(isoDateTime2)); 494 495 // Step 3. 496 auto timeDuration = DifferenceTime(isoDateTime1.time, isoDateTime2.time); 497 498 // Step 4. 499 int32_t timeSign = TimeDurationSign(timeDuration); 500 501 // Step 5. 502 int32_t dateSign = CompareISODate(isoDateTime1.date, isoDateTime2.date); 503 504 // Step 6. 505 auto adjustedDate = isoDateTime2.date; 506 507 // Step 7. 508 if (timeSign == dateSign) { 509 // Step 7.a. 510 adjustedDate = BalanceISODate(adjustedDate, timeSign); 511 512 // Step 7.b. 513 timeDuration = Add24HourDaysToTimeDuration(timeDuration, -timeSign); 514 } 515 516 MOZ_ASSERT(IsValidISODate(adjustedDate)); 517 MOZ_ASSERT(ISODateWithinLimits(adjustedDate)); 518 519 // Step 8. 520 auto dateLargestUnit = std::min(TemporalUnit::Day, largestUnit); 521 522 // Step 9. 523 DateDuration dateDifference; 524 if (!CalendarDateUntil(cx, calendar, isoDateTime1.date, adjustedDate, 525 dateLargestUnit, &dateDifference)) { 526 return false; 527 } 528 529 // Steps 10. 530 if (largestUnit > TemporalUnit::Day) { 531 // Step 10.a. 532 auto days = mozilla::AssertedCast<int32_t>(dateDifference.days); 533 timeDuration = Add24HourDaysToTimeDuration(timeDuration, days); 534 535 // Step 10.b. 536 dateDifference.days = 0; 537 } 538 539 // Step 11. 540 MOZ_ASSERT( 541 DateDurationSign(dateDifference) * TimeDurationSign(timeDuration) >= 0); 542 *result = {dateDifference, timeDuration}; 543 return true; 544 } 545 546 /** 547 * RoundISODateTime ( isoDateTime, increment, unit, roundingMode ) 548 */ 549 ISODateTime js::temporal::RoundISODateTime(const ISODateTime& isoDateTime, 550 Increment increment, 551 TemporalUnit unit, 552 TemporalRoundingMode roundingMode) { 553 MOZ_ASSERT(IsValidISODateTime(isoDateTime)); 554 555 // Step 1. 556 MOZ_ASSERT(ISODateTimeWithinLimits(isoDateTime)); 557 558 // Step 2. 559 auto roundedTime = RoundTime(isoDateTime.time, increment, unit, roundingMode); 560 MOZ_ASSERT(0 <= roundedTime.days && roundedTime.days <= 1); 561 562 // Step 3. 563 auto balanceResult = BalanceISODate(isoDateTime.date, roundedTime.days); 564 565 // Step 4. 566 return {balanceResult, roundedTime.time}; 567 } 568 569 /** 570 * DifferencePlainDateTimeWithRounding ( isoDateTime1, isoDateTime2, calendar, 571 * largestUnit, roundingIncrement, smallestUnit, roundingMode ) 572 */ 573 bool js::temporal::DifferencePlainDateTimeWithRounding( 574 JSContext* cx, const ISODateTime& isoDateTime1, 575 const ISODateTime& isoDateTime2, Handle<CalendarValue> calendar, 576 const DifferenceSettings& settings, InternalDuration* result) { 577 // Step 1. 578 if (isoDateTime1 == isoDateTime2) { 579 // Step 1.a. 580 *result = {}; 581 return true; 582 } 583 584 // Step 2. 585 if (!ISODateTimeWithinLimits(isoDateTime1) || 586 !ISODateTimeWithinLimits(isoDateTime2)) { 587 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 588 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 589 return false; 590 } 591 592 // Step 3. 593 InternalDuration diff; 594 if (!DifferenceISODateTime(cx, isoDateTime1, isoDateTime2, calendar, 595 settings.largestUnit, &diff)) { 596 return false; 597 } 598 599 // Step 4. 600 if (settings.smallestUnit == TemporalUnit::Nanosecond && 601 settings.roundingIncrement == Increment{1}) { 602 *result = diff; 603 return true; 604 } 605 606 // Step 5. 607 auto originEpochNs = GetUTCEpochNanoseconds(isoDateTime1); 608 609 // Step 6. 610 auto destEpochNs = GetUTCEpochNanoseconds(isoDateTime2); 611 612 // Step 7. 613 Rooted<TimeZoneValue> timeZone(cx, TimeZoneValue{}); 614 return RoundRelativeDuration( 615 cx, diff, originEpochNs, destEpochNs, isoDateTime1, timeZone, calendar, 616 settings.largestUnit, settings.roundingIncrement, settings.smallestUnit, 617 settings.roundingMode, result); 618 } 619 620 /** 621 * DifferencePlainDateTimeWithTotal ( isoDateTime1, isoDateTime2, calendar, unit 622 * ) 623 */ 624 bool js::temporal::DifferencePlainDateTimeWithTotal( 625 JSContext* cx, const ISODateTime& isoDateTime1, 626 const ISODateTime& isoDateTime2, Handle<CalendarValue> calendar, 627 TemporalUnit unit, double* result) { 628 // Step 1. 629 if (isoDateTime1 == isoDateTime2) { 630 // Step 1.a. 631 *result = 0; 632 return true; 633 } 634 635 // Step 2. 636 if (!ISODateTimeWithinLimits(isoDateTime1) || 637 !ISODateTimeWithinLimits(isoDateTime2)) { 638 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 639 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 640 return false; 641 } 642 643 // Step 3. 644 InternalDuration diff; 645 if (!DifferenceISODateTime(cx, isoDateTime1, isoDateTime2, calendar, unit, 646 &diff)) { 647 return false; 648 } 649 650 // Step 4. (Optimized to avoid GetUTCEpochNanoseconds for non-calendar units.) 651 if (unit > TemporalUnit::Day) { 652 MOZ_ASSERT(diff.date == DateDuration{}); 653 654 // TotalRelativeDuration, steps 1-2. (Not applicable) 655 656 // TotalRelativeDuration, step 3. 657 *result = TotalTimeDuration(diff.time, unit); 658 return true; 659 } else if (unit == TemporalUnit::Day) { 660 // TotalRelativeDuration, step 1. (Not applicable) 661 662 // TotalRelativeDuration, step 2. 663 auto days = mozilla::AssertedCast<int32_t>(diff.date.days); 664 auto timeDuration = Add24HourDaysToTimeDuration(diff.time, days); 665 666 // TotalRelativeDuration, step 3. 667 *result = TotalTimeDuration(timeDuration, unit); 668 return true; 669 } 670 671 // Step 5. 672 auto originEpochNs = GetUTCEpochNanoseconds(isoDateTime1); 673 674 // Step 6. 675 auto destEpochNs = GetUTCEpochNanoseconds(isoDateTime2); 676 677 // Step 7. 678 Rooted<TimeZoneValue> timeZone(cx, TimeZoneValue{}); 679 return TotalRelativeDuration(cx, diff, originEpochNs, destEpochNs, 680 isoDateTime1, timeZone, calendar, unit, result); 681 } 682 683 /** 684 * DifferenceTemporalPlainDateTime ( operation, dateTime, other, options ) 685 */ 686 static bool DifferenceTemporalPlainDateTime(JSContext* cx, 687 TemporalDifference operation, 688 const CallArgs& args) { 689 Rooted<PlainDateTime> dateTime( 690 cx, &args.thisv().toObject().as<PlainDateTimeObject>()); 691 692 // Step 1. 693 Rooted<PlainDateTime> other(cx); 694 if (!ToTemporalDateTime(cx, args.get(0), &other)) { 695 return false; 696 } 697 698 // Step 2. 699 if (!CalendarEquals(dateTime.calendar(), other.calendar())) { 700 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 701 JSMSG_TEMPORAL_CALENDAR_INCOMPATIBLE, 702 CalendarIdentifier(dateTime.calendar()).data(), 703 CalendarIdentifier(other.calendar()).data()); 704 return false; 705 } 706 707 // Steps 3-4. 708 DifferenceSettings settings; 709 if (args.hasDefined(1)) { 710 // Step 3. 711 Rooted<JSObject*> options( 712 cx, RequireObjectArg(cx, "options", ToName(operation), args[1])); 713 if (!options) { 714 return false; 715 } 716 717 // Step 4. 718 if (!GetDifferenceSettings( 719 cx, operation, options, TemporalUnitGroup::DateTime, 720 TemporalUnit::Nanosecond, TemporalUnit::Day, &settings)) { 721 return false; 722 } 723 } else { 724 // Steps 3-4. 725 settings = { 726 TemporalUnit::Nanosecond, 727 TemporalUnit::Day, 728 TemporalRoundingMode::Trunc, 729 Increment{1}, 730 }; 731 } 732 733 // Step 5. 734 if (dateTime.dateTime() == other.dateTime()) { 735 auto* obj = CreateTemporalDuration(cx, {}); 736 if (!obj) { 737 return false; 738 } 739 740 args.rval().setObject(*obj); 741 return true; 742 } 743 744 // Step 6. 745 InternalDuration internalDuration; 746 if (!DifferencePlainDateTimeWithRounding(cx, dateTime, other, 747 dateTime.calendar(), settings, 748 &internalDuration)) { 749 return false; 750 } 751 MOZ_ASSERT(IsValidDuration(internalDuration)); 752 753 // Step 7. 754 Duration result; 755 if (!TemporalDurationFromInternal(cx, internalDuration, settings.largestUnit, 756 &result)) { 757 return false; 758 } 759 MOZ_ASSERT(IsValidDuration(result)); 760 761 // Step 8. 762 if (operation == TemporalDifference::Since) { 763 result = result.negate(); 764 } 765 766 // Step 9. 767 auto* obj = CreateTemporalDuration(cx, result); 768 if (!obj) { 769 return false; 770 } 771 772 args.rval().setObject(*obj); 773 return true; 774 } 775 776 /** 777 * AddDurationToDateTime ( operation, dateTime, temporalDurationLike, options ) 778 */ 779 static bool AddDurationToDateTime(JSContext* cx, TemporalAddDuration operation, 780 const CallArgs& args) { 781 Rooted<PlainDateTime> dateTime( 782 cx, &args.thisv().toObject().as<PlainDateTimeObject>()); 783 784 // Step 1. 785 Duration duration; 786 if (!ToTemporalDuration(cx, args.get(0), &duration)) { 787 return false; 788 } 789 790 // Step 2. 791 if (operation == TemporalAddDuration::Subtract) { 792 duration = duration.negate(); 793 } 794 795 // Steps 3-4. 796 auto overflow = TemporalOverflow::Constrain; 797 if (args.hasDefined(1)) { 798 // Step 3. 799 Rooted<JSObject*> options( 800 cx, RequireObjectArg(cx, "options", ToName(operation), args[1])); 801 if (!options) { 802 return false; 803 } 804 805 // Step 4. 806 if (!GetTemporalOverflowOption(cx, options, &overflow)) { 807 return false; 808 } 809 } 810 811 // Step 5. 812 auto internalDuration = ToInternalDurationRecordWith24HourDays(duration); 813 MOZ_ASSERT(IsValidDuration(internalDuration)); 814 815 // Step 6. 816 auto timeResult = AddTime(dateTime.time(), internalDuration.time); 817 818 // Step 7. (Inlined AdjustDateDurationRecord) 819 if (std::abs(timeResult.days) > TimeDuration::max().toDays()) { 820 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 821 JSMSG_TEMPORAL_DURATION_INVALID_NORMALIZED_TIME); 822 return false; 823 } 824 auto dateDuration = DateDuration{ 825 internalDuration.date.years, 826 internalDuration.date.months, 827 internalDuration.date.weeks, 828 timeResult.days, 829 }; 830 MOZ_ASSERT(IsValidDuration(dateDuration)); 831 832 // Step 8. 833 ISODate addedDate; 834 if (!CalendarDateAdd(cx, dateTime.calendar(), dateTime.date(), dateDuration, 835 overflow, &addedDate)) { 836 return false; 837 } 838 839 // Step 9. 840 auto result = ISODateTime{addedDate, timeResult.time}; 841 MOZ_ASSERT(IsValidISODateTime(result)); 842 843 // Step 10. 844 auto* obj = CreateTemporalDateTime(cx, result, dateTime.calendar()); 845 if (!obj) { 846 return false; 847 } 848 849 args.rval().setObject(*obj); 850 return true; 851 } 852 853 /** 854 * Temporal.PlainDateTime ( isoYear, isoMonth, isoDay [ , hour [ , minute [ , 855 * second [ , millisecond [ , microsecond [ , nanosecond [ , calendar ] ] ] ] ] 856 * ] ] ) 857 */ 858 static bool PlainDateTimeConstructor(JSContext* cx, unsigned argc, Value* vp) { 859 CallArgs args = CallArgsFromVp(argc, vp); 860 861 // Step 1. 862 if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainDateTime")) { 863 return false; 864 } 865 866 // Step 2. 867 double isoYear; 868 if (!ToIntegerWithTruncation(cx, args.get(0), "year", &isoYear)) { 869 return false; 870 } 871 872 // Step 3. 873 double isoMonth; 874 if (!ToIntegerWithTruncation(cx, args.get(1), "month", &isoMonth)) { 875 return false; 876 } 877 878 // Step 4. 879 double isoDay; 880 if (!ToIntegerWithTruncation(cx, args.get(2), "day", &isoDay)) { 881 return false; 882 } 883 884 // Step 5. 885 double hour = 0; 886 if (args.hasDefined(3)) { 887 if (!ToIntegerWithTruncation(cx, args[3], "hour", &hour)) { 888 return false; 889 } 890 } 891 892 // Step 6. 893 double minute = 0; 894 if (args.hasDefined(4)) { 895 if (!ToIntegerWithTruncation(cx, args[4], "minute", &minute)) { 896 return false; 897 } 898 } 899 900 // Step 7. 901 double second = 0; 902 if (args.hasDefined(5)) { 903 if (!ToIntegerWithTruncation(cx, args[5], "second", &second)) { 904 return false; 905 } 906 } 907 908 // Step 8. 909 double millisecond = 0; 910 if (args.hasDefined(6)) { 911 if (!ToIntegerWithTruncation(cx, args[6], "millisecond", &millisecond)) { 912 return false; 913 } 914 } 915 916 // Step 9. 917 double microsecond = 0; 918 if (args.hasDefined(7)) { 919 if (!ToIntegerWithTruncation(cx, args[7], "microsecond", µsecond)) { 920 return false; 921 } 922 } 923 924 // Step 10. 925 double nanosecond = 0; 926 if (args.hasDefined(8)) { 927 if (!ToIntegerWithTruncation(cx, args[8], "nanosecond", &nanosecond)) { 928 return false; 929 } 930 } 931 932 // Steps 11-13. 933 Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601)); 934 if (args.hasDefined(9)) { 935 // Step 12. 936 if (!args[9].isString()) { 937 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, args[9], 938 nullptr, "not a string"); 939 return false; 940 } 941 942 // Step 13. 943 Rooted<JSString*> calendarString(cx, args[9].toString()); 944 if (!CanonicalizeCalendar(cx, calendarString, &calendar)) { 945 return false; 946 } 947 } 948 949 // Step 14. 950 if (!ThrowIfInvalidISODate(cx, isoYear, isoMonth, isoDay)) { 951 return false; 952 } 953 954 // Step 15. 955 auto isoDate = ISODate{int32_t(isoYear), int32_t(isoMonth), int32_t(isoDay)}; 956 957 // Step 16. 958 if (!ThrowIfInvalidTime(cx, hour, minute, second, millisecond, microsecond, 959 nanosecond)) { 960 return false; 961 } 962 963 // Step 17. 964 auto time = 965 Time{int32_t(hour), int32_t(minute), int32_t(second), 966 int32_t(millisecond), int32_t(microsecond), int32_t(nanosecond)}; 967 968 // Step 18. 969 auto isoDateTime = ISODateTime{isoDate, time}; 970 971 // Step 19. 972 auto* temporalDateTime = 973 CreateTemporalDateTime(cx, args, isoDateTime, calendar); 974 if (!temporalDateTime) { 975 return false; 976 } 977 978 args.rval().setObject(*temporalDateTime); 979 return true; 980 } 981 982 /** 983 * Temporal.PlainDateTime.from ( item [ , options ] ) 984 */ 985 static bool PlainDateTime_from(JSContext* cx, unsigned argc, Value* vp) { 986 CallArgs args = CallArgsFromVp(argc, vp); 987 988 // Step 1. 989 Rooted<PlainDateTime> dateTime(cx); 990 if (!ToTemporalDateTime(cx, args.get(0), args.get(1), &dateTime)) { 991 return false; 992 } 993 994 auto* result = CreateTemporalDateTime(cx, dateTime); 995 if (!result) { 996 return false; 997 } 998 999 args.rval().setObject(*result); 1000 return true; 1001 } 1002 1003 /** 1004 * Temporal.PlainDateTime.compare ( one, two ) 1005 */ 1006 static bool PlainDateTime_compare(JSContext* cx, unsigned argc, Value* vp) { 1007 CallArgs args = CallArgsFromVp(argc, vp); 1008 1009 // Step 1. 1010 Rooted<PlainDateTime> one(cx); 1011 if (!ToTemporalDateTime(cx, args.get(0), &one)) { 1012 return false; 1013 } 1014 1015 // Step 2. 1016 Rooted<PlainDateTime> two(cx); 1017 if (!ToTemporalDateTime(cx, args.get(1), &two)) { 1018 return false; 1019 } 1020 1021 // Step 3. 1022 args.rval().setInt32(CompareISODateTime(one, two)); 1023 return true; 1024 } 1025 1026 /** 1027 * get Temporal.PlainDateTime.prototype.calendarId 1028 */ 1029 static bool PlainDateTime_calendarId(JSContext* cx, const CallArgs& args) { 1030 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1031 1032 // Step 3. 1033 auto* str = 1034 NewStringCopy<CanGC>(cx, CalendarIdentifier(dateTime->calendar())); 1035 if (!str) { 1036 return false; 1037 } 1038 1039 args.rval().setString(str); 1040 return true; 1041 } 1042 1043 /** 1044 * get Temporal.PlainDateTime.prototype.calendarId 1045 */ 1046 static bool PlainDateTime_calendarId(JSContext* cx, unsigned argc, Value* vp) { 1047 // Steps 1-2. 1048 CallArgs args = CallArgsFromVp(argc, vp); 1049 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_calendarId>(cx, 1050 args); 1051 } 1052 1053 /** 1054 * get Temporal.PlainDateTime.prototype.era 1055 */ 1056 static bool PlainDateTime_era(JSContext* cx, const CallArgs& args) { 1057 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1058 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1059 1060 // Step 3. 1061 return CalendarEra(cx, calendar, dateTime->date(), args.rval()); 1062 } 1063 1064 /** 1065 * get Temporal.PlainDateTime.prototype.era 1066 */ 1067 static bool PlainDateTime_era(JSContext* cx, unsigned argc, Value* vp) { 1068 // Steps 1-2. 1069 CallArgs args = CallArgsFromVp(argc, vp); 1070 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_era>(cx, args); 1071 } 1072 1073 /** 1074 * get Temporal.PlainDateTime.prototype.eraYear 1075 */ 1076 static bool PlainDateTime_eraYear(JSContext* cx, const CallArgs& args) { 1077 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1078 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1079 1080 // Steps 3-5. 1081 return CalendarEraYear(cx, calendar, dateTime->date(), args.rval()); 1082 } 1083 1084 /** 1085 * get Temporal.PlainDateTime.prototype.eraYear 1086 */ 1087 static bool PlainDateTime_eraYear(JSContext* cx, unsigned argc, Value* vp) { 1088 // Steps 1-2. 1089 CallArgs args = CallArgsFromVp(argc, vp); 1090 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_eraYear>(cx, args); 1091 } 1092 1093 /** 1094 * get Temporal.PlainDateTime.prototype.year 1095 */ 1096 static bool PlainDateTime_year(JSContext* cx, const CallArgs& args) { 1097 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1098 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1099 1100 // Step 3. 1101 return CalendarYear(cx, calendar, dateTime->date(), args.rval()); 1102 } 1103 1104 /** 1105 * get Temporal.PlainDateTime.prototype.year 1106 */ 1107 static bool PlainDateTime_year(JSContext* cx, unsigned argc, Value* vp) { 1108 // Steps 1-2. 1109 CallArgs args = CallArgsFromVp(argc, vp); 1110 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_year>(cx, args); 1111 } 1112 1113 /** 1114 * get Temporal.PlainDateTime.prototype.month 1115 */ 1116 static bool PlainDateTime_month(JSContext* cx, const CallArgs& args) { 1117 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1118 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1119 1120 // Step 3. 1121 return CalendarMonth(cx, calendar, dateTime->date(), args.rval()); 1122 } 1123 1124 /** 1125 * get Temporal.PlainDateTime.prototype.month 1126 */ 1127 static bool PlainDateTime_month(JSContext* cx, unsigned argc, Value* vp) { 1128 // Steps 1-2. 1129 CallArgs args = CallArgsFromVp(argc, vp); 1130 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_month>(cx, args); 1131 } 1132 1133 /** 1134 * get Temporal.PlainDateTime.prototype.monthCode 1135 */ 1136 static bool PlainDateTime_monthCode(JSContext* cx, const CallArgs& args) { 1137 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1138 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1139 1140 // Step 3. 1141 return CalendarMonthCode(cx, calendar, dateTime->date(), args.rval()); 1142 } 1143 1144 /** 1145 * get Temporal.PlainDateTime.prototype.monthCode 1146 */ 1147 static bool PlainDateTime_monthCode(JSContext* cx, unsigned argc, Value* vp) { 1148 // Steps 1-2. 1149 CallArgs args = CallArgsFromVp(argc, vp); 1150 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthCode>(cx, 1151 args); 1152 } 1153 1154 /** 1155 * get Temporal.PlainDateTime.prototype.day 1156 */ 1157 static bool PlainDateTime_day(JSContext* cx, const CallArgs& args) { 1158 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1159 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1160 1161 // Step 3. 1162 return CalendarDay(cx, calendar, dateTime->date(), args.rval()); 1163 } 1164 1165 /** 1166 * get Temporal.PlainDateTime.prototype.day 1167 */ 1168 static bool PlainDateTime_day(JSContext* cx, unsigned argc, Value* vp) { 1169 // Steps 1-2. 1170 CallArgs args = CallArgsFromVp(argc, vp); 1171 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_day>(cx, args); 1172 } 1173 1174 /** 1175 * get Temporal.PlainDateTime.prototype.hour 1176 */ 1177 static bool PlainDateTime_hour(JSContext* cx, const CallArgs& args) { 1178 // Step 3. 1179 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1180 args.rval().setInt32(dateTime->time().hour); 1181 return true; 1182 } 1183 1184 /** 1185 * get Temporal.PlainDateTime.prototype.hour 1186 */ 1187 static bool PlainDateTime_hour(JSContext* cx, unsigned argc, Value* vp) { 1188 // Steps 1-2. 1189 CallArgs args = CallArgsFromVp(argc, vp); 1190 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_hour>(cx, args); 1191 } 1192 1193 /** 1194 * get Temporal.PlainDateTime.prototype.minute 1195 */ 1196 static bool PlainDateTime_minute(JSContext* cx, const CallArgs& args) { 1197 // Step 3. 1198 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1199 args.rval().setInt32(dateTime->time().minute); 1200 return true; 1201 } 1202 1203 /** 1204 * get Temporal.PlainDateTime.prototype.minute 1205 */ 1206 static bool PlainDateTime_minute(JSContext* cx, unsigned argc, Value* vp) { 1207 // Steps 1-2. 1208 CallArgs args = CallArgsFromVp(argc, vp); 1209 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_minute>(cx, args); 1210 } 1211 1212 /** 1213 * get Temporal.PlainDateTime.prototype.second 1214 */ 1215 static bool PlainDateTime_second(JSContext* cx, const CallArgs& args) { 1216 // Step 3. 1217 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1218 args.rval().setInt32(dateTime->time().second); 1219 return true; 1220 } 1221 1222 /** 1223 * get Temporal.PlainDateTime.prototype.second 1224 */ 1225 static bool PlainDateTime_second(JSContext* cx, unsigned argc, Value* vp) { 1226 // Steps 1-2. 1227 CallArgs args = CallArgsFromVp(argc, vp); 1228 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_second>(cx, args); 1229 } 1230 1231 /** 1232 * get Temporal.PlainDateTime.prototype.millisecond 1233 */ 1234 static bool PlainDateTime_millisecond(JSContext* cx, const CallArgs& args) { 1235 // Step 3. 1236 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1237 args.rval().setInt32(dateTime->time().millisecond); 1238 return true; 1239 } 1240 1241 /** 1242 * get Temporal.PlainDateTime.prototype.millisecond 1243 */ 1244 static bool PlainDateTime_millisecond(JSContext* cx, unsigned argc, Value* vp) { 1245 // Steps 1-2. 1246 CallArgs args = CallArgsFromVp(argc, vp); 1247 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_millisecond>(cx, 1248 args); 1249 } 1250 1251 /** 1252 * get Temporal.PlainDateTime.prototype.microsecond 1253 */ 1254 static bool PlainDateTime_microsecond(JSContext* cx, const CallArgs& args) { 1255 // Step 3. 1256 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1257 args.rval().setInt32(dateTime->time().microsecond); 1258 return true; 1259 } 1260 1261 /** 1262 * get Temporal.PlainDateTime.prototype.microsecond 1263 */ 1264 static bool PlainDateTime_microsecond(JSContext* cx, unsigned argc, Value* vp) { 1265 // Steps 1-2. 1266 CallArgs args = CallArgsFromVp(argc, vp); 1267 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_microsecond>(cx, 1268 args); 1269 } 1270 1271 /** 1272 * get Temporal.PlainDateTime.prototype.nanosecond 1273 */ 1274 static bool PlainDateTime_nanosecond(JSContext* cx, const CallArgs& args) { 1275 // Step 3. 1276 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1277 args.rval().setInt32(dateTime->time().nanosecond); 1278 return true; 1279 } 1280 1281 /** 1282 * get Temporal.PlainDateTime.prototype.nanosecond 1283 */ 1284 static bool PlainDateTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) { 1285 // Steps 1-2. 1286 CallArgs args = CallArgsFromVp(argc, vp); 1287 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_nanosecond>(cx, 1288 args); 1289 } 1290 1291 /** 1292 * get Temporal.PlainDateTime.prototype.dayOfWeek 1293 */ 1294 static bool PlainDateTime_dayOfWeek(JSContext* cx, const CallArgs& args) { 1295 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1296 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1297 1298 // Step 3. 1299 return CalendarDayOfWeek(cx, calendar, dateTime->date(), args.rval()); 1300 } 1301 1302 /** 1303 * get Temporal.PlainDateTime.prototype.dayOfWeek 1304 */ 1305 static bool PlainDateTime_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) { 1306 // Steps 1-2. 1307 CallArgs args = CallArgsFromVp(argc, vp); 1308 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfWeek>(cx, 1309 args); 1310 } 1311 1312 /** 1313 * get Temporal.PlainDateTime.prototype.dayOfYear 1314 */ 1315 static bool PlainDateTime_dayOfYear(JSContext* cx, const CallArgs& args) { 1316 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1317 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1318 1319 // Step 3. 1320 return CalendarDayOfYear(cx, calendar, dateTime->date(), args.rval()); 1321 } 1322 1323 /** 1324 * get Temporal.PlainDateTime.prototype.dayOfYear 1325 */ 1326 static bool PlainDateTime_dayOfYear(JSContext* cx, unsigned argc, Value* vp) { 1327 // Steps 1-2. 1328 CallArgs args = CallArgsFromVp(argc, vp); 1329 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfYear>(cx, 1330 args); 1331 } 1332 1333 /** 1334 * get Temporal.PlainDateTime.prototype.weekOfYear 1335 */ 1336 static bool PlainDateTime_weekOfYear(JSContext* cx, const CallArgs& args) { 1337 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1338 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1339 1340 // Steps 3-5. 1341 return CalendarWeekOfYear(cx, calendar, dateTime->date(), args.rval()); 1342 } 1343 1344 /** 1345 * get Temporal.PlainDateTime.prototype.weekOfYear 1346 */ 1347 static bool PlainDateTime_weekOfYear(JSContext* cx, unsigned argc, Value* vp) { 1348 // Steps 1-2. 1349 CallArgs args = CallArgsFromVp(argc, vp); 1350 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_weekOfYear>(cx, 1351 args); 1352 } 1353 1354 /** 1355 * get Temporal.PlainDateTime.prototype.yearOfWeek 1356 */ 1357 static bool PlainDateTime_yearOfWeek(JSContext* cx, const CallArgs& args) { 1358 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1359 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1360 1361 // Steps 3-5. 1362 return CalendarYearOfWeek(cx, calendar, dateTime->date(), args.rval()); 1363 } 1364 1365 /** 1366 * get Temporal.PlainDateTime.prototype.yearOfWeek 1367 */ 1368 static bool PlainDateTime_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) { 1369 // Steps 1-2. 1370 CallArgs args = CallArgsFromVp(argc, vp); 1371 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_yearOfWeek>(cx, 1372 args); 1373 } 1374 1375 /** 1376 * get Temporal.PlainDateTime.prototype.daysInWeek 1377 */ 1378 static bool PlainDateTime_daysInWeek(JSContext* cx, const CallArgs& args) { 1379 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1380 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1381 1382 // Step 3. 1383 return CalendarDaysInWeek(cx, calendar, dateTime->date(), args.rval()); 1384 } 1385 1386 /** 1387 * get Temporal.PlainDateTime.prototype.daysInWeek 1388 */ 1389 static bool PlainDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) { 1390 // Steps 1-2. 1391 CallArgs args = CallArgsFromVp(argc, vp); 1392 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInWeek>(cx, 1393 args); 1394 } 1395 1396 /** 1397 * get Temporal.PlainDateTime.prototype.daysInMonth 1398 */ 1399 static bool PlainDateTime_daysInMonth(JSContext* cx, const CallArgs& args) { 1400 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1401 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1402 1403 // Step 3. 1404 return CalendarDaysInMonth(cx, calendar, dateTime->date(), args.rval()); 1405 } 1406 1407 /** 1408 * get Temporal.PlainDateTime.prototype.daysInMonth 1409 */ 1410 static bool PlainDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) { 1411 // Steps 1-2. 1412 CallArgs args = CallArgsFromVp(argc, vp); 1413 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInMonth>(cx, 1414 args); 1415 } 1416 1417 /** 1418 * get Temporal.PlainDateTime.prototype.daysInYear 1419 */ 1420 static bool PlainDateTime_daysInYear(JSContext* cx, const CallArgs& args) { 1421 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1422 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1423 1424 // Step 3. 1425 return CalendarDaysInYear(cx, calendar, dateTime->date(), args.rval()); 1426 } 1427 1428 /** 1429 * get Temporal.PlainDateTime.prototype.daysInYear 1430 */ 1431 static bool PlainDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) { 1432 // Steps 1-2. 1433 CallArgs args = CallArgsFromVp(argc, vp); 1434 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInYear>(cx, 1435 args); 1436 } 1437 1438 /** 1439 * get Temporal.PlainDateTime.prototype.monthsInYear 1440 */ 1441 static bool PlainDateTime_monthsInYear(JSContext* cx, const CallArgs& args) { 1442 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1443 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1444 1445 // Step 3. 1446 return CalendarMonthsInYear(cx, calendar, dateTime->date(), args.rval()); 1447 } 1448 1449 /** 1450 * get Temporal.PlainDateTime.prototype.monthsInYear 1451 */ 1452 static bool PlainDateTime_monthsInYear(JSContext* cx, unsigned argc, 1453 Value* vp) { 1454 // Steps 1-2. 1455 CallArgs args = CallArgsFromVp(argc, vp); 1456 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthsInYear>( 1457 cx, args); 1458 } 1459 1460 /** 1461 * get Temporal.PlainDateTime.prototype.inLeapYear 1462 */ 1463 static bool PlainDateTime_inLeapYear(JSContext* cx, const CallArgs& args) { 1464 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1465 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1466 1467 // Step 3. 1468 return CalendarInLeapYear(cx, calendar, dateTime->date(), args.rval()); 1469 } 1470 1471 /** 1472 * get Temporal.PlainDateTime.prototype.inLeapYear 1473 */ 1474 static bool PlainDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) { 1475 // Steps 1-2. 1476 CallArgs args = CallArgsFromVp(argc, vp); 1477 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_inLeapYear>(cx, 1478 args); 1479 } 1480 1481 /** 1482 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] ) 1483 */ 1484 static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) { 1485 Rooted<PlainDateTime> dateTime( 1486 cx, &args.thisv().toObject().as<PlainDateTimeObject>()); 1487 1488 // Step 3. 1489 Rooted<JSObject*> temporalDateTimeLike( 1490 cx, RequireObjectArg(cx, "temporalDateTimeLike", "with", args.get(0))); 1491 if (!temporalDateTimeLike) { 1492 return false; 1493 } 1494 if (!ThrowIfTemporalLikeObject(cx, temporalDateTimeLike)) { 1495 return false; 1496 } 1497 1498 // Step 4. 1499 auto calendar = dateTime.calendar(); 1500 1501 // Step 5. 1502 Rooted<CalendarFields> fields(cx); 1503 if (!ISODateToFields(cx, dateTime, &fields)) { 1504 return false; 1505 } 1506 1507 // Steps 6-11. 1508 fields.setHour(dateTime.time().hour); 1509 fields.setMinute(dateTime.time().minute); 1510 fields.setSecond(dateTime.time().second); 1511 fields.setMillisecond(dateTime.time().millisecond); 1512 fields.setMicrosecond(dateTime.time().microsecond); 1513 fields.setNanosecond(dateTime.time().nanosecond); 1514 1515 // Step 12. 1516 Rooted<CalendarFields> partialDateTime(cx); 1517 if (!PreparePartialCalendarFields(cx, calendar, temporalDateTimeLike, 1518 { 1519 CalendarField::Year, 1520 CalendarField::Month, 1521 CalendarField::MonthCode, 1522 CalendarField::Day, 1523 CalendarField::Hour, 1524 CalendarField::Minute, 1525 CalendarField::Second, 1526 CalendarField::Millisecond, 1527 CalendarField::Microsecond, 1528 CalendarField::Nanosecond, 1529 }, 1530 &partialDateTime)) { 1531 return false; 1532 } 1533 MOZ_ASSERT(!partialDateTime.keys().isEmpty()); 1534 1535 // Step 13. 1536 fields = CalendarMergeFields(calendar, fields, partialDateTime); 1537 1538 // Steps 14-15. 1539 auto overflow = TemporalOverflow::Constrain; 1540 if (args.hasDefined(1)) { 1541 // Step 14. 1542 Rooted<JSObject*> options(cx, 1543 RequireObjectArg(cx, "options", "with", args[1])); 1544 if (!options) { 1545 return false; 1546 } 1547 1548 // Step 15. 1549 if (!GetTemporalOverflowOption(cx, options, &overflow)) { 1550 return false; 1551 } 1552 } 1553 1554 // Step 16. 1555 ISODateTime result; 1556 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, overflow, 1557 &result)) { 1558 return false; 1559 } 1560 MOZ_ASSERT(IsValidISODateTime(result)); 1561 1562 // Step 21. 1563 auto* obj = CreateTemporalDateTime(cx, result, calendar); 1564 if (!obj) { 1565 return false; 1566 } 1567 1568 args.rval().setObject(*obj); 1569 return true; 1570 } 1571 1572 /** 1573 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] ) 1574 */ 1575 static bool PlainDateTime_with(JSContext* cx, unsigned argc, Value* vp) { 1576 // Steps 1-2. 1577 CallArgs args = CallArgsFromVp(argc, vp); 1578 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_with>(cx, args); 1579 } 1580 1581 /** 1582 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] ) 1583 */ 1584 static bool PlainDateTime_withPlainTime(JSContext* cx, const CallArgs& args) { 1585 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1586 auto date = temporalDateTime->date(); 1587 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar()); 1588 1589 // Step 3. (Inlined ToTemporalTimeOrMidnight) 1590 Time time = {}; 1591 if (args.hasDefined(0)) { 1592 if (!ToTemporalTime(cx, args[0], &time)) { 1593 return false; 1594 } 1595 } 1596 1597 // Step 4. 1598 auto isoDateTime = ISODateTime{date, time}; 1599 1600 // Step 5. 1601 auto* obj = CreateTemporalDateTime(cx, isoDateTime, calendar); 1602 if (!obj) { 1603 return false; 1604 } 1605 1606 args.rval().setObject(*obj); 1607 return true; 1608 } 1609 1610 /** 1611 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] ) 1612 */ 1613 static bool PlainDateTime_withPlainTime(JSContext* cx, unsigned argc, 1614 Value* vp) { 1615 // Steps 1-2. 1616 CallArgs args = CallArgsFromVp(argc, vp); 1617 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainTime>( 1618 cx, args); 1619 } 1620 1621 /** 1622 * Temporal.PlainDateTime.prototype.withCalendar ( calendar ) 1623 */ 1624 static bool PlainDateTime_withCalendar(JSContext* cx, const CallArgs& args) { 1625 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1626 auto dateTime = temporalDateTime->dateTime(); 1627 1628 // Step 3. 1629 Rooted<CalendarValue> calendar(cx); 1630 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) { 1631 return false; 1632 } 1633 1634 // Step 4. 1635 auto* result = CreateTemporalDateTime(cx, dateTime, calendar); 1636 if (!result) { 1637 return false; 1638 } 1639 1640 args.rval().setObject(*result); 1641 return true; 1642 } 1643 1644 /** 1645 * Temporal.PlainDateTime.prototype.withCalendar ( calendar ) 1646 */ 1647 static bool PlainDateTime_withCalendar(JSContext* cx, unsigned argc, 1648 Value* vp) { 1649 // Steps 1-2. 1650 CallArgs args = CallArgsFromVp(argc, vp); 1651 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withCalendar>( 1652 cx, args); 1653 } 1654 1655 /** 1656 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] ) 1657 */ 1658 static bool PlainDateTime_add(JSContext* cx, const CallArgs& args) { 1659 // Step 3. 1660 return AddDurationToDateTime(cx, TemporalAddDuration::Add, args); 1661 } 1662 1663 /** 1664 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] ) 1665 */ 1666 static bool PlainDateTime_add(JSContext* cx, unsigned argc, Value* vp) { 1667 // Steps 1-2. 1668 CallArgs args = CallArgsFromVp(argc, vp); 1669 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_add>(cx, args); 1670 } 1671 1672 /** 1673 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options 1674 * ] ) 1675 */ 1676 static bool PlainDateTime_subtract(JSContext* cx, const CallArgs& args) { 1677 // Step 3. 1678 return AddDurationToDateTime(cx, TemporalAddDuration::Subtract, args); 1679 } 1680 1681 /** 1682 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options 1683 * ] ) 1684 */ 1685 static bool PlainDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) { 1686 // Steps 1-2. 1687 CallArgs args = CallArgsFromVp(argc, vp); 1688 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_subtract>(cx, 1689 args); 1690 } 1691 1692 /** 1693 * Temporal.PlainDateTime.prototype.until ( other [ , options ] ) 1694 */ 1695 static bool PlainDateTime_until(JSContext* cx, const CallArgs& args) { 1696 // Step 3. 1697 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Until, args); 1698 } 1699 1700 /** 1701 * Temporal.PlainDateTime.prototype.until ( other [ , options ] ) 1702 */ 1703 static bool PlainDateTime_until(JSContext* cx, unsigned argc, Value* vp) { 1704 // Steps 1-2. 1705 CallArgs args = CallArgsFromVp(argc, vp); 1706 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_until>(cx, args); 1707 } 1708 1709 /** 1710 * Temporal.PlainDateTime.prototype.since ( other [ , options ] ) 1711 */ 1712 static bool PlainDateTime_since(JSContext* cx, const CallArgs& args) { 1713 // Step 3. 1714 return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Since, args); 1715 } 1716 1717 /** 1718 * Temporal.PlainDateTime.prototype.since ( other [ , options ] ) 1719 */ 1720 static bool PlainDateTime_since(JSContext* cx, unsigned argc, Value* vp) { 1721 // Steps 1-2. 1722 CallArgs args = CallArgsFromVp(argc, vp); 1723 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_since>(cx, args); 1724 } 1725 1726 /** 1727 * Temporal.PlainDateTime.prototype.round ( roundTo ) 1728 */ 1729 static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) { 1730 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1731 auto dateTime = temporalDateTime->dateTime(); 1732 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar()); 1733 1734 // Steps 3-13. 1735 auto smallestUnit = TemporalUnit::Unset; 1736 auto roundingMode = TemporalRoundingMode::HalfExpand; 1737 auto roundingIncrement = Increment{1}; 1738 if (args.get(0).isString()) { 1739 // Step 4. (Not applicable in our implementation.) 1740 1741 // Step 9. 1742 Rooted<JSString*> paramString(cx, args[0].toString()); 1743 if (!GetTemporalUnitValuedOption( 1744 cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) { 1745 return false; 1746 } 1747 1748 // Step 10. 1749 if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, 1750 smallestUnit, TemporalUnitGroup::DayTime)) { 1751 return false; 1752 } 1753 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit && 1754 smallestUnit <= TemporalUnit::Nanosecond); 1755 1756 // Steps 6-8 and 11-13. (Implicit) 1757 } else { 1758 // Steps 3 and 5. 1759 Rooted<JSObject*> roundTo( 1760 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0))); 1761 if (!roundTo) { 1762 return false; 1763 } 1764 1765 // Steps 6-7. 1766 if (!GetRoundingIncrementOption(cx, roundTo, &roundingIncrement)) { 1767 return false; 1768 } 1769 1770 // Step 8. 1771 if (!GetRoundingModeOption(cx, roundTo, &roundingMode)) { 1772 return false; 1773 } 1774 1775 // Step 9. 1776 if (!GetTemporalUnitValuedOption(cx, roundTo, TemporalUnitKey::SmallestUnit, 1777 &smallestUnit)) { 1778 return false; 1779 } 1780 1781 if (smallestUnit == TemporalUnit::Unset) { 1782 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1783 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit"); 1784 return false; 1785 } 1786 1787 // Step 10. 1788 if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, 1789 smallestUnit, TemporalUnitGroup::DayTime)) { 1790 return false; 1791 } 1792 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit && 1793 smallestUnit <= TemporalUnit::Nanosecond); 1794 1795 // Steps 11-12. 1796 auto maximum = Increment{1}; 1797 bool inclusive = true; 1798 if (smallestUnit > TemporalUnit::Day) { 1799 maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit); 1800 inclusive = false; 1801 } 1802 1803 // Step 13. 1804 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum, 1805 inclusive)) { 1806 return false; 1807 } 1808 } 1809 1810 // Step 14. 1811 if (smallestUnit == TemporalUnit::Nanosecond && 1812 roundingIncrement == Increment{1}) { 1813 auto* obj = CreateTemporalDateTime(cx, dateTime, calendar); 1814 if (!obj) { 1815 return false; 1816 } 1817 1818 args.rval().setObject(*obj); 1819 return true; 1820 } 1821 1822 // Step 15. 1823 auto result = 1824 RoundISODateTime(dateTime, roundingIncrement, smallestUnit, roundingMode); 1825 1826 // Step 16. 1827 auto* obj = CreateTemporalDateTime(cx, result, calendar); 1828 if (!obj) { 1829 return false; 1830 } 1831 1832 args.rval().setObject(*obj); 1833 return true; 1834 } 1835 1836 /** 1837 * Temporal.PlainDateTime.prototype.round ( roundTo ) 1838 */ 1839 static bool PlainDateTime_round(JSContext* cx, unsigned argc, Value* vp) { 1840 // Steps 1-2. 1841 CallArgs args = CallArgsFromVp(argc, vp); 1842 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_round>(cx, args); 1843 } 1844 1845 /** 1846 * Temporal.PlainDateTime.prototype.equals ( other ) 1847 */ 1848 static bool PlainDateTime_equals(JSContext* cx, const CallArgs& args) { 1849 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1850 auto dateTime = temporalDateTime->dateTime(); 1851 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar()); 1852 1853 // Step 3. 1854 Rooted<PlainDateTime> other(cx); 1855 if (!ToTemporalDateTime(cx, args.get(0), &other)) { 1856 return false; 1857 } 1858 1859 // Steps 4-5. 1860 bool equals = dateTime == other.dateTime() && 1861 CalendarEquals(calendar, other.calendar()); 1862 1863 args.rval().setBoolean(equals); 1864 return true; 1865 } 1866 1867 /** 1868 * Temporal.PlainDateTime.prototype.equals ( other ) 1869 */ 1870 static bool PlainDateTime_equals(JSContext* cx, unsigned argc, Value* vp) { 1871 // Steps 1-2. 1872 CallArgs args = CallArgsFromVp(argc, vp); 1873 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_equals>(cx, args); 1874 } 1875 1876 /** 1877 * Temporal.PlainDateTime.prototype.toString ( [ options ] ) 1878 */ 1879 static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) { 1880 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1881 auto dt = dateTime->dateTime(); 1882 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1883 1884 SecondsStringPrecision precision = {Precision::Auto(), 1885 TemporalUnit::Nanosecond, Increment{1}}; 1886 auto roundingMode = TemporalRoundingMode::Trunc; 1887 auto showCalendar = ShowCalendar::Auto; 1888 if (args.hasDefined(0)) { 1889 // Step 3. 1890 Rooted<JSObject*> options( 1891 cx, RequireObjectArg(cx, "options", "toString", args[0])); 1892 if (!options) { 1893 return false; 1894 } 1895 1896 // Steps 4-5. 1897 if (!GetTemporalShowCalendarNameOption(cx, options, &showCalendar)) { 1898 return false; 1899 } 1900 1901 // Step 6. 1902 auto digits = Precision::Auto(); 1903 if (!GetTemporalFractionalSecondDigitsOption(cx, options, &digits)) { 1904 return false; 1905 } 1906 1907 // Step 7. 1908 if (!GetRoundingModeOption(cx, options, &roundingMode)) { 1909 return false; 1910 } 1911 1912 // Step 8. 1913 auto smallestUnit = TemporalUnit::Unset; 1914 if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, 1915 &smallestUnit)) { 1916 return false; 1917 } 1918 1919 // Step 9. 1920 if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, 1921 smallestUnit, TemporalUnitGroup::Time)) { 1922 return false; 1923 } 1924 1925 // Step 10. 1926 if (smallestUnit == TemporalUnit::Hour) { 1927 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1928 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour", 1929 "smallestUnit"); 1930 return false; 1931 } 1932 1933 // Step 11. 1934 precision = ToSecondsStringPrecision(smallestUnit, digits); 1935 } 1936 1937 // Step 12. 1938 auto result = 1939 RoundISODateTime(dt, precision.increment, precision.unit, roundingMode); 1940 1941 // Step 13. 1942 if (!ISODateTimeWithinLimits(result)) { 1943 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1944 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 1945 return false; 1946 } 1947 1948 // Step 14. 1949 JSString* str = ISODateTimeToString(cx, result, calendar, precision.precision, 1950 showCalendar); 1951 if (!str) { 1952 return false; 1953 } 1954 1955 args.rval().setString(str); 1956 return true; 1957 } 1958 1959 /** 1960 * Temporal.PlainDateTime.prototype.toString ( [ options ] ) 1961 */ 1962 static bool PlainDateTime_toString(JSContext* cx, unsigned argc, Value* vp) { 1963 // Steps 1-2. 1964 CallArgs args = CallArgsFromVp(argc, vp); 1965 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toString>(cx, 1966 args); 1967 } 1968 1969 /** 1970 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] ) 1971 */ 1972 static bool PlainDateTime_toLocaleString(JSContext* cx, const CallArgs& args) { 1973 // Steps 3-4. 1974 return intl::TemporalObjectToLocaleString(cx, args, 1975 intl::DateTimeFormatKind::All); 1976 } 1977 1978 /** 1979 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] ) 1980 */ 1981 static bool PlainDateTime_toLocaleString(JSContext* cx, unsigned argc, 1982 Value* vp) { 1983 // Steps 1-2. 1984 CallArgs args = CallArgsFromVp(argc, vp); 1985 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toLocaleString>( 1986 cx, args); 1987 } 1988 1989 /** 1990 * Temporal.PlainDateTime.prototype.toJSON ( ) 1991 */ 1992 static bool PlainDateTime_toJSON(JSContext* cx, const CallArgs& args) { 1993 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 1994 auto dt = dateTime->dateTime(); 1995 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 1996 1997 // Step 3. 1998 JSString* str = ISODateTimeToString(cx, dt, calendar, Precision::Auto(), 1999 ShowCalendar::Auto); 2000 if (!str) { 2001 return false; 2002 } 2003 2004 args.rval().setString(str); 2005 return true; 2006 } 2007 2008 /** 2009 * Temporal.PlainDateTime.prototype.toJSON ( ) 2010 */ 2011 static bool PlainDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) { 2012 // Steps 1-2. 2013 CallArgs args = CallArgsFromVp(argc, vp); 2014 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toJSON>(cx, args); 2015 } 2016 2017 /** 2018 * Temporal.PlainDateTime.prototype.valueOf ( ) 2019 */ 2020 static bool PlainDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) { 2021 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, 2022 "PlainDateTime", "primitive type"); 2023 return false; 2024 } 2025 2026 /** 2027 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ , 2028 * options ] ) 2029 */ 2030 static bool PlainDateTime_toZonedDateTime(JSContext* cx, const CallArgs& args) { 2031 auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 2032 auto isoDateTime = temporalDateTime->dateTime(); 2033 Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar()); 2034 2035 // Step 3. 2036 Rooted<TimeZoneValue> timeZone(cx); 2037 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) { 2038 return false; 2039 } 2040 2041 auto disambiguation = TemporalDisambiguation::Compatible; 2042 if (args.hasDefined(1)) { 2043 // Step 4. 2044 Rooted<JSObject*> options( 2045 cx, RequireObjectArg(cx, "options", "toZonedDateTime", args[1])); 2046 if (!options) { 2047 return false; 2048 } 2049 2050 // Step 5. 2051 if (!GetTemporalDisambiguationOption(cx, options, &disambiguation)) { 2052 return false; 2053 } 2054 } 2055 2056 // Step 6. 2057 EpochNanoseconds epochNs; 2058 if (!GetEpochNanosecondsFor(cx, timeZone, isoDateTime, disambiguation, 2059 &epochNs)) { 2060 return false; 2061 } 2062 2063 // Step 7. 2064 auto* result = CreateTemporalZonedDateTime(cx, epochNs, timeZone, calendar); 2065 if (!result) { 2066 return false; 2067 } 2068 2069 args.rval().setObject(*result); 2070 return true; 2071 } 2072 2073 /** 2074 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ , 2075 * options ] ) 2076 */ 2077 static bool PlainDateTime_toZonedDateTime(JSContext* cx, unsigned argc, 2078 Value* vp) { 2079 // Steps 1-2. 2080 CallArgs args = CallArgsFromVp(argc, vp); 2081 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toZonedDateTime>( 2082 cx, args); 2083 } 2084 2085 /** 2086 * Temporal.PlainDateTime.prototype.toPlainDate ( ) 2087 */ 2088 static bool PlainDateTime_toPlainDate(JSContext* cx, const CallArgs& args) { 2089 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 2090 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 2091 2092 // Step 3. 2093 auto* obj = CreateTemporalDate(cx, dateTime->date(), calendar); 2094 if (!obj) { 2095 return false; 2096 } 2097 2098 args.rval().setObject(*obj); 2099 return true; 2100 } 2101 2102 /** 2103 * Temporal.PlainDateTime.prototype.toPlainDate ( ) 2104 */ 2105 static bool PlainDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) { 2106 // Steps 1-2. 2107 CallArgs args = CallArgsFromVp(argc, vp); 2108 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainDate>(cx, 2109 args); 2110 } 2111 2112 /** 2113 * Temporal.PlainDateTime.prototype.toPlainTime ( ) 2114 */ 2115 static bool PlainDateTime_toPlainTime(JSContext* cx, const CallArgs& args) { 2116 auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>(); 2117 2118 // Step 3. 2119 auto* obj = CreateTemporalTime(cx, dateTime->time()); 2120 if (!obj) { 2121 return false; 2122 } 2123 2124 args.rval().setObject(*obj); 2125 return true; 2126 } 2127 2128 /** 2129 * Temporal.PlainDateTime.prototype.toPlainTime ( ) 2130 */ 2131 static bool PlainDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) { 2132 // Steps 1-2. 2133 CallArgs args = CallArgsFromVp(argc, vp); 2134 return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainTime>(cx, 2135 args); 2136 } 2137 2138 const JSClass PlainDateTimeObject::class_ = { 2139 "Temporal.PlainDateTime", 2140 JSCLASS_HAS_RESERVED_SLOTS(PlainDateTimeObject::SLOT_COUNT) | 2141 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainDateTime), 2142 JS_NULL_CLASS_OPS, 2143 &PlainDateTimeObject::classSpec_, 2144 }; 2145 2146 const JSClass& PlainDateTimeObject::protoClass_ = PlainObject::class_; 2147 2148 static const JSFunctionSpec PlainDateTime_methods[] = { 2149 JS_FN("from", PlainDateTime_from, 1, 0), 2150 JS_FN("compare", PlainDateTime_compare, 2, 0), 2151 JS_FS_END, 2152 }; 2153 2154 static const JSFunctionSpec PlainDateTime_prototype_methods[] = { 2155 JS_FN("with", PlainDateTime_with, 1, 0), 2156 JS_FN("withPlainTime", PlainDateTime_withPlainTime, 0, 0), 2157 JS_FN("withCalendar", PlainDateTime_withCalendar, 1, 0), 2158 JS_FN("add", PlainDateTime_add, 1, 0), 2159 JS_FN("subtract", PlainDateTime_subtract, 1, 0), 2160 JS_FN("until", PlainDateTime_until, 1, 0), 2161 JS_FN("since", PlainDateTime_since, 1, 0), 2162 JS_FN("round", PlainDateTime_round, 1, 0), 2163 JS_FN("equals", PlainDateTime_equals, 1, 0), 2164 JS_FN("toString", PlainDateTime_toString, 0, 0), 2165 JS_FN("toLocaleString", PlainDateTime_toLocaleString, 0, 0), 2166 JS_FN("toJSON", PlainDateTime_toJSON, 0, 0), 2167 JS_FN("valueOf", PlainDateTime_valueOf, 0, 0), 2168 JS_FN("toZonedDateTime", PlainDateTime_toZonedDateTime, 1, 0), 2169 JS_FN("toPlainDate", PlainDateTime_toPlainDate, 0, 0), 2170 JS_FN("toPlainTime", PlainDateTime_toPlainTime, 0, 0), 2171 JS_FS_END, 2172 }; 2173 2174 static const JSPropertySpec PlainDateTime_prototype_properties[] = { 2175 JS_PSG("calendarId", PlainDateTime_calendarId, 0), 2176 JS_PSG("era", PlainDateTime_era, 0), 2177 JS_PSG("eraYear", PlainDateTime_eraYear, 0), 2178 JS_PSG("year", PlainDateTime_year, 0), 2179 JS_PSG("month", PlainDateTime_month, 0), 2180 JS_PSG("monthCode", PlainDateTime_monthCode, 0), 2181 JS_PSG("day", PlainDateTime_day, 0), 2182 JS_PSG("hour", PlainDateTime_hour, 0), 2183 JS_PSG("minute", PlainDateTime_minute, 0), 2184 JS_PSG("second", PlainDateTime_second, 0), 2185 JS_PSG("millisecond", PlainDateTime_millisecond, 0), 2186 JS_PSG("microsecond", PlainDateTime_microsecond, 0), 2187 JS_PSG("nanosecond", PlainDateTime_nanosecond, 0), 2188 JS_PSG("dayOfWeek", PlainDateTime_dayOfWeek, 0), 2189 JS_PSG("dayOfYear", PlainDateTime_dayOfYear, 0), 2190 JS_PSG("weekOfYear", PlainDateTime_weekOfYear, 0), 2191 JS_PSG("yearOfWeek", PlainDateTime_yearOfWeek, 0), 2192 JS_PSG("daysInWeek", PlainDateTime_daysInWeek, 0), 2193 JS_PSG("daysInMonth", PlainDateTime_daysInMonth, 0), 2194 JS_PSG("daysInYear", PlainDateTime_daysInYear, 0), 2195 JS_PSG("monthsInYear", PlainDateTime_monthsInYear, 0), 2196 JS_PSG("inLeapYear", PlainDateTime_inLeapYear, 0), 2197 JS_STRING_SYM_PS(toStringTag, "Temporal.PlainDateTime", JSPROP_READONLY), 2198 JS_PS_END, 2199 }; 2200 2201 const ClassSpec PlainDateTimeObject::classSpec_ = { 2202 GenericCreateConstructor<PlainDateTimeConstructor, 3, 2203 gc::AllocKind::FUNCTION>, 2204 GenericCreatePrototype<PlainDateTimeObject>, 2205 PlainDateTime_methods, 2206 nullptr, 2207 PlainDateTime_prototype_methods, 2208 PlainDateTime_prototype_properties, 2209 nullptr, 2210 ClassSpec::DontDefineConstructor, 2211 };