Back to index

lightning-sunbird  0.9+nobinonly
jsdate.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 /*
00041  * JS date methods.
00042  */
00043 
00044 /*
00045  * "For example, OS/360 devotes 26 bytes of the permanently
00046  *  resident date-turnover routine to the proper handling of
00047  *  December 31 on leap years (when it is Day 366).  That
00048  *  might have been left to the operator."
00049  *
00050  * Frederick Brooks, 'The Second-System Effect'.
00051  */
00052 
00053 #include "jsstddef.h"
00054 #include <ctype.h>
00055 #include <locale.h>
00056 #include <math.h>
00057 #include <stdlib.h>
00058 #include <string.h>
00059 #include "jstypes.h"
00060 #include "jsprf.h"
00061 #include "prmjtime.h"
00062 #include "jsutil.h" /* Added by JSIFY */
00063 #include "jsapi.h"
00064 #include "jsconfig.h"
00065 #include "jscntxt.h"
00066 #include "jsdate.h"
00067 #include "jsinterp.h"
00068 #include "jsnum.h"
00069 #include "jsobj.h"
00070 #include "jsstr.h"
00071 
00072 /*
00073  * The JS 'Date' object is patterned after the Java 'Date' object.
00074  * Here is an script:
00075  *
00076  *    today = new Date();
00077  *
00078  *    print(today.toLocaleString());
00079  *
00080  *    weekDay = today.getDay();
00081  *
00082  *
00083  * These Java (and ECMA-262) methods are supported:
00084  *
00085  *     UTC
00086  *     getDate (getUTCDate)
00087  *     getDay (getUTCDay)
00088  *     getHours (getUTCHours)
00089  *     getMinutes (getUTCMinutes)
00090  *     getMonth (getUTCMonth)
00091  *     getSeconds (getUTCSeconds)
00092  *     getMilliseconds (getUTCMilliseconds)
00093  *     getTime
00094  *     getTimezoneOffset
00095  *     getYear
00096  *     getFullYear (getUTCFullYear)
00097  *     parse
00098  *     setDate (setUTCDate)
00099  *     setHours (setUTCHours)
00100  *     setMinutes (setUTCMinutes)
00101  *     setMonth (setUTCMonth)
00102  *     setSeconds (setUTCSeconds)
00103  *     setMilliseconds (setUTCMilliseconds)
00104  *     setTime
00105  *     setYear (setFullYear, setUTCFullYear)
00106  *     toGMTString (toUTCString)
00107  *     toLocaleString
00108  *     toString
00109  *
00110  *
00111  * These Java methods are not supported
00112  *
00113  *     setDay
00114  *     before
00115  *     after
00116  *     equals
00117  *     hashCode
00118  */
00119 
00120 /*
00121  * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
00122  * definition and reduce dependence on NSPR.  NSPR is used to get the current
00123  * time in milliseconds, the time zone offset, and the daylight savings time
00124  * offset for a given time.  NSPR is also used for Date.toLocaleString(), for
00125  * locale-specific formatting, and to get a string representing the timezone.
00126  * (Which turns out to be platform-dependent.)
00127  *
00128  * To do:
00129  * (I did some performance tests by timing how long it took to run what
00130  *  I had of the js ECMA conformance tests.)
00131  *
00132  * - look at saving results across multiple calls to supporting
00133  * functions; the toString functions compute some of the same values
00134  * multiple times.  Although - I took a quick stab at this, and I lost
00135  * rather than gained.  (Fractionally.)  Hard to tell what compilers/processors
00136  * are doing these days.
00137  *
00138  * - look at tweaking function return types to return double instead
00139  * of int; this seems to make things run slightly faster sometimes.
00140  * (though it could be architecture-dependent.)  It'd be good to see
00141  * how this does on win32.  (Tried it on irix.)  Types could use a
00142  * general going-over.
00143  */
00144 
00145 /*
00146  * Supporting functions - ECMA 15.9.1.*
00147  */
00148 
00149 #define HalfTimeDomain  8.64e15
00150 #define HoursPerDay     24.0
00151 #define MinutesPerDay   (HoursPerDay * MinutesPerHour)
00152 #define MinutesPerHour  60.0
00153 #define SecondsPerDay   (MinutesPerDay * SecondsPerMinute)
00154 #define SecondsPerHour  (MinutesPerHour * SecondsPerMinute)
00155 #define SecondsPerMinute 60.0
00156 
00157 #if defined(XP_WIN) || defined(XP_OS2)
00158 /* Work around msvc double optimization bug by making these runtime values; if
00159  * they're available at compile time, msvc optimizes division by them by
00160  * computing the reciprocal and multiplying instead of dividing - this loses
00161  * when the reciprocal isn't representable in a double.
00162  */
00163 static jsdouble msPerSecond = 1000.0;
00164 static jsdouble msPerDay = SecondsPerDay * 1000.0;
00165 static jsdouble msPerHour = SecondsPerHour * 1000.0;
00166 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
00167 #else
00168 #define msPerDay        (SecondsPerDay * msPerSecond)
00169 #define msPerHour       (SecondsPerHour * msPerSecond)
00170 #define msPerMinute     (SecondsPerMinute * msPerSecond)
00171 #define msPerSecond     1000.0
00172 #endif
00173 
00174 #define Day(t)          floor((t) / msPerDay)
00175 
00176 static jsdouble
00177 TimeWithinDay(jsdouble t)
00178 {
00179     jsdouble result;
00180     result = fmod(t, msPerDay);
00181     if (result < 0)
00182         result += msPerDay;
00183     return result;
00184 }
00185 
00186 #define DaysInYear(y)   ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0))  \
00187                          ? 366 : 365)
00188 
00189 /* math here has to be f.p, because we need
00190  *  floor((1968 - 1969) / 4) == -1
00191  */
00192 #define DayFromYear(y)  (365 * ((y)-1970) + floor(((y)-1969)/4.0)            \
00193                          - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
00194 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
00195 
00196 static jsint
00197 YearFromTime(jsdouble t)
00198 {
00199     jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
00200     jsdouble t2 = (jsdouble) TimeFromYear(y);
00201 
00202     if (t2 > t) {
00203         y--;
00204     } else {
00205         if (t2 + msPerDay * DaysInYear(y) <= t)
00206             y++;
00207     }
00208     return y;
00209 }
00210 
00211 #define InLeapYear(t)   (JSBool) (DaysInYear(YearFromTime(t)) == 366)
00212 
00213 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
00214 
00215 /*
00216  * The following array contains the day of year for the first day of
00217  * each month, where index 0 is January, and day 0 is January 1.
00218  */
00219 static jsdouble firstDayOfMonth[2][12] = {
00220     {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
00221     {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
00222 };
00223 
00224 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
00225 
00226 static intN
00227 MonthFromTime(jsdouble t)
00228 {
00229     intN d, step;
00230     jsint year = YearFromTime(t);
00231     d = DayWithinYear(t, year);
00232 
00233     if (d < (step = 31))
00234         return 0;
00235     step += (InLeapYear(t) ? 29 : 28);
00236     if (d < step)
00237         return 1;
00238     if (d < (step += 31))
00239         return 2;
00240     if (d < (step += 30))
00241         return 3;
00242     if (d < (step += 31))
00243         return 4;
00244     if (d < (step += 30))
00245         return 5;
00246     if (d < (step += 31))
00247         return 6;
00248     if (d < (step += 31))
00249         return 7;
00250     if (d < (step += 30))
00251         return 8;
00252     if (d < (step += 31))
00253         return 9;
00254     if (d < (step += 30))
00255         return 10;
00256     return 11;
00257 }
00258 
00259 static intN
00260 DateFromTime(jsdouble t)
00261 {
00262     intN d, step, next;
00263     jsint year = YearFromTime(t);
00264     d = DayWithinYear(t, year);
00265 
00266     if (d <= (next = 30))
00267         return d + 1;
00268     step = next;
00269     next += (InLeapYear(t) ? 29 : 28);
00270     if (d <= next)
00271         return d - step;
00272     step = next;
00273     if (d <= (next += 31))
00274         return d - step;
00275     step = next;
00276     if (d <= (next += 30))
00277         return d - step;
00278     step = next;
00279     if (d <= (next += 31))
00280         return d - step;
00281     step = next;
00282     if (d <= (next += 30))
00283         return d - step;
00284     step = next;
00285     if (d <= (next += 31))
00286         return d - step;
00287     step = next;
00288     if (d <= (next += 31))
00289         return d - step;
00290     step = next;
00291     if (d <= (next += 30))
00292         return d - step;
00293     step = next;
00294     if (d <= (next += 31))
00295         return d - step;
00296     step = next;
00297     if (d <= (next += 30))
00298         return d - step;
00299     step = next;
00300     return d - step;
00301 }
00302 
00303 static intN
00304 WeekDay(jsdouble t)
00305 {
00306     jsint result;
00307     result = (jsint) Day(t) + 4;
00308     result = result % 7;
00309     if (result < 0)
00310         result += 7;
00311     return (intN) result;
00312 }
00313 
00314 #define MakeTime(hour, min, sec, ms) \
00315 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
00316 
00317 static jsdouble
00318 MakeDay(jsdouble year, jsdouble month, jsdouble date)
00319 {
00320     JSBool leap;
00321     jsdouble yearday;
00322     jsdouble monthday;
00323 
00324     year += floor(month / 12);
00325 
00326     month = fmod(month, 12.0);
00327     if (month < 0)
00328         month += 12;
00329 
00330     leap = (DaysInYear((jsint) year) == 366);
00331 
00332     yearday = floor(TimeFromYear(year) / msPerDay);
00333     monthday = DayFromMonth(month, leap);
00334 
00335     return yearday + monthday + date - 1;
00336 }
00337 
00338 #define MakeDate(day, time) ((day) * msPerDay + (time))
00339 
00340 /*
00341  * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
00342  *
00343  * yearStartingWith[0][i] is an example non-leap year where
00344  * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
00345  *
00346  * yearStartingWith[1][i] is an example leap year where
00347  * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
00348  */
00349 static jsint yearStartingWith[2][7] = {
00350     {1978, 1973, 1974, 1975, 1981, 1971, 1977},
00351     {1984, 1996, 1980, 1992, 1976, 1988, 1972}
00352 };
00353 
00354 /*
00355  * Find a year for which any given date will fall on the same weekday.
00356  *
00357  * This function should be used with caution when used other than
00358  * for determining DST; it hasn't been proven not to produce an
00359  * incorrect year for times near year boundaries.
00360  */
00361 static jsint
00362 EquivalentYearForDST(jsint year)
00363 {
00364     jsint day;
00365     JSBool isLeapYear;
00366 
00367     day = (jsint) DayFromYear(year) + 4;
00368     day = day % 7;
00369     if (day < 0)
00370         day += 7;
00371 
00372     isLeapYear = (DaysInYear(year) == 366);
00373 
00374     return yearStartingWith[isLeapYear][day];
00375 }
00376 
00377 /* LocalTZA gets set by js_InitDateClass() */
00378 static jsdouble LocalTZA;
00379 
00380 static jsdouble
00381 DaylightSavingTA(jsdouble t)
00382 {
00383     volatile int64 PR_t;
00384     int64 ms2us;
00385     int64 offset;
00386     jsdouble result;
00387 
00388     /* abort if NaN */
00389     if (JSDOUBLE_IS_NaN(t))
00390         return t;
00391 
00392     /*
00393      * If earlier than 1970 or after 2038, potentially beyond the ken of
00394      * many OSes, map it to an equivalent year before asking.
00395      */
00396     if (t < 0.0 || t > 2145916800000.0) {
00397         jsint year;
00398         jsdouble day;
00399 
00400         year = EquivalentYearForDST(YearFromTime(t));
00401         day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
00402         t = MakeDate(day, TimeWithinDay(t));
00403     }
00404 
00405     /* put our t in an LL, and map it to usec for prtime */
00406     JSLL_D2L(PR_t, t);
00407     JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
00408     JSLL_MUL(PR_t, PR_t, ms2us);
00409 
00410     offset = PRMJ_DSTOffset(PR_t);
00411 
00412     JSLL_DIV(offset, offset, ms2us);
00413     JSLL_L2D(result, offset);
00414     return result;
00415 }
00416 
00417 
00418 #define AdjustTime(t)   fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
00419 
00420 #define LocalTime(t)    ((t) + AdjustTime(t))
00421 
00422 static jsdouble
00423 UTC(jsdouble t)
00424 {
00425     return t - AdjustTime(t - LocalTZA);
00426 }
00427 
00428 static intN
00429 HourFromTime(jsdouble t)
00430 {
00431     intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
00432     if (result < 0)
00433         result += (intN)HoursPerDay;
00434     return result;
00435 }
00436 
00437 static intN
00438 MinFromTime(jsdouble t)
00439 {
00440     intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
00441     if (result < 0)
00442         result += (intN)MinutesPerHour;
00443     return result;
00444 }
00445 
00446 static intN
00447 SecFromTime(jsdouble t)
00448 {
00449     intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
00450     if (result < 0)
00451         result += (intN)SecondsPerMinute;
00452     return result;
00453 }
00454 
00455 static intN
00456 msFromTime(jsdouble t)
00457 {
00458     intN result = (intN) fmod(t, msPerSecond);
00459     if (result < 0)
00460         result += (intN)msPerSecond;
00461     return result;
00462 }
00463 
00464 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
00465                       && !((d < 0 ? -d : d) > HalfTimeDomain)) \
00466                      ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
00467 
00471 
00472 /*
00473  * Other Support routines and definitions
00474  */
00475 
00476 JSClass js_DateClass = {
00477     js_Date_str,
00478     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
00479     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
00480     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
00481     JSCLASS_NO_OPTIONAL_MEMBERS
00482 };
00483 
00484 /* for use by date_parse */
00485 
00486 static const char* wtb[] = {
00487     "am", "pm",
00488     "monday", "tuesday", "wednesday", "thursday", "friday",
00489     "saturday", "sunday",
00490     "january", "february", "march", "april", "may", "june",
00491     "july", "august", "september", "october", "november", "december",
00492     "gmt", "ut", "utc",
00493     "est", "edt",
00494     "cst", "cdt",
00495     "mst", "mdt",
00496     "pst", "pdt"
00497     /* time zone table needs to be expanded */
00498 };
00499 
00500 static int ttb[] = {
00501     -1, -2, 0, 0, 0, 0, 0, 0, 0,       /* AM/PM */
00502     2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
00503     10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */
00504     10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */
00505     10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */
00506     10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */
00507     10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */
00508 };
00509 
00510 /* helper for date_parse */
00511 static JSBool
00512 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
00513                    int count, int ignoreCase)
00514 {
00515     JSBool result = JS_FALSE;
00516     /* return true if matches, otherwise, false */
00517 
00518     while (count > 0 && s1[s1off] && s2[s2off]) {
00519         if (ignoreCase) {
00520             if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
00521                 break;
00522             }
00523         } else {
00524             if ((jschar)s1[s1off] != s2[s2off]) {
00525                 break;
00526             }
00527         }
00528         s1off++;
00529         s2off++;
00530         count--;
00531     }
00532 
00533     if (count == 0) {
00534         result = JS_TRUE;
00535     }
00536 
00537     return result;
00538 }
00539 
00540 /* find UTC time from given date... no 1900 correction! */
00541 static jsdouble
00542 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
00543                   jsdouble min, jsdouble sec, jsdouble msec)
00544 {
00545     jsdouble day;
00546     jsdouble msec_time;
00547     jsdouble result;
00548 
00549     day = MakeDay(year, mon, mday);
00550     msec_time = MakeTime(hour, min, sec, msec);
00551     result = MakeDate(day, msec_time);
00552     return result;
00553 }
00554 
00555 /*
00556  * See ECMA 15.9.4.[3-10];
00557  */
00558 /* XXX this function must be above date_parseString to avoid a
00559    horrid bug in the Win16 1.52 compiler */
00560 #define MAXARGS        7
00561 static JSBool
00562 date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00563 {
00564     jsdouble array[MAXARGS];
00565     uintN loop;
00566     jsdouble d;
00567 
00568     for (loop = 0; loop < MAXARGS; loop++) {
00569         if (loop < argc) {
00570             if (!js_ValueToNumber(cx, argv[loop], &d))
00571                 return JS_FALSE;
00572             /* return NaN if any arg is NaN */
00573             if (!JSDOUBLE_IS_FINITE(d)) {
00574                 return js_NewNumberValue(cx, d, rval);
00575             }
00576             array[loop] = floor(d);
00577         } else {
00578             array[loop] = 0;
00579         }
00580     }
00581 
00582     /* adjust 2-digit years into the 20th century */
00583     if (array[0] >= 0 && array[0] <= 99)
00584         array[0] += 1900;
00585 
00586     /* if we got a 0 for 'date' (which is out of range)
00587      * pretend it's a 1.  (So Date.UTC(1972, 5) works) */
00588     if (array[2] < 1)
00589         array[2] = 1;
00590 
00591     d = date_msecFromDate(array[0], array[1], array[2],
00592                               array[3], array[4], array[5], array[6]);
00593     d = TIMECLIP(d);
00594 
00595     return js_NewNumberValue(cx, d, rval);
00596 }
00597 
00598 static JSBool
00599 date_parseString(JSString *str, jsdouble *result)
00600 {
00601     jsdouble msec;
00602 
00603     const jschar *s = JSSTRING_CHARS(str);
00604     size_t limit = JSSTRING_LENGTH(str);
00605     size_t i = 0;
00606     int year = -1;
00607     int mon = -1;
00608     int mday = -1;
00609     int hour = -1;
00610     int min = -1;
00611     int sec = -1;
00612     int c = -1;
00613     int n = -1;
00614     jsdouble tzoffset = -1;  /* was an int, overflowed on win16!!! */
00615     int prevc = 0;
00616     JSBool seenplusminus = JS_FALSE;
00617     int temp;
00618     JSBool seenmonthname = JS_FALSE;
00619 
00620     if (limit == 0)
00621         goto syntax;
00622     while (i < limit) {
00623         c = s[i];
00624         i++;
00625         if (c <= ' ' || c == ',' || c == '-') {
00626             if (c == '-' && '0' <= s[i] && s[i] <= '9') {
00627               prevc = c;
00628             }
00629             continue;
00630         }
00631         if (c == '(') { /* comments) */
00632             int depth = 1;
00633             while (i < limit) {
00634                 c = s[i];
00635                 i++;
00636                 if (c == '(') depth++;
00637                 else if (c == ')')
00638                     if (--depth <= 0)
00639                         break;
00640             }
00641             continue;
00642         }
00643         if ('0' <= c && c <= '9') {
00644             n = c - '0';
00645             while (i < limit && '0' <= (c = s[i]) && c <= '9') {
00646                 n = n * 10 + c - '0';
00647                 i++;
00648             }
00649 
00650             /* allow TZA before the year, so
00651              * 'Wed Nov 05 21:49:11 GMT-0800 1997'
00652              * works */
00653 
00654             /* uses of seenplusminus allow : in TZA, so Java
00655              * no-timezone style of GMT+4:30 works
00656              */
00657 
00658             if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
00659                 /* make ':' case below change tzoffset */
00660                 seenplusminus = JS_TRUE;
00661 
00662                 /* offset */
00663                 if (n < 24)
00664                     n = n * 60; /* EG. "GMT-3" */
00665                 else
00666                     n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
00667                 if (prevc == '+')       /* plus means east of GMT */
00668                     n = -n;
00669                 if (tzoffset != 0 && tzoffset != -1)
00670                     goto syntax;
00671                 tzoffset = n;
00672             } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
00673                 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
00674                     year = n;
00675                 else
00676                     goto syntax;
00677             } else if (c == ':') {
00678                 if (hour < 0)
00679                     hour = /*byte*/ n;
00680                 else if (min < 0)
00681                     min = /*byte*/ n;
00682                 else
00683                     goto syntax;
00684             } else if (c == '/') {
00685                 /* until it is determined that mon is the actual
00686                    month, keep it as 1-based rather than 0-based */
00687                 if (mon < 0)
00688                     mon = /*byte*/ n;
00689                 else if (mday < 0)
00690                     mday = /*byte*/ n;
00691                 else
00692                     goto syntax;
00693             } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
00694                 goto syntax;
00695             } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */
00696                 if (tzoffset < 0)
00697                     tzoffset -= n;
00698                 else
00699                     tzoffset += n;
00700             } else if (hour >= 0 && min < 0) {
00701                 min = /*byte*/ n;
00702             } else if (prevc == ':' && min >= 0 && sec < 0) {
00703                 sec = /*byte*/ n;
00704             } else if (mon < 0) {
00705                 mon = /*byte*/n;
00706             } else if (mon >= 0 && mday < 0) {
00707                 mday = /*byte*/ n;
00708             } else if (mon >= 0 && mday >= 0 && year < 0) {
00709                 year = n;
00710             } else {
00711                 goto syntax;
00712             }
00713             prevc = 0;
00714         } else if (c == '/' || c == ':' || c == '+' || c == '-') {
00715             prevc = c;
00716         } else {
00717             size_t st = i - 1;
00718             int k;
00719             while (i < limit) {
00720                 c = s[i];
00721                 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
00722                     break;
00723                 i++;
00724             }
00725             if (i <= st + 1)
00726                 goto syntax;
00727             for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
00728                 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
00729                     int action = ttb[k];
00730                     if (action != 0) {
00731                         if (action < 0) {
00732                             /*
00733                              * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
00734                              * 12:30, instead of blindly adding 12 if PM.
00735                              */
00736                             JS_ASSERT(action == -1 || action == -2);
00737                             if (hour > 12 || hour < 0) {
00738                                 goto syntax;
00739                             } else {
00740                                 if (action == -1 && hour == 12) { /* am */
00741                                     hour = 0;
00742                                 } else if (action == -2 && hour != 12) { /* pm */
00743                                     hour += 12;
00744                                 }
00745                             }
00746                         } else if (action <= 13) { /* month! */
00747                             /* Adjust mon to be 1-based until the final values
00748                                for mon, mday and year are adjusted below */
00749                             if (seenmonthname) {
00750                                 goto syntax;
00751                             }
00752                             seenmonthname = JS_TRUE;
00753                             temp = /*byte*/ (action - 2) + 1;
00754 
00755                             if (mon < 0) {
00756                                 mon = temp;
00757                             } else if (mday < 0) {
00758                                 mday = mon;
00759                                 mon = temp;
00760                             } else if (year < 0) {
00761                                 year = mon;
00762                                 mon = temp;
00763                             } else {
00764                                 goto syntax;
00765                             }
00766                         } else {
00767                             tzoffset = action - 10000;
00768                         }
00769                     }
00770                     break;
00771                 }
00772             if (k < 0)
00773                 goto syntax;
00774             prevc = 0;
00775         }
00776     }
00777     if (year < 0 || mon < 0 || mday < 0)
00778         goto syntax;
00779     /*
00780       Case 1. The input string contains an English month name.
00781               The form of the string can be month f l, or f month l, or
00782               f l month which each evaluate to the same date.
00783               If f and l are both greater than or equal to 70, or
00784               both less than 70, the date is invalid.
00785               The year is taken to be the greater of the values f, l.
00786               If the year is greater than or equal to 70 and less than 100,
00787               it is considered to be the number of years after 1900.
00788       Case 2. The input string is of the form "f/m/l" where f, m and l are
00789               integers, e.g. 7/16/45.
00790               Adjust the mon, mday and year values to achieve 100% MSIE
00791               compatibility.
00792               a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
00793                  i.  If year < 100, it is the number of years after 1900
00794                  ii. If year >= 100, it is the number of years after 0.
00795               b. If 70 <= f < 100
00796                  i.  If m < 70, f/m/l is interpreted as
00797                      year/month/day where year is the number of years after
00798                      1900.
00799                  ii. If m >= 70, the date is invalid.
00800               c. If f >= 100
00801                  i.  If m < 70, f/m/l is interpreted as
00802                      year/month/day where year is the number of years after 0.
00803                  ii. If m >= 70, the date is invalid.
00804     */
00805     if (seenmonthname) {
00806         if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
00807             goto syntax;
00808         }
00809         if (mday > year) {
00810             temp = year;
00811             year = mday;
00812             mday = temp;
00813         }
00814         if (year >= 70 && year < 100) {
00815             year += 1900;
00816         }
00817     } else if (mon < 70) { /* (a) month/day/year */
00818         if (year < 100) {
00819             year += 1900;
00820         }
00821     } else if (mon < 100) { /* (b) year/month/day */
00822         if (mday < 70) {
00823             temp = year;
00824             year = mon + 1900;
00825             mon = mday;
00826             mday = temp;
00827         } else {
00828             goto syntax;
00829         }
00830     } else { /* (c) year/month/day */
00831         if (mday < 70) {
00832             temp = year;
00833             year = mon;
00834             mon = mday;
00835             mday = temp;
00836         } else {
00837             goto syntax;
00838         }
00839     }
00840     mon -= 1; /* convert month to 0-based */
00841     if (sec < 0)
00842         sec = 0;
00843     if (min < 0)
00844         min = 0;
00845     if (hour < 0)
00846         hour = 0;
00847     if (tzoffset == -1) { /* no time zone specified, have to use local */
00848         jsdouble msec_time;
00849         msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
00850 
00851         *result = UTC(msec_time);
00852         return JS_TRUE;
00853     }
00854 
00855     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
00856     msec += tzoffset * msPerMinute;
00857     *result = msec;
00858     return JS_TRUE;
00859 
00860 syntax:
00861     /* syntax error */
00862     *result = 0;
00863     return JS_FALSE;
00864 }
00865 
00866 static JSBool
00867 date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00868 {
00869     JSString *str;
00870     jsdouble result;
00871 
00872     str = js_ValueToString(cx, argv[0]);
00873     if (!str)
00874         return JS_FALSE;
00875     if (!date_parseString(str, &result)) {
00876         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
00877         return JS_TRUE;
00878     }
00879 
00880     result = TIMECLIP(result);
00881     return js_NewNumberValue(cx, result, rval);
00882 }
00883 
00884 static JSBool
00885 date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00886 {
00887     int64 us, ms, us2ms;
00888     jsdouble msec_time;
00889 
00890     us = PRMJ_Now();
00891     JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
00892     JSLL_DIV(ms, us, us2ms);
00893     JSLL_L2D(msec_time, ms);
00894 
00895     return js_NewDoubleValue(cx, msec_time, rval);
00896 }
00897 
00898 /*
00899  * Check that obj is an object of class Date, and get the date value.
00900  * Return NULL on failure.
00901  */
00902 static jsdouble *
00903 date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)
00904 {
00905     if (!JS_InstanceOf(cx, obj, &js_DateClass, argv))
00906         return NULL;
00907     return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
00908 }
00909 
00910 /*
00911  * See ECMA 15.9.5.4 thru 15.9.5.23
00912  */
00913 static JSBool
00914 date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00915 {
00916     jsdouble *date = date_getProlog(cx, obj, argv);
00917     if (!date)
00918         return JS_FALSE;
00919 
00920     return js_NewNumberValue(cx, *date, rval);
00921 }
00922 
00923 static JSBool
00924 date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00925 {
00926     jsdouble *date;
00927     jsdouble result;
00928 
00929     date = date_getProlog(cx, obj, argv);
00930     if (!date)
00931         return JS_FALSE;
00932 
00933     result = *date;
00934     if (!JSDOUBLE_IS_FINITE(result))
00935         return js_NewNumberValue(cx, result, rval);
00936 
00937     result = YearFromTime(LocalTime(result));
00938 
00939     /* Follow ECMA-262 to the letter, contrary to IE JScript. */
00940     result -= 1900;
00941     return js_NewNumberValue(cx, result, rval);
00942 }
00943 
00944 static JSBool
00945 date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00946                  jsval *rval)
00947 {
00948     jsdouble result;
00949     jsdouble *date = date_getProlog(cx, obj, argv);
00950     if (!date)
00951         return JS_FALSE;
00952     result = *date;
00953 
00954     if (!JSDOUBLE_IS_FINITE(result))
00955         return js_NewNumberValue(cx, result, rval);
00956 
00957     result = YearFromTime(LocalTime(result));
00958     return js_NewNumberValue(cx, result, rval);
00959 }
00960 
00961 static JSBool
00962 date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00963                     jsval *rval)
00964 {
00965     jsdouble result;
00966     jsdouble *date = date_getProlog(cx, obj, argv);
00967     if (!date)
00968         return JS_FALSE;
00969     result = *date;
00970 
00971     if (!JSDOUBLE_IS_FINITE(result))
00972         return js_NewNumberValue(cx, result, rval);
00973 
00974     result = YearFromTime(result);
00975     return js_NewNumberValue(cx, result, rval);
00976 }
00977 
00978 static JSBool
00979 date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00980               jsval *rval)
00981 {
00982     jsdouble result;
00983     jsdouble *date = date_getProlog(cx, obj, argv);
00984     if (!date)
00985         return JS_FALSE;
00986     result = *date;
00987 
00988     if (!JSDOUBLE_IS_FINITE(result))
00989         return js_NewNumberValue(cx, result, rval);
00990 
00991     result = MonthFromTime(LocalTime(result));
00992     return js_NewNumberValue(cx, result, rval);
00993 }
00994 
00995 static JSBool
00996 date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00997                  jsval *rval)
00998 {
00999     jsdouble result;
01000     jsdouble *date = date_getProlog(cx, obj, argv);
01001     if (!date)
01002         return JS_FALSE;
01003     result = *date;
01004 
01005     if (!JSDOUBLE_IS_FINITE(result))
01006         return js_NewNumberValue(cx, result, rval);
01007 
01008     result = MonthFromTime(result);
01009     return js_NewNumberValue(cx, result, rval);
01010 }
01011 
01012 static JSBool
01013 date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01014 {
01015     jsdouble result;
01016     jsdouble *date = date_getProlog(cx, obj, argv);
01017     if (!date)
01018         return JS_FALSE;
01019     result = *date;
01020 
01021     if (!JSDOUBLE_IS_FINITE(result))
01022         return js_NewNumberValue(cx, result, rval);
01023 
01024     result = LocalTime(result);
01025     result = DateFromTime(result);
01026     return js_NewNumberValue(cx, result, rval);
01027 }
01028 
01029 static JSBool
01030 date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01031                 jsval *rval)
01032 {
01033     jsdouble result;
01034     jsdouble *date = date_getProlog(cx, obj, argv);
01035     if (!date)
01036         return JS_FALSE;
01037     result = *date;
01038 
01039     if (!JSDOUBLE_IS_FINITE(result))
01040         return js_NewNumberValue(cx, result, rval);
01041 
01042     result = DateFromTime(result);
01043     return js_NewNumberValue(cx, result, rval);
01044 }
01045 
01046 static JSBool
01047 date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01048 {
01049     jsdouble result;
01050     jsdouble *date = date_getProlog(cx, obj, argv);
01051     if (!date)
01052         return JS_FALSE;
01053     result = *date;
01054 
01055     if (!JSDOUBLE_IS_FINITE(result))
01056         return js_NewNumberValue(cx, result, rval);
01057 
01058     result = LocalTime(result);
01059     result = WeekDay(result);
01060     return js_NewNumberValue(cx, result, rval);
01061 }
01062 
01063 static JSBool
01064 date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01065                jsval *rval)
01066 {
01067     jsdouble result;
01068     jsdouble *date = date_getProlog(cx, obj, argv);
01069     if (!date)
01070         return JS_FALSE;
01071     result = *date;
01072 
01073     if (!JSDOUBLE_IS_FINITE(result))
01074         return js_NewNumberValue(cx, result, rval);
01075 
01076     result = WeekDay(result);
01077     return js_NewNumberValue(cx, result, rval);
01078 }
01079 
01080 static JSBool
01081 date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01082               jsval *rval)
01083 {
01084     jsdouble result;
01085     jsdouble *date = date_getProlog(cx, obj, argv);
01086     if (!date)
01087         return JS_FALSE;
01088     result = *date;
01089 
01090     if (!JSDOUBLE_IS_FINITE(result))
01091         return js_NewNumberValue(cx, result, rval);
01092 
01093     result = HourFromTime(LocalTime(result));
01094     return js_NewNumberValue(cx, result, rval);
01095 }
01096 
01097 static JSBool
01098 date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01099                  jsval *rval)
01100 {
01101     jsdouble result;
01102     jsdouble *date = date_getProlog(cx, obj, argv);
01103     if (!date)
01104         return JS_FALSE;
01105     result = *date;
01106 
01107     if (!JSDOUBLE_IS_FINITE(result))
01108         return js_NewNumberValue(cx, result, rval);
01109 
01110     result = HourFromTime(result);
01111     return js_NewNumberValue(cx, result, rval);
01112 }
01113 
01114 static JSBool
01115 date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01116                 jsval *rval)
01117 {
01118     jsdouble result;
01119     jsdouble *date = date_getProlog(cx, obj, argv);
01120     if (!date)
01121         return JS_FALSE;
01122     result = *date;
01123 
01124     if (!JSDOUBLE_IS_FINITE(result))
01125         return js_NewNumberValue(cx, result, rval);
01126 
01127     result = MinFromTime(LocalTime(result));
01128     return js_NewNumberValue(cx, result, rval);
01129 }
01130 
01131 static JSBool
01132 date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01133                    jsval *rval)
01134 {
01135     jsdouble result;
01136     jsdouble *date = date_getProlog(cx, obj, argv);
01137     if (!date)
01138         return JS_FALSE;
01139     result = *date;
01140 
01141     if (!JSDOUBLE_IS_FINITE(result))
01142         return js_NewNumberValue(cx, result, rval);
01143 
01144     result = MinFromTime(result);
01145     return js_NewNumberValue(cx, result, rval);
01146 }
01147 
01148 /* Date.getSeconds is mapped to getUTCSeconds */
01149 
01150 static JSBool
01151 date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01152                 jsval *rval)
01153 {
01154     jsdouble result;
01155     jsdouble *date = date_getProlog(cx, obj, argv);
01156     if (!date)
01157         return JS_FALSE;
01158     result = *date;
01159 
01160     if (!JSDOUBLE_IS_FINITE(result))
01161         return js_NewNumberValue(cx, result, rval);
01162 
01163     result = SecFromTime(result);
01164     return js_NewNumberValue(cx, result, rval);
01165 }
01166 
01167 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
01168 
01169 static JSBool
01170 date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01171                      jsval *rval)
01172 {
01173     jsdouble result;
01174     jsdouble *date = date_getProlog(cx, obj, argv);
01175     if (!date)
01176         return JS_FALSE;
01177     result = *date;
01178 
01179     if (!JSDOUBLE_IS_FINITE(result))
01180         return js_NewNumberValue(cx, result, rval);
01181 
01182     result = msFromTime(result);
01183     return js_NewNumberValue(cx, result, rval);
01184 }
01185 
01186 static JSBool
01187 date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01188                        jsval *rval)
01189 {
01190     jsdouble result;
01191     jsdouble *date = date_getProlog(cx, obj, argv);
01192     if (!date)
01193         return JS_FALSE;
01194     result = *date;
01195 
01196     /*
01197      * Return the time zone offset in minutes for the current locale
01198      * that is appropriate for this time. This value would be a
01199      * constant except for daylight savings time.
01200      */
01201     result = (result - LocalTime(result)) / msPerMinute;
01202     return js_NewNumberValue(cx, result, rval);
01203 }
01204 
01205 static JSBool
01206 date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01207 {
01208     jsdouble result;
01209     jsdouble *date = date_getProlog(cx, obj, argv);
01210     if (!date)
01211         return JS_FALSE;
01212 
01213     if (!js_ValueToNumber(cx, argv[0], &result))
01214         return JS_FALSE;
01215 
01216     result = TIMECLIP(result);
01217 
01218     *date = result;
01219     return js_NewNumberValue(cx, result, rval);
01220 }
01221 
01222 static JSBool
01223 date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01224               uintN maxargs, JSBool local, jsval *rval)
01225 {
01226     uintN i;
01227     jsdouble args[4], *argp, *stop;
01228     jsdouble hour, min, sec, msec;
01229     jsdouble lorutime; /* Local or UTC version of *date */
01230 
01231     jsdouble msec_time;
01232     jsdouble result;
01233 
01234     jsdouble *date = date_getProlog(cx, obj, argv);
01235     if (!date)
01236         return JS_FALSE;
01237 
01238     result = *date;
01239 
01240     /* just return NaN if the date is already NaN */
01241     if (!JSDOUBLE_IS_FINITE(result))
01242         return js_NewNumberValue(cx, result, rval);
01243 
01244     /* Satisfy the ECMA rule that if a function is called with
01245      * fewer arguments than the specified formal arguments, the
01246      * remaining arguments are set to undefined.  Seems like all
01247      * the Date.setWhatever functions in ECMA are only varargs
01248      * beyond the first argument; this should be set to undefined
01249      * if it's not given.  This means that "d = new Date();
01250      * d.setMilliseconds()" returns NaN.  Blech.
01251      */
01252     if (argc == 0)
01253         argc = 1;   /* should be safe, because length of all setters is 1 */
01254     else if (argc > maxargs)
01255         argc = maxargs;  /* clamp argc */
01256 
01257     for (i = 0; i < argc; i++) {
01258         if (!js_ValueToNumber(cx, argv[i], &args[i]))
01259             return JS_FALSE;
01260         if (!JSDOUBLE_IS_FINITE(args[i])) {
01261             *date = *cx->runtime->jsNaN;
01262             return js_NewNumberValue(cx, *date, rval);
01263         }
01264         args[i] = js_DoubleToInteger(args[i]);
01265     }
01266 
01267     if (local)
01268         lorutime = LocalTime(result);
01269     else
01270         lorutime = result;
01271 
01272     argp = args;
01273     stop = argp + argc;
01274     if (maxargs >= 4 && argp < stop)
01275         hour = *argp++;
01276     else
01277         hour = HourFromTime(lorutime);
01278 
01279     if (maxargs >= 3 && argp < stop)
01280         min = *argp++;
01281     else
01282         min = MinFromTime(lorutime);
01283 
01284     if (maxargs >= 2 && argp < stop)
01285         sec = *argp++;
01286     else
01287         sec = SecFromTime(lorutime);
01288 
01289     if (maxargs >= 1 && argp < stop)
01290         msec = *argp;
01291     else
01292         msec = msFromTime(lorutime);
01293 
01294     msec_time = MakeTime(hour, min, sec, msec);
01295     result = MakeDate(Day(lorutime), msec_time);
01296 
01297 /*     fprintf(stderr, "%f\n", result); */
01298 
01299     if (local)
01300         result = UTC(result);
01301 
01302 /*     fprintf(stderr, "%f\n", result); */
01303 
01304     *date = TIMECLIP(result);
01305     return js_NewNumberValue(cx, *date, rval);
01306 }
01307 
01308 static JSBool
01309 date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
01310                      jsval *argv, jsval *rval)
01311 {
01312     return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
01313 }
01314 
01315 static JSBool
01316 date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
01317                         jsval *argv, jsval *rval)
01318 {
01319     return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
01320 }
01321 
01322 static JSBool
01323 date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
01324                 jsval *argv, jsval *rval)
01325 {
01326     return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
01327 }
01328 
01329 static JSBool
01330 date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
01331                    jsval *argv, jsval *rval)
01332 {
01333     return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
01334 }
01335 
01336 static JSBool
01337 date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
01338                 jsval *argv, jsval *rval)
01339 {
01340     return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
01341 }
01342 
01343 static JSBool
01344 date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
01345                    jsval *argv, jsval *rval)
01346 {
01347     return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
01348 }
01349 
01350 static JSBool
01351 date_setHours(JSContext *cx, JSObject *obj, uintN argc,
01352               jsval *argv, jsval *rval)
01353 {
01354     return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
01355 }
01356 
01357 static JSBool
01358 date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
01359                  jsval *argv, jsval *rval)
01360 {
01361     return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
01362 }
01363 
01364 static JSBool
01365 date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
01366               jsval *argv, uintN maxargs, JSBool local, jsval *rval)
01367 {
01368     uintN i;
01369     jsdouble lorutime; /* local or UTC version of *date */
01370     jsdouble args[3], *argp, *stop;
01371     jsdouble year, month, day;
01372     jsdouble result;
01373 
01374     jsdouble *date = date_getProlog(cx, obj, argv);
01375     if (!date)
01376         return JS_FALSE;
01377 
01378     result = *date;
01379 
01380     /* see complaint about ECMA in date_MakeTime */
01381     if (argc == 0)
01382         argc = 1;   /* should be safe, because length of all setters is 1 */
01383     else if (argc > maxargs)
01384         argc = maxargs;   /* clamp argc */
01385 
01386     for (i = 0; i < argc; i++) {
01387         if (!js_ValueToNumber(cx, argv[i], &args[i]))
01388             return JS_FALSE;
01389         if (!JSDOUBLE_IS_FINITE(args[i])) {
01390             *date = *cx->runtime->jsNaN;
01391             return js_NewNumberValue(cx, *date, rval);
01392         }
01393         args[i] = js_DoubleToInteger(args[i]);
01394     }
01395 
01396     /* return NaN if date is NaN and we're not setting the year,
01397      * If we are, use 0 as the time. */
01398     if (!(JSDOUBLE_IS_FINITE(result))) {
01399         if (maxargs < 3)
01400             return js_NewNumberValue(cx, result, rval);
01401         else
01402             lorutime = +0.;
01403     } else {
01404         if (local)
01405             lorutime = LocalTime(result);
01406         else
01407             lorutime = result;
01408     }
01409 
01410     argp = args;
01411     stop = argp + argc;
01412     if (maxargs >= 3 && argp < stop)
01413         year = *argp++;
01414     else
01415         year = YearFromTime(lorutime);
01416 
01417     if (maxargs >= 2 && argp < stop)
01418         month = *argp++;
01419     else
01420         month = MonthFromTime(lorutime);
01421 
01422     if (maxargs >= 1 && argp < stop)
01423         day = *argp++;
01424     else
01425         day = DateFromTime(lorutime);
01426 
01427     day = MakeDay(year, month, day); /* day within year */
01428     result = MakeDate(day, TimeWithinDay(lorutime));
01429 
01430     if (local)
01431         result = UTC(result);
01432 
01433     *date = TIMECLIP(result);
01434     return js_NewNumberValue(cx, *date, rval);
01435 }
01436 
01437 static JSBool
01438 date_setDate(JSContext *cx, JSObject *obj, uintN argc,
01439              jsval *argv, jsval *rval)
01440 {
01441     return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
01442 }
01443 
01444 static JSBool
01445 date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
01446                 jsval *argv, jsval *rval)
01447 {
01448     return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
01449 }
01450 
01451 static JSBool
01452 date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
01453               jsval *argv, jsval *rval)
01454 {
01455     return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
01456 }
01457 
01458 static JSBool
01459 date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
01460                  jsval *argv, jsval *rval)
01461 {
01462     return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
01463 }
01464 
01465 static JSBool
01466 date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
01467                  jsval *argv, jsval *rval)
01468 {
01469     return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
01470 }
01471 
01472 static JSBool
01473 date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
01474                     jsval *argv, jsval *rval)
01475 {
01476     return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
01477 }
01478 
01479 static JSBool
01480 date_setYear(JSContext *cx, JSObject *obj, uintN argc,
01481              jsval *argv, jsval *rval)
01482 {
01483     jsdouble t;
01484     jsdouble year;
01485     jsdouble day;
01486     jsdouble result;
01487 
01488     jsdouble *date = date_getProlog(cx, obj, argv);
01489     if (!date)
01490         return JS_FALSE;
01491 
01492     result = *date;
01493 
01494     if (!js_ValueToNumber(cx, argv[0], &year))
01495         return JS_FALSE;
01496     if (!JSDOUBLE_IS_FINITE(year)) {
01497         *date = *cx->runtime->jsNaN;
01498         return js_NewNumberValue(cx, *date, rval);
01499     }
01500 
01501     year = js_DoubleToInteger(year);
01502 
01503     if (!JSDOUBLE_IS_FINITE(result)) {
01504         t = +0.0;
01505     } else {
01506         t = LocalTime(result);
01507     }
01508 
01509     if (year >= 0 && year <= 99)
01510         year += 1900;
01511 
01512     day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
01513     result = MakeDate(day, TimeWithinDay(t));
01514     result = UTC(result);
01515 
01516     *date = TIMECLIP(result);
01517     return js_NewNumberValue(cx, *date, rval);
01518 }
01519 
01520 /* constants for toString, toUTCString */
01521 static char js_NaN_date_str[] = "Invalid Date";
01522 static const char* days[] =
01523 {
01524    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
01525 };
01526 static const char* months[] =
01527 {
01528    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
01529 };
01530 
01531 static JSBool
01532 date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
01533                  jsval *argv, jsval *rval)
01534 {
01535     char buf[100];
01536     JSString *str;
01537     jsdouble *date = date_getProlog(cx, obj, argv);
01538     if (!date)
01539         return JS_FALSE;
01540 
01541     if (!JSDOUBLE_IS_FINITE(*date)) {
01542         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
01543     } else {
01544         jsdouble temp = *date;
01545 
01546         /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
01547          * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
01548          */
01549         JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
01550                     days[WeekDay(temp)],
01551                     DateFromTime(temp),
01552                     months[MonthFromTime(temp)],
01553                     YearFromTime(temp),
01554                     HourFromTime(temp),
01555                     MinFromTime(temp),
01556                     SecFromTime(temp));
01557     }
01558     str = JS_NewStringCopyZ(cx, buf);
01559     if (!str)
01560         return JS_FALSE;
01561     *rval = STRING_TO_JSVAL(str);
01562     return JS_TRUE;
01563 }
01564 
01565 /* for Date.toLocaleString; interface to PRMJTime date struct.
01566  * If findEquivalent is true, then try to map the year to an equivalent year
01567  * that's in range.
01568  */
01569 static void
01570 new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)
01571 {
01572     jsint year = YearFromTime(timeval);
01573     int16 adjustedYear;
01574 
01575     /* If the year doesn't fit in a PRMJTime, find something to do about it. */
01576     if (year > 32767 || year < -32768) {
01577         if (findEquivalent) {
01578             /* We're really just trying to get a timezone string; map the year
01579              * to some equivalent year in the range 0 to 2800.  Borrowed from
01580              * A. D. Olsen.
01581              */
01582             jsint cycles;
01583 #define CYCLE_YEARS 2800L
01584             cycles = (year >= 0) ? year / CYCLE_YEARS
01585                                  : -1 - (-1 - year) / CYCLE_YEARS;
01586             adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
01587         } else {
01588             /* Clamp it to the nearest representable year. */
01589             adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
01590         }
01591     } else {
01592         adjustedYear = (int16)year;
01593     }
01594 
01595     split->tm_usec = (int32) msFromTime(timeval) * 1000;
01596     split->tm_sec = (int8) SecFromTime(timeval);
01597     split->tm_min = (int8) MinFromTime(timeval);
01598     split->tm_hour = (int8) HourFromTime(timeval);
01599     split->tm_mday = (int8) DateFromTime(timeval);
01600     split->tm_mon = (int8) MonthFromTime(timeval);
01601     split->tm_wday = (int8) WeekDay(timeval);
01602     split->tm_year = (int16) adjustedYear;
01603     split->tm_yday = (int16) DayWithinYear(timeval, year);
01604 
01605     /* not sure how this affects things, but it doesn't seem
01606        to matter. */
01607     split->tm_isdst = (DaylightSavingTA(timeval) != 0);
01608 }
01609 
01610 typedef enum formatspec {
01611     FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
01612 } formatspec;
01613 
01614 /* helper function */
01615 static JSBool
01616 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
01617 {
01618     char buf[100];
01619     JSString *str;
01620     char tzbuf[100];
01621     JSBool usetz;
01622     size_t i, tzlen;
01623     PRMJTime split;
01624 
01625     if (!JSDOUBLE_IS_FINITE(date)) {
01626         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
01627     } else {
01628         jsdouble local = LocalTime(date);
01629 
01630         /* offset from GMT in minutes.  The offset includes daylight savings,
01631            if it applies. */
01632         jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
01633 
01634         /* map 510 minutes to 0830 hours */
01635         intN offset = (minutes / 60) * 100 + minutes % 60;
01636 
01637         /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
01638          * printed as 'GMT-0800' rather than as 'PST' to avoid
01639          * operating-system dependence on strftime (which
01640          * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
01641          * PST as 'Pacific Standard Time.'  This way we always know
01642          * what we're getting, and can parse it if we produce it.
01643          * The OS TZA string is included as a comment.
01644          */
01645 
01646         /* get a timezone string from the OS to include as a
01647            comment. */
01648         new_explode(date, &split, JS_TRUE);
01649         if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
01650 
01651             /* Decide whether to use the resulting timezone string.
01652              *
01653              * Reject it if it contains any non-ASCII, non-alphanumeric
01654              * characters.  It's then likely in some other character
01655              * encoding, and we probably won't display it correctly.
01656              */
01657             usetz = JS_TRUE;
01658             tzlen = strlen(tzbuf);
01659             if (tzlen > 100) {
01660                 usetz = JS_FALSE;
01661             } else {
01662                 for (i = 0; i < tzlen; i++) {
01663                     jschar c = tzbuf[i];
01664                     if (c > 127 ||
01665                         !(isalpha(c) || isdigit(c) ||
01666                           c == ' ' || c == '(' || c == ')')) {
01667                         usetz = JS_FALSE;
01668                     }
01669                 }
01670             }
01671 
01672             /* Also reject it if it's not parenthesized or if it's '()'. */
01673             if (tzbuf[0] != '(' || tzbuf[1] == ')')
01674                 usetz = JS_FALSE;
01675         } else
01676             usetz = JS_FALSE;
01677 
01678         switch (format) {
01679           case FORMATSPEC_FULL:
01680             /*
01681              * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
01682              * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
01683              */
01684             /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
01685             JS_snprintf(buf, sizeof buf,
01686                         "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
01687                         days[WeekDay(local)],
01688                         months[MonthFromTime(local)],
01689                         DateFromTime(local),
01690                         YearFromTime(local),
01691                         HourFromTime(local),
01692                         MinFromTime(local),
01693                         SecFromTime(local),
01694                         offset,
01695                         usetz ? " " : "",
01696                         usetz ? tzbuf : "");
01697             break;
01698           case FORMATSPEC_DATE:
01699             /* Tue Oct 31 2000 */
01700             JS_snprintf(buf, sizeof buf,
01701                         "%s %s %.2d %.4d",
01702                         days[WeekDay(local)],
01703                         months[MonthFromTime(local)],
01704                         DateFromTime(local),
01705                         YearFromTime(local));
01706             break;
01707           case FORMATSPEC_TIME:
01708             /* 09:41:40 GMT-0800 (PST) */
01709             JS_snprintf(buf, sizeof buf,
01710                         "%.2d:%.2d:%.2d GMT%+.4d%s%s",
01711                         HourFromTime(local),
01712                         MinFromTime(local),
01713                         SecFromTime(local),
01714                         offset,
01715                         usetz ? " " : "",
01716                         usetz ? tzbuf : "");
01717             break;
01718         }
01719     }
01720 
01721     str = JS_NewStringCopyZ(cx, buf);
01722     if (!str)
01723         return JS_FALSE;
01724     *rval = STRING_TO_JSVAL(str);
01725     return JS_TRUE;
01726 }
01727 
01728 static JSBool
01729 date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,
01730                     jsval *argv, jsval *rval, char *format)
01731 {
01732     char buf[100];
01733     JSString *str;
01734     PRMJTime split;
01735     jsdouble *date = date_getProlog(cx, obj, argv);
01736     if (!date)
01737         return JS_FALSE;
01738 
01739     if (!JSDOUBLE_IS_FINITE(*date)) {
01740         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
01741     } else {
01742         intN result_len;
01743         jsdouble local = LocalTime(*date);
01744         new_explode(local, &split, JS_FALSE);
01745 
01746         /* let PRMJTime format it.       */
01747         result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
01748 
01749         /* If it failed, default to toString. */
01750         if (result_len == 0)
01751             return date_format(cx, *date, FORMATSPEC_FULL, rval);
01752 
01753         /* Hacked check against undesired 2-digit year 00/00/00 form. */
01754         if (strcmp(format, "%x") == 0 && result_len >= 6 &&
01755             /* Format %x means use OS settings, which may have 2-digit yr, so
01756                hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
01757             !isdigit(buf[result_len - 3]) &&
01758             isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
01759             /* ...but not if starts with 4-digit year, like 2022/3/11. */
01760             !(isdigit(buf[0]) && isdigit(buf[1]) &&
01761               isdigit(buf[2]) && isdigit(buf[3]))) {
01762             JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
01763                         "%d", js_DateGetYear(cx, obj));
01764         }
01765 
01766     }
01767 
01768     if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
01769         return cx->localeCallbacks->localeToUnicode(cx, buf, rval);
01770 
01771     str = JS_NewStringCopyZ(cx, buf);
01772     if (!str)
01773         return JS_FALSE;
01774     *rval = STRING_TO_JSVAL(str);
01775     return JS_TRUE;
01776 }
01777 
01778 static JSBool
01779 date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
01780                     jsval *argv, jsval *rval)
01781 {
01782     /* Use '%#c' for windows, because '%c' is
01783      * backward-compatible and non-y2k with msvc; '%#c' requests that a
01784      * full year be used in the result string.
01785      */
01786     return date_toLocaleHelper(cx, obj, argc, argv, rval,
01787 #if defined(_WIN32) && !defined(__MWERKS__)
01788                                    "%#c"
01789 #else
01790                                    "%c"
01791 #endif
01792                                    );
01793 }
01794 
01795 static JSBool
01796 date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,
01797                     jsval *argv, jsval *rval)
01798 {
01799     /* Use '%#x' for windows, because '%x' is
01800      * backward-compatible and non-y2k with msvc; '%#x' requests that a
01801      * full year be used in the result string.
01802      */
01803     return date_toLocaleHelper(cx, obj, argc, argv, rval,
01804 #if defined(_WIN32) && !defined(__MWERKS__)
01805                                    "%#x"
01806 #else
01807                                    "%x"
01808 #endif
01809                                    );
01810 }
01811 
01812 static JSBool
01813 date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,
01814                         jsval *argv, jsval *rval)
01815 {
01816     return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");
01817 }
01818 
01819 static JSBool
01820 date_toLocaleFormat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01821                     jsval *rval)
01822 {
01823     JSString *fmt;
01824 
01825     if (argc == 0)
01826         return date_toLocaleString(cx, obj, argc, argv, rval);
01827 
01828     fmt = JS_ValueToString(cx, argv[0]);
01829     if (!fmt)
01830         return JS_FALSE;
01831 
01832     return date_toLocaleHelper(cx, obj, argc, argv, rval,
01833                                JS_GetStringBytes(fmt));
01834 }
01835 
01836 static JSBool
01837 date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,
01838                   jsval *argv, jsval *rval)
01839 {
01840     jsdouble *date = date_getProlog(cx, obj, argv);
01841     if (!date)
01842         return JS_FALSE;
01843     return date_format(cx, *date, FORMATSPEC_TIME, rval);
01844 }
01845 
01846 static JSBool
01847 date_toDateString(JSContext *cx, JSObject *obj, uintN argc,
01848                   jsval *argv, jsval *rval)
01849 {
01850     jsdouble *date = date_getProlog(cx, obj, argv);
01851     if (!date)
01852         return JS_FALSE;
01853     return date_format(cx, *date, FORMATSPEC_DATE, rval);
01854 }
01855 
01856 #if JS_HAS_TOSOURCE
01857 #include <string.h>
01858 #include "jsdtoa.h"
01859 
01860 static JSBool
01861 date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01862               jsval *rval)
01863 {
01864     jsdouble *date;
01865     char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
01866     JSString *str;
01867 
01868     date = date_getProlog(cx, obj, argv);
01869     if (!date)
01870         return JS_FALSE;
01871 
01872     numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);
01873     if (!numStr) {
01874         JS_ReportOutOfMemory(cx);
01875         return JS_FALSE;
01876     }
01877 
01878     bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
01879     if (!bytes) {
01880         JS_ReportOutOfMemory(cx);
01881         return JS_FALSE;
01882     }
01883 
01884     str = JS_NewString(cx, bytes, strlen(bytes));
01885     if (!str) {
01886         free(bytes);
01887         return JS_FALSE;
01888     }
01889     *rval = STRING_TO_JSVAL(str);
01890     return JS_TRUE;
01891 }
01892 #endif
01893 
01894 static JSBool
01895 date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01896               jsval *rval)
01897 {
01898     jsdouble *date = date_getProlog(cx, obj, argv);
01899     if (!date)
01900         return JS_FALSE;
01901     return date_format(cx, *date, FORMATSPEC_FULL, rval);
01902 }
01903 
01904 static JSBool
01905 date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01906              jsval *rval)
01907 {
01908     /* It is an error to call date_valueOf on a non-date object, but we don't
01909      * need to check for that explicitly here because every path calls
01910      * date_getProlog, which does the check.
01911      */
01912 
01913     /* If called directly with no arguments, convert to a time number. */
01914     if (argc == 0)
01915         return date_getTime(cx, obj, argc, argv, rval);
01916 
01917     /* Convert to number only if the hint was given, otherwise favor string. */
01918     if (argc == 1) {
01919         JSString *str, *str2;
01920 
01921         str = js_ValueToString(cx, argv[0]);
01922         if (!str)
01923             return JS_FALSE;
01924         str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
01925         if (js_EqualStrings(str, str2))
01926             return date_getTime(cx, obj, argc, argv, rval);
01927     }
01928     return date_toString(cx, obj, argc, argv, rval);
01929 }
01930 
01931 
01932 /*
01933  * creation and destruction
01934  */
01935 
01936 static JSFunctionSpec date_static_methods[] = {
01937     {"UTC",               date_UTC,               MAXARGS,0,0 },
01938     {"parse",             date_parse,             1,0,0 },
01939     {"now",               date_now,               0,0,0 },
01940     {0,0,0,0,0}
01941 };
01942 
01943 static JSFunctionSpec date_methods[] = {
01944     {"getTime",             date_getTime,           0,0,0 },
01945     {"getTimezoneOffset",   date_getTimezoneOffset, 0,0,0 },
01946     {"getYear",             date_getYear,           0,0,0 },
01947     {"getFullYear",         date_getFullYear,       0,0,0 },
01948     {"getUTCFullYear",      date_getUTCFullYear,    0,0,0 },
01949     {"getMonth",            date_getMonth,          0,0,0 },
01950     {"getUTCMonth",         date_getUTCMonth,       0,0,0 },
01951     {"getDate",             date_getDate,           0,0,0 },
01952     {"getUTCDate",          date_getUTCDate,        0,0,0 },
01953     {"getDay",              date_getDay,            0,0,0 },
01954     {"getUTCDay",           date_getUTCDay,         0,0,0 },
01955     {"getHours",            date_getHours,          0,0,0 },
01956     {"getUTCHours",         date_getUTCHours,       0,0,0 },
01957     {"getMinutes",          date_getMinutes,        0,0,0 },
01958     {"getUTCMinutes",       date_getUTCMinutes,     0,0,0 },
01959     {"getSeconds",          date_getUTCSeconds,     0,0,0 },
01960     {"getUTCSeconds",       date_getUTCSeconds,     0,0,0 },
01961     {"getMilliseconds",     date_getUTCMilliseconds,0,0,0 },
01962     {"getUTCMilliseconds",  date_getUTCMilliseconds,0,0,0 },
01963     {"setTime",             date_setTime,           1,0,0 },
01964     {"setYear",             date_setYear,           1,0,0 },
01965     {"setFullYear",         date_setFullYear,       3,0,0 },
01966     {"setUTCFullYear",      date_setUTCFullYear,    3,0,0 },
01967     {"setMonth",            date_setMonth,          2,0,0 },
01968     {"setUTCMonth",         date_setUTCMonth,       2,0,0 },
01969     {"setDate",             date_setDate,           1,0,0 },
01970     {"setUTCDate",          date_setUTCDate,        1,0,0 },
01971     {"setHours",            date_setHours,          4,0,0 },
01972     {"setUTCHours",         date_setUTCHours,       4,0,0 },
01973     {"setMinutes",          date_setMinutes,        3,0,0 },
01974     {"setUTCMinutes",       date_setUTCMinutes,     3,0,0 },
01975     {"setSeconds",          date_setSeconds,        2,0,0 },
01976     {"setUTCSeconds",       date_setUTCSeconds,     2,0,0 },
01977     {"setMilliseconds",     date_setMilliseconds,   1,0,0 },
01978     {"setUTCMilliseconds",  date_setUTCMilliseconds,1,0,0 },
01979     {"toUTCString",         date_toGMTString,       0,0,0 },
01980     {js_toLocaleString_str, date_toLocaleString,    0,0,0 },
01981     {"toLocaleDateString",  date_toLocaleDateString,0,0,0 },
01982     {"toLocaleTimeString",  date_toLocaleTimeString,0,0,0 },
01983     {"toLocaleFormat",      date_toLocaleFormat,    1,0,0 },
01984     {"toDateString",        date_toDateString,      0,0,0 },
01985     {"toTimeString",        date_toTimeString,      0,0,0 },
01986 #if JS_HAS_TOSOURCE
01987     {js_toSource_str,       date_toSource,          0,0,0 },
01988 #endif
01989     {js_toString_str,       date_toString,          0,0,0 },
01990     {js_valueOf_str,        date_valueOf,           0,0,0 },
01991     {0,0,0,0,0}
01992 };
01993 
01994 static jsdouble *
01995 date_constructor(JSContext *cx, JSObject* obj)
01996 {
01997     jsdouble *date;
01998 
01999     date = js_NewDouble(cx, 0.0, 0);
02000     if (!date)
02001         return NULL;
02002     OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
02003     return date;
02004 }
02005 
02006 static JSBool
02007 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
02008 {
02009     jsdouble *date;
02010     JSString *str;
02011     jsdouble d;
02012 
02013     /* Date called as function. */
02014     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
02015         int64 us, ms, us2ms;
02016         jsdouble msec_time;
02017 
02018         /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
02019          * so compute ms from PRMJ_Now.
02020          */
02021         us = PRMJ_Now();
02022         JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
02023         JSLL_DIV(ms, us, us2ms);
02024         JSLL_L2D(msec_time, ms);
02025 
02026         return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
02027     }
02028 
02029     /* Date called as constructor. */
02030     if (argc == 0) {
02031         int64 us, ms, us2ms;
02032         jsdouble msec_time;
02033 
02034         date = date_constructor(cx, obj);
02035         if (!date)
02036             return JS_FALSE;
02037 
02038         us = PRMJ_Now();
02039         JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
02040         JSLL_DIV(ms, us, us2ms);
02041         JSLL_L2D(msec_time, ms);
02042 
02043         *date = msec_time;
02044     } else if (argc == 1) {
02045         if (!JSVAL_IS_STRING(argv[0])) {
02046             /* the argument is a millisecond number */
02047             if (!js_ValueToNumber(cx, argv[0], &d))
02048                 return JS_FALSE;
02049             date = date_constructor(cx, obj);
02050             if (!date)
02051                 return JS_FALSE;
02052             *date = TIMECLIP(d);
02053         } else {
02054             /* the argument is a string; parse it. */
02055             date = date_constructor(cx, obj);
02056             if (!date)
02057                 return JS_FALSE;
02058 
02059             str = js_ValueToString(cx, argv[0]);
02060             if (!str)
02061                 return JS_FALSE;
02062 
02063             if (!date_parseString(str, date))
02064                 *date = *cx->runtime->jsNaN;
02065             *date = TIMECLIP(*date);
02066         }
02067     } else {
02068         jsdouble array[MAXARGS];
02069         uintN loop;
02070         jsdouble double_arg;
02071         jsdouble day;
02072         jsdouble msec_time;
02073 
02074         for (loop = 0; loop < MAXARGS; loop++) {
02075             if (loop < argc) {
02076                 if (!js_ValueToNumber(cx, argv[loop], &double_arg))
02077                     return JS_FALSE;
02078                 /* if any arg is NaN, make a NaN date object
02079                    and return */
02080                 if (!JSDOUBLE_IS_FINITE(double_arg)) {
02081                     date = date_constructor(cx, obj);
02082                     if (!date)
02083                         return JS_FALSE;
02084                     *date = *cx->runtime->jsNaN;
02085                     return JS_TRUE;
02086                 }
02087                 array[loop] = js_DoubleToInteger(double_arg);
02088             } else {
02089                 if (loop == 2) {
02090                     array[loop] = 1; /* Default the date argument to 1. */
02091                 } else {
02092                     array[loop] = 0;
02093                 }
02094             }
02095         }
02096 
02097         date = date_constructor(cx, obj);
02098         if (!date)
02099             return JS_FALSE;
02100 
02101         /* adjust 2-digit years into the 20th century */
02102         if (array[0] >= 0 && array[0] <= 99)
02103             array[0] += 1900;
02104 
02105         day = MakeDay(array[0], array[1], array[2]);
02106         msec_time = MakeTime(array[3], array[4], array[5], array[6]);
02107         msec_time = MakeDate(day, msec_time);
02108         msec_time = UTC(msec_time);
02109         *date = TIMECLIP(msec_time);
02110     }
02111     return JS_TRUE;
02112 }
02113 
02114 JSObject *
02115 js_InitDateClass(JSContext *cx, JSObject *obj)
02116 {
02117     JSObject *proto;
02118     jsdouble *proto_date;
02119 
02120     /* set static LocalTZA */
02121     LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
02122     proto = JS_InitClass(cx, obj, NULL, &js_DateClass, Date, MAXARGS,
02123                          NULL, date_methods, NULL, date_static_methods);
02124     if (!proto)
02125         return NULL;
02126 
02127     /* Alias toUTCString with toGMTString.  (ECMA B.2.6) */
02128     if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
02129         return NULL;
02130 
02131     /* Set the value of the Date.prototype date to NaN */
02132     proto_date = date_constructor(cx, proto);
02133     if (!proto_date)
02134         return NULL;
02135     *proto_date = *cx->runtime->jsNaN;
02136 
02137     return proto;
02138 }
02139 
02140 JS_FRIEND_API(JSObject *)
02141 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
02142 {
02143     JSObject *obj;
02144     jsdouble *date;
02145 
02146     obj = js_NewObject(cx, &js_DateClass, NULL, NULL);
02147     if (!obj)
02148         return NULL;
02149 
02150     date = date_constructor(cx, obj);
02151     if (!date)
02152         return NULL;
02153 
02154     *date = msec_time;
02155     return obj;
02156 }
02157 
02158 JS_FRIEND_API(JSObject *)
02159 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
02160                  int hour, int min, int sec)
02161 {
02162     JSObject *obj;
02163     jsdouble msec_time;
02164 
02165     msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
02166     obj = js_NewDateObjectMsec(cx, UTC(msec_time));
02167     return obj;
02168 }
02169 
02170 JS_FRIEND_API(JSBool)
02171 js_DateIsValid(JSContext *cx, JSObject* obj)
02172 {
02173     jsdouble *date = date_getProlog(cx, obj, NULL);
02174 
02175     if (!date || JSDOUBLE_IS_NaN(*date))
02176         return JS_FALSE;
02177     else
02178         return JS_TRUE;
02179 }
02180 
02181 JS_FRIEND_API(int)
02182 js_DateGetYear(JSContext *cx, JSObject* obj)
02183 {
02184     jsdouble *date = date_getProlog(cx, obj, NULL);
02185 
02186     /* Preserve legacy API behavior of returning 0 for invalid dates. */
02187     if (!date || JSDOUBLE_IS_NaN(*date))
02188         return 0;
02189     return (int) YearFromTime(LocalTime(*date));
02190 }
02191 
02192 JS_FRIEND_API(int)
02193 js_DateGetMonth(JSContext *cx, JSObject* obj)
02194 {
02195     jsdouble *date = date_getProlog(cx, obj, NULL);
02196 
02197     if (!date || JSDOUBLE_IS_NaN(*date))
02198         return 0;
02199     return (int) MonthFromTime(LocalTime(*date));
02200 }
02201 
02202 JS_FRIEND_API(int)
02203 js_DateGetDate(JSContext *cx, JSObject* obj)
02204 {
02205     jsdouble *date = date_getProlog(cx, obj, NULL);
02206 
02207     if (!date || JSDOUBLE_IS_NaN(*date))
02208         return 0;
02209     return (int) DateFromTime(LocalTime(*date));
02210 }
02211 
02212 JS_FRIEND_API(int)
02213 js_DateGetHours(JSContext *cx, JSObject* obj)
02214 {
02215     jsdouble *date = date_getProlog(cx, obj, NULL);
02216 
02217     if (!date || JSDOUBLE_IS_NaN(*date))
02218         return 0;
02219     return (int) HourFromTime(LocalTime(*date));
02220 }
02221 
02222 JS_FRIEND_API(int)
02223 js_DateGetMinutes(JSContext *cx, JSObject* obj)
02224 {
02225     jsdouble *date = date_getProlog(cx, obj, NULL);
02226 
02227     if (!date || JSDOUBLE_IS_NaN(*date))
02228         return 0;
02229     return (int) MinFromTime(LocalTime(*date));
02230 }
02231 
02232 JS_FRIEND_API(int)
02233 js_DateGetSeconds(JSContext *cx, JSObject* obj)
02234 {
02235     jsdouble *date = date_getProlog(cx, obj, NULL);
02236 
02237     if (!date || JSDOUBLE_IS_NaN(*date))
02238         return 0;
02239     return (int) SecFromTime(*date);
02240 }
02241 
02242 JS_FRIEND_API(void)
02243 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
02244 {
02245     jsdouble local;
02246     jsdouble *date = date_getProlog(cx, obj, NULL);
02247     if (!date)
02248         return;
02249     local = LocalTime(*date);
02250     /* reset date if it was NaN */
02251     if (JSDOUBLE_IS_NaN(local))
02252         local = 0;
02253     local = date_msecFromDate(year,
02254                               MonthFromTime(local),
02255                               DateFromTime(local),
02256                               HourFromTime(local),
02257                               MinFromTime(local),
02258                               SecFromTime(local),
02259                               msFromTime(local));
02260     *date = UTC(local);
02261 }
02262 
02263 JS_FRIEND_API(void)
02264 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
02265 {
02266     jsdouble local;
02267     jsdouble *date = date_getProlog(cx, obj, NULL);
02268     if (!date)
02269         return;
02270     local = LocalTime(*date);
02271     /* bail if date was NaN */
02272     if (JSDOUBLE_IS_NaN(local))
02273         return;
02274     local = date_msecFromDate(YearFromTime(local),
02275                               month,
02276                               DateFromTime(local),
02277                               HourFromTime(local),
02278                               MinFromTime(local),
02279                               SecFromTime(local),
02280                               msFromTime(local));
02281     *date = UTC(local);
02282 }
02283 
02284 JS_FRIEND_API(void)
02285 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
02286 {
02287     jsdouble local;
02288     jsdouble *datep = date_getProlog(cx, obj, NULL);
02289     if (!datep)
02290         return;
02291     local = LocalTime(*datep);
02292     if (JSDOUBLE_IS_NaN(local))
02293         return;
02294     local = date_msecFromDate(YearFromTime(local),
02295                               MonthFromTime(local),
02296                               date,
02297                               HourFromTime(local),
02298                               MinFromTime(local),
02299                               SecFromTime(local),
02300                               msFromTime(local));
02301     *datep = UTC(local);
02302 }
02303 
02304 JS_FRIEND_API(void)
02305 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
02306 {
02307     jsdouble local;
02308     jsdouble *date = date_getProlog(cx, obj, NULL);
02309     if (!date)
02310         return;
02311     local = LocalTime(*date);
02312     if (JSDOUBLE_IS_NaN(local))
02313         return;
02314     local = date_msecFromDate(YearFromTime(local),
02315                               MonthFromTime(local),
02316                               DateFromTime(local),
02317                               hours,
02318                               MinFromTime(local),
02319                               SecFromTime(local),
02320                               msFromTime(local));
02321     *date = UTC(local);
02322 }
02323 
02324 JS_FRIEND_API(void)
02325 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
02326 {
02327     jsdouble local;
02328     jsdouble *date = date_getProlog(cx, obj, NULL);
02329     if (!date)
02330         return;
02331     local = LocalTime(*date);
02332     if (JSDOUBLE_IS_NaN(local))
02333         return;
02334     local = date_msecFromDate(YearFromTime(local),
02335                               MonthFromTime(local),
02336                               DateFromTime(local),
02337                               HourFromTime(local),
02338                               minutes,
02339                               SecFromTime(local),
02340                               msFromTime(local));
02341     *date = UTC(local);
02342 }
02343 
02344 JS_FRIEND_API(void)
02345 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
02346 {
02347     jsdouble local;
02348     jsdouble *date = date_getProlog(cx, obj, NULL);
02349     if (!date)
02350         return;
02351     local = LocalTime(*date);
02352     if (JSDOUBLE_IS_NaN(local))
02353         return;
02354     local = date_msecFromDate(YearFromTime(local),
02355                               MonthFromTime(local),
02356                               DateFromTime(local),
02357                               HourFromTime(local),
02358                               MinFromTime(local),
02359                               seconds,
02360                               msFromTime(local));
02361     *date = UTC(local);
02362 }
02363 
02364 JS_FRIEND_API(jsdouble)
02365 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
02366 {
02367     jsdouble *date = date_getProlog(cx, obj, NULL);
02368     if (!date || JSDOUBLE_IS_NaN(*date))
02369         return 0;
02370     return (*date);
02371 }