001: /*
002: * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.es;
030:
031: import com.caucho.util.Alarm;
032: import com.caucho.util.QDate;
033:
034: /**
035: * JavaScript Date object
036: */
037: class NativeDate extends Native {
038: static final int NEW = 2;
039: static final int UTC = 3;
040: static final int VALUE_OF = 4;
041: static final int TO_STRING = 6;
042: static final int TO_UTC_STRING = 7;
043: static final int TO_ISO_STRING = 8;
044: static final int TO_UTC_ISO_STRING = 9;
045: static final int TO_ISO_DATE = 10;
046: static final int TO_UTC_ISO_DATE = 11;
047: static final int TO_LOCALE_STRING = 12;
048: static final int UTC_FORMAT = 13;
049: static final int FORMAT = 14;
050: static final int PARSE_DATE = 15;
051:
052: static final int GET_FULL_YEAR = 20;
053: static final int GET_UTC_FULL_YEAR = 21;
054: static final int GET_MONTH = 22;
055: static final int GET_UTC_MONTH = 23;
056: static final int GET_DATE = 24;
057: static final int GET_UTC_DATE = 25;
058: static final int GET_DAY = 26;
059: static final int GET_UTC_DAY = 27;
060: static final int GET_HOURS = 28;
061: static final int GET_UTC_HOURS = 29;
062: static final int GET_MINUTES = 30;
063: static final int GET_UTC_MINUTES = 31;
064: static final int GET_SECONDS = 32;
065: static final int GET_UTC_SECONDS = 33;
066: static final int GET_MILLISECONDS = 34;
067: static final int GET_UTC_MILLISECONDS = 35;
068:
069: static final int GET_TIMEZONE_OFFSET = 36;
070:
071: static final int SET_FULL_YEAR = 50;
072: static final int SET_UTC_FULL_YEAR = 51;
073: static final int SET_MONTH = 52;
074: static final int SET_UTC_MONTH = 53;
075: static final int SET_DATE = 54;
076: static final int SET_UTC_DATE = 55;
077: static final int SET_HOURS = 56;
078: static final int SET_UTC_HOURS = 57;
079: static final int SET_MINUTES = 58;
080: static final int SET_UTC_MINUTES = 59;
081: static final int SET_SECONDS = 60;
082: static final int SET_UTC_SECONDS = 61;
083: static final int SET_MILLISECONDS = 62;
084: static final int SET_UTC_MILLISECONDS = 63;
085:
086: static final int GET_MONTH_NAME = SET_UTC_MILLISECONDS + 1;
087: static final int GET_UTC_MONTH_NAME = GET_MONTH_NAME + 1;
088:
089: private static String[] monthNames = new String[] { "January",
090: "February", "March", "April", "May", "June", "July",
091: "August", "September", "October", "November", "December" };
092:
093: QDate localCal = QDate.createLocal();
094: QDate utcCal = new QDate();
095: QDate cal = localCal;
096:
097: /**
098: * Create a new object based on a prototype
099: */
100: private NativeDate(String name, int n, int len) {
101: super (name, len);
102:
103: this .n = n;
104: // this.cal = cal;
105: }
106:
107: /**
108: * Creates the native Object object
109: */
110: static ESObject create(Global resin) {
111: // QDate cal = QDate.createLocal();
112:
113: NativeDate nativeDate = new NativeDate("Date", NEW, 7);
114: ESObject dateProto = new ESDate(Long.MAX_VALUE, resin.objProto);
115: NativeWrapper date = new NativeWrapper(resin, nativeDate,
116: dateProto, ESThunk.DATE_THUNK);
117: nativeDate.newN = nativeDate.n;
118: resin.dateProto = dateProto;
119:
120: put(dateProto, "toString", TO_STRING, 0);
121: put(dateProto, "getTime", VALUE_OF, 0);
122: put(dateProto, "valueOf", VALUE_OF, 0);
123: put(dateProto, "toUTCString", TO_UTC_STRING, 0);
124: put(dateProto, "toGMTString", TO_UTC_STRING, 0);
125: put(dateProto, "toLocalISO8601", TO_ISO_STRING, 0);
126: put(dateProto, "toISO8601", TO_UTC_ISO_STRING, 0);
127: put(dateProto, "format", FORMAT, 0);
128: put(dateProto, "UTCFormat", UTC_FORMAT, 0);
129:
130: put(dateProto, "toLocaleString", TO_LOCALE_STRING, 0);
131: put(dateProto, "getUTCYear", GET_UTC_FULL_YEAR, 0);
132: put(dateProto, "getUTCFullYear", GET_UTC_FULL_YEAR, 0);
133: put(dateProto, "getUTCMonth", GET_UTC_MONTH, 0);
134: put(dateProto, "getUTCDate", GET_UTC_DATE, 0);
135: put(dateProto, "getUTCDay", GET_UTC_DAY, 0);
136: put(dateProto, "getUTCHours", GET_UTC_HOURS, 0);
137: put(dateProto, "getUTCMinutes", GET_UTC_MINUTES, 0);
138: put(dateProto, "getUTCSeconds", GET_UTC_SECONDS, 0);
139: put(dateProto, "getUTCMilliseconds", GET_UTC_MILLISECONDS, 0);
140:
141: put(dateProto, "setUTCYear", SET_UTC_FULL_YEAR, 1);
142: put(dateProto, "setUTCFullYear", SET_UTC_FULL_YEAR, 1);
143: put(dateProto, "setUTCMonth", SET_UTC_MONTH, 2);
144: put(dateProto, "setUTCDate", SET_UTC_DATE, 3);
145: put(dateProto, "setUTCHours", SET_UTC_HOURS, 4);
146: put(dateProto, "setUTCMinutes", SET_UTC_MINUTES, 3);
147: put(dateProto, "setUTCSeconds", SET_UTC_SECONDS, 2);
148: put(dateProto, "setUTCMilliseconds", SET_UTC_MILLISECONDS, 1);
149:
150: put(dateProto, "getYear", GET_FULL_YEAR, 0);
151: put(dateProto, "getFullYear", GET_FULL_YEAR, 0);
152: put(dateProto, "getMonth", GET_MONTH, 0);
153: put(dateProto, "getMonthName", GET_MONTH_NAME, 0);
154: put(dateProto, "getDate", GET_DATE, 0);
155: put(dateProto, "getDay", GET_DAY, 0);
156: put(dateProto, "getHours", GET_HOURS, 0);
157: put(dateProto, "getMinutes", GET_MINUTES, 0);
158: put(dateProto, "getSeconds", GET_SECONDS, 0);
159: put(dateProto, "getMilliseconds", GET_MILLISECONDS, 0);
160:
161: put(dateProto, "getTimezoneOffset", GET_TIMEZONE_OFFSET, 0);
162: put(dateProto, "setYear", SET_FULL_YEAR, 3);
163: put(dateProto, "setFullYear", SET_FULL_YEAR, 3);
164: put(dateProto, "setMonth", SET_MONTH, 2);
165: put(dateProto, "setDate", SET_DATE, 1);
166: put(dateProto, "setHours", SET_HOURS, 4);
167: put(dateProto, "setMinutes", SET_MINUTES, 3);
168: put(dateProto, "setSeconds", SET_SECONDS, 2);
169: put(dateProto, "setMilliseconds", SET_MILLISECONDS, 1);
170:
171: put(date, "UTC", UTC, 7);
172: put(date, "parse", PARSE_DATE, 1);
173:
174: dateProto.setClean();
175: date.setClean();
176:
177: return date;
178: }
179:
180: private static void put(ESObject obj, String name, int n, int len) {
181: obj.put(ESId.intern(name), new NativeDate(name, n, len),
182: DONT_ENUM);
183: }
184:
185: public ESBase call(Call eval, int length) throws Throwable {
186: long time = 0;
187: double value;
188: ESBase error;
189:
190: synchronized (cal) {
191:
192: int off = 0;
193: switch (n) {
194: case NEW:
195: return ESDate.create(create(eval, length, n));
196:
197: case UTC:
198: return ESNumber.create(create(eval, length, n));
199:
200: case TO_STRING:
201: case TO_UTC_STRING:
202: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
203: return error;
204:
205: return ESString.create(cal.printDate());
206:
207: case TO_ISO_STRING:
208: case TO_UTC_ISO_STRING:
209: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
210: return error;
211:
212: return ESString.create(cal.printISO8601());
213:
214: case TO_ISO_DATE:
215: case TO_UTC_ISO_DATE:
216: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
217: return error;
218:
219: return ESString.create(cal.printISO8601Date());
220:
221: case FORMAT:
222: case UTC_FORMAT:
223: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
224: return error;
225:
226: return ESString.create(cal.format(eval.getArgString(0,
227: length)));
228:
229: case TO_LOCALE_STRING:
230: if ((error = calculate(eval.getArg(-1), 1, TO_STRING)) != null)
231: return error;
232:
233: return ESString.create(cal.printLocaleDate());
234:
235: case VALUE_OF:
236: if (!(eval.getArg(-1) instanceof ESDate))
237: throw new ESException(
238: "valueOf must be bound to date");
239:
240: value = (double) ((ESDate) eval.getArg(-1)).time;
241: if (value > 8.64e15 || value < -8.64e15
242: || Double.isNaN(value))
243: value = 0.0 / 0.0;
244:
245: return ESNumber.create(value);
246:
247: case PARSE_DATE:
248: if (length < 0)
249: return ESNumber.NaN;
250:
251: try {
252: long lvalue = cal.parseDate(eval.getArg(0).toStr()
253: .toString());
254: return ESNumber.create(millisToDouble(lvalue));
255: } catch (Exception e) {
256: throw new ESException(e.toString());
257: }
258:
259: case GET_FULL_YEAR:
260: case GET_UTC_FULL_YEAR:
261: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
262: return error;
263:
264: return ESNumber.create((double) cal.get(cal.YEAR));
265:
266: case GET_MONTH:
267: case GET_UTC_MONTH:
268: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
269: return error;
270:
271: return ESNumber.create((double) cal.get(cal.MONTH));
272:
273: case GET_MONTH_NAME:
274: case GET_UTC_MONTH_NAME: {
275: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
276: return error;
277:
278: int month = (int) cal.get(cal.MONTH);
279:
280: return ESString.create(monthNames[month]);
281: }
282:
283: case GET_DATE:
284: case GET_UTC_DATE:
285: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
286: return error;
287:
288: return ESNumber.create((double) cal
289: .get(cal.DAY_OF_MONTH) + 1);
290:
291: case GET_DAY:
292: case GET_UTC_DAY:
293: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
294: return error;
295:
296: return ESNumber.create((double) cal.get(cal.DAY));
297:
298: case GET_HOURS:
299: case GET_UTC_HOURS:
300: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
301: return error;
302:
303: return ESNumber.create((double) (cal.get(cal.HOUR)));
304:
305: case GET_MINUTES:
306: case GET_UTC_MINUTES:
307: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
308: return error;
309:
310: return ESNumber.create((double) (cal.get(cal.MINUTE)));
311:
312: case GET_SECONDS:
313: case GET_UTC_SECONDS:
314: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
315: return error;
316:
317: return ESNumber.create((double) (cal.get(cal.SECOND)));
318:
319: case GET_MILLISECONDS:
320: case GET_UTC_MILLISECONDS:
321: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
322: return error;
323:
324: return ESNumber.create((double) cal
325: .get(cal.MILLISECOND));
326:
327: case GET_TIMEZONE_OFFSET:
328: if ((error = calculate(eval.getArg(-1), 1, n)) != null)
329: return error;
330:
331: return ESNumber
332: .create((double) (cal.getZoneOffset() / 60000));
333:
334: case SET_DATE:
335: case SET_UTC_DATE:
336: off--;
337:
338: case SET_MONTH:
339: case SET_UTC_MONTH:
340: off--;
341:
342: case SET_FULL_YEAR:
343: case SET_UTC_FULL_YEAR:
344: if ((error = calculate(eval.getArg(-1), length, n)) != null)
345: return error;
346:
347: if (0 <= off)
348: cal.set(cal.YEAR, (long) eval.getArg(off).toNum());
349:
350: if (0 <= off + 1 && off + 1 < length) {
351: value = eval.getArg(off + 1).toNum();
352: cal
353: .set(
354: cal.MONTH,
355: (long) (Double.isNaN(value) ? Long.MAX_VALUE
356: : value));
357: }
358: if (0 <= off + 2 && off + 2 < length) {
359: value = eval.getArg(off + 2).toNum();
360: cal
361: .set(cal.DAY_OF_MONTH, (long) (Double
362: .isNaN(value) ? Long.MAX_VALUE
363: : value - 1));
364: }
365:
366: return create(eval.getArg(-1), n);
367:
368: case SET_MILLISECONDS:
369: case SET_UTC_MILLISECONDS:
370: off--;
371:
372: case SET_SECONDS:
373: case SET_UTC_SECONDS:
374: off--;
375:
376: case SET_MINUTES:
377: case SET_UTC_MINUTES:
378: off--;
379:
380: case SET_HOURS:
381: case SET_UTC_HOURS:
382: if ((error = calculate(eval.getArg(-1), length, n)) != null)
383: return error;
384:
385: if (0 <= off) {
386: value = eval.getArg(off).toNum();
387: cal
388: .set(
389: cal.HOUR,
390: (long) (Double.isNaN(value) ? Long.MAX_VALUE
391: : value));
392: }
393:
394: if (0 <= off + 1 && off + 1 < length) {
395: value = eval.getArg(off + 1).toNum();
396: cal
397: .set(cal.MINUTE, (long) (Double
398: .isNaN(value) ? Long.MAX_VALUE
399: : value));
400: }
401: if (0 <= off + 2 && off + 2 < length) {
402: value = eval.getArg(off + 2).toNum();
403: cal
404: .set(cal.SECOND, (long) (Double
405: .isNaN(value) ? Long.MAX_VALUE
406: : value));
407: }
408: if (0 <= off + 3 && off + 3 < length) {
409: value = eval.getArg(off + 3).toNum();
410: cal.set(cal.MILLISECOND, (long) (Double
411: .isNaN(value) ? Long.MAX_VALUE : value));
412: }
413:
414: return create(eval.getArg(-1), n);
415:
416: default:
417: throw new ESException("Unknown object function");
418: }
419: }
420: }
421:
422: public ESBase construct(Call eval, int length) throws Throwable {
423: if (cal == null)
424: cal = new QDate();
425:
426: if (n != NEW)
427: return super .construct(eval, length);
428:
429: synchronized (cal) {
430: return ESDate.create(create(eval, length, NEW));
431: }
432: }
433:
434: private long create(Call eval, int length, int code)
435: throws Throwable {
436: boolean isLocal = (code & 1) == 0;
437: long value = 0;
438:
439: if (length == 0) {
440: return Alarm.getCurrentTime();
441: } else if (length == 1)
442: value = (long) (eval.getArg(0).toNum());
443: else if (length >= 3) {
444: long year = (long) eval.getArg(0).toNum();
445: long month = (long) eval.getArg(1).toNum();
446: long day = (long) eval.getArg(2).toNum() - 1;
447:
448: long hour = 0;
449: if (length >= 4)
450: hour = (long) eval.getArg(3).toNum();
451:
452: long minute = 0;
453: if (length >= 5)
454: minute = (long) eval.getArg(4).toNum();
455:
456: long second = 0;
457: if (length >= 6)
458: second = (long) eval.getArg(5).toNum();
459:
460: long ms = 0;
461: if (length >= 7)
462: ms = (long) eval.getArg(6).toNum();
463:
464: cal.setDate(year, month, day);
465: cal.setTime(hour, minute, second, ms);
466:
467: value = cal.get(cal.TIME);
468:
469: if (isLocal)
470: value -= cal.getZoneOffset();
471: } else
472: value = Long.MIN_VALUE;
473:
474: return value;
475: }
476:
477: private double millisToDouble(long millis) {
478: double dvalue = millis;
479: if (dvalue > 8.64e15 || dvalue < -8.64e15
480: || Double.isNaN(dvalue))
481: dvalue = 0.0 / 0.0;
482:
483: return dvalue;
484: }
485:
486: private ESBase create(ESBase obj, int code) throws ESException {
487: boolean isLocal = (code & 1) == 0;
488:
489: long value = cal.get(cal.TIME);
490:
491: if (isLocal)
492: value -= cal.getZoneOffset();
493:
494: double dvalue = value;
495: if (dvalue > 8.64e15 || dvalue < -8.64e15
496: || Double.isNaN(dvalue))
497: dvalue = 0.0 / 0.0;
498:
499: if (!(obj instanceof ESDate))
500: return ESNumber.create(dvalue);
501:
502: ESNumber newValue = ESNumber.create(dvalue);
503: ((ESDate) obj).time = (long) dvalue;
504:
505: return newValue;
506: }
507:
508: private ESBase calculate(ESBase arg, int length, int code)
509: throws Throwable {
510: boolean isLocal = (code & 1) == 0;
511:
512: double value = arg.toNum();
513: if (Double.isNaN(value) || value > 8.64e15 || value < -8.64e15
514: || length < 1)
515: return ESNumber.NaN;
516:
517: long time = (long) value;
518:
519: if (isLocal)
520: cal = localCal;
521: else
522: cal = utcCal;
523:
524: cal.setGMTTime(time);
525:
526: return null;
527: }
528: }
|