CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / src / jsdate.cpp
blobfc95ce8ed6f43186841a0aff5117c9b69008de95
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"
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"
74 #include "jsobjinlines.h"
76 using namespace js;
79 * The JS 'Date' object is patterned after the Java 'Date' object.
80 * Here is an script:
82 * today = new Date();
84 * print(today.toLocaleString());
86 * weekDay = today.getDay();
89 * These Java (and ECMA-262) methods are supported:
91 * UTC
92 * getDate (getUTCDate)
93 * getDay (getUTCDay)
94 * getHours (getUTCHours)
95 * getMinutes (getUTCMinutes)
96 * getMonth (getUTCMonth)
97 * getSeconds (getUTCSeconds)
98 * getMilliseconds (getUTCMilliseconds)
99 * getTime
100 * getTimezoneOffset
101 * getYear
102 * getFullYear (getUTCFullYear)
103 * parse
104 * setDate (setUTCDate)
105 * setHours (setUTCHours)
106 * setMinutes (setUTCMinutes)
107 * setMonth (setUTCMonth)
108 * setSeconds (setUTCSeconds)
109 * setMilliseconds (setUTCMilliseconds)
110 * setTime
111 * setYear (setFullYear, setUTCFullYear)
112 * toGMTString (toUTCString)
113 * toLocaleString
114 * toString
117 * These Java methods are not supported
119 * setDay
120 * before
121 * after
122 * equals
123 * hashCode
127 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
128 * definition and reduce dependence on NSPR. NSPR is used to get the current
129 * time in milliseconds, the time zone offset, and the daylight savings time
130 * offset for a given time. NSPR is also used for Date.toLocaleString(), for
131 * locale-specific formatting, and to get a string representing the timezone.
132 * (Which turns out to be platform-dependent.)
134 * To do:
135 * (I did some performance tests by timing how long it took to run what
136 * I had of the js ECMA conformance tests.)
138 * - look at saving results across multiple calls to supporting
139 * functions; the toString functions compute some of the same values
140 * multiple times. Although - I took a quick stab at this, and I lost
141 * rather than gained. (Fractionally.) Hard to tell what compilers/processors
142 * are doing these days.
144 * - look at tweaking function return types to return double instead
145 * of int; this seems to make things run slightly faster sometimes.
146 * (though it could be architecture-dependent.) It'd be good to see
147 * how this does on win32. (Tried it on irix.) Types could use a
148 * general going-over.
152 * Supporting functions - ECMA 15.9.1.*
155 #define HoursPerDay 24.0
156 #define MinutesPerDay (HoursPerDay * MinutesPerHour)
157 #define MinutesPerHour 60.0
158 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
159 #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
160 #define SecondsPerMinute 60.0
162 #if defined(XP_WIN) || defined(XP_OS2)
163 /* Work around msvc double optimization bug by making these runtime values; if
164 * they're available at compile time, msvc optimizes division by them by
165 * computing the reciprocal and multiplying instead of dividing - this loses
166 * when the reciprocal isn't representable in a double.
168 static jsdouble msPerSecond = 1000.0;
169 static jsdouble msPerDay = SecondsPerDay * 1000.0;
170 static jsdouble msPerHour = SecondsPerHour * 1000.0;
171 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
172 #else
173 #define msPerDay (SecondsPerDay * msPerSecond)
174 #define msPerHour (SecondsPerHour * msPerSecond)
175 #define msPerMinute (SecondsPerMinute * msPerSecond)
176 #define msPerSecond 1000.0
177 #endif
179 #define Day(t) floor((t) / msPerDay)
181 static jsdouble
182 TimeWithinDay(jsdouble t)
184 jsdouble result;
185 result = fmod(t, msPerDay);
186 if (result < 0)
187 result += msPerDay;
188 return result;
191 static inline bool
192 IsLeapYear(jsint year)
194 return year % 4 == 0 && (year % 100 || (year % 400 == 0));
197 static inline jsint
198 DaysInYear(jsint year)
200 return IsLeapYear(year) ? 366 : 365;
203 static inline jsint
204 DaysInFebruary(jsint year)
206 return IsLeapYear(year) ? 29 : 28;
209 /* math here has to be f.p, because we need
210 * floor((1968 - 1969) / 4) == -1
212 #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
213 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
214 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
216 static jsint
217 YearFromTime(jsdouble t)
219 jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
220 jsdouble t2 = (jsdouble) TimeFromYear(y);
223 * Adjust the year if the approximation was wrong. Since the year was
224 * computed using the average number of ms per year, it will usually
225 * be wrong for dates within several hours of a year transition.
227 if (t2 > t) {
228 y--;
229 } else {
230 if (t2 + msPerDay * DaysInYear(y) <= t)
231 y++;
233 return y;
236 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
239 * The following array contains the day of year for the first day of
240 * each month, where index 0 is January, and day 0 is January 1.
242 static jsdouble firstDayOfMonth[2][13] = {
243 {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},
244 {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}
247 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]
249 static intN
250 DaysInMonth(jsint year, jsint month)
252 JSBool leap = IsLeapYear(year);
253 intN result = intN(DayFromMonth(month, leap) - DayFromMonth(month-1, leap));
254 return result;
257 static intN
258 MonthFromTime(jsdouble t)
260 intN d, step;
261 jsint year = YearFromTime(t);
262 d = DayWithinYear(t, year);
264 if (d < (step = 31))
265 return 0;
266 if (d < (step += DaysInFebruary(year)))
267 return 1;
268 if (d < (step += 31))
269 return 2;
270 if (d < (step += 30))
271 return 3;
272 if (d < (step += 31))
273 return 4;
274 if (d < (step += 30))
275 return 5;
276 if (d < (step += 31))
277 return 6;
278 if (d < (step += 31))
279 return 7;
280 if (d < (step += 30))
281 return 8;
282 if (d < (step += 31))
283 return 9;
284 if (d < (step += 30))
285 return 10;
286 return 11;
289 static intN
290 DateFromTime(jsdouble t)
292 intN d, step, next;
293 jsint year = YearFromTime(t);
294 d = DayWithinYear(t, year);
296 if (d <= (next = 30))
297 return d + 1;
298 step = next;
299 if (d <= (next += DaysInFebruary(year)))
300 return d - step;
301 step = next;
302 if (d <= (next += 31))
303 return d - step;
304 step = next;
305 if (d <= (next += 30))
306 return d - step;
307 step = next;
308 if (d <= (next += 31))
309 return d - step;
310 step = next;
311 if (d <= (next += 30))
312 return d - step;
313 step = next;
314 if (d <= (next += 31))
315 return d - step;
316 step = next;
317 if (d <= (next += 31))
318 return d - step;
319 step = next;
320 if (d <= (next += 30))
321 return d - step;
322 step = next;
323 if (d <= (next += 31))
324 return d - step;
325 step = next;
326 if (d <= (next += 30))
327 return d - step;
328 step = next;
329 return d - step;
332 static intN
333 WeekDay(jsdouble t)
335 jsint result;
336 result = (jsint) Day(t) + 4;
337 result = result % 7;
338 if (result < 0)
339 result += 7;
340 return (intN) result;
343 #define MakeTime(hour, min, sec, ms) \
344 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
346 static jsdouble
347 MakeDay(jsdouble year, jsdouble month, jsdouble date)
349 JSBool leap;
350 jsdouble yearday;
351 jsdouble monthday;
353 year += floor(month / 12);
355 month = fmod(month, 12.0);
356 if (month < 0)
357 month += 12;
359 leap = IsLeapYear((jsint) year);
361 yearday = floor(TimeFromYear(year) / msPerDay);
362 monthday = DayFromMonth(month, leap);
364 return yearday + monthday + date - 1;
367 #define MakeDate(day, time) ((day) * msPerDay + (time))
370 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
372 * yearStartingWith[0][i] is an example non-leap year where
373 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
375 * yearStartingWith[1][i] is an example leap year where
376 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
378 static jsint yearStartingWith[2][7] = {
379 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
380 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
384 * Find a year for which any given date will fall on the same weekday.
386 * This function should be used with caution when used other than
387 * for determining DST; it hasn't been proven not to produce an
388 * incorrect year for times near year boundaries.
390 static jsint
391 EquivalentYearForDST(jsint year)
393 jsint day;
395 day = (jsint) DayFromYear(year) + 4;
396 day = day % 7;
397 if (day < 0)
398 day += 7;
400 return yearStartingWith[IsLeapYear(year)][day];
403 /* LocalTZA gets set by js_InitDateClass() */
404 static jsdouble LocalTZA;
406 static jsdouble
407 DaylightSavingTA(jsdouble t, JSContext *cx)
409 /* abort if NaN */
410 if (JSDOUBLE_IS_NaN(t))
411 return t;
414 * If earlier than 1970 or after 2038, potentially beyond the ken of
415 * many OSes, map it to an equivalent year before asking.
417 if (t < 0.0 || t > 2145916800000.0) {
418 jsint year = EquivalentYearForDST(YearFromTime(t));
419 jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
420 t = MakeDate(day, TimeWithinDay(t));
423 int64 timeMilliseconds = static_cast<int64>(t);
424 int64 offsetMilliseconds = cx->dstOffsetCache.getDSTOffsetMilliseconds(timeMilliseconds, cx);
425 return static_cast<jsdouble>(offsetMilliseconds);
428 static jsdouble
429 AdjustTime(jsdouble date, JSContext *cx)
431 jsdouble t = DaylightSavingTA(date, cx) + LocalTZA;
432 t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
433 return t;
436 static jsdouble
437 LocalTime(jsdouble t, JSContext *cx)
439 return t + AdjustTime(t, cx);
442 static jsdouble
443 UTC(jsdouble t, JSContext *cx)
445 return t - AdjustTime(t - LocalTZA, cx);
448 static intN
449 HourFromTime(jsdouble t)
451 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
452 if (result < 0)
453 result += (intN)HoursPerDay;
454 return result;
457 static intN
458 MinFromTime(jsdouble t)
460 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
461 if (result < 0)
462 result += (intN)MinutesPerHour;
463 return result;
466 static intN
467 SecFromTime(jsdouble t)
469 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
470 if (result < 0)
471 result += (intN)SecondsPerMinute;
472 return result;
475 static intN
476 msFromTime(jsdouble t)
478 intN result = (intN) fmod(t, msPerSecond);
479 if (result < 0)
480 result += (intN)msPerSecond;
481 return result;
485 * end of ECMA 'support' functions
489 * Other Support routines and definitions
492 Class js_DateClass = {
493 js_Date_str,
494 JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_CLASS_RESERVED_SLOTS) |
495 JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
496 PropertyStub, /* addProperty */
497 PropertyStub, /* delProperty */
498 PropertyStub, /* getProperty */
499 StrictPropertyStub, /* setProperty */
500 EnumerateStub,
501 ResolveStub,
502 ConvertStub
505 /* for use by date_parse */
507 static const char* wtb[] = {
508 "am", "pm",
509 "monday", "tuesday", "wednesday", "thursday", "friday",
510 "saturday", "sunday",
511 "january", "february", "march", "april", "may", "june",
512 "july", "august", "september", "october", "november", "december",
513 "gmt", "ut", "utc",
514 "est", "edt",
515 "cst", "cdt",
516 "mst", "mdt",
517 "pst", "pdt"
518 /* time zone table needs to be expanded */
521 static int ttb[] = {
522 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
523 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
524 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
525 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
526 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
527 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
528 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
531 /* helper for date_parse */
532 static JSBool
533 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
534 int count, int ignoreCase)
536 JSBool result = JS_FALSE;
537 /* return true if matches, otherwise, false */
539 while (count > 0 && s1[s1off] && s2[s2off]) {
540 if (ignoreCase) {
541 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
542 break;
544 } else {
545 if ((jschar)s1[s1off] != s2[s2off]) {
546 break;
549 s1off++;
550 s2off++;
551 count--;
554 if (count == 0) {
555 result = JS_TRUE;
558 return result;
561 /* find UTC time from given date... no 1900 correction! */
562 static jsdouble
563 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
564 jsdouble min, jsdouble sec, jsdouble msec)
566 jsdouble day;
567 jsdouble msec_time;
568 jsdouble result;
570 day = MakeDay(year, mon, mday);
571 msec_time = MakeTime(hour, min, sec, msec);
572 result = MakeDate(day, msec_time);
573 return result;
576 /* compute the time in msec (unclipped) from the given args */
577 #define MAXARGS 7
579 static JSBool
580 date_msecFromArgs(JSContext *cx, uintN argc, Value *argv, jsdouble *rval)
582 uintN loop;
583 jsdouble array[MAXARGS];
584 jsdouble msec_time;
586 for (loop = 0; loop < MAXARGS; loop++) {
587 if (loop < argc) {
588 jsdouble d;
589 if (!ValueToNumber(cx, argv[loop], &d))
590 return JS_FALSE;
591 /* return NaN if any arg is not finite */
592 if (!JSDOUBLE_IS_FINITE(d)) {
593 *rval = js_NaN;
594 return JS_TRUE;
596 array[loop] = js_DoubleToInteger(d);
597 } else {
598 if (loop == 2) {
599 array[loop] = 1; /* Default the date argument to 1. */
600 } else {
601 array[loop] = 0;
606 /* adjust 2-digit years into the 20th century */
607 if (array[0] >= 0 && array[0] <= 99)
608 array[0] += 1900;
610 msec_time = date_msecFromDate(array[0], array[1], array[2],
611 array[3], array[4], array[5], array[6]);
612 *rval = msec_time;
613 return JS_TRUE;
617 * See ECMA 15.9.4.[3-10];
619 static JSBool
620 date_UTC(JSContext *cx, uintN argc, Value *vp)
622 jsdouble msec_time;
624 if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
625 return JS_FALSE;
627 msec_time = TIMECLIP(msec_time);
629 vp->setNumber(msec_time);
630 return JS_TRUE;
634 * Read and convert decimal digits from s[*i] into *result
635 * while *i < limit.
637 * Succeed if any digits are converted. Advance *i only
638 * as digits are consumed.
640 static JSBool
641 digits(size_t *result, const jschar *s, size_t *i, size_t limit)
643 size_t init = *i;
644 *result = 0;
645 while (*i < limit &&
646 ('0' <= s[*i] && s[*i] <= '9')) {
647 *result *= 10;
648 *result += (s[*i] - '0');
649 ++(*i);
651 return (*i != init);
655 * Read and convert decimal digits to the right of a decimal point,
656 * representing a fractional integer, from s[*i] into *result
657 * while *i < limit.
659 * Succeed if any digits are converted. Advance *i only
660 * as digits are consumed.
662 static JSBool
663 fractional(jsdouble *result, const jschar *s, size_t *i, size_t limit)
665 jsdouble factor = 0.1;
666 size_t init = *i;
667 *result = 0.0;
668 while (*i < limit &&
669 ('0' <= s[*i] && s[*i] <= '9')) {
670 *result += (s[*i] - '0') * factor;
671 factor *= 0.1;
672 ++(*i);
674 return (*i != init);
678 * Read and convert exactly n decimal digits from s[*i]
679 * to s[min(*i+n,limit)] into *result.
681 * Succeed if exactly n digits are converted. Advance *i only
682 * on success.
684 static JSBool
685 ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
687 size_t init = *i;
689 if (digits(result, s, i, JS_MIN(limit, init+n)))
690 return ((*i - init) == n);
692 *i = init;
693 return JS_FALSE;
697 * Parse a string in one of the date-time formats given by the W3C
698 * "NOTE-datetime" specification. These formats make up a restricted
699 * profile of the ISO 8601 format. Quoted here:
701 * The formats are as follows. Exactly the components shown here
702 * must be present, with exactly this punctuation. Note that the "T"
703 * appears literally in the string, to indicate the beginning of the
704 * time element, as specified in ISO 8601.
706 * Any combination of the date formats with the time formats is
707 * allowed, and also either the date or the time can be missing.
709 * The specification is silent on the meaning when fields are
710 * ommitted so the interpretations are a guess, but hopefully a
711 * reasonable one. We default the month to January, the day to the
712 * 1st, and hours minutes and seconds all to 0. If the date is
713 * missing entirely then we assume 1970-01-01 so that the time can
714 * be aded to a date later. If the time is missing then we assume
715 * 00:00 UTC. If the time is present but the time zone field is
716 * missing then we use local time.
718 * Date part:
720 * Year:
721 * YYYY (eg 1997)
723 * Year and month:
724 * YYYY-MM (eg 1997-07)
726 * Complete date:
727 * YYYY-MM-DD (eg 1997-07-16)
729 * Time part:
731 * Hours and minutes:
732 * Thh:mmTZD (eg T19:20+01:00)
734 * Hours, minutes and seconds:
735 * Thh:mm:ssTZD (eg T19:20:30+01:00)
737 * Hours, minutes, seconds and a decimal fraction of a second:
738 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
740 * where:
742 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
743 * MM = two-digit month (01=January, etc.)
744 * DD = two-digit day of month (01 through 31)
745 * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
746 * mm = two digits of minute (00 through 59)
747 * ss = two digits of second (00 through 59)
748 * s = one or more digits representing a decimal fraction of a second
749 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
752 static JSBool
753 date_parseISOString(JSLinearString *str, jsdouble *result, JSContext *cx)
755 jsdouble msec;
757 const jschar *s;
758 size_t limit;
759 size_t i = 0;
760 int tzMul = 1;
761 int dateMul = 1;
762 size_t year = 1970;
763 size_t month = 1;
764 size_t day = 1;
765 size_t hour = 0;
766 size_t min = 0;
767 size_t sec = 0;
768 jsdouble frac = 0;
769 bool isLocalTime = JS_FALSE;
770 size_t tzHour = 0;
771 size_t tzMin = 0;
773 #define PEEK(ch) (i < limit && s[i] == ch)
775 #define NEED(ch) \
776 JS_BEGIN_MACRO \
777 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
778 JS_END_MACRO
780 #define DONE_DATE_UNLESS(ch) \
781 JS_BEGIN_MACRO \
782 if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
783 JS_END_MACRO
785 #define DONE_UNLESS(ch) \
786 JS_BEGIN_MACRO \
787 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
788 JS_END_MACRO
790 #define NEED_NDIGITS(n, field) \
791 JS_BEGIN_MACRO \
792 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
793 JS_END_MACRO
795 s = str->chars();
796 limit = str->length();
798 if (PEEK('+') || PEEK('-')) {
799 if (PEEK('-'))
800 dateMul = -1;
801 ++i;
802 NEED_NDIGITS(6, year);
803 } else if (!PEEK('T')) {
804 NEED_NDIGITS(4, year);
806 DONE_DATE_UNLESS('-');
807 NEED_NDIGITS(2, month);
808 DONE_DATE_UNLESS('-');
809 NEED_NDIGITS(2, day);
811 done_date:
812 DONE_UNLESS('T');
813 NEED_NDIGITS(2, hour);
814 NEED(':');
815 NEED_NDIGITS(2, min);
817 if (PEEK(':')) {
818 ++i;
819 NEED_NDIGITS(2, sec);
820 if (PEEK('.')) {
821 ++i;
822 if (!fractional(&frac, s, &i, limit))
823 goto syntax;
827 if (PEEK('Z')) {
828 ++i;
829 } else if (PEEK('+') || PEEK('-')) {
830 if (PEEK('-'))
831 tzMul = -1;
832 ++i;
833 NEED_NDIGITS(2, tzHour);
834 NEED(':');
835 NEED_NDIGITS(2, tzMin);
836 } else {
837 isLocalTime = JS_TRUE;
840 done:
841 if (year > 275943 // ceil(1e8/365) + 1970
842 || (month == 0 || month > 12)
843 || (day == 0 || day > size_t(DaysInMonth(year,month)))
844 || hour > 24
845 || ((hour == 24) && (min > 0 || sec > 0))
846 || min > 59
847 || sec > 59
848 || tzHour > 23
849 || tzMin > 59)
850 goto syntax;
852 if (i != limit)
853 goto syntax;
855 month -= 1; /* convert month to 0-based */
857 msec = date_msecFromDate(dateMul * (jsdouble)year, month, day,
858 hour, min, sec,
859 frac * 1000.0);;
861 if (isLocalTime) {
862 msec = UTC(msec, cx);
863 } else {
864 msec -= ((tzMul) * ((tzHour * msPerHour)
865 + (tzMin * msPerMinute)));
868 if (msec < -8.64e15 || msec > 8.64e15)
869 goto syntax;
871 *result = msec;
873 return JS_TRUE;
875 syntax:
876 /* syntax error */
877 *result = 0;
878 return JS_FALSE;
880 #undef PEEK
881 #undef NEED
882 #undef DONE_UNLESS
883 #undef NEED_NDIGITS
886 static JSBool
887 date_parseString(JSLinearString *str, jsdouble *result, JSContext *cx)
889 jsdouble msec;
891 const jschar *s;
892 size_t limit;
893 size_t i = 0;
894 int year = -1;
895 int mon = -1;
896 int mday = -1;
897 int hour = -1;
898 int min = -1;
899 int sec = -1;
900 int c = -1;
901 int n = -1;
902 int tzoffset = -1;
903 int prevc = 0;
904 JSBool seenplusminus = JS_FALSE;
905 int temp;
906 JSBool seenmonthname = JS_FALSE;
908 if (date_parseISOString(str, result, cx))
909 return JS_TRUE;
911 s = str->chars();
912 limit = str->length();
913 if (limit == 0)
914 goto syntax;
915 while (i < limit) {
916 c = s[i];
917 i++;
918 if (c <= ' ' || c == ',' || c == '-') {
919 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
920 prevc = c;
922 continue;
924 if (c == '(') { /* comments) */
925 int depth = 1;
926 while (i < limit) {
927 c = s[i];
928 i++;
929 if (c == '(') depth++;
930 else if (c == ')')
931 if (--depth <= 0)
932 break;
934 continue;
936 if ('0' <= c && c <= '9') {
937 n = c - '0';
938 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
939 n = n * 10 + c - '0';
940 i++;
943 /* allow TZA before the year, so
944 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
945 * works */
947 /* uses of seenplusminus allow : in TZA, so Java
948 * no-timezone style of GMT+4:30 works
951 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
952 /* make ':' case below change tzoffset */
953 seenplusminus = JS_TRUE;
955 /* offset */
956 if (n < 24)
957 n = n * 60; /* EG. "GMT-3" */
958 else
959 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
960 if (prevc == '+') /* plus means east of GMT */
961 n = -n;
962 if (tzoffset != 0 && tzoffset != -1)
963 goto syntax;
964 tzoffset = n;
965 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
966 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
967 year = n;
968 else
969 goto syntax;
970 } else if (c == ':') {
971 if (hour < 0)
972 hour = /*byte*/ n;
973 else if (min < 0)
974 min = /*byte*/ n;
975 else
976 goto syntax;
977 } else if (c == '/') {
978 /* until it is determined that mon is the actual
979 month, keep it as 1-based rather than 0-based */
980 if (mon < 0)
981 mon = /*byte*/ n;
982 else if (mday < 0)
983 mday = /*byte*/ n;
984 else
985 goto syntax;
986 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
987 goto syntax;
988 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
989 if (tzoffset < 0)
990 tzoffset -= n;
991 else
992 tzoffset += n;
993 } else if (hour >= 0 && min < 0) {
994 min = /*byte*/ n;
995 } else if (prevc == ':' && min >= 0 && sec < 0) {
996 sec = /*byte*/ n;
997 } else if (mon < 0) {
998 mon = /*byte*/n;
999 } else if (mon >= 0 && mday < 0) {
1000 mday = /*byte*/ n;
1001 } else if (mon >= 0 && mday >= 0 && year < 0) {
1002 year = n;
1003 } else {
1004 goto syntax;
1006 prevc = 0;
1007 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
1008 prevc = c;
1009 } else {
1010 size_t st = i - 1;
1011 int k;
1012 while (i < limit) {
1013 c = s[i];
1014 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1015 break;
1016 i++;
1018 if (i <= st + 1)
1019 goto syntax;
1020 for (k = JS_ARRAY_LENGTH(wtb); --k >= 0;)
1021 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
1022 int action = ttb[k];
1023 if (action != 0) {
1024 if (action < 0) {
1026 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1027 * 12:30, instead of blindly adding 12 if PM.
1029 JS_ASSERT(action == -1 || action == -2);
1030 if (hour > 12 || hour < 0) {
1031 goto syntax;
1032 } else {
1033 if (action == -1 && hour == 12) { /* am */
1034 hour = 0;
1035 } else if (action == -2 && hour != 12) { /* pm */
1036 hour += 12;
1039 } else if (action <= 13) { /* month! */
1040 /* Adjust mon to be 1-based until the final values
1041 for mon, mday and year are adjusted below */
1042 if (seenmonthname) {
1043 goto syntax;
1045 seenmonthname = JS_TRUE;
1046 temp = /*byte*/ (action - 2) + 1;
1048 if (mon < 0) {
1049 mon = temp;
1050 } else if (mday < 0) {
1051 mday = mon;
1052 mon = temp;
1053 } else if (year < 0) {
1054 year = mon;
1055 mon = temp;
1056 } else {
1057 goto syntax;
1059 } else {
1060 tzoffset = action - 10000;
1063 break;
1065 if (k < 0)
1066 goto syntax;
1067 prevc = 0;
1070 if (year < 0 || mon < 0 || mday < 0)
1071 goto syntax;
1073 Case 1. The input string contains an English month name.
1074 The form of the string can be month f l, or f month l, or
1075 f l month which each evaluate to the same date.
1076 If f and l are both greater than or equal to 70, or
1077 both less than 70, the date is invalid.
1078 The year is taken to be the greater of the values f, l.
1079 If the year is greater than or equal to 70 and less than 100,
1080 it is considered to be the number of years after 1900.
1081 Case 2. The input string is of the form "f/m/l" where f, m and l are
1082 integers, e.g. 7/16/45.
1083 Adjust the mon, mday and year values to achieve 100% MSIE
1084 compatibility.
1085 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1086 i. If year < 100, it is the number of years after 1900
1087 ii. If year >= 100, it is the number of years after 0.
1088 b. If 70 <= f < 100
1089 i. If m < 70, f/m/l is interpreted as
1090 year/month/day where year is the number of years after
1091 1900.
1092 ii. If m >= 70, the date is invalid.
1093 c. If f >= 100
1094 i. If m < 70, f/m/l is interpreted as
1095 year/month/day where year is the number of years after 0.
1096 ii. If m >= 70, the date is invalid.
1098 if (seenmonthname) {
1099 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
1100 goto syntax;
1102 if (mday > year) {
1103 temp = year;
1104 year = mday;
1105 mday = temp;
1107 if (year >= 70 && year < 100) {
1108 year += 1900;
1110 } else if (mon < 70) { /* (a) month/day/year */
1111 if (year < 100) {
1112 year += 1900;
1114 } else if (mon < 100) { /* (b) year/month/day */
1115 if (mday < 70) {
1116 temp = year;
1117 year = mon + 1900;
1118 mon = mday;
1119 mday = temp;
1120 } else {
1121 goto syntax;
1123 } else { /* (c) year/month/day */
1124 if (mday < 70) {
1125 temp = year;
1126 year = mon;
1127 mon = mday;
1128 mday = temp;
1129 } else {
1130 goto syntax;
1133 mon -= 1; /* convert month to 0-based */
1134 if (sec < 0)
1135 sec = 0;
1136 if (min < 0)
1137 min = 0;
1138 if (hour < 0)
1139 hour = 0;
1141 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1143 if (tzoffset == -1) { /* no time zone specified, have to use local */
1144 msec = UTC(msec, cx);
1145 } else {
1146 msec += tzoffset * msPerMinute;
1149 *result = msec;
1150 return JS_TRUE;
1152 syntax:
1153 /* syntax error */
1154 *result = 0;
1155 return JS_FALSE;
1158 static JSBool
1159 date_parse(JSContext *cx, uintN argc, Value *vp)
1161 JSString *str;
1162 jsdouble result;
1164 if (argc == 0) {
1165 vp->setDouble(js_NaN);
1166 return true;
1168 str = js_ValueToString(cx, vp[2]);
1169 if (!str)
1170 return JS_FALSE;
1171 vp[2].setString(str);
1172 JSLinearString *linearStr = str->ensureLinear(cx);
1173 if (!linearStr)
1174 return false;
1176 if (!date_parseString(linearStr, &result, cx)) {
1177 vp->setDouble(js_NaN);
1178 return true;
1181 result = TIMECLIP(result);
1182 vp->setNumber(result);
1183 return true;
1186 static inline jsdouble
1187 NowAsMillis()
1189 return (jsdouble) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1192 static JSBool
1193 date_now(JSContext *cx, uintN argc, Value *vp)
1195 vp->setDouble(NowAsMillis());
1196 return JS_TRUE;
1199 #ifdef JS_TRACER
1200 static jsdouble FASTCALL
1201 date_now_tn(JSContext*)
1203 return NowAsMillis();
1205 #endif
1208 * Get UTC time from the date object. Returns false if the object is not
1209 * Date type.
1211 static JSBool
1212 GetUTCTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp)
1214 if (!InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1215 return JS_FALSE;
1216 *dp = obj->getDateUTCTime().toNumber();
1217 return JS_TRUE;
1221 * Set UTC time to a given time and invalidate cached local time.
1223 static JSBool
1224 SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL)
1226 JS_ASSERT(obj->isDate());
1228 size_t slotCap = JS_MIN(obj->numSlots(), JSObject::DATE_CLASS_RESERVED_SLOTS);
1229 for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START; ind < slotCap; ind++)
1230 obj->getSlotRef(ind).setUndefined();
1232 obj->setDateUTCTime(DoubleValue(t));
1233 if (vp)
1234 vp->setDouble(t);
1235 return true;
1238 static void
1239 SetDateToNaN(JSContext *cx, JSObject *obj, Value *vp = NULL)
1241 jsdouble NaN = cx->runtime->NaNValue.getDoubleRef();
1242 SetUTCTime(cx, obj, NaN, vp);
1246 * Cache the local time, year, month, and so forth of the object.
1247 * If UTC time is not finite (e.g., NaN), the local time
1248 * slots will be set to the UTC time without conversion.
1250 static bool
1251 FillLocalTimes(JSContext *cx, JSObject *obj)
1253 JS_ASSERT(obj->isDate());
1255 jsdouble utcTime = obj->getDateUTCTime().toNumber();
1257 /* Make sure there are slots to store the cached information. */
1258 if (obj->numSlots() < JSObject::DATE_CLASS_RESERVED_SLOTS) {
1259 if (!obj->growSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
1260 return false;
1263 if (!JSDOUBLE_IS_FINITE(utcTime)) {
1264 for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
1265 ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
1266 ind++) {
1267 obj->setSlot(ind, DoubleValue(utcTime));
1269 return true;
1272 jsdouble localTime = LocalTime(utcTime, cx);
1274 obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_TIME, DoubleValue(localTime));
1276 jsint year = (jsint) floor(localTime /(msPerDay*365.2425)) + 1970;
1277 jsdouble yearStartTime = (jsdouble) TimeFromYear(year);
1279 /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
1280 jsint yearDays;
1281 if (yearStartTime > localTime) {
1282 year--;
1283 yearStartTime -= (msPerDay * DaysInYear(year));
1284 yearDays = DaysInYear(year);
1285 } else {
1286 yearDays = DaysInYear(year);
1287 jsdouble nextStart = yearStartTime + (msPerDay * yearDays);
1288 if (nextStart <= localTime) {
1289 year++;
1290 yearStartTime = nextStart;
1291 yearDays = DaysInYear(year);
1295 obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR, Int32Value(year));
1297 uint64 yearTime = uint64(localTime - yearStartTime);
1298 jsint yearSeconds = uint32(yearTime / 1000);
1300 jsint day = yearSeconds / jsint(SecondsPerDay);
1302 jsint step = -1, next = 30;
1303 jsint month;
1305 do {
1306 if (day <= next) {
1307 month = 0;
1308 break;
1310 step = next;
1311 next += ((yearDays == 366) ? 29 : 28);
1312 if (day <= next) {
1313 month = 1;
1314 break;
1316 step = next;
1317 if (day <= (next += 31)) {
1318 month = 2;
1319 break;
1321 step = next;
1322 if (day <= (next += 30)) {
1323 month = 3;
1324 break;
1326 step = next;
1327 if (day <= (next += 31)) {
1328 month = 4;
1329 break;
1331 step = next;
1332 if (day <= (next += 30)) {
1333 month = 5;
1334 break;
1336 step = next;
1337 if (day <= (next += 31)) {
1338 month = 6;
1339 break;
1341 step = next;
1342 if (day <= (next += 31)) {
1343 month = 7;
1344 break;
1346 step = next;
1347 if (day <= (next += 30)) {
1348 month = 8;
1349 break;
1351 step = next;
1352 if (day <= (next += 31)) {
1353 month = 9;
1354 break;
1356 step = next;
1357 if (day <= (next += 30)) {
1358 month = 10;
1359 break;
1361 step = next;
1362 month = 11;
1363 } while (0);
1365 obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH, Int32Value(month));
1366 obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DATE, Int32Value(day - step));
1368 jsint weekday = WeekDay(localTime);
1370 obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DAY, Int32Value(weekday));
1372 jsint seconds = yearSeconds % 60;
1374 obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS, Int32Value(seconds));
1376 jsint minutes = (yearSeconds / 60) % 60;
1378 obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES, Int32Value(minutes));
1380 jsint hours = (yearSeconds / (60 * 60)) % 24;
1382 obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS, Int32Value(hours));
1384 return true;
1387 /* Cache the local times in obj, if necessary. */
1388 static inline JSBool
1389 GetAndCacheLocalTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *time = NULL)
1391 if (!obj || !InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1392 return false;
1394 /* If the local time is undefined, we need to fill in the cached values. */
1395 if (obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).isUndefined()) {
1396 if (!FillLocalTimes(cx, obj))
1397 return false;
1400 if (time)
1401 *time = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).toDouble();
1403 return true;
1406 static inline bool
1407 GetThisUTCTime(JSContext *cx, Value *vp, jsdouble *dp)
1409 JSObject *obj = ToObject(cx, &vp[1]);
1410 if (!obj)
1411 return false;
1412 return GetUTCTime(cx, obj, vp, dp);
1416 * See ECMA 15.9.5.4 thru 15.9.5.23
1418 static JSBool
1419 date_getTime(JSContext *cx, uintN argc, Value *vp)
1421 jsdouble result;
1422 if (!GetThisUTCTime(cx, vp, &result))
1423 return false;
1424 vp->setNumber(result);
1425 return true;
1428 static JSBool
1429 date_getYear(JSContext *cx, uintN argc, Value *vp)
1431 JSObject *obj = ToObject(cx, &vp[1]);
1432 if (!obj)
1433 return false;
1435 if (!GetAndCacheLocalTime(cx, obj, vp))
1436 return false;
1438 Value yearVal = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
1439 if (yearVal.isInt32()) {
1440 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1441 jsint year = yearVal.toInt32() - 1900;
1442 vp->setInt32(year);
1443 } else {
1444 *vp = yearVal;
1447 return true;
1450 static JSBool
1451 date_getFullYear(JSContext *cx, uintN argc, Value *vp)
1453 JSObject *obj = ToObject(cx, &vp[1]);
1454 if (!obj)
1455 return false;
1457 if (!GetAndCacheLocalTime(cx, obj, vp))
1458 return JS_FALSE;
1460 *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
1461 return JS_TRUE;
1464 static JSBool
1465 date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp)
1467 jsdouble result;
1468 if (!GetThisUTCTime(cx, vp, &result))
1469 return false;
1471 if (JSDOUBLE_IS_FINITE(result))
1472 result = YearFromTime(result);
1474 vp->setNumber(result);
1475 return true;
1478 static JSBool
1479 date_getMonth(JSContext *cx, uintN argc, Value *vp)
1481 JSObject *obj = ToObject(cx, &vp[1]);
1482 if (!obj)
1483 return false;
1485 if (!GetAndCacheLocalTime(cx, obj, vp))
1486 return false;
1488 *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH);
1489 return true;
1492 static JSBool
1493 date_getUTCMonth(JSContext *cx, uintN argc, Value *vp)
1495 jsdouble result;
1496 if (!GetThisUTCTime(cx, vp, &result))
1497 return false;
1499 if (JSDOUBLE_IS_FINITE(result))
1500 result = MonthFromTime(result);
1502 vp->setNumber(result);
1503 return true;
1506 static JSBool
1507 date_getDate(JSContext *cx, uintN argc, Value *vp)
1509 JSObject *obj = ToObject(cx, &vp[1]);
1510 if (!obj)
1511 return false;
1513 if (!GetAndCacheLocalTime(cx, obj, vp))
1514 return false;
1516 *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE);
1517 return true;
1520 static JSBool
1521 date_getUTCDate(JSContext *cx, uintN argc, Value *vp)
1523 jsdouble result;
1524 if (!GetThisUTCTime(cx, vp, &result))
1525 return false;
1527 if (JSDOUBLE_IS_FINITE(result))
1528 result = DateFromTime(result);
1530 vp->setNumber(result);
1531 return true;
1534 static JSBool
1535 date_getDay(JSContext *cx, uintN argc, Value *vp)
1537 JSObject *obj = ToObject(cx, &vp[1]);
1538 if (!obj)
1539 return false;
1541 if (!GetAndCacheLocalTime(cx, obj, vp))
1542 return false;
1544 *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY);
1545 return true;
1548 static JSBool
1549 date_getUTCDay(JSContext *cx, uintN argc, Value *vp)
1551 jsdouble result;
1552 if (!GetThisUTCTime(cx, vp, &result))
1553 return false;
1555 if (JSDOUBLE_IS_FINITE(result))
1556 result = WeekDay(result);
1558 vp->setNumber(result);
1559 return true;
1562 static JSBool
1563 date_getHours(JSContext *cx, uintN argc, Value *vp)
1565 JSObject *obj = ToObject(cx, &vp[1]);
1566 if (!obj)
1567 return false;
1569 if (!GetAndCacheLocalTime(cx, obj, vp))
1570 return false;
1572 *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS);
1573 return true;
1576 static JSBool
1577 date_getUTCHours(JSContext *cx, uintN argc, Value *vp)
1579 jsdouble result;
1580 if (!GetThisUTCTime(cx, vp, &result))
1581 return false;
1583 if (JSDOUBLE_IS_FINITE(result))
1584 result = HourFromTime(result);
1586 vp->setNumber(result);
1587 return JS_TRUE;
1590 static JSBool
1591 date_getMinutes(JSContext *cx, uintN argc, Value *vp)
1593 JSObject *obj = ToObject(cx, &vp[1]);
1594 if (!obj)
1595 return false;
1597 if (!GetAndCacheLocalTime(cx, obj, vp))
1598 return false;
1600 *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES);
1601 return true;
1604 static JSBool
1605 date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp)
1607 jsdouble result;
1608 if (!GetThisUTCTime(cx, vp, &result))
1609 return false;
1611 if (JSDOUBLE_IS_FINITE(result))
1612 result = MinFromTime(result);
1614 vp->setNumber(result);
1615 return true;
1618 /* Date.getSeconds is mapped to getUTCSeconds */
1620 static JSBool
1621 date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp)
1623 JSObject *obj = ToObject(cx, &vp[1]);
1624 if (!obj)
1625 return false;
1627 if (!GetAndCacheLocalTime(cx, obj, vp))
1628 return false;
1630 *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS);
1631 return true;
1634 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1636 static JSBool
1637 date_getUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
1639 jsdouble result;
1640 if (!GetThisUTCTime(cx, vp, &result))
1641 return false;
1643 if (JSDOUBLE_IS_FINITE(result))
1644 result = msFromTime(result);
1646 vp->setNumber(result);
1647 return true;
1650 static JSBool
1651 date_getTimezoneOffset(JSContext *cx, uintN argc, Value *vp)
1653 JSObject *obj = ToObject(cx, &vp[1]);
1654 if (!obj)
1655 return false;
1657 jsdouble utctime;
1658 if (!GetUTCTime(cx, obj, vp, &utctime))
1659 return false;
1661 jsdouble localtime;
1662 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime))
1663 return false;
1666 * Return the time zone offset in minutes for the current locale that is
1667 * appropriate for this time. This value would be a constant except for
1668 * daylight savings time.
1670 jsdouble result = (utctime - localtime) / msPerMinute;
1671 vp->setNumber(result);
1672 return true;
1675 static JSBool
1676 date_setTime(JSContext *cx, uintN argc, Value *vp)
1678 JSObject *obj = ToObject(cx, &vp[1]);
1679 if (!obj)
1680 return false;
1682 if (!InstanceOf(cx, obj, &js_DateClass, vp + 2))
1683 return false;
1685 if (argc == 0) {
1686 SetDateToNaN(cx, obj, vp);
1687 return true;
1690 jsdouble result;
1691 if (!ValueToNumber(cx, vp[2], &result))
1692 return false;
1694 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1697 static JSBool
1698 date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp)
1700 Value *argv;
1701 uintN i;
1702 jsdouble args[4], *argp, *stop;
1703 jsdouble hour, min, sec, msec;
1704 jsdouble lorutime; /* Local or UTC version of *date */
1706 jsdouble msec_time;
1707 jsdouble result;
1709 JSObject *obj = ToObject(cx, &vp[1]);
1710 if (!obj)
1711 return false;
1713 if (!GetUTCTime(cx, obj, vp, &result))
1714 return false;
1716 /* just return NaN if the date is already NaN */
1717 if (!JSDOUBLE_IS_FINITE(result)) {
1718 vp->setNumber(result);
1719 return true;
1723 * Satisfy the ECMA rule that if a function is called with
1724 * fewer arguments than the specified formal arguments, the
1725 * remaining arguments are set to undefined. Seems like all
1726 * the Date.setWhatever functions in ECMA are only varargs
1727 * beyond the first argument; this should be set to undefined
1728 * if it's not given. This means that "d = new Date();
1729 * d.setMilliseconds()" returns NaN. Blech.
1731 if (argc == 0) {
1732 SetDateToNaN(cx, obj, vp);
1733 return true;
1735 if (argc > maxargs)
1736 argc = maxargs; /* clamp argc */
1737 JS_ASSERT(argc <= 4);
1739 argv = vp + 2;
1740 for (i = 0; i < argc; i++) {
1741 if (!ValueToNumber(cx, argv[i], &args[i]))
1742 return false;
1743 if (!JSDOUBLE_IS_FINITE(args[i])) {
1744 SetDateToNaN(cx, obj, vp);
1745 return true;
1747 args[i] = js_DoubleToInteger(args[i]);
1750 if (local)
1751 lorutime = LocalTime(result, cx);
1752 else
1753 lorutime = result;
1755 argp = args;
1756 stop = argp + argc;
1757 if (maxargs >= 4 && argp < stop)
1758 hour = *argp++;
1759 else
1760 hour = HourFromTime(lorutime);
1762 if (maxargs >= 3 && argp < stop)
1763 min = *argp++;
1764 else
1765 min = MinFromTime(lorutime);
1767 if (maxargs >= 2 && argp < stop)
1768 sec = *argp++;
1769 else
1770 sec = SecFromTime(lorutime);
1772 if (maxargs >= 1 && argp < stop)
1773 msec = *argp;
1774 else
1775 msec = msFromTime(lorutime);
1777 msec_time = MakeTime(hour, min, sec, msec);
1778 result = MakeDate(Day(lorutime), msec_time);
1780 /* fprintf(stderr, "%f\n", result); */
1782 if (local)
1783 result = UTC(result, cx);
1785 /* fprintf(stderr, "%f\n", result); */
1787 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1790 static JSBool
1791 date_setMilliseconds(JSContext *cx, uintN argc, Value *vp)
1793 return date_makeTime(cx, 1, JS_TRUE, argc, vp);
1796 static JSBool
1797 date_setUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
1799 return date_makeTime(cx, 1, JS_FALSE, argc, vp);
1802 static JSBool
1803 date_setSeconds(JSContext *cx, uintN argc, Value *vp)
1805 return date_makeTime(cx, 2, JS_TRUE, argc, vp);
1808 static JSBool
1809 date_setUTCSeconds(JSContext *cx, uintN argc, Value *vp)
1811 return date_makeTime(cx, 2, JS_FALSE, argc, vp);
1814 static JSBool
1815 date_setMinutes(JSContext *cx, uintN argc, Value *vp)
1817 return date_makeTime(cx, 3, JS_TRUE, argc, vp);
1820 static JSBool
1821 date_setUTCMinutes(JSContext *cx, uintN argc, Value *vp)
1823 return date_makeTime(cx, 3, JS_FALSE, argc, vp);
1826 static JSBool
1827 date_setHours(JSContext *cx, uintN argc, Value *vp)
1829 return date_makeTime(cx, 4, JS_TRUE, argc, vp);
1832 static JSBool
1833 date_setUTCHours(JSContext *cx, uintN argc, Value *vp)
1835 return date_makeTime(cx, 4, JS_FALSE, argc, vp);
1838 static JSBool
1839 date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp)
1841 Value *argv;
1842 uintN i;
1843 jsdouble lorutime; /* local or UTC version of *date */
1844 jsdouble args[3], *argp, *stop;
1845 jsdouble year, month, day;
1846 jsdouble result;
1848 JSObject *obj = ToObject(cx, &vp[1]);
1849 if (!obj)
1850 return false;
1852 if (!GetUTCTime(cx, obj, vp, &result))
1853 return false;
1855 /* see complaint about ECMA in date_MakeTime */
1856 if (argc == 0) {
1857 SetDateToNaN(cx, obj, vp);
1858 return true;
1860 if (argc > maxargs)
1861 argc = maxargs; /* clamp argc */
1862 JS_ASSERT(1 <= argc && argc <= 3);
1864 argv = vp + 2;
1865 for (i = 0; i < argc; i++) {
1866 if (!ValueToNumber(cx, argv[i], &args[i]))
1867 return JS_FALSE;
1868 if (!JSDOUBLE_IS_FINITE(args[i])) {
1869 SetDateToNaN(cx, obj, vp);
1870 return true;
1872 args[i] = js_DoubleToInteger(args[i]);
1875 /* return NaN if date is NaN and we're not setting the year,
1876 * If we are, use 0 as the time. */
1877 if (!(JSDOUBLE_IS_FINITE(result))) {
1878 if (maxargs < 3) {
1879 vp->setDouble(result);
1880 return true;
1882 lorutime = +0.;
1883 } else {
1884 lorutime = local ? LocalTime(result, cx) : result;
1887 argp = args;
1888 stop = argp + argc;
1889 if (maxargs >= 3 && argp < stop)
1890 year = *argp++;
1891 else
1892 year = YearFromTime(lorutime);
1894 if (maxargs >= 2 && argp < stop)
1895 month = *argp++;
1896 else
1897 month = MonthFromTime(lorutime);
1899 if (maxargs >= 1 && argp < stop)
1900 day = *argp++;
1901 else
1902 day = DateFromTime(lorutime);
1904 day = MakeDay(year, month, day); /* day within year */
1905 result = MakeDate(day, TimeWithinDay(lorutime));
1907 if (local)
1908 result = UTC(result, cx);
1910 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1913 static JSBool
1914 date_setDate(JSContext *cx, uintN argc, Value *vp)
1916 return date_makeDate(cx, 1, JS_TRUE, argc, vp);
1919 static JSBool
1920 date_setUTCDate(JSContext *cx, uintN argc, Value *vp)
1922 return date_makeDate(cx, 1, JS_FALSE, argc, vp);
1925 static JSBool
1926 date_setMonth(JSContext *cx, uintN argc, Value *vp)
1928 return date_makeDate(cx, 2, JS_TRUE, argc, vp);
1931 static JSBool
1932 date_setUTCMonth(JSContext *cx, uintN argc, Value *vp)
1934 return date_makeDate(cx, 2, JS_FALSE, argc, vp);
1937 static JSBool
1938 date_setFullYear(JSContext *cx, uintN argc, Value *vp)
1940 return date_makeDate(cx, 3, JS_TRUE, argc, vp);
1943 static JSBool
1944 date_setUTCFullYear(JSContext *cx, uintN argc, Value *vp)
1946 return date_makeDate(cx, 3, JS_FALSE, argc, vp);
1949 static JSBool
1950 date_setYear(JSContext *cx, uintN argc, Value *vp)
1952 JSObject *obj = ToObject(cx, &vp[1]);
1953 if (!obj)
1954 return false;
1956 jsdouble result;
1957 if (!GetUTCTime(cx, obj, vp, &result))
1958 return false;
1960 if (argc == 0) {
1961 /* Call this only after GetUTCTime has verified that obj is Date. */
1962 SetDateToNaN(cx, obj, vp);
1963 return true;
1966 jsdouble year;
1967 if (!ValueToNumber(cx, vp[2], &year))
1968 return false;
1969 if (!JSDOUBLE_IS_FINITE(year)) {
1970 SetDateToNaN(cx, obj, vp);
1971 return true;
1973 year = js_DoubleToInteger(year);
1974 if (year >= 0 && year <= 99)
1975 year += 1900;
1977 jsdouble t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result, cx) : +0.0;
1978 jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1979 result = MakeDate(day, TimeWithinDay(t));
1980 result = UTC(result, cx);
1982 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1985 /* constants for toString, toUTCString */
1986 static char js_NaN_date_str[] = "Invalid Date";
1987 static const char* days[] =
1989 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1991 static const char* months[] =
1993 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1997 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1998 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1999 static void
2000 print_gmt_string(char* buf, size_t size, jsdouble utctime)
2002 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
2003 days[WeekDay(utctime)],
2004 DateFromTime(utctime),
2005 months[MonthFromTime(utctime)],
2006 YearFromTime(utctime),
2007 HourFromTime(utctime),
2008 MinFromTime(utctime),
2009 SecFromTime(utctime));
2012 static void
2013 print_iso_string(char* buf, size_t size, jsdouble utctime)
2015 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2016 YearFromTime(utctime),
2017 MonthFromTime(utctime) + 1,
2018 DateFromTime(utctime),
2019 HourFromTime(utctime),
2020 MinFromTime(utctime),
2021 SecFromTime(utctime),
2022 msFromTime(utctime));
2025 static JSBool
2026 date_utc_format(JSContext *cx, Value *vp,
2027 void (*printFunc)(char*, size_t, jsdouble))
2029 jsdouble utctime;
2030 if (!GetThisUTCTime(cx, vp, &utctime))
2031 return false;
2033 char buf[100];
2034 if (!JSDOUBLE_IS_FINITE(utctime))
2035 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2036 else
2037 (*printFunc)(buf, sizeof buf, utctime);
2039 JSString *str = JS_NewStringCopyZ(cx, buf);
2040 if (!str)
2041 return false;
2042 vp->setString(str);
2043 return true;
2046 static JSBool
2047 date_toGMTString(JSContext *cx, uintN argc, Value *vp)
2049 return date_utc_format(cx, vp, print_gmt_string);
2052 static JSBool
2053 date_toISOString(JSContext *cx, uintN argc, Value *vp)
2055 return date_utc_format(cx, vp, print_iso_string);
2058 /* ES5 15.9.5.44. */
2059 static JSBool
2060 date_toJSON(JSContext *cx, uintN argc, Value *vp)
2062 /* Step 1. */
2063 JSObject *obj = ToObject(cx, &vp[1]);
2064 if (!obj)
2065 return false;
2067 /* Step 2. */
2068 Value &tv = vp[0];
2069 if (!DefaultValue(cx, obj, JSTYPE_NUMBER, &tv))
2070 return false;
2072 /* Step 3. */
2073 if (tv.isDouble() && !JSDOUBLE_IS_FINITE(tv.toDouble())) {
2074 vp->setNull();
2075 return true;
2078 /* Step 4. */
2079 Value &toISO = vp[0];
2080 if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.toISOStringAtom), &toISO))
2081 return false;
2083 /* Step 5. */
2084 if (!js_IsCallable(toISO)) {
2085 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
2086 JSMSG_BAD_TOISOSTRING_PROP);
2087 return false;
2090 /* Step 6. */
2091 LeaveTrace(cx);
2092 InvokeArgsGuard args;
2093 if (!cx->stack().pushInvokeArgs(cx, 0, &args))
2094 return false;
2096 args.callee() = toISO;
2097 args.thisv().setObject(*obj);
2099 if (!Invoke(cx, args, 0))
2100 return false;
2101 *vp = args.rval();
2102 return true;
2105 /* for Date.toLocaleString; interface to PRMJTime date struct.
2107 static void
2108 new_explode(jsdouble timeval, PRMJTime *split, JSContext *cx)
2110 jsint year = YearFromTime(timeval);
2112 split->tm_usec = (int32) msFromTime(timeval) * 1000;
2113 split->tm_sec = (int8) SecFromTime(timeval);
2114 split->tm_min = (int8) MinFromTime(timeval);
2115 split->tm_hour = (int8) HourFromTime(timeval);
2116 split->tm_mday = (int8) DateFromTime(timeval);
2117 split->tm_mon = (int8) MonthFromTime(timeval);
2118 split->tm_wday = (int8) WeekDay(timeval);
2119 split->tm_year = year;
2120 split->tm_yday = (int16) DayWithinYear(timeval, year);
2122 /* not sure how this affects things, but it doesn't seem
2123 to matter. */
2124 split->tm_isdst = (DaylightSavingTA(timeval, cx) != 0);
2127 typedef enum formatspec {
2128 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
2129 } formatspec;
2131 /* helper function */
2132 static JSBool
2133 date_format(JSContext *cx, jsdouble date, formatspec format, Value *rval)
2135 char buf[100];
2136 JSString *str;
2137 char tzbuf[100];
2138 JSBool usetz;
2139 size_t i, tzlen;
2140 PRMJTime split;
2142 if (!JSDOUBLE_IS_FINITE(date)) {
2143 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2144 } else {
2145 jsdouble local = LocalTime(date, cx);
2147 /* offset from GMT in minutes. The offset includes daylight savings,
2148 if it applies. */
2149 jsint minutes = (jsint) floor(AdjustTime(date, cx) / msPerMinute);
2151 /* map 510 minutes to 0830 hours */
2152 intN offset = (minutes / 60) * 100 + minutes % 60;
2154 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
2155 * printed as 'GMT-0800' rather than as 'PST' to avoid
2156 * operating-system dependence on strftime (which
2157 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
2158 * PST as 'Pacific Standard Time.' This way we always know
2159 * what we're getting, and can parse it if we produce it.
2160 * The OS TZA string is included as a comment.
2163 /* get a timezone string from the OS to include as a
2164 comment. */
2165 new_explode(date, &split, cx);
2166 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
2168 /* Decide whether to use the resulting timezone string.
2170 * Reject it if it contains any non-ASCII, non-alphanumeric
2171 * characters. It's then likely in some other character
2172 * encoding, and we probably won't display it correctly.
2174 usetz = JS_TRUE;
2175 tzlen = strlen(tzbuf);
2176 if (tzlen > 100) {
2177 usetz = JS_FALSE;
2178 } else {
2179 for (i = 0; i < tzlen; i++) {
2180 jschar c = tzbuf[i];
2181 if (c > 127 ||
2182 !(isalpha(c) || isdigit(c) ||
2183 c == ' ' || c == '(' || c == ')')) {
2184 usetz = JS_FALSE;
2189 /* Also reject it if it's not parenthesized or if it's '()'. */
2190 if (tzbuf[0] != '(' || tzbuf[1] == ')')
2191 usetz = JS_FALSE;
2192 } else
2193 usetz = JS_FALSE;
2195 switch (format) {
2196 case FORMATSPEC_FULL:
2198 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2199 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
2201 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2202 JS_snprintf(buf, sizeof buf,
2203 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2204 days[WeekDay(local)],
2205 months[MonthFromTime(local)],
2206 DateFromTime(local),
2207 YearFromTime(local),
2208 HourFromTime(local),
2209 MinFromTime(local),
2210 SecFromTime(local),
2211 offset,
2212 usetz ? " " : "",
2213 usetz ? tzbuf : "");
2214 break;
2215 case FORMATSPEC_DATE:
2216 /* Tue Oct 31 2000 */
2217 JS_snprintf(buf, sizeof buf,
2218 "%s %s %.2d %.4d",
2219 days[WeekDay(local)],
2220 months[MonthFromTime(local)],
2221 DateFromTime(local),
2222 YearFromTime(local));
2223 break;
2224 case FORMATSPEC_TIME:
2225 /* 09:41:40 GMT-0800 (PST) */
2226 JS_snprintf(buf, sizeof buf,
2227 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2228 HourFromTime(local),
2229 MinFromTime(local),
2230 SecFromTime(local),
2231 offset,
2232 usetz ? " " : "",
2233 usetz ? tzbuf : "");
2234 break;
2238 str = JS_NewStringCopyZ(cx, buf);
2239 if (!str)
2240 return JS_FALSE;
2241 rval->setString(str);
2242 return JS_TRUE;
2245 static JSBool
2246 date_toLocaleHelper(JSContext *cx, JSObject *obj, const char *format, Value *vp)
2248 char buf[100];
2249 JSString *str;
2250 PRMJTime split;
2251 jsdouble utctime;
2253 if (!GetUTCTime(cx, obj, vp, &utctime))
2254 return false;
2256 if (!JSDOUBLE_IS_FINITE(utctime)) {
2257 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2258 } else {
2259 intN result_len;
2260 jsdouble local = LocalTime(utctime, cx);
2261 new_explode(local, &split, cx);
2263 /* let PRMJTime format it. */
2264 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2266 /* If it failed, default to toString. */
2267 if (result_len == 0)
2268 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2270 /* Hacked check against undesired 2-digit year 00/00/00 form. */
2271 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2272 /* Format %x means use OS settings, which may have 2-digit yr, so
2273 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2274 !isdigit(buf[result_len - 3]) &&
2275 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2276 /* ...but not if starts with 4-digit year, like 2022/3/11. */
2277 !(isdigit(buf[0]) && isdigit(buf[1]) &&
2278 isdigit(buf[2]) && isdigit(buf[3]))) {
2279 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2280 "%d", js_DateGetYear(cx, obj));
2285 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
2286 return cx->localeCallbacks->localeToUnicode(cx, buf, Jsvalify(vp));
2288 str = JS_NewStringCopyZ(cx, buf);
2289 if (!str)
2290 return false;
2291 vp->setString(str);
2292 return true;
2295 static JSBool
2296 date_toLocaleString(JSContext *cx, uintN argc, Value *vp)
2298 JSObject *obj = ToObject(cx, &vp[1]);
2299 if (!obj)
2300 return false;
2303 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2304 * with msvc; '%#c' requests that a full year be used in the result string.
2306 return date_toLocaleHelper(cx, obj,
2307 #if defined(_WIN32) && !defined(__MWERKS__)
2308 "%#c"
2309 #else
2310 "%c"
2311 #endif
2312 , vp);
2315 static JSBool
2316 date_toLocaleDateString(JSContext *cx, uintN argc, Value *vp)
2318 JSObject *obj = ToObject(cx, &vp[1]);
2319 if (!obj)
2320 return false;
2323 * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
2324 * with msvc; '%#x' requests that a full year be used in the result string.
2326 return date_toLocaleHelper(cx, obj,
2327 #if defined(_WIN32) && !defined(__MWERKS__)
2328 "%#x"
2329 #else
2330 "%x"
2331 #endif
2332 , vp);
2335 static JSBool
2336 date_toLocaleTimeString(JSContext *cx, uintN argc, Value *vp)
2338 JSObject *obj = ToObject(cx, &vp[1]);
2339 if (!obj)
2340 return false;
2342 return date_toLocaleHelper(cx, obj, "%X", vp);
2345 static JSBool
2346 date_toLocaleFormat(JSContext *cx, uintN argc, Value *vp)
2348 if (argc == 0)
2349 return date_toLocaleString(cx, argc, vp);
2351 JSObject *obj = ToObject(cx, &vp[1]);
2352 if (!obj)
2353 return false;
2355 JSString *fmt = js_ValueToString(cx, vp[2]);
2356 if (!fmt)
2357 return false;
2358 vp[2].setString(fmt);
2359 JSAutoByteString fmtbytes(cx, fmt);
2360 if (!fmtbytes)
2361 return false;
2363 return date_toLocaleHelper(cx, obj, fmtbytes.ptr(), vp);
2366 static JSBool
2367 date_toTimeString(JSContext *cx, uintN argc, Value *vp)
2369 jsdouble utctime;
2370 if (!GetThisUTCTime(cx, vp, &utctime))
2371 return false;
2372 return date_format(cx, utctime, FORMATSPEC_TIME, vp);
2375 static JSBool
2376 date_toDateString(JSContext *cx, uintN argc, Value *vp)
2378 jsdouble utctime;
2379 if (!GetThisUTCTime(cx, vp, &utctime))
2380 return false;
2381 return date_format(cx, utctime, FORMATSPEC_DATE, vp);
2384 #if JS_HAS_TOSOURCE
2385 #include <string.h>
2386 #include "jsnum.h"
2388 static JSBool
2389 date_toSource(JSContext *cx, uintN argc, Value *vp)
2391 jsdouble utctime;
2392 if (!GetThisUTCTime(cx, vp, &utctime))
2393 return false;
2395 ToCStringBuf cbuf;
2396 char *numStr = NumberToCString(cx, &cbuf, utctime);
2397 if (!numStr) {
2398 JS_ReportOutOfMemory(cx);
2399 return false;
2402 char *bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
2403 if (!bytes) {
2404 JS_ReportOutOfMemory(cx);
2405 return false;
2408 JSString *str = JS_NewStringCopyZ(cx, bytes);
2409 js_free(bytes);
2410 if (!str)
2411 return false;
2412 vp->setString(str);
2413 return true;
2415 #endif
2417 static JSBool
2418 date_toString(JSContext *cx, uintN argc, Value *vp)
2420 jsdouble utctime;
2421 if (!GetThisUTCTime(cx, vp, &utctime))
2422 return false;
2424 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2427 static JSBool
2428 date_valueOf(JSContext *cx, uintN argc, Value *vp)
2431 * It is an error to call date_valueOf on a non-date object, but we don't
2432 * need to check for that explicitly here because every path calls
2433 * GetUTCTime, which does the check.
2436 /* If called directly with no arguments, convert to a time number. */
2437 if (argc == 0)
2438 return date_getTime(cx, argc, vp);
2440 /* Verify this before extracting a string from the first argument. */
2441 JSObject *obj = ToObject(cx, &vp[1]);
2442 if (!obj)
2443 return false;
2445 /* Convert to number only if the hint was given, otherwise favor string. */
2446 JSString *str = js_ValueToString(cx, vp[2]);
2447 if (!str)
2448 return false;
2449 JSLinearString *linear_str = str->ensureLinear(cx);
2450 if (!linear_str)
2451 return false;
2452 JSAtom *number_str = cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER];
2453 if (EqualStrings(linear_str, number_str))
2454 return date_getTime(cx, argc, vp);
2455 return date_toString(cx, argc, vp);
2458 // Don't really need an argument here, but we don't support arg-less builtins
2459 JS_DEFINE_TRCINFO_1(date_now,
2460 (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, nanojit::ACCSET_STORE_ANY)))
2462 static JSFunctionSpec date_static_methods[] = {
2463 JS_FN("UTC", date_UTC, MAXARGS,0),
2464 JS_FN("parse", date_parse, 1,0),
2465 JS_TN("now", date_now, 0,0, &date_now_trcinfo),
2466 JS_FS_END
2469 static JSFunctionSpec date_methods[] = {
2470 JS_FN("getTime", date_getTime, 0,0),
2471 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2472 JS_FN("getYear", date_getYear, 0,0),
2473 JS_FN("getFullYear", date_getFullYear, 0,0),
2474 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2475 JS_FN("getMonth", date_getMonth, 0,0),
2476 JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2477 JS_FN("getDate", date_getDate, 0,0),
2478 JS_FN("getUTCDate", date_getUTCDate, 0,0),
2479 JS_FN("getDay", date_getDay, 0,0),
2480 JS_FN("getUTCDay", date_getUTCDay, 0,0),
2481 JS_FN("getHours", date_getHours, 0,0),
2482 JS_FN("getUTCHours", date_getUTCHours, 0,0),
2483 JS_FN("getMinutes", date_getMinutes, 0,0),
2484 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2485 JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2486 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2487 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2488 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2489 JS_FN("setTime", date_setTime, 1,0),
2490 JS_FN("setYear", date_setYear, 1,0),
2491 JS_FN("setFullYear", date_setFullYear, 3,0),
2492 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2493 JS_FN("setMonth", date_setMonth, 2,0),
2494 JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2495 JS_FN("setDate", date_setDate, 1,0),
2496 JS_FN("setUTCDate", date_setUTCDate, 1,0),
2497 JS_FN("setHours", date_setHours, 4,0),
2498 JS_FN("setUTCHours", date_setUTCHours, 4,0),
2499 JS_FN("setMinutes", date_setMinutes, 3,0),
2500 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2501 JS_FN("setSeconds", date_setSeconds, 2,0),
2502 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2503 JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2504 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2505 JS_FN("toUTCString", date_toGMTString, 0,0),
2506 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2507 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2508 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2509 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2510 JS_FN("toDateString", date_toDateString, 0,0),
2511 JS_FN("toTimeString", date_toTimeString, 0,0),
2512 JS_FN("toISOString", date_toISOString, 0,0),
2513 JS_FN(js_toJSON_str, date_toJSON, 1,0),
2514 #if JS_HAS_TOSOURCE
2515 JS_FN(js_toSource_str, date_toSource, 0,0),
2516 #endif
2517 JS_FN(js_toString_str, date_toString, 0,0),
2518 JS_FN(js_valueOf_str, date_valueOf, 0,0),
2519 JS_FS_END
2522 JSBool
2523 js_Date(JSContext *cx, uintN argc, Value *vp)
2525 /* Date called as function. */
2526 if (!IsConstructing(vp))
2527 return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, vp);
2529 Value *argv = vp + 2;
2531 /* Date called as constructor. */
2532 jsdouble d;
2533 if (argc == 0) {
2534 d = NowAsMillis();
2535 } else if (argc == 1) {
2536 if (!argv[0].isString()) {
2537 /* the argument is a millisecond number */
2538 if (!ValueToNumber(cx, argv[0], &d))
2539 return false;
2540 d = TIMECLIP(d);
2541 } else {
2542 /* the argument is a string; parse it. */
2543 JSString *str = js_ValueToString(cx, argv[0]);
2544 if (!str)
2545 return false;
2546 argv[0].setString(str);
2547 JSLinearString *linearStr = str->ensureLinear(cx);
2548 if (!linearStr)
2549 return false;
2551 if (!date_parseString(linearStr, &d, cx))
2552 d = js_NaN;
2553 else
2554 d = TIMECLIP(d);
2556 } else {
2557 jsdouble msec_time;
2558 if (!date_msecFromArgs(cx, argc, argv, &msec_time))
2559 return false;
2561 if (JSDOUBLE_IS_FINITE(msec_time)) {
2562 msec_time = UTC(msec_time, cx);
2563 msec_time = TIMECLIP(msec_time);
2565 d = msec_time;
2568 JSObject *obj = js_NewDateObjectMsec(cx, d);
2569 if (!obj)
2570 return false;
2571 vp->setObject(*obj);
2573 return true;
2576 JSObject *
2577 js_InitDateClass(JSContext *cx, JSObject *obj)
2579 /* set static LocalTZA */
2580 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2581 JSObject *proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS,
2582 NULL, date_methods, NULL, date_static_methods);
2583 if (!proto)
2584 return NULL;
2586 AutoObjectRooter tvr(cx, proto);
2588 SetDateToNaN(cx, proto);
2591 * ES5 B.2.6:
2592 * The Function object that is the initial value of
2593 * Date.prototype.toGMTString is the same Function
2594 * object that is the initial value of
2595 * Date.prototype.toUTCString.
2597 AutoValueRooter toUTCStringFun(cx);
2598 jsid toUTCStringId = ATOM_TO_JSID(cx->runtime->atomState.toUTCStringAtom);
2599 jsid toGMTStringId = ATOM_TO_JSID(cx->runtime->atomState.toGMTStringAtom);
2600 if (!js_GetProperty(cx, proto, toUTCStringId, toUTCStringFun.addr()) ||
2601 !js_DefineProperty(cx, proto, toGMTStringId, toUTCStringFun.addr(),
2602 PropertyStub, StrictPropertyStub, 0)) {
2603 return NULL;
2606 return proto;
2609 JS_FRIEND_API(JSObject *)
2610 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2612 JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass);
2613 if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
2614 return NULL;
2615 if (!SetUTCTime(cx, obj, msec_time))
2616 return NULL;
2617 return obj;
2620 JS_FRIEND_API(JSObject *)
2621 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2622 int hour, int min, int sec)
2624 JSObject *obj;
2625 jsdouble msec_time;
2627 JS_ASSERT(mon < 12);
2628 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2629 obj = js_NewDateObjectMsec(cx, UTC(msec_time, cx));
2630 return obj;
2633 JS_FRIEND_API(JSBool)
2634 js_DateIsValid(JSContext *cx, JSObject* obj)
2636 jsdouble utctime;
2637 return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime);
2640 JS_FRIEND_API(int)
2641 js_DateGetYear(JSContext *cx, JSObject* obj)
2643 jsdouble localtime;
2645 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2646 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2647 JSDOUBLE_IS_NaN(localtime)) {
2648 return 0;
2651 return (int) YearFromTime(localtime);
2654 JS_FRIEND_API(int)
2655 js_DateGetMonth(JSContext *cx, JSObject* obj)
2657 jsdouble localtime;
2659 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2660 JSDOUBLE_IS_NaN(localtime)) {
2661 return 0;
2664 return (int) MonthFromTime(localtime);
2667 JS_FRIEND_API(int)
2668 js_DateGetDate(JSContext *cx, JSObject* obj)
2670 jsdouble localtime;
2672 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2673 JSDOUBLE_IS_NaN(localtime)) {
2674 return 0;
2677 return (int) DateFromTime(localtime);
2680 JS_FRIEND_API(int)
2681 js_DateGetHours(JSContext *cx, JSObject* obj)
2683 jsdouble localtime;
2685 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2686 JSDOUBLE_IS_NaN(localtime)) {
2687 return 0;
2690 return (int) HourFromTime(localtime);
2693 JS_FRIEND_API(int)
2694 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2696 jsdouble localtime;
2698 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2699 JSDOUBLE_IS_NaN(localtime)) {
2700 return 0;
2703 return (int) MinFromTime(localtime);
2706 JS_FRIEND_API(int)
2707 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2709 jsdouble utctime;
2711 if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime))
2712 return 0;
2714 return (int) SecFromTime(utctime);
2717 JS_FRIEND_API(jsdouble)
2718 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2720 jsdouble utctime;
2721 if (!GetUTCTime(cx, obj, NULL, &utctime))
2722 return 0;
2723 return utctime;
2726 #ifdef JS_THREADSAFE
2727 #include "prinrval.h"
2729 JS_FRIEND_API(uint32)
2730 js_IntervalNow()
2732 return uint32(PR_IntervalToMilliseconds(PR_IntervalNow()));
2735 #else /* !JS_THREADSAFE */
2737 JS_FRIEND_API(uint32)
2738 js_IntervalNow()
2740 return uint32(PRMJ_Now() / PRMJ_USEC_PER_MSEC);
2742 #endif