Code

fix 1243587 and misc fixes
[inkscape.git] / src / dom / js / jsdate.c
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 ***** */
40 /*
41  * JS date methods.
42  */
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  */
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"
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  */
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  */
145 /*
146  * Supporting functions - ECMA 15.9.1.*
147  */
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
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
174 #define Day(t)          floor((t) / msPerDay)
176 static jsdouble
177 TimeWithinDay(jsdouble t)
179     jsdouble result;
180     result = fmod(t, msPerDay);
181     if (result < 0)
182         result += msPerDay;
183     return result;
186 #define DaysInYear(y)   ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0))  \
187                          ? 366 : 365)
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)
196 static jsint
197 YearFromTime(jsdouble t)
199     jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
200     jsdouble t2 = (jsdouble) TimeFromYear(y);
202     if (t2 > t) {
203         y--;
204     } else {
205         if (t2 + msPerDay * DaysInYear(y) <= t)
206             y++;
207     }
208     return y;
211 #define InLeapYear(t)   (JSBool) (DaysInYear(YearFromTime(t)) == 366)
213 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
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 };
224 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
226 static intN
227 MonthFromTime(jsdouble t)
229     intN d, step;
230     jsint year = YearFromTime(t);
231     d = DayWithinYear(t, year);
233     if (d < (step = 31))
234         return 0;
235     step += (InLeapYear(t) ? 29 : 28);
236     if (d < step)
237         return 1;
238     if (d < (step += 31))
239         return 2;
240     if (d < (step += 30))
241         return 3;
242     if (d < (step += 31))
243         return 4;
244     if (d < (step += 30))
245         return 5;
246     if (d < (step += 31))
247         return 6;
248     if (d < (step += 31))
249         return 7;
250     if (d < (step += 30))
251         return 8;
252     if (d < (step += 31))
253         return 9;
254     if (d < (step += 30))
255         return 10;
256     return 11;
259 static intN
260 DateFromTime(jsdouble t)
262     intN d, step, next;
263     jsint year = YearFromTime(t);
264     d = DayWithinYear(t, year);
266     if (d <= (next = 30))
267         return d + 1;
268     step = next;
269     next += (InLeapYear(t) ? 29 : 28);
270     if (d <= next)
271         return d - step;
272     step = next;
273     if (d <= (next += 31))
274         return d - step;
275     step = next;
276     if (d <= (next += 30))
277         return d - step;
278     step = next;
279     if (d <= (next += 31))
280         return d - step;
281     step = next;
282     if (d <= (next += 30))
283         return d - step;
284     step = next;
285     if (d <= (next += 31))
286         return d - step;
287     step = next;
288     if (d <= (next += 31))
289         return d - step;
290     step = next;
291     if (d <= (next += 30))
292         return d - step;
293     step = next;
294     if (d <= (next += 31))
295         return d - step;
296     step = next;
297     if (d <= (next += 30))
298         return d - step;
299     step = next;
300     return d - step;
303 static intN
304 WeekDay(jsdouble t)
306     jsint result;
307     result = (jsint) Day(t) + 4;
308     result = result % 7;
309     if (result < 0)
310         result += 7;
311     return (intN) result;
314 #define MakeTime(hour, min, sec, ms) \
315 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
317 static jsdouble
318 MakeDay(jsdouble year, jsdouble month, jsdouble date)
320     JSBool leap;
321     jsdouble yearday;
322     jsdouble monthday;
324     year += floor(month / 12);
326     month = fmod(month, 12.0);
327     if (month < 0)
328         month += 12;
330     leap = (DaysInYear((jsint) year) == 366);
332     yearday = floor(TimeFromYear(year) / msPerDay);
333     monthday = DayFromMonth(month, leap);
335     return yearday + monthday + date - 1;
338 #define MakeDate(day, time) ((day) * msPerDay + (time))
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 };
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 EquivalentYearForDST(jsint year) {
363     jsint day;
364     JSBool isLeapYear;
366     day = (jsint) DayFromYear(year) + 4;
367     day = day % 7;
368     if (day < 0)
369         day += 7;
371     isLeapYear = (DaysInYear(year) == 366);
373     return yearStartingWith[isLeapYear][day];
376 /* LocalTZA gets set by js_InitDateClass() */
377 static jsdouble LocalTZA;
379 static jsdouble
380 DaylightSavingTA(jsdouble t)
382     volatile int64 PR_t;
383     int64 ms2us;
384     int64 offset;
385     jsdouble result;
387     /* abort if NaN */
388     if (JSDOUBLE_IS_NaN(t))
389         return t;
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     if (t < 0.0 || t > 2145916800000.0) {
396         jsint year;
397         jsdouble day;
399         year = EquivalentYearForDST(YearFromTime(t));
400         day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
401         t = MakeDate(day, TimeWithinDay(t));
402     }
404     /* put our t in an LL, and map it to usec for prtime */
405     JSLL_D2L(PR_t, t);
406     JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
407     JSLL_MUL(PR_t, PR_t, ms2us);
409     offset = PRMJ_DSTOffset(PR_t);
411     JSLL_DIV(offset, offset, ms2us);
412     JSLL_L2D(result, offset);
413     return result;
417 #define AdjustTime(t)   fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
419 #define LocalTime(t)    ((t) + AdjustTime(t))
421 static jsdouble
422 UTC(jsdouble t)
424     return t - AdjustTime(t - LocalTZA);
427 static intN
428 HourFromTime(jsdouble t)
430     intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
431     if (result < 0)
432         result += (intN)HoursPerDay;
433     return result;
436 static intN
437 MinFromTime(jsdouble t)
439     intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
440     if (result < 0)
441         result += (intN)MinutesPerHour;
442     return result;
445 static intN
446 SecFromTime(jsdouble t)
448     intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
449     if (result < 0)
450         result += (intN)SecondsPerMinute;
451     return result;
454 static intN
455 msFromTime(jsdouble t)
457     intN result = (intN) fmod(t, msPerSecond);
458     if (result < 0)
459         result += (intN)msPerSecond;
460     return result;
463 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
464                       && !((d < 0 ? -d : d) > HalfTimeDomain)) \
465                      ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
467 /**
468  * end of ECMA 'support' functions
469  */
470 \f
471 /*
472  * Other Support routines and definitions
473  */
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 };
483 /* for use by date_parse */
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 };
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 };
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)
514     JSBool result = JS_FALSE;
515     /* return true if matches, otherwise, false */
517     while (count > 0 && s1[s1off] && s2[s2off]) {
518         if (ignoreCase) {
519             if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
520                 break;
521             }
522         } else {
523             if ((jschar)s1[s1off] != s2[s2off]) {
524                 break;
525             }
526         }
527         s1off++;
528         s2off++;
529         count--;
530     }
532     if (count == 0) {
533         result = JS_TRUE;
534     }
536     return result;
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)
544     jsdouble day;
545     jsdouble msec_time;
546     jsdouble result;
548     day = MakeDay(year, mon, mday);
549     msec_time = MakeTime(hour, min, sec, msec);
550     result = MakeDate(day, msec_time);
551     return result;
553 \f
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)
563     jsdouble array[MAXARGS];
564     uintN loop;
565     jsdouble d;
567     for (loop = 0; loop < MAXARGS; loop++) {
568         if (loop < argc) {
569             if (!js_ValueToNumber(cx, argv[loop], &d))
570                 return JS_FALSE;
571             /* return NaN if any arg is NaN */
572             if (!JSDOUBLE_IS_FINITE(d)) {
573                 return js_NewNumberValue(cx, d, rval);
574             }
575             array[loop] = floor(d);
576         } else {
577             array[loop] = 0;
578         }
579     }
581     /* adjust 2-digit years into the 20th century */
582     if (array[0] >= 0 && array[0] <= 99)
583         array[0] += 1900;
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     if (array[2] < 1)
588         array[2] = 1;
590     d = date_msecFromDate(array[0], array[1], array[2],
591                               array[3], array[4], array[5], array[6]);
592     d = TIMECLIP(d);
594     return js_NewNumberValue(cx, d, rval);
597 static JSBool
598 date_parseString(JSString *str, jsdouble *result)
600     jsdouble msec;
602     const jschar *s = JSSTRING_CHARS(str);
603     size_t limit = JSSTRING_LENGTH(str);
604     size_t i = 0;
605     int year = -1;
606     int mon = -1;
607     int mday = -1;
608     int hour = -1;
609     int min = -1;
610     int sec = -1;
611     int c = -1;
612     int n = -1;
613     jsdouble tzoffset = -1;  /* was an int, overflowed on win16!!! */
614     int prevc = 0;
615     JSBool seenplusminus = JS_FALSE;
616     int temp;
617     JSBool seenmonthname = JS_FALSE;
619     if (limit == 0)
620         goto syntax;
621     while (i < limit) {
622         c = s[i];
623         i++;
624         if (c <= ' ' || c == ',' || c == '-') {
625             if (c == '-' && '0' <= s[i] && s[i] <= '9') {
626               prevc = c;
627             }
628             continue;
629         }
630         if (c == '(') { /* comments) */
631             int depth = 1;
632             while (i < limit) {
633                 c = s[i];
634                 i++;
635                 if (c == '(') depth++;
636                 else if (c == ')')
637                     if (--depth <= 0)
638                         break;
639             }
640             continue;
641         }
642         if ('0' <= c && c <= '9') {
643             n = c - '0';
644             while (i < limit && '0' <= (c = s[i]) && c <= '9') {
645                 n = n * 10 + c - '0';
646                 i++;
647             }
649             /* allow TZA before the year, so
650              * 'Wed Nov 05 21:49:11 GMT-0800 1997'
651              * works */
653             /* uses of seenplusminus allow : in TZA, so Java
654              * no-timezone style of GMT+4:30 works
655              */
657             if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
658                 /* make ':' case below change tzoffset */
659                 seenplusminus = JS_TRUE;
661                 /* offset */
662                 if (n < 24)
663                     n = n * 60; /* EG. "GMT-3" */
664                 else
665                     n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
666                 if (prevc == '+')       /* plus means east of GMT */
667                     n = -n;
668                 if (tzoffset != 0 && tzoffset != -1)
669                     goto syntax;
670                 tzoffset = n;
671             } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
672                 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
673                     year = n;
674                 else
675                     goto syntax;
676             } else if (c == ':') {
677                 if (hour < 0)
678                     hour = /*byte*/ n;
679                 else if (min < 0)
680                     min = /*byte*/ n;
681                 else
682                     goto syntax;
683             } 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                 if (mon < 0)
687                     mon = /*byte*/ n;
688                 else if (mday < 0)
689                     mday = /*byte*/ n;
690                 else
691                     goto syntax;
692             } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
693                 goto syntax;
694             } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */
695                 if (tzoffset < 0)
696                     tzoffset -= n;
697                 else
698                     tzoffset += n;
699             } else if (hour >= 0 && min < 0) {
700                 min = /*byte*/ n;
701             } else if (prevc == ':' && min >= 0 && sec < 0) {
702                 sec = /*byte*/ n;
703             } else if (mon < 0) {
704                 mon = /*byte*/n;
705             } else if (mon >= 0 && mday < 0) {
706                 mday = /*byte*/ n;
707             } else if (mon >= 0 && mday >= 0 && year < 0) {
708                 year = n;
709             } else {
710                 goto syntax;
711             }
712             prevc = 0;
713         } else if (c == '/' || c == ':' || c == '+' || c == '-') {
714             prevc = c;
715         } else {
716             size_t st = i - 1;
717             int k;
718             while (i < limit) {
719                 c = s[i];
720                 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
721                     break;
722                 i++;
723             }
724             if (i <= st + 1)
725                 goto syntax;
726             for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
727                 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
728                     int action = ttb[k];
729                     if (action != 0) {
730                         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                             JS_ASSERT(action == -1 || action == -2);
736                             if (hour > 12 || hour < 0) {
737                                 goto syntax;
738                             } else {
739                                 if (action == -1 && hour == 12) { /* am */
740                                     hour = 0;
741                                 } else if (action == -2 && hour != 12) { /* pm */
742                                     hour += 12;
743                                 }
744                             }
745                         } 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                             if (seenmonthname) {
749                                 goto syntax;
750                             }
751                             seenmonthname = JS_TRUE;
752                             temp = /*byte*/ (action - 2) + 1;
754                             if (mon < 0) {
755                                 mon = temp;
756                             } else if (mday < 0) {
757                                 mday = mon;
758                                 mon = temp;
759                             } else if (year < 0) {
760                                 year = mon;
761                                 mon = temp;
762                             } else {
763                                 goto syntax;
764                             }
765                         } else {
766                             tzoffset = action - 10000;
767                         }
768                     }
769                     break;
770                 }
771             if (k < 0)
772                 goto syntax;
773             prevc = 0;
774         }
775     }
776     if (year < 0 || mon < 0 || mday < 0)
777         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     if (seenmonthname) {
805         if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
806             goto syntax;
807         }
808         if (mday > year) {
809             temp = year;
810             year = mday;
811             mday = temp;
812         }
813         if (year >= 70 && year < 100) {
814             year += 1900;
815         }
816     } else if (mon < 70) { /* (a) month/day/year */
817         if (year < 100) {
818             year += 1900;
819         }
820     } else if (mon < 100) { /* (b) year/month/day */
821         if (mday < 70) { 
822             temp = year;
823             year = mon + 1900;
824             mon = mday;
825             mday = temp;
826         } else {
827             goto syntax;
828         }
829     } else { /* (c) year/month/day */
830         if (mday < 70) {
831             temp = year;
832             year = mon;
833             mon = mday;
834             mday = temp;
835         } else {
836             goto syntax;
837         }
838     }
839     mon -= 1; /* convert month to 0-based */
840     if (sec < 0)
841         sec = 0;
842     if (min < 0)
843         min = 0;
844     if (hour < 0)
845         hour = 0;
846     if (tzoffset == -1) { /* no time zone specified, have to use local */
847         jsdouble msec_time;
848         msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
850         *result = UTC(msec_time);
851         return JS_TRUE;
852     }
854     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
855     msec += tzoffset * msPerMinute;
856     *result = msec;
857     return JS_TRUE;
859 syntax:
860     /* syntax error */
861     *result = 0;
862     return JS_FALSE;
865 static JSBool
866 date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
868     JSString *str;
869     jsdouble result;
871     str = js_ValueToString(cx, argv[0]);
872     if (!str)
873         return JS_FALSE;
874     if (!date_parseString(str, &result)) {
875         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
876         return JS_TRUE;
877     }
879     result = TIMECLIP(result);
880     return js_NewNumberValue(cx, result, rval);
883 static JSBool
884 date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
886     int64 us, ms, us2ms;
887     jsdouble msec_time;
889     us = PRMJ_Now();
890     JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
891     JSLL_DIV(ms, us, us2ms);
892     JSLL_L2D(msec_time, ms);
894     return js_NewDoubleValue(cx, msec_time, rval);
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)
904     if (!JS_InstanceOf(cx, obj, &date_class, argv))
905         return NULL;
906     return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
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)
915     jsdouble *date = date_getProlog(cx, obj, argv);
916     if (!date)
917         return JS_FALSE;
919     return js_NewNumberValue(cx, *date, rval);
922 static JSBool
923 date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
925     jsdouble *date;
926     jsdouble result;
927     JSVersion version;
929     date = date_getProlog(cx, obj, argv);
930     if (!date)
931         return JS_FALSE;
933     result = *date;
934     if (!JSDOUBLE_IS_FINITE(result))
935         return js_NewNumberValue(cx, result, rval);
937     result = YearFromTime(LocalTime(result));
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     version = cx->version & JSVERSION_MASK;
949     if (version == JSVERSION_1_0 ||
950         version == JSVERSION_1_1 ||
951         version == JSVERSION_1_2)
952     {
953         if (result >= 1900 && result < 2000)
954             result -= 1900;
955     } else {
956         result -= 1900;
957     }
958     return js_NewNumberValue(cx, result, rval);
961 static JSBool
962 date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
963                  jsval *rval)
965     jsdouble result;
966     jsdouble *date = date_getProlog(cx, obj, argv);
967     if (!date)
968         return JS_FALSE;
969     result = *date;
971     if (!JSDOUBLE_IS_FINITE(result))
972         return js_NewNumberValue(cx, result, rval);
974     result = YearFromTime(LocalTime(result));
975     return js_NewNumberValue(cx, result, rval);
978 static JSBool
979 date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
980                     jsval *rval)
982     jsdouble result;
983     jsdouble *date = date_getProlog(cx, obj, argv);
984     if (!date)
985         return JS_FALSE;
986     result = *date;
988     if (!JSDOUBLE_IS_FINITE(result))
989         return js_NewNumberValue(cx, result, rval);
991     result = YearFromTime(result);
992     return js_NewNumberValue(cx, result, rval);
995 static JSBool
996 date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
997               jsval *rval)
999     jsdouble result;
1000     jsdouble *date = date_getProlog(cx, obj, argv);
1001     if (!date)
1002         return JS_FALSE;
1003     result = *date;
1005     if (!JSDOUBLE_IS_FINITE(result))
1006         return js_NewNumberValue(cx, result, rval);
1008     result = MonthFromTime(LocalTime(result));
1009     return js_NewNumberValue(cx, result, rval);
1012 static JSBool
1013 date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1014                  jsval *rval)
1016     jsdouble result;
1017     jsdouble *date = date_getProlog(cx, obj, argv);
1018     if (!date)
1019         return JS_FALSE;
1020     result = *date;
1022     if (!JSDOUBLE_IS_FINITE(result))
1023         return js_NewNumberValue(cx, result, rval);
1025     result = MonthFromTime(result);
1026     return js_NewNumberValue(cx, result, rval);
1029 static JSBool
1030 date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1032     jsdouble result;
1033     jsdouble *date = date_getProlog(cx, obj, argv);
1034     if (!date)
1035         return JS_FALSE;
1036     result = *date;
1038     if (!JSDOUBLE_IS_FINITE(result))
1039         return js_NewNumberValue(cx, result, rval);
1041     result = LocalTime(result);
1042     result = DateFromTime(result);
1043     return js_NewNumberValue(cx, result, rval);
1046 static JSBool
1047 date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1048                 jsval *rval)
1050     jsdouble result;
1051     jsdouble *date = date_getProlog(cx, obj, argv);
1052     if (!date)
1053         return JS_FALSE;
1054     result = *date;
1056     if (!JSDOUBLE_IS_FINITE(result))
1057         return js_NewNumberValue(cx, result, rval);
1059     result = DateFromTime(result);
1060     return js_NewNumberValue(cx, result, rval);
1063 static JSBool
1064 date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1066     jsdouble result;
1067     jsdouble *date = date_getProlog(cx, obj, argv);
1068     if (!date)
1069         return JS_FALSE;
1070     result = *date;
1072     if (!JSDOUBLE_IS_FINITE(result))
1073         return js_NewNumberValue(cx, result, rval);
1075     result = LocalTime(result);
1076     result = WeekDay(result);
1077     return js_NewNumberValue(cx, result, rval);
1080 static JSBool
1081 date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1082                jsval *rval)
1084     jsdouble result;
1085     jsdouble *date = date_getProlog(cx, obj, argv);
1086     if (!date)
1087         return JS_FALSE;
1088     result = *date;
1090     if (!JSDOUBLE_IS_FINITE(result))
1091         return js_NewNumberValue(cx, result, rval);
1093     result = WeekDay(result);
1094     return js_NewNumberValue(cx, result, rval);
1097 static JSBool
1098 date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1099               jsval *rval)
1101     jsdouble result;
1102     jsdouble *date = date_getProlog(cx, obj, argv);
1103     if (!date)
1104         return JS_FALSE;
1105     result = *date;
1107     if (!JSDOUBLE_IS_FINITE(result))
1108         return js_NewNumberValue(cx, result, rval);
1110     result = HourFromTime(LocalTime(result));
1111     return js_NewNumberValue(cx, result, rval);
1114 static JSBool
1115 date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1116                  jsval *rval)
1118     jsdouble result;
1119     jsdouble *date = date_getProlog(cx, obj, argv);
1120     if (!date)
1121         return JS_FALSE;
1122     result = *date;
1124     if (!JSDOUBLE_IS_FINITE(result))
1125         return js_NewNumberValue(cx, result, rval);
1127     result = HourFromTime(result);
1128     return js_NewNumberValue(cx, result, rval);
1131 static JSBool
1132 date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1133                 jsval *rval)
1135     jsdouble result;
1136     jsdouble *date = date_getProlog(cx, obj, argv);
1137     if (!date)
1138         return JS_FALSE;
1139     result = *date;
1141     if (!JSDOUBLE_IS_FINITE(result))
1142         return js_NewNumberValue(cx, result, rval);
1144     result = MinFromTime(LocalTime(result));
1145     return js_NewNumberValue(cx, result, rval);
1148 static JSBool
1149 date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1150                    jsval *rval)
1152     jsdouble result;
1153     jsdouble *date = date_getProlog(cx, obj, argv);
1154     if (!date)
1155         return JS_FALSE;
1156     result = *date;
1158     if (!JSDOUBLE_IS_FINITE(result))
1159         return js_NewNumberValue(cx, result, rval);
1161     result = MinFromTime(result);
1162     return js_NewNumberValue(cx, result, rval);
1165 /* Date.getSeconds is mapped to getUTCSeconds */
1167 static JSBool
1168 date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1169                 jsval *rval)
1171     jsdouble result;
1172     jsdouble *date = date_getProlog(cx, obj, argv);
1173     if (!date)
1174         return JS_FALSE;
1175     result = *date;
1177     if (!JSDOUBLE_IS_FINITE(result))
1178         return js_NewNumberValue(cx, result, rval);
1180     result = SecFromTime(result);
1181     return js_NewNumberValue(cx, result, rval);
1184 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1186 static JSBool
1187 date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1188                      jsval *rval)
1190     jsdouble result;
1191     jsdouble *date = date_getProlog(cx, obj, argv);
1192     if (!date)
1193         return JS_FALSE;
1194     result = *date;
1196     if (!JSDOUBLE_IS_FINITE(result))
1197         return js_NewNumberValue(cx, result, rval);
1199     result = msFromTime(result);
1200     return js_NewNumberValue(cx, result, rval);
1203 static JSBool
1204 date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1205                        jsval *rval)
1207     jsdouble result;
1208     jsdouble *date = date_getProlog(cx, obj, argv);
1209     if (!date)
1210         return JS_FALSE;
1211     result = *date;
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     result = (result - LocalTime(result)) / msPerMinute;
1219     return js_NewNumberValue(cx, result, rval);
1222 static JSBool
1223 date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1225     jsdouble result;
1226     jsdouble *date = date_getProlog(cx, obj, argv);
1227     if (!date)
1228         return JS_FALSE;
1230     if (!js_ValueToNumber(cx, argv[0], &result))
1231         return JS_FALSE;
1233     result = TIMECLIP(result);
1235     *date = result;
1236     return js_NewNumberValue(cx, result, rval);
1239 static JSBool
1240 date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1241               uintN maxargs, JSBool local, jsval *rval)
1243     uintN i;
1244     jsdouble args[4], *argp, *stop;
1245     jsdouble hour, min, sec, msec;
1246     jsdouble lorutime; /* Local or UTC version of *date */
1248     jsdouble msec_time;
1249     jsdouble result;
1251     jsdouble *date = date_getProlog(cx, obj, argv);
1252     if (!date)
1253         return JS_FALSE;
1255     result = *date;
1257     /* just return NaN if the date is already NaN */
1258     if (!JSDOUBLE_IS_FINITE(result))
1259         return js_NewNumberValue(cx, result, rval);
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     if (argc == 0)
1270         argc = 1;   /* should be safe, because length of all setters is 1 */
1271     else if (argc > maxargs)
1272         argc = maxargs;  /* clamp argc */
1274     for (i = 0; i < argc; i++) {
1275         if (!js_ValueToNumber(cx, argv[i], &args[i]))
1276             return JS_FALSE;
1277         if (!JSDOUBLE_IS_FINITE(args[i])) {
1278             *date = *cx->runtime->jsNaN;
1279             return js_NewNumberValue(cx, *date, rval);
1280         }
1281         args[i] = js_DoubleToInteger(args[i]);
1282     }
1284     if (local)
1285         lorutime = LocalTime(result);
1286     else
1287         lorutime = result;
1289     argp = args;
1290     stop = argp + argc;
1291     if (maxargs >= 4 && argp < stop)
1292         hour = *argp++;
1293     else
1294         hour = HourFromTime(lorutime);
1296     if (maxargs >= 3 && argp < stop)
1297         min = *argp++;
1298     else
1299         min = MinFromTime(lorutime);
1301     if (maxargs >= 2 && argp < stop)
1302         sec = *argp++;
1303     else
1304         sec = SecFromTime(lorutime);
1306     if (maxargs >= 1 && argp < stop)
1307         msec = *argp;
1308     else
1309         msec = msFromTime(lorutime);
1311     msec_time = MakeTime(hour, min, sec, msec);
1312     result = MakeDate(Day(lorutime), msec_time);
1314 /*     fprintf(stderr, "%f\n", result); */
1316     if (local)
1317         result = UTC(result);
1319 /*     fprintf(stderr, "%f\n", result); */
1321     *date = TIMECLIP(result);
1322     return js_NewNumberValue(cx, *date, rval);
1325 static JSBool
1326 date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1327                      jsval *argv, jsval *rval)
1329     return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
1332 static JSBool
1333 date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1334                         jsval *argv, jsval *rval)
1336     return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
1339 static JSBool
1340 date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
1341                 jsval *argv, jsval *rval)
1343     return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
1346 static JSBool
1347 date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
1348                    jsval *argv, jsval *rval)
1350     return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
1353 static JSBool
1354 date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
1355                 jsval *argv, jsval *rval)
1357     return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
1360 static JSBool
1361 date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
1362                    jsval *argv, jsval *rval)
1364     return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
1367 static JSBool
1368 date_setHours(JSContext *cx, JSObject *obj, uintN argc,
1369               jsval *argv, jsval *rval)
1371     return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
1374 static JSBool
1375 date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
1376                  jsval *argv, jsval *rval)
1378     return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
1381 static JSBool
1382 date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
1383               jsval *argv, uintN maxargs, JSBool local, jsval *rval)
1385     uintN i;
1386     jsdouble lorutime; /* local or UTC version of *date */
1387     jsdouble args[3], *argp, *stop;
1388     jsdouble year, month, day;
1389     jsdouble result;
1391     jsdouble *date = date_getProlog(cx, obj, argv);
1392     if (!date)
1393         return JS_FALSE;
1395     result = *date;
1397     /* see complaint about ECMA in date_MakeTime */
1398     if (argc == 0)
1399         argc = 1;   /* should be safe, because length of all setters is 1 */
1400     else if (argc > maxargs)
1401         argc = maxargs;   /* clamp argc */
1403     for (i = 0; i < argc; i++) {
1404         if (!js_ValueToNumber(cx, argv[i], &args[i]))
1405             return JS_FALSE;
1406         if (!JSDOUBLE_IS_FINITE(args[i])) {
1407             *date = *cx->runtime->jsNaN;
1408             return js_NewNumberValue(cx, *date, rval);
1409         }
1410         args[i] = js_DoubleToInteger(args[i]);
1411     }
1413     /* return NaN if date is NaN and we're not setting the year,
1414      * If we are, use 0 as the time. */
1415     if (!(JSDOUBLE_IS_FINITE(result))) {
1416         if (argc < 3)
1417             return js_NewNumberValue(cx, result, rval);
1418         else
1419             lorutime = +0.;
1420     } else {
1421         if (local)
1422             lorutime = LocalTime(result);
1423         else
1424             lorutime = result;
1425     }
1427     argp = args;
1428     stop = argp + argc;
1429     if (maxargs >= 3 && argp < stop)
1430         year = *argp++;
1431     else
1432         year = YearFromTime(lorutime);
1434     if (maxargs >= 2 && argp < stop)
1435         month = *argp++;
1436     else
1437         month = MonthFromTime(lorutime);
1439     if (maxargs >= 1 && argp < stop)
1440         day = *argp++;
1441     else
1442         day = DateFromTime(lorutime);
1444     day = MakeDay(year, month, day); /* day within year */
1445     result = MakeDate(day, TimeWithinDay(lorutime));
1447     if (local)
1448         result = UTC(result);
1450     *date = TIMECLIP(result);
1451     return js_NewNumberValue(cx, *date, rval);
1454 static JSBool
1455 date_setDate(JSContext *cx, JSObject *obj, uintN argc,
1456              jsval *argv, jsval *rval)
1458     return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
1461 static JSBool
1462 date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
1463                 jsval *argv, jsval *rval)
1465     return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
1468 static JSBool
1469 date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
1470               jsval *argv, jsval *rval)
1472     return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
1475 static JSBool
1476 date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
1477                  jsval *argv, jsval *rval)
1479     return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
1482 static JSBool
1483 date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
1484                  jsval *argv, jsval *rval)
1486     return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
1489 static JSBool
1490 date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
1491                     jsval *argv, jsval *rval)
1493     return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
1496 static JSBool
1497 date_setYear(JSContext *cx, JSObject *obj, uintN argc,
1498              jsval *argv, jsval *rval)
1500     jsdouble t;
1501     jsdouble year;
1502     jsdouble day;
1503     jsdouble result;
1505     jsdouble *date = date_getProlog(cx, obj, argv);
1506     if (!date)
1507         return JS_FALSE;
1509     result = *date;
1511     if (!js_ValueToNumber(cx, argv[0], &year))
1512         return JS_FALSE;
1513     if (!JSDOUBLE_IS_FINITE(year)) {
1514         *date = *cx->runtime->jsNaN;
1515         return js_NewNumberValue(cx, *date, rval);
1516     }
1518     year = js_DoubleToInteger(year);
1520     if (!JSDOUBLE_IS_FINITE(result)) {
1521         t = +0.0;
1522     } else {
1523         t = LocalTime(result);
1524     }
1526     if (year >= 0 && year <= 99)
1527         year += 1900;
1529     day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1530     result = MakeDate(day, TimeWithinDay(t));
1531     result = UTC(result);
1533     *date = TIMECLIP(result);
1534     return js_NewNumberValue(cx, *date, rval);
1537 /* constants for toString, toUTCString */
1538 static char js_NaN_date_str[] = "Invalid Date";
1539 static const char* days[] =
1541    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1542 };
1543 static const char* months[] =
1545    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1546 };
1548 static JSBool
1549 date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
1550                  jsval *argv, jsval *rval)
1552     char buf[100];
1553     JSString *str;
1554     jsdouble *date = date_getProlog(cx, obj, argv);
1555     if (!date)
1556         return JS_FALSE;
1558     if (!JSDOUBLE_IS_FINITE(*date)) {
1559         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1560     } else {
1561         jsdouble temp = *date;
1563         /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1564          * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
1565          */
1566         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     str = JS_NewStringCopyZ(cx, buf);
1576     if (!str)
1577         return JS_FALSE;
1578     *rval = STRING_TO_JSVAL(str);
1579     return JS_TRUE;
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)
1589     jsint year = YearFromTime(timeval);
1590     int16 adjustedYear;
1592     /* If the year doesn't fit in a PRMJTime, find something to do about it. */
1593     if (year > 32767 || year < -32768) {
1594         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             jsint cycles;
1600 #define CYCLE_YEARS 2800L
1601             cycles = (year >= 0) ? year / CYCLE_YEARS
1602                                  : -1 - (-1 - year) / CYCLE_YEARS;
1603             adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
1604         } else {
1605             /* Clamp it to the nearest representable year. */
1606             adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
1607         }
1608     } else {
1609         adjustedYear = (int16)year;
1610     }
1612     split->tm_usec = (int32) msFromTime(timeval) * 1000;
1613     split->tm_sec = (int8) SecFromTime(timeval);
1614     split->tm_min = (int8) MinFromTime(timeval);
1615     split->tm_hour = (int8) HourFromTime(timeval);
1616     split->tm_mday = (int8) DateFromTime(timeval);
1617     split->tm_mon = (int8) MonthFromTime(timeval);
1618     split->tm_wday = (int8) WeekDay(timeval);
1619     split->tm_year = (int16) adjustedYear;
1620     split->tm_yday = (int16) DayWithinYear(timeval, year);
1622     /* not sure how this affects things, but it doesn't seem
1623        to matter. */
1624     split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1627 typedef enum formatspec {
1628     FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1629 } formatspec;
1631 /* helper function */
1632 static JSBool
1633 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1635     char buf[100];
1636     JSString *str;
1637     char tzbuf[100];
1638     JSBool usetz;
1639     size_t i, tzlen;
1640     PRMJTime split;
1642     if (!JSDOUBLE_IS_FINITE(date)) {
1643         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1644     } else {
1645         jsdouble local = LocalTime(date);
1647         /* offset from GMT in minutes.  The offset includes daylight savings,
1648            if it applies. */
1649         jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1651         /* map 510 minutes to 0830 hours */
1652         intN offset = (minutes / 60) * 100 + minutes % 60;
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          */
1663         /* get a timezone string from the OS to include as a
1664            comment. */
1665         new_explode(date, &split, JS_TRUE);
1666         if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
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             usetz = JS_TRUE;
1675             tzlen = strlen(tzbuf);
1676             if (tzlen > 100) {
1677                 usetz = JS_FALSE;
1678             } else {
1679                 for (i = 0; i < tzlen; i++) {
1680                     jschar c = tzbuf[i];
1681                     if (c > 127 ||
1682                         !(isalpha(c) || isdigit(c) ||
1683                           c == ' ' || c == '(' || c == ')')) {
1684                         usetz = JS_FALSE;
1685                     }
1686                 }
1687             }
1689             /* Also reject it if it's not parenthesized or if it's '()'. */
1690             if (tzbuf[0] != '(' || tzbuf[1] == ')')
1691                 usetz = JS_FALSE;
1692         } else
1693             usetz = JS_FALSE;
1695         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             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             break;
1715           case FORMATSPEC_DATE:
1716             /* Tue Oct 31 2000 */
1717             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             break;
1724           case FORMATSPEC_TIME:
1725             /* 09:41:40 GMT-0800 (PST) */
1726             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     }
1738     str = JS_NewStringCopyZ(cx, buf);
1739     if (!str)
1740         return JS_FALSE;
1741     *rval = STRING_TO_JSVAL(str);
1742     return JS_TRUE;
1745 static JSBool
1746 date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,
1747                     jsval *argv, jsval *rval, char *format)
1749     char buf[100];
1750     JSString *str;
1751     PRMJTime split;
1752     jsdouble *date = date_getProlog(cx, obj, argv);
1753     if (!date)
1754         return JS_FALSE;
1756     if (!JSDOUBLE_IS_FINITE(*date)) {
1757         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1758     } else {
1759         intN result_len;
1760         jsdouble local = LocalTime(*date);
1761         new_explode(local, &split, JS_FALSE);
1763         /* let PRMJTime format it.       */
1764         result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1766         /* If it failed, default to toString. */
1767         if (result_len == 0)
1768             return date_format(cx, *date, FORMATSPEC_FULL, rval);
1770         /* Hacked check against undesired 2-digit year 00/00/00 form. */
1771         if (buf[result_len - 3] == '/' &&
1772             isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) {
1773             JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
1774                         "%d", js_DateGetYear(cx, obj));
1775         }
1777     }
1779     if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
1780         return cx->localeCallbacks->localeToUnicode(cx, buf, rval);
1782     str = JS_NewStringCopyZ(cx, buf);
1783     if (!str)
1784         return JS_FALSE;
1785     *rval = STRING_TO_JSVAL(str);
1786     return JS_TRUE;
1789 static JSBool
1790 date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
1791                     jsval *argv, jsval *rval)
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     return date_toLocaleHelper(cx, obj, argc, argv, rval,
1798 #if defined(_WIN32) && !defined(__MWERKS__)
1799                                    "%#c"
1800 #else
1801                                    "%c"
1802 #endif
1803                                    );
1806 static JSBool
1807 date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,
1808                     jsval *argv, jsval *rval)
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     return date_toLocaleHelper(cx, obj, argc, argv, rval,
1815 #if defined(_WIN32) && !defined(__MWERKS__)
1816                                    "%#x"
1817 #else
1818                                    "%x"
1819 #endif
1820                                    );
1823 static JSBool
1824 date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,
1825                         jsval *argv, jsval *rval)
1827     return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");
1830 static JSBool
1831 date_toLocaleFormat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1832                     jsval *rval)
1834     JSString *fmt;
1836     if (argc == 0)
1837         return date_toLocaleString(cx, obj, argc, argv, rval);
1839     fmt = JS_ValueToString(cx, argv[0]);
1840     if (!fmt)
1841         return JS_FALSE;
1843     return date_toLocaleHelper(cx, obj, argc, argv, rval,
1844                                JS_GetStringBytes(fmt));
1847 static JSBool
1848 date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,
1849                   jsval *argv, jsval *rval)
1851     jsdouble *date = date_getProlog(cx, obj, argv);
1852     if (!date)
1853         return JS_FALSE;
1854     return date_format(cx, *date, FORMATSPEC_TIME, rval);
1857 static JSBool
1858 date_toDateString(JSContext *cx, JSObject *obj, uintN argc,
1859                   jsval *argv, jsval *rval)
1861     jsdouble *date = date_getProlog(cx, obj, argv);
1862     if (!date)
1863         return JS_FALSE;
1864     return date_format(cx, *date, FORMATSPEC_DATE, rval);
1867 #if JS_HAS_TOSOURCE
1868 #include <string.h>
1869 #include "jsdtoa.h"
1871 static JSBool
1872 date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1873               jsval *rval)
1875     jsdouble *date;
1876     char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1877     JSString *str;
1879     date = date_getProlog(cx, obj, argv);
1880     if (!date)
1881         return JS_FALSE;
1883     numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);
1884     if (!numStr) {
1885         JS_ReportOutOfMemory(cx);
1886         return JS_FALSE;
1887     }
1889     bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr);
1890     if (!bytes) {
1891         JS_ReportOutOfMemory(cx);
1892         return JS_FALSE;
1893     }
1895     str = JS_NewString(cx, bytes, strlen(bytes));
1896     if (!str) {
1897         free(bytes);
1898         return JS_FALSE;
1899     }
1900     *rval = STRING_TO_JSVAL(str);
1901     return JS_TRUE;
1903 #endif
1905 static JSBool
1906 date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1907               jsval *rval)
1909     jsdouble *date = date_getProlog(cx, obj, argv);
1910     if (!date)
1911         return JS_FALSE;
1912     return date_format(cx, *date, FORMATSPEC_FULL, rval);
1915 #if JS_HAS_VALUEOF_HINT
1916 static JSBool
1917 date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1918              jsval *rval)
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      */
1925     /* If called directly with no arguments, convert to a time number. */
1926     if (argc == 0)
1927         return date_getTime(cx, obj, argc, argv, rval);
1929     /* Convert to number only if the hint was given, otherwise favor string. */
1930     if (argc == 1) {
1931         JSString *str, *str2;
1933         str = js_ValueToString(cx, argv[0]);
1934         if (!str)
1935             return JS_FALSE;
1936         str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
1937         if (!js_CompareStrings(str, str2))
1938             return date_getTime(cx, obj, argc, argv, rval);
1939     }
1940     return date_toString(cx, obj, argc, argv, rval);
1942 #else
1943 #define date_valueOf date_getTime
1944 #endif
1946 \f
1947 /*
1948  * creation and destruction
1949  */
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 };
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 };
2009 static jsdouble *
2010 date_constructor(JSContext *cx, JSObject* obj)
2012     jsdouble *date;
2014     date = js_NewDouble(cx, 0.0, 0);
2015     if (!date)
2016         return NULL;
2017     OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
2018     return date;
2021 static JSBool
2022 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2024     jsdouble *date;
2025     JSString *str;
2026     jsdouble d;
2028     /* Date called as function. */
2029     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
2030         int64 us, ms, us2ms;
2031         jsdouble msec_time;
2033         /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
2034          * so compute ms from PRMJ_Now.
2035          */
2036         us = PRMJ_Now();
2037         JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2038         JSLL_DIV(ms, us, us2ms);
2039         JSLL_L2D(msec_time, ms);
2041         return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
2042     }
2044     /* Date called as constructor. */
2045     if (argc == 0) {
2046         int64 us, ms, us2ms;
2047         jsdouble msec_time;
2049         date = date_constructor(cx, obj);
2050         if (!date)
2051             return JS_FALSE;
2053         us = PRMJ_Now();
2054         JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2055         JSLL_DIV(ms, us, us2ms);
2056         JSLL_L2D(msec_time, ms);
2058         *date = msec_time;
2059     } else if (argc == 1) {
2060         if (!JSVAL_IS_STRING(argv[0])) {
2061             /* the argument is a millisecond number */
2062             if (!js_ValueToNumber(cx, argv[0], &d))
2063                     return JS_FALSE;
2064             date = date_constructor(cx, obj);
2065             if (!date)
2066                 return JS_FALSE;
2067             *date = TIMECLIP(d);
2068         } else {
2069             /* the argument is a string; parse it. */
2070             date = date_constructor(cx, obj);
2071             if (!date)
2072                 return JS_FALSE;
2074             str = js_ValueToString(cx, argv[0]);
2075             if (!str)
2076                 return JS_FALSE;
2078             if (!date_parseString(str, date))
2079                 *date = *cx->runtime->jsNaN;
2080             *date = TIMECLIP(*date);
2081         }
2082     } else {
2083         jsdouble array[MAXARGS];
2084         uintN loop;
2085         jsdouble double_arg;
2086         jsdouble day;
2087         jsdouble msec_time;
2089         for (loop = 0; loop < MAXARGS; loop++) {
2090             if (loop < argc) {
2091                 if (!js_ValueToNumber(cx, argv[loop], &double_arg))
2092                     return JS_FALSE;
2093                 /* if any arg is NaN, make a NaN date object
2094                    and return */
2095                 if (!JSDOUBLE_IS_FINITE(double_arg)) {
2096                     date = date_constructor(cx, obj);
2097                     if (!date)
2098                         return JS_FALSE;
2099                     *date = *cx->runtime->jsNaN;
2100                     return JS_TRUE;
2101                 }
2102                 array[loop] = js_DoubleToInteger(double_arg);
2103             } else {
2104                 if (loop == 2) {
2105                     array[loop] = 1; /* Default the date argument to 1. */
2106                 } else {
2107                     array[loop] = 0;
2108                 }
2109             }
2110         }
2112         date = date_constructor(cx, obj);
2113         if (!date)
2114             return JS_FALSE;
2116         /* adjust 2-digit years into the 20th century */
2117         if (array[0] >= 0 && array[0] <= 99)
2118             array[0] += 1900;
2120         day = MakeDay(array[0], array[1], array[2]);
2121         msec_time = MakeTime(array[3], array[4], array[5], array[6]);
2122         msec_time = MakeDate(day, msec_time);
2123         msec_time = UTC(msec_time);
2124         *date = TIMECLIP(msec_time);
2125     }
2126     return JS_TRUE;
2129 JSObject *
2130 js_InitDateClass(JSContext *cx, JSObject *obj)
2132     JSObject *proto;
2133     jsdouble *proto_date;
2135     /* set static LocalTZA */
2136     LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2137     proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS,
2138                          NULL, date_methods, NULL, date_static_methods);
2139     if (!proto)
2140         return NULL;
2142     /* Alias toUTCString with toGMTString.  (ECMA B.2.6) */
2143     if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2144         return NULL;
2146     /* Set the value of the Date.prototype date to NaN */
2147     proto_date = date_constructor(cx, proto);
2148     if (!proto_date)
2149         return NULL;
2150     *proto_date = *cx->runtime->jsNaN;
2152     return proto;
2155 JS_FRIEND_API(JSObject *)
2156 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2158     JSObject *obj;
2159     jsdouble *date;
2161     obj = js_NewObject(cx, &date_class, NULL, NULL);
2162     if (!obj)
2163         return NULL;
2165     date = date_constructor(cx, obj);
2166     if (!date)
2167         return NULL;
2169     *date = msec_time;
2170     return obj;
2173 JS_FRIEND_API(JSObject *)
2174 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2175                  int hour, int min, int sec)
2177     JSObject *obj;
2178     jsdouble msec_time;
2180     msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2181     obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2182     return obj;
2185 JS_FRIEND_API(JSBool)
2186 js_DateIsValid(JSContext *cx, JSObject* obj)
2188     jsdouble *date = date_getProlog(cx, obj, NULL);
2190     if (!date || JSDOUBLE_IS_NaN(*date))
2191         return JS_FALSE;
2192     else
2193         return JS_TRUE;
2196 JS_FRIEND_API(int)
2197 js_DateGetYear(JSContext *cx, JSObject* obj)
2199     jsdouble *date = date_getProlog(cx, obj, NULL);
2201     /* Preserve legacy API behavior of returning 0 for invalid dates. */
2202     if (!date || JSDOUBLE_IS_NaN(*date))
2203         return 0;
2204     return (int) YearFromTime(LocalTime(*date));
2207 JS_FRIEND_API(int)
2208 js_DateGetMonth(JSContext *cx, JSObject* obj)
2210     jsdouble *date = date_getProlog(cx, obj, NULL);
2212     if (!date || JSDOUBLE_IS_NaN(*date))
2213         return 0;
2214     return (int) MonthFromTime(LocalTime(*date));
2217 JS_FRIEND_API(int)
2218 js_DateGetDate(JSContext *cx, JSObject* obj)
2220     jsdouble *date = date_getProlog(cx, obj, NULL);
2222     if (!date || JSDOUBLE_IS_NaN(*date))
2223         return 0;
2224     return (int) DateFromTime(LocalTime(*date));
2227 JS_FRIEND_API(int)
2228 js_DateGetHours(JSContext *cx, JSObject* obj)
2230     jsdouble *date = date_getProlog(cx, obj, NULL);
2232     if (!date || JSDOUBLE_IS_NaN(*date))
2233         return 0;
2234     return (int) HourFromTime(LocalTime(*date));
2237 JS_FRIEND_API(int)
2238 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2240     jsdouble *date = date_getProlog(cx, obj, NULL);
2242     if (!date || JSDOUBLE_IS_NaN(*date))
2243         return 0;
2244     return (int) MinFromTime(LocalTime(*date));
2247 JS_FRIEND_API(int)
2248 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2250     jsdouble *date = date_getProlog(cx, obj, NULL);
2252     if (!date || JSDOUBLE_IS_NaN(*date))
2253         return 0;
2254     return (int) SecFromTime(*date);
2257 JS_FRIEND_API(void)
2258 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2260     jsdouble local;
2261     jsdouble *date = date_getProlog(cx, obj, NULL);
2262     if (!date)
2263         return;
2264     local = LocalTime(*date);
2265     /* reset date if it was NaN */
2266     if (JSDOUBLE_IS_NaN(local))
2267         local = 0;
2268     local = date_msecFromDate(year,
2269                               MonthFromTime(local),
2270                               DateFromTime(local),
2271                               HourFromTime(local),
2272                               MinFromTime(local),
2273                               SecFromTime(local),
2274                               msFromTime(local));
2275     *date = UTC(local);
2278 JS_FRIEND_API(void)
2279 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2281     jsdouble local;
2282     jsdouble *date = date_getProlog(cx, obj, NULL);
2283     if (!date)
2284         return;
2285     local = LocalTime(*date);
2286     /* bail if date was NaN */
2287     if (JSDOUBLE_IS_NaN(local))
2288         return;
2289     local = date_msecFromDate(YearFromTime(local),
2290                               month,
2291                               DateFromTime(local),
2292                               HourFromTime(local),
2293                               MinFromTime(local),
2294                               SecFromTime(local),
2295                               msFromTime(local));
2296     *date = UTC(local);
2299 JS_FRIEND_API(void)
2300 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2302     jsdouble local;
2303     jsdouble *datep = date_getProlog(cx, obj, NULL);
2304     if (!datep)
2305         return;
2306     local = LocalTime(*datep);
2307     if (JSDOUBLE_IS_NaN(local))
2308         return;
2309     local = date_msecFromDate(YearFromTime(local),
2310                               MonthFromTime(local),
2311                               date,
2312                               HourFromTime(local),
2313                               MinFromTime(local),
2314                               SecFromTime(local),
2315                               msFromTime(local));
2316     *datep = UTC(local);
2319 JS_FRIEND_API(void)
2320 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2322     jsdouble local;
2323     jsdouble *date = date_getProlog(cx, obj, NULL);
2324     if (!date)
2325         return;
2326     local = LocalTime(*date);
2327     if (JSDOUBLE_IS_NaN(local))
2328         return;
2329     local = date_msecFromDate(YearFromTime(local),
2330                               MonthFromTime(local),
2331                               DateFromTime(local),
2332                               hours,
2333                               MinFromTime(local),
2334                               SecFromTime(local),
2335                               msFromTime(local));
2336     *date = UTC(local);
2339 JS_FRIEND_API(void)
2340 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2342     jsdouble local;
2343     jsdouble *date = date_getProlog(cx, obj, NULL);
2344     if (!date)
2345         return;
2346     local = LocalTime(*date);
2347     if (JSDOUBLE_IS_NaN(local))
2348         return;
2349     local = date_msecFromDate(YearFromTime(local),
2350                               MonthFromTime(local),
2351                               DateFromTime(local),
2352                               HourFromTime(local),
2353                               minutes,
2354                               SecFromTime(local),
2355                               msFromTime(local));
2356     *date = UTC(local);
2359 JS_FRIEND_API(void)
2360 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2362     jsdouble local;
2363     jsdouble *date = date_getProlog(cx, obj, NULL);
2364     if (!date)
2365         return;
2366     local = LocalTime(*date);
2367     if (JSDOUBLE_IS_NaN(local))
2368         return;
2369     local = date_msecFromDate(YearFromTime(local),
2370                               MonthFromTime(local),
2371                               DateFromTime(local),
2372                               HourFromTime(local),
2373                               MinFromTime(local),
2374                               seconds,
2375                               msFromTime(local));
2376     *date = UTC(local);
2379 JS_FRIEND_API(jsdouble)
2380 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2382     jsdouble *date = date_getProlog(cx, obj, NULL);
2383     if (!date || JSDOUBLE_IS_NaN(*date))
2384         return 0;
2385     return (*date);