mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
685 lines
28 KiB
C++
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
|