home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / src / jsdate.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  53.5 KB  |  1,922 lines

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.  * JS date methods.
  21.  */
  22. #include <math.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include "prtypes.h"
  26. #include "prprf.h"
  27. #include "prmjtime.h"
  28. #include "prlog.h"
  29. #include "jsapi.h"
  30. #include "jsconfig.h"
  31. #include "jscntxt.h"
  32. #include "jsdate.h"
  33. #include "jslock.h"
  34. #include "jsnum.h"
  35. #include "jsobj.h"
  36. #include "jsstr.h"
  37.  
  38. #include <stdio.h>
  39.  
  40. /*
  41.  * The JS 'Date' object is patterned after the Java 'Date' object.
  42.  * Here is an script:
  43.  *
  44.  *    today = new Date();
  45.  *
  46.  *    print(today.toLocaleString());
  47.  *
  48.  *    weekDay = today.getDay();
  49.  *
  50.  *
  51.  * These Java (and ECMA-262) methods are supported:
  52.  *
  53.  *     UTC
  54.  *     getDate (getUTCDate)
  55.  *     getDay (getUTCDay)
  56.  *     getHours (getUTCHours)
  57.  *     getMinutes (getUTCMinutes)
  58.  *     getMonth (getUTCMonth)
  59.  *     getSeconds (getUTCSeconds)
  60.  *     getMilliseconds (getUTCMilliseconds)
  61.  *     getTime
  62.  *     getTimezoneOffset
  63.  *     getYear
  64.  *     getFullYear (getUTCFullYear)
  65.  *     parse
  66.  *     setDate (setUTCDate)
  67.  *     setHours (setUTCHours)
  68.  *     setMinutes (setUTCMinutes)
  69.  *     setMonth (setUTCMonth)
  70.  *     setSeconds (setUTCSeconds)
  71.  *     setMilliseconds (setUTCMilliseconds)
  72.  *     setTime
  73.  *     setYear (setFullYear, setUTCFullYear)
  74.  *     toGMTString (toUTCString)
  75.  *     toLocaleString
  76.  *     toString
  77.  *
  78.  *
  79.  * These Java methods are not supported
  80.  *
  81.  *     setDay
  82.  *     before
  83.  *     after
  84.  *     equals
  85.  *     hashCode
  86.  */
  87.  
  88. /*
  89.  * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262
  90.  * language definition and reduce dependence on NSPR.  NSPR is used to
  91.  * get the current time in milliseconds, the time zone offset, and the
  92.  * daylight savings time offset for a given time.  NSPR is also used
  93.  * for Date.toLocaleString() (only), for locale-specific formatting.
  94.  
  95.  * To do:
  96.  * (I did some performance tests by timing how long it took to run what
  97.  *  I had of the js ECMA conformance tests.)
  98.  *
  99.  * - look at saving results across multiple calls to supporting
  100.  * functions; the toString functions compute some of the same values
  101.  * multiple times.  Although - I took a quick stab at this, and I lost
  102.  * rather than gained.  (Fractionally.)  Hard to tell what compilers/processors
  103.  * are doing these days.
  104.  * - look at tweaking function return types to return double instead
  105.  * of int; this seems to make things run slightly faster sometimes.
  106.  * (though it could be architecture-dependent.)  It'd be good to see
  107.  * how this does on win32.  (Tried it on irix.)  Types could use a
  108.  * general going-over.
  109.  */
  110.  
  111. /*
  112.  * Supporting functions - ECMA 15.9.1.*
  113.  */
  114.  
  115. #define HalfTimeDomain  8.64e15
  116. #define HoursPerDay     24.0
  117. #define MinutesPerDay   (HoursPerDay * MinutesPerHour)
  118. #define MinutesPerHour  60.0
  119. #define SecondsPerDay   (MinutesPerDay * SecondsPerMinute)
  120. #define SecondsPerHour  (MinutesPerHour * SecondsPerMinute)
  121. #define SecondsPerMinute 60.0
  122. #define msPerDay        (SecondsPerDay * msPerSecond)
  123. #define msPerHour       (SecondsPerHour * msPerSecond)
  124. #define msPerMinute     (SecondsPerMinute * msPerSecond)
  125. #define msPerSecond     1000.0
  126.  
  127. #define Day(t)          floor((t) / msPerDay)
  128.  
  129. static jsdouble
  130. TimeWithinDay(jsdouble t)
  131. {
  132.     jsdouble result;
  133.     result = fmod(t, msPerDay);
  134.     if (result < 0)
  135.         result += msPerDay;
  136.     return result;
  137. }
  138.  
  139. #define DaysInYear(y)   ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0))  \
  140.                          ? 366 : 365)
  141.  
  142. /* math here has to be f.p, because we need
  143.  *  floor((1968 - 1969) / 4) == -1
  144.  */
  145. #define DayFromYear(y)  (365 * ((y)-1970) + floor(((y)-1969)/4.0)            \
  146.                          - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
  147. #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
  148.  
  149. static jsint
  150. YearFromTime(jsdouble t)
  151. {
  152.     jsint lo = (jsint) floor((t / msPerDay) / 366) + 1970;
  153.     jsint hi = (jsint) floor((t / msPerDay) / 365) + 1970;
  154.     jsint mid;
  155.  
  156.     /* above doesn't work for negative dates... */
  157.     if (hi < lo) {
  158.         jsint temp = lo;
  159.         lo = hi;
  160.         hi = temp;
  161.     }
  162.  
  163.     /* Use a simple binary search algorithm to find the right
  164.        year.  This seems like brute force... but the computation
  165.        of hi and lo years above lands within one year of the
  166.        correct answer for years within a thousand years of
  167.        1970; the loop below only requires six iterations
  168.        for year 270000. */
  169.     while (hi > lo) {
  170.         mid = (hi + lo) / 2;
  171.         if (TimeFromYear(mid) > t) {
  172.             hi = mid - 1;
  173.         } else {
  174.             if (TimeFromYear(mid) <= t) {
  175.                 jsint temp = mid + 1;
  176.                 if (TimeFromYear(temp) > t) {
  177.                     return mid;
  178.                 }
  179.                 lo = mid + 1;
  180.             }
  181.         }
  182.     }
  183.     return lo;
  184. }
  185.  
  186. #define InLeapYear(t)   (JSBool) (DaysInYear(YearFromTime(t)) == 366)
  187.  
  188. #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
  189.  
  190. /*
  191.  * The following array contains the day of year for the first day of
  192.  * each month, where index 0 is January, and day 0 is January 1.
  193.  */
  194. static jsdouble firstDayOfMonth[2][12] = {
  195.     {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
  196.     {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
  197. };
  198.  
  199. #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
  200.  
  201. static intN
  202. MonthFromTime(jsdouble t)
  203. {
  204.     intN d, step;
  205.     jsint year = YearFromTime(t);
  206.     d = DayWithinYear(t, year);
  207.  
  208.     if (d < (step = 31))
  209.         return 0;
  210.     step += (InLeapYear(t) ? 29 : 28);
  211.     if (d < step)
  212.         return 1;
  213.     if (d < (step += 31))
  214.         return 2;
  215.     if (d < (step += 30))
  216.         return 3;
  217.     if (d < (step += 31))
  218.         return 4;
  219.     if (d < (step += 30))
  220.         return 5;
  221.     if (d < (step += 31))
  222.         return 6;
  223.     if (d < (step += 31))
  224.         return 7;
  225.     if (d < (step += 30))
  226.         return 8;
  227.     if (d < (step += 31))
  228.         return 9;
  229.     if (d < (step += 30))
  230.         return 10;
  231.     return 11;
  232. }
  233.  
  234. static intN
  235. DateFromTime(jsdouble t)
  236. {
  237.     intN d, step, next;
  238.     jsint year = YearFromTime(t);
  239.     d = DayWithinYear(t, year);
  240.  
  241.     if (d <= (next = 30))
  242.         return d + 1;
  243.     step = next;
  244.     next += (InLeapYear(t) ? 29 : 28);
  245.     if (d <= next)
  246.         return d - step;
  247.     step = next;
  248.     if (d <= (next += 31))
  249.         return d - step;
  250.     step = next;
  251.     if (d <= (next += 30))
  252.         return d - step;
  253.     step = next;
  254.     if (d <= (next += 31))
  255.         return d - step;
  256.     step = next;
  257.     if (d <= (next += 30))
  258.         return d - step;
  259.     step = next;
  260.     if (d <= (next += 31))
  261.         return d - step;
  262.     step = next;
  263.     if (d <= (next += 31))
  264.         return d - step;
  265.     step = next;
  266.     if (d <= (next += 30))
  267.         return d - step;
  268.     step = next;
  269.     if (d <= (next += 31))
  270.         return d - step;
  271.     step = next;
  272.     if (d <= (next += 30))
  273.         return d - step;
  274.     step = next;
  275.     return d - step;
  276. }
  277.  
  278. static intN
  279. WeekDay(jsdouble t)
  280. {
  281.     jsint result;
  282.     result = (jsint) Day(t) + 4;
  283.     result = result % 7;
  284.     if (result < 0)
  285.         result += 7;
  286.     return (intN) result;
  287. }
  288.  
  289. /* LocalTZA gets set by js_InitDateClass() */
  290. static jsdouble LocalTZA;
  291.  
  292. static jsdouble
  293. DaylightSavingTA(jsdouble t)
  294. {
  295.     int64 PR_t;
  296.     int64 ms2us;
  297.     int64 offset;
  298.     jsdouble result;
  299.  
  300.     /* abort if NaN */
  301.     if (t != t)
  302.         return t;
  303.  
  304.     /* put our t in an LL, and map it to usec for prtime */
  305.     LL_D2L(PR_t, t);
  306.     LL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
  307.     LL_MUL(PR_t, PR_t, ms2us);
  308.  
  309.     offset = PRMJ_DSTOffset(PR_t);
  310.  
  311.     LL_DIV(offset, offset, ms2us);
  312.     LL_L2D(result, offset);
  313.     return result;
  314. }
  315.  
  316. #define LocalTime(t)    ((t) + LocalTZA + DaylightSavingTA(t))
  317. /* #define UTC(t)          ((t) - LocalTZA - DaylightSavingTA((t) - LocalTZA)) */
  318. static jsdouble
  319. UTC(jsdouble t)
  320. {
  321. /*     fprintf(stderr, "time %f\n", t); */
  322. /*     fprintf(stderr, "LocalTZA %f\n", LocalTZA); */
  323. /*     fprintf(stderr, "DaylightSavingTA %f\n", DaylightSavingTA(t - LocalTZA)); */
  324.  
  325.     return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
  326. }
  327.  
  328. static intN
  329. HourFromTime(jsdouble t)
  330. {
  331.     intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
  332.     if (result < 0)
  333.         result += (intN)HoursPerDay;
  334.     return result;
  335. }
  336.  
  337. static intN
  338. MinFromTime(jsdouble t)
  339. {
  340.     intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
  341.     if (result < 0)
  342.         result += (intN)MinutesPerHour;
  343.     return result;
  344. }
  345.  
  346. static intN
  347. SecFromTime(jsdouble t)
  348. {
  349.     intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
  350.     if (result < 0)
  351.         result += (intN)SecondsPerMinute;
  352.     return result;
  353. }
  354.  
  355. static intN
  356. msFromTime(jsdouble t)
  357. {
  358.     intN result = (intN) fmod(t, msPerSecond);
  359.     if (result < 0)
  360.         result += (intN)msPerSecond;
  361.     return result;
  362. }
  363.  
  364. #define MakeTime(hour, min, sec, ms) \
  365. (((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms)
  366.  
  367. static jsdouble
  368. MakeDay(jsdouble year, jsdouble month, jsdouble date)
  369. {
  370.     jsdouble result;
  371.     JSBool leap;
  372.     jsdouble yearday;
  373.     jsdouble monthday;
  374.  
  375.     year += floor(month / 12);
  376.  
  377.     month = fmod(month, 12);
  378.     if (month < 0)
  379.         month += 12;
  380.  
  381.     leap = (DaysInYear((jsint) year) == 366);
  382.  
  383.     yearday = floor(TimeFromYear(year) / msPerDay);
  384.     monthday = DayFromMonth(month, leap);
  385.  
  386.     result = yearday
  387.              + monthday
  388.              + date - 1;
  389.     return result;
  390. }
  391.  
  392. #define MakeDate(day, time) (day * msPerDay + time)
  393.  
  394. #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
  395.                       && !((d < 0 ? -d : d) > HalfTimeDomain)) \
  396.                      ? js_DoubleToInteger(d + (+0.)) : *(cx->runtime->jsNaN))
  397.  
  398. /**
  399.  * end of ECMA 'support' functions
  400.  */
  401.  
  402. /*
  403.  * Other Support routines and definitions
  404.  */
  405.  
  406. static JSClass date_class = {
  407.     "Date",
  408.     JSCLASS_HAS_PRIVATE,
  409.     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
  410.     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
  411. };
  412.  
  413. /* for use by date_parse */
  414.  
  415. static char* wtb[] = {
  416.     "am", "pm",
  417.     "monday", "tuesday", "wednesday", "thursday", "friday",
  418.     "saturday", "sunday",
  419.     "january", "february", "march", "april", "may", "june",
  420.     "july", "august", "september", "october", "november", "december",
  421.     "gmt", "ut", "utc",
  422.     "est", "edt",
  423.     "cst", "cdt",
  424.     "mst", "mdt",
  425.     "pst", "pdt"
  426.     /* time zone table needs to be expanded */
  427. };
  428.  
  429. static int ttb[] = {
  430.     0, 1, 0, 0, 0, 0, 0, 0, 0,
  431.     2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
  432.     10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */
  433.     10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */
  434.     10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */
  435.     10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */
  436.     10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */
  437. };
  438.  
  439. /* helper for date_parse */
  440. static JSBool
  441. date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
  442.                    int count, int ignoreCase)
  443. {
  444.     JSBool result = JS_FALSE;
  445.     /* return true if matches, otherwise, false */
  446.  
  447.     while (count > 0 && s1[s1off] && s2[s2off]) {
  448.         if (ignoreCase) {
  449.             if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
  450.                 break;
  451.             }
  452.         } else {
  453.             if ((jschar)s1[s1off] != s2[s2off]) {
  454.                 break;
  455.             }
  456.         }
  457.         s1off++;
  458.         s2off++;
  459.         count--;
  460.     }
  461.  
  462.     if (count == 0) {
  463.         result = JS_TRUE;
  464.     }
  465.  
  466.     return result;
  467. }
  468.  
  469. /* find UTC time from given date... no 1900 correction! */
  470. static jsdouble
  471. date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
  472.                   jsdouble min, jsdouble sec, jsdouble msec)
  473. {
  474.     jsdouble day;
  475.     jsdouble time;
  476.     jsdouble result;
  477.  
  478.     day = MakeDay(year, mon, mday);
  479.     time = MakeTime(hour, min, sec, msec);
  480.     result = MakeDate(day, time);
  481.     return result;
  482. }
  483.  
  484. /*
  485.  * See ECMA 15.9.4.[3-10];
  486.  */
  487. /* XXX this function must be above date_parseString to avoid a
  488.    horrid bug in the Win16 1.52 compiler */
  489. #define MAXARGS        7
  490. static JSBool
  491. date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  492. {
  493.     jsdouble array[MAXARGS];
  494.     uintN loop;
  495.     jsdouble d;
  496.  
  497.     for (loop = 0; loop < MAXARGS; loop++) {
  498.         if (loop < argc) {
  499.             if (!JS_ValueToNumber(cx, argv[loop], &d))
  500.                 return JS_FALSE;
  501.             /* return NaN if any arg is NaN */
  502.             if (!JSDOUBLE_IS_FINITE(d)) {
  503.                 return js_NewNumberValue(cx, d, rval);
  504.             }
  505.             array[loop] = floor(d);
  506.         } else {
  507.             array[loop] = 0;
  508.         }
  509.     }
  510.  
  511.     /* adjust 2-digit years into the 20th century */
  512.     if (array[0] >= 0 && array[0] <= 99)
  513.         array[0] += 1900;
  514.  
  515.     /* if we got a 0 for 'date' (which is out of range)
  516.      * pretend it's a 1.  (So Date.UTC(1972, 5) works) */
  517.     if (array[2] < 1)
  518.         array[2] = 1;
  519.  
  520.     d = date_msecFromDate(array[0], array[1], array[2],
  521.                               array[3], array[4], array[5], array[6]);
  522.     d = TIMECLIP(d);
  523.  
  524.     return js_NewNumberValue(cx, d, rval);
  525. }
  526.  
  527. static JSBool
  528. date_parseString(const jschar *s, jsdouble *result)
  529. {
  530.     jsdouble msec;
  531.  
  532.     int year = -1;
  533.     int mon = -1;
  534.     int mday = -1;
  535.     int hour = -1;
  536.     int min = -1;
  537.     int sec = -1;
  538.     int c = -1;
  539.     int i = 0;
  540.     int n = -1;
  541.     jsdouble tzoffset = -1;  /* was an int, overflowed on win16!!! */
  542.     int prevc = 0;
  543.     int limit = 0;
  544.     JSBool seenplusminus = JS_FALSE;
  545.  
  546.     if (s == 0)
  547.         goto syntax;
  548.     limit = js_strlen(s);
  549.     while (i < limit) {
  550.         c = s[i];
  551.         i++;
  552.         if (c <= ' ' || c == ',' || c == '-') {
  553.             if (c == '-' && '0' <= s[i] && s[i] <= '9') {
  554.               prevc = c;
  555.             }
  556.             continue;
  557.         }
  558.         if (c == '(') { /* comments) */
  559.             int depth = 1;
  560.             while (i < limit) {
  561.                 c = s[i];
  562.                 i++;
  563.                 if (c == '(') depth++;
  564.                 else if (c == ')')
  565.                     if (--depth <= 0)
  566.                         break;
  567.             }
  568.             continue;
  569.         }
  570.         if ('0' <= c && c <= '9') {
  571.             n = c - '0';
  572.             while (i < limit && '0' <= (c = s[i]) && c <= '9') {
  573.                 n = n * 10 + c - '0';
  574.                 i++;
  575.             }
  576.  
  577.             /* allow TZA before the year, so
  578.              * 'Wed Nov 05 21:49:11 GMT-0800 1997'
  579.              * works */
  580.  
  581.             /* uses of seenplusminus allow : in TZA, so Java
  582.              * no-timezone style of GMT+4:30 works
  583.              */
  584.  
  585.             if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
  586.                 /* make ':' case below change tzoffset */
  587.                 seenplusminus = JS_TRUE;
  588.  
  589.                 /* offset */
  590.                 if (n < 24)
  591.                     n = n * 60; /* EG. "GMT-3" */
  592.                 else
  593.                     n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
  594.                 if (prevc == '+')       /* plus means east of GMT */
  595.                     n = -n;
  596.                 if (tzoffset != 0 && tzoffset != -1)
  597.                     goto syntax;
  598.                 tzoffset = n;
  599.             } else if (n >= 70  ||
  600.                        (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
  601.                 if (year >= 0)
  602.                     goto syntax;
  603.                 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
  604.                     year = n < 100 ? n + 1900 : n;
  605.                 else
  606.                     goto syntax;
  607.             } else if (c == ':') {
  608.                 if (hour < 0)
  609.                     hour = /*byte*/ n;
  610.                 else if (min < 0)
  611.                     min = /*byte*/ n;
  612.                 else
  613.                     goto syntax;
  614.             } else if (c == '/') {
  615.                 if (mon < 0)
  616.                     mon = /*byte*/ n-1;
  617.                 else if (mday < 0)
  618.                     mday = /*byte*/ n;
  619.                 else
  620.                     goto syntax;
  621.             } else if (i < limit && c != ',' && c > ' ' && c != '-') {
  622.                 goto syntax;
  623.             } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */
  624.                 if (tzoffset < 0)
  625.                     tzoffset -= n;
  626.                 else
  627.                     tzoffset += n;
  628.             } else if (hour >= 0 && min < 0) {
  629.                 min = /*byte*/ n;
  630.             } else if (min >= 0 && sec < 0) {
  631.                 sec = /*byte*/ n;
  632.             } else if (mday < 0) {
  633.                 mday = /*byte*/ n;
  634.             } else {
  635.                 goto syntax;
  636.             }
  637.             prevc = 0;
  638.         } else if (c == '/' || c == ':' || c == '+' || c == '-') {
  639.             prevc = c;
  640.         } else {
  641.             int st = i - 1;
  642.             int k;
  643.             while (i < limit) {
  644.                 c = s[i];
  645.                 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
  646.                     break;
  647.                 i++;
  648.             }
  649.             if (i <= st + 1)
  650.                 goto syntax;
  651.             for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
  652.                 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
  653.                     int action = ttb[k];
  654.                     if (action != 0)
  655.                         if (action == 1) /* pm */
  656.                             if (hour > 12 || hour < 0)
  657.                                 goto syntax;
  658.                             else
  659.                                 hour += 12;
  660.                         else if (action <= 13) /* month! */
  661.                             if (mon < 0)
  662.                                 mon = /*byte*/ (action - 2);
  663.                             else
  664.                                 goto syntax;
  665.                         else
  666.                             tzoffset = action - 10000;
  667.                     break;
  668.                 }
  669.             if (k < 0)
  670.                 goto syntax;
  671.             prevc = 0;
  672.         }
  673.     }
  674.     if (year < 0 || mon < 0 || mday < 0)
  675.         goto syntax;
  676.     if (sec < 0)
  677.         sec = 0;
  678.     if (min < 0)
  679.         min = 0;
  680.     if (hour < 0)
  681.         hour = 0;
  682.     if (tzoffset == -1) { /* no time zone specified, have to use local */
  683.         jsdouble time;
  684.         time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
  685.  
  686.         *result = UTC(time);
  687.         return JS_TRUE;
  688.     }
  689.  
  690.     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
  691.     msec += tzoffset * msPerMinute;
  692.     *result = msec;
  693.     return JS_TRUE;
  694.  
  695. syntax:
  696.     /* syntax error */
  697.     *result = 0;
  698.     return JS_FALSE;
  699. }
  700.  
  701. static JSBool
  702. date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  703. {
  704.     JSString *str;
  705.     jsdouble result;
  706.  
  707.     str = JS_ValueToString(cx, argv[0]);
  708.     if (!str)
  709.         return JS_FALSE;
  710.     if (!date_parseString(str->chars, &result)) {
  711.         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
  712.         return JS_TRUE;
  713.     }
  714.  
  715.     result = TIMECLIP(result);
  716.     return js_NewNumberValue(cx, result, rval);
  717. }
  718.  
  719. /*
  720.  * Check that obj is an object of class Date,
  721.  * and get the date value.  Return NULL on failure,
  722.  * which should be out of band, because the value
  723.  * not publicly accessable.
  724.  */
  725. static jsdouble *
  726. date_getProlog(JSContext *cx, JSObject *obj, jsval *argv) {
  727.     jsdouble *result;
  728.     if (!JS_InstanceOf(cx, obj, &date_class, argv)) {
  729.         return NULL;
  730.     }
  731.     JS_LOCK(cx);
  732.     result = JSVAL_TO_DOUBLE(OBJ_GET_SLOT(obj, JSSLOT_PRIVATE));
  733.     JS_UNLOCK(cx);
  734.     return result;
  735. }
  736.  
  737. /*
  738.  * See ECMA 15.9.5.4 thru 15.9.5.23
  739.  */
  740. static JSBool
  741. date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  742. {
  743.     jsdouble *date = date_getProlog(cx, obj, argv);
  744.     if (!date)
  745.         return JS_FALSE;
  746.  
  747.     return js_NewNumberValue(cx, *date, rval);
  748. }
  749.  
  750. static JSBool
  751. date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  752. {
  753.     jsdouble result;
  754.     jsdouble *date = date_getProlog(cx, obj, argv);
  755.     if (!date)
  756.         return JS_FALSE;
  757.     result = *date;
  758.  
  759.     if (!JSDOUBLE_IS_FINITE(result))
  760.         return js_NewNumberValue(cx, result, rval);
  761.  
  762.     result = YearFromTime(LocalTime(result));
  763.     result -= 1900;
  764.     return js_NewNumberValue(cx, result, rval);
  765. }
  766.  
  767. static JSBool
  768. date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  769.                  jsval *rval)
  770. {
  771.     jsdouble result;
  772.     jsdouble *date = date_getProlog(cx, obj, argv);
  773.     if (!date)
  774.         return JS_FALSE;
  775.     result = *date;
  776.  
  777.     if (!JSDOUBLE_IS_FINITE(result))
  778.         return js_NewNumberValue(cx, result, rval);
  779.  
  780.     result = YearFromTime(LocalTime(result));
  781.     return js_NewNumberValue(cx, result, rval);
  782. }
  783.  
  784. static JSBool
  785. date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  786.                     jsval *rval)
  787. {
  788.     jsdouble result;
  789.     jsdouble *date = date_getProlog(cx, obj, argv);
  790.     if (!date)
  791.         return JS_FALSE;
  792.     result = *date;
  793.  
  794.     if (!JSDOUBLE_IS_FINITE(result))
  795.         return js_NewNumberValue(cx, result, rval);
  796.  
  797.     result = YearFromTime(result);
  798.     return js_NewNumberValue(cx, result, rval);
  799. }
  800.  
  801. static JSBool
  802. date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  803.               jsval *rval)
  804. {
  805.     jsdouble result;
  806.     jsdouble *date = date_getProlog(cx, obj, argv);
  807.     if (!date)
  808.         return JS_FALSE;
  809.     result = *date;
  810.  
  811.     if (!JSDOUBLE_IS_FINITE(result))
  812.         return js_NewNumberValue(cx, result, rval);
  813.  
  814.     result = MonthFromTime(LocalTime(result));
  815.     return js_NewNumberValue(cx, result, rval);
  816. }
  817.  
  818. static JSBool
  819. date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  820.                  jsval *rval)
  821. {
  822.     jsdouble result;
  823.     jsdouble *date = date_getProlog(cx, obj, argv);
  824.     if (!date)
  825.         return JS_FALSE;
  826.     result = *date;
  827.  
  828.     if (!JSDOUBLE_IS_FINITE(result))
  829.         return js_NewNumberValue(cx, result, rval);
  830.  
  831.     result = MonthFromTime(result);
  832.     return js_NewNumberValue(cx, result, rval);
  833. }
  834.  
  835. static JSBool
  836. date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  837. {
  838.     jsdouble result;
  839.     jsdouble *date = date_getProlog(cx, obj, argv);
  840.     if (!date)
  841.         return JS_FALSE;
  842.     result = *date;
  843.  
  844.     if (!JSDOUBLE_IS_FINITE(result))
  845.         return js_NewNumberValue(cx, result, rval);
  846.  
  847.     result = LocalTime(result);
  848.     result = DateFromTime(result);
  849.     return js_NewNumberValue(cx, result, rval);
  850. }
  851.  
  852. static JSBool
  853. date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  854.                 jsval *rval)
  855. {
  856.     jsdouble result;
  857.     jsdouble *date = date_getProlog(cx, obj, argv);
  858.     if (!date)
  859.         return JS_FALSE;
  860.     result = *date;
  861.  
  862.     if (!JSDOUBLE_IS_FINITE(result))
  863.         return js_NewNumberValue(cx, result, rval);
  864.  
  865.     result = DateFromTime(result);
  866.     return js_NewNumberValue(cx, result, rval);
  867. }
  868.  
  869. static JSBool
  870. date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  871. {
  872.     jsdouble result;
  873.     jsdouble *date = date_getProlog(cx, obj, argv);
  874.     if (!date)
  875.         return JS_FALSE;
  876.     result = *date;
  877.  
  878.     if (!JSDOUBLE_IS_FINITE(result))
  879.         return js_NewNumberValue(cx, result, rval);
  880.  
  881.     result = LocalTime(result);
  882.     result = WeekDay(result);
  883.     return js_NewNumberValue(cx, result, rval);
  884. }
  885.  
  886. static JSBool
  887. date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  888.                jsval *rval)
  889. {
  890.     jsdouble result;
  891.     jsdouble *date = date_getProlog(cx, obj, argv);
  892.     if (!date)
  893.         return JS_FALSE;
  894.     result = *date;
  895.  
  896.     if (!JSDOUBLE_IS_FINITE(result))
  897.         return js_NewNumberValue(cx, result, rval);
  898.  
  899.     result = WeekDay(result);
  900.     return js_NewNumberValue(cx, result, rval);
  901. }
  902.  
  903. static JSBool
  904. date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  905.               jsval *rval)
  906. {
  907.     jsdouble result;
  908.     jsdouble *date = date_getProlog(cx, obj, argv);
  909.     if (!date)
  910.         return JS_FALSE;
  911.     result = *date;
  912.  
  913.     if (!JSDOUBLE_IS_FINITE(result))
  914.         return js_NewNumberValue(cx, result, rval);
  915.  
  916.     result = HourFromTime(LocalTime(result));
  917.     return js_NewNumberValue(cx, result, rval);
  918. }
  919.  
  920. static JSBool
  921. date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  922.                  jsval *rval)
  923. {
  924.     jsdouble result;
  925.     jsdouble *date = date_getProlog(cx, obj, argv);
  926.     if (!date)
  927.         return JS_FALSE;
  928.     result = *date;
  929.  
  930.     if (!JSDOUBLE_IS_FINITE(result))
  931.         return js_NewNumberValue(cx, result, rval);
  932.  
  933.     result = HourFromTime(result);
  934.     return js_NewNumberValue(cx, result, rval);
  935. }
  936.  
  937. static JSBool
  938. date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  939.                 jsval *rval)
  940. {
  941.     jsdouble result;
  942.     jsdouble *date = date_getProlog(cx, obj, argv);
  943.     if (!date)
  944.         return JS_FALSE;
  945.     result = *date;
  946.  
  947.     if (!JSDOUBLE_IS_FINITE(result))
  948.         return js_NewNumberValue(cx, result, rval);
  949.  
  950.     result = MinFromTime(LocalTime(result));
  951.     return js_NewNumberValue(cx, result, rval);
  952. }
  953.  
  954. static JSBool
  955. date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  956.                    jsval *rval)
  957. {
  958.     jsdouble result;
  959.     jsdouble *date = date_getProlog(cx, obj, argv);
  960.     if (!date)
  961.         return JS_FALSE;
  962.     result = *date;
  963.  
  964.     if (!JSDOUBLE_IS_FINITE(result))
  965.         return js_NewNumberValue(cx, result, rval);
  966.  
  967.     result = MinFromTime(result);
  968.     return js_NewNumberValue(cx, result, rval);
  969. }
  970.  
  971. /* Date.getSeconds is mapped to getUTCSeconds */
  972.  
  973. static JSBool
  974. date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  975.                 jsval *rval)
  976. {
  977.     jsdouble result;
  978.     jsdouble *date = date_getProlog(cx, obj, argv);
  979.     if (!date)
  980.         return JS_FALSE;
  981.     result = *date;
  982.  
  983.     if (!JSDOUBLE_IS_FINITE(result))
  984.         return js_NewNumberValue(cx, result, rval);
  985.  
  986.     result = SecFromTime(result);
  987.     return js_NewNumberValue(cx, result, rval);
  988. }
  989.  
  990. /* Date.getMilliseconds is mapped to getUTCMilliseconds */
  991.  
  992. static JSBool
  993. date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  994.                      jsval *rval)
  995. {
  996.     jsdouble result;
  997.     jsdouble *date = date_getProlog(cx, obj, argv);
  998.     if (!date)
  999.         return JS_FALSE;
  1000.     result = *date;
  1001.  
  1002.     if (!JSDOUBLE_IS_FINITE(result))
  1003.         return js_NewNumberValue(cx, result, rval);
  1004.  
  1005.     result = msFromTime(result);
  1006.     return js_NewNumberValue(cx, result, rval);
  1007. }
  1008.  
  1009. static JSBool
  1010. date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  1011.                        jsval *rval)
  1012. {
  1013.     jsdouble result;
  1014.     jsdouble *date = date_getProlog(cx, obj, argv);
  1015.     if (!date)
  1016.         return JS_FALSE;
  1017.     result = *date;
  1018.  
  1019.     /*
  1020.      * Return the time zone offset in minutes for the current locale
  1021.      * that is appropriate for this time. This value would be a
  1022.      * constant except for daylight savings time.
  1023.      */
  1024.     result = (result - LocalTime(result)) / msPerMinute;
  1025.     return js_NewNumberValue(cx, result, rval);
  1026. }
  1027.  
  1028. static JSBool
  1029. date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1030. {
  1031.     jsdouble result;
  1032.     jsdouble *date = date_getProlog(cx, obj, argv);
  1033.     if (!date)
  1034.         return JS_FALSE;
  1035.  
  1036.     if (!JS_ValueToNumber(cx, argv[0], &result))
  1037.         return JS_FALSE;
  1038.  
  1039.     *date = result;
  1040.     return js_NewNumberValue(cx, result, rval);
  1041. }
  1042.  
  1043. static JSBool
  1044. date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  1045.               uintN maxargs, JSBool local, jsval *rval)
  1046. {
  1047.     uintN i;
  1048.     jsdouble args[4], *argp, *stop;
  1049.     jsdouble hour, min, sec, msec;
  1050.     jsdouble lorutime; /* Local or UTC version of *date */
  1051.  
  1052.     jsdouble time;
  1053.     jsdouble result;
  1054.  
  1055.     jsdouble *date = date_getProlog(cx, obj, argv);
  1056.     if (!date)
  1057.         return JS_FALSE;
  1058.  
  1059.     result = *date;
  1060.  
  1061.     /* just return NaN if the date is already NaN */
  1062.     if (!JSDOUBLE_IS_FINITE(result))
  1063.         return js_NewNumberValue(cx, result, rval);
  1064.  
  1065.     for (i = 0; i < argc; i++) {
  1066.         if (!JS_ValueToNumber(cx, argv[i], &args[i]))
  1067.             return JS_FALSE;
  1068.         if (!JSDOUBLE_IS_FINITE(args[i])) {
  1069.             *date = *(cx->runtime->jsNaN);
  1070.             return js_NewNumberValue(cx, *date, rval);
  1071.         }
  1072.         args[i] = js_DoubleToInteger(args[i]);
  1073.     }
  1074.  
  1075.     if (local)
  1076.         lorutime = LocalTime(result);
  1077.     else
  1078.         lorutime = result;
  1079.  
  1080.     argp = args;
  1081.     stop = argp + argc;
  1082.     if (maxargs >= 4 && argp < stop)
  1083.         hour = *argp++;
  1084.     else
  1085.         hour = HourFromTime(lorutime);
  1086.  
  1087.     if (maxargs >= 3 && argp < stop)
  1088.         min = *argp++;
  1089.     else
  1090.         min = MinFromTime(lorutime);
  1091.  
  1092.     if (maxargs >= 2 && argp < stop)
  1093.         sec = *argp++;
  1094.     else
  1095.         sec = SecFromTime(lorutime);
  1096.  
  1097.     if (maxargs >= 1 && argp < stop)
  1098.         msec = *argp;
  1099.     else
  1100.         msec = msFromTime(lorutime);
  1101.  
  1102.     time = MakeTime(hour, min, sec, msec);
  1103.     result = MakeDate(Day(lorutime), time);
  1104.  
  1105. /*     fprintf(stderr, "%f\n", result); */
  1106.  
  1107.     if (local)
  1108.         result = UTC(result);
  1109.  
  1110. /*     fprintf(stderr, "%f\n", result); */
  1111.  
  1112.     *date = TIMECLIP(result);
  1113.     return js_NewNumberValue(cx, *date, rval);
  1114. }
  1115.  
  1116. static JSBool
  1117. date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
  1118.                      jsval *argv, jsval *rval)
  1119. {
  1120.     return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
  1121. }
  1122.  
  1123. static JSBool
  1124. date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
  1125.                         jsval *argv, jsval *rval)
  1126. {
  1127.     return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
  1128. }
  1129.  
  1130. static JSBool
  1131. date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
  1132.                 jsval *argv, jsval *rval)
  1133. {
  1134.     return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
  1135. }
  1136.  
  1137. static JSBool
  1138. date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
  1139.                    jsval *argv, jsval *rval)
  1140. {
  1141.     return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
  1142. }
  1143.  
  1144. static JSBool
  1145. date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
  1146.                 jsval *argv, jsval *rval)
  1147. {
  1148.     return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
  1149. }
  1150.  
  1151. static JSBool
  1152. date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
  1153.                    jsval *argv, jsval *rval)
  1154. {
  1155.     return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
  1156. }
  1157.  
  1158. static JSBool
  1159. date_setHours(JSContext *cx, JSObject *obj, uintN argc,
  1160.               jsval *argv, jsval *rval)
  1161. {
  1162.     return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
  1163. }
  1164.  
  1165. static JSBool
  1166. date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
  1167.                  jsval *argv, jsval *rval)
  1168. {
  1169.     return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
  1170. }
  1171.  
  1172. static JSBool
  1173. date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
  1174.               jsval *argv, uintN maxargs, JSBool local, jsval *rval)
  1175. {
  1176.     uintN i;
  1177.     jsdouble lorutime; /* local or UTC version of *date */
  1178.     jsdouble args[3], *argp, *stop;
  1179.     jsdouble year, month, day;
  1180.     jsdouble result;
  1181.  
  1182.     jsdouble *date = date_getProlog(cx, obj, argv);
  1183.     if (!date)
  1184.         return JS_FALSE;
  1185.  
  1186.     result = *date;
  1187.  
  1188.     for (i = 0; i < argc; i++) {
  1189.         if (!JS_ValueToNumber(cx, argv[i], &args[i]))
  1190.             return JS_FALSE;
  1191.         if (!JSDOUBLE_IS_FINITE(args[i])) {
  1192.             *date = *(cx->runtime->jsNaN);
  1193.             return js_NewNumberValue(cx, *date, rval);
  1194.         }
  1195.         args[i] = js_DoubleToInteger(args[i]);
  1196.     }
  1197.  
  1198.     /* return NaN if date is NaN and we're not setting the year,
  1199.      * If we are, use 0 as the time. */
  1200.     if (!(JSDOUBLE_IS_FINITE(result))) {
  1201.         if (argc < 3)
  1202.             return js_NewNumberValue(cx, result, rval);
  1203.         else
  1204.             lorutime = +0.;
  1205.     } else {
  1206.         if (local)
  1207.             lorutime = LocalTime(result);
  1208.         else
  1209.             lorutime = result;
  1210.     }
  1211.  
  1212.     argp = args;
  1213.     stop = argp + argc;
  1214.     if (maxargs >= 3 && argp < stop)
  1215.         year = *argp++;
  1216.     else
  1217.         year = YearFromTime(lorutime);
  1218.  
  1219.     if (maxargs >= 2 && argp < stop)
  1220.         month = *argp++;
  1221.     else
  1222.         month = MonthFromTime(lorutime);
  1223.  
  1224.     if (maxargs >= 1 && argp < stop)
  1225.         day = *argp++;
  1226.     else
  1227.         day = DateFromTime(lorutime);
  1228.  
  1229.     day = MakeDay(year, month, day); /* day within year */
  1230.     result = MakeDate(day, TimeWithinDay(lorutime));
  1231.  
  1232.     if (local)
  1233.         result = UTC(result);
  1234.  
  1235.     *date = TIMECLIP(result);
  1236.     return js_NewNumberValue(cx, *date, rval);
  1237. }
  1238.  
  1239. static JSBool
  1240. date_setDate(JSContext *cx, JSObject *obj, uintN argc,
  1241.              jsval *argv, jsval *rval)
  1242. {
  1243.     return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
  1244. }
  1245.  
  1246. static JSBool
  1247. date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
  1248.                 jsval *argv, jsval *rval)
  1249. {
  1250.     return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
  1251. }
  1252.  
  1253. static JSBool
  1254. date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
  1255.               jsval *argv, jsval *rval)
  1256. {
  1257.     return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
  1258. }
  1259.  
  1260. static JSBool
  1261. date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
  1262.                  jsval *argv, jsval *rval)
  1263. {
  1264.     return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
  1265. }
  1266.  
  1267. static JSBool
  1268. date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
  1269.                  jsval *argv, jsval *rval)
  1270. {
  1271.     return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
  1272. }
  1273.  
  1274. static JSBool
  1275. date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
  1276.                     jsval *argv, jsval *rval)
  1277. {
  1278.     return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
  1279. }
  1280.  
  1281. static JSBool
  1282. date_setYear(JSContext *cx, JSObject *obj, uintN argc,
  1283.              jsval *argv, jsval *rval)
  1284. {
  1285.     jsdouble t;
  1286.     jsdouble year;
  1287.     jsdouble day;
  1288.     jsdouble result;
  1289.  
  1290.     jsdouble *date = date_getProlog(cx, obj, argv);
  1291.     if (!date)
  1292.         return JS_FALSE;
  1293.  
  1294.     result = *date;
  1295.  
  1296.     if (!JS_ValueToNumber(cx, argv[0], &year))
  1297.         return JS_FALSE;
  1298.     if (!JSDOUBLE_IS_FINITE(year)) {
  1299.         *date = *(cx->runtime->jsNaN);
  1300.         return js_NewNumberValue(cx, *date, rval);
  1301.     }
  1302.  
  1303.     year = js_DoubleToInteger(year);
  1304.  
  1305.     if (!JSDOUBLE_IS_FINITE(result)) {
  1306.         t = +0.0;
  1307.     } else {
  1308.         t = LocalTime(result);
  1309.     }
  1310.  
  1311.     if (year >= 0 && year <= 99)
  1312.         year += 1900;
  1313.  
  1314.     day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
  1315.     result = MakeDate(day, TimeWithinDay(t));
  1316.     result = UTC(result);
  1317.  
  1318.     *date = TIMECLIP(result);
  1319.     return js_NewNumberValue(cx, *date, rval);
  1320. }
  1321.  
  1322. /* constants for toString, toUTCString */
  1323. static char js_NaN_date_str[] = "Invalid Date";
  1324. static const char* days[] =
  1325. {
  1326.    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
  1327. };
  1328. static const char* months[] =
  1329. {
  1330.    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  1331. };
  1332.  
  1333. static JSBool
  1334. date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
  1335.                  jsval *argv, jsval *rval)
  1336. {
  1337.     char buf[100];
  1338.     JSString *str;
  1339.     jsdouble *date = date_getProlog(cx, obj, argv);
  1340.     if (!date)
  1341.         return JS_FALSE;
  1342.  
  1343.     if (!JSDOUBLE_IS_FINITE(*date)) {
  1344.         PR_snprintf(buf, sizeof buf, js_NaN_date_str);
  1345.     } else {
  1346.         jsdouble temp = *date;
  1347.  
  1348.         /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
  1349.          * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
  1350.          */
  1351.         PR_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
  1352.                     days[WeekDay(temp)],
  1353.                     DateFromTime(temp),
  1354.                     months[MonthFromTime(temp)],
  1355.                     YearFromTime(temp),
  1356.                     HourFromTime(temp),
  1357.                     MinFromTime(temp),
  1358.                     SecFromTime(temp));
  1359.     }
  1360.     str = JS_NewStringCopyZ(cx, buf);
  1361.     if (!str)
  1362.         return JS_FALSE;
  1363.     *rval = STRING_TO_JSVAL(str);
  1364.     return JS_TRUE;
  1365. }
  1366.  
  1367. /* for Date.toLocaleString; interface to PRMJTime date struct. */
  1368. static void
  1369. new_explode(jsdouble time, PRMJTime *split)
  1370. {
  1371.     jsint year = YearFromTime(time);
  1372.  
  1373.     split->tm_usec = (int32) msFromTime(time) * 1000;
  1374.     split->tm_sec = (int8) SecFromTime(time);
  1375.     split->tm_min = (int8) MinFromTime(time);
  1376.     split->tm_hour = (int8) HourFromTime(time);
  1377.     split->tm_mday = (int8) DateFromTime(time);
  1378.     split->tm_mon = (int8) MonthFromTime(time);
  1379.     split->tm_wday = (int8) WeekDay(time);
  1380.     split->tm_year = (int16) year;
  1381.     split->tm_yday = (int16) DayWithinYear(time, year);
  1382.  
  1383.     /* not sure how this affects things, but it doesn't seem
  1384.        to matter. */
  1385.     split->tm_isdst = (DaylightSavingTA(time) != 0);
  1386. }
  1387.  
  1388. static JSBool
  1389. date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
  1390.                     jsval *argv, jsval *rval)
  1391. {
  1392.     char buf[100];
  1393.     JSString *str;
  1394.     PRMJTime split;
  1395.     jsdouble *date = date_getProlog(cx, obj, argv);
  1396.     if (!date)
  1397.         return JS_FALSE;
  1398.  
  1399.     if (!JSDOUBLE_IS_FINITE(*date)) {
  1400.         PR_snprintf(buf, sizeof buf, js_NaN_date_str);
  1401.     } else {
  1402.         jsdouble local = LocalTime(*date);
  1403.         new_explode(local, &split);
  1404.  
  1405.         /* let PRMJTime format it. */
  1406.         PRMJ_FormatTime(buf, sizeof buf, "%c", &split);
  1407.     }
  1408.  
  1409.     str = JS_NewStringCopyZ(cx, buf);
  1410.     if (!str)
  1411.         return JS_FALSE;
  1412.     *rval = STRING_TO_JSVAL(str);
  1413.     return JS_TRUE;
  1414. }
  1415.  
  1416. /* helper function */
  1417. static JSBool
  1418. date_format(JSContext *cx, jsdouble date, jsval *rval)
  1419. {
  1420.     char buf[100];
  1421.     JSString *str;
  1422.     char tzbuf[100];
  1423.     PRMJTime split;
  1424.  
  1425.     if (!JSDOUBLE_IS_FINITE(date)) {
  1426.         PR_snprintf(buf, sizeof buf, js_NaN_date_str);
  1427.     } else {
  1428.         jsdouble local = LocalTime(date);
  1429.  
  1430.         /* offset from GMT in minutes.  The offset includes daylight savings,
  1431.            if it applies. */
  1432.         jsint minutes = (jsint) floor((LocalTZA + DaylightSavingTA(local))
  1433.                                       / msPerMinute);
  1434.  
  1435.         /* map 510 minutes to 0830 hours */
  1436.         intN offset = (minutes / 60) * 100 + minutes % 60;
  1437.  
  1438.         /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
  1439.          * printed as 'GMT-0800' rather than as 'PST' to avoid
  1440.          * operating-system dependence on strftime (which
  1441.          * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
  1442.          * PST as 'Pacific Standard Time.'  This way we always know
  1443.          * what we're getting, and can parse it if we produce it.
  1444.          * The OS TZA string is included as a comment.
  1445.          */
  1446.  
  1447.         /* get a timezone string from the OS to include as a
  1448.            comment. */
  1449.         new_explode(local, &split);
  1450.         PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z) ", &split);
  1451.  
  1452.         /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
  1453.          * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
  1454.          */
  1455.         PR_snprintf(buf, sizeof buf, "%s %s %.2d %.2d:%.2d:%.2d GMT%+.4d %s%.4d",
  1456.                     days[WeekDay(local)],
  1457.                     months[MonthFromTime(local)],
  1458.                     DateFromTime(local),
  1459.                     HourFromTime(local),
  1460.                     MinFromTime(local),
  1461.                     SecFromTime(local),
  1462.                     offset,
  1463.  
  1464.                     /* don't print anything for the TZA comment if we
  1465.                        got the empty string from the OS. */
  1466.             /* (balance: */
  1467.                     (tzbuf[1] != ')' ? tzbuf : ""),
  1468.  
  1469.                     YearFromTime(local));
  1470.     }
  1471.  
  1472.     str = JS_NewStringCopyZ(cx, buf);
  1473.     if (!str)
  1474.         return JS_FALSE;
  1475.     *rval = STRING_TO_JSVAL(str);
  1476.     return JS_TRUE;
  1477. }
  1478.  
  1479. static JSBool
  1480. date_toString(JSContext *cx, JSObject *obj, uintN argc,
  1481.               jsval *argv, jsval *rval)
  1482. {
  1483.     jsdouble *date = date_getProlog(cx, obj, argv);
  1484.     if (!date)
  1485.         return JS_FALSE;
  1486.     return date_format(cx, *date, rval);
  1487. }
  1488.  
  1489. #if JS_HAS_VALUEOF_HINT
  1490. static JSBool
  1491. date_valueOf(JSContext *cx, JSObject *obj, uintN argc,
  1492.              jsval *argv, jsval *rval)
  1493. {
  1494.     /* If called directly with no arguments, convert to a time number. */
  1495.     if (argc == 0)
  1496.         return date_getTime(cx, obj, argc, argv, rval);
  1497.  
  1498.     /* Convert to number only if the hint was given, otherwise favor string. */
  1499.     if (argc == 1) {
  1500.         JSString *str, *str2;
  1501.  
  1502.         str = JS_ValueToString(cx, argv[0]);
  1503.         if (!str)
  1504.             return JS_FALSE;
  1505.         str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
  1506.         if (!js_CompareStrings(str, str2))
  1507.             return date_getTime(cx, obj, argc, argv, rval);
  1508.     }
  1509.     return date_toString(cx, obj, argc, argv, rval);
  1510. }
  1511. #else
  1512. #define date_valueOf date_getTime
  1513. #endif
  1514.  
  1515.  
  1516. /*
  1517.  * creation and destruction
  1518.  */
  1519.  
  1520. static JSFunctionSpec date_static_methods[] = {
  1521.     {"UTC",               date_UTC,               MAXARGS },
  1522.     {"parse",             date_parse,             1 },
  1523.     {0}
  1524. };
  1525.  
  1526. static JSFunctionSpec date_methods[] = {
  1527.     {"getTime",           date_getTime,           0 },
  1528.     {"getTimezoneOffset", date_getTimezoneOffset, 0 },
  1529.     {"getYear",           date_getYear,           0 },
  1530.     {"getFullYear",       date_getFullYear,       0 },
  1531.     {"getUTCFullYear",    date_getUTCFullYear,    0 },
  1532.     {"getMonth",          date_getMonth,          0 },
  1533.     {"getUTCMonth",       date_getUTCMonth,       0 },
  1534.     {"getDate",           date_getDate,           0 },
  1535.     {"getUTCDate",        date_getUTCDate,        0 },
  1536.     {"getDay",            date_getDay,            0 },
  1537.     {"getUTCDay",         date_getUTCDay,         0 },
  1538.     {"getHours",          date_getHours,          0 },
  1539.     {"getUTCHours",       date_getUTCHours,       0 },
  1540.     {"getMinutes",        date_getMinutes,        0 },
  1541.     {"getUTCMinutes",     date_getUTCMinutes,     0 },
  1542.     {"getSeconds",        date_getUTCSeconds,     0 },
  1543.     {"getUTCSeconds",     date_getUTCSeconds,     0 },
  1544.     {"getMilliseconds",   date_getUTCMilliseconds,0 },
  1545.     {"getUTCMilliseconds",date_getUTCMilliseconds,0 },
  1546.     {"setTime",           date_setTime,           1 },
  1547.     {"setYear",           date_setYear,           1 },
  1548.     {"setFullYear",       date_setFullYear,       1 },
  1549.     {"setUTCFullYear",    date_setUTCFullYear,    1 },
  1550.     {"setMonth",          date_setMonth,          1 },
  1551.     {"setUTCMonth",       date_setUTCMonth,       1 },
  1552.     {"setDate",           date_setDate,           1 },
  1553.     {"setUTCDate",        date_setUTCDate,        1 },
  1554.     {"setHours",          date_setHours,          1 },
  1555.     {"setUTCHours",       date_setUTCHours,       1 },
  1556.     {"setMinutes",        date_setMinutes,        1 },
  1557.     {"setUTCMinutes",     date_setUTCMinutes,     1 },
  1558.     {"setSeconds",        date_setSeconds,        1 },
  1559.     {"setUTCSeconds",     date_setUTCSeconds,     1 },
  1560.     {"setMilliseconds",   date_setMilliseconds,   1 },
  1561.     {"setUTCMilliseconds",date_setUTCMilliseconds,1 },
  1562.     {"toGMTString",       date_toGMTString,       0 },
  1563.     {"toUTCString",       date_toGMTString,       0 },
  1564.     {"toLocaleString",    date_toLocaleString,    0 },
  1565.     {js_toString_str,     date_toString,          0 },
  1566.     {js_valueOf_str,      date_valueOf,           0 },
  1567.     {0}
  1568. };
  1569.  
  1570. static jsdouble *
  1571. date_constructor(JSContext *cx, JSObject* obj)
  1572. {
  1573.     jsdouble *date;
  1574.  
  1575.     date = js_NewDouble(cx, 0.0);
  1576.     if (!date)
  1577.         return NULL;
  1578.  
  1579.     JS_LOCK(cx);
  1580.     OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
  1581.     JS_UNLOCK(cx);
  1582.  
  1583.     return date;
  1584. }
  1585.  
  1586. static JSBool
  1587. Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1588. {
  1589.     jsdouble *date;
  1590.     JSString *str;
  1591.     jsdouble d;
  1592.  
  1593.     /* Date called as function */
  1594.     if (obj->map->clasp != &date_class) {
  1595.         int64 us, ms, us2ms;
  1596.         jsdouble time;
  1597.  
  1598.         /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
  1599.          * so compute ms from PRMJ_Now.
  1600.          */
  1601.         us = PRMJ_Now();
  1602.         LL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
  1603.         LL_DIV(ms, us, us2ms);
  1604.         LL_L2D(time, ms);
  1605.  
  1606.         return date_format(cx, time, rval);
  1607.     }
  1608.  
  1609.     /* Date called as constructor */
  1610.     if (argc == 0) {
  1611.         int64 us, ms, us2ms;
  1612.         jsdouble time;
  1613.  
  1614.         date = date_constructor(cx, obj);
  1615.         if (!date)
  1616.             return JS_FALSE;
  1617.  
  1618.         us = PRMJ_Now();
  1619.         LL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
  1620.         LL_DIV(ms, us, us2ms);
  1621.         LL_L2D(time, ms);
  1622.  
  1623.         *date = time;
  1624.     } else if (argc == 1) {
  1625.         if (!JSVAL_IS_STRING(argv[0])) {
  1626.             /* the argument is a millisecond number */
  1627.             if (!JS_ValueToNumber(cx, argv[0], &d))
  1628.                     return JS_FALSE;
  1629.             date = date_constructor(cx, obj);
  1630.             if (!date)
  1631.                 return JS_FALSE;
  1632.             *date = TIMECLIP(d);
  1633.         } else {
  1634.             /* the argument is a string; parse it. */
  1635.             date = date_constructor(cx, obj);
  1636.             if (!date)
  1637.                 return JS_FALSE;
  1638.  
  1639.             str = JS_ValueToString(cx, argv[0]);
  1640.             if (!str)
  1641.                 return JS_FALSE;
  1642.  
  1643.             if (!date_parseString(str->chars, date))
  1644.                 *date = *(cx->runtime->jsNaN);
  1645.             *date = TIMECLIP(*date);
  1646.         }
  1647.     } else {
  1648.         jsdouble array[MAXARGS];
  1649.         uintN loop;
  1650.         jsdouble d;
  1651.         jsdouble day;
  1652.         jsdouble time;
  1653.  
  1654.         for (loop = 0; loop < MAXARGS; loop++) {
  1655.             if (loop < argc) {
  1656.                 if (!JS_ValueToNumber(cx, argv[loop], &d))
  1657.                     return JS_FALSE;
  1658.                 /* if any arg is NaN, make a NaN date object
  1659.                    and return */
  1660.                 if (!JSDOUBLE_IS_FINITE(d)) {
  1661.                     date = date_constructor(cx, obj);
  1662.                     if (!date)
  1663.                         return JS_FALSE;
  1664.                     *date = *(cx->runtime->jsNaN);
  1665.                     return JS_TRUE;
  1666.                 }
  1667.                 array[loop] = js_DoubleToInteger(d);
  1668.             } else {
  1669.                 array[loop] = 0;
  1670.             }
  1671.         }
  1672.  
  1673.         date = date_constructor(cx, obj);
  1674.         if (!date)
  1675.             return JS_FALSE;
  1676.  
  1677.         /* adjust 2-digit years into the 20th century */
  1678.         if (array[0] >= 0 && array[0] <= 99)
  1679.             array[0] += 1900;
  1680.  
  1681.         /* if we got a 0 for 'date' (which is out of range)
  1682.          * pretend it's a 1 */
  1683.         if (array[2] < 1)
  1684.             array[2] = 1;
  1685.  
  1686.         day = MakeDay(array[0], array[1], array[2]);
  1687.  
  1688. /*         fprintf(stderr, "%f\n", day); */
  1689.  
  1690.         time = MakeTime(array[3], array[4], array[5], array[6]);
  1691.  
  1692. /*         fprintf(stderr, "%f\n", time); */
  1693.  
  1694.         time = MakeDate(day, time);
  1695.  
  1696. /*         fprintf(stderr, "%f\n", time); */
  1697.  
  1698.         time = UTC(time);
  1699.  
  1700. /*         fprintf(stderr, "%f\n", time); */
  1701.  
  1702.         *date = TIMECLIP(time);
  1703.  
  1704. /*         fprintf(stderr, "%f\n", *date); */
  1705.     }
  1706.     return JS_TRUE;
  1707. }
  1708.  
  1709. JSObject *
  1710. js_InitDateClass(JSContext *cx, JSObject *obj)
  1711. {
  1712.     /* set static LocalTZA */
  1713.     LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
  1714.     return JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS,
  1715.                         NULL, date_methods, NULL, date_static_methods);
  1716. }
  1717.  
  1718. JS_FRIEND_API(JSObject *)
  1719. js_NewDateObject(JSContext* cx, int year, int mon, int mday,
  1720.                  int hour, int min, int sec)
  1721. {
  1722.     JSObject *obj;
  1723.     jsdouble *date;
  1724.     jsdouble time;
  1725.  
  1726.     obj = js_NewObject(cx, &date_class, NULL, NULL);
  1727.     if (!obj)
  1728.         return NULL;
  1729.  
  1730.     JS_DefineFunctions(cx, obj, date_methods);
  1731.  
  1732.     date = date_constructor(cx, obj);
  1733.     if (!date)
  1734.         return NULL;
  1735.  
  1736.     time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
  1737.     *date = UTC(time);
  1738.     return obj;
  1739. }
  1740.  
  1741. JS_FRIEND_API(int)
  1742. js_DateGetYear(JSContext *cx, JSObject* obj)
  1743. {
  1744.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1745.  
  1746.     if (!date)
  1747.         return 0;
  1748.     return (int) YearFromTime(LocalTime(*date));
  1749. }
  1750.  
  1751. JS_FRIEND_API(int)
  1752. js_DateGetMonth(JSContext *cx, JSObject* obj)
  1753. {
  1754.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1755.  
  1756.     if (!date)
  1757.         return 0;
  1758.     return (int) MonthFromTime(LocalTime(*date));
  1759. }
  1760.  
  1761. JS_FRIEND_API(int)
  1762. js_DateGetDate(JSContext *cx, JSObject* obj)
  1763. {
  1764.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1765.  
  1766.     if (!date)
  1767.         return 0;
  1768.     return (int) DateFromTime(LocalTime(*date));
  1769. }
  1770.  
  1771. JS_FRIEND_API(int)
  1772. js_DateGetHours(JSContext *cx, JSObject* obj)
  1773. {
  1774.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1775.  
  1776.     if (!date)
  1777.         return 0;
  1778.     return (int) HourFromTime(LocalTime(*date));
  1779. }
  1780.  
  1781. JS_FRIEND_API(int)
  1782. js_DateGetMinutes(JSContext *cx, JSObject* obj)
  1783. {
  1784.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1785.  
  1786.     if (!date)
  1787.         return 0;
  1788.     return (int) MinFromTime(LocalTime(*date));
  1789. }
  1790.  
  1791. JS_FRIEND_API(int)
  1792. js_DateGetSeconds(JSContext *cx, JSObject* obj)
  1793. {
  1794.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1795.  
  1796.     if (!date)
  1797.         return 0;
  1798.     return (int) SecFromTime(*date);
  1799. }
  1800.  
  1801. extern JS_FRIEND_API(void)
  1802. js_DateSetYear(JSContext *cx, JSObject *obj, int year)
  1803. {
  1804.     jsdouble local;
  1805.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1806.     if (!date)
  1807.         return;
  1808.     local = LocalTime(*date);
  1809.     /* reset date if it was NaN */
  1810.     if (local != local)
  1811.         local = 0;
  1812.     local = date_msecFromDate(year,
  1813.                               MonthFromTime(local),
  1814.                               DateFromTime(local),
  1815.                               HourFromTime(local),
  1816.                               MinFromTime(local),
  1817.                               SecFromTime(local),
  1818.                               msFromTime(local));
  1819.     *date = UTC(local);
  1820. }
  1821.  
  1822. extern JS_FRIEND_API(void)
  1823. js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
  1824. {
  1825.     jsdouble local;
  1826.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1827.     if (!date)
  1828.         return;
  1829.     local = LocalTime(*date);
  1830.     /* bail if date was NaN */
  1831.     if (local != local)
  1832.         return;
  1833.     local = date_msecFromDate(YearFromTime(local),
  1834.                               month,
  1835.                               DateFromTime(local),
  1836.                               HourFromTime(local),
  1837.                               MinFromTime(local),
  1838.                               SecFromTime(local),
  1839.                               msFromTime(local));
  1840.     *date = UTC(local);
  1841. }
  1842.  
  1843. extern JS_FRIEND_API(void)
  1844. js_DateSetDate(JSContext *cx, JSObject *obj, int date)
  1845. {
  1846.     jsdouble local;
  1847.     jsdouble *datep = date_getProlog(cx, obj, NULL);
  1848.     if (!datep)
  1849.         return;
  1850.     local = LocalTime(*datep);
  1851.     if (local != local)
  1852.         return;
  1853.     local = date_msecFromDate(YearFromTime(local),
  1854.                               MonthFromTime(local),
  1855.                               date,
  1856.                               HourFromTime(local),
  1857.                               MinFromTime(local),
  1858.                               SecFromTime(local),
  1859.                               msFromTime(local));
  1860.     *datep = UTC(local);
  1861. }
  1862.  
  1863. extern JS_FRIEND_API(void)
  1864. js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
  1865. {
  1866.     jsdouble local;
  1867.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1868.     if (!date)
  1869.         return;
  1870.     local = LocalTime(*date);
  1871.     if (local != local)
  1872.         return;
  1873.     local = date_msecFromDate(YearFromTime(local),
  1874.                               MonthFromTime(local),
  1875.                               DateFromTime(local),
  1876.                               hours,
  1877.                               MinFromTime(local),
  1878.                               SecFromTime(local),
  1879.                               msFromTime(local));
  1880.     *date = UTC(local);
  1881. }
  1882.  
  1883. extern JS_FRIEND_API(void)
  1884. js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
  1885. {
  1886.     jsdouble local;
  1887.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1888.     if (!date)
  1889.         return;
  1890.     local = LocalTime(*date);
  1891.     if (local != local)
  1892.         return;
  1893.     local = date_msecFromDate(YearFromTime(local),
  1894.                               MonthFromTime(local),
  1895.                               DateFromTime(local),
  1896.                               HourFromTime(local),
  1897.                               minutes,
  1898.                               SecFromTime(local),
  1899.                               msFromTime(local));
  1900.     *date = UTC(local);
  1901. }
  1902.  
  1903. extern JS_FRIEND_API(void)
  1904. js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
  1905. {
  1906.     jsdouble local;
  1907.     jsdouble *date = date_getProlog(cx, obj, NULL);
  1908.     if (!date)
  1909.         return;
  1910.     local = LocalTime(*date);
  1911.     if (local != local)
  1912.         return;
  1913.     local = date_msecFromDate(YearFromTime(local),
  1914.                               MonthFromTime(local),
  1915.                               DateFromTime(local),
  1916.                               HourFromTime(local),
  1917.                               MinFromTime(local),
  1918.                               seconds,
  1919.                               msFromTime(local));
  1920.     *date = UTC(local);
  1921. }
  1922.