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