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