/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
* Free SoftwareFoundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.es;
import com.caucho.util.Alarm;
import com.caucho.util.QDate;
/**
* JavaScript Date object
*/
class NativeDate extends Native {
static final int NEW = 2;
static final int UTC = 3;
static final int VALUE_OF = 4;
static final int TO_STRING = 6;
static final int TO_UTC_STRING = 7;
static final int TO_ISO_STRING = 8;
static final int TO_UTC_ISO_STRING = 9;
static final int TO_ISO_DATE = 10;
static final int TO_UTC_ISO_DATE = 11;
static final int TO_LOCALE_STRING = 12;
static final int UTC_FORMAT = 13;
static final int FORMAT = 14;
static final int PARSE_DATE = 15;
static final int GET_FULL_YEAR = 20;
static final int GET_UTC_FULL_YEAR = 21;
static final int GET_MONTH = 22;
static final int GET_UTC_MONTH = 23;
static final int GET_DATE = 24;
static final int GET_UTC_DATE = 25;
static final int GET_DAY = 26;
static final int GET_UTC_DAY = 27;
static final int GET_HOURS = 28;
static final int GET_UTC_HOURS = 29;
static final int GET_MINUTES = 30;
static final int GET_UTC_MINUTES = 31;
static final int GET_SECONDS = 32;
static final int GET_UTC_SECONDS = 33;
static final int GET_MILLISECONDS = 34;
static final int GET_UTC_MILLISECONDS = 35;
static final int GET_TIMEZONE_OFFSET = 36;
static final int SET_FULL_YEAR = 50;
static final int SET_UTC_FULL_YEAR = 51;
static final int SET_MONTH = 52;
static final int SET_UTC_MONTH = 53;
static final int SET_DATE = 54;
static final int SET_UTC_DATE = 55;
static final int SET_HOURS = 56;
static final int SET_UTC_HOURS = 57;
static final int SET_MINUTES = 58;
static final int SET_UTC_MINUTES = 59;
static final int SET_SECONDS = 60;
static final int SET_UTC_SECONDS = 61;
static final int SET_MILLISECONDS = 62;
static final int SET_UTC_MILLISECONDS = 63;
static final int GET_MONTH_NAME = SET_UTC_MILLISECONDS + 1;
static final int GET_UTC_MONTH_NAME = GET_MONTH_NAME + 1;
private static String []monthNames = new String[] {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
QDate localCal = QDate.createLocal();
QDate utcCal = new QDate();
QDate cal = localCal;
/**
* Create a new object based on a prototype
*/
private NativeDate(String name, int n, int len)
{
super(name, len);
this.n = n;
// this.cal = cal;
}
/**
* Creates the native Object object
*/
static ESObject create(Global resin)
{
// QDate cal = QDate.createLocal();
NativeDate nativeDate = new NativeDate("Date", NEW, 7);
ESObject dateProto = new ESDate(Long.MAX_VALUE, resin.objProto);
NativeWrapper date = new NativeWrapper(resin, nativeDate,
dateProto, ESThunk.DATE_THUNK);
nativeDate.newN = nativeDate.n;
resin.dateProto = dateProto;
put(dateProto, "toString", TO_STRING, 0);
put(dateProto, "getTime", VALUE_OF, 0);
put(dateProto, "valueOf", VALUE_OF, 0);
put(dateProto, "toUTCString", TO_UTC_STRING, 0);
put(dateProto, "toGMTString", TO_UTC_STRING, 0);
put(dateProto, "toLocalISO8601", TO_ISO_STRING, 0);
put(dateProto, "toISO8601", TO_UTC_ISO_STRING, 0);
put(dateProto, "format", FORMAT, 0);
put(dateProto, "UTCFormat", UTC_FORMAT, 0);
put(dateProto, "toLocaleString", TO_LOCALE_STRING, 0);
put(dateProto, "getUTCYear", GET_UTC_FULL_YEAR, 0);
put(dateProto, "getUTCFullYear", GET_UTC_FULL_YEAR, 0);
put(dateProto, "getUTCMonth", GET_UTC_MONTH, 0);
put(dateProto, "getUTCDate", GET_UTC_DATE, 0);
put(dateProto, "getUTCDay", GET_UTC_DAY, 0);
put(dateProto, "getUTCHours", GET_UTC_HOURS, 0);
put(dateProto, "getUTCMinutes", GET_UTC_MINUTES, 0);
put(dateProto, "getUTCSeconds", GET_UTC_SECONDS, 0);
put(dateProto, "getUTCMilliseconds", GET_UTC_MILLISECONDS, 0);
put(dateProto, "setUTCYear", SET_UTC_FULL_YEAR, 1);
put(dateProto, "setUTCFullYear", SET_UTC_FULL_YEAR, 1);
put(dateProto, "setUTCMonth", SET_UTC_MONTH, 2);
put(dateProto, "setUTCDate", SET_UTC_DATE, 3);
put(dateProto, "setUTCHours", SET_UTC_HOURS, 4);
put(dateProto, "setUTCMinutes", SET_UTC_MINUTES, 3);
put(dateProto, "setUTCSeconds", SET_UTC_SECONDS, 2);
put(dateProto, "setUTCMilliseconds", SET_UTC_MILLISECONDS, 1);
put(dateProto, "getYear", GET_FULL_YEAR, 0);
put(dateProto, "getFullYear", GET_FULL_YEAR, 0);
put(dateProto, "getMonth", GET_MONTH, 0);
put(dateProto, "getMonthName", GET_MONTH_NAME, 0);
put(dateProto, "getDate", GET_DATE, 0);
put(dateProto, "getDay", GET_DAY, 0);
put(dateProto, "getHours", GET_HOURS, 0);
put(dateProto, "getMinutes", GET_MINUTES, 0);
put(dateProto, "getSeconds", GET_SECONDS, 0);
put(dateProto, "getMilliseconds", GET_MILLISECONDS, 0);
put(dateProto, "getTimezoneOffset", GET_TIMEZONE_OFFSET, 0);
put(dateProto, "setYear", SET_FULL_YEAR, 3);
put(dateProto, "setFullYear", SET_FULL_YEAR, 3);
put(dateProto, "setMonth", SET_MONTH, 2);
put(dateProto, "setDate", SET_DATE, 1);
put(dateProto, "setHours", SET_HOURS, 4);
put(dateProto, "setMinutes", SET_MINUTES, 3);
put(dateProto, "setSeconds", SET_SECONDS, 2);
put(dateProto, "setMilliseconds", SET_MILLISECONDS, 1);
put(date, "UTC", UTC, 7);
put(date, "parse", PARSE_DATE, 1);
dateProto.setClean();
date.setClean();
return date;
}
private static void put(ESObject obj, String name, int n, int len)
{
obj.put(ESId.intern(name), new NativeDate(name, n, len), DONT_ENUM);
}
public ESBase call(Call eval, int length) throws Throwable
{
long time = 0;
double value;
ESBase error;
synchronized (cal) {
int off = 0;
switch (n) {
case NEW:
return ESDate.create(create(eval, length, n));
case UTC:
return ESNumber.create(create(eval, length, n));
case TO_STRING:
case TO_UTC_STRING:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESString.create(cal.printDate());
case TO_ISO_STRING:
case TO_UTC_ISO_STRING:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESString.create(cal.printISO8601());
case TO_ISO_DATE:
case TO_UTC_ISO_DATE:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESString.create(cal.printISO8601Date());
case FORMAT:
case UTC_FORMAT:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESString.create(cal.format(eval.getArgString(0, length)));
case TO_LOCALE_STRING:
if ((error = calculate(eval.getArg(-1), 1, TO_STRING)) != null)
return error;
return ESString.create(cal.printLocaleDate());
case VALUE_OF:
if (! (eval.getArg(-1) instanceof ESDate))
throw new ESException("valueOf must be bound to date");
value = (double) ((ESDate) eval.getArg(-1)).time;
if (value > 8.64e15 || value < -8.64e15 || Double.isNaN(value))
value = 0.0/0.0;
return ESNumber.create(value);
case PARSE_DATE:
if (length < 0)
return ESNumber.NaN;
try {
long lvalue = cal.parseDate(eval.getArg(0).toStr().toString());
return ESNumber.create(millisToDouble(lvalue));
} catch (Exception e) {
throw new ESException(e.toString());
}
case GET_FULL_YEAR:
case GET_UTC_FULL_YEAR:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESNumber.create((double) cal.get(cal.YEAR));
case GET_MONTH:
case GET_UTC_MONTH:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESNumber.create((double) cal.get(cal.MONTH));
case GET_MONTH_NAME:
case GET_UTC_MONTH_NAME:
{
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
int month = (int) cal.get(cal.MONTH);
return ESString.create(monthNames[month]);
}
case GET_DATE:
case GET_UTC_DATE:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESNumber.create((double) cal.get(cal.DAY_OF_MONTH) + 1);
case GET_DAY:
case GET_UTC_DAY:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESNumber.create((double) cal.get(cal.DAY));
case GET_HOURS:
case GET_UTC_HOURS:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESNumber.create((double) (cal.get(cal.HOUR)));
case GET_MINUTES:
case GET_UTC_MINUTES:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESNumber.create((double) (cal.get(cal.MINUTE)));
case GET_SECONDS:
case GET_UTC_SECONDS:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESNumber.create((double) (cal.get(cal.SECOND)));
case GET_MILLISECONDS:
case GET_UTC_MILLISECONDS:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESNumber.create((double) cal.get(cal.MILLISECOND));
case GET_TIMEZONE_OFFSET:
if ((error = calculate(eval.getArg(-1), 1, n)) != null)
return error;
return ESNumber.create((double) (cal.getZoneOffset() / 60000));
case SET_DATE:
case SET_UTC_DATE:
off--;
case SET_MONTH:
case SET_UTC_MONTH:
off--;
case SET_FULL_YEAR:
case SET_UTC_FULL_YEAR:
if ((error = calculate(eval.getArg(-1), length, n)) != null)
return error;
if (0 <= off)
cal.set(cal.YEAR, (long) eval.getArg(off).toNum());
if (0 <= off + 1 && off + 1 < length) {
value = eval.getArg(off + 1).toNum();
cal.set(cal.MONTH,
(long) (Double.isNaN(value) ? Long.MAX_VALUE : value));
}
if (0 <= off + 2 && off + 2 < length) {
value = eval.getArg(off + 2).toNum();
cal.set(cal.DAY_OF_MONTH,
(long) (Double.isNaN(value) ? Long.MAX_VALUE : value - 1));
}
return create(eval.getArg(-1), n);
case SET_MILLISECONDS:
case SET_UTC_MILLISECONDS:
off--;
case SET_SECONDS:
case SET_UTC_SECONDS:
off--;
case SET_MINUTES:
case SET_UTC_MINUTES:
off--;
case SET_HOURS:
case SET_UTC_HOURS:
if ((error = calculate(eval.getArg(-1), length, n)) != null)
return error;
if (0 <= off) {
value = eval.getArg(off).toNum();
cal.set(cal.HOUR,
(long) (Double.isNaN(value) ? Long.MAX_VALUE : value));
}
if (0 <= off + 1 && off + 1 < length) {
value = eval.getArg(off + 1).toNum();
cal.set(cal.MINUTE,
(long) (Double.isNaN(value) ? Long.MAX_VALUE : value));
}
if (0 <= off + 2 && off + 2 < length) {
value = eval.getArg(off + 2).toNum();
cal.set(cal.SECOND,
(long) (Double.isNaN(value) ? Long.MAX_VALUE : value));
}
if (0 <= off + 3 && off + 3 < length) {
value = eval.getArg(off + 3).toNum();
cal.set(cal.MILLISECOND,
(long) (Double.isNaN(value) ? Long.MAX_VALUE : value));
}
return create(eval.getArg(-1), n);
default:
throw new ESException("Unknown object function");
}
}
}
public ESBase construct(Call eval, int length) throws Throwable
{
if (cal == null)
cal = new QDate();
if (n != NEW)
return super.construct(eval, length);
synchronized (cal) {
return ESDate.create(create(eval, length, NEW));
}
}
private long create(Call eval, int length, int code)
throws Throwable
{
boolean isLocal = (code & 1) == 0;
long value = 0;
if (length == 0) {
return Alarm.getCurrentTime();
}
else if (length == 1)
value = (long) (eval.getArg(0).toNum());
else if (length >= 3) {
long year = (long) eval.getArg(0).toNum();
long month = (long) eval.getArg(1).toNum();
long day = (long) eval.getArg(2).toNum() - 1;
long hour = 0;
if (length >= 4)
hour = (long) eval.getArg(3).toNum();
long minute = 0;
if (length >= 5)
minute = (long) eval.getArg(4).toNum();
long second = 0;
if (length >= 6)
second = (long) eval.getArg(5).toNum();
long ms = 0;
if (length >= 7)
ms = (long) eval.getArg(6).toNum();
cal.setDate(year, month, day);
cal.setTime(hour, minute, second, ms);
value = cal.get(cal.TIME);
if (isLocal)
value -= cal.getZoneOffset();
}
else
value = Long.MIN_VALUE;
return value;
}
private double millisToDouble(long millis)
{
double dvalue = millis;
if (dvalue > 8.64e15 || dvalue < -8.64e15 || Double.isNaN(dvalue))
dvalue = 0.0/0.0;
return dvalue;
}
private ESBase create(ESBase obj, int code) throws ESException
{
boolean isLocal = (code & 1) == 0;
long value = cal.get(cal.TIME);
if (isLocal)
value -= cal.getZoneOffset();
double dvalue = value;
if (dvalue > 8.64e15 || dvalue < -8.64e15 || Double.isNaN(dvalue))
dvalue = 0.0/0.0;
if (! (obj instanceof ESDate))
return ESNumber.create(dvalue);
ESNumber newValue = ESNumber.create(dvalue);
((ESDate) obj).time = (long) dvalue;
return newValue;
}
private ESBase calculate(ESBase arg, int length, int code)
throws Throwable
{
boolean isLocal = (code & 1) == 0;
double value = arg.toNum();
if (Double.isNaN(value) || value > 8.64e15 || value < -8.64e15 ||
length < 1)
return ESNumber.NaN;
long time = (long) value;
if (isLocal)
cal = localCal;
else
cal = utcCal;
cal.setGMTTime(time);
return null;
}
}