escargot/src/runtime/TemporalPlainDateObject.cpp
Seonghyun Kim 7667784d79 Consider single era correctly in Temporal
Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
2025-12-15 18:43:33 +09:00

685 lines
28 KiB
C++

#if defined(ENABLE_TEMPORAL)
/*
* Copyright (c) 2025-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "Escargot.h"
#include "TemporalPlainDateObject.h"
#include "TemporalPlainTimeObject.h"
#include "TemporalPlainDateTimeObject.h"
#include "TemporalPlainYearMonthObject.h"
#include "TemporalPlainMonthDayObject.h"
#include "TemporalZonedDateTimeObject.h"
#include "TemporalDurationObject.h"
#include "intl/Intl.h"
#include "util/ISO8601.h"
#include "runtime/Context.h"
#include "runtime/GlobalObject.h"
namespace Escargot {
#define CHECK_ICU() \
if (U_FAILURE(status)) { \
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to get value from ICU calendar"); \
}
TemporalPlainDateObject::TemporalPlainDateObject(ExecutionState& state, Object* proto, ISO8601::PlainDate isoDate, Calendar calendar, bool checkBoundery)
: DerivedObject(state, proto)
, m_plainDate(new(PointerFreeGC) ISO8601::PlainDate(isoDate))
, m_calendarID(calendar)
{
if (checkBoundery && !ISO8601::isoDateTimeWithinLimits(isoDate.year(), isoDate.month(), isoDate.day())) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Out of range date");
}
m_icuCalendar = calendar.createICUCalendar(state);
UErrorCode status = U_ZERO_ERROR;
ucal_setMillis(m_icuCalendar, ISO8601::ExactTime::fromPlainDate(isoDate).floorEpochMilliseconds(), &status);
CHECK_ICU()
if (!calendar.isISO8601()) {
auto y = calendar.year(state, m_icuCalendar);
auto m = ucal_get(m_icuCalendar, UCAL_ORDINAL_MONTH, &status) + 1;
CHECK_ICU()
auto d = ucal_get(m_icuCalendar, UCAL_DAY_OF_MONTH, &status);
CHECK_ICU()
*m_plainDate = ISO8601::PlainDate(y, m, d);
}
addFinalizer([](PointerValue* obj, void* data) {
TemporalPlainDateObject* self = (TemporalPlainDateObject*)obj;
ucal_close(self->m_icuCalendar);
},
nullptr);
}
TemporalPlainDateObject::TemporalPlainDateObject(ExecutionState& state, Object* proto, std::pair<UCalendar*, Optional<ISO8601::PlainDate>> fieldResolveResult, Calendar calendar, bool checkBoundery)
: DerivedObject(state, proto)
, m_plainDate(new(PointerFreeGC) ISO8601::PlainDate())
, m_calendarID(calendar)
, m_icuCalendar(fieldResolveResult.first)
{
if (fieldResolveResult.second) {
*m_plainDate = fieldResolveResult.second.value();
} else {
UErrorCode status = U_ZERO_ERROR;
auto y = calendar.year(state, m_icuCalendar);
auto m = ucal_get(m_icuCalendar, UCAL_ORDINAL_MONTH, &status) + 1;
CHECK_ICU()
auto d = ucal_get(m_icuCalendar, UCAL_DAY_OF_MONTH, &status);
CHECK_ICU()
*m_plainDate = ISO8601::PlainDate(y, m, d);
}
if (checkBoundery) {
auto isoDate = computeISODate(state);
if (!ISO8601::isoDateTimeWithinLimits(isoDate.year(), isoDate.month(), isoDate.day())) {
ucal_close(m_icuCalendar);
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid date");
}
}
addFinalizer([](PointerValue* obj, void* data) {
TemporalPlainDateObject* self = (TemporalPlainDateObject*)obj;
ucal_close(self->m_icuCalendar);
},
nullptr);
}
TemporalPlainDateObject::TemporalPlainDateObject(ExecutionState& state, Object* proto, UCalendar* icuCalendar, Calendar calendar)
: DerivedObject(state, proto)
, m_plainDate(new(PointerFreeGC) ISO8601::PlainDate())
, m_calendarID(calendar)
, m_icuCalendar(icuCalendar)
{
UErrorCode status = U_ZERO_ERROR;
auto y = calendar.year(state, m_icuCalendar);
auto m = ucal_get(m_icuCalendar, UCAL_ORDINAL_MONTH, &status) + 1;
CHECK_ICU()
auto d = ucal_get(m_icuCalendar, UCAL_DAY_OF_MONTH, &status);
CHECK_ICU()
*m_plainDate = ISO8601::PlainDate(y, m, d);
addFinalizer([](PointerValue* obj, void* data) {
TemporalPlainDateObject* self = (TemporalPlainDateObject*)obj;
ucal_close(self->m_icuCalendar);
},
nullptr);
}
ISO8601::PlainDate TemporalPlainDateObject::computeISODate(ExecutionState& state)
{
if (!m_calendarID.isISO8601()) {
return Temporal::computeISODate(state, m_icuCalendar);
}
return plainDate();
}
String* TemporalPlainDateObject::temporalDateToString(ISO8601::PlainDate plainDate, Calendar calendar, TemporalShowCalendarNameOption showCalendar)
{
StringBuilder builder;
int32_t year = plainDate.year();
int32_t month = plainDate.month();
int32_t day = plainDate.day();
if (year > 9999 || year < 0) {
builder.appendChar(year < 0 ? '-' : '+');
auto s = pad('0', 6, std::to_string(std::abs(year)));
builder.appendString(String::fromASCII(s.data(), s.length()));
} else {
auto s = pad('0', 4, std::to_string(std::abs(year)));
builder.appendString(String::fromASCII(s.data(), s.length()));
}
builder.appendChar('-');
{
auto s = pad('0', 2, std::to_string(month));
builder.appendString(String::fromASCII(s.data(), s.length()));
}
builder.appendChar('-');
{
auto s = pad('0', 2, std::to_string(day));
builder.appendString(String::fromASCII(s.data(), s.length()));
}
Temporal::formatCalendarAnnotation(builder, calendar, showCalendar);
return builder.finalize();
}
// https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tostring
String* TemporalPlainDateObject::toString(ExecutionState& state, Value options)
{
// Let plainDate be the this value.
// Perform ? RequireInternalSlot(plainDate, [[InitializedTemporalDate]]).
// Let resolvedOptions be ? GetOptionsObject(options).
auto resolvedOptions = Intl::getOptionsObject(state, options);
// Let showCalendar be ? GetTemporalShowCalendarNameOption(resolvedOptions).
auto showCalendar = Temporal::getTemporalShowCalendarNameOption(state, resolvedOptions);
// Return TemporalDateToString(plainDate, showCalendar).
return temporalDateToString(computeISODate(state), m_calendarID, showCalendar);
}
// https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.equals
bool TemporalPlainDateObject::equals(ExecutionState& state, Value other)
{
auto otherDate = Temporal::toTemporalDate(state, other, Value());
int c = compareISODate(state, this, otherDate);
if (c == 0) {
if (calendarID() == otherDate->calendarID()) {
return true;
}
}
return false;
}
TemporalPlainDateObject* TemporalPlainDateObject::addDurationToDate(ExecutionState& state, AddDurationToDateOperation operation, Value temporalDurationLike, Value options)
{
// Let calendar be temporalDate.[[Calendar]].
// Let duration be ? ToTemporalDuration(temporalDurationLike).
ISO8601::Duration duration = Temporal::toTemporalDuration(state, temporalDurationLike)->duration();
// If operation is subtract, set duration to CreateNegatedTemporalDuration(duration).
if (operation == AddDurationToDateOperation::Subtract) {
duration = TemporalDurationObject::createNegatedTemporalDuration(duration);
}
// Let dateDuration be ToDateDurationRecordWithoutTime(duration).
auto dateDuration = TemporalDurationObject::toDateDurationRecordWithoutTime(state, duration);
// Let resolvedOptions be ? GetOptionsObject(options).
auto resolvedOptions = Intl::getOptionsObject(state, options);
// Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
auto overflow = Temporal::getTemporalOverflowOption(state, resolvedOptions);
// Let result be ? CalendarDateAdd(calendar, temporalDate.[[ISODate]], dateDuration, overflow).
auto result = Temporal::calendarDateAdd(state, calendarID(), computeISODate(state), m_icuCalendar, dateDuration, overflow);
// Return ! CreateTemporalDate(result, calendar).
return new TemporalPlainDateObject(state, state.context()->globalObject()->temporalPlainDatePrototype(), result.first, m_calendarID);
}
Value TemporalPlainDateGetter::era(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
if (calendarID.isEraRelated()) {
return calendarID.era(state, icuCalendar);
}
return Value();
}
Value TemporalPlainDateGetter::eraYear(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
if (calendarID.isEraRelated()) {
return Value(calendarID.eraYear(state, icuCalendar));
}
return Value();
}
Value TemporalPlainDateGetter::dayOfWeek(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
UErrorCode status = U_ZERO_ERROR;
auto s = ucal_get(icuCalendar, UCAL_DAY_OF_WEEK, &status);
switch (s) {
case UCAL_SUNDAY:
return Value(7);
case UCAL_MONDAY:
return Value(1);
case UCAL_TUESDAY:
return Value(2);
case UCAL_WEDNESDAY:
return Value(3);
case UCAL_THURSDAY:
return Value(4);
case UCAL_FRIDAY:
return Value(5);
case UCAL_SATURDAY:
return Value(6);
}
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to get value from ICU calendar");
return Value(0);
}
Value TemporalPlainDateGetter::dayOfYear(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
UErrorCode status = U_ZERO_ERROR;
auto s = ucal_get(icuCalendar, UCAL_DAY_OF_YEAR, &status);
CHECK_ICU()
return Value(s);
}
Value TemporalPlainDateGetter::weekOfYear(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
if (calendarID.isISO8601()) {
UErrorCode status = U_ZERO_ERROR;
auto s = ucal_get(icuCalendar, UCAL_WEEK_OF_YEAR, &status);
CHECK_ICU()
return Value(s);
}
return Value();
}
Value TemporalPlainDateGetter::yearOfWeek(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
if (calendarID.isISO8601()) {
UErrorCode status = U_ZERO_ERROR;
auto s = ucal_get(icuCalendar, UCAL_YEAR_WOY, &status);
CHECK_ICU()
return Value(s);
}
return Value();
}
Value TemporalPlainDateGetter::daysInWeek(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
UErrorCode status = U_ZERO_ERROR;
LocalResourcePointer<UCalendar> newCal(ucal_clone(icuCalendar, &status), [](UCalendar* r) {
ucal_close(r);
});
CHECK_ICU()
int count = 0;
for (int i = UCAL_SUNDAY; i <= UCAL_SATURDAY; i++) {
ucal_set(newCal.get(), UCAL_DAY_OF_MONTH, plainDate.day());
ucal_set(newCal.get(), UCAL_DAY_OF_WEEK, i);
if (ucal_get(newCal.get(), UCAL_DAY_OF_WEEK, &status) == i) {
count++;
}
}
return Value(count);
}
Value TemporalPlainDateGetter::daysInMonth(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
UErrorCode status = U_ZERO_ERROR;
LocalResourcePointer<UCalendar> newCal(ucal_clone(icuCalendar, &status), [](UCalendar* r) {
ucal_close(r);
});
CHECK_ICU()
int dayCount = 20;
ucal_set(newCal.get(), UCAL_DAY_OF_MONTH, dayCount);
for (;; dayCount++) {
ucal_set(newCal.get(), UCAL_DAY_OF_MONTH, dayCount);
if (ucal_get(newCal.get(), UCAL_DAY_OF_MONTH, &status) != dayCount) {
CHECK_ICU()
break;
}
}
return Value(dayCount - 1);
}
Value TemporalPlainDateGetter::daysInYear(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
UErrorCode status = U_ZERO_ERROR;
LocalResourcePointer<UCalendar> newCal(ucal_clone(icuCalendar, &status), [](UCalendar* r) {
ucal_close(r);
});
CHECK_ICU()
int dayCount = 353;
ucal_set(newCal.get(), UCAL_DAY_OF_YEAR, dayCount);
for (;; dayCount++) {
ucal_set(newCal.get(), UCAL_DAY_OF_YEAR, dayCount);
if (ucal_get(newCal.get(), UCAL_DAY_OF_YEAR, &status) != dayCount) {
CHECK_ICU()
break;
}
}
return Value(dayCount - 1);
}
Value TemporalPlainDateGetter::monthsInYear(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
UErrorCode status = U_ZERO_ERROR;
LocalResourcePointer<UCalendar> newCal(ucal_clone(icuCalendar, &status), [](UCalendar* r) {
ucal_close(r);
});
CHECK_ICU()
ucal_set(newCal.get(), UCAL_ORDINAL_MONTH, 0);
ucal_set(newCal.get(), UCAL_DAY_OF_MONTH, 1);
int monthCount;
for (monthCount = 0;; monthCount++) {
ucal_set(newCal.get(), UCAL_ORDINAL_MONTH, monthCount);
if (ucal_get(newCal.get(), UCAL_ORDINAL_MONTH, &status) != monthCount) {
CHECK_ICU()
break;
}
}
return Value(monthCount);
}
Value TemporalPlainDateGetter::inLeapYear(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
if (calendarID.isISO8601()) {
auto year = plainDate.year();
return Value((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)));
}
bool ret = false;
switch (calendarID.id()) {
case Calendar::ID::Islamic:
case Calendar::ID::IslamicCivil:
case Calendar::ID::IslamicCivilLegacy:
case Calendar::ID::IslamicRGSA:
case Calendar::ID::IslamicTabular:
case Calendar::ID::IslamicUmmAlQura: {
int32_t days = daysInYear(state, plainDate, calendarID, icuCalendar).asInt32();
ret = days == 355;
break;
}
case Calendar::ID::Chinese:
case Calendar::ID::Dangi:
case Calendar::ID::Hebrew: {
int32_t months = monthsInYear(state, plainDate, calendarID, icuCalendar).asInt32();
ret = months == 13;
break;
}
default: {
int32_t days = daysInYear(state, plainDate, calendarID, icuCalendar).asInt32();
ret = days == 366;
break;
}
}
return Value(ret);
}
Value TemporalPlainDateGetter::monthCode(ExecutionState& state, ISO8601::PlainDate plainDate, Calendar calendarID, UCalendar* icuCalendar)
{
StringBuilder sb;
sb.appendChar('M');
UErrorCode status = U_ZERO_ERROR;
auto m = ucal_get(icuCalendar, calendarID.icuNonOridnalMonthCode(), &status) + 1;
CHECK_ICU()
if (m < 10) {
sb.appendChar('0');
} else {
sb.appendChar('1');
}
sb.appendChar(static_cast<char>('0' + (m % 10)));
auto s = ucal_get(icuCalendar, UCAL_IS_LEAP_MONTH, &status);
CHECK_ICU()
if (s) {
sb.appendChar('L');
}
return sb.finalize();
}
// https://tc39.es/proposal-temporal/#sec-temporal.plaindate.compare
int TemporalPlainDateObject::compare(ExecutionState& state, Value one, Value two)
{
auto oneDate = Temporal::toTemporalDate(state, one, Value());
auto twoDate = Temporal::toTemporalDate(state, two, Value());
return compareISODate(state, oneDate, twoDate);
}
TemporalPlainDateObject* TemporalPlainDateObject::with(ExecutionState& state, Value temporalDateLike, Value options)
{
// Let plainDate be the this value.
// Perform ? RequireInternalSlot(plainDate, [[InitializedTemporalDate]]).
// If ? IsPartialTemporalObject(temporalDateLike) is false, throw a TypeError exception.
if (!Temporal::isPartialTemporalObject(state, temporalDateLike)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid temporalDateLike");
}
// Let calendar be plainDate.[[Calendar]].
auto calendar = m_calendarID;
// Let fields be ISODateToFields(calendar, plainDate.[[ISODate]], date).
CalendarFieldsRecord fields;
auto isoDate = computeISODate(state);
fields.year = isoDate.year();
fields.month = isoDate.month();
MonthCode mc;
mc.monthNumber = isoDate.month();
fields.monthCode = mc;
fields.day = isoDate.day();
// Let partialDate be ? PrepareCalendarFields(calendar, temporalDateLike, « year, month, month-code, day », « », partial).
CalendarField fs[4] = { CalendarField::Year, CalendarField::Month, CalendarField::MonthCode, CalendarField::Day };
auto partialDate = Temporal::prepareCalendarFields(state, calendar, temporalDateLike.asObject(), fs, 4, nullptr, 0, nullptr, SIZE_MAX);
// Set fields to CalendarMergeFields(calendar, fields, partialDate).
fields = Temporal::calendarMergeFields(state, calendar, fields, partialDate);
// Let resolvedOptions be ? GetOptionsObject(options).
auto resolvedOptions = Intl::getOptionsObject(state, options);
// Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
auto overflow = Temporal::getTemporalOverflowOption(state, resolvedOptions);
// Let isoDate be ? CalendarDateFromFields(calendar, fields, overflow).
auto result = Temporal::calendarDateFromFields(state, calendar, fields, overflow);
// Return ! CreateTemporalDate(isoDate, calendar).
return new TemporalPlainDateObject(state, state.context()->globalObject()->temporalPlainDatePrototype(), result, calendar);
}
TemporalPlainDateObject* TemporalPlainDateObject::withCalendar(ExecutionState& state, Value calendarLike)
{
auto calendar = Temporal::toTemporalCalendarIdentifier(state, calendarLike);
auto icuCalendar = calendar.createICUCalendar(state);
UErrorCode status = U_ZERO_ERROR;
ucal_setMillis(icuCalendar, ucal_getMillis(m_icuCalendar, &status), &status);
return new TemporalPlainDateObject(state, state.context()->globalObject()->temporalPlainDatePrototype(), std::make_pair(icuCalendar, NullOption), calendar);
}
TemporalDurationObject* TemporalPlainDateObject::since(ExecutionState& state, Value other, Value options)
{
return new TemporalDurationObject(state, differenceTemporalPlainDate(state, DifferenceTemporalPlainDate::Since, other, options));
}
TemporalDurationObject* TemporalPlainDateObject::until(ExecutionState& state, Value other, Value options)
{
return new TemporalDurationObject(state, differenceTemporalPlainDate(state, DifferenceTemporalPlainDate::Until, other, options));
}
int TemporalPlainDateObject::compareISODate(ExecutionState& state, TemporalPlainDateObject* one, TemporalPlainDateObject* two)
{
UErrorCode status = U_ZERO_ERROR;
auto epochTime1 = ucal_getMillis(one->m_icuCalendar, &status);
CHECK_ICU()
auto epochTime2 = ucal_getMillis(two->m_icuCalendar, &status);
CHECK_ICU()
if (epochTime1 > epochTime2) {
return 1;
} else if (epochTime1 < epochTime2) {
return -1;
}
return 0;
}
ISO8601::PlainDate TemporalPlainDateObject::toPlainDate(ExecutionState& state, const ISO8601::Duration& duration)
{
double yearDouble = duration.years();
unsigned month = duration.months();
unsigned day = duration.days();
if (!ISO8601::isYearWithinLimits(yearDouble)) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "year is out of range");
}
int32_t year = static_cast<int32_t>(yearDouble);
if (!(month >= 1 && month <= 12)) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "month is out of range");
}
double daysInMonth = ISO8601::daysInMonth(year, month);
if (!(day >= 1 && day <= daysInMonth)) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "day is out of range");
}
return ISO8601::PlainDate(year, month, day);
}
ISO8601::Duration TemporalPlainDateObject::differenceTemporalPlainDate(ExecutionState& state, DifferenceTemporalPlainDate operation, Value otherInput, Value options)
{
// Set other to ? ToTemporalDate(other).
auto other = Temporal::toTemporalDate(state, otherInput, Value());
// If CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
if (other->calendarID() != calendarID()) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "other calendar is not same");
}
// Let resolvedOptions be ? GetOptionsObject(options).
auto resolvedOptions = Intl::getOptionsObject(state, options);
// Let settings be ? GetDifferenceSettings(operation, resolvedOptions, date, « », day, day).
auto settings = Temporal::getDifferenceSettings(state, operation == DifferenceTemporalPlainDate::Since, resolvedOptions, ISO8601::DateTimeUnitCategory::Date, nullptr, 0, TemporalUnit::Day, TemporalUnit::Day);
// If CompareISODate(temporalDate.[[ISODate]], other.[[ISODate]]) = 0, then
if (computeISODate(state) == other->computeISODate(state)) {
// Return ! CreateTemporalDuration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
return {};
}
// Let dateDifference be CalendarDateUntil(temporalDate.[[Calendar]], temporalDate.[[ISODate]], other.[[ISODate]], settings.[[LargestUnit]]).
auto dateDifference = Temporal::calendarDateUntil(m_calendarID, computeISODate(state), other->computeISODate(state), toTemporalUnit(settings.largestUnit));
// Let duration be CombineDateAndTimeDuration(dateDifference, 0).
auto duration = ISO8601::InternalDuration::combineDateAndTimeDuration(dateDifference, {});
// If settings.[[SmallestUnit]] is not day or settings.[[RoundingIncrement]] ≠ 1, then
if (settings.smallestUnit != ISO8601::DateTimeUnit::Day || settings.roundingIncrement != 1) {
// Let isoDateTime be CombineISODateAndTimeRecord(temporalDate.[[ISODate]], MidnightTimeRecord()).
auto isoDateTime = computeISODate(state);
// Let isoDateTimeOther be CombineISODateAndTimeRecord(other.[[ISODate]], MidnightTimeRecord()).
auto isoDateTimeOther = other->computeISODate(state);
// Let destEpochNs be GetUTCEpochNanoseconds(isoDateTimeOther).
auto destEpochNs = ISO8601::ExactTime::fromPlainDate(isoDateTimeOther).epochNanoseconds();
// Set duration to ? RoundRelativeDuration(duration, originEpochNs, destEpochNs, isoDateTime, unset, temporalDate.[[Calendar]], settings.[[LargestUnit]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
duration = Temporal::roundRelativeDuration(state, duration, destEpochNs, ISO8601::PlainDateTime(isoDateTime, ISO8601::PlainTime()), NullOption, calendarID(), toTemporalUnit(settings.largestUnit), settings.roundingIncrement, toTemporalUnit(settings.smallestUnit), settings.roundingMode);
}
// Let result be ! TemporalDurationFromInternal(duration, day).
auto result = TemporalDurationObject::temporalDurationFromInternal(state, duration, ISO8601::DateTimeUnit::Day);
// If operation is since, set result to CreateNegatedTemporalDuration(result).
if (operation == DifferenceTemporalPlainDate::Since) {
result = TemporalDurationObject::createNegatedTemporalDuration(result);
}
// Return result.
return result;
}
TemporalPlainDateTimeObject* TemporalPlainDateObject::toPlainDateTime(ExecutionState& state, Value temporalTime)
{
// Let time be ? ToTimeRecordOrMidnight(temporalTime).
auto time = Temporal::toTimeRecordOrMidnight(state, temporalTime);
// Let isoDateTime be CombineISODateAndTimeRecord(plainDate.[[ISODate]], time).
auto isoDate = computeISODate(state);
// Return ? CreateTemporalDateTime(isoDateTime, plainDate.[[Calendar]]).
return new TemporalPlainDateTimeObject(state, state.context()->globalObject()->temporalPlainDateTimePrototype(),
isoDate, time, calendarID());
}
TemporalPlainMonthDayObject* TemporalPlainDateObject::toPlainMonthDay(ExecutionState& state)
{
// Let calendar be plainDate.[[Calendar]].
// Let fields be ISODateToFields(calendar, plainDate.[[ISODate]], date).
auto calendar = m_calendarID;
CalendarFieldsRecord fields;
auto isoDate = computeISODate(state);
fields.year = isoDate.year();
fields.month = isoDate.month();
MonthCode mc;
mc.monthNumber = isoDate.month();
fields.monthCode = mc;
fields.day = isoDate.day();
// Let isoDate be ? CalendarMonthDayFromFields(calendar, fields, constrain).
auto u = Temporal::calendarDateFromFields(state, calendar, fields, TemporalOverflowOption::Constrain, Temporal::CalendarDateFromFieldsMode::MonthDay);
// Return ! CreateTemporalMonthDay(isoDate, calendar).
return new TemporalPlainMonthDayObject(state, state.context()->globalObject()->temporalPlainMonthDayPrototype(), u.first, calendar);
}
TemporalPlainYearMonthObject* TemporalPlainDateObject::toPlainYearMonth(ExecutionState& state)
{
// Let calendar be plainDate.[[Calendar]].
// Let fields be ISODateToFields(calendar, plainDate.[[ISODate]], date).
auto calendar = m_calendarID;
CalendarFieldsRecord fields;
auto isoDate = computeISODate(state);
fields.year = isoDate.year();
fields.month = isoDate.month();
MonthCode mc;
mc.monthNumber = isoDate.month();
fields.monthCode = mc;
fields.day = 1;
// Let isoDate be ? CalendarYearMonthFromFields(calendar, fields, constrain).
auto u = Temporal::calendarDateFromFields(state, calendar, fields, TemporalOverflowOption::Constrain, Temporal::CalendarDateFromFieldsMode::YearMonth);
// Return ! CreateTemporalYearMonth(isoDate, calendar).
return new TemporalPlainYearMonthObject(state, state.context()->globalObject()->temporalPlainYearMonthPrototype(), u, calendar);
}
TemporalZonedDateTimeObject* TemporalPlainDateObject::toZonedDateTime(ExecutionState& state, Value item)
{
TimeZone timeZone;
Value temporalTime;
// If item is an Object, then
if (item.isObject()) {
// Let timeZoneLike be ? Get(item, "timeZone").
auto timeZoneLike = item.asObject()->get(state, state.context()->staticStrings().lazyTimeZone()).value(state, item);
// If timeZoneLike is undefined, then
if (timeZoneLike.isUndefined()) {
// Let timeZone be ? ToTemporalTimeZoneIdentifier(item).
timeZone = Temporal::toTemporalTimezoneIdentifier(state, item);
// Let temporalTime be undefined.
} else {
// Else,
// Let timeZone be ? ToTemporalTimeZoneIdentifier(timeZoneLike).
timeZone = Temporal::toTemporalTimezoneIdentifier(state, timeZoneLike);
// Let temporalTime be ? Get(item, "plainTime").
temporalTime = item.asObject()->get(state, state.context()->staticStrings().lazyPlainTime()).value(state, item);
}
} else {
// Else,
// Let timeZone be ? ToTemporalTimeZoneIdentifier(item).
timeZone = Temporal::toTemporalTimezoneIdentifier(state, item);
// Let temporalTime be undefined.
}
Int128 epochNs;
// If temporalTime is undefined, then
if (temporalTime.isUndefined()) {
// Let epochNs be ? GetStartOfDay(timeZone, plainDate.[[ISODate]]).
epochNs = Temporal::getStartOfDay(state, timeZone, computeISODate(state));
} else {
// Else,
// Set temporalTime to ? ToTemporalTime(temporalTime).
temporalTime = Temporal::toTemporalTime(state, temporalTime, Value());
// Let isoDateTime be CombineISODateAndTimeRecord(plainDate.[[ISODate]], temporalTime.[[Time]]).
auto isoDateTime = ISO8601::PlainDateTime(computeISODate(state), temporalTime.asObject()->asTemporalPlainTimeObject()->plainTime());
// If ISODateTimeWithinLimits(isoDateTime) is false, throw a RangeError exception.
if (!ISO8601::isoDateTimeWithinLimits(isoDateTime)) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Out of range date-time");
}
// Let epochNs be ? GetEpochNanosecondsFor(timeZone, isoDateTime, compatible).
epochNs = Temporal::getEpochNanosecondsFor(state, timeZone, ISO8601::ExactTime::fromPlainDateTime(isoDateTime).epochNanoseconds(), TemporalDisambiguationOption::Compatible);
}
// Return ! CreateTemporalZonedDateTime(epochNs, timeZone, plainDate.[[Calendar]]).
return new TemporalZonedDateTimeObject(state, state.context()->globalObject()->temporalZonedDateTimePrototype(), epochNs, timeZone, calendarID());
}
} // namespace Escargot
#endif