Calendar.cpp 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270
  1. /*
  2. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  3. * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/TypeCasts.h>
  9. #include <LibJS/Runtime/AbstractOperations.h>
  10. #include <LibJS/Runtime/Array.h>
  11. #include <LibJS/Runtime/Completion.h>
  12. #include <LibJS/Runtime/GlobalObject.h>
  13. #include <LibJS/Runtime/Temporal/AbstractOperations.h>
  14. #include <LibJS/Runtime/Temporal/Calendar.h>
  15. #include <LibJS/Runtime/Temporal/CalendarConstructor.h>
  16. #include <LibJS/Runtime/Temporal/Duration.h>
  17. #include <LibJS/Runtime/Temporal/ISO8601.h>
  18. #include <LibJS/Runtime/Temporal/PlainDate.h>
  19. #include <LibJS/Runtime/Temporal/PlainDateTime.h>
  20. #include <LibJS/Runtime/Temporal/PlainMonthDay.h>
  21. #include <LibJS/Runtime/Temporal/PlainTime.h>
  22. #include <LibJS/Runtime/Temporal/PlainYearMonth.h>
  23. #include <LibJS/Runtime/Temporal/TimeZone.h>
  24. #include <LibJS/Runtime/Temporal/ZonedDateTime.h>
  25. #include <LibJS/Runtime/Value.h>
  26. #include <LibJS/Runtime/ValueInlines.h>
  27. namespace JS::Temporal {
  28. JS_DEFINE_ALLOCATOR(Calendar);
  29. // 12 Temporal.Calendar Objects, https://tc39.es/proposal-temporal/#sec-temporal-calendar-objects
  30. Calendar::Calendar(String identifier, Object& prototype)
  31. : Object(ConstructWithPrototypeTag::Tag, prototype)
  32. , m_identifier(move(identifier))
  33. {
  34. }
  35. // 12.1.1 IsBuiltinCalendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal-isbuiltincalendar
  36. bool is_builtin_calendar(StringView identifier)
  37. {
  38. // 1. Let calendars be AvailableCalendars().
  39. auto calendars = available_calendars();
  40. // 2. If calendars contains the ASCII-lowercase of id, return true.
  41. for (auto calendar : calendars) {
  42. if (calendar.equals_ignoring_ascii_case(identifier))
  43. return true;
  44. }
  45. // 3. Return false.
  46. return false;
  47. }
  48. // 12.1.2 AvailableCalendars ( ), https://tc39.es/proposal-temporal/#sec-temporal-availablecalendars
  49. ReadonlySpan<StringView> available_calendars()
  50. {
  51. // 1. Let calendars be the List of String values representing calendar types supported by the implementation.
  52. // NOTE: This can be removed in favor of using `Unicode::get_available_calendars()` once everything is updated to handle non-iso8601 calendars.
  53. static constexpr AK::Array calendars { "iso8601"sv };
  54. // 2. Assert: calendars contains "iso8601".
  55. // 3. Assert: calendars does not contain any element that does not identify a calendar type in the Unicode Common Locale Data Repository (CLDR).
  56. // 4. Sort calendars in order as if an Array of the same values had been sorted using %Array.prototype.sort% with undefined as comparefn.
  57. // 5. Return calendars.
  58. return calendars.span();
  59. }
  60. // 12.2.2 CreateCalendarMethodsRecord ( calendar, methods ), https://tc39.es/proposal-temporal/#sec-temporal-createcalendarmethodsrecord
  61. ThrowCompletionOr<CalendarMethods> create_calendar_methods_record(VM& vm, Variant<String, NonnullGCPtr<Object>> calendar, ReadonlySpan<CalendarMethod> methods)
  62. {
  63. // 1. Let record be the Calendar Methods Record { [[Receiver]]: calendar, [[DateAdd]]: undefined, [[DateFromFields]]: undefined, [[DateUntil]]: undefined, [[Day]]: undefined, [[Fields]]: undefined, [[MergeFields]]: undefined, [[MonthDayFromFields]]: undefined, [[YearMonthFromFields]]: undefined }.
  64. CalendarMethods record {
  65. .receiver = move(calendar),
  66. .date_add = nullptr,
  67. .date_from_fields = nullptr,
  68. .date_until = nullptr,
  69. .day = nullptr,
  70. .fields = nullptr,
  71. .merge_fields = nullptr,
  72. .month_day_from_fields = nullptr,
  73. .year_month_from_fields = nullptr,
  74. };
  75. // 2. For each element methodName in methods, do
  76. for (auto const& method_name : methods) {
  77. // a. Perform ? CalendarMethodsRecordLookup(record, methodName).
  78. TRY(calendar_methods_record_lookup(vm, record, method_name));
  79. }
  80. // 3. Return record.
  81. return record;
  82. }
  83. ThrowCompletionOr<Optional<CalendarMethods>> create_calendar_methods_record_from_relative_to(VM& vm, GCPtr<PlainDate> plain_relative_to, GCPtr<ZonedDateTime> zoned_relative_to, ReadonlySpan<CalendarMethod> methods)
  84. {
  85. // FIXME: The casts to NonnullGCPtr<Object> should not be here, and can be fixed once PlainDate & ZonedDateTime have the updated type in the [[Calendar]] slot.
  86. // 1. If zonedRelativeTo is not undefined, return ? CreateCalendarMethodsRecord(zonedRelativeTo.[[Calendar]], methods).
  87. if (zoned_relative_to)
  88. return TRY(create_calendar_methods_record(vm, NonnullGCPtr<Object> { zoned_relative_to->calendar() }, methods));
  89. // 2. If plainRelativeTo is not undefined, return ? CreateCalendarMethodsRecord(plainRelativeTo.[[Calendar]], methods).
  90. if (plain_relative_to)
  91. return TRY(create_calendar_methods_record(vm, NonnullGCPtr<Object> { plain_relative_to->calendar() }, methods));
  92. // 3. Return undefined.
  93. return OptionalNone {};
  94. }
  95. // 12.2.4 CalendarMethodsRecordLookup ( calendarRec, methodName ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmethodsrecordlookup
  96. ThrowCompletionOr<void> calendar_methods_record_lookup(VM& vm, CalendarMethods& calendar_record, CalendarMethod method_name)
  97. {
  98. auto& realm = *vm.current_realm();
  99. // 1. Assert: CalendarMethodsRecordHasLookedUp(calendarRec, methodName) is false.
  100. // 2. If methodName is DATE-ADD, then
  101. // a. If calendarRec.[[Receiver]] is a String, then
  102. // i. Set calendarRec.[[DateAdd]] to %Temporal.Calendar.prototype.dateAdd%.
  103. // b. Else,
  104. // i. Set calendarRec.[[DateAdd]] to ? GetMethod(calendarRec.[[Receiver]], "dateAdd").
  105. // ii. If calendarRec.[[DateAdd]] is undefined, throw a TypeError exception.
  106. // 3. Else if methodName is DATE-FROM-FIELDS, then
  107. // a. If calendarRec.[[Receiver]] is a String, then
  108. // i. Set calendarRec.[[DateFromFields]] to %Temporal.Calendar.prototype.dateFromFields%.
  109. // b. Else,
  110. // i. Set calendarRec.[[DateFromFields]] to ? GetMethod(calendarRec.[[Receiver]], "dateFromFields").
  111. // ii. If calendarRec.[[DateFromFields]] is undefined, throw a TypeError exception.
  112. // 4. Else if methodName is DATE-UNTIL, then
  113. // a. If calendarRec.[[Receiver]] is a String, then
  114. // i. Set calendarRec.[[DateUntil]] to %Temporal.Calendar.prototype.dateUntil%.
  115. // b. Else,
  116. // i. Set calendarRec.[[DateUntil]] to ? GetMethod(calendarRec.[[Receiver]], "dateUntil").
  117. // ii. If calendarRec.[[DateUntil]] is undefined, throw a TypeError exception.
  118. // 5. Else if methodName is DAY, then
  119. // a. If calendarRec.[[Receiver]] is a String, then
  120. // i. Set calendarRec.[[Day]] to %Temporal.Calendar.prototype.day%.
  121. // b. Else,
  122. // i. Set calendarRec.[[Day]] to ? GetMethod(calendarRec.[[Receiver]], "day").
  123. // ii. If calendarRec.[[Day]] is undefined, throw a TypeError exception.
  124. // 6. Else if methodName is FIELDS, then
  125. // a. If calendarRec.[[Receiver]] is a String, then
  126. // i. Set calendarRec.[[Fields]] to %Temporal.Calendar.prototype.fields%.
  127. // b. Else,
  128. // i. Set calendarRec.[[Fields]] to ? GetMethod(calendarRec.[[Receiver]], "fields").
  129. // ii. If calendarRec.[[Fields]] is undefined, throw a TypeError exception.
  130. // 7. Else if methodName is MERGE-FIELDS, then
  131. // a. If calendarRec.[[Receiver]] is a String, then
  132. // i. Set calendarRec.[[MergeFields]] to %Temporal.Calendar.prototype.mergeFields%.
  133. // b. Else,
  134. // i. Set calendarRec.[[MergeFields]] to ? GetMethod(calendarRec.[[Receiver]], "mergeFields").
  135. // ii. If calendarRec.[[MergeFields]] is undefined, throw a TypeError exception.
  136. // 8. Else if methodName is MONTH-DAY-FROM-FIELDS, then
  137. // a. If calendarRec.[[Receiver]] is a String, then
  138. // i. Set calendarRec.[[MonthDayFromFields]] to %Temporal.Calendar.prototype.monthDayFromFields%.
  139. // b. Else,
  140. // i. Set calendarRec.[[MonthDayFromFields]] to ? GetMethod(calendarRec.[[Receiver]], "monthDayFromFields").
  141. // ii. If calendarRec.[[MonthDayFromFields]] is undefined, throw a TypeError exception.
  142. // 9. Else if methodName is YEAR-MONTH-FROM-FIELDS, then
  143. // a. If calendarRec.[[Receiver]] is a String, then
  144. // i. Set calendarRec.[[YearMonthFromFields]] to %Temporal.Calendar.prototype.yearMonthFromFields%.
  145. // b. Else,
  146. // i. Set calendarRec.[[YearMonthFromFields]] to ? GetMethod(calendarRec.[[Receiver]], "yearMonthFromFields").
  147. // ii. If calendarRec.[[YearMonthFromFields]] is undefined, throw a TypeError exception.
  148. switch (method_name) {
  149. #define __JS_ENUMERATE(PascalName, camelName, snake_name) \
  150. case CalendarMethod::PascalName: { \
  151. VERIFY(!calendar_record.snake_name); \
  152. if (calendar_record.receiver.has<String>()) { \
  153. const auto& calendar_prototype = *realm.intrinsics().temporal_calendar_prototype(); \
  154. calendar_record.snake_name = calendar_prototype.get_without_side_effects(vm.names.camelName).as_function(); \
  155. } else { \
  156. Value calendar { calendar_record.receiver.get<NonnullGCPtr<Object>>() }; \
  157. calendar_record.snake_name = TRY(calendar.get_method(vm, vm.names.camelName)); \
  158. if (!calendar_record.snake_name) \
  159. return vm.throw_completion<TypeError>(ErrorType::IsUndefined, #camelName##sv); \
  160. } \
  161. break; \
  162. }
  163. JS_ENUMERATE_CALENDAR_METHODS
  164. #undef __JS_ENUMERATE
  165. }
  166. // 10. Return unused.
  167. return {};
  168. }
  169. // 12.2.5 CalendarMethodsRecordHasLookedUp ( calendarRec, methodName ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmethodsrecordhaslookedup
  170. bool calendar_methods_record_has_looked_up(CalendarMethods const& calendar_record, CalendarMethod method_name)
  171. {
  172. // 1. If methodName is DATE-ADD, then
  173. // a. Let method be calendarRec.[[DateAdd]].
  174. // 2. Else if methodName is DATE-FROM-FIELDS, then
  175. // a. Let method be calendarRec.[[DateFromFields]].
  176. // 3. Else if methodName is DATE-UNTIL, then
  177. // a. Let method be calendarRec.[[DateUntil]].
  178. // 4. Else if methodName is DAY, then
  179. // a. Let method be calendarRec.[[Day]].
  180. // 5. Else if methodName is FIELDS, then
  181. // a. Let method be calendarRec.[[Fields]].
  182. // 6. Else if methodName is MERGE-FIELDS, then
  183. // a. Let method be calendarRec.[[MergeFields]].
  184. // 7. Else if methodName is MONTH-DAY-FROM-FIELDS, then
  185. // a. Let method be calendarRec.[[MonthDayFromFields]].
  186. // 8. Else if methodName is YEAR-MONTH-FROM-FIELDS, then
  187. // a. Let method be calendarRec.[[YearMonthFromFields]].
  188. // 9. If method is undefined, return false.
  189. // 10. Return true.
  190. switch (method_name) {
  191. #define __JS_ENUMERATE(PascalName, camelName, snake_name) \
  192. case CalendarMethod::PascalName: { \
  193. return calendar_record.snake_name != nullptr; \
  194. }
  195. JS_ENUMERATE_CALENDAR_METHODS
  196. #undef __JS_ENUMERATE
  197. }
  198. VERIFY_NOT_REACHED();
  199. }
  200. // 12.2.6 CalendarMethodsRecordIsBuiltin ( calendarRec ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmethodsrecordisbuiltin
  201. bool calendar_methods_record_is_builtin(CalendarMethods const& calendar_record)
  202. {
  203. // 1. If calendarRec.[[Receiver]] is a String, return true.
  204. if (calendar_record.receiver.has<String>())
  205. return true;
  206. // 2. Return false.
  207. return false;
  208. }
  209. // 12.2.7 CalendarMethodsRecordCall ( calendarRec, methodName, arguments ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmethodsrecordcall
  210. ThrowCompletionOr<Value> calendar_methods_record_call(VM& vm, CalendarMethods const& calendar_record, CalendarMethod method_name, ReadonlySpan<Value> arguments)
  211. {
  212. // 1. Assert: CalendarMethodsRecordHasLookedUp(calendarRec, methodName) is true.
  213. VERIFY(calendar_methods_record_has_looked_up(calendar_record, method_name));
  214. // 2. Let receiver be calendarRec.[[Receiver]].
  215. // 3. If CalendarMethodsRecordIsBuiltin(calendarRec) is true, then
  216. // a. Set receiver to ! CreateTemporalCalendar(calendarRec.[[Receiver]]).
  217. GCPtr<Object> receiver;
  218. if (calendar_methods_record_is_builtin(calendar_record))
  219. receiver = MUST(create_temporal_calendar(vm, calendar_record.receiver.get<String>()));
  220. else
  221. receiver = calendar_record.receiver.get<NonnullGCPtr<Object>>();
  222. // 4. If methodName is DATE-ADD, then
  223. // a. Return ? Call(calendarRec.[[DateAdd]], receiver, arguments).
  224. // 5. If methodName is DATE-FROM-FIELDS, then
  225. // a. Return ? Call(calendarRec.[[DateFromFields]], receiver, arguments).
  226. // 6. If methodName is DATE-UNTIL, then
  227. // a. Return ? Call(calendarRec.[[DateUntil]], receiver, arguments).
  228. // 7. If methodName is DAY, then
  229. // a. Return ? Call(calendarRec.[[Day]], receiver, arguments).
  230. // 8. If methodName is FIELDS, then
  231. // a. Return ? Call(calendarRec.[[Fields]], receiver, arguments).
  232. // 9. If methodName is MERGE-FIELDS, then
  233. // a. Return ? Call(calendarRec.[[MergeFields]], receiver, arguments).
  234. // 10. If methodName is MONTH-DAY-FROM-FIELDS, then
  235. // a. Return ? Call(calendarRec.[[MonthDayFromFields]], receiver, arguments).
  236. // 11. If methodName is YEAR-MONTH-FROM-FIELDS, then
  237. // a. Return ? Call(calendarRec.[[YearMonthFromFields]], receiver, arguments).
  238. switch (method_name) {
  239. #define __JS_ENUMERATE(PascalName, camelName, snake_name) \
  240. case CalendarMethod::PascalName: { \
  241. return TRY(call(vm, calendar_record.snake_name, receiver, arguments)); \
  242. }
  243. JS_ENUMERATE_CALENDAR_METHODS
  244. #undef __JS_ENUMERATE
  245. }
  246. VERIFY_NOT_REACHED();
  247. }
  248. // 12.2.1 CreateTemporalCalendar ( identifier [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalcalendar
  249. ThrowCompletionOr<Calendar*> create_temporal_calendar(VM& vm, String const& identifier, FunctionObject const* new_target)
  250. {
  251. auto& realm = *vm.current_realm();
  252. // 1. Assert: IsBuiltinCalendar(identifier) is true.
  253. VERIFY(is_builtin_calendar(identifier));
  254. // 2. If newTarget is not provided, set newTarget to %Temporal.Calendar%.
  255. if (!new_target)
  256. new_target = realm.intrinsics().temporal_calendar_constructor();
  257. // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Calendar.prototype%", « [[InitializedTemporalCalendar]], [[Identifier]] »).
  258. // 4. Set object.[[Identifier]] to the ASCII-lowercase of identifier.
  259. auto object = TRY(ordinary_create_from_constructor<Calendar>(vm, *new_target, &Intrinsics::temporal_calendar_prototype, TRY_OR_THROW_OOM(vm, identifier.to_lowercase())));
  260. // 5. Return object.
  261. return object.ptr();
  262. }
  263. // 12.2.2 GetBuiltinCalendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal-getbuiltincalendar
  264. ThrowCompletionOr<Calendar*> get_builtin_calendar(VM& vm, String const& identifier)
  265. {
  266. // 1. If IsBuiltinCalendar(id) is false, throw a RangeError exception.
  267. if (!is_builtin_calendar(identifier))
  268. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarIdentifier, identifier);
  269. // 2. Return ! CreateTemporalCalendar(id).
  270. return MUST_OR_THROW_OOM(create_temporal_calendar(vm, identifier));
  271. }
  272. // 12.2.3 GetISO8601Calendar ( ), https://tc39.es/proposal-temporal/#sec-temporal-getiso8601calendar
  273. Calendar* get_iso8601_calendar(VM& vm)
  274. {
  275. // 1. Return ! GetBuiltinCalendar("iso8601").
  276. return MUST(get_builtin_calendar(vm, "iso8601"_string));
  277. }
  278. // 12.2.4 CalendarFields ( calendar, fieldNames ), https://tc39.es/proposal-temporal/#sec-temporal-calendarfields
  279. ThrowCompletionOr<Vector<String>> calendar_fields(VM& vm, Object& calendar, Vector<StringView> const& field_names)
  280. {
  281. auto& realm = *vm.current_realm();
  282. // 1. Let fields be ? GetMethod(calendar, "fields").
  283. auto fields = TRY(Value(&calendar).get_method(vm, vm.names.fields));
  284. // 2. If fields is undefined, return fieldNames.
  285. if (!fields) {
  286. Vector<String> result;
  287. TRY_OR_THROW_OOM(vm, result.try_ensure_capacity(field_names.size()));
  288. for (auto& value : field_names)
  289. result.unchecked_append(TRY_OR_THROW_OOM(vm, String::from_utf8(value)));
  290. return result;
  291. }
  292. // 3. Let fieldsArray be ? Call(fields, calendar, « CreateArrayFromList(fieldNames) »).
  293. auto field_names_array = Array::create_from<StringView>(realm, field_names, [&](auto value) {
  294. return PrimitiveString::create(vm, value);
  295. });
  296. auto fields_array = TRY(call(vm, *fields, &calendar, field_names_array));
  297. // 4. Return ? IterableToListOfType(fieldsArray, « String »).
  298. auto list = TRY(iterable_to_list_of_type(vm, fields_array, { OptionType::String }));
  299. Vector<String> result;
  300. TRY_OR_THROW_OOM(vm, result.try_ensure_capacity(list.size()));
  301. for (auto& value : list)
  302. result.unchecked_append(value.as_string().utf8_string());
  303. return result;
  304. }
  305. // 12.2.5 CalendarMergeFields ( calendar, fields, additionalFields ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmergefields
  306. ThrowCompletionOr<Object*> calendar_merge_fields(VM& vm, Object& calendar, Object& fields, Object& additional_fields)
  307. {
  308. // 1. Let mergeFields be ? GetMethod(calendar, "mergeFields").
  309. auto merge_fields = TRY(Value(&calendar).get_method(vm, vm.names.mergeFields));
  310. // 2. If mergeFields is undefined, then
  311. if (!merge_fields) {
  312. // a. Return ? DefaultMergeCalendarFields(fields, additionalFields).
  313. return TRY(default_merge_calendar_fields(vm, fields, additional_fields));
  314. }
  315. // 3. Let result be ? Call(mergeFields, calendar, « fields, additionalFields »).
  316. auto result = TRY(call(vm, merge_fields, &calendar, &fields, &additional_fields));
  317. // 4. If Type(result) is not Object, throw a TypeError exception.
  318. if (!result.is_object())
  319. return vm.throw_completion<TypeError>(ErrorType::NotAnObject, result.to_string_without_side_effects());
  320. // 5. Return result.
  321. return &result.as_object();
  322. }
  323. // 12.2.6 CalendarDateAdd ( calendar, date, duration [ , options [ , dateAdd ] ] ), https://tc39.es/proposal-temporal/#sec-temporal-calendardateadd
  324. ThrowCompletionOr<PlainDate*> calendar_date_add(VM& vm, Object& calendar, Value date, Duration& duration, Object* options, FunctionObject* date_add)
  325. {
  326. // NOTE: `date` is a `Value` because we sometimes need to pass a PlainDate, sometimes a PlainDateTime, and sometimes undefined.
  327. // 1. Assert: Type(calendar) is Object.
  328. // 2. If options is not present, set options to undefined.
  329. // 3. Assert: Type(options) is Object or Undefined.
  330. // 4. If dateAdd is not present, set dateAdd to ? GetMethod(calendar, "dateAdd").
  331. if (!date_add)
  332. date_add = TRY(Value(&calendar).get_method(vm, vm.names.dateAdd));
  333. // 5. Let addedDate be ? Call(dateAdd, calendar, « date, duration, options »).
  334. auto added_date = TRY(call(vm, date_add ?: js_undefined(), &calendar, date, &duration, options ?: js_undefined()));
  335. // 6. Perform ? RequireInternalSlot(addedDate, [[InitializedTemporalDate]]).
  336. auto added_date_object = TRY(added_date.to_object(vm));
  337. if (!is<PlainDate>(*added_date_object))
  338. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Temporal.PlainDate");
  339. // 7. Return addedDate.
  340. return static_cast<PlainDate*>(added_date_object.ptr());
  341. }
  342. // 12.2.7 CalendarDateUntil ( calendar, one, two, options [ , dateUntil ] ), https://tc39.es/proposal-temporal/#sec-temporal-calendardateuntil
  343. ThrowCompletionOr<NonnullGCPtr<Duration>> calendar_date_until(VM& vm, CalendarMethods const& calendar_record, Value one, Value two, Object const& options)
  344. {
  345. // 1. Let duration be ? CalendarMethodsRecordCall(calendarRec, DATE-UNTIL, « one, two, options »).
  346. auto duration = TRY(calendar_methods_record_call(vm, calendar_record, CalendarMethod::DateUntil, Vector<Value> { one, two, &options }));
  347. // 2. If CalendarMethodsRecordIsBuiltin(calendarRec) is true, return duration.
  348. if (calendar_methods_record_is_builtin(calendar_record))
  349. return verify_cast<Duration>(duration.as_object());
  350. // 3. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
  351. auto duration_object = TRY(duration.to_object(vm));
  352. if (!is<Duration>(*duration_object))
  353. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Temporal.Duration");
  354. // 4. Return duration.
  355. return static_cast<Duration&>(duration.as_object());
  356. }
  357. // 12.2.8 CalendarYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendaryear
  358. ThrowCompletionOr<double> calendar_year(VM& vm, Object& calendar, Object& date_like)
  359. {
  360. // 1. Let result be ? Invoke(calendar, "year", « dateLike »).
  361. auto result = TRY(Value(&calendar).invoke(vm, vm.names.year, &date_like));
  362. // 2. If result is undefined, throw a RangeError exception.
  363. if (result.is_undefined())
  364. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.year.as_string(), vm.names.undefined.as_string());
  365. // 3. Return ? ToIntegerWithTruncation(result).
  366. return TRY(to_integer_with_truncation(vm, result, ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.year.as_string(), vm.names.Infinity.as_string()));
  367. }
  368. // 12.2.9 CalendarMonth ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmonth
  369. ThrowCompletionOr<double> calendar_month(VM& vm, Object& calendar, Object& date_like)
  370. {
  371. // 1. Let result be ? Invoke(calendar, "month", « dateLike »).
  372. auto result = TRY(Value(&calendar).invoke(vm, vm.names.month, &date_like));
  373. // NOTE: Explicitly handled for a better error message similar to the other calendar property AOs
  374. if (result.is_undefined())
  375. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.month.as_string(), vm.names.undefined.as_string());
  376. // 2. Return ? ToPositiveIntegerWithTruncation(result).
  377. return TRY(to_positive_integer_with_truncation(vm, result));
  378. }
  379. // 12.2.10 CalendarMonthCode ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmonthcode
  380. ThrowCompletionOr<String> calendar_month_code(VM& vm, Object& calendar, Object& date_like)
  381. {
  382. // 1. Let result be ? Invoke(calendar, "monthCode", « dateLike »).
  383. auto result = TRY(Value(&calendar).invoke(vm, vm.names.monthCode, &date_like));
  384. // 2. If result is undefined, throw a RangeError exception.
  385. if (result.is_undefined())
  386. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.monthCode.as_string(), vm.names.undefined.as_string());
  387. // 3. Return ? ToString(result).
  388. return result.to_string(vm);
  389. }
  390. // 12.2.11 CalendarDay ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarday
  391. ThrowCompletionOr<double> calendar_day(VM& vm, Object& calendar, Object& date_like)
  392. {
  393. // 1. Let result be ? Invoke(calendar, "day", « dateLike »).
  394. auto result = TRY(Value(&calendar).invoke(vm, vm.names.day, &date_like));
  395. // NOTE: Explicitly handled for a better error message similar to the other calendar property AOs
  396. if (result.is_undefined())
  397. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.day.as_string(), vm.names.undefined.as_string());
  398. // 2. Return ? ToPositiveIntegerWithTruncation(result).
  399. return TRY(to_positive_integer_with_truncation(vm, result));
  400. }
  401. // 12.2.12 CalendarDayOfWeek ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardayofweek
  402. ThrowCompletionOr<double> calendar_day_of_week(VM& vm, Object& calendar, Object& date_like)
  403. {
  404. // 1. Let result be ? Invoke(calendar, "dayOfWeek", « dateLike »).
  405. auto result = TRY(Value(&calendar).invoke(vm, vm.names.dayOfWeek, &date_like));
  406. // NOTE: Explicitly handled for a better error message similar to the other calendar property AOs
  407. if (result.is_undefined())
  408. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.dayOfWeek.as_string(), vm.names.undefined.as_string());
  409. // 2. Return ? ToPositiveIntegerWithTruncation(result).
  410. return TRY(to_positive_integer_with_truncation(vm, result));
  411. }
  412. // 12.2.13 CalendarDayOfYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardayofyear
  413. ThrowCompletionOr<double> calendar_day_of_year(VM& vm, Object& calendar, Object& date_like)
  414. {
  415. // 1. Let result be ? Invoke(calendar, "dayOfYear", « dateLike »).
  416. auto result = TRY(Value(&calendar).invoke(vm, vm.names.dayOfYear, &date_like));
  417. // NOTE: Explicitly handled for a better error message similar to the other calendar property AOs
  418. if (result.is_undefined())
  419. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.dayOfYear.as_string(), vm.names.undefined.as_string());
  420. // 2. Return ? ToPositiveIntegerWithTruncation(result).
  421. return TRY(to_positive_integer_with_truncation(vm, result));
  422. }
  423. // 12.2.14 CalendarWeekOfYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarweekofyear
  424. ThrowCompletionOr<double> calendar_week_of_year(VM& vm, Object& calendar, Object& date_like)
  425. {
  426. // 1. Let result be ? Invoke(calendar, "weekOfYear", « dateLike »).
  427. auto result = TRY(Value(&calendar).invoke(vm, vm.names.weekOfYear, &date_like));
  428. // NOTE: Explicitly handled for a better error message similar to the other calendar property AOs
  429. if (result.is_undefined())
  430. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.weekOfYear.as_string(), vm.names.undefined.as_string());
  431. // 2. Return ? ToPositiveIntegerWithTruncation(result).
  432. return TRY(to_positive_integer_with_truncation(vm, result));
  433. }
  434. // 12.2.15 CalendarYearOfWeek ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendaryearofweek
  435. ThrowCompletionOr<double> calendar_year_of_week(VM& vm, Object& calendar, Object& date_like)
  436. {
  437. // 1. Let result be ? Invoke(calendar, "yearOfWeek", « dateLike »).
  438. auto result = TRY(Value(&calendar).invoke(vm, vm.names.yearOfWeek, &date_like));
  439. // 2. If result is undefined, throw a RangeError exception.
  440. if (result.is_undefined())
  441. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.yearOfWeek.as_string(), vm.names.undefined.as_string());
  442. // 3. Return ? ToIntegerWithTruncation(result).
  443. return TRY(to_integer_with_truncation(vm, result, ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.yearOfWeek.as_string(), vm.names.Infinity.to_string()));
  444. }
  445. // 12.2.16 CalendarDaysInWeek ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardaysinweek
  446. ThrowCompletionOr<double> calendar_days_in_week(VM& vm, Object& calendar, Object& date_like)
  447. {
  448. // 1. Let result be ? Invoke(calendar, "daysInWeek", « dateLike »).
  449. auto result = TRY(Value(&calendar).invoke(vm, vm.names.daysInWeek, &date_like));
  450. // NOTE: Explicitly handled for a better error message similar to the other calendar property AOs
  451. if (result.is_undefined())
  452. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.daysInWeek.as_string(), vm.names.undefined.as_string());
  453. // 2. Return ? ToPositiveIntegerWithTruncation(result).
  454. return TRY(to_positive_integer_with_truncation(vm, result));
  455. }
  456. // 12.2.17 CalendarDaysInMonth ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardaysinmonth
  457. ThrowCompletionOr<double> calendar_days_in_month(VM& vm, Object& calendar, Object& date_like)
  458. {
  459. // 1. Let result be ? Invoke(calendar, "daysInMonth", « dateLike »).
  460. auto result = TRY(Value(&calendar).invoke(vm, vm.names.daysInMonth, &date_like));
  461. // NOTE: Explicitly handled for a better error message similar to the other calendar property AOs
  462. if (result.is_undefined())
  463. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.daysInMonth.as_string(), vm.names.undefined.as_string());
  464. // 2. Return ? ToPositiveIntegerWithTruncation(result).
  465. return TRY(to_positive_integer_with_truncation(vm, result));
  466. }
  467. // 12.2.18 CalendarDaysInYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardaysinyear
  468. ThrowCompletionOr<double> calendar_days_in_year(VM& vm, Object& calendar, Object& date_like)
  469. {
  470. // 1. Let result be ? Invoke(calendar, "daysInYear", « dateLike »).
  471. auto result = TRY(Value(&calendar).invoke(vm, vm.names.daysInYear, &date_like));
  472. // NOTE: Explicitly handled for a better error message similar to the other calendar property AOs
  473. if (result.is_undefined())
  474. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.daysInYear.as_string(), vm.names.undefined.as_string());
  475. // 2. Return ? ToPositiveIntegerWithTruncation(result).
  476. return TRY(to_positive_integer_with_truncation(vm, result));
  477. }
  478. // 12.2.19 CalendarMonthsInYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmonthsinyear
  479. ThrowCompletionOr<double> calendar_months_in_year(VM& vm, Object& calendar, Object& date_like)
  480. {
  481. // 1. Let result be ? Invoke(calendar, "monthsInYear", « dateLike »).
  482. auto result = TRY(Value(&calendar).invoke(vm, vm.names.monthsInYear, &date_like));
  483. // NOTE: Explicitly handled for a better error message similar to the other calendar property AOs
  484. if (result.is_undefined())
  485. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.monthsInYear.as_string(), vm.names.undefined.as_string());
  486. // 2. Return ? ToPositiveIntegerWithTruncation(result).
  487. return TRY(to_positive_integer_with_truncation(vm, result));
  488. }
  489. // 12.2.20 CalendarInLeapYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarinleapyear
  490. ThrowCompletionOr<Value> calendar_in_leap_year(VM& vm, Object& calendar, Object& date_like)
  491. {
  492. // 1. Let result be ? Invoke(calendar, "inLeapYear", « dateLike »).
  493. auto result = TRY(Value(&calendar).invoke(vm, vm.names.inLeapYear, &date_like));
  494. // 2. Return ToBoolean(result).
  495. return result.to_boolean();
  496. }
  497. // 15.6.1.1 CalendarEra ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarera
  498. ThrowCompletionOr<Value> calendar_era(VM& vm, Object& calendar, Object& date_like)
  499. {
  500. // 1. Assert: Type(calendar) is Object.
  501. // 2. Let result be ? Invoke(calendar, "era", « dateLike »).
  502. auto result = TRY(Value(&calendar).invoke(vm, vm.names.era, &date_like));
  503. // 3. If result is not undefined, set result to ? ToString(result).
  504. if (!result.is_undefined())
  505. result = PrimitiveString::create(vm, TRY(result.to_string(vm)));
  506. // 4. Return result.
  507. return result;
  508. }
  509. // 15.6.1.2 CalendarEraYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarerayear
  510. ThrowCompletionOr<Value> calendar_era_year(VM& vm, Object& calendar, Object& date_like)
  511. {
  512. // 1. Assert: Type(calendar) is Object.
  513. // 2. Let result be ? Invoke(calendar, "eraYear", « dateLike »).
  514. auto result = TRY(Value(&calendar).invoke(vm, vm.names.eraYear, &date_like));
  515. // 3. If result is not undefined, set result to ? ToIntegerWithTruncation(result).
  516. if (!result.is_undefined())
  517. result = Value(TRY(to_integer_with_truncation(vm, result, ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.eraYear.as_string(), "Infinity"sv)));
  518. // 4. Return result.
  519. return result;
  520. }
  521. // 12.2.21 ToTemporalCalendar ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendar
  522. ThrowCompletionOr<Object*> to_temporal_calendar(VM& vm, Value temporal_calendar_like)
  523. {
  524. // 1. If Type(temporalCalendarLike) is Object, then
  525. if (temporal_calendar_like.is_object()) {
  526. auto& temporal_calendar_like_object = temporal_calendar_like.as_object();
  527. // a. If temporalCalendarLike has an [[InitializedTemporalCalendar]] internal slot, then
  528. if (is<Calendar>(temporal_calendar_like_object)) {
  529. // i. Return temporalCalendarLike.
  530. return &temporal_calendar_like_object;
  531. }
  532. // b. If temporalCalendarLike has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
  533. // i. Return temporalCalendarLike.[[Calendar]].
  534. if (is<PlainDate>(temporal_calendar_like_object))
  535. return &static_cast<PlainDate&>(temporal_calendar_like_object).calendar();
  536. if (is<PlainDateTime>(temporal_calendar_like_object))
  537. return &static_cast<PlainDateTime&>(temporal_calendar_like_object).calendar();
  538. if (is<PlainMonthDay>(temporal_calendar_like_object))
  539. return &static_cast<PlainMonthDay&>(temporal_calendar_like_object).calendar();
  540. if (is<PlainTime>(temporal_calendar_like_object))
  541. return &static_cast<PlainTime&>(temporal_calendar_like_object).calendar();
  542. if (is<PlainYearMonth>(temporal_calendar_like_object))
  543. return &static_cast<PlainYearMonth&>(temporal_calendar_like_object).calendar();
  544. if (is<ZonedDateTime>(temporal_calendar_like_object))
  545. return &static_cast<ZonedDateTime&>(temporal_calendar_like_object).calendar();
  546. // c. If temporalCalendarLike has an [[InitializedTemporalTimeZone]] internal slot, throw a RangeError exception.
  547. if (is<TimeZone>(temporal_calendar_like_object))
  548. return vm.throw_completion<RangeError>(ErrorType::TemporalUnexpectedTimeZoneObject);
  549. // d. If ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
  550. if (!TRY(temporal_calendar_like_object.has_property(vm.names.calendar)))
  551. return &temporal_calendar_like_object;
  552. // e. Set temporalCalendarLike to ? Get(temporalCalendarLike, "calendar").
  553. temporal_calendar_like = TRY(temporal_calendar_like_object.get(vm.names.calendar));
  554. // f. If Type(temporalCalendarLike) is Object, then
  555. if (temporal_calendar_like.is_object()) {
  556. // i. If temporalCalendarLike has an [[InitializedTemporalTimeZone]] internal slot, throw a RangeError exception.
  557. if (is<TimeZone>(temporal_calendar_like.as_object()))
  558. return vm.throw_completion<RangeError>(ErrorType::TemporalUnexpectedTimeZoneObject);
  559. // ii. If ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
  560. if (!TRY(temporal_calendar_like.as_object().has_property(vm.names.calendar)))
  561. return &temporal_calendar_like.as_object();
  562. }
  563. }
  564. // 2. Let identifier be ? ToString(temporalCalendarLike).
  565. auto identifier = TRY(temporal_calendar_like.to_string(vm));
  566. // 3. Set identifier to ? ParseTemporalCalendarString(identifier).
  567. identifier = TRY(parse_temporal_calendar_string(vm, identifier));
  568. // 4. If IsBuiltinCalendar(identifier) is false, throw a RangeError exception.
  569. if (!is_builtin_calendar(identifier))
  570. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarIdentifier, identifier);
  571. // 5. Return ! CreateTemporalCalendar(identifier).
  572. return MUST_OR_THROW_OOM(create_temporal_calendar(vm, identifier));
  573. }
  574. // 12.2.22 ToTemporalCalendarWithISODefault ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendarwithisodefault
  575. ThrowCompletionOr<Object*> to_temporal_calendar_with_iso_default(VM& vm, Value temporal_calendar_like)
  576. {
  577. // 1. If temporalCalendarLike is undefined, then
  578. if (temporal_calendar_like.is_undefined()) {
  579. // a. Return ! GetISO8601Calendar().
  580. return get_iso8601_calendar(vm);
  581. }
  582. // 2. Return ? ToTemporalCalendar(temporalCalendarLike).
  583. return to_temporal_calendar(vm, temporal_calendar_like);
  584. }
  585. // 12.2.23 GetTemporalCalendarWithISODefault ( item ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporalcalendarwithisodefault
  586. ThrowCompletionOr<Object*> get_temporal_calendar_with_iso_default(VM& vm, Object& item)
  587. {
  588. // 1. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
  589. // a. Return item.[[Calendar]].
  590. if (is<PlainDate>(item))
  591. return &static_cast<PlainDate&>(item).calendar();
  592. if (is<PlainDateTime>(item))
  593. return &static_cast<PlainDateTime&>(item).calendar();
  594. if (is<PlainMonthDay>(item))
  595. return &static_cast<PlainMonthDay&>(item).calendar();
  596. if (is<PlainTime>(item))
  597. return &static_cast<PlainTime&>(item).calendar();
  598. if (is<PlainYearMonth>(item))
  599. return &static_cast<PlainYearMonth&>(item).calendar();
  600. if (is<ZonedDateTime>(item))
  601. return &static_cast<ZonedDateTime&>(item).calendar();
  602. // 2. Let calendarLike be ? Get(item, "calendar").
  603. auto calendar_like = TRY(item.get(vm.names.calendar));
  604. // 3. Return ? ToTemporalCalendarWithISODefault(calendarLike).
  605. return to_temporal_calendar_with_iso_default(vm, calendar_like);
  606. }
  607. // 12.2.24 CalendarDateFromFields ( calendar, fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-calendardatefromfields
  608. ThrowCompletionOr<PlainDate*> calendar_date_from_fields(VM& vm, Object& calendar, Object const& fields, Object const* options)
  609. {
  610. // 1. If options is not present, set options to undefined.
  611. // 2. Let date be ? Invoke(calendar, "dateFromFields", « fields, options »).
  612. auto date = TRY(Value(&calendar).invoke(vm, vm.names.dateFromFields, &fields, options ?: js_undefined()));
  613. // 3. Perform ? RequireInternalSlot(date, [[InitializedTemporalDate]]).
  614. auto date_object = TRY(date.to_object(vm));
  615. if (!is<PlainDate>(*date_object))
  616. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Temporal.PlainDate");
  617. // 4. Return date.
  618. return static_cast<PlainDate*>(date_object.ptr());
  619. }
  620. // 12.2.25 CalendarYearMonthFromFields ( calendar, fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-calendaryearmonthfromfields
  621. ThrowCompletionOr<PlainYearMonth*> calendar_year_month_from_fields(VM& vm, Object& calendar, Object const& fields, Object const* options)
  622. {
  623. // 1. If options is not present, set options to undefined.
  624. // 2. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
  625. auto year_month = TRY(Value(&calendar).invoke(vm, vm.names.yearMonthFromFields, &fields, options ?: js_undefined()));
  626. // 3. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
  627. auto year_month_object = TRY(year_month.to_object(vm));
  628. if (!is<PlainYearMonth>(*year_month_object))
  629. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Temporal.PlainYearMonth");
  630. // 4. Return yearMonth.
  631. return static_cast<PlainYearMonth*>(year_month_object.ptr());
  632. }
  633. // 12.2.26 CalendarMonthDayFromFields ( calendar, fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmonthdayfromfields
  634. ThrowCompletionOr<PlainMonthDay*> calendar_month_day_from_fields(VM& vm, Object& calendar, Object const& fields, Object const* options)
  635. {
  636. // 1. If options is not present, set options to undefined.
  637. // 2. Let monthDay be ? Invoke(calendar, "monthDayFromFields", « fields, options »).
  638. auto month_day = TRY(Value(&calendar).invoke(vm, vm.names.monthDayFromFields, &fields, options ?: js_undefined()));
  639. // 3. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
  640. auto month_day_object = TRY(month_day.to_object(vm));
  641. if (!is<PlainMonthDay>(*month_day_object))
  642. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Temporal.PlainMonthDay");
  643. // 4. Return monthDay.
  644. return static_cast<PlainMonthDay*>(month_day_object.ptr());
  645. }
  646. // 12.2.27 MaybeFormatCalendarAnnotation ( calendarObject, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-maybeformatcalendarannotation
  647. ThrowCompletionOr<String> maybe_format_calendar_annotation(VM& vm, Object const* calendar_object, StringView show_calendar)
  648. {
  649. // 1. If showCalendar is "never", return the empty String.
  650. if (show_calendar == "never"sv)
  651. return String {};
  652. // 2. Assert: Type(calendarObject) is Object.
  653. VERIFY(calendar_object);
  654. // 3. Let calendarID be ? ToString(calendarObject).
  655. auto calendar_id = TRY(Value(calendar_object).to_string(vm));
  656. // 4. Return FormatCalendarAnnotation(calendarID, showCalendar).
  657. return format_calendar_annotation(vm, calendar_id, show_calendar);
  658. }
  659. // 12.2.28 FormatCalendarAnnotation ( id, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-formatcalendarannotation
  660. ThrowCompletionOr<String> format_calendar_annotation(VM& vm, StringView id, StringView show_calendar)
  661. {
  662. VERIFY(show_calendar == "auto"sv || show_calendar == "always"sv || show_calendar == "never"sv || show_calendar == "critical"sv);
  663. // 1. If showCalendar is "never", return the empty String.
  664. if (show_calendar == "never"sv)
  665. return String {};
  666. // 2. If showCalendar is "auto" and id is "iso8601", return the empty String.
  667. if (show_calendar == "auto"sv && id == "iso8601"sv)
  668. return String {};
  669. // 3. If showCalendar is "critical", let flag be "!"; else, let flag be the empty String.
  670. auto flag = show_calendar == "critical"sv ? "!"sv : ""sv;
  671. // 4. Return the string-concatenation of "[", flag, "u-ca=", id, and "]".
  672. return TRY_OR_THROW_OOM(vm, String::formatted("[{}u-ca={}]", flag, id));
  673. }
  674. // 12.2.29 CalendarEquals ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-calendarequals
  675. ThrowCompletionOr<bool> calendar_equals(VM& vm, Object& one, Object& two)
  676. {
  677. // 1. If one and two are the same Object value, return true.
  678. if (&one == &two)
  679. return true;
  680. // 2. Let calendarOne be ? ToString(one).
  681. auto calendar_one = TRY(Value(&one).to_string(vm));
  682. // 3. Let calendarTwo be ? ToString(two).
  683. auto calendar_two = TRY(Value(&two).to_string(vm));
  684. // 4. If calendarOne is calendarTwo, return true.
  685. if (calendar_one == calendar_two)
  686. return true;
  687. // 5. Return false.
  688. return false;
  689. }
  690. // 12.2.30 ConsolidateCalendars ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-consolidatecalendars
  691. ThrowCompletionOr<Object*> consolidate_calendars(VM& vm, Object& one, Object& two)
  692. {
  693. // 1. If one and two are the same Object value, return two.
  694. if (&one == &two)
  695. return &two;
  696. // 2. Let calendarOne be ? ToString(one).
  697. auto calendar_one = TRY(Value(&one).to_string(vm));
  698. // 3. Let calendarTwo be ? ToString(two).
  699. auto calendar_two = TRY(Value(&two).to_string(vm));
  700. // 4. If calendarOne is calendarTwo, return two.
  701. if (calendar_one == calendar_two)
  702. return &two;
  703. // 5. If calendarOne is "iso8601", return two.
  704. if (calendar_one == "iso8601"sv)
  705. return &two;
  706. // 6. If calendarTwo is "iso8601", return one.
  707. if (calendar_two == "iso8601"sv)
  708. return &one;
  709. // 7. Throw a RangeError exception.
  710. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendar);
  711. }
  712. // 12.2.31 ISODaysInMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isodaysinmonth
  713. u8 iso_days_in_month(i32 year, u8 month)
  714. {
  715. // 1. If month is 1, 3, 5, 7, 8, 10, or 12, return 31.
  716. if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
  717. return 31;
  718. // 2. If month is 4, 6, 9, or 11, return 30.
  719. if (month == 4 || month == 6 || month == 9 || month == 11)
  720. return 30;
  721. // 3. Assert: month is 2.
  722. VERIFY(month == 2);
  723. // 4. Return 28 + ℝ(InLeapYear(TimeFromYear(𝔽(year)))).
  724. return 28 + JS::in_leap_year(time_from_year(year));
  725. }
  726. // 12.2.32 ToISOWeekOfYear ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-toisoweekofyear
  727. YearWeekRecord to_iso_week_of_year(i32 year, u8 month, u8 day)
  728. {
  729. // 1. Assert: IsValidISODate(year, month, day) is true.
  730. VERIFY(is_valid_iso_date(year, month, day));
  731. // 2. Let wednesday be 3.
  732. constexpr auto wednesday = 3;
  733. // 3. Let thursday be 4.
  734. constexpr auto thursday = 4;
  735. // 4. Let friday be 5.
  736. constexpr auto friday = 5;
  737. // 5. Let saturday be 6.
  738. constexpr auto saturday = 6;
  739. // 6. Let daysInWeek be 7.
  740. constexpr auto days_in_week = 7;
  741. // 7. Let maxWeekNumber be 53.
  742. constexpr auto max_week_number = 53;
  743. // 8. Let dayOfYear be ToISODayOfYear(year, month, day).
  744. auto day_of_year = to_iso_day_of_year(year, month, day);
  745. // 9. Let dayOfWeek be ToISODayOfWeek(year, month, day).
  746. auto day_of_week = to_iso_day_of_week(year, month, day);
  747. // 10. Let week be floor((dayOfYear + daysInWeek - dayOfWeek + wednesday ) / daysInWeek).
  748. auto week = static_cast<i32>(floor(static_cast<double>(day_of_year + days_in_week - day_of_week + wednesday) / days_in_week));
  749. // 11. If week < 1, then
  750. if (week < 1) {
  751. // a. NOTE: This is the last week of the previous year.
  752. // b. Let dayOfJan1st be ToISODayOfWeek(year, 1, 1).
  753. auto day_of_jan_1st = to_iso_day_of_week(year, 1, 1);
  754. // c. If dayOfJan1st is friday, then
  755. if (day_of_jan_1st == friday) {
  756. // i. Return the Year-Week Record { [[Week]]: maxWeekNumber, [[Year]]: year - 1 }.
  757. return YearWeekRecord { .week = max_week_number, .year = year - 1 };
  758. }
  759. // d. If dayOfJan1st is saturday, and InLeapYear(TimeFromYear(𝔽(year - 1))) is 1𝔽, then
  760. if (day_of_jan_1st == saturday && in_leap_year(time_from_year(year - 1))) {
  761. // i. Return the Year-Week Record { [[Week]]: maxWeekNumber. [[Year]]: year - 1 }.
  762. return YearWeekRecord { .week = max_week_number, .year = year - 1 };
  763. }
  764. // e. Return the Year-Week Record { [[Week]]: maxWeekNumber - 1, [[Year]]: year - 1 }.
  765. return YearWeekRecord { .week = max_week_number - 1, .year = year - 1 };
  766. }
  767. // 12. If week is maxWeekNumber, then
  768. if (week == max_week_number) {
  769. // a. Let daysInYear be DaysInYear(𝔽(year)).
  770. auto days_in_year = JS::days_in_year(year);
  771. // b. Let daysLaterInYear be daysInYear - dayOfYear.
  772. auto days_later_in_year = days_in_year - day_of_year;
  773. // c. Let daysAfterThursday be thursday - dayOfWeek.
  774. auto days_after_thursday = thursday - day_of_week;
  775. // d. If daysLaterInYear < daysAfterThursday, then
  776. if (days_later_in_year < days_after_thursday) {
  777. // i. Return the Year-Week Record { [[Week]]: 1, [[Year]]: year + 1 }.
  778. return YearWeekRecord { .week = 1, .year = year + 1 };
  779. }
  780. }
  781. // 13. Return the Year-Week Record { [[Week]]: week, [[Year]]: year }.
  782. return YearWeekRecord { .week = static_cast<u8>(week), .year = year };
  783. }
  784. // 12.2.33 ISOMonthCode ( month ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthcode
  785. ThrowCompletionOr<String> iso_month_code(VM& vm, u8 month)
  786. {
  787. // 1. Let numberPart be ToZeroPaddedDecimalString(month, 2).
  788. // 2. Return the string-concatenation of "M" and numberPart.
  789. return TRY_OR_THROW_OOM(vm, String::formatted("M{:02}", month));
  790. }
  791. // 12.2.34 ResolveISOMonth ( fields ), https://tc39.es/proposal-temporal/#sec-temporal-resolveisomonth
  792. ThrowCompletionOr<double> resolve_iso_month(VM& vm, Object const& fields)
  793. {
  794. // 1. Assert: fields is an ordinary object with no more and no less than the own data properties listed in Table 13.
  795. // 2. Let month be ! Get(fields, "month").
  796. auto month = MUST(fields.get(vm.names.month));
  797. // 3. Assert: month is undefined or month is a Number.
  798. VERIFY(month.is_undefined() || month.is_number());
  799. // 4. Let monthCode be ! Get(fields, "monthCode").
  800. auto month_code = MUST(fields.get(vm.names.monthCode));
  801. // 5. If monthCode is undefined, then
  802. if (month_code.is_undefined()) {
  803. // a. If month is undefined, throw a TypeError exception.
  804. if (month.is_undefined())
  805. return vm.throw_completion<TypeError>(ErrorType::MissingRequiredProperty, vm.names.month.as_string());
  806. // b. Return ℝ(month).
  807. return month.as_double();
  808. }
  809. // 6. Assert: Type(monthCode) is String.
  810. VERIFY(month_code.is_string());
  811. auto month_code_string = month_code.as_string().byte_string();
  812. // 7. If the length of monthCode is not 3, throw a RangeError exception.
  813. auto month_length = month_code_string.length();
  814. if (month_length != 3)
  815. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidMonthCode);
  816. // 8. If the first code unit of monthCode is not 0x004D (LATIN CAPITAL LETTER M), throw a RangeError exception.
  817. if (month_code_string[0] != 0x4D)
  818. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidMonthCode);
  819. // 9. Let monthCodeDigits be the substring of monthCode from 1.
  820. auto month_code_digits = month_code_string.substring(1);
  821. // 10. If ParseText(StringToCodePoints(monthCodeDigits), DateMonth) is a List of errors, throw a RangeError exception.
  822. auto parse_result = parse_iso8601(Production::DateMonth, month_code_digits);
  823. if (!parse_result.has_value())
  824. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidMonthCode);
  825. // 11. Let monthCodeNumber be ! ToIntegerOrInfinity(monthCodeDigits).
  826. auto month_code_number = MUST(Value(PrimitiveString::create(vm, move(month_code_digits))).to_integer_or_infinity(vm));
  827. // 12. Assert: SameValue(monthCode, ISOMonthCode(monthCodeNumber)) is true.
  828. VERIFY(month_code_string.view() == TRY(iso_month_code(vm, month_code_number)));
  829. // 13. If month is not undefined and SameValue(month, monthCodeNumber) is false, throw a RangeError exception.
  830. if (!month.is_undefined() && month.as_double() != month_code_number)
  831. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidMonthCode);
  832. // 14. Return monthCodeNumber.
  833. return month_code_number;
  834. }
  835. // 12.2.35 ISODateFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isodatefromfields
  836. ThrowCompletionOr<ISODateRecord> iso_date_from_fields(VM& vm, Object const& fields, Object const& options)
  837. {
  838. // 1. Assert: Type(fields) is Object.
  839. // 2. Set fields to ? PrepareTemporalFields(fields, « "day", "month", "monthCode", "year" », « "year", "day" »).
  840. auto* prepared_fields = TRY(prepare_temporal_fields(vm, fields,
  841. { "day"_string,
  842. "month"_string,
  843. "monthCode"_string,
  844. "year"_string },
  845. Vector<StringView> { "year"sv, "day"sv }));
  846. // 3. Let overflow be ? ToTemporalOverflow(options).
  847. auto overflow = TRY(to_temporal_overflow(vm, &options));
  848. // 4. Let year be ! Get(fields, "year").
  849. auto year = MUST(prepared_fields->get(vm.names.year));
  850. // 5. Assert: Type(year) is Number.
  851. VERIFY(year.is_number());
  852. // 6. Let month be ? ResolveISOMonth(fields).
  853. auto month = TRY(resolve_iso_month(vm, *prepared_fields));
  854. // 7. Let day be ! Get(fields, "day").
  855. auto day = MUST(prepared_fields->get(vm.names.day));
  856. // 8. Assert: Type(day) is Number.
  857. VERIFY(day.is_number());
  858. // 9. Return ? RegulateISODate(ℝ(year), month, ℝ(day), overflow).
  859. return regulate_iso_date(vm, year.as_double(), month, day.as_double(), overflow);
  860. }
  861. // 12.2.36 ISOYearMonthFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isoyearmonthfromfields
  862. ThrowCompletionOr<ISOYearMonth> iso_year_month_from_fields(VM& vm, Object const& fields, Object const& options)
  863. {
  864. // 1. Assert: Type(fields) is Object.
  865. // 2. Set fields to ? PrepareTemporalFields(fields, « "month", "monthCode", "year" », « "year" »).
  866. auto* prepared_fields = TRY(prepare_temporal_fields(vm, fields,
  867. { "month"_string,
  868. "monthCode"_string,
  869. "year"_string },
  870. Vector<StringView> { "year"sv }));
  871. // 3. Let overflow be ? ToTemporalOverflow(options).
  872. auto overflow = TRY(to_temporal_overflow(vm, &options));
  873. // 4. Let year be ! Get(fields, "year").
  874. auto year = MUST(prepared_fields->get(vm.names.year));
  875. // 5. Assert: Type(year) is Number.
  876. VERIFY(year.is_number());
  877. // 6. Let month be ? ResolveISOMonth(fields).
  878. auto month = TRY(resolve_iso_month(vm, *prepared_fields));
  879. // 7. Let result be ? RegulateISOYearMonth(ℝ(year), month, overflow).
  880. auto result = TRY(regulate_iso_year_month(vm, year.as_double(), month, overflow));
  881. // 8. Return the Record { [[Year]]: result.[[Year]], [[Month]]: result.[[Month]], [[ReferenceISODay]]: 1 }.
  882. return ISOYearMonth { .year = result.year, .month = result.month, .reference_iso_day = 1 };
  883. }
  884. // 12.2.37 ISOMonthDayFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthdayfromfields
  885. ThrowCompletionOr<ISOMonthDay> iso_month_day_from_fields(VM& vm, Object const& fields, Object const& options)
  886. {
  887. // 1. Assert: Type(fields) is Object.
  888. // 2. Set fields to ? PrepareTemporalFields(fields, « "day", "month", "monthCode", "year" », « "day" »).
  889. auto* prepared_fields = TRY(prepare_temporal_fields(vm, fields,
  890. { "day"_string,
  891. "month"_string,
  892. "monthCode"_string,
  893. "year"_string },
  894. Vector<StringView> { "day"sv }));
  895. // 3. Let overflow be ? ToTemporalOverflow(options).
  896. auto overflow = TRY(to_temporal_overflow(vm, &options));
  897. // 4. Let month be ! Get(fields, "month").
  898. auto month_value = MUST(prepared_fields->get(vm.names.month));
  899. // 5. Let monthCode be ! Get(fields, "monthCode").
  900. auto month_code = MUST(prepared_fields->get(vm.names.monthCode));
  901. // 6. Let year be ! Get(fields, "year").
  902. auto year = MUST(prepared_fields->get(vm.names.year));
  903. // 7. If month is not undefined, and monthCode and year are both undefined, then
  904. if (!month_value.is_undefined() && month_code.is_undefined() && year.is_undefined()) {
  905. // a. Throw a TypeError exception.
  906. return vm.throw_completion<TypeError>(ErrorType::MissingRequiredProperty, "monthCode or year");
  907. }
  908. // 8. Set month to ? ResolveISOMonth(fields).
  909. auto month = TRY(resolve_iso_month(vm, *prepared_fields));
  910. // 9. Let day be ! Get(fields, "day").
  911. auto day = MUST(prepared_fields->get(vm.names.day));
  912. // 10. Assert: Type(day) is Number.
  913. VERIFY(day.is_number());
  914. // 11. Let referenceISOYear be 1972 (the first leap year after the Unix epoch).
  915. i32 reference_iso_year = 1972;
  916. Optional<ISODateRecord> result;
  917. // 12. If monthCode is undefined, then
  918. if (month_code.is_undefined()) {
  919. // a. Assert: Type(year) is Number.
  920. VERIFY(year.is_number());
  921. // b. Let result be ? RegulateISODate(ℝ(year), month, ℝ(day), overflow).
  922. result = TRY(regulate_iso_date(vm, year.as_double(), month, day.as_double(), overflow));
  923. }
  924. // 13. Else,
  925. else {
  926. // a. Let result be ? RegulateISODate(referenceISOYear, month, ℝ(day), overflow).
  927. result = TRY(regulate_iso_date(vm, reference_iso_year, month, day.as_double(), overflow));
  928. }
  929. // 14. Return the Record { [[Month]]: result.[[Month]], [[Day]]: result.[[Day]], [[ReferenceISOYear]]: referenceISOYear }.
  930. return ISOMonthDay { .month = result->month, .day = result->day, .reference_iso_year = reference_iso_year };
  931. }
  932. // 12.2.38 DefaultMergeCalendarFields ( fields, additionalFields ), https://tc39.es/proposal-temporal/#sec-temporal-defaultmergecalendarfields
  933. ThrowCompletionOr<Object*> default_merge_calendar_fields(VM& vm, Object const& fields, Object const& additional_fields)
  934. {
  935. auto& realm = *vm.current_realm();
  936. // 1. Let merged be OrdinaryObjectCreate(%Object.prototype%).
  937. auto merged = Object::create(realm, realm.intrinsics().object_prototype());
  938. // 2. Let fieldsKeys be ? EnumerableOwnPropertyNames(fields, key).
  939. auto fields_keys = TRY(fields.enumerable_own_property_names(Object::PropertyKind::Key));
  940. // 3. For each element key of fieldsKeys, do
  941. for (auto& key : fields_keys) {
  942. // a. If key is not "month" or "monthCode", then
  943. if (!key.as_string().byte_string().is_one_of(vm.names.month.as_string(), vm.names.monthCode.as_string())) {
  944. auto property_key = MUST(PropertyKey::from_value(vm, key));
  945. // i. Let propValue be ? Get(fields, key).
  946. auto prop_value = TRY(fields.get(property_key));
  947. // ii. If propValue is not undefined, then
  948. if (!prop_value.is_undefined()) {
  949. // 1. Perform ! CreateDataPropertyOrThrow(merged, key, propValue).
  950. MUST(merged->create_data_property_or_throw(property_key, prop_value));
  951. }
  952. }
  953. }
  954. // 4. Let additionalFieldsKeys be ? EnumerableOwnPropertyNames(additionalFields, key).
  955. auto additional_fields_keys = TRY(additional_fields.enumerable_own_property_names(Object::PropertyKind::Key));
  956. // IMPLEMENTATION DEFINED: This is an optimization, so we don't have to iterate new_keys three times (worst case), but only once.
  957. bool additional_fields_keys_contains_month_or_month_code_property = false;
  958. // 5. For each element key of additionalFieldsKeys, do
  959. for (auto& key : additional_fields_keys) {
  960. auto property_key = MUST(PropertyKey::from_value(vm, key));
  961. // a. Let propValue be ? Get(additionalFields, key).
  962. auto prop_value = TRY(additional_fields.get(property_key));
  963. // b. If propValue is not undefined, then
  964. if (!prop_value.is_undefined()) {
  965. // i. Perform ! CreateDataPropertyOrThrow(merged, key, propValue).
  966. MUST(merged->create_data_property_or_throw(property_key, prop_value));
  967. }
  968. // See comment above.
  969. additional_fields_keys_contains_month_or_month_code_property |= key.as_string().byte_string() == vm.names.month.as_string() || key.as_string().byte_string() == vm.names.monthCode.as_string();
  970. }
  971. // 6. If additionalFieldsKeys does not contain either "month" or "monthCode", then
  972. if (!additional_fields_keys_contains_month_or_month_code_property) {
  973. // a. Let month be ? Get(fields, "month").
  974. auto month = TRY(fields.get(vm.names.month));
  975. // b. If month is not undefined, then
  976. if (!month.is_undefined()) {
  977. // i. Perform ! CreateDataPropertyOrThrow(merged, "month", month).
  978. MUST(merged->create_data_property_or_throw(vm.names.month, month));
  979. }
  980. // c. Let monthCode be ? Get(fields, "monthCode").
  981. auto month_code = TRY(fields.get(vm.names.monthCode));
  982. // d. If monthCode is not undefined, then
  983. if (!month_code.is_undefined()) {
  984. // i. Perform ! CreateDataPropertyOrThrow(merged, "monthCode", monthCode).
  985. MUST(merged->create_data_property_or_throw(vm.names.monthCode, month_code));
  986. }
  987. }
  988. // 7. Return merged.
  989. return merged.ptr();
  990. }
  991. // 12.2.39 ToISODayOfYear ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-toisodayofyear
  992. u16 to_iso_day_of_year(i32 year, u8 month, u8 day)
  993. {
  994. // 1. Assert: IsValidISODate(year, month, day) is true.
  995. VERIFY(is_valid_iso_date(year, month, day));
  996. // 2. Let epochDays be MakeDay(𝔽(year), 𝔽(month - 1), 𝔽(day)).
  997. auto epoch_days = make_day(year, month - 1, day);
  998. // 3. Assert: epochDays is finite.
  999. VERIFY(isfinite(epoch_days));
  1000. // 4. Return ℝ(DayWithinYear(MakeDate(epochDays, +0𝔽))) + 1.
  1001. return day_within_year(make_date(epoch_days, 0)) + 1;
  1002. }
  1003. // 12.2.40 ToISODayOfWeek ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-toisodayofweek
  1004. u8 to_iso_day_of_week(i32 year, u8 month, u8 day)
  1005. {
  1006. // 1. Assert: IsValidISODate(year, month, day) is true.
  1007. VERIFY(is_valid_iso_date(year, month, day));
  1008. // 2. Let epochDays be MakeDay(𝔽(year), 𝔽(month - 1), 𝔽(day)).
  1009. auto epoch_days = make_day(year, month - 1, day);
  1010. // 3. Assert: epochDays is finite.
  1011. VERIFY(isfinite(epoch_days));
  1012. // 4. Let dayOfWeek be WeekDay(MakeDate(epochDays, +0𝔽)).
  1013. auto day_of_week = week_day(make_date(epoch_days, 0));
  1014. // 5. If dayOfWeek = +0𝔽, return 7.
  1015. if (day_of_week == 0)
  1016. return 7;
  1017. // 6. Return ℝ(dayOfWeek).
  1018. return day_of_week;
  1019. }
  1020. }