AbstractOperations.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/CharacterTypes.h>
  7. #include <AK/DateTimeLexer.h>
  8. #include <LibJS/Runtime/Temporal/AbstractOperations.h>
  9. #include <LibJS/Runtime/Temporal/PlainDate.h>
  10. #include <LibJS/Runtime/Temporal/PlainTime.h>
  11. #include <LibJS/Runtime/Temporal/TimeZone.h>
  12. namespace JS::Temporal {
  13. // 13.34 ParseISODateTime ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parseisodatetime
  14. Optional<ISODateTime> parse_iso_date_time(GlobalObject& global_object, [[maybe_unused]] String const& iso_string)
  15. {
  16. auto& vm = global_object.vm();
  17. // 1. Assert: Type(isoString) is String.
  18. // 2. Let year, month, day, hour, minute, second, fraction, and calendar be the parts of isoString produced respectively by the DateYear, DateMonth, DateDay, TimeHour, TimeMinute, TimeSecond, TimeFractionalPart, and CalendarName productions, or undefined if not present.
  19. Optional<StringView> year_part;
  20. Optional<StringView> month_part;
  21. Optional<StringView> day_part;
  22. Optional<StringView> hour_part;
  23. Optional<StringView> minute_part;
  24. Optional<StringView> second_part;
  25. Optional<StringView> fraction_part;
  26. Optional<StringView> calendar_part;
  27. TODO();
  28. // 3. Let year be the part of isoString produced by the DateYear production.
  29. // 4. If the first code unit of year is 0x2212 (MINUS SIGN), replace it with the code unit 0x002D (HYPHEN-MINUS).
  30. String normalized_year;
  31. if (year_part.has_value() && year_part->starts_with("\xE2\x88\x92"sv))
  32. normalized_year = String::formatted("-{}", year_part->substring_view(3));
  33. else
  34. normalized_year = year_part.value_or("");
  35. // 5. Set year to ! ToIntegerOrInfinity(year).
  36. i32 year = Value(js_string(vm, normalized_year)).to_integer_or_infinity(global_object);
  37. i32 month;
  38. // 6. If month is undefined, then
  39. if (!month_part.has_value()) {
  40. // a. Set month to 1.
  41. month = 1;
  42. }
  43. // 7. Else,
  44. else {
  45. // a. Set month to ! ToIntegerOrInfinity(month).
  46. month = Value(js_string(vm, *month_part)).to_integer_or_infinity(global_object);
  47. }
  48. i32 day;
  49. // 8. If day is undefined, then
  50. if (!day_part.has_value()) {
  51. // a. Set day to 1.
  52. day = 1;
  53. }
  54. // 9. Else,
  55. else {
  56. // a. Set day to ! ToIntegerOrInfinity(day).
  57. day = Value(js_string(vm, *day_part)).to_integer_or_infinity(global_object);
  58. }
  59. // 10. Set hour to ! ToIntegerOrInfinity(hour).
  60. i32 hour = Value(js_string(vm, hour_part.value_or(""sv))).to_integer_or_infinity(global_object);
  61. // 11. Set minute to ! ToIntegerOrInfinity(minute).
  62. i32 minute = Value(js_string(vm, minute_part.value_or(""sv))).to_integer_or_infinity(global_object);
  63. // 12. Set second to ! ToIntegerOrInfinity(second).
  64. i32 second = Value(js_string(vm, second_part.value_or(""sv))).to_integer_or_infinity(global_object);
  65. // 13. If second is 60, then
  66. if (second == 60) {
  67. // a. Set second to 59.
  68. second = 59;
  69. }
  70. i32 millisecond;
  71. i32 microsecond;
  72. i32 nanosecond;
  73. // 14. If fraction is not undefined, then
  74. if (fraction_part.has_value()) {
  75. // a. Set fraction to the string-concatenation of the previous value of fraction and the string "000000000".
  76. auto fraction = String::formatted("{}000000000", *fraction_part);
  77. // b. Let millisecond be the String value equal to the substring of fraction from 0 to 3.
  78. // c. Set millisecond to ! ToIntegerOrInfinity(millisecond).
  79. millisecond = Value(js_string(vm, fraction.substring(0, 3))).to_integer_or_infinity(global_object);
  80. // d. Let microsecond be the String value equal to the substring of fraction from 3 to 6.
  81. // e. Set microsecond to ! ToIntegerOrInfinity(microsecond).
  82. microsecond = Value(js_string(vm, fraction.substring(3, 3))).to_integer_or_infinity(global_object);
  83. // f. Let nanosecond be the String value equal to the substring of fraction from 6 to 9.
  84. // g. Set nanosecond to ! ToIntegerOrInfinity(nanosecond).
  85. nanosecond = Value(js_string(vm, fraction.substring(6, 3))).to_integer_or_infinity(global_object);
  86. }
  87. // 15. Else,
  88. else {
  89. // a. Let millisecond be 0.
  90. millisecond = 0;
  91. // b. Let microsecond be 0.
  92. microsecond = 0;
  93. // c. Let nanosecond be 0.
  94. nanosecond = 0;
  95. }
  96. // 16. If ! IsValidISODate(year, month, day) is false, throw a RangeError exception.
  97. if (!is_valid_iso_date(year, month, day)) {
  98. vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidISODate);
  99. return {};
  100. }
  101. // 17. If ! IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.
  102. if (!is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond)) {
  103. vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidTime);
  104. return {};
  105. }
  106. // 18. Return the new Record { [[Year]]: year, [[Month]]: month, [[Day]]: day, [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond, [[Calendar]]: calendar }.
  107. return ISODateTime { .year = year, .month = month, .day = day, .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = microsecond, .nanosecond = nanosecond, .calendar = calendar_part.has_value() ? *calendar_part : Optional<String>() };
  108. }
  109. // 13.35 ParseTemporalInstantString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalinstantstring
  110. Optional<TemporalInstant> parse_temporal_instant_string(GlobalObject& global_object, String const& iso_string)
  111. {
  112. auto& vm = global_object.vm();
  113. // 1. Assert: Type(isoString) is String.
  114. // 2. If isoString does not satisfy the syntax of a TemporalInstantString (see 13.33), then
  115. // a. Throw a RangeError exception.
  116. // TODO
  117. // 3. Let result be ! ParseISODateTime(isoString).
  118. auto result = parse_iso_date_time(global_object, iso_string);
  119. // 4. Let timeZoneResult be ? ParseTemporalTimeZoneString(isoString).
  120. auto time_zone_result = parse_temporal_time_zone_string(global_object, iso_string);
  121. if (vm.exception())
  122. return {};
  123. // 5. Assert: timeZoneResult.[[OffsetString]] is not undefined.
  124. VERIFY(time_zone_result->offset.has_value());
  125. // 6. Return the new Record { [[Year]]: result.[[Year]], [[Month]]: result.[[Month]], [[Day]]: result.[[Day]], [[Hour]]: result.[[Hour]], [[Minute]]: result.[[Minute]], [[Second]]: result.[[Second]], [[Millisecond]]: result.[[Millisecond]], [[Microsecond]]: result.[[Microsecond]], [[Nanosecond]]: result.[[Nanosecond]], [[TimeZoneOffsetString]]: timeZoneResult.[[OffsetString]] }.
  126. return TemporalInstant { .year = result->year, .month = result->month, .day = result->day, .hour = result->hour, .minute = result->minute, .second = result->second, .millisecond = result->millisecond, .microsecond = result->microsecond, .nanosecond = result->nanosecond, .time_zone_offset = move(time_zone_result->offset) };
  127. }
  128. // 13.43 ParseTemporalTimeZoneString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimezonestring
  129. Optional<TemporalTimeZone> parse_temporal_time_zone_string(GlobalObject& global_object, [[maybe_unused]] String const& iso_string)
  130. {
  131. auto& vm = global_object.vm();
  132. // 1. Assert: Type(isoString) is String.
  133. // 2. If isoString does not satisfy the syntax of a TemporalTimeZoneString (see 13.33), then
  134. // a. Throw a RangeError exception.
  135. // 3. Let z, sign, hours, minutes, seconds, fraction and name be the parts of isoString produced respectively by the UTCDesignator, TimeZoneUTCOffsetSign, TimeZoneUTCOffsetHour, TimeZoneUTCOffsetMinute, TimeZoneUTCOffsetSecond, TimeZoneUTCOffsetFraction, and TimeZoneIANAName productions, or undefined if not present.
  136. Optional<StringView> z_part;
  137. Optional<StringView> sign_part;
  138. Optional<StringView> hours_part;
  139. Optional<StringView> minutes_part;
  140. Optional<StringView> seconds_part;
  141. Optional<StringView> fraction_part;
  142. Optional<StringView> name_part;
  143. TODO();
  144. // 4. If z is not undefined, then
  145. if (z_part.has_value()) {
  146. // a. Return the new Record: { [[Z]]: "Z", [[OffsetString]]: "+00:00", [[Name]]: undefined }.
  147. return TemporalTimeZone { .z = true, .offset = "+00:00", .name = {} };
  148. }
  149. Optional<String> offset;
  150. // 5. If hours is undefined, then
  151. if (!hours_part.has_value()) {
  152. // a. Let offsetString be undefined.
  153. // NOTE: No-op.
  154. }
  155. // 6. Else,
  156. else {
  157. // a. Assert: sign is not undefined.
  158. VERIFY(sign_part.has_value());
  159. // b. Set hours to ! ToIntegerOrInfinity(hours).
  160. i32 hours = Value(js_string(vm, *hours_part)).to_integer_or_infinity(global_object);
  161. i32 sign;
  162. // c. If sign is the code unit 0x002D (HYPHEN-MINUS) or the code unit 0x2212 (MINUS SIGN), then
  163. if (sign_part->is_one_of("-", "\u2212")) {
  164. // i. Set sign to −1.
  165. sign = -1;
  166. }
  167. // d. Else,
  168. else {
  169. // i. Set sign to 1.
  170. sign = 1;
  171. }
  172. // e. Set minutes to ! ToIntegerOrInfinity(minutes).
  173. i32 minutes = Value(js_string(vm, minutes_part.value_or(""sv))).to_integer_or_infinity(global_object);
  174. // f. Set seconds to ! ToIntegerOrInfinity(seconds).
  175. i32 seconds = Value(js_string(vm, seconds_part.value_or(""sv))).to_integer_or_infinity(global_object);
  176. i32 nanoseconds;
  177. // g. If fraction is not undefined, then
  178. if (fraction_part.has_value()) {
  179. // i. Set fraction to the string-concatenation of the previous value of fraction and the string "000000000".
  180. auto fraction = String::formatted("{}000000000", *fraction_part);
  181. // ii. Let nanoseconds be the String value equal to the substring of fraction from 0 to 9.
  182. // iii. Set nanoseconds to ! ToIntegerOrInfinity(nanoseconds).
  183. nanoseconds = Value(js_string(vm, fraction.substring(0, 9))).to_integer_or_infinity(global_object);
  184. }
  185. // h. Else,
  186. else {
  187. // i. Let nanoseconds be 0.
  188. nanoseconds = 0;
  189. }
  190. // i. Let offsetNanoseconds be sign × (((hours × 60 + minutes) × 60 + seconds) × 10^9 + nanoseconds).
  191. auto offset_nanoseconds = sign * (((hours * 60 + minutes) * 60 + seconds) * 1000000000 + nanoseconds);
  192. // j. Let offsetString be ! FormatTimeZoneOffsetString(offsetNanoseconds).
  193. offset = format_time_zone_offset_string(offset_nanoseconds);
  194. }
  195. Optional<String> name;
  196. // 7. If name is not undefined, then
  197. if (name_part.has_value()) {
  198. // a. If ! IsValidTimeZoneName(name) is false, throw a RangeError exception.
  199. if (!is_valid_time_zone_name(*name_part)) {
  200. vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidTimeZoneName);
  201. return {};
  202. }
  203. // b. Set name to ! CanonicalizeTimeZoneName(name).
  204. name = canonicalize_time_zone_name(*name_part);
  205. }
  206. // 8. Return the new Record: { [[Z]]: undefined, [[OffsetString]]: offsetString, [[Name]]: name }.
  207. return TemporalTimeZone { .z = false, .offset = offset, .name = name };
  208. }
  209. }