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

Contents of /trunk/js/jsdate.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (9 years, 11 months ago) by siliconforks
File size: 69445 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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

  ViewVC Help
Powered by ViewVC 1.1.24