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 #define MakeTime(hour, min, sec, ms) \
315 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
316 
317 static jsdouble
MakeDay(jsdouble year,jsdouble month,jsdouble date)318 MakeDay(jsdouble year, jsdouble month, jsdouble date)
319 {
320     JSBool leap;
321     jsdouble yearday;
322     jsdouble monthday;
323 
324     year += floor(month / 12);
325 
326     month = fmod(month, 12.0);
327     if (month < 0)
328         month += 12;
329 
330     leap = (DaysInYear((jsint) year) == 366);
331 
332     yearday = floor(TimeFromYear(year) / msPerDay);
333     monthday = DayFromMonth(month, leap);
334 
335     return yearday + monthday + date - 1;
336 }
337 
338 #define MakeDate(day, time) ((day) * msPerDay + (time))
339 
340 /*
341  * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
342  *
343  * yearStartingWith[0][i] is an example non-leap year where
344  * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
345  *
346  * yearStartingWith[1][i] is an example leap year where
347  * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
348  */
349 static jsint yearStartingWith[2][7] = {
350     {1978, 1973, 1974, 1975, 1981, 1971, 1977},
351     {1984, 1996, 1980, 1992, 1976, 1988, 1972}
352 };
353 
354 /*
355  * Find a year for which any given date will fall on the same weekday.
356  *
357  * This function should be used with caution when used other than
358  * for determining DST; it hasn't been proven not to produce an
359  * incorrect year for times near year boundaries.
360  */
361 static jsint
EquivalentYearForDST(jsint year)362 EquivalentYearForDST(jsint year)
363 {
364     jsint day;
365     JSBool isLeapYear;
366 
367     day = (jsint) DayFromYear(year) + 4;
368     day = day % 7;
369     if (day < 0)
370         day += 7;
371 
372     isLeapYear = (DaysInYear(year) == 366);
373 
374     return yearStartingWith[isLeapYear][day];
375 }
376 
377 /* LocalTZA gets set by js_InitDateClass() */
378 static jsdouble LocalTZA;
379 
380 static jsdouble
DaylightSavingTA(jsdouble t)381 DaylightSavingTA(jsdouble t)
382 {
383     volatile int64 PR_t;
384     int64 ms2us;
385     int64 offset;
386     jsdouble result;
387 
388     /* abort if NaN */
389     if (JSDOUBLE_IS_NaN(t))
390         return t;
391 
392     /*
393      * If earlier than 1970 or after 2038, potentially beyond the ken of
394      * many OSes, map it to an equivalent year before asking.
395      */
396     if (t < 0.0 || t > 2145916800000.0) {
397         jsint year;
398         jsdouble day;
399 
400         year = EquivalentYearForDST(YearFromTime(t));
401         day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
402         t = MakeDate(day, TimeWithinDay(t));
403     }
404 
405     /* put our t in an LL, and map it to usec for prtime */
406     JSLL_D2L(PR_t, t);
407     JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
408     JSLL_MUL(PR_t, PR_t, ms2us);
409 
410     offset = PRMJ_DSTOffset(PR_t);
411 
412     JSLL_DIV(offset, offset, ms2us);
413     JSLL_L2D(result, offset);
414     return result;
415 }
416 
417 
418 #define AdjustTime(t)   fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
419 
420 #define LocalTime(t)    ((t) + AdjustTime(t))
421 
422 static jsdouble
UTC(jsdouble t)423 UTC(jsdouble t)
424 {
425     return t - AdjustTime(t - LocalTZA);
426 }
427 
428 static intN
HourFromTime(jsdouble t)429 HourFromTime(jsdouble t)
430 {
431     intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
432     if (result < 0)
433         result += (intN)HoursPerDay;
434     return result;
435 }
436 
437 static intN
MinFromTime(jsdouble t)438 MinFromTime(jsdouble t)
439 {
440     intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
441     if (result < 0)
442         result += (intN)MinutesPerHour;
443     return result;
444 }
445 
446 static intN
SecFromTime(jsdouble t)447 SecFromTime(jsdouble t)
448 {
449     intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
450     if (result < 0)
451         result += (intN)SecondsPerMinute;
452     return result;
453 }
454 
455 static intN
msFromTime(jsdouble t)456 msFromTime(jsdouble t)
457 {
458     intN result = (intN) fmod(t, msPerSecond);
459     if (result < 0)
460         result += (intN)msPerSecond;
461     return result;
462 }
463 
464 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
465                       && !((d < 0 ? -d : d) > HalfTimeDomain)) \
466                      ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
467 
468 /**
469  * end of ECMA 'support' functions
470  */
471 
472 /*
473  * Other Support routines and definitions
474  */
475 
476 JSClass js_DateClass = {
477     js_Date_str,
478     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
479     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
480     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
481     JSCLASS_NO_OPTIONAL_MEMBERS
482 };
483 
484 /* for use by date_parse */
485 
486 static const char* wtb[] = {
487     "am", "pm",
488     "monday", "tuesday", "wednesday", "thursday", "friday",
489     "saturday", "sunday",
490     "january", "february", "march", "april", "may", "june",
491     "july", "august", "september", "october", "november", "december",
492     "gmt", "ut", "utc",
493     "est", "edt",
494     "cst", "cdt",
495     "mst", "mdt",
496     "pst", "pdt"
497     /* time zone table needs to be expanded */
498 };
499 
500 static int ttb[] = {
501     -1, -2, 0, 0, 0, 0, 0, 0, 0,       /* AM/PM */
502     2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
503     10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */
504     10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */
505     10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */
506     10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */
507     10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */
508 };
509 
510 /* helper for date_parse */
511 static JSBool
date_regionMatches(const char * s1,int s1off,const jschar * s2,int s2off,int count,int ignoreCase)512 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
513                    int count, int ignoreCase)
514 {
515     JSBool result = JS_FALSE;
516     /* return true if matches, otherwise, false */
517 
518     while (count > 0 && s1[s1off] && s2[s2off]) {
519         if (ignoreCase) {
520             if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
521                 break;
522             }
523         } else {
524             if ((jschar)s1[s1off] != s2[s2off]) {
525                 break;
526             }
527         }
528         s1off++;
529         s2off++;
530         count--;
531     }
532 
533     if (count == 0) {
534         result = JS_TRUE;
535     }
536 
537     return result;
538 }
539 
540 /* find UTC time from given date... no 1900 correction! */
541 static jsdouble
date_msecFromDate(jsdouble year,jsdouble mon,jsdouble mday,jsdouble hour,jsdouble min,jsdouble sec,jsdouble msec)542 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
543                   jsdouble min, jsdouble sec, jsdouble msec)
544 {
545     jsdouble day;
546     jsdouble msec_time;
547     jsdouble result;
548 
549     day = MakeDay(year, mon, mday);
550     msec_time = MakeTime(hour, min, sec, msec);
551     result = MakeDate(day, msec_time);
552     return result;
553 }
554 
555 /*
556  * See ECMA 15.9.4.[3-10];
557  */
558 /* XXX this function must be above date_parseString to avoid a
559    horrid bug in the Win16 1.52 compiler */
560 #define MAXARGS        7
561 static JSBool
date_UTC(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)562 date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
563 {
564     jsdouble array[MAXARGS];
565     uintN loop;
566     jsdouble d;
567 
568     for (loop = 0; loop < MAXARGS; loop++) {
569         if (loop < argc) {
570             if (!js_ValueToNumber(cx, argv[loop], &d))
571                 return JS_FALSE;
572             /* return NaN if any arg is NaN */
573             if (!JSDOUBLE_IS_FINITE(d)) {
574                 return js_NewNumberValue(cx, d, rval);
575             }
576             array[loop] = floor(d);
577         } else {
578             array[loop] = 0;
579         }
580     }
581 
582     /* adjust 2-digit years into the 20th century */
583     if (array[0] >= 0 && array[0] <= 99)
584         array[0] += 1900;
585 
586     /* if we got a 0 for 'date' (which is out of range)
587      * pretend it's a 1.  (So Date.UTC(1972, 5) works) */
588     if (array[2] < 1)
589         array[2] = 1;
590 
591     d = date_msecFromDate(array[0], array[1], array[2],
592                               array[3], array[4], array[5], array[6]);
593     d = TIMECLIP(d);
594 
595     return js_NewNumberValue(cx, d, rval);
596 }
597 
598 static JSBool
date_parseString(JSString * str,jsdouble * result)599 date_parseString(JSString *str, jsdouble *result)
600 {
601     jsdouble msec;
602 
603     const jschar *s = JSSTRING_CHARS(str);
604     size_t limit = JSSTRING_LENGTH(str);
605     size_t i = 0;
606     int year = -1;
607     int mon = -1;
608     int mday = -1;
609     int hour = -1;
610     int min = -1;
611     int sec = -1;
612     int c = -1;
613     int n = -1;
614     jsdouble tzoffset = -1;  /* was an int, overflowed on win16!!! */
615     int prevc = 0;
616     JSBool seenplusminus = JS_FALSE;
617     int temp;
618     JSBool seenmonthname = JS_FALSE;
619 
620     if (limit == 0)
621         goto syntax;
622     while (i < limit) {
623         c = s[i];
624         i++;
625         if (c <= ' ' || c == ',' || c == '-') {
626             if (c == '-' && '0' <= s[i] && s[i] <= '9') {
627               prevc = c;
628             }
629             continue;
630         }
631         if (c == '(') { /* comments) */
632             int depth = 1;
633             while (i < limit) {
634                 c = s[i];
635                 i++;
636                 if (c == '(') depth++;
637                 else if (c == ')')
638                     if (--depth <= 0)
639                         break;
640             }
641             continue;
642         }
643         if ('0' <= c && c <= '9') {
644             n = c - '0';
645             while (i < limit && '0' <= (c = s[i]) && c <= '9') {
646                 n = n * 10 + c - '0';
647                 i++;
648             }
649 
650             /* allow TZA before the year, so
651              * 'Wed Nov 05 21:49:11 GMT-0800 1997'
652              * works */
653 
654             /* uses of seenplusminus allow : in TZA, so Java
655              * no-timezone style of GMT+4:30 works
656              */
657 
658             if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
659                 /* make ':' case below change tzoffset */
660                 seenplusminus = JS_TRUE;
661 
662                 /* offset */
663                 if (n < 24)
664                     n = n * 60; /* EG. "GMT-3" */
665                 else
666                     n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
667                 if (prevc == '+')       /* plus means east of GMT */
668                     n = -n;
669                 if (tzoffset != 0 && tzoffset != -1)
670                     goto syntax;
671                 tzoffset = n;
672             } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
673                 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
674                     year = n;
675                 else
676                     goto syntax;
677             } else if (c == ':') {
678                 if (hour < 0)
679                     hour = /*byte*/ n;
680                 else if (min < 0)
681                     min = /*byte*/ n;
682                 else
683                     goto syntax;
684             } else if (c == '/') {
685                 /* until it is determined that mon is the actual
686                    month, keep it as 1-based rather than 0-based */
687                 if (mon < 0)
688                     mon = /*byte*/ n;
689                 else if (mday < 0)
690                     mday = /*byte*/ n;
691                 else
692                     goto syntax;
693             } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
694                 goto syntax;
695             } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */
696                 if (tzoffset < 0)
697                     tzoffset -= n;
698                 else
699                     tzoffset += n;
700             } else if (hour >= 0 && min < 0) {
701                 min = /*byte*/ n;
702             } else if (prevc == ':' && min >= 0 && sec < 0) {
703                 sec = /*byte*/ n;
704             } else if (mon < 0) {
705                 mon = /*byte*/n;
706             } else if (mon >= 0 && mday < 0) {
707                 mday = /*byte*/ n;
708             } else if (mon >= 0 && mday >= 0 && year < 0) {
709                 year = n;
710             } else {
711                 goto syntax;
712             }
713             prevc = 0;
714         } else if (c == '/' || c == ':' || c == '+' || c == '-') {
715             prevc = c;
716         } else {
717             size_t st = i - 1;
718             int k;
719             while (i < limit) {
720                 c = s[i];
721                 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
722                     break;
723                 i++;
724             }
725             if (i <= st + 1)
726                 goto syntax;
727             for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
728                 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
729                     int action = ttb[k];
730                     if (action != 0) {
731                         if (action < 0) {
732                             /*
733                              * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
734                              * 12:30, instead of blindly adding 12 if PM.
735                              */
736                             JS_ASSERT(action == -1 || action == -2);
737                             if (hour > 12 || hour < 0) {
738                                 goto syntax;
739                             } else {
740                                 if (action == -1 && hour == 12) { /* am */
741                                     hour = 0;
742                                 } else if (action == -2 && hour != 12) { /* pm */
743                                     hour += 12;
744                                 }
745                             }
746                         } else if (action <= 13) { /* month! */
747                             /* Adjust mon to be 1-based until the final values
748                                for mon, mday and year are adjusted below */
749                             if (seenmonthname) {
750                                 goto syntax;
751                             }
752                             seenmonthname = JS_TRUE;
753                             temp = /*byte*/ (action - 2) + 1;
754 
755                             if (mon < 0) {
756                                 mon = temp;
757                             } else if (mday < 0) {
758                                 mday = mon;
759                                 mon = temp;
760                             } else if (year < 0) {
761                                 year = mon;
762                                 mon = temp;
763                             } else {
764                                 goto syntax;
765                             }
766                         } else {
767                             tzoffset = action - 10000;
768                         }
769                     }
770                     break;
771                 }
772             if (k < 0)
773                 goto syntax;
774             prevc = 0;
775         }
776     }
777     if (year < 0 || mon < 0 || mday < 0)
778         goto syntax;
779     /*
780       Case 1. The input string contains an English month name.
781               The form of the string can be month f l, or f month l, or
782               f l month which each evaluate to the same date.
783               If f and l are both greater than or equal to 70, or
784               both less than 70, the date is invalid.
785               The year is taken to be the greater of the values f, l.
786               If the year is greater than or equal to 70 and less than 100,
787               it is considered to be the number of years after 1900.
788       Case 2. The input string is of the form "f/m/l" where f, m and l are
789               integers, e.g. 7/16/45.
790               Adjust the mon, mday and year values to achieve 100% MSIE
791               compatibility.
792               a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
793                  i.  If year < 100, it is the number of years after 1900
794                  ii. If year >= 100, it is the number of years after 0.
795               b. If 70 <= f < 100
796                  i.  If m < 70, f/m/l is interpreted as
797                      year/month/day where year is the number of years after
798                      1900.
799                  ii. If m >= 70, the date is invalid.
800               c. If f >= 100
801                  i.  If m < 70, f/m/l is interpreted as
802                      year/month/day where year is the number of years after 0.
803                  ii. If m >= 70, the date is invalid.
804     */
805     if (seenmonthname) {
806         if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
807             goto syntax;
808         }
809         if (mday > year) {
810             temp = year;
811             year = mday;
812             mday = temp;
813         }
814         if (year >= 70 && year < 100) {
815             year += 1900;
816         }
817     } else if (mon < 70) { /* (a) month/day/year */
818         if (year < 100) {
819             year += 1900;
820         }
821     } else if (mon < 100) { /* (b) year/month/day */
822         if (mday < 70) {
823             temp = year;
824             year = mon + 1900;
825             mon = mday;
826             mday = temp;
827         } else {
828             goto syntax;
829         }
830     } else { /* (c) year/month/day */
831         if (mday < 70) {
832             temp = year;
833             year = mon;
834             mon = mday;
835             mday = temp;
836         } else {
837             goto syntax;
838         }
839     }
840     mon -= 1; /* convert month to 0-based */
841     if (sec < 0)
842         sec = 0;
843     if (min < 0)
844         min = 0;
845     if (hour < 0)
846         hour = 0;
847     if (tzoffset == -1) { /* no time zone specified, have to use local */
848         jsdouble msec_time;
849         msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
850 
851         *result = UTC(msec_time);
852         return JS_TRUE;
853     }
854 
855     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
856     msec += tzoffset * msPerMinute;
857     *result = msec;
858     return JS_TRUE;
859 
860 syntax:
861     /* syntax error */
862     *result = 0;
863     return JS_FALSE;
864 }
865 
866 static JSBool
date_parse(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)867 date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
868 {
869     JSString *str;
870     jsdouble result;
871 
872     str = js_ValueToString(cx, argv[0]);
873     if (!str)
874         return JS_FALSE;
875     if (!date_parseString(str, &result)) {
876         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
877         return JS_TRUE;
878     }
879 
880     result = TIMECLIP(result);
881     return js_NewNumberValue(cx, result, rval);
882 }
883 
884 static JSBool
date_now(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)885 date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
886 {
887     int64 us, ms, us2ms;
888     jsdouble msec_time;
889 
890     us = PRMJ_Now();
891     JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
892     JSLL_DIV(ms, us, us2ms);
893     JSLL_L2D(msec_time, ms);
894 
895     return js_NewDoubleValue(cx, msec_time, rval);
896 }
897 
898 /*
899  * Check that obj is an object of class Date, and get the date value.
900  * Return NULL on failure.
901  */
902 static jsdouble *
date_getProlog(JSContext * cx,JSObject * obj,jsval * argv)903 date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)
904 {
905     if (!JS_InstanceOf(cx, obj, &js_DateClass, argv))
906         return NULL;
907     return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
908 }
909 
910 /*
911  * See ECMA 15.9.5.4 thru 15.9.5.23
912  */
913 static JSBool
date_getTime(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)914 date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
915 {
916     jsdouble *date = date_getProlog(cx, obj, argv);
917     if (!date)
918         return JS_FALSE;
919 
920     return js_NewNumberValue(cx, *date, rval);
921 }
922 
923 static JSBool
date_getYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)924 date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
925 {
926     jsdouble *date;
927     jsdouble result;
928 
929     date = date_getProlog(cx, obj, argv);
930     if (!date)
931         return JS_FALSE;
932 
933     result = *date;
934     if (!JSDOUBLE_IS_FINITE(result))
935         return js_NewNumberValue(cx, result, rval);
936 
937     result = YearFromTime(LocalTime(result));
938 
939     /* Follow ECMA-262 to the letter, contrary to IE JScript. */
940     result -= 1900;
941     return js_NewNumberValue(cx, result, rval);
942 }
943 
944 static JSBool
date_getFullYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)945 date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
946                  jsval *rval)
947 {
948     jsdouble result;
949     jsdouble *date = date_getProlog(cx, obj, argv);
950     if (!date)
951         return JS_FALSE;
952     result = *date;
953 
954     if (!JSDOUBLE_IS_FINITE(result))
955         return js_NewNumberValue(cx, result, rval);
956 
957     result = YearFromTime(LocalTime(result));
958     return js_NewNumberValue(cx, result, rval);
959 }
960 
961 static JSBool
date_getUTCFullYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)962 date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
963                     jsval *rval)
964 {
965     jsdouble result;
966     jsdouble *date = date_getProlog(cx, obj, argv);
967     if (!date)
968         return JS_FALSE;
969     result = *date;
970 
971     if (!JSDOUBLE_IS_FINITE(result))
972         return js_NewNumberValue(cx, result, rval);
973 
974     result = YearFromTime(result);
975     return js_NewNumberValue(cx, result, rval);
976 }
977 
978 static JSBool
date_getMonth(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)979 date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
980               jsval *rval)
981 {
982     jsdouble result;
983     jsdouble *date = date_getProlog(cx, obj, argv);
984     if (!date)
985         return JS_FALSE;
986     result = *date;
987 
988     if (!JSDOUBLE_IS_FINITE(result))
989         return js_NewNumberValue(cx, result, rval);
990 
991     result = MonthFromTime(LocalTime(result));
992     return js_NewNumberValue(cx, result, rval);
993 }
994 
995 static JSBool
date_getUTCMonth(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)996 date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
997                  jsval *rval)
998 {
999     jsdouble result;
1000     jsdouble *date = date_getProlog(cx, obj, argv);
1001     if (!date)
1002         return JS_FALSE;
1003     result = *date;
1004 
1005     if (!JSDOUBLE_IS_FINITE(result))
1006         return js_NewNumberValue(cx, result, rval);
1007 
1008     result = MonthFromTime(result);
1009     return js_NewNumberValue(cx, result, rval);
1010 }
1011 
1012 static JSBool
date_getDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1013 date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1014 {
1015     jsdouble result;
1016     jsdouble *date = date_getProlog(cx, obj, argv);
1017     if (!date)
1018         return JS_FALSE;
1019     result = *date;
1020 
1021     if (!JSDOUBLE_IS_FINITE(result))
1022         return js_NewNumberValue(cx, result, rval);
1023 
1024     result = LocalTime(result);
1025     result = DateFromTime(result);
1026     return js_NewNumberValue(cx, result, rval);
1027 }
1028 
1029 static JSBool
date_getUTCDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1030 date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1031                 jsval *rval)
1032 {
1033     jsdouble result;
1034     jsdouble *date = date_getProlog(cx, obj, argv);
1035     if (!date)
1036         return JS_FALSE;
1037     result = *date;
1038 
1039     if (!JSDOUBLE_IS_FINITE(result))
1040         return js_NewNumberValue(cx, result, rval);
1041 
1042     result = DateFromTime(result);
1043     return js_NewNumberValue(cx, result, rval);
1044 }
1045 
1046 static JSBool
date_getDay(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1047 date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1048 {
1049     jsdouble result;
1050     jsdouble *date = date_getProlog(cx, obj, argv);
1051     if (!date)
1052         return JS_FALSE;
1053     result = *date;
1054 
1055     if (!JSDOUBLE_IS_FINITE(result))
1056         return js_NewNumberValue(cx, result, rval);
1057 
1058     result = LocalTime(result);
1059     result = WeekDay(result);
1060     return js_NewNumberValue(cx, result, rval);
1061 }
1062 
1063 static JSBool
date_getUTCDay(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1064 date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1065                jsval *rval)
1066 {
1067     jsdouble result;
1068     jsdouble *date = date_getProlog(cx, obj, argv);
1069     if (!date)
1070         return JS_FALSE;
1071     result = *date;
1072 
1073     if (!JSDOUBLE_IS_FINITE(result))
1074         return js_NewNumberValue(cx, result, rval);
1075 
1076     result = WeekDay(result);
1077     return js_NewNumberValue(cx, result, rval);
1078 }
1079 
1080 static JSBool
date_getHours(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1081 date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1082               jsval *rval)
1083 {
1084     jsdouble result;
1085     jsdouble *date = date_getProlog(cx, obj, argv);
1086     if (!date)
1087         return JS_FALSE;
1088     result = *date;
1089 
1090     if (!JSDOUBLE_IS_FINITE(result))
1091         return js_NewNumberValue(cx, result, rval);
1092 
1093     result = HourFromTime(LocalTime(result));
1094     return js_NewNumberValue(cx, result, rval);
1095 }
1096 
1097 static JSBool
date_getUTCHours(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1098 date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1099                  jsval *rval)
1100 {
1101     jsdouble result;
1102     jsdouble *date = date_getProlog(cx, obj, argv);
1103     if (!date)
1104         return JS_FALSE;
1105     result = *date;
1106 
1107     if (!JSDOUBLE_IS_FINITE(result))
1108         return js_NewNumberValue(cx, result, rval);
1109 
1110     result = HourFromTime(result);
1111     return js_NewNumberValue(cx, result, rval);
1112 }
1113 
1114 static JSBool
date_getMinutes(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1115 date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1116                 jsval *rval)
1117 {
1118     jsdouble result;
1119     jsdouble *date = date_getProlog(cx, obj, argv);
1120     if (!date)
1121         return JS_FALSE;
1122     result = *date;
1123 
1124     if (!JSDOUBLE_IS_FINITE(result))
1125         return js_NewNumberValue(cx, result, rval);
1126 
1127     result = MinFromTime(LocalTime(result));
1128     return js_NewNumberValue(cx, result, rval);
1129 }
1130 
1131 static JSBool
date_getUTCMinutes(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1132 date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1133                    jsval *rval)
1134 {
1135     jsdouble result;
1136     jsdouble *date = date_getProlog(cx, obj, argv);
1137     if (!date)
1138         return JS_FALSE;
1139     result = *date;
1140 
1141     if (!JSDOUBLE_IS_FINITE(result))
1142         return js_NewNumberValue(cx, result, rval);
1143 
1144     result = MinFromTime(result);
1145     return js_NewNumberValue(cx, result, rval);
1146 }
1147 
1148 /* Date.getSeconds is mapped to getUTCSeconds */
1149 
1150 static JSBool
date_getUTCSeconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1151 date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1152                 jsval *rval)
1153 {
1154     jsdouble result;
1155     jsdouble *date = date_getProlog(cx, obj, argv);
1156     if (!date)
1157         return JS_FALSE;
1158     result = *date;
1159 
1160     if (!JSDOUBLE_IS_FINITE(result))
1161         return js_NewNumberValue(cx, result, rval);
1162 
1163     result = SecFromTime(result);
1164     return js_NewNumberValue(cx, result, rval);
1165 }
1166 
1167 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1168 
1169 static JSBool
date_getUTCMilliseconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1170 date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1171                      jsval *rval)
1172 {
1173     jsdouble result;
1174     jsdouble *date = date_getProlog(cx, obj, argv);
1175     if (!date)
1176         return JS_FALSE;
1177     result = *date;
1178 
1179     if (!JSDOUBLE_IS_FINITE(result))
1180         return js_NewNumberValue(cx, result, rval);
1181 
1182     result = msFromTime(result);
1183     return js_NewNumberValue(cx, result, rval);
1184 }
1185 
1186 static JSBool
date_getTimezoneOffset(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1187 date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1188                        jsval *rval)
1189 {
1190     jsdouble result;
1191     jsdouble *date = date_getProlog(cx, obj, argv);
1192     if (!date)
1193         return JS_FALSE;
1194     result = *date;
1195 
1196     /*
1197      * Return the time zone offset in minutes for the current locale
1198      * that is appropriate for this time. This value would be a
1199      * constant except for daylight savings time.
1200      */
1201     result = (result - LocalTime(result)) / msPerMinute;
1202     return js_NewNumberValue(cx, result, rval);
1203 }
1204 
1205 static JSBool
date_setTime(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1206 date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1207 {
1208     jsdouble result;
1209     jsdouble *date = date_getProlog(cx, obj, argv);
1210     if (!date)
1211         return JS_FALSE;
1212 
1213     if (!js_ValueToNumber(cx, argv[0], &result))
1214         return JS_FALSE;
1215 
1216     result = TIMECLIP(result);
1217 
1218     *date = result;
1219     return js_NewNumberValue(cx, result, rval);
1220 }
1221 
1222 static JSBool
date_makeTime(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,uintN maxargs,JSBool local,jsval * rval)1223 date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1224               uintN maxargs, JSBool local, jsval *rval)
1225 {
1226     uintN i;
1227     jsdouble args[4], *argp, *stop;
1228     jsdouble hour, min, sec, msec;
1229     jsdouble lorutime; /* Local or UTC version of *date */
1230 
1231     jsdouble msec_time;
1232     jsdouble result;
1233 
1234     jsdouble *date = date_getProlog(cx, obj, argv);
1235     if (!date)
1236         return JS_FALSE;
1237 
1238     result = *date;
1239 
1240     /* just return NaN if the date is already NaN */
1241     if (!JSDOUBLE_IS_FINITE(result))
1242         return js_NewNumberValue(cx, result, rval);
1243 
1244     /* Satisfy the ECMA rule that if a function is called with
1245      * fewer arguments than the specified formal arguments, the
1246      * remaining arguments are set to undefined.  Seems like all
1247      * the Date.setWhatever functions in ECMA are only varargs
1248      * beyond the first argument; this should be set to undefined
1249      * if it's not given.  This means that "d = new Date();
1250      * d.setMilliseconds()" returns NaN.  Blech.
1251      */
1252     if (argc == 0)
1253         argc = 1;   /* should be safe, because length of all setters is 1 */
1254     else if (argc > maxargs)
1255         argc = maxargs;  /* clamp argc */
1256 
1257     for (i = 0; i < argc; i++) {
1258         if (!js_ValueToNumber(cx, argv[i], &args[i]))
1259             return JS_FALSE;
1260         if (!JSDOUBLE_IS_FINITE(args[i])) {
1261             *date = *cx->runtime->jsNaN;
1262             return js_NewNumberValue(cx, *date, rval);
1263         }
1264         args[i] = js_DoubleToInteger(args[i]);
1265     }
1266 
1267     if (local)
1268         lorutime = LocalTime(result);
1269     else
1270         lorutime = result;
1271 
1272     argp = args;
1273     stop = argp + argc;
1274     if (maxargs >= 4 && argp < stop)
1275         hour = *argp++;
1276     else
1277         hour = HourFromTime(lorutime);
1278 
1279     if (maxargs >= 3 && argp < stop)
1280         min = *argp++;
1281     else
1282         min = MinFromTime(lorutime);
1283 
1284     if (maxargs >= 2 && argp < stop)
1285         sec = *argp++;
1286     else
1287         sec = SecFromTime(lorutime);
1288 
1289     if (maxargs >= 1 && argp < stop)
1290         msec = *argp;
1291     else
1292         msec = msFromTime(lorutime);
1293 
1294     msec_time = MakeTime(hour, min, sec, msec);
1295     result = MakeDate(Day(lorutime), msec_time);
1296 
1297 /*     fprintf(stderr, "%f\n", result); */
1298 
1299     if (local)
1300         result = UTC(result);
1301 
1302 /*     fprintf(stderr, "%f\n", result); */
1303 
1304     *date = TIMECLIP(result);
1305     return js_NewNumberValue(cx, *date, rval);
1306 }
1307 
1308 static JSBool
date_setMilliseconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1309 date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1310                      jsval *argv, jsval *rval)
1311 {
1312     return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
1313 }
1314 
1315 static JSBool
date_setUTCMilliseconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1316 date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1317                         jsval *argv, jsval *rval)
1318 {
1319     return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
1320 }
1321 
1322 static JSBool
date_setSeconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1323 date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
1324                 jsval *argv, jsval *rval)
1325 {
1326     return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
1327 }
1328 
1329 static JSBool
date_setUTCSeconds(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1330 date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
1331                    jsval *argv, jsval *rval)
1332 {
1333     return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
1334 }
1335 
1336 static JSBool
date_setMinutes(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1337 date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
1338                 jsval *argv, jsval *rval)
1339 {
1340     return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
1341 }
1342 
1343 static JSBool
date_setUTCMinutes(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1344 date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
1345                    jsval *argv, jsval *rval)
1346 {
1347     return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
1348 }
1349 
1350 static JSBool
date_setHours(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1351 date_setHours(JSContext *cx, JSObject *obj, uintN argc,
1352               jsval *argv, jsval *rval)
1353 {
1354     return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
1355 }
1356 
1357 static JSBool
date_setUTCHours(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1358 date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
1359                  jsval *argv, jsval *rval)
1360 {
1361     return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
1362 }
1363 
1364 static JSBool
date_makeDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,uintN maxargs,JSBool local,jsval * rval)1365 date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
1366               jsval *argv, uintN maxargs, JSBool local, jsval *rval)
1367 {
1368     uintN i;
1369     jsdouble lorutime; /* local or UTC version of *date */
1370     jsdouble args[3], *argp, *stop;
1371     jsdouble year, month, day;
1372     jsdouble result;
1373 
1374     jsdouble *date = date_getProlog(cx, obj, argv);
1375     if (!date)
1376         return JS_FALSE;
1377 
1378     result = *date;
1379 
1380     /* see complaint about ECMA in date_MakeTime */
1381     if (argc == 0)
1382         argc = 1;   /* should be safe, because length of all setters is 1 */
1383     else if (argc > maxargs)
1384         argc = maxargs;   /* clamp argc */
1385 
1386     for (i = 0; i < argc; i++) {
1387         if (!js_ValueToNumber(cx, argv[i], &args[i]))
1388             return JS_FALSE;
1389         if (!JSDOUBLE_IS_FINITE(args[i])) {
1390             *date = *cx->runtime->jsNaN;
1391             return js_NewNumberValue(cx, *date, rval);
1392         }
1393         args[i] = js_DoubleToInteger(args[i]);
1394     }
1395 
1396     /* return NaN if date is NaN and we're not setting the year,
1397      * If we are, use 0 as the time. */
1398     if (!(JSDOUBLE_IS_FINITE(result))) {
1399         if (maxargs < 3)
1400             return js_NewNumberValue(cx, result, rval);
1401         else
1402             lorutime = +0.;
1403     } else {
1404         if (local)
1405             lorutime = LocalTime(result);
1406         else
1407             lorutime = result;
1408     }
1409 
1410     argp = args;
1411     stop = argp + argc;
1412     if (maxargs >= 3 && argp < stop)
1413         year = *argp++;
1414     else
1415         year = YearFromTime(lorutime);
1416 
1417     if (maxargs >= 2 && argp < stop)
1418         month = *argp++;
1419     else
1420         month = MonthFromTime(lorutime);
1421 
1422     if (maxargs >= 1 && argp < stop)
1423         day = *argp++;
1424     else
1425         day = DateFromTime(lorutime);
1426 
1427     day = MakeDay(year, month, day); /* day within year */
1428     result = MakeDate(day, TimeWithinDay(lorutime));
1429 
1430     if (local)
1431         result = UTC(result);
1432 
1433     *date = TIMECLIP(result);
1434     return js_NewNumberValue(cx, *date, rval);
1435 }
1436 
1437 static JSBool
date_setDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1438 date_setDate(JSContext *cx, JSObject *obj, uintN argc,
1439              jsval *argv, jsval *rval)
1440 {
1441     return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
1442 }
1443 
1444 static JSBool
date_setUTCDate(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1445 date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
1446                 jsval *argv, jsval *rval)
1447 {
1448     return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
1449 }
1450 
1451 static JSBool
date_setMonth(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1452 date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
1453               jsval *argv, jsval *rval)
1454 {
1455     return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
1456 }
1457 
1458 static JSBool
date_setUTCMonth(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1459 date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
1460                  jsval *argv, jsval *rval)
1461 {
1462     return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
1463 }
1464 
1465 static JSBool
date_setFullYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1466 date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
1467                  jsval *argv, jsval *rval)
1468 {
1469     return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
1470 }
1471 
1472 static JSBool
date_setUTCFullYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1473 date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
1474                     jsval *argv, jsval *rval)
1475 {
1476     return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
1477 }
1478 
1479 static JSBool
date_setYear(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1480 date_setYear(JSContext *cx, JSObject *obj, uintN argc,
1481              jsval *argv, jsval *rval)
1482 {
1483     jsdouble t;
1484     jsdouble year;
1485     jsdouble day;
1486     jsdouble result;
1487 
1488     jsdouble *date = date_getProlog(cx, obj, argv);
1489     if (!date)
1490         return JS_FALSE;
1491 
1492     result = *date;
1493 
1494     if (!js_ValueToNumber(cx, argv[0], &year))
1495         return JS_FALSE;
1496     if (!JSDOUBLE_IS_FINITE(year)) {
1497         *date = *cx->runtime->jsNaN;
1498         return js_NewNumberValue(cx, *date, rval);
1499     }
1500 
1501     year = js_DoubleToInteger(year);
1502 
1503     if (!JSDOUBLE_IS_FINITE(result)) {
1504         t = +0.0;
1505     } else {
1506         t = LocalTime(result);
1507     }
1508 
1509     if (year >= 0 && year <= 99)
1510         year += 1900;
1511 
1512     day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1513     result = MakeDate(day, TimeWithinDay(t));
1514     result = UTC(result);
1515 
1516     *date = TIMECLIP(result);
1517     return js_NewNumberValue(cx, *date, rval);
1518 }
1519 
1520 /* constants for toString, toUTCString */
1521 static char js_NaN_date_str[] = "Invalid Date";
1522 static const char* days[] =
1523 {
1524    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1525 };
1526 static const char* months[] =
1527 {
1528    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1529 };
1530 
1531 static JSBool
date_toGMTString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1532 date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
1533                  jsval *argv, jsval *rval)
1534 {
1535     char buf[100];
1536     JSString *str;
1537     jsdouble *date = date_getProlog(cx, obj, argv);
1538     if (!date)
1539         return JS_FALSE;
1540 
1541     if (!JSDOUBLE_IS_FINITE(*date)) {
1542         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1543     } else {
1544         jsdouble temp = *date;
1545 
1546         /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1547          * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
1548          */
1549         JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1550                     days[WeekDay(temp)],
1551                     DateFromTime(temp),
1552                     months[MonthFromTime(temp)],
1553                     YearFromTime(temp),
1554                     HourFromTime(temp),
1555                     MinFromTime(temp),
1556                     SecFromTime(temp));
1557     }
1558     str = JS_NewStringCopyZ(cx, buf);
1559     if (!str)
1560         return JS_FALSE;
1561     *rval = STRING_TO_JSVAL(str);
1562     return JS_TRUE;
1563 }
1564 
1565 /* for Date.toLocaleString; interface to PRMJTime date struct.
1566  * If findEquivalent is true, then try to map the year to an equivalent year
1567  * that's in range.
1568  */
1569 static void
new_explode(jsdouble timeval,PRMJTime * split,JSBool findEquivalent)1570 new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)
1571 {
1572     jsint year = YearFromTime(timeval);
1573     int16 adjustedYear;
1574 
1575     /* If the year doesn't fit in a PRMJTime, find something to do about it. */
1576     if (year > 32767 || year < -32768) {
1577         if (findEquivalent) {
1578             /* We're really just trying to get a timezone string; map the year
1579              * to some equivalent year in the range 0 to 2800.  Borrowed from
1580              * A. D. Olsen.
1581              */
1582             jsint cycles;
1583 #define CYCLE_YEARS 2800L
1584             cycles = (year >= 0) ? year / CYCLE_YEARS
1585                                  : -1 - (-1 - year) / CYCLE_YEARS;
1586             adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
1587         } else {
1588             /* Clamp it to the nearest representable year. */
1589             adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
1590         }
1591     } else {
1592         adjustedYear = (int16)year;
1593     }
1594 
1595     split->tm_usec = (int32) msFromTime(timeval) * 1000;
1596     split->tm_sec = (int8) SecFromTime(timeval);
1597     split->tm_min = (int8) MinFromTime(timeval);
1598     split->tm_hour = (int8) HourFromTime(timeval);
1599     split->tm_mday = (int8) DateFromTime(timeval);
1600     split->tm_mon = (int8) MonthFromTime(timeval);
1601     split->tm_wday = (int8) WeekDay(timeval);
1602     split->tm_year = (int16) adjustedYear;
1603     split->tm_yday = (int16) DayWithinYear(timeval, year);
1604 
1605     /* not sure how this affects things, but it doesn't seem
1606        to matter. */
1607     split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1608 }
1609 
1610 typedef enum formatspec {
1611     FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1612 } formatspec;
1613 
1614 /* helper function */
1615 static JSBool
date_format(JSContext * cx,jsdouble date,formatspec format,jsval * rval)1616 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1617 {
1618     char buf[100];
1619     JSString *str;
1620     char tzbuf[100];
1621     JSBool usetz;
1622     size_t i, tzlen;
1623     PRMJTime split;
1624 
1625     if (!JSDOUBLE_IS_FINITE(date)) {
1626         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1627     } else {
1628         jsdouble local = LocalTime(date);
1629 
1630         /* offset from GMT in minutes.  The offset includes daylight savings,
1631            if it applies. */
1632         jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1633 
1634         /* map 510 minutes to 0830 hours */
1635         intN offset = (minutes / 60) * 100 + minutes % 60;
1636 
1637         /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1638          * printed as 'GMT-0800' rather than as 'PST' to avoid
1639          * operating-system dependence on strftime (which
1640          * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
1641          * PST as 'Pacific Standard Time.'  This way we always know
1642          * what we're getting, and can parse it if we produce it.
1643          * The OS TZA string is included as a comment.
1644          */
1645 
1646         /* get a timezone string from the OS to include as a
1647            comment. */
1648         new_explode(date, &split, JS_TRUE);
1649         if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1650 
1651             /* Decide whether to use the resulting timezone string.
1652              *
1653              * Reject it if it contains any non-ASCII, non-alphanumeric
1654              * characters.  It's then likely in some other character
1655              * encoding, and we probably won't display it correctly.
1656              */
1657             usetz = JS_TRUE;
1658             tzlen = strlen(tzbuf);
1659             if (tzlen > 100) {
1660                 usetz = JS_FALSE;
1661             } else {
1662                 for (i = 0; i < tzlen; i++) {
1663                     jschar c = tzbuf[i];
1664                     if (c > 127 ||
1665                         !(isalpha(c) || isdigit(c) ||
1666                           c == ' ' || c == '(' || c == ')')) {
1667                         usetz = JS_FALSE;
1668                     }
1669                 }
1670             }
1671 
1672             /* Also reject it if it's not parenthesized or if it's '()'. */
1673             if (tzbuf[0] != '(' || tzbuf[1] == ')')
1674                 usetz = JS_FALSE;
1675         } else
1676             usetz = JS_FALSE;
1677 
1678         switch (format) {
1679           case FORMATSPEC_FULL:
1680             /*
1681              * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1682              * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
1683              */
1684             /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1685             JS_snprintf(buf, sizeof buf,
1686                         "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1687                         days[WeekDay(local)],
1688                         months[MonthFromTime(local)],
1689                         DateFromTime(local),
1690                         YearFromTime(local),
1691                         HourFromTime(local),
1692                         MinFromTime(local),
1693                         SecFromTime(local),
1694                         offset,
1695                         usetz ? " " : "",
1696                         usetz ? tzbuf : "");
1697             break;
1698           case FORMATSPEC_DATE:
1699             /* Tue Oct 31 2000 */
1700             JS_snprintf(buf, sizeof buf,
1701                         "%s %s %.2d %.4d",
1702                         days[WeekDay(local)],
1703                         months[MonthFromTime(local)],
1704                         DateFromTime(local),
1705                         YearFromTime(local));
1706             break;
1707           case FORMATSPEC_TIME:
1708             /* 09:41:40 GMT-0800 (PST) */
1709             JS_snprintf(buf, sizeof buf,
1710                         "%.2d:%.2d:%.2d GMT%+.4d%s%s",
1711                         HourFromTime(local),
1712                         MinFromTime(local),
1713                         SecFromTime(local),
1714                         offset,
1715                         usetz ? " " : "",
1716                         usetz ? tzbuf : "");
1717             break;
1718         }
1719     }
1720 
1721     str = JS_NewStringCopyZ(cx, buf);
1722     if (!str)
1723         return JS_FALSE;
1724     *rval = STRING_TO_JSVAL(str);
1725     return JS_TRUE;
1726 }
1727 
1728 static JSBool
date_toLocaleHelper(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval,char * format)1729 date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,
1730                     jsval *argv, jsval *rval, char *format)
1731 {
1732     char buf[100];
1733     JSString *str;
1734     PRMJTime split;
1735     jsdouble *date = date_getProlog(cx, obj, argv);
1736     if (!date)
1737         return JS_FALSE;
1738 
1739     if (!JSDOUBLE_IS_FINITE(*date)) {
1740         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1741     } else {
1742         intN result_len;
1743         jsdouble local = LocalTime(*date);
1744         new_explode(local, &split, JS_FALSE);
1745 
1746         /* let PRMJTime format it.       */
1747         result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1748 
1749         /* If it failed, default to toString. */
1750         if (result_len == 0)
1751             return date_format(cx, *date, FORMATSPEC_FULL, rval);
1752 
1753         /* Hacked check against undesired 2-digit year 00/00/00 form. */
1754         if (strcmp(format, "%x") == 0 && result_len >= 6 &&
1755             /* Format %x means use OS settings, which may have 2-digit yr, so
1756                hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
1757             !isdigit(buf[result_len - 3]) &&
1758             isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
1759             /* ...but not if starts with 4-digit year, like 2022/3/11. */
1760             !(isdigit(buf[0]) && isdigit(buf[1]) &&
1761               isdigit(buf[2]) && isdigit(buf[3]))) {
1762             JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
1763                         "%d", js_DateGetYear(cx, obj));
1764         }
1765 
1766     }
1767 
1768     if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
1769         return cx->localeCallbacks->localeToUnicode(cx, buf, rval);
1770 
1771     str = JS_NewStringCopyZ(cx, buf);
1772     if (!str)
1773         return JS_FALSE;
1774     *rval = STRING_TO_JSVAL(str);
1775     return JS_TRUE;
1776 }
1777 
1778 static JSBool
date_toLocaleString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1779 date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
1780                     jsval *argv, jsval *rval)
1781 {
1782     /* Use '%#c' for windows, because '%c' is
1783      * backward-compatible and non-y2k with msvc; '%#c' requests that a
1784      * full year be used in the result string.
1785      */
1786     return date_toLocaleHelper(cx, obj, argc, argv, rval,
1787 #if defined(_WIN32) && !defined(__MWERKS__)
1788                                    "%#c"
1789 #else
1790                                    "%c"
1791 #endif
1792                                    );
1793 }
1794 
1795 static JSBool
date_toLocaleDateString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1796 date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,
1797                     jsval *argv, jsval *rval)
1798 {
1799     /* Use '%#x' for windows, because '%x' is
1800      * backward-compatible and non-y2k with msvc; '%#x' requests that a
1801      * full year be used in the result string.
1802      */
1803     return date_toLocaleHelper(cx, obj, argc, argv, rval,
1804 #if defined(_WIN32) && !defined(__MWERKS__)
1805                                    "%#x"
1806 #else
1807                                    "%x"
1808 #endif
1809                                    );
1810 }
1811 
1812 static JSBool
date_toLocaleTimeString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1813 date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,
1814                         jsval *argv, jsval *rval)
1815 {
1816     return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");
1817 }
1818 
1819 static JSBool
date_toLocaleFormat(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1820 date_toLocaleFormat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1821                     jsval *rval)
1822 {
1823     JSString *fmt;
1824 
1825     if (argc == 0)
1826         return date_toLocaleString(cx, obj, argc, argv, rval);
1827 
1828     fmt = JS_ValueToString(cx, argv[0]);
1829     if (!fmt)
1830         return JS_FALSE;
1831 
1832     return date_toLocaleHelper(cx, obj, argc, argv, rval,
1833                                JS_GetStringBytes(fmt));
1834 }
1835 
1836 static JSBool
date_toTimeString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1837 date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,
1838                   jsval *argv, jsval *rval)
1839 {
1840     jsdouble *date = date_getProlog(cx, obj, argv);
1841     if (!date)
1842         return JS_FALSE;
1843     return date_format(cx, *date, FORMATSPEC_TIME, rval);
1844 }
1845 
1846 static JSBool
date_toDateString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1847 date_toDateString(JSContext *cx, JSObject *obj, uintN argc,
1848                   jsval *argv, jsval *rval)
1849 {
1850     jsdouble *date = date_getProlog(cx, obj, argv);
1851     if (!date)
1852         return JS_FALSE;
1853     return date_format(cx, *date, FORMATSPEC_DATE, rval);
1854 }
1855 
1856 #if JS_HAS_TOSOURCE
1857 #include <string.h>
1858 #include "jsdtoa.h"
1859 
1860 static JSBool
date_toSource(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1861 date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1862               jsval *rval)
1863 {
1864     jsdouble *date;
1865     char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1866     JSString *str;
1867 
1868     date = date_getProlog(cx, obj, argv);
1869     if (!date)
1870         return JS_FALSE;
1871 
1872     numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);
1873     if (!numStr) {
1874         JS_ReportOutOfMemory(cx);
1875         return JS_FALSE;
1876     }
1877 
1878     bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
1879     if (!bytes) {
1880         JS_ReportOutOfMemory(cx);
1881         return JS_FALSE;
1882     }
1883 
1884     str = JS_NewString(cx, bytes, strlen(bytes));
1885     if (!str) {
1886         free(bytes);
1887         return JS_FALSE;
1888     }
1889     *rval = STRING_TO_JSVAL(str);
1890     return JS_TRUE;
1891 }
1892 #endif
1893 
1894 static JSBool
date_toString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1895 date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1896               jsval *rval)
1897 {
1898     jsdouble *date = date_getProlog(cx, obj, argv);
1899     if (!date)
1900         return JS_FALSE;
1901     return date_format(cx, *date, FORMATSPEC_FULL, rval);
1902 }
1903 
1904 static JSBool
date_valueOf(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1905 date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1906              jsval *rval)
1907 {
1908     /* It is an error to call date_valueOf on a non-date object, but we don't
1909      * need to check for that explicitly here because every path calls
1910      * date_getProlog, which does the check.
1911      */
1912 
1913     /* If called directly with no arguments, convert to a time number. */
1914     if (argc == 0)
1915         return date_getTime(cx, obj, argc, argv, rval);
1916 
1917     /* Convert to number only if the hint was given, otherwise favor string. */
1918     if (argc == 1) {
1919         JSString *str, *str2;
1920 
1921         str = js_ValueToString(cx, argv[0]);
1922         if (!str)
1923             return JS_FALSE;
1924         str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
1925         if (js_EqualStrings(str, str2))
1926             return date_getTime(cx, obj, argc, argv, rval);
1927     }
1928     return date_toString(cx, obj, argc, argv, rval);
1929 }
1930 
1931 
1932 /*
1933  * creation and destruction
1934  */
1935 
1936 static JSFunctionSpec date_static_methods[] = {
1937     {"UTC",               date_UTC,               MAXARGS,0,0 },
1938     {"parse",             date_parse,             1,0,0 },
1939     {"now",               date_now,               0,0,0 },
1940     {0,0,0,0,0}
1941 };
1942 
1943 static JSFunctionSpec date_methods[] = {
1944     {"getTime",             date_getTime,           0,0,0 },
1945     {"getTimezoneOffset",   date_getTimezoneOffset, 0,0,0 },
1946     {"getYear",             date_getYear,           0,0,0 },
1947     {"getFullYear",         date_getFullYear,       0,0,0 },
1948     {"getUTCFullYear",      date_getUTCFullYear,    0,0,0 },
1949     {"getMonth",            date_getMonth,          0,0,0 },
1950     {"getUTCMonth",         date_getUTCMonth,       0,0,0 },
1951     {"getDate",             date_getDate,           0,0,0 },
1952     {"getUTCDate",          date_getUTCDate,        0,0,0 },
1953     {"getDay",              date_getDay,            0,0,0 },
1954     {"getUTCDay",           date_getUTCDay,         0,0,0 },
1955     {"getHours",            date_getHours,          0,0,0 },
1956     {"getUTCHours",         date_getUTCHours,       0,0,0 },
1957     {"getMinutes",          date_getMinutes,        0,0,0 },
1958     {"getUTCMinutes",       date_getUTCMinutes,     0,0,0 },
1959     {"getSeconds",          date_getUTCSeconds,     0,0,0 },
1960     {"getUTCSeconds",       date_getUTCSeconds,     0,0,0 },
1961     {"getMilliseconds",     date_getUTCMilliseconds,0,0,0 },
1962     {"getUTCMilliseconds",  date_getUTCMilliseconds,0,0,0 },
1963     {"setTime",             date_setTime,           1,0,0 },
1964     {"setYear",             date_setYear,           1,0,0 },
1965     {"setFullYear",         date_setFullYear,       3,0,0 },
1966     {"setUTCFullYear",      date_setUTCFullYear,    3,0,0 },
1967     {"setMonth",            date_setMonth,          2,0,0 },
1968     {"setUTCMonth",         date_setUTCMonth,       2,0,0 },
1969     {"setDate",             date_setDate,           1,0,0 },
1970     {"setUTCDate",          date_setUTCDate,        1,0,0 },
1971     {"setHours",            date_setHours,          4,0,0 },
1972     {"setUTCHours",         date_setUTCHours,       4,0,0 },
1973     {"setMinutes",          date_setMinutes,        3,0,0 },
1974     {"setUTCMinutes",       date_setUTCMinutes,     3,0,0 },
1975     {"setSeconds",          date_setSeconds,        2,0,0 },
1976     {"setUTCSeconds",       date_setUTCSeconds,     2,0,0 },
1977     {"setMilliseconds",     date_setMilliseconds,   1,0,0 },
1978     {"setUTCMilliseconds",  date_setUTCMilliseconds,1,0,0 },
1979     {"toUTCString",         date_toGMTString,       0,0,0 },
1980     {js_toLocaleString_str, date_toLocaleString,    0,0,0 },
1981     {"toLocaleDateString",  date_toLocaleDateString,0,0,0 },
1982     {"toLocaleTimeString",  date_toLocaleTimeString,0,0,0 },
1983     {"toLocaleFormat",      date_toLocaleFormat,    1,0,0 },
1984     {"toDateString",        date_toDateString,      0,0,0 },
1985     {"toTimeString",        date_toTimeString,      0,0,0 },
1986 #if JS_HAS_TOSOURCE
1987     {js_toSource_str,       date_toSource,          0,0,0 },
1988 #endif
1989     {js_toString_str,       date_toString,          0,0,0 },
1990     {js_valueOf_str,        date_valueOf,           0,0,0 },
1991     {0,0,0,0,0}
1992 };
1993 
1994 static jsdouble *
date_constructor(JSContext * cx,JSObject * obj)1995 date_constructor(JSContext *cx, JSObject* obj)
1996 {
1997     jsdouble *date;
1998 
1999     date = js_NewDouble(cx, 0.0, 0);
2000     if (!date)
2001         return NULL;
2002     OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
2003     return date;
2004 }
2005 
2006 static JSBool
Date(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)2007 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2008 {
2009     jsdouble *date;
2010     JSString *str;
2011     jsdouble d;
2012 
2013     /* Date called as function. */
2014     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
2015         int64 us, ms, us2ms;
2016         jsdouble msec_time;
2017 
2018         /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
2019          * so compute ms from PRMJ_Now.
2020          */
2021         us = PRMJ_Now();
2022         JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2023         JSLL_DIV(ms, us, us2ms);
2024         JSLL_L2D(msec_time, ms);
2025 
2026         return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
2027     }
2028 
2029     /* Date called as constructor. */
2030     if (argc == 0) {
2031         int64 us, ms, us2ms;
2032         jsdouble msec_time;
2033 
2034         date = date_constructor(cx, obj);
2035         if (!date)
2036             return JS_FALSE;
2037 
2038         us = PRMJ_Now();
2039         JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2040         JSLL_DIV(ms, us, us2ms);
2041         JSLL_L2D(msec_time, ms);
2042 
2043         *date = msec_time;
2044     } else if (argc == 1) {
2045         if (!JSVAL_IS_STRING(argv[0])) {
2046             /* the argument is a millisecond number */
2047             if (!js_ValueToNumber(cx, argv[0], &d))
2048                 return JS_FALSE;
2049             date = date_constructor(cx, obj);
2050             if (!date)
2051                 return JS_FALSE;
2052             *date = TIMECLIP(d);
2053         } else {
2054             /* the argument is a string; parse it. */
2055             date = date_constructor(cx, obj);
2056             if (!date)
2057                 return JS_FALSE;
2058 
2059             str = js_ValueToString(cx, argv[0]);
2060             if (!str)
2061                 return JS_FALSE;
2062 
2063             if (!date_parseString(str, date))
2064                 *date = *cx->runtime->jsNaN;
2065             *date = TIMECLIP(*date);
2066         }
2067     } else {
2068         jsdouble array[MAXARGS];
2069         uintN loop;
2070         jsdouble double_arg;
2071         jsdouble day;
2072         jsdouble msec_time;
2073 
2074         for (loop = 0; loop < MAXARGS; loop++) {
2075             if (loop < argc) {
2076                 if (!js_ValueToNumber(cx, argv[loop], &double_arg))
2077                     return JS_FALSE;
2078                 /* if any arg is NaN, make a NaN date object
2079                    and return */
2080                 if (!JSDOUBLE_IS_FINITE(double_arg)) {
2081                     date = date_constructor(cx, obj);
2082                     if (!date)
2083                         return JS_FALSE;
2084                     *date = *cx->runtime->jsNaN;
2085                     return JS_TRUE;
2086                 }
2087                 array[loop] = js_DoubleToInteger(double_arg);
2088             } else {
2089                 if (loop == 2) {
2090                     array[loop] = 1; /* Default the date argument to 1. */
2091                 } else {
2092                     array[loop] = 0;
2093                 }
2094             }
2095         }
2096 
2097         date = date_constructor(cx, obj);
2098         if (!date)
2099             return JS_FALSE;
2100 
2101         /* adjust 2-digit years into the 20th century */
2102         if (array[0] >= 0 && array[0] <= 99)
2103             array[0] += 1900;
2104 
2105         day = MakeDay(array[0], array[1], array[2]);
2106         msec_time = MakeTime(array[3], array[4], array[5], array[6]);
2107         msec_time = MakeDate(day, msec_time);
2108         msec_time = UTC(msec_time);
2109         *date = TIMECLIP(msec_time);
2110     }
2111     return JS_TRUE;
2112 }
2113 
2114 JSObject *
js_InitDateClass(JSContext * cx,JSObject * obj)2115 js_InitDateClass(JSContext *cx, JSObject *obj)
2116 {
2117     JSObject *proto;
2118     jsdouble *proto_date;
2119 
2120     /* set static LocalTZA */
2121     LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2122     proto = JS_InitClass(cx, obj, NULL, &js_DateClass, Date, MAXARGS,
2123                          NULL, date_methods, NULL, date_static_methods);
2124     if (!proto)
2125         return NULL;
2126 
2127     /* Alias toUTCString with toGMTString.  (ECMA B.2.6) */
2128     if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2129         return NULL;
2130 
2131     /* Set the value of the Date.prototype date to NaN */
2132     proto_date = date_constructor(cx, proto);
2133     if (!proto_date)
2134         return NULL;
2135     *proto_date = *cx->runtime->jsNaN;
2136 
2137     return proto;
2138 }
2139 
2140 JS_FRIEND_API(JSObject *)
js_NewDateObjectMsec(JSContext * cx,jsdouble msec_time)2141 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2142 {
2143     JSObject *obj;
2144     jsdouble *date;
2145 
2146     obj = js_NewObject(cx, &js_DateClass, NULL, NULL);
2147     if (!obj)
2148         return NULL;
2149 
2150     date = date_constructor(cx, obj);
2151     if (!date)
2152         return NULL;
2153 
2154     *date = msec_time;
2155     return obj;
2156 }
2157 
2158 JS_FRIEND_API(JSObject *)
js_NewDateObject(JSContext * cx,int year,int mon,int mday,int hour,int min,int sec)2159 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2160                  int hour, int min, int sec)
2161 {
2162     JSObject *obj;
2163     jsdouble msec_time;
2164 
2165     msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2166     obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2167     return obj;
2168 }
2169 
2170 JS_FRIEND_API(JSBool)
js_DateIsValid(JSContext * cx,JSObject * obj)2171 js_DateIsValid(JSContext *cx, JSObject* obj)
2172 {
2173     jsdouble *date = date_getProlog(cx, obj, NULL);
2174 
2175     if (!date || JSDOUBLE_IS_NaN(*date))
2176         return JS_FALSE;
2177     else
2178         return JS_TRUE;
2179 }
2180 
2181 JS_FRIEND_API(int)
js_DateGetYear(JSContext * cx,JSObject * obj)2182 js_DateGetYear(JSContext *cx, JSObject* obj)
2183 {
2184     jsdouble *date = date_getProlog(cx, obj, NULL);
2185 
2186     /* Preserve legacy API behavior of returning 0 for invalid dates. */
2187     if (!date || JSDOUBLE_IS_NaN(*date))
2188         return 0;
2189     return (int) YearFromTime(LocalTime(*date));
2190 }
2191 
2192 JS_FRIEND_API(int)
js_DateGetMonth(JSContext * cx,JSObject * obj)2193 js_DateGetMonth(JSContext *cx, JSObject* obj)
2194 {
2195     jsdouble *date = date_getProlog(cx, obj, NULL);
2196 
2197     if (!date || JSDOUBLE_IS_NaN(*date))
2198         return 0;
2199     return (int) MonthFromTime(LocalTime(*date));
2200 }
2201 
2202 JS_FRIEND_API(int)
js_DateGetDate(JSContext * cx,JSObject * obj)2203 js_DateGetDate(JSContext *cx, JSObject* obj)
2204 {
2205     jsdouble *date = date_getProlog(cx, obj, NULL);
2206 
2207     if (!date || JSDOUBLE_IS_NaN(*date))
2208         return 0;
2209     return (int) DateFromTime(LocalTime(*date));
2210 }
2211 
2212 JS_FRIEND_API(int)
js_DateGetHours(JSContext * cx,JSObject * obj)2213 js_DateGetHours(JSContext *cx, JSObject* obj)
2214 {
2215     jsdouble *date = date_getProlog(cx, obj, NULL);
2216 
2217     if (!date || JSDOUBLE_IS_NaN(*date))
2218         return 0;
2219     return (int) HourFromTime(LocalTime(*date));
2220 }
2221 
2222 JS_FRIEND_API(int)
js_DateGetMinutes(JSContext * cx,JSObject * obj)2223 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2224 {
2225     jsdouble *date = date_getProlog(cx, obj, NULL);
2226 
2227     if (!date || JSDOUBLE_IS_NaN(*date))
2228         return 0;
2229     return (int) MinFromTime(LocalTime(*date));
2230 }
2231 
2232 JS_FRIEND_API(int)
js_DateGetSeconds(JSContext * cx,JSObject * obj)2233 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2234 {
2235     jsdouble *date = date_getProlog(cx, obj, NULL);
2236 
2237     if (!date || JSDOUBLE_IS_NaN(*date))
2238         return 0;
2239     return (int) SecFromTime(*date);
2240 }
2241 
2242 JS_FRIEND_API(void)
js_DateSetYear(JSContext * cx,JSObject * obj,int year)2243 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2244 {
2245     jsdouble local;
2246     jsdouble *date = date_getProlog(cx, obj, NULL);
2247     if (!date)
2248         return;
2249     local = LocalTime(*date);
2250     /* reset date if it was NaN */
2251     if (JSDOUBLE_IS_NaN(local))
2252         local = 0;
2253     local = date_msecFromDate(year,
2254                               MonthFromTime(local),
2255                               DateFromTime(local),
2256                               HourFromTime(local),
2257                               MinFromTime(local),
2258                               SecFromTime(local),
2259                               msFromTime(local));
2260     *date = UTC(local);
2261 }
2262 
2263 JS_FRIEND_API(void)
js_DateSetMonth(JSContext * cx,JSObject * obj,int month)2264 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2265 {
2266     jsdouble local;
2267     jsdouble *date = date_getProlog(cx, obj, NULL);
2268     if (!date)
2269         return;
2270     local = LocalTime(*date);
2271     /* bail if date was NaN */
2272     if (JSDOUBLE_IS_NaN(local))
2273         return;
2274     local = date_msecFromDate(YearFromTime(local),
2275                               month,
2276                               DateFromTime(local),
2277                               HourFromTime(local),
2278                               MinFromTime(local),
2279                               SecFromTime(local),
2280                               msFromTime(local));
2281     *date = UTC(local);
2282 }
2283 
2284 JS_FRIEND_API(void)
js_DateSetDate(JSContext * cx,JSObject * obj,int date)2285 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2286 {
2287     jsdouble local;
2288     jsdouble *datep = date_getProlog(cx, obj, NULL);
2289     if (!datep)
2290         return;
2291     local = LocalTime(*datep);
2292     if (JSDOUBLE_IS_NaN(local))
2293         return;
2294     local = date_msecFromDate(YearFromTime(local),
2295                               MonthFromTime(local),
2296                               date,
2297                               HourFromTime(local),
2298                               MinFromTime(local),
2299                               SecFromTime(local),
2300                               msFromTime(local));
2301     *datep = UTC(local);
2302 }
2303 
2304 JS_FRIEND_API(void)
js_DateSetHours(JSContext * cx,JSObject * obj,int hours)2305 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2306 {
2307     jsdouble local;
2308     jsdouble *date = date_getProlog(cx, obj, NULL);
2309     if (!date)
2310         return;
2311     local = LocalTime(*date);
2312     if (JSDOUBLE_IS_NaN(local))
2313         return;
2314     local = date_msecFromDate(YearFromTime(local),
2315                               MonthFromTime(local),
2316                               DateFromTime(local),
2317                               hours,
2318                               MinFromTime(local),
2319                               SecFromTime(local),
2320                               msFromTime(local));
2321     *date = UTC(local);
2322 }
2323 
2324 JS_FRIEND_API(void)
js_DateSetMinutes(JSContext * cx,JSObject * obj,int minutes)2325 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2326 {
2327     jsdouble local;
2328     jsdouble *date = date_getProlog(cx, obj, NULL);
2329     if (!date)
2330         return;
2331     local = LocalTime(*date);
2332     if (JSDOUBLE_IS_NaN(local))
2333         return;
2334     local = date_msecFromDate(YearFromTime(local),
2335                               MonthFromTime(local),
2336                               DateFromTime(local),
2337                               HourFromTime(local),
2338                               minutes,
2339                               SecFromTime(local),
2340                               msFromTime(local));
2341     *date = UTC(local);
2342 }
2343 
2344 JS_FRIEND_API(void)
js_DateSetSeconds(JSContext * cx,JSObject * obj,int seconds)2345 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2346 {
2347     jsdouble local;
2348     jsdouble *date = date_getProlog(cx, obj, NULL);
2349     if (!date)
2350         return;
2351     local = LocalTime(*date);
2352     if (JSDOUBLE_IS_NaN(local))
2353         return;
2354     local = date_msecFromDate(YearFromTime(local),
2355                               MonthFromTime(local),
2356                               DateFromTime(local),
2357                               HourFromTime(local),
2358                               MinFromTime(local),
2359                               seconds,
2360                               msFromTime(local));
2361     *date = UTC(local);
2362 }
2363 
2364 JS_FRIEND_API(jsdouble)
js_DateGetMsecSinceEpoch(JSContext * cx,JSObject * obj)2365 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2366 {
2367     jsdouble *date = date_getProlog(cx, obj, NULL);
2368     if (!date || JSDOUBLE_IS_NaN(*date))
2369         return 0;
2370     return (*date);
2371 }
2372