Calendar.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. /*
  2. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  3. * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibJS/Runtime/AbstractOperations.h>
  8. #include <LibJS/Runtime/Array.h>
  9. #include <LibJS/Runtime/Completion.h>
  10. #include <LibJS/Runtime/GlobalObject.h>
  11. #include <LibJS/Runtime/Temporal/AbstractOperations.h>
  12. #include <LibJS/Runtime/Temporal/Calendar.h>
  13. #include <LibJS/Runtime/Temporal/CalendarConstructor.h>
  14. #include <LibJS/Runtime/Temporal/PlainDate.h>
  15. #include <LibJS/Runtime/Temporal/PlainDateTime.h>
  16. #include <LibJS/Runtime/Temporal/PlainMonthDay.h>
  17. #include <LibJS/Runtime/Temporal/PlainTime.h>
  18. #include <LibJS/Runtime/Temporal/PlainYearMonth.h>
  19. #include <LibJS/Runtime/Temporal/ZonedDateTime.h>
  20. #include <LibJS/Runtime/Value.h>
  21. namespace JS::Temporal {
  22. // 12 Temporal.Calendar Objects, https://tc39.es/proposal-temporal/#sec-temporal-calendar-objects
  23. Calendar::Calendar(String identifier, Object& prototype)
  24. : Object(prototype)
  25. , m_identifier(move(identifier))
  26. {
  27. }
  28. // 12.1.1 CreateTemporalCalendar ( identifier [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalcalendar
  29. ThrowCompletionOr<Calendar*> create_temporal_calendar(GlobalObject& global_object, String const& identifier, FunctionObject const* new_target)
  30. {
  31. // 1. Assert: ! IsBuiltinCalendar(identifier) is true.
  32. VERIFY(is_builtin_calendar(identifier));
  33. // 2. If newTarget is not provided, set newTarget to %Temporal.Calendar%.
  34. if (!new_target)
  35. new_target = global_object.temporal_calendar_constructor();
  36. // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Calendar.prototype%", « [[InitializedTemporalCalendar]], [[Identifier]] »).
  37. // 4. Set object.[[Identifier]] to identifier.
  38. auto* object = TRY(ordinary_create_from_constructor<Calendar>(global_object, *new_target, &GlobalObject::temporal_calendar_prototype, identifier));
  39. // 5. Return object.
  40. return object;
  41. }
  42. // 12.1.2 IsBuiltinCalendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal-isbuiltincalendar
  43. // NOTE: This is the minimum IsBuiltinCalendar implementation for engines without ECMA-402.
  44. bool is_builtin_calendar(String const& identifier)
  45. {
  46. // 1. If id is not "iso8601", return false.
  47. if (identifier != "iso8601"sv)
  48. return false;
  49. // 2. Return true.
  50. return true;
  51. }
  52. // 12.1.3 GetBuiltinCalendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal-getbuiltincalendar
  53. ThrowCompletionOr<Calendar*> get_builtin_calendar(GlobalObject& global_object, String const& identifier)
  54. {
  55. auto& vm = global_object.vm();
  56. // 1. If ! IsBuiltinCalendar(id) is false, throw a RangeError exception.
  57. if (!is_builtin_calendar(identifier))
  58. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidCalendarIdentifier, identifier);
  59. // 2. Return ? Construct(%Temporal.Calendar%, « id »).
  60. MarkedValueList arguments(vm.heap());
  61. arguments.append(js_string(vm, identifier));
  62. return static_cast<Calendar*>(TRY(construct(global_object, *global_object.temporal_calendar_constructor(), move(arguments))));
  63. }
  64. // 12.1.4 GetISO8601Calendar ( )
  65. Calendar* get_iso8601_calendar(GlobalObject& global_object)
  66. {
  67. // 1. Return ! GetBuiltinCalendar("iso8601").
  68. return MUST(get_builtin_calendar(global_object, "iso8601"));
  69. }
  70. // 12.1.5 CalendarFields ( calendar, fieldNames ), https://tc39.es/proposal-temporal/#sec-temporal-calendarfields
  71. ThrowCompletionOr<Vector<String>> calendar_fields(GlobalObject& global_object, Object& calendar, Vector<StringView> const& field_names)
  72. {
  73. auto& vm = global_object.vm();
  74. // 1. Let fields be ? GetMethod(calendar, "fields").
  75. auto fields = TRY(Value(&calendar).get_method(global_object, vm.names.fields));
  76. // 2. Let fieldsArray be ! CreateArrayFromList(fieldNames).
  77. auto field_names_values = MarkedValueList { vm.heap() };
  78. for (auto& field_name : field_names)
  79. field_names_values.append(js_string(vm, field_name));
  80. Value fields_array = Array::create_from(global_object, field_names_values);
  81. // 3. If fields is not undefined, then
  82. if (fields) {
  83. // a. Set fieldsArray to ? Call(fields, calendar, « fieldsArray »).
  84. fields_array = TRY(vm.call(*fields, &calendar, fields_array));
  85. }
  86. // 4. Return ? IterableToListOfType(fieldsArray, « String »).
  87. auto list = TRY(iterable_to_list_of_type(global_object, fields_array, { OptionType::String }));
  88. Vector<String> result;
  89. for (auto& value : list)
  90. result.append(value.as_string().string());
  91. return result;
  92. }
  93. // 12.1.6 CalendarMergeFields ( calendar, fields, additionalFields ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmergefields
  94. ThrowCompletionOr<Object*> calendar_merge_fields(GlobalObject& global_object, Object& calendar, Object& fields, Object& additional_fields)
  95. {
  96. auto& vm = global_object.vm();
  97. // 1. Let mergeFields be ? GetMethod(calendar, "mergeFields").
  98. auto* merge_fields = TRY(Value(&calendar).get_method(global_object, vm.names.mergeFields));
  99. // 2. If mergeFields is undefined, then
  100. if (!merge_fields) {
  101. // a. Return ? DefaultMergeFields(fields, additionalFields).
  102. return TRY(default_merge_fields(global_object, fields, additional_fields));
  103. }
  104. // 3. Let result be ? Call(mergeFields, calendar, « fields, additionalFields »).
  105. auto result = TRY(call(global_object, merge_fields, &calendar, &fields, &additional_fields));
  106. // 4. If Type(result) is not Object, throw a TypeError exception.
  107. if (!result.is_object())
  108. return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, result.to_string_without_side_effects());
  109. // 5. Return result.
  110. return &result.as_object();
  111. }
  112. // 12.1.9 CalendarYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendaryear
  113. ThrowCompletionOr<double> calendar_year(GlobalObject& global_object, Object& calendar, Object& date_like)
  114. {
  115. auto& vm = global_object.vm();
  116. // 1. Assert: Type(calendar) is Object.
  117. // 2. Let result be ? Invoke(calendar, "year", « dateLike »).
  118. auto result = TRY(Value(&calendar).invoke(global_object, vm.names.year, &date_like));
  119. // 3. If result is undefined, throw a RangeError exception.
  120. if (result.is_undefined())
  121. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.year.as_string(), vm.names.undefined.as_string());
  122. // 4. Return ? ToIntegerThrowOnInfinity(result).
  123. return TRY(to_integer_throw_on_infinity(global_object, result, ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.year.as_string(), vm.names.Infinity.as_string()));
  124. }
  125. // 12.1.10 CalendarMonth ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmonth
  126. ThrowCompletionOr<double> calendar_month(GlobalObject& global_object, Object& calendar, Object& date_like)
  127. {
  128. auto& vm = global_object.vm();
  129. // 1. Assert: Type(calendar) is Object.
  130. // 2. Let result be ? Invoke(calendar, "month", « dateLike »).
  131. auto result = TRY(Value(&calendar).invoke(global_object, vm.names.month, &date_like));
  132. // 3. If result is undefined, throw a RangeError exception.
  133. if (result.is_undefined())
  134. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.month.as_string(), vm.names.undefined.as_string());
  135. // 4. Return ? ToPositiveInteger(result).
  136. return TRY(to_positive_integer(global_object, result));
  137. }
  138. // 12.1.11 CalendarMonthCode ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmonthcode
  139. ThrowCompletionOr<String> calendar_month_code(GlobalObject& global_object, Object& calendar, Object& date_like)
  140. {
  141. auto& vm = global_object.vm();
  142. // 1. Assert: Type(calendar) is Object.
  143. // 2. Let result be ? Invoke(calendar, "monthCode", « dateLike »).
  144. auto result = TRY(Value(&calendar).invoke(global_object, vm.names.monthCode, &date_like));
  145. // 3. If result is undefined, throw a RangeError exception.
  146. if (result.is_undefined())
  147. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.monthCode.as_string(), vm.names.undefined.as_string());
  148. // 4. Return ? ToString(result).
  149. return result.to_string(global_object);
  150. }
  151. // 12.1.12 CalendarDay ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarday
  152. ThrowCompletionOr<double> calendar_day(GlobalObject& global_object, Object& calendar, Object& date_like)
  153. {
  154. auto& vm = global_object.vm();
  155. // 1. Assert: Type(calendar) is Object.
  156. // 2. Let result be ? Invoke(calendar, "day", « dateLike »).
  157. auto result = TRY(Value(&calendar).invoke(global_object, vm.names.day, &date_like));
  158. // 3. If result is undefined, throw a RangeError exception.
  159. if (result.is_undefined())
  160. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.day.as_string(), vm.names.undefined.as_string());
  161. // 4. Return ? ToPositiveInteger(result).
  162. return TRY(to_positive_integer(global_object, result));
  163. }
  164. // 12.1.13 CalendarDayOfWeek ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardayofweek
  165. ThrowCompletionOr<Value> calendar_day_of_week(GlobalObject& global_object, Object& calendar, Object& date_like)
  166. {
  167. auto& vm = global_object.vm();
  168. // 1. Assert: Type(calendar) is Object.
  169. // 2. Return ? Invoke(calendar, "dayOfWeek", « dateLike »).
  170. return TRY(Value(&calendar).invoke(global_object, vm.names.dayOfWeek, &date_like));
  171. }
  172. // 12.1.14 CalendarDayOfYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardayofyear
  173. ThrowCompletionOr<Value> calendar_day_of_year(GlobalObject& global_object, Object& calendar, Object& date_like)
  174. {
  175. auto& vm = global_object.vm();
  176. // 1. Assert: Type(calendar) is Object.
  177. // 2. Return ? Invoke(calendar, "dayOfYear", « dateLike »).
  178. return TRY(Value(&calendar).invoke(global_object, vm.names.dayOfYear, &date_like));
  179. }
  180. // 12.1.15 CalendarWeekOfYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarweekofyear
  181. ThrowCompletionOr<Value> calendar_week_of_year(GlobalObject& global_object, Object& calendar, Object& date_like)
  182. {
  183. auto& vm = global_object.vm();
  184. // 1. Assert: Type(calendar) is Object.
  185. // 2. Return ? Invoke(calendar, "weekOfYear", « dateLike »).
  186. return TRY(Value(&calendar).invoke(global_object, vm.names.weekOfYear, &date_like));
  187. }
  188. // 12.1.16 CalendarDaysInWeek ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardaysinweek
  189. ThrowCompletionOr<Value> calendar_days_in_week(GlobalObject& global_object, Object& calendar, Object& date_like)
  190. {
  191. auto& vm = global_object.vm();
  192. // 1. Assert: Type(calendar) is Object.
  193. // 2. Return ? Invoke(calendar, "daysInWeek", « dateLike »).
  194. return TRY(Value(&calendar).invoke(global_object, vm.names.daysInWeek, &date_like));
  195. }
  196. // 12.1.17 CalendarDaysInMonth ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardaysinmonth
  197. ThrowCompletionOr<Value> calendar_days_in_month(GlobalObject& global_object, Object& calendar, Object& date_like)
  198. {
  199. auto& vm = global_object.vm();
  200. // 1. Assert: Type(calendar) is Object.
  201. // 2. Return ? Invoke(calendar, "daysInMonth", « dateLike »).
  202. return TRY(Value(&calendar).invoke(global_object, vm.names.daysInMonth, &date_like));
  203. }
  204. // 12.1.18 CalendarDaysInYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendardaysinyear
  205. ThrowCompletionOr<Value> calendar_days_in_year(GlobalObject& global_object, Object& calendar, Object& date_like)
  206. {
  207. auto& vm = global_object.vm();
  208. // 1. Assert: Type(calendar) is Object.
  209. // 2. Return ? Invoke(calendar, "daysInYear", « dateLike »).
  210. return TRY(Value(&calendar).invoke(global_object, vm.names.daysInYear, &date_like));
  211. }
  212. // 12.1.19 CalendarMonthsInYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmonthsinyear
  213. ThrowCompletionOr<Value> calendar_months_in_year(GlobalObject& global_object, Object& calendar, Object& date_like)
  214. {
  215. auto& vm = global_object.vm();
  216. // 1. Assert: Type(calendar) is Object.
  217. // 2. Return ? Invoke(calendar, "monthsInYear", « dateLike »).
  218. return TRY(Value(&calendar).invoke(global_object, vm.names.monthsInYear, &date_like));
  219. }
  220. // 12.1.20 CalendarInLeapYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarinleapyear
  221. ThrowCompletionOr<Value> calendar_in_leap_year(GlobalObject& global_object, Object& calendar, Object& date_like)
  222. {
  223. auto& vm = global_object.vm();
  224. // 1. Assert: Type(calendar) is Object.
  225. // 2. Return ? Invoke(calendar, "inLeapYear", « dateLike »).
  226. return TRY(Value(&calendar).invoke(global_object, vm.names.inLeapYear, &date_like));
  227. }
  228. // 15.6.1.2 CalendarEra ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarera
  229. ThrowCompletionOr<Value> calendar_era(GlobalObject& global_object, Object& calendar, Object& date_like)
  230. {
  231. auto& vm = global_object.vm();
  232. // 1. Assert: Type(calendar) is Object.
  233. // 2. Let result be ? Invoke(calendar, "era", « dateLike »).
  234. auto result = TRY(Value(&calendar).invoke(global_object, vm.names.era, &date_like));
  235. // 3. If result is not undefined, set result to ? ToString(result).
  236. if (!result.is_undefined())
  237. result = js_string(vm, TRY(result.to_string(global_object)));
  238. // 4. Return result.
  239. return result;
  240. }
  241. // 15.6.1.3 CalendarEraYear ( calendar, dateLike ), https://tc39.es/proposal-temporal/#sec-temporal-calendarerayear
  242. ThrowCompletionOr<Value> calendar_era_year(GlobalObject& global_object, Object& calendar, Object& date_like)
  243. {
  244. auto& vm = global_object.vm();
  245. // 1. Assert: Type(calendar) is Object.
  246. // 2. Let result be ? Invoke(calendar, "eraYear", « dateLike »).
  247. auto result = TRY(Value(&calendar).invoke(global_object, vm.names.eraYear, &date_like));
  248. // 3. If result is not undefined, set result to ? ToIntegerThrowOnInfinity(result).
  249. if (!result.is_undefined())
  250. result = Value(TRY(to_integer_throw_on_infinity(global_object, result, ErrorType::TemporalInvalidCalendarFunctionResult, vm.names.eraYear.as_string(), "Infinity"sv)));
  251. // 4. Return result.
  252. return result;
  253. }
  254. // 12.1.21 ToTemporalCalendar ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendar
  255. ThrowCompletionOr<Object*> to_temporal_calendar(GlobalObject& global_object, Value temporal_calendar_like)
  256. {
  257. auto& vm = global_object.vm();
  258. // 1. If Type(temporalCalendarLike) is Object, then
  259. if (temporal_calendar_like.is_object()) {
  260. auto& temporal_calendar_like_object = temporal_calendar_like.as_object();
  261. // a. If temporalCalendarLike has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
  262. // i. Return temporalCalendarLike.[[Calendar]].
  263. if (is<PlainDate>(temporal_calendar_like_object))
  264. return &static_cast<PlainDate&>(temporal_calendar_like_object).calendar();
  265. if (is<PlainDateTime>(temporal_calendar_like_object))
  266. return &static_cast<PlainDateTime&>(temporal_calendar_like_object).calendar();
  267. if (is<PlainMonthDay>(temporal_calendar_like_object))
  268. return &static_cast<PlainMonthDay&>(temporal_calendar_like_object).calendar();
  269. if (is<PlainTime>(temporal_calendar_like_object))
  270. return &static_cast<PlainTime&>(temporal_calendar_like_object).calendar();
  271. if (is<PlainYearMonth>(temporal_calendar_like_object))
  272. return &static_cast<PlainYearMonth&>(temporal_calendar_like_object).calendar();
  273. if (is<ZonedDateTime>(temporal_calendar_like_object))
  274. return &static_cast<ZonedDateTime&>(temporal_calendar_like_object).calendar();
  275. // b. If ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
  276. if (!TRY(temporal_calendar_like_object.has_property(vm.names.calendar)))
  277. return &temporal_calendar_like_object;
  278. // c. Set temporalCalendarLike to ? Get(temporalCalendarLike, "calendar").
  279. temporal_calendar_like = TRY(temporal_calendar_like_object.get(vm.names.calendar));
  280. // d. If Type(temporalCalendarLike) is Object and ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
  281. if (temporal_calendar_like.is_object() && !TRY(temporal_calendar_like.as_object().has_property(vm.names.calendar)))
  282. return &temporal_calendar_like.as_object();
  283. }
  284. // 2. Let identifier be ? ToString(temporalCalendarLike).
  285. auto identifier = TRY(temporal_calendar_like.to_string(global_object));
  286. // 3. If ! IsBuiltinCalendar(identifier) is false, then
  287. if (!is_builtin_calendar(identifier)) {
  288. // a. Let identifier be ? ParseTemporalCalendarString(identifier).
  289. identifier = TRY(parse_temporal_calendar_string(global_object, identifier));
  290. }
  291. // 4. Return ! CreateTemporalCalendar(identifier).
  292. return MUST(create_temporal_calendar(global_object, identifier));
  293. }
  294. // 12.1.22 ToTemporalCalendarWithISODefault ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendarwithisodefault
  295. ThrowCompletionOr<Object*> to_temporal_calendar_with_iso_default(GlobalObject& global_object, Value temporal_calendar_like)
  296. {
  297. // 1. If temporalCalendarLike is undefined, then
  298. if (temporal_calendar_like.is_undefined()) {
  299. // a. Return ! GetISO8601Calendar().
  300. return get_iso8601_calendar(global_object);
  301. }
  302. // 2. Return ? ToTemporalCalendar(temporalCalendarLike).
  303. return to_temporal_calendar(global_object, temporal_calendar_like);
  304. }
  305. // 12.1.23 GetTemporalCalendarWithISODefault ( item ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporalcalendarwithisodefault
  306. ThrowCompletionOr<Object*> get_temporal_calendar_with_iso_default(GlobalObject& global_object, Object& item)
  307. {
  308. auto& vm = global_object.vm();
  309. // 1. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
  310. // a. Return item.[[Calendar]].
  311. if (is<PlainDate>(item))
  312. return &static_cast<PlainDate&>(item).calendar();
  313. if (is<PlainDateTime>(item))
  314. return &static_cast<PlainDateTime&>(item).calendar();
  315. if (is<PlainMonthDay>(item))
  316. return &static_cast<PlainMonthDay&>(item).calendar();
  317. if (is<PlainTime>(item))
  318. return &static_cast<PlainTime&>(item).calendar();
  319. if (is<PlainYearMonth>(item))
  320. return &static_cast<PlainYearMonth&>(item).calendar();
  321. if (is<ZonedDateTime>(item))
  322. return &static_cast<ZonedDateTime&>(item).calendar();
  323. // 2. Let calendar be ? Get(item, "calendar").
  324. auto calendar = TRY(item.get(vm.names.calendar));
  325. // 3. Return ? ToTemporalCalendarWithISODefault(calendar).
  326. return to_temporal_calendar_with_iso_default(global_object, calendar);
  327. }
  328. // 12.1.24 DateFromFields ( calendar, fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-datefromfields
  329. ThrowCompletionOr<PlainDate*> date_from_fields(GlobalObject& global_object, Object& calendar, Object const& fields, Object const& options)
  330. {
  331. auto& vm = global_object.vm();
  332. // 1. Assert: Type(calendar) is Object.
  333. // 2. Assert: Type(fields) is Object.
  334. // 3. Let date be ? Invoke(calendar, "dateFromFields", « fields, options »).
  335. auto date = TRY(Value(&calendar).invoke(global_object, vm.names.dateFromFields, &fields, &options));
  336. // 4. Perform ? RequireInternalSlot(date, [[InitializedTemporalDate]]).
  337. auto* date_object = TRY(date.to_object(global_object));
  338. if (!is<PlainDate>(date_object))
  339. return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObjectOfType, "Temporal.PlainDate");
  340. // 5. Return date.
  341. return static_cast<PlainDate*>(date_object);
  342. }
  343. // 12.1.25 YearMonthFromFields ( calendar, fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-yearmonthfromfields
  344. ThrowCompletionOr<PlainYearMonth*> year_month_from_fields(GlobalObject& global_object, Object& calendar, Object const& fields, Object const* options)
  345. {
  346. auto& vm = global_object.vm();
  347. // 1. Assert: Type(calendar) is Object.
  348. // 2. Assert: Type(fields) is Object.
  349. // 3. If options is not present, then
  350. // a. Set options to undefined.
  351. // 4. Else,
  352. // a. Assert: Type(options) is Object.
  353. // 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
  354. auto year_month = TRY(Value(&calendar).invoke(global_object, vm.names.yearMonthFromFields, &fields, options ?: js_undefined()));
  355. // 6. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
  356. auto* year_month_object = TRY(year_month.to_object(global_object));
  357. if (!is<PlainYearMonth>(year_month_object))
  358. return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObjectOfType, "Temporal.PlainYearMonth");
  359. // 7. Return yearMonth.
  360. return static_cast<PlainYearMonth*>(year_month_object);
  361. }
  362. // 12.1.26 MonthDayFromFields ( calendar, fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-monthdayfromfields
  363. ThrowCompletionOr<PlainMonthDay*> month_day_from_fields(GlobalObject& global_object, Object& calendar, Object const& fields, Object const* options)
  364. {
  365. auto& vm = global_object.vm();
  366. // 1. Assert: Type(calendar) is Object.
  367. // 2. Assert: Type(fields) is Object.
  368. // 3. If options is not present, then
  369. // a. Set options to undefined.
  370. // 4. Else,
  371. // a. Assert: Type(options) is Object.
  372. // 5. Let monthDay be ? Invoke(calendar, "monthDayFromFields", « fields, options »).
  373. auto month_day = TRY(Value(&calendar).invoke(global_object, vm.names.monthDayFromFields, &fields, options ?: js_undefined()));
  374. // 6. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
  375. auto* month_day_object = TRY(month_day.to_object(global_object));
  376. if (!is<PlainMonthDay>(month_day_object))
  377. return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObjectOfType, "Temporal.PlainMonthDay");
  378. // 7. Return monthDay.
  379. return static_cast<PlainMonthDay*>(month_day_object);
  380. }
  381. // 12.1.27 FormatCalendarAnnotation ( id, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-formatcalendarannotation
  382. String format_calendar_annotation(StringView id, StringView show_calendar)
  383. {
  384. // 1. Assert: showCalendar is "auto", "always", or "never".
  385. VERIFY(show_calendar == "auto"sv || show_calendar == "always"sv || show_calendar == "never"sv);
  386. // 2. If showCalendar is "never", return the empty String.
  387. if (show_calendar == "never"sv)
  388. return String::empty();
  389. // 3. If showCalendar is "auto" and id is "iso8601", return the empty String.
  390. if (show_calendar == "auto"sv && id == "iso8601"sv)
  391. return String::empty();
  392. // 4. Return the string-concatenation of "[u-ca=", id, and "]".
  393. return String::formatted("[u-ca={}]", id);
  394. }
  395. // 12.1.28 CalendarEquals ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-calendarequals
  396. ThrowCompletionOr<bool> calendar_equals(GlobalObject& global_object, Object& one, Object& two)
  397. {
  398. // 1. If one and two are the same Object value, return true.
  399. if (&one == &two)
  400. return true;
  401. // 2. Let calendarOne be ? ToString(one).
  402. auto calendar_one = TRY(Value(&one).to_string(global_object));
  403. // 3. Let calendarTwo be ? ToString(two).
  404. auto calendar_two = TRY(Value(&two).to_string(global_object));
  405. // 4. If calendarOne is calendarTwo, return true.
  406. if (calendar_one == calendar_two)
  407. return true;
  408. // 5. Return false.
  409. return false;
  410. }
  411. // 12.1.29 ConsolidateCalendars ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-consolidatecalendars
  412. ThrowCompletionOr<Object*> consolidate_calendars(GlobalObject& global_object, Object& one, Object& two)
  413. {
  414. auto& vm = global_object.vm();
  415. // 1. If one and two are the same Object value, return two.
  416. if (&one == &two)
  417. return &two;
  418. // 2. Let calendarOne be ? ToString(one).
  419. auto calendar_one = TRY(Value(&one).to_string(global_object));
  420. // 3. Let calendarTwo be ? ToString(two).
  421. auto calendar_two = TRY(Value(&two).to_string(global_object));
  422. // 4. If calendarOne is calendarTwo, return two.
  423. if (calendar_one == calendar_two)
  424. return &two;
  425. // 5. If calendarOne is "iso8601", return two.
  426. if (calendar_one == "iso8601"sv)
  427. return &two;
  428. // 6. If calendarTwo is "iso8601", return one.
  429. if (calendar_two == "iso8601"sv)
  430. return &one;
  431. // 7. Throw a RangeError exception.
  432. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidCalendar);
  433. }
  434. // 12.1.30 IsISOLeapYear ( year ), https://tc39.es/proposal-temporal/#sec-temporal-isisoleapyear
  435. bool is_iso_leap_year(i32 year)
  436. {
  437. // 1. Assert: year is an integer.
  438. // 2. If year modulo 4 ≠ 0, return false.
  439. if (year % 4 != 0)
  440. return false;
  441. // 3. If year modulo 400 = 0, return true.
  442. if (year % 400 == 0)
  443. return true;
  444. // 4. If year modulo 100 = 0, return false.
  445. if (year % 100 == 0)
  446. return false;
  447. // 5. Return true.
  448. return true;
  449. }
  450. // 12.1.31 ISODaysInYear ( year ), https://tc39.es/proposal-temporal/#sec-temporal-isodaysinyear
  451. u16 iso_days_in_year(i32 year)
  452. {
  453. // 1. Assert: year is an integer.
  454. // 2. If ! IsISOLeapYear(year) is true, then
  455. if (is_iso_leap_year(year)) {
  456. // a. Return 366.
  457. return 366;
  458. }
  459. // 3. Return 365.
  460. return 365;
  461. }
  462. // 12.1.32 ISODaysInMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isodaysinmonth
  463. u8 iso_days_in_month(i32 year, u8 month)
  464. {
  465. // 1. Assert: year is an integer.
  466. // 2. Assert: month is an integer, month ≥ 1, and month ≤ 12.
  467. VERIFY(month >= 1 && month <= 12);
  468. // 3. If month is 1, 3, 5, 7, 8, 10, or 12, return 31.
  469. if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
  470. return 31;
  471. // 4. If month is 4, 6, 9, or 11, return 30.
  472. if (month == 4 || month == 6 || month == 9 || month == 11)
  473. return 30;
  474. // 5. If ! IsISOLeapYear(year) is true, return 29.
  475. if (is_iso_leap_year(year))
  476. return 29;
  477. // 6. Return 28.
  478. return 28;
  479. }
  480. // 12.1.33 ToISODayOfWeek ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-toisodayofweek
  481. u8 to_iso_day_of_week(i32 year, u8 month, u8 day)
  482. {
  483. // 1. Assert: year is an integer.
  484. // 2. Assert: month is an integer.
  485. // 3. Assert: day is an integer.
  486. // 4. Let date be the date given by year, month, and day.
  487. // 5. Return date's day of the week according to ISO-8601.
  488. // NOTE: Implemented based on https://cs.uwaterloo.ca/~alopez-o/math-faq/node73.html
  489. auto normalized_month = month + (month < 3 ? 10 : -2);
  490. auto normalized_year = year - (month < 3 ? 1 : 0);
  491. auto century = normalized_year / 100;
  492. auto truncated_year = normalized_year - (century * 100);
  493. auto result = (day + static_cast<u8>((2.6 * normalized_month) - 0.2) - (2 * century) + truncated_year + (truncated_year / 4) + (century / 4)) % 7;
  494. if (result <= 0) // Mathematical modulo
  495. result += 7;
  496. return result;
  497. }
  498. // 12.1.34 ToISODayOfYear ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-toisodayofyear
  499. u16 to_iso_day_of_year(i32 year, u8 month, u8 day)
  500. {
  501. // 1. Assert: year is an integer.
  502. // 2. Assert: month is an integer.
  503. // 3. Assert: day is an integer.
  504. // 4. Let date be the date given by year, month, and day.
  505. // 5. Return date's ordinal date in the year according to ISO-8601.
  506. u16 days = day;
  507. for (u8 i = month - 1; i > 0; --i)
  508. days += iso_days_in_month(year, i);
  509. return days;
  510. }
  511. // 12.1.35 ToISOWeekOfYear ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-toisoweekofyear
  512. u8 to_iso_week_of_year(i32 year, u8 month, u8 day)
  513. {
  514. // 1. Assert: year is an integer.
  515. // 2. Assert: month is an integer.
  516. // 3. Assert: day is an integer.
  517. // 4. Let date be the date given by year, month, and day.
  518. // 5. Return date's week number according to ISO-8601.
  519. auto day_of_year = to_iso_day_of_year(year, month, day);
  520. auto day_of_week = to_iso_day_of_week(year, month, day);
  521. auto week = (day_of_year - day_of_week + 10) / 7;
  522. if (week < 1) {
  523. auto day_of_jump = to_iso_day_of_week(year, 1, 1);
  524. if (day_of_jump == 5 || (is_iso_leap_year(year) && day_of_jump == 6))
  525. return 53;
  526. else
  527. return 52;
  528. } else if (week == 53) {
  529. auto days_in_year = iso_days_in_year(year);
  530. if (days_in_year - day_of_year < 4 - day_of_week)
  531. return 1;
  532. }
  533. return week;
  534. }
  535. // 12.1.36 BuildISOMonthCode ( month ), https://tc39.es/proposal-temporal/#sec-buildisomonthcode
  536. String build_iso_month_code(u8 month)
  537. {
  538. return String::formatted("M{:02}", month);
  539. }
  540. // 12.1.37 ResolveISOMonth ( fields ), https://tc39.es/proposal-temporal/#sec-temporal-resolveisomonth
  541. ThrowCompletionOr<double> resolve_iso_month(GlobalObject& global_object, Object const& fields)
  542. {
  543. auto& vm = global_object.vm();
  544. // 1. Let month be ? Get(fields, "month").
  545. auto month = TRY(fields.get(vm.names.month));
  546. // 2. Let monthCode be ? Get(fields, "monthCode").
  547. auto month_code = TRY(fields.get(vm.names.monthCode));
  548. // 3. If monthCode is undefined, then
  549. if (month_code.is_undefined()) {
  550. // a. If month is undefined, throw a TypeError exception.
  551. if (month.is_undefined())
  552. return vm.throw_completion<TypeError>(global_object, ErrorType::MissingRequiredProperty, vm.names.month.as_string());
  553. // b. Return month.
  554. return month.as_double();
  555. }
  556. // 4. Assert: Type(monthCode) is String.
  557. VERIFY(month_code.is_string());
  558. auto& month_code_string = month_code.as_string().string();
  559. // 5. Let monthLength be the length of monthCode.
  560. auto month_length = month_code_string.length();
  561. // 6. If monthLength is not 3, throw a RangeError exception.
  562. if (month_length != 3)
  563. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidMonthCode);
  564. // 7. Let numberPart be the substring of monthCode from 1.
  565. auto number_part = month_code_string.substring(1);
  566. // 8. Set numberPart to ! ToIntegerOrInfinity(numberPart).
  567. auto number_part_integer = MUST(Value(js_string(vm, move(number_part))).to_integer_or_infinity(global_object));
  568. // 9. If numberPart < 1 or numberPart > 12, throw a RangeError exception.
  569. if (number_part_integer < 1 || number_part_integer > 12)
  570. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidMonthCode);
  571. // 10. If month is not undefined, and month ≠ numberPart, then
  572. if (!month.is_undefined() && month.as_double() != number_part_integer) {
  573. // a. Throw a RangeError exception.
  574. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidMonthCode);
  575. }
  576. // 11. If ! SameValueNonNumeric(monthCode, ! BuildISOMonthCode(numberPart)) is false, then
  577. if (month_code_string != build_iso_month_code(number_part_integer)) {
  578. // a. Throw a RangeError exception.
  579. return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidMonthCode);
  580. }
  581. // 12. Return numberPart.
  582. return number_part_integer;
  583. }
  584. // 12.1.38 ISODateFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isodatefromfields
  585. ThrowCompletionOr<ISODate> iso_date_from_fields(GlobalObject& global_object, Object const& fields, Object const& options)
  586. {
  587. auto& vm = global_object.vm();
  588. // 1. Assert: Type(fields) is Object.
  589. // 2. Let overflow be ? ToTemporalOverflow(options).
  590. auto overflow = TRY(to_temporal_overflow(global_object, options));
  591. // 3. Set fields to ? PrepareTemporalFields(fields, « "day", "month", "monthCode", "year" », «»).
  592. auto* prepared_fields = TRY(prepare_temporal_fields(global_object, fields, { "day", "month", "monthCode", "year" }, {}));
  593. // 4. Let year be ? Get(fields, "year").
  594. auto year = TRY(prepared_fields->get(vm.names.year));
  595. // 5. If year is undefined, throw a TypeError exception.
  596. if (year.is_undefined())
  597. return vm.throw_completion<TypeError>(global_object, ErrorType::MissingRequiredProperty, vm.names.year.as_string());
  598. // 6. Let month be ? ResolveISOMonth(fields).
  599. auto month = TRY(resolve_iso_month(global_object, *prepared_fields));
  600. // 7. Let day be ? Get(fields, "day").
  601. auto day = TRY(prepared_fields->get(vm.names.day));
  602. // 8. If day is undefined, throw a TypeError exception.
  603. if (day.is_undefined())
  604. return vm.throw_completion<TypeError>(global_object, ErrorType::MissingRequiredProperty, vm.names.day.as_string());
  605. // 9. Return ? RegulateISODate(year, month, day, overflow).
  606. return regulate_iso_date(global_object, year.as_double(), month, day.as_double(), overflow);
  607. }
  608. // 12.1.39 ISOYearMonthFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isoyearmonthfromfields
  609. ThrowCompletionOr<ISOYearMonth> iso_year_month_from_fields(GlobalObject& global_object, Object const& fields, Object const& options)
  610. {
  611. auto& vm = global_object.vm();
  612. // 1. Assert: Type(fields) is Object.
  613. // 2. Let overflow be ? ToTemporalOverflow(options).
  614. auto overflow = TRY(to_temporal_overflow(global_object, options));
  615. // 3. Set fields to ? PrepareTemporalFields(fields, « "month", "monthCode", "year" », «»).
  616. auto* prepared_fields = TRY(prepare_temporal_fields(global_object, fields, { "month"sv, "monthCode"sv, "year"sv }, {}));
  617. // 4. Let year be ? Get(fields, "year").
  618. auto year = TRY(prepared_fields->get(vm.names.year));
  619. // 5. If year is undefined, throw a TypeError exception.
  620. if (year.is_undefined())
  621. return vm.throw_completion<TypeError>(global_object, ErrorType::MissingRequiredProperty, vm.names.year.as_string());
  622. // 6. Let month be ? ResolveISOMonth(fields).
  623. auto month = TRY(resolve_iso_month(global_object, *prepared_fields));
  624. // 7. Let result be ? RegulateISOYearMonth(year, month, overflow).
  625. auto result = TRY(regulate_iso_year_month(global_object, year.as_double(), month, overflow));
  626. // 8. Return the Record { [[Year]]: result.[[Year]], [[Month]]: result.[[Month]], [[ReferenceISODay]]: 1 }.
  627. return ISOYearMonth { .year = result.year, .month = result.month, .reference_iso_day = 1 };
  628. }
  629. // 12.1.40 ISOMonthDayFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthdayfromfields
  630. ThrowCompletionOr<ISOMonthDay> iso_month_day_from_fields(GlobalObject& global_object, Object const& fields, Object const& options)
  631. {
  632. auto& vm = global_object.vm();
  633. // 1. Assert: Type(fields) is Object.
  634. // 2. Let overflow be ? ToTemporalOverflow(options).
  635. auto overflow = TRY(to_temporal_overflow(global_object, options));
  636. // 3. Set fields to ? PrepareTemporalFields(fields, « "day", "month", "monthCode", "year" », «»).
  637. auto* prepared_fields = TRY(prepare_temporal_fields(global_object, fields, { "day"sv, "month"sv, "monthCode"sv, "year"sv }, {}));
  638. // 4. Let month be ? Get(fields, "month").
  639. auto month_value = TRY(prepared_fields->get(vm.names.month));
  640. // 5. Let monthCode be ? Get(fields, "monthCode").
  641. auto month_code = TRY(prepared_fields->get(vm.names.monthCode));
  642. // 6. Let year be ? Get(fields, "year").
  643. auto year = TRY(prepared_fields->get(vm.names.year));
  644. // 7. If month is not undefined, and monthCode and year are both undefined, then
  645. if (!month_value.is_undefined() && month_code.is_undefined() && year.is_undefined()) {
  646. // a. Throw a TypeError exception.
  647. return vm.throw_completion<TypeError>(global_object, ErrorType::MissingRequiredProperty, "monthCode or year");
  648. }
  649. // 8. Set month to ? ResolveISOMonth(fields).
  650. auto month = TRY(resolve_iso_month(global_object, *prepared_fields));
  651. // 9. Let day be ? Get(fields, "day").
  652. auto day = TRY(prepared_fields->get(vm.names.day));
  653. // 10. If day is undefined, throw a TypeError exception.
  654. if (day.is_undefined())
  655. return vm.throw_completion<TypeError>(global_object, ErrorType::MissingRequiredProperty, vm.names.day.as_string());
  656. // 11. Let referenceISOYear be 1972 (the first leap year after the Unix epoch).
  657. i32 reference_iso_year = 1972;
  658. Optional<ISODate> result;
  659. // 12. If monthCode is undefined, then
  660. if (month_code.is_undefined()) {
  661. // a. Let result be ? RegulateISODate(year, month, day, overflow).
  662. result = TRY(regulate_iso_date(global_object, year.as_double(), month, day.as_double(), overflow));
  663. }
  664. // 13. Else,
  665. else {
  666. // a. Let result be ? RegulateISODate(referenceISOYear, month, day, overflow).
  667. result = TRY(regulate_iso_date(global_object, reference_iso_year, month, day.as_double(), overflow));
  668. }
  669. // 14. Return the Record { [[Month]]: result.[[Month]], [[Day]]: result.[[Day]], [[ReferenceISOYear]]: referenceISOYear }.
  670. return ISOMonthDay { .month = result->month, .day = result->day, .reference_iso_year = reference_iso_year };
  671. }
  672. // 12.1.41 ISOYear ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isoyear
  673. i32 iso_year(Object& temporal_object)
  674. {
  675. // 1. Assert: temporalObject has an [[ISOYear]] internal slot.
  676. // NOTE: Asserted by the VERIFY_NOT_REACHED at the end
  677. // 2. Return 𝔽(temporalObject.[[ISOYear]]).
  678. if (is<PlainDate>(temporal_object))
  679. return static_cast<PlainDate&>(temporal_object).iso_year();
  680. if (is<PlainDateTime>(temporal_object))
  681. return static_cast<PlainDateTime&>(temporal_object).iso_year();
  682. if (is<PlainYearMonth>(temporal_object))
  683. return static_cast<PlainYearMonth&>(temporal_object).iso_year();
  684. if (is<PlainMonthDay>(temporal_object))
  685. return static_cast<PlainMonthDay&>(temporal_object).iso_year();
  686. VERIFY_NOT_REACHED();
  687. }
  688. // 12.1.42 ISOMonth ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isomonth
  689. u8 iso_month(Object& temporal_object)
  690. {
  691. // 1. Assert: temporalObject has an [[ISOMonth]] internal slot.
  692. // NOTE: Asserted by the VERIFY_NOT_REACHED at the end
  693. // 2. Return 𝔽(temporalObject.[[ISOMonth]]).
  694. if (is<PlainDate>(temporal_object))
  695. return static_cast<PlainDate&>(temporal_object).iso_month();
  696. if (is<PlainDateTime>(temporal_object))
  697. return static_cast<PlainDateTime&>(temporal_object).iso_month();
  698. if (is<PlainYearMonth>(temporal_object))
  699. return static_cast<PlainYearMonth&>(temporal_object).iso_month();
  700. if (is<PlainMonthDay>(temporal_object))
  701. return static_cast<PlainMonthDay&>(temporal_object).iso_month();
  702. VERIFY_NOT_REACHED();
  703. }
  704. // 12.1.43 ISOMonthCode ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthcode
  705. String iso_month_code(Object& temporal_object)
  706. {
  707. // 1. Assert: temporalObject has an [[ISOMonth]] internal slot.
  708. // NOTE: Asserted by the VERIFY_NOT_REACHED at the end
  709. // 2. Return ! BuildISOMonthCode(temporalObject.[[ISOMonth]]).
  710. if (is<PlainDate>(temporal_object))
  711. return build_iso_month_code(static_cast<PlainDate&>(temporal_object).iso_month());
  712. if (is<PlainDateTime>(temporal_object))
  713. return build_iso_month_code(static_cast<PlainDateTime&>(temporal_object).iso_month());
  714. if (is<PlainYearMonth>(temporal_object))
  715. return build_iso_month_code(static_cast<PlainYearMonth&>(temporal_object).iso_month());
  716. if (is<PlainMonthDay>(temporal_object))
  717. return build_iso_month_code(static_cast<PlainMonthDay&>(temporal_object).iso_month());
  718. VERIFY_NOT_REACHED();
  719. }
  720. // 12.1.44 ISODay ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthcode
  721. u8 iso_day(Object& temporal_object)
  722. {
  723. // 1. Assert: temporalObject has an [[ISODay]] internal slot.
  724. // NOTE: Asserted by the VERIFY_NOT_REACHED at the end
  725. // 2. Return 𝔽(temporalObject.[[ISODay]]).
  726. if (is<PlainDate>(temporal_object))
  727. return static_cast<PlainDate&>(temporal_object).iso_day();
  728. if (is<PlainDateTime>(temporal_object))
  729. return static_cast<PlainDateTime&>(temporal_object).iso_day();
  730. if (is<PlainYearMonth>(temporal_object))
  731. return static_cast<PlainYearMonth&>(temporal_object).iso_day();
  732. if (is<PlainMonthDay>(temporal_object))
  733. return static_cast<PlainMonthDay&>(temporal_object).iso_day();
  734. VERIFY_NOT_REACHED();
  735. }
  736. // 12.1.45 DefaultMergeFields ( fields, additionalFields ), https://tc39.es/proposal-temporal/#sec-temporal-defaultmergefields
  737. ThrowCompletionOr<Object*> default_merge_fields(GlobalObject& global_object, Object const& fields, Object const& additional_fields)
  738. {
  739. auto& vm = global_object.vm();
  740. // 1. Let merged be ! OrdinaryObjectCreate(%Object.prototype%).
  741. auto* merged = Object::create(global_object, global_object.object_prototype());
  742. // 2. Let originalKeys be ? EnumerableOwnPropertyNames(fields, key).
  743. auto original_keys = TRY(fields.enumerable_own_property_names(Object::PropertyKind::Key));
  744. // 3. For each element nextKey of originalKeys, do
  745. for (auto& next_key : original_keys) {
  746. // a. If nextKey is not "month" or "monthCode", then
  747. if (next_key.as_string().string() != vm.names.month.as_string() && next_key.as_string().string() != vm.names.monthCode.as_string()) {
  748. auto property_name = PropertyName::from_value(global_object, next_key);
  749. // i. Let propValue be ? Get(fields, nextKey).
  750. auto prop_value = TRY(fields.get(property_name));
  751. // ii. If propValue is not undefined, then
  752. if (!prop_value.is_undefined()) {
  753. // 1. Perform ! CreateDataPropertyOrThrow(merged, nextKey, propValue).
  754. MUST(merged->create_data_property_or_throw(property_name, prop_value));
  755. }
  756. }
  757. }
  758. // 4. Let newKeys be ? EnumerableOwnPropertyNames(additionalFields, key).
  759. auto new_keys = TRY(additional_fields.enumerable_own_property_names(Object::PropertyKind::Key));
  760. // IMPLEMENTATION DEFINED: This is an optimization, so we don't have to iterate new_keys three times (worst case), but only once.
  761. bool new_keys_contains_month_or_month_code_property = false;
  762. // 5. For each element nextKey of newKeys, do
  763. for (auto& next_key : new_keys) {
  764. auto property_name = PropertyName::from_value(global_object, next_key);
  765. // a. Let propValue be ? Get(additionalFields, nextKey).
  766. auto prop_value = TRY(additional_fields.get(property_name));
  767. // b. If propValue is not undefined, then
  768. if (!prop_value.is_undefined()) {
  769. // i. Perform ! CreateDataPropertyOrThrow(merged, nextKey, propValue).
  770. MUST(merged->create_data_property_or_throw(property_name, prop_value));
  771. }
  772. // See comment above.
  773. new_keys_contains_month_or_month_code_property |= next_key.as_string().string() == vm.names.month.as_string() || next_key.as_string().string() == vm.names.monthCode.as_string();
  774. }
  775. // 6. If newKeys does not contain either "month" or "monthCode", then
  776. if (!new_keys_contains_month_or_month_code_property) {
  777. // a. Let month be ? Get(fields, "month").
  778. auto month = TRY(fields.get(vm.names.month));
  779. // b. If month is not undefined, then
  780. if (!month.is_undefined()) {
  781. // i. Perform ! CreateDataPropertyOrThrow(merged, "month", month).
  782. MUST(merged->create_data_property_or_throw(vm.names.month, month));
  783. }
  784. // c. Let monthCode be ? Get(fields, "monthCode").
  785. auto month_code = TRY(fields.get(vm.names.monthCode));
  786. // d. If monthCode is not undefined, then
  787. if (!month_code.is_undefined()) {
  788. // i. Perform ! CreateDataPropertyOrThrow(merged, "monthCode", monthCode).
  789. MUST(merged->create_data_property_or_throw(vm.names.monthCode, month_code));
  790. }
  791. }
  792. // 7. Return merged.
  793. return merged;
  794. }
  795. }