escargot/src/runtime/TemporalObject.cpp
HyukWoo Park 22c8ca389e Fix minor code defects
Signed-off-by: HyukWoo Park <hyukwoo.park@samsung.com>
2023-03-06 15:22:53 +09:00

3259 lines
180 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#if defined(ESCARGOT_ENABLE_TEMPORAL)
/*
* Copyright (c) 2022-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 "TemporalObject.h"
#include "DateObject.h"
#include "runtime/ArrayObject.h"
namespace Escargot {
TemporalObject::TemporalObject(ExecutionState& state)
: TemporalObject(state, state.context()->globalObject()->objectPrototype())
{
}
TemporalObject::TemporalObject(ExecutionState& state, Object* proto)
: Temporal(state, proto)
{
}
Value TemporalObject::toISODateTime(ExecutionState& state, DateObject& d)
{
auto result = std::to_string(d.getFullYear(state))
+ "-"
+ (d.getMonth(state) + 1 < 10 ? "0" : "")
+ std::to_string(d.getMonth(state) + 1)
+ "-"
+ (d.getDate(state) < 10 ? "0" : "")
+ std::to_string(d.getDate(state))
+ "T"
+ (d.getHours(state) < 10 ? "0" : "")
+ std::to_string(d.getHours(state))
+ ":"
+ (d.getMinutes(state) < 10 ? "0" : "")
+ std::to_string(d.getMinutes(state))
+ ":"
+ (d.getSeconds(state) < 10 ? "0" : "")
+ std::to_string(d.getSeconds(state)) + "+00:00";
return Value(new ASCIIString(result.data(), result.length()));
}
Value TemporalObject::toISODate(ExecutionState& state, DateObject& d)
{
auto result = std::to_string(d.getFullYear(state)) + "-" + (d.getMonth(state) + 1 < 10 ? "0" : "") + std::to_string(d.getMonth(state) + 1) + "-" + (d.getDate(state) < 10 ? "0" : "") + std::to_string(d.getDate(state));
return Value(new ASCIIString(result.data(), result.length()));
}
Value TemporalObject::toISOTime(ExecutionState& state, DateObject& d)
{
auto result = (d.getHours(state) < 10 ? "0" : "") + std::to_string(d.getHours(state)) + ":" + (d.getMinutes(state) < 10 ? "0" : "") + std::to_string(d.getMinutes(state)) + ":" + (d.getSeconds(state) < 10 ? "0" : "") + std::to_string(d.getSeconds(state)) + "." + std::to_string(d.getMilliseconds(state));
return Value(new ASCIIString(result.data(), result.length()));
}
String* TemporalObject::dateTimeUnitString(ExecutionState& state, DateTimeUnits unit)
{
StaticStrings& strings = state.context()->staticStrings();
switch (unit) {
case YEAR_UNIT:
return strings.lazyYear().string();
case MONTH_UNIT:
return strings.lazyMonth().string();
case WEEK_UNIT:
return strings.lazyWeek().string();
case DAY_UNIT:
return strings.lazyDay().string();
case HOUR_UNIT:
return strings.lazyHour().string();
case MINUTE_UNIT:
return strings.lazyMinute().string();
case SECOND_UNIT:
return strings.lazySecond().string();
case MILLISECOND_UNIT:
return strings.lazymillisecond().string();
case MICROSECOND_UNIT:
return strings.lazymicrosecond().string();
case NANOSECOND_UNIT:
return strings.lazynanosecond().string();
default:
return String::emptyString;
}
return String::emptyString;
}
std::string TemporalObject::getNNumberFromString(ExecutionState& state, std::string& isoString, const int n, unsigned int& index)
{
std::string retVal;
if (!TemporalObject::isNumber(isoString.substr(index, n))) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
}
retVal = isoString.substr(index, n);
index += n;
return retVal;
}
std::map<TemporalObject::DateTimeUnits, int> TemporalObject::getSeconds(ExecutionState& state, std::string& isoString, unsigned int& index)
{
int counter = index + 1;
while (std::isdigit(isoString[counter++]))
;
counter -= (index + 2);
if (counter > 9) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
}
std::string tmp = isoString.substr(index + 1, counter);
index += counter;
for (int i = counter; i < 9; ++i) {
tmp += '0';
}
return { { MILLISECOND_UNIT, std::stoi(tmp.substr(0, 3)) },
{ MICROSECOND_UNIT, std::stoi(tmp.substr(3, 3)) },
{ NANOSECOND_UNIT, std::stoi(tmp.substr(6, 3)) } };
}
std::string TemporalObject::offset(ExecutionState& state, std::string& isoString, unsigned int& index)
{
std::string result;
if (isoString.rfind("", index) != std::string::npos) {
result += "-";
index += 2;
} else if (isoString[index] == '-') {
result += '-';
} else {
result += "+";
}
++index;
if (!(((isoString[index] == '0' || isoString[index] == '1') && isoString[index + 1] >= '0' && isoString[index + 1] <= '9') || (isoString[index] == '2' && isoString[index + 1] >= 0 && isoString[index + 1] <= '3'))) {
return "";
}
result += isoString.substr(index, 2);
index += 2;
int counter = 0;
while (index < isoString.length() && isoString[index] != '.') {
if (isoString[index] == ':') {
index++;
}
result += ':';
if (isoString[index] >= '0' && isoString[index] <= '5' && std::isdigit(isoString[index + 1])) {
result += isoString.substr(index, 2);
index += 2;
counter++;
if (counter == 3) {
return "";
}
} else if (isoString[index] == '[') {
result = result.substr(0, result.length() - 1);
break;
} else {
return "";
}
}
if (isoString[index] == '.' || isoString[index] == ',') {
if (counter != 2) {
return "";
}
unsigned int start = index;
TemporalObject::getSeconds(state, isoString, index);
result += isoString.substr(start, index - start + 1);
for (unsigned int i = 0; i < 9 - (index - start); i++) {
result += '0';
}
}
if (result.length() == 3) {
result += ":00";
}
return result;
}
std::string TemporalObject::tzComponent(ExecutionState& state, std::string& isoString, unsigned int& index)
{
std::string timeZoneID;
unsigned int i = 0;
index++;
if (isoString[index] == '.') {
for (i = index + 2; i < index + 14; ++i) {
if (i > isoString.length() || !(isoString[i] == '-' || isoString[i] == '_' || isoString[i] == '.' || std::isalpha(isoString[i]))) {
break;
}
}
if (!(isoString[index + 1] != '.' && (isoString[index + 1] == '-' || isoString[index + 1] == '_' || std::isalpha(isoString[index + 1]))) || (isoString[index + 1] == '.' && i == 2)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid TimeZone");
}
} else if (isoString[index] == '_' || std::isalpha(isoString[index])) {
for (i = index + 1; i < index + 13; ++i) {
if (!(isoString[i] == '-' || isoString[i] == '_' || isoString[i] == '.' || std::isalpha(isoString[i]))) {
break;
}
}
}
timeZoneID = isoString.substr(index, i - index);
if (i != 0) {
index = i;
}
return timeZoneID;
}
TemporalObject::DateTime TemporalObject::parseValidIso8601String(ExecutionState& state, std::string isoString, const bool parseTimeZone = false)
{
if (isoString.empty()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "Invalid ISO string");
}
DateTime dateTime = { 0, 1, 1, 0, 0, 0, 0, 0, 0, String::emptyString, new TimeZone(false, String::emptyString, String::emptyString) };
unsigned int index = 0;
try {
bool end = false;
if (isoString.at(index) != 'T' && isoString.find('-') != std::string::npos) {
// Date
bool monthDay = false;
if (isoString.rfind("", index) == 0 && TemporalObject::isNumber(isoString.substr(3 + index, 4))) {
dateTime.year = std::stoi("-" + isoString.substr(3 + index, 4));
index += 7;
} else if (TemporalObject::isNumber(isoString.substr(index, 4))) {
dateTime.year = std::stoi(isoString.substr(0, 4));
index += 4;
} else if (isoString.rfind("--", 0) == 0) {
monthDay = true;
index += 2;
}
if (isoString.at(index) == '-') {
++index;
}
if ((isoString.at(index) == '0' && std::isdigit(isoString.at(index + 1))) || (isoString.at(index) == '1' && isoString.at(index + 1) >= '0' && isoString.at(index + 1) <= '2')) {
dateTime.month = std::stoi(isoString.substr(index, 2));
index += 2;
end = index == isoString.length();
}
if (isoString.at(index) != '-' && monthDay) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
} else if (isoString.at(index) == '-') {
++index;
}
if ((isoString.at(index) >= '0' && isoString.at(index) <= '2' && std::isdigit(isoString.at(index + 1))) || (isoString.at(index) == '3' && (isoString.at(index + 1) == '0' || isoString.at(index + 1) == '1'))) {
dateTime.day = std::stoi(isoString.substr(index, 2));
index += 2;
if (index == isoString.length()) {
end = true;
}
if (monthDay) {
if (index != isoString.length()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
}
end = true;
}
} else if (!end && !(isoString.at(index) == '+' || isoString.at(index) == '-' || isoString.rfind("", index) == 0)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
}
}
if (!end) {
// Time
if (isoString.at(index) == 'T' || isoString.rfind("\\s", index) == 0) {
++index;
for (; isoString.at(index) == 's'; ++index)
;
}
if (isoString.find(':') != std::string::npos) {
dateTime.hour = std::stoi(TemporalObject::getNNumberFromString(state, isoString, 2, index));
if (isoString.at(index) == ':') {
++index;
}
dateTime.minute = std::stoi(TemporalObject::getNNumberFromString(state, isoString, 2, index));
if (isoString.at(index) == ':') {
++index;
dateTime.second = std::stoi(TemporalObject::getNNumberFromString(state, isoString, 2, index));
}
if (TemporalObject::isNumber(isoString.substr(index, 2))) {
dateTime.second = std::stoi(TemporalObject::getNNumberFromString(state, isoString, 2, index));
}
if (isoString.at(index) == '.' || isoString.at(index) == ',') {
std::map<TemporalObject::DateTimeUnits, int> seconds;
seconds = TemporalObject::getSeconds(state, isoString, index);
dateTime.millisecond = seconds[MILLISECOND_UNIT];
dateTime.microsecond = seconds[MICROSECOND_UNIT];
dateTime.nanosecond = seconds[NANOSECOND_UNIT];
}
}
auto timeZoneOffset = TemporalObject::parseTimeZoneOffset(state, isoString, index);
if (parseTimeZone && timeZoneOffset.offsetString && timeZoneOffset.name) {
dateTime.tz = new TimeZone{ timeZoneOffset.z, timeZoneOffset.offsetString, timeZoneOffset.name };
} else if (!(timeZoneOffset.offsetString && timeZoneOffset.name)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
}
// Calendar
if (isoString.rfind("[u-ca=", index) != std::string::npos) {
index += 6;
std::string calendar;
for (int i = 0; isoString.at(index) != ']'; ++i) {
// Check if current char is a number or digit
if (std::isalpha(isoString.at(index)) || std::isdigit(isoString.at(index))) {
calendar += isoString.at(index);
++index;
}
// A calendar can't be longer than 8 character
if (i == 8) {
if (isoString.at(index) != '-' && isoString.at(index) != ']') {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
}
i = 0;
}
// A calendar must be at least 3 character
if (i < 3 && (isoString.at(index) == '-' || isoString.at(index) == ']')) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
}
}
if (isoString.at(index) == ']') {
dateTime.calendar = new ASCIIString(calendar.c_str());
index++;
}
}
}
} catch (const std::invalid_argument& ia) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
} catch (const std::out_of_range& oor) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
}
if (isoString.length() != index) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO string");
}
if (!TemporalPlainDateObject::isValidISODate(state, dateTime.year, dateTime.month, dateTime.day)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO date");
}
if (!TemporalPlainTimeObject::isValidTime(state, dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond, dateTime.microsecond, dateTime.nanosecond)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid ISO time");
}
if (parseTimeZone) {
if (dateTime.tz->z) {
dateTime.tz->offsetString = String::emptyString;
}
}
return dateTime;
}
TemporalObject::DateTime TemporalObject::parseTemporalDateString(ExecutionState& state, const std::string& isoString)
{
return TemporalObject::parseTemporalDateTimeString(state, isoString);
}
TemporalObject::DateTime TemporalObject::parseTemporalDateTimeString(ExecutionState& state, const std::string& isoString)
{
if (isoString.find('z') != std::string::npos || isoString.find('Z') != std::string::npos) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "isoString contains UTCDesignator");
}
return TemporalObject::parseValidIso8601String(state, isoString);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalObject::parseTemporalDurationString(ExecutionState& state, const std::string& isoString)
{
int sign = 1;
unsigned int index = 0;
int years = -1;
int months = -1;
int weeks = -1;
int days = -1;
int hours = -1;
int minutes = -1;
int seconds = -1;
int fHours = -1;
int fMinutes = -1;
int fSeconds = -1;
if (isoString.length() < 3 || isoString[index + 1] != 'P') {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration string");
}
if (isoString.rfind("", index) == 0) {
sign = -1;
index += 4;
if (isoString.length() < 5) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration string");
}
} else {
index += 2;
}
while (isoString[index] == '/') {
index++;
std::string value;
unsigned int start = index;
while (std::isdigit(isoString[index])) {
index++;
}
value = isoString.substr(start, index - start);
switch (isoString[index]) {
case 'Y':
years = std::stoi(value);
break;
case 'M':
months = std::stoi(value);
break;
case 'W':
weeks = std::stoi(value);
break;
case 'D':
days = std::stoi(value);
break;
default:
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration string");
}
index++;
}
if (isoString[index] == 'T') {
std::string value;
std::string fvalue;
unsigned int start = index;
while (std::isdigit(isoString[index])) {
index++;
}
value = isoString.substr(start, index - start);
if (isoString[index] == '.' || isoString[index] == ',') {
start = index;
while (std::isdigit(isoString[index])) {
index++;
if (index - start == 10) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration string");
}
}
fvalue = isoString.substr(start, index - start);
}
switch (isoString[index]) {
case 'H':
hours = std::stoi(value);
if (!fvalue.empty()) {
fHours = std::stoi(fvalue);
}
break;
case 'M':
minutes = std::stoi(value);
if (!fvalue.empty()) {
fMinutes = std::stoi(fvalue);
}
break;
case 'S':
seconds = std::stoi(value);
if (!fvalue.empty()) {
fSeconds = std::stoi(fvalue);
}
break;
default:
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration string");
}
index++;
}
int minutesMV;
int secondsMV;
int milliSecondsMV;
if (fHours != -1) {
if (minutes != -1 || fMinutes != -1 || seconds != -1 || fSeconds != -1) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration string");
}
minutesMV = fHours / pow(10, trunc(log10(fHours)) + 1) * 60;
} else {
minutesMV = minutes;
}
if (fMinutes != -1) {
if (seconds != -1 || fSeconds != -1) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration string");
}
secondsMV = fMinutes / pow(10, trunc(log10(fMinutes)) + 1) * 60;
} else if (seconds != -1) {
secondsMV = seconds;
} else {
secondsMV = (minutesMV % 1) * 60;
}
if (fSeconds != -1) {
milliSecondsMV = fSeconds / pow(10, trunc(log10(fSeconds)) + 1) * 60;
} else {
milliSecondsMV = (secondsMV % 1) * 1000;
}
int microSecondsMV = (milliSecondsMV % 1) * 1000;
int nanoSecondsMV = (microSecondsMV % 1) * 1000;
return TemporalDurationObject::createDurationRecord(state, years * sign, months * sign, weeks * sign, days * sign, hours * sign, std::floor(minutesMV) * sign, std::floor(secondsMV) * sign, std::floor(milliSecondsMV) * sign, std::floor(microSecondsMV) * sign, std::floor(nanoSecondsMV) * sign);
}
TemporalObject::DateTime TemporalObject::parseTemporalYearMonthString(ExecutionState& state, const std::string& isoString)
{
return TemporalObject::parseTemporalDateTimeString(state, isoString);
}
TemporalObject::DateTime TemporalObject::parseTemporalInstantString(ExecutionState& state, const std::string& isoString)
{
auto result = parseValidIso8601String(state, isoString, true);
if (result.tz->z) {
result.tz->offsetString = new ASCIIString("+00:00");
}
if (!result.tz->offsetString || result.tz->offsetString->length() == 0) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "undefined offsetString");
}
return result;
}
TemporalObject::TimeZone TemporalObject::parseTemporalTimeZoneString(ExecutionState& state, const std::string& isoString)
{
auto result = TimeZone(false, String::emptyString, String::emptyString);
if (isoString == "UTC") {
result.z = true;
return result;
}
if (isoString == "z" || isoString == "Z") {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "z is not valid timeZone string");
}
unsigned int index = 0;
result = TemporalObject::parseTimeZoneOffset(state, const_cast<std::string&>(isoString), index);
if (!(result.offsetString && result.name) || (result.offsetString->length() == 0 && result.name->length() == 0 && !result.z)) {
result = *TemporalObject::parseValidIso8601String(state, isoString, true).tz;
}
if (result.z) {
result.offsetString = String::emptyString;
}
if (!(result.offsetString && result.name) || (result.offsetString->length() == 0 && result.name->length() == 0 && !result.z)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "bare date-time string is not a time zone");
}
return result;
}
TemporalObject::TimeZone TemporalObject::parseTimeZoneOffset(ExecutionState& state, std::string& isoString, unsigned int& index)
{
// Offset
auto tz = TimeZone(false, String::emptyString, String::emptyString);
if (isoString[index] == 'z' || isoString[index] == 'Z') {
tz.z = true;
++index;
} else if (isoString[index] == '+' || isoString[index] == '-' || isoString.rfind("", index) == index) {
tz.offsetString = new ASCIIString(TemporalObject::offset(state, isoString, index).c_str());
if (tz.offsetString->length() == 0) {
return TemporalObject::TimeZone{ false, nullptr, nullptr };
}
}
// TimeZone
if (isoString[index] == '[' && isoString.rfind("[u-ca=", index) != index) {
std::string tmp = TemporalObject::tzComponent(state, isoString, index);
while (tmp.length() != 0) {
tz.name = new ASCIIString(std::string(tz.name->toNonGCUTF8StringData()).append(tmp).c_str());
if (isoString[index] == '/') {
tmp = TemporalObject::tzComponent(state, isoString, index);
}
if (isoString[index] == ']') {
tz.name = new ASCIIString(std::string(tz.name->toNonGCUTF8StringData()).append("/" + tmp).c_str());
index++;
break;
}
}
} else if (isoString.rfind("Etc/GMT", index) == 0) {
index += 7;
if (isoString[index] == '+' || isoString[index] == '-') {
if (!std::isdigit(isoString[index + 1])) {
return TemporalObject::TimeZone{ false, nullptr, nullptr };
}
if (isNumber(isoString.substr(index + 1, 2))) {
index += 3;
}
index += 2;
}
} else if (isoString[index] == '+' || isoString[index] == '-' || (index < isoString.length() && isoString.rfind("", index) == 0)) {
TemporalObject::offset(state, isoString, index);
}
return tz;
}
TemporalObject::DateTime TemporalObject::parseTemporalZonedDateTimeString(ExecutionState& state, const std::string& isoString)
{
return TemporalObject::parseValidIso8601String(state, isoString, true);
}
TemporalObject::DateTime TemporalObject::parseTemporalMonthDayString(ExecutionState& state, const std::string& isoString)
{
return parseTemporalDateTimeString(state, isoString);
}
TemporalPlainTimeObject::TemporalPlainTimeObject(ExecutionState& state, Object* proto, const TemporalTime& time, TemporalCalendarObject* calendar)
: TemporalObject(state, proto)
, m_time(time)
, m_calendar(calendar)
{
}
Value TemporalObject::toRelativeTemporalObject(ExecutionState& state, Object* options)
{
auto value = options->get(state, ObjectPropertyName(state.context()->staticStrings().lazyrelativeTo())).value(state, options);
if (value.isUndefined()) {
return value;
}
TemporalZonedDateTimeObject::OffsetBehaviour offsetBehaviour = TemporalZonedDateTimeObject::OPTION;
TemporalZonedDateTimeObject::MatchBehaviour matchBehaviour = TemporalZonedDateTimeObject::EXACTLY;
Value offsetString;
Value timeZone = Value();
Value calendar;
std::map<TemporalObject::DateTimeUnits, int> result;
if (value.isObject()) {
Object* valueObject = value.asObject();
if (valueObject->isTemporalPlainDateObject() || valueObject->isTemporalZonedDateTimeObject()) {
return value;
}
if (valueObject->isTemporalPlainDateTimeObject()) {
TemporalPlainDateTimeObject* plainDateTimeObject = valueObject->asTemporalPlainDateTimeObject();
return TemporalPlainDateObject::createTemporalDate(state, plainDateTimeObject->getYear(), plainDateTimeObject->getMonth(), plainDateTimeObject->getDay(), plainDateTimeObject->getCalendar());
}
calendar = TemporalCalendarObject::getTemporalCalendarWithISODefault(state, value);
ValueVector values = { Value(state.context()->staticStrings().lazyDay().string()),
Value(state.context()->staticStrings().lazyHour().string()),
Value(state.context()->staticStrings().lazymicrosecond().string()),
Value(state.context()->staticStrings().lazymillisecond().string()),
Value(state.context()->staticStrings().lazyMinute().string()),
Value(state.context()->staticStrings().lazyMonth().string()),
Value(state.context()->staticStrings().lazymonthCode().string()),
Value(state.context()->staticStrings().lazynanosecond().string()),
Value(state.context()->staticStrings().lazySecond().string()),
Value(state.context()->staticStrings().lazyYear().string()) };
ValueVector fieldNames = TemporalCalendarObject::calendarFields(state, calendar, values);
Value fields = Temporal::prepareTemporalFields(state, value, fieldNames, ValueVector());
result = TemporalPlainDateTimeObject::interpretTemporalDateTimeFields(state, calendar, fields, options);
offsetString = valueObject->get(state, ObjectPropertyName(state.context()->staticStrings().lazyoffset())).value(state, value);
timeZone = valueObject->get(state, ObjectPropertyName(state.context()->staticStrings().lazyoffset())).value(state, value);
if (!timeZone.isUndefined()) {
timeZone = TemporalTimeZoneObject::toTemporalTimeZone(state, timeZone);
}
if (offsetString.isUndefined()) {
offsetBehaviour = TemporalZonedDateTimeObject::WALL;
}
} else {
auto parseResult = TemporalObject::parseTemporalZonedDateTimeString(state, value.asString()->toNonGCUTF8StringData());
calendar = TemporalCalendarObject::toTemporalCalendarWithISODefault(state, Value((parseResult.calendar)));
if (parseResult.tz->name->length() != 0) {
if (!TemporalTimeZoneObject::isValidTimeZoneName(parseResult.tz->name->toNonGCUTF8StringData())) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid TimeZone name");
}
parseResult.tz->name = new ASCIIString(TemporalTimeZoneObject::canonicalizeTimeZoneName(parseResult.tz->name->toNonGCUTF8StringData()).c_str());
timeZone = TemporalTimeZoneObject::createTemporalTimeZone(state, parseResult.tz->name->toNonGCUTF8StringData());
}
if (parseResult.tz->z) {
offsetBehaviour = TemporalZonedDateTimeObject::EXACT;
} else if (parseResult.tz->offsetString->length() == 0) {
offsetBehaviour = TemporalZonedDateTimeObject::WALL;
}
matchBehaviour = TemporalZonedDateTimeObject::MINUTES;
result = {
{ TemporalObject::YEAR_UNIT, parseResult.year },
{ TemporalObject::MONTH_UNIT, parseResult.month },
{ TemporalObject::DAY_UNIT, parseResult.day },
{ TemporalObject::HOUR_UNIT, parseResult.hour },
{ TemporalObject::MINUTE_UNIT, parseResult.minute },
{ TemporalObject::SECOND_UNIT, parseResult.second },
{ TemporalObject::MILLISECOND_UNIT, parseResult.millisecond },
{ TemporalObject::MICROSECOND_UNIT, parseResult.microsecond },
{ TemporalObject::NANOSECOND_UNIT, parseResult.nanosecond }
};
}
if (!timeZone.isUndefined()) {
int64_t offsetNanoseconds = 0;
if (offsetBehaviour == TemporalZonedDateTimeObject::OPTION) {
offsetNanoseconds = TemporalInstantObject::offsetStringToNanoseconds(state, offsetString.asString());
}
auto epochNanoseconds = TemporalZonedDateTimeObject::interpretISODateTimeOffset(state, result, offsetBehaviour, offsetNanoseconds, timeZone, Value(state.context()->staticStrings().lazycompatible().string()), Value(state.context()->staticStrings().reject.string()), matchBehaviour);
return TemporalZonedDateTimeObject::createTemporalZonedDateTime(state, *epochNanoseconds.asBigInt(), timeZone.asObject()->asTemporalTimeZoneObject(), calendar.asObject()->asTemporalCalendarObject());
}
return TemporalPlainDateObject::createTemporalDate(state, result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::DAY_UNIT], calendar);
}
Object* TemporalObject::mergeLargestUnitOption(ExecutionState& state, const Value& option, TemporalObject::DateTimeUnits largestUnit)
{
auto merged = new Object(state, Object::PrototypeIsNull);
for (auto& nextKey : Object::enumerableOwnProperties(state, option.asObject(), EnumerableOwnPropertiesType::Key)) {
Value propValue = option.asObject()->get(state, ObjectPropertyName(state, nextKey.toPropertyKey(state).asString())).value(state, option);
merged->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, nextKey.toPropertyKey(state).asString()), ObjectPropertyDescriptor(propValue));
}
merged->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, state.context()->staticStrings().lazylargestUnit().string()), ObjectPropertyDescriptor(dateTimeUnitString(state, largestUnit)));
return merged;
}
// The rounding modes accepted by this abstract operation are intended to be the same as whatever is eventually standardized in the Intl.NumberFormat V3 proposal.
BigInt* TemporalObject::roundNumberToIncrementAsIfPositive(ExecutionState& state, BigInt* x, int64_t increment, RoundingMode roundingMode)
{
BigInt* quotient = x->division(state, new BigInt(increment));
UnsignedRoundingMode unsignedRoundingMode = TemporalObject::getUnsignedRoundingMode(state, roundingMode, false);
BigInt* r1 = quotient->addition(state, new BigInt((int64_t)0));
BigInt* r2 = quotient->addition(state, new BigInt((int64_t)1));
return TemporalObject::applyUnsignedRoundingMode(state, quotient, r1, r2, unsignedRoundingMode)->multiply(state, new BigInt(increment));
}
UnsignedRoundingMode TemporalObject::getUnsignedRoundingMode(ExecutionState& state, RoundingMode roundingMode, bool isNegative)
{
if (roundingMode == RoundingMode::HALF_EVEN) {
return UNSIGNED_HALF_EVEN;
}
if (isNegative) {
return negativeUnsignedRoundingMode[roundingMode];
}
return positiveUnsignedRoundingMode[roundingMode];
}
BigInt* TemporalObject::applyUnsignedRoundingMode(ExecutionState& state, BigInt* x, BigInt* r1, BigInt* r2, UnsignedRoundingMode unsignedRoundingMode)
{
if (x->equals(r1)) {
return r1;
}
if (r1->greaterThan(x) || x->greaterThan(r2)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid rounding");
}
if (unsignedRoundingMode == UnsignedRoundingMode::ZERO) {
return r1;
}
if (unsignedRoundingMode == UnsignedRoundingMode::INF) {
return r2;
}
BigInt* d1 = x->subtraction(state, r1);
BigInt* d2 = r2->subtraction(state, x);
if (d1->lessThan(d2)) {
return r1;
}
if (d2->lessThan(d1)) {
return r2;
}
if (!d1->equals(d2)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid rounding");
}
if (unsignedRoundingMode == UnsignedRoundingMode::HALF_ZERO) {
return r1;
}
if (unsignedRoundingMode == UnsignedRoundingMode::HALF_INF) {
return r2;
}
if (r1->division(state, r2->subtraction(state, r1))->remainder(state, new BigInt((int64_t)2))->isZero()) {
return r1;
}
return r2;
}
int64_t TemporalObject::floor(double num)
{
return (num < 0 ? -1 : 1) * std::ceil(std::abs(num));
}
int64_t TemporalObject::modulo(int64_t num1, int64_t num2)
{
return num1 -= TemporalObject::floor(num1 / (double)num2);
}
Value TemporalPlainTimeObject::createTemporalTime(ExecutionState& state, int hour, int minute, int second, int millisecond, int microsecond, int nanosecond, Optional<Object*> newTarget)
{
TemporalTime time(hour, minute, second, millisecond, microsecond, nanosecond);
TemporalCalendarObject* calendar = new TemporalCalendarObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalCalendarPrototype(), state.context()->staticStrings().lazyISO8601().string());
return new TemporalPlainTimeObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalPlainTimePrototype(), time, calendar);
}
Value TemporalPlainTimeObject::createFromPlainDateTimeObject(ExecutionState& state, TemporalPlainDateTimeObject* plainDateTime)
{
return new TemporalPlainTimeObject(state, plainDateTime->getPrototypeObject(state), plainDateTime->time(), plainDateTime->getCalendar());
}
bool TemporalPlainTimeObject::isValidTime(ExecutionState& state, const int h, const int m, const int s, const int ms, const int us, const int ns)
{
if (h < 0 || h > 23 || m < 0 || m > 59 || s < 0 || s > 59 || ms < 0 || ms > 999 || us < 0 || us > 999 || ns < 0 || ns > 999) {
return false;
}
return true;
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainTimeObject::balanceTime(ExecutionState& state, const int hour, const int minute, const int second, const int millisecond, const int microsecond, const int nanosecond)
{
std::map<TemporalObject::DateTimeUnits, int> result;
result[TemporalObject::MICROSECOND_UNIT] = microsecond + TemporalObject::floor(nanosecond / 1000.0);
result[TemporalObject::NANOSECOND_UNIT] = nanosecond < 0 ? 1000 + nanosecond % 1000 : nanosecond % 1000;
result[TemporalObject::MILLISECOND_UNIT] = millisecond + TemporalObject::floor(result[TemporalObject::MICROSECOND_UNIT] / 1000);
result[TemporalObject::MICROSECOND_UNIT] = TemporalObject::modulo(result[TemporalObject::MICROSECOND_UNIT], 1000);
result[TemporalObject::SECOND_UNIT] = second + TemporalObject::floor(result[TemporalObject::MILLISECOND_UNIT] / 1000);
result[TemporalObject::MILLISECOND_UNIT] %= 1000;
result[TemporalObject::MINUTE_UNIT] = minute + TemporalObject::floor(result[TemporalObject::SECOND_UNIT] / 60);
result[TemporalObject::SECOND_UNIT] %= 60;
result[TemporalObject::HOUR_UNIT] = hour + TemporalObject::floor(result[TemporalObject::MINUTE_UNIT] / 60);
result[TemporalObject::MINUTE_UNIT] %= 60;
result[TemporalObject::DAY_UNIT] = TemporalObject::floor(result[TemporalObject::HOUR_UNIT] / 24);
result[TemporalObject::HOUR_UNIT] %= 24;
return result;
}
Value TemporalPlainTimeObject::toTemporalTime(ExecutionState& state, const Value& item, Value options)
{
if (options.isUndefined()) {
options = Value(state.context()->staticStrings().lazyConstrain().string());
}
ASSERT(options.asString()->equals(state.context()->staticStrings().lazyConstrain().string()) || options.asString()->equals(state.context()->staticStrings().reject.string()));
std::map<TemporalObject::DateTimeUnits, int> result;
if (item.isObject()) {
if (item.asObject()->isTemporalPlainTimeObject()) {
return item;
}
if (item.asObject()->isTemporalZonedDateTimeObject()) {
Value instant = TemporalInstantObject::createTemporalInstant(state, item.asObject()->asTemporalZonedDateTimeObject()->getNanoseconds());
TemporalPlainDateTimeObject* plainDateTime = TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(state, item.asObject()->asTemporalZonedDateTimeObject()->getTimeZone(), instant, item.asObject()->asTemporalZonedDateTimeObject()->getCalendar()).asObject()->asTemporalPlainDateTimeObject();
return TemporalPlainTimeObject::createTemporalTime(state, plainDateTime->getHour(), plainDateTime->getMinute(),
plainDateTime->getSecond(), plainDateTime->getMillisecond(),
plainDateTime->getMicrosecond(),
plainDateTime->getNanosecond(), nullptr);
}
if (item.asObject()->isTemporalPlainDateTimeObject()) {
return TemporalPlainTimeObject::createFromPlainDateTimeObject(state, item.asObject()->asTemporalPlainDateTimeObject());
}
Value calendar = TemporalCalendarObject::getTemporalCalendarWithISODefault(state, item);
if (!calendar.asObject()->asTemporalCalendarObject()->getIdentifier()->equals(state.context()->staticStrings().lazyISO8601().string())) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Calendar is not ISO8601");
}
result = TemporalPlainTimeObject::toTemporalTimeRecord(state, item);
result = TemporalPlainTimeObject::regulateTime(state, result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT], options);
return TemporalPlainTimeObject::createTemporalTime(state, result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT], nullptr);
}
Temporal::toTemporalOverflow(state, options);
auto tmp = TemporalObject::parseTemporalDateTimeString(state, item.asString()->toNonGCUTF8StringData());
ASSERT(TemporalPlainTimeObject::isValidTime(state, tmp.hour, tmp.minute, tmp.second, tmp.millisecond, tmp.microsecond, tmp.nanosecond));
if (tmp.calendar->length() != 0 && !tmp.calendar->equals("iso8601")) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Calendar is not ISO8601");
}
return TemporalPlainTimeObject::createTemporalTime(state, tmp.hour, tmp.minute, tmp.second, tmp.millisecond, tmp.microsecond, tmp.nanosecond, nullptr);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainTimeObject::toTemporalTimeRecord(ExecutionState& state, const Value& temporalTimeLike)
{
ASSERT(temporalTimeLike.isObject());
std::map<TemporalObject::DateTimeUnits, int> result = { { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::MINUTE_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, 0 } };
TemporalObject::DateTimeUnits temporalTimeLikeProp[6] = { TemporalObject::HOUR_UNIT, TemporalObject::MICROSECOND_UNIT, TemporalObject::MILLISECOND_UNIT, TemporalObject::MINUTE_UNIT, TemporalObject::NANOSECOND_UNIT, TemporalObject::SECOND_UNIT };
ValueVector values = { Value(state.context()->staticStrings().lazyHour().string()),
Value(state.context()->staticStrings().lazymicrosecond().string()),
Value(state.context()->staticStrings().lazymillisecond().string()),
Value(state.context()->staticStrings().lazyMinute().string()),
Value(state.context()->staticStrings().lazynanosecond().string()),
Value(state.context()->staticStrings().lazySecond().string()) };
Value partial = TemporalCalendarObject::prepareTemporalFields(state, temporalTimeLike, values, {});
for (auto i : temporalTimeLikeProp) {
ObjectHasPropertyResult objectHasPropertyResult = partial.asObject()->hasProperty(state, ObjectPropertyName(state, TemporalObject::dateTimeUnitString(state, i)));
if (!objectHasPropertyResult.hasProperty()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "any is false");
}
Value value = objectHasPropertyResult.value(state, ObjectPropertyName(state, TemporalObject::dateTimeUnitString(state, i)), partial);
if (!value.isInt32() || (value.isDouble() && !std::isfinite(value.asDouble()))) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "any is false");
}
result[i] = value.asInt32();
}
return result;
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainTimeObject::regulateTime(ExecutionState& state, int hour, int minute, int second, int millisecond, int microsecond, int nanosecond, const Value& overflow)
{
ASSERT(overflow.asString()->equals(state.context()->staticStrings().lazyConstrain().string()) || overflow.asString()->equals(state.context()->staticStrings().reject.string()));
if (overflow.asString()->equals(state.context()->staticStrings().lazyConstrain().string())) {
return TemporalPlainTimeObject::constrainTime(state, hour, minute, second, millisecond, microsecond, nanosecond);
}
if (TemporalPlainTimeObject::isValidTime(state, hour, minute, second, millisecond, microsecond, nanosecond)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid time");
}
return { { TemporalObject::HOUR_UNIT, hour }, { TemporalObject::MINUTE_UNIT, minute }, { TemporalObject::SECOND_UNIT, second }, { TemporalObject::MILLISECOND_UNIT, millisecond }, { TemporalObject::MICROSECOND_UNIT, microsecond }, { TemporalObject::NANOSECOND_UNIT, nanosecond } };
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainTimeObject::constrainTime(ExecutionState& state, int hour, int minute, int second, int millisecond, int microsecond, int nanosecond)
{
return { { TemporalObject::HOUR_UNIT, std::max(0, std::min(hour, 23)) }, { TemporalObject::MINUTE_UNIT, std::max(0, std::min(minute, 59)) }, { TemporalObject::SECOND_UNIT, std::max(0, std::min(second, 59)) }, { TemporalObject::MILLISECOND_UNIT, std::max(0, std::min(millisecond, 999)) }, { TemporalObject::MICROSECOND_UNIT, std::max(0, std::min(microsecond, 999)) }, { TemporalObject::NANOSECOND_UNIT, std::max(0, std::min(nanosecond, 999)) } };
}
int TemporalPlainTimeObject::compareTemporalTime(ExecutionState& state, const int time1[6], const int time2[6])
{
for (int i = 0; i < 6; ++i) {
if (time1[i] > time2[i]) {
return 1;
}
if (time1[i] < time2[i]) {
return -1;
}
}
return 0;
}
std::map<TemporalObject::DateTimeUnits, Value> TemporalPlainTimeObject::toPartialTime(ExecutionState& state, const Value& temporalTimeLike)
{
ASSERT(temporalTimeLike.isObject());
std::map<TemporalObject::DateTimeUnits, Value> result = { { TemporalObject::HOUR_UNIT, Value() }, { TemporalObject::MINUTE_UNIT, Value() }, { TemporalObject::SECOND_UNIT, Value() }, { TemporalObject::MILLISECOND_UNIT, Value() }, { TemporalObject::MICROSECOND_UNIT, Value() }, { TemporalObject::NANOSECOND_UNIT, Value() } };
TemporalObject::DateTimeUnits temporalTimeLikeProp[6] = { TemporalObject::HOUR_UNIT, TemporalObject::MICROSECOND_UNIT, TemporalObject::MILLISECOND_UNIT, TemporalObject::MINUTE_UNIT, TemporalObject::NANOSECOND_UNIT, TemporalObject::SECOND_UNIT };
bool any = false;
for (auto i : temporalTimeLikeProp) {
Value value = temporalTimeLike.asObject()->get(state, ObjectPropertyName(state, TemporalObject::dateTimeUnitString(state, i))).value(state, temporalTimeLike);
if (!value.isUndefined()) {
any = true;
result[i] = value;
}
}
if (!any) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "any is false");
}
return result;
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainTimeObject::addTime(ExecutionState& state, std::map<TemporalObject::DateTimeUnits, int>& first, std::map<TemporalObject::DateTimeUnits, int>& second)
{
ASSERT(TemporalPlainTimeObject::isValidTime(state, first[TemporalObject::HOUR_UNIT], first[TemporalObject::MINUTE_UNIT], first[TemporalObject::SECOND_UNIT], first[TemporalObject::MILLISECOND_UNIT], first[TemporalObject::MICROSECOND_UNIT], first[TemporalObject::NANOSECOND_UNIT]));
return TemporalPlainTimeObject::balanceTime(state, first[TemporalObject::HOUR_UNIT] + second[TemporalObject::HOUR_UNIT], first[TemporalObject::MINUTE_UNIT] + second[TemporalObject::MINUTE_UNIT], first[TemporalObject::SECOND_UNIT] + second[TemporalObject::SECOND_UNIT], first[TemporalObject::MILLISECOND_UNIT] + second[TemporalObject::MILLISECOND_UNIT], first[TemporalObject::MICROSECOND_UNIT] + second[TemporalObject::MICROSECOND_UNIT], first[TemporalObject::NANOSECOND_UNIT] + second[TemporalObject::NANOSECOND_UNIT]);
}
Value TemporalPlainTimeObject::addDurationToOrSubtractDurationFromPlainTime(ExecutionState& state, Operation operation, TemporalPlainTimeObject* temporalTime, const Value& temporalDurationLike)
{
auto durationObject = TemporalDurationObject::toTemporalDuration(state, temporalDurationLike).asObject()->asTemporalDurationObject();
std::map<TemporalObject::DateTimeUnits, int> time = { { TemporalObject::HOUR_UNIT, temporalTime->getHour() }, { TemporalObject::MINUTE_UNIT, temporalTime->getMinute() }, { TemporalObject::SECOND_UNIT, temporalTime->getSecond() }, { TemporalObject::MILLISECOND_UNIT, temporalTime->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, temporalTime->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, temporalTime->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> duration = { { TemporalObject::HOUR_UNIT, operation * durationObject->getHour() }, { TemporalObject::MINUTE_UNIT, operation * durationObject->getMinute() }, { TemporalObject::SECOND_UNIT, operation * durationObject->getMinute() }, { TemporalObject::MILLISECOND_UNIT, operation * durationObject->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, operation * durationObject->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, operation * durationObject->getNanosecond() } };
auto result = TemporalPlainTimeObject::addTime(state, time, duration);
if (!TemporalPlainTimeObject::isValidTime(state, result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT])) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid time");
}
return TemporalPlainTimeObject::createTemporalTime(state, result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT]);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainTimeObject::differenceTime(ExecutionState& state, std::map<TemporalObject::DateTimeUnits, int> first, std::map<TemporalObject::DateTimeUnits, int> second)
{
int hours = first[TemporalObject::HOUR_UNIT] - second[TemporalObject::HOUR_UNIT];
int minutes = first[TemporalObject::MINUTE_UNIT] - second[TemporalObject::MINUTE_UNIT];
int seconds = first[TemporalObject::SECOND_UNIT] - second[TemporalObject::SECOND_UNIT];
int milliseconds = first[TemporalObject::MILLISECOND_UNIT] - second[TemporalObject::MILLISECOND_UNIT];
int microseconds = first[TemporalObject::MICROSECOND_UNIT] - second[TemporalObject::MICROSECOND_UNIT];
int nanoseconds = first[TemporalObject::NANOSECOND_UNIT] - second[TemporalObject::NANOSECOND_UNIT];
int dateTime[] = { hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0, 0, 0, 0 };
int sign = TemporalDurationObject::durationSign(dateTime);
std::map<TemporalObject::DateTimeUnits, int> bt = TemporalPlainTimeObject::balanceTime(state, hours * sign, minutes * sign, seconds * sign, milliseconds * sign, microseconds * sign, nanoseconds * sign);
return TemporalDurationObject::createTimeDurationRecord(state, 0, bt[TemporalObject::HOUR_UNIT] * sign, bt[TemporalObject::MINUTE_UNIT] * sign, bt[TemporalObject::SECOND_UNIT] * sign, bt[TemporalObject::MILLISECOND_UNIT] * sign, bt[TemporalObject::MICROSECOND_UNIT] * sign, bt[TemporalObject::NANOSECOND_UNIT] * sign);
}
bool TemporalCalendarObject::isBuiltinCalendar(String* id)
{
return id->equals("iso8601");
}
TemporalCalendarObject::TemporalCalendarObject(ExecutionState& state)
: TemporalCalendarObject(state, state.context()->globalObject()->objectPrototype())
{
}
TemporalCalendarObject::TemporalCalendarObject(ExecutionState& state, Object* proto, String* identifier)
: TemporalObject(state, proto)
, m_identifier(identifier)
{
}
TemporalCalendarObject* TemporalCalendarObject::createTemporalCalendar(ExecutionState& state, String* id, Optional<Object*> newTarget)
{
ASSERT(TemporalCalendarObject::isBuiltinCalendar(id));
return new TemporalCalendarObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalCalendarPrototype(), id);
}
Value TemporalCalendarObject::getBuiltinCalendar(ExecutionState& state, String* id)
{
if (!TemporalCalendarObject::isBuiltinCalendar(id)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, ErrorObject::Messages::IsNotDefined);
}
return TemporalCalendarObject::createTemporalCalendar(state, id);
}
Value TemporalCalendarObject::getISO8601Calendar(ExecutionState& state)
{
return TemporalCalendarObject::getBuiltinCalendar(state, state.context()->staticStrings().lazyISO8601().string());
}
Value TemporalCalendarObject::toTemporalCalendar(ExecutionState& state, Value calendar)
{
if (calendar.isObject()) {
auto* calendarObject = calendar.asObject();
if (calendarObject->isTemporalCalendarObject()) {
return Value(TemporalCalendarObject::createTemporalCalendar(state, calendarObject->asTemporalCalendarObject()->getIdentifier()));
}
if (calendarObject->isTemporalObject() && calendarObject->asTemporalObject()->hasCalendar()) {
return calendarObject->asTemporalObject()->getCalendar();
}
if (!calendarObject->hasProperty(state, state.context()->staticStrings().calendar)) {
return calendar;
}
calendar = calendarObject->getOwnProperty(state, state.context()->staticStrings().calendar).value(state, calendar);
if (calendar.isObject() && !calendar.asObject()->hasProperty(state, state.context()->staticStrings().calendar)) {
return calendar;
}
}
if (!calendar.isString()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid type");
}
String* identifier = calendar.asString();
if (!TemporalCalendarObject::isBuiltinCalendar(identifier)) {
identifier = TemporalCalendarObject::parseTemporalCalendarString(state, identifier).asString();
if (!TemporalCalendarObject::isBuiltinCalendar(identifier)) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, ErrorObject::Messages::IsNotDefined);
}
}
return Value(TemporalCalendarObject::createTemporalCalendar(state, identifier, nullptr));
}
Value TemporalCalendarObject::parseTemporalCalendarString(ExecutionState& state, const Value& isoString)
{
ASSERT(isoString.isString());
if (isoString.asString()->equals(String::emptyString)) {
return Value(state.context()->staticStrings().lazyISO8601().string());
}
TemporalObject::DateTime time = TemporalObject::parseValidIso8601String(state, isoString.asString()->toNonGCUTF8StringData());
return time.calendar->length() == 0 ? Value(state.context()->staticStrings().lazyISO8601().string()) : Value(time.calendar);
}
Value TemporalCalendarObject::ISODaysInMonth(ExecutionState& state, const int year, const int m)
{
ASSERT(m > 0 && m < 13);
if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) {
return Value(31);
}
if (m == 4 || m == 6 || m == 9 || m == 11) {
return Value(30);
}
if (TemporalCalendarObject::isIsoLeapYear(state, year)) {
return Value(29);
}
return Value(28);
}
bool TemporalCalendarObject::isIsoLeapYear(ExecutionState& state, const int year)
{
if (year % 4 != 0) {
return false;
}
if (year % 400 == 0) {
return true;
}
if (year % 100 == 0) {
return false;
}
return true;
}
std::string TemporalCalendarObject::buildISOMonthCode(ExecutionState& state, const int month)
{
return "M" + std::string((month < 10 ? "0" : "") + char(48 + month));
}
int TemporalCalendarObject::ISOYear(ExecutionState& state, const Value& temporalObject)
{
ASSERT(temporalObject.asObject()->asTemporalPlainDateObject()->year());
return temporalObject.asObject()->asTemporalPlainDateObject()->year();
}
int TemporalCalendarObject::ISOMonth(ExecutionState& state, const Value& temporalObject)
{
ASSERT(temporalObject.asObject()->asTemporalPlainDateObject()->month());
return temporalObject.asObject()->asTemporalPlainDateObject()->month();
}
std::string TemporalCalendarObject::ISOMonthCode(ExecutionState& state, const Value& temporalObject)
{
ASSERT(temporalObject.asObject()->asTemporalPlainDateObject()->month());
return TemporalCalendarObject::buildISOMonthCode(state, temporalObject.asObject()->asTemporalPlainDateObject()->month());
}
int TemporalCalendarObject::ISODay(ExecutionState& state, const Value& temporalObject)
{
ASSERT(temporalObject.asObject()->asTemporalPlainDateObject()->day());
return temporalObject.asObject()->asTemporalPlainDateObject()->day();
}
/*12.1.6 toTemporalCalendarWithISODefault*/
Value TemporalCalendarObject::toTemporalCalendarWithISODefault(ExecutionState& state, const Value& calendar)
{
if (calendar.isUndefined()) {
return TemporalCalendarObject::getISO8601Calendar(state);
}
return TemporalCalendarObject::toTemporalCalendar(state, calendar);
}
Value TemporalCalendarObject::defaultMergeFields(ExecutionState& state, const Value& fields, const Value& additionalFields)
{
auto& strings = state.context()->staticStrings();
Object* merged = new Object(state);
auto originalKeys = Object::enumerableOwnProperties(state, fields.asObject(), EnumerableOwnPropertiesType::Key);
for (auto& nextKey : originalKeys) {
if (!nextKey.asString()->equals(strings.lazyMonth().string()) || !nextKey.asString()->equals(strings.lazymonthCode().string())) {
Value propValue;
fields.asObject()->get(state, AtomicString(state, nextKey.asString()), propValue);
if (!propValue.isUndefined()) {
merged->defineOwnPropertyThrowsException(state, AtomicString(state, nextKey.asString()), ObjectPropertyDescriptor(propValue, ObjectPropertyDescriptor::AllPresent));
}
}
}
auto newKeys = Object::enumerableOwnProperties(state, additionalFields.asObject(), EnumerableOwnPropertiesType::Key);
bool containsMonth = false;
for (unsigned int i = 0; i < newKeys.size(); ++i) {
Value nextKey = originalKeys[i];
if (!nextKey.asString()->equals(strings.lazyMonth().string()) || !nextKey.asString()->equals(strings.lazymonthCode().string())) {
containsMonth = true;
}
Value propValue;
fields.asObject()->get(state, AtomicString(state, nextKey.asString()), propValue);
if (!propValue.isUndefined()) {
merged->defineOwnPropertyThrowsException(state, AtomicString(state, nextKey.asString()), ObjectPropertyDescriptor(propValue, ObjectPropertyDescriptor::AllPresent));
}
}
if (!containsMonth) {
Value month;
fields.asObject()->get(state, AtomicString(state, "month"), month);
if (!month.isUndefined()) {
merged->defineOwnPropertyThrowsException(state, AtomicString(state, "month"), ObjectPropertyDescriptor(month, ObjectPropertyDescriptor::AllPresent));
}
Value monthCode;
fields.asObject()->get(state, AtomicString(state, "monthCode"), monthCode);
if (!monthCode.isUndefined()) {
merged->defineOwnPropertyThrowsException(state, AtomicString(state, "monthCode"), ObjectPropertyDescriptor(monthCode, ObjectPropertyDescriptor::AllPresent));
}
}
return Value(merged);
}
Value TemporalCalendarObject::getTemporalCalendarWithISODefault(ExecutionState& state, const Value& item)
{
if (item.isObject() && item.asObject()->isTemporalObject() && item.asObject()->asTemporalObject()->hasCalendar()) {
return item.asObject()->asTemporalObject()->getCalendar();
}
Value calendarLike = item.asObject()->Object::get(state, ObjectPropertyName(state.context()->staticStrings().calendar)).value(state, item);
return TemporalCalendarObject::toTemporalCalendarWithISODefault(state, calendarLike);
}
ValueVector TemporalCalendarObject::calendarFields(ExecutionState& state, const Value& calendar, const ValueVector& fieldNames)
{
Value fields = Object::getMethod(state, calendar.asObject(), ObjectPropertyName(state.context()->staticStrings().lazyfields()));
Value fieldsArray = Object::createArrayFromList(state, fieldNames);
if (!fields.isUndefined()) {
fieldsArray = Object::call(state, fields, calendar, 1, &fieldsArray);
}
return IteratorObject::iterableToListOfType(state, fieldsArray, state.context()->staticStrings().String.string());
}
Value TemporalCalendarObject::getterHelper(ExecutionState& state, const Value& callee, Object* thisValue, Value* argv)
{
Value result = Object::call(state, callee, thisValue, 1, argv);
if (result.isUndefined()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Undefined");
}
return result;
}
Value TemporalCalendarObject::calendarYear(ExecutionState& state, Object* calendar, const Value& dateLike)
{
return TemporalCalendarObject::getterHelper(state, calendar->get(state, state.context()->staticStrings().lazyYear()).value(state, calendar), calendar, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarMonth(ExecutionState& state, Object* calendar, const Value& dateLike)
{
return TemporalCalendarObject::getterHelper(state, calendar->get(state, state.context()->staticStrings().lazyMonth()).value(state, calendar), calendar, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarMonthCode(ExecutionState& state, Object* calendar, const Value& dateLike)
{
return TemporalCalendarObject::getterHelper(state, calendar->get(state, state.context()->staticStrings().lazymonthCode()).value(state, calendar), calendar, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarDay(ExecutionState& state, Object* calendar, const Value& dateLike)
{
return TemporalCalendarObject::getterHelper(state, calendar->get(state, state.context()->staticStrings().lazyDay()).value(state, calendar), calendar, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarDayOfWeek(ExecutionState& state, Object* calendar, const Value& dateLike)
{
Value dayOfWeek = calendar->get(state, state.context()->staticStrings().lazydayOfWeek()).value(state, calendar);
return Object::call(state, dayOfWeek, calendar, 1, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarDayOfYear(ExecutionState& state, Object* calendar, const Value& dateLike)
{
Value dayOfYear = calendar->get(state, state.context()->staticStrings().lazydayOfYear()).value(state, calendar);
return Object::call(state, dayOfYear, calendar, 1, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarWeekOfYear(ExecutionState& state, Object* calendar, const Value& dateLike)
{
Value weekOfYear = calendar->get(state, state.context()->staticStrings().lazyweekOfYear()).value(state, calendar);
return Object::call(state, weekOfYear, calendar, 1, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarDaysInWeek(ExecutionState& state, Object* calendar, const Value& dateLike)
{
Value daysInWeek = calendar->get(state, state.context()->staticStrings().lazydaysInWeek()).value(state, calendar);
return Object::call(state, daysInWeek, calendar, 1, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarDaysInMonth(ExecutionState& state, Object* calendar, const Value& dateLike)
{
Value daysInMonth = calendar->get(state, state.context()->staticStrings().lazydaysInMonth()).value(state, calendar);
return Object::call(state, daysInMonth, calendar, 1, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarDaysInYear(ExecutionState& state, Object* calendar, const Value& dateLike)
{
Value daysInYear = calendar->get(state, state.context()->staticStrings().lazydaysInYear()).value(state, calendar);
return Object::call(state, daysInYear, calendar, 1, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarMonthsInYear(ExecutionState& state, Object* calendar, const Value& dateLike)
{
Value monthsInYear = calendar->get(state, state.context()->staticStrings().lazymonthsInYear()).value(state, calendar);
return Object::call(state, monthsInYear, calendar, 1, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::calendarInLeapYear(ExecutionState& state, Object* calendar, const Value& dateLike)
{
Value inLeapYear = calendar->get(state, state.context()->staticStrings().lazyinLeapYear()).value(state, calendar);
return Object::call(state, inLeapYear, calendar, 1, const_cast<Value*>(&dateLike));
}
Value TemporalCalendarObject::dateFromFields(ExecutionState& state, const Value& calendar, const Value& fields, const Value& options)
{
ASSERT(calendar.isObject());
ASSERT(fields.isObject());
Value dateFromFields = calendar.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().lazydateFromFields())).value(state, calendar);
Value argv[2] = { fields, options };
return Object::call(state, dateFromFields, calendar, 2, argv);
}
int TemporalCalendarObject::toISOWeekOfYear(ExecutionState& state, const int year, const int month, const int day)
{
Value epochDays = DateObject::makeDay(state, Value(year), Value(month - 1), Value(day));
int dayOfYear = TemporalCalendarObject::dayOfYear(state, epochDays);
int dayOfWeek = DateObject::weekDay(DateObject::makeDate(state, epochDays, Value(0)).toUint32(state));
int dayOfWeekOfJanFirst = DateObject::weekDay(DateObject::makeDate(state, DateObject::makeDay(state, Value(year), Value(0), Value(1)), Value(0)).toUint32(state));
int weekNum = (dayOfYear + 6) / 7;
if (dayOfWeek < dayOfWeekOfJanFirst) {
++weekNum;
}
return weekNum;
}
Value TemporalCalendarObject::calendarDateAdd(ExecutionState& state, const Value& calendar, const Value& date, const Value& duration, const Value& options, Value dateAdd)
{
ASSERT(calendar.isObject());
ASSERT(options.isObject() || options.isUndefined());
if (dateAdd.isUndefined()) {
dateAdd = Object::getMethod(state, calendar, state.context()->staticStrings().lazydateAdd());
}
Value argv[3] = { date, duration, options };
return Object::call(state, dateAdd, calendar, 3, argv);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalCalendarObject::ISODateFromFields(ExecutionState& state, Value fields, const Value& options)
{
ASSERT(fields.isObject());
auto overflow = Temporal::toTemporalOverflow(state, options);
ValueVector fieldNames = { Value(state.context()->staticStrings().lazyDay().string()), Value(state.context()->staticStrings().lazyMonth().string()), Value(state.context()->staticStrings().lazymonthCode().string()), Value(state.context()->staticStrings().lazyYear().string()) };
ValueVector requiredFields = { Value(state.context()->staticStrings().lazyDay().string()), Value(state.context()->staticStrings().lazyYear().string()) };
fields = Temporal::prepareTemporalFields(state, fields, fieldNames, requiredFields);
auto year = fields.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().lazyYear().string())).value(state, fields);
ASSERT(year.isNumber());
int month = TemporalCalendarObject::resolveISOMonth(state, fields);
auto day = fields.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().lazyDay().string())).value(state, fields);
if (!day.isInt32()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "month and montCode is undefined");
}
int dayNumber = day.asInt32();
if (dayNumber < 1 || dayNumber > 31) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "day is out of range");
}
return TemporalPlainDateObject::regulateISODate(state, year.asInt32(), month, dayNumber, overflow);
}
int TemporalCalendarObject::resolveISOMonth(ExecutionState& state, const Value& fields)
{
auto month = fields.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().lazyMonth().string())).value(state, fields);
auto monthCode = fields.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().lazymonthCode().string())).value(state, fields);
if (monthCode.isUndefined()) {
if (!month.isInt32()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "month and montCode is undefined");
}
int monthNumber = month.asInt32();
if (monthNumber < 1 || monthNumber > 12) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "month is out of range");
}
return month.asInt32();
}
ASSERT(monthCode.isString());
if (monthCode.asString()->length() != 3) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "monthCode length is invalid");
}
if (monthCode.asString()->toNonGCUTF8StringData()[0] == 'm' || !std::isdigit(monthCode.asString()->toNonGCUTF8StringData()[1]) || !std::isdigit(monthCode.asString()->toNonGCUTF8StringData()[2])) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "monthCode is invalid");
}
int numberPart = stoi(monthCode.asString()->toNonGCUTF8StringData().substr(1));
if (numberPart < 1 || numberPart > 12) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "monthCode number is out of range");
}
if (!month.isUndefined() && numberPart != month.asInt32()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "monthCode number doesn't match with month");
}
return numberPart;
}
Value TemporalCalendarObject::calendarYearMonthFromFields(ExecutionState& state, const Value& calendar, const Value& fields, const Value& options)
{
Value argv[] = { fields, options };
return Object::call(state, Object::getMethod(state, calendar, ObjectPropertyName(state.context()->staticStrings().lazyyearMonthFromFields())), calendar, 2, argv);
}
bool TemporalCalendarObject::calendarEquals(const TemporalCalendarObject& firstCalendar, const TemporalCalendarObject& secondCalendar)
{
return &firstCalendar == &secondCalendar || firstCalendar.getIdentifier() == secondCalendar.getIdentifier();
}
bool TemporalCalendarObject::operator==(const TemporalCalendarObject& rhs) const
{
return calendarEquals(*this, rhs);
}
bool TemporalCalendarObject::operator!=(const TemporalCalendarObject& rhs) const
{
return !(rhs == *this);
}
Value TemporalCalendarObject::calendarMonthDayFromFields(ExecutionState& state, const Value& calendar, const Value& fields, const Value& options)
{
Value argv[] = { fields, options };
return Object::call(state, Object::getMethod(state, calendar, ObjectPropertyName(state.context()->staticStrings().lazymonthDayFromFields())), calendar, 2, argv);
}
Value TemporalCalendarObject::calendarDateUntil(ExecutionState& state, const Value& calendar, const Value& first, const Value& second, const Value& options, Value dateUntil)
{
ASSERT(calendar.isObject());
if (dateUntil.isUndefined()) {
dateUntil = Object::getMethod(state, calendar, ObjectPropertyName(state, state.context()->staticStrings().lazydateUntil().string()));
}
Value argv[] = { first, second, options };
return Object::call(state, calendar, dateUntil, 3, argv);
}
bool TemporalPlainDateObject::isValidISODate(ExecutionState& state, const int year, const int month, const int day)
{
if (month < 1 || month > 12) {
return false;
}
Value daysInMonth = TemporalCalendarObject::ISODaysInMonth(state, year, month);
if (day < 1 || day > daysInMonth.asInt32()) {
return false;
}
return true;
}
TemporalPlainDateObject::TemporalPlainDateObject(ExecutionState& state, Object* proto, const TemporalDate& date, TemporalCalendarObject* calendar)
: TemporalObject(state, proto)
, m_date(date)
, m_calendar(calendar)
{
}
Value TemporalPlainDateObject::createTemporalDate(ExecutionState& state, const int isoYear, const int isoMonth, const int isoDay, const Value& calendar, Optional<Object*> newTarget)
{
ASSERT(calendar.isObject());
if (!TemporalPlainDateObject::isValidISODate(state, isoYear, isoMonth, isoDay)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Not valid ISOdate");
}
if (!TemporalPlainDateTimeObject::ISODateTimeWithinLimits(state, isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Not valid ISOdate");
}
TemporalDate date(isoYear, isoMonth, isoDay);
return new TemporalPlainDateObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalPlainDatePrototype(), date, calendar.asObject()->asTemporalCalendarObject());
}
Value TemporalPlainDateObject::createFromPlainDateTimeObject(ExecutionState& state, TemporalPlainDateTimeObject* plainDateTime)
{
return new TemporalPlainDateObject(state, plainDateTime->getPrototypeObject(state), plainDateTime->date(), plainDateTime->getCalendar());
}
Value TemporalPlainDateObject::toTemporalDate(ExecutionState& state, const Value& item, Optional<Object*> options)
{
if (!options.hasValue()) {
options = new Object(state, Object::PrototypeIsNull);
}
ASSERT(options->isObject());
if (item.isObject()) {
Temporal* tItem = item.asObject()->asTemporalObject();
if (tItem->isTemporalPlainDateObject()) {
return item;
}
if (tItem->isTemporalZonedDateTimeObject()) {
Value instant = TemporalInstantObject::createTemporalInstant(state, tItem->asTemporalZonedDateTimeObject()->getNanoseconds());
Value plainDateTime = TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(state, tItem->asTemporalZonedDateTimeObject()->getTimeZone(), instant, tItem->asTemporalZonedDateTimeObject()->getCalendar());
return TemporalPlainDateObject::createTemporalDate(state, plainDateTime.asObject()->asTemporalPlainDateTimeObject()->getYear(), plainDateTime.asObject()->asTemporalPlainDateTimeObject()->getMonth(), plainDateTime.asObject()->asTemporalPlainDateTimeObject()->getDay(), plainDateTime.asObject()->asTemporalPlainDateTimeObject()->getCalendar());
}
if (tItem->isTemporalPlainDateTimeObject()) {
return TemporalPlainDateObject::createFromPlainDateTimeObject(state, tItem->asTemporalPlainDateTimeObject());
}
Value calendar = TemporalCalendarObject::getTemporalCalendarWithISODefault(state, item);
ValueVector requiredFields{
Value(state.context()->staticStrings().lazyDay().string()),
Value(state.context()->staticStrings().lazyMonth().string()),
Value(state.context()->staticStrings().lazymonthCode().string()),
Value(state.context()->staticStrings().lazyYear().string())
};
ValueVector fieldNames = TemporalCalendarObject::calendarFields(state, calendar, ValueVector());
Value fields = Temporal::prepareTemporalFields(state, item, fieldNames, ValueVector());
return TemporalCalendarObject::dateFromFields(state, calendar, fields, options.value());
}
Temporal::toTemporalOverflow(state, options.value());
auto result = TemporalObject::parseTemporalDateString(state, item.toString(state)->toNonGCUTF8StringData());
ASSERT(TemporalPlainDateObject::isValidISODate(state, result.year, result.month, result.day));
return TemporalPlainDateObject::createTemporalDate(state, result.year, result.month, result.day,
TemporalCalendarObject::toTemporalCalendarWithISODefault(state, Value(result.calendar)),
nullptr);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainDateObject::balanceISODate(ExecutionState& state, int year, int month, int day)
{
Value epochDays = DateObject::makeDay(state, Value(year), Value(month), Value(day));
ASSERT(std::isfinite(epochDays.asNumber()));
time64_t ms = DateObject::makeDate(state, epochDays, Value(0)).toLength(state);
return TemporalPlainDateObject::createISODateRecord(state, DateObject::yearFromTime(ms), DateObject::monthFromTime(ms) + 1, DateObject::dateFromTime(ms));
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainDateObject::createISODateRecord(ExecutionState& state, const int year, const int month, const int day)
{
ASSERT(TemporalPlainDateObject::isValidISODate(state, year, month, day));
return { { TemporalObject::YEAR_UNIT, year }, { TemporalObject::MONTH_UNIT, month }, { TemporalObject::DAY_UNIT, day } };
}
int TemporalPlainDateObject::compareISODate(int firstYear, int firstMonth, int firstDay, int secondYear, int secondMonth, int secondDay)
{
if (firstYear > secondYear) {
return 1;
} else if (firstYear < secondYear) {
return -1;
}
if (firstMonth > secondMonth) {
return 1;
} else if (firstMonth < secondMonth) {
return -1;
}
if (firstDay > secondDay) {
return 1;
} else if (firstDay < secondDay) {
return -1;
}
return 0;
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainDateObject::regulateISODate(ExecutionState& state, int year, int month, int day, const Value& overflow)
{
if (overflow.asString()->equals(state.context()->staticStrings().lazyConstrain().string())) {
if (month > 12) {
month = 12;
} else if (month < 1) {
month = 1;
}
if (day < 1) {
day = 1;
}
if (day > 28) {
int daysInMonth = TemporalCalendarObject::ISODaysInMonth(state, year, month).asInt32();
if (day > daysInMonth) {
day = daysInMonth;
}
}
} else {
ASSERT(overflow.asString()->equals(state.context()->staticStrings().reject.string()));
if (!TemporalPlainDateObject::isValidISODate(state, year, month, day)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Not valid ISODate");
}
}
return { { TemporalObject::YEAR_UNIT, year }, { TemporalObject::MONTH_UNIT, month }, { TemporalObject::DAY_UNIT, day } };
}
TemporalPlainDateTimeObject::TemporalPlainDateTimeObject(ExecutionState& state, Object* proto, const TemporalDate& date, const TemporalTime& time, TemporalCalendarObject* calendar)
: TemporalObject(state, proto)
, m_date(date)
, m_time(time)
, m_calendar(calendar)
{
}
uint64_t TemporalPlainDateTimeObject::getEpochFromISOParts(ExecutionState& state, const int year, const int month, const int day, const int hour, const int minute, const int second, const int millisecond, const int microsecond, const int nanosecond)
{
ASSERT(TemporalPlainDateObject::isValidISODate(state, year, month, day));
ASSERT(TemporalPlainTimeObject::isValidTime(state, hour, minute, second, millisecond, microsecond, nanosecond));
Value date = DateObject::makeDay(state, Value(year), Value(month), Value(day));
Value time = DateObject::makeTime(state, Value(hour), Value(minute), Value(second), Value(millisecond));
Value ms = DateObject::makeDate(state, date, time);
ASSERT(std::isfinite(ms.asNumber()));
return ms.toLength(state);
}
bool TemporalPlainDateTimeObject::ISODateTimeWithinLimits(ExecutionState& state, const int year, const int month, const int day, const int hour, const int minute, const int second, const int millisecond, const int microsecond, const int nanosecond)
{
time64_t ms = TemporalPlainDateTimeObject::getEpochFromISOParts(state, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
return IS_IN_TIME_RANGE(ms);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainDateTimeObject::balanceISODateTime(ExecutionState& state, const int year, const int month, const int day, const int hour, const int minute, const int second, const int millisecond, const int microsecond, const int nanosecond)
{
auto balancedTime = TemporalPlainTimeObject::balanceTime(state, hour, minute, second, millisecond, microsecond, nanosecond);
auto balancedDate = TemporalPlainDateObject::balanceISODate(state, year, month, day + balancedTime[TemporalObject::DAY_UNIT]);
balancedDate.insert(balancedTime.begin(), balancedTime.end());
return balancedDate;
}
Value TemporalPlainDateTimeObject::createTemporalDateTime(ExecutionState& state, const int isoYear, const int isoMonth, const int isoDay, const int hour, const int minute, const int second, const int millisecond, const int microsecond, const int nanosecond, const Value& calendar, Optional<Object*> newTarget)
{
ASSERT(calendar.isObject());
if (!TemporalPlainDateObject::isValidISODate(state, isoYear, isoMonth, isoDay)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "invalid Date");
}
if (!TemporalPlainTimeObject::isValidTime(state, hour, minute, second, millisecond, microsecond, nanosecond)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "invalid Time");
}
if (!TemporalPlainDateTimeObject::ISODateTimeWithinLimits(state, isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "ISODateTime is out of limits");
}
TemporalDate date(isoYear, isoMonth, isoDay);
TemporalTime time(hour, minute, second, millisecond, microsecond, nanosecond);
return new TemporalPlainDateTimeObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalPlainDateTimePrototype(), date, time, calendar.asObject()->asTemporalCalendarObject());
}
Value TemporalPlainDateTimeObject::createFromPlainDateObject(ExecutionState& state, TemporalPlainDateObject* plainDate)
{
TemporalTime time;
return new TemporalPlainDateTimeObject(state, plainDate->getPrototypeObject(state), plainDate->date(), time, plainDate->calendar());
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainDateTimeObject::interpretTemporalDateTimeFields(ExecutionState& state, const Value& calendar, const Value& fields, const Value& options)
{
auto timeResult = TemporalPlainTimeObject::toTemporalTimeRecord(state, fields);
auto overFlow = Temporal::toTemporalOverflow(state, options);
auto temporalDate = TemporalCalendarObject::dateFromFields(state, calendar, fields, options).asObject()->asTemporalPlainDateObject();
timeResult = TemporalPlainTimeObject::regulateTime(state, timeResult[TemporalObject::HOUR_UNIT], timeResult[TemporalObject::MINUTE_UNIT], timeResult[TemporalObject::SECOND_UNIT], timeResult[TemporalObject::MILLISECOND_UNIT], timeResult[TemporalObject::MICROSECOND_UNIT], timeResult[TemporalObject::NANOSECOND_UNIT], overFlow);
timeResult[TemporalObject::YEAR_UNIT] = temporalDate->year();
timeResult[TemporalObject::MONTH_UNIT] = static_cast<unsigned char>(temporalDate->month());
timeResult[TemporalObject::DAY_UNIT] = static_cast<unsigned char>(temporalDate->day());
return timeResult;
}
Value TemporalPlainDateTimeObject::toTemporalDateTime(ExecutionState& state, const Value& item, const Value& options)
{
ASSERT(options.isObject() || options.isUndefined());
Value calendar;
std::map<TemporalObject::DateTimeUnits, int> result = {};
if (item.isObject()) {
if (item.asObject()->isTemporalPlainDateTimeObject()) {
return item;
} else if (item.asObject()->isTemporalZonedDateTimeObject()) {
Value instant = TemporalInstantObject::createTemporalInstant(state, item.asObject()->asTemporalZonedDateTimeObject()->getNanoseconds());
return TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(state, item.asObject()->asTemporalZonedDateTimeObject()->getTimeZone(), instant, item.asObject()->asTemporalZonedDateTimeObject()->getCalendar());
} else if (item.asObject()->isTemporalPlainDateObject()) {
return TemporalPlainDateTimeObject::createFromPlainDateObject(state, item.asObject()->asTemporalPlainDateObject());
}
calendar = TemporalCalendarObject::getTemporalCalendarWithISODefault(state, item);
ValueVector values = { Value(state.context()->staticStrings().lazyDay().string()),
Value(state.context()->staticStrings().lazyHour().string()),
Value(state.context()->staticStrings().lazymicrosecond().string()),
Value(state.context()->staticStrings().lazymillisecond().string()),
Value(state.context()->staticStrings().lazyMinute().string()),
Value(state.context()->staticStrings().lazyMonth().string()),
Value(state.context()->staticStrings().lazymonthCode().string()),
Value(state.context()->staticStrings().lazynanosecond().string()),
Value(state.context()->staticStrings().lazySecond().string()),
Value(state.context()->staticStrings().lazyYear().string()) };
ValueVector fieldNames = TemporalCalendarObject::calendarFields(state, calendar, values);
Value fields = Temporal::prepareTemporalFields(state, item, fieldNames, ValueVector());
result = TemporalPlainDateTimeObject::interpretTemporalDateTimeFields(state, calendar, fields, options);
return TemporalPlainDateTimeObject::createTemporalDateTime(state, result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT], calendar, new Object(state));
}
Temporal::toTemporalOverflow(state, options);
auto tmp = TemporalObject::parseTemporalDateTimeString(state, item.toString(state)->toNonGCUTF8StringData());
ASSERT(TemporalPlainDateObject::isValidISODate(state, tmp.year, tmp.month, tmp.day));
ASSERT(TemporalPlainTimeObject::isValidTime(state, tmp.hour, tmp.minute, tmp.second, tmp.millisecond, tmp.microsecond, tmp.nanosecond));
return TemporalPlainDateTimeObject::createTemporalDateTime(state, tmp.year, tmp.month, tmp.day, tmp.hour, tmp.minute, tmp.second, tmp.millisecond, tmp.microsecond, tmp.nanosecond, TemporalCalendarObject::toTemporalCalendarWithISODefault(state, Value(tmp.calendar)), new Object(state));
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainDateTimeObject::addDateTime(ExecutionState& state, std::map<TemporalObject::DateTimeUnits, int>& first, const Value& calendar, std::map<TemporalObject::DateTimeUnits, int>& second, Object* options)
{
ASSERT(TemporalPlainDateTimeObject::ISODateTimeWithinLimits(state, first[TemporalObject::YEAR_UNIT], first[TemporalObject::MONTH_UNIT], first[TemporalObject::DAY_UNIT], first[TemporalObject::HOUR_UNIT], first[TemporalObject::MINUTE_UNIT], first[TemporalObject::SECOND_UNIT], first[TemporalObject::MILLISECOND_UNIT], first[TemporalObject::MICROSECOND_UNIT], first[TemporalObject::NANOSECOND_UNIT]));
auto timeResult = TemporalPlainTimeObject::addTime(state, first, second);
auto datePart = TemporalPlainDateObject::createTemporalDate(state, first[TemporalObject::YEAR_UNIT], first[TemporalObject::MONTH_UNIT], first[TemporalObject::DAY_UNIT], calendar).asObject()->asTemporalPlainDateObject();
auto dateDuration = TemporalDurationObject::createTemporalDuration(state, second[TemporalObject::YEAR_UNIT], second[TemporalObject::MONTH_UNIT], second[TemporalObject::WEEK_UNIT], second[TemporalObject::DAY_UNIT] + timeResult[TemporalObject::DAY_UNIT], 0, 0, 0, 0, 0, 0);
auto addedDate = TemporalCalendarObject::calendarDateAdd(state, datePart, dateDuration, options).asObject()->asTemporalPlainDateObject();
std::map<TemporalObject::DateTimeUnits, int> result = { { TemporalObject::YEAR_UNIT, addedDate->year() }, { TemporalObject::MONTH_UNIT, addedDate->month() }, { TemporalObject::DAY_UNIT, addedDate->day() } };
result.insert(timeResult.begin(), timeResult.end());
return result;
}
Value TemporalPlainDateTimeObject::addDurationToOrSubtractDurationFromPlainDateTime(ExecutionState& state, TemporalPlainTimeObject::Operation operation, TemporalPlainDateTimeObject* temporalDateTime, const Value& temporalDurationLike, const Value& options)
{
auto durationObject = TemporalDurationObject::toTemporalDuration(state, temporalDurationLike).asObject()->asTemporalDurationObject();
std::map<TemporalObject::DateTimeUnits, int> time = { { TemporalObject::YEAR_UNIT, temporalDateTime->getYear() }, { TemporalObject::MONTH_UNIT, temporalDateTime->getMonth() }, { TemporalObject::DAY_UNIT, temporalDateTime->getDay() }, { TemporalObject::HOUR_UNIT, temporalDateTime->getHour() }, { TemporalObject::MINUTE_UNIT, temporalDateTime->getMinute() }, { TemporalObject::SECOND_UNIT, temporalDateTime->getSecond() }, { TemporalObject::MILLISECOND_UNIT, temporalDateTime->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, temporalDateTime->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, temporalDateTime->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> duration = { { TemporalObject::YEAR_UNIT, operation * durationObject->getYear() }, { TemporalObject::MONTH_UNIT, operation * durationObject->getMonth() }, { TemporalObject::WEEK_UNIT, operation * durationObject->getWeek() }, { TemporalObject::DAY_UNIT, operation * durationObject->getDay() }, { TemporalObject::HOUR_UNIT, operation * durationObject->getHour() }, { TemporalObject::MINUTE_UNIT, operation * durationObject->getMinute() }, { TemporalObject::SECOND_UNIT, operation * durationObject->getMinute() }, { TemporalObject::MILLISECOND_UNIT, operation * durationObject->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, operation * durationObject->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, operation * durationObject->getNanosecond() } };
auto result = TemporalPlainDateTimeObject::addDateTime(state, time, temporalDateTime->getCalendar(), duration, Temporal::getOptionsObject(state, options).asObject());
if (!TemporalPlainDateObject::isValidISODate(state, result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::DAY_UNIT]) || !TemporalPlainTimeObject::isValidTime(state, result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT])) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid date or time");
}
return TemporalPlainDateTimeObject::createTemporalDateTime(state, result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT], temporalDateTime->getCalendar());
}
std::map<TemporalObject::DateTimeUnits, int> TemporalPlainDateTimeObject::differenceISODateTime(ExecutionState& state, std::map<TemporalObject::DateTimeUnits, int> first, std::map<TemporalObject::DateTimeUnits, int> second, const Value& calendar, TemporalObject::DateTimeUnits largestUnit, const Value& options)
{
if (!TemporalPlainDateTimeObject::ISODateTimeWithinLimits(state, first[TemporalObject::YEAR_UNIT], first[TemporalObject::MONTH_UNIT], first[TemporalObject::DAY_UNIT], first[TemporalObject::HOUR_UNIT], first[TemporalObject::MINUTE_UNIT], first[TemporalObject::SECOND_UNIT], first[TemporalObject::MILLISECOND_UNIT], first[TemporalObject::MICROSECOND_UNIT], first[TemporalObject::NANOSECOND_UNIT])
|| !TemporalPlainDateTimeObject::ISODateTimeWithinLimits(state, second[TemporalObject::YEAR_UNIT], second[TemporalObject::MONTH_UNIT], second[TemporalObject::DAY_UNIT], second[TemporalObject::HOUR_UNIT], second[TemporalObject::MINUTE_UNIT], second[TemporalObject::SECOND_UNIT], first[TemporalObject::MILLISECOND_UNIT], second[TemporalObject::MICROSECOND_UNIT], second[TemporalObject::NANOSECOND_UNIT])) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid date or time");
}
std::map<TemporalObject::DateTimeUnits, int> timeDifference = TemporalPlainTimeObject::differenceTime(state, first, second);
int time[] = { 0, 0, 0, 0, timeDifference[TemporalObject::HOUR_UNIT], timeDifference[TemporalObject::MINUTE_UNIT], timeDifference[TemporalObject::SECOND_UNIT], timeDifference[TemporalObject::MILLISECOND_UNIT], timeDifference[TemporalObject::MICROSECOND_UNIT], timeDifference[TemporalObject::NANOSECOND_UNIT] };
int timeSign = TemporalDurationObject::durationSign(time);
int dateSign = TemporalPlainDateObject::compareISODate(second[TemporalObject::YEAR_UNIT], second[TemporalObject::MONTH_UNIT], second[TemporalObject::DAY_UNIT], first[TemporalObject::YEAR_UNIT], first[TemporalObject::MONTH_UNIT], first[TemporalObject::DAY_UNIT]);
std::map<TemporalObject::DateTimeUnits, int> adjustedDate = TemporalPlainDateObject::createISODateRecord(state, first[TemporalObject::YEAR_UNIT], first[TemporalObject::MONTH_UNIT], first[TemporalObject::DAY_UNIT]);
if (timeSign == -dateSign) {
adjustedDate = TemporalPlainDateObject::balanceISODate(state, adjustedDate[TemporalObject::YEAR_UNIT], adjustedDate[TemporalObject::MONTH_UNIT], adjustedDate[TemporalObject::DAY_UNIT]);
std::map<TemporalObject::DateTimeUnits, int> tmp = { { TemporalObject::DAY_UNIT, -timeSign } };
tmp.insert(timeDifference.begin(), timeDifference.end());
timeDifference = TemporalDurationObject::balanceDuration(state, tmp, largestUnit);
}
TemporalPlainDateObject* date1 = TemporalPlainDateObject::createTemporalDate(state, adjustedDate[TemporalObject::YEAR_UNIT], adjustedDate[TemporalObject::MONTH_UNIT], adjustedDate[TemporalObject::DAY_UNIT], calendar).asObject()->asTemporalPlainDateObject();
TemporalPlainDateObject* date2 = TemporalPlainDateObject::createTemporalDate(state, second[TemporalObject::YEAR_UNIT], second[TemporalObject::MONTH_UNIT], second[TemporalObject::DAY_UNIT], calendar).asObject()->asTemporalPlainDateObject();
TemporalObject::DateTimeUnits dateLargestUnit = TemporalObject::DAY_UNIT > largestUnit ? TemporalObject::DAY_UNIT : largestUnit;
Object* untilOptions = TemporalObject::mergeLargestUnitOption(state, options, largestUnit);
TemporalDurationObject* dateDifference = TemporalCalendarObject::calendarDateUntil(state, calendar, date1, date2, untilOptions).asObject()->asTemporalDurationObject();
std::map<TemporalObject::DateTimeUnits, int> balanceResult = TemporalDurationObject::balanceDuration(state, { { TemporalObject::DAY_UNIT, dateDifference->getDay() }, { TemporalObject::HOUR_UNIT, timeDifference[TemporalObject::HOUR_UNIT] }, { TemporalObject::MINUTE_UNIT, timeDifference[TemporalObject::MINUTE_UNIT] }, { TemporalObject::SECOND_UNIT, timeDifference[TemporalObject::SECOND_UNIT] }, { TemporalObject::MILLISECOND_UNIT, timeDifference[TemporalObject::MILLISECOND_UNIT] }, { TemporalObject::MICROSECOND_UNIT, timeDifference[TemporalObject::MICROSECOND_UNIT] }, { TemporalObject::NANOSECOND_UNIT, timeDifference[TemporalObject::NANOSECOND_UNIT] } }, largestUnit);
return TemporalDurationObject::createDurationRecord(state, dateDifference->getYear(), dateDifference->getMonth(), dateDifference->getWeek(), balanceResult[TemporalObject::DAY_UNIT], balanceResult[TemporalObject::HOUR_UNIT], balanceResult[TemporalObject::MINUTE_UNIT], balanceResult[TemporalObject::SECOND_UNIT], balanceResult[TemporalObject::MILLISECOND_UNIT], balanceResult[TemporalObject::MICROSECOND_UNIT], balanceResult[TemporalObject::NANOSECOND_UNIT]);
}
TemporalZonedDateTimeObject::TemporalZonedDateTimeObject(ExecutionState& state, const BigInt* nanoseconds, const TemporalTimeZoneObject* timeZone, TemporalCalendarObject* calendar)
: TemporalZonedDateTimeObject(state, state.context()->globalObject()->objectPrototype(), nanoseconds, timeZone, calendar)
{
}
TemporalZonedDateTimeObject::TemporalZonedDateTimeObject(ExecutionState& state, Object* proto, const BigInt* nanoseconds, const TemporalTimeZoneObject* timeZone, TemporalCalendarObject* calendar)
: TemporalObject(state, proto)
, m_nanoseconds(nanoseconds)
, m_timeZone(timeZone)
, m_calendar(calendar)
{
}
Value TemporalZonedDateTimeObject::createTemporalZonedDateTime(ExecutionState& state, const BigInt& epochNanoseconds, const TemporalTimeZoneObject* timeZone, const TemporalCalendarObject* calendar, Optional<Object*> newTarget)
{
ASSERT(TemporalInstantObject::isValidEpochNanoseconds(&epochNanoseconds));
return new TemporalZonedDateTimeObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalZonedDateTimePrototype(), &epochNanoseconds, timeZone, const_cast<TemporalCalendarObject*>(calendar));
}
Value TemporalZonedDateTimeObject::toTemporalZonedDateTime(ExecutionState& state, const Value& item, const Value& options)
{
ASSERT(options.isUndefined() || options.isObject());
std::map<TemporalObject::DateTimeUnits, int> result;
MatchBehaviour matchBehaviour = TemporalZonedDateTimeObject::EXACTLY;
OffsetBehaviour offsetBehaviour = TemporalZonedDateTimeObject::OPTION;
String* offsetString;
TemporalTimeZoneObject* timeZone;
TemporalCalendarObject* calendar;
if (item.isObject()) {
if (item.asObject()->isTemporalZonedDateTimeObject()) {
return item;
}
calendar = TemporalCalendarObject::getTemporalCalendarWithISODefault(state, item).asObject()->asTemporalCalendarObject();
ValueVector values = { Value(state.context()->staticStrings().lazyDay().string()),
Value(state.context()->staticStrings().lazyHour().string()),
Value(state.context()->staticStrings().lazymicrosecond().string()),
Value(state.context()->staticStrings().lazymillisecond().string()),
Value(state.context()->staticStrings().lazyMinute().string()),
Value(state.context()->staticStrings().lazyMonth().string()),
Value(state.context()->staticStrings().lazymonthCode().string()),
Value(state.context()->staticStrings().lazynanosecond().string()),
Value(state.context()->staticStrings().lazySecond().string()),
Value(state.context()->staticStrings().lazyYear().string()) };
auto fieldNames = TemporalCalendarObject::calendarFields(state, calendar, values);
fieldNames.push_back(Value(state.context()->staticStrings().lazytimeZone().string()));
fieldNames.push_back(Value(state.context()->staticStrings().lazyoffset().string()));
auto fields = Temporal::prepareTemporalFields(state, item, fieldNames, { Value(state.context()->staticStrings().lazytimeZone().string()) });
timeZone = TemporalTimeZoneObject::toTemporalTimeZone(state, fields.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().lazytimeZone())).value(state, fields)).asObject()->asTemporalTimeZoneObject();
auto offsetStringValue = fields.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().lazyoffset())).value(state, fields);
if (offsetStringValue.isUndefined()) {
offsetBehaviour = TemporalZonedDateTimeObject::WALL;
} else {
offsetString = offsetStringValue.asString();
}
result = TemporalPlainDateTimeObject::interpretTemporalDateTimeFields(state, calendar, fields, options);
} else {
Temporal::toTemporalOverflow(state, options);
auto parseResult = TemporalObject::parseTemporalZonedDateTimeString(state, item.asString()->toNonGCUTF8StringData());
ASSERT(parseResult.tz->name->length() != 0);
unsigned int index = 0;
std::string timeZoneName = parseResult.tz->name->toNonGCUTF8StringData();
if (TemporalObject::offset(state, timeZoneName, index).empty()) {
if (!TemporalTimeZoneObject::isValidTimeZoneName(timeZoneName)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid TimeZone identifier");
}
parseResult.tz->name = new ASCIIString(TemporalTimeZoneObject::canonicalizeTimeZoneName(timeZoneName).c_str());
}
offsetString = parseResult.tz->offsetString;
if (parseResult.tz->z) {
offsetBehaviour = TemporalZonedDateTimeObject::EXACT;
} else if (offsetString->length() == 0) {
offsetBehaviour = TemporalZonedDateTimeObject::WALL;
}
timeZone = TemporalTimeZoneObject::createTemporalTimeZone(state, timeZoneName).asObject()->asTemporalTimeZoneObject();
calendar = TemporalCalendarObject::toTemporalCalendarWithISODefault(state, parseResult.calendar).asObject()->asTemporalCalendarObject();
matchBehaviour = TemporalZonedDateTimeObject::MINUTES;
}
int64_t offsetNanoseconds = 0;
if (offsetBehaviour == TemporalZonedDateTimeObject::OPTION) {
offsetNanoseconds = TemporalInstantObject::offsetStringToNanoseconds(state, offsetString);
}
auto disambiguation = Temporal::toTemporalDisambiguation(state, options);
auto offsetOption = Temporal::toTemporalOffset(state, options, state.context()->staticStrings().reject.string());
auto epochNanoseconds = TemporalZonedDateTimeObject::interpretISODateTimeOffset(state, result, offsetBehaviour, offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour).toBigInt(state);
return TemporalZonedDateTimeObject::createTemporalZonedDateTime(state, *epochNanoseconds, timeZone, calendar);
}
Value TemporalZonedDateTimeObject::interpretISODateTimeOffset(ExecutionState& state, std::map<TemporalObject::DateTimeUnits, int>& dateTime, TemporalZonedDateTimeObject::OffsetBehaviour offsetBehaviour, time64_t offsetNanoseconds, const Value& timeZone, const Value& disambiguation, const Value& offsetOption, TemporalZonedDateTimeObject::MatchBehaviour matchBehaviour)
{
auto calendar = TemporalCalendarObject::getISO8601Calendar(state).asObject()->asTemporalCalendarObject();
auto dateTimeObject = TemporalPlainDateTimeObject::createTemporalDateTime(state, dateTime[TemporalObject::YEAR_UNIT], dateTime[TemporalObject::MONTH_UNIT], dateTime[TemporalObject::DAY_UNIT], dateTime[TemporalObject::HOUR_UNIT], dateTime[TemporalObject::MINUTE_UNIT], dateTime[TemporalObject::SECOND_UNIT], dateTime[TemporalObject::MILLISECOND_UNIT], dateTime[TemporalObject::MICROSECOND_UNIT], dateTime[TemporalObject::NANOSECOND_UNIT], calendar, nullptr);
if (offsetBehaviour == TemporalZonedDateTimeObject::WALL || offsetOption.toString(state)->equals(state.context()->staticStrings().lazyignore().string())) {
return TemporalTimeZoneObject::builtinTimeZoneGetInstantFor(state, timeZone, dateTimeObject, disambiguation).asObject()->asTemporalInstantObject()->getNanoseconds();
}
if (offsetBehaviour == TemporalZonedDateTimeObject::EXACT || offsetOption.toString(state)->equals(state.context()->staticStrings().lazyuse().string())) {
time64_t epochNanoseconds = TemporalPlainDateTimeObject::getEpochFromISOParts(state, dateTime[TemporalObject::YEAR_UNIT], dateTime[TemporalObject::MONTH_UNIT], dateTime[TemporalObject::DAY_UNIT], dateTime[TemporalObject::HOUR_UNIT], dateTime[TemporalObject::MINUTE_UNIT], dateTime[TemporalObject::SECOND_UNIT], dateTime[TemporalObject::MILLISECOND_UNIT], dateTime[TemporalObject::MICROSECOND_UNIT], dateTime[TemporalObject::NANOSECOND_UNIT]) - offsetNanoseconds;
if (!TemporalInstantObject::isValidEpochNanoseconds(Value(epochNanoseconds))) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid epoch nanoseconds");
}
return Value(epochNanoseconds);
}
ASSERT(offsetBehaviour == TemporalZonedDateTimeObject::OPTION);
ASSERT(offsetOption.toString(state)->equals(state.context()->staticStrings().lazyprefer().string()) || offsetOption.toString(state)->equals(state.context()->staticStrings().reject.string()));
auto possibleInstants = TemporalTimeZoneObject::getPossibleInstantsFor(state, timeZone, dateTimeObject);
for (auto& candidate : possibleInstants) {
time64_t candidateNanoseconds = TemporalTimeZoneObject::getOffsetNanosecondsFor(state, timeZone, candidate);
if (candidateNanoseconds == offsetNanoseconds) {
return candidate.asObject()->asTemporalInstantObject()->getNanoseconds();
}
if (matchBehaviour == TemporalZonedDateTimeObject::MINUTES) {
return candidate.asObject()->asTemporalInstantObject()->getNanoseconds();
}
}
if (offsetOption.toString(state)->equals(state.context()->staticStrings().reject.string())) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "option is reject");
}
return TemporalTimeZoneObject::disambiguatePossibleInstants(state, possibleInstants, timeZone, dateTimeObject.asObject()->asTemporalPlainDateTimeObject(), disambiguation).asObject()->asTemporalInstantObject()->getNanoseconds();
}
TemporalPlainDateTimeObject* TemporalZonedDateTimeObject::toTemporalPlainDateTime(ExecutionState& state)
{
auto instant = TemporalInstantObject::createTemporalInstant(state, this->getNanoseconds());
return TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(state, this->getTimeZone(), instant, this->getCalendar()).asObject()->asTemporalPlainDateTimeObject();
}
Value TemporalZonedDateTimeObject::addDurationToOrSubtractDurationFromZonedDateTime(ExecutionState& state, TemporalPlainTimeObject::Operation operation, TemporalZonedDateTimeObject* zonedDateTimeObject, const Value& temporalDurationLike, const Value& options)
{
auto durationObject = TemporalDurationObject::toTemporalDuration(state, temporalDurationLike).asObject()->asTemporalDurationObject();
auto timeZone = zonedDateTimeObject->getTimeZone();
auto calendar = zonedDateTimeObject->getCalendar();
std::map<TemporalObject::DateTimeUnits, int> duration = { { TemporalObject::YEAR_UNIT, operation * durationObject->getYear() }, { TemporalObject::MONTH_UNIT, operation * durationObject->getMonth() }, { TemporalObject::WEEK_UNIT, operation * durationObject->getWeek() }, { TemporalObject::DAY_UNIT, operation * durationObject->getDay() }, { TemporalObject::HOUR_UNIT, operation * durationObject->getHour() }, { TemporalObject::MINUTE_UNIT, operation * durationObject->getMinute() }, { TemporalObject::SECOND_UNIT, operation * durationObject->getMinute() }, { TemporalObject::MILLISECOND_UNIT, operation * durationObject->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, operation * durationObject->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, operation * durationObject->getNanosecond() } };
auto epochNanoseconds = TemporalZonedDateTimeObject::addZonedDateTime(state, zonedDateTimeObject->getNanoseconds(), timeZone, calendar, duration, options);
return TemporalZonedDateTimeObject::createTemporalZonedDateTime(state, *epochNanoseconds, timeZone, calendar);
}
BigInt* TemporalZonedDateTimeObject::addZonedDateTime(ExecutionState& state, const BigInt* epochNanoseconds, const TemporalTimeZoneObject* timeZone, const TemporalCalendarObject* calendar, std::map<TemporalObject::DateTimeUnits, int> duration, const Value& options)
{
ASSERT(options.isObject() || options.isUndefined());
if (duration[TemporalObject::YEAR_UNIT] == 0 && duration[TemporalObject::MONTH_UNIT] == 0 && duration[TemporalObject::WEEK_UNIT] == 0 && duration[TemporalObject::DAY_UNIT] == 0) {
return TemporalInstantObject::addInstant(state, epochNanoseconds, duration);
}
auto instant = TemporalInstantObject::createTemporalInstant(state, epochNanoseconds);
auto temporalDateTime = TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(state, timeZone, instant, calendar).asObject()->asTemporalPlainDateTimeObject();
auto datePart = TemporalPlainDateObject::createTemporalDate(state, temporalDateTime->getYear(), temporalDateTime->getMonth(), temporalDateTime->getDay(), calendar);
auto dateDuration = TemporalDurationObject::createTemporalDuration(state, duration[TemporalObject::YEAR_UNIT], duration[TemporalObject::MONTH_UNIT], duration[TemporalObject::WEEK_UNIT], duration[TemporalObject::DAY_UNIT], 0, 0, 0, 0, 0, 0);
auto addedDate = TemporalCalendarObject::calendarDateAdd(state, calendar, datePart, dateDuration, options).asObject()->asTemporalPlainDateObject();
auto intermediateDateTime = TemporalPlainDateTimeObject::createTemporalDateTime(state, addedDate->year(), addedDate->month(), addedDate->day(), temporalDateTime->getHour(), temporalDateTime->getMinute(), temporalDateTime->getSecond(), temporalDateTime->getMillisecond(), temporalDateTime->getMicrosecond(), temporalDateTime->getNanosecond(), calendar);
auto intermediateInstant = TemporalTimeZoneObject::builtinTimeZoneGetInstantFor(state, timeZone, intermediateDateTime, Value(state.context()->staticStrings().lazycompatible().string()));
return TemporalInstantObject::addInstant(state, intermediateInstant.asObject()->asTemporalInstantObject()->getNanoseconds(), duration);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalZonedDateTimeObject::nanosecondsToDays(ExecutionState& state, int64_t nanoseconds, const Value relativeTo)
{
int64_t dayLengthNs = TemporalInstantObject::dayToNanosecond;
if (nanoseconds == 0) {
return { { TemporalObject::DAY_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, 0 }, { TemporalObject::DAY_LENGTH, dayLengthNs } };
}
int sign = 1;
if (nanoseconds < 0) {
sign = -1;
}
if (!relativeTo.isObject() || !relativeTo.asObject()->isTemporalZonedDateTimeObject()) {
return { { TemporalObject::DAY_UNIT, std::floor(nanoseconds / dayLengthNs) }, { TemporalObject::NANOSECOND_UNIT, (std::abs(nanoseconds) % dayLengthNs) * sign }, { TemporalObject::DAY_LENGTH, dayLengthNs } };
}
TemporalZonedDateTimeObject* relativeToObject = relativeTo.asObject()->asTemporalZonedDateTimeObject();
TemporalPlainDateTimeObject* startDateTime = TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(state, relativeToObject->getTimeZone(), TemporalInstantObject::createTemporalInstant(state, relativeToObject->getNanoseconds()), relativeToObject->getCalendar()).asObject()->asTemporalPlainDateTimeObject();
BigInt* endNs = relativeToObject->getNanoseconds()->addition(state, new BigInt(nanoseconds));
if (!TemporalInstantObject::isValidEpochNanoseconds(endNs)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "epochNanosecond is out of limits");
}
TemporalInstantObject* endInstant = TemporalInstantObject::createTemporalInstant(state, endNs).asObject()->asTemporalInstantObject();
TemporalPlainDateTimeObject* endDateTime = TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(state, relativeToObject->getTimeZone(), endInstant, relativeToObject->getCalendar()).asObject()->asTemporalPlainDateTimeObject();
std::map<TemporalObject::DateTimeUnits, int> start = { { TemporalObject::YEAR_UNIT, startDateTime->getYear() }, { TemporalObject::MONTH_UNIT, startDateTime->getMonth() }, { TemporalObject::DAY_UNIT, startDateTime->getDay() }, { TemporalObject::HOUR_UNIT, startDateTime->getHour() }, { TemporalObject::MINUTE_UNIT, startDateTime->getMinute() }, { TemporalObject::SECOND_UNIT, startDateTime->getSecond() }, { TemporalObject::MILLISECOND_UNIT, startDateTime->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, startDateTime->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, startDateTime->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> end = { { TemporalObject::YEAR_UNIT, endDateTime->getYear() }, { TemporalObject::MONTH_UNIT, endDateTime->getMonth() }, { TemporalObject::DAY_UNIT, endDateTime->getDay() }, { TemporalObject::HOUR_UNIT, endDateTime->getHour() }, { TemporalObject::MINUTE_UNIT, endDateTime->getMinute() }, { TemporalObject::SECOND_UNIT, endDateTime->getSecond() }, { TemporalObject::MILLISECOND_UNIT, endDateTime->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, endDateTime->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, endDateTime->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> dateDifference = TemporalPlainDateTimeObject::differenceISODateTime(state, start, end, relativeToObject->getCalendar(), TemporalObject::DAY_UNIT, new Object(state, Object::PrototypeIsNull));
int days = dateDifference[TemporalObject::DAY_UNIT];
BigInt* intermediateNs = TemporalZonedDateTimeObject::addZonedDateTime(state, relativeToObject->getNanoseconds(), relativeToObject->getTimeZone(), relativeToObject->getCalendar(), { { TemporalObject::YEAR_UNIT, 0 }, { TemporalObject::MONTH_UNIT, 0 }, { TemporalObject::DAY_UNIT, days }, { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::MINUTE_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, 0 } });
if (sign == 1) {
while (days > 0 && intermediateNs->greaterThan(endNs)) {
days--;
intermediateNs = TemporalZonedDateTimeObject::addZonedDateTime(state, relativeToObject->getNanoseconds(), relativeToObject->getTimeZone(), relativeToObject->getCalendar(), { { TemporalObject::YEAR_UNIT, 0 }, { TemporalObject::MONTH_UNIT, 0 }, { TemporalObject::DAY_UNIT, days }, { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::MINUTE_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, 0 } });
}
}
nanoseconds = endNs->subtraction(state, intermediateNs)->toInt64();
bool done = false;
while (!done) {
BigInt* oneDayFartherNs = TemporalZonedDateTimeObject::addZonedDateTime(state, intermediateNs, relativeToObject->getTimeZone(), relativeToObject->getCalendar(), { { TemporalObject::YEAR_UNIT, 0 }, { TemporalObject::MONTH_UNIT, 0 }, { TemporalObject::DAY_UNIT, sign }, { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::MINUTE_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, 0 } });
dayLengthNs = oneDayFartherNs->subtraction(state, intermediateNs)->toInt64();
if ((nanoseconds - dayLengthNs) * sign >= 0) {
nanoseconds -= dayLengthNs;
intermediateNs = oneDayFartherNs;
days += sign;
} else {
done = true;
}
}
return { { TemporalObject::DAY_UNIT, days }, { TemporalObject::NANOSECOND_UNIT, nanoseconds }, { TemporalObject::DAY_LENGTH, dayLengthNs } };
}
std::map<TemporalObject::DateTimeUnits, int> TemporalZonedDateTimeObject::differenceZonedDateTime(ExecutionState& state, BigInt* ns1, BigInt* ns2, const Value& timeZone, const Value& calendar, TemporalObject::DateTimeUnits largestUnit, const Value& options)
{
if (ns1->equals(ns2)) {
return TemporalDurationObject::createDurationRecord(state, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
TemporalInstantObject* startInstant = TemporalInstantObject::createTemporalInstant(state, ns1).asObject()->asTemporalInstantObject();
TemporalPlainDateTimeObject* startDateTime = TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(state, timeZone, startInstant, calendar).asObject()->asTemporalPlainDateTimeObject();
TemporalInstantObject* endInstant = TemporalInstantObject::createTemporalInstant(state, ns2).asObject()->asTemporalInstantObject();
TemporalPlainDateTimeObject* endDateTime = TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(state, timeZone, endInstant, calendar).asObject()->asTemporalPlainDateTimeObject();
std::map<TemporalObject::DateTimeUnits, int> start = { { TemporalObject::YEAR_UNIT, startDateTime->getYear() }, { TemporalObject::MONTH_UNIT, startDateTime->getMonth() }, { TemporalObject::DAY_UNIT, startDateTime->getDay() }, { TemporalObject::HOUR_UNIT, startDateTime->getHour() }, { TemporalObject::SECOND_UNIT, startDateTime->getSecond() }, { TemporalObject::MILLISECOND_UNIT, startDateTime->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, startDateTime->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, startDateTime->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> end = { { TemporalObject::YEAR_UNIT, endDateTime->getYear() }, { TemporalObject::MONTH_UNIT, endDateTime->getMonth() }, { TemporalObject::DAY_UNIT, endDateTime->getDay() }, { TemporalObject::HOUR_UNIT, endDateTime->getHour() }, { TemporalObject::SECOND_UNIT, endDateTime->getSecond() }, { TemporalObject::MILLISECOND_UNIT, endDateTime->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, endDateTime->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, endDateTime->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> dateDifference = TemporalPlainDateTimeObject::differenceISODateTime(state, start, end, calendar, largestUnit, options);
std::map<TemporalObject::DateTimeUnits, int> tmp = { { TemporalObject::DAY_UNIT, 0 }, { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, 0 } };
tmp.insert(dateDifference.begin(), dateDifference.end());
BigInt* intermediateNs = TemporalZonedDateTimeObject::addZonedDateTime(state, ns1, timeZone.asObject()->asTemporalTimeZoneObject(), calendar.asObject()->asTemporalCalendarObject(), tmp);
Value intermediate = TemporalZonedDateTimeObject::createTemporalZonedDateTime(state, *intermediateNs, timeZone.asObject()->asTemporalTimeZoneObject(), calendar.asObject()->asTemporalCalendarObject());
std::map<TemporalObject::DateTimeUnits, int> result = TemporalZonedDateTimeObject::nanosecondsToDays(state, intermediateNs->toInt64(), intermediate);
tmp = { { TemporalObject::YEAR_UNIT, 0 }, { TemporalObject::MONTH_UNIT, 0 }, { TemporalObject::DAY_UNIT, 0 }, { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 } };
tmp.insert(result.begin(), result.end());
std::map<TemporalObject::DateTimeUnits, int> timeDifference = TemporalDurationObject::balanceDuration(state, tmp, TemporalObject::HOUR_UNIT);
return TemporalDurationObject::createDurationRecord(state, dateDifference[TemporalObject::YEAR_UNIT], dateDifference[TemporalObject::MONTH_UNIT], dateDifference[TemporalObject::WEEK_UNIT], result[TemporalObject::DAY_UNIT], timeDifference[TemporalObject::HOUR_UNIT], timeDifference[TemporalObject::MINUTE_UNIT], timeDifference[TemporalObject::SECOND_UNIT], timeDifference[TemporalObject::MILLISECOND_UNIT], timeDifference[TemporalObject::MICROSECOND_UNIT], timeDifference[TemporalObject::NANOSECOND_UNIT]);
}
TemporalInstantObject::TemporalInstantObject(ExecutionState& state)
: TemporalInstantObject(state, state.context()->globalObject()->objectPrototype())
{
}
TemporalInstantObject::TemporalInstantObject(ExecutionState& state, Object* proto)
: TemporalObject(state, proto)
, m_nanoseconds(nullptr)
{
}
Value TemporalInstantObject::createTemporalInstant(ExecutionState& state, const Value& epochNanoseconds, Optional<Object*> newTarget)
{
ASSERT(epochNanoseconds.isBigInt());
ASSERT(TemporalInstantObject::isValidEpochNanoseconds(epochNanoseconds));
auto* object = new TemporalInstantObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalInstantPrototype());
object->setNanoseconds(epochNanoseconds.asBigInt());
return object;
}
bool TemporalInstantObject::isValidEpochNanoseconds(const Value& epochNanoseconds)
{
ASSERT(epochNanoseconds.isBigInt());
/* Maximum possible and minimum possible Nanoseconds */
const char min[] = "-8640000000000000000000";
const char max[] = "8640000000000000000000";
BigInt* minEpoch = BigInt::parseString(min, sizeof(min) - 1).value();
BigInt* maxEpoch = BigInt::parseString(max, sizeof(max) - 1).value();
return epochNanoseconds.asBigInt()->greaterThanEqual(minEpoch) && epochNanoseconds.asBigInt()->lessThanEqual(maxEpoch);
}
Value TemporalInstantObject::toTemporalInstant(ExecutionState& state, const Value& item)
{
if (item.isObject()) {
if (item.asObject()->isTemporalInstantObject()) {
return item;
}
if (item.asObject()->isTemporalZonedDateTimeObject()) {
return TemporalInstantObject::createTemporalInstant(state, item.asObject()->asTemporalZonedDateTimeObject()->getNanoseconds());
}
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid type");
}
if (!item.isString() || item.asString()->length() == 0) {
if (item.isSymbol()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "Invalid type");
}
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid type");
}
return TemporalInstantObject::createTemporalInstant(state, parseTemporalInstant(state, std::string(item.asString()->toNonGCUTF8StringData())));
}
Value TemporalInstantObject::parseTemporalInstant(ExecutionState& state, const std::string& isoString)
{
auto result = TemporalObject::parseTemporalInstantString(state, isoString);
int64_t offsetNanoseconds = offsetStringToNanoseconds(state, result.tz->offsetString);
time64_t utc = TemporalPlainDateTimeObject::getEpochFromISOParts(state, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond);
auto utcWithOffset = new BigInt(utc - offsetNanoseconds);
if (!TemporalInstantObject::isValidEpochNanoseconds(utcWithOffset)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "epochNanosecond is out of limits");
}
return utcWithOffset;
}
int64_t TemporalInstantObject::offsetStringToNanoseconds(ExecutionState& state, String* offset)
{
if (offset->length() == 0) {
return 0;
}
UTF8StringData utf8StringData = offset->toUTF8StringData();
std::string offsetString(utf8StringData.data(), utf8StringData.length());
int64_t result = std::stoi(offsetString.substr(1, 2)) * TemporalInstantObject::HourToNanosecond;
if (offsetString[3] == ':') {
result += std::stoi(offsetString.substr(4, 2)) * TemporalInstantObject::MinuteToNanosecond;
if (offsetString[6] == ':') {
result += std::stoi(offsetString.substr(7, 2)) * TemporalInstantObject::SecondToNanosecond;
if (offsetString[9] == '.') {
result += std::stoi(offsetString.substr(10, 3)) * TemporalInstantObject::MillisecondToNanosecond;
result += std::stoi(offsetString.substr(13, 3)) * TemporalInstantObject::MicrosecondToNanosecond;
result += std::stoi(offsetString.substr(16, 3));
}
}
}
if (offsetString[0] == '-') {
result *= -1;
}
return result;
}
int TemporalInstantObject::compareEpochNanoseconds(ExecutionState& state, const BigInt& firstNanoSeconds, const BigInt& secondNanoSeconds)
{
if (firstNanoSeconds.greaterThan(&secondNanoSeconds)) {
return 1;
} else if (firstNanoSeconds.lessThan(&secondNanoSeconds)) {
return -1;
}
return 0;
}
BigInt* TemporalInstantObject::addInstant(ExecutionState& state, const BigInt* epochNanoseconds, std::map<TemporalObject::DateTimeUnits, int>& duration)
{
auto result = epochNanoseconds->addition(state, new BigInt((int64_t)duration[TemporalObject::NANOSECOND_UNIT]))->addition(state, new BigInt((int64_t)duration[TemporalObject::MICROSECOND_UNIT] * TemporalInstantObject::MicrosecondToNanosecond))->addition(state, new BigInt((int64_t)duration[TemporalObject::MILLISECOND_UNIT] * TemporalInstantObject::MillisecondToNanosecond))->addition(state, new BigInt((int64_t)duration[TemporalObject::SECOND_UNIT] * TemporalInstantObject::SecondToNanosecond))->addition(state, new BigInt((int64_t)duration[TemporalObject::MINUTE_UNIT] * TemporalInstantObject::MinuteToNanosecond))->addition(state, new BigInt((int64_t)duration[TemporalObject::HOUR_UNIT] * TemporalInstantObject::HourToNanosecond));
if (!TemporalInstantObject::isValidEpochNanoseconds(result)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid epoch Nanosecond");
}
return result;
}
BigInt* TemporalInstantObject::differenceInstant(ExecutionState& state, const BigInt* ns1, BigInt* ns2, int roundingIncrement, TemporalObject::DateTimeUnits smallestUnit, RoundingMode roundingMode)
{
return roundTemporalInstant(state, ns2->subtraction(state, ns1), roundingIncrement, smallestUnit, roundingMode);
}
BigInt* TemporalInstantObject::roundTemporalInstant(ExecutionState& state, BigInt* ns, int increment, TemporalObject::DateTimeUnits unit, RoundingMode roundingMode)
{
int64_t incrementNs = increment;
switch (unit) {
case TemporalObject::HOUR_UNIT:
incrementNs *= 3.6 * 1000000000000;
break;
case TemporalObject::MINUTE_UNIT:
incrementNs *= 6 * 10000000000;
break;
case TemporalObject::SECOND_UNIT:
incrementNs *= 1000000000;
break;
case TemporalObject::MILLISECOND_UNIT:
incrementNs *= 1000000;
break;
case TemporalObject::MICROSECOND_UNIT:
incrementNs *= 1000;
break;
default:
break;
}
return TemporalObject::roundNumberToIncrementAsIfPositive(state, ns, incrementNs, roundingMode);
}
Value TemporalInstantObject::addDurationToOrSubtractDurationFromInstant(ExecutionState& state, TemporalPlainTimeObject::Operation operation, TemporalInstantObject* instant, const Value& other, const Value& options)
{
std::map<TemporalObject::DateTimeUnits, int> duration = TemporalDurationObject::toTemporalDurationRecord(state, other);
if (duration[TemporalObject::YEAR_UNIT] != 0 || duration[TemporalObject::MONTH_UNIT] != 0 || duration[TemporalObject::DAY_UNIT] != 0 || duration[TemporalObject::WEEK_UNIT] != 0) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration");
}
std::map<TemporalObject::DateTimeUnits, int> tmp = { { TemporalObject::HOUR_UNIT, duration[TemporalObject::HOUR_UNIT] * operation }, { TemporalObject::MINUTE_UNIT, duration[TemporalObject::MINUTE_UNIT] * operation }, { TemporalObject::SECOND_UNIT, duration[TemporalObject::SECOND_UNIT] * operation }, { TemporalObject::MILLISECOND_UNIT, duration[TemporalObject::MILLISECOND_UNIT] * operation }, { TemporalObject::MICROSECOND_UNIT, duration[TemporalObject::MICROSECOND_UNIT] * operation }, { TemporalObject::NANOSECOND_UNIT, duration[TemporalObject::NANOSECOND_UNIT] * operation } };
return TemporalInstantObject::createTemporalInstant(state, TemporalInstantObject::addInstant(state, instant->getNanoseconds(), tmp));
}
TemporalTimeZoneObject::TemporalTimeZoneObject(ExecutionState& state)
: TemporalTimeZoneObject(state, state.context()->globalObject()->objectPrototype())
{
}
TemporalTimeZoneObject::TemporalTimeZoneObject(ExecutionState& state, Object* proto, ASCIIString* identifier, const Value& offsetNanoseconds)
: TemporalObject(state, proto)
, m_identifier(identifier)
, m_offsetNanoseconds(offsetNanoseconds)
{
}
Value TemporalTimeZoneObject::builtinTimeZoneGetPlainDateTimeFor(ExecutionState& state, const Value& timeZone, const Value& instant, const Value& calendar)
{
ASSERT(instant.asObject()->isTemporalInstantObject());
int64_t offsetNanoseconds = TemporalTimeZoneObject::getOffsetNanosecondsFor(state, timeZone, instant);
auto result = TemporalTimeZoneObject::getISOPartsFromEpoch(state, Value(instant.asObject()->asTemporalInstantObject()->getNanoseconds()));
result = TemporalPlainDateTimeObject::balanceISODateTime(state, result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT] + offsetNanoseconds);
return TemporalPlainDateTimeObject::createTemporalDateTime(state, result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT], calendar, new Object(state));
}
int64_t TemporalTimeZoneObject::getOffsetNanosecondsFor(ExecutionState& state, const Value& timeZone, const Value& instant)
{
Value getOffsetNanosecondsFor = Object::getMethod(state, timeZone, ObjectPropertyName(state.context()->staticStrings().lazygetOffsetNanosecondsFor()));
Value offsetNanoseconds = Object::call(state, getOffsetNanosecondsFor, timeZone, 1, const_cast<Value*>(&instant));
if (!offsetNanoseconds.isNumber()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "offsetNanoseconds is not a Number");
}
if (!offsetNanoseconds.isInt32()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "offsetNanoseconds is not an Integer");
}
int64_t nanoseconds = offsetNanoseconds.asInt32();
if (std::abs(nanoseconds) > TemporalInstantObject::dayToNanosecond) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "offsetNanoseconds is out of range");
}
return nanoseconds;
}
std::map<TemporalObject::DateTimeUnits, int> TemporalTimeZoneObject::getISOPartsFromEpoch(ExecutionState& state, const Value& epochNanoseconds)
{
ASSERT(TemporalInstantObject::isValidEpochNanoseconds(epochNanoseconds));
BigInt ns = BigInt((uint64_t)1000000);
time64_t remainderNs = epochNanoseconds.asBigInt()->remainder(state, &ns)->toInt64();
time64_t epochMilliseconds = epochNanoseconds.asBigInt()->division(state, &ns)->toInt64();
;
if (remainderNs < 0) {
epochMilliseconds--;
remainderNs = 1000000 + remainderNs;
}
std::map<TemporalObject::DateTimeUnits, int> result;
result[TemporalObject::YEAR_UNIT] = DateObject::yearFromTime(epochMilliseconds);
result[TemporalObject::MONTH_UNIT] = DateObject::monthFromTime(epochMilliseconds) + 1;
result[TemporalObject::DAY_UNIT] = DateObject::dateFromTime(epochMilliseconds);
result[TemporalObject::HOUR_UNIT] = DateObject::hourFromTime(epochMilliseconds);
result[TemporalObject::MINUTE_UNIT] = DateObject::minFromTime(epochMilliseconds);
result[TemporalObject::SECOND_UNIT] = DateObject::secFromTime(epochMilliseconds);
result[TemporalObject::MILLISECOND_UNIT] = DateObject::msFromTime(epochMilliseconds);
result[TemporalObject::MICROSECOND_UNIT] = remainderNs / 1000.0;
result[TemporalObject::NANOSECOND_UNIT] = remainderNs % 1000;
return result;
}
bool TemporalTimeZoneObject::isValidTimeZoneName(const std::string& timeZone)
{
return timeZone == "UTC";
}
std::string TemporalTimeZoneObject::canonicalizeTimeZoneName(const std::string& timeZone)
{
return "UTC";
}
Value TemporalTimeZoneObject::createTemporalTimeZone(ExecutionState& state, const std::string& identifier, Optional<Object*> newTarget)
{
Object* proto;
if (newTarget.hasValue()) {
proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* {
return state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalTimeZonePrototype();
});
} else {
proto = state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalTimeZonePrototype();
}
if (TemporalTimeZoneObject::isValidTimeZoneName(identifier)) {
ASSERT(TemporalTimeZoneObject::canonicalizeTimeZoneName(identifier) == identifier);
return new TemporalTimeZoneObject(state, proto, new ASCIIString(identifier.c_str()), Value(0));
}
int64_t offsetNanoseconds = TemporalInstantObject::offsetStringToNanoseconds(state, new ASCIIString(identifier.c_str()));
return new TemporalTimeZoneObject(state, proto, new ASCIIString(TemporalTimeZoneObject::formatTimeZoneOffsetString(offsetNanoseconds).c_str()), Value(offsetNanoseconds));
}
std::string TemporalTimeZoneObject::formatTimeZoneOffsetString(long long offsetNanoseconds)
{
std::string result = offsetNanoseconds >= 0 ? "+" : "-";
offsetNanoseconds = std::abs(offsetNanoseconds);
int nanoseconds = offsetNanoseconds % TemporalInstantObject::SecondToNanosecond;
int seconds = int(std::floor(offsetNanoseconds / TemporalInstantObject::SecondToNanosecond)) % 60;
int minutes = int(std::floor(offsetNanoseconds / TemporalInstantObject::MinuteToNanosecond)) % 60;
int hours = int(std::floor(offsetNanoseconds / TemporalInstantObject::HourToNanosecond));
result += (hours == 0 ? "00" : ((hours < 10 ? "0" : "") + std::to_string(hours))) + ":" + (minutes < 10 ? "0" : "") + std::to_string(minutes);
if (nanoseconds != 0) {
std::string second;
int num = nanoseconds;
unsigned int digits = 9;
while (num != 0 && digits != 0) {
num /= 10;
digits--;
}
for (unsigned int i = 0; i < digits; ++i) {
second += '0';
}
second += std::to_string(nanoseconds);
int index = second.size() - 1;
for (int i = second.size() - 1; i >= 0; i--) {
if (second[i] == '0') {
index--;
} else {
break;
}
}
result += ":";
result += (hours < 10 ? "0" : "") + std::to_string(seconds) + "." + second.substr(0, index + 1);
} else if (seconds != 0) {
result += ":";
result += (hours < 10 ? "0" : "") + std::to_string(seconds);
}
return result;
}
Value TemporalTimeZoneObject::toTemporalTimeZone(ExecutionState& state, const Value& temporalTimeZoneLike)
{
auto timeZoneLike = Value();
if (temporalTimeZoneLike.isObject()) {
auto item = temporalTimeZoneLike.asObject();
if (item->isTemporalZonedDateTimeObject()) {
return item->asTemporalZonedDateTimeObject()->getTimeZone();
}
if (!item->hasOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().lazyTimeZone()))) {
/*if (!item->asTemporalTimeZoneObject()->getOffsetNanoseconds().isNumber()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "Invalid TimeZone offset");
}*/
return temporalTimeZoneLike;
}
timeZoneLike = item->get(state, ObjectPropertyName(state.context()->staticStrings().lazyTimeZone())).value(state, item);
if (timeZoneLike.isObject() && !timeZoneLike.asObject()->hasOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().lazyTimeZone()))) {
return timeZoneLike;
}
}
if (temporalTimeZoneLike.isSymbol()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "string expected");
}
if (!timeZoneLike.isString() && !temporalTimeZoneLike.isString()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "string expected");
}
TemporalObject::TimeZone parseResult = TemporalObject::parseTemporalTimeZoneString(state, timeZoneLike.isUndefined() ? temporalTimeZoneLike.asString()->toNonGCUTF8StringData() : timeZoneLike.asString()->toNonGCUTF8StringData());
if (parseResult.name->length() != 0) {
unsigned int index = 0;
std::string name = std::string(parseResult.name->toNonGCUTF8StringData());
if (TemporalObject::offset(state, name, index).empty()) {
if (!TemporalTimeZoneObject::isValidTimeZoneName(name)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid TimeZone identifier");
}
parseResult.name = new ASCIIString(TemporalTimeZoneObject::canonicalizeTimeZoneName(name).c_str());
}
return TemporalTimeZoneObject::createTemporalTimeZone(state, name);
}
if (parseResult.z) {
return TemporalTimeZoneObject::createTemporalTimeZone(state, "UTC");
}
return TemporalTimeZoneObject::createTemporalTimeZone(state, parseResult.offsetString->toNonGCUTF8StringData());
}
Value TemporalTimeZoneObject::builtinTimeZoneGetOffsetStringFor(ExecutionState& state, const Value& timeZone, const Value& instant)
{
return Value(new ASCIIString(TemporalTimeZoneObject::formatTimeZoneOffsetString(TemporalTimeZoneObject::getOffsetNanosecondsFor(state, timeZone, instant)).c_str()));
}
Value TemporalTimeZoneObject::getIANATimeZoneOffsetNanoseconds(ExecutionState& state, const Value& epochNanoseconds, const std::string& timeZoneIdentifier)
{
auto u16 = utf8StringToUTF16String(timeZoneIdentifier.data(), timeZoneIdentifier.size());
return epochNanoseconds.asBigInt()->addition(state, new BigInt((int64_t)vzone_getRawOffset(vzone_openID(u16.data(), u16.size()))));
}
std::map<TemporalObject::DateTimeUnits, int> TemporalTimeZoneObject::getIANATimeZoneDateTimeParts(ExecutionState& state, const Value& epochNanoseconds)
{
auto result = TemporalTimeZoneObject::getISOPartsFromEpoch(state, epochNanoseconds);
return TemporalPlainDateTimeObject::balanceISODateTime(state, result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT]);
}
ValueVector TemporalTimeZoneObject::getIANATimeZoneEpochValue(ExecutionState& state, const std::string& timeZoneIdentifier, int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, int nanosecond)
{
auto ns = TemporalPlainDateTimeObject::getEpochFromISOParts(state, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
auto nsEarlier = new BigInt(int64_t(ns - TemporalInstantObject::dayToNanosecond));
if (!TemporalInstantObject::isValidEpochNanoseconds(nsEarlier)) {
nsEarlier = new BigInt(ns);
}
auto nsLater = new BigInt(int64_t(ns + TemporalInstantObject::dayToNanosecond));
if (!TemporalInstantObject::isValidEpochNanoseconds(nsLater)) {
nsLater = new BigInt(ns);
}
auto earliest = TemporalTimeZoneObject::getIANATimeZoneOffsetNanoseconds(state, nsEarlier, timeZoneIdentifier).asBigInt();
auto latest = TemporalTimeZoneObject::getIANATimeZoneOffsetNanoseconds(state, nsLater, timeZoneIdentifier).asBigInt();
BigInt* found[] = { earliest, latest };
ValueVector retVal = {};
for (auto& i : found) {
auto epochNanoseconds = (new BigInt(ns))->subtraction(state, i);
auto parts = getIANATimeZoneDateTimeParts(state, epochNanoseconds);
if (year != parts[TemporalObject::YEAR_UNIT] || month != parts[TemporalObject::MONTH_UNIT] || day != parts[TemporalObject::DAY_UNIT] || hour != parts[TemporalObject::HOUR_UNIT] || minute != parts[TemporalObject::MINUTE_UNIT] || second != parts[TemporalObject::SECOND_UNIT] || millisecond != parts[TemporalObject::MILLISECOND_UNIT] || microsecond != parts[TemporalObject::MICROSECOND_UNIT] || nanosecond != parts[TemporalObject::NANOSECOND_UNIT]) {
retVal.push_back(Value());
continue;
}
retVal.push_back(epochNanoseconds);
}
return retVal;
}
Value TemporalTimeZoneObject::getIANATimeZoneNextTransition(ExecutionState& state, const Value& epochNanoseconds, std::string timeZoneIdentifier)
{
auto upperCap = new BigInt(DateObject::currentTime() + (TemporalInstantObject::dayToNanosecond * 366));
auto leftNanos = epochNanoseconds.asBigInt();
auto leftOffsetNs = TemporalTimeZoneObject::getIANATimeZoneOffsetNanoseconds(state, leftNanos, timeZoneIdentifier).asBigInt();
auto rightNanos = leftNanos;
auto rightOffsetNs = leftOffsetNs;
return TemporalTimeZoneObject::getIANATimeZoneTransition(state, rightNanos, rightOffsetNs, leftNanos, leftOffsetNs, upperCap, timeZoneIdentifier);
}
Value TemporalTimeZoneObject::getIANATimeZonePreviousTransition(ExecutionState& state, const Value& epochNanoseconds, std::string timeZoneIdentifier)
{
auto lowerCap = (new BigInt((int64_t)-388152))->multiply(state, new BigInt((int64_t)1e13)); // 1847-01-01T00:00:00Z
auto rightNanos = epochNanoseconds.asBigInt()->subtraction(state, new BigInt((int64_t)1));
auto rightOffsetNs = TemporalTimeZoneObject::getIANATimeZoneOffsetNanoseconds(state, rightNanos, timeZoneIdentifier).asBigInt();
auto leftNanos = rightNanos;
auto leftOffsetNs = rightOffsetNs;
return TemporalTimeZoneObject::getIANATimeZoneTransition(state, rightNanos, rightOffsetNs, leftNanos, leftOffsetNs, lowerCap, timeZoneIdentifier);
}
Value TemporalTimeZoneObject::getIANATimeZoneTransition(ExecutionState& state, BigInt* rightNanos, BigInt* rightOffsetNs, BigInt* leftNanos, BigInt* leftOffsetNs, BigInt* cap, const std::string& timeZoneIdentifier)
{
while (leftOffsetNs == rightOffsetNs && leftNanos->greaterThan(cap)) {
leftNanos = leftNanos->subtraction(state, new BigInt(TemporalInstantObject::dayToNanosecond * 2 * 7));
leftOffsetNs = TemporalTimeZoneObject::getIANATimeZoneOffsetNanoseconds(state, leftNanos, timeZoneIdentifier).asBigInt();
if (leftOffsetNs->equals(rightOffsetNs)) {
leftNanos = rightNanos;
}
}
while (rightNanos->subtraction(state, leftNanos)->greaterThan(1)) {
auto middle = leftNanos->addition(state, rightNanos)->division(state, new BigInt((uint64_t)2));
auto mState = TemporalTimeZoneObject::getIANATimeZoneOffsetNanoseconds(state, middle, timeZoneIdentifier).asBigInt();
if (mState->equals(leftOffsetNs)) {
leftNanos = middle;
leftOffsetNs = mState;
} else if (mState->equals(rightOffsetNs)) {
rightNanos = middle;
rightOffsetNs = mState;
} else {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "invalid state");
break;
}
}
return rightNanos;
}
Value TemporalTimeZoneObject::builtinTimeZoneGetInstantFor(ExecutionState& state, const Value& timeZone, const Value& dateTime, const Value& disambiguation)
{
ASSERT(dateTime.asObject()->isTemporalPlainDateTimeObject());
auto possibleInstant = TemporalTimeZoneObject::getPossibleInstantsFor(state, timeZone, dateTime);
return TemporalTimeZoneObject::disambiguatePossibleInstants(state, possibleInstant, timeZone, dateTime.asObject()->asTemporalPlainDateTimeObject(), disambiguation);
}
ValueVector TemporalTimeZoneObject::getPossibleInstantsFor(ExecutionState& state, const Value& timeZone, const Value& dateTime)
{
ASSERT(dateTime.asObject()->isTemporalPlainDateTimeObject());
Value argument[1] = { dateTime };
auto possibleInstants = Object::call(state, timeZone.asObject()->get(state, state.context()->staticStrings().lazygetPossibleInstantsFor()).value(state, timeZone), timeZone, 1, argument);
auto iteratorRecord = IteratorObject::getIterator(state, possibleInstants, true);
ValueVector list = {};
Optional<Object*> next;
do {
next = IteratorObject::iteratorStep(state, iteratorRecord);
if (next.hasValue()) {
if (!next.value()->isObject() || !next.value()->asObject()->isTemporalInstantObject()) {
IteratorObject::iteratorClose(state, iteratorRecord, ErrorObject::createBuiltinError(state, ErrorObject::TypeError, "iterator value is not TemporalInstantObject"), true);
}
list.push_back(next.value());
}
} while (next.hasValue());
return list;
}
Value TemporalTimeZoneObject::disambiguatePossibleInstants(ExecutionState& state, ValueVector& possibleInstants, const Value& timeZone, TemporalPlainDateTimeObject* dateTime, const Value& disambiguation)
{
ASSERT(dateTime->asObject()->isTemporalPlainDateTimeObject());
if (possibleInstants.size() == 1) {
return possibleInstants[0];
}
if (!possibleInstants.empty()) {
if (disambiguation.asString()->equals(state.context()->staticStrings().lazyearlier().string()) || disambiguation.asString()->equals(state.context()->staticStrings().lazycompatible().string())) {
return possibleInstants[0];
}
if (disambiguation.asString()->equals(state.context()->staticStrings().lazylater().string())) {
return possibleInstants[possibleInstants.size() - 1];
}
ASSERT(disambiguation.asString()->equals(state.context()->staticStrings().reject.string()));
ErrorObject::createBuiltinError(state, ErrorObject::RangeError, "Invalid disambiguation");
}
ASSERT(possibleInstants.empty());
if (disambiguation.asString()->equals(state.context()->staticStrings().reject.string())) {
ErrorObject::createBuiltinError(state, ErrorObject::RangeError, "disambiguation is reject");
}
uint64_t epochNanoseconds = TemporalPlainDateTimeObject::getEpochFromISOParts(state, dateTime->getYear(), dateTime->getMonth(), dateTime->getDay(), dateTime->getHour(), dateTime->getMinute(), dateTime->getSecond(), dateTime->getMillisecond(), dateTime->getMicrosecond(), dateTime->getNanosecond());
Value daysBeforeNs = Value(new BigInt(epochNanoseconds - (const_Date_msPerDay * 1000000)));
if (!TemporalInstantObject::isValidEpochNanoseconds(daysBeforeNs)) {
ErrorObject::createBuiltinError(state, ErrorObject::RangeError, "invalid epoch nanoseconds");
}
auto dayBefore = TemporalInstantObject::createTemporalInstant(state, daysBeforeNs);
Value daysAfterNs = Value(new BigInt(epochNanoseconds + (const_Date_msPerDay * 1000000)));
if (!TemporalInstantObject::isValidEpochNanoseconds(daysAfterNs)) {
ErrorObject::createBuiltinError(state, ErrorObject::RangeError, "invalid epoch nanoseconds");
}
auto dayAfter = TemporalInstantObject::createTemporalInstant(state, daysAfterNs);
int64_t offsetBefore = TemporalTimeZoneObject::getOffsetNanosecondsFor(state, timeZone, dayBefore);
int64_t offsetAfter = TemporalTimeZoneObject::getOffsetNanosecondsFor(state, timeZone, dayAfter);
int64_t nanoseconds = offsetAfter - offsetBefore;
if (disambiguation.asString()->equals(state.context()->staticStrings().lazyearlier().string())) {
std::map<TemporalObject::DateTimeUnits, int> dateTimeMap = { { TemporalObject::YEAR_UNIT, dateTime->getYear() }, { TemporalObject::MONTH_UNIT, dateTime->getMonth() }, { TemporalObject::DAY_UNIT, dateTime->getDay() }, { TemporalObject::HOUR_UNIT, dateTime->getHour() }, { TemporalObject::MINUTE_UNIT, dateTime->getMinute() }, { TemporalObject::SECOND_UNIT, dateTime->getSecond() }, { TemporalObject::MILLISECOND_UNIT, dateTime->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, dateTime->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, dateTime->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> durationMap = { { TemporalObject::YEAR_UNIT, 0 }, { TemporalObject::MONTH_UNIT, 0 }, { TemporalObject::DAY_UNIT, 0 }, { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::MINUTE_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, -nanoseconds } };
auto earlier = TemporalPlainDateTimeObject::addDateTime(state, dateTimeMap, dateTime->getCalendar(), durationMap, nullptr);
auto earlierDateTime = TemporalPlainDateTimeObject::createTemporalDateTime(state, earlier[TemporalObject::YEAR_UNIT], earlier[TemporalObject::MONTH_UNIT], earlier[TemporalObject::DAY_UNIT], earlier[TemporalObject::HOUR_UNIT], earlier[TemporalObject::MINUTE_UNIT], earlier[TemporalObject::SECOND_UNIT], earlier[TemporalObject::MILLISECOND_UNIT], earlier[TemporalObject::MICROSECOND_UNIT], earlier[TemporalObject::NANOSECOND_UNIT], dateTime->getCalendar());
possibleInstants = TemporalTimeZoneObject::getPossibleInstantsFor(state, timeZone, earlierDateTime);
if (possibleInstants.empty()) {
ErrorObject::createBuiltinError(state, ErrorObject::RangeError, "no possible instants");
}
return possibleInstants[0];
}
ASSERT(disambiguation.asString()->equals(state.context()->staticStrings().lazylater().string()) || disambiguation.asString()->equals(state.context()->staticStrings().lazycompatible().string()));
std::map<TemporalObject::DateTimeUnits, int> dateTimeMap = { { TemporalObject::YEAR_UNIT, dateTime->getYear() }, { TemporalObject::MONTH_UNIT, dateTime->getMonth() }, { TemporalObject::DAY_UNIT, dateTime->getDay() }, { TemporalObject::HOUR_UNIT, dateTime->getHour() }, { TemporalObject::MINUTE_UNIT, dateTime->getMinute() }, { TemporalObject::SECOND_UNIT, dateTime->getSecond() }, { TemporalObject::MILLISECOND_UNIT, dateTime->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, dateTime->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, dateTime->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> durationMap = { { TemporalObject::YEAR_UNIT, 0 }, { TemporalObject::MONTH_UNIT, 0 }, { TemporalObject::DAY_UNIT, 0 }, { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::MINUTE_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, nanoseconds } };
auto earlier = TemporalPlainDateTimeObject::addDateTime(state, dateTimeMap, dateTime->getCalendar(), durationMap, nullptr);
auto earlierDateTime = TemporalPlainDateTimeObject::createTemporalDateTime(state, earlier[TemporalObject::YEAR_UNIT], earlier[TemporalObject::MONTH_UNIT], earlier[TemporalObject::DAY_UNIT], earlier[TemporalObject::HOUR_UNIT], earlier[TemporalObject::MINUTE_UNIT], earlier[TemporalObject::SECOND_UNIT], earlier[TemporalObject::MILLISECOND_UNIT], earlier[TemporalObject::MICROSECOND_UNIT], earlier[TemporalObject::NANOSECOND_UNIT], dateTime->getCalendar());
possibleInstants = TemporalTimeZoneObject::getPossibleInstantsFor(state, timeZone, earlierDateTime);
if (possibleInstants.empty()) {
ErrorObject::createBuiltinError(state, ErrorObject::RangeError, "no possible instants");
}
return possibleInstants[possibleInstants.size() - 1];
}
TemporalPlainYearMonthObject::TemporalPlainYearMonthObject(ExecutionState& state)
: TemporalPlainYearMonthObject(state, state.context()->globalObject()->objectPrototype())
{
}
TemporalPlainYearMonthObject::TemporalPlainYearMonthObject(ExecutionState& state, Object* proto, int isoYear, int isoMonth, TemporalCalendarObject* calendar, int referenceISODay)
: TemporalObject(state, proto)
, m_isoYear(isoYear)
, m_isoMonth(isoMonth)
, m_calendar(calendar)
, m_referenceISODay(referenceISODay)
{
}
Value TemporalPlainYearMonthObject::createTemporalYearMonth(ExecutionState& state, int isoYear, int isoMonth, const Value& calendar, int referenceISODay, Optional<Object*> newTarget)
{
ASSERT(calendar.isObject());
if (!TemporalPlainDateObject::isValidISODate(state, isoYear, isoMonth, referenceISODay)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid date");
}
if (!TemporalPlainYearMonthObject::isoYearMonthWithinLimits(isoYear, isoMonth)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid date");
}
if (!newTarget.hasValue()) {
newTarget = state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalPlainYearMonthPrototype();
}
return new TemporalPlainYearMonthObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalPlainYearMonthPrototype(), isoYear, isoMonth, calendar.asObject()->asTemporalCalendarObject(), referenceISODay);
}
bool TemporalPlainYearMonthObject::isoYearMonthWithinLimits(int isoYear, int isoMonth)
{
return (isoYear > -271821 && isoYear < 275760) || (isoYear == -271821 && isoMonth < 4) || (isoYear == 275760 && isoMonth < 9);
}
Value TemporalPlainYearMonthObject::toTemporalYearMonth(ExecutionState& state, const Value& item, const Value& options)
{
ASSERT(options.isObject() || options.isUndefined());
if (item.isObject()) {
if (item.asObject()->isTemporalPlainYearMonthObject()) {
return item;
}
StaticStrings& strings = state.context()->staticStrings();
auto calendar = TemporalCalendarObject::getTemporalCalendarWithISODefault(state, item);
auto fieldNames = TemporalCalendarObject::calendarFields(state, calendar, { strings.lazyMonth().string(), strings.lazymonthCode().string(), strings.lazyYear().string() });
auto fields = Temporal::prepareTemporalFields(state, item, fieldNames, {});
return TemporalCalendarObject::calendarYearMonthFromFields(state, calendar, fields, options);
}
Temporal::toTemporalOverflow(state, options);
auto result = TemporalObject::parseTemporalYearMonthString(state, item.asString()->toNonGCUTF8StringData());
auto calendar = TemporalCalendarObject::toTemporalCalendarWithISODefault(state, Value(result.calendar));
auto retVal = TemporalPlainYearMonthObject::createTemporalYearMonth(state, result.year, result.month, calendar, result.day);
return TemporalCalendarObject::calendarYearMonthFromFields(state, calendar, retVal, Value());
}
Value TemporalPlainYearMonthObject::addDurationToOrSubtractDurationFromPlainYearMonth(ExecutionState& state, TemporalPlainTimeObject::Operation operation, TemporalPlainYearMonthObject* yearMonth, const Value& temporalDurationLike, const Value& options)
{
TemporalDurationObject* duration = TemporalDurationObject::toTemporalDuration(state, temporalDurationLike).asObject()->asTemporalDurationObject();
if (operation == TemporalPlainTimeObject::SUBTRACT) {
duration = TemporalDurationObject::createNegatedTemporalDuration(state, duration).asObject()->asTemporalDurationObject();
}
std::map<TemporalObject::DateTimeUnits, int> tmp = { { TemporalObject::DAY_UNIT, duration->getDay() }, { TemporalObject::HOUR_UNIT, duration->getHour() }, { TemporalObject::MINUTE_UNIT, duration->getMinute() }, { TemporalObject::SECOND_UNIT, duration->getSecond() }, { TemporalObject::MILLISECOND_UNIT, duration->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, duration->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, duration->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> balanceResult = TemporalDurationObject::balanceDuration(state, tmp, TemporalObject::DAY_UNIT);
Value optionsObject = Temporal::getOptionsObject(state, options);
ValueVector fieldNames = TemporalCalendarObject::calendarFields(state, yearMonth->getCalendar(), { Value(state.context()->staticStrings().lazymonthCode().string()), Value(state.context()->staticStrings().lazyYear().string()) });
Value fields = Temporal::prepareTemporalFields(state, yearMonth, fieldNames, {});
int durationArray[] = { duration->getYear(), duration->getMonth(), duration->getWeek(), balanceResult[TemporalObject::DAY_UNIT], 0, 0, 0, 0, 0, 0 };
int sign = TemporalDurationObject::durationSign(durationArray);
int day = 1;
if (sign < 0) {
day = TemporalCalendarObject::calendarDaysInMonth(state, yearMonth->getCalendar(), yearMonth).asInt32();
if (day <= 0) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid days in Month");
}
}
fields.asObject()->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, state.context()->staticStrings().lazyDay().string()), ObjectPropertyDescriptor(Value(day), ObjectPropertyDescriptor::AllPresent));
Value date = TemporalCalendarObject::dateFromFields(state, yearMonth->getCalendar(), fields);
Value durationToAdd = TemporalDurationObject::createTemporalDuration(state, duration->getYear(), duration->getMonth(), duration->getWeek(), balanceResult[TemporalObject::DAY_UNIT], 0, 0, 0, 0, 0, 0);
auto optionsCopy = new Object(state, Object::PrototypeIsNull);
ValueVectorWithInlineStorage entries = Object::enumerableOwnProperties(state, options.asObject(), EnumerableOwnPropertiesType::KeyAndValue);
for (auto& entry : entries) {
auto entryArray = entry.asObject()->asArrayObject();
optionsCopy->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, entryArray->getOwnProperty(state, ObjectPropertyName(state, (int64_t)0)).value(state, entryArray)), ObjectPropertyDescriptor(entryArray->getOwnProperty(state, ObjectPropertyName(state, (int64_t)1)).value(state, entryArray)));
}
Value addedDate = TemporalCalendarObject::calendarDateAdd(state, yearMonth->getCalendar(), date, durationToAdd, options);
Value addedDateFields = Temporal::prepareTemporalFields(state, addedDate, fieldNames, {});
return TemporalCalendarObject::calendarYearMonthFromFields(state, yearMonth->getCalendar(), addedDateFields, optionsCopy);
}
TemporalDurationObject::TemporalDurationObject(ExecutionState& state, int years = 0, int months = 0, int weeks = 0, int days = 0, int hours = 0, int minutes = 0, int seconds = 0, int milliseconds = 0, int microseconds = 0, int nanoseconds = 0)
: TemporalDurationObject(state, state.context()->globalObject()->objectPrototype(), years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds)
{
}
TemporalDurationObject::TemporalDurationObject(ExecutionState& state, Object* proto, int years = 0, int months = 0, int weeks = 0, int days = 0, int hours = 0, int minutes = 0, int seconds = 0, int milliseconds = 0, int microseconds = 0, int nanoseconds = 0)
: TemporalObject(state, proto)
, TemporalDate(years, months, days)
, TemporalTime(hours, minutes, seconds, microseconds, milliseconds, nanoseconds)
{
}
bool TemporalDurationObject::isValidDuration(const int fields[])
{
int sign = TemporalDurationObject::durationSign(fields);
for (int i = 0; i < 10; i++) {
if ((fields[i] < 0 && sign > 0) || (fields[i] > 0 && sign < 0)) {
return false;
}
}
return true;
}
int TemporalDurationObject::durationSign(const int fields[])
{
for (int i = 0; i < 10; i++) {
if (fields[i] < 0) {
return -1;
} else if (fields[i] > 0) {
return 1;
}
}
return 0;
}
Value TemporalDurationObject::createTemporalDuration(ExecutionState& state, int years, int months, int weeks, int days, int hours, int minutes, int seconds, int milliseconds, int microseconds, int nanoseconds, Optional<Object*> newTarget)
{
int dateTime[] = { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds };
if (!isValidDuration(dateTime)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid time");
}
return new TemporalDurationObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalDurationPrototype(), years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
}
Value TemporalDurationObject::toTemporalDuration(ExecutionState& state, const Value& item)
{
if (item.isObject() && item.asObject()->isTemporalDurationObject()) {
return item;
}
auto result = TemporalDurationObject::toTemporalDurationRecord(state, item);
return TemporalDurationObject::createTemporalDuration(state, result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::WEEK_UNIT], result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT], nullptr);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalDurationObject::toTemporalDurationRecord(ExecutionState& state, const Value& temporalDurationLike)
{
if (!temporalDurationLike.isObject()) {
return TemporalObject::parseTemporalDurationString(state, temporalDurationLike.asString()->toNonGCUTF8StringData());
} else if (temporalDurationLike.asObject()->isTemporalDurationObject()) {
auto duration = temporalDurationLike.asObject()->asTemporalDurationObject();
return TemporalDurationObject::createDurationRecord(state, duration->getYear(), duration->getMonth(), duration->getWeek(), duration->getDay(), duration->getHour(), duration->getMinute(), duration->getSecond(), duration->getMillisecond(), duration->getMicrosecond(), duration->getNanosecond());
}
std::map<TemporalObject::DateTimeUnits, int> result = { { TemporalObject::YEAR_UNIT, 0 }, { TemporalObject::MONTH_UNIT, 0 }, { TemporalObject::WEEK_UNIT, 0 }, { TemporalObject::DAY_UNIT, 0 }, { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::MINUTE_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, 0 } };
auto partial = TemporalDurationObject::toTemporalPartialDurationRecord(state, temporalDurationLike);
for (auto const& x : partial) {
if (!x.second.isUndefined()) {
result[x.first] = x.second.asInt32();
}
}
int dateTime[] = { result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::WEEK_UNIT], result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT] };
if (!TemporalDurationObject::isValidDuration(dateTime)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration");
}
return result;
}
std::map<TemporalObject::DateTimeUnits, Value> TemporalDurationObject::toTemporalPartialDurationRecord(ExecutionState& state, const Value& temporalDurationLike)
{
if (!temporalDurationLike.isObject()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Expected object");
}
std::map<TemporalObject::DateTimeUnits, Value> result;
TemporalObject::DateTimeUnits temporalTimeLikeProp[10] = { TemporalObject::YEAR_UNIT, TemporalObject::MONTH_UNIT, TemporalObject::WEEK_UNIT, TemporalObject::DAY_UNIT, TemporalObject::HOUR_UNIT, TemporalObject::MINUTE_UNIT, TemporalObject::SECOND_UNIT, TemporalObject::MILLISECOND_UNIT, TemporalObject::MICROSECOND_UNIT, TemporalObject::NANOSECOND_UNIT };
bool any = false;
for (auto i : temporalTimeLikeProp) {
Value value = temporalDurationLike.asObject()->get(state, ObjectPropertyName(state, TemporalObject::dateTimeUnitString(state, i))).value(state, temporalDurationLike);
if (!value.isUndefined()) {
any = true;
result[i] = value;
}
}
if (!any) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "any is false");
}
return result;
}
std::map<TemporalObject::DateTimeUnits, int> TemporalDurationObject::createDurationRecord(ExecutionState& state, int years, int months, int weeks, int days, int hours, int minutes, int seconds, int milliseconds, int microseconds, int nanoseconds)
{
int dateTime[] = { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds };
if (!TemporalDurationObject::isValidDuration(dateTime)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration");
}
return { { TemporalObject::YEAR_UNIT, years }, { TemporalObject::MONTH_UNIT, months }, { TemporalObject::WEEK_UNIT, weeks }, { TemporalObject::DAY_UNIT, days }, { TemporalObject::HOUR_UNIT, hours }, { TemporalObject::MINUTE_UNIT, minutes }, { TemporalObject::SECOND_UNIT, seconds }, { TemporalObject::MILLISECOND_UNIT, milliseconds }, { TemporalObject::MICROSECOND_UNIT, microseconds }, { TemporalObject::NANOSECOND_UNIT, nanoseconds } };
}
Value TemporalDurationObject::createNegatedTemporalDuration(ExecutionState& state, const Value& duration)
{
auto durationObject = duration.asObject()->asTemporalDurationObject();
return TemporalDurationObject::createTemporalDuration(state, -durationObject->m_year, -durationObject->m_month, -durationObject->m_week, -durationObject->m_day, -durationObject->m_hour, -durationObject->m_minute, -durationObject->m_second, -durationObject->m_millisecond, -durationObject->m_microsecond, -durationObject->m_nanosecond, nullptr);
}
Value TemporalDurationObject::addDurationToOrSubtractDurationFromDuration(ExecutionState& state, TemporalPlainTimeObject::Operation operation, TemporalDurationObject* duration, const Value& other, const Value& options)
{
auto otherDurationObject = TemporalDurationObject::toTemporalDurationRecord(state, other);
auto relativeTo = TemporalObject::toRelativeTemporalObject(state, Temporal::getOptionsObject(state, options).asObject());
TemporalDurationObject* otherObject = other.asObject()->asTemporalDurationObject();
std::map<TemporalObject::DateTimeUnits, int> durationMap1 = { { TemporalObject::YEAR_UNIT, duration->getYear() }, { TemporalObject::MONTH_UNIT, duration->getMonth() }, { TemporalObject::WEEK_UNIT, duration->getWeek() }, { TemporalObject::DAY_UNIT, duration->getDay() }, { TemporalObject::HOUR_UNIT, duration->getHour() }, { TemporalObject::MINUTE_UNIT, duration->getMinute() }, { TemporalObject::SECOND_UNIT, duration->getSecond() }, { TemporalObject::MILLISECOND_UNIT, duration->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, duration->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, duration->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> durationMap2 = { { TemporalObject::YEAR_UNIT, operation * otherObject->getYear() }, { TemporalObject::MONTH_UNIT, operation * otherObject->getMonth() }, { TemporalObject::WEEK_UNIT, operation * otherObject->getWeek() }, { TemporalObject::DAY_UNIT, operation * otherObject->getDay() }, { TemporalObject::HOUR_UNIT, operation * otherObject->getHour() }, { TemporalObject::MINUTE_UNIT, operation * otherObject->getMinute() }, { TemporalObject::SECOND_UNIT, operation * otherObject->getSecond() }, { TemporalObject::MILLISECOND_UNIT, operation * otherObject->getMillisecond() }, { TemporalObject::MICROSECOND_UNIT, operation * otherObject->getMicrosecond() }, { TemporalObject::NANOSECOND_UNIT, operation * otherObject->getNanosecond() } };
std::map<TemporalObject::DateTimeUnits, int> result = TemporalDurationObject::addDuration(state, durationMap1, durationMap2, relativeTo);
return TemporalDurationObject::createTemporalDuration(state, result[TemporalObject::YEAR_UNIT], result[TemporalObject::MONTH_UNIT], result[TemporalObject::WEEK_UNIT], result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT]);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalDurationObject::addDuration(ExecutionState& state, std::map<TemporalObject::DateTimeUnits, int> first, std::map<TemporalObject::DateTimeUnits, int> second, const Value& relativeTo)
{
auto largestUnit = TemporalDurationObject::defaultTemporalLargestUnit(first);
auto largestUnit2 = TemporalDurationObject::defaultTemporalLargestUnit(second);
largestUnit = largestUnit2 > largestUnit ? largestUnit2 : largestUnit;
std::map<TemporalObject::DateTimeUnits, int> duration = {
{ TemporalObject::DAY_UNIT, first[TemporalObject::DAY_UNIT] + second[TemporalObject::DAY_UNIT] },
{ TemporalObject::HOUR_UNIT, first[TemporalObject::HOUR_UNIT] + second[TemporalObject::HOUR_UNIT] },
{ TemporalObject::MINUTE_UNIT, first[TemporalObject::MINUTE_UNIT] + second[TemporalObject::MINUTE_UNIT] },
{ TemporalObject::SECOND_UNIT, first[TemporalObject::SECOND_UNIT] + second[TemporalObject::SECOND_UNIT] },
{ TemporalObject::MILLISECOND_UNIT, first[TemporalObject::MILLISECOND_UNIT] + second[TemporalObject::MILLISECOND_UNIT] },
{ TemporalObject::MICROSECOND_UNIT, first[TemporalObject::MICROSECOND_UNIT] + second[TemporalObject::MICROSECOND_UNIT] },
{ TemporalObject::NANOSECOND_UNIT, first[TemporalObject::NANOSECOND_UNIT] + second[TemporalObject::NANOSECOND_UNIT] },
};
if (relativeTo.isUndefined()) {
if (largestUnit < TemporalObject::DAY_UNIT) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Largest unit is invalid");
}
auto result = TemporalDurationObject::balanceDuration(state, duration, largestUnit);
return TemporalDurationObject::createDurationRecord(state, 0, 0, 0, result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT]);
}
if (relativeTo.asObject()->isTemporalPlainDateTimeObject()) {
auto dateTime = relativeTo.asObject()->asTemporalPlainDateTimeObject();
Value calendar = dateTime->getCalendar();
Value dateDuration1 = TemporalDurationObject::createTemporalDuration(state, first[TemporalObject::YEAR_UNIT], first[TemporalObject::MONTH_UNIT], first[TemporalObject::WEEK_UNIT], first[TemporalObject::DAY_UNIT], 0, 0, 0, 0, 0, 0);
Value dateDuration2 = TemporalDurationObject::createTemporalDuration(state, second[TemporalObject::YEAR_UNIT], second[TemporalObject::MONTH_UNIT], second[TemporalObject::WEEK_UNIT], second[TemporalObject::DAY_UNIT], 0, 0, 0, 0, 0, 0);
Value dateAdd = Object::getMethod(state, calendar, ObjectPropertyName(state, state.context()->staticStrings().lazydateAdd().string()));
Value intermediate = TemporalCalendarObject::calendarDateAdd(state, calendar, relativeTo, dateDuration1, Value(), dateAdd);
Value end = TemporalCalendarObject::calendarDateAdd(state, calendar, intermediate, dateDuration2, Value(), dateAdd);
auto dateLargestUnit = TemporalObject::DAY_UNIT > largestUnit ? TemporalObject::DAY_UNIT : largestUnit;
auto differenceOptions = new Object(state, Object::PrototypeIsNull);
differenceOptions->defineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().lazylargestUnit()), ObjectPropertyDescriptor(TemporalObject::dateTimeUnitString(state, dateLargestUnit), ObjectPropertyDescriptor::AllPresent));
auto dateDifference = TemporalCalendarObject::calendarDateUntil(state, calendar, relativeTo, end, differenceOptions).asObject()->asTemporalDurationObject();
duration[TemporalObject::DAY_UNIT] = dateDifference->getDay();
auto result = TemporalDurationObject::balanceDuration(state, duration, largestUnit);
return TemporalDurationObject::createDurationRecord(state, dateDifference->getYear(), dateDifference->getMonth(), dateDifference->getWeek(), result[TemporalObject::DAY_UNIT], result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT]);
}
auto zonedDateTime = relativeTo.asObject()->asTemporalZonedDateTimeObject();
auto timeZone = zonedDateTime->getTimeZone();
auto calendar = zonedDateTime->getCalendar();
auto intermediateNs = TemporalZonedDateTimeObject::addZonedDateTime(state, zonedDateTime->getNanoseconds(), timeZone, calendar, first);
auto endNs = TemporalZonedDateTimeObject::addZonedDateTime(state, intermediateNs, timeZone, calendar, second);
if (largestUnit <= TemporalObject::DAY_UNIT) {
auto diffNs = TemporalInstantObject::differenceInstant(state, zonedDateTime->getNanoseconds(), endNs, 1, TemporalObject::NANOSECOND_UNIT, HALF_EXPAND)->toInt64();
std::map<TemporalObject::DateTimeUnits, int> result = TemporalDurationObject::balanceDuration(state, { { TemporalObject::DAY_UNIT, 0 }, { TemporalObject::HOUR_UNIT, 0 }, { TemporalObject::MINUTE_UNIT, 0 }, { TemporalObject::SECOND_UNIT, 0 }, { TemporalObject::MILLISECOND_UNIT, 0 }, { TemporalObject::MICROSECOND_UNIT, 0 }, { TemporalObject::NANOSECOND_UNIT, diffNs } }, largestUnit);
return TemporalDurationObject::createDurationRecord(state, 0, 0, 0, 0, result[TemporalObject::HOUR_UNIT], result[TemporalObject::MINUTE_UNIT], result[TemporalObject::SECOND_UNIT], result[TemporalObject::MILLISECOND_UNIT], result[TemporalObject::MICROSECOND_UNIT], result[TemporalObject::NANOSECOND_UNIT]);
}
return TemporalZonedDateTimeObject::differenceZonedDateTime(state, const_cast<BigInt*>(zonedDateTime->getNanoseconds()), endNs, timeZone, calendar, largestUnit, new Object(state, Object::PrototypeIsNull));
}
TemporalObject::DateTimeUnits TemporalDurationObject::defaultTemporalLargestUnit(std::map<TemporalObject::DateTimeUnits, int> temporalObject)
{
for (auto const& keyValue : temporalObject) {
if (keyValue.second != 0) {
return keyValue.first;
}
}
return TemporalObject::DateTimeUnits::NANOSECOND_UNIT;
}
std::map<TemporalObject::DateTimeUnits, int> TemporalDurationObject::balanceDuration(ExecutionState& state, std::map<TemporalObject::DateTimeUnits, int> duration, TemporalObject::DateTimeUnits largestUnit, const Value& relativeTo)
{
auto balanceResult = TemporalDurationObject::balancePossiblyInfiniteDuration(state, duration, largestUnit, relativeTo);
if (balanceResult.empty()) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "duration is positive or negative overflow");
}
return balanceResult;
}
std::map<TemporalObject::DateTimeUnits, int> TemporalDurationObject::balancePossiblyInfiniteDuration(ExecutionState& state, std::map<TemporalObject::DateTimeUnits, int> duration, TemporalObject::DateTimeUnits largestUnit, const Value& relativeTo)
{
int64_t nanoseconds;
std::map<TemporalObject::DateTimeUnits, double> result = {
{ TemporalObject::DAY_UNIT, 0 },
{ TemporalObject::HOUR_UNIT, 0 },
{ TemporalObject::MINUTE_UNIT, 0 },
{ TemporalObject::SECOND_UNIT, 0 },
{ TemporalObject::MILLISECOND_UNIT, 0 },
{ TemporalObject::MICROSECOND_UNIT, 0 }
};
if (relativeTo.isObject() && relativeTo.asObject()->isTemporalZonedDateTimeObject()) {
auto zonedDateTime = relativeTo.asObject()->asTemporalZonedDateTimeObject();
std::map<TemporalObject::DateTimeUnits, int> tmpDuration = { { TemporalObject::YEAR_UNIT, 0 }, { TemporalObject::MONTH_UNIT, 0 }, { TemporalObject::WEEK_UNIT, 0 } };
tmpDuration.insert(duration.begin(), duration.end());
nanoseconds = TemporalZonedDateTimeObject::addZonedDateTime(state, zonedDateTime->getNanoseconds(), zonedDateTime->getTimeZone(), zonedDateTime->getCalendar(), tmpDuration)->subtraction(state, zonedDateTime->getNanoseconds())->toInt64();
} else {
nanoseconds = TemporalDurationObject::totalDurationNanoseconds(state, duration, 0)->toInt64();
}
if (largestUnit <= TemporalObject::DAY_UNIT) {
std::map<TemporalObject::DateTimeUnits, int> nsToDays = TemporalZonedDateTimeObject::nanosecondsToDays(state, nanoseconds, relativeTo);
result[TemporalObject::DAY_UNIT] = nsToDays[TemporalObject::DAY_UNIT];
result[TemporalObject::NANOSECOND_UNIT] = nsToDays[TemporalObject::NANOSECOND_UNIT];
}
int sign = 1;
if (nanoseconds < 0) {
sign = -1;
}
nanoseconds = std::abs(nanoseconds);
if (largestUnit <= TemporalObject::MICROSECOND_UNIT) {
result[TemporalObject::MICROSECOND_UNIT] = std::floor(nanoseconds / TemporalInstantObject::MicrosecondToNanosecond);
}
if (largestUnit <= TemporalObject::MILLISECOND_UNIT) {
result[TemporalObject::MILLISECOND_UNIT] = std::floor(result[TemporalObject::MICROSECOND_UNIT] / 1000);
result[TemporalObject::MICROSECOND_UNIT] = (int64_t)result[TemporalObject::MICROSECOND_UNIT] % 1000;
}
if (largestUnit <= TemporalObject::SECOND_UNIT) {
result[TemporalObject::SECOND_UNIT] = std::floor(result[TemporalObject::MILLISECOND_UNIT] / 1000);
result[TemporalObject::MILLISECOND_UNIT] = (int64_t)result[TemporalObject::MILLISECOND_UNIT] % 1000;
}
if (largestUnit <= TemporalObject::MINUTE_UNIT) {
result[TemporalObject::MINUTE_UNIT] = std::floor(result[TemporalObject::SECOND_UNIT] / 60);
result[TemporalObject::SECOND_UNIT] = (int64_t)result[TemporalObject::SECOND_UNIT] % 60;
}
if (largestUnit <= TemporalObject::HOUR_UNIT) {
result[TemporalObject::HOUR_UNIT] = std::floor(result[TemporalObject::MINUTE_UNIT] / 60);
result[TemporalObject::MINUTE_UNIT] = (int64_t)result[TemporalObject::MINUTE_UNIT] % 60;
}
for (auto& keyValue : result) {
if (!std::isfinite(keyValue.second)) {
return {};
}
}
return TemporalDurationObject::createTimeDurationRecord(state, result[TemporalObject::DAY_UNIT], sign * result[TemporalObject::HOUR_UNIT], sign * result[TemporalObject::MINUTE_UNIT], sign * result[TemporalObject::SECOND_UNIT], sign * result[TemporalObject::MILLISECOND_UNIT], sign * result[TemporalObject::MICROSECOND_UNIT], sign * result[TemporalObject::NANOSECOND_UNIT]);
}
BigInt* TemporalDurationObject::totalDurationNanoseconds(ExecutionState& state, std::map<TemporalObject::DateTimeUnits, int> duration, int offsetShift)
{
auto nanoseconds = new BigInt((int64_t)0);
if (duration[TemporalObject::DAY_UNIT] == 0) {
nanoseconds = new BigInt((int64_t)duration[TemporalObject::NANOSECOND_UNIT] - offsetShift);
}
auto result = new BigInt(duration[TemporalObject::HOUR_UNIT] + duration[TemporalObject::DAY_UNIT] * const_Date_hoursPerDay);
result = result->multiply(state, new BigInt((int64_t)const_Date_minutesPerHour))->addition(state, new BigInt((int64_t)duration[TemporalObject::MINUTE_UNIT]));
result = result->multiply(state, new BigInt((int64_t)const_Date_secondsPerMinute))->addition(state, new BigInt((int64_t)duration[TemporalObject::SECOND_UNIT]));
result = result->multiply(state, new BigInt((int64_t)1000))->addition(state, new BigInt((int64_t)duration[TemporalObject::MILLISECOND_UNIT]));
result = result->multiply(state, new BigInt((int64_t)1000))->addition(state, new BigInt((int64_t)duration[TemporalObject::MICROSECOND_UNIT]));
return result->multiply(state, new BigInt((int64_t)1000))->addition(state, nanoseconds);
}
std::map<TemporalObject::DateTimeUnits, int> TemporalDurationObject::createTimeDurationRecord(ExecutionState& state, int days, int hours, int minutes, int seconds, int milliseconds, int microseconds, int nanoseconds)
{
int duration[] = { 0, 0, 0, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds };
if (TemporalDurationObject::isValidDuration(duration)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid duration");
}
return {
{ TemporalObject::DAY_UNIT, days },
{ TemporalObject::HOUR_UNIT, hours },
{ TemporalObject::MINUTE_UNIT, minutes },
{ TemporalObject::SECOND_UNIT, seconds },
{ TemporalObject::MILLISECOND_UNIT, milliseconds },
{ TemporalObject::MICROSECOND_UNIT, microseconds },
{ TemporalObject::NANOSECOND_UNIT, nanoseconds },
};
}
TemporalPlainMonthDayObject::TemporalPlainMonthDayObject(ExecutionState& state)
: TemporalPlainMonthDayObject(state, state.context()->globalObject()->objectPrototype())
{
}
TemporalPlainMonthDayObject::TemporalPlainMonthDayObject(ExecutionState& state, Object* proto, int isoMonth, int isoDay, TemporalCalendarObject* calendar, int referenceISOYear)
: TemporalObject(state, proto)
, m_isoMonth(isoMonth)
, m_isoDay(isoDay)
, m_calendar(calendar)
, m_referenceISOYear(referenceISOYear)
{
}
Value TemporalPlainMonthDayObject::createTemporalMonthDay(ExecutionState& state, int isoMonth, int isoDay, TemporalCalendarObject* calendar, int referenceISOYear, Optional<Object*> newTarget)
{
if (!TemporalPlainDateObject::isValidISODate(state, referenceISOYear, isoMonth, isoDay)) {
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Invalid date");
}
return new TemporalPlainMonthDayObject(state, state.context()->globalObject()->temporal()->asTemporalObject()->getTemporalPlainMonthDayPrototype(), isoMonth, isoDay, calendar, referenceISOYear);
}
Value TemporalPlainMonthDayObject::toTemporalMonthDay(ExecutionState& state, const Value& item, const Value& options)
{
ASSERT(options.isObject() || options.isUndefined());
StaticStrings& strings = state.context()->staticStrings();
int referenceISOYear = 1972;
if (!item.isObject()) {
Temporal::toTemporalOverflow(state, options);
auto result = TemporalObject::parseTemporalMonthDayString(state, item.asString()->toNonGCUTF8StringData());
auto calendar = TemporalCalendarObject::toTemporalCalendarWithISODefault(state, Value(result.calendar));
if (result.year == 0) {
return createTemporalMonthDay(state, result.month, result.day, calendar.asObject()->asTemporalCalendarObject(), referenceISOYear);
}
return TemporalCalendarObject::calendarMonthDayFromFields(state, calendar, createTemporalMonthDay(state, result.month, result.day, calendar.asObject()->asTemporalCalendarObject(), referenceISOYear), options);
}
TemporalCalendarObject* calendar = nullptr;
bool calendarAbsent = true;
Object* itemObject = item.asObject();
if (itemObject->isTemporalPlainMonthDayObject()) {
return item;
}
if (itemObject->isTemporalPlainDateObject() || itemObject->isTemporalPlainDateTimeObject() || itemObject->isTemporalPlainTimeObject() || itemObject->isTemporalPlainYearMonthObject() || itemObject->isTemporalZonedDateTimeObject()) {
calendarAbsent = false;
} else {
Value calendarLike = itemObject->get(state, strings.calendar).value(state, itemObject);
calendarAbsent = !calendarLike.isUndefined();
calendar = TemporalCalendarObject::toTemporalCalendarWithISODefault(state, calendarLike).asObject()->asTemporalCalendarObject();
}
ValueVector fieldNames = TemporalCalendarObject::calendarFields(state, calendar, { strings.lazyDay().string(), strings.lazyMonth().string(), strings.lazymonthCode().string(), strings.lazyYear().string() });
Value fields = Temporal::prepareTemporalFields(state, item, fieldNames, {});
Value month = fields.asObject()->get(state, strings.lazyMonth()).value(state, fields);
Value monthCode = fields.asObject()->get(state, strings.lazymonthCode()).value(state, fields);
Value year = fields.asObject()->get(state, strings.lazyYear()).value(state, fields);
if (calendarAbsent && month.isUndefined() && monthCode.isUndefined() && year.isUndefined()) {
fields.asObject()->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, strings.lazyYear()), ObjectPropertyDescriptor(Value(referenceISOYear), ObjectPropertyDescriptor::AllPresent));
}
return TemporalCalendarObject::calendarMonthDayFromFields(state, calendar, fields, options);
}
TemporalObject::DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, int nanosecond, String* calendar, TemporalObject::TimeZone* tz)
: year(year)
, month(month)
, day(day)
, hour(hour)
, minute(minute)
, second(second)
, millisecond(millisecond)
, microsecond(microsecond)
, nanosecond(nanosecond)
, calendar(calendar)
, tz(tz)
{
}
TemporalObject::TimeZone::TimeZone(bool z, String* offsetString, String* name)
: z(z)
, offsetString(offsetString)
, name(name)
{
}
} // namespace Escargot
#endif