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 ***** */
39 
40 /*
41  * JS date methods.
42  */
43 
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  */
52 
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"
71 
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  */
119 
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  */
144 
145 /*
146  * Supporting functions - ECMA 15.9.1.*
147  */
148 
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
156 
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
173 
174 #define Day(t)          floor((t) / msPerDay)
175 
176 static jsdouble
TimeWithinDay(jsdouble t)177 TimeWithinDay(jsdouble t)
178 {
179     jsdouble result;
180     result = fmod(t, msPerDay);
181     if (result < 0)
182 	result += msPerDay;
183     return result;
184 }
185 
186 #define DaysInYear(y)   ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0))  \
187 			 ? 366 : 365)
188 
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)
195 
196 static jsint
YearFromTime(jsdouble t)197 YearFromTime(jsdouble t)
198 {
199     jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
200     jsdouble t2 = (jsdouble) TimeFromYear(y);
201 
202     if (t2 > t) {
203         y--;
204     } else {
205         if (t2 + msPerDay * DaysInYear(y) <= t)
206             y++;
207     }
208     return y;
209 }
210 
211 #define InLeapYear(t)   (JSBool) (DaysInYear(YearFromTime(t)) == 366)
212 
213 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
214 
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 };
223 
224 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
225 
226 static intN
MonthFromTime(jsdouble t)227 MonthFromTime(jsdouble t)
228 {
229     intN d, step;
230     jsint year = YearFromTime(t);
231     d = DayWithinYear(t, year);
232 
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;
257 }
258 
259 static intN
DateFromTime(jsdouble t)260 DateFromTime(jsdouble t)
261 {
262     intN d, step, next;
263     jsint year = YearFromTime(t);
264     d = DayWithinYear(t, year);
265 
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;
301 }
302 
303 static intN
WeekDay(jsdouble t)304 WeekDay(jsdouble t)
305 {
306     jsint result;
307     result = (jsint) Day(t) + 4;
308     result = result % 7;
309     if (result < 0)
310 	result += 7;
311     return (intN) result;
312 }
313 
314 /* LocalTZA gets set by js_InitDateClass() */
315 static jsdouble LocalTZA;
316 
317 static jsdouble
DaylightSavingTA(jsdouble t)318 DaylightSavingTA(jsdouble t)
319 {
320     volatile int64 PR_t;
321     int64 ms2us;
322     int64 offset;
323     jsdouble result;
324 
325     /* abort if NaN */
326     if (JSDOUBLE_IS_NaN(t))
327 	return t;
328 
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);
333 
334     offset = PRMJ_DSTOffset(PR_t);
335 
336     JSLL_DIV(offset, offset, ms2us);
337     JSLL_L2D(result, offset);
338     return result;
339 }
340 
341 
342 #define AdjustTime(t)   fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
343 
344 #define LocalTime(t)    ((t) + AdjustTime(t))
345 
346 static jsdouble
UTC(jsdouble t)347 UTC(jsdouble t)
348 {
349     return t - AdjustTime(t - LocalTZA);
350 }
351 
352 static intN
HourFromTime(jsdouble t)353 HourFromTime(jsdouble t)
354 {
355     intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
356     if (result < 0)
357 	result += (intN)HoursPerDay;
358     return result;
359 }
360 
361 static intN
MinFromTime(jsdouble t)362 MinFromTime(jsdouble t)
363 {
364     intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
365     if (result < 0)
366 	result += (intN)MinutesPerHour;
367     return result;
368 }
369 
370 static intN
SecFromTime(jsdouble t)371 SecFromTime(jsdouble t)
372 {
373     intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
374     if (result < 0)
375 	result += (intN)SecondsPerMinute;
376     return result;
377 }
378 
379 static intN
msFromTime(jsdouble t)380 msFromTime(jsdouble t)
381 {
382     intN result = (intN) fmod(t, msPerSecond);
383     if (result < 0)
384 	result += (intN)msPerSecond;
385     return result;
386 }
387 
388 #define MakeTime(hour, min, sec, ms) \
389 (((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms)
390 
391 static jsdouble
MakeDay(jsdouble year,jsdouble month,jsdouble date)392 MakeDay(jsdouble year, jsdouble month, jsdouble date)
393 {
394     jsdouble result;
395     JSBool leap;
396     jsdouble yearday;
397     jsdouble monthday;
398 
399     year += floor(month / 12);
400 
401     month = fmod(month, 12.0);
402     if (month < 0)
403 	month += 12;
404 
405     leap = (DaysInYear((jsint) year) == 366);
406 
407     yearday = floor(TimeFromYear(year) / msPerDay);
408     monthday = DayFromMonth(month, leap);
409 
410     result = yearday
411 	     + monthday
412 	     + date - 1;
413     return result;
414 }
415 
416 #define MakeDate(day, time) (day * msPerDay + time)
417 
418 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
419 		      && !((d < 0 ? -d : d) > HalfTimeDomain)) \
420 		     ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
421 
422 /**
423  * end of ECMA 'support' functions
424  */
425 
426 /*
427  * Other Support routines and definitions
428  */
429 
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 };
437 
438 /* for use by date_parse */
439 
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 };
453 
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 };
463 
464 /* helper for date_parse */
465 static JSBool
date_regionMatches(const char * s1,int s1off,const jschar * s2,int s2off,int count,int ignoreCase)466 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
467 		   int count, int ignoreCase)
468 {
469     JSBool result = JS_FALSE;
470     /* return true if matches, otherwise, false */
471 
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     }
486 
487     if (count == 0) {
488 	result = JS_TRUE;
489     }
490 
491     return result;
492 }
493 
494 /* find UTC time from given date... no 1900 correction! */
495 static jsdouble
date_msecFromDate(jsdouble year,jsdouble mon,jsdouble mday,jsdouble hour,jsdouble min,jsdouble sec,jsdouble msec)496 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
497 		  jsdouble min, jsdouble sec, jsdouble msec)
498 {
499     jsdouble day;
500     jsdouble msec_time;
501     jsdouble result;
502 
503     day = MakeDay(year, mon, mday);
504     msec_time = MakeTime(hour, min, sec, msec);
505     result = MakeDate(day, msec_time);
506     return result;
507 }
508 
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
date_UTC(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)516 date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
517 {
518     jsdouble array[MAXARGS];
519     uintN loop;
520     jsdouble d;
521 
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     }
535 
536     /* adjust 2-digit years into the 20th century */
537     if (array[0] >= 0 && array[0] <= 99)
538 	array[0] += 1900;
539 
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;
544 
545     d = date_msecFromDate(array[0], array[1], array[2],
546 			      array[3], array[4], array[5], array[6]);
547     d = TIMECLIP(d);
548 
549     return js_NewNumberValue(cx, d, rval);
550 }
551 
552 static JSBool
date_parseString(JSString * str,jsdouble * result)553 date_parseString(JSString *str, jsdouble *result)
554 {
555     jsdouble msec;
556 
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;
571 
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 	    }
601 
602 	    /* allow TZA before the year, so
603 	     * 'Wed Nov 05 21:49:11 GMT-0800 1997'
604 	     * works */
605 
606 	    /* uses of seenplusminus allow : in TZA, so Java
607 	     * no-timezone style of GMT+4:30 works
608 	     */
609 
610 	    if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
611 		/* make ':' case below change tzoffset */
612 		seenplusminus = JS_TRUE;
613 
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);
723 
724 	*result = UTC(msec_time);
725 	return JS_TRUE;
726     }
727 
728     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
729     msec += tzoffset * msPerMinute;
730     *result = msec;
731     return JS_TRUE;
732 
733 syntax:
734     /* syntax error */
735     *result = 0;
736     return JS_FALSE;
737 }
738 
739 static JSBool
date_parse(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)740 date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
741 {
742     JSString *str;
743     jsdouble result;
744 
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     }
752 
753     result = TIMECLIP(result);
754     return js_NewNumberValue(cx, result, rval);
755 }
756 
757 static JSBool
date_now(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)758 date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
759 {
760     int64 us, ms, us2ms;
761     jsdouble msec_time;
762 
763     us = PRMJ_Now();
764     JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
765     JSLL_DIV(ms, us, us2ms);
766     JSLL_L2D(msec_time, ms);
767 
768     return js_NewDoubleValue(cx, msec_time, rval);
769 }
770 
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 *
date_getProlog(JSContext * cx,JSObject * obj,jsval * argv)776 date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)
777 {
778     if (!JS_InstanceOf(cx, obj, &date_class, argv))
779 	return NULL;
780     return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
781 }
782 
783 /*
784  * See ECMA 15.9.5.4 thru 15.9.5.23
785  */
786 static JSBool
date_getTime(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)787 date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
788 {
789     jsdouble *date = date_getProlog(cx, obj, argv);
790     if (!date)
791 	return JS_FALSE;
792 
793     return js_NewNumberValue(cx, *date, rval);
794 }
795 
796 static JSBool
date_getYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)797 date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
798 {
799     jsdouble result;
800     jsdouble *date = date_getProlog(cx, obj, argv);
801     if (!date)
802 	return JS_FALSE;
803     result = *date;
804 
805     if (!JSDOUBLE_IS_FINITE(result))
806 	return js_NewNumberValue(cx, result, rval);
807 
808     result = YearFromTime(LocalTime(result));
809 
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);
829 }
830 
831 static JSBool
date_getFullYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)832 date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
833 		 jsval *rval)
834 {
835     jsdouble result;
836     jsdouble *date = date_getProlog(cx, obj, argv);
837     if (!date)
838 	return JS_FALSE;
839     result = *date;
840 
841     if (!JSDOUBLE_IS_FINITE(result))
842 	return js_NewNumberValue(cx, result, rval);
843 
844     result = YearFromTime(LocalTime(result));
845     return js_NewNumberValue(cx, result, rval);
846 }
847 
848 static JSBool
date_getUTCFullYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)849 date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
850 		    jsval *rval)
851 {
852     jsdouble result;
853     jsdouble *date = date_getProlog(cx, obj, argv);
854     if (!date)
855 	return JS_FALSE;
856     result = *date;
857 
858     if (!JSDOUBLE_IS_FINITE(result))
859 	return js_NewNumberValue(cx, result, rval);
860 
861     result = YearFromTime(result);
862     return js_NewNumberValue(cx, result, rval);
863 }
864 
865 static JSBool
date_getMonth(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)866 date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
867 	      jsval *rval)
868 {
869     jsdouble result;
870     jsdouble *date = date_getProlog(cx, obj, argv);
871     if (!date)
872 	return JS_FALSE;
873     result = *date;
874 
875     if (!JSDOUBLE_IS_FINITE(result))
876 	return js_NewNumberValue(cx, result, rval);
877 
878     result = MonthFromTime(LocalTime(result));
879     return js_NewNumberValue(cx, result, rval);
880 }
881 
882 static JSBool
date_getUTCMonth(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)883 date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
884 		 jsval *rval)
885 {
886     jsdouble result;
887     jsdouble *date = date_getProlog(cx, obj, argv);
888     if (!date)
889 	return JS_FALSE;
890     result = *date;
891 
892     if (!JSDOUBLE_IS_FINITE(result))
893 	return js_NewNumberValue(cx, result, rval);
894 
895     result = MonthFromTime(result);
896     return js_NewNumberValue(cx, result, rval);
897 }
898 
899 static JSBool
date_getDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)900 date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
901 {
902     jsdouble result;
903     jsdouble *date = date_getProlog(cx, obj, argv);
904     if (!date)
905 	return JS_FALSE;
906     result = *date;
907 
908     if (!JSDOUBLE_IS_FINITE(result))
909 	return js_NewNumberValue(cx, result, rval);
910 
911     result = LocalTime(result);
912     result = DateFromTime(result);
913     return js_NewNumberValue(cx, result, rval);
914 }
915 
916 static JSBool
date_getUTCDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)917 date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
918 		jsval *rval)
919 {
920     jsdouble result;
921     jsdouble *date = date_getProlog(cx, obj, argv);
922     if (!date)
923 	return JS_FALSE;
924     result = *date;
925 
926     if (!JSDOUBLE_IS_FINITE(result))
927 	return js_NewNumberValue(cx, result, rval);
928 
929     result = DateFromTime(result);
930     return js_NewNumberValue(cx, result, rval);
931 }
932 
933 static JSBool
date_getDay(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)934 date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
935 {
936     jsdouble result;
937     jsdouble *date = date_getProlog(cx, obj, argv);
938     if (!date)
939 	return JS_FALSE;
940     result = *date;
941 
942     if (!JSDOUBLE_IS_FINITE(result))
943 	return js_NewNumberValue(cx, result, rval);
944 
945     result = LocalTime(result);
946     result = WeekDay(result);
947     return js_NewNumberValue(cx, result, rval);
948 }
949 
950 static JSBool
date_getUTCDay(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)951 date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
952 	       jsval *rval)
953 {
954     jsdouble result;
955     jsdouble *date = date_getProlog(cx, obj, argv);
956     if (!date)
957 	return JS_FALSE;
958     result = *date;
959 
960     if (!JSDOUBLE_IS_FINITE(result))
961 	return js_NewNumberValue(cx, result, rval);
962 
963     result = WeekDay(result);
964     return js_NewNumberValue(cx, result, rval);
965 }
966 
967 static JSBool
date_getHours(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)968 date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
969 	      jsval *rval)
970 {
971     jsdouble result;
972     jsdouble *date = date_getProlog(cx, obj, argv);
973     if (!date)
974 	return JS_FALSE;
975     result = *date;
976 
977     if (!JSDOUBLE_IS_FINITE(result))
978 	return js_NewNumberValue(cx, result, rval);
979 
980     result = HourFromTime(LocalTime(result));
981     return js_NewNumberValue(cx, result, rval);
982 }
983 
984 static JSBool
date_getUTCHours(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)985 date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
986 		 jsval *rval)
987 {
988     jsdouble result;
989     jsdouble *date = date_getProlog(cx, obj, argv);
990     if (!date)
991 	return JS_FALSE;
992     result = *date;
993 
994     if (!JSDOUBLE_IS_FINITE(result))
995 	return js_NewNumberValue(cx, result, rval);
996 
997     result = HourFromTime(result);
998     return js_NewNumberValue(cx, result, rval);
999 }
1000 
1001 static JSBool
date_getMinutes(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1002 date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1003 		jsval *rval)
1004 {
1005     jsdouble result;
1006     jsdouble *date = date_getProlog(cx, obj, argv);
1007     if (!date)
1008 	return JS_FALSE;
1009     result = *date;
1010 
1011     if (!JSDOUBLE_IS_FINITE(result))
1012 	return js_NewNumberValue(cx, result, rval);
1013 
1014     result = MinFromTime(LocalTime(result));
1015     return js_NewNumberValue(cx, result, rval);
1016 }
1017 
1018 static JSBool
date_getUTCMinutes(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1019 date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1020 		   jsval *rval)
1021 {
1022     jsdouble result;
1023     jsdouble *date = date_getProlog(cx, obj, argv);
1024     if (!date)
1025 	return JS_FALSE;
1026     result = *date;
1027 
1028     if (!JSDOUBLE_IS_FINITE(result))
1029 	return js_NewNumberValue(cx, result, rval);
1030 
1031     result = MinFromTime(result);
1032     return js_NewNumberValue(cx, result, rval);
1033 }
1034 
1035 /* Date.getSeconds is mapped to getUTCSeconds */
1036 
1037 static JSBool
date_getUTCSeconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1038 date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1039 		jsval *rval)
1040 {
1041     jsdouble result;
1042     jsdouble *date = date_getProlog(cx, obj, argv);
1043     if (!date)
1044 	return JS_FALSE;
1045     result = *date;
1046 
1047     if (!JSDOUBLE_IS_FINITE(result))
1048 	return js_NewNumberValue(cx, result, rval);
1049 
1050     result = SecFromTime(result);
1051     return js_NewNumberValue(cx, result, rval);
1052 }
1053 
1054 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1055 
1056 static JSBool
date_getUTCMilliseconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1057 date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1058 		     jsval *rval)
1059 {
1060     jsdouble result;
1061     jsdouble *date = date_getProlog(cx, obj, argv);
1062     if (!date)
1063 	return JS_FALSE;
1064     result = *date;
1065 
1066     if (!JSDOUBLE_IS_FINITE(result))
1067 	return js_NewNumberValue(cx, result, rval);
1068 
1069     result = msFromTime(result);
1070     return js_NewNumberValue(cx, result, rval);
1071 }
1072 
1073 static JSBool
date_getTimezoneOffset(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1074 date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1075 		       jsval *rval)
1076 {
1077     jsdouble result;
1078     jsdouble *date = date_getProlog(cx, obj, argv);
1079     if (!date)
1080 	return JS_FALSE;
1081     result = *date;
1082 
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);
1090 }
1091 
1092 static JSBool
date_setTime(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1093 date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1094 {
1095     jsdouble result;
1096     jsdouble *date = date_getProlog(cx, obj, argv);
1097     if (!date)
1098 	return JS_FALSE;
1099 
1100     if (!js_ValueToNumber(cx, argv[0], &result))
1101 	return JS_FALSE;
1102 
1103     result = TIMECLIP(result);
1104 
1105     *date = result;
1106     return js_NewNumberValue(cx, result, rval);
1107 }
1108 
1109 static JSBool
date_makeTime(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,uintN maxargs,JSBool local,jsval * rval)1110 date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1111 	      uintN maxargs, JSBool local, jsval *rval)
1112 {
1113     uintN i;
1114     jsdouble args[4], *argp, *stop;
1115     jsdouble hour, min, sec, msec;
1116     jsdouble lorutime; /* Local or UTC version of *date */
1117 
1118     jsdouble msec_time;
1119     jsdouble result;
1120 
1121     jsdouble *date = date_getProlog(cx, obj, argv);
1122     if (!date)
1123 	return JS_FALSE;
1124 
1125     result = *date;
1126 
1127     /* just return NaN if the date is already NaN */
1128     if (!JSDOUBLE_IS_FINITE(result))
1129 	return js_NewNumberValue(cx, result, rval);
1130 
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 */
1143 
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     }
1153 
1154     if (local)
1155 	lorutime = LocalTime(result);
1156     else
1157 	lorutime = result;
1158 
1159     argp = args;
1160     stop = argp + argc;
1161     if (maxargs >= 4 && argp < stop)
1162 	hour = *argp++;
1163     else
1164 	hour = HourFromTime(lorutime);
1165 
1166     if (maxargs >= 3 && argp < stop)
1167 	min = *argp++;
1168     else
1169 	min = MinFromTime(lorutime);
1170 
1171     if (maxargs >= 2 && argp < stop)
1172 	sec = *argp++;
1173     else
1174 	sec = SecFromTime(lorutime);
1175 
1176     if (maxargs >= 1 && argp < stop)
1177 	msec = *argp;
1178     else
1179 	msec = msFromTime(lorutime);
1180 
1181     msec_time = MakeTime(hour, min, sec, msec);
1182     result = MakeDate(Day(lorutime), msec_time);
1183 
1184 /*     fprintf(stderr, "%f\n", result); */
1185 
1186     if (local)
1187 	result = UTC(result);
1188 
1189 /*     fprintf(stderr, "%f\n", result); */
1190 
1191     *date = TIMECLIP(result);
1192     return js_NewNumberValue(cx, *date, rval);
1193 }
1194 
1195 static JSBool
date_setMilliseconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1196 date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1197 		     jsval *argv, jsval *rval)
1198 {
1199     return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
1200 }
1201 
1202 static JSBool
date_setUTCMilliseconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1203 date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1204 			jsval *argv, jsval *rval)
1205 {
1206     return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
1207 }
1208 
1209 static JSBool
date_setSeconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1210 date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
1211 		jsval *argv, jsval *rval)
1212 {
1213     return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
1214 }
1215 
1216 static JSBool
date_setUTCSeconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1217 date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
1218 		   jsval *argv, jsval *rval)
1219 {
1220     return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
1221 }
1222 
1223 static JSBool
date_setMinutes(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1224 date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
1225 		jsval *argv, jsval *rval)
1226 {
1227     return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
1228 }
1229 
1230 static JSBool
date_setUTCMinutes(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1231 date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
1232 		   jsval *argv, jsval *rval)
1233 {
1234     return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
1235 }
1236 
1237 static JSBool
date_setHours(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1238 date_setHours(JSContext *cx, JSObject *obj, uintN argc,
1239 	      jsval *argv, jsval *rval)
1240 {
1241     return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
1242 }
1243 
1244 static JSBool
date_setUTCHours(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1245 date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
1246 		 jsval *argv, jsval *rval)
1247 {
1248     return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
1249 }
1250 
1251 static JSBool
date_makeDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,uintN maxargs,JSBool local,jsval * rval)1252 date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
1253 	      jsval *argv, uintN maxargs, JSBool local, jsval *rval)
1254 {
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;
1260 
1261     jsdouble *date = date_getProlog(cx, obj, argv);
1262     if (!date)
1263 	return JS_FALSE;
1264 
1265     result = *date;
1266 
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 */
1272 
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     }
1282 
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     }
1296 
1297     argp = args;
1298     stop = argp + argc;
1299     if (maxargs >= 3 && argp < stop)
1300 	year = *argp++;
1301     else
1302 	year = YearFromTime(lorutime);
1303 
1304     if (maxargs >= 2 && argp < stop)
1305 	month = *argp++;
1306     else
1307 	month = MonthFromTime(lorutime);
1308 
1309     if (maxargs >= 1 && argp < stop)
1310 	day = *argp++;
1311     else
1312 	day = DateFromTime(lorutime);
1313 
1314     day = MakeDay(year, month, day); /* day within year */
1315     result = MakeDate(day, TimeWithinDay(lorutime));
1316 
1317     if (local)
1318 	result = UTC(result);
1319 
1320     *date = TIMECLIP(result);
1321     return js_NewNumberValue(cx, *date, rval);
1322 }
1323 
1324 static JSBool
date_setDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1325 date_setDate(JSContext *cx, JSObject *obj, uintN argc,
1326 	     jsval *argv, jsval *rval)
1327 {
1328     return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
1329 }
1330 
1331 static JSBool
date_setUTCDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1332 date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
1333 		jsval *argv, jsval *rval)
1334 {
1335     return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
1336 }
1337 
1338 static JSBool
date_setMonth(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1339 date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
1340 	      jsval *argv, jsval *rval)
1341 {
1342     return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
1343 }
1344 
1345 static JSBool
date_setUTCMonth(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1346 date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
1347 		 jsval *argv, jsval *rval)
1348 {
1349     return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
1350 }
1351 
1352 static JSBool
date_setFullYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1353 date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
1354 		 jsval *argv, jsval *rval)
1355 {
1356     return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
1357 }
1358 
1359 static JSBool
date_setUTCFullYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1360 date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
1361 		    jsval *argv, jsval *rval)
1362 {
1363     return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
1364 }
1365 
1366 static JSBool
date_setYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1367 date_setYear(JSContext *cx, JSObject *obj, uintN argc,
1368 	     jsval *argv, jsval *rval)
1369 {
1370     jsdouble t;
1371     jsdouble year;
1372     jsdouble day;
1373     jsdouble result;
1374 
1375     jsdouble *date = date_getProlog(cx, obj, argv);
1376     if (!date)
1377 	return JS_FALSE;
1378 
1379     result = *date;
1380 
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     }
1387 
1388     year = js_DoubleToInteger(year);
1389 
1390     if (!JSDOUBLE_IS_FINITE(result)) {
1391 	t = +0.0;
1392     } else {
1393 	t = LocalTime(result);
1394     }
1395 
1396     if (year >= 0 && year <= 99)
1397 	year += 1900;
1398 
1399     day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1400     result = MakeDate(day, TimeWithinDay(t));
1401     result = UTC(result);
1402 
1403     *date = TIMECLIP(result);
1404     return js_NewNumberValue(cx, *date, rval);
1405 }
1406 
1407 /* constants for toString, toUTCString */
1408 static char js_NaN_date_str[] = "Invalid Date";
1409 static const char* days[] =
1410 {
1411    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1412 };
1413 static const char* months[] =
1414 {
1415    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1416 };
1417 
1418 static JSBool
date_toGMTString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1419 date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
1420 		 jsval *argv, jsval *rval)
1421 {
1422     char buf[100];
1423     JSString *str;
1424     jsdouble *date = date_getProlog(cx, obj, argv);
1425     if (!date)
1426 	return JS_FALSE;
1427 
1428     if (!JSDOUBLE_IS_FINITE(*date)) {
1429 	JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1430     } else {
1431 	jsdouble temp = *date;
1432 
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;
1450 }
1451 
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
new_explode(jsdouble timeval,PRMJTime * split,JSBool findEquivalent)1457 new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)
1458 {
1459     jsint year = YearFromTime(timeval);
1460     int16 adjustedYear;
1461 
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     }
1481 
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);
1491 
1492     /* not sure how this affects things, but it doesn't seem
1493        to matter. */
1494     split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1495 }
1496 
1497 typedef enum formatspec {
1498     FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1499 } formatspec;
1500 
1501 /* helper function */
1502 static JSBool
date_format(JSContext * cx,jsdouble date,formatspec format,jsval * rval)1503 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1504 {
1505     char buf[100];
1506     JSString *str;
1507     char tzbuf[100];
1508     JSBool usetz;
1509     size_t i, tzlen;
1510     PRMJTime split;
1511 
1512     if (!JSDOUBLE_IS_FINITE(date)) {
1513 	JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1514     } else {
1515 	jsdouble local = LocalTime(date);
1516 
1517 	/* offset from GMT in minutes.  The offset includes daylight savings,
1518 	   if it applies. */
1519 	jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1520 
1521 	/* map 510 minutes to 0830 hours */
1522 	intN offset = (minutes / 60) * 100 + minutes % 60;
1523 
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 	 */
1532 
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) {
1537 
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             }
1558 
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;
1564 
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     }
1607 
1608     str = JS_NewStringCopyZ(cx, buf);
1609     if (!str)
1610 	return JS_FALSE;
1611     *rval = STRING_TO_JSVAL(str);
1612     return JS_TRUE;
1613 }
1614 
1615 static JSBool
date_toLocaleHelper(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval,char * format)1616 date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,
1617 		    jsval *argv, jsval *rval, char *format)
1618 {
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;
1625 
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);
1632 
1633 	/* let PRMJTime format it.	 */
1634 	result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1635 
1636 	/* If it failed, default to toString. */
1637 	if (result_len == 0)
1638 	    return date_format(cx, *date, FORMATSPEC_FULL, rval);
1639 
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         }
1646 
1647     }
1648 
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;
1657 }
1658 
1659 static JSBool
date_toLocaleString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1660 date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
1661 		    jsval *argv, jsval *rval)
1662 {
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 				   );
1674 }
1675 
1676 static JSBool
date_toLocaleDateString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1677 date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,
1678 		    jsval *argv, jsval *rval)
1679 {
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 				   );
1691 }
1692 
1693 static JSBool
date_toLocaleTimeString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1694 date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,
1695 		    jsval *argv, jsval *rval)
1696 {
1697     return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");
1698 }
1699 
1700 static JSBool
date_toTimeString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1701 date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,
1702 		    jsval *argv, jsval *rval)
1703 {
1704     jsdouble *date = date_getProlog(cx, obj, argv);
1705     if (!date)
1706 	return JS_FALSE;
1707     return date_format(cx, *date, FORMATSPEC_TIME, rval);
1708 }
1709 
1710 static JSBool
date_toDateString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1711 date_toDateString(JSContext *cx, JSObject *obj, uintN argc,
1712 		    jsval *argv, jsval *rval)
1713 {
1714     jsdouble *date = date_getProlog(cx, obj, argv);
1715     if (!date)
1716 	return JS_FALSE;
1717     return date_format(cx, *date, FORMATSPEC_DATE, rval);
1718 }
1719 
1720 #if JS_HAS_TOSOURCE
1721 #include <string.h>
1722 #include "jsdtoa.h"
1723 
1724 static JSBool
date_toSource(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1725 date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1726 	      jsval *rval)
1727 {
1728     jsdouble *date;
1729     char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1730     JSString *str;
1731 
1732     date = date_getProlog(cx, obj, argv);
1733     if (!date)
1734 	return JS_FALSE;
1735 
1736     numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);
1737     if (!numStr) {
1738 	JS_ReportOutOfMemory(cx);
1739 	return JS_FALSE;
1740     }
1741 
1742     bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr);
1743     if (!bytes) {
1744 	JS_ReportOutOfMemory(cx);
1745 	return JS_FALSE;
1746     }
1747 
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;
1755 }
1756 #endif
1757 
1758 static JSBool
date_toString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1759 date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1760 	      jsval *rval)
1761 {
1762     jsdouble *date = date_getProlog(cx, obj, argv);
1763     if (!date)
1764 	return JS_FALSE;
1765     return date_format(cx, *date, FORMATSPEC_FULL, rval);
1766 }
1767 
1768 #if JS_HAS_VALUEOF_HINT
1769 static JSBool
date_valueOf(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1770 date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1771 	     jsval *rval)
1772 {
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      */
1777 
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);
1781 
1782     /* Convert to number only if the hint was given, otherwise favor string. */
1783     if (argc == 1) {
1784 	JSString *str, *str2;
1785 
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);
1794 }
1795 #else
1796 #define date_valueOf date_getTime
1797 #endif
1798 
1799 
1800 /*
1801  * creation and destruction
1802  */
1803 
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 };
1810 
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 };
1860 
1861 static jsdouble *
date_constructor(JSContext * cx,JSObject * obj)1862 date_constructor(JSContext *cx, JSObject* obj)
1863 {
1864     jsdouble *date;
1865 
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;
1871 }
1872 
1873 static JSBool
Date(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1874 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1875 {
1876     jsdouble *date;
1877     JSString *str;
1878     jsdouble d;
1879 
1880     /* Date called as function */
1881     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
1882 	int64 us, ms, us2ms;
1883 	jsdouble msec_time;
1884 
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);
1892 
1893 	return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
1894     }
1895 
1896     /* Date called as constructor */
1897     if (argc == 0) {
1898 	int64 us, ms, us2ms;
1899 	jsdouble msec_time;
1900 
1901 	date = date_constructor(cx, obj);
1902 	if (!date)
1903 	    return JS_FALSE;
1904 
1905 	us = PRMJ_Now();
1906 	JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
1907 	JSLL_DIV(ms, us, us2ms);
1908 	JSLL_L2D(msec_time, ms);
1909 
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;
1925 
1926 	    str = js_ValueToString(cx, argv[0]);
1927 	    if (!str)
1928 		return JS_FALSE;
1929 
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;
1940 
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 	}
1963 
1964 	date = date_constructor(cx, obj);
1965 	if (!date)
1966 	    return JS_FALSE;
1967 
1968 	/* adjust 2-digit years into the 20th century */
1969 	if (array[0] >= 0 && array[0] <= 99)
1970 	    array[0] += 1900;
1971 
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;
1979 }
1980 
1981 JSObject *
js_InitDateClass(JSContext * cx,JSObject * obj)1982 js_InitDateClass(JSContext *cx, JSObject *obj)
1983 {
1984     JSObject *proto;
1985     jsdouble *proto_date;
1986 
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;
1993 
1994     /* Alias toUTCString with toGMTString.  (ECMA B.2.6) */
1995     if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
1996         return NULL;
1997 
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;
2003 
2004     return proto;
2005 }
2006 
2007 JS_FRIEND_API(JSObject *)
js_NewDateObjectMsec(JSContext * cx,jsdouble msec_time)2008 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2009 {
2010     JSObject *obj;
2011     jsdouble *date;
2012 
2013     obj = js_NewObject(cx, &date_class, NULL, NULL);
2014     if (!obj)
2015 	return NULL;
2016 
2017     date = date_constructor(cx, obj);
2018     if (!date)
2019 	return NULL;
2020 
2021     *date = msec_time;
2022     return obj;
2023 }
2024 
2025 JS_FRIEND_API(JSObject *)
js_NewDateObject(JSContext * cx,int year,int mon,int mday,int hour,int min,int sec)2026 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2027                  int hour, int min, int sec)
2028 {
2029     JSObject *obj;
2030     jsdouble msec_time;
2031 
2032     msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2033     obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2034     return obj;
2035 }
2036 
2037 JS_FRIEND_API(JSBool)
js_DateIsValid(JSContext * cx,JSObject * obj)2038 js_DateIsValid(JSContext *cx, JSObject* obj)
2039 {
2040     jsdouble *date = date_getProlog(cx, obj, NULL);
2041 
2042     if (!date || JSDOUBLE_IS_NaN(*date))
2043         return JS_FALSE;
2044     else
2045         return JS_TRUE;
2046 }
2047 
2048 JS_FRIEND_API(int)
js_DateGetYear(JSContext * cx,JSObject * obj)2049 js_DateGetYear(JSContext *cx, JSObject* obj)
2050 {
2051     jsdouble *date = date_getProlog(cx, obj, NULL);
2052 
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));
2057 }
2058 
2059 JS_FRIEND_API(int)
js_DateGetMonth(JSContext * cx,JSObject * obj)2060 js_DateGetMonth(JSContext *cx, JSObject* obj)
2061 {
2062     jsdouble *date = date_getProlog(cx, obj, NULL);
2063 
2064     if (!date || JSDOUBLE_IS_NaN(*date))
2065 	return 0;
2066     return (int) MonthFromTime(LocalTime(*date));
2067 }
2068 
2069 JS_FRIEND_API(int)
js_DateGetDate(JSContext * cx,JSObject * obj)2070 js_DateGetDate(JSContext *cx, JSObject* obj)
2071 {
2072     jsdouble *date = date_getProlog(cx, obj, NULL);
2073 
2074     if (!date || JSDOUBLE_IS_NaN(*date))
2075 	return 0;
2076     return (int) DateFromTime(LocalTime(*date));
2077 }
2078 
2079 JS_FRIEND_API(int)
js_DateGetHours(JSContext * cx,JSObject * obj)2080 js_DateGetHours(JSContext *cx, JSObject* obj)
2081 {
2082     jsdouble *date = date_getProlog(cx, obj, NULL);
2083 
2084     if (!date || JSDOUBLE_IS_NaN(*date))
2085 	return 0;
2086     return (int) HourFromTime(LocalTime(*date));
2087 }
2088 
2089 JS_FRIEND_API(int)
js_DateGetMinutes(JSContext * cx,JSObject * obj)2090 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2091 {
2092     jsdouble *date = date_getProlog(cx, obj, NULL);
2093 
2094     if (!date || JSDOUBLE_IS_NaN(*date))
2095 	return 0;
2096     return (int) MinFromTime(LocalTime(*date));
2097 }
2098 
2099 JS_FRIEND_API(int)
js_DateGetSeconds(JSContext * cx,JSObject * obj)2100 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2101 {
2102     jsdouble *date = date_getProlog(cx, obj, NULL);
2103 
2104     if (!date || JSDOUBLE_IS_NaN(*date))
2105 	return 0;
2106     return (int) SecFromTime(*date);
2107 }
2108 
2109 JS_FRIEND_API(void)
js_DateSetYear(JSContext * cx,JSObject * obj,int year)2110 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2111 {
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);
2128 }
2129 
2130 JS_FRIEND_API(void)
js_DateSetMonth(JSContext * cx,JSObject * obj,int month)2131 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2132 {
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);
2149 }
2150 
2151 JS_FRIEND_API(void)
js_DateSetDate(JSContext * cx,JSObject * obj,int date)2152 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2153 {
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);
2169 }
2170 
2171 JS_FRIEND_API(void)
js_DateSetHours(JSContext * cx,JSObject * obj,int hours)2172 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2173 {
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);
2189 }
2190 
2191 JS_FRIEND_API(void)
js_DateSetMinutes(JSContext * cx,JSObject * obj,int minutes)2192 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2193 {
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);
2209 }
2210 
2211 JS_FRIEND_API(void)
js_DateSetSeconds(JSContext * cx,JSObject * obj,int seconds)2212 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2213 {
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);
2229 }
2230 
2231 JS_FRIEND_API(jsdouble)
js_DateGetMsecSinceEpoch(JSContext * cx,JSObject * obj)2232 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2233 {
2234     jsdouble *date = date_getProlog(cx, obj, NULL);
2235     if (!date || JSDOUBLE_IS_NaN(*date))
2236         return 0;
2237     return (*date);
2238 }
2239