package com.oracle.truffle.js.runtime.builtins;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Year;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import com.oracle.truffle.api.CompilerDirectives;
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.object.DynamicObject;
import com.oracle.truffle.api.object.Shape;
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.objects.JSAttributes;
import com.oracle.truffle.js.runtime.objects.JSDynamicObject;
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 String CLASS_NAME = "Date";
public static final String PROTOTYPE_NAME = "Date.prototype";
private static DateTimeFormatter jsDateFormat;
private static DateTimeFormatter jsDateFormatBeforeYear0;
private static DateTimeFormatter jsDateFormatAfterYear9999;
private static DateTimeFormatter jsDateFormatISO;
private static DateTimeFormatter jsShortDateFormat;
private static DateTimeFormatter jsShortDateLocalFormat;
private static DateTimeFormatter jsShortTimeFormat;
private static DateTimeFormatter jsShortTimeLocalFormat;
private static DateTimeFormatter jsDateToStringFormat;
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;
private static final int MS_PER_SECOND = 1000;
public static final int MS_PER_MINUTE = 60000;
private static final int MS_PER_HOUR = 3600000;
public static final int MS_PER_DAY = 3600000 * 24;
public static final double MAX_DATE = 8.64E15;
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;
private static final int YEAR_SHIFT = 280000;
private static final int DAY_SHIFT = (YEAR_SHIFT / 400) * DAYS_IN_400_YEARS;
public static final String INVALID_DATE_STRING = "Invalid Date";
private JSDate() {
}
public static void setTimeMillisField(DynamicObject obj, double timeMillis) {
assert isJSDate(obj);
((JSDateObject) obj).setTimeMillis(timeMillis);
}
public static double getTimeMillisField(DynamicObject obj) {
assert isJSDate(obj);
return ((JSDateObject) obj).getTimeMillis();
}
public static boolean isJSDate(Object obj) {
return obj instanceof JSDateObject;
}
@Override
public String getClassName() {
return CLASS_NAME;
}
@Override
public String getClassName(DynamicObject object) {
return getClassName();
}
@Override
public String getBuiltinToStringTag(DynamicObject object) {
return getClassName(object);
}
@Override
public DynamicObject createPrototype(JSRealm realm, DynamicObject ctor) {
JSContext ctx = realm.getContext();
DynamicObject datePrototype;
if (ctx.getEcmaScriptVersion() < 6) {
Shape protoShape = JSShape.createPrototypeShape(realm.getContext(), INSTANCE, realm.getObjectPrototype());
datePrototype = JSDateObject.create(protoShape, Double.NaN);
JSObjectUtil.setOrVerifyPrototype(ctx, datePrototype, realm.getObjectPrototype());
} else {
datePrototype = JSObjectUtil.createOrdinaryPrototypeObject(realm);
}
JSObjectUtil.putConstructorProperty(ctx, datePrototype, ctor);
JSObjectUtil.putFunctionsFromContainer(realm, datePrototype, DatePrototypeBuiltins.BUILTINS);
if (ctx.isOptionAnnexB()) {
Object utcStringFunction = JSDynamicObject.getOrNull(datePrototype, "toUTCString");
JSObjectUtil.putDataProperty(ctx, datePrototype, "toGMTString", utcStringFunction, JSAttributes.getDefaultNotEnumerable());
}
return datePrototype;
}
@Override
public Shape makeInitialShape(JSContext ctx, DynamicObject 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, JSContext context) {
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, context);
}
private static double getArgOrDefault(double[] argsEvaluated, int index, int def) {
if (argsEvaluated.length > index) {
return argsEvaluated[index];
}
return def;
}
private static double day(double t) {
return floor(t / MS_PER_DAY);
}
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) {
int daysAfter2000 = daysAfter1970 - DAYS_FROM_1970_TO_2000;
int days = daysAfter2000 + DAY_SHIFT;
assert days > 0;
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;
}
private static boolean isLeapYear(int year) {
if (year % 4 != 0) {
return false;
}
if (year % 100 != 0) {
return true;
}
return year % 400 == 0;
}
@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))) : "should not reach here";
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;
}
}
private static int dayWithinYear(long t, int year) {
return (int) Math.floorDiv(t, MS_PER_DAY) - dayFromYear(year);
}
@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 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;
}
public static double weekDay(double t) {
int result = ((int) day(t) + 4) % 7;
return result >= 0 ? result : result + 7;
}
public static double localTime(double t, JSContext context) {
return t + localTZA(t, true, context);
}
private static double utc(double t, JSContext context) {
return t - localTZA(t, false, context);
}
public static long localTZA(double t, boolean isUTC, JSContext context) {
return localTZA(t, isUTC, context.getRealm().getLocalTimeZoneId());
}
@TruffleBoundary
public static long localTZA(double t, boolean isUTC, ZoneId zoneId) {
ZoneOffset zoneOffset;
if (isUTC) {
zoneOffset = zoneId.getRules().getOffset(Instant.ofEpochMilli((long) t));
} else {
if (!(Math.abs(t) < MAX_DATE + MS_PER_DAY)) {
return 0;
}
LocalDateTime localDateTime = LocalDateTime.of(yearFromTime((long) t), 1 + monthFromTime(t), dateFromTime(t), hourFromTime(t), minFromTime(t), secFromTime(t), msFromTime(t));
zoneOffset = zoneId.getRules().getOffset(localDateTime);
}
return zoneOffset.getTotalSeconds() * 1000L;
}
@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;
}
}
@TruffleBoundary
private static double makeTime(double hour, double min, double sec, double ms) {
if (!isFinite(hour) || !isFinite(min) || !isFinite(sec) || !isFinite(ms)) {
return Double.NaN;
}
long h = doubleToLong(hour);
long m = doubleToLong(min);
long s = doubleToLong(sec);
long milli = doubleToLong(ms);
return h * MS_PER_HOUR + m * MS_PER_MINUTE + s * MS_PER_SECOND + (double) milli;
}
@TruffleBoundary
private static double makeDay(double year, double month, double date) {
if (!isFinite(year) || !isFinite(month) || !isFinite(date)) {
return Double.NaN;
}
double y = doubleToLong(year);
double m = doubleToLong(month);
double dt = doubleToLong(date);
double ym = y + floor(m / 12);
int mn = (int) (m % 12);
if (mn < 0) {
mn += 12;
}
if (ym < Year.MIN_VALUE || ym > Year.MAX_VALUE) {
return Double.NaN;
}
double t = LocalDate.of((int) ym, mn + 1, 1).atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli();
return day(t) + dt - 1;
}
private static long doubleToLong(double value) {
assert !Double.isNaN(value);
return (long) value;
}
@TruffleBoundary
private 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, JSContext context) {
double day = makeDay(y, m, d);
double time = makeTime(h, min, sec, ms);
double date = makeDate(day, time);
if (timezone == null) {
date = utc(date, context);
} else {
date -= timezone * 60000;
}
return timeClip(date);
}
public static double timeClip(double time) {
if (Double.isInfinite(time) || Double.isNaN(time) || Math.abs(time) > MAX_DATE) {
return Double.NaN;
}
return ((Double) time).longValue();
}
private static boolean isFinite(double d) {
return !(Double.isNaN(d) || Double.isInfinite(d));
}
private static double floor(double d) {
return JSRuntime.mathFloor(d);
}
public static DynamicObject create(JSContext context, double timeMillis) {
JSRealm realm = context.getRealm();
JSObjectFactory factory = context.getDateFactory();
DynamicObject obj = JSDateObject.create(factory.getShape(realm), timeMillis);
factory.initProto(obj, realm);
assert isJSDate(obj);
return context.trackAllocation(obj);
}
public static double setTime(DynamicObject thisDate, double time) {
double v = timeClip(time);
setTimeMillisField(thisDate, v);
return v;
}
public static double setMilliseconds(DynamicObject thisDate, double ms, boolean isUTC, JSContext context) {
double t = localTime(getTimeMillisField(thisDate), isUTC, context);
double time = makeTime(hourFromTime(t), minFromTime(t), secFromTime(t), ms);
double u = timeClip(utc(makeDate(day(t), time), isUTC, context));
setTimeMillisField(thisDate, u);
return u;
}
public static double setSeconds(DynamicObject thisDate, double s, double ms, boolean msSpecified, boolean isUTC, JSContext context) {
double t = localTime(getTimeMillisField(thisDate), isUTC, context);
double milli = msSpecified ? ms : msFromTime(t);
double date = makeDate(day(t), makeTime(hourFromTime(t), minFromTime(t), s, milli));
double u = timeClip(utc(date, isUTC, context));
setTimeMillisField(thisDate, u);
return u;
}
public static double setMinutes(DynamicObject thisDate, double m, double s, boolean sSpecified, double ms, boolean msSpecified, boolean isUTC, JSContext context) {
double t = localTime(getTimeMillisField(thisDate), isUTC, context);
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, context));
setTimeMillisField(thisDate, u);
return u;
}
public static double setHours(DynamicObject thisDate, double h, double m, boolean mSpecified, double s, boolean sSpecified, double ms, boolean msSpecified, boolean isUTC, JSContext context) {
double t = localTime(getTimeMillisField(thisDate), isUTC, context);
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, context));
setTimeMillisField(thisDate, u);
return u;
}
public static double setDate(DynamicObject thisDate, double date, boolean isUTC, JSContext context) {
double t = localTime(getTimeMillisField(thisDate), isUTC, context);
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, context));
}
setTimeMillisField(thisDate, u);
return u;
}
public static double setMonth(DynamicObject thisDate, double month, double date, boolean dateSpecified, boolean isUTC, JSContext context) {
double t = localTime(getTimeMillisField(thisDate), isUTC, context);
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, context));
}
setTimeMillisField(thisDate, newDate);
return newDate;
}
public static double setFullYear(DynamicObject thisDate, double year, double month, boolean monthSpecified, double date, boolean dateSpecified, boolean isUTC, JSContext context) {
double timeFieldValue = getTimeMillisField(thisDate);
double t = Double.isNaN(timeFieldValue) ? 0 : localTime(timeFieldValue, isUTC, context);
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, context));
setTimeMillisField(thisDate, u);
return u;
}
public static double setYear(DynamicObject thisDate, double year, JSContext context) {
double t = getTimeMillisField(thisDate);
t = Double.isNaN(t) ? 0 : localTime(t, context);
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)), context));
setTimeMillisField(thisDate, r6);
return r6;
}
private static double toFullYear(double year) {
if (-1 < year && year < 100) {
return 1900 + (int) year;
}
return year;
}
@TruffleBoundary
public static String formatLocal(DateTimeFormatter format, double time, JSRealm realm) {
return Instant.ofEpochMilli((long) time).atZone(realm.getLocalTimeZoneId()).format(format);
}
@TruffleBoundary
public static String formatUTC(DateTimeFormatter format, double time) {
return Instant.ofEpochMilli((long) time).atZone(ZoneOffset.UTC).format(format);
}
@TruffleBoundary
public static String toString(double time, JSRealm realm) {
if (Double.isNaN(time)) {
return INVALID_DATE_STRING;
}
return formatLocal(getDateToStringFormat(), time, realm);
}
@TruffleBoundary
public static String toISOStringIntl(double time) {
return formatUTC(getJSDateFormat(time), time);
}
public static boolean isTimeValid(double time) {
return !(Double.isNaN(time) || Double.isInfinite(time));
}
private static double localTime(double time, boolean isUTC, JSContext context) {
return isUTC ? time : localTime(time, context);
}
private static double utc(double time, boolean isUTC, JSContext context) {
return isUTC ? time : utc(time, context);
}
public static boolean isValidDate(DynamicObject date) {
return isJSDate(date) && !Double.isNaN(getTimeMillisField(date));
}
@TruffleBoundary
public static Instant asInstant(DynamicObject date) {
assert isValidDate(date);
return Instant.ofEpochMilli((long) getTimeMillisField(date));
}
@TruffleBoundary
public static LocalDate asLocalDate(DynamicObject date, JSRealm realm) {
return LocalDate.from(asInstant(date).atZone(realm.getLocalTimeZoneId()));
}
@TruffleBoundary
public static LocalTime asLocalTime(DynamicObject 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;
}
}
public static DateTimeFormatter getJSDateFormat(double time) {
long milliseconds = (long) time;
if (milliseconds < -62167219200000L) {
if (jsDateFormatBeforeYear0 == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
jsDateFormatBeforeYear0 = DateTimeFormatter.ofPattern("uuuuuu-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
}
return jsDateFormatBeforeYear0;
} else if (milliseconds >= 253402300800000L) {
if (jsDateFormatAfterYear9999 == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
jsDateFormatAfterYear9999 = DateTimeFormatter.ofPattern("+uuuuuu-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
}
return jsDateFormatAfterYear9999;
} else {
if (jsDateFormat == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
jsDateFormat = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
}
return jsDateFormat;
}
}
public static DateTimeFormatter getJSDateUTCFormat() {
if (jsDateFormatISO == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
jsDateFormatISO = DateTimeFormatter.ofPattern("EEE, dd MMM uuuu HH:mm:ss 'GMT'", Locale.US);
}
return jsDateFormatISO;
}
public static DateTimeFormatter getJSShortDateFormat() {
if (jsShortDateFormat == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
jsShortDateFormat = DateTimeFormatter.ofPattern("EEE MMM dd uuuu", Locale.US);
}
return jsShortDateFormat;
}
public static DateTimeFormatter getJSShortDateLocalFormat() {
if (jsShortDateLocalFormat == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
jsShortDateLocalFormat = DateTimeFormatter.ofPattern("uuuu-MM-dd", Locale.US);
}
return jsShortDateLocalFormat;
}
public static DateTimeFormatter getJSShortTimeFormat() {
if (jsShortTimeFormat == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
jsShortTimeFormat = DateTimeFormatter.ofPattern("HH:mm:ss 'GMT'Z (z)", Locale.US);
}
return jsShortTimeFormat;
}
public static DateTimeFormatter getJSShortTimeLocalFormat() {
if (jsShortTimeLocalFormat == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
jsShortTimeLocalFormat = DateTimeFormatter.ofPattern("HH:mm:ss", Locale.US);
}
return jsShortTimeLocalFormat;
}
public static DateTimeFormatter getDateToStringFormat() {
if (jsDateToStringFormat == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
jsDateToStringFormat = DateTimeFormatter.ofPattern("EEE MMM dd uuuu HH:mm:ss 'GMT'Z (z)", Locale.US);
}
return jsDateToStringFormat;
}
@TruffleBoundary
@Override
public String toDisplayStringImpl(DynamicObject obj, int depth, boolean allowSideEffects, JSContext context) {
double time = getTimeMillisField(obj);
String formattedDate;
if (isTimeValid(time)) {
formattedDate = toISOStringIntl(time);
} else {
formattedDate = INVALID_DATE_STRING;
}
if (context.isOptionNashornCompatibilityMode()) {
return "[Date " + formattedDate + "]";
} else {
return formattedDate;
}
}
@Override
public DynamicObject getIntrinsicDefaultProto(JSRealm realm) {
return realm.getDatePrototype();
}
}