/[jscoverage]/trunk/js/jsdate.cpp
ViewVC logotype

Annotation of /trunk/js/jsdate.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (hide annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 11 months ago) by siliconforks
File size: 74886 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 siliconforks 507 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 siliconforks 332 * vim: set ts=8 sw=4 et tw=78:
3     *
4     * ***** BEGIN LICENSE BLOCK *****
5     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6     *
7     * The contents of this file are subject to the Mozilla Public License Version
8     * 1.1 (the "License"); you may not use this file except in compliance with
9     * the License. You may obtain a copy of the License at
10     * http://www.mozilla.org/MPL/
11     *
12     * Software distributed under the License is distributed on an "AS IS" basis,
13     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14     * for the specific language governing rights and limitations under the
15     * License.
16     *
17     * The Original Code is Mozilla Communicator client code, released
18     * March 31, 1998.
19     *
20     * The Initial Developer of the Original Code is
21     * Netscape Communications Corporation.
22     * Portions created by the Initial Developer are Copyright (C) 1998
23     * the Initial Developer. All Rights Reserved.
24     *
25     * Contributor(s):
26     *
27     * Alternatively, the contents of this file may be used under the terms of
28     * either of the GNU General Public License Version 2 or later (the "GPL"),
29     * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30     * in which case the provisions of the GPL or the LGPL are applicable instead
31     * of those above. If you wish to allow use of your version of this file only
32     * under the terms of either the GPL or the LGPL, and not to allow others to
33     * use your version of this file under the terms of the MPL, indicate your
34     * decision by deleting the provisions above and replace them with the notice
35     * and other provisions required by the GPL or the LGPL. If you do not delete
36     * the provisions above, a recipient may use your version of this file under
37     * the terms of any one of the MPL, the GPL or the LGPL.
38     *
39     * ***** END LICENSE BLOCK ***** */
40    
41     /*
42     * JS date methods.
43     */
44    
45     /*
46     * "For example, OS/360 devotes 26 bytes of the permanently
47     * resident date-turnover routine to the proper handling of
48     * December 31 on leap years (when it is Day 366). That
49     * might have been left to the operator."
50     *
51     * Frederick Brooks, 'The Second-System Effect'.
52     */
53    
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 siliconforks 507 #include "jsstdint.h"
61 siliconforks 332 #include "jsprf.h"
62     #include "prmjtime.h"
63     #include "jsutil.h" /* Added by JSIFY */
64     #include "jsapi.h"
65     #include "jsversion.h"
66 siliconforks 399 #include "jsbuiltins.h"
67 siliconforks 332 #include "jscntxt.h"
68     #include "jsdate.h"
69     #include "jsinterp.h"
70     #include "jsnum.h"
71     #include "jsobj.h"
72     #include "jsstr.h"
73    
74     /*
75     * The JS 'Date' object is patterned after the Java 'Date' object.
76     * Here is an script:
77     *
78     * today = new Date();
79     *
80     * print(today.toLocaleString());
81     *
82     * weekDay = today.getDay();
83     *
84     *
85     * These Java (and ECMA-262) methods are supported:
86     *
87     * UTC
88     * getDate (getUTCDate)
89     * getDay (getUTCDay)
90     * getHours (getUTCHours)
91     * getMinutes (getUTCMinutes)
92     * getMonth (getUTCMonth)
93     * getSeconds (getUTCSeconds)
94     * getMilliseconds (getUTCMilliseconds)
95     * getTime
96     * getTimezoneOffset
97     * getYear
98     * getFullYear (getUTCFullYear)
99     * parse
100     * setDate (setUTCDate)
101     * setHours (setUTCHours)
102     * setMinutes (setUTCMinutes)
103     * setMonth (setUTCMonth)
104     * setSeconds (setUTCSeconds)
105     * setMilliseconds (setUTCMilliseconds)
106     * setTime
107     * setYear (setFullYear, setUTCFullYear)
108     * toGMTString (toUTCString)
109     * toLocaleString
110     * toString
111     *
112     *
113     * These Java methods are not supported
114     *
115     * setDay
116     * before
117     * after
118     * equals
119     * hashCode
120     */
121    
122     /*
123     * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
124     * definition and reduce dependence on NSPR. NSPR is used to get the current
125     * time in milliseconds, the time zone offset, and the daylight savings time
126     * offset for a given time. NSPR is also used for Date.toLocaleString(), for
127     * locale-specific formatting, and to get a string representing the timezone.
128     * (Which turns out to be platform-dependent.)
129     *
130     * To do:
131     * (I did some performance tests by timing how long it took to run what
132     * I had of the js ECMA conformance tests.)
133     *
134     * - look at saving results across multiple calls to supporting
135     * functions; the toString functions compute some of the same values
136     * multiple times. Although - I took a quick stab at this, and I lost
137     * rather than gained. (Fractionally.) Hard to tell what compilers/processors
138     * are doing these days.
139     *
140     * - look at tweaking function return types to return double instead
141     * of int; this seems to make things run slightly faster sometimes.
142     * (though it could be architecture-dependent.) It'd be good to see
143     * how this does on win32. (Tried it on irix.) Types could use a
144     * general going-over.
145     */
146    
147     /*
148     * Supporting functions - ECMA 15.9.1.*
149     */
150    
151     #define HalfTimeDomain 8.64e15
152     #define HoursPerDay 24.0
153     #define MinutesPerDay (HoursPerDay * MinutesPerHour)
154     #define MinutesPerHour 60.0
155     #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
156     #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
157     #define SecondsPerMinute 60.0
158    
159     #if defined(XP_WIN) || defined(XP_OS2)
160     /* Work around msvc double optimization bug by making these runtime values; if
161     * they're available at compile time, msvc optimizes division by them by
162     * computing the reciprocal and multiplying instead of dividing - this loses
163     * when the reciprocal isn't representable in a double.
164     */
165     static jsdouble msPerSecond = 1000.0;
166     static jsdouble msPerDay = SecondsPerDay * 1000.0;
167     static jsdouble msPerHour = SecondsPerHour * 1000.0;
168     static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
169     #else
170     #define msPerDay (SecondsPerDay * msPerSecond)
171     #define msPerHour (SecondsPerHour * msPerSecond)
172     #define msPerMinute (SecondsPerMinute * msPerSecond)
173     #define msPerSecond 1000.0
174     #endif
175    
176     #define Day(t) floor((t) / msPerDay)
177    
178     static jsdouble
179     TimeWithinDay(jsdouble t)
180     {
181     jsdouble result;
182     result = fmod(t, msPerDay);
183     if (result < 0)
184     result += msPerDay;
185     return result;
186     }
187    
188     #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
189     ? 366 : 365)
190    
191     /* math here has to be f.p, because we need
192     * floor((1968 - 1969) / 4) == -1
193     */
194     #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
195     - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
196     #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
197    
198     static jsint
199     YearFromTime(jsdouble t)
200     {
201     jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
202     jsdouble t2 = (jsdouble) TimeFromYear(y);
203    
204     if (t2 > t) {
205     y--;
206     } else {
207     if (t2 + msPerDay * DaysInYear(y) <= t)
208     y++;
209     }
210     return y;
211     }
212    
213     #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
214    
215     #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
216    
217     /*
218     * The following array contains the day of year for the first day of
219     * each month, where index 0 is January, and day 0 is January 1.
220     */
221 siliconforks 507 static jsdouble firstDayOfMonth[2][13] = {
222     {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0, 365.0},
223     {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0, 366.0}
224 siliconforks 332 };
225    
226 siliconforks 507 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]
227 siliconforks 332
228     static intN
229 siliconforks 507 DaysInMonth(jsint year, jsint month)
230     {
231     JSBool leap = (DaysInYear(year) == 366);
232     intN result = DayFromMonth(month, leap) - DayFromMonth(month-1, leap);
233     return result;
234     }
235    
236     static intN
237 siliconforks 332 MonthFromTime(jsdouble t)
238     {
239     intN d, step;
240     jsint year = YearFromTime(t);
241     d = DayWithinYear(t, year);
242    
243     if (d < (step = 31))
244     return 0;
245     step += (InLeapYear(t) ? 29 : 28);
246     if (d < step)
247     return 1;
248     if (d < (step += 31))
249     return 2;
250     if (d < (step += 30))
251     return 3;
252     if (d < (step += 31))
253     return 4;
254     if (d < (step += 30))
255     return 5;
256     if (d < (step += 31))
257     return 6;
258     if (d < (step += 31))
259     return 7;
260     if (d < (step += 30))
261     return 8;
262     if (d < (step += 31))
263     return 9;
264     if (d < (step += 30))
265     return 10;
266     return 11;
267     }
268    
269     static intN
270     DateFromTime(jsdouble t)
271     {
272     intN d, step, next;
273     jsint year = YearFromTime(t);
274     d = DayWithinYear(t, year);
275    
276     if (d <= (next = 30))
277     return d + 1;
278     step = next;
279     next += (InLeapYear(t) ? 29 : 28);
280     if (d <= next)
281     return d - step;
282     step = next;
283     if (d <= (next += 31))
284     return d - step;
285     step = next;
286     if (d <= (next += 30))
287     return d - step;
288     step = next;
289     if (d <= (next += 31))
290     return d - step;
291     step = next;
292     if (d <= (next += 30))
293     return d - step;
294     step = next;
295     if (d <= (next += 31))
296     return d - step;
297     step = next;
298     if (d <= (next += 31))
299     return d - step;
300     step = next;
301     if (d <= (next += 30))
302     return d - step;
303     step = next;
304     if (d <= (next += 31))
305     return d - step;
306     step = next;
307     if (d <= (next += 30))
308     return d - step;
309     step = next;
310     return d - step;
311     }
312    
313     static intN
314     WeekDay(jsdouble t)
315     {
316     jsint result;
317     result = (jsint) Day(t) + 4;
318     result = result % 7;
319     if (result < 0)
320     result += 7;
321     return (intN) result;
322     }
323    
324     #define MakeTime(hour, min, sec, ms) \
325     ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
326    
327     static jsdouble
328     MakeDay(jsdouble year, jsdouble month, jsdouble date)
329     {
330     JSBool leap;
331     jsdouble yearday;
332     jsdouble monthday;
333    
334     year += floor(month / 12);
335    
336     month = fmod(month, 12.0);
337     if (month < 0)
338     month += 12;
339    
340     leap = (DaysInYear((jsint) year) == 366);
341    
342     yearday = floor(TimeFromYear(year) / msPerDay);
343     monthday = DayFromMonth(month, leap);
344    
345     return yearday + monthday + date - 1;
346     }
347    
348     #define MakeDate(day, time) ((day) * msPerDay + (time))
349    
350     /*
351     * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
352     *
353     * yearStartingWith[0][i] is an example non-leap year where
354     * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
355     *
356     * yearStartingWith[1][i] is an example leap year where
357     * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
358     */
359     static jsint yearStartingWith[2][7] = {
360     {1978, 1973, 1974, 1975, 1981, 1971, 1977},
361     {1984, 1996, 1980, 1992, 1976, 1988, 1972}
362     };
363    
364     /*
365     * Find a year for which any given date will fall on the same weekday.
366     *
367     * This function should be used with caution when used other than
368     * for determining DST; it hasn't been proven not to produce an
369     * incorrect year for times near year boundaries.
370     */
371     static jsint
372     EquivalentYearForDST(jsint year)
373     {
374     jsint day;
375     JSBool isLeapYear;
376    
377     day = (jsint) DayFromYear(year) + 4;
378     day = day % 7;
379     if (day < 0)
380     day += 7;
381    
382     isLeapYear = (DaysInYear(year) == 366);
383    
384     return yearStartingWith[isLeapYear][day];
385     }
386    
387     /* LocalTZA gets set by js_InitDateClass() */
388     static jsdouble LocalTZA;
389    
390     static jsdouble
391     DaylightSavingTA(jsdouble t)
392     {
393     volatile int64 PR_t;
394     int64 ms2us;
395     int64 offset;
396     jsdouble result;
397    
398     /* abort if NaN */
399     if (JSDOUBLE_IS_NaN(t))
400     return t;
401    
402     /*
403     * If earlier than 1970 or after 2038, potentially beyond the ken of
404     * many OSes, map it to an equivalent year before asking.
405     */
406     if (t < 0.0 || t > 2145916800000.0) {
407     jsint year;
408     jsdouble day;
409    
410     year = EquivalentYearForDST(YearFromTime(t));
411     day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
412     t = MakeDate(day, TimeWithinDay(t));
413     }
414    
415     /* put our t in an LL, and map it to usec for prtime */
416     JSLL_D2L(PR_t, t);
417     JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
418     JSLL_MUL(PR_t, PR_t, ms2us);
419    
420     offset = PRMJ_DSTOffset(PR_t);
421    
422     JSLL_DIV(offset, offset, ms2us);
423     JSLL_L2D(result, offset);
424     return result;
425     }
426    
427 siliconforks 460 static jsdouble
428     AdjustTime(jsdouble date)
429     {
430     jsdouble t = DaylightSavingTA(date) + LocalTZA;
431     t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
432     return t;
433     }
434 siliconforks 332
435     #define LocalTime(t) ((t) + AdjustTime(t))
436    
437     static jsdouble
438     UTC(jsdouble t)
439     {
440     return t - AdjustTime(t - LocalTZA);
441     }
442    
443     static intN
444     HourFromTime(jsdouble t)
445     {
446     intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
447     if (result < 0)
448     result += (intN)HoursPerDay;
449     return result;
450     }
451    
452     static intN
453     MinFromTime(jsdouble t)
454     {
455     intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
456     if (result < 0)
457     result += (intN)MinutesPerHour;
458     return result;
459     }
460    
461     static intN
462     SecFromTime(jsdouble t)
463     {
464     intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
465     if (result < 0)
466     result += (intN)SecondsPerMinute;
467     return result;
468     }
469    
470     static intN
471     msFromTime(jsdouble t)
472     {
473     intN result = (intN) fmod(t, msPerSecond);
474     if (result < 0)
475     result += (intN)msPerSecond;
476     return result;
477     }
478    
479     #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
480     && !((d < 0 ? -d : d) > HalfTimeDomain)) \
481     ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
482    
483     /**
484     * end of ECMA 'support' functions
485     */
486    
487     /*
488     * Other Support routines and definitions
489     */
490    
491     /*
492     * We use the first reseved slot to store UTC time, and the second for caching
493     * the local time. The initial value of the cache entry is NaN.
494     */
495     const uint32 JSSLOT_UTC_TIME = JSSLOT_PRIVATE;
496     const uint32 JSSLOT_LOCAL_TIME = JSSLOT_PRIVATE + 1;
497    
498     const uint32 DATE_RESERVED_SLOTS = 2;
499    
500     JSClass js_DateClass = {
501     js_Date_str,
502     JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS) |
503     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
504     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
505 siliconforks 507 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
506 siliconforks 332 JSCLASS_NO_OPTIONAL_MEMBERS
507     };
508    
509     /* for use by date_parse */
510    
511     static const char* wtb[] = {
512     "am", "pm",
513     "monday", "tuesday", "wednesday", "thursday", "friday",
514     "saturday", "sunday",
515     "january", "february", "march", "april", "may", "june",
516     "july", "august", "september", "october", "november", "december",
517     "gmt", "ut", "utc",
518     "est", "edt",
519     "cst", "cdt",
520     "mst", "mdt",
521     "pst", "pdt"
522     /* time zone table needs to be expanded */
523     };
524    
525     static int ttb[] = {
526     -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
527     2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
528     10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
529     10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
530     10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
531     10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
532     10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
533     };
534    
535     /* helper for date_parse */
536     static JSBool
537     date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
538     int count, int ignoreCase)
539     {
540     JSBool result = JS_FALSE;
541     /* return true if matches, otherwise, false */
542    
543     while (count > 0 && s1[s1off] && s2[s2off]) {
544     if (ignoreCase) {
545     if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
546     break;
547     }
548     } else {
549     if ((jschar)s1[s1off] != s2[s2off]) {
550     break;
551     }
552     }
553     s1off++;
554     s2off++;
555     count--;
556     }
557    
558     if (count == 0) {
559     result = JS_TRUE;
560     }
561    
562     return result;
563     }
564    
565     /* find UTC time from given date... no 1900 correction! */
566     static jsdouble
567     date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
568     jsdouble min, jsdouble sec, jsdouble msec)
569     {
570     jsdouble day;
571     jsdouble msec_time;
572     jsdouble result;
573    
574     day = MakeDay(year, mon, mday);
575     msec_time = MakeTime(hour, min, sec, msec);
576     result = MakeDate(day, msec_time);
577     return result;
578     }
579    
580     /* compute the time in msec (unclipped) from the given args */
581     #define MAXARGS 7
582    
583     static JSBool
584     date_msecFromArgs(JSContext *cx, uintN argc, jsval *argv, jsdouble *rval)
585     {
586     uintN loop;
587     jsdouble array[MAXARGS];
588     jsdouble d;
589     jsdouble msec_time;
590    
591     for (loop = 0; loop < MAXARGS; loop++) {
592     if (loop < argc) {
593     d = js_ValueToNumber(cx, &argv[loop]);
594     if (JSVAL_IS_NULL(argv[loop]))
595     return JS_FALSE;
596     /* return NaN if any arg is not finite */
597     if (!JSDOUBLE_IS_FINITE(d)) {
598     *rval = *cx->runtime->jsNaN;
599     return JS_TRUE;
600     }
601     array[loop] = js_DoubleToInteger(d);
602     } else {
603     if (loop == 2) {
604     array[loop] = 1; /* Default the date argument to 1. */
605     } else {
606     array[loop] = 0;
607     }
608     }
609     }
610    
611     /* adjust 2-digit years into the 20th century */
612     if (array[0] >= 0 && array[0] <= 99)
613     array[0] += 1900;
614    
615     msec_time = date_msecFromDate(array[0], array[1], array[2],
616     array[3], array[4], array[5], array[6]);
617     *rval = msec_time;
618     return JS_TRUE;
619     }
620    
621     /*
622     * See ECMA 15.9.4.[3-10];
623     */
624     static JSBool
625     date_UTC(JSContext *cx, uintN argc, jsval *vp)
626     {
627     jsdouble msec_time;
628    
629     if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
630     return JS_FALSE;
631    
632     msec_time = TIMECLIP(msec_time);
633    
634     return js_NewNumberInRootedValue(cx, msec_time, vp);
635     }
636    
637 siliconforks 507 /*
638     * Read and convert decimal digits from s[*i] into *result
639     * while *i < limit.
640     *
641     * Succeed if any digits are converted. Advance *i only
642     * as digits are consumed.
643     */
644 siliconforks 332 static JSBool
645 siliconforks 507 digits(size_t *result, const jschar *s, size_t *i, size_t limit)
646     {
647     size_t init = *i;
648     *result = 0;
649     while (*i < limit &&
650     ('0' <= s[*i] && s[*i] <= '9')) {
651     *result *= 10;
652     *result += (s[*i] - '0');
653     ++(*i);
654     }
655     return (*i != init);
656     }
657    
658     /*
659     * Read and convert decimal digits to the right of a decimal point,
660     * representing a fractional integer, from s[*i] into *result
661     * while *i < limit.
662     *
663     * Succeed if any digits are converted. Advance *i only
664     * as digits are consumed.
665     */
666     static JSBool
667     fractional(jsdouble *result, const jschar *s, size_t *i, size_t limit)
668     {
669     jsdouble factor = 0.1;
670     size_t init = *i;
671     *result = 0.0;
672     while (*i < limit &&
673     ('0' <= s[*i] && s[*i] <= '9')) {
674     *result += (s[*i] - '0') * factor;
675     factor *= 0.1;
676     ++(*i);
677     }
678     return (*i != init);
679     }
680    
681     /*
682     * Read and convert exactly n decimal digits from s[*i]
683     * to s[min(*i+n,limit)] into *result.
684     *
685     * Succeed if exactly n digits are converted. Advance *i only
686     * on success.
687     */
688     static JSBool
689     ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
690     {
691     size_t init = *i;
692    
693     if (digits(result, s, i, JS_MIN(limit, init+n)))
694     return ((*i - init) == n);
695    
696     *i = init;
697     return JS_FALSE;
698     }
699    
700     /*
701     * Parse a string in one of the date-time formats given by the W3C
702     * "NOTE-datetime" specification. These formats make up a restricted
703     * profile of the ISO 8601 format. Quoted here:
704     *
705     * The formats are as follows. Exactly the components shown here
706     * must be present, with exactly this punctuation. Note that the "T"
707     * appears literally in the string, to indicate the beginning of the
708     * time element, as specified in ISO 8601.
709     *
710     * Any combination of the date formats with the time formats is
711     * allowed, and also either the date or the time can be missing.
712     *
713     * The specification is silent on the meaning when fields are
714     * ommitted so the interpretations are a guess, but hopefully a
715     * reasonable one. We default the month to January, the day to the
716     * 1st, and hours minutes and seconds all to 0. If the date is
717     * missing entirely then we assume 1970-01-01 so that the time can
718     * be aded to a date later. If the time is missing then we assume
719     * 00:00 UTC. If the time is present but the time zone field is
720     * missing then we use local time.
721     *
722     * Date part:
723     *
724     * Year:
725     * YYYY (eg 1997)
726     *
727     * Year and month:
728     * YYYY-MM (eg 1997-07)
729     *
730     * Complete date:
731     * YYYY-MM-DD (eg 1997-07-16)
732     *
733     * Time part:
734     *
735     * Hours and minutes:
736     * Thh:mmTZD (eg T19:20+01:00)
737     *
738     * Hours, minutes and seconds:
739     * Thh:mm:ssTZD (eg T19:20:30+01:00)
740     *
741     * Hours, minutes, seconds and a decimal fraction of a second:
742     * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
743     *
744     * where:
745     *
746     * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
747     * MM = two-digit month (01=January, etc.)
748     * DD = two-digit day of month (01 through 31)
749     * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
750     * mm = two digits of minute (00 through 59)
751     * ss = two digits of second (00 through 59)
752     * s = one or more digits representing a decimal fraction of a second
753     * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
754     */
755    
756     static JSBool
757     date_parseISOString(JSString *str, jsdouble *result)
758     {
759     jsdouble msec;
760    
761     const jschar *s;
762     size_t limit;
763     size_t i = 0;
764     int tzMul = 1;
765     int dateMul = 1;
766     size_t year = 1970;
767     size_t month = 1;
768     size_t day = 1;
769     size_t hour = 0;
770     size_t min = 0;
771     size_t sec = 0;
772     jsdouble frac = 0;
773     bool isLocalTime = JS_FALSE;
774     size_t tzHour = 0;
775     size_t tzMin = 0;
776    
777     #define PEEK(ch) (i < limit && s[i] == ch)
778    
779     #define NEED(ch) \
780     JS_BEGIN_MACRO \
781     if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
782     JS_END_MACRO
783    
784     #define DONE_DATE_UNLESS(ch) \
785     JS_BEGIN_MACRO \
786     if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
787     JS_END_MACRO
788    
789     #define DONE_UNLESS(ch) \
790     JS_BEGIN_MACRO \
791     if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
792     JS_END_MACRO
793    
794     #define NEED_NDIGITS(n, field) \
795     JS_BEGIN_MACRO \
796     if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
797     JS_END_MACRO
798    
799     str->getCharsAndLength(s, limit);
800    
801     if (PEEK('+') || PEEK('-')) {
802     if (PEEK('-'))
803     dateMul = -1;
804     ++i;
805     NEED_NDIGITS(6, year);
806     } else if (!PEEK('T')) {
807     NEED_NDIGITS(4, year);
808     }
809     DONE_DATE_UNLESS('-');
810     NEED_NDIGITS(2, month);
811     DONE_DATE_UNLESS('-');
812     NEED_NDIGITS(2, day);
813    
814     done_date:
815     DONE_UNLESS('T');
816     NEED_NDIGITS(2, hour);
817     NEED(':');
818     NEED_NDIGITS(2, min);
819    
820     if (PEEK(':')) {
821     ++i;
822     NEED_NDIGITS(2, sec);
823     if (PEEK('.')) {
824     ++i;
825     if (!fractional(&frac, s, &i, limit))
826     goto syntax;
827     }
828     }
829    
830     if (PEEK('Z')) {
831     ++i;
832     } else if (PEEK('+') || PEEK('-')) {
833     if (PEEK('-'))
834     tzMul = -1;
835     ++i;
836     NEED_NDIGITS(2, tzHour);
837     NEED(':');
838     NEED_NDIGITS(2, tzMin);
839     } else {
840     isLocalTime = JS_TRUE;
841     }
842    
843     done:
844     if (year > 275943 // ceil(1e8/365) + 1970
845     || (month == 0 || month > 12)
846     || (day == 0 || day > size_t(DaysInMonth(year,month)))
847     || hour > 24
848     || ((hour == 24) && (min > 0 || sec > 0))
849     || min > 59
850     || sec > 59
851     || tzHour > 23
852     || tzMin > 59)
853     goto syntax;
854    
855     if (i != limit)
856     goto syntax;
857    
858     month -= 1; /* convert month to 0-based */
859    
860     msec = date_msecFromDate(dateMul * (jsdouble)year, month, day,
861     hour, min, sec,
862     frac * 1000.0);;
863    
864     if (isLocalTime) {
865     msec = UTC(msec);
866     } else {
867     msec -= ((tzMul) * ((tzHour * msPerHour)
868     + (tzMin * msPerMinute)));
869     }
870    
871     if (msec < -8.64e15 || msec > 8.64e15)
872     goto syntax;
873    
874     *result = msec;
875    
876     return JS_TRUE;
877    
878     syntax:
879     /* syntax error */
880     *result = 0;
881     return JS_FALSE;
882    
883     #undef PEEK
884     #undef NEED
885     #undef DONE_UNLESS
886     #undef NEED_NDIGITS
887     }
888    
889     static JSBool
890 siliconforks 332 date_parseString(JSString *str, jsdouble *result)
891     {
892     jsdouble msec;
893    
894     const jschar *s;
895     size_t limit;
896     size_t i = 0;
897     int year = -1;
898     int mon = -1;
899     int mday = -1;
900     int hour = -1;
901     int min = -1;
902     int sec = -1;
903     int c = -1;
904     int n = -1;
905     int tzoffset = -1;
906     int prevc = 0;
907     JSBool seenplusminus = JS_FALSE;
908     int temp;
909     JSBool seenmonthname = JS_FALSE;
910    
911 siliconforks 507 if (date_parseISOString(str, result))
912     return JS_TRUE;
913    
914     str->getCharsAndLength(s, limit);
915 siliconforks 332 if (limit == 0)
916     goto syntax;
917     while (i < limit) {
918     c = s[i];
919     i++;
920     if (c <= ' ' || c == ',' || c == '-') {
921     if (c == '-' && '0' <= s[i] && s[i] <= '9') {
922     prevc = c;
923     }
924     continue;
925     }
926     if (c == '(') { /* comments) */
927     int depth = 1;
928     while (i < limit) {
929     c = s[i];
930     i++;
931     if (c == '(') depth++;
932     else if (c == ')')
933     if (--depth <= 0)
934     break;
935     }
936     continue;
937     }
938     if ('0' <= c && c <= '9') {
939     n = c - '0';
940     while (i < limit && '0' <= (c = s[i]) && c <= '9') {
941     n = n * 10 + c - '0';
942     i++;
943     }
944    
945     /* allow TZA before the year, so
946     * 'Wed Nov 05 21:49:11 GMT-0800 1997'
947     * works */
948    
949     /* uses of seenplusminus allow : in TZA, so Java
950     * no-timezone style of GMT+4:30 works
951     */
952    
953     if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
954     /* make ':' case below change tzoffset */
955     seenplusminus = JS_TRUE;
956    
957     /* offset */
958     if (n < 24)
959     n = n * 60; /* EG. "GMT-3" */
960     else
961     n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
962     if (prevc == '+') /* plus means east of GMT */
963     n = -n;
964     if (tzoffset != 0 && tzoffset != -1)
965     goto syntax;
966     tzoffset = n;
967     } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
968     if (c <= ' ' || c == ',' || c == '/' || i >= limit)
969     year = n;
970     else
971     goto syntax;
972     } else if (c == ':') {
973     if (hour < 0)
974     hour = /*byte*/ n;
975     else if (min < 0)
976     min = /*byte*/ n;
977     else
978     goto syntax;
979     } else if (c == '/') {
980     /* until it is determined that mon is the actual
981     month, keep it as 1-based rather than 0-based */
982     if (mon < 0)
983     mon = /*byte*/ n;
984     else if (mday < 0)
985     mday = /*byte*/ n;
986     else
987     goto syntax;
988     } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
989     goto syntax;
990     } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
991     if (tzoffset < 0)
992     tzoffset -= n;
993     else
994     tzoffset += n;
995     } else if (hour >= 0 && min < 0) {
996     min = /*byte*/ n;
997     } else if (prevc == ':' && min >= 0 && sec < 0) {
998     sec = /*byte*/ n;
999     } else if (mon < 0) {
1000     mon = /*byte*/n;
1001     } else if (mon >= 0 && mday < 0) {
1002     mday = /*byte*/ n;
1003     } else if (mon >= 0 && mday >= 0 && year < 0) {
1004     year = n;
1005     } else {
1006     goto syntax;
1007     }
1008     prevc = 0;
1009     } else if (c == '/' || c == ':' || c == '+' || c == '-') {
1010     prevc = c;
1011     } else {
1012     size_t st = i - 1;
1013     int k;
1014     while (i < limit) {
1015     c = s[i];
1016     if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1017     break;
1018     i++;
1019     }
1020     if (i <= st + 1)
1021     goto syntax;
1022     for (k = JS_ARRAY_LENGTH(wtb); --k >= 0;)
1023     if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
1024     int action = ttb[k];
1025     if (action != 0) {
1026     if (action < 0) {
1027     /*
1028     * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1029     * 12:30, instead of blindly adding 12 if PM.
1030     */
1031     JS_ASSERT(action == -1 || action == -2);
1032     if (hour > 12 || hour < 0) {
1033     goto syntax;
1034     } else {
1035     if (action == -1 && hour == 12) { /* am */
1036     hour = 0;
1037     } else if (action == -2 && hour != 12) { /* pm */
1038     hour += 12;
1039     }
1040     }
1041     } else if (action <= 13) { /* month! */
1042     /* Adjust mon to be 1-based until the final values
1043     for mon, mday and year are adjusted below */
1044     if (seenmonthname) {
1045     goto syntax;
1046     }
1047     seenmonthname = JS_TRUE;
1048     temp = /*byte*/ (action - 2) + 1;
1049    
1050     if (mon < 0) {
1051     mon = temp;
1052     } else if (mday < 0) {
1053     mday = mon;
1054     mon = temp;
1055     } else if (year < 0) {
1056     year = mon;
1057     mon = temp;
1058     } else {
1059     goto syntax;
1060     }
1061     } else {
1062     tzoffset = action - 10000;
1063     }
1064     }
1065     break;
1066     }
1067     if (k < 0)
1068     goto syntax;
1069     prevc = 0;
1070     }
1071     }
1072     if (year < 0 || mon < 0 || mday < 0)
1073     goto syntax;
1074     /*
1075     Case 1. The input string contains an English month name.
1076     The form of the string can be month f l, or f month l, or
1077     f l month which each evaluate to the same date.
1078     If f and l are both greater than or equal to 70, or
1079     both less than 70, the date is invalid.
1080     The year is taken to be the greater of the values f, l.
1081     If the year is greater than or equal to 70 and less than 100,
1082     it is considered to be the number of years after 1900.
1083     Case 2. The input string is of the form "f/m/l" where f, m and l are
1084     integers, e.g. 7/16/45.
1085     Adjust the mon, mday and year values to achieve 100% MSIE
1086     compatibility.
1087     a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1088     i. If year < 100, it is the number of years after 1900
1089     ii. If year >= 100, it is the number of years after 0.
1090     b. If 70 <= f < 100
1091     i. If m < 70, f/m/l is interpreted as
1092     year/month/day where year is the number of years after
1093     1900.
1094     ii. If m >= 70, the date is invalid.
1095     c. If f >= 100
1096     i. If m < 70, f/m/l is interpreted as
1097     year/month/day where year is the number of years after 0.
1098     ii. If m >= 70, the date is invalid.
1099     */
1100     if (seenmonthname) {
1101     if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
1102     goto syntax;
1103     }
1104     if (mday > year) {
1105     temp = year;
1106     year = mday;
1107     mday = temp;
1108     }
1109     if (year >= 70 && year < 100) {
1110     year += 1900;
1111     }
1112     } else if (mon < 70) { /* (a) month/day/year */
1113     if (year < 100) {
1114     year += 1900;
1115     }
1116     } else if (mon < 100) { /* (b) year/month/day */
1117     if (mday < 70) {
1118     temp = year;
1119     year = mon + 1900;
1120     mon = mday;
1121     mday = temp;
1122     } else {
1123     goto syntax;
1124     }
1125     } else { /* (c) year/month/day */
1126     if (mday < 70) {
1127     temp = year;
1128     year = mon;
1129     mon = mday;
1130     mday = temp;
1131     } else {
1132     goto syntax;
1133     }
1134     }
1135     mon -= 1; /* convert month to 0-based */
1136     if (sec < 0)
1137     sec = 0;
1138     if (min < 0)
1139     min = 0;
1140     if (hour < 0)
1141     hour = 0;
1142 siliconforks 507
1143     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1144    
1145 siliconforks 332 if (tzoffset == -1) { /* no time zone specified, have to use local */
1146 siliconforks 507 msec = UTC(msec);
1147     } else {
1148     msec += tzoffset * msPerMinute;
1149 siliconforks 332 }
1150    
1151     *result = msec;
1152     return JS_TRUE;
1153    
1154     syntax:
1155     /* syntax error */
1156     *result = 0;
1157     return JS_FALSE;
1158     }
1159    
1160     static JSBool
1161     date_parse(JSContext *cx, uintN argc, jsval *vp)
1162     {
1163     JSString *str;
1164     jsdouble result;
1165    
1166     if (argc == 0) {
1167     *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1168     return JS_TRUE;
1169     }
1170     str = js_ValueToString(cx, vp[2]);
1171     if (!str)
1172     return JS_FALSE;
1173     vp[2] = STRING_TO_JSVAL(str);
1174     if (!date_parseString(str, &result)) {
1175     *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1176     return JS_TRUE;
1177     }
1178    
1179     result = TIMECLIP(result);
1180     return js_NewNumberInRootedValue(cx, result, vp);
1181     }
1182    
1183 siliconforks 460 static inline jsdouble
1184     NowAsMillis()
1185     {
1186     return (jsdouble) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1187     }
1188    
1189 siliconforks 399 static JSBool
1190     date_now(JSContext *cx, uintN argc, jsval *vp)
1191 siliconforks 332 {
1192 siliconforks 460 return js_NewDoubleInRootedValue(cx, NowAsMillis(), vp);
1193 siliconforks 332 }
1194    
1195 siliconforks 399 #ifdef JS_TRACER
1196     static jsdouble FASTCALL
1197     date_now_tn(JSContext*)
1198     {
1199 siliconforks 460 return NowAsMillis();
1200 siliconforks 399 }
1201     #endif
1202    
1203 siliconforks 332 /*
1204     * Get UTC time from the date object. Returns false if the object is not
1205     * Date type.
1206     */
1207     static JSBool
1208     GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
1209     {
1210     if (!JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1211     return JS_FALSE;
1212     *dp = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
1213     return JS_TRUE;
1214     }
1215    
1216 siliconforks 507 static void
1217     SetDateToNaN(JSContext *cx, JSObject *obj, jsval *vp = NULL)
1218 siliconforks 332 {
1219 siliconforks 507 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_DateClass);
1220 siliconforks 332
1221 siliconforks 507 jsval nan = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1222     obj->fslots[JSSLOT_LOCAL_TIME] = nan;
1223     obj->fslots[JSSLOT_UTC_TIME] = nan;
1224     if (vp)
1225     *vp = nan;
1226 siliconforks 332 }
1227    
1228     /*
1229 siliconforks 507 * Set UTC time to a given time and invalidate cached local time.
1230 siliconforks 332 */
1231     static JSBool
1232 siliconforks 507 SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, jsval *vp = NULL)
1233 siliconforks 332 {
1234 siliconforks 507 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_DateClass);
1235    
1236     obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1237     if (!js_NewDoubleInRootedValue(cx, t, &obj->fslots[JSSLOT_UTC_TIME]))
1238     return false;
1239     if (vp)
1240     *vp = obj->fslots[JSSLOT_UTC_TIME];
1241     return true;
1242 siliconforks 332 }
1243    
1244     /*
1245     * Get the local time, cache it if necessary. If UTC time is not finite
1246     * (e.g., NaN), the local time slot is set to the UTC time without conversion.
1247     */
1248     static JSBool
1249     GetAndCacheLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
1250     {
1251     jsval v;
1252     jsdouble result;
1253     jsdouble *cached;
1254    
1255     if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1256     return JS_FALSE;
1257     v = obj->fslots[JSSLOT_LOCAL_TIME];
1258    
1259     result = *JSVAL_TO_DOUBLE(v);
1260    
1261     if (JSDOUBLE_IS_NaN(result)) {
1262     if (!GetUTCTime(cx, obj, vp, &result))
1263     return JS_FALSE;
1264    
1265     /* if result is NaN, it couldn't be finite. */
1266     if (JSDOUBLE_IS_FINITE(result))
1267     result = LocalTime(result);
1268    
1269     cached = js_NewWeaklyRootedDouble(cx, result);
1270     if (!cached)
1271     return JS_FALSE;
1272    
1273     obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cached);
1274     }
1275    
1276     *dp = result;
1277     return JS_TRUE;
1278     }
1279    
1280     /*
1281     * See ECMA 15.9.5.4 thru 15.9.5.23
1282     */
1283     static JSBool
1284     date_getTime(JSContext *cx, uintN argc, jsval *vp)
1285     {
1286     jsdouble result;
1287    
1288     return GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result) &&
1289     js_NewNumberInRootedValue(cx, result, vp);
1290     }
1291    
1292     static JSBool
1293     GetYear(JSContext *cx, JSBool fullyear, jsval *vp)
1294     {
1295     jsdouble result;
1296    
1297     if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1298     return JS_FALSE;
1299    
1300     if (JSDOUBLE_IS_FINITE(result)) {
1301     result = YearFromTime(result);
1302    
1303     /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1304     if (!fullyear)
1305     result -= 1900;
1306     }
1307    
1308     return js_NewNumberInRootedValue(cx, result, vp);
1309     }
1310    
1311     static JSBool
1312     date_getYear(JSContext *cx, uintN argc, jsval *vp)
1313     {
1314     return GetYear(cx, JS_FALSE, vp);
1315     }
1316    
1317     static JSBool
1318     date_getFullYear(JSContext *cx, uintN argc, jsval *vp)
1319     {
1320     return GetYear(cx, JS_TRUE, vp);
1321     }
1322    
1323     static JSBool
1324     date_getUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1325     {
1326     jsdouble result;
1327    
1328     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1329     return JS_FALSE;
1330    
1331     if (JSDOUBLE_IS_FINITE(result))
1332     result = YearFromTime(result);
1333    
1334     return js_NewNumberInRootedValue(cx, result, vp);
1335     }
1336    
1337     static JSBool
1338     date_getMonth(JSContext *cx, uintN argc, jsval *vp)
1339     {
1340     jsdouble result;
1341    
1342     if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1343     return JS_FALSE;
1344    
1345     if (JSDOUBLE_IS_FINITE(result))
1346     result = MonthFromTime(result);
1347    
1348     return js_NewNumberInRootedValue(cx, result, vp);
1349     }
1350    
1351     static JSBool
1352     date_getUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1353     {
1354     jsdouble result;
1355    
1356     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1357     return JS_FALSE;
1358    
1359     if (JSDOUBLE_IS_FINITE(result))
1360     result = MonthFromTime(result);
1361    
1362     return js_NewNumberInRootedValue(cx, result, vp);
1363     }
1364    
1365     static JSBool
1366     date_getDate(JSContext *cx, uintN argc, jsval *vp)
1367     {
1368     jsdouble result;
1369    
1370     if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1371     return JS_FALSE;
1372    
1373     if (JSDOUBLE_IS_FINITE(result))
1374     result = DateFromTime(result);
1375    
1376     return js_NewNumberInRootedValue(cx, result, vp);
1377     }
1378    
1379     static JSBool
1380     date_getUTCDate(JSContext *cx, uintN argc, jsval *vp)
1381     {
1382     jsdouble result;
1383    
1384     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1385     return JS_FALSE;
1386    
1387     if (JSDOUBLE_IS_FINITE(result))
1388     result = DateFromTime(result);
1389    
1390     return js_NewNumberInRootedValue(cx, result, vp);
1391     }
1392    
1393     static JSBool
1394     date_getDay(JSContext *cx, uintN argc, jsval *vp)
1395     {
1396     jsdouble result;
1397    
1398     if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1399     return JS_FALSE;
1400    
1401     if (JSDOUBLE_IS_FINITE(result))
1402     result = WeekDay(result);
1403    
1404     return js_NewNumberInRootedValue(cx, result, vp);
1405     }
1406    
1407     static JSBool
1408     date_getUTCDay(JSContext *cx, uintN argc, jsval *vp)
1409     {
1410     jsdouble result;
1411    
1412     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1413     return JS_FALSE;
1414    
1415     if (JSDOUBLE_IS_FINITE(result))
1416     result = WeekDay(result);
1417    
1418     return js_NewNumberInRootedValue(cx, result, vp);
1419     }
1420    
1421     static JSBool
1422     date_getHours(JSContext *cx, uintN argc, jsval *vp)
1423     {
1424     jsdouble result;
1425    
1426     if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1427     return JS_FALSE;
1428    
1429     if (JSDOUBLE_IS_FINITE(result))
1430     result = HourFromTime(result);
1431    
1432     return js_NewNumberInRootedValue(cx, result, vp);
1433     }
1434    
1435     static JSBool
1436     date_getUTCHours(JSContext *cx, uintN argc, jsval *vp)
1437     {
1438     jsdouble result;
1439    
1440     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1441     return JS_FALSE;
1442    
1443     if (JSDOUBLE_IS_FINITE(result))
1444     result = HourFromTime(result);
1445    
1446     return js_NewNumberInRootedValue(cx, result, vp);
1447     }
1448    
1449     static JSBool
1450     date_getMinutes(JSContext *cx, uintN argc, jsval *vp)
1451     {
1452     jsdouble result;
1453    
1454     if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1455     return JS_FALSE;
1456    
1457     if (JSDOUBLE_IS_FINITE(result))
1458     result = MinFromTime(result);
1459    
1460     return js_NewNumberInRootedValue(cx, result, vp);
1461     }
1462    
1463     static JSBool
1464     date_getUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1465     {
1466     jsdouble result;
1467    
1468     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1469     return JS_FALSE;
1470    
1471     if (JSDOUBLE_IS_FINITE(result))
1472     result = MinFromTime(result);
1473    
1474     return js_NewNumberInRootedValue(cx, result, vp);
1475     }
1476    
1477     /* Date.getSeconds is mapped to getUTCSeconds */
1478    
1479     static JSBool
1480     date_getUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1481     {
1482     jsdouble result;
1483    
1484     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1485     return JS_FALSE;
1486    
1487     if (JSDOUBLE_IS_FINITE(result))
1488     result = SecFromTime(result);
1489    
1490     return js_NewNumberInRootedValue(cx, result, vp);
1491     }
1492    
1493     /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1494    
1495     static JSBool
1496     date_getUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1497     {
1498     jsdouble result;
1499    
1500     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1501     return JS_FALSE;
1502    
1503     if (JSDOUBLE_IS_FINITE(result))
1504     result = msFromTime(result);
1505    
1506     return js_NewNumberInRootedValue(cx, result, vp);
1507     }
1508    
1509     static JSBool
1510     date_getTimezoneOffset(JSContext *cx, uintN argc, jsval *vp)
1511     {
1512     JSObject *obj;
1513     jsdouble utctime, localtime, result;
1514    
1515     obj = JS_THIS_OBJECT(cx, vp);
1516     if (!GetUTCTime(cx, obj, vp, &utctime))
1517     return JS_FALSE;
1518     if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime))
1519     return JS_FALSE;
1520    
1521     /*
1522     * Return the time zone offset in minutes for the current locale that is
1523     * appropriate for this time. This value would be a constant except for
1524     * daylight savings time.
1525     */
1526     result = (utctime - localtime) / msPerMinute;
1527     return js_NewNumberInRootedValue(cx, result, vp);
1528     }
1529    
1530     static JSBool
1531 siliconforks 507 date_setTime(JSContext *cx, uintN argc, jsval *vp)
1532 siliconforks 332 {
1533 siliconforks 507 JSObject *obj = JS_THIS_OBJECT(cx, vp);
1534     if (!JS_InstanceOf(cx, obj, &js_DateClass, vp + 2))
1535     return false;
1536 siliconforks 332
1537 siliconforks 507 if (argc == 0) {
1538     SetDateToNaN(cx, obj, vp);
1539     return true;
1540     }
1541 siliconforks 332
1542 siliconforks 507 jsdouble result = js_ValueToNumber(cx, &vp[2]);
1543 siliconforks 332 if (JSVAL_IS_NULL(vp[2]))
1544 siliconforks 507 return false;
1545 siliconforks 332
1546 siliconforks 507 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1547 siliconforks 332 }
1548    
1549     static JSBool
1550     date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1551     {
1552     JSObject *obj;
1553     jsval *argv;
1554     uintN i;
1555     jsdouble args[4], *argp, *stop;
1556     jsdouble hour, min, sec, msec;
1557     jsdouble lorutime; /* Local or UTC version of *date */
1558    
1559     jsdouble msec_time;
1560     jsdouble result;
1561    
1562     obj = JS_THIS_OBJECT(cx, vp);
1563     if (!GetUTCTime(cx, obj, vp, &result))
1564 siliconforks 507 return false;
1565 siliconforks 332
1566     /* just return NaN if the date is already NaN */
1567     if (!JSDOUBLE_IS_FINITE(result))
1568     return js_NewNumberInRootedValue(cx, result, vp);
1569    
1570     /*
1571     * Satisfy the ECMA rule that if a function is called with
1572     * fewer arguments than the specified formal arguments, the
1573     * remaining arguments are set to undefined. Seems like all
1574     * the Date.setWhatever functions in ECMA are only varargs
1575     * beyond the first argument; this should be set to undefined
1576     * if it's not given. This means that "d = new Date();
1577     * d.setMilliseconds()" returns NaN. Blech.
1578     */
1579 siliconforks 507 if (argc == 0) {
1580     SetDateToNaN(cx, obj, vp);
1581     return true;
1582     }
1583 siliconforks 332 if (argc > maxargs)
1584     argc = maxargs; /* clamp argc */
1585     JS_ASSERT(argc <= 4);
1586    
1587     argv = vp + 2;
1588     for (i = 0; i < argc; i++) {
1589     args[i] = js_ValueToNumber(cx, &argv[i]);
1590     if (JSVAL_IS_NULL(argv[i]))
1591 siliconforks 507 return false;
1592     if (!JSDOUBLE_IS_FINITE(args[i])) {
1593     SetDateToNaN(cx, obj, vp);
1594     return true;
1595     }
1596 siliconforks 332 args[i] = js_DoubleToInteger(args[i]);
1597     }
1598    
1599     if (local)
1600     lorutime = LocalTime(result);
1601     else
1602     lorutime = result;
1603    
1604     argp = args;
1605     stop = argp + argc;
1606     if (maxargs >= 4 && argp < stop)
1607     hour = *argp++;
1608     else
1609     hour = HourFromTime(lorutime);
1610    
1611     if (maxargs >= 3 && argp < stop)
1612     min = *argp++;
1613     else
1614     min = MinFromTime(lorutime);
1615    
1616     if (maxargs >= 2 && argp < stop)
1617     sec = *argp++;
1618     else
1619     sec = SecFromTime(lorutime);
1620    
1621     if (maxargs >= 1 && argp < stop)
1622     msec = *argp;
1623     else
1624     msec = msFromTime(lorutime);
1625    
1626     msec_time = MakeTime(hour, min, sec, msec);
1627     result = MakeDate(Day(lorutime), msec_time);
1628    
1629     /* fprintf(stderr, "%f\n", result); */
1630    
1631     if (local)
1632     result = UTC(result);
1633    
1634     /* fprintf(stderr, "%f\n", result); */
1635    
1636 siliconforks 507 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1637 siliconforks 332 }
1638    
1639     static JSBool
1640     date_setMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1641     {
1642     return date_makeTime(cx, 1, JS_TRUE, argc, vp);
1643     }
1644    
1645     static JSBool
1646     date_setUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1647     {
1648     return date_makeTime(cx, 1, JS_FALSE, argc, vp);
1649     }
1650    
1651     static JSBool
1652     date_setSeconds(JSContext *cx, uintN argc, jsval *vp)
1653     {
1654     return date_makeTime(cx, 2, JS_TRUE, argc, vp);
1655     }
1656    
1657     static JSBool
1658     date_setUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1659     {
1660     return date_makeTime(cx, 2, JS_FALSE, argc, vp);
1661     }
1662    
1663     static JSBool
1664     date_setMinutes(JSContext *cx, uintN argc, jsval *vp)
1665     {
1666     return date_makeTime(cx, 3, JS_TRUE, argc, vp);
1667     }
1668    
1669     static JSBool
1670     date_setUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1671     {
1672     return date_makeTime(cx, 3, JS_FALSE, argc, vp);
1673     }
1674    
1675     static JSBool
1676     date_setHours(JSContext *cx, uintN argc, jsval *vp)
1677     {
1678     return date_makeTime(cx, 4, JS_TRUE, argc, vp);
1679     }
1680    
1681     static JSBool
1682     date_setUTCHours(JSContext *cx, uintN argc, jsval *vp)
1683     {
1684     return date_makeTime(cx, 4, JS_FALSE, argc, vp);
1685     }
1686    
1687     static JSBool
1688     date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1689     {
1690     JSObject *obj;
1691     jsval *argv;
1692     uintN i;
1693     jsdouble lorutime; /* local or UTC version of *date */
1694     jsdouble args[3], *argp, *stop;
1695     jsdouble year, month, day;
1696     jsdouble result;
1697    
1698     obj = JS_THIS_OBJECT(cx, vp);
1699     if (!GetUTCTime(cx, obj, vp, &result))
1700 siliconforks 507 return false;
1701 siliconforks 332
1702     /* see complaint about ECMA in date_MakeTime */
1703 siliconforks 507 if (argc == 0) {
1704     SetDateToNaN(cx, obj, vp);
1705     return true;
1706     }
1707 siliconforks 332 if (argc > maxargs)
1708     argc = maxargs; /* clamp argc */
1709     JS_ASSERT(1 <= argc && argc <= 3);
1710    
1711     argv = vp + 2;
1712     for (i = 0; i < argc; i++) {
1713     args[i] = js_ValueToNumber(cx, &argv[i]);
1714     if (JSVAL_IS_NULL(argv[i]))
1715     return JS_FALSE;
1716 siliconforks 507 if (!JSDOUBLE_IS_FINITE(args[i])) {
1717     SetDateToNaN(cx, obj, vp);
1718     return true;
1719     }
1720 siliconforks 332 args[i] = js_DoubleToInteger(args[i]);
1721     }
1722    
1723     /* return NaN if date is NaN and we're not setting the year,
1724     * If we are, use 0 as the time. */
1725     if (!(JSDOUBLE_IS_FINITE(result))) {
1726     if (maxargs < 3)
1727     return js_NewNumberInRootedValue(cx, result, vp);
1728     lorutime = +0.;
1729     } else {
1730     lorutime = local ? LocalTime(result) : result;
1731     }
1732    
1733     argp = args;
1734     stop = argp + argc;
1735     if (maxargs >= 3 && argp < stop)
1736     year = *argp++;
1737     else
1738     year = YearFromTime(lorutime);
1739    
1740     if (maxargs >= 2 && argp < stop)
1741     month = *argp++;
1742     else
1743     month = MonthFromTime(lorutime);
1744    
1745     if (maxargs >= 1 && argp < stop)
1746     day = *argp++;
1747     else
1748     day = DateFromTime(lorutime);
1749    
1750     day = MakeDay(year, month, day); /* day within year */
1751     result = MakeDate(day, TimeWithinDay(lorutime));
1752    
1753     if (local)
1754     result = UTC(result);
1755    
1756 siliconforks 507 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1757 siliconforks 332 }
1758    
1759     static JSBool
1760     date_setDate(JSContext *cx, uintN argc, jsval *vp)
1761     {
1762     return date_makeDate(cx, 1, JS_TRUE, argc, vp);
1763     }
1764    
1765     static JSBool
1766     date_setUTCDate(JSContext *cx, uintN argc, jsval *vp)
1767     {
1768     return date_makeDate(cx, 1, JS_FALSE, argc, vp);
1769     }
1770    
1771     static JSBool
1772     date_setMonth(JSContext *cx, uintN argc, jsval *vp)
1773     {
1774     return date_makeDate(cx, 2, JS_TRUE, argc, vp);
1775     }
1776    
1777     static JSBool
1778     date_setUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1779     {
1780     return date_makeDate(cx, 2, JS_FALSE, argc, vp);
1781     }
1782    
1783     static JSBool
1784     date_setFullYear(JSContext *cx, uintN argc, jsval *vp)
1785     {
1786     return date_makeDate(cx, 3, JS_TRUE, argc, vp);
1787     }
1788    
1789     static JSBool
1790     date_setUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1791     {
1792     return date_makeDate(cx, 3, JS_FALSE, argc, vp);
1793     }
1794    
1795     static JSBool
1796     date_setYear(JSContext *cx, uintN argc, jsval *vp)
1797     {
1798 siliconforks 507 JSObject *obj = JS_THIS_OBJECT(cx, vp);
1799    
1800 siliconforks 332 jsdouble result;
1801     if (!GetUTCTime(cx, obj, vp, &result))
1802 siliconforks 507 return false;
1803 siliconforks 332
1804 siliconforks 507 if (argc == 0) {
1805     /* Call this only after GetUTCTime has verified that obj is Date. */
1806     SetDateToNaN(cx, obj, vp);
1807     return true;
1808     }
1809    
1810     jsdouble year = js_ValueToNumber(cx, &vp[2]);
1811 siliconforks 332 if (JSVAL_IS_NULL(vp[2]))
1812 siliconforks 507 return false;
1813     if (!JSDOUBLE_IS_FINITE(year)) {
1814     SetDateToNaN(cx, obj, vp);
1815     return true;
1816     }
1817 siliconforks 332 year = js_DoubleToInteger(year);
1818     if (year >= 0 && year <= 99)
1819     year += 1900;
1820    
1821 siliconforks 507 jsdouble t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result) : +0.0;
1822     jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1823 siliconforks 332 result = MakeDate(day, TimeWithinDay(t));
1824     result = UTC(result);
1825    
1826 siliconforks 507 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1827 siliconforks 332 }
1828    
1829     /* constants for toString, toUTCString */
1830     static char js_NaN_date_str[] = "Invalid Date";
1831     static const char* days[] =
1832     {
1833     "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1834     };
1835     static const char* months[] =
1836     {
1837     "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1838     };
1839    
1840 siliconforks 399
1841     // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1842     // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1843     static void
1844     print_gmt_string(char* buf, size_t size, jsdouble utctime)
1845     {
1846     JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1847     days[WeekDay(utctime)],
1848     DateFromTime(utctime),
1849     months[MonthFromTime(utctime)],
1850     YearFromTime(utctime),
1851     HourFromTime(utctime),
1852     MinFromTime(utctime),
1853     SecFromTime(utctime));
1854     }
1855    
1856     static void
1857     print_iso_string(char* buf, size_t size, jsdouble utctime)
1858     {
1859     JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
1860     YearFromTime(utctime),
1861     MonthFromTime(utctime) + 1,
1862     DateFromTime(utctime),
1863     HourFromTime(utctime),
1864     MinFromTime(utctime),
1865     SecFromTime(utctime),
1866     msFromTime(utctime));
1867     }
1868    
1869 siliconforks 332 static JSBool
1870 siliconforks 399 date_utc_format(JSContext *cx, jsval *vp,
1871     void (*printFunc)(char*, size_t, jsdouble))
1872 siliconforks 332 {
1873     char buf[100];
1874     JSString *str;
1875     jsdouble utctime;
1876    
1877     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1878     return JS_FALSE;
1879    
1880     if (!JSDOUBLE_IS_FINITE(utctime)) {
1881     JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1882     } else {
1883 siliconforks 399 (*printFunc)(buf, sizeof buf, utctime);
1884 siliconforks 332 }
1885     str = JS_NewStringCopyZ(cx, buf);
1886     if (!str)
1887     return JS_FALSE;
1888     *vp = STRING_TO_JSVAL(str);
1889     return JS_TRUE;
1890     }
1891    
1892 siliconforks 399 static JSBool
1893     date_toGMTString(JSContext *cx, uintN argc, jsval *vp)
1894     {
1895     return date_utc_format(cx, vp, print_gmt_string);
1896     }
1897    
1898     static JSBool
1899     date_toISOString(JSContext *cx, uintN argc, jsval *vp)
1900     {
1901     return date_utc_format(cx, vp, print_iso_string);
1902     }
1903    
1904 siliconforks 332 /* for Date.toLocaleString; interface to PRMJTime date struct.
1905     */
1906     static void
1907     new_explode(jsdouble timeval, PRMJTime *split)
1908     {
1909     jsint year = YearFromTime(timeval);
1910    
1911     split->tm_usec = (int32) msFromTime(timeval) * 1000;
1912     split->tm_sec = (int8) SecFromTime(timeval);
1913     split->tm_min = (int8) MinFromTime(timeval);
1914     split->tm_hour = (int8) HourFromTime(timeval);
1915     split->tm_mday = (int8) DateFromTime(timeval);
1916     split->tm_mon = (int8) MonthFromTime(timeval);
1917     split->tm_wday = (int8) WeekDay(timeval);
1918     split->tm_year = year;
1919     split->tm_yday = (int16) DayWithinYear(timeval, year);
1920    
1921     /* not sure how this affects things, but it doesn't seem
1922     to matter. */
1923     split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1924     }
1925    
1926     typedef enum formatspec {
1927     FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1928     } formatspec;
1929    
1930     /* helper function */
1931     static JSBool
1932     date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1933     {
1934     char buf[100];
1935     JSString *str;
1936     char tzbuf[100];
1937     JSBool usetz;
1938     size_t i, tzlen;
1939     PRMJTime split;
1940    
1941     if (!JSDOUBLE_IS_FINITE(date)) {
1942     JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1943     } else {
1944     jsdouble local = LocalTime(date);
1945    
1946     /* offset from GMT in minutes. The offset includes daylight savings,
1947     if it applies. */
1948     jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1949    
1950     /* map 510 minutes to 0830 hours */
1951     intN offset = (minutes / 60) * 100 + minutes % 60;
1952    
1953     /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1954     * printed as 'GMT-0800' rather than as 'PST' to avoid
1955     * operating-system dependence on strftime (which
1956     * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1957     * PST as 'Pacific Standard Time.' This way we always know
1958     * what we're getting, and can parse it if we produce it.
1959     * The OS TZA string is included as a comment.
1960     */
1961    
1962     /* get a timezone string from the OS to include as a
1963     comment. */
1964     new_explode(date, &split);
1965     if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1966    
1967     /* Decide whether to use the resulting timezone string.
1968     *
1969     * Reject it if it contains any non-ASCII, non-alphanumeric
1970     * characters. It's then likely in some other character
1971     * encoding, and we probably won't display it correctly.
1972     */
1973     usetz = JS_TRUE;
1974     tzlen = strlen(tzbuf);
1975     if (tzlen > 100) {
1976     usetz = JS_FALSE;
1977     } else {
1978     for (i = 0; i < tzlen; i++) {
1979     jschar c = tzbuf[i];
1980     if (c > 127 ||
1981     !(isalpha(c) || isdigit(c) ||
1982     c == ' ' || c == '(' || c == ')')) {
1983     usetz = JS_FALSE;
1984     }
1985     }
1986     }
1987    
1988     /* Also reject it if it's not parenthesized or if it's '()'. */
1989     if (tzbuf[0] != '(' || tzbuf[1] == ')')
1990     usetz = JS_FALSE;
1991     } else
1992     usetz = JS_FALSE;
1993    
1994     switch (format) {
1995     case FORMATSPEC_FULL:
1996     /*
1997     * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1998     * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1999     */
2000     /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2001     JS_snprintf(buf, sizeof buf,
2002     "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2003     days[WeekDay(local)],
2004     months[MonthFromTime(local)],
2005     DateFromTime(local),
2006     YearFromTime(local),
2007     HourFromTime(local),
2008     MinFromTime(local),
2009     SecFromTime(local),
2010     offset,
2011     usetz ? " " : "",
2012     usetz ? tzbuf : "");
2013     break;
2014     case FORMATSPEC_DATE:
2015     /* Tue Oct 31 2000 */
2016     JS_snprintf(buf, sizeof buf,
2017     "%s %s %.2d %.4d",
2018     days[WeekDay(local)],
2019     months[MonthFromTime(local)],
2020     DateFromTime(local),
2021     YearFromTime(local));
2022     break;
2023     case FORMATSPEC_TIME:
2024     /* 09:41:40 GMT-0800 (PST) */
2025     JS_snprintf(buf, sizeof buf,
2026     "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2027     HourFromTime(local),
2028     MinFromTime(local),
2029     SecFromTime(local),
2030     offset,
2031     usetz ? " " : "",
2032     usetz ? tzbuf : "");
2033     break;
2034     }
2035     }
2036    
2037     str = JS_NewStringCopyZ(cx, buf);
2038     if (!str)
2039     return JS_FALSE;
2040     *rval = STRING_TO_JSVAL(str);
2041     return JS_TRUE;
2042     }
2043    
2044     static JSBool
2045     date_toLocaleHelper(JSContext *cx, const char *format, jsval *vp)
2046     {
2047     JSObject *obj;
2048     char buf[100];
2049     JSString *str;
2050     PRMJTime split;
2051     jsdouble utctime;
2052    
2053     obj = JS_THIS_OBJECT(cx, vp);
2054     if (!GetUTCTime(cx, obj, vp, &utctime))
2055     return JS_FALSE;
2056    
2057     if (!JSDOUBLE_IS_FINITE(utctime)) {
2058     JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2059     } else {
2060     intN result_len;
2061     jsdouble local = LocalTime(utctime);
2062     new_explode(local, &split);
2063    
2064     /* let PRMJTime format it. */
2065     result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2066    
2067     /* If it failed, default to toString. */
2068     if (result_len == 0)
2069     return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2070    
2071     /* Hacked check against undesired 2-digit year 00/00/00 form. */
2072     if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2073     /* Format %x means use OS settings, which may have 2-digit yr, so
2074     hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2075     !isdigit(buf[result_len - 3]) &&
2076     isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2077     /* ...but not if starts with 4-digit year, like 2022/3/11. */
2078     !(isdigit(buf[0]) && isdigit(buf[1]) &&
2079     isdigit(buf[2]) && isdigit(buf[3]))) {
2080     JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2081     "%d", js_DateGetYear(cx, obj));
2082     }
2083    
2084     }
2085    
2086     if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
2087     return cx->localeCallbacks->localeToUnicode(cx, buf, vp);
2088    
2089     str = JS_NewStringCopyZ(cx, buf);
2090     if (!str)
2091     return JS_FALSE;
2092     *vp = STRING_TO_JSVAL(str);
2093     return JS_TRUE;
2094     }
2095    
2096     static JSBool
2097     date_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
2098     {
2099     /* Use '%#c' for windows, because '%c' is
2100     * backward-compatible and non-y2k with msvc; '%#c' requests that a
2101     * full year be used in the result string.
2102     */
2103     return date_toLocaleHelper(cx,
2104     #if defined(_WIN32) && !defined(__MWERKS__)
2105     "%#c"
2106     #else
2107     "%c"
2108     #endif
2109     , vp);
2110     }
2111    
2112     static JSBool
2113     date_toLocaleDateString(JSContext *cx, uintN argc, jsval *vp)
2114     {
2115     /* Use '%#x' for windows, because '%x' is
2116     * backward-compatible and non-y2k with msvc; '%#x' requests that a
2117     * full year be used in the result string.
2118     */
2119     return date_toLocaleHelper(cx,
2120     #if defined(_WIN32) && !defined(__MWERKS__)
2121     "%#x"
2122     #else
2123     "%x"
2124     #endif
2125     , vp);
2126     }
2127    
2128     static JSBool
2129     date_toLocaleTimeString(JSContext *cx, uintN argc, jsval *vp)
2130     {
2131     return date_toLocaleHelper(cx, "%X", vp);
2132     }
2133    
2134     static JSBool
2135     date_toLocaleFormat(JSContext *cx, uintN argc, jsval *vp)
2136     {
2137     JSString *fmt;
2138     const char *fmtbytes;
2139    
2140     if (argc == 0)
2141     return date_toLocaleString(cx, argc, vp);
2142    
2143     fmt = js_ValueToString(cx, vp[2]);
2144     if (!fmt)
2145     return JS_FALSE;
2146     vp[2] = STRING_TO_JSVAL(fmt);
2147     fmtbytes = js_GetStringBytes(cx, fmt);
2148     if (!fmtbytes)
2149     return JS_FALSE;
2150    
2151     return date_toLocaleHelper(cx, fmtbytes, vp);
2152     }
2153    
2154     static JSBool
2155     date_toTimeString(JSContext *cx, uintN argc, jsval *vp)
2156     {
2157     jsdouble utctime;
2158    
2159     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2160     return JS_FALSE;
2161     return date_format(cx, utctime, FORMATSPEC_TIME, vp);
2162     }
2163    
2164     static JSBool
2165     date_toDateString(JSContext *cx, uintN argc, jsval *vp)
2166     {
2167     jsdouble utctime;
2168    
2169     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2170     return JS_FALSE;
2171     return date_format(cx, utctime, FORMATSPEC_DATE, vp);
2172     }
2173    
2174     #if JS_HAS_TOSOURCE
2175     #include <string.h>
2176     #include "jsdtoa.h"
2177    
2178     static JSBool
2179     date_toSource(JSContext *cx, uintN argc, jsval *vp)
2180     {
2181     jsdouble utctime;
2182     char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
2183     JSString *str;
2184    
2185     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2186     return JS_FALSE;
2187    
2188     numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
2189     if (!numStr) {
2190     JS_ReportOutOfMemory(cx);
2191     return JS_FALSE;
2192     }
2193    
2194     bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
2195     if (!bytes) {
2196     JS_ReportOutOfMemory(cx);
2197     return JS_FALSE;
2198     }
2199    
2200     str = JS_NewString(cx, bytes, strlen(bytes));
2201     if (!str) {
2202 siliconforks 507 js_free(bytes);
2203 siliconforks 332 return JS_FALSE;
2204     }
2205     *vp = STRING_TO_JSVAL(str);
2206     return JS_TRUE;
2207     }
2208     #endif
2209    
2210     static JSBool
2211     date_toString(JSContext *cx, uintN argc, jsval *vp)
2212     {
2213     jsdouble utctime;
2214    
2215     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2216     return JS_FALSE;
2217     return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2218     }
2219    
2220 siliconforks 399 #ifdef JS_TRACER
2221     static jsval FASTCALL
2222     date_valueOf_tn(JSContext* cx, JSObject* obj, JSString* str)
2223     {
2224     JS_ASSERT(JS_InstanceOf(cx, obj, &js_DateClass, NULL));
2225     jsdouble t = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
2226    
2227     JSString* number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
2228     jsval v;
2229     if (js_EqualStrings(str, number_str)) {
2230     if (!js_NewNumberInRootedValue(cx, t, &v))
2231     return JSVAL_ERROR_COOKIE;
2232     } else {
2233     if (!date_format(cx, t, FORMATSPEC_FULL, &v))
2234     return JSVAL_ERROR_COOKIE;
2235     }
2236     return v;
2237     }
2238     #endif
2239    
2240 siliconforks 332 static JSBool
2241     date_valueOf(JSContext *cx, uintN argc, jsval *vp)
2242     {
2243 siliconforks 399 JSString *str, *number_str;
2244 siliconforks 332
2245     /* It is an error to call date_valueOf on a non-date object, but we don't
2246     * need to check for that explicitly here because every path calls
2247     * GetUTCTime, which does the check.
2248     */
2249    
2250     /* If called directly with no arguments, convert to a time number. */
2251     if (argc == 0)
2252     return date_getTime(cx, argc, vp);
2253    
2254     /* Convert to number only if the hint was given, otherwise favor string. */
2255     str = js_ValueToString(cx, vp[2]);
2256     if (!str)
2257     return JS_FALSE;
2258 siliconforks 399 number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
2259     if (js_EqualStrings(str, number_str))
2260 siliconforks 332 return date_getTime(cx, argc, vp);
2261     return date_toString(cx, argc, vp);
2262     }
2263    
2264 siliconforks 399 // Don't really need an argument here, but we don't support arg-less builtins
2265     JS_DEFINE_TRCINFO_1(date_now,
2266     (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, 0)))
2267    
2268 siliconforks 332 static JSFunctionSpec date_static_methods[] = {
2269     JS_FN("UTC", date_UTC, MAXARGS,0),
2270     JS_FN("parse", date_parse, 1,0),
2271 siliconforks 507 JS_TN("now", date_now, 0,0, &date_now_trcinfo),
2272 siliconforks 332 JS_FS_END
2273     };
2274    
2275 siliconforks 399 JS_DEFINE_TRCINFO_1(date_valueOf,
2276 siliconforks 460 (3, (static, JSVAL_RETRY, date_valueOf_tn, CONTEXT, THIS, STRING, 0, 0)))
2277 siliconforks 399
2278 siliconforks 332 static JSFunctionSpec date_methods[] = {
2279     JS_FN("getTime", date_getTime, 0,0),
2280     JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2281     JS_FN("getYear", date_getYear, 0,0),
2282     JS_FN("getFullYear", date_getFullYear, 0,0),
2283     JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2284     JS_FN("getMonth", date_getMonth, 0,0),
2285     JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2286     JS_FN("getDate", date_getDate, 0,0),
2287     JS_FN("getUTCDate", date_getUTCDate, 0,0),
2288     JS_FN("getDay", date_getDay, 0,0),
2289     JS_FN("getUTCDay", date_getUTCDay, 0,0),
2290     JS_FN("getHours", date_getHours, 0,0),
2291     JS_FN("getUTCHours", date_getUTCHours, 0,0),
2292     JS_FN("getMinutes", date_getMinutes, 0,0),
2293     JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2294     JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2295     JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2296     JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2297     JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2298     JS_FN("setTime", date_setTime, 1,0),
2299     JS_FN("setYear", date_setYear, 1,0),
2300     JS_FN("setFullYear", date_setFullYear, 3,0),
2301     JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2302     JS_FN("setMonth", date_setMonth, 2,0),
2303     JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2304     JS_FN("setDate", date_setDate, 1,0),
2305     JS_FN("setUTCDate", date_setUTCDate, 1,0),
2306     JS_FN("setHours", date_setHours, 4,0),
2307     JS_FN("setUTCHours", date_setUTCHours, 4,0),
2308     JS_FN("setMinutes", date_setMinutes, 3,0),
2309     JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2310     JS_FN("setSeconds", date_setSeconds, 2,0),
2311     JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2312     JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2313     JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2314     JS_FN("toUTCString", date_toGMTString, 0,0),
2315     JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2316     JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2317     JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2318     JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2319     JS_FN("toDateString", date_toDateString, 0,0),
2320     JS_FN("toTimeString", date_toTimeString, 0,0),
2321 siliconforks 399 JS_FN("toISOString", date_toISOString, 0,0),
2322     JS_FN(js_toJSON_str, date_toISOString, 0,0),
2323    
2324 siliconforks 332 #if JS_HAS_TOSOURCE
2325     JS_FN(js_toSource_str, date_toSource, 0,0),
2326     #endif
2327     JS_FN(js_toString_str, date_toString, 0,0),
2328 siliconforks 507 JS_TN(js_valueOf_str, date_valueOf, 0,0, &date_valueOf_trcinfo),
2329 siliconforks 332 JS_FS_END
2330     };
2331    
2332 siliconforks 399 JSBool
2333     js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2334 siliconforks 332 {
2335     /* Date called as function. */
2336 siliconforks 460 if (!JS_IsConstructing(cx))
2337     return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, rval);
2338 siliconforks 332
2339     /* Date called as constructor. */
2340 siliconforks 507 jsdouble d;
2341 siliconforks 332 if (argc == 0) {
2342 siliconforks 507 d = NowAsMillis();
2343 siliconforks 332 } else if (argc == 1) {
2344     if (!JSVAL_IS_STRING(argv[0])) {
2345     /* the argument is a millisecond number */
2346     d = js_ValueToNumber(cx, &argv[0]);
2347     if (JSVAL_IS_NULL(argv[0]))
2348     return JS_FALSE;
2349 siliconforks 507 d = TIMECLIP(d);
2350 siliconforks 332 } else {
2351     /* the argument is a string; parse it. */
2352 siliconforks 507 JSString *str = js_ValueToString(cx, argv[0]);
2353 siliconforks 332 if (!str)
2354     return JS_FALSE;
2355 siliconforks 507 argv[0] = STRING_TO_JSVAL(str);
2356 siliconforks 332
2357 siliconforks 507 if (!date_parseString(str, &d))
2358     d = js_NaN;
2359     else
2360     d = TIMECLIP(d);
2361 siliconforks 332 }
2362     } else {
2363     jsdouble msec_time;
2364     if (!date_msecFromArgs(cx, argc, argv, &msec_time))
2365     return JS_FALSE;
2366    
2367     if (JSDOUBLE_IS_FINITE(msec_time)) {
2368     msec_time = UTC(msec_time);
2369     msec_time = TIMECLIP(msec_time);
2370     }
2371 siliconforks 507 d = msec_time;
2372 siliconforks 332 }
2373 siliconforks 507 return SetUTCTime(cx, obj, d);
2374 siliconforks 332 }
2375    
2376     JSObject *
2377     js_InitDateClass(JSContext *cx, JSObject *obj)
2378     {
2379     JSObject *proto;
2380    
2381     /* set static LocalTZA */
2382     LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2383 siliconforks 460 proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS,
2384 siliconforks 332 NULL, date_methods, NULL, date_static_methods);
2385     if (!proto)
2386     return NULL;
2387    
2388 siliconforks 507 SetDateToNaN(cx, proto);
2389    
2390 siliconforks 332 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2391     if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2392     return NULL;
2393    
2394     return proto;
2395     }
2396    
2397     JS_FRIEND_API(JSObject *)
2398     js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2399     {
2400 siliconforks 507 JSObject *obj = js_NewObject(cx, &js_DateClass, NULL, NULL);
2401     if (!obj || !SetUTCTime(cx, obj, msec_time))
2402 siliconforks 332 return NULL;
2403     return obj;
2404     }
2405    
2406     JS_FRIEND_API(JSObject *)
2407     js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2408     int hour, int min, int sec)
2409     {
2410     JSObject *obj;
2411     jsdouble msec_time;
2412    
2413     JS_ASSERT(mon < 12);
2414     msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2415     obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2416     return obj;
2417     }
2418    
2419     JS_FRIEND_API(JSBool)
2420     js_DateIsValid(JSContext *cx, JSObject* obj)
2421     {
2422     jsdouble utctime;
2423     return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime);
2424     }
2425    
2426     JS_FRIEND_API(int)
2427     js_DateGetYear(JSContext *cx, JSObject* obj)
2428     {
2429     jsdouble localtime;
2430    
2431     /* Preserve legacy API behavior of returning 0 for invalid dates. */
2432     if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2433     JSDOUBLE_IS_NaN(localtime)) {
2434     return 0;
2435     }
2436    
2437     return (int) YearFromTime(localtime);
2438     }
2439    
2440     JS_FRIEND_API(int)
2441     js_DateGetMonth(JSContext *cx, JSObject* obj)
2442     {
2443     jsdouble localtime;
2444    
2445     if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2446     JSDOUBLE_IS_NaN(localtime)) {
2447     return 0;
2448     }
2449    
2450     return (int) MonthFromTime(localtime);
2451     }
2452    
2453     JS_FRIEND_API(int)
2454     js_DateGetDate(JSContext *cx, JSObject* obj)
2455     {
2456     jsdouble localtime;
2457    
2458     if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2459     JSDOUBLE_IS_NaN(localtime)) {
2460     return 0;
2461     }
2462    
2463     return (int) DateFromTime(localtime);
2464     }
2465    
2466     JS_FRIEND_API(int)
2467     js_DateGetHours(JSContext *cx, JSObject* obj)
2468     {
2469     jsdouble localtime;
2470    
2471     if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2472     JSDOUBLE_IS_NaN(localtime)) {
2473     return 0;
2474     }
2475    
2476     return (int) HourFromTime(localtime);
2477     }
2478    
2479     JS_FRIEND_API(int)
2480     js_DateGetMinutes(JSContext *cx, JSObject* obj)
2481     {
2482     jsdouble localtime;
2483    
2484     if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2485     JSDOUBLE_IS_NaN(localtime)) {
2486     return 0;
2487     }
2488    
2489     return (int) MinFromTime(localtime);
2490     }
2491    
2492     JS_FRIEND_API(int)
2493     js_DateGetSeconds(JSContext *cx, JSObject* obj)
2494     {
2495     jsdouble utctime;
2496    
2497     if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime))
2498     return 0;
2499    
2500     return (int) SecFromTime(utctime);
2501     }
2502    
2503     JS_FRIEND_API(void)
2504     js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2505     {
2506     jsdouble local;
2507    
2508     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2509     return;
2510    
2511     /* reset date if it was NaN */
2512     if (JSDOUBLE_IS_NaN(local))
2513     local = 0;
2514    
2515     local = date_msecFromDate(year,
2516     MonthFromTime(local),
2517     DateFromTime(local),
2518     HourFromTime(local),
2519     MinFromTime(local),
2520     SecFromTime(local),
2521     msFromTime(local));
2522    
2523     /* SetUTCTime also invalidates local time cache. */
2524 siliconforks 507 SetUTCTime(cx, obj, UTC(local));
2525 siliconforks 332 }
2526    
2527     JS_FRIEND_API(void)
2528     js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2529     {
2530     jsdouble local;
2531    
2532     JS_ASSERT(month < 12);
2533    
2534     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2535     return;
2536    
2537     /* bail if date was NaN */
2538     if (JSDOUBLE_IS_NaN(local))
2539     return;
2540    
2541     local = date_msecFromDate(YearFromTime(local),
2542     month,
2543     DateFromTime(local),
2544     HourFromTime(local),
2545     MinFromTime(local),
2546     SecFromTime(local),
2547     msFromTime(local));
2548 siliconforks 507 SetUTCTime(cx, obj, UTC(local));
2549 siliconforks 332 }
2550    
2551     JS_FRIEND_API(void)
2552     js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2553     {
2554     jsdouble local;
2555    
2556     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2557     return;
2558    
2559     if (JSDOUBLE_IS_NaN(local))
2560     return;
2561    
2562     local = date_msecFromDate(YearFromTime(local),
2563     MonthFromTime(local),
2564     date,
2565     HourFromTime(local),
2566     MinFromTime(local),
2567     SecFromTime(local),
2568     msFromTime(local));
2569 siliconforks 507 SetUTCTime(cx, obj, UTC(local));
2570 siliconforks 332 }
2571    
2572     JS_FRIEND_API(void)
2573     js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2574     {
2575     jsdouble local;
2576    
2577     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2578     return;
2579    
2580     if (JSDOUBLE_IS_NaN(local))
2581     return;
2582     local = date_msecFromDate(YearFromTime(local),
2583     MonthFromTime(local),
2584     DateFromTime(local),
2585     hours,
2586     MinFromTime(local),
2587     SecFromTime(local),
2588     msFromTime(local));
2589 siliconforks 507 SetUTCTime(cx, obj, UTC(local));
2590 siliconforks 332 }
2591    
2592     JS_FRIEND_API(void)
2593     js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2594     {
2595     jsdouble local;
2596    
2597     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2598     return;
2599    
2600     if (JSDOUBLE_IS_NaN(local))
2601     return;
2602     local = date_msecFromDate(YearFromTime(local),
2603     MonthFromTime(local),
2604     DateFromTime(local),
2605     HourFromTime(local),
2606     minutes,
2607     SecFromTime(local),
2608     msFromTime(local));
2609 siliconforks 507 SetUTCTime(cx, obj, UTC(local));
2610 siliconforks 332 }
2611    
2612     JS_FRIEND_API