Duration.cpp 4.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. /*
  2. * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  3. * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
  4. * Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
  5. * Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include <AK/Math.h>
  10. #include <AK/NumericLimits.h>
  11. #include <LibCrypto/BigInt/SignedBigInteger.h>
  12. #include <LibJS/Runtime/Temporal/Duration.h>
  13. #include <LibJS/Runtime/Value.h>
  14. #include <math.h>
  15. namespace JS::Temporal {
  16. // 7.5.16 IsValidDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidduration
  17. bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
  18. {
  19. // 1. Let sign be 0.
  20. auto sign = 0;
  21. // 2. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
  22. for (auto value : { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }) {
  23. // a. If 𝔽(v) is not finite, return false.
  24. if (!isfinite(value))
  25. return false;
  26. // b. If v < 0, then
  27. if (value < 0) {
  28. // i. If sign > 0, return false.
  29. if (sign > 0)
  30. return false;
  31. // ii. Set sign to -1.
  32. sign = -1;
  33. }
  34. // c. Else if v > 0, then
  35. else if (value > 0) {
  36. // i. If sign < 0, return false.
  37. if (sign < 0)
  38. return false;
  39. // ii. Set sign to 1.
  40. sign = 1;
  41. }
  42. }
  43. // 3. If abs(years) ≥ 2**32, return false.
  44. if (AK::fabs(years) > NumericLimits<u32>::max())
  45. return false;
  46. // 4. If abs(months) ≥ 2**32, return false.
  47. if (AK::fabs(months) > NumericLimits<u32>::max())
  48. return false;
  49. // 5. If abs(weeks) ≥ 2**32, return false.
  50. if (AK::fabs(weeks) > NumericLimits<u32>::max())
  51. return false;
  52. // 6. Let totalFractionalSeconds be days × 86,400 + hours × 3600 + minutes × 60 + seconds + ℝ(𝔽(milliseconds)) × 10**-3 + ℝ(𝔽(microseconds)) × 10**-6 + ℝ(𝔽(nanoseconds)) × 10**-9.
  53. // 7. NOTE: The above step cannot be implemented directly using floating-point arithmetic. Multiplying by 10**-3,
  54. // 10**-6, and 10**-9 respectively may be imprecise when milliseconds, microseconds, or nanoseconds is an
  55. // unsafe integer. This multiplication can be implemented in C++ with an implementation of std::remquo()
  56. // with sufficient bits in the quotient. String manipulation will also give an exact result, since the
  57. // multiplication is by a power of 10.
  58. static Crypto::SignedBigInteger days_to_nanoseconds { 8.64e13 };
  59. static Crypto::SignedBigInteger hours_to_nanoseconds { 3.6e12 };
  60. static Crypto::SignedBigInteger minutes_to_nanoseconds { 6e10 };
  61. static Crypto::SignedBigInteger seconds_to_nanoseconds { 1e9 };
  62. static Crypto::SignedBigInteger milliseconds_to_nanoseconds { 1e6 };
  63. static Crypto::SignedBigInteger microseconds_to_nanoseconds { 1e3 };
  64. auto normalized_nanoseconds = Crypto::SignedBigInteger { days }.multiplied_by(days_to_nanoseconds);
  65. normalized_nanoseconds = normalized_nanoseconds.plus(Crypto::SignedBigInteger { hours }.multiplied_by(hours_to_nanoseconds));
  66. normalized_nanoseconds = normalized_nanoseconds.plus(Crypto::SignedBigInteger { minutes }.multiplied_by(minutes_to_nanoseconds));
  67. normalized_nanoseconds = normalized_nanoseconds.plus(Crypto::SignedBigInteger { seconds }.multiplied_by(seconds_to_nanoseconds));
  68. normalized_nanoseconds = normalized_nanoseconds.plus(Crypto::SignedBigInteger { milliseconds }.multiplied_by(milliseconds_to_nanoseconds));
  69. normalized_nanoseconds = normalized_nanoseconds.plus(Crypto::SignedBigInteger { microseconds }.multiplied_by(microseconds_to_nanoseconds));
  70. normalized_nanoseconds = normalized_nanoseconds.plus(Crypto::SignedBigInteger { nanoseconds });
  71. // 8. If abs(normalizedSeconds) ≥ 2**53, return false.
  72. static auto maximum_time = Crypto::SignedBigInteger { MAX_ARRAY_LIKE_INDEX }.plus(1_bigint).multiplied_by(seconds_to_nanoseconds);
  73. if (normalized_nanoseconds.is_negative())
  74. normalized_nanoseconds.negate();
  75. if (normalized_nanoseconds >= maximum_time)
  76. return false;
  77. // 9. Return true.
  78. return true;
  79. }
  80. }