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

Contents of /trunk/js/jsdate.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 67137 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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

  ViewVC Help
Powered by ViewVC 1.1.24