Bug 550784 - [OOPP] Flash deadlocks during script evals that trigger focus related...
[mozilla-central.git] / js / src / jsdate.cpp
blobb5347e228b3e71349ed5af4da9962c16c8e2279f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
39 * ***** END LICENSE BLOCK ***** */
42 * JS date methods.
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."
51 * Frederick Brooks, 'The Second-System Effect'.
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 "jsstdint.h"
61 #include "jsprf.h"
62 #include "prmjtime.h"
63 #include "jsutil.h" /* Added by JSIFY */
64 #include "jsapi.h"
65 #include "jsversion.h"
66 #include "jsbuiltins.h"
67 #include "jscntxt.h"
68 #include "jsdate.h"
69 #include "jsinterp.h"
70 #include "jsnum.h"
71 #include "jsobj.h"
72 #include "jsstr.h"
75 * The JS 'Date' object is patterned after the Java 'Date' object.
76 * Here is an script:
78 * today = new Date();
80 * print(today.toLocaleString());
82 * weekDay = today.getDay();
85 * These Java (and ECMA-262) methods are supported:
87 * UTC
88 * getDate (getUTCDate)
89 * getDay (getUTCDay)
90 * getHours (getUTCHours)
91 * getMinutes (getUTCMinutes)
92 * getMonth (getUTCMonth)
93 * getSeconds (getUTCSeconds)
94 * getMilliseconds (getUTCMilliseconds)
95 * getTime
96 * getTimezoneOffset
97 * getYear
98 * getFullYear (getUTCFullYear)
99 * parse
100 * setDate (setUTCDate)
101 * setHours (setUTCHours)
102 * setMinutes (setUTCMinutes)
103 * setMonth (setUTCMonth)
104 * setSeconds (setUTCSeconds)
105 * setMilliseconds (setUTCMilliseconds)
106 * setTime
107 * setYear (setFullYear, setUTCFullYear)
108 * toGMTString (toUTCString)
109 * toLocaleString
110 * toString
113 * These Java methods are not supported
115 * setDay
116 * before
117 * after
118 * equals
119 * hashCode
123 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
124 * definition and reduce dependence on NSPR. NSPR is used to get the current
125 * time in milliseconds, the time zone offset, and the daylight savings time
126 * offset for a given time. NSPR is also used for Date.toLocaleString(), for
127 * locale-specific formatting, and to get a string representing the timezone.
128 * (Which turns out to be platform-dependent.)
130 * To do:
131 * (I did some performance tests by timing how long it took to run what
132 * I had of the js ECMA conformance tests.)
134 * - look at saving results across multiple calls to supporting
135 * functions; the toString functions compute some of the same values
136 * multiple times. Although - I took a quick stab at this, and I lost
137 * rather than gained. (Fractionally.) Hard to tell what compilers/processors
138 * are doing these days.
140 * - look at tweaking function return types to return double instead
141 * of int; this seems to make things run slightly faster sometimes.
142 * (though it could be architecture-dependent.) It'd be good to see
143 * how this does on win32. (Tried it on irix.) Types could use a
144 * general going-over.
148 * Supporting functions - ECMA 15.9.1.*
151 #define HalfTimeDomain 8.64e15
152 #define HoursPerDay 24.0
153 #define MinutesPerDay (HoursPerDay * MinutesPerHour)
154 #define MinutesPerHour 60.0
155 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
156 #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
157 #define SecondsPerMinute 60.0
159 #if defined(XP_WIN) || defined(XP_OS2)
160 /* Work around msvc double optimization bug by making these runtime values; if
161 * they're available at compile time, msvc optimizes division by them by
162 * computing the reciprocal and multiplying instead of dividing - this loses
163 * when the reciprocal isn't representable in a double.
165 static jsdouble msPerSecond = 1000.0;
166 static jsdouble msPerDay = SecondsPerDay * 1000.0;
167 static jsdouble msPerHour = SecondsPerHour * 1000.0;
168 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
169 #else
170 #define msPerDay (SecondsPerDay * msPerSecond)
171 #define msPerHour (SecondsPerHour * msPerSecond)
172 #define msPerMinute (SecondsPerMinute * msPerSecond)
173 #define msPerSecond 1000.0
174 #endif
176 #define Day(t) floor((t) / msPerDay)
178 static jsdouble
179 TimeWithinDay(jsdouble t)
181 jsdouble result;
182 result = fmod(t, msPerDay);
183 if (result < 0)
184 result += msPerDay;
185 return result;
188 #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
189 ? 366 : 365)
191 /* math here has to be f.p, because we need
192 * floor((1968 - 1969) / 4) == -1
194 #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
195 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
196 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
198 static jsint
199 YearFromTime(jsdouble t)
201 jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
202 jsdouble t2 = (jsdouble) TimeFromYear(y);
204 if (t2 > t) {
205 y--;
206 } else {
207 if (t2 + msPerDay * DaysInYear(y) <= t)
208 y++;
210 return y;
213 #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
215 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
218 * The following array contains the day of year for the first day of
219 * each month, where index 0 is January, and day 0 is January 1.
221 static jsdouble firstDayOfMonth[2][13] = {
222 {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},
223 {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}
226 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]
228 static intN
229 DaysInMonth(jsint year, jsint month)
231 JSBool leap = (DaysInYear(year) == 366);
232 intN result = intN(DayFromMonth(month, leap) - DayFromMonth(month-1, leap));
233 return result;
236 static intN
237 MonthFromTime(jsdouble t)
239 intN d, step;
240 jsint year = YearFromTime(t);
241 d = DayWithinYear(t, year);
243 if (d < (step = 31))
244 return 0;
245 step += (InLeapYear(t) ? 29 : 28);
246 if (d < step)
247 return 1;
248 if (d < (step += 31))
249 return 2;
250 if (d < (step += 30))
251 return 3;
252 if (d < (step += 31))
253 return 4;
254 if (d < (step += 30))
255 return 5;
256 if (d < (step += 31))
257 return 6;
258 if (d < (step += 31))
259 return 7;
260 if (d < (step += 30))
261 return 8;
262 if (d < (step += 31))
263 return 9;
264 if (d < (step += 30))
265 return 10;
266 return 11;
269 static intN
270 DateFromTime(jsdouble t)
272 intN d, step, next;
273 jsint year = YearFromTime(t);
274 d = DayWithinYear(t, year);
276 if (d <= (next = 30))
277 return d + 1;
278 step = next;
279 next += (InLeapYear(t) ? 29 : 28);
280 if (d <= next)
281 return d - step;
282 step = next;
283 if (d <= (next += 31))
284 return d - step;
285 step = next;
286 if (d <= (next += 30))
287 return d - step;
288 step = next;
289 if (d <= (next += 31))
290 return d - step;
291 step = next;
292 if (d <= (next += 30))
293 return d - step;
294 step = next;
295 if (d <= (next += 31))
296 return d - step;
297 step = next;
298 if (d <= (next += 31))
299 return d - step;
300 step = next;
301 if (d <= (next += 30))
302 return d - step;
303 step = next;
304 if (d <= (next += 31))
305 return d - step;
306 step = next;
307 if (d <= (next += 30))
308 return d - step;
309 step = next;
310 return d - step;
313 static intN
314 WeekDay(jsdouble t)
316 jsint result;
317 result = (jsint) Day(t) + 4;
318 result = result % 7;
319 if (result < 0)
320 result += 7;
321 return (intN) result;
324 #define MakeTime(hour, min, sec, ms) \
325 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
327 static jsdouble
328 MakeDay(jsdouble year, jsdouble month, jsdouble date)
330 JSBool leap;
331 jsdouble yearday;
332 jsdouble monthday;
334 year += floor(month / 12);
336 month = fmod(month, 12.0);
337 if (month < 0)
338 month += 12;
340 leap = (DaysInYear((jsint) year) == 366);
342 yearday = floor(TimeFromYear(year) / msPerDay);
343 monthday = DayFromMonth(month, leap);
345 return yearday + monthday + date - 1;
348 #define MakeDate(day, time) ((day) * msPerDay + (time))
351 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
353 * yearStartingWith[0][i] is an example non-leap year where
354 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
356 * yearStartingWith[1][i] is an example leap year where
357 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
359 static jsint yearStartingWith[2][7] = {
360 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
361 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
365 * Find a year for which any given date will fall on the same weekday.
367 * This function should be used with caution when used other than
368 * for determining DST; it hasn't been proven not to produce an
369 * incorrect year for times near year boundaries.
371 static jsint
372 EquivalentYearForDST(jsint year)
374 jsint day;
375 JSBool isLeapYear;
377 day = (jsint) DayFromYear(year) + 4;
378 day = day % 7;
379 if (day < 0)
380 day += 7;
382 isLeapYear = (DaysInYear(year) == 366);
384 return yearStartingWith[isLeapYear][day];
387 /* LocalTZA gets set by js_InitDateClass() */
388 static jsdouble LocalTZA;
390 static jsdouble
391 DaylightSavingTA(jsdouble t)
393 volatile int64 PR_t;
394 int64 ms2us;
395 int64 offset;
396 jsdouble result;
398 /* abort if NaN */
399 if (JSDOUBLE_IS_NaN(t))
400 return t;
403 * If earlier than 1970 or after 2038, potentially beyond the ken of
404 * many OSes, map it to an equivalent year before asking.
406 if (t < 0.0 || t > 2145916800000.0) {
407 jsint year;
408 jsdouble day;
410 year = EquivalentYearForDST(YearFromTime(t));
411 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
412 t = MakeDate(day, TimeWithinDay(t));
415 /* put our t in an LL, and map it to usec for prtime */
416 JSLL_D2L(PR_t, t);
417 JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
418 JSLL_MUL(PR_t, PR_t, ms2us);
420 offset = PRMJ_DSTOffset(PR_t);
422 JSLL_DIV(offset, offset, ms2us);
423 JSLL_L2D(result, offset);
424 return result;
427 static jsdouble
428 AdjustTime(jsdouble date)
430 jsdouble t = DaylightSavingTA(date) + LocalTZA;
431 t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
432 return t;
435 #define LocalTime(t) ((t) + AdjustTime(t))
437 static jsdouble
438 UTC(jsdouble t)
440 return t - AdjustTime(t - LocalTZA);
443 static intN
444 HourFromTime(jsdouble t)
446 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
447 if (result < 0)
448 result += (intN)HoursPerDay;
449 return result;
452 static intN
453 MinFromTime(jsdouble t)
455 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
456 if (result < 0)
457 result += (intN)MinutesPerHour;
458 return result;
461 static intN
462 SecFromTime(jsdouble t)
464 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
465 if (result < 0)
466 result += (intN)SecondsPerMinute;
467 return result;
470 static intN
471 msFromTime(jsdouble t)
473 intN result = (intN) fmod(t, msPerSecond);
474 if (result < 0)
475 result += (intN)msPerSecond;
476 return result;
479 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
480 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
481 ? js_DoubleToInteger(d + (+0.)) : js_NaN)
484 * end of ECMA 'support' functions
488 * Other Support routines and definitions
492 * We use the first reseved slot to store UTC time, and the second for caching
493 * the local time. The initial value of the cache entry is NaN.
495 const uint32 JSSLOT_UTC_TIME = JSSLOT_PRIVATE;
496 const uint32 JSSLOT_LOCAL_TIME = JSSLOT_PRIVATE + 1;
498 const uint32 DATE_RESERVED_SLOTS = 2;
500 JSClass js_DateClass = {
501 js_Date_str,
502 JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS) |
503 JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
504 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
505 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
506 JSCLASS_NO_OPTIONAL_MEMBERS
509 /* for use by date_parse */
511 static const char* wtb[] = {
512 "am", "pm",
513 "monday", "tuesday", "wednesday", "thursday", "friday",
514 "saturday", "sunday",
515 "january", "february", "march", "april", "may", "june",
516 "july", "august", "september", "october", "november", "december",
517 "gmt", "ut", "utc",
518 "est", "edt",
519 "cst", "cdt",
520 "mst", "mdt",
521 "pst", "pdt"
522 /* time zone table needs to be expanded */
525 static int ttb[] = {
526 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
527 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
528 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
529 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
530 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
531 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
532 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
535 /* helper for date_parse */
536 static JSBool
537 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
538 int count, int ignoreCase)
540 JSBool result = JS_FALSE;
541 /* return true if matches, otherwise, false */
543 while (count > 0 && s1[s1off] && s2[s2off]) {
544 if (ignoreCase) {
545 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
546 break;
548 } else {
549 if ((jschar)s1[s1off] != s2[s2off]) {
550 break;
553 s1off++;
554 s2off++;
555 count--;
558 if (count == 0) {
559 result = JS_TRUE;
562 return result;
565 /* find UTC time from given date... no 1900 correction! */
566 static jsdouble
567 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
568 jsdouble min, jsdouble sec, jsdouble msec)
570 jsdouble day;
571 jsdouble msec_time;
572 jsdouble result;
574 day = MakeDay(year, mon, mday);
575 msec_time = MakeTime(hour, min, sec, msec);
576 result = MakeDate(day, msec_time);
577 return result;
580 /* compute the time in msec (unclipped) from the given args */
581 #define MAXARGS 7
583 static JSBool
584 date_msecFromArgs(JSContext *cx, uintN argc, jsval *argv, jsdouble *rval)
586 uintN loop;
587 jsdouble array[MAXARGS];
588 jsdouble d;
589 jsdouble msec_time;
591 for (loop = 0; loop < MAXARGS; loop++) {
592 if (loop < argc) {
593 d = js_ValueToNumber(cx, &argv[loop]);
594 if (JSVAL_IS_NULL(argv[loop]))
595 return JS_FALSE;
596 /* return NaN if any arg is not finite */
597 if (!JSDOUBLE_IS_FINITE(d)) {
598 *rval = js_NaN;
599 return JS_TRUE;
601 array[loop] = js_DoubleToInteger(d);
602 } else {
603 if (loop == 2) {
604 array[loop] = 1; /* Default the date argument to 1. */
605 } else {
606 array[loop] = 0;
611 /* adjust 2-digit years into the 20th century */
612 if (array[0] >= 0 && array[0] <= 99)
613 array[0] += 1900;
615 msec_time = date_msecFromDate(array[0], array[1], array[2],
616 array[3], array[4], array[5], array[6]);
617 *rval = msec_time;
618 return JS_TRUE;
622 * See ECMA 15.9.4.[3-10];
624 static JSBool
625 date_UTC(JSContext *cx, uintN argc, jsval *vp)
627 jsdouble msec_time;
629 if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
630 return JS_FALSE;
632 msec_time = TIMECLIP(msec_time);
634 return js_NewNumberInRootedValue(cx, msec_time, vp);
638 * Read and convert decimal digits from s[*i] into *result
639 * while *i < limit.
641 * Succeed if any digits are converted. Advance *i only
642 * as digits are consumed.
644 static JSBool
645 digits(size_t *result, const jschar *s, size_t *i, size_t limit)
647 size_t init = *i;
648 *result = 0;
649 while (*i < limit &&
650 ('0' <= s[*i] && s[*i] <= '9')) {
651 *result *= 10;
652 *result += (s[*i] - '0');
653 ++(*i);
655 return (*i != init);
659 * Read and convert decimal digits to the right of a decimal point,
660 * representing a fractional integer, from s[*i] into *result
661 * while *i < limit.
663 * Succeed if any digits are converted. Advance *i only
664 * as digits are consumed.
666 static JSBool
667 fractional(jsdouble *result, const jschar *s, size_t *i, size_t limit)
669 jsdouble factor = 0.1;
670 size_t init = *i;
671 *result = 0.0;
672 while (*i < limit &&
673 ('0' <= s[*i] && s[*i] <= '9')) {
674 *result += (s[*i] - '0') * factor;
675 factor *= 0.1;
676 ++(*i);
678 return (*i != init);
682 * Read and convert exactly n decimal digits from s[*i]
683 * to s[min(*i+n,limit)] into *result.
685 * Succeed if exactly n digits are converted. Advance *i only
686 * on success.
688 static JSBool
689 ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
691 size_t init = *i;
693 if (digits(result, s, i, JS_MIN(limit, init+n)))
694 return ((*i - init) == n);
696 *i = init;
697 return JS_FALSE;
701 * Parse a string in one of the date-time formats given by the W3C
702 * "NOTE-datetime" specification. These formats make up a restricted
703 * profile of the ISO 8601 format. Quoted here:
705 * The formats are as follows. Exactly the components shown here
706 * must be present, with exactly this punctuation. Note that the "T"
707 * appears literally in the string, to indicate the beginning of the
708 * time element, as specified in ISO 8601.
710 * Any combination of the date formats with the time formats is
711 * allowed, and also either the date or the time can be missing.
713 * The specification is silent on the meaning when fields are
714 * ommitted so the interpretations are a guess, but hopefully a
715 * reasonable one. We default the month to January, the day to the
716 * 1st, and hours minutes and seconds all to 0. If the date is
717 * missing entirely then we assume 1970-01-01 so that the time can
718 * be aded to a date later. If the time is missing then we assume
719 * 00:00 UTC. If the time is present but the time zone field is
720 * missing then we use local time.
722 * Date part:
724 * Year:
725 * YYYY (eg 1997)
727 * Year and month:
728 * YYYY-MM (eg 1997-07)
730 * Complete date:
731 * YYYY-MM-DD (eg 1997-07-16)
733 * Time part:
735 * Hours and minutes:
736 * Thh:mmTZD (eg T19:20+01:00)
738 * Hours, minutes and seconds:
739 * Thh:mm:ssTZD (eg T19:20:30+01:00)
741 * Hours, minutes, seconds and a decimal fraction of a second:
742 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
744 * where:
746 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
747 * MM = two-digit month (01=January, etc.)
748 * DD = two-digit day of month (01 through 31)
749 * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
750 * mm = two digits of minute (00 through 59)
751 * ss = two digits of second (00 through 59)
752 * s = one or more digits representing a decimal fraction of a second
753 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
756 static JSBool
757 date_parseISOString(JSString *str, jsdouble *result)
759 jsdouble msec;
761 const jschar *s;
762 size_t limit;
763 size_t i = 0;
764 int tzMul = 1;
765 int dateMul = 1;
766 size_t year = 1970;
767 size_t month = 1;
768 size_t day = 1;
769 size_t hour = 0;
770 size_t min = 0;
771 size_t sec = 0;
772 jsdouble frac = 0;
773 bool isLocalTime = JS_FALSE;
774 size_t tzHour = 0;
775 size_t tzMin = 0;
777 #define PEEK(ch) (i < limit && s[i] == ch)
779 #define NEED(ch) \
780 JS_BEGIN_MACRO \
781 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
782 JS_END_MACRO
784 #define DONE_DATE_UNLESS(ch) \
785 JS_BEGIN_MACRO \
786 if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
787 JS_END_MACRO
789 #define DONE_UNLESS(ch) \
790 JS_BEGIN_MACRO \
791 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
792 JS_END_MACRO
794 #define NEED_NDIGITS(n, field) \
795 JS_BEGIN_MACRO \
796 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
797 JS_END_MACRO
799 str->getCharsAndLength(s, limit);
801 if (PEEK('+') || PEEK('-')) {
802 if (PEEK('-'))
803 dateMul = -1;
804 ++i;
805 NEED_NDIGITS(6, year);
806 } else if (!PEEK('T')) {
807 NEED_NDIGITS(4, year);
809 DONE_DATE_UNLESS('-');
810 NEED_NDIGITS(2, month);
811 DONE_DATE_UNLESS('-');
812 NEED_NDIGITS(2, day);
814 done_date:
815 DONE_UNLESS('T');
816 NEED_NDIGITS(2, hour);
817 NEED(':');
818 NEED_NDIGITS(2, min);
820 if (PEEK(':')) {
821 ++i;
822 NEED_NDIGITS(2, sec);
823 if (PEEK('.')) {
824 ++i;
825 if (!fractional(&frac, s, &i, limit))
826 goto syntax;
830 if (PEEK('Z')) {
831 ++i;
832 } else if (PEEK('+') || PEEK('-')) {
833 if (PEEK('-'))
834 tzMul = -1;
835 ++i;
836 NEED_NDIGITS(2, tzHour);
837 NEED(':');
838 NEED_NDIGITS(2, tzMin);
839 } else {
840 isLocalTime = JS_TRUE;
843 done:
844 if (year > 275943 // ceil(1e8/365) + 1970
845 || (month == 0 || month > 12)
846 || (day == 0 || day > size_t(DaysInMonth(year,month)))
847 || hour > 24
848 || ((hour == 24) && (min > 0 || sec > 0))
849 || min > 59
850 || sec > 59
851 || tzHour > 23
852 || tzMin > 59)
853 goto syntax;
855 if (i != limit)
856 goto syntax;
858 month -= 1; /* convert month to 0-based */
860 msec = date_msecFromDate(dateMul * (jsdouble)year, month, day,
861 hour, min, sec,
862 frac * 1000.0);;
864 if (isLocalTime) {
865 msec = UTC(msec);
866 } else {
867 msec -= ((tzMul) * ((tzHour * msPerHour)
868 + (tzMin * msPerMinute)));
871 if (msec < -8.64e15 || msec > 8.64e15)
872 goto syntax;
874 *result = msec;
876 return JS_TRUE;
878 syntax:
879 /* syntax error */
880 *result = 0;
881 return JS_FALSE;
883 #undef PEEK
884 #undef NEED
885 #undef DONE_UNLESS
886 #undef NEED_NDIGITS
889 static JSBool
890 date_parseString(JSString *str, jsdouble *result)
892 jsdouble msec;
894 const jschar *s;
895 size_t limit;
896 size_t i = 0;
897 int year = -1;
898 int mon = -1;
899 int mday = -1;
900 int hour = -1;
901 int min = -1;
902 int sec = -1;
903 int c = -1;
904 int n = -1;
905 int tzoffset = -1;
906 int prevc = 0;
907 JSBool seenplusminus = JS_FALSE;
908 int temp;
909 JSBool seenmonthname = JS_FALSE;
911 if (date_parseISOString(str, result))
912 return JS_TRUE;
914 str->getCharsAndLength(s, limit);
915 if (limit == 0)
916 goto syntax;
917 while (i < limit) {
918 c = s[i];
919 i++;
920 if (c <= ' ' || c == ',' || c == '-') {
921 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
922 prevc = c;
924 continue;
926 if (c == '(') { /* comments) */
927 int depth = 1;
928 while (i < limit) {
929 c = s[i];
930 i++;
931 if (c == '(') depth++;
932 else if (c == ')')
933 if (--depth <= 0)
934 break;
936 continue;
938 if ('0' <= c && c <= '9') {
939 n = c - '0';
940 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
941 n = n * 10 + c - '0';
942 i++;
945 /* allow TZA before the year, so
946 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
947 * works */
949 /* uses of seenplusminus allow : in TZA, so Java
950 * no-timezone style of GMT+4:30 works
953 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
954 /* make ':' case below change tzoffset */
955 seenplusminus = JS_TRUE;
957 /* offset */
958 if (n < 24)
959 n = n * 60; /* EG. "GMT-3" */
960 else
961 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
962 if (prevc == '+') /* plus means east of GMT */
963 n = -n;
964 if (tzoffset != 0 && tzoffset != -1)
965 goto syntax;
966 tzoffset = n;
967 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
968 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
969 year = n;
970 else
971 goto syntax;
972 } else if (c == ':') {
973 if (hour < 0)
974 hour = /*byte*/ n;
975 else if (min < 0)
976 min = /*byte*/ n;
977 else
978 goto syntax;
979 } else if (c == '/') {
980 /* until it is determined that mon is the actual
981 month, keep it as 1-based rather than 0-based */
982 if (mon < 0)
983 mon = /*byte*/ n;
984 else if (mday < 0)
985 mday = /*byte*/ n;
986 else
987 goto syntax;
988 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
989 goto syntax;
990 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
991 if (tzoffset < 0)
992 tzoffset -= n;
993 else
994 tzoffset += n;
995 } else if (hour >= 0 && min < 0) {
996 min = /*byte*/ n;
997 } else if (prevc == ':' && min >= 0 && sec < 0) {
998 sec = /*byte*/ n;
999 } else if (mon < 0) {
1000 mon = /*byte*/n;
1001 } else if (mon >= 0 && mday < 0) {
1002 mday = /*byte*/ n;
1003 } else if (mon >= 0 && mday >= 0 && year < 0) {
1004 year = n;
1005 } else {
1006 goto syntax;
1008 prevc = 0;
1009 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
1010 prevc = c;
1011 } else {
1012 size_t st = i - 1;
1013 int k;
1014 while (i < limit) {
1015 c = s[i];
1016 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1017 break;
1018 i++;
1020 if (i <= st + 1)
1021 goto syntax;
1022 for (k = JS_ARRAY_LENGTH(wtb); --k >= 0;)
1023 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
1024 int action = ttb[k];
1025 if (action != 0) {
1026 if (action < 0) {
1028 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1029 * 12:30, instead of blindly adding 12 if PM.
1031 JS_ASSERT(action == -1 || action == -2);
1032 if (hour > 12 || hour < 0) {
1033 goto syntax;
1034 } else {
1035 if (action == -1 && hour == 12) { /* am */
1036 hour = 0;
1037 } else if (action == -2 && hour != 12) { /* pm */
1038 hour += 12;
1041 } else if (action <= 13) { /* month! */
1042 /* Adjust mon to be 1-based until the final values
1043 for mon, mday and year are adjusted below */
1044 if (seenmonthname) {
1045 goto syntax;
1047 seenmonthname = JS_TRUE;
1048 temp = /*byte*/ (action - 2) + 1;
1050 if (mon < 0) {
1051 mon = temp;
1052 } else if (mday < 0) {
1053 mday = mon;
1054 mon = temp;
1055 } else if (year < 0) {
1056 year = mon;
1057 mon = temp;
1058 } else {
1059 goto syntax;
1061 } else {
1062 tzoffset = action - 10000;
1065 break;
1067 if (k < 0)
1068 goto syntax;
1069 prevc = 0;
1072 if (year < 0 || mon < 0 || mday < 0)
1073 goto syntax;
1075 Case 1. The input string contains an English month name.
1076 The form of the string can be month f l, or f month l, or
1077 f l month which each evaluate to the same date.
1078 If f and l are both greater than or equal to 70, or
1079 both less than 70, the date is invalid.
1080 The year is taken to be the greater of the values f, l.
1081 If the year is greater than or equal to 70 and less than 100,
1082 it is considered to be the number of years after 1900.
1083 Case 2. The input string is of the form "f/m/l" where f, m and l are
1084 integers, e.g. 7/16/45.
1085 Adjust the mon, mday and year values to achieve 100% MSIE
1086 compatibility.
1087 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1088 i. If year < 100, it is the number of years after 1900
1089 ii. If year >= 100, it is the number of years after 0.
1090 b. If 70 <= f < 100
1091 i. If m < 70, f/m/l is interpreted as
1092 year/month/day where year is the number of years after
1093 1900.
1094 ii. If m >= 70, the date is invalid.
1095 c. If f >= 100
1096 i. If m < 70, f/m/l is interpreted as
1097 year/month/day where year is the number of years after 0.
1098 ii. If m >= 70, the date is invalid.
1100 if (seenmonthname) {
1101 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
1102 goto syntax;
1104 if (mday > year) {
1105 temp = year;
1106 year = mday;
1107 mday = temp;
1109 if (year >= 70 && year < 100) {
1110 year += 1900;
1112 } else if (mon < 70) { /* (a) month/day/year */
1113 if (year < 100) {
1114 year += 1900;
1116 } else if (mon < 100) { /* (b) year/month/day */
1117 if (mday < 70) {
1118 temp = year;
1119 year = mon + 1900;
1120 mon = mday;
1121 mday = temp;
1122 } else {
1123 goto syntax;
1125 } else { /* (c) year/month/day */
1126 if (mday < 70) {
1127 temp = year;
1128 year = mon;
1129 mon = mday;
1130 mday = temp;
1131 } else {
1132 goto syntax;
1135 mon -= 1; /* convert month to 0-based */
1136 if (sec < 0)
1137 sec = 0;
1138 if (min < 0)
1139 min = 0;
1140 if (hour < 0)
1141 hour = 0;
1143 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1145 if (tzoffset == -1) { /* no time zone specified, have to use local */
1146 msec = UTC(msec);
1147 } else {
1148 msec += tzoffset * msPerMinute;
1151 *result = msec;
1152 return JS_TRUE;
1154 syntax:
1155 /* syntax error */
1156 *result = 0;
1157 return JS_FALSE;
1160 static JSBool
1161 date_parse(JSContext *cx, uintN argc, jsval *vp)
1163 JSString *str;
1164 jsdouble result;
1166 if (argc == 0) {
1167 *vp = cx->runtime->NaNValue;
1168 return true;
1170 str = js_ValueToString(cx, vp[2]);
1171 if (!str)
1172 return JS_FALSE;
1173 vp[2] = STRING_TO_JSVAL(str);
1174 if (!date_parseString(str, &result)) {
1175 *vp = cx->runtime->NaNValue;
1176 return true;
1179 result = TIMECLIP(result);
1180 return js_NewNumberInRootedValue(cx, result, vp);
1183 static inline jsdouble
1184 NowAsMillis()
1186 return (jsdouble) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1189 static JSBool
1190 date_now(JSContext *cx, uintN argc, jsval *vp)
1192 return js_NewDoubleInRootedValue(cx, NowAsMillis(), vp);
1195 #ifdef JS_TRACER
1196 static jsdouble FASTCALL
1197 date_now_tn(JSContext*)
1199 return NowAsMillis();
1201 #endif
1204 * Get UTC time from the date object. Returns false if the object is not
1205 * Date type.
1207 static JSBool
1208 GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
1210 if (!JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1211 return JS_FALSE;
1212 *dp = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
1213 return JS_TRUE;
1216 static void
1217 SetDateToNaN(JSContext *cx, JSObject *obj, jsval *vp = NULL)
1219 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_DateClass);
1221 obj->fslots[JSSLOT_LOCAL_TIME] = cx->runtime->NaNValue;
1222 obj->fslots[JSSLOT_UTC_TIME] = cx->runtime->NaNValue;
1223 if (vp)
1224 *vp = cx->runtime->NaNValue;
1228 * Set UTC time to a given time and invalidate cached local time.
1230 static JSBool
1231 SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, jsval *vp = NULL)
1233 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_DateClass);
1235 obj->fslots[JSSLOT_LOCAL_TIME] = cx->runtime->NaNValue;
1236 if (!js_NewDoubleInRootedValue(cx, t, &obj->fslots[JSSLOT_UTC_TIME]))
1237 return false;
1238 if (vp)
1239 *vp = obj->fslots[JSSLOT_UTC_TIME];
1240 return true;
1244 * Get the local time, cache it if necessary. If UTC time is not finite
1245 * (e.g., NaN), the local time slot is set to the UTC time without conversion.
1247 static JSBool
1248 GetAndCacheLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
1250 if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1251 return false;
1253 jsval *slotp = &obj->fslots[JSSLOT_LOCAL_TIME];
1254 jsdouble result = *JSVAL_TO_DOUBLE(*slotp);
1255 if (JSDOUBLE_IS_NaN(result)) {
1256 result = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
1258 /* if result is NaN, it couldn't be finite. */
1259 if (JSDOUBLE_IS_FINITE(result))
1260 result = LocalTime(result);
1262 if (!js_NewDoubleInRootedValue(cx, result, slotp))
1263 return false;
1266 *dp = result;
1267 return true;
1271 * See ECMA 15.9.5.4 thru 15.9.5.23
1273 static JSBool
1274 date_getTime(JSContext *cx, uintN argc, jsval *vp)
1276 jsdouble result;
1278 return GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result) &&
1279 js_NewNumberInRootedValue(cx, result, vp);
1282 static JSBool
1283 GetYear(JSContext *cx, JSBool fullyear, jsval *vp)
1285 jsdouble result;
1287 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1288 return JS_FALSE;
1290 if (JSDOUBLE_IS_FINITE(result)) {
1291 result = YearFromTime(result);
1293 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1294 if (!fullyear)
1295 result -= 1900;
1298 return js_NewNumberInRootedValue(cx, result, vp);
1301 static JSBool
1302 date_getYear(JSContext *cx, uintN argc, jsval *vp)
1304 return GetYear(cx, JS_FALSE, vp);
1307 static JSBool
1308 date_getFullYear(JSContext *cx, uintN argc, jsval *vp)
1310 return GetYear(cx, JS_TRUE, vp);
1313 static JSBool
1314 date_getUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1316 jsdouble result;
1318 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1319 return JS_FALSE;
1321 if (JSDOUBLE_IS_FINITE(result))
1322 result = YearFromTime(result);
1324 return js_NewNumberInRootedValue(cx, result, vp);
1327 static JSBool
1328 date_getMonth(JSContext *cx, uintN argc, jsval *vp)
1330 jsdouble result;
1332 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1333 return JS_FALSE;
1335 if (JSDOUBLE_IS_FINITE(result))
1336 result = MonthFromTime(result);
1338 return js_NewNumberInRootedValue(cx, result, vp);
1341 static JSBool
1342 date_getUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1344 jsdouble result;
1346 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1347 return JS_FALSE;
1349 if (JSDOUBLE_IS_FINITE(result))
1350 result = MonthFromTime(result);
1352 return js_NewNumberInRootedValue(cx, result, vp);
1355 static JSBool
1356 date_getDate(JSContext *cx, uintN argc, jsval *vp)
1358 jsdouble result;
1360 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1361 return JS_FALSE;
1363 if (JSDOUBLE_IS_FINITE(result))
1364 result = DateFromTime(result);
1366 return js_NewNumberInRootedValue(cx, result, vp);
1369 static JSBool
1370 date_getUTCDate(JSContext *cx, uintN argc, jsval *vp)
1372 jsdouble result;
1374 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1375 return JS_FALSE;
1377 if (JSDOUBLE_IS_FINITE(result))
1378 result = DateFromTime(result);
1380 return js_NewNumberInRootedValue(cx, result, vp);
1383 static JSBool
1384 date_getDay(JSContext *cx, uintN argc, jsval *vp)
1386 jsdouble result;
1388 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1389 return JS_FALSE;
1391 if (JSDOUBLE_IS_FINITE(result))
1392 result = WeekDay(result);
1394 return js_NewNumberInRootedValue(cx, result, vp);
1397 static JSBool
1398 date_getUTCDay(JSContext *cx, uintN argc, jsval *vp)
1400 jsdouble result;
1402 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1403 return JS_FALSE;
1405 if (JSDOUBLE_IS_FINITE(result))
1406 result = WeekDay(result);
1408 return js_NewNumberInRootedValue(cx, result, vp);
1411 static JSBool
1412 date_getHours(JSContext *cx, uintN argc, jsval *vp)
1414 jsdouble result;
1416 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1417 return JS_FALSE;
1419 if (JSDOUBLE_IS_FINITE(result))
1420 result = HourFromTime(result);
1422 return js_NewNumberInRootedValue(cx, result, vp);
1425 static JSBool
1426 date_getUTCHours(JSContext *cx, uintN argc, jsval *vp)
1428 jsdouble result;
1430 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1431 return JS_FALSE;
1433 if (JSDOUBLE_IS_FINITE(result))
1434 result = HourFromTime(result);
1436 return js_NewNumberInRootedValue(cx, result, vp);
1439 static JSBool
1440 date_getMinutes(JSContext *cx, uintN argc, jsval *vp)
1442 jsdouble result;
1444 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1445 return JS_FALSE;
1447 if (JSDOUBLE_IS_FINITE(result))
1448 result = MinFromTime(result);
1450 return js_NewNumberInRootedValue(cx, result, vp);
1453 static JSBool
1454 date_getUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1456 jsdouble result;
1458 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1459 return JS_FALSE;
1461 if (JSDOUBLE_IS_FINITE(result))
1462 result = MinFromTime(result);
1464 return js_NewNumberInRootedValue(cx, result, vp);
1467 /* Date.getSeconds is mapped to getUTCSeconds */
1469 static JSBool
1470 date_getUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1472 jsdouble result;
1474 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1475 return JS_FALSE;
1477 if (JSDOUBLE_IS_FINITE(result))
1478 result = SecFromTime(result);
1480 return js_NewNumberInRootedValue(cx, result, vp);
1483 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1485 static JSBool
1486 date_getUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1488 jsdouble result;
1490 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1491 return JS_FALSE;
1493 if (JSDOUBLE_IS_FINITE(result))
1494 result = msFromTime(result);
1496 return js_NewNumberInRootedValue(cx, result, vp);
1499 static JSBool
1500 date_getTimezoneOffset(JSContext *cx, uintN argc, jsval *vp)
1502 JSObject *obj;
1503 jsdouble utctime, localtime, result;
1505 obj = JS_THIS_OBJECT(cx, vp);
1506 if (!GetUTCTime(cx, obj, vp, &utctime))
1507 return JS_FALSE;
1508 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime))
1509 return JS_FALSE;
1512 * Return the time zone offset in minutes for the current locale that is
1513 * appropriate for this time. This value would be a constant except for
1514 * daylight savings time.
1516 result = (utctime - localtime) / msPerMinute;
1517 return js_NewNumberInRootedValue(cx, result, vp);
1520 static JSBool
1521 date_setTime(JSContext *cx, uintN argc, jsval *vp)
1523 JSObject *obj = JS_THIS_OBJECT(cx, vp);
1524 if (!JS_InstanceOf(cx, obj, &js_DateClass, vp + 2))
1525 return false;
1527 if (argc == 0) {
1528 SetDateToNaN(cx, obj, vp);
1529 return true;
1532 jsdouble result = js_ValueToNumber(cx, &vp[2]);
1533 if (JSVAL_IS_NULL(vp[2]))
1534 return false;
1536 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1539 static JSBool
1540 date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1542 JSObject *obj;
1543 jsval *argv;
1544 uintN i;
1545 jsdouble args[4], *argp, *stop;
1546 jsdouble hour, min, sec, msec;
1547 jsdouble lorutime; /* Local or UTC version of *date */
1549 jsdouble msec_time;
1550 jsdouble result;
1552 obj = JS_THIS_OBJECT(cx, vp);
1553 if (!GetUTCTime(cx, obj, vp, &result))
1554 return false;
1556 /* just return NaN if the date is already NaN */
1557 if (!JSDOUBLE_IS_FINITE(result))
1558 return js_NewNumberInRootedValue(cx, result, vp);
1561 * Satisfy the ECMA rule that if a function is called with
1562 * fewer arguments than the specified formal arguments, the
1563 * remaining arguments are set to undefined. Seems like all
1564 * the Date.setWhatever functions in ECMA are only varargs
1565 * beyond the first argument; this should be set to undefined
1566 * if it's not given. This means that "d = new Date();
1567 * d.setMilliseconds()" returns NaN. Blech.
1569 if (argc == 0) {
1570 SetDateToNaN(cx, obj, vp);
1571 return true;
1573 if (argc > maxargs)
1574 argc = maxargs; /* clamp argc */
1575 JS_ASSERT(argc <= 4);
1577 argv = vp + 2;
1578 for (i = 0; i < argc; i++) {
1579 args[i] = js_ValueToNumber(cx, &argv[i]);
1580 if (JSVAL_IS_NULL(argv[i]))
1581 return false;
1582 if (!JSDOUBLE_IS_FINITE(args[i])) {
1583 SetDateToNaN(cx, obj, vp);
1584 return true;
1586 args[i] = js_DoubleToInteger(args[i]);
1589 if (local)
1590 lorutime = LocalTime(result);
1591 else
1592 lorutime = result;
1594 argp = args;
1595 stop = argp + argc;
1596 if (maxargs >= 4 && argp < stop)
1597 hour = *argp++;
1598 else
1599 hour = HourFromTime(lorutime);
1601 if (maxargs >= 3 && argp < stop)
1602 min = *argp++;
1603 else
1604 min = MinFromTime(lorutime);
1606 if (maxargs >= 2 && argp < stop)
1607 sec = *argp++;
1608 else
1609 sec = SecFromTime(lorutime);
1611 if (maxargs >= 1 && argp < stop)
1612 msec = *argp;
1613 else
1614 msec = msFromTime(lorutime);
1616 msec_time = MakeTime(hour, min, sec, msec);
1617 result = MakeDate(Day(lorutime), msec_time);
1619 /* fprintf(stderr, "%f\n", result); */
1621 if (local)
1622 result = UTC(result);
1624 /* fprintf(stderr, "%f\n", result); */
1626 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1629 static JSBool
1630 date_setMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1632 return date_makeTime(cx, 1, JS_TRUE, argc, vp);
1635 static JSBool
1636 date_setUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1638 return date_makeTime(cx, 1, JS_FALSE, argc, vp);
1641 static JSBool
1642 date_setSeconds(JSContext *cx, uintN argc, jsval *vp)
1644 return date_makeTime(cx, 2, JS_TRUE, argc, vp);
1647 static JSBool
1648 date_setUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1650 return date_makeTime(cx, 2, JS_FALSE, argc, vp);
1653 static JSBool
1654 date_setMinutes(JSContext *cx, uintN argc, jsval *vp)
1656 return date_makeTime(cx, 3, JS_TRUE, argc, vp);
1659 static JSBool
1660 date_setUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1662 return date_makeTime(cx, 3, JS_FALSE, argc, vp);
1665 static JSBool
1666 date_setHours(JSContext *cx, uintN argc, jsval *vp)
1668 return date_makeTime(cx, 4, JS_TRUE, argc, vp);
1671 static JSBool
1672 date_setUTCHours(JSContext *cx, uintN argc, jsval *vp)
1674 return date_makeTime(cx, 4, JS_FALSE, argc, vp);
1677 static JSBool
1678 date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1680 JSObject *obj;
1681 jsval *argv;
1682 uintN i;
1683 jsdouble lorutime; /* local or UTC version of *date */
1684 jsdouble args[3], *argp, *stop;
1685 jsdouble year, month, day;
1686 jsdouble result;
1688 obj = JS_THIS_OBJECT(cx, vp);
1689 if (!GetUTCTime(cx, obj, vp, &result))
1690 return false;
1692 /* see complaint about ECMA in date_MakeTime */
1693 if (argc == 0) {
1694 SetDateToNaN(cx, obj, vp);
1695 return true;
1697 if (argc > maxargs)
1698 argc = maxargs; /* clamp argc */
1699 JS_ASSERT(1 <= argc && argc <= 3);
1701 argv = vp + 2;
1702 for (i = 0; i < argc; i++) {
1703 args[i] = js_ValueToNumber(cx, &argv[i]);
1704 if (JSVAL_IS_NULL(argv[i]))
1705 return JS_FALSE;
1706 if (!JSDOUBLE_IS_FINITE(args[i])) {
1707 SetDateToNaN(cx, obj, vp);
1708 return true;
1710 args[i] = js_DoubleToInteger(args[i]);
1713 /* return NaN if date is NaN and we're not setting the year,
1714 * If we are, use 0 as the time. */
1715 if (!(JSDOUBLE_IS_FINITE(result))) {
1716 if (maxargs < 3)
1717 return js_NewNumberInRootedValue(cx, result, vp);
1718 lorutime = +0.;
1719 } else {
1720 lorutime = local ? LocalTime(result) : result;
1723 argp = args;
1724 stop = argp + argc;
1725 if (maxargs >= 3 && argp < stop)
1726 year = *argp++;
1727 else
1728 year = YearFromTime(lorutime);
1730 if (maxargs >= 2 && argp < stop)
1731 month = *argp++;
1732 else
1733 month = MonthFromTime(lorutime);
1735 if (maxargs >= 1 && argp < stop)
1736 day = *argp++;
1737 else
1738 day = DateFromTime(lorutime);
1740 day = MakeDay(year, month, day); /* day within year */
1741 result = MakeDate(day, TimeWithinDay(lorutime));
1743 if (local)
1744 result = UTC(result);
1746 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1749 static JSBool
1750 date_setDate(JSContext *cx, uintN argc, jsval *vp)
1752 return date_makeDate(cx, 1, JS_TRUE, argc, vp);
1755 static JSBool
1756 date_setUTCDate(JSContext *cx, uintN argc, jsval *vp)
1758 return date_makeDate(cx, 1, JS_FALSE, argc, vp);
1761 static JSBool
1762 date_setMonth(JSContext *cx, uintN argc, jsval *vp)
1764 return date_makeDate(cx, 2, JS_TRUE, argc, vp);
1767 static JSBool
1768 date_setUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1770 return date_makeDate(cx, 2, JS_FALSE, argc, vp);
1773 static JSBool
1774 date_setFullYear(JSContext *cx, uintN argc, jsval *vp)
1776 return date_makeDate(cx, 3, JS_TRUE, argc, vp);
1779 static JSBool
1780 date_setUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1782 return date_makeDate(cx, 3, JS_FALSE, argc, vp);
1785 static JSBool
1786 date_setYear(JSContext *cx, uintN argc, jsval *vp)
1788 JSObject *obj = JS_THIS_OBJECT(cx, vp);
1790 jsdouble result;
1791 if (!GetUTCTime(cx, obj, vp, &result))
1792 return false;
1794 if (argc == 0) {
1795 /* Call this only after GetUTCTime has verified that obj is Date. */
1796 SetDateToNaN(cx, obj, vp);
1797 return true;
1800 jsdouble year = js_ValueToNumber(cx, &vp[2]);
1801 if (JSVAL_IS_NULL(vp[2]))
1802 return false;
1803 if (!JSDOUBLE_IS_FINITE(year)) {
1804 SetDateToNaN(cx, obj, vp);
1805 return true;
1807 year = js_DoubleToInteger(year);
1808 if (year >= 0 && year <= 99)
1809 year += 1900;
1811 jsdouble t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result) : +0.0;
1812 jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1813 result = MakeDate(day, TimeWithinDay(t));
1814 result = UTC(result);
1816 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1819 /* constants for toString, toUTCString */
1820 static char js_NaN_date_str[] = "Invalid Date";
1821 static const char* days[] =
1823 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1825 static const char* months[] =
1827 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1831 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1832 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1833 static void
1834 print_gmt_string(char* buf, size_t size, jsdouble utctime)
1836 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1837 days[WeekDay(utctime)],
1838 DateFromTime(utctime),
1839 months[MonthFromTime(utctime)],
1840 YearFromTime(utctime),
1841 HourFromTime(utctime),
1842 MinFromTime(utctime),
1843 SecFromTime(utctime));
1846 static void
1847 print_iso_string(char* buf, size_t size, jsdouble utctime)
1849 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
1850 YearFromTime(utctime),
1851 MonthFromTime(utctime) + 1,
1852 DateFromTime(utctime),
1853 HourFromTime(utctime),
1854 MinFromTime(utctime),
1855 SecFromTime(utctime),
1856 msFromTime(utctime));
1859 static JSBool
1860 date_utc_format(JSContext *cx, jsval *vp,
1861 void (*printFunc)(char*, size_t, jsdouble))
1863 char buf[100];
1864 JSString *str;
1865 jsdouble utctime;
1867 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1868 return JS_FALSE;
1870 if (!JSDOUBLE_IS_FINITE(utctime)) {
1871 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1872 } else {
1873 (*printFunc)(buf, sizeof buf, utctime);
1875 str = JS_NewStringCopyZ(cx, buf);
1876 if (!str)
1877 return JS_FALSE;
1878 *vp = STRING_TO_JSVAL(str);
1879 return JS_TRUE;
1882 static JSBool
1883 date_toGMTString(JSContext *cx, uintN argc, jsval *vp)
1885 return date_utc_format(cx, vp, print_gmt_string);
1888 static JSBool
1889 date_toISOString(JSContext *cx, uintN argc, jsval *vp)
1891 return date_utc_format(cx, vp, print_iso_string);
1894 /* for Date.toLocaleString; interface to PRMJTime date struct.
1896 static void
1897 new_explode(jsdouble timeval, PRMJTime *split)
1899 jsint year = YearFromTime(timeval);
1901 split->tm_usec = (int32) msFromTime(timeval) * 1000;
1902 split->tm_sec = (int8) SecFromTime(timeval);
1903 split->tm_min = (int8) MinFromTime(timeval);
1904 split->tm_hour = (int8) HourFromTime(timeval);
1905 split->tm_mday = (int8) DateFromTime(timeval);
1906 split->tm_mon = (int8) MonthFromTime(timeval);
1907 split->tm_wday = (int8) WeekDay(timeval);
1908 split->tm_year = year;
1909 split->tm_yday = (int16) DayWithinYear(timeval, year);
1911 /* not sure how this affects things, but it doesn't seem
1912 to matter. */
1913 split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1916 typedef enum formatspec {
1917 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1918 } formatspec;
1920 /* helper function */
1921 static JSBool
1922 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1924 char buf[100];
1925 JSString *str;
1926 char tzbuf[100];
1927 JSBool usetz;
1928 size_t i, tzlen;
1929 PRMJTime split;
1931 if (!JSDOUBLE_IS_FINITE(date)) {
1932 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1933 } else {
1934 jsdouble local = LocalTime(date);
1936 /* offset from GMT in minutes. The offset includes daylight savings,
1937 if it applies. */
1938 jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1940 /* map 510 minutes to 0830 hours */
1941 intN offset = (minutes / 60) * 100 + minutes % 60;
1943 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1944 * printed as 'GMT-0800' rather than as 'PST' to avoid
1945 * operating-system dependence on strftime (which
1946 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1947 * PST as 'Pacific Standard Time.' This way we always know
1948 * what we're getting, and can parse it if we produce it.
1949 * The OS TZA string is included as a comment.
1952 /* get a timezone string from the OS to include as a
1953 comment. */
1954 new_explode(date, &split);
1955 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1957 /* Decide whether to use the resulting timezone string.
1959 * Reject it if it contains any non-ASCII, non-alphanumeric
1960 * characters. It's then likely in some other character
1961 * encoding, and we probably won't display it correctly.
1963 usetz = JS_TRUE;
1964 tzlen = strlen(tzbuf);
1965 if (tzlen > 100) {
1966 usetz = JS_FALSE;
1967 } else {
1968 for (i = 0; i < tzlen; i++) {
1969 jschar c = tzbuf[i];
1970 if (c > 127 ||
1971 !(isalpha(c) || isdigit(c) ||
1972 c == ' ' || c == '(' || c == ')')) {
1973 usetz = JS_FALSE;
1978 /* Also reject it if it's not parenthesized or if it's '()'. */
1979 if (tzbuf[0] != '(' || tzbuf[1] == ')')
1980 usetz = JS_FALSE;
1981 } else
1982 usetz = JS_FALSE;
1984 switch (format) {
1985 case FORMATSPEC_FULL:
1987 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1988 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1990 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1991 JS_snprintf(buf, sizeof buf,
1992 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1993 days[WeekDay(local)],
1994 months[MonthFromTime(local)],
1995 DateFromTime(local),
1996 YearFromTime(local),
1997 HourFromTime(local),
1998 MinFromTime(local),
1999 SecFromTime(local),
2000 offset,
2001 usetz ? " " : "",
2002 usetz ? tzbuf : "");
2003 break;
2004 case FORMATSPEC_DATE:
2005 /* Tue Oct 31 2000 */
2006 JS_snprintf(buf, sizeof buf,
2007 "%s %s %.2d %.4d",
2008 days[WeekDay(local)],
2009 months[MonthFromTime(local)],
2010 DateFromTime(local),
2011 YearFromTime(local));
2012 break;
2013 case FORMATSPEC_TIME:
2014 /* 09:41:40 GMT-0800 (PST) */
2015 JS_snprintf(buf, sizeof buf,
2016 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2017 HourFromTime(local),
2018 MinFromTime(local),
2019 SecFromTime(local),
2020 offset,
2021 usetz ? " " : "",
2022 usetz ? tzbuf : "");
2023 break;
2027 str = JS_NewStringCopyZ(cx, buf);
2028 if (!str)
2029 return JS_FALSE;
2030 *rval = STRING_TO_JSVAL(str);
2031 return JS_TRUE;
2034 static JSBool
2035 date_toLocaleHelper(JSContext *cx, const char *format, jsval *vp)
2037 JSObject *obj;
2038 char buf[100];
2039 JSString *str;
2040 PRMJTime split;
2041 jsdouble utctime;
2043 obj = JS_THIS_OBJECT(cx, vp);
2044 if (!GetUTCTime(cx, obj, vp, &utctime))
2045 return JS_FALSE;
2047 if (!JSDOUBLE_IS_FINITE(utctime)) {
2048 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2049 } else {
2050 intN result_len;
2051 jsdouble local = LocalTime(utctime);
2052 new_explode(local, &split);
2054 /* let PRMJTime format it. */
2055 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2057 /* If it failed, default to toString. */
2058 if (result_len == 0)
2059 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2061 /* Hacked check against undesired 2-digit year 00/00/00 form. */
2062 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2063 /* Format %x means use OS settings, which may have 2-digit yr, so
2064 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2065 !isdigit(buf[result_len - 3]) &&
2066 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2067 /* ...but not if starts with 4-digit year, like 2022/3/11. */
2068 !(isdigit(buf[0]) && isdigit(buf[1]) &&
2069 isdigit(buf[2]) && isdigit(buf[3]))) {
2070 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2071 "%d", js_DateGetYear(cx, obj));
2076 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
2077 return cx->localeCallbacks->localeToUnicode(cx, buf, vp);
2079 str = JS_NewStringCopyZ(cx, buf);
2080 if (!str)
2081 return JS_FALSE;
2082 *vp = STRING_TO_JSVAL(str);
2083 return JS_TRUE;
2086 static JSBool
2087 date_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
2089 /* Use '%#c' for windows, because '%c' is
2090 * backward-compatible and non-y2k with msvc; '%#c' requests that a
2091 * full year be used in the result string.
2093 return date_toLocaleHelper(cx,
2094 #if defined(_WIN32) && !defined(__MWERKS__)
2095 "%#c"
2096 #else
2097 "%c"
2098 #endif
2099 , vp);
2102 static JSBool
2103 date_toLocaleDateString(JSContext *cx, uintN argc, jsval *vp)
2105 /* Use '%#x' for windows, because '%x' is
2106 * backward-compatible and non-y2k with msvc; '%#x' requests that a
2107 * full year be used in the result string.
2109 return date_toLocaleHelper(cx,
2110 #if defined(_WIN32) && !defined(__MWERKS__)
2111 "%#x"
2112 #else
2113 "%x"
2114 #endif
2115 , vp);
2118 static JSBool
2119 date_toLocaleTimeString(JSContext *cx, uintN argc, jsval *vp)
2121 return date_toLocaleHelper(cx, "%X", vp);
2124 static JSBool
2125 date_toLocaleFormat(JSContext *cx, uintN argc, jsval *vp)
2127 JSString *fmt;
2128 const char *fmtbytes;
2130 if (argc == 0)
2131 return date_toLocaleString(cx, argc, vp);
2133 fmt = js_ValueToString(cx, vp[2]);
2134 if (!fmt)
2135 return JS_FALSE;
2136 vp[2] = STRING_TO_JSVAL(fmt);
2137 fmtbytes = js_GetStringBytes(cx, fmt);
2138 if (!fmtbytes)
2139 return JS_FALSE;
2141 return date_toLocaleHelper(cx, fmtbytes, vp);
2144 static JSBool
2145 date_toTimeString(JSContext *cx, uintN argc, jsval *vp)
2147 jsdouble utctime;
2149 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2150 return JS_FALSE;
2151 return date_format(cx, utctime, FORMATSPEC_TIME, vp);
2154 static JSBool
2155 date_toDateString(JSContext *cx, uintN argc, jsval *vp)
2157 jsdouble utctime;
2159 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2160 return JS_FALSE;
2161 return date_format(cx, utctime, FORMATSPEC_DATE, vp);
2164 #if JS_HAS_TOSOURCE
2165 #include <string.h>
2166 #include "jsdtoa.h"
2168 static JSBool
2169 date_toSource(JSContext *cx, uintN argc, jsval *vp)
2171 jsdouble utctime;
2172 char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
2173 JSString *str;
2175 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2176 return JS_FALSE;
2178 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
2179 if (!numStr) {
2180 JS_ReportOutOfMemory(cx);
2181 return JS_FALSE;
2184 bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
2185 if (!bytes) {
2186 JS_ReportOutOfMemory(cx);
2187 return JS_FALSE;
2190 str = JS_NewString(cx, bytes, strlen(bytes));
2191 if (!str) {
2192 js_free(bytes);
2193 return JS_FALSE;
2195 *vp = STRING_TO_JSVAL(str);
2196 return JS_TRUE;
2198 #endif
2200 static JSBool
2201 date_toString(JSContext *cx, uintN argc, jsval *vp)
2203 jsdouble utctime;
2205 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2206 return JS_FALSE;
2207 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2210 #ifdef JS_TRACER
2211 static jsval FASTCALL
2212 date_valueOf_tn(JSContext* cx, JSObject* obj, JSString* str)
2214 JS_ASSERT(JS_InstanceOf(cx, obj, &js_DateClass, NULL));
2215 jsdouble t = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
2217 JSString* number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
2218 jsval v;
2219 if (js_EqualStrings(str, number_str)) {
2220 if (!js_NewNumberInRootedValue(cx, t, &v))
2221 return JSVAL_ERROR_COOKIE;
2222 } else {
2223 if (!date_format(cx, t, FORMATSPEC_FULL, &v))
2224 return JSVAL_ERROR_COOKIE;
2226 return v;
2228 #endif
2230 static JSBool
2231 date_valueOf(JSContext *cx, uintN argc, jsval *vp)
2233 JSString *str, *number_str;
2235 /* It is an error to call date_valueOf on a non-date object, but we don't
2236 * need to check for that explicitly here because every path calls
2237 * GetUTCTime, which does the check.
2240 /* If called directly with no arguments, convert to a time number. */
2241 if (argc == 0)
2242 return date_getTime(cx, argc, vp);
2244 /* Convert to number only if the hint was given, otherwise favor string. */
2245 str = js_ValueToString(cx, vp[2]);
2246 if (!str)
2247 return JS_FALSE;
2248 number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
2249 if (js_EqualStrings(str, number_str))
2250 return date_getTime(cx, argc, vp);
2251 return date_toString(cx, argc, vp);
2254 // Don't really need an argument here, but we don't support arg-less builtins
2255 JS_DEFINE_TRCINFO_1(date_now,
2256 (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, 0)))
2258 static JSFunctionSpec date_static_methods[] = {
2259 JS_FN("UTC", date_UTC, MAXARGS,0),
2260 JS_FN("parse", date_parse, 1,0),
2261 JS_TN("now", date_now, 0,0, &date_now_trcinfo),
2262 JS_FS_END
2265 JS_DEFINE_TRCINFO_1(date_valueOf,
2266 (3, (static, JSVAL_RETRY, date_valueOf_tn, CONTEXT, THIS, STRING, 0, 0)))
2268 static JSFunctionSpec date_methods[] = {
2269 JS_FN("getTime", date_getTime, 0,0),
2270 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2271 JS_FN("getYear", date_getYear, 0,0),
2272 JS_FN("getFullYear", date_getFullYear, 0,0),
2273 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2274 JS_FN("getMonth", date_getMonth, 0,0),
2275 JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2276 JS_FN("getDate", date_getDate, 0,0),
2277 JS_FN("getUTCDate", date_getUTCDate, 0,0),
2278 JS_FN("getDay", date_getDay, 0,0),
2279 JS_FN("getUTCDay", date_getUTCDay, 0,0),
2280 JS_FN("getHours", date_getHours, 0,0),
2281 JS_FN("getUTCHours", date_getUTCHours, 0,0),
2282 JS_FN("getMinutes", date_getMinutes, 0,0),
2283 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2284 JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2285 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2286 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2287 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2288 JS_FN("setTime", date_setTime, 1,0),
2289 JS_FN("setYear", date_setYear, 1,0),
2290 JS_FN("setFullYear", date_setFullYear, 3,0),
2291 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2292 JS_FN("setMonth", date_setMonth, 2,0),
2293 JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2294 JS_FN("setDate", date_setDate, 1,0),
2295 JS_FN("setUTCDate", date_setUTCDate, 1,0),
2296 JS_FN("setHours", date_setHours, 4,0),
2297 JS_FN("setUTCHours", date_setUTCHours, 4,0),
2298 JS_FN("setMinutes", date_setMinutes, 3,0),
2299 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2300 JS_FN("setSeconds", date_setSeconds, 2,0),
2301 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2302 JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2303 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2304 JS_FN("toUTCString", date_toGMTString, 0,0),
2305 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2306 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2307 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2308 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2309 JS_FN("toDateString", date_toDateString, 0,0),
2310 JS_FN("toTimeString", date_toTimeString, 0,0),
2311 JS_FN("toISOString", date_toISOString, 0,0),
2312 JS_FN(js_toJSON_str, date_toISOString, 0,0),
2314 #if JS_HAS_TOSOURCE
2315 JS_FN(js_toSource_str, date_toSource, 0,0),
2316 #endif
2317 JS_FN(js_toString_str, date_toString, 0,0),
2318 JS_TN(js_valueOf_str, date_valueOf, 0,0, &date_valueOf_trcinfo),
2319 JS_FS_END
2322 JSBool
2323 js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2325 /* Date called as function. */
2326 if (!JS_IsConstructing(cx))
2327 return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, rval);
2329 /* Date called as constructor. */
2330 jsdouble d;
2331 if (argc == 0) {
2332 d = NowAsMillis();
2333 } else if (argc == 1) {
2334 if (!JSVAL_IS_STRING(argv[0])) {
2335 /* the argument is a millisecond number */
2336 d = js_ValueToNumber(cx, &argv[0]);
2337 if (JSVAL_IS_NULL(argv[0]))
2338 return JS_FALSE;
2339 d = TIMECLIP(d);
2340 } else {
2341 /* the argument is a string; parse it. */
2342 JSString *str = js_ValueToString(cx, argv[0]);
2343 if (!str)
2344 return JS_FALSE;
2345 argv[0] = STRING_TO_JSVAL(str);
2347 if (!date_parseString(str, &d))
2348 d = js_NaN;
2349 else
2350 d = TIMECLIP(d);
2352 } else {
2353 jsdouble msec_time;
2354 if (!date_msecFromArgs(cx, argc, argv, &msec_time))
2355 return JS_FALSE;
2357 if (JSDOUBLE_IS_FINITE(msec_time)) {
2358 msec_time = UTC(msec_time);
2359 msec_time = TIMECLIP(msec_time);
2361 d = msec_time;
2363 return SetUTCTime(cx, obj, d);
2366 JSObject *
2367 js_InitDateClass(JSContext *cx, JSObject *obj)
2369 JSObject *proto;
2371 /* set static LocalTZA */
2372 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2373 proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS,
2374 NULL, date_methods, NULL, date_static_methods);
2375 if (!proto)
2376 return NULL;
2378 SetDateToNaN(cx, proto);
2380 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2381 if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2382 return NULL;
2384 return proto;
2387 JS_FRIEND_API(JSObject *)
2388 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2390 JSObject *obj = js_NewObject(cx, &js_DateClass, NULL, NULL);
2391 if (!obj || !SetUTCTime(cx, obj, msec_time))
2392 return NULL;
2393 return obj;
2396 JS_FRIEND_API(JSObject *)
2397 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2398 int hour, int min, int sec)
2400 JSObject *obj;
2401 jsdouble msec_time;
2403 JS_ASSERT(mon < 12);
2404 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2405 obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2406 return obj;
2409 JS_FRIEND_API(JSBool)
2410 js_DateIsValid(JSContext *cx, JSObject* obj)
2412 jsdouble utctime;
2413 return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime);
2416 JS_FRIEND_API(int)
2417 js_DateGetYear(JSContext *cx, JSObject* obj)
2419 jsdouble localtime;
2421 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2422 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2423 JSDOUBLE_IS_NaN(localtime)) {
2424 return 0;
2427 return (int) YearFromTime(localtime);
2430 JS_FRIEND_API(int)
2431 js_DateGetMonth(JSContext *cx, JSObject* obj)
2433 jsdouble localtime;
2435 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2436 JSDOUBLE_IS_NaN(localtime)) {
2437 return 0;
2440 return (int) MonthFromTime(localtime);
2443 JS_FRIEND_API(int)
2444 js_DateGetDate(JSContext *cx, JSObject* obj)
2446 jsdouble localtime;
2448 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2449 JSDOUBLE_IS_NaN(localtime)) {
2450 return 0;
2453 return (int) DateFromTime(localtime);
2456 JS_FRIEND_API(int)
2457 js_DateGetHours(JSContext *cx, JSObject* obj)
2459 jsdouble localtime;
2461 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2462 JSDOUBLE_IS_NaN(localtime)) {
2463 return 0;
2466 return (int) HourFromTime(localtime);
2469 JS_FRIEND_API(int)
2470 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2472 jsdouble localtime;
2474 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2475 JSDOUBLE_IS_NaN(localtime)) {
2476 return 0;
2479 return (int) MinFromTime(localtime);
2482 JS_FRIEND_API(int)
2483 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2485 jsdouble utctime;
2487 if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime))
2488 return 0;
2490 return (int) SecFromTime(utctime);
2493 JS_FRIEND_API(void)
2494 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2496 jsdouble local;
2498 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2499 return;
2501 /* reset date if it was NaN */
2502 if (JSDOUBLE_IS_NaN(local))
2503 local = 0;
2505 local = date_msecFromDate(year,
2506 MonthFromTime(local),
2507 DateFromTime(local),
2508 HourFromTime(local),
2509 MinFromTime(local),
2510 SecFromTime(local),
2511 msFromTime(local));
2513 /* SetUTCTime also invalidates local time cache. */
2514 SetUTCTime(cx, obj, UTC(local));
2517 JS_FRIEND_API(void)
2518 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2520 jsdouble local;
2522 JS_ASSERT(month < 12);
2524 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2525 return;
2527 /* bail if date was NaN */
2528 if (JSDOUBLE_IS_NaN(local))
2529 return;
2531 local = date_msecFromDate(YearFromTime(local),
2532 month,
2533 DateFromTime(local),
2534 HourFromTime(local),
2535 MinFromTime(local),
2536 SecFromTime(local),
2537 msFromTime(local));
2538 SetUTCTime(cx, obj, UTC(local));
2541 JS_FRIEND_API(void)
2542 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2544 jsdouble local;
2546 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2547 return;
2549 if (JSDOUBLE_IS_NaN(local))
2550 return;
2552 local = date_msecFromDate(YearFromTime(local),
2553 MonthFromTime(local),
2554 date,
2555 HourFromTime(local),
2556 MinFromTime(local),
2557 SecFromTime(local),
2558 msFromTime(local));
2559 SetUTCTime(cx, obj, UTC(local));
2562 JS_FRIEND_API(void)
2563 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2565 jsdouble local;
2567 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2568 return;
2570 if (JSDOUBLE_IS_NaN(local))
2571 return;
2572 local = date_msecFromDate(YearFromTime(local),
2573 MonthFromTime(local),
2574 DateFromTime(local),
2575 hours,
2576 MinFromTime(local),
2577 SecFromTime(local),
2578 msFromTime(local));
2579 SetUTCTime(cx, obj, UTC(local));
2582 JS_FRIEND_API(void)
2583 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2585 jsdouble local;
2587 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2588 return;
2590 if (JSDOUBLE_IS_NaN(local))
2591 return;
2592 local = date_msecFromDate(YearFromTime(local),
2593 MonthFromTime(local),
2594 DateFromTime(local),
2595 HourFromTime(local),
2596 minutes,
2597 SecFromTime(local),
2598 msFromTime(local));
2599 SetUTCTime(cx, obj, UTC(local));
2602 JS_FRIEND_API(void)
2603 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2605 jsdouble local;
2607 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2608 return;
2610 if (JSDOUBLE_IS_NaN(local))
2611 return;
2612 local = date_msecFromDate(YearFromTime(local),
2613 MonthFromTime(local),
2614 DateFromTime(local),
2615 HourFromTime(local),
2616 MinFromTime(local),
2617 seconds,
2618 msFromTime(local));
2619 SetUTCTime(cx, obj, UTC(local));
2622 JS_FRIEND_API(jsdouble)
2623 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2625 jsdouble utctime;
2626 if (!GetUTCTime(cx, obj, NULL, &utctime))
2627 return 0;
2628 return utctime;
2631 #ifdef JS_THREADSAFE
2632 #include "prinrval.h"
2634 JS_FRIEND_API(uint32)
2635 js_IntervalNow()
2637 return uint32(PR_IntervalToMilliseconds(PR_IntervalNow()));
2640 #else /* !JS_THREADSAFE */
2642 JS_FRIEND_API(uint32)
2643 js_IntervalNow()
2645 return uint32(PRMJ_Now() / PRMJ_USEC_PER_MSEC);
2647 #endif