Instant.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /*
  2. * Copyright (c) 2021-2022, 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/TypeCasts.h>
  8. #include <AK/Variant.h>
  9. #include <LibCrypto/BigInt/SignedBigInteger.h>
  10. #include <LibJS/Runtime/AbstractOperations.h>
  11. #include <LibJS/Runtime/Completion.h>
  12. #include <LibJS/Runtime/Date.h>
  13. #include <LibJS/Runtime/GlobalObject.h>
  14. #include <LibJS/Runtime/Temporal/AbstractOperations.h>
  15. #include <LibJS/Runtime/Temporal/Calendar.h>
  16. #include <LibJS/Runtime/Temporal/Instant.h>
  17. #include <LibJS/Runtime/Temporal/InstantConstructor.h>
  18. #include <LibJS/Runtime/Temporal/PlainDateTime.h>
  19. #include <LibJS/Runtime/Temporal/TimeZone.h>
  20. #include <LibJS/Runtime/Temporal/ZonedDateTime.h>
  21. namespace JS::Temporal {
  22. // 8 Temporal.Instant Objects, https://tc39.es/proposal-temporal/#sec-temporal-instant-objects
  23. Instant::Instant(BigInt const& nanoseconds, Object& prototype)
  24. : Object(prototype)
  25. , m_nanoseconds(nanoseconds)
  26. {
  27. }
  28. void Instant::visit_edges(Cell::Visitor& visitor)
  29. {
  30. Base::visit_edges(visitor);
  31. visitor.visit(&m_nanoseconds);
  32. }
  33. // 8.5.1 IsValidEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds
  34. bool is_valid_epoch_nanoseconds(BigInt const& epoch_nanoseconds)
  35. {
  36. return is_valid_epoch_nanoseconds(epoch_nanoseconds.big_integer());
  37. }
  38. // 8.5.1 IsValidEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds
  39. bool is_valid_epoch_nanoseconds(Crypto::SignedBigInteger const& epoch_nanoseconds)
  40. {
  41. // 1. Assert: Type(epochNanoseconds) is BigInt.
  42. // 2. If ℝ(epochNanoseconds) < nsMinInstant or ℝ(epochNanoseconds) > nsMaxInstant, then
  43. if (epoch_nanoseconds < ns_min_instant || epoch_nanoseconds > ns_max_instant) {
  44. // a. Return false.
  45. return false;
  46. }
  47. // 3. Return true.
  48. return true;
  49. }
  50. // 8.5.2 CreateTemporalInstant ( epochNanoseconds [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalinstant
  51. ThrowCompletionOr<Instant*> create_temporal_instant(VM& vm, BigInt const& epoch_nanoseconds, FunctionObject const* new_target)
  52. {
  53. auto& realm = *vm.current_realm();
  54. // 1. Assert: Type(epochNanoseconds) is BigInt.
  55. // 2. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true.
  56. VERIFY(is_valid_epoch_nanoseconds(epoch_nanoseconds));
  57. // 3. If newTarget is not present, set newTarget to %Temporal.Instant%.
  58. if (!new_target)
  59. new_target = realm.intrinsics().temporal_instant_constructor();
  60. // 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Instant.prototype%", « [[InitializedTemporalInstant]], [[Nanoseconds]] »).
  61. // 5. Set object.[[Nanoseconds]] to epochNanoseconds.
  62. auto* object = TRY(ordinary_create_from_constructor<Instant>(vm, *new_target, &Intrinsics::temporal_instant_prototype, epoch_nanoseconds));
  63. // 6. Return object.
  64. return object;
  65. }
  66. // 8.5.3 ToTemporalInstant ( item ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalinstant
  67. ThrowCompletionOr<Instant*> to_temporal_instant(VM& vm, Value item)
  68. {
  69. // 1. If Type(item) is Object, then
  70. if (item.is_object()) {
  71. // a. If item has an [[InitializedTemporalInstant]] internal slot, then
  72. if (is<Instant>(item.as_object())) {
  73. // i. Return item.
  74. return &static_cast<Instant&>(item.as_object());
  75. }
  76. // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
  77. if (is<ZonedDateTime>(item.as_object())) {
  78. auto& zoned_date_time = static_cast<ZonedDateTime&>(item.as_object());
  79. // i. Return ! CreateTemporalInstant(item.[[Nanoseconds]]).
  80. return create_temporal_instant(vm, zoned_date_time.nanoseconds());
  81. }
  82. }
  83. // 2. Let string be ? ToString(item).
  84. auto string = TRY(item.to_string(vm));
  85. // 3. Let epochNanoseconds be ? ParseTemporalInstant(string).
  86. auto* epoch_nanoseconds = TRY(parse_temporal_instant(vm, string));
  87. // 4. Return ! CreateTemporalInstant(ℤ(epochNanoseconds)).
  88. return create_temporal_instant(vm, *epoch_nanoseconds);
  89. }
  90. // 8.5.4 ParseTemporalInstant ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalinstant
  91. ThrowCompletionOr<BigInt*> parse_temporal_instant(VM& vm, DeprecatedString const& iso_string)
  92. {
  93. // 1. Assert: Type(isoString) is String.
  94. // 2. Let result be ? ParseTemporalInstantString(isoString).
  95. auto result = TRY(parse_temporal_instant_string(vm, iso_string));
  96. // 3. Let offsetString be result.[[TimeZoneOffsetString]].
  97. auto& offset_string = result.time_zone_offset;
  98. // 4. Assert: offsetString is not undefined.
  99. VERIFY(offset_string.has_value());
  100. // 5. Let utc be GetUTCEpochNanoseconds(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
  101. auto utc = get_utc_epoch_nanoseconds(result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond);
  102. // 6. If IsTimeZoneOffsetString(offsetString) is false, throw a RangeError exception.
  103. if (!is_time_zone_offset_string(*offset_string))
  104. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidTimeZoneName, *offset_string);
  105. // 7. Let offsetNanoseconds be ParseTimeZoneOffsetString(offsetString).
  106. auto offset_nanoseconds = parse_time_zone_offset_string(*offset_string);
  107. // 7. Let result be utc - ℤ(offsetNanoseconds).
  108. auto result_ns = utc.minus(Crypto::SignedBigInteger { offset_nanoseconds });
  109. // 8. If ! IsValidEpochNanoseconds(result) is false, then
  110. if (!is_valid_epoch_nanoseconds(result_ns)) {
  111. // a. Throw a RangeError exception.
  112. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
  113. }
  114. // 9. Return result.
  115. return BigInt::create(vm, move(result_ns)).ptr();
  116. }
  117. // 8.5.5 CompareEpochNanoseconds ( epochNanosecondsOne, epochNanosecondsTwo ), https://tc39.es/proposal-temporal/#sec-temporal-compareepochnanoseconds
  118. i32 compare_epoch_nanoseconds(BigInt const& epoch_nanoseconds_one, BigInt const& epoch_nanoseconds_two)
  119. {
  120. // 1. If epochNanosecondsOne > epochNanosecondsTwo, return 1.
  121. if (epoch_nanoseconds_one.big_integer() > epoch_nanoseconds_two.big_integer())
  122. return 1;
  123. // 2. If epochNanosecondsOne < epochNanosecondsTwo, return -1.
  124. if (epoch_nanoseconds_one.big_integer() < epoch_nanoseconds_two.big_integer())
  125. return -1;
  126. // 3. Return 0.
  127. return 0;
  128. }
  129. // 8.5.6 AddInstant ( epochNanoseconds, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-addinstant
  130. ThrowCompletionOr<BigInt*> add_instant(VM& vm, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
  131. {
  132. VERIFY(hours == trunc(hours) && minutes == trunc(minutes) && seconds == trunc(seconds) && milliseconds == trunc(milliseconds) && microseconds == trunc(microseconds) && nanoseconds == trunc(nanoseconds));
  133. // 1. Let result be epochNanoseconds + ℤ(nanoseconds) + ℤ(microseconds) × 1000ℤ + ℤ(milliseconds) × 10^6ℤ + ℤ(seconds) × 10^9ℤ + ℤ(minutes) × 60ℤ × 10^9ℤ + ℤ(hours) × 3600ℤ × 10^9ℤ.
  134. auto result = BigInt::create(vm,
  135. epoch_nanoseconds.big_integer()
  136. .plus(Crypto::SignedBigInteger { nanoseconds })
  137. .plus(Crypto::SignedBigInteger { microseconds }.multiplied_by(Crypto::SignedBigInteger { 1'000 }))
  138. .plus(Crypto::SignedBigInteger { milliseconds }.multiplied_by(Crypto::SignedBigInteger { 1'000'000 }))
  139. .plus(Crypto::SignedBigInteger { seconds }.multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
  140. .plus(Crypto::SignedBigInteger { minutes }.multiplied_by(Crypto::SignedBigInteger { 60 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
  141. .plus(Crypto::SignedBigInteger { hours }.multiplied_by(Crypto::SignedBigInteger { 3600 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 })));
  142. // 2. If ! IsValidEpochNanoseconds(result) is false, throw a RangeError exception.
  143. if (!is_valid_epoch_nanoseconds(*result))
  144. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
  145. // 3. Return result.
  146. return result.ptr();
  147. }
  148. // 8.5.7 DifferenceInstant ( ns1, ns2, roundingIncrement, smallestUnit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-differenceinstant
  149. BigInt* difference_instant(VM& vm, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView rounding_mode)
  150. {
  151. // 1. Assert: Type(ns1) is BigInt.
  152. // 2. Assert: Type(ns2) is BigInt.
  153. // 3. Return ! RoundTemporalInstant(ns2 - ns1, roundingIncrement, smallestUnit, roundingMode).
  154. return round_temporal_instant(vm, BigInt::create(vm, nanoseconds2.big_integer().minus(nanoseconds1.big_integer())), rounding_increment, smallest_unit, rounding_mode);
  155. }
  156. // 8.5.8 RoundTemporalInstant ( ns, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundtemporalinstant
  157. BigInt* round_temporal_instant(VM& vm, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode)
  158. {
  159. // 1. Assert: Type(ns) is BigInt.
  160. u64 increment_nanoseconds;
  161. // 2. If unit is "hour", then
  162. if (unit == "hour"sv) {
  163. // a. Let incrementNs be increment × 3.6 × 10^12.
  164. increment_nanoseconds = increment * 3600000000000;
  165. }
  166. // 3. Else if unit is "minute", then
  167. else if (unit == "minute"sv) {
  168. // a. Let incrementNs be increment × 6 × 10^10.
  169. increment_nanoseconds = increment * 60000000000;
  170. }
  171. // 4. Else if unit is "second", then
  172. else if (unit == "second"sv) {
  173. // a. Let incrementNs be increment × 10^9.
  174. increment_nanoseconds = increment * 1000000000;
  175. }
  176. // 5. Else if unit is "millisecond", then
  177. else if (unit == "millisecond"sv) {
  178. // a. Let incrementNs be increment × 10^6.
  179. increment_nanoseconds = increment * 1000000;
  180. }
  181. // 6. Else if unit is "microsecond", then
  182. else if (unit == "microsecond"sv) {
  183. // a. Let incrementNs be increment × 10^3.
  184. increment_nanoseconds = increment * 1000;
  185. }
  186. // 7. Else,
  187. else {
  188. // a. Assert: unit is "nanosecond".
  189. VERIFY(unit == "nanosecond"sv);
  190. // b. Let incrementNs be increment.
  191. increment_nanoseconds = increment;
  192. }
  193. // 8. Return RoundNumberToIncrementAsIfPositive(ℝ(ns), incrementNs, roundingMode).
  194. return BigInt::create(vm, round_number_to_increment_as_if_positive(nanoseconds.big_integer(), increment_nanoseconds, rounding_mode));
  195. }
  196. // 8.5.9 TemporalInstantToString ( instant, timeZone, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporalinstanttostring
  197. ThrowCompletionOr<DeprecatedString> temporal_instant_to_string(VM& vm, Instant& instant, Value time_zone, Variant<StringView, u8> const& precision)
  198. {
  199. // 1. Assert: Type(instant) is Object.
  200. // 2. Assert: instant has an [[InitializedTemporalInstant]] internal slot.
  201. // 3. Let outputTimeZone be timeZone.
  202. auto output_time_zone = time_zone;
  203. // 4. If outputTimeZone is undefined, then
  204. if (output_time_zone.is_undefined()) {
  205. // a. Set outputTimeZone to ! CreateTemporalTimeZone("UTC").
  206. output_time_zone = MUST(create_temporal_time_zone(vm, "UTC"sv));
  207. }
  208. // 5. Let isoCalendar be ! GetISO8601Calendar().
  209. auto* iso_calendar = get_iso8601_calendar(vm);
  210. // 6. Let dateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(outputTimeZone, instant, isoCalendar).
  211. auto* date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, output_time_zone, instant, *iso_calendar));
  212. // 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").
  213. auto date_time_string = TRY(temporal_date_time_to_string(vm, 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(), nullptr, precision, "never"sv));
  214. DeprecatedString time_zone_string;
  215. // 8. If timeZone is undefined, then
  216. if (time_zone.is_undefined()) {
  217. // a. Let timeZoneString be "Z".
  218. time_zone_string = "Z"sv;
  219. }
  220. // 9. Else,
  221. else {
  222. // a. Let offsetNs be ? GetOffsetNanosecondsFor(timeZone, instant).
  223. auto offset_ns = TRY(get_offset_nanoseconds_for(vm, time_zone, instant));
  224. // b. Let timeZoneString be ! FormatISOTimeZoneOffsetString(offsetNs).
  225. time_zone_string = format_iso_time_zone_offset_string(offset_ns);
  226. }
  227. // 10. Return the string-concatenation of dateTimeString and timeZoneString.
  228. return DeprecatedString::formatted("{}{}", date_time_string, time_zone_string);
  229. }
  230. // 8.5.10 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
  231. ThrowCompletionOr<Duration*> difference_temporal_instant(VM& vm, DifferenceOperation operation, Instant const& instant, Value other_value, Value options_value)
  232. {
  233. // 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
  234. i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
  235. // 2. Set other to ? ToTemporalInstant(other).
  236. auto* other = TRY(to_temporal_instant(vm, other_value));
  237. // 3. Let settings be ? GetDifferenceSettings(operation, options, time, « », "nanosecond", "second").
  238. auto settings = TRY(get_difference_settings(vm, operation, options_value, UnitGroup::Time, {}, { "nanosecond"sv }, "second"sv));
  239. // 4. Let roundedNs be ! DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
  240. auto* rounded_ns = difference_instant(vm, instant.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.rounding_mode);
  241. // 5. Assert: The following steps cannot fail due to overflow in the Number domain because abs(roundedNs) ≤ 2 × nsMaxInstant.
  242. // 6. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs, settings.[[LargestUnit]]).
  243. auto result = MUST(balance_duration(vm, 0, 0, 0, 0, 0, 0, rounded_ns->big_integer(), settings.largest_unit));
  244. // 7. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
  245. return MUST(create_temporal_duration(vm, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
  246. }
  247. // 8.5.11 AddDurationToOrSubtractDurationFromInstant ( operation, instant, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfrominstant
  248. ThrowCompletionOr<Instant*> add_duration_to_or_subtract_duration_from_instant(VM& vm, ArithmeticOperation operation, Instant const& instant, Value temporal_duration_like)
  249. {
  250. // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
  251. i8 sign = operation == ArithmeticOperation::Subtract ? -1 : 1;
  252. // 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
  253. auto duration = TRY(to_temporal_duration_record(vm, temporal_duration_like));
  254. // 3. If duration.[[Days]] is not 0, throw a RangeError exception.
  255. if (duration.days != 0)
  256. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "days", duration.days);
  257. // 4. If duration.[[Months]] is not 0, throw a RangeError exception.
  258. if (duration.months != 0)
  259. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "months", duration.months);
  260. // 5. If duration.[[Weeks]] is not 0, throw a RangeError exception.
  261. if (duration.weeks != 0)
  262. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "weeks", duration.weeks);
  263. // 6. If duration.[[Years]] is not 0, throw a RangeError exception.
  264. if (duration.years != 0)
  265. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "years", duration.years);
  266. // 7. Let ns be ? AddInstant(instant.[[Nanoseconds]], sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]]).
  267. auto* ns = TRY(add_instant(vm, instant.nanoseconds(), sign * duration.hours, sign * duration.minutes, sign * duration.seconds, sign * duration.milliseconds, sign * duration.microseconds, sign * duration.nanoseconds));
  268. // 8. Return ! CreateTemporalInstant(ns).
  269. return MUST(create_temporal_instant(vm, *ns));
  270. }
  271. }