[JAEGER] Predict unbound names as being on the global object (bug 564949).
[mozilla-central.git] / js / src / jsdate.cpp
blobb09b6e4ed289b46c44f04e899d0a5fa60af7cb9c
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 #define __STDC_LIMIT_MACROS
45 * JS date methods.
49 * "For example, OS/360 devotes 26 bytes of the permanently
50 * resident date-turnover routine to the proper handling of
51 * December 31 on leap years (when it is Day 366). That
52 * might have been left to the operator."
54 * Frederick Brooks, 'The Second-System Effect'.
57 #include <ctype.h>
58 #include <locale.h>
59 #include <math.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include "jstypes.h"
63 #include "jsstdint.h"
64 #include "jsprf.h"
65 #include "prmjtime.h"
66 #include "jsutil.h" /* Added by JSIFY */
67 #include "jsapi.h"
68 #include "jsversion.h"
69 #include "jsbuiltins.h"
70 #include "jscntxt.h"
71 #include "jsdate.h"
72 #include "jsinterp.h"
73 #include "jsnum.h"
74 #include "jsobj.h"
75 #include "jsstr.h"
77 #include "jsobjinlines.h"
79 using namespace js;
82 * The JS 'Date' object is patterned after the Java 'Date' object.
83 * Here is an script:
85 * today = new Date();
87 * print(today.toLocaleString());
89 * weekDay = today.getDay();
92 * These Java (and ECMA-262) methods are supported:
94 * UTC
95 * getDate (getUTCDate)
96 * getDay (getUTCDay)
97 * getHours (getUTCHours)
98 * getMinutes (getUTCMinutes)
99 * getMonth (getUTCMonth)
100 * getSeconds (getUTCSeconds)
101 * getMilliseconds (getUTCMilliseconds)
102 * getTime
103 * getTimezoneOffset
104 * getYear
105 * getFullYear (getUTCFullYear)
106 * parse
107 * setDate (setUTCDate)
108 * setHours (setUTCHours)
109 * setMinutes (setUTCMinutes)
110 * setMonth (setUTCMonth)
111 * setSeconds (setUTCSeconds)
112 * setMilliseconds (setUTCMilliseconds)
113 * setTime
114 * setYear (setFullYear, setUTCFullYear)
115 * toGMTString (toUTCString)
116 * toLocaleString
117 * toString
120 * These Java methods are not supported
122 * setDay
123 * before
124 * after
125 * equals
126 * hashCode
130 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
131 * definition and reduce dependence on NSPR. NSPR is used to get the current
132 * time in milliseconds, the time zone offset, and the daylight savings time
133 * offset for a given time. NSPR is also used for Date.toLocaleString(), for
134 * locale-specific formatting, and to get a string representing the timezone.
135 * (Which turns out to be platform-dependent.)
137 * To do:
138 * (I did some performance tests by timing how long it took to run what
139 * I had of the js ECMA conformance tests.)
141 * - look at saving results across multiple calls to supporting
142 * functions; the toString functions compute some of the same values
143 * multiple times. Although - I took a quick stab at this, and I lost
144 * rather than gained. (Fractionally.) Hard to tell what compilers/processors
145 * are doing these days.
147 * - look at tweaking function return types to return double instead
148 * of int; this seems to make things run slightly faster sometimes.
149 * (though it could be architecture-dependent.) It'd be good to see
150 * how this does on win32. (Tried it on irix.) Types could use a
151 * general going-over.
155 * Supporting functions - ECMA 15.9.1.*
158 #define HalfTimeDomain 8.64e15
159 #define HoursPerDay 24.0
160 #define MinutesPerDay (HoursPerDay * MinutesPerHour)
161 #define MinutesPerHour 60.0
162 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
163 #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
164 #define SecondsPerMinute 60.0
166 #if defined(XP_WIN) || defined(XP_OS2)
167 /* Work around msvc double optimization bug by making these runtime values; if
168 * they're available at compile time, msvc optimizes division by them by
169 * computing the reciprocal and multiplying instead of dividing - this loses
170 * when the reciprocal isn't representable in a double.
172 static jsdouble msPerSecond = 1000.0;
173 static jsdouble msPerDay = SecondsPerDay * 1000.0;
174 static jsdouble msPerHour = SecondsPerHour * 1000.0;
175 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
176 #else
177 #define msPerDay (SecondsPerDay * msPerSecond)
178 #define msPerHour (SecondsPerHour * msPerSecond)
179 #define msPerMinute (SecondsPerMinute * msPerSecond)
180 #define msPerSecond 1000.0
181 #endif
183 #define Day(t) floor((t) / msPerDay)
185 static jsdouble
186 TimeWithinDay(jsdouble t)
188 jsdouble result;
189 result = fmod(t, msPerDay);
190 if (result < 0)
191 result += msPerDay;
192 return result;
195 #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
196 ? 366 : 365)
198 /* math here has to be f.p, because we need
199 * floor((1968 - 1969) / 4) == -1
201 #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
202 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
203 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
205 static jsint
206 YearFromTime(jsdouble t)
208 jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
209 jsdouble t2 = (jsdouble) TimeFromYear(y);
211 if (t2 > t) {
212 y--;
213 } else {
214 if (t2 + msPerDay * DaysInYear(y) <= t)
215 y++;
217 return y;
220 #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
222 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
225 * The following array contains the day of year for the first day of
226 * each month, where index 0 is January, and day 0 is January 1.
228 static jsdouble firstDayOfMonth[2][13] = {
229 {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},
230 {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}
233 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]
235 static intN
236 DaysInMonth(jsint year, jsint month)
238 JSBool leap = (DaysInYear(year) == 366);
239 intN result = intN(DayFromMonth(month, leap) - DayFromMonth(month-1, leap));
240 return result;
243 static intN
244 MonthFromTime(jsdouble t)
246 intN d, step;
247 jsint year = YearFromTime(t);
248 d = DayWithinYear(t, year);
250 if (d < (step = 31))
251 return 0;
252 step += (InLeapYear(t) ? 29 : 28);
253 if (d < step)
254 return 1;
255 if (d < (step += 31))
256 return 2;
257 if (d < (step += 30))
258 return 3;
259 if (d < (step += 31))
260 return 4;
261 if (d < (step += 30))
262 return 5;
263 if (d < (step += 31))
264 return 6;
265 if (d < (step += 31))
266 return 7;
267 if (d < (step += 30))
268 return 8;
269 if (d < (step += 31))
270 return 9;
271 if (d < (step += 30))
272 return 10;
273 return 11;
276 static intN
277 DateFromTime(jsdouble t)
279 intN d, step, next;
280 jsint year = YearFromTime(t);
281 d = DayWithinYear(t, year);
283 if (d <= (next = 30))
284 return d + 1;
285 step = next;
286 next += (InLeapYear(t) ? 29 : 28);
287 if (d <= next)
288 return d - step;
289 step = next;
290 if (d <= (next += 31))
291 return d - step;
292 step = next;
293 if (d <= (next += 30))
294 return d - step;
295 step = next;
296 if (d <= (next += 31))
297 return d - step;
298 step = next;
299 if (d <= (next += 30))
300 return d - step;
301 step = next;
302 if (d <= (next += 31))
303 return d - step;
304 step = next;
305 if (d <= (next += 31))
306 return d - step;
307 step = next;
308 if (d <= (next += 30))
309 return d - step;
310 step = next;
311 if (d <= (next += 31))
312 return d - step;
313 step = next;
314 if (d <= (next += 30))
315 return d - step;
316 step = next;
317 return d - step;
320 static intN
321 WeekDay(jsdouble t)
323 jsint result;
324 result = (jsint) Day(t) + 4;
325 result = result % 7;
326 if (result < 0)
327 result += 7;
328 return (intN) result;
331 #define MakeTime(hour, min, sec, ms) \
332 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
334 static jsdouble
335 MakeDay(jsdouble year, jsdouble month, jsdouble date)
337 JSBool leap;
338 jsdouble yearday;
339 jsdouble monthday;
341 year += floor(month / 12);
343 month = fmod(month, 12.0);
344 if (month < 0)
345 month += 12;
347 leap = (DaysInYear((jsint) year) == 366);
349 yearday = floor(TimeFromYear(year) / msPerDay);
350 monthday = DayFromMonth(month, leap);
352 return yearday + monthday + date - 1;
355 #define MakeDate(day, time) ((day) * msPerDay + (time))
358 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
360 * yearStartingWith[0][i] is an example non-leap year where
361 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
363 * yearStartingWith[1][i] is an example leap year where
364 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
366 static jsint yearStartingWith[2][7] = {
367 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
368 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
372 * Find a year for which any given date will fall on the same weekday.
374 * This function should be used with caution when used other than
375 * for determining DST; it hasn't been proven not to produce an
376 * incorrect year for times near year boundaries.
378 static jsint
379 EquivalentYearForDST(jsint year)
381 jsint day;
382 JSBool isLeapYear;
384 day = (jsint) DayFromYear(year) + 4;
385 day = day % 7;
386 if (day < 0)
387 day += 7;
389 isLeapYear = (DaysInYear(year) == 366);
391 return yearStartingWith[isLeapYear][day];
394 /* LocalTZA gets set by js_InitDateClass() */
395 static jsdouble LocalTZA;
397 static jsdouble
398 DaylightSavingTA(jsdouble t, JSContext *cx)
400 /* abort if NaN */
401 if (JSDOUBLE_IS_NaN(t))
402 return t;
405 * If earlier than 1970 or after 2038, potentially beyond the ken of
406 * many OSes, map it to an equivalent year before asking.
408 if (t < 0.0 || t > 2145916800000.0) {
409 jsint year = EquivalentYearForDST(YearFromTime(t));
410 jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
411 t = MakeDate(day, TimeWithinDay(t));
414 int64 timeMilliseconds = static_cast<int64>(t);
415 int64 offsetMilliseconds = cx->dstOffsetCache.getDSTOffsetMilliseconds(timeMilliseconds, cx);
416 return static_cast<jsdouble>(offsetMilliseconds);
419 static jsdouble
420 AdjustTime(jsdouble date, JSContext *cx)
422 jsdouble t = DaylightSavingTA(date, cx) + LocalTZA;
423 t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
424 return t;
427 static jsdouble
428 LocalTime(jsdouble t, JSContext *cx)
430 return t + AdjustTime(t, cx);
433 static jsdouble
434 UTC(jsdouble t, JSContext *cx)
436 return t - AdjustTime(t - LocalTZA, cx);
439 static intN
440 HourFromTime(jsdouble t)
442 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
443 if (result < 0)
444 result += (intN)HoursPerDay;
445 return result;
448 static intN
449 MinFromTime(jsdouble t)
451 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
452 if (result < 0)
453 result += (intN)MinutesPerHour;
454 return result;
457 static intN
458 SecFromTime(jsdouble t)
460 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
461 if (result < 0)
462 result += (intN)SecondsPerMinute;
463 return result;
466 static intN
467 msFromTime(jsdouble t)
469 intN result = (intN) fmod(t, msPerSecond);
470 if (result < 0)
471 result += (intN)msPerSecond;
472 return result;
475 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
476 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
477 ? js_DoubleToInteger(d + (+0.)) : js_NaN)
480 * end of ECMA 'support' functions
484 * Other Support routines and definitions
487 Class js_DateClass = {
488 js_Date_str,
489 JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_FIXED_RESERVED_SLOTS) |
490 JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
491 PropertyStub, PropertyStub, PropertyStub, PropertyStub,
492 EnumerateStub, ResolveStub, ConvertStub, NULL,
493 JSCLASS_NO_OPTIONAL_MEMBERS
496 /* for use by date_parse */
498 static const char* wtb[] = {
499 "am", "pm",
500 "monday", "tuesday", "wednesday", "thursday", "friday",
501 "saturday", "sunday",
502 "january", "february", "march", "april", "may", "june",
503 "july", "august", "september", "october", "november", "december",
504 "gmt", "ut", "utc",
505 "est", "edt",
506 "cst", "cdt",
507 "mst", "mdt",
508 "pst", "pdt"
509 /* time zone table needs to be expanded */
512 static int ttb[] = {
513 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
514 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
515 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
516 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
517 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
518 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
519 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
522 /* helper for date_parse */
523 static JSBool
524 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
525 int count, int ignoreCase)
527 JSBool result = JS_FALSE;
528 /* return true if matches, otherwise, false */
530 while (count > 0 && s1[s1off] && s2[s2off]) {
531 if (ignoreCase) {
532 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
533 break;
535 } else {
536 if ((jschar)s1[s1off] != s2[s2off]) {
537 break;
540 s1off++;
541 s2off++;
542 count--;
545 if (count == 0) {
546 result = JS_TRUE;
549 return result;
552 /* find UTC time from given date... no 1900 correction! */
553 static jsdouble
554 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
555 jsdouble min, jsdouble sec, jsdouble msec)
557 jsdouble day;
558 jsdouble msec_time;
559 jsdouble result;
561 day = MakeDay(year, mon, mday);
562 msec_time = MakeTime(hour, min, sec, msec);
563 result = MakeDate(day, msec_time);
564 return result;
567 /* compute the time in msec (unclipped) from the given args */
568 #define MAXARGS 7
570 static JSBool
571 date_msecFromArgs(JSContext *cx, uintN argc, Value *argv, jsdouble *rval)
573 uintN loop;
574 jsdouble array[MAXARGS];
575 jsdouble msec_time;
577 for (loop = 0; loop < MAXARGS; loop++) {
578 if (loop < argc) {
579 jsdouble d;
580 if (!ValueToNumber(cx, argv[loop], &d))
581 return JS_FALSE;
582 /* return NaN if any arg is not finite */
583 if (!JSDOUBLE_IS_FINITE(d)) {
584 *rval = js_NaN;
585 return JS_TRUE;
587 array[loop] = js_DoubleToInteger(d);
588 } else {
589 if (loop == 2) {
590 array[loop] = 1; /* Default the date argument to 1. */
591 } else {
592 array[loop] = 0;
597 /* adjust 2-digit years into the 20th century */
598 if (array[0] >= 0 && array[0] <= 99)
599 array[0] += 1900;
601 msec_time = date_msecFromDate(array[0], array[1], array[2],
602 array[3], array[4], array[5], array[6]);
603 *rval = msec_time;
604 return JS_TRUE;
608 * See ECMA 15.9.4.[3-10];
610 static JSBool
611 date_UTC(JSContext *cx, uintN argc, Value *vp)
613 jsdouble msec_time;
615 if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
616 return JS_FALSE;
618 msec_time = TIMECLIP(msec_time);
620 vp->setDouble(msec_time);
621 return JS_TRUE;
625 * Read and convert decimal digits from s[*i] into *result
626 * while *i < limit.
628 * Succeed if any digits are converted. Advance *i only
629 * as digits are consumed.
631 static JSBool
632 digits(size_t *result, const jschar *s, size_t *i, size_t limit)
634 size_t init = *i;
635 *result = 0;
636 while (*i < limit &&
637 ('0' <= s[*i] && s[*i] <= '9')) {
638 *result *= 10;
639 *result += (s[*i] - '0');
640 ++(*i);
642 return (*i != init);
646 * Read and convert decimal digits to the right of a decimal point,
647 * representing a fractional integer, from s[*i] into *result
648 * while *i < limit.
650 * Succeed if any digits are converted. Advance *i only
651 * as digits are consumed.
653 static JSBool
654 fractional(jsdouble *result, const jschar *s, size_t *i, size_t limit)
656 jsdouble factor = 0.1;
657 size_t init = *i;
658 *result = 0.0;
659 while (*i < limit &&
660 ('0' <= s[*i] && s[*i] <= '9')) {
661 *result += (s[*i] - '0') * factor;
662 factor *= 0.1;
663 ++(*i);
665 return (*i != init);
669 * Read and convert exactly n decimal digits from s[*i]
670 * to s[min(*i+n,limit)] into *result.
672 * Succeed if exactly n digits are converted. Advance *i only
673 * on success.
675 static JSBool
676 ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
678 size_t init = *i;
680 if (digits(result, s, i, JS_MIN(limit, init+n)))
681 return ((*i - init) == n);
683 *i = init;
684 return JS_FALSE;
688 * Parse a string in one of the date-time formats given by the W3C
689 * "NOTE-datetime" specification. These formats make up a restricted
690 * profile of the ISO 8601 format. Quoted here:
692 * The formats are as follows. Exactly the components shown here
693 * must be present, with exactly this punctuation. Note that the "T"
694 * appears literally in the string, to indicate the beginning of the
695 * time element, as specified in ISO 8601.
697 * Any combination of the date formats with the time formats is
698 * allowed, and also either the date or the time can be missing.
700 * The specification is silent on the meaning when fields are
701 * ommitted so the interpretations are a guess, but hopefully a
702 * reasonable one. We default the month to January, the day to the
703 * 1st, and hours minutes and seconds all to 0. If the date is
704 * missing entirely then we assume 1970-01-01 so that the time can
705 * be aded to a date later. If the time is missing then we assume
706 * 00:00 UTC. If the time is present but the time zone field is
707 * missing then we use local time.
709 * Date part:
711 * Year:
712 * YYYY (eg 1997)
714 * Year and month:
715 * YYYY-MM (eg 1997-07)
717 * Complete date:
718 * YYYY-MM-DD (eg 1997-07-16)
720 * Time part:
722 * Hours and minutes:
723 * Thh:mmTZD (eg T19:20+01:00)
725 * Hours, minutes and seconds:
726 * Thh:mm:ssTZD (eg T19:20:30+01:00)
728 * Hours, minutes, seconds and a decimal fraction of a second:
729 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
731 * where:
733 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
734 * MM = two-digit month (01=January, etc.)
735 * DD = two-digit day of month (01 through 31)
736 * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
737 * mm = two digits of minute (00 through 59)
738 * ss = two digits of second (00 through 59)
739 * s = one or more digits representing a decimal fraction of a second
740 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
743 static JSBool
744 date_parseISOString(JSString *str, jsdouble *result, JSContext *cx)
746 jsdouble msec;
748 const jschar *s;
749 size_t limit;
750 size_t i = 0;
751 int tzMul = 1;
752 int dateMul = 1;
753 size_t year = 1970;
754 size_t month = 1;
755 size_t day = 1;
756 size_t hour = 0;
757 size_t min = 0;
758 size_t sec = 0;
759 jsdouble frac = 0;
760 bool isLocalTime = JS_FALSE;
761 size_t tzHour = 0;
762 size_t tzMin = 0;
764 #define PEEK(ch) (i < limit && s[i] == ch)
766 #define NEED(ch) \
767 JS_BEGIN_MACRO \
768 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
769 JS_END_MACRO
771 #define DONE_DATE_UNLESS(ch) \
772 JS_BEGIN_MACRO \
773 if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
774 JS_END_MACRO
776 #define DONE_UNLESS(ch) \
777 JS_BEGIN_MACRO \
778 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
779 JS_END_MACRO
781 #define NEED_NDIGITS(n, field) \
782 JS_BEGIN_MACRO \
783 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
784 JS_END_MACRO
786 str->getCharsAndLength(s, limit);
788 if (PEEK('+') || PEEK('-')) {
789 if (PEEK('-'))
790 dateMul = -1;
791 ++i;
792 NEED_NDIGITS(6, year);
793 } else if (!PEEK('T')) {
794 NEED_NDIGITS(4, year);
796 DONE_DATE_UNLESS('-');
797 NEED_NDIGITS(2, month);
798 DONE_DATE_UNLESS('-');
799 NEED_NDIGITS(2, day);
801 done_date:
802 DONE_UNLESS('T');
803 NEED_NDIGITS(2, hour);
804 NEED(':');
805 NEED_NDIGITS(2, min);
807 if (PEEK(':')) {
808 ++i;
809 NEED_NDIGITS(2, sec);
810 if (PEEK('.')) {
811 ++i;
812 if (!fractional(&frac, s, &i, limit))
813 goto syntax;
817 if (PEEK('Z')) {
818 ++i;
819 } else if (PEEK('+') || PEEK('-')) {
820 if (PEEK('-'))
821 tzMul = -1;
822 ++i;
823 NEED_NDIGITS(2, tzHour);
824 NEED(':');
825 NEED_NDIGITS(2, tzMin);
826 } else {
827 isLocalTime = JS_TRUE;
830 done:
831 if (year > 275943 // ceil(1e8/365) + 1970
832 || (month == 0 || month > 12)
833 || (day == 0 || day > size_t(DaysInMonth(year,month)))
834 || hour > 24
835 || ((hour == 24) && (min > 0 || sec > 0))
836 || min > 59
837 || sec > 59
838 || tzHour > 23
839 || tzMin > 59)
840 goto syntax;
842 if (i != limit)
843 goto syntax;
845 month -= 1; /* convert month to 0-based */
847 msec = date_msecFromDate(dateMul * (jsdouble)year, month, day,
848 hour, min, sec,
849 frac * 1000.0);;
851 if (isLocalTime) {
852 msec = UTC(msec, cx);
853 } else {
854 msec -= ((tzMul) * ((tzHour * msPerHour)
855 + (tzMin * msPerMinute)));
858 if (msec < -8.64e15 || msec > 8.64e15)
859 goto syntax;
861 *result = msec;
863 return JS_TRUE;
865 syntax:
866 /* syntax error */
867 *result = 0;
868 return JS_FALSE;
870 #undef PEEK
871 #undef NEED
872 #undef DONE_UNLESS
873 #undef NEED_NDIGITS
876 static JSBool
877 date_parseString(JSString *str, jsdouble *result, JSContext *cx)
879 jsdouble msec;
881 const jschar *s;
882 size_t limit;
883 size_t i = 0;
884 int year = -1;
885 int mon = -1;
886 int mday = -1;
887 int hour = -1;
888 int min = -1;
889 int sec = -1;
890 int c = -1;
891 int n = -1;
892 int tzoffset = -1;
893 int prevc = 0;
894 JSBool seenplusminus = JS_FALSE;
895 int temp;
896 JSBool seenmonthname = JS_FALSE;
898 if (date_parseISOString(str, result, cx))
899 return JS_TRUE;
901 str->getCharsAndLength(s, limit);
902 if (limit == 0)
903 goto syntax;
904 while (i < limit) {
905 c = s[i];
906 i++;
907 if (c <= ' ' || c == ',' || c == '-') {
908 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
909 prevc = c;
911 continue;
913 if (c == '(') { /* comments) */
914 int depth = 1;
915 while (i < limit) {
916 c = s[i];
917 i++;
918 if (c == '(') depth++;
919 else if (c == ')')
920 if (--depth <= 0)
921 break;
923 continue;
925 if ('0' <= c && c <= '9') {
926 n = c - '0';
927 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
928 n = n * 10 + c - '0';
929 i++;
932 /* allow TZA before the year, so
933 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
934 * works */
936 /* uses of seenplusminus allow : in TZA, so Java
937 * no-timezone style of GMT+4:30 works
940 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
941 /* make ':' case below change tzoffset */
942 seenplusminus = JS_TRUE;
944 /* offset */
945 if (n < 24)
946 n = n * 60; /* EG. "GMT-3" */
947 else
948 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
949 if (prevc == '+') /* plus means east of GMT */
950 n = -n;
951 if (tzoffset != 0 && tzoffset != -1)
952 goto syntax;
953 tzoffset = n;
954 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
955 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
956 year = n;
957 else
958 goto syntax;
959 } else if (c == ':') {
960 if (hour < 0)
961 hour = /*byte*/ n;
962 else if (min < 0)
963 min = /*byte*/ n;
964 else
965 goto syntax;
966 } else if (c == '/') {
967 /* until it is determined that mon is the actual
968 month, keep it as 1-based rather than 0-based */
969 if (mon < 0)
970 mon = /*byte*/ n;
971 else if (mday < 0)
972 mday = /*byte*/ n;
973 else
974 goto syntax;
975 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
976 goto syntax;
977 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
978 if (tzoffset < 0)
979 tzoffset -= n;
980 else
981 tzoffset += n;
982 } else if (hour >= 0 && min < 0) {
983 min = /*byte*/ n;
984 } else if (prevc == ':' && min >= 0 && sec < 0) {
985 sec = /*byte*/ n;
986 } else if (mon < 0) {
987 mon = /*byte*/n;
988 } else if (mon >= 0 && mday < 0) {
989 mday = /*byte*/ n;
990 } else if (mon >= 0 && mday >= 0 && year < 0) {
991 year = n;
992 } else {
993 goto syntax;
995 prevc = 0;
996 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
997 prevc = c;
998 } else {
999 size_t st = i - 1;
1000 int k;
1001 while (i < limit) {
1002 c = s[i];
1003 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1004 break;
1005 i++;
1007 if (i <= st + 1)
1008 goto syntax;
1009 for (k = JS_ARRAY_LENGTH(wtb); --k >= 0;)
1010 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
1011 int action = ttb[k];
1012 if (action != 0) {
1013 if (action < 0) {
1015 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1016 * 12:30, instead of blindly adding 12 if PM.
1018 JS_ASSERT(action == -1 || action == -2);
1019 if (hour > 12 || hour < 0) {
1020 goto syntax;
1021 } else {
1022 if (action == -1 && hour == 12) { /* am */
1023 hour = 0;
1024 } else if (action == -2 && hour != 12) { /* pm */
1025 hour += 12;
1028 } else if (action <= 13) { /* month! */
1029 /* Adjust mon to be 1-based until the final values
1030 for mon, mday and year are adjusted below */
1031 if (seenmonthname) {
1032 goto syntax;
1034 seenmonthname = JS_TRUE;
1035 temp = /*byte*/ (action - 2) + 1;
1037 if (mon < 0) {
1038 mon = temp;
1039 } else if (mday < 0) {
1040 mday = mon;
1041 mon = temp;
1042 } else if (year < 0) {
1043 year = mon;
1044 mon = temp;
1045 } else {
1046 goto syntax;
1048 } else {
1049 tzoffset = action - 10000;
1052 break;
1054 if (k < 0)
1055 goto syntax;
1056 prevc = 0;
1059 if (year < 0 || mon < 0 || mday < 0)
1060 goto syntax;
1062 Case 1. The input string contains an English month name.
1063 The form of the string can be month f l, or f month l, or
1064 f l month which each evaluate to the same date.
1065 If f and l are both greater than or equal to 70, or
1066 both less than 70, the date is invalid.
1067 The year is taken to be the greater of the values f, l.
1068 If the year is greater than or equal to 70 and less than 100,
1069 it is considered to be the number of years after 1900.
1070 Case 2. The input string is of the form "f/m/l" where f, m and l are
1071 integers, e.g. 7/16/45.
1072 Adjust the mon, mday and year values to achieve 100% MSIE
1073 compatibility.
1074 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1075 i. If year < 100, it is the number of years after 1900
1076 ii. If year >= 100, it is the number of years after 0.
1077 b. If 70 <= f < 100
1078 i. If m < 70, f/m/l is interpreted as
1079 year/month/day where year is the number of years after
1080 1900.
1081 ii. If m >= 70, the date is invalid.
1082 c. If f >= 100
1083 i. If m < 70, f/m/l is interpreted as
1084 year/month/day where year is the number of years after 0.
1085 ii. If m >= 70, the date is invalid.
1087 if (seenmonthname) {
1088 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
1089 goto syntax;
1091 if (mday > year) {
1092 temp = year;
1093 year = mday;
1094 mday = temp;
1096 if (year >= 70 && year < 100) {
1097 year += 1900;
1099 } else if (mon < 70) { /* (a) month/day/year */
1100 if (year < 100) {
1101 year += 1900;
1103 } else if (mon < 100) { /* (b) year/month/day */
1104 if (mday < 70) {
1105 temp = year;
1106 year = mon + 1900;
1107 mon = mday;
1108 mday = temp;
1109 } else {
1110 goto syntax;
1112 } else { /* (c) year/month/day */
1113 if (mday < 70) {
1114 temp = year;
1115 year = mon;
1116 mon = mday;
1117 mday = temp;
1118 } else {
1119 goto syntax;
1122 mon -= 1; /* convert month to 0-based */
1123 if (sec < 0)
1124 sec = 0;
1125 if (min < 0)
1126 min = 0;
1127 if (hour < 0)
1128 hour = 0;
1130 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1132 if (tzoffset == -1) { /* no time zone specified, have to use local */
1133 msec = UTC(msec, cx);
1134 } else {
1135 msec += tzoffset * msPerMinute;
1138 *result = msec;
1139 return JS_TRUE;
1141 syntax:
1142 /* syntax error */
1143 *result = 0;
1144 return JS_FALSE;
1147 static JSBool
1148 date_parse(JSContext *cx, uintN argc, Value *vp)
1150 JSString *str;
1151 jsdouble result;
1153 if (argc == 0) {
1154 vp->setDouble(js_NaN);
1155 return true;
1157 str = js_ValueToString(cx, vp[2]);
1158 if (!str)
1159 return JS_FALSE;
1160 vp[2].setString(str);
1161 if (!date_parseString(str, &result, cx)) {
1162 vp->setDouble(js_NaN);
1163 return true;
1166 result = TIMECLIP(result);
1167 vp->setDouble(result);
1168 return true;
1171 static inline jsdouble
1172 NowAsMillis()
1174 return (jsdouble) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1177 static JSBool
1178 date_now(JSContext *cx, uintN argc, Value *vp)
1180 vp->setDouble(NowAsMillis());
1181 return JS_TRUE;
1184 #ifdef JS_TRACER
1185 static jsdouble FASTCALL
1186 date_now_tn(JSContext*)
1188 return NowAsMillis();
1190 #endif
1193 * Get UTC time from the date object. Returns false if the object is not
1194 * Date type.
1196 static JSBool
1197 GetUTCTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp)
1199 if (!InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1200 return JS_FALSE;
1201 *dp = obj->getDateUTCTime().asDouble();
1202 return JS_TRUE;
1205 static void
1206 SetDateToNaN(JSContext *cx, JSObject *obj, Value *vp = NULL)
1208 JS_ASSERT(obj->getClass() == &js_DateClass);
1210 obj->setDateLocalTime(cx->runtime->NaNValue);
1211 obj->setDateUTCTime(cx->runtime->NaNValue);
1212 if (vp)
1213 vp->setDouble(js_NaN);
1217 * Set UTC time to a given time and invalidate cached local time.
1219 static JSBool
1220 SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL)
1222 JS_ASSERT(obj->getClass() == &js_DateClass);
1224 obj->setDateLocalTime(cx->runtime->NaNValue);
1225 obj->setDateUTCTime(DoubleTag(t));
1226 if (vp)
1227 vp->setDouble(t);
1228 return true;
1232 * Get the local time, cache it if necessary. If UTC time is not finite
1233 * (e.g., NaN), the local time slot is set to the UTC time without conversion.
1235 static JSBool
1236 GetAndCacheLocalTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp)
1238 if (!obj || !InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1239 return false;
1241 jsdouble result = obj->getDateLocalTime().asDouble();
1242 if (JSDOUBLE_IS_NaN(result)) {
1243 result = obj->getDateUTCTime().asDouble();
1245 /* if result is NaN, it couldn't be finite. */
1246 if (JSDOUBLE_IS_FINITE(result))
1247 result = LocalTime(result, cx);
1249 obj->setDateLocalTime(DoubleTag(result));
1252 *dp = result;
1253 return true;
1257 * See ECMA 15.9.5.4 thru 15.9.5.23
1259 static JSBool
1260 date_getTime(JSContext *cx, uintN argc, Value *vp)
1262 jsdouble result;
1264 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1265 return JS_FALSE;
1266 vp->setDouble(result);
1267 return JS_TRUE;
1270 static JSBool
1271 GetYear(JSContext *cx, JSBool fullyear, Value *vp)
1273 jsdouble result;
1275 if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1276 return JS_FALSE;
1278 if (JSDOUBLE_IS_FINITE(result)) {
1279 result = YearFromTime(result);
1281 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1282 if (!fullyear)
1283 result -= 1900;
1286 vp->setDouble(result);
1287 return JS_TRUE;
1290 static JSBool
1291 date_getYear(JSContext *cx, uintN argc, Value *vp)
1293 return GetYear(cx, JS_FALSE, vp);
1296 static JSBool
1297 date_getFullYear(JSContext *cx, uintN argc, Value *vp)
1299 return GetYear(cx, JS_TRUE, vp);
1302 static JSBool
1303 date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp)
1305 jsdouble result;
1307 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1308 return JS_FALSE;
1310 if (JSDOUBLE_IS_FINITE(result))
1311 result = YearFromTime(result);
1313 vp->setDouble(result);
1314 return JS_TRUE;
1317 static JSBool
1318 date_getMonth(JSContext *cx, uintN argc, Value *vp)
1320 jsdouble result;
1322 if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1323 return JS_FALSE;
1325 if (JSDOUBLE_IS_FINITE(result))
1326 result = MonthFromTime(result);
1328 vp->setDouble(result);
1329 return JS_TRUE;
1332 static JSBool
1333 date_getUTCMonth(JSContext *cx, uintN argc, Value *vp)
1335 jsdouble result;
1337 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1338 return JS_FALSE;
1340 if (JSDOUBLE_IS_FINITE(result))
1341 result = MonthFromTime(result);
1343 vp->setDouble(result);
1344 return JS_TRUE;
1347 static JSBool
1348 date_getDate(JSContext *cx, uintN argc, Value *vp)
1350 jsdouble result;
1352 if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1353 return JS_FALSE;
1355 if (JSDOUBLE_IS_FINITE(result))
1356 result = DateFromTime(result);
1358 vp->setDouble(result);
1359 return JS_TRUE;
1362 static JSBool
1363 date_getUTCDate(JSContext *cx, uintN argc, Value *vp)
1365 jsdouble result;
1367 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1368 return JS_FALSE;
1370 if (JSDOUBLE_IS_FINITE(result))
1371 result = DateFromTime(result);
1373 vp->setDouble(result);
1374 return JS_TRUE;
1377 static JSBool
1378 date_getDay(JSContext *cx, uintN argc, Value *vp)
1380 jsdouble result;
1382 if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1383 return JS_FALSE;
1385 if (JSDOUBLE_IS_FINITE(result))
1386 result = WeekDay(result);
1388 vp->setDouble(result);
1389 return JS_TRUE;
1392 static JSBool
1393 date_getUTCDay(JSContext *cx, uintN argc, Value *vp)
1395 jsdouble result;
1397 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1398 return JS_FALSE;
1400 if (JSDOUBLE_IS_FINITE(result))
1401 result = WeekDay(result);
1403 vp->setDouble(result);
1404 return JS_TRUE;
1407 static JSBool
1408 date_getHours(JSContext *cx, uintN argc, Value *vp)
1410 jsdouble result;
1412 if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1413 return JS_FALSE;
1415 if (JSDOUBLE_IS_FINITE(result))
1416 result = HourFromTime(result);
1418 vp->setDouble(result);
1419 return JS_TRUE;
1422 static JSBool
1423 date_getUTCHours(JSContext *cx, uintN argc, Value *vp)
1425 jsdouble result;
1427 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1428 return JS_FALSE;
1430 if (JSDOUBLE_IS_FINITE(result))
1431 result = HourFromTime(result);
1433 vp->setDouble(result);
1434 return JS_TRUE;
1437 static JSBool
1438 date_getMinutes(JSContext *cx, uintN argc, Value *vp)
1440 jsdouble result;
1442 if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1443 return JS_FALSE;
1445 if (JSDOUBLE_IS_FINITE(result))
1446 result = MinFromTime(result);
1448 vp->setDouble(result);
1449 return JS_TRUE;
1452 static JSBool
1453 date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp)
1455 jsdouble result;
1457 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1458 return JS_FALSE;
1460 if (JSDOUBLE_IS_FINITE(result))
1461 result = MinFromTime(result);
1463 vp->setDouble(result);
1464 return JS_TRUE;
1467 /* Date.getSeconds is mapped to getUTCSeconds */
1469 static JSBool
1470 date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp)
1472 jsdouble result;
1474 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1475 return JS_FALSE;
1477 if (JSDOUBLE_IS_FINITE(result))
1478 result = SecFromTime(result);
1480 vp->setDouble(result);
1481 return JS_TRUE;
1484 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1486 static JSBool
1487 date_getUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
1489 jsdouble result;
1491 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
1492 return JS_FALSE;
1494 if (JSDOUBLE_IS_FINITE(result))
1495 result = msFromTime(result);
1497 vp->setDouble(result);
1498 return JS_TRUE;
1501 static JSBool
1502 date_getTimezoneOffset(JSContext *cx, uintN argc, Value *vp)
1504 JSObject *obj;
1505 jsdouble utctime, localtime, result;
1507 obj = ComputeThisObjectFromVp(cx, vp);
1508 if (!GetUTCTime(cx, obj, vp, &utctime))
1509 return JS_FALSE;
1510 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime))
1511 return JS_FALSE;
1514 * Return the time zone offset in minutes for the current locale that is
1515 * appropriate for this time. This value would be a constant except for
1516 * daylight savings time.
1518 result = (utctime - localtime) / msPerMinute;
1519 vp->setDouble(result);
1520 return JS_TRUE;
1523 static JSBool
1524 date_setTime(JSContext *cx, uintN argc, Value *vp)
1526 JSObject *obj = ComputeThisObjectFromVp(cx, vp);
1527 if (!InstanceOf(cx, obj, &js_DateClass, vp + 2))
1528 return false;
1530 if (argc == 0) {
1531 SetDateToNaN(cx, obj, vp);
1532 return true;
1535 jsdouble result;
1536 if (!ValueToNumber(cx, vp[2], &result))
1537 return false;
1539 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1542 static JSBool
1543 date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp)
1545 JSObject *obj;
1546 Value *argv;
1547 uintN i;
1548 jsdouble args[4], *argp, *stop;
1549 jsdouble hour, min, sec, msec;
1550 jsdouble lorutime; /* Local or UTC version of *date */
1552 jsdouble msec_time;
1553 jsdouble result;
1555 obj = ComputeThisObjectFromVp(cx, vp);
1556 if (!GetUTCTime(cx, obj, vp, &result))
1557 return false;
1559 /* just return NaN if the date is already NaN */
1560 if (!JSDOUBLE_IS_FINITE(result)) {
1561 vp->setDouble(result);
1562 return true;
1566 * Satisfy the ECMA rule that if a function is called with
1567 * fewer arguments than the specified formal arguments, the
1568 * remaining arguments are set to undefined. Seems like all
1569 * the Date.setWhatever functions in ECMA are only varargs
1570 * beyond the first argument; this should be set to undefined
1571 * if it's not given. This means that "d = new Date();
1572 * d.setMilliseconds()" returns NaN. Blech.
1574 if (argc == 0) {
1575 SetDateToNaN(cx, obj, vp);
1576 return true;
1578 if (argc > maxargs)
1579 argc = maxargs; /* clamp argc */
1580 JS_ASSERT(argc <= 4);
1582 argv = vp + 2;
1583 for (i = 0; i < argc; i++) {
1584 if (!ValueToNumber(cx, argv[i], &args[i]))
1585 return false;
1586 if (!JSDOUBLE_IS_FINITE(args[i])) {
1587 SetDateToNaN(cx, obj, vp);
1588 return true;
1590 args[i] = js_DoubleToInteger(args[i]);
1593 if (local)
1594 lorutime = LocalTime(result, cx);
1595 else
1596 lorutime = result;
1598 argp = args;
1599 stop = argp + argc;
1600 if (maxargs >= 4 && argp < stop)
1601 hour = *argp++;
1602 else
1603 hour = HourFromTime(lorutime);
1605 if (maxargs >= 3 && argp < stop)
1606 min = *argp++;
1607 else
1608 min = MinFromTime(lorutime);
1610 if (maxargs >= 2 && argp < stop)
1611 sec = *argp++;
1612 else
1613 sec = SecFromTime(lorutime);
1615 if (maxargs >= 1 && argp < stop)
1616 msec = *argp;
1617 else
1618 msec = msFromTime(lorutime);
1620 msec_time = MakeTime(hour, min, sec, msec);
1621 result = MakeDate(Day(lorutime), msec_time);
1623 /* fprintf(stderr, "%f\n", result); */
1625 if (local)
1626 result = UTC(result, cx);
1628 /* fprintf(stderr, "%f\n", result); */
1630 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1633 static JSBool
1634 date_setMilliseconds(JSContext *cx, uintN argc, Value *vp)
1636 return date_makeTime(cx, 1, JS_TRUE, argc, vp);
1639 static JSBool
1640 date_setUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
1642 return date_makeTime(cx, 1, JS_FALSE, argc, vp);
1645 static JSBool
1646 date_setSeconds(JSContext *cx, uintN argc, Value *vp)
1648 return date_makeTime(cx, 2, JS_TRUE, argc, vp);
1651 static JSBool
1652 date_setUTCSeconds(JSContext *cx, uintN argc, Value *vp)
1654 return date_makeTime(cx, 2, JS_FALSE, argc, vp);
1657 static JSBool
1658 date_setMinutes(JSContext *cx, uintN argc, Value *vp)
1660 return date_makeTime(cx, 3, JS_TRUE, argc, vp);
1663 static JSBool
1664 date_setUTCMinutes(JSContext *cx, uintN argc, Value *vp)
1666 return date_makeTime(cx, 3, JS_FALSE, argc, vp);
1669 static JSBool
1670 date_setHours(JSContext *cx, uintN argc, Value *vp)
1672 return date_makeTime(cx, 4, JS_TRUE, argc, vp);
1675 static JSBool
1676 date_setUTCHours(JSContext *cx, uintN argc, Value *vp)
1678 return date_makeTime(cx, 4, JS_FALSE, argc, vp);
1681 static JSBool
1682 date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp)
1684 JSObject *obj;
1685 Value *argv;
1686 uintN i;
1687 jsdouble lorutime; /* local or UTC version of *date */
1688 jsdouble args[3], *argp, *stop;
1689 jsdouble year, month, day;
1690 jsdouble result;
1692 obj = ComputeThisObjectFromVp(cx, vp);
1693 if (!GetUTCTime(cx, obj, vp, &result))
1694 return false;
1696 /* see complaint about ECMA in date_MakeTime */
1697 if (argc == 0) {
1698 SetDateToNaN(cx, obj, vp);
1699 return true;
1701 if (argc > maxargs)
1702 argc = maxargs; /* clamp argc */
1703 JS_ASSERT(1 <= argc && argc <= 3);
1705 argv = vp + 2;
1706 for (i = 0; i < argc; i++) {
1707 if (!ValueToNumber(cx, argv[i], &args[i]))
1708 return JS_FALSE;
1709 if (!JSDOUBLE_IS_FINITE(args[i])) {
1710 SetDateToNaN(cx, obj, vp);
1711 return true;
1713 args[i] = js_DoubleToInteger(args[i]);
1716 /* return NaN if date is NaN and we're not setting the year,
1717 * If we are, use 0 as the time. */
1718 if (!(JSDOUBLE_IS_FINITE(result))) {
1719 if (maxargs < 3) {
1720 vp->setDouble(result);
1721 return true;
1723 lorutime = +0.;
1724 } else {
1725 lorutime = local ? LocalTime(result, cx) : result;
1728 argp = args;
1729 stop = argp + argc;
1730 if (maxargs >= 3 && argp < stop)
1731 year = *argp++;
1732 else
1733 year = YearFromTime(lorutime);
1735 if (maxargs >= 2 && argp < stop)
1736 month = *argp++;
1737 else
1738 month = MonthFromTime(lorutime);
1740 if (maxargs >= 1 && argp < stop)
1741 day = *argp++;
1742 else
1743 day = DateFromTime(lorutime);
1745 day = MakeDay(year, month, day); /* day within year */
1746 result = MakeDate(day, TimeWithinDay(lorutime));
1748 if (local)
1749 result = UTC(result, cx);
1751 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1754 static JSBool
1755 date_setDate(JSContext *cx, uintN argc, Value *vp)
1757 return date_makeDate(cx, 1, JS_TRUE, argc, vp);
1760 static JSBool
1761 date_setUTCDate(JSContext *cx, uintN argc, Value *vp)
1763 return date_makeDate(cx, 1, JS_FALSE, argc, vp);
1766 static JSBool
1767 date_setMonth(JSContext *cx, uintN argc, Value *vp)
1769 return date_makeDate(cx, 2, JS_TRUE, argc, vp);
1772 static JSBool
1773 date_setUTCMonth(JSContext *cx, uintN argc, Value *vp)
1775 return date_makeDate(cx, 2, JS_FALSE, argc, vp);
1778 static JSBool
1779 date_setFullYear(JSContext *cx, uintN argc, Value *vp)
1781 return date_makeDate(cx, 3, JS_TRUE, argc, vp);
1784 static JSBool
1785 date_setUTCFullYear(JSContext *cx, uintN argc, Value *vp)
1787 return date_makeDate(cx, 3, JS_FALSE, argc, vp);
1790 static JSBool
1791 date_setYear(JSContext *cx, uintN argc, Value *vp)
1793 JSObject *obj = ComputeThisObjectFromVp(cx, vp);
1795 jsdouble result;
1796 if (!GetUTCTime(cx, obj, vp, &result))
1797 return false;
1799 if (argc == 0) {
1800 /* Call this only after GetUTCTime has verified that obj is Date. */
1801 SetDateToNaN(cx, obj, vp);
1802 return true;
1805 jsdouble year;
1806 if (!ValueToNumber(cx, vp[2], &year))
1807 return false;
1808 if (!JSDOUBLE_IS_FINITE(year)) {
1809 SetDateToNaN(cx, obj, vp);
1810 return true;
1812 year = js_DoubleToInteger(year);
1813 if (year >= 0 && year <= 99)
1814 year += 1900;
1816 jsdouble t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result, cx) : +0.0;
1817 jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1818 result = MakeDate(day, TimeWithinDay(t));
1819 result = UTC(result, cx);
1821 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1824 /* constants for toString, toUTCString */
1825 static char js_NaN_date_str[] = "Invalid Date";
1826 static const char* days[] =
1828 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1830 static const char* months[] =
1832 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1836 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1837 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1838 static void
1839 print_gmt_string(char* buf, size_t size, jsdouble utctime)
1841 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1842 days[WeekDay(utctime)],
1843 DateFromTime(utctime),
1844 months[MonthFromTime(utctime)],
1845 YearFromTime(utctime),
1846 HourFromTime(utctime),
1847 MinFromTime(utctime),
1848 SecFromTime(utctime));
1851 static void
1852 print_iso_string(char* buf, size_t size, jsdouble utctime)
1854 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
1855 YearFromTime(utctime),
1856 MonthFromTime(utctime) + 1,
1857 DateFromTime(utctime),
1858 HourFromTime(utctime),
1859 MinFromTime(utctime),
1860 SecFromTime(utctime),
1861 msFromTime(utctime));
1864 static JSBool
1865 date_utc_format(JSContext *cx, Value *vp,
1866 void (*printFunc)(char*, size_t, jsdouble))
1868 char buf[100];
1869 JSString *str;
1870 jsdouble utctime;
1872 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
1873 return JS_FALSE;
1875 if (!JSDOUBLE_IS_FINITE(utctime)) {
1876 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1877 } else {
1878 (*printFunc)(buf, sizeof buf, utctime);
1880 str = JS_NewStringCopyZ(cx, buf);
1881 if (!str)
1882 return JS_FALSE;
1883 vp->setString(str);
1884 return JS_TRUE;
1887 static JSBool
1888 date_toGMTString(JSContext *cx, uintN argc, Value *vp)
1890 return date_utc_format(cx, vp, print_gmt_string);
1893 static JSBool
1894 date_toISOString(JSContext *cx, uintN argc, Value *vp)
1896 return date_utc_format(cx, vp, print_iso_string);
1899 /* for Date.toLocaleString; interface to PRMJTime date struct.
1901 static void
1902 new_explode(jsdouble timeval, PRMJTime *split, JSContext *cx)
1904 jsint year = YearFromTime(timeval);
1906 split->tm_usec = (int32) msFromTime(timeval) * 1000;
1907 split->tm_sec = (int8) SecFromTime(timeval);
1908 split->tm_min = (int8) MinFromTime(timeval);
1909 split->tm_hour = (int8) HourFromTime(timeval);
1910 split->tm_mday = (int8) DateFromTime(timeval);
1911 split->tm_mon = (int8) MonthFromTime(timeval);
1912 split->tm_wday = (int8) WeekDay(timeval);
1913 split->tm_year = year;
1914 split->tm_yday = (int16) DayWithinYear(timeval, year);
1916 /* not sure how this affects things, but it doesn't seem
1917 to matter. */
1918 split->tm_isdst = (DaylightSavingTA(timeval, cx) != 0);
1921 typedef enum formatspec {
1922 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1923 } formatspec;
1925 /* helper function */
1926 static JSBool
1927 date_format(JSContext *cx, jsdouble date, formatspec format, Value *rval)
1929 char buf[100];
1930 JSString *str;
1931 char tzbuf[100];
1932 JSBool usetz;
1933 size_t i, tzlen;
1934 PRMJTime split;
1936 if (!JSDOUBLE_IS_FINITE(date)) {
1937 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1938 } else {
1939 jsdouble local = LocalTime(date, cx);
1941 /* offset from GMT in minutes. The offset includes daylight savings,
1942 if it applies. */
1943 jsint minutes = (jsint) floor(AdjustTime(date, cx) / msPerMinute);
1945 /* map 510 minutes to 0830 hours */
1946 intN offset = (minutes / 60) * 100 + minutes % 60;
1948 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1949 * printed as 'GMT-0800' rather than as 'PST' to avoid
1950 * operating-system dependence on strftime (which
1951 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1952 * PST as 'Pacific Standard Time.' This way we always know
1953 * what we're getting, and can parse it if we produce it.
1954 * The OS TZA string is included as a comment.
1957 /* get a timezone string from the OS to include as a
1958 comment. */
1959 new_explode(date, &split, cx);
1960 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1962 /* Decide whether to use the resulting timezone string.
1964 * Reject it if it contains any non-ASCII, non-alphanumeric
1965 * characters. It's then likely in some other character
1966 * encoding, and we probably won't display it correctly.
1968 usetz = JS_TRUE;
1969 tzlen = strlen(tzbuf);
1970 if (tzlen > 100) {
1971 usetz = JS_FALSE;
1972 } else {
1973 for (i = 0; i < tzlen; i++) {
1974 jschar c = tzbuf[i];
1975 if (c > 127 ||
1976 !(isalpha(c) || isdigit(c) ||
1977 c == ' ' || c == '(' || c == ')')) {
1978 usetz = JS_FALSE;
1983 /* Also reject it if it's not parenthesized or if it's '()'. */
1984 if (tzbuf[0] != '(' || tzbuf[1] == ')')
1985 usetz = JS_FALSE;
1986 } else
1987 usetz = JS_FALSE;
1989 switch (format) {
1990 case FORMATSPEC_FULL:
1992 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1993 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1995 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1996 JS_snprintf(buf, sizeof buf,
1997 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1998 days[WeekDay(local)],
1999 months[MonthFromTime(local)],
2000 DateFromTime(local),
2001 YearFromTime(local),
2002 HourFromTime(local),
2003 MinFromTime(local),
2004 SecFromTime(local),
2005 offset,
2006 usetz ? " " : "",
2007 usetz ? tzbuf : "");
2008 break;
2009 case FORMATSPEC_DATE:
2010 /* Tue Oct 31 2000 */
2011 JS_snprintf(buf, sizeof buf,
2012 "%s %s %.2d %.4d",
2013 days[WeekDay(local)],
2014 months[MonthFromTime(local)],
2015 DateFromTime(local),
2016 YearFromTime(local));
2017 break;
2018 case FORMATSPEC_TIME:
2019 /* 09:41:40 GMT-0800 (PST) */
2020 JS_snprintf(buf, sizeof buf,
2021 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2022 HourFromTime(local),
2023 MinFromTime(local),
2024 SecFromTime(local),
2025 offset,
2026 usetz ? " " : "",
2027 usetz ? tzbuf : "");
2028 break;
2032 str = JS_NewStringCopyZ(cx, buf);
2033 if (!str)
2034 return JS_FALSE;
2035 rval->setString(str);
2036 return JS_TRUE;
2039 static JSBool
2040 date_toLocaleHelper(JSContext *cx, const char *format, Value *vp)
2042 JSObject *obj;
2043 char buf[100];
2044 JSString *str;
2045 PRMJTime split;
2046 jsdouble utctime;
2048 obj = ComputeThisObjectFromVp(cx, vp);
2049 if (!GetUTCTime(cx, obj, vp, &utctime))
2050 return JS_FALSE;
2052 if (!JSDOUBLE_IS_FINITE(utctime)) {
2053 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2054 } else {
2055 intN result_len;
2056 jsdouble local = LocalTime(utctime, cx);
2057 new_explode(local, &split, cx);
2059 /* let PRMJTime format it. */
2060 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2062 /* If it failed, default to toString. */
2063 if (result_len == 0)
2064 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2066 /* Hacked check against undesired 2-digit year 00/00/00 form. */
2067 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2068 /* Format %x means use OS settings, which may have 2-digit yr, so
2069 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2070 !isdigit(buf[result_len - 3]) &&
2071 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2072 /* ...but not if starts with 4-digit year, like 2022/3/11. */
2073 !(isdigit(buf[0]) && isdigit(buf[1]) &&
2074 isdigit(buf[2]) && isdigit(buf[3]))) {
2075 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2076 "%d", js_DateGetYear(cx, obj));
2081 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
2082 return cx->localeCallbacks->localeToUnicode(cx, buf, Jsvalify(vp));
2084 str = JS_NewStringCopyZ(cx, buf);
2085 if (!str)
2086 return JS_FALSE;
2087 vp->setString(str);
2088 return JS_TRUE;
2091 static JSBool
2092 date_toLocaleString(JSContext *cx, uintN argc, Value *vp)
2094 /* Use '%#c' for windows, because '%c' is
2095 * backward-compatible and non-y2k with msvc; '%#c' requests that a
2096 * full year be used in the result string.
2098 return date_toLocaleHelper(cx,
2099 #if defined(_WIN32) && !defined(__MWERKS__)
2100 "%#c"
2101 #else
2102 "%c"
2103 #endif
2104 , vp);
2107 static JSBool
2108 date_toLocaleDateString(JSContext *cx, uintN argc, Value *vp)
2110 /* Use '%#x' for windows, because '%x' is
2111 * backward-compatible and non-y2k with msvc; '%#x' requests that a
2112 * full year be used in the result string.
2114 return date_toLocaleHelper(cx,
2115 #if defined(_WIN32) && !defined(__MWERKS__)
2116 "%#x"
2117 #else
2118 "%x"
2119 #endif
2120 , vp);
2123 static JSBool
2124 date_toLocaleTimeString(JSContext *cx, uintN argc, Value *vp)
2126 return date_toLocaleHelper(cx, "%X", vp);
2129 static JSBool
2130 date_toLocaleFormat(JSContext *cx, uintN argc, Value *vp)
2132 JSString *fmt;
2133 const char *fmtbytes;
2135 if (argc == 0)
2136 return date_toLocaleString(cx, argc, vp);
2138 fmt = js_ValueToString(cx, vp[2]);
2139 if (!fmt)
2140 return JS_FALSE;
2141 vp[2].setString(fmt);
2142 fmtbytes = js_GetStringBytes(cx, fmt);
2143 if (!fmtbytes)
2144 return JS_FALSE;
2146 return date_toLocaleHelper(cx, fmtbytes, vp);
2149 static JSBool
2150 date_toTimeString(JSContext *cx, uintN argc, Value *vp)
2152 jsdouble utctime;
2154 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
2155 return JS_FALSE;
2156 return date_format(cx, utctime, FORMATSPEC_TIME, vp);
2159 static JSBool
2160 date_toDateString(JSContext *cx, uintN argc, Value *vp)
2162 jsdouble utctime;
2164 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
2165 return JS_FALSE;
2166 return date_format(cx, utctime, FORMATSPEC_DATE, vp);
2169 #if JS_HAS_TOSOURCE
2170 #include <string.h>
2171 #include "jsdtoa.h"
2173 static JSBool
2174 date_toSource(JSContext *cx, uintN argc, Value *vp)
2176 jsdouble utctime;
2177 char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
2178 JSString *str;
2180 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
2181 return JS_FALSE;
2183 numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
2184 if (!numStr) {
2185 JS_ReportOutOfMemory(cx);
2186 return JS_FALSE;
2189 bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
2190 if (!bytes) {
2191 JS_ReportOutOfMemory(cx);
2192 return JS_FALSE;
2195 str = JS_NewString(cx, bytes, strlen(bytes));
2196 if (!str) {
2197 js_free(bytes);
2198 return JS_FALSE;
2200 vp->setString(str);
2201 return JS_TRUE;
2203 #endif
2205 static JSBool
2206 date_toString(JSContext *cx, uintN argc, Value *vp)
2208 jsdouble utctime;
2210 if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
2211 return JS_FALSE;
2212 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2215 static JSBool
2216 date_valueOf(JSContext *cx, uintN argc, Value *vp)
2218 JSString *str, *number_str;
2220 /* It is an error to call date_valueOf on a non-date object, but we don't
2221 * need to check for that explicitly here because every path calls
2222 * GetUTCTime, which does the check.
2225 /* If called directly with no arguments, convert to a time number. */
2226 if (argc == 0)
2227 return date_getTime(cx, argc, vp);
2229 /* Convert to number only if the hint was given, otherwise favor string. */
2230 str = js_ValueToString(cx, vp[2]);
2231 if (!str)
2232 return JS_FALSE;
2233 number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
2234 if (js_EqualStrings(str, number_str))
2235 return date_getTime(cx, argc, vp);
2236 return date_toString(cx, argc, vp);
2239 // Don't really need an argument here, but we don't support arg-less builtins
2240 JS_DEFINE_TRCINFO_1(date_now,
2241 (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, nanojit::ACC_STORE_ANY)))
2243 static JSFunctionSpec date_static_methods[] = {
2244 JS_FN("UTC", date_UTC, MAXARGS,0),
2245 JS_FN("parse", date_parse, 1,0),
2246 JS_TN("now", date_now, 0,0, &date_now_trcinfo),
2247 JS_FS_END
2250 static JSFunctionSpec date_methods[] = {
2251 JS_FN("getTime", date_getTime, 0,0),
2252 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2253 JS_FN("getYear", date_getYear, 0,0),
2254 JS_FN("getFullYear", date_getFullYear, 0,0),
2255 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2256 JS_FN("getMonth", date_getMonth, 0,0),
2257 JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2258 JS_FN("getDate", date_getDate, 0,0),
2259 JS_FN("getUTCDate", date_getUTCDate, 0,0),
2260 JS_FN("getDay", date_getDay, 0,0),
2261 JS_FN("getUTCDay", date_getUTCDay, 0,0),
2262 JS_FN("getHours", date_getHours, 0,0),
2263 JS_FN("getUTCHours", date_getUTCHours, 0,0),
2264 JS_FN("getMinutes", date_getMinutes, 0,0),
2265 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2266 JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2267 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2268 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2269 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2270 JS_FN("setTime", date_setTime, 1,0),
2271 JS_FN("setYear", date_setYear, 1,0),
2272 JS_FN("setFullYear", date_setFullYear, 3,0),
2273 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2274 JS_FN("setMonth", date_setMonth, 2,0),
2275 JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2276 JS_FN("setDate", date_setDate, 1,0),
2277 JS_FN("setUTCDate", date_setUTCDate, 1,0),
2278 JS_FN("setHours", date_setHours, 4,0),
2279 JS_FN("setUTCHours", date_setUTCHours, 4,0),
2280 JS_FN("setMinutes", date_setMinutes, 3,0),
2281 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2282 JS_FN("setSeconds", date_setSeconds, 2,0),
2283 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2284 JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2285 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2286 JS_FN("toUTCString", date_toGMTString, 0,0),
2287 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2288 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2289 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2290 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2291 JS_FN("toDateString", date_toDateString, 0,0),
2292 JS_FN("toTimeString", date_toTimeString, 0,0),
2293 JS_FN("toISOString", date_toISOString, 0,0),
2294 JS_FN(js_toJSON_str, date_toISOString, 0,0),
2295 #if JS_HAS_TOSOURCE
2296 JS_FN(js_toSource_str, date_toSource, 0,0),
2297 #endif
2298 JS_FN(js_toString_str, date_toString, 0,0),
2299 JS_FN(js_valueOf_str, date_valueOf, 0,0),
2300 JS_FS_END
2303 JSBool
2304 js_Date(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
2306 /* Date called as function. */
2307 if (!JS_IsConstructing(cx))
2308 return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, rval);
2310 /* Date called as constructor. */
2311 jsdouble d;
2312 if (argc == 0) {
2313 d = NowAsMillis();
2314 } else if (argc == 1) {
2315 if (!argv[0].isString()) {
2316 /* the argument is a millisecond number */
2317 if (!ValueToNumber(cx, argv[0], &d))
2318 return JS_FALSE;
2319 d = TIMECLIP(d);
2320 } else {
2321 /* the argument is a string; parse it. */
2322 JSString *str = js_ValueToString(cx, argv[0]);
2323 if (!str)
2324 return JS_FALSE;
2325 argv[0].setString(str);
2327 if (!date_parseString(str, &d, cx))
2328 d = js_NaN;
2329 else
2330 d = TIMECLIP(d);
2332 } else {
2333 jsdouble msec_time;
2334 if (!date_msecFromArgs(cx, argc, argv, &msec_time))
2335 return JS_FALSE;
2337 if (JSDOUBLE_IS_FINITE(msec_time)) {
2338 msec_time = UTC(msec_time, cx);
2339 msec_time = TIMECLIP(msec_time);
2341 d = msec_time;
2343 return SetUTCTime(cx, obj, d);
2346 JSObject *
2347 js_InitDateClass(JSContext *cx, JSObject *obj)
2349 JSObject *proto;
2351 /* set static LocalTZA */
2352 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2353 proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date,
2354 MAXARGS, NULL, date_methods, NULL,
2355 date_static_methods);
2356 if (!proto)
2357 return NULL;
2359 SetDateToNaN(cx, proto);
2361 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2362 if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2363 return NULL;
2365 return proto;
2368 JS_FRIEND_API(JSObject *)
2369 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2371 JSObject *obj = NewObject(cx, &js_DateClass, NULL, NULL);
2372 if (!obj || !SetUTCTime(cx, obj, msec_time))
2373 return NULL;
2374 return obj;
2377 JS_FRIEND_API(JSObject *)
2378 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2379 int hour, int min, int sec)
2381 JSObject *obj;
2382 jsdouble msec_time;
2384 JS_ASSERT(mon < 12);
2385 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2386 obj = js_NewDateObjectMsec(cx, UTC(msec_time, cx));
2387 return obj;
2390 JS_FRIEND_API(JSBool)
2391 js_DateIsValid(JSContext *cx, JSObject* obj)
2393 jsdouble utctime;
2394 return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime);
2397 JS_FRIEND_API(int)
2398 js_DateGetYear(JSContext *cx, JSObject* obj)
2400 jsdouble localtime;
2402 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2403 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2404 JSDOUBLE_IS_NaN(localtime)) {
2405 return 0;
2408 return (int) YearFromTime(localtime);
2411 JS_FRIEND_API(int)
2412 js_DateGetMonth(JSContext *cx, JSObject* obj)
2414 jsdouble localtime;
2416 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2417 JSDOUBLE_IS_NaN(localtime)) {
2418 return 0;
2421 return (int) MonthFromTime(localtime);
2424 JS_FRIEND_API(int)
2425 js_DateGetDate(JSContext *cx, JSObject* obj)
2427 jsdouble localtime;
2429 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2430 JSDOUBLE_IS_NaN(localtime)) {
2431 return 0;
2434 return (int) DateFromTime(localtime);
2437 JS_FRIEND_API(int)
2438 js_DateGetHours(JSContext *cx, JSObject* obj)
2440 jsdouble localtime;
2442 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2443 JSDOUBLE_IS_NaN(localtime)) {
2444 return 0;
2447 return (int) HourFromTime(localtime);
2450 JS_FRIEND_API(int)
2451 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2453 jsdouble localtime;
2455 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2456 JSDOUBLE_IS_NaN(localtime)) {
2457 return 0;
2460 return (int) MinFromTime(localtime);
2463 JS_FRIEND_API(int)
2464 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2466 jsdouble utctime;
2468 if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime))
2469 return 0;
2471 return (int) SecFromTime(utctime);
2474 JS_FRIEND_API(void)
2475 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2477 jsdouble local;
2479 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2480 return;
2482 /* reset date if it was NaN */
2483 if (JSDOUBLE_IS_NaN(local))
2484 local = 0;
2486 local = date_msecFromDate(year,
2487 MonthFromTime(local),
2488 DateFromTime(local),
2489 HourFromTime(local),
2490 MinFromTime(local),
2491 SecFromTime(local),
2492 msFromTime(local));
2494 /* SetUTCTime also invalidates local time cache. */
2495 SetUTCTime(cx, obj, UTC(local, cx));
2498 JS_FRIEND_API(void)
2499 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2501 jsdouble local;
2503 JS_ASSERT(month < 12);
2505 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2506 return;
2508 /* bail if date was NaN */
2509 if (JSDOUBLE_IS_NaN(local))
2510 return;
2512 local = date_msecFromDate(YearFromTime(local),
2513 month,
2514 DateFromTime(local),
2515 HourFromTime(local),
2516 MinFromTime(local),
2517 SecFromTime(local),
2518 msFromTime(local));
2519 SetUTCTime(cx, obj, UTC(local, cx));
2522 JS_FRIEND_API(void)
2523 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2525 jsdouble local;
2527 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2528 return;
2530 if (JSDOUBLE_IS_NaN(local))
2531 return;
2533 local = date_msecFromDate(YearFromTime(local),
2534 MonthFromTime(local),
2535 date,
2536 HourFromTime(local),
2537 MinFromTime(local),
2538 SecFromTime(local),
2539 msFromTime(local));
2540 SetUTCTime(cx, obj, UTC(local, cx));
2543 JS_FRIEND_API(void)
2544 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2546 jsdouble local;
2548 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2549 return;
2551 if (JSDOUBLE_IS_NaN(local))
2552 return;
2553 local = date_msecFromDate(YearFromTime(local),
2554 MonthFromTime(local),
2555 DateFromTime(local),
2556 hours,
2557 MinFromTime(local),
2558 SecFromTime(local),
2559 msFromTime(local));
2560 SetUTCTime(cx, obj, UTC(local, cx));
2563 JS_FRIEND_API(void)
2564 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2566 jsdouble local;
2568 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2569 return;
2571 if (JSDOUBLE_IS_NaN(local))
2572 return;
2573 local = date_msecFromDate(YearFromTime(local),
2574 MonthFromTime(local),
2575 DateFromTime(local),
2576 HourFromTime(local),
2577 minutes,
2578 SecFromTime(local),
2579 msFromTime(local));
2580 SetUTCTime(cx, obj, UTC(local, cx));
2583 JS_FRIEND_API(void)
2584 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2586 jsdouble local;
2588 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2589 return;
2591 if (JSDOUBLE_IS_NaN(local))
2592 return;
2593 local = date_msecFromDate(YearFromTime(local),
2594 MonthFromTime(local),
2595 DateFromTime(local),
2596 HourFromTime(local),
2597 MinFromTime(local),
2598 seconds,
2599 msFromTime(local));
2600 SetUTCTime(cx, obj, UTC(local, cx));
2603 JS_FRIEND_API(jsdouble)
2604 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2606 jsdouble utctime;
2607 if (!GetUTCTime(cx, obj, NULL, &utctime))
2608 return 0;
2609 return utctime;
2612 #ifdef JS_THREADSAFE
2613 #include "prinrval.h"
2615 JS_FRIEND_API(uint32)
2616 js_IntervalNow()
2618 return uint32(PR_IntervalToMilliseconds(PR_IntervalNow()));
2621 #else /* !JS_THREADSAFE */
2623 JS_FRIEND_API(uint32)
2624 js_IntervalNow()
2626 return uint32(PRMJ_Now() / PRMJ_USEC_PER_MSEC);
2628 #endif