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