ZonedDateTime.cpp (96290B)
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/ZonedDateTime.h" 8 9 #include "mozilla/Assertions.h" 10 #include "mozilla/Maybe.h" 11 12 #include <algorithm> 13 #include <cstdlib> 14 15 #include "jspubtd.h" 16 #include "NamespaceImports.h" 17 18 #include "builtin/intl/DateTimeFormat.h" 19 #include "builtin/temporal/Calendar.h" 20 #include "builtin/temporal/CalendarFields.h" 21 #include "builtin/temporal/Duration.h" 22 #include "builtin/temporal/Instant.h" 23 #include "builtin/temporal/PlainDate.h" 24 #include "builtin/temporal/PlainDateTime.h" 25 #include "builtin/temporal/PlainMonthDay.h" 26 #include "builtin/temporal/PlainTime.h" 27 #include "builtin/temporal/PlainYearMonth.h" 28 #include "builtin/temporal/Temporal.h" 29 #include "builtin/temporal/TemporalParser.h" 30 #include "builtin/temporal/TemporalRoundingMode.h" 31 #include "builtin/temporal/TemporalTypes.h" 32 #include "builtin/temporal/TemporalUnit.h" 33 #include "builtin/temporal/TimeZone.h" 34 #include "builtin/temporal/ToString.h" 35 #include "gc/AllocKind.h" 36 #include "gc/Barrier.h" 37 #include "gc/GCEnum.h" 38 #include "js/CallArgs.h" 39 #include "js/CallNonGenericMethod.h" 40 #include "js/Class.h" 41 #include "js/ErrorReport.h" 42 #include "js/friend/ErrorMessages.h" 43 #include "js/Printer.h" 44 #include "js/PropertyDescriptor.h" 45 #include "js/PropertySpec.h" 46 #include "js/RootingAPI.h" 47 #include "js/Value.h" 48 #include "vm/BigIntType.h" 49 #include "vm/BytecodeUtil.h" 50 #include "vm/GlobalObject.h" 51 #include "vm/Int128.h" 52 #include "vm/JSAtomState.h" 53 #include "vm/JSContext.h" 54 #include "vm/JSObject.h" 55 #include "vm/PlainObject.h" 56 #include "vm/StringType.h" 57 58 #include "vm/JSObject-inl.h" 59 #include "vm/NativeObject-inl.h" 60 61 using namespace js; 62 using namespace js::temporal; 63 64 static inline bool IsZonedDateTime(Handle<Value> v) { 65 return v.isObject() && v.toObject().is<ZonedDateTimeObject>(); 66 } 67 68 // Returns |RoundNumberToIncrement(offsetNanoseconds, 60 × 10^9, "halfExpand")|. 69 static int64_t RoundNanosecondsToMinutesIncrement(int64_t offsetNanoseconds) { 70 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day)); 71 72 constexpr int64_t increment = ToNanoseconds(TemporalUnit::Minute); 73 74 int64_t quotient = offsetNanoseconds / increment; 75 int64_t remainder = offsetNanoseconds % increment; 76 if (std::abs(remainder * 2) >= increment) { 77 quotient += (offsetNanoseconds > 0 ? 1 : -1); 78 } 79 return quotient * increment; 80 } 81 82 /** 83 * InterpretISODateTimeOffset ( isoDate, time, offsetBehaviour, 84 * offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour ) 85 */ 86 bool js::temporal::InterpretISODateTimeOffset( 87 JSContext* cx, const ISODateTime& dateTime, OffsetBehaviour offsetBehaviour, 88 int64_t offsetNanoseconds, Handle<TimeZoneValue> timeZone, 89 TemporalDisambiguation disambiguation, TemporalOffset offsetOption, 90 MatchBehaviour matchBehaviour, EpochNanoseconds* result) { 91 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day)); 92 MOZ_ASSERT(IsValidISODateTime(dateTime)); 93 94 // FIXME: spec issue - avoid calling with date-time outside of limits 95 // https://github.com/tc39/proposal-temporal/pull/3014 96 if (!ISODateTimeWithinLimits(dateTime)) { 97 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 98 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 99 return false; 100 } 101 102 // Steps 1-2. (Not applicable in our implementation.) 103 104 // Step 3. 105 if (offsetBehaviour == OffsetBehaviour::Wall || 106 (offsetBehaviour == OffsetBehaviour::Option && 107 offsetOption == TemporalOffset::Ignore)) { 108 // Steps 3.a-b. 109 return GetEpochNanosecondsFor(cx, timeZone, dateTime, disambiguation, 110 result); 111 } 112 113 // Step 4. 114 if (offsetBehaviour == OffsetBehaviour::Exact || 115 (offsetBehaviour == OffsetBehaviour::Option && 116 offsetOption == TemporalOffset::Use)) { 117 // Step 4.a. 118 auto epochNanoseconds = GetUTCEpochNanoseconds(dateTime) - 119 EpochDuration::fromNanoseconds(offsetNanoseconds); 120 121 // Step 4.b. 122 if (!IsValidEpochNanoseconds(epochNanoseconds)) { 123 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 124 JSMSG_TEMPORAL_INSTANT_INVALID); 125 return false; 126 } 127 128 // Step 4.c. 129 *result = epochNanoseconds; 130 return true; 131 } 132 133 // Step 5. 134 MOZ_ASSERT(offsetBehaviour == OffsetBehaviour::Option); 135 136 // Step 6. 137 MOZ_ASSERT(offsetOption == TemporalOffset::Prefer || 138 offsetOption == TemporalOffset::Reject); 139 140 // Step 7. 141 PossibleEpochNanoseconds possibleEpochNs; 142 if (!GetPossibleEpochNanoseconds(cx, timeZone, dateTime, &possibleEpochNs)) { 143 return false; 144 } 145 146 // Step 8.a. 147 for (const auto& candidate : possibleEpochNs) { 148 // Step 8.a.i. 149 int64_t candidateNanoseconds; 150 if (!GetOffsetNanosecondsFor(cx, timeZone, candidate, 151 &candidateNanoseconds)) { 152 return false; 153 } 154 MOZ_ASSERT(std::abs(candidateNanoseconds) < 155 ToNanoseconds(TemporalUnit::Day)); 156 157 // Step 8.a.ii. 158 if (candidateNanoseconds == offsetNanoseconds) { 159 *result = candidate; 160 return true; 161 } 162 163 // Step 8.a.iii. 164 if (matchBehaviour == MatchBehaviour::MatchMinutes) { 165 // Step 8.a.iii.1. 166 int64_t roundedCandidateNanoseconds = 167 RoundNanosecondsToMinutesIncrement(candidateNanoseconds); 168 169 // Step 8.a.iii.2. 170 if (roundedCandidateNanoseconds == offsetNanoseconds) { 171 // Step 8.a.iii.2.a. 172 *result = candidate; 173 return true; 174 } 175 } 176 } 177 178 // Step 9. 179 if (offsetOption == TemporalOffset::Reject) { 180 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 181 JSMSG_TEMPORAL_ZONED_DATE_TIME_NO_TIME_FOUND); 182 return false; 183 } 184 185 // Step 10. 186 return DisambiguatePossibleEpochNanoseconds(cx, possibleEpochNs, timeZone, 187 dateTime, disambiguation, result); 188 } 189 190 /** 191 * InterpretISODateTimeOffset ( isoDate, time, offsetBehaviour, 192 * offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour ) 193 */ 194 bool js::temporal::InterpretISODateTimeOffset( 195 JSContext* cx, const ISODate& isoDate, OffsetBehaviour offsetBehaviour, 196 int64_t offsetNanoseconds, Handle<TimeZoneValue> timeZone, 197 TemporalDisambiguation disambiguation, TemporalOffset offsetOption, 198 MatchBehaviour matchBehaviour, EpochNanoseconds* result) { 199 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day)); 200 MOZ_ASSERT(IsValidISODate(isoDate)); 201 202 // Step 1. (Not applicable in our implementation.) 203 204 // Step 2.a. 205 MOZ_ASSERT(offsetBehaviour == OffsetBehaviour::Wall); 206 207 // Step 2.b. 208 MOZ_ASSERT(offsetNanoseconds == 0); 209 210 // Step 2.c. 211 return GetStartOfDay(cx, timeZone, isoDate, result); 212 } 213 214 struct ZonedDateTimeOptions { 215 TemporalDisambiguation disambiguation = TemporalDisambiguation::Compatible; 216 TemporalOffset offset = TemporalOffset::Reject; 217 TemporalOverflow overflow = TemporalOverflow::Constrain; 218 }; 219 220 /** 221 * ToTemporalZonedDateTime ( item [ , options ] ) 222 */ 223 static bool ToTemporalZonedDateTimeOptions(JSContext* cx, Handle<Value> options, 224 ZonedDateTimeOptions* result) { 225 if (options.isUndefined()) { 226 *result = {}; 227 return true; 228 } 229 230 // NOTE: |options| are only passed from `Temporal.ZonedDateTime.from`. 231 232 Rooted<JSObject*> resolvedOptions( 233 cx, RequireObjectArg(cx, "options", "from", options)); 234 if (!resolvedOptions) { 235 return false; 236 } 237 238 auto disambiguation = TemporalDisambiguation::Compatible; 239 if (!GetTemporalDisambiguationOption(cx, resolvedOptions, &disambiguation)) { 240 return false; 241 } 242 243 auto offset = TemporalOffset::Reject; 244 if (!GetTemporalOffsetOption(cx, resolvedOptions, &offset)) { 245 return false; 246 } 247 248 auto overflow = TemporalOverflow::Constrain; 249 if (!GetTemporalOverflowOption(cx, resolvedOptions, &overflow)) { 250 return false; 251 } 252 253 *result = {disambiguation, offset, overflow}; 254 return true; 255 } 256 257 /** 258 * ToTemporalZonedDateTime ( item [ , options ] ) 259 */ 260 static bool ToTemporalZonedDateTime(JSContext* cx, Handle<JSObject*> item, 261 Handle<Value> options, 262 MutableHandle<ZonedDateTime> result) { 263 // Step 1. (Not applicable in our implementation.) 264 265 // Step 2. (Not applicable) 266 267 // Step 3. 268 auto matchBehaviour = MatchBehaviour::MatchExactly; 269 270 // Step 4.a. 271 if (auto* zonedDateTime = item->maybeUnwrapIf<ZonedDateTimeObject>()) { 272 auto epochNs = zonedDateTime->epochNanoseconds(); 273 Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone()); 274 Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar()); 275 276 if (!timeZone.wrap(cx)) { 277 return false; 278 } 279 if (!calendar.wrap(cx)) { 280 return false; 281 } 282 283 // Steps 4.a.i-v. 284 ZonedDateTimeOptions ignoredOptions; 285 if (!ToTemporalZonedDateTimeOptions(cx, options, &ignoredOptions)) { 286 return false; 287 } 288 289 // Step 4.a.vi. 290 result.set(ZonedDateTime{epochNs, timeZone, calendar}); 291 return true; 292 } 293 294 // Step 4.b. 295 Rooted<CalendarValue> calendar(cx); 296 if (!GetTemporalCalendarWithISODefault(cx, item, &calendar)) { 297 return false; 298 } 299 300 // Step 4.c. 301 Rooted<CalendarFields> fields(cx); 302 if (!PrepareCalendarFields(cx, calendar, item, 303 { 304 CalendarField::Year, 305 CalendarField::Month, 306 CalendarField::MonthCode, 307 CalendarField::Day, 308 CalendarField::Hour, 309 CalendarField::Minute, 310 CalendarField::Second, 311 CalendarField::Millisecond, 312 CalendarField::Microsecond, 313 CalendarField::Nanosecond, 314 CalendarField::Offset, 315 CalendarField::TimeZone, 316 }, 317 {CalendarField::TimeZone}, &fields)) { 318 return false; 319 } 320 321 // Step 4.d. 322 auto timeZone = fields.timeZone(); 323 324 // Step 4.e. 325 auto offsetString = fields.offset(); 326 327 // Steps 4.f-i. 328 ZonedDateTimeOptions resolvedOptions; 329 if (!ToTemporalZonedDateTimeOptions(cx, options, &resolvedOptions)) { 330 return false; 331 } 332 auto [disambiguation, offsetOption, overflow] = resolvedOptions; 333 334 // Steps 4.j-l. 335 ISODateTime dateTime; 336 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, overflow, 337 &dateTime)) { 338 return false; 339 } 340 341 // Steps 5-6. (Not applicable) 342 343 // Steps 7-8. 344 auto offsetBehaviour = !fields.has(CalendarField::Offset) 345 ? OffsetBehaviour::Wall 346 : OffsetBehaviour::Option; 347 348 // Step 9. 349 int64_t offsetNanoseconds = 0; 350 351 // Step 10. 352 if (offsetBehaviour == OffsetBehaviour::Option) { 353 offsetNanoseconds = int64_t(offsetString); 354 } 355 356 // Step 11. 357 EpochNanoseconds epochNanoseconds; 358 if (!InterpretISODateTimeOffset( 359 cx, dateTime, offsetBehaviour, offsetNanoseconds, timeZone, 360 disambiguation, offsetOption, matchBehaviour, &epochNanoseconds)) { 361 return false; 362 } 363 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds)); 364 365 // Step 12. 366 result.set(ZonedDateTime{epochNanoseconds, timeZone, calendar}); 367 return true; 368 } 369 370 /** 371 * ToTemporalZonedDateTime ( item [ , options ] ) 372 */ 373 static bool ToTemporalZonedDateTime(JSContext* cx, Handle<Value> item, 374 Handle<Value> options, 375 MutableHandle<ZonedDateTime> result) { 376 // Step 1. (Not applicable in our implementation.) 377 378 // Steps 2-3. (Not applicable) 379 380 // Step 4. 381 if (item.isObject()) { 382 Rooted<JSObject*> itemObj(cx, &item.toObject()); 383 return ToTemporalZonedDateTime(cx, itemObj, options, result); 384 } 385 386 // Step 5.a. 387 if (!item.isString()) { 388 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item, 389 nullptr, "not a string"); 390 return false; 391 } 392 Rooted<JSString*> string(cx, item.toString()); 393 394 // Case 1: 19700101Z[+02:00] 395 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" } 396 // 397 // Case 2: 19700101+00:00[+02:00] 398 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "+02:00" } 399 // 400 // Case 3: 19700101[+02:00] 401 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" } 402 // 403 // Case 4: 19700101Z[Europe/Berlin] 404 // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" } 405 // 406 // Case 5: 19700101+00:00[Europe/Berlin] 407 // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "Europe/Berlin" } 408 // 409 // Case 6: 19700101[Europe/Berlin] 410 // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" } 411 412 // Steps 5.b-c. 413 Rooted<ParsedZonedDateTime> parsed(cx); 414 if (!ParseTemporalZonedDateTimeString(cx, string, &parsed)) { 415 return false; 416 } 417 418 // Step 5.d. 419 MOZ_ASSERT(parsed.timeZoneAnnotation()); 420 421 // Step 5.e. 422 Rooted<TimeZoneValue> timeZone(cx); 423 if (!ToTemporalTimeZone(cx, parsed.timeZoneAnnotation(), &timeZone)) { 424 return false; 425 } 426 427 // Step 5.f. (Not applicable in our implementation.) 428 429 // Step 5.g. 430 bool hasUTCDesignator = parsed.timeZone().constructed<UTCTimeZone>(); 431 432 // Steps 5.h-i. 433 Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601)); 434 if (parsed.calendar()) { 435 if (!CanonicalizeCalendar(cx, parsed.calendar(), &calendar)) { 436 return false; 437 } 438 } 439 440 // Step 5.k. 441 auto matchBehaviour = MatchBehaviour::MatchMinutes; 442 443 // Step 5.l. 444 if (parsed.timeZone().constructed<OffsetTimeZone>()) { 445 // Steps 5.l.i-iii. 446 if (parsed.timeZone().ref<OffsetTimeZone>().hasSubMinutePrecision) { 447 matchBehaviour = MatchBehaviour::MatchExactly; 448 } 449 } 450 451 // Steps 5.m-p. 452 ZonedDateTimeOptions resolvedOptions; 453 if (!ToTemporalZonedDateTimeOptions(cx, options, &resolvedOptions)) { 454 return false; 455 } 456 auto [disambiguation, offsetOption, overflow] = resolvedOptions; 457 458 // Steps 5.q-r. 459 const auto& isoDateTime = parsed.dateTime(); 460 461 // Steps 6-8. 462 auto offsetBehaviour = hasUTCDesignator ? OffsetBehaviour::Exact 463 : parsed.timeZone().empty() ? OffsetBehaviour::Wall 464 : OffsetBehaviour::Option; 465 466 // Step 9. 467 int64_t offsetNanoseconds = 0; 468 469 // Step 10. 470 if (offsetBehaviour == OffsetBehaviour::Option) { 471 MOZ_ASSERT(parsed.timeZone().constructed<OffsetTimeZone>()); 472 offsetNanoseconds = parsed.timeZone().ref<OffsetTimeZone>().offset; 473 } 474 475 // Step 11. 476 EpochNanoseconds epochNanoseconds; 477 if (parsed.isStartOfDay()) { 478 if (!InterpretISODateTimeOffset( 479 cx, isoDateTime.date, offsetBehaviour, offsetNanoseconds, timeZone, 480 disambiguation, offsetOption, matchBehaviour, &epochNanoseconds)) { 481 return false; 482 } 483 } else { 484 if (!InterpretISODateTimeOffset( 485 cx, isoDateTime, offsetBehaviour, offsetNanoseconds, timeZone, 486 disambiguation, offsetOption, matchBehaviour, &epochNanoseconds)) { 487 return false; 488 } 489 } 490 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds)); 491 492 // Step 12. 493 result.set(ZonedDateTime{epochNanoseconds, timeZone, calendar}); 494 return true; 495 } 496 497 /** 498 * ToTemporalZonedDateTime ( item [ , options ] ) 499 */ 500 static bool ToTemporalZonedDateTime(JSContext* cx, Handle<Value> item, 501 MutableHandle<ZonedDateTime> result) { 502 return ToTemporalZonedDateTime(cx, item, UndefinedHandleValue, result); 503 } 504 505 /** 506 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ , 507 * newTarget ] ) 508 */ 509 static ZonedDateTimeObject* CreateTemporalZonedDateTime( 510 JSContext* cx, const CallArgs& args, Handle<BigInt*> epochNanoseconds, 511 Handle<TimeZoneValue> timeZone, Handle<CalendarValue> calendar) { 512 // Step 1. 513 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds)); 514 515 // Steps 3-4. 516 Rooted<JSObject*> proto(cx); 517 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ZonedDateTime, 518 &proto)) { 519 return nullptr; 520 } 521 522 auto* object = NewObjectWithClassProto<ZonedDateTimeObject>(cx, proto); 523 if (!object) { 524 return nullptr; 525 } 526 527 // Step 4. 528 auto epochNs = ToEpochNanoseconds(epochNanoseconds); 529 object->initFixedSlot(ZonedDateTimeObject::SECONDS_SLOT, 530 NumberValue(epochNs.seconds)); 531 object->initFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT, 532 Int32Value(epochNs.nanoseconds)); 533 534 // Step 5. 535 object->initFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT, 536 timeZone.toSlotValue()); 537 538 // Step 6. 539 object->initFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT, 540 calendar.toSlotValue()); 541 542 // Step 7. 543 return object; 544 } 545 546 /** 547 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ , 548 * newTarget ] ) 549 */ 550 ZonedDateTimeObject* js::temporal::CreateTemporalZonedDateTime( 551 JSContext* cx, const EpochNanoseconds& epochNanoseconds, 552 Handle<TimeZoneValue> timeZone, Handle<CalendarValue> calendar) { 553 // Step 1. 554 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds)); 555 556 // Steps 2-3. 557 auto* object = NewBuiltinClassInstance<ZonedDateTimeObject>(cx); 558 if (!object) { 559 return nullptr; 560 } 561 562 // Step 4. 563 object->initFixedSlot(ZonedDateTimeObject::SECONDS_SLOT, 564 NumberValue(epochNanoseconds.seconds)); 565 object->initFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT, 566 Int32Value(epochNanoseconds.nanoseconds)); 567 568 // Step 5. 569 object->initFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT, 570 timeZone.toSlotValue()); 571 572 // Step 6. 573 object->initFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT, 574 calendar.toSlotValue()); 575 576 // Step 7. 577 return object; 578 } 579 580 /** 581 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ , 582 * newTarget ] ) 583 */ 584 static auto* CreateTemporalZonedDateTime(JSContext* cx, 585 Handle<ZonedDateTime> zonedDateTime) { 586 return CreateTemporalZonedDateTime(cx, zonedDateTime.epochNanoseconds(), 587 zonedDateTime.timeZone(), 588 zonedDateTime.calendar()); 589 } 590 591 /** 592 * AddZonedDateTime ( epochNanoseconds, timeZone, calendar, duration, overflow ) 593 */ 594 static bool AddZonedDateTime(JSContext* cx, Handle<ZonedDateTime> zonedDateTime, 595 const InternalDuration& duration, 596 TemporalOverflow overflow, 597 EpochNanoseconds* result) { 598 MOZ_ASSERT(IsValidDuration(duration)); 599 600 // Step 1. 601 if (duration.date == DateDuration{}) { 602 // Step 1.a. 603 return AddInstant(cx, zonedDateTime.epochNanoseconds(), duration.time, 604 result); 605 } 606 607 // Step 2. 608 ISODateTime isoDateTime; 609 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 610 zonedDateTime.epochNanoseconds(), &isoDateTime)) { 611 return false; 612 } 613 614 // Step 3. 615 ISODate addedDate; 616 if (!CalendarDateAdd(cx, zonedDateTime.calendar(), isoDateTime.date, 617 duration.date, overflow, &addedDate)) { 618 return false; 619 } 620 621 // Step 4. 622 auto intermediateDateTime = ISODateTime{addedDate, isoDateTime.time}; 623 624 // Step 5. 625 if (!ISODateTimeWithinLimits(intermediateDateTime)) { 626 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 627 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 628 return false; 629 } 630 631 // Step 6. 632 EpochNanoseconds intermediateNs; 633 if (!GetEpochNanosecondsFor( 634 cx, zonedDateTime.timeZone(), intermediateDateTime, 635 TemporalDisambiguation::Compatible, &intermediateNs)) { 636 return false; 637 } 638 639 // Step 7. 640 return AddInstant(cx, intermediateNs, duration.time, result); 641 } 642 643 /** 644 * AddZonedDateTime ( epochNanoseconds, timeZone, calendar, duration, overflow ) 645 */ 646 bool js::temporal::AddZonedDateTime(JSContext* cx, 647 Handle<ZonedDateTime> zonedDateTime, 648 const InternalDuration& duration, 649 EpochNanoseconds* result) { 650 return ::AddZonedDateTime(cx, zonedDateTime, duration, 651 TemporalOverflow::Constrain, result); 652 } 653 654 /** 655 * DifferenceZonedDateTime ( ns1, ns2, timeZone, calendar, largestUnit ) 656 */ 657 static bool DifferenceZonedDateTime(JSContext* cx, const EpochNanoseconds& ns1, 658 const EpochNanoseconds& ns2, 659 Handle<TimeZoneValue> timeZone, 660 Handle<CalendarValue> calendar, 661 TemporalUnit largestUnit, 662 InternalDuration* result) { 663 MOZ_ASSERT(IsValidEpochNanoseconds(ns1)); 664 MOZ_ASSERT(IsValidEpochNanoseconds(ns2)); 665 666 // Steps 1. 667 if (ns1 == ns2) { 668 *result = InternalDuration{{}, {}}; 669 return true; 670 } 671 672 // Step 2. 673 ISODateTime startDateTime; 674 if (!GetISODateTimeFor(cx, timeZone, ns1, &startDateTime)) { 675 return false; 676 } 677 678 // Steps 2-3. 679 ISODateTime endDateTime; 680 if (!GetISODateTimeFor(cx, timeZone, ns2, &endDateTime)) { 681 return false; 682 } 683 684 // Step 4. 685 if (CompareISODate(startDateTime.date, endDateTime.date) == 0) { 686 // Step 4.a. 687 auto timeDuration = TimeDurationFromEpochNanosecondsDifference(ns2, ns1); 688 689 // Step 4.b. 690 *result = {{}, timeDuration}; 691 return true; 692 } 693 694 // Step 5. 695 int32_t sign = (ns2 - ns1 < EpochDuration{}) ? -1 : 1; 696 697 // Step 6. 698 int32_t maxDayCorrection = 1 + (sign > 0); 699 700 // Step 7. 701 int32_t dayCorrection = 0; 702 703 // Step 8. 704 auto timeDuration = DifferenceTime(startDateTime.time, endDateTime.time); 705 706 // Step 9. 707 if (TimeDurationSign(timeDuration) == -sign) { 708 dayCorrection += 1; 709 } 710 711 // Steps 10-11. 712 while (dayCorrection <= maxDayCorrection) { 713 // Step 11.a. 714 auto intermediateDate = 715 BalanceISODate(endDateTime.date, -dayCorrection * sign); 716 717 // Step 11.b. 718 auto intermediateDateTime = 719 ISODateTime{intermediateDate, startDateTime.time}; 720 if (!ISODateTimeWithinLimits(intermediateDateTime)) { 721 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 722 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 723 return false; 724 } 725 726 // Step 11.c. 727 EpochNanoseconds intermediateNs; 728 if (!GetEpochNanosecondsFor(cx, timeZone, intermediateDateTime, 729 TemporalDisambiguation::Compatible, 730 &intermediateNs)) { 731 return false; 732 } 733 734 // Step 11.d. 735 auto timeDuration = 736 TimeDurationFromEpochNanosecondsDifference(ns2, intermediateNs); 737 738 // Step 11.e. 739 int32_t timeSign = TimeDurationSign(timeDuration); 740 741 // Step 11.f. 742 if (sign != -timeSign) { 743 // Step 13. 744 auto dateLargestUnit = std::min(largestUnit, TemporalUnit::Day); 745 746 // Step 14. 747 DateDuration dateDifference; 748 if (!CalendarDateUntil(cx, calendar, startDateTime.date, intermediateDate, 749 dateLargestUnit, &dateDifference)) { 750 return false; 751 } 752 753 // Step 15. 754 MOZ_ASSERT(DateDurationSign(dateDifference) * 755 TimeDurationSign(timeDuration) >= 756 0); 757 *result = {dateDifference, timeDuration}; 758 return true; 759 } 760 761 // Step 11.g. 762 dayCorrection += 1; 763 } 764 765 // Step 12. 766 JS_ReportErrorNumberASCII( 767 cx, GetErrorMessage, nullptr, 768 JSMSG_TEMPORAL_ZONED_DATE_TIME_INCONSISTENT_INSTANT); 769 return false; 770 } 771 772 /** 773 * DifferenceZonedDateTimeWithRounding ( ns1, ns2, timeZone, calendar, 774 * largestUnit, roundingIncrement, smallestUnit, roundingMode ) 775 */ 776 bool js::temporal::DifferenceZonedDateTimeWithRounding( 777 JSContext* cx, JS::Handle<ZonedDateTime> zonedDateTime, 778 const EpochNanoseconds& ns2, const DifferenceSettings& settings, 779 InternalDuration* result) { 780 MOZ_ASSERT(IsValidEpochNanoseconds(ns2)); 781 MOZ_ASSERT(settings.smallestUnit >= settings.largestUnit); 782 783 const auto& ns1 = zonedDateTime.epochNanoseconds(); 784 auto timeZone = zonedDateTime.timeZone(); 785 auto calendar = zonedDateTime.calendar(); 786 787 // Step 1. 788 if (settings.largestUnit > TemporalUnit::Day) { 789 // Step 1.a. 790 auto difference = 791 DifferenceInstant(ns1, ns2, settings.roundingIncrement, 792 settings.smallestUnit, settings.roundingMode); 793 *result = InternalDuration{{}, difference}; 794 return true; 795 } 796 797 // Step 2. 798 InternalDuration difference; 799 if (!DifferenceZonedDateTime(cx, ns1, ns2, timeZone, calendar, 800 settings.largestUnit, &difference)) { 801 return false; 802 } 803 804 // Step 3. 805 if (settings.smallestUnit == TemporalUnit::Nanosecond && 806 settings.roundingIncrement == Increment{1}) { 807 // Step 3.a. 808 *result = difference; 809 return true; 810 } 811 812 // Step 4. 813 ISODateTime dateTime; 814 if (!GetISODateTimeFor(cx, timeZone, ns1, &dateTime)) { 815 return false; 816 } 817 818 // Step 5. 819 return RoundRelativeDuration( 820 cx, difference, ns1, ns2, dateTime, timeZone, calendar, 821 settings.largestUnit, settings.roundingIncrement, settings.smallestUnit, 822 settings.roundingMode, result); 823 } 824 825 /** 826 * DifferenceZonedDateTimeWithTotal ( ns1, ns2, timeZone, calendar, unit ) 827 */ 828 bool js::temporal::DifferenceZonedDateTimeWithTotal( 829 JSContext* cx, JS::Handle<ZonedDateTime> zonedDateTime, 830 const EpochNanoseconds& ns2, TemporalUnit unit, double* result) { 831 MOZ_ASSERT(IsValidEpochNanoseconds(ns2)); 832 833 const auto& ns1 = zonedDateTime.epochNanoseconds(); 834 auto timeZone = zonedDateTime.timeZone(); 835 auto calendar = zonedDateTime.calendar(); 836 837 // Step 1. 838 if (unit > TemporalUnit::Day) { 839 // Step 1.a. 840 auto difference = TimeDurationFromEpochNanosecondsDifference(ns2, ns1); 841 MOZ_ASSERT(IsValidEpochDuration(difference.to<EpochDuration>())); 842 843 // Step 1.b. 844 *result = TotalTimeDuration(difference, unit); 845 return true; 846 } 847 848 // Step 2. 849 InternalDuration difference; 850 if (!DifferenceZonedDateTime(cx, ns1, ns2, timeZone, calendar, unit, 851 &difference)) { 852 return false; 853 } 854 855 // Step 3. 856 ISODateTime dateTime; 857 if (!GetISODateTimeFor(cx, timeZone, ns1, &dateTime)) { 858 return false; 859 } 860 861 // Step 5. 862 return TotalRelativeDuration(cx, difference, ns1, ns2, dateTime, timeZone, 863 calendar, unit, result); 864 } 865 866 /** 867 * DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options ) 868 */ 869 static bool DifferenceTemporalZonedDateTime(JSContext* cx, 870 TemporalDifference operation, 871 const CallArgs& args) { 872 Rooted<ZonedDateTime> zonedDateTime( 873 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 874 875 // Step 1. 876 Rooted<ZonedDateTime> other(cx); 877 if (!ToTemporalZonedDateTime(cx, args.get(0), &other)) { 878 return false; 879 } 880 881 // Step 2. 882 if (!CalendarEquals(zonedDateTime.calendar(), other.calendar())) { 883 JS_ReportErrorNumberASCII( 884 cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_CALENDAR_INCOMPATIBLE, 885 CalendarIdentifier(zonedDateTime.calendar()).data(), 886 CalendarIdentifier(other.calendar()).data()); 887 return false; 888 } 889 890 // Steps 3-4. 891 DifferenceSettings settings; 892 if (args.hasDefined(1)) { 893 // Step 3. 894 Rooted<JSObject*> options( 895 cx, RequireObjectArg(cx, "options", ToName(operation), args[1])); 896 if (!options) { 897 return false; 898 } 899 900 // Step 4. 901 if (!GetDifferenceSettings( 902 cx, operation, options, TemporalUnitGroup::DateTime, 903 TemporalUnit::Nanosecond, TemporalUnit::Hour, &settings)) { 904 return false; 905 } 906 } else { 907 // Steps 3-4. 908 settings = { 909 TemporalUnit::Nanosecond, 910 TemporalUnit::Hour, 911 TemporalRoundingMode::Trunc, 912 Increment{1}, 913 }; 914 } 915 916 // Step 5. 917 if (settings.largestUnit > TemporalUnit::Day) { 918 MOZ_ASSERT(settings.smallestUnit >= settings.largestUnit); 919 920 // Step 5.a. 921 auto timeDuration = 922 DifferenceInstant(zonedDateTime.epochNanoseconds(), 923 other.epochNanoseconds(), settings.roundingIncrement, 924 settings.smallestUnit, settings.roundingMode); 925 926 // Step 5.b. 927 Duration result; 928 if (!TemporalDurationFromInternal(cx, timeDuration, settings.largestUnit, 929 &result)) { 930 return false; 931 } 932 933 // Step 5.c. 934 if (operation == TemporalDifference::Since) { 935 result = result.negate(); 936 } 937 938 // Step 5.d. 939 auto* obj = CreateTemporalDuration(cx, result); 940 if (!obj) { 941 return false; 942 } 943 944 args.rval().setObject(*obj); 945 return true; 946 } 947 948 // Steps 6-7. 949 if (!TimeZoneEquals(zonedDateTime.timeZone(), other.timeZone())) { 950 if (auto one = QuoteString(cx, zonedDateTime.timeZone().identifier())) { 951 if (auto two = QuoteString(cx, other.timeZone().identifier())) { 952 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 953 JSMSG_TEMPORAL_TIMEZONE_INCOMPATIBLE, 954 one.get(), two.get()); 955 } 956 } 957 return false; 958 } 959 960 // Step 8. 961 if (zonedDateTime.epochNanoseconds() == other.epochNanoseconds()) { 962 auto* obj = CreateTemporalDuration(cx, {}); 963 if (!obj) { 964 return false; 965 } 966 967 args.rval().setObject(*obj); 968 return true; 969 } 970 971 // Step 9. 972 InternalDuration internalDuration; 973 if (!DifferenceZonedDateTimeWithRounding(cx, zonedDateTime, 974 other.epochNanoseconds(), settings, 975 &internalDuration)) { 976 return false; 977 } 978 MOZ_ASSERT(IsValidDuration(internalDuration)); 979 980 // Step 10. 981 Duration result; 982 if (!TemporalDurationFromInternal(cx, internalDuration, TemporalUnit::Hour, 983 &result)) { 984 return false; 985 } 986 987 // Step 11. 988 if (operation == TemporalDifference::Since) { 989 result = result.negate(); 990 } 991 992 // Step 12. 993 auto* obj = CreateTemporalDuration(cx, result); 994 if (!obj) { 995 return false; 996 } 997 998 args.rval().setObject(*obj); 999 return true; 1000 } 1001 1002 /** 1003 * AddDurationToZonedDateTime ( operation, zonedDateTime, temporalDurationLike, 1004 * options ) 1005 */ 1006 static bool AddDurationToZonedDateTime(JSContext* cx, 1007 TemporalAddDuration operation, 1008 const CallArgs& args) { 1009 Rooted<ZonedDateTime> zonedDateTime( 1010 cx, &args.thisv().toObject().as<ZonedDateTimeObject>()); 1011 1012 // Step 1. 1013 Duration duration; 1014 if (!ToTemporalDuration(cx, args.get(0), &duration)) { 1015 return false; 1016 } 1017 1018 // Step 2. 1019 if (operation == TemporalAddDuration::Subtract) { 1020 duration = duration.negate(); 1021 } 1022 1023 // Steps 3-4. 1024 auto overflow = TemporalOverflow::Constrain; 1025 if (args.hasDefined(1)) { 1026 // Step 3. 1027 Rooted<JSObject*> options( 1028 cx, RequireObjectArg(cx, "options", ToName(operation), args[1])); 1029 if (!options) { 1030 return false; 1031 } 1032 1033 // Step 4. 1034 if (!GetTemporalOverflowOption(cx, options, &overflow)) { 1035 return false; 1036 } 1037 } 1038 1039 // Step 5. 1040 auto calendar = zonedDateTime.calendar(); 1041 1042 // Step 6. 1043 auto timeZone = zonedDateTime.timeZone(); 1044 1045 // Step 7. 1046 auto internalDuration = ToInternalDurationRecord(duration); 1047 1048 // Step 8. 1049 EpochNanoseconds epochNanoseconds; 1050 if (!::AddZonedDateTime(cx, zonedDateTime, internalDuration, overflow, 1051 &epochNanoseconds)) { 1052 return false; 1053 } 1054 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds)); 1055 1056 // Step 9. 1057 auto* result = 1058 CreateTemporalZonedDateTime(cx, epochNanoseconds, timeZone, calendar); 1059 if (!result) { 1060 return false; 1061 } 1062 1063 args.rval().setObject(*result); 1064 return true; 1065 } 1066 1067 /** 1068 * FormatUTCOffsetNanoseconds ( offsetNanoseconds ) 1069 */ 1070 static JSString* FormatUTCOffsetNanoseconds(JSContext* cx, 1071 int64_t offsetNanoseconds) { 1072 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day)); 1073 1074 // Step 1. 1075 char sign = offsetNanoseconds >= 0 ? '+' : '-'; 1076 1077 // Step 2. 1078 int64_t absoluteNanoseconds = std::abs(offsetNanoseconds); 1079 1080 // Step 6. (Reordered) 1081 int32_t subSecondNanoseconds = int32_t(absoluteNanoseconds % 1'000'000'000); 1082 1083 // Step 5. (Reordered) 1084 int32_t quotient = int32_t(absoluteNanoseconds / 1'000'000'000); 1085 int32_t second = quotient % 60; 1086 1087 // Step 4. (Reordered) 1088 quotient /= 60; 1089 int32_t minute = quotient % 60; 1090 1091 // Step 3. 1092 int32_t hour = quotient / 60; 1093 MOZ_ASSERT(hour < 24, "time zone offset mustn't exceed 24-hours"); 1094 1095 // Format: "sign hour{2} : minute{2} : second{2} . fractional{9}" 1096 constexpr size_t maxLength = 1 + 2 + 1 + 2 + 1 + 2 + 1 + 9; 1097 char result[maxLength]; 1098 1099 size_t n = 0; 1100 1101 // Steps 7-8. (Inlined FormatTimeString). 1102 result[n++] = sign; 1103 result[n++] = char('0' + (hour / 10)); 1104 result[n++] = char('0' + (hour % 10)); 1105 result[n++] = ':'; 1106 result[n++] = char('0' + (minute / 10)); 1107 result[n++] = char('0' + (minute % 10)); 1108 1109 if (second != 0 || subSecondNanoseconds != 0) { 1110 result[n++] = ':'; 1111 result[n++] = char('0' + (second / 10)); 1112 result[n++] = char('0' + (second % 10)); 1113 1114 if (uint32_t fractional = subSecondNanoseconds) { 1115 result[n++] = '.'; 1116 1117 uint32_t k = 100'000'000; 1118 do { 1119 result[n++] = char('0' + (fractional / k)); 1120 fractional %= k; 1121 k /= 10; 1122 } while (fractional); 1123 } 1124 } 1125 1126 MOZ_ASSERT(n <= maxLength); 1127 1128 // Step 9. 1129 return NewStringCopyN<CanGC>(cx, result, n); 1130 } 1131 1132 /** 1133 * Temporal.ZonedDateTime ( epochNanoseconds, timeZone [ , calendar ] ) 1134 */ 1135 static bool ZonedDateTimeConstructor(JSContext* cx, unsigned argc, Value* vp) { 1136 CallArgs args = CallArgsFromVp(argc, vp); 1137 1138 // Step 1. 1139 if (!ThrowIfNotConstructing(cx, args, "Temporal.ZonedDateTime")) { 1140 return false; 1141 } 1142 1143 // Step 2. 1144 Rooted<BigInt*> epochNanoseconds(cx, js::ToBigInt(cx, args.get(0))); 1145 if (!epochNanoseconds) { 1146 return false; 1147 } 1148 1149 // Step 3. 1150 if (!IsValidEpochNanoseconds(epochNanoseconds)) { 1151 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1152 JSMSG_TEMPORAL_INSTANT_INVALID); 1153 return false; 1154 } 1155 1156 // Step 4. 1157 if (!args.get(1).isString()) { 1158 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, args.get(1), 1159 nullptr, "not a string"); 1160 return false; 1161 } 1162 1163 // Step 5. 1164 Rooted<JSString*> timeZoneString(cx, args[1].toString()); 1165 Rooted<ParsedTimeZone> timeZoneParse(cx); 1166 if (!ParseTimeZoneIdentifier(cx, timeZoneString, &timeZoneParse)) { 1167 return false; 1168 } 1169 1170 // Steps 6-7. 1171 Rooted<TimeZoneValue> timeZone(cx); 1172 if (!ToTemporalTimeZone(cx, timeZoneParse, &timeZone)) { 1173 return false; 1174 } 1175 1176 // Steps 8-10. 1177 Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601)); 1178 if (args.hasDefined(2)) { 1179 // Step 9. 1180 if (!args[2].isString()) { 1181 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, args[2], 1182 nullptr, "not a string"); 1183 return false; 1184 } 1185 1186 // Step 10. 1187 Rooted<JSString*> calendarString(cx, args[2].toString()); 1188 if (!CanonicalizeCalendar(cx, calendarString, &calendar)) { 1189 return false; 1190 } 1191 } 1192 1193 // Step 11. 1194 auto* obj = CreateTemporalZonedDateTime(cx, args, epochNanoseconds, timeZone, 1195 calendar); 1196 if (!obj) { 1197 return false; 1198 } 1199 1200 args.rval().setObject(*obj); 1201 return true; 1202 } 1203 1204 /** 1205 * Temporal.ZonedDateTime.from ( item [ , options ] ) 1206 */ 1207 static bool ZonedDateTime_from(JSContext* cx, unsigned argc, Value* vp) { 1208 CallArgs args = CallArgsFromVp(argc, vp); 1209 1210 // Step 1. 1211 Rooted<ZonedDateTime> zonedDateTime(cx); 1212 if (!ToTemporalZonedDateTime(cx, args.get(0), args.get(1), &zonedDateTime)) { 1213 return false; 1214 } 1215 1216 auto* result = CreateTemporalZonedDateTime(cx, zonedDateTime); 1217 if (!result) { 1218 return false; 1219 } 1220 1221 args.rval().setObject(*result); 1222 return true; 1223 } 1224 1225 /** 1226 * Temporal.ZonedDateTime.compare ( one, two ) 1227 */ 1228 static bool ZonedDateTime_compare(JSContext* cx, unsigned argc, Value* vp) { 1229 CallArgs args = CallArgsFromVp(argc, vp); 1230 1231 // Step 1. 1232 Rooted<ZonedDateTime> one(cx); 1233 if (!ToTemporalZonedDateTime(cx, args.get(0), &one)) { 1234 return false; 1235 } 1236 1237 // Step 2. 1238 Rooted<ZonedDateTime> two(cx); 1239 if (!ToTemporalZonedDateTime(cx, args.get(1), &two)) { 1240 return false; 1241 } 1242 1243 // Step 3. 1244 const auto& oneNs = one.epochNanoseconds(); 1245 const auto& twoNs = two.epochNanoseconds(); 1246 args.rval().setInt32(oneNs > twoNs ? 1 : oneNs < twoNs ? -1 : 0); 1247 return true; 1248 } 1249 1250 /** 1251 * get Temporal.ZonedDateTime.prototype.calendarId 1252 */ 1253 static bool ZonedDateTime_calendarId(JSContext* cx, const CallArgs& args) { 1254 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>(); 1255 1256 // Step 3. 1257 auto* str = 1258 NewStringCopy<CanGC>(cx, CalendarIdentifier(zonedDateTime->calendar())); 1259 if (!str) { 1260 return false; 1261 } 1262 1263 args.rval().setString(str); 1264 return true; 1265 } 1266 1267 /** 1268 * get Temporal.ZonedDateTime.prototype.calendarId 1269 */ 1270 static bool ZonedDateTime_calendarId(JSContext* cx, unsigned argc, Value* vp) { 1271 // Steps 1-2. 1272 CallArgs args = CallArgsFromVp(argc, vp); 1273 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_calendarId>(cx, 1274 args); 1275 } 1276 1277 /** 1278 * get Temporal.ZonedDateTime.prototype.timeZoneId 1279 */ 1280 static bool ZonedDateTime_timeZoneId(JSContext* cx, const CallArgs& args) { 1281 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>(); 1282 1283 // Step 3. 1284 args.rval().setString(zonedDateTime->timeZone().identifier()); 1285 return true; 1286 } 1287 1288 /** 1289 * get Temporal.ZonedDateTime.prototype.timeZoneId 1290 */ 1291 static bool ZonedDateTime_timeZoneId(JSContext* cx, unsigned argc, Value* vp) { 1292 // Steps 1-2. 1293 CallArgs args = CallArgsFromVp(argc, vp); 1294 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_timeZoneId>(cx, 1295 args); 1296 } 1297 1298 /** 1299 * get Temporal.ZonedDateTime.prototype.era 1300 */ 1301 static bool ZonedDateTime_era(JSContext* cx, const CallArgs& args) { 1302 Rooted<ZonedDateTime> zonedDateTime( 1303 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1304 1305 // Step 3. 1306 ISODateTime dateTime; 1307 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1308 zonedDateTime.epochNanoseconds(), &dateTime)) { 1309 return false; 1310 } 1311 1312 // Step 4. 1313 return CalendarEra(cx, zonedDateTime.calendar(), dateTime.date, args.rval()); 1314 } 1315 1316 /** 1317 * get Temporal.ZonedDateTime.prototype.era 1318 */ 1319 static bool ZonedDateTime_era(JSContext* cx, unsigned argc, Value* vp) { 1320 // Steps 1-2. 1321 CallArgs args = CallArgsFromVp(argc, vp); 1322 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_era>(cx, args); 1323 } 1324 1325 /** 1326 * get Temporal.ZonedDateTime.prototype.eraYear 1327 */ 1328 static bool ZonedDateTime_eraYear(JSContext* cx, const CallArgs& args) { 1329 Rooted<ZonedDateTime> zonedDateTime( 1330 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1331 1332 // Step 3. 1333 ISODateTime dateTime; 1334 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1335 zonedDateTime.epochNanoseconds(), &dateTime)) { 1336 return false; 1337 } 1338 1339 // Steps 4-6. 1340 return CalendarEraYear(cx, zonedDateTime.calendar(), dateTime.date, 1341 args.rval()); 1342 } 1343 1344 /** 1345 * get Temporal.ZonedDateTime.prototype.eraYear 1346 */ 1347 static bool ZonedDateTime_eraYear(JSContext* cx, unsigned argc, Value* vp) { 1348 // Steps 1-2. 1349 CallArgs args = CallArgsFromVp(argc, vp); 1350 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_eraYear>(cx, args); 1351 } 1352 1353 /** 1354 * get Temporal.ZonedDateTime.prototype.year 1355 */ 1356 static bool ZonedDateTime_year(JSContext* cx, const CallArgs& args) { 1357 Rooted<ZonedDateTime> zonedDateTime( 1358 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1359 1360 // Step 3. 1361 ISODateTime dateTime; 1362 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1363 zonedDateTime.epochNanoseconds(), &dateTime)) { 1364 return false; 1365 } 1366 1367 // Step 4. 1368 return CalendarYear(cx, zonedDateTime.calendar(), dateTime.date, args.rval()); 1369 } 1370 1371 /** 1372 * get Temporal.ZonedDateTime.prototype.year 1373 */ 1374 static bool ZonedDateTime_year(JSContext* cx, unsigned argc, Value* vp) { 1375 // Steps 1-2. 1376 CallArgs args = CallArgsFromVp(argc, vp); 1377 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_year>(cx, args); 1378 } 1379 1380 /** 1381 * get Temporal.ZonedDateTime.prototype.month 1382 */ 1383 static bool ZonedDateTime_month(JSContext* cx, const CallArgs& args) { 1384 Rooted<ZonedDateTime> zonedDateTime( 1385 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1386 1387 // Step 3. 1388 ISODateTime dateTime; 1389 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1390 zonedDateTime.epochNanoseconds(), &dateTime)) { 1391 return false; 1392 } 1393 1394 // Step 4. 1395 return CalendarMonth(cx, zonedDateTime.calendar(), dateTime.date, 1396 args.rval()); 1397 } 1398 1399 /** 1400 * get Temporal.ZonedDateTime.prototype.month 1401 */ 1402 static bool ZonedDateTime_month(JSContext* cx, unsigned argc, Value* vp) { 1403 // Steps 1-2. 1404 CallArgs args = CallArgsFromVp(argc, vp); 1405 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_month>(cx, args); 1406 } 1407 1408 /** 1409 * get Temporal.ZonedDateTime.prototype.monthCode 1410 */ 1411 static bool ZonedDateTime_monthCode(JSContext* cx, const CallArgs& args) { 1412 Rooted<ZonedDateTime> zonedDateTime( 1413 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1414 1415 // Step 3. 1416 ISODateTime dateTime; 1417 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1418 zonedDateTime.epochNanoseconds(), &dateTime)) { 1419 return false; 1420 } 1421 1422 // Step 4. 1423 return CalendarMonthCode(cx, zonedDateTime.calendar(), dateTime.date, 1424 args.rval()); 1425 } 1426 1427 /** 1428 * get Temporal.ZonedDateTime.prototype.monthCode 1429 */ 1430 static bool ZonedDateTime_monthCode(JSContext* cx, unsigned argc, Value* vp) { 1431 // Steps 1-2. 1432 CallArgs args = CallArgsFromVp(argc, vp); 1433 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_monthCode>(cx, 1434 args); 1435 } 1436 1437 /** 1438 * get Temporal.ZonedDateTime.prototype.day 1439 */ 1440 static bool ZonedDateTime_day(JSContext* cx, const CallArgs& args) { 1441 Rooted<ZonedDateTime> zonedDateTime( 1442 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1443 1444 // Step 3. 1445 ISODateTime dateTime; 1446 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1447 zonedDateTime.epochNanoseconds(), &dateTime)) { 1448 return false; 1449 } 1450 1451 // Step 4. 1452 return CalendarDay(cx, zonedDateTime.calendar(), dateTime.date, args.rval()); 1453 } 1454 1455 /** 1456 * get Temporal.ZonedDateTime.prototype.day 1457 */ 1458 static bool ZonedDateTime_day(JSContext* cx, unsigned argc, Value* vp) { 1459 // Steps 1-2. 1460 CallArgs args = CallArgsFromVp(argc, vp); 1461 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_day>(cx, args); 1462 } 1463 1464 /** 1465 * get Temporal.ZonedDateTime.prototype.hour 1466 */ 1467 static bool ZonedDateTime_hour(JSContext* cx, const CallArgs& args) { 1468 Rooted<ZonedDateTime> zonedDateTime( 1469 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1470 1471 // Step 3. 1472 ISODateTime dateTime; 1473 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1474 zonedDateTime.epochNanoseconds(), &dateTime)) { 1475 return false; 1476 } 1477 1478 // Step 4. 1479 args.rval().setInt32(dateTime.time.hour); 1480 return true; 1481 } 1482 1483 /** 1484 * get Temporal.ZonedDateTime.prototype.hour 1485 */ 1486 static bool ZonedDateTime_hour(JSContext* cx, unsigned argc, Value* vp) { 1487 // Steps 1-2. 1488 CallArgs args = CallArgsFromVp(argc, vp); 1489 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_hour>(cx, args); 1490 } 1491 1492 /** 1493 * get Temporal.ZonedDateTime.prototype.minute 1494 */ 1495 static bool ZonedDateTime_minute(JSContext* cx, const CallArgs& args) { 1496 Rooted<ZonedDateTime> zonedDateTime( 1497 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1498 1499 // Step 3. 1500 ISODateTime dateTime; 1501 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1502 zonedDateTime.epochNanoseconds(), &dateTime)) { 1503 return false; 1504 } 1505 1506 // Step 4. 1507 args.rval().setInt32(dateTime.time.minute); 1508 return true; 1509 } 1510 1511 /** 1512 * get Temporal.ZonedDateTime.prototype.minute 1513 */ 1514 static bool ZonedDateTime_minute(JSContext* cx, unsigned argc, Value* vp) { 1515 // Steps 1-2. 1516 CallArgs args = CallArgsFromVp(argc, vp); 1517 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_minute>(cx, args); 1518 } 1519 1520 /** 1521 * get Temporal.ZonedDateTime.prototype.second 1522 */ 1523 static bool ZonedDateTime_second(JSContext* cx, const CallArgs& args) { 1524 Rooted<ZonedDateTime> zonedDateTime( 1525 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1526 1527 // Step 3. 1528 ISODateTime dateTime; 1529 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1530 zonedDateTime.epochNanoseconds(), &dateTime)) { 1531 return false; 1532 } 1533 1534 // Step 4. 1535 args.rval().setInt32(dateTime.time.second); 1536 return true; 1537 } 1538 1539 /** 1540 * get Temporal.ZonedDateTime.prototype.second 1541 */ 1542 static bool ZonedDateTime_second(JSContext* cx, unsigned argc, Value* vp) { 1543 // Steps 1-2. 1544 CallArgs args = CallArgsFromVp(argc, vp); 1545 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_second>(cx, args); 1546 } 1547 1548 /** 1549 * get Temporal.ZonedDateTime.prototype.millisecond 1550 */ 1551 static bool ZonedDateTime_millisecond(JSContext* cx, const CallArgs& args) { 1552 Rooted<ZonedDateTime> zonedDateTime( 1553 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1554 1555 // Step 3. 1556 ISODateTime dateTime; 1557 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1558 zonedDateTime.epochNanoseconds(), &dateTime)) { 1559 return false; 1560 } 1561 1562 // Step 4. 1563 args.rval().setInt32(dateTime.time.millisecond); 1564 return true; 1565 } 1566 1567 /** 1568 * get Temporal.ZonedDateTime.prototype.millisecond 1569 */ 1570 static bool ZonedDateTime_millisecond(JSContext* cx, unsigned argc, Value* vp) { 1571 // Steps 1-2. 1572 CallArgs args = CallArgsFromVp(argc, vp); 1573 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_millisecond>(cx, 1574 args); 1575 } 1576 1577 /** 1578 * get Temporal.ZonedDateTime.prototype.microsecond 1579 */ 1580 static bool ZonedDateTime_microsecond(JSContext* cx, const CallArgs& args) { 1581 Rooted<ZonedDateTime> zonedDateTime( 1582 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1583 1584 // Step 3. 1585 ISODateTime dateTime; 1586 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1587 zonedDateTime.epochNanoseconds(), &dateTime)) { 1588 return false; 1589 } 1590 1591 // Step 4. 1592 args.rval().setInt32(dateTime.time.microsecond); 1593 return true; 1594 } 1595 1596 /** 1597 * get Temporal.ZonedDateTime.prototype.microsecond 1598 */ 1599 static bool ZonedDateTime_microsecond(JSContext* cx, unsigned argc, Value* vp) { 1600 // Steps 1-2. 1601 CallArgs args = CallArgsFromVp(argc, vp); 1602 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_microsecond>(cx, 1603 args); 1604 } 1605 1606 /** 1607 * get Temporal.ZonedDateTime.prototype.nanosecond 1608 */ 1609 static bool ZonedDateTime_nanosecond(JSContext* cx, const CallArgs& args) { 1610 Rooted<ZonedDateTime> zonedDateTime( 1611 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1612 1613 // Step 3. 1614 ISODateTime dateTime; 1615 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1616 zonedDateTime.epochNanoseconds(), &dateTime)) { 1617 return false; 1618 } 1619 1620 // Step 4. 1621 args.rval().setInt32(dateTime.time.nanosecond); 1622 return true; 1623 } 1624 1625 /** 1626 * get Temporal.ZonedDateTime.prototype.nanosecond 1627 */ 1628 static bool ZonedDateTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) { 1629 // Steps 1-2. 1630 CallArgs args = CallArgsFromVp(argc, vp); 1631 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_nanosecond>(cx, 1632 args); 1633 } 1634 1635 /** 1636 * get Temporal.ZonedDateTime.prototype.epochMilliseconds 1637 */ 1638 static bool ZonedDateTime_epochMilliseconds(JSContext* cx, 1639 const CallArgs& args) { 1640 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>(); 1641 1642 // Step 3. 1643 auto epochNs = zonedDateTime->epochNanoseconds(); 1644 1645 // Steps 4-5. 1646 args.rval().setNumber(epochNs.floorToMilliseconds()); 1647 return true; 1648 } 1649 1650 /** 1651 * get Temporal.ZonedDateTime.prototype.epochMilliseconds 1652 */ 1653 static bool ZonedDateTime_epochMilliseconds(JSContext* cx, unsigned argc, 1654 Value* vp) { 1655 // Steps 1-2. 1656 CallArgs args = CallArgsFromVp(argc, vp); 1657 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochMilliseconds>( 1658 cx, args); 1659 } 1660 1661 /** 1662 * get Temporal.ZonedDateTime.prototype.epochNanoseconds 1663 */ 1664 static bool ZonedDateTime_epochNanoseconds(JSContext* cx, 1665 const CallArgs& args) { 1666 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>(); 1667 1668 // Step 3. 1669 auto* nanoseconds = ToBigInt(cx, zonedDateTime->epochNanoseconds()); 1670 if (!nanoseconds) { 1671 return false; 1672 } 1673 1674 args.rval().setBigInt(nanoseconds); 1675 return true; 1676 } 1677 1678 /** 1679 * get Temporal.ZonedDateTime.prototype.epochNanoseconds 1680 */ 1681 static bool ZonedDateTime_epochNanoseconds(JSContext* cx, unsigned argc, 1682 Value* vp) { 1683 // Steps 1-2. 1684 CallArgs args = CallArgsFromVp(argc, vp); 1685 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochNanoseconds>( 1686 cx, args); 1687 } 1688 1689 /** 1690 * get Temporal.ZonedDateTime.prototype.dayOfWeek 1691 */ 1692 static bool ZonedDateTime_dayOfWeek(JSContext* cx, const CallArgs& args) { 1693 Rooted<ZonedDateTime> zonedDateTime( 1694 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1695 1696 // Step 3. 1697 ISODateTime dateTime; 1698 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1699 zonedDateTime.epochNanoseconds(), &dateTime)) { 1700 return false; 1701 } 1702 1703 // Step 4. 1704 return CalendarDayOfWeek(cx, zonedDateTime.calendar(), dateTime.date, 1705 args.rval()); 1706 } 1707 1708 /** 1709 * get Temporal.ZonedDateTime.prototype.dayOfWeek 1710 */ 1711 static bool ZonedDateTime_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) { 1712 // Steps 1-2. 1713 CallArgs args = CallArgsFromVp(argc, vp); 1714 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_dayOfWeek>(cx, 1715 args); 1716 } 1717 1718 /** 1719 * get Temporal.ZonedDateTime.prototype.dayOfYear 1720 */ 1721 static bool ZonedDateTime_dayOfYear(JSContext* cx, const CallArgs& args) { 1722 Rooted<ZonedDateTime> zonedDateTime( 1723 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1724 1725 // Step 3. 1726 ISODateTime dateTime; 1727 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1728 zonedDateTime.epochNanoseconds(), &dateTime)) { 1729 return false; 1730 } 1731 1732 // Step 4. 1733 return CalendarDayOfYear(cx, zonedDateTime.calendar(), dateTime.date, 1734 args.rval()); 1735 } 1736 1737 /** 1738 * get Temporal.ZonedDateTime.prototype.dayOfYear 1739 */ 1740 static bool ZonedDateTime_dayOfYear(JSContext* cx, unsigned argc, Value* vp) { 1741 // Steps 1-2. 1742 CallArgs args = CallArgsFromVp(argc, vp); 1743 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_dayOfYear>(cx, 1744 args); 1745 } 1746 1747 /** 1748 * get Temporal.ZonedDateTime.prototype.weekOfYear 1749 */ 1750 static bool ZonedDateTime_weekOfYear(JSContext* cx, const CallArgs& args) { 1751 Rooted<ZonedDateTime> zonedDateTime( 1752 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1753 1754 // Step 3. 1755 ISODateTime dateTime; 1756 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1757 zonedDateTime.epochNanoseconds(), &dateTime)) { 1758 return false; 1759 } 1760 1761 // Steps 4-6. 1762 return CalendarWeekOfYear(cx, zonedDateTime.calendar(), dateTime.date, 1763 args.rval()); 1764 } 1765 1766 /** 1767 * get Temporal.ZonedDateTime.prototype.weekOfYear 1768 */ 1769 static bool ZonedDateTime_weekOfYear(JSContext* cx, unsigned argc, Value* vp) { 1770 // Steps 1-2. 1771 CallArgs args = CallArgsFromVp(argc, vp); 1772 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_weekOfYear>(cx, 1773 args); 1774 } 1775 1776 /** 1777 * get Temporal.ZonedDateTime.prototype.yearOfWeek 1778 */ 1779 static bool ZonedDateTime_yearOfWeek(JSContext* cx, const CallArgs& args) { 1780 Rooted<ZonedDateTime> zonedDateTime( 1781 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1782 1783 // Step 3. 1784 ISODateTime dateTime; 1785 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1786 zonedDateTime.epochNanoseconds(), &dateTime)) { 1787 return false; 1788 } 1789 1790 // Steps 4-6. 1791 return CalendarYearOfWeek(cx, zonedDateTime.calendar(), dateTime.date, 1792 args.rval()); 1793 } 1794 1795 /** 1796 * get Temporal.ZonedDateTime.prototype.yearOfWeek 1797 */ 1798 static bool ZonedDateTime_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) { 1799 // Steps 1-2. 1800 CallArgs args = CallArgsFromVp(argc, vp); 1801 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_yearOfWeek>(cx, 1802 args); 1803 } 1804 1805 /** 1806 * get Temporal.ZonedDateTime.prototype.hoursInDay 1807 */ 1808 static bool ZonedDateTime_hoursInDay(JSContext* cx, const CallArgs& args) { 1809 Rooted<ZonedDateTime> zonedDateTime( 1810 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1811 1812 // Step 3. 1813 auto timeZone = zonedDateTime.timeZone(); 1814 1815 // Step 4. 1816 ISODateTime dateTime; 1817 if (!GetISODateTimeFor(cx, timeZone, zonedDateTime.epochNanoseconds(), 1818 &dateTime)) { 1819 return false; 1820 } 1821 1822 // Step 5. 1823 const auto& today = dateTime.date; 1824 1825 // Step 6. 1826 auto tomorrow = BalanceISODate(today, 1); 1827 if (!ISODateWithinLimits(tomorrow)) { 1828 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1829 JSMSG_TEMPORAL_PLAIN_DATE_INVALID); 1830 return false; 1831 } 1832 1833 // Step 7. 1834 EpochNanoseconds todayNs; 1835 if (!GetStartOfDay(cx, timeZone, today, &todayNs)) { 1836 return false; 1837 } 1838 1839 // Step 8. 1840 EpochNanoseconds tomorrowNs; 1841 if (!GetStartOfDay(cx, timeZone, tomorrow, &tomorrowNs)) { 1842 return false; 1843 } 1844 1845 // Step 9. 1846 auto diff = tomorrowNs - todayNs; 1847 MOZ_ASSERT(diff.abs() <= EpochDuration::fromDays(2), 1848 "maximum day length for repeated days doesn't exceed two days"); 1849 1850 static_assert(EpochDuration::fromDays(2).toNanoseconds() < Int128{INT64_MAX}, 1851 "two days in nanoseconds fits into int64_t"); 1852 1853 // Step 10. (Inlined TotalTimeDuration) 1854 constexpr auto nsPerHour = ToNanoseconds(TemporalUnit::Hour); 1855 args.rval().setNumber( 1856 FractionToDouble(int64_t(diff.toNanoseconds()), nsPerHour)); 1857 return true; 1858 } 1859 1860 /** 1861 * get Temporal.ZonedDateTime.prototype.hoursInDay 1862 */ 1863 static bool ZonedDateTime_hoursInDay(JSContext* cx, unsigned argc, Value* vp) { 1864 // Steps 1-2. 1865 CallArgs args = CallArgsFromVp(argc, vp); 1866 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_hoursInDay>(cx, 1867 args); 1868 } 1869 1870 /** 1871 * get Temporal.ZonedDateTime.prototype.daysInWeek 1872 */ 1873 static bool ZonedDateTime_daysInWeek(JSContext* cx, const CallArgs& args) { 1874 Rooted<ZonedDateTime> zonedDateTime( 1875 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1876 1877 // Step 3. 1878 ISODateTime dateTime; 1879 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1880 zonedDateTime.epochNanoseconds(), &dateTime)) { 1881 return false; 1882 } 1883 1884 // Step 4. 1885 return CalendarDaysInWeek(cx, zonedDateTime.calendar(), dateTime.date, 1886 args.rval()); 1887 } 1888 1889 /** 1890 * get Temporal.ZonedDateTime.prototype.daysInWeek 1891 */ 1892 static bool ZonedDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) { 1893 // Steps 1-2. 1894 CallArgs args = CallArgsFromVp(argc, vp); 1895 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInWeek>(cx, 1896 args); 1897 } 1898 1899 /** 1900 * get Temporal.ZonedDateTime.prototype.daysInMonth 1901 */ 1902 static bool ZonedDateTime_daysInMonth(JSContext* cx, const CallArgs& args) { 1903 Rooted<ZonedDateTime> zonedDateTime( 1904 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1905 1906 // Step 3. 1907 ISODateTime dateTime; 1908 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1909 zonedDateTime.epochNanoseconds(), &dateTime)) { 1910 return false; 1911 } 1912 1913 // Step 4. 1914 return CalendarDaysInMonth(cx, zonedDateTime.calendar(), dateTime.date, 1915 args.rval()); 1916 } 1917 1918 /** 1919 * get Temporal.ZonedDateTime.prototype.daysInMonth 1920 */ 1921 static bool ZonedDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) { 1922 // Steps 1-2. 1923 CallArgs args = CallArgsFromVp(argc, vp); 1924 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInMonth>(cx, 1925 args); 1926 } 1927 1928 /** 1929 * get Temporal.ZonedDateTime.prototype.daysInYear 1930 */ 1931 static bool ZonedDateTime_daysInYear(JSContext* cx, const CallArgs& args) { 1932 Rooted<ZonedDateTime> zonedDateTime( 1933 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1934 1935 // Step 3. 1936 ISODateTime dateTime; 1937 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1938 zonedDateTime.epochNanoseconds(), &dateTime)) { 1939 return false; 1940 } 1941 1942 // Step 4. 1943 return CalendarDaysInYear(cx, zonedDateTime.calendar(), dateTime.date, 1944 args.rval()); 1945 } 1946 1947 /** 1948 * get Temporal.ZonedDateTime.prototype.daysInYear 1949 */ 1950 static bool ZonedDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) { 1951 // Steps 1-2. 1952 CallArgs args = CallArgsFromVp(argc, vp); 1953 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInYear>(cx, 1954 args); 1955 } 1956 1957 /** 1958 * get Temporal.ZonedDateTime.prototype.monthsInYear 1959 */ 1960 static bool ZonedDateTime_monthsInYear(JSContext* cx, const CallArgs& args) { 1961 Rooted<ZonedDateTime> zonedDateTime( 1962 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1963 1964 // Step 3. 1965 ISODateTime dateTime; 1966 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1967 zonedDateTime.epochNanoseconds(), &dateTime)) { 1968 return false; 1969 } 1970 1971 // Step 4. 1972 return CalendarMonthsInYear(cx, zonedDateTime.calendar(), dateTime.date, 1973 args.rval()); 1974 } 1975 1976 /** 1977 * get Temporal.ZonedDateTime.prototype.monthsInYear 1978 */ 1979 static bool ZonedDateTime_monthsInYear(JSContext* cx, unsigned argc, 1980 Value* vp) { 1981 // Steps 1-2. 1982 CallArgs args = CallArgsFromVp(argc, vp); 1983 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_monthsInYear>( 1984 cx, args); 1985 } 1986 1987 /** 1988 * get Temporal.ZonedDateTime.prototype.inLeapYear 1989 */ 1990 static bool ZonedDateTime_inLeapYear(JSContext* cx, const CallArgs& args) { 1991 Rooted<ZonedDateTime> zonedDateTime( 1992 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 1993 1994 // Step 3. 1995 ISODateTime dateTime; 1996 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 1997 zonedDateTime.epochNanoseconds(), &dateTime)) { 1998 return false; 1999 } 2000 2001 // Step 4. 2002 return CalendarInLeapYear(cx, zonedDateTime.calendar(), dateTime.date, 2003 args.rval()); 2004 } 2005 2006 /** 2007 * get Temporal.ZonedDateTime.prototype.inLeapYear 2008 */ 2009 static bool ZonedDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) { 2010 // Steps 1-2. 2011 CallArgs args = CallArgsFromVp(argc, vp); 2012 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_inLeapYear>(cx, 2013 args); 2014 } 2015 2016 /** 2017 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds 2018 */ 2019 static bool ZonedDateTime_offsetNanoseconds(JSContext* cx, 2020 const CallArgs& args) { 2021 Rooted<ZonedDateTime> zonedDateTime( 2022 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2023 2024 // Step 3. 2025 int64_t offsetNanoseconds; 2026 if (!GetOffsetNanosecondsFor(cx, zonedDateTime.timeZone(), 2027 zonedDateTime.epochNanoseconds(), 2028 &offsetNanoseconds)) { 2029 return false; 2030 } 2031 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day)); 2032 2033 args.rval().setNumber(offsetNanoseconds); 2034 return true; 2035 } 2036 2037 /** 2038 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds 2039 */ 2040 static bool ZonedDateTime_offsetNanoseconds(JSContext* cx, unsigned argc, 2041 Value* vp) { 2042 // Steps 1-2. 2043 CallArgs args = CallArgsFromVp(argc, vp); 2044 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offsetNanoseconds>( 2045 cx, args); 2046 } 2047 2048 /** 2049 * get Temporal.ZonedDateTime.prototype.offset 2050 */ 2051 static bool ZonedDateTime_offset(JSContext* cx, const CallArgs& args) { 2052 Rooted<ZonedDateTime> zonedDateTime( 2053 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2054 2055 // Step 3. 2056 int64_t offsetNanoseconds; 2057 if (!GetOffsetNanosecondsFor(cx, zonedDateTime.timeZone(), 2058 zonedDateTime.epochNanoseconds(), 2059 &offsetNanoseconds)) { 2060 return false; 2061 } 2062 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day)); 2063 2064 // Step 4. 2065 JSString* str = FormatUTCOffsetNanoseconds(cx, offsetNanoseconds); 2066 if (!str) { 2067 return false; 2068 } 2069 2070 args.rval().setString(str); 2071 return true; 2072 } 2073 2074 /** 2075 * get Temporal.ZonedDateTime.prototype.offset 2076 */ 2077 static bool ZonedDateTime_offset(JSContext* cx, unsigned argc, Value* vp) { 2078 // Steps 1-2. 2079 CallArgs args = CallArgsFromVp(argc, vp); 2080 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offset>(cx, args); 2081 } 2082 2083 /** 2084 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options 2085 * ] ) 2086 */ 2087 static bool ZonedDateTime_with(JSContext* cx, const CallArgs& args) { 2088 Rooted<ZonedDateTime> zonedDateTime( 2089 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2090 2091 // Step 3. 2092 Rooted<JSObject*> temporalZonedDateTimeLike( 2093 cx, 2094 RequireObjectArg(cx, "temporalZonedDateTimeLike", "with", args.get(0))); 2095 if (!temporalZonedDateTimeLike) { 2096 return false; 2097 } 2098 if (!ThrowIfTemporalLikeObject(cx, temporalZonedDateTimeLike)) { 2099 return false; 2100 } 2101 2102 // Step 4. 2103 const auto& epochNs = zonedDateTime.epochNanoseconds(); 2104 2105 // Step 5. 2106 auto timeZone = zonedDateTime.timeZone(); 2107 2108 // Step 6. 2109 auto calendar = zonedDateTime.calendar(); 2110 2111 // Step 7. 2112 int64_t offsetNanoseconds; 2113 if (!GetOffsetNanosecondsFor(cx, timeZone, epochNs, &offsetNanoseconds)) { 2114 return false; 2115 } 2116 2117 // Step 8. 2118 auto dateTime = GetISODateTimeFor(epochNs, offsetNanoseconds); 2119 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime)); 2120 2121 // Step 9. 2122 Rooted<PlainDate> date(cx, PlainDate{dateTime.date, calendar}); 2123 Rooted<CalendarFields> fields(cx); 2124 if (!ISODateToFields(cx, date, &fields)) { 2125 return false; 2126 } 2127 2128 // Steps 10-16. 2129 fields.setHour(dateTime.time.hour); 2130 fields.setMinute(dateTime.time.minute); 2131 fields.setSecond(dateTime.time.second); 2132 fields.setMillisecond(dateTime.time.millisecond); 2133 fields.setMicrosecond(dateTime.time.microsecond); 2134 fields.setNanosecond(dateTime.time.nanosecond); 2135 fields.setOffset(OffsetField{offsetNanoseconds}); 2136 2137 // Step 17. 2138 Rooted<CalendarFields> partialZonedDateTime(cx); 2139 if (!PreparePartialCalendarFields(cx, calendar, temporalZonedDateTimeLike, 2140 { 2141 CalendarField::Year, 2142 CalendarField::Month, 2143 CalendarField::MonthCode, 2144 CalendarField::Day, 2145 CalendarField::Hour, 2146 CalendarField::Minute, 2147 CalendarField::Second, 2148 CalendarField::Millisecond, 2149 CalendarField::Microsecond, 2150 CalendarField::Nanosecond, 2151 CalendarField::Offset, 2152 }, 2153 &partialZonedDateTime)) { 2154 return false; 2155 } 2156 MOZ_ASSERT(!partialZonedDateTime.keys().isEmpty()); 2157 2158 // Step 18. 2159 fields = CalendarMergeFields(calendar, fields, partialZonedDateTime); 2160 2161 // Steps 19-22. 2162 auto disambiguation = TemporalDisambiguation::Compatible; 2163 auto offset = TemporalOffset::Prefer; 2164 auto overflow = TemporalOverflow::Constrain; 2165 if (args.hasDefined(1)) { 2166 // Step 19. 2167 Rooted<JSObject*> options(cx, 2168 RequireObjectArg(cx, "options", "with", args[1])); 2169 if (!options) { 2170 return false; 2171 } 2172 2173 // Step 20. 2174 if (!GetTemporalDisambiguationOption(cx, options, &disambiguation)) { 2175 return false; 2176 } 2177 2178 // Step 21. 2179 if (!GetTemporalOffsetOption(cx, options, &offset)) { 2180 return false; 2181 } 2182 2183 // Step 22. 2184 if (!GetTemporalOverflowOption(cx, options, &overflow)) { 2185 return false; 2186 } 2187 } 2188 2189 // Step 23. 2190 ISODateTime dateTimeResult; 2191 if (!InterpretTemporalDateTimeFields(cx, calendar, fields, overflow, 2192 &dateTimeResult)) { 2193 return false; 2194 } 2195 2196 // Step 24. 2197 int64_t newOffsetNanoseconds = int64_t(fields.offset()); 2198 2199 // Step 25. 2200 EpochNanoseconds epochNanoseconds; 2201 if (!InterpretISODateTimeOffset( 2202 cx, dateTimeResult, OffsetBehaviour::Option, newOffsetNanoseconds, 2203 timeZone, disambiguation, offset, MatchBehaviour::MatchExactly, 2204 &epochNanoseconds)) { 2205 return false; 2206 } 2207 2208 // Step 26. 2209 auto* result = 2210 CreateTemporalZonedDateTime(cx, epochNanoseconds, timeZone, calendar); 2211 if (!result) { 2212 return false; 2213 } 2214 2215 args.rval().setObject(*result); 2216 return true; 2217 } 2218 2219 /** 2220 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options 2221 * ] ) 2222 */ 2223 static bool ZonedDateTime_with(JSContext* cx, unsigned argc, Value* vp) { 2224 // Steps 1-2. 2225 CallArgs args = CallArgsFromVp(argc, vp); 2226 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_with>(cx, args); 2227 } 2228 2229 /** 2230 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] ) 2231 */ 2232 static bool ZonedDateTime_withPlainTime(JSContext* cx, const CallArgs& args) { 2233 Rooted<ZonedDateTime> zonedDateTime( 2234 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2235 2236 // Step 3. 2237 auto timeZone = zonedDateTime.timeZone(); 2238 2239 // Step 4. 2240 auto calendar = zonedDateTime.calendar(); 2241 2242 // Step 5. 2243 ISODateTime isoDateTime; 2244 if (!GetISODateTimeFor(cx, timeZone, zonedDateTime.epochNanoseconds(), 2245 &isoDateTime)) { 2246 return false; 2247 } 2248 2249 // Steps 6-7. 2250 EpochNanoseconds epochNs; 2251 if (!args.hasDefined(0)) { 2252 // Step 6.a. 2253 if (!GetStartOfDay(cx, timeZone, isoDateTime.date, &epochNs)) { 2254 return false; 2255 } 2256 } else { 2257 // Step 7.a. 2258 Time time; 2259 if (!ToTemporalTime(cx, args[0], &time)) { 2260 return false; 2261 } 2262 2263 // Step 7.b. 2264 auto resultISODateTime = ISODateTime{isoDateTime.date, time}; 2265 if (!ISODateTimeWithinLimits(resultISODateTime)) { 2266 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2267 JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); 2268 return false; 2269 } 2270 2271 // Step 7.c. 2272 if (!GetEpochNanosecondsFor(cx, timeZone, resultISODateTime, 2273 TemporalDisambiguation::Compatible, &epochNs)) { 2274 return false; 2275 } 2276 } 2277 2278 // Step 8. 2279 auto* result = CreateTemporalZonedDateTime(cx, epochNs, timeZone, calendar); 2280 if (!result) { 2281 return false; 2282 } 2283 2284 args.rval().setObject(*result); 2285 return true; 2286 } 2287 2288 /** 2289 * Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] ) 2290 */ 2291 static bool ZonedDateTime_withPlainTime(JSContext* cx, unsigned argc, 2292 Value* vp) { 2293 // Steps 1-2. 2294 CallArgs args = CallArgsFromVp(argc, vp); 2295 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withPlainTime>( 2296 cx, args); 2297 } 2298 2299 /** 2300 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike ) 2301 */ 2302 static bool ZonedDateTime_withTimeZone(JSContext* cx, const CallArgs& args) { 2303 Rooted<ZonedDateTime> zonedDateTime( 2304 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2305 2306 // Step 3. 2307 Rooted<TimeZoneValue> timeZone(cx); 2308 if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) { 2309 return false; 2310 } 2311 2312 // Step 4. 2313 auto* result = CreateTemporalZonedDateTime( 2314 cx, zonedDateTime.epochNanoseconds(), timeZone, zonedDateTime.calendar()); 2315 if (!result) { 2316 return false; 2317 } 2318 2319 args.rval().setObject(*result); 2320 return true; 2321 } 2322 2323 /** 2324 * Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike ) 2325 */ 2326 static bool ZonedDateTime_withTimeZone(JSContext* cx, unsigned argc, 2327 Value* vp) { 2328 // Steps 1-2. 2329 CallArgs args = CallArgsFromVp(argc, vp); 2330 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withTimeZone>( 2331 cx, args); 2332 } 2333 2334 /** 2335 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike ) 2336 */ 2337 static bool ZonedDateTime_withCalendar(JSContext* cx, const CallArgs& args) { 2338 Rooted<ZonedDateTime> zonedDateTime( 2339 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2340 2341 // Step 3. 2342 Rooted<CalendarValue> calendar(cx); 2343 if (!ToTemporalCalendar(cx, args.get(0), &calendar)) { 2344 return false; 2345 } 2346 2347 // Step 4. 2348 auto* result = CreateTemporalZonedDateTime( 2349 cx, zonedDateTime.epochNanoseconds(), zonedDateTime.timeZone(), calendar); 2350 if (!result) { 2351 return false; 2352 } 2353 2354 args.rval().setObject(*result); 2355 return true; 2356 } 2357 2358 /** 2359 * Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike ) 2360 */ 2361 static bool ZonedDateTime_withCalendar(JSContext* cx, unsigned argc, 2362 Value* vp) { 2363 // Steps 1-2. 2364 CallArgs args = CallArgsFromVp(argc, vp); 2365 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_withCalendar>( 2366 cx, args); 2367 } 2368 2369 /** 2370 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] ) 2371 */ 2372 static bool ZonedDateTime_add(JSContext* cx, const CallArgs& args) { 2373 // Step 3. 2374 return AddDurationToZonedDateTime(cx, TemporalAddDuration::Add, args); 2375 } 2376 2377 /** 2378 * Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] ) 2379 */ 2380 static bool ZonedDateTime_add(JSContext* cx, unsigned argc, Value* vp) { 2381 // Steps 1-2. 2382 CallArgs args = CallArgsFromVp(argc, vp); 2383 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_add>(cx, args); 2384 } 2385 2386 /** 2387 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options 2388 * ] ) 2389 */ 2390 static bool ZonedDateTime_subtract(JSContext* cx, const CallArgs& args) { 2391 // Step 3. 2392 return AddDurationToZonedDateTime(cx, TemporalAddDuration::Subtract, args); 2393 } 2394 2395 /** 2396 * Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options 2397 * ] ) 2398 */ 2399 static bool ZonedDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) { 2400 // Steps 1-2. 2401 CallArgs args = CallArgsFromVp(argc, vp); 2402 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_subtract>(cx, 2403 args); 2404 } 2405 2406 /** 2407 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] ) 2408 */ 2409 static bool ZonedDateTime_until(JSContext* cx, const CallArgs& args) { 2410 // Step 3. 2411 return DifferenceTemporalZonedDateTime(cx, TemporalDifference::Until, args); 2412 } 2413 2414 /** 2415 * Temporal.ZonedDateTime.prototype.until ( other [ , options ] ) 2416 */ 2417 static bool ZonedDateTime_until(JSContext* cx, unsigned argc, Value* vp) { 2418 // Steps 1-2. 2419 CallArgs args = CallArgsFromVp(argc, vp); 2420 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_until>(cx, args); 2421 } 2422 2423 /** 2424 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] ) 2425 */ 2426 static bool ZonedDateTime_since(JSContext* cx, const CallArgs& args) { 2427 // Step 3. 2428 return DifferenceTemporalZonedDateTime(cx, TemporalDifference::Since, args); 2429 } 2430 2431 /** 2432 * Temporal.ZonedDateTime.prototype.since ( other [ , options ] ) 2433 */ 2434 static bool ZonedDateTime_since(JSContext* cx, unsigned argc, Value* vp) { 2435 // Steps 1-2. 2436 CallArgs args = CallArgsFromVp(argc, vp); 2437 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_since>(cx, args); 2438 } 2439 2440 /** 2441 * Temporal.ZonedDateTime.prototype.round ( roundTo ) 2442 */ 2443 static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { 2444 Rooted<ZonedDateTime> zonedDateTime( 2445 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2446 2447 // Steps 3-13. 2448 auto smallestUnit = TemporalUnit::Unset; 2449 auto roundingMode = TemporalRoundingMode::HalfExpand; 2450 auto roundingIncrement = Increment{1}; 2451 if (args.get(0).isString()) { 2452 // Step 4. (Not applicable in our implementation.) 2453 2454 // Step 9. 2455 Rooted<JSString*> paramString(cx, args[0].toString()); 2456 if (!GetTemporalUnitValuedOption( 2457 cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) { 2458 return false; 2459 } 2460 2461 // Step 10. 2462 if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, 2463 smallestUnit, TemporalUnitGroup::DayTime)) { 2464 return false; 2465 } 2466 2467 // Steps 6-8 and 11-13. (Implicit) 2468 } else { 2469 // Steps 3 and 5.a 2470 Rooted<JSObject*> roundTo( 2471 cx, RequireObjectArg(cx, "roundTo", "round", args.get(0))); 2472 if (!roundTo) { 2473 return false; 2474 } 2475 2476 // Steps 6-7. 2477 if (!GetRoundingIncrementOption(cx, roundTo, &roundingIncrement)) { 2478 return false; 2479 } 2480 2481 // Step 8. 2482 if (!GetRoundingModeOption(cx, roundTo, &roundingMode)) { 2483 return false; 2484 } 2485 2486 // Step 9. 2487 if (!GetTemporalUnitValuedOption(cx, roundTo, TemporalUnitKey::SmallestUnit, 2488 &smallestUnit)) { 2489 return false; 2490 } 2491 2492 if (smallestUnit == TemporalUnit::Unset) { 2493 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2494 JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit"); 2495 return false; 2496 } 2497 2498 // Step 10. 2499 if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, 2500 smallestUnit, TemporalUnitGroup::DayTime)) { 2501 return false; 2502 } 2503 MOZ_ASSERT(TemporalUnit::Day <= smallestUnit && 2504 smallestUnit <= TemporalUnit::Nanosecond); 2505 2506 // Steps 11-12. 2507 auto maximum = Increment{1}; 2508 bool inclusive = true; 2509 if (smallestUnit > TemporalUnit::Day) { 2510 maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit); 2511 inclusive = false; 2512 } 2513 2514 // Step 13. 2515 if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum, 2516 inclusive)) { 2517 return false; 2518 } 2519 } 2520 2521 // Step 14. 2522 if (smallestUnit == TemporalUnit::Nanosecond && 2523 roundingIncrement == Increment{1}) { 2524 // Step 14.a. 2525 auto* result = CreateTemporalZonedDateTime( 2526 cx, zonedDateTime.epochNanoseconds(), zonedDateTime.timeZone(), 2527 zonedDateTime.calendar()); 2528 if (!result) { 2529 return false; 2530 } 2531 2532 args.rval().setObject(*result); 2533 return true; 2534 } 2535 2536 // Step 15. 2537 auto thisNs = zonedDateTime.epochNanoseconds(); 2538 2539 // Step 16. 2540 auto timeZone = zonedDateTime.timeZone(); 2541 2542 // Step 17. 2543 auto calendar = zonedDateTime.calendar(); 2544 2545 // Step 18. 2546 ISODateTime isoDateTime; 2547 if (!GetISODateTimeFor(cx, timeZone, thisNs, &isoDateTime)) { 2548 return false; 2549 } 2550 2551 // Steps 19-20. 2552 EpochNanoseconds epochNanoseconds; 2553 if (smallestUnit == TemporalUnit::Day) { 2554 // Step 19.a. 2555 const auto& dateStart = isoDateTime.date; 2556 2557 // Step 19.b. 2558 auto dateEnd = BalanceISODate(dateStart, 1); 2559 if (!ISODateWithinLimits(dateEnd)) { 2560 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2561 JSMSG_TEMPORAL_PLAIN_DATE_INVALID); 2562 return false; 2563 } 2564 2565 // Step 19.c. 2566 EpochNanoseconds startNs; 2567 if (!GetStartOfDay(cx, timeZone, dateStart, &startNs)) { 2568 return false; 2569 } 2570 2571 // Step 19.d. 2572 MOZ_ASSERT(thisNs >= startNs); 2573 2574 // Step 19.e. 2575 EpochNanoseconds endNs; 2576 if (!GetStartOfDay(cx, timeZone, dateEnd, &endNs)) { 2577 return false; 2578 } 2579 2580 // Step 19.f. 2581 MOZ_ASSERT(thisNs < endNs); 2582 2583 // Step 19.g. 2584 auto dayLengthNs = endNs - startNs; 2585 MOZ_ASSERT(IsValidEpochDuration(dayLengthNs)); 2586 MOZ_ASSERT(dayLengthNs > EpochDuration{}, "dayLengthNs is positive"); 2587 2588 // Step 19.h. (Inlined TimeDurationFromEpochNanosecondsDifference) 2589 auto dayProgressNs = thisNs - startNs; 2590 MOZ_ASSERT(IsValidEpochDuration(dayProgressNs)); 2591 MOZ_ASSERT(dayProgressNs >= EpochDuration{}, 2592 "dayProgressNs is non-negative"); 2593 2594 MOZ_ASSERT(startNs <= thisNs && thisNs < endNs); 2595 MOZ_ASSERT(dayProgressNs < dayLengthNs); 2596 MOZ_ASSERT(dayLengthNs <= EpochDuration::fromDays(2), 2597 "maximum day length for repeated days"); 2598 2599 // Step 19.i. (Inlined RoundTimeDurationToIncrement) 2600 auto rounded = RoundNumberToIncrement( 2601 static_cast<int64_t>(dayProgressNs.toNanoseconds()), 2602 static_cast<int64_t>(dayLengthNs.toNanoseconds()), roundingMode); 2603 auto roundedDaysNs = EpochDuration::fromNanoseconds(rounded); 2604 MOZ_ASSERT(roundedDaysNs == EpochDuration{} || 2605 roundedDaysNs == dayLengthNs); 2606 MOZ_ASSERT(IsValidEpochDuration(roundedDaysNs)); 2607 2608 // Step 19.j. (Inlined AddTimeDurationToEpochNanoseconds) 2609 epochNanoseconds = startNs + roundedDaysNs; 2610 MOZ_ASSERT(epochNanoseconds == startNs || epochNanoseconds == endNs); 2611 } else { 2612 // Step 20.a. 2613 auto roundResult = RoundISODateTime(isoDateTime, roundingIncrement, 2614 smallestUnit, roundingMode); 2615 2616 // Step 20.b. 2617 int64_t offsetNanoseconds; 2618 if (!GetOffsetNanosecondsFor(cx, timeZone, thisNs, &offsetNanoseconds)) { 2619 return false; 2620 } 2621 MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day)); 2622 2623 // Step 20.c. 2624 if (!InterpretISODateTimeOffset( 2625 cx, roundResult, OffsetBehaviour::Option, offsetNanoseconds, 2626 timeZone, TemporalDisambiguation::Compatible, 2627 TemporalOffset::Prefer, MatchBehaviour::MatchExactly, 2628 &epochNanoseconds)) { 2629 return false; 2630 } 2631 } 2632 MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds)); 2633 2634 // Step 21. 2635 auto* result = 2636 CreateTemporalZonedDateTime(cx, epochNanoseconds, timeZone, calendar); 2637 if (!result) { 2638 return false; 2639 } 2640 2641 args.rval().setObject(*result); 2642 return true; 2643 } 2644 2645 /** 2646 * Temporal.ZonedDateTime.prototype.round ( roundTo ) 2647 */ 2648 static bool ZonedDateTime_round(JSContext* cx, unsigned argc, Value* vp) { 2649 // Steps 1-2. 2650 CallArgs args = CallArgsFromVp(argc, vp); 2651 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_round>(cx, args); 2652 } 2653 2654 /** 2655 * Temporal.ZonedDateTime.prototype.equals ( other ) 2656 */ 2657 static bool ZonedDateTime_equals(JSContext* cx, const CallArgs& args) { 2658 Rooted<ZonedDateTime> zonedDateTime( 2659 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2660 2661 // Step 3. 2662 Rooted<ZonedDateTime> other(cx); 2663 if (!ToTemporalZonedDateTime(cx, args.get(0), &other)) { 2664 return false; 2665 } 2666 2667 // Steps 4-6. 2668 bool equals = zonedDateTime.epochNanoseconds() == other.epochNanoseconds() && 2669 TimeZoneEquals(zonedDateTime.timeZone(), other.timeZone()) && 2670 CalendarEquals(zonedDateTime.calendar(), other.calendar()); 2671 2672 args.rval().setBoolean(equals); 2673 return true; 2674 } 2675 2676 /** 2677 * Temporal.ZonedDateTime.prototype.equals ( other ) 2678 */ 2679 static bool ZonedDateTime_equals(JSContext* cx, unsigned argc, Value* vp) { 2680 // Steps 1-2. 2681 CallArgs args = CallArgsFromVp(argc, vp); 2682 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_equals>(cx, args); 2683 } 2684 2685 /** 2686 * Temporal.ZonedDateTime.prototype.toString ( [ options ] ) 2687 */ 2688 static bool ZonedDateTime_toString(JSContext* cx, const CallArgs& args) { 2689 Rooted<ZonedDateTime> zonedDateTime( 2690 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2691 2692 SecondsStringPrecision precision = {Precision::Auto(), 2693 TemporalUnit::Nanosecond, Increment{1}}; 2694 auto roundingMode = TemporalRoundingMode::Trunc; 2695 auto showCalendar = ShowCalendar::Auto; 2696 auto showTimeZone = ShowTimeZoneName::Auto; 2697 auto showOffset = ShowOffset::Auto; 2698 if (args.hasDefined(0)) { 2699 // Step 3. 2700 Rooted<JSObject*> options( 2701 cx, RequireObjectArg(cx, "options", "toString", args[0])); 2702 if (!options) { 2703 return false; 2704 } 2705 2706 // Steps 4-5. 2707 if (!GetTemporalShowCalendarNameOption(cx, options, &showCalendar)) { 2708 return false; 2709 } 2710 2711 // Step 6. 2712 auto digits = Precision::Auto(); 2713 if (!GetTemporalFractionalSecondDigitsOption(cx, options, &digits)) { 2714 return false; 2715 } 2716 2717 // Step 7. 2718 if (!GetTemporalShowOffsetOption(cx, options, &showOffset)) { 2719 return false; 2720 } 2721 2722 // Step 8. 2723 if (!GetRoundingModeOption(cx, options, &roundingMode)) { 2724 return false; 2725 } 2726 2727 // Step 9. 2728 auto smallestUnit = TemporalUnit::Unset; 2729 if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, 2730 &smallestUnit)) { 2731 return false; 2732 } 2733 2734 // Step 10. 2735 if (!GetTemporalShowTimeZoneNameOption(cx, options, &showTimeZone)) { 2736 return false; 2737 } 2738 2739 // Step 11. 2740 if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, 2741 smallestUnit, TemporalUnitGroup::Time)) { 2742 return false; 2743 } 2744 2745 // Step 12. 2746 if (smallestUnit == TemporalUnit::Hour) { 2747 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2748 JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour", 2749 "smallestUnit"); 2750 return false; 2751 } 2752 2753 // Step 13. 2754 precision = ToSecondsStringPrecision(smallestUnit, digits); 2755 } 2756 2757 // Step 14. 2758 JSString* str = TemporalZonedDateTimeToString( 2759 cx, zonedDateTime, precision.precision, showCalendar, showTimeZone, 2760 showOffset, precision.increment, precision.unit, roundingMode); 2761 if (!str) { 2762 return false; 2763 } 2764 2765 args.rval().setString(str); 2766 return true; 2767 } 2768 2769 /** 2770 * Temporal.ZonedDateTime.prototype.toString ( [ options ] ) 2771 */ 2772 static bool ZonedDateTime_toString(JSContext* cx, unsigned argc, Value* vp) { 2773 // Steps 1-2. 2774 CallArgs args = CallArgsFromVp(argc, vp); 2775 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toString>(cx, 2776 args); 2777 } 2778 2779 /** 2780 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] ) 2781 */ 2782 static bool ZonedDateTime_toLocaleString(JSContext* cx, const CallArgs& args) { 2783 Rooted<ZonedDateTime> zonedDateTime( 2784 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2785 2786 // Steps 3-6. 2787 Rooted<Value> timeZone(cx, 2788 StringValue(zonedDateTime.timeZone().identifier())); 2789 return intl::TemporalObjectToLocaleString( 2790 cx, args, intl::DateTimeFormatKind::All, timeZone); 2791 } 2792 2793 /** 2794 * Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] ) 2795 */ 2796 static bool ZonedDateTime_toLocaleString(JSContext* cx, unsigned argc, 2797 Value* vp) { 2798 // Steps 1-2. 2799 CallArgs args = CallArgsFromVp(argc, vp); 2800 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toLocaleString>( 2801 cx, args); 2802 } 2803 2804 /** 2805 * Temporal.ZonedDateTime.prototype.toJSON ( ) 2806 */ 2807 static bool ZonedDateTime_toJSON(JSContext* cx, const CallArgs& args) { 2808 Rooted<ZonedDateTime> zonedDateTime( 2809 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2810 2811 // Step 3. 2812 JSString* str = TemporalZonedDateTimeToString( 2813 cx, zonedDateTime, Precision::Auto(), ShowCalendar::Auto, 2814 ShowTimeZoneName::Auto, ShowOffset::Auto); 2815 if (!str) { 2816 return false; 2817 } 2818 2819 args.rval().setString(str); 2820 return true; 2821 } 2822 2823 /** 2824 * Temporal.ZonedDateTime.prototype.toJSON ( ) 2825 */ 2826 static bool ZonedDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) { 2827 // Steps 1-2. 2828 CallArgs args = CallArgsFromVp(argc, vp); 2829 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toJSON>(cx, args); 2830 } 2831 2832 /** 2833 * Temporal.ZonedDateTime.prototype.valueOf ( ) 2834 */ 2835 static bool ZonedDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) { 2836 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, 2837 "ZonedDateTime", "primitive type"); 2838 return false; 2839 } 2840 2841 /** 2842 * Temporal.ZonedDateTime.prototype.startOfDay ( ) 2843 */ 2844 static bool ZonedDateTime_startOfDay(JSContext* cx, const CallArgs& args) { 2845 Rooted<ZonedDateTime> zonedDateTime( 2846 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2847 2848 // Step 3. 2849 auto timeZone = zonedDateTime.timeZone(); 2850 2851 // Step 4. 2852 auto calendar = zonedDateTime.calendar(); 2853 2854 // Step 5. 2855 ISODateTime dateTime; 2856 if (!GetISODateTimeFor(cx, timeZone, zonedDateTime.epochNanoseconds(), 2857 &dateTime)) { 2858 return false; 2859 } 2860 2861 // Step 6. 2862 EpochNanoseconds epochNs; 2863 if (!GetStartOfDay(cx, timeZone, dateTime.date, &epochNs)) { 2864 return false; 2865 } 2866 2867 // Step 7. 2868 auto* result = CreateTemporalZonedDateTime(cx, epochNs, timeZone, calendar); 2869 if (!result) { 2870 return false; 2871 } 2872 2873 args.rval().setObject(*result); 2874 return true; 2875 } 2876 2877 /** 2878 * Temporal.ZonedDateTime.prototype.startOfDay ( ) 2879 */ 2880 static bool ZonedDateTime_startOfDay(JSContext* cx, unsigned argc, Value* vp) { 2881 // Steps 1-2. 2882 CallArgs args = CallArgsFromVp(argc, vp); 2883 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_startOfDay>(cx, 2884 args); 2885 } 2886 2887 /** 2888 * Temporal.ZonedDateTime.prototype.getTimeZoneTransition ( directionParam ) 2889 */ 2890 static bool ZonedDateTime_getTimeZoneTransition(JSContext* cx, 2891 const CallArgs& args) { 2892 Rooted<ZonedDateTime> zonedDateTime( 2893 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 2894 2895 // Step 3. 2896 auto timeZone = zonedDateTime.timeZone(); 2897 2898 // Steps 4-7. 2899 auto direction = Direction::Next; 2900 if (args.get(0).isString()) { 2901 // Steps 5 and 7. 2902 Rooted<JSString*> directionString(cx, args[0].toString()); 2903 if (!GetDirectionOption(cx, directionString, &direction)) { 2904 return false; 2905 } 2906 } else { 2907 // Steps 4 and 6. 2908 Rooted<JSObject*> options(cx, RequireObjectArg(cx, "getTimeZoneTransition", 2909 "direction", args.get(0))); 2910 if (!options) { 2911 return false; 2912 } 2913 2914 // Step 7. 2915 if (!GetDirectionOption(cx, options, &direction)) { 2916 return false; 2917 } 2918 } 2919 2920 // Step 8. 2921 if (timeZone.isOffset()) { 2922 args.rval().setNull(); 2923 return true; 2924 } 2925 2926 // Steps 9-10. 2927 mozilla::Maybe<EpochNanoseconds> transition; 2928 if (direction == Direction::Next) { 2929 if (!GetNamedTimeZoneNextTransition( 2930 cx, timeZone, zonedDateTime.epochNanoseconds(), &transition)) { 2931 return false; 2932 } 2933 } else { 2934 if (!GetNamedTimeZonePreviousTransition( 2935 cx, timeZone, zonedDateTime.epochNanoseconds(), &transition)) { 2936 return false; 2937 } 2938 } 2939 2940 // Step 11. 2941 if (!transition) { 2942 args.rval().setNull(); 2943 return true; 2944 } 2945 2946 // Step 12. 2947 auto* result = CreateTemporalZonedDateTime(cx, *transition, timeZone, 2948 zonedDateTime.calendar()); 2949 if (!result) { 2950 return false; 2951 } 2952 2953 args.rval().setObject(*result); 2954 return true; 2955 } 2956 2957 /** 2958 * Temporal.ZonedDateTime.prototype.getTimeZoneTransition ( directionParam ) 2959 */ 2960 static bool ZonedDateTime_getTimeZoneTransition(JSContext* cx, unsigned argc, 2961 Value* vp) { 2962 // Steps 1-2. 2963 CallArgs args = CallArgsFromVp(argc, vp); 2964 return CallNonGenericMethod<IsZonedDateTime, 2965 ZonedDateTime_getTimeZoneTransition>(cx, args); 2966 } 2967 2968 /** 2969 * Temporal.ZonedDateTime.prototype.toInstant ( ) 2970 */ 2971 static bool ZonedDateTime_toInstant(JSContext* cx, const CallArgs& args) { 2972 auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>(); 2973 auto epochNs = zonedDateTime->epochNanoseconds(); 2974 2975 // Step 3. 2976 auto* result = CreateTemporalInstant(cx, epochNs); 2977 if (!result) { 2978 return false; 2979 } 2980 2981 args.rval().setObject(*result); 2982 return true; 2983 } 2984 2985 /** 2986 * Temporal.ZonedDateTime.prototype.toInstant ( ) 2987 */ 2988 static bool ZonedDateTime_toInstant(JSContext* cx, unsigned argc, Value* vp) { 2989 // Steps 1-2. 2990 CallArgs args = CallArgsFromVp(argc, vp); 2991 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toInstant>(cx, 2992 args); 2993 } 2994 2995 /** 2996 * Temporal.ZonedDateTime.prototype.toPlainDate ( ) 2997 */ 2998 static bool ZonedDateTime_toPlainDate(JSContext* cx, const CallArgs& args) { 2999 Rooted<ZonedDateTime> zonedDateTime( 3000 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 3001 3002 // Step 3. 3003 ISODateTime temporalDateTime; 3004 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 3005 zonedDateTime.epochNanoseconds(), &temporalDateTime)) { 3006 return false; 3007 } 3008 3009 // Step 4. 3010 auto* result = 3011 CreateTemporalDate(cx, temporalDateTime.date, zonedDateTime.calendar()); 3012 if (!result) { 3013 return false; 3014 } 3015 3016 args.rval().setObject(*result); 3017 return true; 3018 } 3019 3020 /** 3021 * Temporal.ZonedDateTime.prototype.toPlainDate ( ) 3022 */ 3023 static bool ZonedDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) { 3024 // Steps 1-2. 3025 CallArgs args = CallArgsFromVp(argc, vp); 3026 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainDate>(cx, 3027 args); 3028 } 3029 3030 /** 3031 * Temporal.ZonedDateTime.prototype.toPlainTime ( ) 3032 */ 3033 static bool ZonedDateTime_toPlainTime(JSContext* cx, const CallArgs& args) { 3034 Rooted<ZonedDateTime> zonedDateTime( 3035 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 3036 3037 // Step 3. 3038 ISODateTime temporalDateTime; 3039 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 3040 zonedDateTime.epochNanoseconds(), &temporalDateTime)) { 3041 return false; 3042 } 3043 3044 // Step 4. 3045 auto* result = CreateTemporalTime(cx, temporalDateTime.time); 3046 if (!result) { 3047 return false; 3048 } 3049 3050 args.rval().setObject(*result); 3051 return true; 3052 } 3053 3054 /** 3055 * Temporal.ZonedDateTime.prototype.toPlainTime ( ) 3056 */ 3057 static bool ZonedDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) { 3058 // Steps 1-2. 3059 CallArgs args = CallArgsFromVp(argc, vp); 3060 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainTime>(cx, 3061 args); 3062 } 3063 3064 /** 3065 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( ) 3066 */ 3067 static bool ZonedDateTime_toPlainDateTime(JSContext* cx, const CallArgs& args) { 3068 Rooted<ZonedDateTime> zonedDateTime( 3069 cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); 3070 3071 // Step 3. 3072 ISODateTime dateTime; 3073 if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(), 3074 zonedDateTime.epochNanoseconds(), &dateTime)) { 3075 return false; 3076 } 3077 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime)); 3078 3079 // Step 4. 3080 auto* result = CreateTemporalDateTime(cx, dateTime, zonedDateTime.calendar()); 3081 if (!result) { 3082 return false; 3083 } 3084 3085 args.rval().setObject(*result); 3086 return true; 3087 } 3088 3089 /** 3090 * Temporal.ZonedDateTime.prototype.toPlainDateTime ( ) 3091 */ 3092 static bool ZonedDateTime_toPlainDateTime(JSContext* cx, unsigned argc, 3093 Value* vp) { 3094 // Steps 1-2. 3095 CallArgs args = CallArgsFromVp(argc, vp); 3096 return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_toPlainDateTime>( 3097 cx, args); 3098 } 3099 3100 const JSClass ZonedDateTimeObject::class_ = { 3101 "Temporal.ZonedDateTime", 3102 JSCLASS_HAS_RESERVED_SLOTS(ZonedDateTimeObject::SLOT_COUNT) | 3103 JSCLASS_HAS_CACHED_PROTO(JSProto_ZonedDateTime), 3104 JS_NULL_CLASS_OPS, 3105 &ZonedDateTimeObject::classSpec_, 3106 }; 3107 3108 const JSClass& ZonedDateTimeObject::protoClass_ = PlainObject::class_; 3109 3110 static const JSFunctionSpec ZonedDateTime_methods[] = { 3111 JS_FN("from", ZonedDateTime_from, 1, 0), 3112 JS_FN("compare", ZonedDateTime_compare, 2, 0), 3113 JS_FS_END, 3114 }; 3115 3116 static const JSFunctionSpec ZonedDateTime_prototype_methods[] = { 3117 JS_FN("with", ZonedDateTime_with, 1, 0), 3118 JS_FN("withPlainTime", ZonedDateTime_withPlainTime, 0, 0), 3119 JS_FN("withTimeZone", ZonedDateTime_withTimeZone, 1, 0), 3120 JS_FN("withCalendar", ZonedDateTime_withCalendar, 1, 0), 3121 JS_FN("add", ZonedDateTime_add, 1, 0), 3122 JS_FN("subtract", ZonedDateTime_subtract, 1, 0), 3123 JS_FN("until", ZonedDateTime_until, 1, 0), 3124 JS_FN("since", ZonedDateTime_since, 1, 0), 3125 JS_FN("round", ZonedDateTime_round, 1, 0), 3126 JS_FN("equals", ZonedDateTime_equals, 1, 0), 3127 JS_FN("toString", ZonedDateTime_toString, 0, 0), 3128 JS_FN("toLocaleString", ZonedDateTime_toLocaleString, 0, 0), 3129 JS_FN("toJSON", ZonedDateTime_toJSON, 0, 0), 3130 JS_FN("valueOf", ZonedDateTime_valueOf, 0, 0), 3131 JS_FN("startOfDay", ZonedDateTime_startOfDay, 0, 0), 3132 JS_FN("getTimeZoneTransition", ZonedDateTime_getTimeZoneTransition, 1, 0), 3133 JS_FN("toInstant", ZonedDateTime_toInstant, 0, 0), 3134 JS_FN("toPlainDate", ZonedDateTime_toPlainDate, 0, 0), 3135 JS_FN("toPlainTime", ZonedDateTime_toPlainTime, 0, 0), 3136 JS_FN("toPlainDateTime", ZonedDateTime_toPlainDateTime, 0, 0), 3137 JS_FS_END, 3138 }; 3139 3140 static const JSPropertySpec ZonedDateTime_prototype_properties[] = { 3141 JS_PSG("calendarId", ZonedDateTime_calendarId, 0), 3142 JS_PSG("timeZoneId", ZonedDateTime_timeZoneId, 0), 3143 JS_PSG("era", ZonedDateTime_era, 0), 3144 JS_PSG("eraYear", ZonedDateTime_eraYear, 0), 3145 JS_PSG("year", ZonedDateTime_year, 0), 3146 JS_PSG("month", ZonedDateTime_month, 0), 3147 JS_PSG("monthCode", ZonedDateTime_monthCode, 0), 3148 JS_PSG("day", ZonedDateTime_day, 0), 3149 JS_PSG("hour", ZonedDateTime_hour, 0), 3150 JS_PSG("minute", ZonedDateTime_minute, 0), 3151 JS_PSG("second", ZonedDateTime_second, 0), 3152 JS_PSG("millisecond", ZonedDateTime_millisecond, 0), 3153 JS_PSG("microsecond", ZonedDateTime_microsecond, 0), 3154 JS_PSG("nanosecond", ZonedDateTime_nanosecond, 0), 3155 JS_PSG("epochMilliseconds", ZonedDateTime_epochMilliseconds, 0), 3156 JS_PSG("epochNanoseconds", ZonedDateTime_epochNanoseconds, 0), 3157 JS_PSG("dayOfWeek", ZonedDateTime_dayOfWeek, 0), 3158 JS_PSG("dayOfYear", ZonedDateTime_dayOfYear, 0), 3159 JS_PSG("weekOfYear", ZonedDateTime_weekOfYear, 0), 3160 JS_PSG("yearOfWeek", ZonedDateTime_yearOfWeek, 0), 3161 JS_PSG("hoursInDay", ZonedDateTime_hoursInDay, 0), 3162 JS_PSG("daysInWeek", ZonedDateTime_daysInWeek, 0), 3163 JS_PSG("daysInMonth", ZonedDateTime_daysInMonth, 0), 3164 JS_PSG("daysInYear", ZonedDateTime_daysInYear, 0), 3165 JS_PSG("monthsInYear", ZonedDateTime_monthsInYear, 0), 3166 JS_PSG("inLeapYear", ZonedDateTime_inLeapYear, 0), 3167 JS_PSG("offsetNanoseconds", ZonedDateTime_offsetNanoseconds, 0), 3168 JS_PSG("offset", ZonedDateTime_offset, 0), 3169 JS_STRING_SYM_PS(toStringTag, "Temporal.ZonedDateTime", JSPROP_READONLY), 3170 JS_PS_END, 3171 }; 3172 3173 const ClassSpec ZonedDateTimeObject::classSpec_ = { 3174 GenericCreateConstructor<ZonedDateTimeConstructor, 2, 3175 gc::AllocKind::FUNCTION>, 3176 GenericCreatePrototype<ZonedDateTimeObject>, 3177 ZonedDateTime_methods, 3178 nullptr, 3179 ZonedDateTime_prototype_methods, 3180 ZonedDateTime_prototype_properties, 3181 nullptr, 3182 ClassSpec::DontDefineConstructor, 3183 };