1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JS date methods.
43 : */
44 :
45 : /*
46 : * "For example, OS/360 devotes 26 bytes of the permanently
47 : * resident date-turnover routine to the proper handling of
48 : * December 31 on leap years (when it is Day 366). That
49 : * might have been left to the operator."
50 : *
51 : * Frederick Brooks, 'The Second-System Effect'.
52 : */
53 :
54 : #include <ctype.h>
55 : #include <locale.h>
56 : #include <math.h>
57 : #include <stdlib.h>
58 : #include <string.h>
59 :
60 : #include "mozilla/Util.h"
61 :
62 : #include "jstypes.h"
63 : #include "jsprf.h"
64 : #include "prmjtime.h"
65 : #include "jsutil.h"
66 : #include "jsapi.h"
67 : #include "jsversion.h"
68 : #include "jscntxt.h"
69 : #include "jsdate.h"
70 : #include "jsinterp.h"
71 : #include "jsnum.h"
72 : #include "jsobj.h"
73 : #include "jsstr.h"
74 : #include "jslibmath.h"
75 :
76 : #include "vm/GlobalObject.h"
77 :
78 : #include "jsinferinlines.h"
79 : #include "jsobjinlines.h"
80 :
81 : #include "vm/MethodGuard-inl.h"
82 : #include "vm/Stack-inl.h"
83 : #include "vm/StringBuffer-inl.h"
84 :
85 : using namespace mozilla;
86 : using namespace js;
87 : using namespace js::types;
88 :
89 : /*
90 : * The JS 'Date' object is patterned after the Java 'Date' object.
91 : * Here is an script:
92 : *
93 : * today = new Date();
94 : *
95 : * print(today.toLocaleString());
96 : *
97 : * weekDay = today.getDay();
98 : *
99 : *
100 : * These Java (and ECMA-262) methods are supported:
101 : *
102 : * UTC
103 : * getDate (getUTCDate)
104 : * getDay (getUTCDay)
105 : * getHours (getUTCHours)
106 : * getMinutes (getUTCMinutes)
107 : * getMonth (getUTCMonth)
108 : * getSeconds (getUTCSeconds)
109 : * getMilliseconds (getUTCMilliseconds)
110 : * getTime
111 : * getTimezoneOffset
112 : * getYear
113 : * getFullYear (getUTCFullYear)
114 : * parse
115 : * setDate (setUTCDate)
116 : * setHours (setUTCHours)
117 : * setMinutes (setUTCMinutes)
118 : * setMonth (setUTCMonth)
119 : * setSeconds (setUTCSeconds)
120 : * setMilliseconds (setUTCMilliseconds)
121 : * setTime
122 : * setYear (setFullYear, setUTCFullYear)
123 : * toGMTString (toUTCString)
124 : * toLocaleString
125 : * toString
126 : *
127 : *
128 : * These Java methods are not supported
129 : *
130 : * setDay
131 : * before
132 : * after
133 : * equals
134 : * hashCode
135 : */
136 :
137 : /*
138 : * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
139 : * definition and reduce dependence on NSPR. NSPR is used to get the current
140 : * time in milliseconds, the time zone offset, and the daylight savings time
141 : * offset for a given time. NSPR is also used for Date.toLocaleString(), for
142 : * locale-specific formatting, and to get a string representing the timezone.
143 : * (Which turns out to be platform-dependent.)
144 : *
145 : * To do:
146 : * (I did some performance tests by timing how long it took to run what
147 : * I had of the js ECMA conformance tests.)
148 : *
149 : * - look at saving results across multiple calls to supporting
150 : * functions; the toString functions compute some of the same values
151 : * multiple times. Although - I took a quick stab at this, and I lost
152 : * rather than gained. (Fractionally.) Hard to tell what compilers/processors
153 : * are doing these days.
154 : *
155 : * - look at tweaking function return types to return double instead
156 : * of int; this seems to make things run slightly faster sometimes.
157 : * (though it could be architecture-dependent.) It'd be good to see
158 : * how this does on win32. (Tried it on irix.) Types could use a
159 : * general going-over.
160 : */
161 :
162 : /*
163 : * Supporting functions - ECMA 15.9.1.*
164 : */
165 :
166 : #define HoursPerDay 24.0
167 : #define MinutesPerDay (HoursPerDay * MinutesPerHour)
168 : #define MinutesPerHour 60.0
169 : #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
170 : #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
171 : #define SecondsPerMinute 60.0
172 :
173 : #if defined(XP_WIN) || defined(XP_OS2)
174 : /* Work around msvc double optimization bug by making these runtime values; if
175 : * they're available at compile time, msvc optimizes division by them by
176 : * computing the reciprocal and multiplying instead of dividing - this loses
177 : * when the reciprocal isn't representable in a double.
178 : */
179 : static double msPerSecond = 1000.0;
180 : static double msPerDay = SecondsPerDay * 1000.0;
181 : static double msPerHour = SecondsPerHour * 1000.0;
182 : static double msPerMinute = SecondsPerMinute * 1000.0;
183 : #else
184 : #define msPerDay (SecondsPerDay * msPerSecond)
185 : #define msPerHour (SecondsPerHour * msPerSecond)
186 : #define msPerMinute (SecondsPerMinute * msPerSecond)
187 : #define msPerSecond 1000.0
188 : #endif
189 :
190 : #define Day(t) floor((t) / msPerDay)
191 :
192 : static double
193 24 : TimeWithinDay(double t)
194 : {
195 : double result;
196 24 : result = fmod(t, msPerDay);
197 24 : if (result < 0)
198 1 : result += msPerDay;
199 24 : return result;
200 : }
201 :
202 : static inline bool
203 94695 : IsLeapYear(int year)
204 : {
205 94695 : return year % 4 == 0 && (year % 100 || (year % 400 == 0));
206 : }
207 :
208 : static inline int
209 76042 : DaysInYear(int year)
210 : {
211 76042 : return IsLeapYear(year) ? 366 : 365;
212 : }
213 :
214 : static inline int
215 9052 : DaysInFebruary(int year)
216 : {
217 9052 : return IsLeapYear(year) ? 29 : 28;
218 : }
219 :
220 : /* math here has to be f.p, because we need
221 : * floor((1968 - 1969) / 4) == -1
222 : */
223 : #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
224 : - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
225 : #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
226 :
227 : static int
228 13805 : YearFromTime(double t)
229 : {
230 13805 : int y = (int) floor(t /(msPerDay*365.2425)) + 1970;
231 13805 : double t2 = (double) TimeFromYear(y);
232 :
233 : /*
234 : * Adjust the year if the approximation was wrong. Since the year was
235 : * computed using the average number of ms per year, it will usually
236 : * be wrong for dates within several hours of a year transition.
237 : */
238 13805 : if (t2 > t) {
239 0 : y--;
240 : } else {
241 13805 : if (t2 + msPerDay * DaysInYear(y) <= t)
242 27 : y++;
243 : }
244 13805 : return y;
245 : }
246 :
247 : #define DayWithinYear(t, year) ((int) (Day(t) - DayFromYear(year)))
248 :
249 : /*
250 : * The following array contains the day of year for the first day of
251 : * each month, where index 0 is January, and day 0 is January 1.
252 : */
253 : static double firstDayOfMonth[2][13] = {
254 : {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0, 365.0},
255 : {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0, 366.0}
256 : };
257 :
258 : #define DayFromMonth(m, leap) firstDayOfMonth[leap][(int)m]
259 :
260 : static int
261 213 : DaysInMonth(int year, int month)
262 : {
263 213 : JSBool leap = IsLeapYear(year);
264 213 : int result = int(DayFromMonth(month, leap) - DayFromMonth(month-1, leap));
265 213 : return result;
266 : }
267 :
268 : static int
269 4600 : MonthFromTime(double t)
270 : {
271 : int d, step;
272 4600 : int year = YearFromTime(t);
273 4600 : d = DayWithinYear(t, year);
274 :
275 4600 : if (d < (step = 31))
276 74 : return 0;
277 4526 : if (d < (step += DaysInFebruary(year)))
278 20 : return 1;
279 4506 : if (d < (step += 31))
280 15 : return 2;
281 4491 : if (d < (step += 30))
282 66 : return 3;
283 4425 : if (d < (step += 31))
284 164 : return 4;
285 4261 : if (d < (step += 30))
286 4179 : return 5;
287 82 : if (d < (step += 31))
288 0 : return 6;
289 82 : if (d < (step += 31))
290 18 : return 7;
291 64 : if (d < (step += 30))
292 15 : return 8;
293 49 : if (d < (step += 31))
294 0 : return 9;
295 49 : if (d < (step += 30))
296 3 : return 10;
297 46 : return 11;
298 : }
299 :
300 : static int
301 4600 : DateFromTime(double t)
302 : {
303 : int d, step, next;
304 4600 : int year = YearFromTime(t);
305 4600 : d = DayWithinYear(t, year);
306 :
307 4600 : if (d <= (next = 30))
308 74 : return d + 1;
309 4526 : step = next;
310 4526 : if (d <= (next += DaysInFebruary(year)))
311 20 : return d - step;
312 4506 : step = next;
313 4506 : if (d <= (next += 31))
314 15 : return d - step;
315 4491 : step = next;
316 4491 : if (d <= (next += 30))
317 66 : return d - step;
318 4425 : step = next;
319 4425 : if (d <= (next += 31))
320 164 : return d - step;
321 4261 : step = next;
322 4261 : if (d <= (next += 30))
323 4179 : return d - step;
324 82 : step = next;
325 82 : if (d <= (next += 31))
326 0 : return d - step;
327 82 : step = next;
328 82 : if (d <= (next += 31))
329 18 : return d - step;
330 64 : step = next;
331 64 : if (d <= (next += 30))
332 15 : return d - step;
333 49 : step = next;
334 49 : if (d <= (next += 31))
335 0 : return d - step;
336 49 : step = next;
337 49 : if (d <= (next += 30))
338 3 : return d - step;
339 46 : step = next;
340 46 : return d - step;
341 : }
342 :
343 : static int
344 57701 : WeekDay(double t)
345 : {
346 : int result;
347 57701 : result = (int) Day(t) + 4;
348 57701 : result = result % 7;
349 57701 : if (result < 0)
350 0 : result += 7;
351 57701 : return (int) result;
352 : }
353 :
354 : #define MakeTime(hour, min, sec, ms) \
355 : ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
356 :
357 : static double
358 9377 : MakeDay(double year, double month, double date)
359 : {
360 : JSBool leap;
361 : double yearday;
362 : double monthday;
363 :
364 9377 : year += floor(month / 12);
365 :
366 9377 : month = fmod(month, 12.0);
367 9377 : if (month < 0)
368 0 : month += 12;
369 :
370 9377 : leap = IsLeapYear((int) year);
371 :
372 9377 : yearday = floor(TimeFromYear(year) / msPerDay);
373 9377 : monthday = DayFromMonth(month, leap);
374 :
375 9377 : return yearday + monthday + date - 1;
376 : }
377 :
378 : #define MakeDate(day, time) ((day) * msPerDay + (time))
379 :
380 : /*
381 : * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
382 : *
383 : * yearStartingWith[0][i] is an example non-leap year where
384 : * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
385 : *
386 : * yearStartingWith[1][i] is an example leap year where
387 : * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
388 : */
389 : static int yearStartingWith[2][7] = {
390 : {1978, 1973, 1974, 1975, 1981, 1971, 1977},
391 : {1984, 1996, 1980, 1992, 1976, 1988, 1972}
392 : };
393 :
394 : /*
395 : * Find a year for which any given date will fall on the same weekday.
396 : *
397 : * This function should be used with caution when used other than
398 : * for determining DST; it hasn't been proven not to produce an
399 : * incorrect year for times near year boundaries.
400 : */
401 : static int
402 11 : EquivalentYearForDST(int year)
403 : {
404 : int day;
405 :
406 11 : day = (int) DayFromYear(year) + 4;
407 11 : day = day % 7;
408 11 : if (day < 0)
409 10 : day += 7;
410 :
411 11 : return yearStartingWith[IsLeapYear(year)][day];
412 : }
413 :
414 : /* LocalTZA gets set by js_InitDateClass() */
415 : static double LocalTZA;
416 :
417 : static double
418 55701 : DaylightSavingTA(double t, JSContext *cx)
419 : {
420 : /* abort if NaN */
421 55701 : if (JSDOUBLE_IS_NaN(t))
422 0 : return t;
423 :
424 : /*
425 : * If earlier than 1970 or after 2038, potentially beyond the ken of
426 : * many OSes, map it to an equivalent year before asking.
427 : */
428 55701 : if (t < 0.0 || t > 2145916800000.0) {
429 11 : int year = EquivalentYearForDST(YearFromTime(t));
430 11 : double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
431 11 : t = MakeDate(day, TimeWithinDay(t));
432 : }
433 :
434 55701 : int64_t timeMilliseconds = static_cast<int64_t>(t);
435 55701 : int64_t offsetMilliseconds = cx->dstOffsetCache.getDSTOffsetMilliseconds(timeMilliseconds, cx);
436 55701 : return static_cast<double>(offsetMilliseconds);
437 : }
438 :
439 : static double
440 54968 : AdjustTime(double date, JSContext *cx)
441 : {
442 54968 : double t = DaylightSavingTA(date, cx) + LocalTZA;
443 54968 : t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
444 54968 : return t;
445 : }
446 :
447 : static double
448 54248 : LocalTime(double t, JSContext *cx)
449 : {
450 54248 : return t + AdjustTime(t, cx);
451 : }
452 :
453 : static double
454 454 : UTC(double t, JSContext *cx)
455 : {
456 454 : return t - AdjustTime(t - LocalTZA, cx);
457 : }
458 :
459 : static int
460 4802 : HourFromTime(double t)
461 : {
462 4802 : int result = (int) fmod(floor(t/msPerHour), HoursPerDay);
463 4802 : if (result < 0)
464 9 : result += (int)HoursPerDay;
465 4802 : return result;
466 : }
467 :
468 : static int
469 4803 : MinFromTime(double t)
470 : {
471 4803 : int result = (int) fmod(floor(t / msPerMinute), MinutesPerHour);
472 4803 : if (result < 0)
473 0 : result += (int)MinutesPerHour;
474 4803 : return result;
475 : }
476 :
477 : static int
478 1444 : SecFromTime(double t)
479 : {
480 1444 : int result = (int) fmod(floor(t / msPerSecond), SecondsPerMinute);
481 1444 : if (result < 0)
482 0 : result += (int)SecondsPerMinute;
483 1444 : return result;
484 : }
485 :
486 : static int
487 1040 : msFromTime(double t)
488 : {
489 1040 : int result = (int) fmod(t, msPerSecond);
490 1040 : if (result < 0)
491 0 : result += (int)msPerSecond;
492 1040 : return result;
493 : }
494 :
495 : /**
496 : * end of ECMA 'support' functions
497 : */
498 :
499 : static JSBool
500 450 : date_convert(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
501 : {
502 450 : JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
503 450 : JS_ASSERT(obj->isDate());
504 :
505 450 : return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp);
506 : }
507 :
508 : /*
509 : * Other Support routines and definitions
510 : */
511 :
512 : Class js::DateClass = {
513 : js_Date_str,
514 : JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_CLASS_RESERVED_SLOTS) |
515 : JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
516 : JS_PropertyStub, /* addProperty */
517 : JS_PropertyStub, /* delProperty */
518 : JS_PropertyStub, /* getProperty */
519 : JS_StrictPropertyStub, /* setProperty */
520 : JS_EnumerateStub,
521 : JS_ResolveStub,
522 : date_convert
523 : };
524 :
525 : /* for use by date_parse */
526 :
527 : static const char* wtb[] = {
528 : "am", "pm",
529 : "monday", "tuesday", "wednesday", "thursday", "friday",
530 : "saturday", "sunday",
531 : "january", "february", "march", "april", "may", "june",
532 : "july", "august", "september", "october", "november", "december",
533 : "gmt", "ut", "utc",
534 : "est", "edt",
535 : "cst", "cdt",
536 : "mst", "mdt",
537 : "pst", "pdt"
538 : /* time zone table needs to be expanded */
539 : };
540 :
541 : static int ttb[] = {
542 : -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
543 : 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
544 : 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
545 : 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
546 : 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
547 : 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
548 : 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
549 : };
550 :
551 : /* helper for date_parse */
552 : static JSBool
553 210497 : date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
554 : int count, int ignoreCase)
555 : {
556 210497 : JSBool result = JS_FALSE;
557 : /* return true if matches, otherwise, false */
558 :
559 502786 : while (count > 0 && s1[s1off] && s2[s2off]) {
560 283102 : if (ignoreCase) {
561 283102 : if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off]))
562 201310 : break;
563 : } else {
564 0 : if ((jschar)s1[s1off] != s2[s2off]) {
565 0 : break;
566 : }
567 : }
568 81792 : s1off++;
569 81792 : s2off++;
570 81792 : count--;
571 : }
572 :
573 210497 : if (count == 0) {
574 9187 : result = JS_TRUE;
575 : }
576 :
577 210497 : return result;
578 : }
579 :
580 : /* find UTC time from given date... no 1900 correction! */
581 : static double
582 9353 : date_msecFromDate(double year, double mon, double mday, double hour,
583 : double min, double sec, double msec)
584 : {
585 : double day;
586 : double msec_time;
587 : double result;
588 :
589 9353 : day = MakeDay(year, mon, mday);
590 9353 : msec_time = MakeTime(hour, min, sec, msec);
591 9353 : result = MakeDate(day, msec_time);
592 9353 : return result;
593 : }
594 :
595 : /* compute the time in msec (unclipped) from the given args */
596 : #define MAXARGS 7
597 :
598 : static JSBool
599 66 : date_msecFromArgs(JSContext *cx, CallArgs args, double *rval)
600 : {
601 : unsigned loop;
602 : double array[MAXARGS];
603 : double msec_time;
604 :
605 465 : for (loop = 0; loop < MAXARGS; loop++) {
606 408 : if (loop < args.length()) {
607 : double d;
608 311 : if (!ToNumber(cx, args[loop], &d))
609 0 : return JS_FALSE;
610 : /* return NaN if any arg is not finite */
611 311 : if (!JSDOUBLE_IS_FINITE(d)) {
612 9 : *rval = js_NaN;
613 9 : return JS_TRUE;
614 : }
615 302 : array[loop] = js_DoubleToInteger(d);
616 : } else {
617 97 : if (loop == 2) {
618 0 : array[loop] = 1; /* Default the date argument to 1. */
619 : } else {
620 97 : array[loop] = 0;
621 : }
622 : }
623 : }
624 :
625 : /* adjust 2-digit years into the 20th century */
626 57 : if (array[0] >= 0 && array[0] <= 99)
627 0 : array[0] += 1900;
628 :
629 : msec_time = date_msecFromDate(array[0], array[1], array[2],
630 57 : array[3], array[4], array[5], array[6]);
631 57 : *rval = msec_time;
632 57 : return JS_TRUE;
633 : }
634 :
635 : /*
636 : * See ECMA 15.9.4.[3-10];
637 : */
638 : static JSBool
639 21 : date_UTC(JSContext *cx, unsigned argc, Value *vp)
640 : {
641 21 : CallArgs args = CallArgsFromVp(argc, vp);
642 :
643 : double msec_time;
644 21 : if (!date_msecFromArgs(cx, args, &msec_time))
645 0 : return JS_FALSE;
646 :
647 21 : msec_time = TIMECLIP(msec_time);
648 :
649 21 : args.rval().setNumber(msec_time);
650 21 : return JS_TRUE;
651 : }
652 :
653 : /*
654 : * Read and convert decimal digits from s[*i] into *result
655 : * while *i < limit.
656 : *
657 : * Succeed if any digits are converted. Advance *i only
658 : * as digits are consumed.
659 : */
660 : static JSBool
661 10191 : digits(size_t *result, const jschar *s, size_t *i, size_t limit)
662 : {
663 10191 : size_t init = *i;
664 10191 : *result = 0;
665 46519 : while (*i < limit &&
666 23477 : ('0' <= s[*i] && s[*i] <= '9')) {
667 2660 : *result *= 10;
668 2660 : *result += (s[*i] - '0');
669 2660 : ++(*i);
670 : }
671 10191 : return (*i != init);
672 : }
673 :
674 : /*
675 : * Read and convert decimal digits to the right of a decimal point,
676 : * representing a fractional integer, from s[*i] into *result
677 : * while *i < limit.
678 : *
679 : * Succeed if any digits are converted. Advance *i only
680 : * as digits are consumed.
681 : */
682 : static JSBool
683 5 : fractional(double *result, const jschar *s, size_t *i, size_t limit)
684 : {
685 5 : double factor = 0.1;
686 5 : size_t init = *i;
687 5 : *result = 0.0;
688 46 : while (*i < limit &&
689 24 : ('0' <= s[*i] && s[*i] <= '9')) {
690 12 : *result += (s[*i] - '0') * factor;
691 12 : factor *= 0.1;
692 12 : ++(*i);
693 : }
694 5 : return (*i != init);
695 : }
696 :
697 : /*
698 : * Read and convert exactly n decimal digits from s[*i]
699 : * to s[min(*i+n,limit)] into *result.
700 : *
701 : * Succeed if exactly n digits are converted. Advance *i only
702 : * on success.
703 : */
704 : static JSBool
705 10191 : ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
706 : {
707 10191 : size_t init = *i;
708 :
709 10191 : if (digits(result, s, i, JS_MIN(limit, init+n)))
710 1125 : return ((*i - init) == n);
711 :
712 9066 : *i = init;
713 9066 : return JS_FALSE;
714 : }
715 :
716 : /*
717 : * Parse a string in one of the date-time formats given by the W3C
718 : * "NOTE-datetime" specification. These formats make up a restricted
719 : * profile of the ISO 8601 format. Quoted here:
720 : *
721 : * The formats are as follows. Exactly the components shown here
722 : * must be present, with exactly this punctuation. Note that the "T"
723 : * appears literally in the string, to indicate the beginning of the
724 : * time element, as specified in ISO 8601.
725 : *
726 : * Any combination of the date formats with the time formats is
727 : * allowed, and also either the date or the time can be missing.
728 : *
729 : * The specification is silent on the meaning when fields are
730 : * ommitted so the interpretations are a guess, but hopefully a
731 : * reasonable one. We default the month to January, the day to the
732 : * 1st, and hours minutes and seconds all to 0. If the date is
733 : * missing entirely then we assume 1970-01-01 so that the time can
734 : * be aded to a date later. If the time is missing then we assume
735 : * 00:00 UTC. If the time is present but the time zone field is
736 : * missing then we use local time.
737 : *
738 : * Date part:
739 : *
740 : * Year:
741 : * YYYY (eg 1997)
742 : *
743 : * Year and month:
744 : * YYYY-MM (eg 1997-07)
745 : *
746 : * Complete date:
747 : * YYYY-MM-DD (eg 1997-07-16)
748 : *
749 : * Time part:
750 : *
751 : * Hours and minutes:
752 : * Thh:mmTZD (eg T19:20+01:00)
753 : *
754 : * Hours, minutes and seconds:
755 : * Thh:mm:ssTZD (eg T19:20:30+01:00)
756 : *
757 : * Hours, minutes, seconds and a decimal fraction of a second:
758 : * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
759 : *
760 : * where:
761 : *
762 : * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
763 : * MM = two-digit month (01=January, etc.)
764 : * DD = two-digit day of month (01 through 31)
765 : * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
766 : * mm = two digits of minute (00 through 59)
767 : * ss = two digits of second (00 through 59)
768 : * s = one or more digits representing a decimal fraction of a second
769 : * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
770 : */
771 :
772 : static JSBool
773 9301 : date_parseISOString(JSLinearString *str, double *result, JSContext *cx)
774 : {
775 : double msec;
776 :
777 : const jschar *s;
778 : size_t limit;
779 9301 : size_t i = 0;
780 9301 : int tzMul = 1;
781 9301 : int dateMul = 1;
782 9301 : size_t year = 1970;
783 9301 : size_t month = 1;
784 9301 : size_t day = 1;
785 9301 : size_t hour = 0;
786 9301 : size_t min = 0;
787 9301 : size_t sec = 0;
788 9301 : double frac = 0;
789 9301 : bool isLocalTime = JS_FALSE;
790 9301 : size_t tzHour = 0;
791 9301 : size_t tzMin = 0;
792 :
793 : #define PEEK(ch) (i < limit && s[i] == ch)
794 :
795 : #define NEED(ch) \
796 : JS_BEGIN_MACRO \
797 : if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
798 : JS_END_MACRO
799 :
800 : #define DONE_DATE_UNLESS(ch) \
801 : JS_BEGIN_MACRO \
802 : if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
803 : JS_END_MACRO
804 :
805 : #define DONE_UNLESS(ch) \
806 : JS_BEGIN_MACRO \
807 : if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
808 : JS_END_MACRO
809 :
810 : #define NEED_NDIGITS(n, field) \
811 : JS_BEGIN_MACRO \
812 : if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
813 : JS_END_MACRO
814 :
815 9301 : s = str->chars();
816 9301 : limit = str->length();
817 :
818 9301 : if (PEEK('+') || PEEK('-')) {
819 0 : if (PEEK('-'))
820 0 : dateMul = -1;
821 0 : ++i;
822 0 : NEED_NDIGITS(6, year);
823 9301 : } else if (!PEEK('T')) {
824 9283 : NEED_NDIGITS(4, year);
825 : }
826 234 : DONE_DATE_UNLESS('-');
827 140 : NEED_NDIGITS(2, month);
828 140 : DONE_DATE_UNLESS('-');
829 140 : NEED_NDIGITS(2, day);
830 :
831 : done_date:
832 234 : DONE_UNLESS('T');
833 146 : NEED_NDIGITS(2, hour);
834 125 : NEED(':');
835 125 : NEED_NDIGITS(2, min);
836 :
837 125 : if (PEEK(':')) {
838 125 : ++i;
839 125 : NEED_NDIGITS(2, sec);
840 125 : if (PEEK('.')) {
841 5 : ++i;
842 5 : if (!fractional(&frac, s, &i, limit))
843 0 : goto syntax;
844 : }
845 : }
846 :
847 125 : if (PEEK('Z')) {
848 3 : ++i;
849 122 : } else if (PEEK('+') || PEEK('-')) {
850 116 : if (PEEK('-'))
851 116 : tzMul = -1;
852 116 : ++i;
853 116 : NEED_NDIGITS(2, tzHour);
854 : /*
855 : * Non-standard extension to the ISO date format (permitted by ES5):
856 : * allow "-0700" as a time zone offset, not just "-07:00".
857 : */
858 116 : if (PEEK(':'))
859 116 : ++i;
860 116 : NEED_NDIGITS(2, tzMin);
861 : } else {
862 6 : isLocalTime = JS_TRUE;
863 : }
864 :
865 : done:
866 426 : if (year > 275943 // ceil(1e8/365) + 1970
867 : || (month == 0 || month > 12)
868 213 : || (day == 0 || day > size_t(DaysInMonth(year,month)))
869 : || hour > 24
870 : || ((hour == 24) && (min > 0 || sec > 0))
871 : || min > 59
872 : || sec > 59
873 : || tzHour > 23
874 : || tzMin > 59)
875 0 : goto syntax;
876 :
877 213 : if (i != limit)
878 76 : goto syntax;
879 :
880 137 : month -= 1; /* convert month to 0-based */
881 :
882 : msec = date_msecFromDate(dateMul * (double)year, month, day,
883 : hour, min, sec,
884 137 : frac * 1000.0);;
885 :
886 137 : if (isLocalTime) {
887 6 : msec = UTC(msec, cx);
888 : } else {
889 : msec -= ((tzMul) * ((tzHour * msPerHour)
890 131 : + (tzMin * msPerMinute)));
891 : }
892 :
893 137 : if (msec < -8.64e15 || msec > 8.64e15)
894 : goto syntax;
895 :
896 137 : *result = msec;
897 :
898 137 : return JS_TRUE;
899 :
900 : syntax:
901 : /* syntax error */
902 9164 : *result = 0;
903 9164 : return JS_FALSE;
904 :
905 : #undef PEEK
906 : #undef NEED
907 : #undef DONE_UNLESS
908 : #undef NEED_NDIGITS
909 : }
910 :
911 : static JSBool
912 9301 : date_parseString(JSLinearString *str, double *result, JSContext *cx)
913 : {
914 : double msec;
915 :
916 : const jschar *s;
917 : size_t limit;
918 9301 : size_t i = 0;
919 9301 : int year = -1;
920 9301 : int mon = -1;
921 9301 : int mday = -1;
922 9301 : int hour = -1;
923 9301 : int min = -1;
924 9301 : int sec = -1;
925 9301 : int c = -1;
926 9301 : int n = -1;
927 9301 : int tzoffset = -1;
928 9301 : int prevc = 0;
929 9301 : JSBool seenplusminus = JS_FALSE;
930 : int temp;
931 9301 : JSBool seenmonthname = JS_FALSE;
932 :
933 9301 : if (date_parseISOString(str, result, cx))
934 137 : return JS_TRUE;
935 :
936 9164 : s = str->chars();
937 9164 : limit = str->length();
938 9164 : if (limit == 0)
939 0 : goto syntax;
940 146053 : while (i < limit) {
941 127730 : c = s[i];
942 127730 : i++;
943 127730 : if (c <= ' ' || c == ',' || c == '-') {
944 36449 : if (c == '-' && '0' <= s[i] && s[i] <= '9') {
945 50 : prevc = c;
946 : }
947 36449 : continue;
948 : }
949 91281 : if (c == '(') { /* comments) */
950 43 : int depth = 1;
951 215 : while (i < limit) {
952 172 : c = s[i];
953 172 : i++;
954 172 : if (c == '(') depth++;
955 172 : else if (c == ')')
956 43 : if (--depth <= 0)
957 43 : break;
958 : }
959 43 : continue;
960 : }
961 91238 : if ('0' <= c && c <= '9') {
962 54704 : n = c - '0';
963 191467 : while (i < limit && '0' <= (c = s[i]) && c <= '9') {
964 82059 : n = n * 10 + c - '0';
965 82059 : i++;
966 : }
967 :
968 : /* allow TZA before the year, so
969 : * 'Wed Nov 05 21:49:11 GMT-0800 1997'
970 : * works */
971 :
972 : /* uses of seenplusminus allow : in TZA, so Java
973 : * no-timezone style of GMT+4:30 works
974 : */
975 :
976 54704 : if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
977 : /* make ':' case below change tzoffset */
978 9051 : seenplusminus = JS_TRUE;
979 :
980 : /* offset */
981 9051 : if (n < 24)
982 9007 : n = n * 60; /* EG. "GMT-3" */
983 : else
984 44 : n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
985 9051 : if (prevc == '+') /* plus means east of GMT */
986 9001 : n = -n;
987 9051 : if (tzoffset != 0 && tzoffset != -1)
988 3 : goto syntax;
989 9048 : tzoffset = n;
990 45653 : } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
991 186 : if (c <= ' ' || c == ',' || c == '/' || i >= limit)
992 93 : year = n;
993 : else
994 : goto syntax;
995 45560 : } else if (c == ':') {
996 18158 : if (hour < 0)
997 9079 : hour = /*byte*/ n;
998 9079 : else if (min < 0)
999 9079 : min = /*byte*/ n;
1000 : else
1001 0 : goto syntax;
1002 27402 : } else if (c == '/') {
1003 : /* until it is determined that mon is the actual
1004 : month, keep it as 1-based rather than 0-based */
1005 186 : if (mon < 0)
1006 93 : mon = /*byte*/ n;
1007 93 : else if (mday < 0)
1008 93 : mday = /*byte*/ n;
1009 : else
1010 0 : goto syntax;
1011 27216 : } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
1012 : goto syntax;
1013 27215 : } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
1014 2 : if (tzoffset < 0)
1015 0 : tzoffset -= n;
1016 : else
1017 1 : tzoffset += n;
1018 27214 : } else if (hour >= 0 && min < 0) {
1019 0 : min = /*byte*/ n;
1020 27214 : } else if (prevc == ':' && min >= 0 && sec < 0) {
1021 9079 : sec = /*byte*/ n;
1022 18135 : } else if (mon < 0) {
1023 22 : mon = /*byte*/n;
1024 18113 : } else if (mon >= 0 && mday < 0) {
1025 9047 : mday = /*byte*/ n;
1026 9066 : } else if (mon >= 0 && mday >= 0 && year < 0) {
1027 9066 : year = n;
1028 : } else {
1029 : goto syntax;
1030 : }
1031 54700 : prevc = 0;
1032 36534 : } else if (c == '/' || c == ':' || c == '+' || c == '-') {
1033 27346 : prevc = c;
1034 : } else {
1035 9188 : size_t st = i - 1;
1036 : int k;
1037 72779 : while (i < limit) {
1038 63574 : c = s[i];
1039 63574 : if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1040 9171 : break;
1041 54403 : i++;
1042 : }
1043 9188 : if (i <= st + 1)
1044 0 : goto syntax;
1045 219686 : for (k = ArrayLength(wtb); --k >= 0;)
1046 210497 : if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
1047 9187 : int action = ttb[k];
1048 9187 : if (action != 0) {
1049 9126 : if (action < 0) {
1050 : /*
1051 : * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1052 : * 12:30, instead of blindly adding 12 if PM.
1053 : */
1054 0 : JS_ASSERT(action == -1 || action == -2);
1055 0 : if (hour > 12 || hour < 0) {
1056 : goto syntax;
1057 : } else {
1058 0 : if (action == -1 && hour == 12) { /* am */
1059 0 : hour = 0;
1060 0 : } else if (action == -2 && hour != 12) { /* pm */
1061 0 : hour += 12;
1062 : }
1063 : }
1064 9126 : } else if (action <= 13) { /* month! */
1065 : /* Adjust mon to be 1-based until the final values
1066 : for mon, mday and year are adjusted below */
1067 9066 : if (seenmonthname) {
1068 0 : goto syntax;
1069 : }
1070 9066 : seenmonthname = JS_TRUE;
1071 9066 : temp = /*byte*/ (action - 2) + 1;
1072 :
1073 9066 : if (mon < 0) {
1074 9047 : mon = temp;
1075 19 : } else if (mday < 0) {
1076 19 : mday = mon;
1077 19 : mon = temp;
1078 0 : } else if (year < 0) {
1079 0 : year = mon;
1080 0 : mon = temp;
1081 : } else {
1082 0 : goto syntax;
1083 : }
1084 : } else {
1085 60 : tzoffset = action - 10000;
1086 : }
1087 : }
1088 9187 : break;
1089 : }
1090 9188 : if (k < 0)
1091 1 : goto syntax;
1092 9187 : prevc = 0;
1093 : }
1094 : }
1095 9159 : if (year < 0 || mon < 0 || mday < 0)
1096 : goto syntax;
1097 : /*
1098 : Case 1. The input string contains an English month name.
1099 : The form of the string can be month f l, or f month l, or
1100 : f l month which each evaluate to the same date.
1101 : If f and l are both greater than or equal to 70, or
1102 : both less than 70, the date is invalid.
1103 : The year is taken to be the greater of the values f, l.
1104 : If the year is greater than or equal to 70 and less than 100,
1105 : it is considered to be the number of years after 1900.
1106 : Case 2. The input string is of the form "f/m/l" where f, m and l are
1107 : integers, e.g. 7/16/45.
1108 : Adjust the mon, mday and year values to achieve 100% MSIE
1109 : compatibility.
1110 : a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1111 : i. If year < 100, it is the number of years after 1900
1112 : ii. If year >= 100, it is the number of years after 0.
1113 : b. If 70 <= f < 100
1114 : i. If m < 70, f/m/l is interpreted as
1115 : year/month/day where year is the number of years after
1116 : 1900.
1117 : ii. If m >= 70, the date is invalid.
1118 : c. If f >= 100
1119 : i. If m < 70, f/m/l is interpreted as
1120 : year/month/day where year is the number of years after 0.
1121 : ii. If m >= 70, the date is invalid.
1122 : */
1123 9159 : if (seenmonthname) {
1124 9066 : if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
1125 : goto syntax;
1126 : }
1127 9066 : if (mday > year) {
1128 0 : temp = year;
1129 0 : year = mday;
1130 0 : mday = temp;
1131 : }
1132 9066 : if (year >= 70 && year < 100) {
1133 0 : year += 1900;
1134 : }
1135 93 : } else if (mon < 70) { /* (a) month/day/year */
1136 18 : if (year < 100) {
1137 0 : year += 1900;
1138 : }
1139 75 : } else if (mon < 100) { /* (b) year/month/day */
1140 0 : if (mday < 70) {
1141 0 : temp = year;
1142 0 : year = mon + 1900;
1143 0 : mon = mday;
1144 0 : mday = temp;
1145 : } else {
1146 0 : goto syntax;
1147 : }
1148 : } else { /* (c) year/month/day */
1149 75 : if (mday < 70) {
1150 75 : temp = year;
1151 75 : year = mon;
1152 75 : mon = mday;
1153 75 : mday = temp;
1154 : } else {
1155 0 : goto syntax;
1156 : }
1157 : }
1158 9159 : mon -= 1; /* convert month to 0-based */
1159 9159 : if (sec < 0)
1160 80 : sec = 0;
1161 9159 : if (min < 0)
1162 80 : min = 0;
1163 9159 : if (hour < 0)
1164 80 : hour = 0;
1165 :
1166 9159 : msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1167 :
1168 9159 : if (tzoffset == -1) { /* no time zone specified, have to use local */
1169 97 : msec = UTC(msec, cx);
1170 : } else {
1171 9062 : msec += tzoffset * msPerMinute;
1172 : }
1173 :
1174 9159 : *result = msec;
1175 9159 : return JS_TRUE;
1176 :
1177 : syntax:
1178 : /* syntax error */
1179 5 : *result = 0;
1180 5 : return JS_FALSE;
1181 : }
1182 :
1183 : static JSBool
1184 43 : date_parse(JSContext *cx, unsigned argc, Value *vp)
1185 : {
1186 : JSString *str;
1187 : double result;
1188 :
1189 43 : if (argc == 0) {
1190 0 : vp->setDouble(js_NaN);
1191 0 : return true;
1192 : }
1193 43 : str = ToString(cx, vp[2]);
1194 43 : if (!str)
1195 0 : return JS_FALSE;
1196 43 : vp[2].setString(str);
1197 43 : JSLinearString *linearStr = str->ensureLinear(cx);
1198 43 : if (!linearStr)
1199 0 : return false;
1200 :
1201 43 : if (!date_parseString(linearStr, &result, cx)) {
1202 0 : vp->setDouble(js_NaN);
1203 0 : return true;
1204 : }
1205 :
1206 43 : result = TIMECLIP(result);
1207 43 : vp->setNumber(result);
1208 43 : return true;
1209 : }
1210 :
1211 : static inline double
1212 37506 : NowAsMillis()
1213 : {
1214 37506 : return (double) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1215 : }
1216 :
1217 : static JSBool
1218 35483 : date_now(JSContext *cx, unsigned argc, Value *vp)
1219 : {
1220 35483 : vp->setDouble(NowAsMillis());
1221 35483 : return JS_TRUE;
1222 : }
1223 :
1224 : /*
1225 : * Set UTC time to a given time and invalidate cached local time.
1226 : */
1227 : static JSBool
1228 58881 : SetUTCTime(JSContext *cx, JSObject *obj, double t, Value *vp = NULL)
1229 : {
1230 58881 : JS_ASSERT(obj->isDate());
1231 :
1232 529929 : for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
1233 : ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
1234 : ind++) {
1235 471048 : obj->setSlot(ind, UndefinedValue());
1236 : }
1237 :
1238 58881 : obj->setDateUTCTime(DoubleValue(t));
1239 58881 : if (vp)
1240 41238 : vp->setDouble(t);
1241 58881 : return true;
1242 : }
1243 :
1244 : static void
1245 2810 : SetDateToNaN(JSContext *cx, JSObject *obj, Value *vp = NULL)
1246 : {
1247 2810 : double NaN = cx->runtime->NaNValue.getDoubleRef();
1248 2810 : SetUTCTime(cx, obj, NaN, vp);
1249 2810 : }
1250 :
1251 : /*
1252 : * Cache the local time, year, month, and so forth of the object.
1253 : * If UTC time is not finite (e.g., NaN), the local time
1254 : * slots will be set to the UTC time without conversion.
1255 : */
1256 : static bool
1257 53209 : FillLocalTimes(JSContext *cx, JSObject *obj)
1258 : {
1259 53209 : JS_ASSERT(obj->isDate());
1260 :
1261 53209 : double utcTime = obj->getDateUTCTime().toNumber();
1262 :
1263 53209 : if (!JSDOUBLE_IS_FINITE(utcTime)) {
1264 0 : for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
1265 : ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
1266 : ind++) {
1267 0 : obj->setSlot(ind, DoubleValue(utcTime));
1268 : }
1269 0 : return true;
1270 : }
1271 :
1272 53209 : double localTime = LocalTime(utcTime, cx);
1273 :
1274 53209 : obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_TIME, DoubleValue(localTime));
1275 :
1276 53209 : int year = (int) floor(localTime /(msPerDay*365.2425)) + 1970;
1277 53209 : double yearStartTime = (double) TimeFromYear(year);
1278 :
1279 : /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
1280 : int yearDays;
1281 53209 : if (yearStartTime > localTime) {
1282 9028 : year--;
1283 9028 : yearStartTime -= (msPerDay * DaysInYear(year));
1284 9028 : yearDays = DaysInYear(year);
1285 : } else {
1286 44181 : yearDays = DaysInYear(year);
1287 44181 : double nextStart = yearStartTime + (msPerDay * yearDays);
1288 44181 : if (nextStart <= localTime) {
1289 0 : year++;
1290 0 : yearStartTime = nextStart;
1291 0 : yearDays = DaysInYear(year);
1292 : }
1293 : }
1294 :
1295 53209 : obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR, Int32Value(year));
1296 :
1297 53209 : uint64_t yearTime = uint64_t(localTime - yearStartTime);
1298 53209 : int yearSeconds = uint32_t(yearTime / 1000);
1299 :
1300 53209 : int day = yearSeconds / int(SecondsPerDay);
1301 :
1302 53209 : int step = -1, next = 30;
1303 : int month;
1304 :
1305 : do {
1306 53209 : if (day <= next) {
1307 3712 : month = 0;
1308 3712 : break;
1309 : }
1310 49497 : step = next;
1311 49497 : next += ((yearDays == 366) ? 29 : 28);
1312 49497 : if (day <= next) {
1313 3406 : month = 1;
1314 3406 : break;
1315 : }
1316 46091 : step = next;
1317 46091 : if (day <= (next += 31)) {
1318 3730 : month = 2;
1319 3730 : break;
1320 : }
1321 42361 : step = next;
1322 42361 : if (day <= (next += 30)) {
1323 3595 : month = 3;
1324 3595 : break;
1325 : }
1326 38766 : step = next;
1327 38766 : if (day <= (next += 31)) {
1328 3450 : month = 4;
1329 3450 : break;
1330 : }
1331 35316 : step = next;
1332 35316 : if (day <= (next += 30)) {
1333 6984 : month = 5;
1334 6984 : break;
1335 : }
1336 28332 : step = next;
1337 28332 : if (day <= (next += 31)) {
1338 3447 : month = 6;
1339 3447 : break;
1340 : }
1341 24885 : step = next;
1342 24885 : if (day <= (next += 31)) {
1343 3447 : month = 7;
1344 3447 : break;
1345 : }
1346 21438 : step = next;
1347 21438 : if (day <= (next += 30)) {
1348 3078 : month = 8;
1349 3078 : break;
1350 : }
1351 18360 : step = next;
1352 18360 : if (day <= (next += 31)) {
1353 3141 : month = 9;
1354 3141 : break;
1355 : }
1356 15219 : step = next;
1357 15219 : if (day <= (next += 30)) {
1358 3051 : month = 10;
1359 3051 : break;
1360 : }
1361 12168 : step = next;
1362 12168 : month = 11;
1363 : } while (0);
1364 :
1365 53209 : obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH, Int32Value(month));
1366 53209 : obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DATE, Int32Value(day - step));
1367 :
1368 53209 : int weekday = WeekDay(localTime);
1369 :
1370 53209 : obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DAY, Int32Value(weekday));
1371 :
1372 53209 : int seconds = yearSeconds % 60;
1373 :
1374 53209 : obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS, Int32Value(seconds));
1375 :
1376 53209 : int minutes = (yearSeconds / 60) % 60;
1377 :
1378 53209 : obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES, Int32Value(minutes));
1379 :
1380 53209 : int hours = (yearSeconds / (60 * 60)) % 24;
1381 :
1382 53209 : obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS, Int32Value(hours));
1383 :
1384 53209 : return true;
1385 : }
1386 :
1387 : /* Cache the local times in obj, if necessary. */
1388 : static inline bool
1389 518254 : GetAndCacheLocalTime(JSContext *cx, JSObject *obj)
1390 : {
1391 518254 : JS_ASSERT(obj->isDate());
1392 :
1393 : /* If the local time is undefined, we need to fill in the cached values. */
1394 518254 : if (obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).isUndefined()) {
1395 53209 : if (!FillLocalTimes(cx, obj))
1396 0 : return false;
1397 : }
1398 518254 : return true;
1399 : }
1400 :
1401 : static inline bool
1402 115 : GetAndCacheLocalTime(JSContext *cx, JSObject *obj, double *time)
1403 : {
1404 115 : if (!obj || !GetAndCacheLocalTime(cx, obj))
1405 0 : return false;
1406 :
1407 115 : *time = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).toDouble();
1408 115 : return true;
1409 : }
1410 :
1411 : /*
1412 : * See ECMA 15.9.5.4 thru 15.9.5.23
1413 : */
1414 : static JSBool
1415 41728 : date_getTime(JSContext *cx, unsigned argc, Value *vp)
1416 : {
1417 41728 : CallArgs args = CallArgsFromVp(argc, vp);
1418 :
1419 : bool ok;
1420 41728 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getTime, &DateClass, &ok);
1421 41728 : if (!obj)
1422 63 : return ok;
1423 :
1424 41665 : args.rval() = obj->getDateUTCTime();
1425 41665 : return true;
1426 : }
1427 :
1428 : static JSBool
1429 9090 : date_getYear(JSContext *cx, unsigned argc, Value *vp)
1430 : {
1431 9090 : CallArgs args = CallArgsFromVp(argc, vp);
1432 :
1433 : bool ok;
1434 9090 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getYear, &DateClass, &ok);
1435 9090 : if (!obj)
1436 63 : return ok;
1437 :
1438 9027 : if (!GetAndCacheLocalTime(cx, obj))
1439 0 : return false;
1440 :
1441 9027 : Value yearVal = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
1442 9027 : if (yearVal.isInt32()) {
1443 : /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1444 9027 : int year = yearVal.toInt32() - 1900;
1445 9027 : args.rval().setInt32(year);
1446 : } else {
1447 0 : args.rval() = yearVal;
1448 : }
1449 :
1450 9027 : return true;
1451 : }
1452 :
1453 : static JSBool
1454 81118 : date_getFullYear(JSContext *cx, unsigned argc, Value *vp)
1455 : {
1456 81118 : CallArgs args = CallArgsFromVp(argc, vp);
1457 :
1458 : bool ok;
1459 81118 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getFullYear, &DateClass, &ok);
1460 81118 : if (!obj)
1461 63 : return ok;
1462 :
1463 81055 : if (!GetAndCacheLocalTime(cx, obj))
1464 0 : return false;
1465 :
1466 81055 : args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
1467 81055 : return true;
1468 : }
1469 :
1470 : static JSBool
1471 3422 : date_getUTCFullYear(JSContext *cx, unsigned argc, Value *vp)
1472 : {
1473 3422 : CallArgs args = CallArgsFromVp(argc, vp);
1474 :
1475 : bool ok;
1476 3422 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCFullYear, &DateClass, &ok);
1477 3422 : if (!obj)
1478 63 : return ok;
1479 :
1480 3359 : double result = obj->getDateUTCTime().toNumber();
1481 3359 : if (JSDOUBLE_IS_FINITE(result))
1482 3359 : result = YearFromTime(result);
1483 :
1484 3359 : args.rval().setNumber(result);
1485 3359 : return true;
1486 : }
1487 :
1488 : static JSBool
1489 85612 : date_getMonth(JSContext *cx, unsigned argc, Value *vp)
1490 : {
1491 85612 : CallArgs args = CallArgsFromVp(argc, vp);
1492 :
1493 : bool ok;
1494 85612 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getMonth, &DateClass, &ok);
1495 85612 : if (!obj)
1496 63 : return ok;
1497 :
1498 85549 : if (!GetAndCacheLocalTime(cx, obj))
1499 0 : return false;
1500 :
1501 85549 : args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH);
1502 85549 : return true;
1503 : }
1504 :
1505 : static JSBool
1506 3422 : date_getUTCMonth(JSContext *cx, unsigned argc, Value *vp)
1507 : {
1508 3422 : CallArgs args = CallArgsFromVp(argc, vp);
1509 :
1510 : bool ok;
1511 3422 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMonth, &DateClass, &ok);
1512 3422 : if (!obj)
1513 63 : return ok;
1514 :
1515 3359 : double result = obj->getDateUTCTime().toNumber();
1516 3359 : if (JSDOUBLE_IS_FINITE(result))
1517 3359 : result = MonthFromTime(result);
1518 :
1519 3359 : args.rval().setNumber(result);
1520 3359 : return true;
1521 : }
1522 :
1523 : static JSBool
1524 90107 : date_getDate(JSContext *cx, unsigned argc, Value *vp)
1525 : {
1526 90107 : CallArgs args = CallArgsFromVp(argc, vp);
1527 :
1528 : bool ok;
1529 90107 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getDate, &DateClass, &ok);
1530 90107 : if (!obj)
1531 63 : return ok;
1532 :
1533 90044 : if (!GetAndCacheLocalTime(cx, obj))
1534 0 : return false;
1535 :
1536 90044 : args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE);
1537 90044 : return true;
1538 : }
1539 :
1540 : static JSBool
1541 3422 : date_getUTCDate(JSContext *cx, unsigned argc, Value *vp)
1542 : {
1543 3422 : CallArgs args = CallArgsFromVp(argc, vp);
1544 :
1545 : bool ok;
1546 3422 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCDate, &DateClass, &ok);
1547 3422 : if (!obj)
1548 63 : return ok;
1549 :
1550 3359 : double result = obj->getDateUTCTime().toNumber();
1551 3359 : if (JSDOUBLE_IS_FINITE(result))
1552 3359 : result = DateFromTime(result);
1553 :
1554 3359 : args.rval().setNumber(result);
1555 3359 : return true;
1556 : }
1557 :
1558 : static JSBool
1559 40590 : date_getDay(JSContext *cx, unsigned argc, Value *vp)
1560 : {
1561 40590 : CallArgs args = CallArgsFromVp(argc, vp);
1562 :
1563 : bool ok;
1564 40590 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getDay, &DateClass, &ok);
1565 40590 : if (!obj)
1566 63 : return ok;
1567 :
1568 40527 : if (!GetAndCacheLocalTime(cx, obj))
1569 0 : return false;
1570 :
1571 40527 : args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY);
1572 40527 : return true;
1573 : }
1574 :
1575 : static JSBool
1576 3422 : date_getUTCDay(JSContext *cx, unsigned argc, Value *vp)
1577 : {
1578 3422 : CallArgs args = CallArgsFromVp(argc, vp);
1579 :
1580 : bool ok;
1581 3422 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCDay, &DateClass, &ok);
1582 3422 : if (!obj)
1583 63 : return ok;
1584 :
1585 3359 : double result = obj->getDateUTCTime().toNumber();
1586 3359 : if (JSDOUBLE_IS_FINITE(result))
1587 3359 : result = WeekDay(result);
1588 :
1589 3359 : args.rval().setNumber(result);
1590 3359 : return true;
1591 : }
1592 :
1593 : static JSBool
1594 118577 : date_getHours(JSContext *cx, unsigned argc, Value *vp)
1595 : {
1596 118577 : CallArgs args = CallArgsFromVp(argc, vp);
1597 :
1598 : bool ok;
1599 118577 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getHours, &DateClass, &ok);
1600 118577 : if (!obj)
1601 63 : return ok;
1602 :
1603 118514 : if (!GetAndCacheLocalTime(cx, obj))
1604 0 : return false;
1605 :
1606 118514 : args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS);
1607 118514 : return true;
1608 : }
1609 :
1610 : static JSBool
1611 3422 : date_getUTCHours(JSContext *cx, unsigned argc, Value *vp)
1612 : {
1613 3422 : CallArgs args = CallArgsFromVp(argc, vp);
1614 :
1615 : bool ok;
1616 3422 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCHours, &DateClass, &ok);
1617 3422 : if (!obj)
1618 63 : return ok;
1619 :
1620 3359 : double result = obj->getDateUTCTime().toNumber();
1621 3359 : if (JSDOUBLE_IS_FINITE(result))
1622 3359 : result = HourFromTime(result);
1623 :
1624 3359 : args.rval().setNumber(result);
1625 3359 : return JS_TRUE;
1626 : }
1627 :
1628 : static JSBool
1629 45100 : date_getMinutes(JSContext *cx, unsigned argc, Value *vp)
1630 : {
1631 45100 : CallArgs args = CallArgsFromVp(argc, vp);
1632 :
1633 : bool ok;
1634 45100 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getMinutes, &DateClass, &ok);
1635 45100 : if (!obj)
1636 63 : return ok;
1637 :
1638 45037 : if (!GetAndCacheLocalTime(cx, obj))
1639 0 : return false;
1640 :
1641 45037 : args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES);
1642 45037 : return true;
1643 : }
1644 :
1645 : static JSBool
1646 3422 : date_getUTCMinutes(JSContext *cx, unsigned argc, Value *vp)
1647 : {
1648 3422 : CallArgs args = CallArgsFromVp(argc, vp);
1649 :
1650 : bool ok;
1651 3422 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMinutes, &DateClass, &ok);
1652 3422 : if (!obj)
1653 63 : return ok;
1654 :
1655 3359 : double result = obj->getDateUTCTime().toNumber();
1656 3359 : if (JSDOUBLE_IS_FINITE(result))
1657 3359 : result = MinFromTime(result);
1658 :
1659 3359 : args.rval().setNumber(result);
1660 3359 : return true;
1661 : }
1662 :
1663 : /* Date.getSeconds is mapped to getUTCSeconds */
1664 :
1665 : static JSBool
1666 48512 : date_getUTCSeconds(JSContext *cx, unsigned argc, Value *vp)
1667 : {
1668 48512 : CallArgs args = CallArgsFromVp(argc, vp);
1669 :
1670 : bool ok;
1671 48512 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCSeconds, &DateClass, &ok);
1672 48512 : if (!obj)
1673 126 : return ok;
1674 :
1675 48386 : if (!GetAndCacheLocalTime(cx, obj))
1676 0 : return false;
1677 :
1678 48386 : args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS);
1679 48386 : return true;
1680 : }
1681 :
1682 : /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1683 :
1684 : static JSBool
1685 0 : date_getUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp)
1686 : {
1687 0 : CallArgs args = CallArgsFromVp(argc, vp);
1688 :
1689 : bool ok;
1690 0 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMilliseconds, &DateClass, &ok);
1691 0 : if (!obj)
1692 0 : return ok;
1693 :
1694 0 : double result = obj->getDateUTCTime().toNumber();
1695 0 : if (JSDOUBLE_IS_FINITE(result))
1696 0 : result = msFromTime(result);
1697 :
1698 0 : args.rval().setNumber(result);
1699 0 : return true;
1700 : }
1701 :
1702 : static JSBool
1703 178 : date_getTimezoneOffset(JSContext *cx, unsigned argc, Value *vp)
1704 : {
1705 178 : CallArgs args = CallArgsFromVp(argc, vp);
1706 :
1707 : bool ok;
1708 178 : JSObject *obj = NonGenericMethodGuard(cx, args, date_getTimezoneOffset, &DateClass, &ok);
1709 178 : if (!obj)
1710 63 : return ok;
1711 :
1712 115 : double utctime = obj->getDateUTCTime().toNumber();
1713 :
1714 : double localtime;
1715 115 : if (!GetAndCacheLocalTime(cx, obj, &localtime))
1716 0 : return false;
1717 :
1718 : /*
1719 : * Return the time zone offset in minutes for the current locale that is
1720 : * appropriate for this time. This value would be a constant except for
1721 : * daylight savings time.
1722 : */
1723 115 : double result = (utctime - localtime) / msPerMinute;
1724 115 : args.rval().setNumber(result);
1725 115 : return true;
1726 : }
1727 :
1728 : static JSBool
1729 40590 : date_setTime(JSContext *cx, unsigned argc, Value *vp)
1730 : {
1731 40590 : CallArgs args = CallArgsFromVp(argc, vp);
1732 :
1733 : bool ok;
1734 40590 : JSObject *obj = NonGenericMethodGuard(cx, args, date_setTime, &DateClass, &ok);
1735 40590 : if (!obj)
1736 63 : return ok;
1737 :
1738 40527 : if (args.length() == 0) {
1739 27 : SetDateToNaN(cx, obj, &args.rval());
1740 27 : return true;
1741 : }
1742 :
1743 : double result;
1744 40500 : if (!ToNumber(cx, args[0], &result))
1745 0 : return false;
1746 :
1747 40500 : return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
1748 : }
1749 :
1750 : static JSBool
1751 1013 : date_makeTime(JSContext *cx, Native native, unsigned maxargs, JSBool local, unsigned argc, Value *vp)
1752 : {
1753 1013 : CallArgs args = CallArgsFromVp(argc, vp);
1754 :
1755 : bool ok;
1756 1013 : JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
1757 1013 : if (!obj)
1758 504 : return ok;
1759 :
1760 509 : double result = obj->getDateUTCTime().toNumber();
1761 :
1762 : /* just return NaN if the date is already NaN */
1763 509 : if (!JSDOUBLE_IS_FINITE(result)) {
1764 0 : args.rval().setNumber(result);
1765 0 : return true;
1766 : }
1767 :
1768 : /*
1769 : * Satisfy the ECMA rule that if a function is called with
1770 : * fewer arguments than the specified formal arguments, the
1771 : * remaining arguments are set to undefined. Seems like all
1772 : * the Date.setWhatever functions in ECMA are only varargs
1773 : * beyond the first argument; this should be set to undefined
1774 : * if it's not given. This means that "d = new Date();
1775 : * d.setMilliseconds()" returns NaN. Blech.
1776 : */
1777 509 : if (args.length() == 0) {
1778 216 : SetDateToNaN(cx, obj, &args.rval());
1779 216 : return true;
1780 : }
1781 :
1782 293 : unsigned numNums = Min(args.length(), maxargs);
1783 293 : JS_ASSERT(numNums <= 4);
1784 : double nums[4];
1785 586 : for (unsigned i = 0; i < numNums; i++) {
1786 293 : if (!ToNumber(cx, args[i], &nums[i]))
1787 0 : return false;
1788 293 : if (!JSDOUBLE_IS_FINITE(nums[i])) {
1789 0 : SetDateToNaN(cx, obj, &args.rval());
1790 0 : return true;
1791 : }
1792 293 : nums[i] = js_DoubleToInteger(nums[i]);
1793 : }
1794 :
1795 : double lorutime; /* Local or UTC version of *date */
1796 293 : if (local)
1797 293 : lorutime = LocalTime(result, cx);
1798 : else
1799 0 : lorutime = result;
1800 :
1801 293 : double *argp = nums;
1802 293 : double *stop = argp + numNums;
1803 : double hour;
1804 293 : if (maxargs >= 4 && argp < stop)
1805 73 : hour = *argp++;
1806 : else
1807 220 : hour = HourFromTime(lorutime);
1808 :
1809 : double min;
1810 293 : if (maxargs >= 3 && argp < stop)
1811 72 : min = *argp++;
1812 : else
1813 221 : min = MinFromTime(lorutime);
1814 :
1815 : double sec;
1816 293 : if (maxargs >= 2 && argp < stop)
1817 72 : sec = *argp++;
1818 : else
1819 221 : sec = SecFromTime(lorutime);
1820 :
1821 : double msec;
1822 293 : if (maxargs >= 1 && argp < stop)
1823 76 : msec = *argp;
1824 : else
1825 217 : msec = msFromTime(lorutime);
1826 :
1827 293 : double msec_time = MakeTime(hour, min, sec, msec);
1828 293 : result = MakeDate(Day(lorutime), msec_time);
1829 :
1830 293 : if (local)
1831 293 : result = UTC(result, cx);
1832 :
1833 293 : return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
1834 : }
1835 :
1836 : static JSBool
1837 166 : date_setMilliseconds(JSContext *cx, unsigned argc, Value *vp)
1838 : {
1839 166 : return date_makeTime(cx, date_setMilliseconds, 1, JS_TRUE, argc, vp);
1840 : }
1841 :
1842 : static JSBool
1843 90 : date_setUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp)
1844 : {
1845 90 : return date_makeTime(cx, date_setUTCMilliseconds, 1, JS_FALSE, argc, vp);
1846 : }
1847 :
1848 : static JSBool
1849 162 : date_setSeconds(JSContext *cx, unsigned argc, Value *vp)
1850 : {
1851 162 : return date_makeTime(cx, date_setSeconds, 2, JS_TRUE, argc, vp);
1852 : }
1853 :
1854 : static JSBool
1855 90 : date_setUTCSeconds(JSContext *cx, unsigned argc, Value *vp)
1856 : {
1857 90 : return date_makeTime(cx, date_setUTCSeconds, 2, JS_FALSE, argc, vp);
1858 : }
1859 :
1860 : static JSBool
1861 162 : date_setMinutes(JSContext *cx, unsigned argc, Value *vp)
1862 : {
1863 162 : return date_makeTime(cx, date_setMinutes, 3, JS_TRUE, argc, vp);
1864 : }
1865 :
1866 : static JSBool
1867 90 : date_setUTCMinutes(JSContext *cx, unsigned argc, Value *vp)
1868 : {
1869 90 : return date_makeTime(cx, date_setUTCMinutes, 3, JS_FALSE, argc, vp);
1870 : }
1871 :
1872 : static JSBool
1873 163 : date_setHours(JSContext *cx, unsigned argc, Value *vp)
1874 : {
1875 163 : return date_makeTime(cx, date_setHours, 4, JS_TRUE, argc, vp);
1876 : }
1877 :
1878 : static JSBool
1879 90 : date_setUTCHours(JSContext *cx, unsigned argc, Value *vp)
1880 : {
1881 90 : return date_makeTime(cx, date_setUTCHours, 4, JS_FALSE, argc, vp);
1882 : }
1883 :
1884 : static JSBool
1885 552 : date_makeDate(JSContext *cx, Native native, unsigned maxargs, JSBool local, unsigned argc, Value *vp)
1886 : {
1887 552 : CallArgs args = CallArgsFromVp(argc, vp);
1888 :
1889 : bool ok;
1890 552 : JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
1891 552 : if (!obj)
1892 378 : return ok;
1893 :
1894 174 : double result = obj->getDateUTCTime().toNumber();
1895 :
1896 : /* see complaint about ECMA in date_MakeTime */
1897 174 : if (args.length() == 0) {
1898 162 : SetDateToNaN(cx, obj, &args.rval());
1899 162 : return true;
1900 : }
1901 :
1902 12 : unsigned numNums = Min(args.length(), maxargs);
1903 12 : JS_ASSERT(1 <= numNums && numNums <= 3);
1904 : double nums[3];
1905 24 : for (unsigned i = 0; i < numNums; i++) {
1906 12 : if (!ToNumber(cx, args[i], &nums[i]))
1907 0 : return JS_FALSE;
1908 12 : if (!JSDOUBLE_IS_FINITE(nums[i])) {
1909 0 : SetDateToNaN(cx, obj, &args.rval());
1910 0 : return true;
1911 : }
1912 12 : nums[i] = js_DoubleToInteger(nums[i]);
1913 : }
1914 :
1915 : /*
1916 : * return NaN if date is NaN and we're not setting the year, If we are, use
1917 : * 0 as the time.
1918 : */
1919 : double lorutime; /* local or UTC version of *date */
1920 12 : if (!(JSDOUBLE_IS_FINITE(result))) {
1921 0 : if (maxargs < 3) {
1922 0 : args.rval().setDouble(result);
1923 0 : return true;
1924 : }
1925 0 : lorutime = +0.;
1926 : } else {
1927 12 : lorutime = local ? LocalTime(result, cx) : result;
1928 : }
1929 :
1930 12 : double *argp = nums;
1931 12 : double *stop = argp + numNums;
1932 : double year;
1933 12 : if (maxargs >= 3 && argp < stop)
1934 0 : year = *argp++;
1935 : else
1936 12 : year = YearFromTime(lorutime);
1937 :
1938 : double month;
1939 12 : if (maxargs >= 2 && argp < stop)
1940 6 : month = *argp++;
1941 : else
1942 6 : month = MonthFromTime(lorutime);
1943 :
1944 : double day;
1945 12 : if (maxargs >= 1 && argp < stop)
1946 6 : day = *argp++;
1947 : else
1948 6 : day = DateFromTime(lorutime);
1949 :
1950 12 : day = MakeDay(year, month, day); /* day within year */
1951 12 : result = MakeDate(day, TimeWithinDay(lorutime));
1952 :
1953 12 : if (local)
1954 12 : result = UTC(result, cx);
1955 :
1956 12 : return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
1957 : }
1958 :
1959 : static JSBool
1960 96 : date_setDate(JSContext *cx, unsigned argc, Value *vp)
1961 : {
1962 96 : return date_makeDate(cx, date_setDate, 1, JS_TRUE, argc, vp);
1963 : }
1964 :
1965 : static JSBool
1966 90 : date_setUTCDate(JSContext *cx, unsigned argc, Value *vp)
1967 : {
1968 90 : return date_makeDate(cx, date_setUTCDate, 1, JS_FALSE, argc, vp);
1969 : }
1970 :
1971 : static JSBool
1972 96 : date_setMonth(JSContext *cx, unsigned argc, Value *vp)
1973 : {
1974 96 : return date_makeDate(cx, date_setMonth, 2, JS_TRUE, argc, vp);
1975 : }
1976 :
1977 : static JSBool
1978 90 : date_setUTCMonth(JSContext *cx, unsigned argc, Value *vp)
1979 : {
1980 90 : return date_makeDate(cx, date_setUTCMonth, 2, JS_FALSE, argc, vp);
1981 : }
1982 :
1983 : static JSBool
1984 90 : date_setFullYear(JSContext *cx, unsigned argc, Value *vp)
1985 : {
1986 90 : return date_makeDate(cx, date_setFullYear, 3, JS_TRUE, argc, vp);
1987 : }
1988 :
1989 : static JSBool
1990 90 : date_setUTCFullYear(JSContext *cx, unsigned argc, Value *vp)
1991 : {
1992 90 : return date_makeDate(cx, date_setUTCFullYear, 3, JS_FALSE, argc, vp);
1993 : }
1994 :
1995 : static JSBool
1996 91 : date_setYear(JSContext *cx, unsigned argc, Value *vp)
1997 : {
1998 91 : CallArgs args = CallArgsFromVp(argc, vp);
1999 :
2000 : bool ok;
2001 91 : JSObject *obj = NonGenericMethodGuard(cx, args, date_setYear, &DateClass, &ok);
2002 91 : if (!obj)
2003 63 : return ok;
2004 :
2005 28 : if (args.length() == 0) {
2006 : /* Call this only after verifying that obj.[[Class]] = "Date". */
2007 27 : SetDateToNaN(cx, obj, &args.rval());
2008 27 : return true;
2009 : }
2010 :
2011 1 : double result = obj->getDateUTCTime().toNumber();
2012 :
2013 : double year;
2014 1 : if (!ToNumber(cx, args[0], &year))
2015 0 : return false;
2016 1 : if (!JSDOUBLE_IS_FINITE(year)) {
2017 0 : SetDateToNaN(cx, obj, &args.rval());
2018 0 : return true;
2019 : }
2020 1 : year = js_DoubleToInteger(year);
2021 1 : if (year >= 0 && year <= 99)
2022 0 : year += 1900;
2023 :
2024 1 : double t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result, cx) : +0.0;
2025 1 : double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
2026 1 : result = MakeDate(day, TimeWithinDay(t));
2027 1 : result = UTC(result, cx);
2028 :
2029 1 : return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
2030 : }
2031 :
2032 : /* constants for toString, toUTCString */
2033 : static char js_NaN_date_str[] = "Invalid Date";
2034 : static const char* days[] =
2035 : {
2036 : "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
2037 : };
2038 : static const char* months[] =
2039 : {
2040 : "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2041 : };
2042 :
2043 :
2044 : // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2045 : // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
2046 : static void
2047 161 : print_gmt_string(char* buf, size_t size, double utctime)
2048 : {
2049 : JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
2050 161 : days[WeekDay(utctime)],
2051 : DateFromTime(utctime),
2052 161 : months[MonthFromTime(utctime)],
2053 : YearFromTime(utctime),
2054 : HourFromTime(utctime),
2055 : MinFromTime(utctime),
2056 483 : SecFromTime(utctime));
2057 161 : }
2058 :
2059 : static void
2060 90 : print_iso_string(char* buf, size_t size, double utctime)
2061 : {
2062 : JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2063 : YearFromTime(utctime),
2064 90 : MonthFromTime(utctime) + 1,
2065 : DateFromTime(utctime),
2066 : HourFromTime(utctime),
2067 : MinFromTime(utctime),
2068 : SecFromTime(utctime),
2069 180 : msFromTime(utctime));
2070 90 : }
2071 :
2072 : static JSBool
2073 386 : date_utc_format(JSContext *cx, Native native, CallArgs args,
2074 : void (*printFunc)(char*, size_t, double))
2075 : {
2076 : bool ok;
2077 386 : JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
2078 386 : if (!obj)
2079 126 : return ok;
2080 :
2081 260 : double utctime = obj->getDateUTCTime().toNumber();
2082 :
2083 : char buf[100];
2084 260 : if (!JSDOUBLE_IS_FINITE(utctime)) {
2085 9 : if (printFunc == print_iso_string) {
2086 9 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DATE);
2087 9 : return false;
2088 : }
2089 :
2090 0 : JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2091 : } else {
2092 251 : (*printFunc)(buf, sizeof buf, utctime);
2093 : }
2094 :
2095 251 : JSString *str = JS_NewStringCopyZ(cx, buf);
2096 251 : if (!str)
2097 0 : return false;
2098 251 : args.rval().setString(str);
2099 251 : return true;
2100 : }
2101 :
2102 : static JSBool
2103 224 : date_toGMTString(JSContext *cx, unsigned argc, Value *vp)
2104 : {
2105 224 : return date_utc_format(cx, date_toGMTString, CallArgsFromVp(argc, vp), print_gmt_string);
2106 : }
2107 :
2108 : static JSBool
2109 162 : date_toISOString(JSContext *cx, unsigned argc, Value *vp)
2110 : {
2111 162 : return date_utc_format(cx, date_toISOString, CallArgsFromVp(argc, vp), print_iso_string);
2112 : }
2113 :
2114 : /* ES5 15.9.5.44. */
2115 : static JSBool
2116 0 : date_toJSON(JSContext *cx, unsigned argc, Value *vp)
2117 : {
2118 : /* Step 1. */
2119 0 : JSObject *obj = ToObject(cx, &vp[1]);
2120 0 : if (!obj)
2121 0 : return false;
2122 :
2123 : /* Step 2. */
2124 0 : Value tv = ObjectValue(*obj);
2125 0 : if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv))
2126 0 : return false;
2127 :
2128 : /* Step 3. */
2129 0 : if (tv.isDouble() && !JSDOUBLE_IS_FINITE(tv.toDouble())) {
2130 0 : vp->setNull();
2131 0 : return true;
2132 : }
2133 :
2134 : /* Step 4. */
2135 0 : Value &toISO = vp[0];
2136 0 : if (!obj->getProperty(cx, cx->runtime->atomState.toISOStringAtom, &toISO))
2137 0 : return false;
2138 :
2139 : /* Step 5. */
2140 0 : if (!js_IsCallable(toISO)) {
2141 : JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
2142 0 : JSMSG_BAD_TOISOSTRING_PROP);
2143 0 : return false;
2144 : }
2145 :
2146 : /* Step 6. */
2147 0 : InvokeArgsGuard args;
2148 0 : if (!cx->stack.pushInvokeArgs(cx, 0, &args))
2149 0 : return false;
2150 :
2151 0 : args.calleev() = toISO;
2152 0 : args.thisv().setObject(*obj);
2153 :
2154 0 : if (!Invoke(cx, args))
2155 0 : return false;
2156 0 : *vp = args.rval();
2157 0 : return true;
2158 : }
2159 :
2160 : /* for Date.toLocaleString; interface to PRMJTime date struct.
2161 : */
2162 : static void
2163 733 : new_explode(double timeval, PRMJTime *split, JSContext *cx)
2164 : {
2165 733 : int year = YearFromTime(timeval);
2166 :
2167 733 : split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
2168 733 : split->tm_sec = int8_t(SecFromTime(timeval));
2169 733 : split->tm_min = int8_t(MinFromTime(timeval));
2170 733 : split->tm_hour = int8_t(HourFromTime(timeval));
2171 733 : split->tm_mday = int8_t(DateFromTime(timeval));
2172 733 : split->tm_mon = int8_t(MonthFromTime(timeval));
2173 733 : split->tm_wday = int8_t(WeekDay(timeval));
2174 733 : split->tm_year = year;
2175 733 : split->tm_yday = int16_t(DayWithinYear(timeval, year));
2176 :
2177 : /* not sure how this affects things, but it doesn't seem
2178 : to matter. */
2179 733 : split->tm_isdst = (DaylightSavingTA(timeval, cx) != 0);
2180 733 : }
2181 :
2182 : typedef enum formatspec {
2183 : FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
2184 : } formatspec;
2185 :
2186 : /* helper function */
2187 : static JSBool
2188 266 : date_format(JSContext *cx, double date, formatspec format, CallReceiver call)
2189 : {
2190 : char buf[100];
2191 : JSString *str;
2192 : char tzbuf[100];
2193 : JSBool usetz;
2194 : size_t i, tzlen;
2195 : PRMJTime split;
2196 :
2197 266 : if (!JSDOUBLE_IS_FINITE(date)) {
2198 0 : JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2199 : } else {
2200 266 : double local = LocalTime(date, cx);
2201 :
2202 : /* offset from GMT in minutes. The offset includes daylight savings,
2203 : if it applies. */
2204 266 : int minutes = (int) floor(AdjustTime(date, cx) / msPerMinute);
2205 :
2206 : /* map 510 minutes to 0830 hours */
2207 266 : int offset = (minutes / 60) * 100 + minutes % 60;
2208 :
2209 : /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
2210 : * printed as 'GMT-0800' rather than as 'PST' to avoid
2211 : * operating-system dependence on strftime (which
2212 : * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
2213 : * PST as 'Pacific Standard Time.' This way we always know
2214 : * what we're getting, and can parse it if we produce it.
2215 : * The OS TZA string is included as a comment.
2216 : */
2217 :
2218 : /* get a timezone string from the OS to include as a
2219 : comment. */
2220 266 : new_explode(date, &split, cx);
2221 266 : if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
2222 :
2223 : /* Decide whether to use the resulting timezone string.
2224 : *
2225 : * Reject it if it contains any non-ASCII, non-alphanumeric
2226 : * characters. It's then likely in some other character
2227 : * encoding, and we probably won't display it correctly.
2228 : */
2229 266 : usetz = JS_TRUE;
2230 266 : tzlen = strlen(tzbuf);
2231 266 : if (tzlen > 100) {
2232 0 : usetz = JS_FALSE;
2233 : } else {
2234 1596 : for (i = 0; i < tzlen; i++) {
2235 1330 : jschar c = tzbuf[i];
2236 2660 : if (c > 127 ||
2237 1862 : !(isalpha(c) || isdigit(c) ||
2238 532 : c == ' ' || c == '(' || c == ')')) {
2239 0 : usetz = JS_FALSE;
2240 : }
2241 : }
2242 : }
2243 :
2244 : /* Also reject it if it's not parenthesized or if it's '()'. */
2245 266 : if (tzbuf[0] != '(' || tzbuf[1] == ')')
2246 0 : usetz = JS_FALSE;
2247 : } else
2248 0 : usetz = JS_FALSE;
2249 :
2250 266 : switch (format) {
2251 : case FORMATSPEC_FULL:
2252 : /*
2253 : * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2254 : * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
2255 : */
2256 : /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2257 : JS_snprintf(buf, sizeof buf,
2258 : "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2259 212 : days[WeekDay(local)],
2260 212 : months[MonthFromTime(local)],
2261 : DateFromTime(local),
2262 : YearFromTime(local),
2263 : HourFromTime(local),
2264 : MinFromTime(local),
2265 : SecFromTime(local),
2266 : offset,
2267 : usetz ? " " : "",
2268 636 : usetz ? tzbuf : "");
2269 212 : break;
2270 : case FORMATSPEC_DATE:
2271 : /* Tue Oct 31 2000 */
2272 : JS_snprintf(buf, sizeof buf,
2273 : "%s %s %.2d %.4d",
2274 27 : days[WeekDay(local)],
2275 27 : months[MonthFromTime(local)],
2276 : DateFromTime(local),
2277 81 : YearFromTime(local));
2278 27 : break;
2279 : case FORMATSPEC_TIME:
2280 : /* 09:41:40 GMT-0800 (PST) */
2281 : JS_snprintf(buf, sizeof buf,
2282 : "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2283 : HourFromTime(local),
2284 : MinFromTime(local),
2285 : SecFromTime(local),
2286 : offset,
2287 : usetz ? " " : "",
2288 27 : usetz ? tzbuf : "");
2289 27 : break;
2290 : }
2291 : }
2292 :
2293 266 : str = JS_NewStringCopyZ(cx, buf);
2294 266 : if (!str)
2295 0 : return JS_FALSE;
2296 266 : call.rval().setString(str);
2297 266 : return JS_TRUE;
2298 : }
2299 :
2300 : static bool
2301 467 : ToLocaleHelper(JSContext *cx, CallReceiver call, JSObject *obj, const char *format)
2302 : {
2303 467 : double utctime = obj->getDateUTCTime().toNumber();
2304 :
2305 : char buf[100];
2306 467 : if (!JSDOUBLE_IS_FINITE(utctime)) {
2307 0 : JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2308 : } else {
2309 : int result_len;
2310 467 : double local = LocalTime(utctime, cx);
2311 : PRMJTime split;
2312 467 : new_explode(local, &split, cx);
2313 :
2314 : /* let PRMJTime format it. */
2315 467 : result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2316 :
2317 : /* If it failed, default to toString. */
2318 467 : if (result_len == 0)
2319 0 : return date_format(cx, utctime, FORMATSPEC_FULL, call);
2320 :
2321 : /* Hacked check against undesired 2-digit year 00/00/00 form. */
2322 494 : if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2323 : /* Format %x means use OS settings, which may have 2-digit yr, so
2324 : hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2325 27 : !isdigit(buf[result_len - 3]) &&
2326 0 : isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2327 : /* ...but not if starts with 4-digit year, like 2022/3/11. */
2328 0 : !(isdigit(buf[0]) && isdigit(buf[1]) &&
2329 0 : isdigit(buf[2]) && isdigit(buf[3]))) {
2330 0 : JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2331 0 : "%d", js_DateGetYear(cx, obj));
2332 : }
2333 :
2334 : }
2335 :
2336 467 : if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
2337 359 : return cx->localeCallbacks->localeToUnicode(cx, buf, &call.rval());
2338 :
2339 108 : JSString *str = JS_NewStringCopyZ(cx, buf);
2340 108 : if (!str)
2341 0 : return false;
2342 108 : call.rval().setString(str);
2343 108 : return true;
2344 : }
2345 :
2346 : /*
2347 : * NB: Because of NonGenericMethodGuard, the calling native return immediately
2348 : * after calling date_toLocaleHelper, even if it returns 'true'.
2349 : */
2350 : static JSBool
2351 360 : date_toLocaleHelper(JSContext *cx, unsigned argc, Value *vp, Native native, const char *format)
2352 : {
2353 360 : CallArgs args = CallArgsFromVp(argc, vp);
2354 :
2355 : bool ok;
2356 360 : JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
2357 360 : if (!obj)
2358 252 : return ok;
2359 :
2360 108 : return ToLocaleHelper(cx, args, obj, format);
2361 : }
2362 :
2363 : static JSBool
2364 180 : date_toLocaleStringHelper(JSContext *cx, Native native, unsigned argc, Value *vp)
2365 : {
2366 : /*
2367 : * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2368 : * with msvc; '%#c' requests that a full year be used in the result string.
2369 : */
2370 : return date_toLocaleHelper(cx, argc, vp, native,
2371 : #if defined(_WIN32) && !defined(__MWERKS__)
2372 : "%#c"
2373 : #else
2374 : "%c"
2375 : #endif
2376 180 : );
2377 : }
2378 :
2379 : static JSBool
2380 90 : date_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
2381 : {
2382 90 : return date_toLocaleStringHelper(cx, date_toLocaleString, argc, vp);
2383 : }
2384 :
2385 : static JSBool
2386 90 : date_toLocaleDateString(JSContext *cx, unsigned argc, Value *vp)
2387 : {
2388 : /*
2389 : * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
2390 : * with msvc; '%#x' requests that a full year be used in the result string.
2391 : */
2392 : return date_toLocaleHelper(cx, argc, vp, date_toLocaleDateString,
2393 : #if defined(_WIN32) && !defined(__MWERKS__)
2394 : "%#x"
2395 : #else
2396 : "%x"
2397 : #endif
2398 90 : );
2399 : }
2400 :
2401 : static JSBool
2402 90 : date_toLocaleTimeString(JSContext *cx, unsigned argc, Value *vp)
2403 : {
2404 90 : return date_toLocaleHelper(cx, argc, vp, date_toLocaleTimeString, "%X");
2405 : }
2406 :
2407 : static JSBool
2408 449 : date_toLocaleFormat(JSContext *cx, unsigned argc, Value *vp)
2409 : {
2410 449 : if (argc == 0)
2411 90 : return date_toLocaleStringHelper(cx, date_toLocaleFormat, argc, vp);
2412 :
2413 359 : CallArgs args = CallArgsFromVp(argc, vp);
2414 :
2415 : bool ok;
2416 359 : JSObject *obj = NonGenericMethodGuard(cx, args, date_toLocaleFormat, &DateClass, &ok);
2417 359 : if (!obj)
2418 0 : return ok;
2419 :
2420 359 : JSString *fmt = ToString(cx, args[0]);
2421 359 : if (!fmt)
2422 0 : return false;
2423 :
2424 359 : args[0].setString(fmt);
2425 718 : JSAutoByteString fmtbytes(cx, fmt);
2426 359 : if (!fmtbytes)
2427 0 : return false;
2428 :
2429 359 : return ToLocaleHelper(cx, args, obj, fmtbytes.ptr());
2430 : }
2431 :
2432 : static JSBool
2433 90 : date_toTimeString(JSContext *cx, unsigned argc, Value *vp)
2434 : {
2435 90 : CallArgs args = CallArgsFromVp(argc, vp);
2436 :
2437 : bool ok;
2438 90 : JSObject *obj = NonGenericMethodGuard(cx, args, date_toTimeString, &DateClass, &ok);
2439 90 : if (!obj)
2440 63 : return ok;
2441 :
2442 27 : return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_TIME, args);
2443 : }
2444 :
2445 : static JSBool
2446 90 : date_toDateString(JSContext *cx, unsigned argc, Value *vp)
2447 : {
2448 90 : CallArgs args = CallArgsFromVp(argc, vp);
2449 :
2450 : bool ok;
2451 90 : JSObject *obj = NonGenericMethodGuard(cx, args, date_toDateString, &DateClass, &ok);
2452 90 : if (!obj)
2453 63 : return ok;
2454 :
2455 27 : return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_DATE, args);
2456 : }
2457 :
2458 : #if JS_HAS_TOSOURCE
2459 : static JSBool
2460 92 : date_toSource(JSContext *cx, unsigned argc, Value *vp)
2461 : {
2462 92 : CallArgs args = CallArgsFromVp(argc, vp);
2463 :
2464 : bool ok;
2465 92 : JSObject *obj = NonGenericMethodGuard(cx, args, date_toSource, &DateClass, &ok);
2466 92 : if (!obj)
2467 63 : return ok;
2468 :
2469 58 : StringBuffer sb(cx);
2470 58 : if (!sb.append("(new Date(") || !NumberValueToStringBuffer(cx, obj->getDateUTCTime(), sb) ||
2471 29 : !sb.append("))"))
2472 : {
2473 0 : return false;
2474 : }
2475 :
2476 29 : JSString *str = sb.finishString();
2477 29 : if (!str)
2478 0 : return false;
2479 29 : args.rval().setString(str);
2480 29 : return true;
2481 : }
2482 : #endif
2483 :
2484 : static JSBool
2485 273 : date_toString(JSContext *cx, unsigned argc, Value *vp)
2486 : {
2487 273 : CallArgs args = CallArgsFromVp(argc, vp);
2488 :
2489 : bool ok;
2490 273 : JSObject *obj = NonGenericMethodGuard(cx, args, date_toString, &DateClass, &ok);
2491 273 : if (!obj)
2492 63 : return ok;
2493 :
2494 210 : return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_FULL, args);
2495 : }
2496 :
2497 : static JSBool
2498 536 : date_valueOf(JSContext *cx, unsigned argc, Value *vp)
2499 : {
2500 536 : CallArgs args = CallArgsFromVp(argc, vp);
2501 :
2502 : bool ok;
2503 536 : JSObject *obj = NonGenericMethodGuard(cx, args, date_valueOf, &DateClass, &ok);
2504 536 : if (!obj)
2505 63 : return ok;
2506 :
2507 : /* If called directly with no arguments, convert to a time number. */
2508 473 : if (argc == 0) {
2509 473 : args.rval() = obj->getDateUTCTime();
2510 473 : return true;
2511 : }
2512 :
2513 : /* Convert to number only if the hint was given, otherwise favor string. */
2514 0 : JSString *str = ToString(cx, args[0]);
2515 0 : if (!str)
2516 0 : return false;
2517 0 : JSLinearString *linear_str = str->ensureLinear(cx);
2518 0 : if (!linear_str)
2519 0 : return false;
2520 0 : JSAtom *number_str = cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER];
2521 0 : if (EqualStrings(linear_str, number_str)) {
2522 0 : args.rval() = obj->getDateUTCTime();
2523 0 : return true;
2524 : }
2525 0 : return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_FULL, args);
2526 : }
2527 :
2528 : static JSFunctionSpec date_static_methods[] = {
2529 : JS_FN("UTC", date_UTC, MAXARGS,0),
2530 : JS_FN("parse", date_parse, 1,0),
2531 : JS_FN("now", date_now, 0,0),
2532 : JS_FS_END
2533 : };
2534 :
2535 : static JSFunctionSpec date_methods[] = {
2536 : JS_FN("getTime", date_getTime, 0,0),
2537 : JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2538 : JS_FN("getYear", date_getYear, 0,0),
2539 : JS_FN("getFullYear", date_getFullYear, 0,0),
2540 : JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2541 : JS_FN("getMonth", date_getMonth, 0,0),
2542 : JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2543 : JS_FN("getDate", date_getDate, 0,0),
2544 : JS_FN("getUTCDate", date_getUTCDate, 0,0),
2545 : JS_FN("getDay", date_getDay, 0,0),
2546 : JS_FN("getUTCDay", date_getUTCDay, 0,0),
2547 : JS_FN("getHours", date_getHours, 0,0),
2548 : JS_FN("getUTCHours", date_getUTCHours, 0,0),
2549 : JS_FN("getMinutes", date_getMinutes, 0,0),
2550 : JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2551 : JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2552 : JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2553 : JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2554 : JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2555 : JS_FN("setTime", date_setTime, 1,0),
2556 : JS_FN("setYear", date_setYear, 1,0),
2557 : JS_FN("setFullYear", date_setFullYear, 3,0),
2558 : JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2559 : JS_FN("setMonth", date_setMonth, 2,0),
2560 : JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2561 : JS_FN("setDate", date_setDate, 1,0),
2562 : JS_FN("setUTCDate", date_setUTCDate, 1,0),
2563 : JS_FN("setHours", date_setHours, 4,0),
2564 : JS_FN("setUTCHours", date_setUTCHours, 4,0),
2565 : JS_FN("setMinutes", date_setMinutes, 3,0),
2566 : JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2567 : JS_FN("setSeconds", date_setSeconds, 2,0),
2568 : JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2569 : JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2570 : JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2571 : JS_FN("toUTCString", date_toGMTString, 0,0),
2572 : JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2573 : JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2574 : JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2575 : JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2576 : JS_FN("toDateString", date_toDateString, 0,0),
2577 : JS_FN("toTimeString", date_toTimeString, 0,0),
2578 : JS_FN("toISOString", date_toISOString, 0,0),
2579 : JS_FN(js_toJSON_str, date_toJSON, 1,0),
2580 : #if JS_HAS_TOSOURCE
2581 : JS_FN(js_toSource_str, date_toSource, 0,0),
2582 : #endif
2583 : JS_FN(js_toString_str, date_toString, 0,0),
2584 : JS_FN(js_valueOf_str, date_valueOf, 0,0),
2585 : JS_FS_END
2586 : };
2587 :
2588 : JSBool
2589 15209 : js_Date(JSContext *cx, unsigned argc, Value *vp)
2590 : {
2591 15209 : CallArgs args = CallArgsFromVp(argc, vp);
2592 :
2593 : /* Date called as function. */
2594 15209 : if (!IsConstructing(args))
2595 2 : return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args);
2596 :
2597 : /* Date called as constructor. */
2598 : double d;
2599 15207 : if (args.length() == 0) {
2600 2021 : d = NowAsMillis();
2601 13186 : } else if (args.length() == 1) {
2602 13141 : if (!args[0].isString()) {
2603 : /* the argument is a millisecond number */
2604 3883 : if (!ToNumber(cx, args[0], &d))
2605 0 : return false;
2606 3883 : d = TIMECLIP(d);
2607 : } else {
2608 : /* the argument is a string; parse it. */
2609 9258 : JSString *str = ToString(cx, args[0]);
2610 9258 : if (!str)
2611 0 : return false;
2612 9258 : args[0].setString(str);
2613 9258 : JSLinearString *linearStr = str->ensureLinear(cx);
2614 9258 : if (!linearStr)
2615 0 : return false;
2616 :
2617 9258 : if (!date_parseString(linearStr, &d, cx))
2618 5 : d = js_NaN;
2619 : else
2620 9253 : d = TIMECLIP(d);
2621 : }
2622 : } else {
2623 : double msec_time;
2624 45 : if (!date_msecFromArgs(cx, args, &msec_time))
2625 0 : return false;
2626 :
2627 45 : if (JSDOUBLE_IS_FINITE(msec_time)) {
2628 45 : msec_time = UTC(msec_time, cx);
2629 45 : msec_time = TIMECLIP(msec_time);
2630 : }
2631 45 : d = msec_time;
2632 : }
2633 :
2634 15207 : JSObject *obj = js_NewDateObjectMsec(cx, d);
2635 15207 : if (!obj)
2636 0 : return false;
2637 :
2638 15207 : args.rval().setObject(*obj);
2639 15207 : return true;
2640 : }
2641 :
2642 : JSObject *
2643 2378 : js_InitDateClass(JSContext *cx, JSObject *obj)
2644 : {
2645 2378 : JS_ASSERT(obj->isNative());
2646 :
2647 : /* Set the static LocalTZA. */
2648 2378 : LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2649 :
2650 2378 : GlobalObject *global = &obj->asGlobal();
2651 :
2652 2378 : JSObject *dateProto = global->createBlankPrototype(cx, &DateClass);
2653 2378 : if (!dateProto)
2654 0 : return NULL;
2655 2378 : SetDateToNaN(cx, dateProto);
2656 :
2657 : JSFunction *ctor = global->createConstructor(cx, js_Date, &DateClass,
2658 2378 : CLASS_ATOM(cx, Date), MAXARGS);
2659 2378 : if (!ctor)
2660 0 : return NULL;
2661 :
2662 2378 : if (!LinkConstructorAndPrototype(cx, ctor, dateProto))
2663 0 : return NULL;
2664 :
2665 2378 : if (!DefinePropertiesAndBrand(cx, ctor, NULL, date_static_methods))
2666 0 : return NULL;
2667 :
2668 : /*
2669 : * Define all Date.prototype.* functions, then brand for trace-jitted code.
2670 : * Date.prototype.toGMTString has the same initial value as
2671 : * Date.prototype.toUTCString.
2672 : */
2673 2378 : if (!JS_DefineFunctions(cx, dateProto, date_methods))
2674 0 : return NULL;
2675 : Value toUTCStringFun;
2676 2378 : jsid toUTCStringId = ATOM_TO_JSID(cx->runtime->atomState.toUTCStringAtom);
2677 2378 : jsid toGMTStringId = ATOM_TO_JSID(cx->runtime->atomState.toGMTStringAtom);
2678 4756 : if (!js_GetProperty(cx, dateProto, toUTCStringId, &toUTCStringFun) ||
2679 : !js_DefineProperty(cx, dateProto, toGMTStringId, &toUTCStringFun,
2680 2378 : JS_PropertyStub, JS_StrictPropertyStub, 0))
2681 : {
2682 0 : return NULL;
2683 : }
2684 :
2685 2378 : if (!DefineConstructorAndPrototype(cx, global, JSProto_Date, ctor, dateProto))
2686 0 : return NULL;
2687 :
2688 2378 : return dateProto;
2689 : }
2690 :
2691 : JS_FRIEND_API(JSObject *)
2692 15265 : js_NewDateObjectMsec(JSContext *cx, double msec_time)
2693 : {
2694 15265 : JSObject *obj = NewBuiltinClassInstance(cx, &DateClass);
2695 15265 : if (!obj)
2696 0 : return NULL;
2697 15265 : if (!SetUTCTime(cx, obj, msec_time))
2698 0 : return NULL;
2699 15265 : return obj;
2700 : }
2701 :
2702 : JS_FRIEND_API(JSObject *)
2703 0 : js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2704 : int hour, int min, int sec)
2705 : {
2706 : JSObject *obj;
2707 : double msec_time;
2708 :
2709 0 : JS_ASSERT(mon < 12);
2710 0 : msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2711 0 : obj = js_NewDateObjectMsec(cx, UTC(msec_time, cx));
2712 0 : return obj;
2713 : }
2714 :
2715 : JS_FRIEND_API(JSBool)
2716 5 : js_DateIsValid(JSContext *cx, JSObject* obj)
2717 : {
2718 5 : return obj->isDate() && !JSDOUBLE_IS_NaN(obj->getDateUTCTime().toNumber());
2719 : }
2720 :
2721 : JS_FRIEND_API(int)
2722 0 : js_DateGetYear(JSContext *cx, JSObject* obj)
2723 : {
2724 : double localtime;
2725 :
2726 : /* Preserve legacy API behavior of returning 0 for invalid dates. */
2727 0 : if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2728 0 : JSDOUBLE_IS_NaN(localtime)) {
2729 0 : return 0;
2730 : }
2731 :
2732 0 : return (int) YearFromTime(localtime);
2733 : }
2734 :
2735 : JS_FRIEND_API(int)
2736 0 : js_DateGetMonth(JSContext *cx, JSObject* obj)
2737 : {
2738 : double localtime;
2739 :
2740 0 : if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2741 0 : JSDOUBLE_IS_NaN(localtime)) {
2742 0 : return 0;
2743 : }
2744 :
2745 0 : return (int) MonthFromTime(localtime);
2746 : }
2747 :
2748 : JS_FRIEND_API(int)
2749 0 : js_DateGetDate(JSContext *cx, JSObject* obj)
2750 : {
2751 : double localtime;
2752 :
2753 0 : if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2754 0 : JSDOUBLE_IS_NaN(localtime)) {
2755 0 : return 0;
2756 : }
2757 :
2758 0 : return (int) DateFromTime(localtime);
2759 : }
2760 :
2761 : JS_FRIEND_API(int)
2762 0 : js_DateGetHours(JSContext *cx, JSObject* obj)
2763 : {
2764 : double localtime;
2765 :
2766 0 : if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2767 0 : JSDOUBLE_IS_NaN(localtime)) {
2768 0 : return 0;
2769 : }
2770 :
2771 0 : return (int) HourFromTime(localtime);
2772 : }
2773 :
2774 : JS_FRIEND_API(int)
2775 0 : js_DateGetMinutes(JSContext *cx, JSObject* obj)
2776 : {
2777 : double localtime;
2778 :
2779 0 : if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2780 0 : JSDOUBLE_IS_NaN(localtime)) {
2781 0 : return 0;
2782 : }
2783 :
2784 0 : return (int) MinFromTime(localtime);
2785 : }
2786 :
2787 : JS_FRIEND_API(int)
2788 0 : js_DateGetSeconds(JSContext *cx, JSObject* obj)
2789 : {
2790 0 : if (!obj->isDate())
2791 0 : return 0;
2792 :
2793 0 : double utctime = obj->getDateUTCTime().toNumber();
2794 0 : if (JSDOUBLE_IS_NaN(utctime))
2795 0 : return 0;
2796 0 : return (int) SecFromTime(utctime);
2797 : }
2798 :
2799 : JS_FRIEND_API(double)
2800 862 : js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2801 : {
2802 862 : return obj->isDate() ? obj->getDateUTCTime().toNumber() : 0;
2803 : }
2804 :
2805 : #ifdef JS_THREADSAFE
2806 : #include "prinrval.h"
2807 :
2808 : JS_FRIEND_API(uint32_t)
2809 18405 : js_IntervalNow()
2810 : {
2811 18405 : return uint32_t(PR_IntervalToMilliseconds(PR_IntervalNow()));
2812 : }
2813 :
2814 : #else /* !JS_THREADSAFE */
2815 :
2816 : JS_FRIEND_API(uint32_t)
2817 : js_IntervalNow()
2818 : {
2819 : return uint32_t(PRMJ_Now() / PRMJ_USEC_PER_MSEC);
2820 : }
2821 : #endif
|