PlainDate.cpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /*
  2. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  3. * Copyright (c) 2021-2023, Linus Groh <linusg@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/Checked.h>
  10. #include <LibJS/Runtime/Temporal/Calendar.h>
  11. #include <LibJS/Runtime/Temporal/PlainDate.h>
  12. #include <LibJS/Runtime/Temporal/PlainDateTime.h>
  13. #include <LibJS/Runtime/Temporal/PlainTime.h>
  14. namespace JS::Temporal {
  15. // 3.5.2 CreateISODateRecord ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-create-iso-date-record
  16. ISODate create_iso_date_record(double year, double month, double day)
  17. {
  18. // 1. Assert: IsValidISODate(year, month, day) is true.
  19. VERIFY(is_valid_iso_date(year, month, day));
  20. // 2. Return ISO Date Record { [[Year]]: year, [[Month]]: month, [[Day]]: day }.
  21. return { .year = static_cast<i32>(year), .month = static_cast<u8>(month), .day = static_cast<u8>(day) };
  22. }
  23. // 3.5.6 RegulateISODate ( year, month, day, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulateisodate
  24. ThrowCompletionOr<ISODate> regulate_iso_date(VM& vm, double year, double month, double day, Overflow overflow)
  25. {
  26. switch (overflow) {
  27. // 1. If overflow is CONSTRAIN, then
  28. case Overflow::Constrain:
  29. // a. Set month to the result of clamping month between 1 and 12.
  30. month = clamp(month, 1, 12);
  31. // b. Let daysInMonth be ISODaysInMonth(year, month).
  32. // c. Set day to the result of clamping day between 1 and daysInMonth.
  33. day = clamp(day, 1, iso_days_in_month(year, month));
  34. // AD-HOC: We further clamp the year to the range allowed by ISOYearMonthWithinLimits, to ensure we do not
  35. // overflow when we store the year as an integer.
  36. year = clamp(year, -271821, 275760);
  37. break;
  38. // 2. Else,
  39. case Overflow::Reject:
  40. // a. Assert: overflow is REJECT.
  41. // b. If IsValidISODate(year, month, day) is false, throw a RangeError exception.
  42. if (!is_valid_iso_date(year, month, day))
  43. return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidISODate);
  44. break;
  45. }
  46. // 3. Return CreateISODateRecord(year, month, day).
  47. return create_iso_date_record(year, month, day);
  48. }
  49. // 3.5.7 IsValidISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidisodate
  50. bool is_valid_iso_date(double year, double month, double day)
  51. {
  52. // AD-HOC: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
  53. // This does not change the exposed behavior as the call to CreateISODateRecord will immediately check that
  54. // these values are valid ISO values (years: [-271821, 275760], months: [1, 12], days: [1, 31]), all of
  55. // which are subsets of this check.
  56. if (!AK::is_within_range<i32>(year) || !AK::is_within_range<u8>(month) || !AK::is_within_range<u8>(day))
  57. return false;
  58. // 1. If month < 1 or month > 12, then
  59. if (month < 1 || month > 12) {
  60. // a. Return false.
  61. return false;
  62. }
  63. // 2. Let daysInMonth be ISODaysInMonth(year, month).
  64. auto days_in_month = iso_days_in_month(year, month);
  65. // 3. If day < 1 or day > daysInMonth, then
  66. if (day < 1 || day > days_in_month) {
  67. // a. Return false.
  68. return false;
  69. }
  70. // 4. Return true.
  71. return true;
  72. }
  73. // 3.5.11 ISODateWithinLimits ( isoDate ), https://tc39.es/proposal-temporal/#sec-temporal-isodatewithinlimits
  74. bool iso_date_within_limits(ISODate iso_date)
  75. {
  76. // 1. Let isoDateTime be CombineISODateAndTimeRecord(isoDate, NoonTimeRecord()).
  77. auto iso_date_time = combine_iso_date_and_time_record(iso_date, noon_time_record());
  78. // 2. Return ISODateTimeWithinLimits(isoDateTime).
  79. return iso_date_time_within_limits(iso_date_time);
  80. }
  81. }