PlainDate.cpp (50091B)
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/PlainDate.h" 8 9 #include "mozilla/Assertions.h" 10 #include "mozilla/CheckedInt.h" 11 12 #include <cmath> 13 #include <cstdlib> 14 #include <stdint.h> 15 16 #include "jsdate.h" 17 #include "jsnum.h" 18 #include "jspubtd.h" 19 #include "jstypes.h" 20 #include "NamespaceImports.h" 21 22 #include "builtin/intl/DateTimeFormat.h" 23 #include "builtin/temporal/Calendar.h" 24 #include "builtin/temporal/CalendarFields.h" 25 #include "builtin/temporal/Duration.h" 26 #include "builtin/temporal/Instant.h" 27 #include "builtin/temporal/PlainDateTime.h" 28 #include "builtin/temporal/PlainMonthDay.h" 29 #include "builtin/temporal/PlainTime.h" 30 #include "builtin/temporal/PlainYearMonth.h" 31 #include "builtin/temporal/Temporal.h" 32 #include "builtin/temporal/TemporalParser.h" 33 #include "builtin/temporal/TemporalRoundingMode.h" 34 #include "builtin/temporal/TemporalTypes.h" 35 #include "builtin/temporal/TemporalUnit.h" 36 #include "builtin/temporal/TimeZone.h" 37 #include "builtin/temporal/ToString.h" 38 #include "builtin/temporal/ZonedDateTime.h" 39 #include "gc/AllocKind.h" 40 #include "gc/Barrier.h" 41 #include "gc/GCEnum.h" 42 #include "js/CallArgs.h" 43 #include "js/CallNonGenericMethod.h" 44 #include "js/Class.h" 45 #include "js/ErrorReport.h" 46 #include "js/friend/ErrorMessages.h" 47 #include "js/PropertyDescriptor.h" 48 #include "js/PropertySpec.h" 49 #include "js/RootingAPI.h" 50 #include "js/TypeDecls.h" 51 #include "js/Value.h" 52 #include "vm/BytecodeUtil.h" 53 #include "vm/GlobalObject.h" 54 #include "vm/JSAtomState.h" 55 #include "vm/JSContext.h" 56 #include "vm/JSObject.h" 57 #include "vm/PlainObject.h" 58 #include "vm/StringType.h" 59 60 #include "vm/JSObject-inl.h" 61 #include "vm/NativeObject-inl.h" 62 #include "vm/ObjectOperations-inl.h" 63 64 using namespace js; 65 using namespace js::temporal; 66 67 static inline bool IsPlainDate(Handle<Value> v) { 68 return v.isObject() && v.toObject().is<PlainDateObject>(); 69 } 70 71 #ifdef DEBUG 72 /** 73 * IsValidISODate ( year, month, day ) 74 */ 75 bool js::temporal::IsValidISODate(const ISODate& date) { 76 const auto& [year, month, day] = date; 77 78 // Step 1. 79 if (month < 1 || month > 12) { 80 return false; 81 } 82 83 // Step 2. 84 int32_t daysInMonth = js::temporal::ISODaysInMonth(year, month); 85 86 // Steps 3-4. 87 return 1 <= day && day <= daysInMonth; 88 } 89 #endif 90 91 /** 92 * ISODateWithinLimits ( isoDate ) 93 */ 94 bool js::temporal::ISODateWithinLimits(const ISODate& isoDate) { 95 MOZ_ASSERT(IsValidISODate(isoDate)); 96 97 constexpr auto min = ISODate::min(); 98 constexpr auto max = ISODate::max(); 99 100 const auto& year = isoDate.year; 101 102 // Fast-path when the input is definitely in range. 103 if (min.year < year && year < max.year) { 104 return true; 105 } 106 107 // Check |isoDate| is within the valid limits. 108 if (year < 0) { 109 return isoDate >= min; 110 } 111 return isoDate <= max; 112 } 113 114 static void ReportInvalidDateValue(JSContext* cx, const char* name, int32_t min, 115 int32_t max, double num) { 116 Int32ToCStringBuf minCbuf; 117 const char* minStr = Int32ToCString(&minCbuf, min); 118 119 Int32ToCStringBuf maxCbuf; 120 const char* maxStr = Int32ToCString(&maxCbuf, max); 121 122 ToCStringBuf numCbuf; 123 const char* numStr = NumberToCString(&numCbuf, num); 124 125 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 126 JSMSG_TEMPORAL_PLAIN_DATE_INVALID_VALUE, name, 127 minStr, maxStr, numStr); 128 } 129 130 template <typename T> 131 static inline bool ThrowIfInvalidDateValue(JSContext* cx, const char* name, 132 int32_t min, int32_t max, T num) { 133 if (min <= num && num <= max) { 134 return true; 135 } 136 ReportInvalidDateValue(cx, name, min, max, num); 137 return false; 138 } 139 140 /** 141 * IsValidISODate ( year, month, day ) 142 */ 143 template <typename T> 144 static bool ThrowIfInvalidISODate(JSContext* cx, T year, T month, T day) { 145 static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, double>); 146 147 // Step 1. 148 MOZ_ASSERT(IsInteger(year)); 149 MOZ_ASSERT(IsInteger(month)); 150 MOZ_ASSERT(IsInteger(day)); 151 152 if constexpr (std::is_same_v<T, double>) { 153 if (!ThrowIfInvalidDateValue(cx, "year", INT32_MIN, INT32_MAX, year)) { 154 return false; 155 } 156 } 157 158 // Step 2. 159 if (!ThrowIfInvalidDateValue(cx, "month", 1, 12, month)) { 160 return false; 161 } 162 163 // Step 3. 164 int32_t daysInMonth = 165 js::temporal::ISODaysInMonth(int32_t(year), int32_t(month)); 166 167 // Steps 4-5. 168 return ThrowIfInvalidDateValue(cx, "day", 1, daysInMonth, day); 169 } 170 171 /** 172 * IsValidISODate ( year, month, day ) 173 */ 174 bool js::temporal::ThrowIfInvalidISODate(JSContext* cx, const ISODate& date) { 175 const auto& [year, month, day] = date; 176 return ::ThrowIfInvalidISODate(cx, year, month, day); 177 } 178 179 /** 180 * IsValidISODate ( year, month, day ) 181 */ 182 bool js::temporal::ThrowIfInvalidISODate(JSContext* cx, double year, 183 double month, double day) { 184 return ::ThrowIfInvalidISODate(cx, year, month, day); 185 } 186 187 /** 188 * CreateTemporalDate ( isoDate, calendar [ , newTarget ] ) 189 */ 190 static PlainDateObject* CreateTemporalDate(JSContext* cx, const CallArgs& args, 191 const ISODate& isoDate, 192 Handle<CalendarValue> calendar) { 193 MOZ_ASSERT(IsValidISODate(isoDate)); 194 195 // Step 1. 196 if (!ISODateWithinLimits(isoDate)) { 197 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 198 JSMSG_TEMPORAL_PLAIN_DATE_INVALID); 199 return nullptr; 200 } 201 202 // Steps 2-3. 203 Rooted<JSObject*> proto(cx); 204 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainDate, 205 &proto)) { 206 return nullptr; 207 } 208 209 auto* object = NewObjectWithClassProto<PlainDateObject>(cx, proto); 210 if (!object) { 211 return nullptr; 212 } 213 214 // Step 4. 215 auto packedDate = PackedDate::pack(isoDate); 216 object->initFixedSlot(PlainDateObject::PACKED_DATE_SLOT, 217 PrivateUint32Value(packedDate.value)); 218 219 // Step 5. 220 object->initFixedSlot(PlainDateObject::CALENDAR_SLOT, calendar.toSlotValue()); 221 222 // Step 6. 223 return object; 224 } 225 226 /** 227 * CreateTemporalDate ( isoDate, calendar [ , newTarget ] ) 228 */ 229 PlainDateObject* js::temporal::CreateTemporalDate( 230 JSContext* cx, const ISODate& isoDate, Handle<CalendarValue> calendar) { 231 MOZ_ASSERT(IsValidISODate(isoDate)); 232 233 // Step 1. 234 if (!ISODateWithinLimits(isoDate)) { 235 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 236 JSMSG_TEMPORAL_PLAIN_DATE_INVALID); 237 return nullptr; 238 } 239 240 // Steps 2-3. 241 auto* object = NewBuiltinClassInstance<PlainDateObject>(cx); 242 if (!object) { 243 return nullptr; 244 } 245 246 // Step 4. 247 auto packedDate = PackedDate::pack(isoDate); 248 object->initFixedSlot(PlainDateObject::PACKED_DATE_SLOT, 249 PrivateUint32Value(packedDate.value)); 250 251 // Step 5. 252 object->initFixedSlot(PlainDateObject::CALENDAR_SLOT, calendar.toSlotValue()); 253 254 // Step 6. 255 return object; 256 } 257 258 /** 259 * CreateTemporalDate ( isoDate, calendar [ , newTarget ] ) 260 */ 261 PlainDateObject* js::temporal::CreateTemporalDate(JSContext* cx, 262 Handle<PlainDate> date) { 263 MOZ_ASSERT(ISODateWithinLimits(date)); 264 return CreateTemporalDate(cx, date, date.calendar()); 265 } 266 267 /** 268 * CreateTemporalDate ( isoDate, calendar [ , newTarget ] ) 269 */ 270 bool js::temporal::CreateTemporalDate(JSContext* cx, const ISODate& isoDate, 271 Handle<CalendarValue> calendar, 272 MutableHandle<PlainDate> result) { 273 MOZ_ASSERT(IsValidISODate(isoDate)); 274 275 // Step 1. 276 if (!ISODateWithinLimits(isoDate)) { 277 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 278 JSMSG_TEMPORAL_PLAIN_DATE_INVALID); 279 return false; 280 } 281 282 // Steps 2-6. 283 result.set(PlainDate{isoDate, calendar}); 284 return true; 285 } 286 287 struct DateOptions { 288 TemporalOverflow overflow = TemporalOverflow::Constrain; 289 }; 290 291 /** 292 * ToTemporalDate ( item [ , options ] ) 293 */ 294 static bool ToTemporalDateOptions(JSContext* cx, Handle<Value> options, 295 DateOptions* result) { 296 if (options.isUndefined()) { 297 *result = {}; 298 return true; 299 } 300 301 // NOTE: |options| are only passed from `Temporal.PlainDate.from`. 302 303 Rooted<JSObject*> resolvedOptions( 304 cx, RequireObjectArg(cx, "options", "from", options)); 305 if (!resolvedOptions) { 306 return false; 307 } 308 309 auto overflow = TemporalOverflow::Constrain; 310 if (!GetTemporalOverflowOption(cx, resolvedOptions, &overflow)) { 311 return false; 312 } 313 314 *result = {overflow}; 315 return true; 316 } 317 318 /** 319 * ToTemporalDate ( item [ , options ] ) 320 */ 321 static bool ToTemporalDate(JSContext* cx, Handle<JSObject*> item, 322 Handle<Value> options, 323 MutableHandle<PlainDate> result) { 324 // Step 1. (Not applicable in our implementation.) 325 326 // Step 2.a. 327 if (auto* plainDate = item->maybeUnwrapIf<PlainDateObject>()) { 328 auto date = plainDate->date(); 329 Rooted<CalendarValue> calendar(cx, plainDate->calendar()); 330 if (!calendar.wrap(cx)) { 331 return false; 332 } 333 334 // Steps 2.a.i-ii. 335 DateOptions ignoredOptions; 336 if (!ToTemporalDateOptions(cx, options, &ignoredOptions)) { 337 return false; 338 } 339 340 // Step 2.a.iii. 341 result.set(PlainDate{date, calendar}); 342 return true; 343 } 344 345 // Step 2.b. 346 if (auto* zonedDateTime = item->maybeUnwrapIf<ZonedDateTimeObject>()) { 347 auto epochNs = zonedDateTime->epochNanoseconds(); 348 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone()); 349 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar()); 350 351 if (!timeZone.wrap(cx)) { 352 return false; 353 } 354 if (!calendar.wrap(cx)) { 355 return false; 356 } 357 358 // Steps 2.b.ii. 359 ISODateTime isoDateTime; 360 if (!GetISODateTimeFor(cx, timeZone, epochNs, &isoDateTime)) { 361 return false; 362 } 363 364 // Steps 2.b.ii-iii. 365 DateOptions ignoredOptions; 366 if (!ToTemporalDateOptions(cx, options, &ignoredOptions)) { 367 return false; 368 } 369 370 // Step 2.b.iv. 371 result.set(PlainDate{isoDateTime.date, calendar}); 372 return true; 373 } 374 375 // Step 2.c. 376 if (auto* dateTime = item->maybeUnwrapIf<PlainDateTimeObject>()) { 377 auto date = dateTime->date(); 378 Rooted<CalendarValue> calendar(cx, dateTime->calendar()); 379 if (!calendar.wrap(cx)) { 380 return false; 381 } 382 383 // Steps 2.c.i-ii. 384 DateOptions ignoredOptions; 385 if (!ToTemporalDateOptions(cx, options, &ignoredOptions)) { 386 return false; 387 } 388 389 // Step 2.c.iii. 390 result.set(PlainDate{date, calendar}); 391 return true; 392 } 393 394 // Step 2.d. 395 Rooted<CalendarValue> calendar(cx); 396 if (!GetTemporalCalendarWithISODefault(cx, item, &calendar)) { 397 return false; 398 } 399 400 // Step 2.e. 401 Rooted<CalendarFields> fields(cx); 402 if (!PrepareCalendarFields(cx, calendar, item, 403 { 404 CalendarField::Year, 405 CalendarField::Month, 406 CalendarField::MonthCode, 407 CalendarField::Day, 408 }, 409 &fields)) { 410 return false; 411 } 412 413 // Steps 2.f-g. 414 DateOptions resolvedOptions; 415 if (!ToTemporalDateOptions(cx, options, &resolvedOptions)) { 416 return false; 417 } 418 auto [overflow] = resolvedOptions; 419 420 // Steps 2.h-i. 421 return CalendarDateFromFields(cx, calendar, fields, overflow, result); 422 } 423 424 /** 425 * ToTemporalDate ( item [ , options ] ) 426 */ 427 static bool ToTemporalDate(JSContext* cx, Handle<Value> item, 428 Handle<Value> options, 429 MutableHandle<PlainDate> result) { 430 // Step 1. (Not applicable in our implementation.) 431 432 // Step 2. 433 if (item.isObject()) { 434 Rooted<JSObject*> itemObj(cx, &item.toObject()); 435 return ToTemporalDate(cx, itemObj, options, result); 436 } 437 438 // Step 3. 439 if (!item.isString()) { 440 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item, 441 nullptr, "not a string"); 442 return false; 443 } 444 Rooted<JSString*> string(cx, item.toString()); 445 446 // Step 4. 447 ISODateTime dateTime; 448 Rooted<JSString*> calendarString(cx); 449 if (!ParseTemporalDateTimeString(cx, string, &dateTime, &calendarString)) { 450 return false; 451 } 452 MOZ_ASSERT(IsValidISODate(dateTime.date)); 453 454 // Steps 5-7. 455 Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601)); 456 if (calendarString) { 457 if (!CanonicalizeCalendar(cx, calendarString, &calendar)) { 458 return false; 459 } 460 } 461 462 // Steps 8-9. 463 DateOptions ignoredOptions; 464 if (!ToTemporalDateOptions(cx, options, &ignoredOptions)) { 465 return false; 466 } 467 468 // Steps 10-11. 469 return CreateTemporalDate(cx, dateTime.date, calendar, result); 470 } 471 472 /** 473 * ToTemporalDate ( item [ , options ] ) 474 */ 475 static bool ToTemporalDate(JSContext* cx, Handle<Value> item, 476 MutableHandle<PlainDate> result) { 477 return ToTemporalDate(cx, item, UndefinedHandleValue, result); 478 } 479 480 static bool IsValidISODateEpochMilliseconds(int64_t epochMilliseconds) { 481 // Epoch nanoseconds limits, adjusted to the range supported by ISODate. 482 constexpr auto oneDay = EpochDuration::fromDays(1); 483 constexpr auto min = EpochNanoseconds::min() - oneDay; 484 constexpr auto max = EpochNanoseconds::max() + oneDay; 485 486 // NB: Minimum limit is inclusive, whereas maximim limit is exclusive. 487 auto epochNs = EpochNanoseconds::fromMilliseconds(epochMilliseconds); 488 return min <= epochNs && epochNs < max; 489 } 490 491 /** 492 * BalanceISODate ( year, month, day ) 493 */ 494 bool js::temporal::BalanceISODate(JSContext* cx, const ISODate& date, 495 int64_t days, ISODate* result) { 496 MOZ_ASSERT(IsValidISODate(date)); 497 MOZ_ASSERT(ISODateWithinLimits(date)); 498 499 // Step 1. 500 auto epochDays = MakeDay(date) + mozilla::CheckedInt64{days}; 501 502 // Step 2. 503 auto epochMilliseconds = epochDays * ToMilliseconds(TemporalUnit::Day); 504 if (!epochMilliseconds.isValid() || 505 !IsValidISODateEpochMilliseconds(epochMilliseconds.value())) { 506 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 507 JSMSG_TEMPORAL_PLAIN_DATE_INVALID); 508 return false; 509 } 510 511 // Step 3. 512 auto [year, month, day] = ToYearMonthDay(epochMilliseconds.value()); 513 514 *result = ISODate{year, month + 1, day}; 515 MOZ_ASSERT(IsValidISODate(*result)); 516 MOZ_ASSERT(ISODateWithinLimits(*result)); 517 518 return true; 519 } 520 521 /** 522 * BalanceISODate ( year, month, day ) 523 */ 524 ISODate js::temporal::BalanceISODate(const ISODate& date, int32_t days) { 525 MOZ_ASSERT(IsValidISODate(date)); 526 MOZ_ASSERT(ISODateWithinLimits(date)); 527 MOZ_ASSERT(std::abs(days) <= 400'000'000, "days limit for ToYearMonthDay"); 528 529 // Step 1. 530 int32_t epochDays = MakeDay(date) + days; 531 532 // Step 2. 533 int64_t epochMilliseconds = epochDays * ToMilliseconds(TemporalUnit::Day); 534 535 // Step 3. 536 auto [year, month, day] = ToYearMonthDay(epochMilliseconds); 537 538 // NB: The returned date is possibly outside the valid limits! 539 auto result = ISODate{year, month + 1, day}; 540 MOZ_ASSERT(IsValidISODate(result)); 541 542 return result; 543 } 544 545 /** 546 * CompareISODate ( y1, m1, d1, y2, m2, d2 ) 547 */ 548 int32_t js::temporal::CompareISODate(const ISODate& one, const ISODate& two) { 549 // Steps 1-2. 550 if (one.year != two.year) { 551 return one.year < two.year ? -1 : 1; 552 } 553 554 // Steps 3-4. 555 if (one.month != two.month) { 556 return one.month < two.month ? -1 : 1; 557 } 558 559 // Steps 5-6. 560 if (one.day != two.day) { 561 return one.day < two.day ? -1 : 1; 562 } 563 564 // Step 7. 565 return 0; 566 } 567 568 /** 569 * DifferenceTemporalPlainDate ( operation, temporalDate, other, options ) 570 */ 571 static bool DifferenceTemporalPlainDate(JSContext* cx, 572 TemporalDifference operation, 573 const CallArgs& args) { 574 Rooted<PlainDate> temporalDate( 575 cx, &args.thisv().toObject().as<PlainDateObject>()); 576 577 // Step 1. 578 Rooted<PlainDate> other(cx); 579 if (!ToTemporalDate(cx, args.get(0), &other)) { 580 return false; 581 } 582 583 // Step 2. 584 if (!CalendarEquals(temporalDate.calendar(), other.calendar())) { 585 JS_ReportErrorNumberASCII( 586 cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_CALENDAR_INCOMPATIBLE, 587 CalendarIdentifier(temporalDate.calendar()).data(), 588 CalendarIdentifier(other.calendar()).data()); 589 return false; 590 } 591 592 // Steps 3-4. 593 DifferenceSettings settings; 594 if (args.hasDefined(1)) { 595 // Step 3. 596 Rooted<JSObject*> options( 597 cx, RequireObjectArg(cx, "options", ToName(operation), args[1])); 598 if (!options) { 599 return false; 600 } 601 602 // Step 4. 603 if (!GetDifferenceSettings(cx, operation, options, TemporalUnitGroup::Date, 604 TemporalUnit::Day, TemporalUnit::Day, 605 &settings)) { 606 return false; 607 } 608 } else { 609 // Steps 3-4. 610 settings = { 611 TemporalUnit::Day, 612 TemporalUnit::Day, 613 TemporalRoundingMode::Trunc, 614 Increment{1}, 615 }; 616 } 617 618 // Step 5. 619 if (temporalDate.date() == other.date()) { 620 auto* obj = CreateTemporalDuration(cx, {}); 621 if (!obj) { 622 return false; 623 } 624 625 args.rval().setObject(*obj); 626 return true; 627 } 628 629 // Step 6. 630 DateDuration dateDifference; 631 if (!CalendarDateUntil(cx, temporalDate.calendar(), temporalDate.date(), 632 other.date(), settings.largestUnit, &dateDifference)) { 633 return false; 634 } 635 636 // Step 7. 637 auto duration = InternalDuration{dateDifference, {}}; 638 639 // Step 8. 640 if (settings.smallestUnit != TemporalUnit::Day || 641 settings.roundingIncrement != Increment{1}) { 642 // Step 8.a. 643 auto isoDateTime = ISODateTime{temporalDate.date(), {}}; 644 645 // Step 8.b. 646 auto originEpochNs = GetUTCEpochNanoseconds(isoDateTime); 647 648 // Step 8.c. 649 auto isoDateTimeOther = ISODateTime{other.date(), {}}; 650 651 // Step 8.d. 652 auto destEpochNs = GetUTCEpochNanoseconds(isoDateTimeOther); 653 654 // Step 8.e. 655 Rooted<TimeZoneValue> timeZone(cx, TimeZoneValue{}); 656 if (!RoundRelativeDuration(cx, duration, originEpochNs, destEpochNs, 657 isoDateTime, timeZone, temporalDate.calendar(), 658 settings.largestUnit, settings.roundingIncrement, 659 settings.smallestUnit, settings.roundingMode, 660 &duration)) { 661 return false; 662 } 663 } 664 MOZ_ASSERT(IsValidDuration(duration)); 665 MOZ_ASSERT(duration.time == TimeDuration{}); 666 667 // Step 9. (Inlined TemporalDurationFromInternal) 668 auto result = duration.date.toDuration(); 669 670 // Step 10. 671 if (operation == TemporalDifference::Since) { 672 result = result.negate(); 673 } 674 MOZ_ASSERT(IsValidDuration(result)); 675 676 // Step 11. 677 auto* obj = CreateTemporalDuration(cx, result); 678 if (!obj) { 679 return false; 680 } 681 682 args.rval().setObject(*obj); 683 return true; 684 } 685 686 /** 687 * AddDurationToDate ( operation, temporalDate, temporalDurationLike, options ) 688 */ 689 static bool AddDurationToDate(JSContext* cx, TemporalAddDuration operation, 690 const CallArgs& args) { 691 Rooted<PlainDate> temporalDate( 692 cx, &args.thisv().toObject().as<PlainDateObject>()); 693 694 // Step 1. 695 auto calendar = temporalDate.calendar(); 696 697 // Step 2. 698 Duration duration; 699 if (!ToTemporalDuration(cx, args.get(0), &duration)) { 700 return false; 701 } 702 703 // Step 3. 704 if (operation == TemporalAddDuration::Subtract) { 705 duration = duration.negate(); 706 } 707 708 // Step 4. 709 auto dateDuration = ToDateDurationRecordWithoutTime(duration); 710 711 // Steps 6-7. 712 auto overflow = TemporalOverflow::Constrain; 713 if (args.hasDefined(1)) { 714 // Step 6. 715 Rooted<JSObject*> options( 716 cx, RequireObjectArg(cx, "options", ToName(operation), args[1])); 717 if (!options) { 718 return false; 719 } 720 721 // Step 7. 722 if (!GetTemporalOverflowOption(cx, options, &overflow)) { 723 return false; 724 } 725 } 726 727 // Step 8. 728 ISODate result; 729 if (!CalendarDateAdd(cx, calendar, temporalDate.date(), dateDuration, 730 overflow, &result)) { 731 return false; 732 } 733 734 // Step 9. 735 auto* obj = CreateTemporalDate(cx, result, calendar); 736 if (!obj) { 737 return false; 738 } 739 740 args.rval().setObject(*obj); 741 return true; 742 } 743 744 /** 745 * Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendar ] ) 746 */ 747 static bool PlainDateConstructor(JSContext* cx, unsigned argc, Value* vp) { 748 CallArgs args = CallArgsFromVp(argc, vp); 749 750 // Step 1. 751 if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainDate")) { 752 return false; 753 } 754 755 // Step 2. 756 double isoYear; 757 if (!ToIntegerWithTruncation(cx, args.get(0), "year", &isoYear)) { 758 return false; 759 } 760 761 // Step 3. 762 double isoMonth; 763 if (!ToIntegerWithTruncation(cx, args.get(1), "month", &isoMonth)) { 764 return false; 765 } 766 767 // Step 4. 768 double isoDay; 769 if (!ToIntegerWithTruncation(cx, args.get(2), "day", &isoDay)) { 770 return false; 771 } 772 773 // Steps 5-7. 774 Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601)); 775 if (args.hasDefined(3)) { 776 // Step 6. 777 if (!args[3].isString()) { 778 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, args[3], 779 nullptr, "not a string"); 780 return false; 781 } 782 783 // Step 7. 784 Rooted<JSString*> calendarString(cx, args[3].toString()); 785 if (!CanonicalizeCalendar(cx, calendarString, &calendar)) { 786 return false; 787 } 788 } 789 790 // Step 8. 791 if (!ThrowIfInvalidISODate(cx, isoYear, isoMonth, isoDay)) { 792 return false; 793 } 794 795 // Step 9. 796 auto isoDate = ISODate{int32_t(isoYear), int32_t(isoMonth), int32_t(isoDay)}; 797 798 // Step 10. 799 auto* temporalDate = CreateTemporalDate(cx, args, isoDate, calendar); 800 if (!temporalDate) { 801 return false; 802 } 803 804 args.rval().setObject(*temporalDate); 805 return true; 806 } 807 808 /** 809 * Temporal.PlainDate.from ( item [ , options ] ) 810 */ 811 static bool PlainDate_from(JSContext* cx, unsigned argc, Value* vp) { 812 CallArgs args = CallArgsFromVp(argc, vp); 813 814 // Step 1. 815 Rooted<PlainDate> date(cx); 816 if (!ToTemporalDate(cx, args.get(0), args.get(1), &date)) { 817 return false; 818 } 819 820 auto* result = CreateTemporalDate(cx, date); 821 if (!result) { 822 return false; 823 } 824 825 args.rval().setObject(*result); 826 return true; 827 } 828 829 /** 830 * Temporal.PlainDate.compare ( one, two ) 831 */ 832 static bool PlainDate_compare(JSContext* cx, unsigned argc, Value* vp) { 833 CallArgs args = CallArgsFromVp(argc, vp); 834 835 // Step 1. 836 Rooted<PlainDate> one(cx); 837 if (!ToTemporalDate(cx, args.get(0), &one)) { 838 return false; 839 } 840 841 // Step 2. 842 Rooted<PlainDate> two(cx); 843 if (!ToTemporalDate(cx, args.get(1), &two)) { 844 return false; 845 } 846 847 // Step 3. 848 args.rval().setInt32(CompareISODate(one, two)); 849 return true; 850 } 851 852 /** 853 * get Temporal.PlainDate.prototype.calendarId 854 */ 855 static bool PlainDate_calendarId(JSContext* cx, const CallArgs& args) { 856 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 857 858 // Step 3. 859 auto* str = 860 NewStringCopy<CanGC>(cx, CalendarIdentifier(temporalDate->calendar())); 861 if (!str) { 862 return false; 863 } 864 865 args.rval().setString(str); 866 return true; 867 } 868 869 /** 870 * get Temporal.PlainDate.prototype.calendarId 871 */ 872 static bool PlainDate_calendarId(JSContext* cx, unsigned argc, Value* vp) { 873 // Steps 1-2. 874 CallArgs args = CallArgsFromVp(argc, vp); 875 return CallNonGenericMethod<IsPlainDate, PlainDate_calendarId>(cx, args); 876 } 877 878 /** 879 * get Temporal.PlainDate.prototype.era 880 */ 881 static bool PlainDate_era(JSContext* cx, const CallArgs& args) { 882 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 883 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 884 885 // Step 3. 886 return CalendarEra(cx, calendar, temporalDate->date(), args.rval()); 887 } 888 889 /** 890 * get Temporal.PlainDate.prototype.era 891 */ 892 static bool PlainDate_era(JSContext* cx, unsigned argc, Value* vp) { 893 // Steps 1-2. 894 CallArgs args = CallArgsFromVp(argc, vp); 895 return CallNonGenericMethod<IsPlainDate, PlainDate_era>(cx, args); 896 } 897 898 /** 899 * get Temporal.PlainDate.prototype.eraYear 900 */ 901 static bool PlainDate_eraYear(JSContext* cx, const CallArgs& args) { 902 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 903 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 904 905 // Steps 3-5. 906 return CalendarEraYear(cx, calendar, temporalDate->date(), args.rval()); 907 } 908 909 /** 910 * get Temporal.PlainDate.prototype.eraYear 911 */ 912 static bool PlainDate_eraYear(JSContext* cx, unsigned argc, Value* vp) { 913 // Steps 1-2. 914 CallArgs args = CallArgsFromVp(argc, vp); 915 return CallNonGenericMethod<IsPlainDate, PlainDate_eraYear>(cx, args); 916 } 917 918 /** 919 * get Temporal.PlainDate.prototype.year 920 */ 921 static bool PlainDate_year(JSContext* cx, const CallArgs& args) { 922 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 923 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 924 925 // Step 3. 926 return CalendarYear(cx, calendar, temporalDate->date(), args.rval()); 927 } 928 929 /** 930 * get Temporal.PlainDate.prototype.year 931 */ 932 static bool PlainDate_year(JSContext* cx, unsigned argc, Value* vp) { 933 // Steps 1-2. 934 CallArgs args = CallArgsFromVp(argc, vp); 935 return CallNonGenericMethod<IsPlainDate, PlainDate_year>(cx, args); 936 } 937 938 /** 939 * get Temporal.PlainDate.prototype.month 940 */ 941 static bool PlainDate_month(JSContext* cx, const CallArgs& args) { 942 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 943 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 944 945 // Step 3. 946 return CalendarMonth(cx, calendar, temporalDate->date(), args.rval()); 947 } 948 949 /** 950 * get Temporal.PlainDate.prototype.month 951 */ 952 static bool PlainDate_month(JSContext* cx, unsigned argc, Value* vp) { 953 // Steps 1-2. 954 CallArgs args = CallArgsFromVp(argc, vp); 955 return CallNonGenericMethod<IsPlainDate, PlainDate_month>(cx, args); 956 } 957 958 /** 959 * get Temporal.PlainDate.prototype.monthCode 960 */ 961 static bool PlainDate_monthCode(JSContext* cx, const CallArgs& args) { 962 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 963 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 964 965 // Step 3. 966 return CalendarMonthCode(cx, calendar, temporalDate->date(), args.rval()); 967 } 968 969 /** 970 * get Temporal.PlainDate.prototype.monthCode 971 */ 972 static bool PlainDate_monthCode(JSContext* cx, unsigned argc, Value* vp) { 973 // Steps 1-2. 974 CallArgs args = CallArgsFromVp(argc, vp); 975 return CallNonGenericMethod<IsPlainDate, PlainDate_monthCode>(cx, args); 976 } 977 978 /** 979 * get Temporal.PlainDate.prototype.day 980 */ 981 static bool PlainDate_day(JSContext* cx, const CallArgs& args) { 982 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 983 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 984 985 // Step 3. 986 return CalendarDay(cx, calendar, temporalDate->date(), args.rval()); 987 } 988 989 /** 990 * get Temporal.PlainDate.prototype.day 991 */ 992 static bool PlainDate_day(JSContext* cx, unsigned argc, Value* vp) { 993 // Steps 1-2. 994 CallArgs args = CallArgsFromVp(argc, vp); 995 return CallNonGenericMethod<IsPlainDate, PlainDate_day>(cx, args); 996 } 997 998 /** 999 * get Temporal.PlainDate.prototype.dayOfWeek 1000 */ 1001 static bool PlainDate_dayOfWeek(JSContext* cx, const CallArgs& args) { 1002 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1003 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1004 1005 // Step 3. 1006 return CalendarDayOfWeek(cx, calendar, temporalDate->date(), args.rval()); 1007 } 1008 1009 /** 1010 * get Temporal.PlainDate.prototype.dayOfWeek 1011 */ 1012 static bool PlainDate_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) { 1013 // Steps 1-2. 1014 CallArgs args = CallArgsFromVp(argc, vp); 1015 return CallNonGenericMethod<IsPlainDate, PlainDate_dayOfWeek>(cx, args); 1016 } 1017 1018 /** 1019 * get Temporal.PlainDate.prototype.dayOfYear 1020 */ 1021 static bool PlainDate_dayOfYear(JSContext* cx, const CallArgs& args) { 1022 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1023 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1024 1025 // Step 3. 1026 return CalendarDayOfYear(cx, calendar, temporalDate->date(), args.rval()); 1027 } 1028 1029 /** 1030 * get Temporal.PlainDate.prototype.dayOfYear 1031 */ 1032 static bool PlainDate_dayOfYear(JSContext* cx, unsigned argc, Value* vp) { 1033 // Steps 1-2. 1034 CallArgs args = CallArgsFromVp(argc, vp); 1035 return CallNonGenericMethod<IsPlainDate, PlainDate_dayOfYear>(cx, args); 1036 } 1037 1038 /** 1039 * get Temporal.PlainDate.prototype.weekOfYear 1040 */ 1041 static bool PlainDate_weekOfYear(JSContext* cx, const CallArgs& args) { 1042 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1043 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1044 1045 // Steps 3-5. 1046 return CalendarWeekOfYear(cx, calendar, temporalDate->date(), args.rval()); 1047 } 1048 1049 /** 1050 * get Temporal.PlainDate.prototype.weekOfYear 1051 */ 1052 static bool PlainDate_weekOfYear(JSContext* cx, unsigned argc, Value* vp) { 1053 // Steps 1-2. 1054 CallArgs args = CallArgsFromVp(argc, vp); 1055 return CallNonGenericMethod<IsPlainDate, PlainDate_weekOfYear>(cx, args); 1056 } 1057 1058 /** 1059 * get Temporal.PlainDate.prototype.yearOfWeek 1060 */ 1061 static bool PlainDate_yearOfWeek(JSContext* cx, const CallArgs& args) { 1062 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1063 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1064 1065 // Steps 3-5. 1066 return CalendarYearOfWeek(cx, calendar, temporalDate->date(), args.rval()); 1067 } 1068 1069 /** 1070 * get Temporal.PlainDate.prototype.yearOfWeek 1071 */ 1072 static bool PlainDate_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) { 1073 // Steps 1-2. 1074 CallArgs args = CallArgsFromVp(argc, vp); 1075 return CallNonGenericMethod<IsPlainDate, PlainDate_yearOfWeek>(cx, args); 1076 } 1077 1078 /** 1079 * get Temporal.PlainDate.prototype.daysInWeek 1080 */ 1081 static bool PlainDate_daysInWeek(JSContext* cx, const CallArgs& args) { 1082 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1083 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1084 1085 // Step 3. 1086 return CalendarDaysInWeek(cx, calendar, temporalDate->date(), args.rval()); 1087 } 1088 1089 /** 1090 * get Temporal.PlainDate.prototype.daysInWeek 1091 */ 1092 static bool PlainDate_daysInWeek(JSContext* cx, unsigned argc, Value* vp) { 1093 // Steps 1-2. 1094 CallArgs args = CallArgsFromVp(argc, vp); 1095 return CallNonGenericMethod<IsPlainDate, PlainDate_daysInWeek>(cx, args); 1096 } 1097 1098 /** 1099 * get Temporal.PlainDate.prototype.daysInMonth 1100 */ 1101 static bool PlainDate_daysInMonth(JSContext* cx, const CallArgs& args) { 1102 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1103 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1104 1105 // Step 3. 1106 return CalendarDaysInMonth(cx, calendar, temporalDate->date(), args.rval()); 1107 } 1108 1109 /** 1110 * get Temporal.PlainDate.prototype.daysInMonth 1111 */ 1112 static bool PlainDate_daysInMonth(JSContext* cx, unsigned argc, Value* vp) { 1113 // Steps 1-2. 1114 CallArgs args = CallArgsFromVp(argc, vp); 1115 return CallNonGenericMethod<IsPlainDate, PlainDate_daysInMonth>(cx, args); 1116 } 1117 1118 /** 1119 * get Temporal.PlainDate.prototype.daysInYear 1120 */ 1121 static bool PlainDate_daysInYear(JSContext* cx, const CallArgs& args) { 1122 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1123 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1124 1125 // Step 3. 1126 return CalendarDaysInYear(cx, calendar, temporalDate->date(), args.rval()); 1127 } 1128 1129 /** 1130 * get Temporal.PlainDate.prototype.daysInYear 1131 */ 1132 static bool PlainDate_daysInYear(JSContext* cx, unsigned argc, Value* vp) { 1133 // Steps 1-2. 1134 CallArgs args = CallArgsFromVp(argc, vp); 1135 return CallNonGenericMethod<IsPlainDate, PlainDate_daysInYear>(cx, args); 1136 } 1137 1138 /** 1139 * get Temporal.PlainDate.prototype.monthsInYear 1140 */ 1141 static bool PlainDate_monthsInYear(JSContext* cx, const CallArgs& args) { 1142 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1143 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1144 1145 // Step 3. 1146 return CalendarMonthsInYear(cx, calendar, temporalDate->date(), args.rval()); 1147 } 1148 1149 /** 1150 * get Temporal.PlainDate.prototype.monthsInYear 1151 */ 1152 static bool PlainDate_monthsInYear(JSContext* cx, unsigned argc, Value* vp) { 1153 // Steps 1-2. 1154 CallArgs args = CallArgsFromVp(argc, vp); 1155 return CallNonGenericMethod<IsPlainDate, PlainDate_monthsInYear>(cx, args); 1156 } 1157 1158 /** 1159 * get Temporal.PlainDate.prototype.inLeapYear 1160 */ 1161 static bool PlainDate_inLeapYear(JSContext* cx, const CallArgs& args) { 1162 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1163 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1164 1165 // Step 3. 1166 return CalendarInLeapYear(cx, calendar, temporalDate->date(), args.rval()); 1167 } 1168 1169 /** 1170 * get Temporal.PlainDate.prototype.inLeapYear 1171 */ 1172 static bool PlainDate_inLeapYear(JSContext* cx, unsigned argc, Value* vp) { 1173 // Steps 1-2. 1174 CallArgs args = CallArgsFromVp(argc, vp); 1175 return CallNonGenericMethod<IsPlainDate, PlainDate_inLeapYear>(cx, args); 1176 } 1177 1178 /** 1179 * Temporal.PlainDate.prototype.toPlainYearMonth ( ) 1180 */ 1181 static bool PlainDate_toPlainYearMonth(JSContext* cx, const CallArgs& args) { 1182 Rooted<PlainDate> temporalDate( 1183 cx, &args.thisv().toObject().as<PlainDateObject>()); 1184 1185 // Step 3. 1186 auto calendar = temporalDate.calendar(); 1187 1188 // Step 4. 1189 Rooted<CalendarFields> fields(cx); 1190 if (!ISODateToFields(cx, temporalDate, &fields)) { 1191 return false; 1192 } 1193 1194 // Step 5. 1195 Rooted<PlainYearMonth> result(cx); 1196 if (!CalendarYearMonthFromFields(cx, calendar, fields, 1197 TemporalOverflow::Constrain, &result)) { 1198 return false; 1199 } 1200 1201 // Steps 6-7. 1202 auto* obj = CreateTemporalYearMonth(cx, result); 1203 if (!obj) { 1204 return false; 1205 } 1206 1207 args.rval().setObject(*obj); 1208 return true; 1209 } 1210 1211 /** 1212 * Temporal.PlainDate.prototype.toPlainYearMonth ( ) 1213 */ 1214 static bool PlainDate_toPlainYearMonth(JSContext* cx, unsigned argc, 1215 Value* vp) { 1216 // Steps 1-2. 1217 CallArgs args = CallArgsFromVp(argc, vp); 1218 return CallNonGenericMethod<IsPlainDate, PlainDate_toPlainYearMonth>(cx, 1219 args); 1220 } 1221 1222 /** 1223 * Temporal.PlainDate.prototype.toPlainMonthDay ( ) 1224 */ 1225 static bool PlainDate_toPlainMonthDay(JSContext* cx, const CallArgs& args) { 1226 Rooted<PlainDate> temporalDate( 1227 cx, &args.thisv().toObject().as<PlainDateObject>()); 1228 1229 // Step 3. 1230 auto calendar = temporalDate.calendar(); 1231 1232 // Step 4. 1233 Rooted<CalendarFields> fields(cx); 1234 if (!ISODateToFields(cx, temporalDate, &fields)) { 1235 return false; 1236 } 1237 1238 // Step 5. 1239 Rooted<PlainMonthDay> result(cx); 1240 if (!CalendarMonthDayFromFields(cx, calendar, fields, 1241 TemporalOverflow::Constrain, &result)) { 1242 return false; 1243 } 1244 1245 // Steps 6-7. 1246 auto* obj = CreateTemporalMonthDay(cx, result); 1247 if (!obj) { 1248 return false; 1249 } 1250 1251 args.rval().setObject(*obj); 1252 return true; 1253 } 1254 1255 /** 1256 * Temporal.PlainDate.prototype.toPlainMonthDay ( ) 1257 */ 1258 static bool PlainDate_toPlainMonthDay(JSContext* cx, unsigned argc, Value* vp) { 1259 // Steps 1-2. 1260 CallArgs args = CallArgsFromVp(argc, vp); 1261 return CallNonGenericMethod<IsPlainDate, PlainDate_toPlainMonthDay>(cx, args); 1262 } 1263 1264 /** 1265 * Temporal.PlainDate.prototype.toPlainDateTime ( [ temporalTime ] ) 1266 */ 1267 static bool PlainDate_toPlainDateTime(JSContext* cx, const CallArgs& args) { 1268 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1269 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1270 1271 // Step 4. (Reordered) 1272 // 1273 // Default initialize the time component to all zero. 1274 auto isoDateTime = ISODateTime{temporalDate->date(), {}}; 1275 1276 // Step 3. (Inlined ToTemporalTimeOrMidnight) 1277 if (args.hasDefined(0)) { 1278 if (!ToTemporalTime(cx, args[0], &isoDateTime.time)) { 1279 return false; 1280 } 1281 } 1282 1283 // Step 4. (Moved above) 1284 1285 // Step 5. 1286 auto* obj = CreateTemporalDateTime(cx, isoDateTime, calendar); 1287 if (!obj) { 1288 return false; 1289 } 1290 1291 args.rval().setObject(*obj); 1292 return true; 1293 } 1294 1295 /** 1296 * Temporal.PlainDate.prototype.toPlainDateTime ( [ temporalTime ] ) 1297 */ 1298 static bool PlainDate_toPlainDateTime(JSContext* cx, unsigned argc, Value* vp) { 1299 // Steps 1-2. 1300 CallArgs args = CallArgsFromVp(argc, vp); 1301 return CallNonGenericMethod<IsPlainDate, PlainDate_toPlainDateTime>(cx, args); 1302 } 1303 1304 /** 1305 * Temporal.PlainDate.prototype.add ( temporalDurationLike [ , options ] ) 1306 */ 1307 static bool PlainDate_add(JSContext* cx, const CallArgs& args) { 1308 // Step 3. 1309 return AddDurationToDate(cx, TemporalAddDuration::Add, args); 1310 } 1311 1312 /** 1313 * Temporal.PlainDate.prototype.add ( temporalDurationLike [ , options ] ) 1314 */ 1315 static bool PlainDate_add(JSContext* cx, unsigned argc, Value* vp) { 1316 // Steps 1-2. 1317 CallArgs args = CallArgsFromVp(argc, vp); 1318 return CallNonGenericMethod<IsPlainDate, PlainDate_add>(cx, args); 1319 } 1320 1321 /** 1322 * Temporal.PlainDate.prototype.subtract ( temporalDurationLike [ , options ] ) 1323 */ 1324 static bool PlainDate_subtract(JSContext* cx, const CallArgs& args) { 1325 // Step 3. 1326 return AddDurationToDate(cx, TemporalAddDuration::Subtract, args); 1327 } 1328 1329 /** 1330 * Temporal.PlainDate.prototype.subtract ( temporalDurationLike [ , options ] ) 1331 */ 1332 static bool PlainDate_subtract(JSContext* cx, unsigned argc, Value* vp) { 1333 // Steps 1-2. 1334 CallArgs args = CallArgsFromVp(argc, vp); 1335 return CallNonGenericMethod<IsPlainDate, PlainDate_subtract>(cx, args); 1336 } 1337 1338 /** 1339 * Temporal.PlainDate.prototype.with ( temporalDateLike [ , options ] ) 1340 */ 1341 static bool PlainDate_with(JSContext* cx, const CallArgs& args) { 1342 Rooted<PlainDate> temporalDate( 1343 cx, &args.thisv().toObject().as<PlainDateObject>()); 1344 1345 // Step 3. 1346 Rooted<JSObject*> temporalDateLike( 1347 cx, RequireObjectArg(cx, "temporalDateLike", "with", args.get(0))); 1348 if (!temporalDateLike) { 1349 return false; 1350 } 1351 if (!ThrowIfTemporalLikeObject(cx, temporalDateLike)) { 1352 return false; 1353 } 1354 1355 // Step 4. 1356 auto calendar = temporalDate.calendar(); 1357 1358 // Step 5. 1359 Rooted<CalendarFields> fields(cx); 1360 if (!ISODateToFields(cx, temporalDate, &fields)) { 1361 return false; 1362 } 1363 1364 // Step 6. 1365 Rooted<CalendarFields> partialDate(cx); 1366 if (!PreparePartialCalendarFields(cx, calendar, temporalDateLike, 1367 { 1368 CalendarField::Year, 1369 CalendarField::Month, 1370 CalendarField::MonthCode, 1371 CalendarField::Day, 1372 }, 1373 &partialDate)) { 1374 return false; 1375 } 1376 MOZ_ASSERT(!partialDate.keys().isEmpty()); 1377 1378 // Step 7. 1379 fields = CalendarMergeFields(calendar, fields, partialDate); 1380 1381 // Steps 8-9. 1382 auto overflow = TemporalOverflow::Constrain; 1383 if (args.hasDefined(1)) { 1384 // Step 8. 1385 Rooted<JSObject*> options(cx, 1386 RequireObjectArg(cx, "options", "with", args[1])); 1387 if (!options) { 1388 return false; 1389 } 1390 1391 // Step 9. 1392 if (!GetTemporalOverflowOption(cx, options, &overflow)) { 1393 return false; 1394 } 1395 } 1396 1397 // Step 10. 1398 Rooted<PlainDate> date(cx); 1399 if (!CalendarDateFromFields(cx, calendar, fields, overflow, &date)) { 1400 return false; 1401 } 1402 1403 // Step 11. 1404 auto* result = CreateTemporalDate(cx, date); 1405 if (!result) { 1406 return false; 1407 } 1408 1409 args.rval().setObject(*result); 1410 return true; 1411 } 1412 1413 /** 1414 * Temporal.PlainDate.prototype.with ( temporalDateLike [ , options ] ) 1415 */ 1416 static bool PlainDate_with(JSContext* cx, unsigned argc, Value* vp) { 1417 // Steps 1-2. 1418 CallArgs args = CallArgsFromVp(argc, vp); 1419 return CallNonGenericMethod<IsPlainDate, PlainDate_with>(cx, args); 1420 } 1421 1422 /** 1423 * Temporal.PlainDate.prototype.withCalendar ( calendar ) 1424 */ 1425 static bool PlainDate_withCalendar(JSContext* cx, const CallArgs& args) { 1426 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1427 auto date = temporalDate->date(); 1428 1429 // Step 3. 1430 Rooted<CalendarValue> calendar(cx); 1431 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) { 1432 return false; 1433 } 1434 1435 // Step 4. 1436 auto* result = CreateTemporalDate(cx, date, calendar); 1437 if (!result) { 1438 return false; 1439 } 1440 1441 args.rval().setObject(*result); 1442 return true; 1443 } 1444 1445 /** 1446 * Temporal.PlainDate.prototype.withCalendar ( calendar ) 1447 */ 1448 static bool PlainDate_withCalendar(JSContext* cx, unsigned argc, Value* vp) { 1449 // Steps 1-2. 1450 CallArgs args = CallArgsFromVp(argc, vp); 1451 return CallNonGenericMethod<IsPlainDate, PlainDate_withCalendar>(cx, args); 1452 } 1453 1454 /** 1455 * Temporal.PlainDate.prototype.until ( other [ , options ] ) 1456 */ 1457 static bool PlainDate_until(JSContext* cx, const CallArgs& args) { 1458 // Step 3. 1459 return DifferenceTemporalPlainDate(cx, TemporalDifference::Until, args); 1460 } 1461 1462 /** 1463 * Temporal.PlainDate.prototype.until ( other [ , options ] ) 1464 */ 1465 static bool PlainDate_until(JSContext* cx, unsigned argc, Value* vp) { 1466 // Steps 1-2. 1467 CallArgs args = CallArgsFromVp(argc, vp); 1468 return CallNonGenericMethod<IsPlainDate, PlainDate_until>(cx, args); 1469 } 1470 1471 /** 1472 * Temporal.PlainDate.prototype.since ( other [ , options ] ) 1473 */ 1474 static bool PlainDate_since(JSContext* cx, const CallArgs& args) { 1475 // Step 3. 1476 return DifferenceTemporalPlainDate(cx, TemporalDifference::Since, args); 1477 } 1478 1479 /** 1480 * Temporal.PlainDate.prototype.since ( other [ , options ] ) 1481 */ 1482 static bool PlainDate_since(JSContext* cx, unsigned argc, Value* vp) { 1483 // Steps 1-2. 1484 CallArgs args = CallArgsFromVp(argc, vp); 1485 return CallNonGenericMethod<IsPlainDate, PlainDate_since>(cx, args); 1486 } 1487 1488 /** 1489 * Temporal.PlainDate.prototype.equals ( other ) 1490 */ 1491 static bool PlainDate_equals(JSContext* cx, const CallArgs& args) { 1492 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1493 auto date = temporalDate->date(); 1494 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1495 1496 // Step 3. 1497 Rooted<PlainDate> other(cx); 1498 if (!ToTemporalDate(cx, args.get(0), &other)) { 1499 return false; 1500 } 1501 1502 // Steps 4-5. 1503 bool equals = 1504 date == other.date() && CalendarEquals(calendar, other.calendar()); 1505 1506 args.rval().setBoolean(equals); 1507 return true; 1508 } 1509 1510 /** 1511 * Temporal.PlainDate.prototype.equals ( other ) 1512 */ 1513 static bool PlainDate_equals(JSContext* cx, unsigned argc, Value* vp) { 1514 // Steps 1-2. 1515 CallArgs args = CallArgsFromVp(argc, vp); 1516 return CallNonGenericMethod<IsPlainDate, PlainDate_equals>(cx, args); 1517 } 1518 1519 /** 1520 * Temporal.PlainDate.prototype.toZonedDateTime ( item ) 1521 * 1522 * The |item| argument represents either a time zone or an options object. The 1523 * following cases are supported: 1524 * - |item| is an options object with `timeZone` and `plainTime` properties. 1525 * - |item| is a time zone identifier string. 1526 */ 1527 static bool PlainDate_toZonedDateTime(JSContext* cx, const CallArgs& args) { 1528 auto* temporalDate = &args.thisv().toObject().as<PlainDateObject>(); 1529 auto date = temporalDate->date(); 1530 Rooted<CalendarValue> calendar(cx, temporalDate->calendar()); 1531 1532 // Steps 3-4 1533 Rooted<TimeZoneValue> timeZone(cx); 1534 Rooted<Value> temporalTime(cx); 1535 if (args.get(0).isObject()) { 1536 Rooted<JSObject*> item(cx, &args[0].toObject()); 1537 1538 // Step 3.a. 1539 Rooted<Value> timeZoneLike(cx); 1540 if (!GetProperty(cx, item, item, cx->names().timeZone, &timeZoneLike)) { 1541 return false; 1542 } 1543 1544 // Steps 3.b-c. 1545 if (timeZoneLike.isUndefined()) { 1546 // Step 3.b.i. 1547 if (!ToTemporalTimeZone(cx, args[0], &timeZone)) { 1548 return false; 1549 } 1550 1551 // Step 3.b.ii. 1552 MOZ_ASSERT(temporalTime.isUndefined()); 1553 } else { 1554 // Step 3.c.i. 1555 if (!ToTemporalTimeZone(cx, timeZoneLike, &timeZone)) { 1556 return false; 1557 } 1558 1559 // Step 3.c.ii. 1560 if (!GetProperty(cx, item, item, cx->names().plainTime, &temporalTime)) { 1561 return false; 1562 } 1563 } 1564 } else { 1565 // Step 4.a. 1566 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) { 1567 return false; 1568 } 1569 1570 // Step 4.b. 1571 MOZ_ASSERT(temporalTime.isUndefined()); 1572 } 1573 1574 // Steps 5-6. 1575 EpochNanoseconds epochNs; 1576 if (temporalTime.isUndefined()) { 1577 // Step 5.a. 1578 if (!GetStartOfDay(cx, timeZone, date, &epochNs)) { 1579 return false; 1580 } 1581 } else { 1582 // Step 6.a. 1583 Time time; 1584 if (!ToTemporalTime(cx, temporalTime, &time)) { 1585 return false; 1586 } 1587 1588 // Step 6.b. 1589 auto isoDateTime = ISODateTime{date, time}; 1590 1591 // Step 6.c. 1592 if (!ISODateTimeWithinLimits(isoDateTime)) { 1593 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1594 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 1595 return false; 1596 } 1597 1598 // Step 6.d. 1599 if (!GetEpochNanosecondsFor(cx, timeZone, isoDateTime, 1600 TemporalDisambiguation::Compatible, &epochNs)) { 1601 return false; 1602 } 1603 } 1604 1605 // Step 7. 1606 auto* result = CreateTemporalZonedDateTime(cx, epochNs, timeZone, calendar); 1607 if (!result) { 1608 return false; 1609 } 1610 1611 args.rval().setObject(*result); 1612 return true; 1613 } 1614 1615 /** 1616 * Temporal.PlainDate.prototype.toZonedDateTime ( item ) 1617 */ 1618 static bool PlainDate_toZonedDateTime(JSContext* cx, unsigned argc, Value* vp) { 1619 // Steps 1-2. 1620 CallArgs args = CallArgsFromVp(argc, vp); 1621 return CallNonGenericMethod<IsPlainDate, PlainDate_toZonedDateTime>(cx, args); 1622 } 1623 1624 /** 1625 * Temporal.PlainDate.prototype.toString ( [ options ] ) 1626 */ 1627 static bool PlainDate_toString(JSContext* cx, const CallArgs& args) { 1628 Rooted<PlainDateObject*> temporalDate( 1629 cx, &args.thisv().toObject().as<PlainDateObject>()); 1630 1631 auto showCalendar = ShowCalendar::Auto; 1632 if (args.hasDefined(0)) { 1633 // Step 3. 1634 Rooted<JSObject*> options( 1635 cx, RequireObjectArg(cx, "options", "toString", args[0])); 1636 if (!options) { 1637 return false; 1638 } 1639 1640 // Step 4. 1641 if (!GetTemporalShowCalendarNameOption(cx, options, &showCalendar)) { 1642 return false; 1643 } 1644 } 1645 1646 // Step 5. 1647 JSString* str = TemporalDateToString(cx, temporalDate, showCalendar); 1648 if (!str) { 1649 return false; 1650 } 1651 1652 args.rval().setString(str); 1653 return true; 1654 } 1655 1656 /** 1657 * Temporal.PlainDate.prototype.toString ( [ options ] ) 1658 */ 1659 static bool PlainDate_toString(JSContext* cx, unsigned argc, Value* vp) { 1660 // Steps 1-2. 1661 CallArgs args = CallArgsFromVp(argc, vp); 1662 return CallNonGenericMethod<IsPlainDate, PlainDate_toString>(cx, args); 1663 } 1664 1665 /** 1666 * Temporal.PlainDate.prototype.toLocaleString ( [ locales [ , options ] ] ) 1667 */ 1668 static bool PlainDate_toLocaleString(JSContext* cx, const CallArgs& args) { 1669 // Steps 3-4. 1670 return intl::TemporalObjectToLocaleString(cx, args, 1671 intl::DateTimeFormatKind::Date); 1672 } 1673 1674 /** 1675 * Temporal.PlainDate.prototype.toLocaleString ( [ locales [ , options ] ] ) 1676 */ 1677 static bool PlainDate_toLocaleString(JSContext* cx, unsigned argc, Value* vp) { 1678 // Steps 1-2. 1679 CallArgs args = CallArgsFromVp(argc, vp); 1680 return CallNonGenericMethod<IsPlainDate, PlainDate_toLocaleString>(cx, args); 1681 } 1682 1683 /** 1684 * Temporal.PlainDate.prototype.toJSON ( ) 1685 */ 1686 static bool PlainDate_toJSON(JSContext* cx, const CallArgs& args) { 1687 Rooted<PlainDateObject*> temporalDate( 1688 cx, &args.thisv().toObject().as<PlainDateObject>()); 1689 1690 // Step 3. 1691 JSString* str = TemporalDateToString(cx, temporalDate, ShowCalendar::Auto); 1692 if (!str) { 1693 return false; 1694 } 1695 1696 args.rval().setString(str); 1697 return true; 1698 } 1699 1700 /** 1701 * Temporal.PlainDate.prototype.toJSON ( ) 1702 */ 1703 static bool PlainDate_toJSON(JSContext* cx, unsigned argc, Value* vp) { 1704 // Steps 1-2. 1705 CallArgs args = CallArgsFromVp(argc, vp); 1706 return CallNonGenericMethod<IsPlainDate, PlainDate_toJSON>(cx, args); 1707 } 1708 1709 /** 1710 * Temporal.PlainDate.prototype.valueOf ( ) 1711 */ 1712 static bool PlainDate_valueOf(JSContext* cx, unsigned argc, Value* vp) { 1713 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, 1714 "PlainDate", "primitive type"); 1715 return false; 1716 } 1717 1718 const JSClass PlainDateObject::class_ = { 1719 "Temporal.PlainDate", 1720 JSCLASS_HAS_RESERVED_SLOTS(PlainDateObject::SLOT_COUNT) | 1721 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainDate), 1722 JS_NULL_CLASS_OPS, 1723 &PlainDateObject::classSpec_, 1724 }; 1725 1726 const JSClass& PlainDateObject::protoClass_ = PlainObject::class_; 1727 1728 static const JSFunctionSpec PlainDate_methods[] = { 1729 JS_FN("from", PlainDate_from, 1, 0), 1730 JS_FN("compare", PlainDate_compare, 2, 0), 1731 JS_FS_END, 1732 }; 1733 1734 static const JSFunctionSpec PlainDate_prototype_methods[] = { 1735 JS_FN("toPlainMonthDay", PlainDate_toPlainMonthDay, 0, 0), 1736 JS_FN("toPlainYearMonth", PlainDate_toPlainYearMonth, 0, 0), 1737 JS_FN("toPlainDateTime", PlainDate_toPlainDateTime, 0, 0), 1738 JS_FN("add", PlainDate_add, 1, 0), 1739 JS_FN("subtract", PlainDate_subtract, 1, 0), 1740 JS_FN("with", PlainDate_with, 1, 0), 1741 JS_FN("withCalendar", PlainDate_withCalendar, 1, 0), 1742 JS_FN("until", PlainDate_until, 1, 0), 1743 JS_FN("since", PlainDate_since, 1, 0), 1744 JS_FN("equals", PlainDate_equals, 1, 0), 1745 JS_FN("toZonedDateTime", PlainDate_toZonedDateTime, 1, 0), 1746 JS_FN("toString", PlainDate_toString, 0, 0), 1747 JS_FN("toLocaleString", PlainDate_toLocaleString, 0, 0), 1748 JS_FN("toJSON", PlainDate_toJSON, 0, 0), 1749 JS_FN("valueOf", PlainDate_valueOf, 0, 0), 1750 JS_FS_END, 1751 }; 1752 1753 static const JSPropertySpec PlainDate_prototype_properties[] = { 1754 JS_PSG("calendarId", PlainDate_calendarId, 0), 1755 JS_PSG("era", PlainDate_era, 0), 1756 JS_PSG("eraYear", PlainDate_eraYear, 0), 1757 JS_PSG("year", PlainDate_year, 0), 1758 JS_PSG("month", PlainDate_month, 0), 1759 JS_PSG("monthCode", PlainDate_monthCode, 0), 1760 JS_PSG("day", PlainDate_day, 0), 1761 JS_PSG("dayOfWeek", PlainDate_dayOfWeek, 0), 1762 JS_PSG("dayOfYear", PlainDate_dayOfYear, 0), 1763 JS_PSG("weekOfYear", PlainDate_weekOfYear, 0), 1764 JS_PSG("yearOfWeek", PlainDate_yearOfWeek, 0), 1765 JS_PSG("daysInWeek", PlainDate_daysInWeek, 0), 1766 JS_PSG("daysInMonth", PlainDate_daysInMonth, 0), 1767 JS_PSG("daysInYear", PlainDate_daysInYear, 0), 1768 JS_PSG("monthsInYear", PlainDate_monthsInYear, 0), 1769 JS_PSG("inLeapYear", PlainDate_inLeapYear, 0), 1770 JS_STRING_SYM_PS(toStringTag, "Temporal.PlainDate", JSPROP_READONLY), 1771 JS_PS_END, 1772 }; 1773 1774 const ClassSpec PlainDateObject::classSpec_ = { 1775 GenericCreateConstructor<PlainDateConstructor, 3, gc::AllocKind::FUNCTION>, 1776 GenericCreatePrototype<PlainDateObject>, 1777 PlainDate_methods, 1778 nullptr, 1779 PlainDate_prototype_methods, 1780 PlainDate_prototype_properties, 1781 nullptr, 1782 ClassSpec::DontDefineConstructor, 1783 };