Code

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