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

Contents of /trunk/js/jsdate.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (show annotations)
Tue Dec 9 03:37:47 2008 UTC (11 years ago) by siliconforks
File size: 70272 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

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

  ViewVC Help
Powered by ViewVC 1.1.24