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