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
17 * The Original Code is Mozilla Communicator client code, released
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.
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
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'.
66 #include "jsutil.h" /* Added by JSIFY */
68 #include "jsversion.h"
69 #include "jsbuiltins.h"
77 #include "jsobjinlines.h"
82 * The JS 'Date' object is patterned after the Java 'Date' object.
87 * print(today.toLocaleString());
89 * weekDay = today.getDay();
92 * These Java (and ECMA-262) methods are supported:
95 * getDate (getUTCDate)
97 * getHours (getUTCHours)
98 * getMinutes (getUTCMinutes)
99 * getMonth (getUTCMonth)
100 * getSeconds (getUTCSeconds)
101 * getMilliseconds (getUTCMilliseconds)
105 * getFullYear (getUTCFullYear)
107 * setDate (setUTCDate)
108 * setHours (setUTCHours)
109 * setMinutes (setUTCMinutes)
110 * setMonth (setUTCMonth)
111 * setSeconds (setUTCSeconds)
112 * setMilliseconds (setUTCMilliseconds)
114 * setYear (setFullYear, setUTCFullYear)
115 * toGMTString (toUTCString)
120 * These Java methods are not supported
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.)
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;
177 #define msPerDay (SecondsPerDay * msPerSecond)
178 #define msPerHour (SecondsPerHour * msPerSecond)
179 #define msPerMinute (SecondsPerMinute * msPerSecond)
180 #define msPerSecond 1000.0
183 #define Day(t) floor((t) / msPerDay)
186 TimeWithinDay(jsdouble t
)
189 result
= fmod(t
, msPerDay
);
195 #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
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)
206 YearFromTime(jsdouble t
)
208 jsint y
= (jsint
) floor(t
/(msPerDay
*365.2425)) + 1970;
209 jsdouble t2
= (jsdouble
) TimeFromYear(y
);
214 if (t2
+ msPerDay
* DaysInYear(y
) <= t
)
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]
236 DaysInMonth(jsint year
, jsint month
)
238 JSBool leap
= (DaysInYear(year
) == 366);
239 intN result
= intN(DayFromMonth(month
, leap
) - DayFromMonth(month
-1, leap
));
244 MonthFromTime(jsdouble t
)
247 jsint year
= YearFromTime(t
);
248 d
= DayWithinYear(t
, year
);
252 step
+= (InLeapYear(t
) ? 29 : 28);
255 if (d
< (step
+= 31))
257 if (d
< (step
+= 30))
259 if (d
< (step
+= 31))
261 if (d
< (step
+= 30))
263 if (d
< (step
+= 31))
265 if (d
< (step
+= 31))
267 if (d
< (step
+= 30))
269 if (d
< (step
+= 31))
271 if (d
< (step
+= 30))
277 DateFromTime(jsdouble t
)
280 jsint year
= YearFromTime(t
);
281 d
= DayWithinYear(t
, year
);
283 if (d
<= (next
= 30))
286 next
+= (InLeapYear(t
) ? 29 : 28);
290 if (d
<= (next
+= 31))
293 if (d
<= (next
+= 30))
296 if (d
<= (next
+= 31))
299 if (d
<= (next
+= 30))
302 if (d
<= (next
+= 31))
305 if (d
<= (next
+= 31))
308 if (d
<= (next
+= 30))
311 if (d
<= (next
+= 31))
314 if (d
<= (next
+= 30))
324 result
= (jsint
) Day(t
) + 4;
328 return (intN
) result
;
331 #define MakeTime(hour, min, sec, ms) \
332 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
335 MakeDay(jsdouble year
, jsdouble month
, jsdouble date
)
341 year
+= floor(month
/ 12);
343 month
= fmod(month
, 12.0);
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.
379 EquivalentYearForDST(jsint year
)
384 day
= (jsint
) DayFromYear(year
) + 4;
389 isLeapYear
= (DaysInYear(year
) == 366);
391 return yearStartingWith
[isLeapYear
][day
];
394 /* LocalTZA gets set by js_InitDateClass() */
395 static jsdouble LocalTZA
;
398 DaylightSavingTA(jsdouble t
, JSContext
*cx
)
401 if (JSDOUBLE_IS_NaN(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
);
420 AdjustTime(jsdouble date
, JSContext
*cx
)
422 jsdouble t
= DaylightSavingTA(date
, cx
) + LocalTZA
;
423 t
= (LocalTZA
>= 0) ? fmod(t
, msPerDay
) : -fmod(msPerDay
- t
, msPerDay
);
428 LocalTime(jsdouble t
, JSContext
*cx
)
430 return t
+ AdjustTime(t
, cx
);
434 UTC(jsdouble t
, JSContext
*cx
)
436 return t
- AdjustTime(t
- LocalTZA
, cx
);
440 HourFromTime(jsdouble t
)
442 intN result
= (intN
) fmod(floor(t
/msPerHour
), HoursPerDay
);
444 result
+= (intN
)HoursPerDay
;
449 MinFromTime(jsdouble t
)
451 intN result
= (intN
) fmod(floor(t
/ msPerMinute
), MinutesPerHour
);
453 result
+= (intN
)MinutesPerHour
;
458 SecFromTime(jsdouble t
)
460 intN result
= (intN
) fmod(floor(t
/ msPerSecond
), SecondsPerMinute
);
462 result
+= (intN
)SecondsPerMinute
;
467 msFromTime(jsdouble t
)
469 intN result
= (intN
) fmod(t
, msPerSecond
);
471 result
+= (intN
)msPerSecond
;
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
= {
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
[] = {
500 "monday", "tuesday", "wednesday", "thursday", "friday",
501 "saturday", "sunday",
502 "january", "february", "march", "april", "may", "june",
503 "july", "august", "september", "october", "november", "december",
509 /* time zone table needs to be expanded */
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 */
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
]) {
532 if (JS_TOLOWER((jschar
)s1
[s1off
]) != JS_TOLOWER(s2
[s2off
])) {
536 if ((jschar
)s1
[s1off
] != s2
[s2off
]) {
552 /* find UTC time from given date... no 1900 correction! */
554 date_msecFromDate(jsdouble year
, jsdouble mon
, jsdouble mday
, jsdouble hour
,
555 jsdouble min
, jsdouble sec
, jsdouble msec
)
561 day
= MakeDay(year
, mon
, mday
);
562 msec_time
= MakeTime(hour
, min
, sec
, msec
);
563 result
= MakeDate(day
, msec_time
);
567 /* compute the time in msec (unclipped) from the given args */
571 date_msecFromArgs(JSContext
*cx
, uintN argc
, Value
*argv
, jsdouble
*rval
)
574 jsdouble array
[MAXARGS
];
577 for (loop
= 0; loop
< MAXARGS
; loop
++) {
580 if (!ValueToNumber(cx
, argv
[loop
], &d
))
582 /* return NaN if any arg is not finite */
583 if (!JSDOUBLE_IS_FINITE(d
)) {
587 array
[loop
] = js_DoubleToInteger(d
);
590 array
[loop
] = 1; /* Default the date argument to 1. */
597 /* adjust 2-digit years into the 20th century */
598 if (array
[0] >= 0 && array
[0] <= 99)
601 msec_time
= date_msecFromDate(array
[0], array
[1], array
[2],
602 array
[3], array
[4], array
[5], array
[6]);
608 * See ECMA 15.9.4.[3-10];
611 date_UTC(JSContext
*cx
, uintN argc
, Value
*vp
)
615 if (!date_msecFromArgs(cx
, argc
, vp
+ 2, &msec_time
))
618 msec_time
= TIMECLIP(msec_time
);
620 vp
->setDouble(msec_time
);
625 * Read and convert decimal digits from s[*i] into *result
628 * Succeed if any digits are converted. Advance *i only
629 * as digits are consumed.
632 digits(size_t *result
, const jschar
*s
, size_t *i
, size_t limit
)
637 ('0' <= s
[*i
] && s
[*i
] <= '9')) {
639 *result
+= (s
[*i
] - '0');
646 * Read and convert decimal digits to the right of a decimal point,
647 * representing a fractional integer, from s[*i] into *result
650 * Succeed if any digits are converted. Advance *i only
651 * as digits are consumed.
654 fractional(jsdouble
*result
, const jschar
*s
, size_t *i
, size_t limit
)
656 jsdouble factor
= 0.1;
660 ('0' <= s
[*i
] && s
[*i
] <= '9')) {
661 *result
+= (s
[*i
] - '0') * factor
;
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
676 ndigits(size_t n
, size_t *result
, const jschar
*s
, size_t* i
, size_t limit
)
680 if (digits(result
, s
, i
, JS_MIN(limit
, init
+n
)))
681 return ((*i
- init
) == n
);
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.
715 * YYYY-MM (eg 1997-07)
718 * YYYY-MM-DD (eg 1997-07-16)
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)
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)
744 date_parseISOString(JSString
*str
, jsdouble
*result
, JSContext
*cx
)
760 bool isLocalTime
= JS_FALSE
;
764 #define PEEK(ch) (i < limit && s[i] == ch)
768 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
771 #define DONE_DATE_UNLESS(ch) \
773 if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
776 #define DONE_UNLESS(ch) \
778 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
781 #define NEED_NDIGITS(n, field) \
783 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
786 str
->getCharsAndLength(s
, limit
);
788 if (PEEK('+') || PEEK('-')) {
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
);
803 NEED_NDIGITS(2, hour
);
805 NEED_NDIGITS(2, min
);
809 NEED_NDIGITS(2, sec
);
812 if (!fractional(&frac
, s
, &i
, limit
))
819 } else if (PEEK('+') || PEEK('-')) {
823 NEED_NDIGITS(2, tzHour
);
825 NEED_NDIGITS(2, tzMin
);
827 isLocalTime
= JS_TRUE
;
831 if (year
> 275943 // ceil(1e8/365) + 1970
832 || (month
== 0 || month
> 12)
833 || (day
== 0 || day
> size_t(DaysInMonth(year
,month
)))
835 || ((hour
== 24) && (min
> 0 || sec
> 0))
845 month
-= 1; /* convert month to 0-based */
847 msec
= date_msecFromDate(dateMul
* (jsdouble
)year
, month
, day
,
852 msec
= UTC(msec
, cx
);
854 msec
-= ((tzMul
) * ((tzHour
* msPerHour
)
855 + (tzMin
* msPerMinute
)));
858 if (msec
< -8.64e15
|| msec
> 8.64e15
)
877 date_parseString(JSString
*str
, jsdouble
*result
, JSContext
*cx
)
894 JSBool seenplusminus
= JS_FALSE
;
896 JSBool seenmonthname
= JS_FALSE
;
898 if (date_parseISOString(str
, result
, cx
))
901 str
->getCharsAndLength(s
, limit
);
907 if (c
<= ' ' || c
== ',' || c
== '-') {
908 if (c
== '-' && '0' <= s
[i
] && s
[i
] <= '9') {
913 if (c
== '(') { /* comments) */
918 if (c
== '(') depth
++;
925 if ('0' <= c
&& c
<= '9') {
927 while (i
< limit
&& '0' <= (c
= s
[i
]) && c
<= '9') {
928 n
= n
* 10 + c
- '0';
932 /* allow TZA before the year, so
933 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
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
;
946 n
= n
* 60; /* EG. "GMT-3" */
948 n
= n
% 100 + n
/ 100 * 60; /* eg "GMT-0430" */
949 if (prevc
== '+') /* plus means east of GMT */
951 if (tzoffset
!= 0 && tzoffset
!= -1)
954 } else if (prevc
== '/' && mon
>= 0 && mday
>= 0 && year
< 0) {
955 if (c
<= ' ' || c
== ',' || c
== '/' || i
>= limit
)
959 } else if (c
== ':') {
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 */
975 } else if (i
< limit
&& c
!= ',' && c
> ' ' && c
!= '-' && c
!= '(') {
977 } else if (seenplusminus
&& n
< 60) { /* handle GMT-3:30 */
982 } else if (hour
>= 0 && min
< 0) {
984 } else if (prevc
== ':' && min
>= 0 && sec
< 0) {
986 } else if (mon
< 0) {
988 } else if (mon
>= 0 && mday
< 0) {
990 } else if (mon
>= 0 && mday
>= 0 && year
< 0) {
996 } else if (c
== '/' || c
== ':' || c
== '+' || c
== '-') {
1003 if (!(('A' <= c
&& c
<= 'Z') || ('a' <= c
&& c
<= 'z')))
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
];
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) {
1022 if (action
== -1 && hour
== 12) { /* am */
1024 } else if (action
== -2 && hour
!= 12) { /* pm */
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
) {
1034 seenmonthname
= JS_TRUE
;
1035 temp
= /*byte*/ (action
- 2) + 1;
1039 } else if (mday
< 0) {
1042 } else if (year
< 0) {
1049 tzoffset
= action
- 10000;
1059 if (year
< 0 || mon
< 0 || mday
< 0)
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
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.
1078 i. If m < 70, f/m/l is interpreted as
1079 year/month/day where year is the number of years after
1081 ii. If m >= 70, the date is invalid.
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)) {
1096 if (year
>= 70 && year
< 100) {
1099 } else if (mon
< 70) { /* (a) month/day/year */
1103 } else if (mon
< 100) { /* (b) year/month/day */
1112 } else { /* (c) year/month/day */
1122 mon
-= 1; /* convert month to 0-based */
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
);
1135 msec
+= tzoffset
* msPerMinute
;
1148 date_parse(JSContext
*cx
, uintN argc
, Value
*vp
)
1154 vp
->setDouble(js_NaN
);
1157 str
= js_ValueToString(cx
, vp
[2]);
1160 vp
[2].setString(str
);
1161 if (!date_parseString(str
, &result
, cx
)) {
1162 vp
->setDouble(js_NaN
);
1166 result
= TIMECLIP(result
);
1167 vp
->setDouble(result
);
1171 static inline jsdouble
1174 return (jsdouble
) (PRMJ_Now() / PRMJ_USEC_PER_MSEC
);
1178 date_now(JSContext
*cx
, uintN argc
, Value
*vp
)
1180 vp
->setDouble(NowAsMillis());
1185 static jsdouble FASTCALL
1186 date_now_tn(JSContext
*)
1188 return NowAsMillis();
1193 * Get UTC time from the date object. Returns false if the object is not
1197 GetUTCTime(JSContext
*cx
, JSObject
*obj
, Value
*vp
, jsdouble
*dp
)
1199 if (!InstanceOf(cx
, obj
, &js_DateClass
, vp
? vp
+ 2 : NULL
))
1201 *dp
= obj
->getDateUTCTime().asDouble();
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
);
1213 vp
->setDouble(js_NaN
);
1217 * Set UTC time to a given time and invalidate cached local time.
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
));
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.
1236 GetAndCacheLocalTime(JSContext
*cx
, JSObject
*obj
, Value
*vp
, jsdouble
*dp
)
1238 if (!obj
|| !InstanceOf(cx
, obj
, &js_DateClass
, vp
? vp
+ 2 : NULL
))
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
));
1257 * See ECMA 15.9.5.4 thru 15.9.5.23
1260 date_getTime(JSContext
*cx
, uintN argc
, Value
*vp
)
1264 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1266 vp
->setDouble(result
);
1271 GetYear(JSContext
*cx
, JSBool fullyear
, Value
*vp
)
1275 if (!GetAndCacheLocalTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1278 if (JSDOUBLE_IS_FINITE(result
)) {
1279 result
= YearFromTime(result
);
1281 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1286 vp
->setDouble(result
);
1291 date_getYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1293 return GetYear(cx
, JS_FALSE
, vp
);
1297 date_getFullYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1299 return GetYear(cx
, JS_TRUE
, vp
);
1303 date_getUTCFullYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1307 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1310 if (JSDOUBLE_IS_FINITE(result
))
1311 result
= YearFromTime(result
);
1313 vp
->setDouble(result
);
1318 date_getMonth(JSContext
*cx
, uintN argc
, Value
*vp
)
1322 if (!GetAndCacheLocalTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1325 if (JSDOUBLE_IS_FINITE(result
))
1326 result
= MonthFromTime(result
);
1328 vp
->setDouble(result
);
1333 date_getUTCMonth(JSContext
*cx
, uintN argc
, Value
*vp
)
1337 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1340 if (JSDOUBLE_IS_FINITE(result
))
1341 result
= MonthFromTime(result
);
1343 vp
->setDouble(result
);
1348 date_getDate(JSContext
*cx
, uintN argc
, Value
*vp
)
1352 if (!GetAndCacheLocalTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1355 if (JSDOUBLE_IS_FINITE(result
))
1356 result
= DateFromTime(result
);
1358 vp
->setDouble(result
);
1363 date_getUTCDate(JSContext
*cx
, uintN argc
, Value
*vp
)
1367 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1370 if (JSDOUBLE_IS_FINITE(result
))
1371 result
= DateFromTime(result
);
1373 vp
->setDouble(result
);
1378 date_getDay(JSContext
*cx
, uintN argc
, Value
*vp
)
1382 if (!GetAndCacheLocalTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1385 if (JSDOUBLE_IS_FINITE(result
))
1386 result
= WeekDay(result
);
1388 vp
->setDouble(result
);
1393 date_getUTCDay(JSContext
*cx
, uintN argc
, Value
*vp
)
1397 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1400 if (JSDOUBLE_IS_FINITE(result
))
1401 result
= WeekDay(result
);
1403 vp
->setDouble(result
);
1408 date_getHours(JSContext
*cx
, uintN argc
, Value
*vp
)
1412 if (!GetAndCacheLocalTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1415 if (JSDOUBLE_IS_FINITE(result
))
1416 result
= HourFromTime(result
);
1418 vp
->setDouble(result
);
1423 date_getUTCHours(JSContext
*cx
, uintN argc
, Value
*vp
)
1427 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1430 if (JSDOUBLE_IS_FINITE(result
))
1431 result
= HourFromTime(result
);
1433 vp
->setDouble(result
);
1438 date_getMinutes(JSContext
*cx
, uintN argc
, Value
*vp
)
1442 if (!GetAndCacheLocalTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1445 if (JSDOUBLE_IS_FINITE(result
))
1446 result
= MinFromTime(result
);
1448 vp
->setDouble(result
);
1453 date_getUTCMinutes(JSContext
*cx
, uintN argc
, Value
*vp
)
1457 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1460 if (JSDOUBLE_IS_FINITE(result
))
1461 result
= MinFromTime(result
);
1463 vp
->setDouble(result
);
1467 /* Date.getSeconds is mapped to getUTCSeconds */
1470 date_getUTCSeconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1474 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1477 if (JSDOUBLE_IS_FINITE(result
))
1478 result
= SecFromTime(result
);
1480 vp
->setDouble(result
);
1484 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1487 date_getUTCMilliseconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1491 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &result
))
1494 if (JSDOUBLE_IS_FINITE(result
))
1495 result
= msFromTime(result
);
1497 vp
->setDouble(result
);
1502 date_getTimezoneOffset(JSContext
*cx
, uintN argc
, Value
*vp
)
1505 jsdouble utctime
, localtime
, result
;
1507 obj
= ComputeThisObjectFromVp(cx
, vp
);
1508 if (!GetUTCTime(cx
, obj
, vp
, &utctime
))
1510 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
))
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
);
1524 date_setTime(JSContext
*cx
, uintN argc
, Value
*vp
)
1526 JSObject
*obj
= ComputeThisObjectFromVp(cx
, vp
);
1527 if (!InstanceOf(cx
, obj
, &js_DateClass
, vp
+ 2))
1531 SetDateToNaN(cx
, obj
, vp
);
1536 if (!ValueToNumber(cx
, vp
[2], &result
))
1539 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1543 date_makeTime(JSContext
*cx
, uintN maxargs
, JSBool local
, uintN argc
, Value
*vp
)
1548 jsdouble args
[4], *argp
, *stop
;
1549 jsdouble hour
, min
, sec
, msec
;
1550 jsdouble lorutime
; /* Local or UTC version of *date */
1555 obj
= ComputeThisObjectFromVp(cx
, vp
);
1556 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1559 /* just return NaN if the date is already NaN */
1560 if (!JSDOUBLE_IS_FINITE(result
)) {
1561 vp
->setDouble(result
);
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.
1575 SetDateToNaN(cx
, obj
, vp
);
1579 argc
= maxargs
; /* clamp argc */
1580 JS_ASSERT(argc
<= 4);
1583 for (i
= 0; i
< argc
; i
++) {
1584 if (!ValueToNumber(cx
, argv
[i
], &args
[i
]))
1586 if (!JSDOUBLE_IS_FINITE(args
[i
])) {
1587 SetDateToNaN(cx
, obj
, vp
);
1590 args
[i
] = js_DoubleToInteger(args
[i
]);
1594 lorutime
= LocalTime(result
, cx
);
1600 if (maxargs
>= 4 && argp
< stop
)
1603 hour
= HourFromTime(lorutime
);
1605 if (maxargs
>= 3 && argp
< stop
)
1608 min
= MinFromTime(lorutime
);
1610 if (maxargs
>= 2 && argp
< stop
)
1613 sec
= SecFromTime(lorutime
);
1615 if (maxargs
>= 1 && argp
< stop
)
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); */
1626 result
= UTC(result
, cx
);
1628 /* fprintf(stderr, "%f\n", result); */
1630 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1634 date_setMilliseconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1636 return date_makeTime(cx
, 1, JS_TRUE
, argc
, vp
);
1640 date_setUTCMilliseconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1642 return date_makeTime(cx
, 1, JS_FALSE
, argc
, vp
);
1646 date_setSeconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1648 return date_makeTime(cx
, 2, JS_TRUE
, argc
, vp
);
1652 date_setUTCSeconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1654 return date_makeTime(cx
, 2, JS_FALSE
, argc
, vp
);
1658 date_setMinutes(JSContext
*cx
, uintN argc
, Value
*vp
)
1660 return date_makeTime(cx
, 3, JS_TRUE
, argc
, vp
);
1664 date_setUTCMinutes(JSContext
*cx
, uintN argc
, Value
*vp
)
1666 return date_makeTime(cx
, 3, JS_FALSE
, argc
, vp
);
1670 date_setHours(JSContext
*cx
, uintN argc
, Value
*vp
)
1672 return date_makeTime(cx
, 4, JS_TRUE
, argc
, vp
);
1676 date_setUTCHours(JSContext
*cx
, uintN argc
, Value
*vp
)
1678 return date_makeTime(cx
, 4, JS_FALSE
, argc
, vp
);
1682 date_makeDate(JSContext
*cx
, uintN maxargs
, JSBool local
, uintN argc
, Value
*vp
)
1687 jsdouble lorutime
; /* local or UTC version of *date */
1688 jsdouble args
[3], *argp
, *stop
;
1689 jsdouble year
, month
, day
;
1692 obj
= ComputeThisObjectFromVp(cx
, vp
);
1693 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1696 /* see complaint about ECMA in date_MakeTime */
1698 SetDateToNaN(cx
, obj
, vp
);
1702 argc
= maxargs
; /* clamp argc */
1703 JS_ASSERT(1 <= argc
&& argc
<= 3);
1706 for (i
= 0; i
< argc
; i
++) {
1707 if (!ValueToNumber(cx
, argv
[i
], &args
[i
]))
1709 if (!JSDOUBLE_IS_FINITE(args
[i
])) {
1710 SetDateToNaN(cx
, obj
, vp
);
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
))) {
1720 vp
->setDouble(result
);
1725 lorutime
= local
? LocalTime(result
, cx
) : result
;
1730 if (maxargs
>= 3 && argp
< stop
)
1733 year
= YearFromTime(lorutime
);
1735 if (maxargs
>= 2 && argp
< stop
)
1738 month
= MonthFromTime(lorutime
);
1740 if (maxargs
>= 1 && argp
< stop
)
1743 day
= DateFromTime(lorutime
);
1745 day
= MakeDay(year
, month
, day
); /* day within year */
1746 result
= MakeDate(day
, TimeWithinDay(lorutime
));
1749 result
= UTC(result
, cx
);
1751 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1755 date_setDate(JSContext
*cx
, uintN argc
, Value
*vp
)
1757 return date_makeDate(cx
, 1, JS_TRUE
, argc
, vp
);
1761 date_setUTCDate(JSContext
*cx
, uintN argc
, Value
*vp
)
1763 return date_makeDate(cx
, 1, JS_FALSE
, argc
, vp
);
1767 date_setMonth(JSContext
*cx
, uintN argc
, Value
*vp
)
1769 return date_makeDate(cx
, 2, JS_TRUE
, argc
, vp
);
1773 date_setUTCMonth(JSContext
*cx
, uintN argc
, Value
*vp
)
1775 return date_makeDate(cx
, 2, JS_FALSE
, argc
, vp
);
1779 date_setFullYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1781 return date_makeDate(cx
, 3, JS_TRUE
, argc
, vp
);
1785 date_setUTCFullYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1787 return date_makeDate(cx
, 3, JS_FALSE
, argc
, vp
);
1791 date_setYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1793 JSObject
*obj
= ComputeThisObjectFromVp(cx
, vp
);
1796 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1800 /* Call this only after GetUTCTime has verified that obj is Date. */
1801 SetDateToNaN(cx
, obj
, vp
);
1806 if (!ValueToNumber(cx
, vp
[2], &year
))
1808 if (!JSDOUBLE_IS_FINITE(year
)) {
1809 SetDateToNaN(cx
, obj
, vp
);
1812 year
= js_DoubleToInteger(year
);
1813 if (year
>= 0 && year
<= 99)
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.
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
));
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
));
1865 date_utc_format(JSContext
*cx
, Value
*vp
,
1866 void (*printFunc
)(char*, size_t, jsdouble
))
1872 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &utctime
))
1875 if (!JSDOUBLE_IS_FINITE(utctime
)) {
1876 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
1878 (*printFunc
)(buf
, sizeof buf
, utctime
);
1880 str
= JS_NewStringCopyZ(cx
, buf
);
1888 date_toGMTString(JSContext
*cx
, uintN argc
, Value
*vp
)
1890 return date_utc_format(cx
, vp
, print_gmt_string
);
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.
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
1918 split
->tm_isdst
= (DaylightSavingTA(timeval
, cx
) != 0);
1921 typedef enum formatspec
{
1922 FORMATSPEC_FULL
, FORMATSPEC_DATE
, FORMATSPEC_TIME
1925 /* helper function */
1927 date_format(JSContext
*cx
, jsdouble date
, formatspec format
, Value
*rval
)
1936 if (!JSDOUBLE_IS_FINITE(date
)) {
1937 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
1939 jsdouble local
= LocalTime(date
, cx
);
1941 /* offset from GMT in minutes. The offset includes daylight savings,
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
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.
1969 tzlen
= strlen(tzbuf
);
1973 for (i
= 0; i
< tzlen
; i
++) {
1974 jschar c
= tzbuf
[i
];
1976 !(isalpha(c
) || isdigit(c
) ||
1977 c
== ' ' || c
== '(' || c
== ')')) {
1983 /* Also reject it if it's not parenthesized or if it's '()'. */
1984 if (tzbuf
[0] != '(' || tzbuf
[1] == ')')
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
),
2007 usetz
? tzbuf
: "");
2009 case FORMATSPEC_DATE
:
2010 /* Tue Oct 31 2000 */
2011 JS_snprintf(buf
, sizeof buf
,
2013 days
[WeekDay(local
)],
2014 months
[MonthFromTime(local
)],
2015 DateFromTime(local
),
2016 YearFromTime(local
));
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
),
2027 usetz
? tzbuf
: "");
2032 str
= JS_NewStringCopyZ(cx
, buf
);
2035 rval
->setString(str
);
2040 date_toLocaleHelper(JSContext
*cx
, const char *format
, Value
*vp
)
2048 obj
= ComputeThisObjectFromVp(cx
, vp
);
2049 if (!GetUTCTime(cx
, obj
, vp
, &utctime
))
2052 if (!JSDOUBLE_IS_FINITE(utctime
)) {
2053 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
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
);
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__)
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__)
2124 date_toLocaleTimeString(JSContext
*cx
, uintN argc
, Value
*vp
)
2126 return date_toLocaleHelper(cx
, "%X", vp
);
2130 date_toLocaleFormat(JSContext
*cx
, uintN argc
, Value
*vp
)
2133 const char *fmtbytes
;
2136 return date_toLocaleString(cx
, argc
, vp
);
2138 fmt
= js_ValueToString(cx
, vp
[2]);
2141 vp
[2].setString(fmt
);
2142 fmtbytes
= js_GetStringBytes(cx
, fmt
);
2146 return date_toLocaleHelper(cx
, fmtbytes
, vp
);
2150 date_toTimeString(JSContext
*cx
, uintN argc
, Value
*vp
)
2154 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &utctime
))
2156 return date_format(cx
, utctime
, FORMATSPEC_TIME
, vp
);
2160 date_toDateString(JSContext
*cx
, uintN argc
, Value
*vp
)
2164 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &utctime
))
2166 return date_format(cx
, utctime
, FORMATSPEC_DATE
, vp
);
2174 date_toSource(JSContext
*cx
, uintN argc
, Value
*vp
)
2177 char buf
[DTOSTR_STANDARD_BUFFER_SIZE
], *numStr
, *bytes
;
2180 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &utctime
))
2183 numStr
= js_dtostr(JS_THREAD_DATA(cx
)->dtoaState
, buf
, sizeof buf
, DTOSTR_STANDARD
, 0, utctime
);
2185 JS_ReportOutOfMemory(cx
);
2189 bytes
= JS_smprintf("(new %s(%s))", js_Date_str
, numStr
);
2191 JS_ReportOutOfMemory(cx
);
2195 str
= JS_NewString(cx
, bytes
, strlen(bytes
));
2206 date_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
2210 if (!GetUTCTime(cx
, ComputeThisObjectFromVp(cx
, vp
), vp
, &utctime
))
2212 return date_format(cx
, utctime
, FORMATSPEC_FULL
, vp
);
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. */
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]);
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
),
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),
2296 JS_FN(js_toSource_str
, date_toSource
, 0,0),
2298 JS_FN(js_toString_str
, date_toString
, 0,0),
2299 JS_FN(js_valueOf_str
, date_valueOf
, 0,0),
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. */
2314 } else if (argc
== 1) {
2315 if (!argv
[0].isString()) {
2316 /* the argument is a millisecond number */
2317 if (!ValueToNumber(cx
, argv
[0], &d
))
2321 /* the argument is a string; parse it. */
2322 JSString
*str
= js_ValueToString(cx
, argv
[0]);
2325 argv
[0].setString(str
);
2327 if (!date_parseString(str
, &d
, cx
))
2334 if (!date_msecFromArgs(cx
, argc
, argv
, &msec_time
))
2337 if (JSDOUBLE_IS_FINITE(msec_time
)) {
2338 msec_time
= UTC(msec_time
, cx
);
2339 msec_time
= TIMECLIP(msec_time
);
2343 return SetUTCTime(cx
, obj
, d
);
2347 js_InitDateClass(JSContext
*cx
, JSObject
*obj
)
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
);
2359 SetDateToNaN(cx
, proto
);
2361 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2362 if (!JS_AliasProperty(cx
, proto
, "toUTCString", "toGMTString"))
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
))
2377 JS_FRIEND_API(JSObject
*)
2378 js_NewDateObject(JSContext
* cx
, int year
, int mon
, int mday
,
2379 int hour
, int min
, int sec
)
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
));
2390 JS_FRIEND_API(JSBool
)
2391 js_DateIsValid(JSContext
*cx
, JSObject
* obj
)
2394 return GetUTCTime(cx
, obj
, NULL
, &utctime
) && !JSDOUBLE_IS_NaN(utctime
);
2398 js_DateGetYear(JSContext
*cx
, JSObject
* obj
)
2402 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2403 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2404 JSDOUBLE_IS_NaN(localtime
)) {
2408 return (int) YearFromTime(localtime
);
2412 js_DateGetMonth(JSContext
*cx
, JSObject
* obj
)
2416 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2417 JSDOUBLE_IS_NaN(localtime
)) {
2421 return (int) MonthFromTime(localtime
);
2425 js_DateGetDate(JSContext
*cx
, JSObject
* obj
)
2429 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2430 JSDOUBLE_IS_NaN(localtime
)) {
2434 return (int) DateFromTime(localtime
);
2438 js_DateGetHours(JSContext
*cx
, JSObject
* obj
)
2442 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2443 JSDOUBLE_IS_NaN(localtime
)) {
2447 return (int) HourFromTime(localtime
);
2451 js_DateGetMinutes(JSContext
*cx
, JSObject
* obj
)
2455 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2456 JSDOUBLE_IS_NaN(localtime
)) {
2460 return (int) MinFromTime(localtime
);
2464 js_DateGetSeconds(JSContext
*cx
, JSObject
* obj
)
2468 if (!GetUTCTime(cx
, obj
, NULL
, &utctime
) || JSDOUBLE_IS_NaN(utctime
))
2471 return (int) SecFromTime(utctime
);
2475 js_DateSetYear(JSContext
*cx
, JSObject
*obj
, int year
)
2479 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2482 /* reset date if it was NaN */
2483 if (JSDOUBLE_IS_NaN(local
))
2486 local
= date_msecFromDate(year
,
2487 MonthFromTime(local
),
2488 DateFromTime(local
),
2489 HourFromTime(local
),
2494 /* SetUTCTime also invalidates local time cache. */
2495 SetUTCTime(cx
, obj
, UTC(local
, cx
));
2499 js_DateSetMonth(JSContext
*cx
, JSObject
*obj
, int month
)
2503 JS_ASSERT(month
< 12);
2505 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2508 /* bail if date was NaN */
2509 if (JSDOUBLE_IS_NaN(local
))
2512 local
= date_msecFromDate(YearFromTime(local
),
2514 DateFromTime(local
),
2515 HourFromTime(local
),
2519 SetUTCTime(cx
, obj
, UTC(local
, cx
));
2523 js_DateSetDate(JSContext
*cx
, JSObject
*obj
, int date
)
2527 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2530 if (JSDOUBLE_IS_NaN(local
))
2533 local
= date_msecFromDate(YearFromTime(local
),
2534 MonthFromTime(local
),
2536 HourFromTime(local
),
2540 SetUTCTime(cx
, obj
, UTC(local
, cx
));
2544 js_DateSetHours(JSContext
*cx
, JSObject
*obj
, int hours
)
2548 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2551 if (JSDOUBLE_IS_NaN(local
))
2553 local
= date_msecFromDate(YearFromTime(local
),
2554 MonthFromTime(local
),
2555 DateFromTime(local
),
2560 SetUTCTime(cx
, obj
, UTC(local
, cx
));
2564 js_DateSetMinutes(JSContext
*cx
, JSObject
*obj
, int minutes
)
2568 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2571 if (JSDOUBLE_IS_NaN(local
))
2573 local
= date_msecFromDate(YearFromTime(local
),
2574 MonthFromTime(local
),
2575 DateFromTime(local
),
2576 HourFromTime(local
),
2580 SetUTCTime(cx
, obj
, UTC(local
, cx
));
2584 js_DateSetSeconds(JSContext
*cx
, JSObject
*obj
, int seconds
)
2588 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2591 if (JSDOUBLE_IS_NaN(local
))
2593 local
= date_msecFromDate(YearFromTime(local
),
2594 MonthFromTime(local
),
2595 DateFromTime(local
),
2596 HourFromTime(local
),
2600 SetUTCTime(cx
, obj
, UTC(local
, cx
));
2603 JS_FRIEND_API(jsdouble
)
2604 js_DateGetMsecSinceEpoch(JSContext
*cx
, JSObject
*obj
)
2607 if (!GetUTCTime(cx
, obj
, NULL
, &utctime
))
2612 #ifdef JS_THREADSAFE
2613 #include "prinrval.h"
2615 JS_FRIEND_API(uint32
)
2618 return uint32(PR_IntervalToMilliseconds(PR_IntervalNow()));
2621 #else /* !JS_THREADSAFE */
2623 JS_FRIEND_API(uint32
)
2626 return uint32(PRMJ_Now() / PRMJ_USEC_PER_MSEC
);