Instant.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
  3. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Variant.h>
  8. #include <LibCrypto/BigInt/SignedBigInteger.h>
  9. #include <LibJS/Runtime/AbstractOperations.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/Instant.h>
  14. #include <LibJS/Runtime/Temporal/InstantConstructor.h>
  15. #include <LibJS/Runtime/Temporal/PlainDateTime.h>
  16. #include <LibJS/Runtime/Temporal/TimeZone.h>
  17. #include <LibJS/Runtime/Temporal/ZonedDateTime.h>
  18. namespace JS::Temporal {
  19. // 8 Temporal.Instant Objects, https://tc39.es/proposal-temporal/#sec-temporal-instant-objects
  20. Instant::Instant(BigInt& nanoseconds, Object& prototype)
  21. : Object(prototype)
  22. , m_nanoseconds(nanoseconds)
  23. {
  24. }
  25. void Instant::visit_edges(Cell::Visitor& visitor)
  26. {
  27. Base::visit_edges(visitor);
  28. visitor.visit(&m_nanoseconds);
  29. }
  30. // 8.5.1 IsValidEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds
  31. bool is_valid_epoch_nanoseconds(BigInt const& epoch_nanoseconds)
  32. {
  33. // 1. Assert: Type(epochNanoseconds) is BigInt.
  34. // 2. If epochNanoseconds < −86400ℤ × 10^17ℤ or epochNanoseconds > 86400ℤ × 10^17ℤ, then
  35. if (epoch_nanoseconds.big_integer() < INSTANT_NANOSECONDS_MIN || epoch_nanoseconds.big_integer() > INSTANT_NANOSECONDS_MAX) {
  36. // a. Return false.
  37. return false;
  38. }
  39. // 3. Return true.
  40. return true;
  41. }
  42. // 8.5.2 CreateTemporalInstant ( epochNanoseconds [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalinstant
  43. Instant* create_temporal_instant(GlobalObject& global_object, BigInt& epoch_nanoseconds, FunctionObject const* new_target)
  44. {
  45. auto& vm = global_object.vm();
  46. // 1. Assert: Type(epochNanoseconds) is BigInt.
  47. // 2. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true.
  48. VERIFY(is_valid_epoch_nanoseconds(epoch_nanoseconds));
  49. // 3. If newTarget is not present, set it to %Temporal.Instant%.
  50. if (!new_target)
  51. new_target = global_object.temporal_instant_constructor();
  52. // 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Instant.prototype%", « [[InitializedTemporalInstant]], [[Nanoseconds]] »).
  53. // 5. Set object.[[Nanoseconds]] to epochNanoseconds.
  54. auto* object = ordinary_create_from_constructor<Instant>(global_object, *new_target, &GlobalObject::temporal_instant_prototype, epoch_nanoseconds);
  55. if (vm.exception())
  56. return {};
  57. // 6. Return object.
  58. return object;
  59. }
  60. // 8.5.3 ToTemporalInstant ( item ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalinstant
  61. Instant* to_temporal_instant(GlobalObject& global_object, Value item)
  62. {
  63. auto& vm = global_object.vm();
  64. // 1. If Type(item) is Object, then
  65. if (item.is_object()) {
  66. // a. If item has an [[InitializedTemporalInstant]] internal slot, then
  67. if (is<Instant>(item.as_object())) {
  68. // i. Return item.
  69. return &static_cast<Instant&>(item.as_object());
  70. }
  71. // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
  72. if (is<ZonedDateTime>(item.as_object())) {
  73. auto& zoned_date_time = static_cast<ZonedDateTime&>(item.as_object());
  74. // i. Return ! CreateTemporalInstant(item.[[Nanoseconds]]).
  75. return create_temporal_instant(global_object, zoned_date_time.nanoseconds());
  76. }
  77. }
  78. // 2. Let string be ? ToString(item).
  79. auto string = item.to_string(global_object);
  80. if (vm.exception())
  81. return {};
  82. // 3. Let epochNanoseconds be ? ParseTemporalInstant(string).
  83. auto* epoch_nanoseconds = parse_temporal_instant(global_object, string);
  84. if (vm.exception())
  85. return {};
  86. // 4. Return ! CreateTemporalInstant(ℤ(epochNanoseconds)).
  87. return create_temporal_instant(global_object, *epoch_nanoseconds);
  88. }
  89. // 8.5.4 ParseTemporalInstant ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalinstant
  90. BigInt* parse_temporal_instant(GlobalObject& global_object, String const& iso_string)
  91. {
  92. auto& vm = global_object.vm();
  93. // 1. Assert: Type(isoString) is String.
  94. // 2. Let result be ? ParseTemporalInstantString(isoString).
  95. auto result = parse_temporal_instant_string(global_object, iso_string);
  96. if (vm.exception())
  97. return {};
  98. // 3. Let offsetString be result.[[TimeZoneOffsetString]].
  99. auto& offset_string = result->time_zone_offset;
  100. // 4. Assert: offsetString is not undefined.
  101. VERIFY(offset_string.has_value());
  102. // 5. Let utc be ? GetEpochFromISOParts(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
  103. auto* utc = get_epoch_from_iso_parts(global_object, result->year, result->month, result->day, result->hour, result->minute, result->second, result->millisecond, result->microsecond, result->nanosecond);
  104. if (vm.exception())
  105. return {};
  106. // 6. If utc < −8.64 × 10^21 or utc > 8.64 × 10^21, then
  107. if (utc->big_integer() < INSTANT_NANOSECONDS_MIN || utc->big_integer() > INSTANT_NANOSECONDS_MAX) {
  108. // a. Throw a RangeError exception.
  109. vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidEpochNanoseconds);
  110. return {};
  111. }
  112. // 7. Let offsetNanoseconds be ? ParseTimeZoneOffsetString(offsetString).
  113. auto offset_nanoseconds = parse_time_zone_offset_string(global_object, *offset_string);
  114. if (vm.exception())
  115. return {};
  116. // 8. Return utc − offsetNanoseconds.
  117. return js_bigint(vm, utc->big_integer().minus(Crypto::SignedBigInteger::create_from(offset_nanoseconds)));
  118. }
  119. // 8.5.5 CompareEpochNanoseconds ( epochNanosecondsOne, epochNanosecondsTwo ), https://tc39.es/proposal-temporal/#sec-temporal-compareepochnanoseconds
  120. i32 compare_epoch_nanoseconds(BigInt const& epoch_nanoseconds_one, BigInt const& epoch_nanoseconds_two)
  121. {
  122. // 1. If epochNanosecondsOne > epochNanosecondsTwo, return 1.
  123. if (epoch_nanoseconds_one.big_integer() > epoch_nanoseconds_two.big_integer())
  124. return 1;
  125. // 2. If epochNanosecondsOne < epochNanosecondsTwo, return -1.
  126. if (epoch_nanoseconds_one.big_integer() < epoch_nanoseconds_two.big_integer())
  127. return -1;
  128. // 3. Return 0.
  129. return 0;
  130. }
  131. // 8.5.6 AddInstant ( epochNanoseconds, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-addinstant
  132. BigInt* add_instant(GlobalObject& global_object, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
  133. {
  134. auto& vm = global_object.vm();
  135. // 1. Assert: hours, minutes, seconds, milliseconds, microseconds, and nanoseconds are integer Number values.
  136. VERIFY(hours == trunc(hours) && minutes == trunc(minutes) && seconds == trunc(seconds) && milliseconds == trunc(milliseconds) && microseconds == trunc(microseconds) && nanoseconds == trunc(nanoseconds));
  137. // 2. Let result be epochNanoseconds + ℤ(nanoseconds) + ℤ(microseconds) × 1000ℤ + ℤ(milliseconds) × 10^6ℤ + ℤ(seconds) × 10^9ℤ + ℤ(minutes) × 60ℤ × 10^9ℤ + ℤ(hours) × 3600ℤ × 10^9ℤ.
  138. // FIXME: Pretty sure i64's are not sufficient for the extreme cases.
  139. auto* result = js_bigint(vm,
  140. epoch_nanoseconds.big_integer()
  141. .plus(Crypto::SignedBigInteger::create_from((i64)nanoseconds))
  142. .plus(Crypto::SignedBigInteger::create_from((i64)microseconds).multiplied_by(Crypto::SignedBigInteger { 1'000 }))
  143. .plus(Crypto::SignedBigInteger::create_from((i64)milliseconds).multiplied_by(Crypto::SignedBigInteger { 1'000'000 }))
  144. .plus(Crypto::SignedBigInteger::create_from((i64)seconds).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
  145. .plus(Crypto::SignedBigInteger::create_from((i64)minutes).multiplied_by(Crypto::SignedBigInteger { 60 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
  146. .plus(Crypto::SignedBigInteger::create_from((i64)hours).multiplied_by(Crypto::SignedBigInteger { 3600 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 })));
  147. // If ! IsValidEpochNanoseconds(result) is false, throw a RangeError exception.
  148. if (!is_valid_epoch_nanoseconds(*result)) {
  149. vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidEpochNanoseconds);
  150. return {};
  151. }
  152. // 4. Return result.
  153. return result;
  154. }
  155. // 8.5.7 DifferenceInstant ( ns1, ns2, roundingIncrement, smallestUnit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-differenceinstant
  156. BigInt* difference_instant(GlobalObject& global_object, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView rounding_mode)
  157. {
  158. auto& vm = global_object.vm();
  159. // 1. Assert: Type(ns1) is BigInt.
  160. // 2. Assert: Type(ns2) is BigInt.
  161. // 3. Return ! RoundTemporalInstant(ns2 − ns1, roundingIncrement, smallestUnit, roundingMode).
  162. return round_temporal_instant(global_object, *js_bigint(vm, nanoseconds2.big_integer().minus(nanoseconds1.big_integer())), rounding_increment, smallest_unit, rounding_mode);
  163. }
  164. // 8.5.8 RoundTemporalInstant ( ns, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundtemporalinstant
  165. BigInt* round_temporal_instant(GlobalObject& global_object, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode)
  166. {
  167. // 1. Assert: Type(ns) is BigInt.
  168. u64 increment_nanoseconds;
  169. // 2. If unit is "hour", then
  170. if (unit == "hour"sv) {
  171. // a. Let incrementNs be increment × 3.6 × 10^12.
  172. increment_nanoseconds = increment * 3600000000000;
  173. }
  174. // 3. Else if unit is "minute", then
  175. else if (unit == "minute"sv) {
  176. // a. Let incrementNs be increment × 6 × 10^10.
  177. increment_nanoseconds = increment * 60000000000;
  178. }
  179. // 4. Else if unit is "second", then
  180. else if (unit == "second"sv) {
  181. // a. Let incrementNs be increment × 10^9.
  182. increment_nanoseconds = increment * 1000000000;
  183. }
  184. // 5. Else if unit is "millisecond", then
  185. else if (unit == "millisecond"sv) {
  186. // a. Let incrementNs be increment × 10^6.
  187. increment_nanoseconds = increment * 1000000;
  188. }
  189. // 6. Else if unit is "microsecond", then
  190. else if (unit == "microsecond"sv) {
  191. // a. Let incrementNs be increment × 10^3.
  192. increment_nanoseconds = increment * 1000;
  193. }
  194. // 7. Else,
  195. else {
  196. // a. Assert: unit is "nanosecond".
  197. VERIFY(unit == "nanosecond"sv);
  198. // b. Let incrementNs be increment.
  199. increment_nanoseconds = increment;
  200. }
  201. // 8. Return ! RoundNumberToIncrement(ℝ(ns), incrementNs, roundingMode).
  202. return round_number_to_increment(global_object, nanoseconds, increment_nanoseconds, rounding_mode);
  203. }
  204. // 8.5.9 TemporalInstantToString ( instant, timeZone, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporalinstanttostring
  205. Optional<String> temporal_instant_to_string(GlobalObject& global_object, Instant& instant, Value time_zone, Variant<StringView, u8> const& precision)
  206. {
  207. auto& vm = global_object.vm();
  208. // 1. Assert: Type(instant) is Object.
  209. // 2. Assert: instant has an [[InitializedTemporalInstant]] internal slot.
  210. // 3. Let outputTimeZone be timeZone.
  211. auto output_time_zone = time_zone;
  212. // 4. If outputTimeZone is undefined, then
  213. if (output_time_zone.is_undefined()) {
  214. // a. Set outputTimeZone to ? CreateTemporalTimeZone("UTC").
  215. output_time_zone = create_temporal_time_zone(global_object, "UTC"sv);
  216. // TODO: Can this really throw...?
  217. if (vm.exception())
  218. return {};
  219. }
  220. // 5. Let isoCalendar be ! GetISO8601Calendar().
  221. auto* iso_calendar = get_iso8601_calendar(global_object);
  222. // 6. Let dateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(outputTimeZone, instant, isoCalendar).
  223. auto* date_time = builtin_time_zone_get_plain_date_time_for(global_object, output_time_zone, instant, *iso_calendar);
  224. if (vm.exception())
  225. return {};
  226. // 7. Let dateTimeString be ? TemporalDateTimeToString(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], undefined, precision, "never").
  227. auto date_time_string = temporal_date_time_to_string(global_object, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), js_undefined(), precision, "never"sv);
  228. if (vm.exception())
  229. return {};
  230. Optional<String> time_zone_string;
  231. // 8. If timeZone is undefined, then
  232. if (time_zone.is_undefined()) {
  233. // a. Let timeZoneString be "Z".
  234. time_zone_string = "Z"sv;
  235. }
  236. // 9. Else,
  237. else {
  238. // a. Let timeZoneString be ? BuiltinTimeZoneGetOffsetStringFor(timeZone, instant).
  239. time_zone_string = builtin_time_zone_get_offset_string_for(global_object, time_zone, instant);
  240. if (vm.exception())
  241. return {};
  242. }
  243. // 10. Return the string-concatenation of dateTimeString and timeZoneString.
  244. return String::formatted("{}{}", *date_time_string, *time_zone_string);
  245. }
  246. }