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