PlainYearMonth.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /*
  2. * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  3. * Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibJS/Runtime/AbstractOperations.h>
  8. #include <LibJS/Runtime/Intrinsics.h>
  9. #include <LibJS/Runtime/Realm.h>
  10. #include <LibJS/Runtime/Temporal/Calendar.h>
  11. #include <LibJS/Runtime/Temporal/PlainYearMonth.h>
  12. #include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
  13. #include <LibJS/Runtime/VM.h>
  14. namespace JS::Temporal {
  15. GC_DEFINE_ALLOCATOR(PlainYearMonth);
  16. // 9 Temporal.PlainYearMonth Objects, https://tc39.es/proposal-temporal/#sec-temporal-plainyearmonth-objects
  17. PlainYearMonth::PlainYearMonth(ISODate iso_date, String calendar, Object& prototype)
  18. : Object(ConstructWithPrototypeTag::Tag, prototype)
  19. , m_iso_date(iso_date)
  20. , m_calendar(move(calendar))
  21. {
  22. }
  23. // 9.5.2 ToTemporalYearMonth ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalyearmonth
  24. ThrowCompletionOr<GC::Ref<PlainYearMonth>> to_temporal_year_month(VM& vm, Value item, Value options)
  25. {
  26. // 1. If options is not present, set options to undefined.
  27. // 2. If item is an Object, then
  28. if (item.is_object()) {
  29. auto const& object = item.as_object();
  30. // a. If item has an [[InitializedTemporalYearMonth]] internal slot, then
  31. if (is<PlainYearMonth>(object)) {
  32. auto const& plain_year_month = static_cast<PlainYearMonth const&>(object);
  33. // i. Let resolvedOptions be ? GetOptionsObject(options).
  34. auto resolved_options = TRY(get_options_object(vm, options));
  35. // ii. Perform ? GetTemporalOverflowOption(resolvedOptions).
  36. TRY(get_temporal_overflow_option(vm, resolved_options));
  37. // iii. Return ! CreateTemporalYearMonth(item.[[ISODate]], item.[[Calendar]]).
  38. return MUST(create_temporal_year_month(vm, plain_year_month.iso_date(), plain_year_month.calendar()));
  39. }
  40. // b. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).
  41. auto calendar = TRY(get_temporal_calendar_identifier_with_iso_default(vm, object));
  42. // c. Let fields be ? PrepareCalendarFields(calendar, item, « YEAR, MONTH, MONTH-CODE », «», «»).
  43. auto fields = TRY(prepare_calendar_fields(vm, calendar, object, { { CalendarField::Year, CalendarField::Month, CalendarField::MonthCode } }, {}, CalendarFieldList {}));
  44. // d. Let resolvedOptions be ? GetOptionsObject(options).
  45. auto resolved_options = TRY(get_options_object(vm, options));
  46. // e. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
  47. auto overflow = TRY(get_temporal_overflow_option(vm, resolved_options));
  48. // f. Let isoDate be ? CalendarYearMonthFromFields(calendar, fields, overflow).
  49. auto iso_date = TRY(calendar_year_month_from_fields(vm, calendar, move(fields), overflow));
  50. // g. Return ! CreateTemporalYearMonth(isoDate, calendar).
  51. return MUST(create_temporal_year_month(vm, iso_date, move(calendar)));
  52. }
  53. // 3. If item is not a String, throw a TypeError exception.
  54. if (!item.is_string())
  55. return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidPlainYearMonth);
  56. // 4. Let result be ? ParseISODateTime(item, « TemporalYearMonthString »).
  57. auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string_view(), { { Production::TemporalYearMonthString } }));
  58. // 5. Let calendar be result.[[Calendar]].
  59. // 6. If calendar is empty, set calendar to "iso8601".
  60. auto calendar = parse_result.calendar.value_or("iso8601"_string);
  61. // 7. Set calendar to ? CanonicalizeCalendar(calendar).
  62. calendar = TRY(canonicalize_calendar(vm, calendar));
  63. // 8. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).
  64. auto iso_date = create_iso_date_record(*parse_result.year, parse_result.month, parse_result.day);
  65. // 9. Set result to ISODateToFields(calendar, isoDate, YEAR-MONTH).
  66. auto result = iso_date_to_fields(calendar, iso_date, DateType::YearMonth);
  67. // 10. Let resolvedOptions be ? GetOptionsObject(options).
  68. auto resolved_options = TRY(get_options_object(vm, options));
  69. // 11. Perform ? GetTemporalOverflowOption(resolvedOptions).
  70. TRY(get_temporal_overflow_option(vm, resolved_options));
  71. // 12. NOTE: The following operation is called with CONSTRAIN regardless of the value of overflow, in order for the
  72. // calendar to store a canonical value in the [[Day]] field of the [[ISODate]] internal slot of the result.
  73. // 13. Set isoDate to ? CalendarYearMonthFromFields(calendar, result, CONSTRAIN).
  74. iso_date = TRY(calendar_year_month_from_fields(vm, calendar, result, Overflow::Constrain));
  75. // 14. Return ! CreateTemporalYearMonth(isoDate, calendar).
  76. return MUST(create_temporal_year_month(vm, iso_date, move(calendar)));
  77. }
  78. // 9.5.3 ISOYearMonthWithinLimits ( isoDate ), https://tc39.es/proposal-temporal/#sec-temporal-isoyearmonthwithinlimits
  79. bool iso_year_month_within_limits(ISODate iso_date)
  80. {
  81. // 1. If isoDate.[[Year]] < -271821 or isoDate.[[Year]] > 275760, then
  82. if (iso_date.year < -271821 || iso_date.year > 275760) {
  83. // a. Return false.
  84. return false;
  85. }
  86. // 2. If isoDate.[[Year]] = -271821 and isoDate.[[Month]] < 4, then
  87. if (iso_date.year == -271821 && iso_date.month < 4) {
  88. // a. Return false.
  89. return false;
  90. }
  91. // 3. If isoDate.[[Year]] = 275760 and isoDate.[[Month]] > 9, then
  92. if (iso_date.year == 275760 && iso_date.month > 9) {
  93. // a. Return false.
  94. return false;
  95. }
  96. // 4. Return true.
  97. return true;
  98. }
  99. // 9.5.5 CreateTemporalYearMonth ( isoDate, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalyearmonth
  100. ThrowCompletionOr<GC::Ref<PlainYearMonth>> create_temporal_year_month(VM& vm, ISODate iso_date, String calendar, GC::Ptr<FunctionObject> new_target)
  101. {
  102. auto& realm = *vm.current_realm();
  103. // 1. If ISOYearMonthWithinLimits(isoDate) is false, throw a RangeError exception.
  104. if (!iso_year_month_within_limits(iso_date))
  105. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainYearMonth);
  106. // 2. If newTarget is not present, set newTarget to %Temporal.PlainYearMonth%.
  107. if (!new_target)
  108. new_target = realm.intrinsics().temporal_plain_year_month_constructor();
  109. // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainYearMonth.prototype%", « [[InitializedTemporalYearMonth]], [[ISODate]], [[Calendar]] »).
  110. // 4. Set object.[[ISODate]] to isoDate.
  111. // 5. Set object.[[Calendar]] to calendar.
  112. auto object = TRY(ordinary_create_from_constructor<PlainYearMonth>(vm, *new_target, &Intrinsics::temporal_plain_year_month_prototype, iso_date, move(calendar)));
  113. // 6. Return object.
  114. return object;
  115. }
  116. }