Code

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