2002/03/21 01:19:33
[org.ibex.core.git] / src / org / mozilla / javascript / NativeDate.java
1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
2  *\r
3  * The contents of this file are subject to the Netscape Public\r
4  * License Version 1.1 (the "License"); you may not use this file\r
5  * except in compliance with the License. You may obtain a copy of\r
6  * the License at http://www.mozilla.org/NPL/\r
7  *\r
8  * Software distributed under the License is distributed on an "AS\r
9  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
10  * implied. See the License for the specific language governing\r
11  * rights and limitations under the License.\r
12  *\r
13  * The Original Code is Rhino code, released\r
14  * May 6, 1999.\r
15  *\r
16  * The Initial Developer of the Original Code is Netscape\r
17  * Communications Corporation.  Portions created by Netscape are\r
18  * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
19  * Rights Reserved.\r
20  *\r
21  * Contributor(s):\r
22  * Norris Boyd\r
23  * Mike McCabe\r
24  *\r
25  * Alternatively, the contents of this file may be used under the\r
26  * terms of the GNU Public License (the "GPL"), in which case the\r
27  * provisions of the GPL are applicable instead of those above.\r
28  * If you wish to allow use of your version of this file only\r
29  * under the terms of the GPL and not to allow others to use your\r
30  * version of this file under the NPL, indicate your decision by\r
31  * deleting the provisions above and replace them with the notice\r
32  * and other provisions required by the GPL.  If you do not delete\r
33  * the provisions above, a recipient may use your version of this\r
34  * file under either the NPL or the GPL.\r
35  */\r
36 \r
37 package org.mozilla.javascript;\r
38 \r
39 import java.util.Date;\r
40 import java.util.TimeZone;\r
41 import java.util.Locale;\r
42 import java.text.NumberFormat;\r
43 import java.text.DateFormat;\r
44 import java.text.SimpleDateFormat;\r
45 \r
46 /**\r
47  * This class implements the Date native object.\r
48  * See ECMA 15.9.\r
49  * @author Mike McCabe\r
50  */\r
51 public class NativeDate extends IdScriptable {\r
52 \r
53     public static void init(Context cx, Scriptable scope, boolean sealed) {\r
54         NativeDate obj = new NativeDate();\r
55         obj.prototypeFlag = true;\r
56         \r
57         // Set the value of the prototype Date to NaN ('invalid date');\r
58         obj.date = ScriptRuntime.NaN;\r
59 \r
60         obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
61     }\r
62     \r
63     public NativeDate() {\r
64         if (thisTimeZone == null) {\r
65             // j.u.TimeZone is synchronized, so setting class statics from it\r
66             // should be OK.\r
67             thisTimeZone = java.util.TimeZone.getDefault();\r
68             LocalTZA = thisTimeZone.getRawOffset();\r
69         }\r
70     }\r
71 \r
72     public String getClassName() {\r
73         return "Date";\r
74     }\r
75 \r
76     public Object getDefaultValue(Class typeHint) {\r
77         if (typeHint == null)\r
78             typeHint = ScriptRuntime.StringClass;\r
79         return super.getDefaultValue(typeHint);\r
80     }\r
81 \r
82     protected void fillConstructorProperties\r
83         (Context cx, IdFunction ctor, boolean sealed)\r
84     {\r
85         addIdFunctionProperty(ctor, ConstructorId_UTC, sealed);\r
86         addIdFunctionProperty(ctor, ConstructorId_parse, sealed);\r
87         super.fillConstructorProperties(cx, ctor, sealed);\r
88     }\r
89 \r
90     public int methodArity(int methodId) {\r
91         if (prototypeFlag) {\r
92             switch (methodId) {\r
93                 case ConstructorId_UTC:     return 1;\r
94                 case ConstructorId_parse:   return 1;\r
95                 case Id_constructor:        return 1; \r
96                 case Id_toString:           return 0;\r
97                 case Id_toTimeString:       return 0;\r
98                 case Id_toDateString:       return 0;\r
99                 case Id_toLocaleString:     return 0;\r
100                 case Id_toLocaleTimeString: return 0;\r
101                 case Id_toLocaleDateString: return 0;\r
102                 case Id_toUTCString:        return 0;\r
103                 case Id_valueOf:            return 0;\r
104                 case Id_getTime:            return 0;\r
105                 case Id_getYear:            return 0;\r
106                 case Id_getFullYear:        return 0;\r
107                 case Id_getUTCFullYear:     return 0;\r
108                 case Id_getMonth:           return 0;\r
109                 case Id_getUTCMonth:        return 0;\r
110                 case Id_getDate:            return 0;\r
111                 case Id_getUTCDate:         return 0;\r
112                 case Id_getDay:             return 0;\r
113                 case Id_getUTCDay:          return 0;\r
114                 case Id_getHours:           return 0;\r
115                 case Id_getUTCHours:        return 0;\r
116                 case Id_getMinutes:         return 0;\r
117                 case Id_getUTCMinutes:      return 0;\r
118                 case Id_getSeconds:         return 0;\r
119                 case Id_getUTCSeconds:      return 0;\r
120                 case Id_getMilliseconds:    return 0;\r
121                 case Id_getUTCMilliseconds: return 0;\r
122                 case Id_getTimezoneOffset:  return 0;\r
123                 case Id_setTime:            return 1;\r
124                 case Id_setMilliseconds:    return 1;\r
125                 case Id_setUTCMilliseconds: return 1;\r
126                 case Id_setSeconds:         return 2;\r
127                 case Id_setUTCSeconds:      return 2;\r
128                 case Id_setMinutes:         return 3;\r
129                 case Id_setUTCMinutes:      return 3;\r
130                 case Id_setHours:           return 4;\r
131                 case Id_setUTCHours:        return 4;\r
132                 case Id_setDate:            return 1;\r
133                 case Id_setUTCDate:         return 1;\r
134                 case Id_setMonth:           return 2;\r
135                 case Id_setUTCMonth:        return 2;\r
136                 case Id_setFullYear:        return 3;\r
137                 case Id_setUTCFullYear:     return 3;\r
138                 case Id_setYear:            return 1;\r
139             }\r
140         }\r
141         return super.methodArity(methodId);\r
142     }\r
143 \r
144     public Object execMethod\r
145         (int methodId, IdFunction f,\r
146          Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
147         throws JavaScriptException\r
148     {\r
149         if (prototypeFlag) {\r
150             switch (methodId) {\r
151                 case ConstructorId_UTC: \r
152                     return wrap_double(jsStaticFunction_UTC(args));\r
153 \r
154                 case ConstructorId_parse: \r
155                     return wrap_double(jsStaticFunction_parse\r
156                         (ScriptRuntime.toString(args, 0)));\r
157 \r
158                 case Id_constructor:\r
159                     return jsConstructor(args, thisObj == null);\r
160 \r
161                 case Id_toString: {\r
162                     double t = realThis(thisObj, f, true).date;\r
163                     return date_format(t, FORMATSPEC_FULL);\r
164                 }\r
165 \r
166                 case Id_toTimeString: {\r
167                     double t = realThis(thisObj, f, true).date;\r
168                     return date_format(t, FORMATSPEC_TIME);\r
169                 }\r
170 \r
171                 case Id_toDateString: {\r
172                     double t = realThis(thisObj, f, true).date;\r
173                     return date_format(t, FORMATSPEC_DATE);\r
174                 }\r
175 \r
176                 case Id_toLocaleString: {\r
177                     double t = realThis(thisObj, f, true).date;\r
178                     return jsFunction_toLocaleString(t);\r
179                 }\r
180 \r
181                 case Id_toLocaleTimeString: {\r
182                     double t = realThis(thisObj, f, true).date;\r
183                     return jsFunction_toLocaleTimeString(t);\r
184                 }\r
185 \r
186                 case Id_toLocaleDateString: {\r
187                     double t = realThis(thisObj, f, true).date;\r
188                     return jsFunction_toLocaleDateString(t);\r
189                 }\r
190 \r
191                 case Id_toUTCString: {\r
192                     double t = realThis(thisObj, f, true).date;\r
193                     if (t == t) { return jsFunction_toUTCString(t); }\r
194                     return jsFunction_NaN_date_str;\r
195                 }\r
196 \r
197                 case Id_valueOf: \r
198                     return wrap_double(realThis(thisObj, f, true).date);\r
199 \r
200                 case Id_getTime: \r
201                     return wrap_double(realThis(thisObj, f, true).date);\r
202 \r
203                 case Id_getYear: {\r
204                     double t = realThis(thisObj, f, true).date;\r
205                     if (t == t) { t = jsFunction_getYear(cx, t); }\r
206                     return wrap_double(t);\r
207                 }\r
208 \r
209                 case Id_getFullYear: {\r
210                     double t = realThis(thisObj, f, true).date;\r
211                     if (t == t) { t = YearFromTime(LocalTime(t)); }\r
212                     return wrap_double(t);\r
213                 }\r
214 \r
215                 case Id_getUTCFullYear: {\r
216                     double t = realThis(thisObj, f, true).date;\r
217                     if (t == t) { t = YearFromTime(t); }\r
218                     return wrap_double(t);\r
219                 }\r
220 \r
221                 case Id_getMonth: {\r
222                     double t = realThis(thisObj, f, true).date;\r
223                     if (t == t) { t = MonthFromTime(LocalTime(t)); }\r
224                     return wrap_double(t);\r
225                 }\r
226                 \r
227                 case Id_getUTCMonth: {\r
228                     double t = realThis(thisObj, f, true).date;\r
229                     if (t == t) { t = MonthFromTime(t); }\r
230                     return wrap_double(t);\r
231                 }\r
232 \r
233                 case Id_getDate: {\r
234                     double t = realThis(thisObj, f, true).date;\r
235                     if (t == t) { t = DateFromTime(LocalTime(t)); }\r
236                     return wrap_double(t);\r
237                 }\r
238 \r
239                 case Id_getUTCDate: {\r
240                     double t = realThis(thisObj, f, true).date;\r
241                     if (t == t) { t = DateFromTime(t); }\r
242                     return wrap_double(t);\r
243                 } \r
244 \r
245                 case Id_getDay: {\r
246                     double t = realThis(thisObj, f, true).date;\r
247                     if (t == t) { t = WeekDay(LocalTime(t)); }\r
248                     return wrap_double(t);\r
249                 }\r
250 \r
251                 case Id_getUTCDay: {\r
252                     double t = realThis(thisObj, f, true).date;\r
253                     if (t == t) { t = WeekDay(t); }\r
254                     return wrap_double(t);\r
255                 } \r
256 \r
257                 case Id_getHours: {\r
258                     double t = realThis(thisObj, f, true).date;\r
259                     if (t == t) { t = HourFromTime(LocalTime(t)); }\r
260                     return wrap_double(t);\r
261                 } \r
262 \r
263                 case Id_getUTCHours: {\r
264                     double t = realThis(thisObj, f, true).date;\r
265                     if (t == t) { t = HourFromTime(t); }\r
266                     return wrap_double(t);\r
267                 }\r
268 \r
269                 case Id_getMinutes: {\r
270                     double t = realThis(thisObj, f, true).date;\r
271                     if (t == t) { t = MinFromTime(LocalTime(t)); }\r
272                     return wrap_double(t);\r
273                 } \r
274 \r
275                 case Id_getUTCMinutes: {\r
276                     double t = realThis(thisObj, f, true).date;\r
277                     if (t == t) { t = MinFromTime(t); }\r
278                     return wrap_double(t);\r
279                 } \r
280 \r
281                 case Id_getSeconds: {\r
282                     double t = realThis(thisObj, f, true).date;\r
283                     if (t == t) { t = SecFromTime(LocalTime(t)); }\r
284                     return wrap_double(t);\r
285                 }\r
286 \r
287                 case Id_getUTCSeconds: {\r
288                     double t = realThis(thisObj, f, true).date;\r
289                     if (t == t) { t = SecFromTime(t); }\r
290                     return wrap_double(t);\r
291                 }\r
292                 \r
293                 case Id_getMilliseconds: {\r
294                     double t = realThis(thisObj, f, true).date;\r
295                     if (t == t) { t = msFromTime(LocalTime(t)); }\r
296                     return wrap_double(t);\r
297                 }\r
298 \r
299                 case Id_getUTCMilliseconds: {\r
300                     double t = realThis(thisObj, f, true).date;\r
301                     if (t == t) { t = msFromTime(t); }\r
302                     return wrap_double(t);\r
303                 }\r
304                 \r
305                 case Id_getTimezoneOffset: {\r
306                     double t = realThis(thisObj, f, true).date;\r
307                     if (t == t) { t = jsFunction_getTimezoneOffset(t); }\r
308                     return wrap_double(t);\r
309                 }\r
310 \r
311                 case Id_setTime: \r
312                     return wrap_double(realThis(thisObj, f, true).\r
313                         jsFunction_setTime(ScriptRuntime.toNumber(args, 0)));\r
314 \r
315                 case Id_setMilliseconds: \r
316                     return wrap_double(realThis(thisObj, f, false).\r
317                         makeTime(args, 1, true));\r
318 \r
319                 case Id_setUTCMilliseconds: \r
320                     return wrap_double(realThis(thisObj, f, false).\r
321                         makeTime(args, 1, false));\r
322 \r
323                 case Id_setSeconds: \r
324                     return wrap_double(realThis(thisObj, f, false).\r
325                         makeTime(args, 2, true));\r
326 \r
327                 case Id_setUTCSeconds: \r
328                     return wrap_double(realThis(thisObj, f, false).\r
329                         makeTime(args, 2, false));\r
330 \r
331                 case Id_setMinutes: \r
332                     return wrap_double(realThis(thisObj, f, false).\r
333                         makeTime(args, 3, true));\r
334 \r
335                 case Id_setUTCMinutes: \r
336                     return wrap_double(realThis(thisObj, f, false).\r
337                         makeTime(args, 3, false));\r
338 \r
339                 case Id_setHours: \r
340                     return wrap_double(realThis(thisObj, f, false).\r
341                         makeTime(args, 4, true));\r
342 \r
343                 case Id_setUTCHours: \r
344                     return wrap_double(realThis(thisObj, f, false).\r
345                         makeTime(args, 4, false));\r
346 \r
347                 case Id_setDate: \r
348                     return wrap_double(realThis(thisObj, f, false).\r
349                         makeDate(args, 1, true));\r
350 \r
351                 case Id_setUTCDate: \r
352                     return wrap_double(realThis(thisObj, f, false).\r
353                         makeDate(args, 1, false));\r
354 \r
355                 case Id_setMonth: \r
356                     return wrap_double(realThis(thisObj, f, false).\r
357                         makeDate(args, 2, true));\r
358 \r
359                 case Id_setUTCMonth: \r
360                     return wrap_double(realThis(thisObj, f, false).\r
361                         makeDate(args, 2, false));\r
362 \r
363                 case Id_setFullYear: \r
364                     return wrap_double(realThis(thisObj, f, false).\r
365                         makeDate(args, 3, true));\r
366 \r
367                 case Id_setUTCFullYear: \r
368                     return wrap_double(realThis(thisObj, f, false).\r
369                         makeDate(args, 3, false));\r
370 \r
371                 case Id_setYear: \r
372                     return wrap_double(realThis(thisObj, f, false).\r
373                         jsFunction_setYear(ScriptRuntime.toNumber(args, 0)));\r
374             }\r
375         }\r
376 \r
377         return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
378     }\r
379 \r
380     private NativeDate realThis(Scriptable thisObj, IdFunction f, \r
381                                 boolean readOnly)\r
382     {\r
383         while (!(thisObj instanceof NativeDate)) {\r
384             thisObj = nextInstanceCheck(thisObj, f, readOnly);\r
385         }\r
386         return (NativeDate)thisObj;\r
387     }\r
388 \r
389     /* ECMA helper functions */\r
390 \r
391     private static final double HalfTimeDomain = 8.64e15;\r
392     private static final double HoursPerDay    = 24.0;\r
393     private static final double MinutesPerHour = 60.0;\r
394     private static final double SecondsPerMinute = 60.0;\r
395     private static final double msPerSecond    = 1000.0;\r
396     private static final double MinutesPerDay  = (HoursPerDay * MinutesPerHour);\r
397     private static final double SecondsPerDay  = (MinutesPerDay * SecondsPerMinute);\r
398     private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute);\r
399     private static final double msPerDay       = (SecondsPerDay * msPerSecond);\r
400     private static final double msPerHour      = (SecondsPerHour * msPerSecond);\r
401     private static final double msPerMinute    = (SecondsPerMinute * msPerSecond);\r
402 \r
403     private static double Day(double t) {\r
404         return Math.floor(t / msPerDay);\r
405     }\r
406 \r
407     private static double TimeWithinDay(double t) {\r
408         double result;\r
409         result = t % msPerDay;\r
410         if (result < 0)\r
411             result += msPerDay;\r
412         return result;\r
413     }\r
414 \r
415     private static int DaysInYear(int y) {\r
416         if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))\r
417             return 366;\r
418         else\r
419             return 365;\r
420     }\r
421 \r
422 \r
423     /* math here has to be f.p, because we need\r
424      *  floor((1968 - 1969) / 4) == -1\r
425      */\r
426     private static double DayFromYear(double y) {\r
427         return ((365 * ((y)-1970) + Math.floor(((y)-1969)/4.0)\r
428                  - Math.floor(((y)-1901)/100.0) + Math.floor(((y)-1601)/400.0)));\r
429     }\r
430 \r
431     private static double TimeFromYear(double y) {\r
432         return DayFromYear(y) * msPerDay;\r
433     }\r
434 \r
435     private static int YearFromTime(double t) {\r
436         int lo = (int) Math.floor((t / msPerDay) / 366) + 1970;\r
437         int hi = (int) Math.floor((t / msPerDay) / 365) + 1970;\r
438         int mid;\r
439 \r
440         /* above doesn't work for negative dates... */\r
441         if (hi < lo) {\r
442             int temp = lo;\r
443             lo = hi;\r
444             hi = temp;\r
445         }\r
446 \r
447         /* Use a simple binary search algorithm to find the right\r
448            year.  This seems like brute force... but the computation\r
449            of hi and lo years above lands within one year of the\r
450            correct answer for years within a thousand years of\r
451            1970; the loop below only requires six iterations\r
452            for year 270000. */\r
453         while (hi > lo) {\r
454             mid = (hi + lo) / 2;\r
455             if (TimeFromYear(mid) > t) {\r
456                 hi = mid - 1;\r
457             } else {\r
458                 if (TimeFromYear(mid) <= t) {\r
459                     int temp = mid + 1;\r
460                     if (TimeFromYear(temp) > t) {\r
461                         return mid;\r
462                     }\r
463                     lo = mid + 1;\r
464                 }\r
465             }\r
466         }\r
467         return lo;\r
468     }\r
469 \r
470     private static boolean InLeapYear(double t) {\r
471         return DaysInYear(YearFromTime(t)) == 366;\r
472     }\r
473 \r
474     private static int DayWithinYear(double t) {\r
475         int year = YearFromTime(t);\r
476         return (int) (Day(t) - DayFromYear(year));\r
477     }\r
478     /*\r
479      * The following array contains the day of year for the first day of\r
480      * each month, where index 0 is January, and day 0 is January 1.\r
481      */\r
482 \r
483     private static double DayFromMonth(int m, boolean leap) {\r
484         int day = m * 30;\r
485 \r
486         if (m >= 7) { day += m / 2 - 1; }\r
487         else if (m >= 2) { day += (m - 1) / 2 - 1; }\r
488         else { day += m; }\r
489 \r
490         if (leap && m >= 2) { ++day; }\r
491 \r
492         return day;\r
493     }\r
494 \r
495     private static int MonthFromTime(double t) {\r
496         int d, step;\r
497 \r
498         d = DayWithinYear(t);\r
499 \r
500         if (d < (step = 31))\r
501             return 0;\r
502 \r
503         // Originally coded as step += (InLeapYear(t) ? 29 : 28);\r
504         // but some jits always returned 28!\r
505         if (InLeapYear(t))\r
506             step += 29;\r
507         else\r
508             step += 28;\r
509 \r
510         if (d < step)\r
511             return 1;\r
512         if (d < (step += 31))\r
513             return 2;\r
514         if (d < (step += 30))\r
515             return 3;\r
516         if (d < (step += 31))\r
517             return 4;\r
518         if (d < (step += 30))\r
519             return 5;\r
520         if (d < (step += 31))\r
521             return 6;\r
522         if (d < (step += 31))\r
523             return 7;\r
524         if (d < (step += 30))\r
525             return 8;\r
526         if (d < (step += 31))\r
527             return 9;\r
528         if (d < (step += 30))\r
529             return 10;\r
530         return 11;\r
531     }\r
532 \r
533     private static int DateFromTime(double t) {\r
534         int d, step, next;\r
535 \r
536         d = DayWithinYear(t);\r
537         if (d <= (next = 30))\r
538             return d + 1;\r
539         step = next;\r
540 \r
541         // Originally coded as next += (InLeapYear(t) ? 29 : 28);\r
542         // but some jits always returned 28!\r
543         if (InLeapYear(t))\r
544             next += 29;\r
545         else\r
546             next += 28;\r
547 \r
548         if (d <= next)\r
549             return d - step;\r
550         step = next;\r
551         if (d <= (next += 31))\r
552             return d - step;\r
553         step = next;\r
554         if (d <= (next += 30))\r
555             return d - step;\r
556         step = next;\r
557         if (d <= (next += 31))\r
558             return d - step;\r
559         step = next;\r
560         if (d <= (next += 30))\r
561             return d - step;\r
562         step = next;\r
563         if (d <= (next += 31))\r
564             return d - step;\r
565         step = next;\r
566         if (d <= (next += 31))\r
567             return d - step;\r
568         step = next;\r
569         if (d <= (next += 30))\r
570             return d - step;\r
571         step = next;\r
572         if (d <= (next += 31))\r
573             return d - step;\r
574         step = next;\r
575         if (d <= (next += 30))\r
576             return d - step;\r
577         step = next;\r
578 \r
579         return d - step;\r
580     }\r
581 \r
582     private static int WeekDay(double t) {\r
583         double result;\r
584         result = Day(t) + 4;\r
585         result = result % 7;\r
586         if (result < 0)\r
587             result += 7;\r
588         return (int) result;\r
589     }\r
590 \r
591     private static double Now() {\r
592         return (double) System.currentTimeMillis();\r
593     }\r
594 \r
595     /* Should be possible to determine the need for this dynamically\r
596      * if we go with the workaround... I'm not using it now, because I\r
597      * can't think of any clean way to make toLocaleString() and the\r
598      * time zone (comment) in toString match the generated string\r
599      * values.  Currently it's wrong-but-consistent in all but the\r
600      * most recent betas of the JRE - seems to work in 1.1.7.\r
601      */\r
602     private final static boolean TZO_WORKAROUND = false;\r
603     private static double DaylightSavingTA(double t) {\r
604         if (!TZO_WORKAROUND) {\r
605             Date date = new Date((long) t);\r
606             if (thisTimeZone.inDaylightTime(date))\r
607                 return msPerHour;\r
608             else\r
609                 return 0;\r
610         } else {\r
611             /* Use getOffset if inDaylightTime() is broken, because it\r
612              * seems to work acceptably.  We don't switch over to it\r
613              * entirely, because it requires (expensive) exploded date arguments,\r
614              * and the api makes it impossible to handle dst\r
615              * changeovers cleanly.\r
616              */\r
617 \r
618             // Hardcode the assumption that the changeover always\r
619             // happens at 2:00 AM:\r
620             t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);\r
621 \r
622             int year = YearFromTime(t);\r
623             double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,\r
624                                                    year,\r
625                                                    MonthFromTime(t),\r
626                                                    DateFromTime(t),\r
627                                                    WeekDay(t),\r
628                                                    (int)TimeWithinDay(t));\r
629 \r
630             if ((offset - LocalTZA) != 0)\r
631                 return msPerHour;\r
632             else\r
633                 return 0;\r
634             //         return offset - LocalTZA;\r
635         }\r
636     }\r
637 \r
638     private static double LocalTime(double t) {\r
639         return t + LocalTZA + DaylightSavingTA(t);\r
640     }\r
641 \r
642     public static double internalUTC(double t) {\r
643         return t - LocalTZA - DaylightSavingTA(t - LocalTZA);\r
644     }\r
645 \r
646     private static int HourFromTime(double t) {\r
647         double result;\r
648         result = Math.floor(t / msPerHour) % HoursPerDay;\r
649         if (result < 0)\r
650             result += HoursPerDay;\r
651         return (int) result;\r
652     }\r
653 \r
654     private static int MinFromTime(double t) {\r
655         double result;\r
656         result = Math.floor(t / msPerMinute) % MinutesPerHour;\r
657         if (result < 0)\r
658             result += MinutesPerHour;\r
659         return (int) result;\r
660     }\r
661 \r
662     private static int SecFromTime(double t) {\r
663         double result;\r
664         result = Math.floor(t / msPerSecond) % SecondsPerMinute;\r
665         if (result < 0)\r
666             result += SecondsPerMinute;\r
667         return (int) result;\r
668     }\r
669 \r
670     private static int msFromTime(double t) {\r
671         double result;\r
672         result =  t % msPerSecond;\r
673         if (result < 0)\r
674             result += msPerSecond;\r
675         return (int) result;\r
676     }\r
677 \r
678     private static double MakeTime(double hour, double min,\r
679                                    double sec, double ms)\r
680     {\r
681         return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)\r
682             * msPerSecond + ms;\r
683     }\r
684 \r
685     private static double MakeDay(double year, double month, double date) {\r
686         double result;\r
687         boolean leap;\r
688         double yearday;\r
689         double monthday;\r
690 \r
691         year += Math.floor(month / 12);\r
692 \r
693         month = month % 12;\r
694         if (month < 0)\r
695             month += 12;\r
696 \r
697         leap = (DaysInYear((int) year) == 366);\r
698 \r
699         yearday = Math.floor(TimeFromYear(year) / msPerDay);\r
700         monthday = DayFromMonth((int) month, leap);\r
701 \r
702         result = yearday\r
703             + monthday\r
704             + date - 1;\r
705         return result;\r
706     }\r
707 \r
708     private static double MakeDate(double day, double time) {\r
709         return day * msPerDay + time;\r
710     }\r
711 \r
712     private static double TimeClip(double d) {\r
713         if (d != d ||\r
714             d == Double.POSITIVE_INFINITY ||\r
715             d == Double.NEGATIVE_INFINITY ||\r
716             Math.abs(d) > HalfTimeDomain)\r
717         {\r
718             return ScriptRuntime.NaN;\r
719         }\r
720         if (d > 0.0)\r
721             return Math.floor(d + 0.);\r
722         else\r
723             return Math.ceil(d + 0.);\r
724     }\r
725 \r
726     /* end of ECMA helper functions */\r
727 \r
728     /* find UTC time from given date... no 1900 correction! */\r
729     public static double date_msecFromDate(double year, double mon,\r
730                                             double mday, double hour,\r
731                                             double min, double sec,\r
732                                             double msec)\r
733     {\r
734         double day;\r
735         double time;\r
736         double result;\r
737 \r
738         day = MakeDay(year, mon, mday);\r
739         time = MakeTime(hour, min, sec, msec);\r
740         result = MakeDate(day, time);\r
741         return result;\r
742     }\r
743 \r
744 \r
745     private static final int MAXARGS = 7;\r
746     private static double jsStaticFunction_UTC(Object[] args) {\r
747         double array[] = new double[MAXARGS];\r
748         int loop;\r
749         double d;\r
750 \r
751         for (loop = 0; loop < MAXARGS; loop++) {\r
752             if (loop < args.length) {\r
753                 d = ScriptRuntime.toNumber(args[loop]);\r
754                 if (d != d || Double.isInfinite(d)) {\r
755                     return ScriptRuntime.NaN;\r
756                 }\r
757                 array[loop] = ScriptRuntime.toInteger(args[loop]);\r
758             } else {\r
759                 array[loop] = 0;\r
760             }\r
761         }\r
762 \r
763         /* adjust 2-digit years into the 20th century */\r
764         if (array[0] >= 0 && array[0] <= 99)\r
765             array[0] += 1900;\r
766 \r
767             /* if we got a 0 for 'date' (which is out of range)\r
768              * pretend it's a 1.  (So Date.UTC(1972, 5) works) */\r
769         if (array[2] < 1)\r
770             array[2] = 1;\r
771 \r
772         d = date_msecFromDate(array[0], array[1], array[2],\r
773                               array[3], array[4], array[5], array[6]);\r
774         d = TimeClip(d);\r
775         return d;\r
776         //        return new Double(d);\r
777     }\r
778 \r
779     /*\r
780      * Use ported code from jsdate.c rather than the locale-specific\r
781      * date-parsing code from Java, to keep js and rhino consistent.\r
782      * Is this the right strategy?\r
783      */\r
784 \r
785     /* for use by date_parse */\r
786 \r
787     /* replace this with byte arrays?  Cheaper? */\r
788     private static String wtb[] = {\r
789         "am", "pm",\r
790         "monday", "tuesday", "wednesday", "thursday", "friday",\r
791         "saturday", "sunday",\r
792         "january", "february", "march", "april", "may", "june",\r
793         "july", "august", "september", "october", "november", "december",\r
794         "gmt", "ut", "utc", "est", "edt", "cst", "cdt",\r
795         "mst", "mdt", "pst", "pdt"\r
796         /* time zone table needs to be expanded */\r
797     };\r
798 \r
799     private static int ttb[] = {\r
800         -1, -2, 0, 0, 0, 0, 0, 0, 0,     /* AM/PM */\r
801         2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,\r
802         10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */\r
803         10000 + 5 * 60, 10000 + 4 * 60,  /* EDT */\r
804         10000 + 6 * 60, 10000 + 5 * 60,\r
805         10000 + 7 * 60, 10000 + 6 * 60,\r
806         10000 + 8 * 60, 10000 + 7 * 60\r
807     };\r
808 \r
809     /* helper for date_parse */\r
810     private static boolean date_regionMatches(String s1, int s1off,\r
811                                               String s2, int s2off,\r
812                                               int count)\r
813     {\r
814         boolean result = false;\r
815         /* return true if matches, otherwise, false */\r
816         int s1len = s1.length();\r
817         int s2len = s2.length();\r
818 \r
819         while (count > 0 && s1off < s1len && s2off < s2len) {\r
820             if (Character.toLowerCase(s1.charAt(s1off)) !=\r
821                 Character.toLowerCase(s2.charAt(s2off)))\r
822                 break;\r
823             s1off++;\r
824             s2off++;\r
825             count--;\r
826         }\r
827 \r
828         if (count == 0) {\r
829             result = true;\r
830         }\r
831         return result;\r
832     }\r
833 \r
834     private static double date_parseString(String s) {\r
835         double msec;\r
836 \r
837         int year = -1;\r
838         int mon = -1;\r
839         int mday = -1;\r
840         int hour = -1;\r
841         int min = -1;\r
842         int sec = -1;\r
843         char c = 0;\r
844         char si = 0;\r
845         int i = 0;\r
846         int n = -1;\r
847         double tzoffset = -1;\r
848         char prevc = 0;\r
849         int limit = 0;\r
850         boolean seenplusminus = false;\r
851 \r
852         if (s == null)  // ??? Will s be null?\r
853             return ScriptRuntime.NaN;\r
854         limit = s.length();\r
855         while (i < limit) {\r
856             c = s.charAt(i);\r
857             i++;\r
858             if (c <= ' ' || c == ',' || c == '-') {\r
859                 if (i < limit) {\r
860                     si = s.charAt(i);\r
861                     if (c == '-' && '0' <= si && si <= '9') {\r
862                         prevc = c;\r
863                     }\r
864                 }\r
865                 continue;\r
866             }\r
867             if (c == '(') { /* comments) */\r
868                 int depth = 1;\r
869                 while (i < limit) {\r
870                     c = s.charAt(i);\r
871                     i++;\r
872                     if (c == '(')\r
873                         depth++;\r
874                     else if (c == ')')\r
875                         if (--depth <= 0)\r
876                             break;\r
877                 }\r
878                 continue;\r
879             }\r
880             if ('0' <= c && c <= '9') {\r
881                 n = c - '0';\r
882                 while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {\r
883                     n = n * 10 + c - '0';\r
884                     i++;\r
885                 }\r
886 \r
887                 /* allow TZA before the year, so\r
888                  * 'Wed Nov 05 21:49:11 GMT-0800 1997'\r
889                  * works */\r
890 \r
891                 /* uses of seenplusminus allow : in TZA, so Java\r
892                  * no-timezone style of GMT+4:30 works\r
893                  */\r
894                 if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {\r
895                     /* make ':' case below change tzoffset */\r
896                     seenplusminus = true;\r
897 \r
898                     /* offset */\r
899                     if (n < 24)\r
900                         n = n * 60; /* EG. "GMT-3" */\r
901                     else\r
902                         n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */\r
903                     if (prevc == '+')       /* plus means east of GMT */\r
904                         n = -n;\r
905                     if (tzoffset != 0 && tzoffset != -1)\r
906                         return ScriptRuntime.NaN;\r
907                     tzoffset = n;\r
908                 } else if (n >= 70  ||\r
909                            (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {\r
910                     if (year >= 0)\r
911                         return ScriptRuntime.NaN;\r
912                     else if (c <= ' ' || c == ',' || c == '/' || i >= limit)\r
913                         year = n < 100 ? n + 1900 : n;\r
914                     else\r
915                         return ScriptRuntime.NaN;\r
916                 } else if (c == ':') {\r
917                     if (hour < 0)\r
918                         hour = /*byte*/ n;\r
919                     else if (min < 0)\r
920                         min = /*byte*/ n;\r
921                     else\r
922                         return ScriptRuntime.NaN;\r
923                 } else if (c == '/') {\r
924                     if (mon < 0)\r
925                         mon = /*byte*/ n-1;\r
926                     else if (mday < 0)\r
927                         mday = /*byte*/ n;\r
928                     else\r
929                         return ScriptRuntime.NaN;\r
930                 } else if (i < limit && c != ',' && c > ' ' && c != '-') {\r
931                     return ScriptRuntime.NaN;\r
932                 } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */\r
933                     if (tzoffset < 0)\r
934                         tzoffset -= n;\r
935                     else\r
936                         tzoffset += n;\r
937                 } else if (hour >= 0 && min < 0) {\r
938                     min = /*byte*/ n;\r
939                 } else if (min >= 0 && sec < 0) {\r
940                     sec = /*byte*/ n;\r
941                 } else if (mday < 0) {\r
942                     mday = /*byte*/ n;\r
943                 } else {\r
944                     return ScriptRuntime.NaN;\r
945                 }\r
946                 prevc = 0;\r
947             } else if (c == '/' || c == ':' || c == '+' || c == '-') {\r
948                 prevc = c;\r
949             } else {\r
950                 int st = i - 1;\r
951                 int k;\r
952                 while (i < limit) {\r
953                     c = s.charAt(i);\r
954                     if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))\r
955                         break;\r
956                     i++;\r
957                 }\r
958                 if (i <= st + 1)\r
959                     return ScriptRuntime.NaN;\r
960                 for (k = wtb.length; --k >= 0;)\r
961                     if (date_regionMatches(wtb[k], 0, s, st, i-st)) {\r
962                         int action = ttb[k];\r
963                         if (action != 0) {\r
964                             if (action < 0) {\r
965                                 /*\r
966                                  * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as\r
967                                  * 12:30, instead of blindly adding 12 if PM.\r
968                                  */\r
969                                 if (hour > 12 || hour < 0) {\r
970                                     return ScriptRuntime.NaN;\r
971                                 } else {\r
972                                     if (action == -1 && hour == 12) { // am\r
973                                         hour = 0;\r
974                                     } else if (action == -2 && hour != 12) {// pm\r
975                                         hour += 12;\r
976                                     }\r
977                                 }\r
978                             } else if (action <= 13) { /* month! */\r
979                                 if (mon < 0) {\r
980                                     mon = /*byte*/ (action - 2);\r
981                                 } else {\r
982                                     return ScriptRuntime.NaN;\r
983                                 }\r
984                             } else {\r
985                                 tzoffset = action - 10000;\r
986                             }\r
987                         }\r
988                         break;\r
989                     }\r
990                 if (k < 0)\r
991                     return ScriptRuntime.NaN;\r
992                 prevc = 0;\r
993             }\r
994         }\r
995         if (year < 0 || mon < 0 || mday < 0)\r
996             return ScriptRuntime.NaN;\r
997         if (sec < 0)\r
998             sec = 0;\r
999         if (min < 0)\r
1000             min = 0;\r
1001         if (hour < 0)\r
1002             hour = 0;\r
1003         if (tzoffset == -1) { /* no time zone specified, have to use local */\r
1004             double time;\r
1005             time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);\r
1006             return internalUTC(time);\r
1007         }\r
1008 \r
1009         msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);\r
1010         msec += tzoffset * msPerMinute;\r
1011         return msec;\r
1012     }\r
1013 \r
1014     private static double jsStaticFunction_parse(String s) {\r
1015         return date_parseString(s);\r
1016     }\r
1017 \r
1018     private static final int FORMATSPEC_FULL = 0;\r
1019     private static final int FORMATSPEC_DATE = 1;\r
1020     private static final int FORMATSPEC_TIME = 2;\r
1021 \r
1022     private static String date_format(double t, int format) {\r
1023         if (t != t)\r
1024             return jsFunction_NaN_date_str;\r
1025 \r
1026         StringBuffer result = new StringBuffer(60);\r
1027         double local = LocalTime(t);\r
1028 \r
1029         /* offset from GMT in minutes.  The offset includes daylight savings,\r
1030            if it applies. */\r
1031         int minutes = (int) Math.floor((LocalTZA + DaylightSavingTA(t))\r
1032                                        / msPerMinute);\r
1033         /* map 510 minutes to 0830 hours */\r
1034         int offset = (minutes / 60) * 100 + minutes % 60;\r
1035 \r
1036         String dateStr = Integer.toString(DateFromTime(local));\r
1037         String hourStr = Integer.toString(HourFromTime(local));\r
1038         String minStr = Integer.toString(MinFromTime(local));\r
1039         String secStr = Integer.toString(SecFromTime(local));\r
1040         String offsetStr = Integer.toString(offset > 0 ? offset : -offset);\r
1041         int year = YearFromTime(local);\r
1042         String yearStr = Integer.toString(year > 0 ? year : -year);\r
1043 \r
1044         /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */\r
1045         /* Tue Oct 31 2000 */\r
1046         /* 09:41:40 GMT-0800 (PST) */\r
1047 \r
1048         if (format != FORMATSPEC_TIME) {\r
1049             result.append(days[WeekDay(local)]);\r
1050             result.append(' ');\r
1051             result.append(months[MonthFromTime(local)]);\r
1052             if (dateStr.length() == 1)\r
1053                 result.append(" 0");\r
1054             else\r
1055                 result.append(' ');\r
1056             result.append(dateStr);\r
1057             result.append(' ');\r
1058         }\r
1059 \r
1060         if (format != FORMATSPEC_DATE) {\r
1061             if (hourStr.length() == 1)\r
1062                 result.append('0');\r
1063             result.append(hourStr);\r
1064             if (minStr.length() == 1)\r
1065                 result.append(":0");\r
1066             else\r
1067                 result.append(':');\r
1068             result.append(minStr);\r
1069             if (secStr.length() == 1)\r
1070                 result.append(":0");\r
1071             else\r
1072                 result.append(':');\r
1073             result.append(secStr);\r
1074             if (offset > 0)\r
1075                 result.append(" GMT+");\r
1076             else\r
1077                 result.append(" GMT-");\r
1078             for (int i = offsetStr.length(); i < 4; i++)\r
1079                 result.append('0');\r
1080             result.append(offsetStr);\r
1081 \r
1082             if (timeZoneFormatter == null)\r
1083                 timeZoneFormatter = new java.text.SimpleDateFormat("zzz");\r
1084 \r
1085             if (timeZoneFormatter != null) {\r
1086                 result.append(" (");\r
1087                 java.util.Date date = new Date((long) t);\r
1088                 result.append(timeZoneFormatter.format(date));\r
1089                 result.append(')');\r
1090             }\r
1091             if (format != FORMATSPEC_TIME)\r
1092                 result.append(' ');\r
1093         }\r
1094 \r
1095         if (format != FORMATSPEC_TIME) {\r
1096             if (year < 0)\r
1097                 result.append('-');\r
1098             for (int i = yearStr.length(); i < 4; i++)\r
1099                 result.append('0');\r
1100             result.append(yearStr);\r
1101         }\r
1102 \r
1103         return result.toString();\r
1104     }\r
1105 \r
1106     /* the javascript constructor */\r
1107     private static Object jsConstructor(Object[] args, boolean inNewExpr) {\r
1108         // if called as a function, just return a string\r
1109         // representing the current time.\r
1110         if (!inNewExpr)\r
1111             return date_format(Now(), FORMATSPEC_FULL);\r
1112 \r
1113         NativeDate obj = new NativeDate();\r
1114 \r
1115         // if called as a constructor with no args,\r
1116         // return a new Date with the current time.\r
1117         if (args.length == 0) {\r
1118             obj.date = Now();\r
1119             return obj;\r
1120         }\r
1121 \r
1122         // if called with just one arg -\r
1123         if (args.length == 1) {\r
1124             double date;\r
1125             if (args[0] instanceof Scriptable)\r
1126                 args[0] = ((Scriptable) args[0]).getDefaultValue(null);\r
1127             if (!(args[0] instanceof String)) {\r
1128                 // if it's not a string, use it as a millisecond date\r
1129                 date = ScriptRuntime.toNumber(args[0]);\r
1130             } else {\r
1131                 // it's a string; parse it.\r
1132                 String str = (String) args[0];\r
1133                 date = date_parseString(str);\r
1134             }\r
1135             obj.date = TimeClip(date);\r
1136             return obj;\r
1137         }\r
1138 \r
1139         // multiple arguments; year, month, day etc.\r
1140         double array[] = new double[MAXARGS];\r
1141         int loop;\r
1142         double d;\r
1143 \r
1144         for (loop = 0; loop < MAXARGS; loop++) {\r
1145             if (loop < args.length) {\r
1146                 d = ScriptRuntime.toNumber(args[loop]);\r
1147 \r
1148                 if (d != d || Double.isInfinite(d)) {\r
1149                     obj.date = ScriptRuntime.NaN;\r
1150                     return obj;\r
1151                 }\r
1152                 array[loop] = ScriptRuntime.toInteger(args[loop]);\r
1153             } else {\r
1154                 array[loop] = 0;\r
1155             }\r
1156         }\r
1157 \r
1158         /* adjust 2-digit years into the 20th century */\r
1159         if (array[0] >= 0 && array[0] <= 99)\r
1160             array[0] += 1900;\r
1161 \r
1162         /* if we got a 0 for 'date' (which is out of range)\r
1163          * pretend it's a 1 */\r
1164         if (array[2] < 1)\r
1165             array[2] = 1;\r
1166 \r
1167         double day = MakeDay(array[0], array[1], array[2]);\r
1168         double time = MakeTime(array[3], array[4], array[5], array[6]);\r
1169         time = MakeDate(day, time);\r
1170         time = internalUTC(time);\r
1171         obj.date = TimeClip(time);\r
1172 \r
1173         return obj;\r
1174     }\r
1175 \r
1176     /* constants for toString, toUTCString */\r
1177     private static String jsFunction_NaN_date_str = "Invalid Date";\r
1178 \r
1179     private static String[] days = {\r
1180         "Sun","Mon","Tue","Wed","Thu","Fri","Sat"\r
1181     };\r
1182 \r
1183     private static String[] months = {\r
1184         "Jan", "Feb", "Mar", "Apr", "May", "Jun",\r
1185         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"\r
1186     };\r
1187 \r
1188     private static String toLocale_helper(double t,\r
1189                                           java.text.DateFormat formatter)\r
1190     {\r
1191         if (t != t)\r
1192             return jsFunction_NaN_date_str;\r
1193 \r
1194         java.util.Date tempdate = new Date((long) t);\r
1195         return formatter.format(tempdate);\r
1196     }\r
1197 \r
1198     private static String jsFunction_toLocaleString(double date) {\r
1199         if (localeDateTimeFormatter == null)\r
1200             localeDateTimeFormatter =\r
1201                 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);\r
1202 \r
1203         return toLocale_helper(date, localeDateTimeFormatter);\r
1204     }\r
1205 \r
1206     private static String jsFunction_toLocaleTimeString(double date) {\r
1207         if (localeTimeFormatter == null)\r
1208             localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);\r
1209 \r
1210         return toLocale_helper(date, localeTimeFormatter);\r
1211     }\r
1212 \r
1213     private static String jsFunction_toLocaleDateString(double date) {\r
1214         if (localeDateFormatter == null)\r
1215             localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);\r
1216 \r
1217         return toLocale_helper(date, localeDateFormatter);\r
1218     }\r
1219 \r
1220     private static String jsFunction_toUTCString(double date) {\r
1221         StringBuffer result = new StringBuffer(60);\r
1222 \r
1223         String dateStr = Integer.toString(DateFromTime(date));\r
1224         String hourStr = Integer.toString(HourFromTime(date));\r
1225         String minStr = Integer.toString(MinFromTime(date));\r
1226         String secStr = Integer.toString(SecFromTime(date));\r
1227         int year = YearFromTime(date);\r
1228         String yearStr = Integer.toString(year > 0 ? year : -year);\r
1229 \r
1230         result.append(days[WeekDay(date)]);\r
1231         result.append(", ");\r
1232         if (dateStr.length() == 1)\r
1233             result.append('0');\r
1234         result.append(dateStr);\r
1235         result.append(' ');\r
1236         result.append(months[MonthFromTime(date)]);\r
1237         if (year < 0)\r
1238             result.append(" -");\r
1239         else\r
1240             result.append(' ');\r
1241         int i;\r
1242         for (i = yearStr.length(); i < 4; i++)\r
1243             result.append('0');\r
1244         result.append(yearStr);\r
1245 \r
1246         if (hourStr.length() == 1)\r
1247             result.append(" 0");\r
1248         else\r
1249             result.append(' ');\r
1250         result.append(hourStr);\r
1251         if (minStr.length() == 1)\r
1252             result.append(":0");\r
1253         else\r
1254             result.append(':');\r
1255         result.append(minStr);\r
1256         if (secStr.length() == 1)\r
1257             result.append(":0");\r
1258         else\r
1259             result.append(':');\r
1260         result.append(secStr);\r
1261 \r
1262         result.append(" GMT");\r
1263         return result.toString();\r
1264     }\r
1265 \r
1266     private static double jsFunction_getYear(Context cx, double date) {\r
1267 \r
1268         int result = YearFromTime(LocalTime(date));\r
1269 \r
1270         if (cx.hasFeature(Context.FEATURE_NON_ECMA_GET_YEAR)) {\r
1271             if (result >= 1900 && result < 2000) {\r
1272                 result -= 1900;\r
1273             }\r
1274         } \r
1275         else {\r
1276             result -= 1900;\r
1277         }\r
1278         return result;\r
1279     }\r
1280 \r
1281     private static double jsFunction_getTimezoneOffset(double date) {\r
1282         return (date - LocalTime(date)) / msPerMinute;\r
1283     }\r
1284 \r
1285     public double jsFunction_setTime(double time) {\r
1286         this.date = TimeClip(time);\r
1287         return this.date;\r
1288     }\r
1289 \r
1290     private double makeTime(Object[] args, int maxargs, boolean local) {\r
1291         int i;\r
1292         double conv[] = new double[4];\r
1293         double hour, min, sec, msec;\r
1294         double lorutime; /* Local or UTC version of date */\r
1295 \r
1296         double time;\r
1297         double result;\r
1298 \r
1299         double date = this.date;\r
1300 \r
1301         /* just return NaN if the date is already NaN */\r
1302         if (date != date)\r
1303             return date;\r
1304 \r
1305         /* Satisfy the ECMA rule that if a function is called with\r
1306          * fewer arguments than the specified formal arguments, the\r
1307          * remaining arguments are set to undefined.  Seems like all\r
1308          * the Date.setWhatever functions in ECMA are only varargs\r
1309          * beyond the first argument; this should be set to undefined\r
1310          * if it's not given.  This means that "d = new Date();\r
1311          * d.setMilliseconds()" returns NaN.  Blech.\r
1312          */\r
1313         if (args.length == 0)\r
1314             args = ScriptRuntime.padArguments(args, 1);\r
1315 \r
1316         for (i = 0; i < args.length && i < maxargs; i++) {\r
1317             conv[i] = ScriptRuntime.toNumber(args[i]);\r
1318 \r
1319             // limit checks that happen in MakeTime in ECMA.\r
1320             if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {\r
1321                 this.date = ScriptRuntime.NaN;\r
1322                 return this.date;\r
1323             }\r
1324             conv[i] = ScriptRuntime.toInteger(conv[i]);\r
1325         }\r
1326 \r
1327         if (local)\r
1328             lorutime = LocalTime(date);\r
1329         else\r
1330             lorutime = date;\r
1331 \r
1332         i = 0;\r
1333         int stop = args.length;\r
1334 \r
1335         if (maxargs >= 4 && i < stop)\r
1336             hour = conv[i++];\r
1337         else\r
1338             hour = HourFromTime(lorutime);\r
1339 \r
1340         if (maxargs >= 3 && i < stop)\r
1341             min = conv[i++];\r
1342         else\r
1343             min = MinFromTime(lorutime);\r
1344 \r
1345         if (maxargs >= 2 && i < stop)\r
1346             sec = conv[i++];\r
1347         else\r
1348             sec = SecFromTime(lorutime);\r
1349 \r
1350         if (maxargs >= 1 && i < stop)\r
1351             msec = conv[i++];\r
1352         else\r
1353             msec = msFromTime(lorutime);\r
1354 \r
1355         time = MakeTime(hour, min, sec, msec);\r
1356         result = MakeDate(Day(lorutime), time);\r
1357 \r
1358         if (local)\r
1359             result = internalUTC(result);\r
1360         date = TimeClip(result);\r
1361 \r
1362         this.date = date;\r
1363         return date;\r
1364     }\r
1365 \r
1366     private double jsFunction_setHours(Object[] args) {\r
1367         return makeTime(args, 4, true);\r
1368     }\r
1369 \r
1370     private double jsFunction_setUTCHours(Object[] args) {\r
1371         return makeTime(args, 4, false);\r
1372     }\r
1373 \r
1374     private double makeDate(Object[] args, int maxargs, boolean local) {\r
1375         int i;\r
1376         double conv[] = new double[3];\r
1377         double year, month, day;\r
1378         double lorutime; /* local or UTC version of date */\r
1379         double result;\r
1380 \r
1381         double date = this.date;\r
1382 \r
1383         /* See arg padding comment in makeTime.*/\r
1384         if (args.length == 0)\r
1385             args = ScriptRuntime.padArguments(args, 1);\r
1386 \r
1387         for (i = 0; i < args.length && i < maxargs; i++) {\r
1388             conv[i] = ScriptRuntime.toNumber(args[i]);\r
1389 \r
1390             // limit checks that happen in MakeDate in ECMA.\r
1391             if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {\r
1392                 this.date = ScriptRuntime.NaN;\r
1393                 return this.date;\r
1394             }\r
1395             conv[i] = ScriptRuntime.toInteger(conv[i]);\r
1396         }\r
1397 \r
1398         /* return NaN if date is NaN and we're not setting the year,\r
1399          * If we are, use 0 as the time. */\r
1400         if (date != date) {\r
1401             if (args.length < 3) {\r
1402                 return ScriptRuntime.NaN;\r
1403             } else {\r
1404                 lorutime = 0;\r
1405             }\r
1406         } else {\r
1407             if (local)\r
1408                 lorutime = LocalTime(date);\r
1409             else\r
1410                 lorutime = date;\r
1411         }\r
1412 \r
1413         i = 0;\r
1414         int stop = args.length;\r
1415 \r
1416         if (maxargs >= 3 && i < stop)\r
1417             year = conv[i++];\r
1418         else\r
1419             year = YearFromTime(lorutime);\r
1420 \r
1421         if (maxargs >= 2 && i < stop)\r
1422             month = conv[i++];\r
1423         else\r
1424             month = MonthFromTime(lorutime);\r
1425 \r
1426         if (maxargs >= 1 && i < stop)\r
1427             day = conv[i++];\r
1428         else\r
1429             day = DateFromTime(lorutime);\r
1430 \r
1431         day = MakeDay(year, month, day); /* day within year */\r
1432         result = MakeDate(day, TimeWithinDay(lorutime));\r
1433 \r
1434         if (local)\r
1435             result = internalUTC(result);\r
1436 \r
1437         date = TimeClip(result);\r
1438 \r
1439         this.date = date;\r
1440         return date;\r
1441     }\r
1442 \r
1443     private double jsFunction_setYear(double year) {\r
1444         double day, result;\r
1445         if (year != year || Double.isInfinite(year)) {\r
1446             this.date = ScriptRuntime.NaN;\r
1447             return this.date;\r
1448         }\r
1449 \r
1450         if (this.date != this.date) {\r
1451             this.date = 0;\r
1452         } else {\r
1453             this.date = LocalTime(this.date);\r
1454         }\r
1455 \r
1456         if (year >= 0 && year <= 99)\r
1457             year += 1900;\r
1458 \r
1459         day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));\r
1460         result = MakeDate(day, TimeWithinDay(this.date));\r
1461         result = internalUTC(result);\r
1462 \r
1463         this.date = TimeClip(result);\r
1464         return this.date;\r
1465     }\r
1466 \r
1467     protected String getIdName(int id) {\r
1468         if (prototypeFlag) {\r
1469             switch (id) {\r
1470                 case ConstructorId_UTC:     return "UTC";\r
1471                 case ConstructorId_parse:   return "parse";\r
1472                 case Id_constructor:        return "constructor"; \r
1473                 case Id_toString:           return "toString";\r
1474                 case Id_toTimeString:       return "toTimeString";\r
1475                 case Id_toDateString:       return "toDateString";\r
1476                 case Id_toLocaleString:     return "toLocaleString";\r
1477                 case Id_toLocaleTimeString: return "toLocaleTimeString";\r
1478                 case Id_toLocaleDateString: return "toLocaleDateString";\r
1479                 case Id_toUTCString:        return "toUTCString";\r
1480                 case Id_valueOf:            return "valueOf";\r
1481                 case Id_getTime:            return "getTime";\r
1482                 case Id_getYear:            return "getYear";\r
1483                 case Id_getFullYear:        return "getFullYear";\r
1484                 case Id_getUTCFullYear:     return "getUTCFullYear";\r
1485                 case Id_getMonth:           return "getMonth";\r
1486                 case Id_getUTCMonth:        return "getUTCMonth";\r
1487                 case Id_getDate:            return "getDate";\r
1488                 case Id_getUTCDate:         return "getUTCDate";\r
1489                 case Id_getDay:             return "getDay";\r
1490                 case Id_getUTCDay:          return "getUTCDay";\r
1491                 case Id_getHours:           return "getHours";\r
1492                 case Id_getUTCHours:        return "getUTCHours";\r
1493                 case Id_getMinutes:         return "getMinutes";\r
1494                 case Id_getUTCMinutes:      return "getUTCMinutes";\r
1495                 case Id_getSeconds:         return "getSeconds";\r
1496                 case Id_getUTCSeconds:      return "getUTCSeconds";\r
1497                 case Id_getMilliseconds:    return "getMilliseconds";\r
1498                 case Id_getUTCMilliseconds: return "getUTCMilliseconds";\r
1499                 case Id_getTimezoneOffset:  return "getTimezoneOffset";\r
1500                 case Id_setTime:            return "setTime";\r
1501                 case Id_setMilliseconds:    return "setMilliseconds";\r
1502                 case Id_setUTCMilliseconds: return "setUTCMilliseconds";\r
1503                 case Id_setSeconds:         return "setSeconds";\r
1504                 case Id_setUTCSeconds:      return "setUTCSeconds";\r
1505                 case Id_setMinutes:         return "setMinutes";\r
1506                 case Id_setUTCMinutes:      return "setUTCMinutes";\r
1507                 case Id_setHours:           return "setHours";\r
1508                 case Id_setUTCHours:        return "setUTCHours";\r
1509                 case Id_setDate:            return "setDate";\r
1510                 case Id_setUTCDate:         return "setUTCDate";\r
1511                 case Id_setMonth:           return "setMonth";\r
1512                 case Id_setUTCMonth:        return "setUTCMonth";\r
1513                 case Id_setFullYear:        return "setFullYear";\r
1514                 case Id_setUTCFullYear:     return "setUTCFullYear";\r
1515                 case Id_setYear:            return "setYear";\r
1516             }\r
1517         }\r
1518         return null;        \r
1519     }\r
1520 \r
1521 // #string_id_map#\r
1522 \r
1523     protected int mapNameToId(String s) {\r
1524         if (!prototypeFlag) { return 0; }\r
1525         int id;\r
1526 // #generated# Last update: 2001-04-22 23:46:59 CEST\r
1527         L0: { id = 0; String X = null; int c;\r
1528             L: switch (s.length()) {\r
1529             case 6: X="getDay";id=Id_getDay; break L;\r
1530             case 7: switch (s.charAt(3)) {\r
1531                 case 'D': c=s.charAt(0);\r
1532                     if (c=='g') { X="getDate";id=Id_getDate; }\r
1533                     else if (c=='s') { X="setDate";id=Id_setDate; }\r
1534                     break L;\r
1535                 case 'T': c=s.charAt(0);\r
1536                     if (c=='g') { X="getTime";id=Id_getTime; }\r
1537                     else if (c=='s') { X="setTime";id=Id_setTime; }\r
1538                     break L;\r
1539                 case 'Y': c=s.charAt(0);\r
1540                     if (c=='g') { X="getYear";id=Id_getYear; }\r
1541                     else if (c=='s') { X="setYear";id=Id_setYear; }\r
1542                     break L;\r
1543                 case 'u': X="valueOf";id=Id_valueOf; break L;\r
1544                 } break L;\r
1545             case 8: c=s.charAt(0);\r
1546                 if (c=='g') {\r
1547                     c=s.charAt(7);\r
1548                     if (c=='h') { X="getMonth";id=Id_getMonth; }\r
1549                     else if (c=='s') { X="getHours";id=Id_getHours; }\r
1550                 }\r
1551                 else if (c=='s') {\r
1552                     c=s.charAt(7);\r
1553                     if (c=='h') { X="setMonth";id=Id_setMonth; }\r
1554                     else if (c=='s') { X="setHours";id=Id_setHours; }\r
1555                 }\r
1556                 else if (c=='t') { X="toString";id=Id_toString; }\r
1557                 break L;\r
1558             case 9: X="getUTCDay";id=Id_getUTCDay; break L;\r
1559             case 10: c=s.charAt(3);\r
1560                 if (c=='M') {\r
1561                     c=s.charAt(0);\r
1562                     if (c=='g') { X="getMinutes";id=Id_getMinutes; }\r
1563                     else if (c=='s') { X="setMinutes";id=Id_setMinutes; }\r
1564                 }\r
1565                 else if (c=='S') {\r
1566                     c=s.charAt(0);\r
1567                     if (c=='g') { X="getSeconds";id=Id_getSeconds; }\r
1568                     else if (c=='s') { X="setSeconds";id=Id_setSeconds; }\r
1569                 }\r
1570                 else if (c=='U') {\r
1571                     c=s.charAt(0);\r
1572                     if (c=='g') { X="getUTCDate";id=Id_getUTCDate; }\r
1573                     else if (c=='s') { X="setUTCDate";id=Id_setUTCDate; }\r
1574                 }\r
1575                 break L;\r
1576             case 11: switch (s.charAt(3)) {\r
1577                 case 'F': c=s.charAt(0);\r
1578                     if (c=='g') { X="getFullYear";id=Id_getFullYear; }\r
1579                     else if (c=='s') { X="setFullYear";id=Id_setFullYear; }\r
1580                     break L;\r
1581                 case 'M': X="toGMTString";id=Id_toGMTString; break L;\r
1582                 case 'T': X="toUTCString";id=Id_toUTCString; break L;\r
1583                 case 'U': c=s.charAt(0);\r
1584                     if (c=='g') {\r
1585                         c=s.charAt(9);\r
1586                         if (c=='r') { X="getUTCHours";id=Id_getUTCHours; }\r
1587                         else if (c=='t') { X="getUTCMonth";id=Id_getUTCMonth; }\r
1588                     }\r
1589                     else if (c=='s') {\r
1590                         c=s.charAt(9);\r
1591                         if (c=='r') { X="setUTCHours";id=Id_setUTCHours; }\r
1592                         else if (c=='t') { X="setUTCMonth";id=Id_setUTCMonth; }\r
1593                     }\r
1594                     break L;\r
1595                 case 's': X="constructor";id=Id_constructor; break L;\r
1596                 } break L;\r
1597             case 12: c=s.charAt(2);\r
1598                 if (c=='D') { X="toDateString";id=Id_toDateString; }\r
1599                 else if (c=='T') { X="toTimeString";id=Id_toTimeString; }\r
1600                 break L;\r
1601             case 13: c=s.charAt(0);\r
1602                 if (c=='g') {\r
1603                     c=s.charAt(6);\r
1604                     if (c=='M') { X="getUTCMinutes";id=Id_getUTCMinutes; }\r
1605                     else if (c=='S') { X="getUTCSeconds";id=Id_getUTCSeconds; }\r
1606                 }\r
1607                 else if (c=='s') {\r
1608                     c=s.charAt(6);\r
1609                     if (c=='M') { X="setUTCMinutes";id=Id_setUTCMinutes; }\r
1610                     else if (c=='S') { X="setUTCSeconds";id=Id_setUTCSeconds; }\r
1611                 }\r
1612                 break L;\r
1613             case 14: c=s.charAt(0);\r
1614                 if (c=='g') { X="getUTCFullYear";id=Id_getUTCFullYear; }\r
1615                 else if (c=='s') { X="setUTCFullYear";id=Id_setUTCFullYear; }\r
1616                 else if (c=='t') { X="toLocaleString";id=Id_toLocaleString; }\r
1617                 break L;\r
1618             case 15: c=s.charAt(0);\r
1619                 if (c=='g') { X="getMilliseconds";id=Id_getMilliseconds; }\r
1620                 else if (c=='s') { X="setMilliseconds";id=Id_setMilliseconds; }\r
1621                 break L;\r
1622             case 17: X="getTimezoneOffset";id=Id_getTimezoneOffset; break L;\r
1623             case 18: c=s.charAt(0);\r
1624                 if (c=='g') { X="getUTCMilliseconds";id=Id_getUTCMilliseconds; }\r
1625                 else if (c=='s') { X="setUTCMilliseconds";id=Id_setUTCMilliseconds; }\r
1626                 else if (c=='t') {\r
1627                     c=s.charAt(8);\r
1628                     if (c=='D') { X="toLocaleDateString";id=Id_toLocaleDateString; }\r
1629                     else if (c=='T') { X="toLocaleTimeString";id=Id_toLocaleTimeString; }\r
1630                 }\r
1631                 break L;\r
1632             }\r
1633             if (X!=null && X!=s && !X.equals(s)) id = 0;\r
1634         }\r
1635 // #/generated#\r
1636         return id;\r
1637     }\r
1638 \r
1639     private static final int\r
1640         ConstructorId_UTC       = -2,\r
1641         ConstructorId_parse     = -1,\r
1642 \r
1643         Id_constructor          =  1,\r
1644         Id_toString             =  2,\r
1645         Id_toTimeString         =  3,\r
1646         Id_toDateString         =  4,\r
1647         Id_toLocaleString       =  5,\r
1648         Id_toLocaleTimeString   =  6,\r
1649         Id_toLocaleDateString   =  7,\r
1650         Id_toUTCString          =  8,\r
1651         Id_valueOf              =  9,\r
1652         Id_getTime              = 10,\r
1653         Id_getYear              = 11,\r
1654         Id_getFullYear          = 12,\r
1655         Id_getUTCFullYear       = 13,\r
1656         Id_getMonth             = 14,\r
1657         Id_getUTCMonth          = 15,\r
1658         Id_getDate              = 16,\r
1659         Id_getUTCDate           = 17,\r
1660         Id_getDay               = 18,\r
1661         Id_getUTCDay            = 19,\r
1662         Id_getHours             = 20,\r
1663         Id_getUTCHours          = 21,\r
1664         Id_getMinutes           = 22,\r
1665         Id_getUTCMinutes        = 23,\r
1666         Id_getSeconds           = 24,\r
1667         Id_getUTCSeconds        = 25,\r
1668         Id_getMilliseconds      = 26,\r
1669         Id_getUTCMilliseconds   = 27,\r
1670         Id_getTimezoneOffset    = 28,\r
1671         Id_setTime              = 29,\r
1672         Id_setMilliseconds      = 30,\r
1673         Id_setUTCMilliseconds   = 31,\r
1674         Id_setSeconds           = 32,\r
1675         Id_setUTCSeconds        = 33,\r
1676         Id_setMinutes           = 34,\r
1677         Id_setUTCMinutes        = 35,\r
1678         Id_setHours             = 36,\r
1679         Id_setUTCHours          = 37,\r
1680         Id_setDate              = 38,\r
1681         Id_setUTCDate           = 39,\r
1682         Id_setMonth             = 40,\r
1683         Id_setUTCMonth          = 41,\r
1684         Id_setFullYear          = 42,\r
1685         Id_setUTCFullYear       = 43,\r
1686         Id_setYear              = 44,\r
1687 \r
1688         MAX_PROTOTYPE_ID        = 44;\r
1689 \r
1690     private static final int\r
1691         Id_toGMTString  =  Id_toUTCString; // Alias, see Ecma B.2.6\r
1692 // #/string_id_map#\r
1693 \r
1694     /* cached values */\r
1695     private static java.util.TimeZone thisTimeZone;\r
1696     private static double LocalTZA;\r
1697     private static java.text.DateFormat timeZoneFormatter;\r
1698     private static java.text.DateFormat localeDateTimeFormatter;\r
1699     private static java.text.DateFormat localeDateFormatter;\r
1700     private static java.text.DateFormat localeTimeFormatter;\r
1701 \r
1702     private double date;\r
1703 \r
1704     private boolean prototypeFlag;\r
1705 }\r
1706 \r
1707 \r