com.oracle.truffle.js.runtime.builtins.JSDate Maven / Gradle / Ivy
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.js.runtime.builtins;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import org.graalvm.shadowed.com.ibm.icu.impl.Grego;
import org.graalvm.shadowed.com.ibm.icu.text.DateFormat;
import org.graalvm.shadowed.com.ibm.icu.util.GregorianCalendar;
import org.graalvm.shadowed.com.ibm.icu.util.TimeZone;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.js.builtins.DateFunctionBuiltins;
import com.oracle.truffle.js.builtins.DatePrototypeBuiltins;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.Strings;
import com.oracle.truffle.js.runtime.objects.JSAttributes;
import com.oracle.truffle.js.runtime.objects.JSDynamicObject;
import com.oracle.truffle.js.runtime.objects.JSObject;
import com.oracle.truffle.js.runtime.objects.JSObjectUtil;
import com.oracle.truffle.js.runtime.objects.JSShape;
public final class JSDate extends JSNonProxy implements JSConstructorFactory.Default.WithFunctions, PrototypeSupplier {
public static final TruffleString CLASS_NAME = Strings.constant("Date");
public static final TruffleString PROTOTYPE_NAME = Strings.constant("Date.prototype");
public static final JSDate INSTANCE = new JSDate();
private static final int HOURS_PER_DAY = 24;
private static final int MINUTES_PER_HOUR = 60;
private static final int SECONDS_PER_MINUTE = 60;
public static final int MS_PER_SECOND = 1000;
public static final int MS_PER_MINUTE = 60000;
public static final int MS_PER_HOUR = 3600000;
public static final int MS_PER_DAY = 3600000 * 24;
public static final double MAX_DATE = 8.64E15;
// slightly beyond MAX_DATE (+/- 273,790 years)
// cf. https://tc39.es/ecma262/#sec-time-values-and-time-range
public static final double MAX_YEAR_VALUE = 300000;
public static final double MIN_YEAR_VALUE = -300000;
private static final int DAYS_IN_4_YEARS = 4 * 365 + 1;
private static final int DAYS_IN_100_YEARS = 25 * DAYS_IN_4_YEARS - 1;
private static final int DAYS_IN_400_YEARS = 4 * DAYS_IN_100_YEARS + 1;
private static final int DAYS_FROM_1970_TO_2000 = 30 * 365 + 7;
// Helper constants for yearFromTime(), YEAR_SHIFT must be divisible by 400
// and represent more than 30 years plus 100,000,000 days
private static final int YEAR_SHIFT = 280000;
private static final int DAY_SHIFT = (YEAR_SHIFT / 400) * DAYS_IN_400_YEARS;
public static final TruffleString INVALID_DATE_STRING = Strings.constant("Invalid Date");
private JSDate() {
}
public static void setTimeMillisField(JSDateObject obj, double timeMillis) {
assert isJSDate(obj);
obj.setTimeMillis(timeMillis);
}
public static double getTimeMillisField(JSDateObject obj) {
assert isJSDate(obj);
return obj.getTimeMillis();
}
public static boolean isJSDate(Object obj) {
return obj instanceof JSDateObject;
}
@Override
public TruffleString getClassName() {
return CLASS_NAME;
}
@Override
public JSDynamicObject createPrototype(JSRealm realm, JSFunctionObject ctor) {
JSContext ctx = realm.getContext();
JSObject datePrototype;
if (ctx.getEcmaScriptVersion() < 6) {
Shape protoShape = JSShape.createPrototypeShape(realm.getContext(), INSTANCE, realm.getObjectPrototype());
datePrototype = JSDateObject.create(protoShape, realm.getObjectPrototype(), Double.NaN);
JSObjectUtil.setOrVerifyPrototype(ctx, datePrototype, realm.getObjectPrototype());
} else {
datePrototype = JSObjectUtil.createOrdinaryPrototypeObject(realm);
}
JSObjectUtil.putConstructorProperty(datePrototype, ctor);
JSObjectUtil.putFunctionsFromContainer(realm, datePrototype, DatePrototypeBuiltins.BUILTINS);
if (ctx.isOptionAnnexB()) {
Object utcStringFunction = JSDynamicObject.getOrNull(datePrototype, Strings.TO_UTC_STRING);
JSObjectUtil.putDataProperty(datePrototype, Strings.TO_GMT_STRING, utcStringFunction, JSAttributes.getDefaultNotEnumerable());
}
return datePrototype;
}
@Override
public Shape makeInitialShape(JSContext ctx, JSDynamicObject prototype) {
Shape initialShape = JSObjectUtil.getProtoChildShape(prototype, INSTANCE, ctx);
return initialShape;
}
public static JSConstructor createConstructor(JSRealm realm) {
return INSTANCE.createConstructorAndPrototype(realm, DateFunctionBuiltins.BUILTINS);
}
@TruffleBoundary
public static double executeConstructor(double[] argsEvaluated, boolean inputIsUTC) {
double year = argsEvaluated.length > 0 ? argsEvaluated[0] : Double.NaN;
double month = argsEvaluated.length > 1 ? argsEvaluated[1] : 0;
if (Double.isNaN(year) || Double.isInfinite(year) || Double.isNaN(month) || Double.isInfinite(month)) {
return Double.NaN;
}
double day = getArgOrDefault(argsEvaluated, 2, 1);
double hour = getArgOrDefault(argsEvaluated, 3, 0);
double minute = getArgOrDefault(argsEvaluated, 4, 0);
double second = getArgOrDefault(argsEvaluated, 5, 0);
double ms = getArgOrDefault(argsEvaluated, 6, 0);
return makeDate(toFullYear(year), month, day, hour, minute, second, ms, inputIsUTC ? 0 : null);
}
private static double getArgOrDefault(double[] argsEvaluated, int index, int def) {
if (argsEvaluated.length > index) {
return argsEvaluated[index];
}
return def;
}
// 15.9.1.2
public static double day(double t) {
return floor(t / MS_PER_DAY);
}
// 15.9.1.2
private static double timeWithinDay(double t) {
return secureNegativeModulo(t, MS_PER_DAY);
}
public static int dayFromYear(int y) {
return 365 * (y - 1970) + Math.floorDiv(y - 1969, 4) - Math.floorDiv(y - 1901, 100) + Math.floorDiv(y - 1601, 400);
}
@TruffleBoundary
public static int yearFromTime(long t) {
long daysAfter1970 = Math.floorDiv(t, MS_PER_DAY);
assert JSRuntime.longIsRepresentableAsInt(daysAfter1970);
return yearFromDays((int) daysAfter1970);
}
public static int yearFromDays(int daysAfter1970) {
// we need days relative to a year divisible by 400
int daysAfter2000 = daysAfter1970 - DAYS_FROM_1970_TO_2000;
// days after year (2000 - yearShift)
int days = daysAfter2000 + DAY_SHIFT;
// we need days > 0 to ensure that integer division rounds correctly
assert days > 0 : days;
int year = 400 * (days / DAYS_IN_400_YEARS);
int remainingDays = days % DAYS_IN_400_YEARS;
remainingDays--;
year += 100 * (remainingDays / DAYS_IN_100_YEARS);
remainingDays %= DAYS_IN_100_YEARS;
remainingDays++;
year += 4 * (remainingDays / DAYS_IN_4_YEARS);
remainingDays %= DAYS_IN_4_YEARS;
remainingDays--;
year += remainingDays / 365;
return year - YEAR_SHIFT + 2000;
}
public static boolean isLeapYear(int year) {
if (year % 4 != 0) {
return false;
}
if (year % 100 != 0) {
return true;
}
return year % 400 == 0;
}
// 15.9.1.4
@TruffleBoundary
public static int monthFromTime(double dt) {
assert JSRuntime.doubleIsRepresentableAsLong(dt);
long t = (long) dt;
int year = yearFromTime(t);
boolean leapYear = isLeapYear(year);
int day = dayWithinYear(t, year);
return monthFromTimeIntl(leapYear, day);
}
private static int monthFromTimeIntl(boolean leapYear, int day) {
assert (0 <= day) && (day < (365 + (leapYear ? 1 : 0))) : day;
if (day < 31) {
return 0;
}
if (!leapYear) {
if (day < 59) {
return 1;
}
if (day < 90) {
return 2;
}
if (day < 120) {
return 3;
}
if (day < 151) {
return 4;
}
if (day < 181) {
return 5;
}
if (day < 212) {
return 6;
}
if (day < 243) {
return 7;
}
if (day < 273) {
return 8;
}
if (day < 304) {
return 9;
}
if (day < 334) {
return 10;
}
return 11;
} else {
if (day < 60) {
return 1;
}
if (day < 91) {
return 2;
}
if (day < 121) {
return 3;
}
if (day < 152) {
return 4;
}
if (day < 182) {
return 5;
}
if (day < 213) {
return 6;
}
if (day < 244) {
return 7;
}
if (day < 274) {
return 8;
}
if (day < 305) {
return 9;
}
if (day < 335) {
return 10;
}
return 11;
}
}
// 15.9.1.4
private static int dayWithinYear(long t, int year) {
return (int) Math.floorDiv(t, MS_PER_DAY) - dayFromYear(year);
}
// 15.9.1.5
@TruffleBoundary
public static int dateFromTime(double dt) {
assert JSRuntime.doubleIsRepresentableAsLong(dt);
long t = (long) dt;
int year = yearFromTime(t);
int day = dayWithinYear(t, year);
return dateFromDayInYear(year, day);
}
public static int dateFromDayInYear(int year, int day) {
if (day < 31) {
return day + 1;
}
boolean leapYear = isLeapYear(year);
int dayMinusLeap = day - (leapYear ? 1 : 0);
switch (monthFromTimeIntl(leapYear, day)) {
// case 0: //handled above
case 1:
return day - 30;
case 2:
return dayMinusLeap - 58;
case 3:
return dayMinusLeap - 89;
case 4:
return dayMinusLeap - 119;
case 5:
return dayMinusLeap - 150;
case 6:
return dayMinusLeap - 180;
case 7:
return dayMinusLeap - 211;
case 8:
return dayMinusLeap - 242;
case 9:
return dayMinusLeap - 272;
case 10:
return dayMinusLeap - 303;
case 11:
return dayMinusLeap - 333;
}
assert false : "should not reach here";
return -1;
}
// 15.9.1.6
public static double weekDay(double t) {
int result = ((int) day(t) + 4) % 7; // cast to int to avoid -0.0
return result >= 0 ? result : result + 7;
}
public static double localTime(double t, Node node) {
return t + localTZA(t, true, node);
}
private static double utc(double t, Node node) {
return t - localTZA(t, false, node);
}
public static long localTZA(double t, boolean isUTC, Node node) {
return localTZA(t, isUTC, JSRealm.get(node).getLocalTimeZone());
}
private static int getOffset(TimeZone timeZone, long date, int[] fields) {
Grego.timeToFields(date, fields);
return timeZone.getOffset(GregorianCalendar.AD, fields[0], fields[1], fields[2], fields[3], fields[5]);
}
private static int getOffset(TimeZone timeZone, long t, boolean isUTC) {
int rawOffset = timeZone.getRawOffset();
long date = isUTC ? (t + rawOffset) : t; // now in local standard millis
int[] fields = new int[6];
int offset = getOffset(timeZone, date, fields);
if (isUTC) {
return offset;
}
// getOffset() does not match the needs of ECMAScript specification
// when local time does not exist (during STD->DST transition) or when
// it occurs twice (during DST->STD transition). We have to check
// for these corner cases by looking back in time. This apporach
// is taken from TimeZone.getOffset() that does similar tricks
// (but is not ECMAScript compliant still).
if (offset != rawOffset) { // dstOffset != 0
int dstOffset = offset - rawOffset;
// dstOffset != 0 => good, we know how far back in time to look
return getOffset(timeZone, date - dstOffset, fields);
}
// dstOffset = 0, look back by standard DST savings
int dstSavings = timeZone.getDSTSavings();
if (dstSavings == 0) {
// getDSTSavings() returns 0 for some time-zones (like America/Sao_Paulo)
// that stopped to use DST. Unfortunately, we may have a date that
// used DST still => try to use the usual DST savings (1 hour)
dstSavings = 3600000;
}
offset = getOffset(timeZone, date - dstSavings, fields);
int dstOffset = offset - rawOffset;
if (dstOffset != 0 && dstOffset != dstSavings) {
// unexpected irregular (historical) DST => have to correct how far
// back in time we look
offset = getOffset(timeZone, date - dstOffset, fields);
}
return offset;
}
@TruffleBoundary
public static int localTZA(double t, boolean isUTC, TimeZone timeZone) {
return getOffset(timeZone, (long) t, isUTC);
}
// 15.9.1.10
@TruffleBoundary
public static int hourFromTime(double t) {
return (int) secureNegativeModulo(floor(t / MS_PER_HOUR), HOURS_PER_DAY);
}
@TruffleBoundary
public static int minFromTime(double t) {
return (int) secureNegativeModulo(floor(t / MS_PER_MINUTE), MINUTES_PER_HOUR);
}
@TruffleBoundary
public static int secFromTime(double t) {
return (int) secureNegativeModulo(floor(t / MS_PER_SECOND), SECONDS_PER_MINUTE);
}
@TruffleBoundary
public static int msFromTime(double t) {
return (int) secureNegativeModulo(t, MS_PER_SECOND);
}
private static double secureNegativeModulo(double value, double modulo) {
double result = value % modulo;
if (result >= 0) {
return result;
} else {
return result + modulo;
}
}
// 15.9.1.11
@TruffleBoundary
public static double makeTime(double hour, double min, double sec, double ms) {
if (!isFinite(hour) || !isFinite(min) || !isFinite(sec) || !isFinite(ms)) {
return Double.NaN;
}
double h = JSRuntime.truncateDouble(hour);
double m = JSRuntime.truncateDouble(min);
double s = JSRuntime.truncateDouble(sec);
double milli = JSRuntime.truncateDouble(ms);
return h * MS_PER_HOUR + m * MS_PER_MINUTE + s * MS_PER_SECOND + milli;
}
// 15.9.1.12
@TruffleBoundary
public static double makeDay(double year, double month, double date) {
if (!isFinite(year) || !isFinite(month) || !isFinite(date)) {
return Double.NaN;
}
double y = JSRuntime.truncateDouble(year);
double m = JSRuntime.truncateDouble(month);
double dt = JSRuntime.truncateDouble(date);
double ym = y + floor(m / 12);
int mn = (int) (m % 12);
if (mn < 0) {
mn += 12;
}
if (ym < MIN_YEAR_VALUE || ym > MAX_YEAR_VALUE) {
return Double.NaN;
}
return isoDateToEpochDaysResolvedYM((int) ym, mn + 1, 1) + dt - 1;
}
@TruffleBoundary
public static long isoDateToEpochDays(int year, int month, int date) {
int resolvedYear = year + month / 12;
int resolvedMonth = month % 12;
if (resolvedMonth < 0) {
resolvedMonth += 12;
}
return isoDateToEpochDaysResolvedYM(resolvedYear, resolvedMonth + 1, date);
}
@TruffleBoundary
private static long isoDateToEpochDaysResolvedYM(int y, int m, int date) {
return LocalDate.of(y, m, 1).toEpochDay() + date - 1;
}
// 15.9.1.13
@TruffleBoundary
public static double makeDate(double day, double time) {
if (!isFinite(day) || !isFinite(time)) {
return Double.NaN;
}
return (day * MS_PER_DAY + time);
}
@TruffleBoundary
public static double makeDate(double y, double m, double d, double h, double min, double sec, double ms, Integer timezone) {
double day = makeDay(y, m, d);
double time = makeTime(h, min, sec, ms);
double date = makeDate(day, time);
if (timezone == null) {
date = utc(date, null);
} else {
date -= timezone * 60000;
}
return timeClip(date);
}
/**
* Implementation of ECMAScript 5.1 15.9.1.14 TimeClip.
*/
public static double timeClip(double time) {
if (Double.isInfinite(time) || Double.isNaN(time) || Math.abs(time) > MAX_DATE) {
return Double.NaN;
}
// The standard expects only integer values, cf. 15.9.1.1
// it does not state, however, WHERE the conversion should happen
return ((Double) time).longValue();
}
// helper function
private static boolean isFinite(double d) {
return !(Double.isNaN(d) || Double.isInfinite(d));
}
// helper function
private static double floor(double d) {
return Math.floor(d);
}
public static JSDateObject create(JSContext context, JSRealm realm, double timeMillis) {
JSObjectFactory factory = context.getDateFactory();
return create(factory, realm, factory.getPrototype(realm), timeMillis);
}
public static JSDateObject create(JSContext context, JSRealm realm, JSDynamicObject proto, double timeMillis) {
JSObjectFactory factory = context.getDateFactory();
return create(factory, realm, proto, timeMillis);
}
private static JSDateObject create(JSObjectFactory factory, JSRealm realm, JSDynamicObject proto, double timeMillis) {
var shape = factory.getShape(realm, proto);
var newObj = factory.initProto(new JSDateObject(shape, proto, timeMillis), realm, proto);
return factory.trackAllocation(newObj);
}
public static double setTime(JSDateObject thisDate, double time) {
double v = timeClip(time);
setTimeMillisField(thisDate, v);
return v;
}
public static double setMilliseconds(JSDateObject thisDate, double ms, boolean isUTC, Node node) {
double t = localTime(getTimeMillisField(thisDate), isUTC, node);
double time = makeTime(hourFromTime(t), minFromTime(t), secFromTime(t), ms);
double u = timeClip(utc(makeDate(day(t), time), isUTC, node));
setTimeMillisField(thisDate, u);
return u;
}
public static double setSeconds(JSDateObject thisDate, double s, double ms, boolean msSpecified, boolean isUTC, Node node) {
double t = localTime(getTimeMillisField(thisDate), isUTC, node);
double milli = msSpecified ? ms : msFromTime(t);
double date = makeDate(day(t), makeTime(hourFromTime(t), minFromTime(t), s, milli));
double u = timeClip(utc(date, isUTC, node));
setTimeMillisField(thisDate, u);
return u;
}
public static double setMinutes(JSDateObject thisDate, double m, double s, boolean sSpecified, double ms, boolean msSpecified, boolean isUTC, Node node) {
double t = localTime(getTimeMillisField(thisDate), isUTC, node);
double milli = msSpecified ? ms : msFromTime(t);
double sec = sSpecified ? s : secFromTime(t);
double date = makeDate(day(t), makeTime(hourFromTime(t), m, sec, milli));
double u = timeClip(utc(date, isUTC, node));
setTimeMillisField(thisDate, u);
return u;
}
public static double setHours(JSDateObject thisDate, double h, double m, boolean mSpecified, double s, boolean sSpecified, double ms, boolean msSpecified, boolean isUTC, Node node) {
double t = localTime(getTimeMillisField(thisDate), isUTC, node);
double milli = msSpecified ? ms : msFromTime(t);
double sec = sSpecified ? s : secFromTime(t);
double min = mSpecified ? m : minFromTime(t);
double date = makeDate(day(t), makeTime(h, min, sec, milli));
double u = timeClip(utc(date, isUTC, node));
setTimeMillisField(thisDate, u);
return u;
}
public static double setDate(JSDateObject thisDate, double date, boolean isUTC, Node node) {
double t = localTime(getTimeMillisField(thisDate), isUTC, node);
double u;
if (Double.isNaN(t)) {
u = Double.NaN;
} else {
double newDate = makeDate(makeDay(yearFromTime((long) t), monthFromTime(t), date), timeWithinDay(t));
u = timeClip(utc(newDate, isUTC, node));
}
setTimeMillisField(thisDate, u);
return u;
}
public static double setMonth(JSDateObject thisDate, double month, double date, boolean dateSpecified, boolean isUTC, Node node) {
double t = localTime(getTimeMillisField(thisDate), isUTC, node);
double newDate;
if (Double.isNaN(t)) {
newDate = Double.NaN;
} else {
double dt = dateSpecified ? date : dateFromTime(t);
newDate = timeClip(utc(makeDate(makeDay(yearFromTime((long) t), month, dt), timeWithinDay(t)), isUTC, node));
}
setTimeMillisField(thisDate, newDate);
return newDate;
}
public static double setFullYear(JSDateObject thisDate, double year, double month, boolean monthSpecified, double date, boolean dateSpecified, boolean isUTC, Node node) {
double timeFieldValue = getTimeMillisField(thisDate);
double t = Double.isNaN(timeFieldValue) ? 0 : localTime(timeFieldValue, isUTC, node);
double dt = dateSpecified ? date : dateFromTime(t);
double m = monthSpecified ? month : monthFromTime(t);
double newDate = makeDate(makeDay(year, m, dt), timeWithinDay(t));
double u = timeClip(utc(newDate, isUTC, node));
setTimeMillisField(thisDate, u);
return u;
}
public static double setYear(JSDateObject thisDate, double year, Node node) {
double t = getTimeMillisField(thisDate);
t = Double.isNaN(t) ? 0 : localTime(t, node); // cf. B.2.5, clause 1
if (Double.isNaN(year)) {
setTimeMillisField(thisDate, Double.NaN);
return Double.NaN;
}
double fullYear = toFullYear(year);
double r5 = makeDay(fullYear, monthFromTime(t), dateFromTime(t));
double r6 = timeClip(utc(makeDate(r5, timeWithinDay(t)), node));
setTimeMillisField(thisDate, r6);
return r6;
}
private static double toFullYear(double year) {
// 0 <= ToInteger(year) <= 99 according to standard, but we are omitting the ToInteger here!
if (-1 < year && year < 100) {
return 1900 + (int) year;
}
return year;
}
@TruffleBoundary
public static TruffleString format(DateFormat format, double time) {
return Strings.fromJavaString(format.format(time));
}
public static TruffleString toString(double time, JSRealm realm) {
if (Double.isNaN(time)) {
return INVALID_DATE_STRING;
}
return format(realm.getDateToStringFormat(), time);
}
public static TruffleString toISOStringIntl(double time, JSRealm realm) {
return format(realm.getJSDateISOFormat(time), time);
}
public static boolean isTimeValid(double time) {
return !(Double.isNaN(time) || Double.isInfinite(time));
}
private static double localTime(double time, boolean isUTC, Node node) {
return isUTC ? time : localTime(time, node);
}
private static double utc(double time, boolean isUTC, Node node) {
return isUTC ? time : utc(time, node);
}
public static boolean isValidDate(JSDateObject date) {
return !Double.isNaN(getTimeMillisField(date));
}
@TruffleBoundary
public static Instant asInstant(JSDateObject date) {
assert isValidDate(date);
return Instant.ofEpochMilli((long) getTimeMillisField(date));
}
@TruffleBoundary
public static LocalDate asLocalDate(JSDateObject date, JSRealm realm) {
return LocalDate.from(asInstant(date).atZone(realm.getLocalTimeZoneId()));
}
@TruffleBoundary
public static LocalTime asLocalTime(JSDateObject date, JSRealm realm) {
return LocalTime.from(asInstant(date).atZone(realm.getLocalTimeZoneId()));
}
public static double getDateValueFromInstant(Object receiver, InteropLibrary interop) {
Instant instant;
try {
instant = interop.asInstant(receiver);
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorInteropException(receiver, e, "asInstant", null);
}
try {
return instant.toEpochMilli();
} catch (ArithmeticException e) {
return Double.NaN;
}
}
@Override
public JSDynamicObject getIntrinsicDefaultProto(JSRealm realm) {
return realm.getDatePrototype();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy