1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 /*
41  * JS date methods.
42  */
43
44 /*
45  * "For example, OS/360 devotes 26 bytes of the permanently
46  *  resident date-turnover routine to the proper handling of
47  *  December 31 on leap years (when it is Day 366).  That
48  *  might have been left to the operator."
49  *
50  * Frederick Brooks, 'The Second-System Effect'.
51  */
52
53 #include "jsstddef.h"
54 #include <ctype.h>
55 #include <locale.h>
56 #include <math.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include "jstypes.h"
60 #include "jsprf.h"
61 #include "prmjtime.h"
62 #include "jsutil.h" /* Added by JSIFY */
63 #include "jsapi.h"
64 #include "jsconfig.h"
65 #include "jscntxt.h"
66 #include "jsdate.h"
67 #include "jsinterp.h"
68 #include "jsnum.h"
69 #include "jsobj.h"
70 #include "jsstr.h"
71
72 /*
73  * The JS 'Date' object is patterned after the Java 'Date' object.
74  * Here is an script:
75  *
76  *    today = new Date();
77  *
78  *    print(today.toLocaleString());
79  *
80  *    weekDay = today.getDay();
81  *
82  *
83  * These Java (and ECMA-262) methods are supported:
84  *
85  *     UTC
86  *     getDate (getUTCDate)
87  *     getDay (getUTCDay)
88  *     getHours (getUTCHours)
89  *     getMinutes (getUTCMinutes)
90  *     getMonth (getUTCMonth)
91  *     getSeconds (getUTCSeconds)
92  *     getMilliseconds (getUTCMilliseconds)
93  *     getTime
94  *     getTimezoneOffset
95  *     getYear
96  *     getFullYear (getUTCFullYear)
97  *     parse
98  *     setDate (setUTCDate)
99  *     setHours (setUTCHours)
100  *     setMinutes (setUTCMinutes)
101  *     setMonth (setUTCMonth)
102  *     setSeconds (setUTCSeconds)
103  *     setMilliseconds (setUTCMilliseconds)
104  *     setTime
105  *     setYear (setFullYear, setUTCFullYear)
106  *     toGMTString (toUTCString)
107  *     toLocaleString
108  *     toString
109  *
110  *
111  * These Java methods are not supported
112  *
113  *     setDay
114  *     before
115  *     after
116  *     equals
117  *     hashCode
118  */
119
120 /*
121  * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
122  * definition and reduce dependence on NSPR.  NSPR is used to get the current
123  * time in milliseconds, the time zone offset, and the daylight savings time
124  * offset for a given time.  NSPR is also used for Date.toLocaleString(), for
125  * locale-specific formatting, and to get a string representing the timezone.
126  * (Which turns out to be platform-dependent.)
127  *
128  * To do:
129  * (I did some performance tests by timing how long it took to run what
130  *  I had of the js ECMA conformance tests.)
131  *
132  * - look at saving results across multiple calls to supporting
133  * functions; the toString functions compute some of the same values
134  * multiple times.  Although - I took a quick stab at this, and I lost
135  * rather than gained.  (Fractionally.)  Hard to tell what compilers/processors
136  * are doing these days.
137  *
138  * - look at tweaking function return types to return double instead
139  * of int; this seems to make things run slightly faster sometimes.
140  * (though it could be architecture-dependent.)  It'd be good to see
141  * how this does on win32.  (Tried it on irix.)  Types could use a
142  * general going-over.
143  */
144
145 /*
146  * Supporting functions - ECMA 15.9.1.*
147  */
148
149 #define HalfTimeDomain  8.64e15
150 #define HoursPerDay     24.0
151 #define MinutesPerDay   (HoursPerDay * MinutesPerHour)
152 #define MinutesPerHour  60.0
153 #define SecondsPerDay   (MinutesPerDay * SecondsPerMinute)
154 #define SecondsPerHour  (MinutesPerHour * SecondsPerMinute)
155 #define SecondsPerMinute 60.0
156
157 #if defined(XP_WIN) || defined(XP_OS2)
158 /* Work around msvc double optimization bug by making these runtime values; if
159  * they're available at compile time, msvc optimizes division by them by
160  * computing the reciprocal and multiplying instead of dividing - this loses
161  * when the reciprocal isn't representable in a double.
162  */
163 static jsdouble msPerSecond = 1000.0;
164 static jsdouble msPerDay = SecondsPerDay * 1000.0;
165 static jsdouble msPerHour = SecondsPerHour * 1000.0;
166 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
167 #else
168 #define msPerDay        (SecondsPerDay * msPerSecond)
169 #define msPerHour       (SecondsPerHour * msPerSecond)
170 #define msPerMinute     (SecondsPerMinute * msPerSecond)
171 #define msPerSecond     1000.0
172 #endif
173
174 #define Day(t)          floor((t) / msPerDay)
175
176 static jsdouble
177 TimeWithinDay(jsdouble t)
178 0 {
179 0     jsdouble result;
180 0     result = fmod(t, msPerDay);
181 0     if (result < 0)
182 0         result += msPerDay;
183 0     return result;
184 }
185
186 #define DaysInYear(y)   ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0))  \
187                          ? 366 : 365)
188
189 /* math here has to be f.p, because we need
190  *  floor((1968 - 1969) / 4) == -1
191  */
192 #define DayFromYear(y)  (365 * ((y)-1970) + floor(((y)-1969)/4.0)            \
193                          - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
194 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
195
196 static jsint
197 YearFromTime(jsdouble t)
198 0 {
199 0     jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
200 0     jsdouble t2 = (jsdouble) TimeFromYear(y);
201
202 0     if (t2 > t) {
203 0         y--;
204     } else {
205 0         if (t2 + msPerDay * DaysInYear(y) <= t)
206 0             y++;
207     }
208 0     return y;
209 }
210
211 #define InLeapYear(t)   (JSBool) (DaysInYear(YearFromTime(t)) == 366)
212
213 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
214
215 /*
216  * The following array contains the day of year for the first day of
217  * each month, where index 0 is January, and day 0 is January 1.
218  */
219 static jsdouble firstDayOfMonth[2][12] = {
220     {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
221     {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
222 };
223
224 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
225
226 static intN
227 MonthFromTime(jsdouble t)
228 0 {
229 0     intN d, step;
230 0     jsint year = YearFromTime(t);
231 0     d = DayWithinYear(t, year);
232
233 0     if (d < (step = 31))
234 0         return 0;
235 0     step += (InLeapYear(t) ? 29 : 28);
236 0     if (d < step)
237 0         return 1;
238 0     if (d < (step += 31))
239 0         return 2;
240 0     if (d < (step += 30))
241 0         return 3;
242 0     if (d < (step += 31))
243 0         return 4;
244 0     if (d < (step += 30))
245 0         return 5;
246 0     if (d < (step += 31))
247 0         return 6;
248 0     if (d < (step += 31))
249 0         return 7;
250 0     if (d < (step += 30))
251 0         return 8;
252 0     if (d < (step += 31))
253 0         return 9;
254 0     if (d < (step += 30))
255 0         return 10;
256 0     return 11;
257 }
258
259 static intN
260 DateFromTime(jsdouble t)
261 0 {
262 0     intN d, step, next;
263 0     jsint year = YearFromTime(t);
264 0     d = DayWithinYear(t, year);
265
266 0     if (d <= (next = 30))
267 0         return d + 1;
268 0     step = next;
269 0     next += (InLeapYear(t) ? 29 : 28);
270 0     if (d <= next)
271 0         return d - step;
272 0     step = next;
273 0     if (d <= (next += 31))
274 0         return d - step;
275 0     step = next;
276 0     if (d <= (next += 30))
277 0         return d - step;
278 0     step = next;
279 0     if (d <= (next += 31))
280 0         return d - step;
281 0     step = next;
282 0     if (d <= (next += 30))
283 0         return d - step;
284 0     step = next;
285 0     if (d <= (next += 31))
286 0         return d - step;
287 0     step = next;
288 0     if (d <= (next += 31))
289 0         return d - step;
290 0     step = next;
291 0     if (d <= (next += 30))
292 0         return d - step;
293 0     step = next;
294 0     if (d <= (next += 31))
295 0         return d - step;
296 0     step = next;
297 0     if (d <= (next += 30))
298 0         return d - step;
299 0     step = next;
300 0     return d - step;
301 }
302
303 static intN
304 WeekDay(jsdouble t)
305 0 {
306 0     jsint result;
307 0     result = (jsint) Day(t) + 4;
308 0     result = result % 7;
309 0     if (result < 0)
310 0         result += 7;
311 0     return (intN) result;
312 }
313
314 #define MakeTime(hour, min, sec, ms) \
315 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
316
317 static jsdouble
318 MakeDay(jsdouble year, jsdouble month, jsdouble date)
319 0 {
320 0     JSBool leap;
321 0     jsdouble yearday;
322 0     jsdouble monthday;
323
324 0     year += floor(month / 12);
325
326 0     month = fmod(month, 12.0);
327 0     if (month < 0)
328 0         month += 12;
329
330 0     leap = (DaysInYear((jsint) year) == 366);
331
332 0     yearday = floor(TimeFromYear(year) / msPerDay);
333 0     monthday = DayFromMonth(month, leap);
334
335 0     return yearday + monthday + date - 1;
336 }
337
338 #define MakeDate(day, time) ((day) * msPerDay + (time))
339
340 /* 
341  * Years and leap years on which Jan 1 is a Sunday, Monday, etc. 
342  *
343  * yearStartingWith[0][i] is an example non-leap year where
344  * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
345  *
346  * yearStartingWith[1][i] is an example leap year where
347  * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
348  */
349 static jsint yearStartingWith[2][7] = {
350     {1978, 1973, 1974, 1975, 1981, 1971, 1977},
351     {1984, 1996, 1980, 1992, 1976, 1988, 1972}
352 };
353
354 /*
355  * Find a year for which any given date will fall on the same weekday.
356  *
357  * This function should be used with caution when used other than
358  * for determining DST; it hasn't been proven not to produce an
359  * incorrect year for times near year boundaries.
360  */
361 static jsint
362 0 EquivalentYearForDST(jsint year) {
363 0     jsint day;
364 0     JSBool isLeapYear;
365
366 0     day = (jsint) DayFromYear(year) + 4;
367 0     day = day % 7;
368 0     if (day < 0)
369 0 day += 7;
370
371 0     isLeapYear = (DaysInYear(year) == 366);
372
373 0     return yearStartingWith[isLeapYear][day];
374 }
375
376 /* LocalTZA gets set by js_InitDateClass() */
377 static jsdouble LocalTZA;
378
379 static jsdouble
380 DaylightSavingTA(jsdouble t)
381 0 {
382 0     volatile int64 PR_t;
383 0     int64 ms2us;
384 0     int64 offset;
385 0     jsdouble result;
386
387     /* abort if NaN */
388 0     if (JSDOUBLE_IS_NaN(t))
389 0         return t;
390
391     /*
392      * If earlier than 1970 or after 2038, potentially beyond the ken of
393      * many OSes, map it to an equivalent year before asking.
394      */
395 0     if (t < 0.0 || t > 2145916800000.0) {
396 0         jsint year;
397 0         jsdouble day;
398
399 0         year = EquivalentYearForDST(YearFromTime(t));
400 0         day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
401 0         t = MakeDate(day, TimeWithinDay(t));
402     }
403
404     /* put our t in an LL, and map it to usec for prtime */
405 0     JSLL_D2L(PR_t, t);
406 0     JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
407 0     JSLL_MUL(PR_t, PR_t, ms2us);
408
409 0     offset = PRMJ_DSTOffset(PR_t);
410
411 0     JSLL_DIV(offset, offset, ms2us);
412 0     JSLL_L2D(result, offset);
413 0     return result;
414 }
415
416
417 #define AdjustTime(t)   fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
418
419 #define LocalTime(t)    ((t) + AdjustTime(t))
420
421 static jsdouble
422 UTC(jsdouble t)
423 0 {
424 0     return t - AdjustTime(t - LocalTZA);
425 }
426
427 static intN
428 HourFromTime(jsdouble t)
429 0 {
430 0     intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
431 0     if (result < 0)
432 0         result += (intN)HoursPerDay;
433 0     return result;
434 }
435
436 static intN
437 MinFromTime(jsdouble t)
438 0 {
439 0     intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
440 0     if (result < 0)
441 0         result += (intN)MinutesPerHour;
442 0     return result;
443 }
444
445 static intN
446 SecFromTime(jsdouble t)
447 0 {
448 0     intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
449 0     if (result < 0)
450 0         result += (intN)SecondsPerMinute;
451 0     return result;
452 }
453
454 static intN
455 msFromTime(jsdouble t)
456 0 {
457 0     intN result = (intN) fmod(t, msPerSecond);
458 0     if (result < 0)
459 0         result += (intN)msPerSecond;
460 0     return result;
461 }
462
463 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
464                       && !((d < 0 ? -d : d) > HalfTimeDomain)) \
465                      ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
466
467 /**
468  * end of ECMA 'support' functions
469  */
470
471 /*
472  * Other Support routines and definitions
473  */
474
475 static JSClass date_class = {
476     js_Date_str,
477     JSCLASS_HAS_PRIVATE,
478     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
479     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
480     JSCLASS_NO_OPTIONAL_MEMBERS
481 };
482
483 /* for use by date_parse */
484
485 static const char* wtb[] = {
486     "am", "pm",
487     "monday", "tuesday", "wednesday", "thursday", "friday",
488     "saturday", "sunday",
489     "january", "february", "march", "april", "may", "june",
490     "july", "august", "september", "october", "november", "december",
491     "gmt", "ut", "utc",
492     "est", "edt",
493     "cst", "cdt",
494     "mst", "mdt",
495     "pst", "pdt"
496     /* time zone table needs to be expanded */
497 };
498
499 static int ttb[] = {
500     -1, -2, 0, 0, 0, 0, 0, 0, 0,       /* AM/PM */
501     2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
502     10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */
503     10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */
504     10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */
505     10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */
506     10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */
507 };
508
509 /* helper for date_parse */
510 static JSBool
511 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
512                    int count, int ignoreCase)
513 0 {
514 0     JSBool result = JS_FALSE;
515     /* return true if matches, otherwise, false */
516
517 0     while (count > 0 && s1[s1off] && s2[s2off]) {
518 0         if (ignoreCase) {
519 0             if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
520 0                 break;
521             }
522         } else {
523 0             if ((jschar)s1[s1off] != s2[s2off]) {
524 0                 break;
525             }
526         }
527 0         s1off++;
528 0         s2off++;
529 0         count--;
530     }
531
532 0     if (count == 0) {
533 0         result = JS_TRUE;
534     }
535
536 0     return result;
537 }
538
539 /* find UTC time from given date... no 1900 correction! */
540 static jsdouble
541 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
542                   jsdouble min, jsdouble sec, jsdouble msec)
543 0 {
544 0     jsdouble day;
545 0     jsdouble msec_time;
546 0     jsdouble result;
547
548 0     day = MakeDay(year, mon, mday);
549 0     msec_time = MakeTime(hour, min, sec, msec);
550 0     result = MakeDate(day, msec_time);
551 0     return result;
552 }
553
554 /*
555  * See ECMA 15.9.4.[3-10];
556  */
557 /* XXX this function must be above date_parseString to avoid a
558    horrid bug in the Win16 1.52 compiler */
559 #define MAXARGS        7
560 static JSBool
561 date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
562 0 {
563 0     jsdouble array[MAXARGS];
564 0     uintN loop;
565 0     jsdouble d;
566
567 0     for (loop = 0; loop < MAXARGS; loop++) {
568 0         if (loop < argc) {
569 0             if (!js_ValueToNumber(cx, argv[loop], &d))
570 0                 return JS_FALSE;
571             /* return NaN if any arg is NaN */
572 0             if (!JSDOUBLE_IS_FINITE(d)) {
573 0                 return js_NewNumberValue(cx, d, rval);
574             }
575 0             array[loop] = floor(d);
576         } else {
577 0             array[loop] = 0;
578         }
579     }
580
581     /* adjust 2-digit years into the 20th century */
582 0     if (array[0] >= 0 && array[0] <= 99)
583 0         array[0] += 1900;
584
585     /* if we got a 0 for 'date' (which is out of range)
586      * pretend it's a 1.  (So Date.UTC(1972, 5) works) */
587 0     if (array[2] < 1)
588 0         array[2] = 1;
589
590 0     d = date_msecFromDate(array[0], array[1], array[2],
591                               array[3], array[4], array[5], array[6]);
592 0     d = TIMECLIP(d);
593
594 0     return js_NewNumberValue(cx, d, rval);
595 }
596
597 static JSBool
598 date_parseString(JSString *str, jsdouble *result)
599 0 {
600 0     jsdouble msec;
601
602 0     const jschar *s = JSSTRING_CHARS(str);
603 0     size_t limit = JSSTRING_LENGTH(str);
604 0     size_t i = 0;
605 0     int year = -1;
606 0     int mon = -1;
607 0     int mday = -1;
608 0     int hour = -1;
609 0     int min = -1;
610 0     int sec = -1;
611 0     int c = -1;
612 0     int n = -1;
613 0     jsdouble tzoffset = -1;  /* was an int, overflowed on win16!!! */
614 0     int prevc = 0;
615 0     JSBool seenplusminus = JS_FALSE;
616 0     int temp;
617 0     JSBool seenmonthname = JS_FALSE;
618
619 0     if (limit == 0)
620 0         goto syntax;
621 0     while (i < limit) {
622 0         c = s[i];
623 0         i++;
624 0         if (c <= ' ' || c == ',' || c == '-') {
625 0             if (c == '-' && '0' <= s[i] && s[i] <= '9') {
626 0               prevc = c;
627             }
628 0             continue;
629         }
630 0         if (c == '(') { /* comments) */
631 0             int depth = 1;
632 0             while (i < limit) {
633 0                 c = s[i];
634 0                 i++;
635 0                 if (c == '(') depth++;
636 0                 else if (c == ')')
637 0                     if (--depth <= 0)
638 0                         break;
639             }
640 0             continue;
641         }
642 0         if ('0' <= c && c <= '9') {
643 0             n = c - '0';
644 0             while (i < limit && '0' <= (c = s[i]) && c <= '9') {
645 0                 n = n * 10 + c - '0';
646 0                 i++;
647             }
648
649             /* allow TZA before the year, so
650              * 'Wed Nov 05 21:49:11 GMT-0800 1997'
651              * works */
652
653             /* uses of seenplusminus allow : in TZA, so Java
654              * no-timezone style of GMT+4:30 works
655              */
656
657 0             if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
658                 /* make ':' case below change tzoffset */
659 0                 seenplusminus = JS_TRUE;
660
661                 /* offset */
662 0                 if (n < 24)
663 0                     n = n * 60; /* EG. "GMT-3" */
664                 else
665 0                     n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
666 0                 if (prevc == '+')       /* plus means east of GMT */
667 0                     n = -n;
668 0                 if (tzoffset != 0 && tzoffset != -1)
669 0                     goto syntax;
670 0                 tzoffset = n;
671 0             } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
672 0                 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
673 0                     year = n;
674                 else
675 0                     goto syntax;
676 0             } else if (c == ':') {
677 0                 if (hour < 0)
678 0                     hour = /*byte*/ n;
679 0                 else if (min < 0)
680 0                     min = /*byte*/ n;
681                 else
682 0                     goto syntax;
683 0             } else if (c == '/') {
684                 /* until it is determined that mon is the actual
685                    month, keep it as 1-based rather than 0-based */
686 0                 if (mon < 0)
687 0                     mon = /*byte*/ n;
688 0                 else if (mday < 0)
689 0                     mday = /*byte*/ n;
690                 else
691 0                     goto syntax;
692 0             } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
693 0                 goto syntax;
694 0             } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */
695 0                 if (tzoffset < 0)
696 0                     tzoffset -= n;
697                 else
698 0                     tzoffset += n;
699 0             } else if (hour >= 0 && min < 0) {
700 0                 min = /*byte*/ n;
701 0             } else if (prevc == ':' && min >= 0 && sec < 0) {
702 0                 sec = /*byte*/ n;
703 0             } else if (mon < 0) {
704 0                 mon = /*byte*/n;
705 0             } else if (mon >= 0 && mday < 0) {
706 0                 mday = /*byte*/ n;
707 0             } else if (mon >= 0 && mday >= 0 && year < 0) {
708 0                 year = n;
709             } else {
710 0                 goto syntax;
711             }
712 0             prevc = 0;
713 0         } else if (c == '/' || c == ':' || c == '+' || c == '-') {
714 0             prevc = c;
715         } else {
716 0             size_t st = i - 1;
717 0             int k;
718 0             while (i < limit) {
719 0                 c = s[i];
720 0                 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
721 0                     break;
722 0                 i++;
723             }
724 0             if (i <= st + 1)
725 0                 goto syntax;
726 0             for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
727 0                 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
728 0                     int action = ttb[k];
729 0                     if (action != 0) {
730 0                         if (action < 0) {
731                             /*
732                              * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
733                              * 12:30, instead of blindly adding 12 if PM.
734                              */
735 0                             JS_ASSERT(action == -1 || action == -2);
736 0                             if (hour > 12 || hour < 0) {
737 0                                 goto syntax;
738                             } else {
739 0                                 if (action == -1 && hour == 12) { /* am */
740 0                                     hour = 0;
741 0                                 } else if (action == -2 && hour != 12) { /* pm */
742 0                                     hour += 12;
743                                 }
744                             }
745 0                         } else if (action <= 13) { /* month! */
746                             /* Adjust mon to be 1-based until the final values
747                                for mon, mday and year are adjusted below */
748 0                             if (seenmonthname) {
749 0                                 goto syntax;
750                             }
751 0                             seenmonthname = JS_TRUE;
752 0                             temp = /*byte*/ (action - 2) + 1;
753
754 0                             if (mon < 0) {
755 0                                 mon = temp;
756 0                             } else if (mday < 0) {
757 0                                 mday = mon;
758 0                                 mon = temp;
759 0                             } else if (year < 0) {
760 0                                 year = mon;
761 0                                 mon = temp;
762                             } else {
763 0                                 goto syntax;
764                             }
765                         } else {
766 0                             tzoffset = action - 10000;
767                         }
768                     }
769                     break;
770                 }
771 0             if (k < 0)
772 0                 goto syntax;
773 0             prevc = 0;
774         }
775     }
776 0     if (year < 0 || mon < 0 || mday < 0)
777 0         goto syntax;
778     /*
779       Case 1. The input string contains an English month name.
780               The form of the string can be month f l, or f month l, or
781               f l month which each evaluate to the same date. 
782               If f and l are both greater than or equal to 70, or
783               both less than 70, the date is invalid.
784               The year is taken to be the greater of the values f, l.
785               If the year is greater than or equal to 70 and less than 100,
786               it is considered to be the number of years after 1900.
787       Case 2. The input string is of the form "f/m/l" where f, m and l are
788               integers, e.g. 7/16/45.
789               Adjust the mon, mday and year values to achieve 100% MSIE 
790               compatibility.
791               a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
792                  i.  If year < 100, it is the number of years after 1900
793                  ii. If year >= 100, it is the number of years after 0.
794               b. If 70 <= f < 100
795                  i.  If m < 70, f/m/l is interpreted as
796                      year/month/day where year is the number of years after
797                      1900.
798                  ii. If m >= 70, the date is invalid.
799               c. If f >= 100
800                  i.  If m < 70, f/m/l is interpreted as
801                      year/month/day where year is the number of years after 0.
802                  ii. If m >= 70, the date is invalid.
803     */
804 0     if (seenmonthname) {
805 0         if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
806 0             goto syntax;
807         }
808 0         if (mday > year) {
809 0             temp = year;
810 0             year = mday;
811 0             mday = temp;
812         }
813 0         if (year >= 70 && year < 100) {
814 0             year += 1900;
815         }
816 0     } else if (mon < 70) { /* (a) month/day/year */
817 0         if (year < 100) {
818 0             year += 1900;
819         }
820 0     } else if (mon < 100) { /* (b) year/month/day */
821 0         if (mday < 70) { 
822 0             temp = year;
823 0             year = mon + 1900;
824 0             mon = mday;
825 0             mday = temp;
826         } else {
827 0             goto syntax;
828         }
829     } else { /* (c) year/month/day */
830 0         if (mday < 70) {
831 0             temp = year;
832 0             year = mon;
833 0             mon = mday;
834 0             mday = temp;
835         } else {
836 0             goto syntax;
837         }
838     }
839 0     mon -= 1; /* convert month to 0-based */
840 0     if (sec < 0)
841 0         sec = 0;
842 0     if (min < 0)
843 0         min = 0;
844 0     if (hour < 0)
845 0         hour = 0;
846 0     if (tzoffset == -1) { /* no time zone specified, have to use local */
847 0         jsdouble msec_time;
848 0         msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
849
850 0         *result = UTC(msec_time);
851 0         return JS_TRUE;
852     }
853
854 0     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
855 0     msec += tzoffset * msPerMinute;
856 0     *result = msec;
857 0     return JS_TRUE;
858
859 syntax:
860     /* syntax error */
861 0     *result = 0;
862 0     return JS_FALSE;
863 }
864
865 static JSBool
866 date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
867 0 {
868 0     JSString *str;
869 0     jsdouble result;
870
871 0     str = js_ValueToString(cx, argv[0]);
872 0     if (!str)
873 0         return JS_FALSE;
874 0     if (!date_parseString(str, &result)) {
875 0         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
876 0         return JS_TRUE;
877     }
878
879 0     result = TIMECLIP(result);
880 0     return js_NewNumberValue(cx, result, rval);
881 }
882
883 static JSBool
884 date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
885 0 {
886 0     int64 us, ms, us2ms;
887 0     jsdouble msec_time;
888
889 0     us = PRMJ_Now();
890 0     JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
891 0     JSLL_DIV(ms, us, us2ms);
892 0     JSLL_L2D(msec_time, ms);
893
894 0     return js_NewDoubleValue(cx, msec_time, rval);
895 }
896
897 /*
898  * Check that obj is an object of class Date, and get the date value.
899  * Return NULL on failure.
900  */
901 static jsdouble *
902 date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)
903 0 {
904 0     if (!JS_InstanceOf(cx, obj, &date_class, argv))
905 0         return NULL;
906 0     return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
907 }
908
909 /*
910  * See ECMA 15.9.5.4 thru 15.9.5.23
911  */
912 static JSBool
913 date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
914 0 {
915 0     jsdouble *date = date_getProlog(cx, obj, argv);
916 0     if (!date)
917 0         return JS_FALSE;
918
919 0     return js_NewNumberValue(cx, *date, rval);
920 }
921
922 static JSBool
923 date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
924 0 {
925 0     jsdouble *date;
926 0     jsdouble result;
927 0     JSVersion version;
928
929 0     date = date_getProlog(cx, obj, argv);
930 0     if (!date)
931 0         return JS_FALSE;
932
933 0     result = *date;
934 0     if (!JSDOUBLE_IS_FINITE(result))
935 0         return js_NewNumberValue(cx, result, rval);
936
937 0     result = YearFromTime(LocalTime(result));
938
939     /*
940      * During the great date rewrite of 1.3, we tried to track the evolving ECMA
941      * standard, which then had a definition of getYear which always subtracted
942      * 1900.  Which we implemented, not realizing that it was incompatible with
943      * the old behavior...  now, rather than thrash the behavior yet again,
944      * we've decided to leave it with the - 1900 behavior and point people to
945      * the getFullYear method.  But we try to protect existing scripts that
946      * have specified a version...
947      */
948 0     version = cx->version & JSVERSION_MASK;
949 0     if (version == JSVERSION_1_0 ||
950         version == JSVERSION_1_1 ||
951         version == JSVERSION_1_2)
952     {
953 0         if (result >= 1900 && result < 2000)
954 0             result -= 1900;
955     } else {
956 0         result -= 1900;
957     }
958 0     return js_NewNumberValue(cx, result, rval);
959 }
960
961 static JSBool
962 date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
963                  jsval *rval)
964 0 {
965 0     jsdouble result;
966 0     jsdouble *date = date_getProlog(cx, obj, argv);
967 0     if (!date)
968 0         return JS_FALSE;
969 0     result = *date;
970
971 0     if (!JSDOUBLE_IS_FINITE(result))
972 0         return js_NewNumberValue(cx, result, rval);
973
974 0     result = YearFromTime(LocalTime(result));
975 0     return js_NewNumberValue(cx, result, rval);
976 }
977
978 static JSBool
979 date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
980                     jsval *rval)
981 0 {
982 0     jsdouble result;
983 0     jsdouble *date = date_getProlog(cx, obj, argv);
984 0     if (!date)
985 0         return JS_FALSE;
986 0     result = *date;
987
988 0     if (!JSDOUBLE_IS_FINITE(result))
989 0         return js_NewNumberValue(cx, result, rval);
990
991 0     result = YearFromTime(result);
992 0     return js_NewNumberValue(cx, result, rval);
993 }
994
995 static JSBool
996 date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
997               jsval *rval)
998 0 {
999 0     jsdouble result;
1000 0     jsdouble *date = date_getProlog(cx, obj, argv);
1001 0     if (!date)
1002 0         return JS_FALSE;
1003 0     result = *date;
1004
1005 0     if (!JSDOUBLE_IS_FINITE(result))
1006 0         return js_NewNumberValue(cx, result, rval);
1007
1008 0     result = MonthFromTime(LocalTime(result));
1009 0     return js_NewNumberValue(cx, result, rval);
1010 }
1011
1012 static JSBool
1013 date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1014                  jsval *rval)
1015 0 {
1016 0     jsdouble result;
1017 0     jsdouble *date = date_getProlog(cx, obj, argv);
1018 0     if (!date)
1019 0         return JS_FALSE;
1020 0     result = *date;
1021
1022 0     if (!JSDOUBLE_IS_FINITE(result))
1023 0         return js_NewNumberValue(cx, result, rval);
1024
1025 0     result = MonthFromTime(result);
1026 0     return js_NewNumberValue(cx, result, rval);
1027 }
1028
1029 static JSBool
1030 date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1031 0 {
1032 0     jsdouble result;
1033 0     jsdouble *date = date_getProlog(cx, obj, argv);
1034 0     if (!date)
1035 0         return JS_FALSE;
1036 0     result = *date;
1037
1038 0     if (!JSDOUBLE_IS_FINITE(result))
1039 0         return js_NewNumberValue(cx, result, rval);
1040
1041 0     result = LocalTime(result);
1042 0     result = DateFromTime(result);
1043 0     return js_NewNumberValue(cx, result, rval);
1044 }
1045
1046 static JSBool
1047 date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1048                 jsval *rval)
1049 0 {
1050 0     jsdouble result;
1051 0     jsdouble *date = date_getProlog(cx, obj, argv);
1052 0     if (!date)
1053 0         return JS_FALSE;
1054 0     result = *date;
1055
1056 0     if (!JSDOUBLE_IS_FINITE(result))
1057 0         return js_NewNumberValue(cx, result, rval);
1058
1059 0     result = DateFromTime(result);
1060 0     return js_NewNumberValue(cx, result, rval);
1061 }
1062
1063 static JSBool
1064 date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1065 0 {
1066 0     jsdouble result;
1067 0     jsdouble *date = date_getProlog(cx, obj, argv);
1068 0     if (!date)
1069 0         return JS_FALSE;
1070 0     result = *date;
1071
1072 0     if (!JSDOUBLE_IS_FINITE(result))
1073 0         return js_NewNumberValue(cx, result, rval);
1074
1075 0     result = LocalTime(result);
1076 0     result = WeekDay(result);
1077 0     return js_NewNumberValue(cx, result, rval);
1078 }
1079
1080 static JSBool
1081 date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1082                jsval *rval)
1083 0 {
1084 0     jsdouble result;
1085 0     jsdouble *date = date_getProlog(cx, obj, argv);
1086 0     if (!date)
1087 0         return JS_FALSE;
1088 0     result = *date;
1089
1090 0     if (!JSDOUBLE_IS_FINITE(result))
1091 0         return js_NewNumberValue(cx, result, rval);
1092
1093 0     result = WeekDay(result);
1094 0     return js_NewNumberValue(cx, result, rval);
1095 }
1096
1097 static JSBool
1098 date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1099               jsval *rval)
1100 0 {
1101 0     jsdouble result;
1102 0     jsdouble *date = date_getProlog(cx, obj, argv);
1103 0     if (!date)
1104 0         return JS_FALSE;
1105 0     result = *date;
1106
1107 0     if (!JSDOUBLE_IS_FINITE(result))
1108 0         return js_NewNumberValue(cx, result, rval);
1109
1110 0     result = HourFromTime(LocalTime(result));
1111 0     return js_NewNumberValue(cx, result, rval);
1112 }
1113
1114 static JSBool
1115 date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1116                  jsval *rval)
1117 0 {
1118 0     jsdouble result;
1119 0     jsdouble *date = date_getProlog(cx, obj, argv);
1120 0     if (!date)
1121 0         return JS_FALSE;
1122 0     result = *date;
1123
1124 0     if (!JSDOUBLE_IS_FINITE(result))
1125 0         return js_NewNumberValue(cx, result, rval);
1126
1127 0     result = HourFromTime(result);
1128 0     return js_NewNumberValue(cx, result, rval);
1129 }
1130
1131 static JSBool
1132 date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1133                 jsval *rval)
1134 0 {
1135 0     jsdouble result;
1136 0     jsdouble *date = date_getProlog(cx, obj, argv);
1137 0     if (!date)
1138 0         return JS_FALSE;
1139 0     result = *date;
1140
1141 0     if (!JSDOUBLE_IS_FINITE(result))
1142 0         return js_NewNumberValue(cx, result, rval);
1143
1144 0     result = MinFromTime(LocalTime(result));
1145 0     return js_NewNumberValue(cx, result, rval);
1146 }
1147
1148 static JSBool
1149 date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1150                    jsval *rval)
1151 0 {
1152 0     jsdouble result;
1153 0     jsdouble *date = date_getProlog(cx, obj, argv);
1154 0     if (!date)
1155 0         return JS_FALSE;
1156 0     result = *date;
1157
1158 0     if (!JSDOUBLE_IS_FINITE(result))
1159 0         return js_NewNumberValue(cx, result, rval);
1160
1161 0     result = MinFromTime(result);
1162 0     return js_NewNumberValue(cx, result, rval);
1163 }
1164
1165 /* Date.getSeconds is mapped to getUTCSeconds */
1166
1167 static JSBool
1168 date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1169                 jsval *rval)
1170 0 {
1171 0     jsdouble result;
1172 0     jsdouble *date = date_getProlog(cx, obj, argv);
1173 0     if (!date)
1174 0         return JS_FALSE;
1175 0     result = *date;
1176
1177 0     if (!JSDOUBLE_IS_FINITE(result))
1178 0         return js_NewNumberValue(cx, result, rval);
1179
1180 0     result = SecFromTime(result);
1181 0     return js_NewNumberValue(cx, result, rval);
1182 }
1183
1184 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1185
1186 static JSBool
1187 date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1188                      jsval *rval)
1189 0 {
1190 0     jsdouble result;
1191 0     jsdouble *date = date_getProlog(cx, obj, argv);
1192 0     if (!date)
1193 0         return JS_FALSE;
1194 0     result = *date;
1195
1196 0     if (!JSDOUBLE_IS_FINITE(result))
1197 0         return js_NewNumberValue(cx, result, rval);
1198
1199 0     result = msFromTime(result);
1200 0     return js_NewNumberValue(cx, result, rval);
1201 }
1202
1203 static JSBool
1204 date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1205                        jsval *rval)
1206 0 {
1207 0     jsdouble result;
1208 0     jsdouble *date = date_getProlog(cx, obj, argv);
1209 0     if (!date)
1210 0         return JS_FALSE;
1211 0     result = *date;
1212
1213     /*
1214      * Return the time zone offset in minutes for the current locale
1215      * that is appropriate for this time. This value would be a
1216      * constant except for daylight savings time.
1217      */
1218 0     result = (result - LocalTime(result)) / msPerMinute;
1219 0     return js_NewNumberValue(cx, result, rval);
1220 }
1221
1222 static JSBool
1223 date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1224 0 {
1225 0     jsdouble result;
1226 0     jsdouble *date = date_getProlog(cx, obj, argv);
1227 0     if (!date)
1228 0         return JS_FALSE;
1229
1230 0     if (!js_ValueToNumber(cx, argv[0], &result))
1231 0         return JS_FALSE;
1232
1233 0     result = TIMECLIP(result);
1234
1235 0     *date = result;
1236 0     return js_NewNumberValue(cx, result, rval);
1237 }
1238
1239 static JSBool
1240 date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1241               uintN maxargs, JSBool local, jsval *rval)
1242 0 {
1243 0     uintN i;
1244 0     jsdouble args[4], *argp, *stop;
1245 0     jsdouble hour, min, sec, msec;
1246 0     jsdouble lorutime; /* Local or UTC version of *date */
1247
1248 0     jsdouble msec_time;
1249 0     jsdouble result;
1250
1251 0     jsdouble *date = date_getProlog(cx, obj, argv);
1252 0     if (!date)
1253 0         return JS_FALSE;
1254
1255 0     result = *date;
1256
1257     /* just return NaN if the date is already NaN */
1258 0     if (!JSDOUBLE_IS_FINITE(result))
1259 0         return js_NewNumberValue(cx, result, rval);
1260
1261     /* Satisfy the ECMA rule that if a function is called with
1262      * fewer arguments than the specified formal arguments, the
1263      * remaining arguments are set to undefined.  Seems like all
1264      * the Date.setWhatever functions in ECMA are only varargs
1265      * beyond the first argument; this should be set to undefined
1266      * if it's not given.  This means that "d = new Date();
1267      * d.setMilliseconds()" returns NaN.  Blech.
1268      */
1269 0     if (argc == 0)
1270 0         argc = 1;   /* should be safe, because length of all setters is 1 */
1271 0     else if (argc > maxargs)
1272 0         argc = maxargs;  /* clamp argc */
1273
1274 0     for (i = 0; i < argc; i++) {
1275 0         if (!js_ValueToNumber(cx, argv[i], &args[i]))
1276 0             return JS_FALSE;
1277 0         if (!JSDOUBLE_IS_FINITE(args[i])) {
1278 0             *date = *cx->runtime->jsNaN;
1279 0             return js_NewNumberValue(cx, *date, rval);
1280         }
1281 0         args[i] = js_DoubleToInteger(args[i]);
1282     }
1283
1284 0     if (local)
1285 0         lorutime = LocalTime(result);
1286     else
1287 0         lorutime = result;
1288
1289 0     argp = args;
1290 0     stop = argp + argc;
1291 0     if (maxargs >= 4 && argp < stop)
1292 0         hour = *argp++;
1293     else
1294 0         hour = HourFromTime(lorutime);
1295
1296 0     if (maxargs >= 3 && argp < stop)
1297 0         min = *argp++;
1298     else
1299 0         min = MinFromTime(lorutime);
1300
1301 0     if (maxargs >= 2 && argp < stop)
1302 0         sec = *argp++;
1303     else
1304 0         sec = SecFromTime(lorutime);
1305
1306 0     if (maxargs >= 1 && argp < stop)
1307 0         msec = *argp;
1308     else
1309 0         msec = msFromTime(lorutime);
1310
1311 0     msec_time = MakeTime(hour, min, sec, msec);
1312 0     result = MakeDate(Day(lorutime), msec_time);
1313
1314 /*     fprintf(stderr, "%f\n", result); */
1315
1316 0     if (local)
1317 0         result = UTC(result);
1318
1319 /*     fprintf(stderr, "%f\n", result); */
1320
1321 0     *date = TIMECLIP(result);
1322 0     return js_NewNumberValue(cx, *date, rval);
1323 }
1324
1325 static JSBool
1326 date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1327                      jsval *argv, jsval *rval)
1328 0 {
1329 0     return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
1330 }
1331
1332 static JSBool
1333 date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1334                         jsval *argv, jsval *rval)
1335 0 {
1336 0     return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
1337 }
1338
1339 static JSBool
1340 date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
1341                 jsval *argv, jsval *rval)
1342 0 {
1343 0     return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
1344 }
1345
1346 static JSBool
1347 date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
1348                    jsval *argv, jsval *rval)
1349 0 {
1350 0     return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
1351 }
1352
1353 static JSBool
1354 date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
1355                 jsval *argv, jsval *rval)
1356 0 {
1357 0     return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
1358 }
1359
1360 static JSBool
1361 date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
1362                    jsval *argv, jsval *rval)
1363 0 {
1364 0     return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
1365 }
1366
1367 static JSBool
1368 date_setHours(JSContext *cx, JSObject *obj, uintN argc,
1369               jsval *argv, jsval *rval)
1370 0 {
1371 0     return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
1372 }
1373
1374 static JSBool
1375 date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
1376                  jsval *argv, jsval *rval)
1377 0 {
1378 0     return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
1379 }
1380
1381 static JSBool
1382 date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
1383               jsval *argv, uintN maxargs, JSBool local, jsval *rval)
1384 0 {
1385 0     uintN i;
1386 0     jsdouble lorutime; /* local or UTC version of *date */
1387 0     jsdouble args[3], *argp, *stop;
1388 0     jsdouble year, month, day;
1389 0     jsdouble result;
1390
1391 0     jsdouble *date = date_getProlog(cx, obj, argv);
1392 0     if (!date)
1393 0         return JS_FALSE;
1394
1395 0     result = *date;
1396
1397     /* see complaint about ECMA in date_MakeTime */
1398 0     if (argc == 0)
1399 0         argc = 1;   /* should be safe, because length of all setters is 1 */
1400 0     else if (argc > maxargs)
1401 0         argc = maxargs;   /* clamp argc */
1402
1403 0     for (i = 0; i < argc; i++) {
1404 0         if (!js_ValueToNumber(cx, argv[i], &args[i]))
1405 0             return JS_FALSE;
1406 0         if (!JSDOUBLE_IS_FINITE(args[i])) {
1407 0             *date = *cx->runtime->jsNaN;
1408 0             return js_NewNumberValue(cx, *date, rval);
1409         }
1410 0         args[i] = js_DoubleToInteger(args[i]);
1411     }
1412
1413     /* return NaN if date is NaN and we're not setting the year,
1414      * If we are, use 0 as the time. */
1415 0     if (!(JSDOUBLE_IS_FINITE(result))) {
1416 0         if (argc < 3)
1417 0             return js_NewNumberValue(cx, result, rval);
1418         else
1419 0             lorutime = +0.;
1420     } else {
1421 0         if (local)
1422 0             lorutime = LocalTime(result);
1423         else
1424 0             lorutime = result;
1425     }
1426
1427 0     argp = args;
1428 0     stop = argp + argc;
1429 0     if (maxargs >= 3 && argp < stop)
1430 0         year = *argp++;
1431     else
1432 0         year = YearFromTime(lorutime);
1433
1434 0     if (maxargs >= 2 && argp < stop)
1435 0         month = *argp++;
1436     else
1437 0         month = MonthFromTime(lorutime);
1438
1439 0     if (maxargs >= 1 && argp < stop)
1440 0         day = *argp++;
1441     else
1442 0         day = DateFromTime(lorutime);
1443
1444 0     day = MakeDay(year, month, day); /* day within year */
1445 0     result = MakeDate(day, TimeWithinDay(lorutime));
1446
1447 0     if (local)
1448 0         result = UTC(result);
1449
1450 0     *date = TIMECLIP(result);
1451 0     return js_NewNumberValue(cx, *date, rval);
1452 }
1453
1454 static JSBool
1455 date_setDate(JSContext *cx, JSObject *obj, uintN argc,
1456              jsval *argv, jsval *rval)
1457 0 {
1458 0     return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
1459 }
1460
1461 static JSBool
1462 date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
1463                 jsval *argv, jsval *rval)
1464 0 {
1465 0     return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
1466 }
1467
1468 static JSBool
1469 date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
1470               jsval *argv, jsval *rval)
1471 0 {
1472 0     return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
1473 }
1474
1475 static JSBool
1476 date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
1477                  jsval *argv, jsval *rval)
1478 0 {
1479 0     return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
1480 }
1481
1482 static JSBool
1483 date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
1484                  jsval *argv, jsval *rval)
1485 0 {
1486 0     return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
1487 }
1488
1489 static JSBool
1490 date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
1491                     jsval *argv, jsval *rval)
1492 0 {
1493 0     return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
1494 }
1495
1496 static JSBool
1497 date_setYear(JSContext *cx, JSObject *obj, uintN argc,
1498              jsval *argv, jsval *rval)
1499 0 {
1500 0     jsdouble t;
1501 0     jsdouble year;
1502 0     jsdouble day;
1503 0     jsdouble result;
1504
1505 0     jsdouble *date = date_getProlog(cx, obj, argv);
1506 0     if (!date)
1507 0         return JS_FALSE;
1508
1509 0     result = *date;
1510
1511 0     if (!js_ValueToNumber(cx, argv[0], &year))
1512 0         return JS_FALSE;
1513 0     if (!JSDOUBLE_IS_FINITE(year)) {
1514 0         *date = *cx->runtime->jsNaN;
1515 0         return js_NewNumberValue(cx, *date, rval);
1516     }
1517
1518 0     year = js_DoubleToInteger(year);
1519
1520 0     if (!JSDOUBLE_IS_FINITE(result)) {
1521 0         t = +0.0;
1522     } else {
1523 0         t = LocalTime(result);
1524     }
1525
1526 0     if (year >= 0 && year <= 99)
1527 0         year += 1900;
1528
1529 0     day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1530 0     result = MakeDate(day, TimeWithinDay(t));
1531 0     result = UTC(result);
1532
1533 0     *date = TIMECLIP(result);
1534 0     return js_NewNumberValue(cx, *date, rval);
1535 }
1536
1537 /* constants for toString, toUTCString */
1538 static char js_NaN_date_str[] = "Invalid Date";
1539 static const char* days[] =
1540 {
1541    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1542 };
1543 static const char* months[] =
1544 {
1545    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1546 };
1547
1548 static JSBool
1549 date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
1550                  jsval *argv, jsval *rval)
1551 0 {
1552 0     char buf[100];
1553 0     JSString *str;
1554 0     jsdouble *date = date_getProlog(cx, obj, argv);
1555 0     if (!date)
1556 0         return JS_FALSE;
1557
1558 0     if (!JSDOUBLE_IS_FINITE(*date)) {
1559 0         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1560     } else {
1561 0         jsdouble temp = *date;
1562
1563         /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1564          * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
1565          */
1566 0         JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1567                     days[WeekDay(temp)],
1568                     DateFromTime(temp),
1569                     months[MonthFromTime(temp)],
1570                     YearFromTime(temp),
1571                     HourFromTime(temp),
1572                     MinFromTime(temp),
1573                     SecFromTime(temp));
1574     }
1575 0     str = JS_NewStringCopyZ(cx, buf);
1576 0     if (!str)
1577 0         return JS_FALSE;
1578 0     *rval = STRING_TO_JSVAL(str);
1579 0     return JS_TRUE;
1580 }
1581
1582 /* for Date.toLocaleString; interface to PRMJTime date struct.
1583  * If findEquivalent is true, then try to map the year to an equivalent year
1584  * that's in range.
1585  */
1586 static void
1587 new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)
1588 0 {
1589 0     jsint year = YearFromTime(timeval);
1590 0     int16 adjustedYear;
1591
1592     /* If the year doesn't fit in a PRMJTime, find something to do about it. */
1593 0     if (year > 32767 || year < -32768) {
1594 0         if (findEquivalent) {
1595             /* We're really just trying to get a timezone string; map the year
1596              * to some equivalent year in the range 0 to 2800.  Borrowed from
1597              * A. D. Olsen.
1598              */
1599 0             jsint cycles;
1600 #define CYCLE_YEARS 2800L
1601 0             cycles = (year >= 0) ? year / CYCLE_YEARS
1602                                  : -1 - (-1 - year) / CYCLE_YEARS;
1603 0             adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
1604         } else {
1605             /* Clamp it to the nearest representable year. */
1606 0             adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
1607         }
1608     } else {
1609 0         adjustedYear = (int16)year;
1610     }
1611
1612 0     split->tm_usec = (int32) msFromTime(timeval) * 1000;
1613 0     split->tm_sec = (int8) SecFromTime(timeval);
1614 0     split->tm_min = (int8) MinFromTime(timeval);
1615 0     split->tm_hour = (int8) HourFromTime(timeval);
1616 0     split->tm_mday = (int8) DateFromTime(timeval);
1617 0     split->tm_mon = (int8) MonthFromTime(timeval);
1618 0     split->tm_wday = (int8) WeekDay(timeval);
1619 0     split->tm_year = (int16) adjustedYear;
1620 0     split->tm_yday = (int16) DayWithinYear(timeval, year);
1621
1622     /* not sure how this affects things, but it doesn't seem
1623        to matter. */
1624 0     split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1625 }
1626
1627 typedef enum formatspec {
1628     FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1629 } formatspec;
1630
1631 /* helper function */
1632 static JSBool
1633 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1634 0 {
1635 0     char buf[100];
1636 0     JSString *str;
1637 0     char tzbuf[100];
1638 0     JSBool usetz;
1639 0     size_t i, tzlen;
1640 0     PRMJTime split;
1641
1642 0     if (!JSDOUBLE_IS_FINITE(date)) {
1643 0         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1644     } else {
1645 0         jsdouble local = LocalTime(date);
1646
1647         /* offset from GMT in minutes.  The offset includes daylight savings,
1648            if it applies. */
1649 0         jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1650
1651         /* map 510 minutes to 0830 hours */
1652 0         intN offset = (minutes / 60) * 100 + minutes % 60;
1653
1654         /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1655          * printed as 'GMT-0800' rather than as 'PST' to avoid
1656          * operating-system dependence on strftime (which
1657          * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
1658          * PST as 'Pacific Standard Time.'  This way we always know
1659          * what we're getting, and can parse it if we produce it.
1660          * The OS TZA string is included as a comment.
1661          */
1662
1663         /* get a timezone string from the OS to include as a
1664            comment. */
1665 0         new_explode(date, &split, JS_TRUE);
1666 0         if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1667
1668             /* Decide whether to use the resulting timezone string.
1669              *
1670              * Reject it if it contains any non-ASCII, non-alphanumeric
1671              * characters.  It's then likely in some other character
1672              * encoding, and we probably won't display it correctly.
1673              */
1674 0             usetz = JS_TRUE;
1675 0             tzlen = strlen(tzbuf);
1676 0             if (tzlen > 100) {
1677 0                 usetz = JS_FALSE;
1678             } else {
1679 0                 for (i = 0; i < tzlen; i++) {
1680 0                     jschar c = tzbuf[i];
1681 0                     if (c > 127 ||
1682                         !(isalpha(c) || isdigit(c) ||
1683                           c == ' ' || c == '(' || c == ')')) {
1684 0                         usetz = JS_FALSE;
1685                     }
1686                 }
1687             }
1688
1689             /* Also reject it if it's not parenthesized or if it's '()'. */
1690 0             if (tzbuf[0] != '(' || tzbuf[1] == ')')
1691 0                 usetz = JS_FALSE;
1692         } else
1693 0             usetz = JS_FALSE;
1694
1695 0         switch (format) {
1696           case FORMATSPEC_FULL:
1697             /*
1698              * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1699              * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
1700              */
1701             /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1702 0             JS_snprintf(buf, sizeof buf,
1703                         "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1704                         days[WeekDay(local)],
1705                         months[MonthFromTime(local)],
1706                         DateFromTime(local),
1707                         YearFromTime(local),
1708                         HourFromTime(local),
1709                         MinFromTime(local),
1710                         SecFromTime(local),
1711                         offset,
1712                         usetz ? " " : "",
1713                         usetz ? tzbuf : "");
1714 0             break;
1715           case FORMATSPEC_DATE:
1716             /* Tue Oct 31 2000 */
1717 0             JS_snprintf(buf, sizeof buf,
1718                         "%s %s %.2d %.4d",
1719                         days[WeekDay(local)],
1720                         months[MonthFromTime(local)],
1721                         DateFromTime(local),
1722                         YearFromTime(local));
1723 0             break;
1724           case FORMATSPEC_TIME:
1725             /* 09:41:40 GMT-0800 (PST) */
1726 0             JS_snprintf(buf, sizeof buf,
1727                         "%.2d:%.2d:%.2d GMT%+.4d%s%s",
1728                         HourFromTime(local),
1729                         MinFromTime(local),
1730                         SecFromTime(local),
1731                         offset,
1732                         usetz ? " " : "",
1733                         usetz ? tzbuf : "");
1734             break;
1735         }
1736     }
1737
1738 0     str = JS_NewStringCopyZ(cx, buf);
1739 0     if (!str)
1740 0         return JS_FALSE;
1741 0     *rval = STRING_TO_JSVAL(str);
1742 0     return JS_TRUE;
1743 }
1744
1745 static JSBool
1746 date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,
1747                     jsval *argv, jsval *rval, char *format)
1748 0 {
1749 0     char buf[100];
1750 0     JSString *str;
1751 0     PRMJTime split;
1752 0     jsdouble *date = date_getProlog(cx, obj, argv);
1753 0     if (!date)
1754 0         return JS_FALSE;
1755
1756 0     if (!JSDOUBLE_IS_FINITE(*date)) {
1757 0         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1758     } else {
1759 0         intN result_len;
1760 0         jsdouble local = LocalTime(*date);
1761 0         new_explode(local, &split, JS_FALSE);
1762
1763         /* let PRMJTime format it.       */
1764 0         result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1765
1766         /* If it failed, default to toString. */
1767 0         if (result_len == 0)
1768 0             return date_format(cx, *date, FORMATSPEC_FULL, rval);
1769
1770         /* Hacked check against undesired 2-digit year 00/00/00 form. */
1771 0         if (buf[result_len - 3] == '/' &&
1772             isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) {
1773 0             JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
1774                         "%d", js_DateGetYear(cx, obj));
1775         }
1776
1777     }
1778
1779 0     if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
1780 0         return cx->localeCallbacks->localeToUnicode(cx, buf, rval);
1781
1782 0     str = JS_NewStringCopyZ(cx, buf);
1783 0     if (!str)
1784 0         return JS_FALSE;
1785 0     *rval = STRING_TO_JSVAL(str);
1786 0     return JS_TRUE;
1787 }
1788
1789 static JSBool
1790 date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
1791                     jsval *argv, jsval *rval)
1792 0 {
1793     /* Use '%#c' for windows, because '%c' is
1794      * backward-compatible and non-y2k with msvc; '%#c' requests that a
1795      * full year be used in the result string.
1796      */
1797 0     return date_toLocaleHelper(cx, obj, argc, argv, rval,
1798 #if defined(_WIN32) && !defined(__MWERKS__)
1799                                    "%#c"
1800 #else
1801                                    "%c"
1802 #endif
1803                                    );
1804 }
1805
1806 static JSBool
1807 date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,
1808                     jsval *argv, jsval *rval)
1809 0 {
1810     /* Use '%#x' for windows, because '%x' is
1811      * backward-compatible and non-y2k with msvc; '%#x' requests that a
1812      * full year be used in the result string.
1813      */
1814 0     return date_toLocaleHelper(cx, obj, argc, argv, rval,
1815 #if defined(_WIN32) && !defined(__MWERKS__)
1816                                    "%#x"
1817 #else
1818                                    "%x"
1819 #endif
1820                                    );
1821 }
1822
1823 static JSBool
1824 date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,
1825                         jsval *argv, jsval *rval)
1826 0 {
1827 0     return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");
1828 }
1829
1830 static JSBool
1831 date_toLocaleFormat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1832                     jsval *rval)
1833 0 {
1834 0     JSString *fmt;
1835
1836 0     if (argc == 0)
1837 0         return date_toLocaleString(cx, obj, argc, argv, rval);
1838
1839 0     fmt = JS_ValueToString(cx, argv[0]);
1840 0     if (!fmt)
1841 0         return JS_FALSE;
1842
1843 0     return date_toLocaleHelper(cx, obj, argc, argv, rval,
1844                                JS_GetStringBytes(fmt));
1845 }
1846
1847 static JSBool
1848 date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,
1849                   jsval *argv, jsval *rval)
1850 0 {
1851 0     jsdouble *date = date_getProlog(cx, obj, argv);
1852 0     if (!date)
1853 0         return JS_FALSE;
1854 0     return date_format(cx, *date, FORMATSPEC_TIME, rval);
1855 }
1856
1857 static JSBool
1858 date_toDateString(JSContext *cx, JSObject *obj, uintN argc,
1859                   jsval *argv, jsval *rval)
1860 0 {
1861 0     jsdouble *date = date_getProlog(cx, obj, argv);
1862 0     if (!date)
1863 0         return JS_FALSE;
1864 0     return date_format(cx, *date, FORMATSPEC_DATE, rval);
1865 }
1866
1867 #if JS_HAS_TOSOURCE
1868 #include <string.h>
1869 #include "jsdtoa.h"
1870
1871 static JSBool
1872 date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1873               jsval *rval)
1874 0 {
1875 0     jsdouble *date;
1876 0     char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1877 0     JSString *str;
1878
1879 0     date = date_getProlog(cx, obj, argv);
1880 0     if (!date)
1881 0         return JS_FALSE;
1882
1883 0     numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);
1884 0     if (!numStr) {
1885 0         JS_ReportOutOfMemory(cx);
1886 0         return JS_FALSE;
1887     }
1888
1889 0     bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr);
1890 0     if (!bytes) {
1891 0         JS_ReportOutOfMemory(cx);
1892 0         return JS_FALSE;
1893     }
1894
1895 0     str = JS_NewString(cx, bytes, strlen(bytes));
1896 0     if (!str) {
1897 0         free(bytes);
1898 0         return JS_FALSE;
1899     }
1900 0     *rval = STRING_TO_JSVAL(str);
1901 0     return JS_TRUE;
1902 }
1903 #endif
1904
1905 static JSBool
1906 date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1907               jsval *rval)
1908 0 {
1909 0     jsdouble *date = date_getProlog(cx, obj, argv);
1910 0     if (!date)
1911 0         return JS_FALSE;
1912 0     return date_format(cx, *date, FORMATSPEC_FULL, rval);
1913 }
1914
1915 #if JS_HAS_VALUEOF_HINT
1916 static JSBool
1917 date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1918              jsval *rval)
1919 0 {
1920     /* It is an error to call date_valueOf on a non-date object, but we don't
1921      * need to check for that explicitly here because every path calls
1922      * date_getProlog, which does the check.
1923      */
1924
1925     /* If called directly with no arguments, convert to a time number. */
1926 0     if (argc == 0)
1927 0         return date_getTime(cx, obj, argc, argv, rval);
1928
1929     /* Convert to number only if the hint was given, otherwise favor string. */
1930 0     if (argc == 1) {
1931 0         JSString *str, *str2;
1932
1933 0         str = js_ValueToString(cx, argv[0]);
1934 0         if (!str)
1935 0             return JS_FALSE;
1936 0         str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
1937 0         if (!js_CompareStrings(str, str2))
1938 0             return date_getTime(cx, obj, argc, argv, rval);
1939     }
1940 0     return date_toString(cx, obj, argc, argv, rval);
1941 }
1942 #else
1943 #define date_valueOf date_getTime
1944 #endif
1945
1946
1947 /*
1948  * creation and destruction
1949  */
1950
1951 static JSFunctionSpec date_static_methods[] = {
1952     {"UTC",               date_UTC,               MAXARGS,0,0 },
1953     {"parse",             date_parse,             1,0,0 },
1954     {"now",               date_now,               0,0,0 },
1955     {0,0,0,0,0}
1956 };
1957
1958 static JSFunctionSpec date_methods[] = {
1959     {"getTime",             date_getTime,           0,0,0 },
1960     {"getTimezoneOffset",   date_getTimezoneOffset, 0,0,0 },
1961     {"getYear",             date_getYear,           0,0,0 },
1962     {"getFullYear",         date_getFullYear,       0,0,0 },
1963     {"getUTCFullYear",      date_getUTCFullYear,    0,0,0 },
1964     {"getMonth",            date_getMonth,          0,0,0 },
1965     {"getUTCMonth",         date_getUTCMonth,       0,0,0 },
1966     {"getDate",             date_getDate,           0,0,0 },
1967     {"getUTCDate",          date_getUTCDate,        0,0,0 },
1968     {"getDay",              date_getDay,            0,0,0 },
1969     {"getUTCDay",           date_getUTCDay,         0,0,0 },
1970     {"getHours",            date_getHours,          0,0,0 },
1971     {"getUTCHours",         date_getUTCHours,       0,0,0 },
1972     {"getMinutes",          date_getMinutes,        0,0,0 },
1973     {"getUTCMinutes",       date_getUTCMinutes,     0,0,0 },
1974     {"getSeconds",          date_getUTCSeconds,     0,0,0 },
1975     {"getUTCSeconds",       date_getUTCSeconds,     0,0,0 },
1976     {"getMilliseconds",     date_getUTCMilliseconds,0,0,0 },
1977     {"getUTCMilliseconds",  date_getUTCMilliseconds,0,0,0 },
1978     {"setTime",             date_setTime,           1,0,0 },
1979     {"setYear",             date_setYear,           1,0,0 },
1980     {"setFullYear",         date_setFullYear,       3,0,0 },
1981     {"setUTCFullYear",      date_setUTCFullYear,    3,0,0 },
1982     {"setMonth",            date_setMonth,          2,0,0 },
1983     {"setUTCMonth",         date_setUTCMonth,       2,0,0 },
1984     {"setDate",             date_setDate,           1,0,0 },
1985     {"setUTCDate",          date_setUTCDate,        1,0,0 },
1986     {"setHours",            date_setHours,          4,0,0 },
1987     {"setUTCHours",         date_setUTCHours,       4,0,0 },
1988     {"setMinutes",          date_setMinutes,        3,0,0 },
1989     {"setUTCMinutes",       date_setUTCMinutes,     3,0,0 },
1990     {"setSeconds",          date_setSeconds,        2,0,0 },
1991     {"setUTCSeconds",       date_setUTCSeconds,     2,0,0 },
1992     {"setMilliseconds",     date_setMilliseconds,   1,0,0 },
1993     {"setUTCMilliseconds",  date_setUTCMilliseconds,1,0,0 },
1994     {"toUTCString",         date_toGMTString,       0,0,0 },
1995     {js_toLocaleString_str, date_toLocaleString,    0,0,0 },
1996     {"toLocaleDateString",  date_toLocaleDateString,0,0,0 },
1997     {"toLocaleTimeString",  date_toLocaleTimeString,0,0,0 },
1998     {"toLocaleFormat",      date_toLocaleFormat,    1,0,0 },
1999     {"toDateString",        date_toDateString,      0,0,0 },
2000     {"toTimeString",        date_toTimeString,      0,0,0 },
2001 #if JS_HAS_TOSOURCE
2002     {js_toSource_str,       date_toSource,          0,0,0 },
2003 #endif
2004     {js_toString_str,       date_toString,          0,0,0 },
2005     {js_valueOf_str,        date_valueOf,           0,0,0 },
2006     {0,0,0,0,0}
2007 };
2008
2009 static jsdouble *
2010 date_constructor(JSContext *cx, JSObject* obj)
2011 16 {
2012 16     jsdouble *date;
2013
2014 16     date = js_NewDouble(cx, 0.0, 0);
2015 16     if (!date)
2016 0         return NULL;
2017 16     OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
2018 16     return date;
2019 }
2020
2021 static JSBool
2022 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2023 0 {
2024 0     jsdouble *date;
2025 0     JSString *str;
2026 0     jsdouble d;
2027
2028     /* Date called as function. */
2029 0     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
2030 0         int64 us, ms, us2ms;
2031 0         jsdouble msec_time;
2032
2033         /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
2034          * so compute ms from PRMJ_Now.
2035          */
2036 0         us = PRMJ_Now();
2037 0         JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2038 0         JSLL_DIV(ms, us, us2ms);
2039 0         JSLL_L2D(msec_time, ms);
2040
2041 0         return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
2042     }
2043
2044     /* Date called as constructor. */
2045 0     if (argc == 0) {
2046 0         int64 us, ms, us2ms;
2047 0         jsdouble msec_time;
2048
2049 0         date = date_constructor(cx, obj);
2050 0         if (!date)
2051 0             return JS_FALSE;
2052
2053 0         us = PRMJ_Now();
2054 0         JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2055 0         JSLL_DIV(ms, us, us2ms);
2056 0         JSLL_L2D(msec_time, ms);
2057
2058 0         *date = msec_time;
2059 0     } else if (argc == 1) {
2060 0         if (!JSVAL_IS_STRING(argv[0])) {
2061             /* the argument is a millisecond number */
2062 0             if (!js_ValueToNumber(cx, argv[0], &d))
2063 0                     return JS_FALSE;
2064 0             date = date_constructor(cx, obj);
2065 0             if (!date)
2066 0                 return JS_FALSE;
2067 0             *date = TIMECLIP(d);
2068         } else {
2069             /* the argument is a string; parse it. */
2070 0             date = date_constructor(cx, obj);
2071 0             if (!date)
2072 0                 return JS_FALSE;
2073
2074 0             str = js_ValueToString(cx, argv[0]);
2075 0             if (!str)
2076 0                 return JS_FALSE;
2077
2078 0             if (!date_parseString(str, date))
2079 0                 *date = *cx->runtime->jsNaN;
2080 0             *date = TIMECLIP(*date);
2081         }
2082     } else {
2083 0         jsdouble array[MAXARGS];
2084 0         uintN loop;
2085 0         jsdouble double_arg;
2086 0         jsdouble day;
2087 0         jsdouble msec_time;
2088
2089 0         for (loop = 0; loop < MAXARGS; loop++) {
2090 0             if (loop < argc) {
2091 0                 if (!js_ValueToNumber(cx, argv[loop], &double_arg))
2092 0                     return JS_FALSE;
2093                 /* if any arg is NaN, make a NaN date object
2094                    and return */
2095 0                 if (!JSDOUBLE_IS_FINITE(double_arg)) {
2096 0                     date = date_constructor(cx, obj);
2097 0                     if (!date)
2098 0                         return JS_FALSE;
2099 0                     *date = *cx->runtime->jsNaN;
2100 0                     return JS_TRUE;
2101                 }
2102 0                 array[loop] = js_DoubleToInteger(double_arg);
2103             } else {
2104 0                 if (loop == 2) {
2105 0                     array[loop] = 1; /* Default the date argument to 1. */
2106                 } else {
2107 0                     array[loop] = 0;
2108                 }
2109             }
2110         }
2111
2112 0         date = date_constructor(cx, obj);
2113 0         if (!date)
2114 0             return JS_FALSE;
2115
2116         /* adjust 2-digit years into the 20th century */
2117 0         if (array[0] >= 0 && array[0] <= 99)
2118 0             array[0] += 1900;
2119
2120 0         day = MakeDay(array[0], array[1], array[2]);
2121 0         msec_time = MakeTime(array[3], array[4], array[5], array[6]);
2122 0         msec_time = MakeDate(day, msec_time);
2123 0         msec_time = UTC(msec_time);
2124 0         *date = TIMECLIP(msec_time);
2125     }
2126 0     return JS_TRUE;
2127 }
2128
2129 JSObject *
2130 js_InitDateClass(JSContext *cx, JSObject *obj)
2131 16 {
2132 16     JSObject *proto;
2133 16     jsdouble *proto_date;
2134
2135     /* set static LocalTZA */
2136 16     LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2137 16     proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS,
2138                          NULL, date_methods, NULL, date_static_methods);
2139 16     if (!proto)
2140 0         return NULL;
2141
2142     /* Alias toUTCString with toGMTString.  (ECMA B.2.6) */
2143 16     if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2144 0         return NULL;
2145
2146     /* Set the value of the Date.prototype date to NaN */
2147 16     proto_date = date_constructor(cx, proto);
2148 16     if (!proto_date)
2149 0         return NULL;
2150 16     *proto_date = *cx->runtime->jsNaN;
2151
2152 16     return proto;
2153 }
2154
2155 JS_FRIEND_API(JSObject *)
2156 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2157 0 {
2158 0     JSObject *obj;
2159 0     jsdouble *date;
2160
2161 0     obj = js_NewObject(cx, &date_class, NULL, NULL);
2162 0     if (!obj)
2163 0         return NULL;
2164
2165 0     date = date_constructor(cx, obj);
2166 0     if (!date)
2167 0         return NULL;
2168
2169 0     *date = msec_time;
2170 0     return obj;
2171 }
2172
2173 JS_FRIEND_API(JSObject *)
2174 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2175                  int hour, int min, int sec)
2176 0 {
2177 0     JSObject *obj;
2178 0     jsdouble msec_time;
2179
2180 0     msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2181 0     obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2182 0     return obj;
2183 }
2184
2185 JS_FRIEND_API(JSBool)
2186 js_DateIsValid(JSContext *cx, JSObject* obj)
2187 0 {
2188 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2189
2190 0     if (!date || JSDOUBLE_IS_NaN(*date))
2191 0         return JS_FALSE;
2192     else
2193 0         return JS_TRUE;
2194 }
2195
2196 JS_FRIEND_API(int)
2197 js_DateGetYear(JSContext *cx, JSObject* obj)
2198 0 {
2199 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2200
2201     /* Preserve legacy API behavior of returning 0 for invalid dates. */
2202 0     if (!date || JSDOUBLE_IS_NaN(*date))
2203 0         return 0;
2204 0     return (int) YearFromTime(LocalTime(*date));
2205 }
2206
2207 JS_FRIEND_API(int)
2208 js_DateGetMonth(JSContext *cx, JSObject* obj)
2209 0 {
2210 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2211
2212 0     if (!date || JSDOUBLE_IS_NaN(*date))
2213 0         return 0;
2214 0     return (int) MonthFromTime(LocalTime(*date));
2215 }
2216
2217 JS_FRIEND_API(int)
2218 js_DateGetDate(JSContext *cx, JSObject* obj)
2219 0 {
2220 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2221
2222 0     if (!date || JSDOUBLE_IS_NaN(*date))
2223 0         return 0;
2224 0     return (int) DateFromTime(LocalTime(*date));
2225 }
2226
2227 JS_FRIEND_API(int)
2228 js_DateGetHours(JSContext *cx, JSObject* obj)
2229 0 {
2230 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2231
2232 0     if (!date || JSDOUBLE_IS_NaN(*date))
2233 0         return 0;
2234 0     return (int) HourFromTime(LocalTime(*date));
2235 }
2236
2237 JS_FRIEND_API(int)
2238 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2239 0 {
2240 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2241
2242 0     if (!date || JSDOUBLE_IS_NaN(*date))
2243 0         return 0;
2244 0     return (int) MinFromTime(LocalTime(*date));
2245 }
2246
2247 JS_FRIEND_API(int)
2248 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2249 0 {
2250 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2251
2252 0     if (!date || JSDOUBLE_IS_NaN(*date))
2253 0         return 0;
2254 0     return (int) SecFromTime(*date);
2255 }
2256
2257 JS_FRIEND_API(void)
2258 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2259 0 {
2260 0     jsdouble local;
2261 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2262 0     if (!date)
2263 0         return;
2264 0     local = LocalTime(*date);
2265     /* reset date if it was NaN */
2266 0     if (JSDOUBLE_IS_NaN(local))
2267 0         local = 0;
2268 0     local = date_msecFromDate(year,
2269                               MonthFromTime(local),
2270                               DateFromTime(local),
2271                               HourFromTime(local),
2272                               MinFromTime(local),
2273                               SecFromTime(local),
2274                               msFromTime(local));
2275 0     *date = UTC(local);
2276 }
2277
2278 JS_FRIEND_API(void)
2279 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2280 0 {
2281 0     jsdouble local;
2282 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2283 0     if (!date)
2284 0         return;
2285 0     local = LocalTime(*date);
2286     /* bail if date was NaN */
2287 0     if (JSDOUBLE_IS_NaN(local))
2288 0         return;
2289 0     local = date_msecFromDate(YearFromTime(local),
2290                               month,
2291                               DateFromTime(local),
2292                               HourFromTime(local),
2293                               MinFromTime(local),
2294                               SecFromTime(local),
2295                               msFromTime(local));
2296 0     *date = UTC(local);
2297 }
2298
2299 JS_FRIEND_API(void)
2300 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2301 0 {
2302 0     jsdouble local;
2303 0     jsdouble *datep = date_getProlog(cx, obj, NULL);
2304 0     if (!datep)
2305 0         return;
2306 0     local = LocalTime(*datep);
2307 0     if (JSDOUBLE_IS_NaN(local))
2308 0         return;
2309 0     local = date_msecFromDate(YearFromTime(local),
2310                               MonthFromTime(local),
2311                               date,
2312                               HourFromTime(local),
2313                               MinFromTime(local),
2314                               SecFromTime(local),
2315                               msFromTime(local));
2316 0     *datep = UTC(local);
2317 }
2318
2319 JS_FRIEND_API(void)
2320 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2321 0 {
2322 0     jsdouble local;
2323 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2324 0     if (!date)
2325 0         return;
2326 0     local = LocalTime(*date);
2327 0     if (JSDOUBLE_IS_NaN(local))
2328 0         return;
2329 0     local = date_msecFromDate(YearFromTime(local),
2330                               MonthFromTime(local),
2331                               DateFromTime(local),
2332                               hours,
2333                               MinFromTime(local),
2334                               SecFromTime(local),
2335                               msFromTime(local));
2336 0     *date = UTC(local);
2337 }
2338
2339 JS_FRIEND_API(void)
2340 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2341 0 {
2342 0     jsdouble local;
2343 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2344 0     if (!date)
2345 0         return;
2346 0     local = LocalTime(*date);
2347 0     if (JSDOUBLE_IS_NaN(local))
2348 0         return;
2349 0     local = date_msecFromDate(YearFromTime(local),
2350                               MonthFromTime(local),
2351                               DateFromTime(local),
2352                               HourFromTime(local),
2353                               minutes,
2354                               SecFromTime(local),
2355                               msFromTime(local));
2356 0     *date = UTC(local);
2357 }
2358
2359 JS_FRIEND_API(void)
2360 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2361 0 {
2362 0     jsdouble local;
2363 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2364 0     if (!date)
2365 0         return;
2366 0     local = LocalTime(*date);
2367 0     if (JSDOUBLE_IS_NaN(local))
2368 0         return;
2369 0     local = date_msecFromDate(YearFromTime(local),
2370                               MonthFromTime(local),
2371                               DateFromTime(local),
2372                               HourFromTime(local),
2373                               MinFromTime(local),
2374                               seconds,
2375                               msFromTime(local));
2376 0     *date = UTC(local);
2377 }
2378
2379 JS_FRIEND_API(jsdouble)
2380 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2381 0 {
2382 0     jsdouble *date = date_getProlog(cx, obj, NULL);
2383 0     if (!date || JSDOUBLE_IS_NaN(*date))
2384 0         return 0;
2385 0     return (*date);