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 ***** */
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'.
65 #include "jsversion.h"
66 #include "jsbuiltins.h"
74 #include "jsobjinlines.h"
79 * The JS 'Date' object is patterned after the Java 'Date' object.
84 * print(today.toLocaleString());
86 * weekDay = today.getDay();
89 * These Java (and ECMA-262) methods are supported:
92 * getDate (getUTCDate)
94 * getHours (getUTCHours)
95 * getMinutes (getUTCMinutes)
96 * getMonth (getUTCMonth)
97 * getSeconds (getUTCSeconds)
98 * getMilliseconds (getUTCMilliseconds)
102 * getFullYear (getUTCFullYear)
104 * setDate (setUTCDate)
105 * setHours (setUTCHours)
106 * setMinutes (setUTCMinutes)
107 * setMonth (setUTCMonth)
108 * setSeconds (setUTCSeconds)
109 * setMilliseconds (setUTCMilliseconds)
111 * setYear (setFullYear, setUTCFullYear)
112 * toGMTString (toUTCString)
117 * These Java methods are not supported
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.)
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;
173 #define msPerDay (SecondsPerDay * msPerSecond)
174 #define msPerHour (SecondsPerHour * msPerSecond)
175 #define msPerMinute (SecondsPerMinute * msPerSecond)
176 #define msPerSecond 1000.0
179 #define Day(t) floor((t) / msPerDay)
182 TimeWithinDay(jsdouble t
)
185 result
= fmod(t
, msPerDay
);
192 IsLeapYear(jsint year
)
194 return year
% 4 == 0 && (year
% 100 || (year
% 400 == 0));
198 DaysInYear(jsint year
)
200 return IsLeapYear(year
) ? 366 : 365;
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)
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.
230 if (t2
+ msPerDay
* DaysInYear(y
) <= t
)
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]
250 DaysInMonth(jsint year
, jsint month
)
252 JSBool leap
= IsLeapYear(year
);
253 intN result
= intN(DayFromMonth(month
, leap
) - DayFromMonth(month
-1, leap
));
258 MonthFromTime(jsdouble t
)
261 jsint year
= YearFromTime(t
);
262 d
= DayWithinYear(t
, year
);
266 if (d
< (step
+= DaysInFebruary(year
)))
268 if (d
< (step
+= 31))
270 if (d
< (step
+= 30))
272 if (d
< (step
+= 31))
274 if (d
< (step
+= 30))
276 if (d
< (step
+= 31))
278 if (d
< (step
+= 31))
280 if (d
< (step
+= 30))
282 if (d
< (step
+= 31))
284 if (d
< (step
+= 30))
290 DateFromTime(jsdouble t
)
293 jsint year
= YearFromTime(t
);
294 d
= DayWithinYear(t
, year
);
296 if (d
<= (next
= 30))
299 if (d
<= (next
+= DaysInFebruary(year
)))
302 if (d
<= (next
+= 31))
305 if (d
<= (next
+= 30))
308 if (d
<= (next
+= 31))
311 if (d
<= (next
+= 30))
314 if (d
<= (next
+= 31))
317 if (d
<= (next
+= 31))
320 if (d
<= (next
+= 30))
323 if (d
<= (next
+= 31))
326 if (d
<= (next
+= 30))
336 result
= (jsint
) Day(t
) + 4;
340 return (intN
) result
;
343 #define MakeTime(hour, min, sec, ms) \
344 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
347 MakeDay(jsdouble year
, jsdouble month
, jsdouble date
)
353 year
+= floor(month
/ 12);
355 month
= fmod(month
, 12.0);
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.
391 EquivalentYearForDST(jsint year
)
395 day
= (jsint
) DayFromYear(year
) + 4;
400 return yearStartingWith
[IsLeapYear(year
)][day
];
403 /* LocalTZA gets set by js_InitDateClass() */
404 static jsdouble LocalTZA
;
407 DaylightSavingTA(jsdouble t
, JSContext
*cx
)
410 if (JSDOUBLE_IS_NaN(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
);
429 AdjustTime(jsdouble date
, JSContext
*cx
)
431 jsdouble t
= DaylightSavingTA(date
, cx
) + LocalTZA
;
432 t
= (LocalTZA
>= 0) ? fmod(t
, msPerDay
) : -fmod(msPerDay
- t
, msPerDay
);
437 LocalTime(jsdouble t
, JSContext
*cx
)
439 return t
+ AdjustTime(t
, cx
);
443 UTC(jsdouble t
, JSContext
*cx
)
445 return t
- AdjustTime(t
- LocalTZA
, cx
);
449 HourFromTime(jsdouble t
)
451 intN result
= (intN
) fmod(floor(t
/msPerHour
), HoursPerDay
);
453 result
+= (intN
)HoursPerDay
;
458 MinFromTime(jsdouble t
)
460 intN result
= (intN
) fmod(floor(t
/ msPerMinute
), MinutesPerHour
);
462 result
+= (intN
)MinutesPerHour
;
467 SecFromTime(jsdouble t
)
469 intN result
= (intN
) fmod(floor(t
/ msPerSecond
), SecondsPerMinute
);
471 result
+= (intN
)SecondsPerMinute
;
476 msFromTime(jsdouble t
)
478 intN result
= (intN
) fmod(t
, msPerSecond
);
480 result
+= (intN
)msPerSecond
;
485 * end of ECMA 'support' functions
489 * Other Support routines and definitions
492 Class js_DateClass
= {
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 */
505 /* for use by date_parse */
507 static const char* wtb
[] = {
509 "monday", "tuesday", "wednesday", "thursday", "friday",
510 "saturday", "sunday",
511 "january", "february", "march", "april", "may", "june",
512 "july", "august", "september", "october", "november", "december",
518 /* time zone table needs to be expanded */
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 */
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
]) {
541 if (JS_TOLOWER((jschar
)s1
[s1off
]) != JS_TOLOWER(s2
[s2off
])) {
545 if ((jschar
)s1
[s1off
] != s2
[s2off
]) {
561 /* find UTC time from given date... no 1900 correction! */
563 date_msecFromDate(jsdouble year
, jsdouble mon
, jsdouble mday
, jsdouble hour
,
564 jsdouble min
, jsdouble sec
, jsdouble msec
)
570 day
= MakeDay(year
, mon
, mday
);
571 msec_time
= MakeTime(hour
, min
, sec
, msec
);
572 result
= MakeDate(day
, msec_time
);
576 /* compute the time in msec (unclipped) from the given args */
580 date_msecFromArgs(JSContext
*cx
, uintN argc
, Value
*argv
, jsdouble
*rval
)
583 jsdouble array
[MAXARGS
];
586 for (loop
= 0; loop
< MAXARGS
; loop
++) {
589 if (!ValueToNumber(cx
, argv
[loop
], &d
))
591 /* return NaN if any arg is not finite */
592 if (!JSDOUBLE_IS_FINITE(d
)) {
596 array
[loop
] = js_DoubleToInteger(d
);
599 array
[loop
] = 1; /* Default the date argument to 1. */
606 /* adjust 2-digit years into the 20th century */
607 if (array
[0] >= 0 && array
[0] <= 99)
610 msec_time
= date_msecFromDate(array
[0], array
[1], array
[2],
611 array
[3], array
[4], array
[5], array
[6]);
617 * See ECMA 15.9.4.[3-10];
620 date_UTC(JSContext
*cx
, uintN argc
, Value
*vp
)
624 if (!date_msecFromArgs(cx
, argc
, vp
+ 2, &msec_time
))
627 msec_time
= TIMECLIP(msec_time
);
629 vp
->setNumber(msec_time
);
634 * Read and convert decimal digits from s[*i] into *result
637 * Succeed if any digits are converted. Advance *i only
638 * as digits are consumed.
641 digits(size_t *result
, const jschar
*s
, size_t *i
, size_t limit
)
646 ('0' <= s
[*i
] && s
[*i
] <= '9')) {
648 *result
+= (s
[*i
] - '0');
655 * Read and convert decimal digits to the right of a decimal point,
656 * representing a fractional integer, from s[*i] into *result
659 * Succeed if any digits are converted. Advance *i only
660 * as digits are consumed.
663 fractional(jsdouble
*result
, const jschar
*s
, size_t *i
, size_t limit
)
665 jsdouble factor
= 0.1;
669 ('0' <= s
[*i
] && s
[*i
] <= '9')) {
670 *result
+= (s
[*i
] - '0') * factor
;
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
685 ndigits(size_t n
, size_t *result
, const jschar
*s
, size_t* i
, size_t limit
)
689 if (digits(result
, s
, i
, JS_MIN(limit
, init
+n
)))
690 return ((*i
- init
) == n
);
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.
724 * YYYY-MM (eg 1997-07)
727 * YYYY-MM-DD (eg 1997-07-16)
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)
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)
753 date_parseISOString(JSLinearString
*str
, jsdouble
*result
, JSContext
*cx
)
769 bool isLocalTime
= JS_FALSE
;
773 #define PEEK(ch) (i < limit && s[i] == ch)
777 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
780 #define DONE_DATE_UNLESS(ch) \
782 if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
785 #define DONE_UNLESS(ch) \
787 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
790 #define NEED_NDIGITS(n, field) \
792 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
796 limit
= str
->length();
798 if (PEEK('+') || PEEK('-')) {
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
);
813 NEED_NDIGITS(2, hour
);
815 NEED_NDIGITS(2, min
);
819 NEED_NDIGITS(2, sec
);
822 if (!fractional(&frac
, s
, &i
, limit
))
829 } else if (PEEK('+') || PEEK('-')) {
833 NEED_NDIGITS(2, tzHour
);
835 NEED_NDIGITS(2, tzMin
);
837 isLocalTime
= JS_TRUE
;
841 if (year
> 275943 // ceil(1e8/365) + 1970
842 || (month
== 0 || month
> 12)
843 || (day
== 0 || day
> size_t(DaysInMonth(year
,month
)))
845 || ((hour
== 24) && (min
> 0 || sec
> 0))
855 month
-= 1; /* convert month to 0-based */
857 msec
= date_msecFromDate(dateMul
* (jsdouble
)year
, month
, day
,
862 msec
= UTC(msec
, cx
);
864 msec
-= ((tzMul
) * ((tzHour
* msPerHour
)
865 + (tzMin
* msPerMinute
)));
868 if (msec
< -8.64e15
|| msec
> 8.64e15
)
887 date_parseString(JSLinearString
*str
, jsdouble
*result
, JSContext
*cx
)
904 JSBool seenplusminus
= JS_FALSE
;
906 JSBool seenmonthname
= JS_FALSE
;
908 if (date_parseISOString(str
, result
, cx
))
912 limit
= str
->length();
918 if (c
<= ' ' || c
== ',' || c
== '-') {
919 if (c
== '-' && '0' <= s
[i
] && s
[i
] <= '9') {
924 if (c
== '(') { /* comments) */
929 if (c
== '(') depth
++;
936 if ('0' <= c
&& c
<= '9') {
938 while (i
< limit
&& '0' <= (c
= s
[i
]) && c
<= '9') {
939 n
= n
* 10 + c
- '0';
943 /* allow TZA before the year, so
944 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
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
;
957 n
= n
* 60; /* EG. "GMT-3" */
959 n
= n
% 100 + n
/ 100 * 60; /* eg "GMT-0430" */
960 if (prevc
== '+') /* plus means east of GMT */
962 if (tzoffset
!= 0 && tzoffset
!= -1)
965 } else if (prevc
== '/' && mon
>= 0 && mday
>= 0 && year
< 0) {
966 if (c
<= ' ' || c
== ',' || c
== '/' || i
>= limit
)
970 } else if (c
== ':') {
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 */
986 } else if (i
< limit
&& c
!= ',' && c
> ' ' && c
!= '-' && c
!= '(') {
988 } else if (seenplusminus
&& n
< 60) { /* handle GMT-3:30 */
993 } else if (hour
>= 0 && min
< 0) {
995 } else if (prevc
== ':' && min
>= 0 && sec
< 0) {
997 } else if (mon
< 0) {
999 } else if (mon
>= 0 && mday
< 0) {
1001 } else if (mon
>= 0 && mday
>= 0 && year
< 0) {
1007 } else if (c
== '/' || c
== ':' || c
== '+' || c
== '-') {
1014 if (!(('A' <= c
&& c
<= 'Z') || ('a' <= c
&& c
<= 'z')))
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
];
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) {
1033 if (action
== -1 && hour
== 12) { /* am */
1035 } else if (action
== -2 && hour
!= 12) { /* pm */
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
) {
1045 seenmonthname
= JS_TRUE
;
1046 temp
= /*byte*/ (action
- 2) + 1;
1050 } else if (mday
< 0) {
1053 } else if (year
< 0) {
1060 tzoffset
= action
- 10000;
1070 if (year
< 0 || mon
< 0 || mday
< 0)
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
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.
1089 i. If m < 70, f/m/l is interpreted as
1090 year/month/day where year is the number of years after
1092 ii. If m >= 70, the date is invalid.
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)) {
1107 if (year
>= 70 && year
< 100) {
1110 } else if (mon
< 70) { /* (a) month/day/year */
1114 } else if (mon
< 100) { /* (b) year/month/day */
1123 } else { /* (c) year/month/day */
1133 mon
-= 1; /* convert month to 0-based */
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
);
1146 msec
+= tzoffset
* msPerMinute
;
1159 date_parse(JSContext
*cx
, uintN argc
, Value
*vp
)
1165 vp
->setDouble(js_NaN
);
1168 str
= js_ValueToString(cx
, vp
[2]);
1171 vp
[2].setString(str
);
1172 JSLinearString
*linearStr
= str
->ensureLinear(cx
);
1176 if (!date_parseString(linearStr
, &result
, cx
)) {
1177 vp
->setDouble(js_NaN
);
1181 result
= TIMECLIP(result
);
1182 vp
->setNumber(result
);
1186 static inline jsdouble
1189 return (jsdouble
) (PRMJ_Now() / PRMJ_USEC_PER_MSEC
);
1193 date_now(JSContext
*cx
, uintN argc
, Value
*vp
)
1195 vp
->setDouble(NowAsMillis());
1200 static jsdouble FASTCALL
1201 date_now_tn(JSContext
*)
1203 return NowAsMillis();
1208 * Get UTC time from the date object. Returns false if the object is not
1212 GetUTCTime(JSContext
*cx
, JSObject
*obj
, Value
*vp
, jsdouble
*dp
)
1214 if (!InstanceOf(cx
, obj
, &js_DateClass
, vp
? vp
+ 2 : NULL
))
1216 *dp
= obj
->getDateUTCTime().toNumber();
1221 * Set UTC time to a given time and invalidate cached local time.
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
));
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.
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
))
1263 if (!JSDOUBLE_IS_FINITE(utcTime
)) {
1264 for (size_t ind
= JSObject::JSSLOT_DATE_COMPONENTS_START
;
1265 ind
< JSObject::DATE_CLASS_RESERVED_SLOTS
;
1267 obj
->setSlot(ind
, DoubleValue(utcTime
));
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. */
1281 if (yearStartTime
> localTime
) {
1283 yearStartTime
-= (msPerDay
* DaysInYear(year
));
1284 yearDays
= DaysInYear(year
);
1286 yearDays
= DaysInYear(year
);
1287 jsdouble nextStart
= yearStartTime
+ (msPerDay
* yearDays
);
1288 if (nextStart
<= localTime
) {
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;
1311 next
+= ((yearDays
== 366) ? 29 : 28);
1317 if (day
<= (next
+= 31)) {
1322 if (day
<= (next
+= 30)) {
1327 if (day
<= (next
+= 31)) {
1332 if (day
<= (next
+= 30)) {
1337 if (day
<= (next
+= 31)) {
1342 if (day
<= (next
+= 31)) {
1347 if (day
<= (next
+= 30)) {
1352 if (day
<= (next
+= 31)) {
1357 if (day
<= (next
+= 30)) {
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
));
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
))
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
))
1401 *time
= obj
->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME
).toDouble();
1407 GetThisUTCTime(JSContext
*cx
, Value
*vp
, jsdouble
*dp
)
1409 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1412 return GetUTCTime(cx
, obj
, vp
, dp
);
1416 * See ECMA 15.9.5.4 thru 15.9.5.23
1419 date_getTime(JSContext
*cx
, uintN argc
, Value
*vp
)
1422 if (!GetThisUTCTime(cx
, vp
, &result
))
1424 vp
->setNumber(result
);
1429 date_getYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1431 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1435 if (!GetAndCacheLocalTime(cx
, obj
, vp
))
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;
1451 date_getFullYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1453 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1457 if (!GetAndCacheLocalTime(cx
, obj
, vp
))
1460 *vp
= obj
->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR
);
1465 date_getUTCFullYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1468 if (!GetThisUTCTime(cx
, vp
, &result
))
1471 if (JSDOUBLE_IS_FINITE(result
))
1472 result
= YearFromTime(result
);
1474 vp
->setNumber(result
);
1479 date_getMonth(JSContext
*cx
, uintN argc
, Value
*vp
)
1481 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1485 if (!GetAndCacheLocalTime(cx
, obj
, vp
))
1488 *vp
= obj
->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH
);
1493 date_getUTCMonth(JSContext
*cx
, uintN argc
, Value
*vp
)
1496 if (!GetThisUTCTime(cx
, vp
, &result
))
1499 if (JSDOUBLE_IS_FINITE(result
))
1500 result
= MonthFromTime(result
);
1502 vp
->setNumber(result
);
1507 date_getDate(JSContext
*cx
, uintN argc
, Value
*vp
)
1509 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1513 if (!GetAndCacheLocalTime(cx
, obj
, vp
))
1516 *vp
= obj
->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE
);
1521 date_getUTCDate(JSContext
*cx
, uintN argc
, Value
*vp
)
1524 if (!GetThisUTCTime(cx
, vp
, &result
))
1527 if (JSDOUBLE_IS_FINITE(result
))
1528 result
= DateFromTime(result
);
1530 vp
->setNumber(result
);
1535 date_getDay(JSContext
*cx
, uintN argc
, Value
*vp
)
1537 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1541 if (!GetAndCacheLocalTime(cx
, obj
, vp
))
1544 *vp
= obj
->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY
);
1549 date_getUTCDay(JSContext
*cx
, uintN argc
, Value
*vp
)
1552 if (!GetThisUTCTime(cx
, vp
, &result
))
1555 if (JSDOUBLE_IS_FINITE(result
))
1556 result
= WeekDay(result
);
1558 vp
->setNumber(result
);
1563 date_getHours(JSContext
*cx
, uintN argc
, Value
*vp
)
1565 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1569 if (!GetAndCacheLocalTime(cx
, obj
, vp
))
1572 *vp
= obj
->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS
);
1577 date_getUTCHours(JSContext
*cx
, uintN argc
, Value
*vp
)
1580 if (!GetThisUTCTime(cx
, vp
, &result
))
1583 if (JSDOUBLE_IS_FINITE(result
))
1584 result
= HourFromTime(result
);
1586 vp
->setNumber(result
);
1591 date_getMinutes(JSContext
*cx
, uintN argc
, Value
*vp
)
1593 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1597 if (!GetAndCacheLocalTime(cx
, obj
, vp
))
1600 *vp
= obj
->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES
);
1605 date_getUTCMinutes(JSContext
*cx
, uintN argc
, Value
*vp
)
1608 if (!GetThisUTCTime(cx
, vp
, &result
))
1611 if (JSDOUBLE_IS_FINITE(result
))
1612 result
= MinFromTime(result
);
1614 vp
->setNumber(result
);
1618 /* Date.getSeconds is mapped to getUTCSeconds */
1621 date_getUTCSeconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1623 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1627 if (!GetAndCacheLocalTime(cx
, obj
, vp
))
1630 *vp
= obj
->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS
);
1634 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1637 date_getUTCMilliseconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1640 if (!GetThisUTCTime(cx
, vp
, &result
))
1643 if (JSDOUBLE_IS_FINITE(result
))
1644 result
= msFromTime(result
);
1646 vp
->setNumber(result
);
1651 date_getTimezoneOffset(JSContext
*cx
, uintN argc
, Value
*vp
)
1653 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1658 if (!GetUTCTime(cx
, obj
, vp
, &utctime
))
1662 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
))
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
);
1676 date_setTime(JSContext
*cx
, uintN argc
, Value
*vp
)
1678 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1682 if (!InstanceOf(cx
, obj
, &js_DateClass
, vp
+ 2))
1686 SetDateToNaN(cx
, obj
, vp
);
1691 if (!ValueToNumber(cx
, vp
[2], &result
))
1694 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1698 date_makeTime(JSContext
*cx
, uintN maxargs
, JSBool local
, uintN argc
, Value
*vp
)
1702 jsdouble args
[4], *argp
, *stop
;
1703 jsdouble hour
, min
, sec
, msec
;
1704 jsdouble lorutime
; /* Local or UTC version of *date */
1709 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1713 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1716 /* just return NaN if the date is already NaN */
1717 if (!JSDOUBLE_IS_FINITE(result
)) {
1718 vp
->setNumber(result
);
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.
1732 SetDateToNaN(cx
, obj
, vp
);
1736 argc
= maxargs
; /* clamp argc */
1737 JS_ASSERT(argc
<= 4);
1740 for (i
= 0; i
< argc
; i
++) {
1741 if (!ValueToNumber(cx
, argv
[i
], &args
[i
]))
1743 if (!JSDOUBLE_IS_FINITE(args
[i
])) {
1744 SetDateToNaN(cx
, obj
, vp
);
1747 args
[i
] = js_DoubleToInteger(args
[i
]);
1751 lorutime
= LocalTime(result
, cx
);
1757 if (maxargs
>= 4 && argp
< stop
)
1760 hour
= HourFromTime(lorutime
);
1762 if (maxargs
>= 3 && argp
< stop
)
1765 min
= MinFromTime(lorutime
);
1767 if (maxargs
>= 2 && argp
< stop
)
1770 sec
= SecFromTime(lorutime
);
1772 if (maxargs
>= 1 && argp
< stop
)
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); */
1783 result
= UTC(result
, cx
);
1785 /* fprintf(stderr, "%f\n", result); */
1787 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1791 date_setMilliseconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1793 return date_makeTime(cx
, 1, JS_TRUE
, argc
, vp
);
1797 date_setUTCMilliseconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1799 return date_makeTime(cx
, 1, JS_FALSE
, argc
, vp
);
1803 date_setSeconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1805 return date_makeTime(cx
, 2, JS_TRUE
, argc
, vp
);
1809 date_setUTCSeconds(JSContext
*cx
, uintN argc
, Value
*vp
)
1811 return date_makeTime(cx
, 2, JS_FALSE
, argc
, vp
);
1815 date_setMinutes(JSContext
*cx
, uintN argc
, Value
*vp
)
1817 return date_makeTime(cx
, 3, JS_TRUE
, argc
, vp
);
1821 date_setUTCMinutes(JSContext
*cx
, uintN argc
, Value
*vp
)
1823 return date_makeTime(cx
, 3, JS_FALSE
, argc
, vp
);
1827 date_setHours(JSContext
*cx
, uintN argc
, Value
*vp
)
1829 return date_makeTime(cx
, 4, JS_TRUE
, argc
, vp
);
1833 date_setUTCHours(JSContext
*cx
, uintN argc
, Value
*vp
)
1835 return date_makeTime(cx
, 4, JS_FALSE
, argc
, vp
);
1839 date_makeDate(JSContext
*cx
, uintN maxargs
, JSBool local
, uintN argc
, Value
*vp
)
1843 jsdouble lorutime
; /* local or UTC version of *date */
1844 jsdouble args
[3], *argp
, *stop
;
1845 jsdouble year
, month
, day
;
1848 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1852 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1855 /* see complaint about ECMA in date_MakeTime */
1857 SetDateToNaN(cx
, obj
, vp
);
1861 argc
= maxargs
; /* clamp argc */
1862 JS_ASSERT(1 <= argc
&& argc
<= 3);
1865 for (i
= 0; i
< argc
; i
++) {
1866 if (!ValueToNumber(cx
, argv
[i
], &args
[i
]))
1868 if (!JSDOUBLE_IS_FINITE(args
[i
])) {
1869 SetDateToNaN(cx
, obj
, vp
);
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
))) {
1879 vp
->setDouble(result
);
1884 lorutime
= local
? LocalTime(result
, cx
) : result
;
1889 if (maxargs
>= 3 && argp
< stop
)
1892 year
= YearFromTime(lorutime
);
1894 if (maxargs
>= 2 && argp
< stop
)
1897 month
= MonthFromTime(lorutime
);
1899 if (maxargs
>= 1 && argp
< stop
)
1902 day
= DateFromTime(lorutime
);
1904 day
= MakeDay(year
, month
, day
); /* day within year */
1905 result
= MakeDate(day
, TimeWithinDay(lorutime
));
1908 result
= UTC(result
, cx
);
1910 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1914 date_setDate(JSContext
*cx
, uintN argc
, Value
*vp
)
1916 return date_makeDate(cx
, 1, JS_TRUE
, argc
, vp
);
1920 date_setUTCDate(JSContext
*cx
, uintN argc
, Value
*vp
)
1922 return date_makeDate(cx
, 1, JS_FALSE
, argc
, vp
);
1926 date_setMonth(JSContext
*cx
, uintN argc
, Value
*vp
)
1928 return date_makeDate(cx
, 2, JS_TRUE
, argc
, vp
);
1932 date_setUTCMonth(JSContext
*cx
, uintN argc
, Value
*vp
)
1934 return date_makeDate(cx
, 2, JS_FALSE
, argc
, vp
);
1938 date_setFullYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1940 return date_makeDate(cx
, 3, JS_TRUE
, argc
, vp
);
1944 date_setUTCFullYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1946 return date_makeDate(cx
, 3, JS_FALSE
, argc
, vp
);
1950 date_setYear(JSContext
*cx
, uintN argc
, Value
*vp
)
1952 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1957 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1961 /* Call this only after GetUTCTime has verified that obj is Date. */
1962 SetDateToNaN(cx
, obj
, vp
);
1967 if (!ValueToNumber(cx
, vp
[2], &year
))
1969 if (!JSDOUBLE_IS_FINITE(year
)) {
1970 SetDateToNaN(cx
, obj
, vp
);
1973 year
= js_DoubleToInteger(year
);
1974 if (year
>= 0 && year
<= 99)
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.
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
));
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
));
2026 date_utc_format(JSContext
*cx
, Value
*vp
,
2027 void (*printFunc
)(char*, size_t, jsdouble
))
2030 if (!GetThisUTCTime(cx
, vp
, &utctime
))
2034 if (!JSDOUBLE_IS_FINITE(utctime
))
2035 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
2037 (*printFunc
)(buf
, sizeof buf
, utctime
);
2039 JSString
*str
= JS_NewStringCopyZ(cx
, buf
);
2047 date_toGMTString(JSContext
*cx
, uintN argc
, Value
*vp
)
2049 return date_utc_format(cx
, vp
, print_gmt_string
);
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. */
2060 date_toJSON(JSContext
*cx
, uintN argc
, Value
*vp
)
2063 JSObject
*obj
= ToObject(cx
, &vp
[1]);
2069 if (!DefaultValue(cx
, obj
, JSTYPE_NUMBER
, &tv
))
2073 if (tv
.isDouble() && !JSDOUBLE_IS_FINITE(tv
.toDouble())) {
2079 Value
&toISO
= vp
[0];
2080 if (!obj
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.toISOStringAtom
), &toISO
))
2084 if (!js_IsCallable(toISO
)) {
2085 JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
, js_GetErrorMessage
, NULL
,
2086 JSMSG_BAD_TOISOSTRING_PROP
);
2092 InvokeArgsGuard args
;
2093 if (!cx
->stack().pushInvokeArgs(cx
, 0, &args
))
2096 args
.callee() = toISO
;
2097 args
.thisv().setObject(*obj
);
2099 if (!Invoke(cx
, args
, 0))
2105 /* for Date.toLocaleString; interface to PRMJTime date struct.
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
2124 split
->tm_isdst
= (DaylightSavingTA(timeval
, cx
) != 0);
2127 typedef enum formatspec
{
2128 FORMATSPEC_FULL
, FORMATSPEC_DATE
, FORMATSPEC_TIME
2131 /* helper function */
2133 date_format(JSContext
*cx
, jsdouble date
, formatspec format
, Value
*rval
)
2142 if (!JSDOUBLE_IS_FINITE(date
)) {
2143 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
2145 jsdouble local
= LocalTime(date
, cx
);
2147 /* offset from GMT in minutes. The offset includes daylight savings,
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
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.
2175 tzlen
= strlen(tzbuf
);
2179 for (i
= 0; i
< tzlen
; i
++) {
2180 jschar c
= tzbuf
[i
];
2182 !(isalpha(c
) || isdigit(c
) ||
2183 c
== ' ' || c
== '(' || c
== ')')) {
2189 /* Also reject it if it's not parenthesized or if it's '()'. */
2190 if (tzbuf
[0] != '(' || tzbuf
[1] == ')')
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
),
2213 usetz
? tzbuf
: "");
2215 case FORMATSPEC_DATE
:
2216 /* Tue Oct 31 2000 */
2217 JS_snprintf(buf
, sizeof buf
,
2219 days
[WeekDay(local
)],
2220 months
[MonthFromTime(local
)],
2221 DateFromTime(local
),
2222 YearFromTime(local
));
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
),
2233 usetz
? tzbuf
: "");
2238 str
= JS_NewStringCopyZ(cx
, buf
);
2241 rval
->setString(str
);
2246 date_toLocaleHelper(JSContext
*cx
, JSObject
*obj
, const char *format
, Value
*vp
)
2253 if (!GetUTCTime(cx
, obj
, vp
, &utctime
))
2256 if (!JSDOUBLE_IS_FINITE(utctime
)) {
2257 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
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
);
2296 date_toLocaleString(JSContext
*cx
, uintN argc
, Value
*vp
)
2298 JSObject
*obj
= ToObject(cx
, &vp
[1]);
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__)
2316 date_toLocaleDateString(JSContext
*cx
, uintN argc
, Value
*vp
)
2318 JSObject
*obj
= ToObject(cx
, &vp
[1]);
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__)
2336 date_toLocaleTimeString(JSContext
*cx
, uintN argc
, Value
*vp
)
2338 JSObject
*obj
= ToObject(cx
, &vp
[1]);
2342 return date_toLocaleHelper(cx
, obj
, "%X", vp
);
2346 date_toLocaleFormat(JSContext
*cx
, uintN argc
, Value
*vp
)
2349 return date_toLocaleString(cx
, argc
, vp
);
2351 JSObject
*obj
= ToObject(cx
, &vp
[1]);
2355 JSString
*fmt
= js_ValueToString(cx
, vp
[2]);
2358 vp
[2].setString(fmt
);
2359 JSAutoByteString
fmtbytes(cx
, fmt
);
2363 return date_toLocaleHelper(cx
, obj
, fmtbytes
.ptr(), vp
);
2367 date_toTimeString(JSContext
*cx
, uintN argc
, Value
*vp
)
2370 if (!GetThisUTCTime(cx
, vp
, &utctime
))
2372 return date_format(cx
, utctime
, FORMATSPEC_TIME
, vp
);
2376 date_toDateString(JSContext
*cx
, uintN argc
, Value
*vp
)
2379 if (!GetThisUTCTime(cx
, vp
, &utctime
))
2381 return date_format(cx
, utctime
, FORMATSPEC_DATE
, vp
);
2389 date_toSource(JSContext
*cx
, uintN argc
, Value
*vp
)
2392 if (!GetThisUTCTime(cx
, vp
, &utctime
))
2396 char *numStr
= NumberToCString(cx
, &cbuf
, utctime
);
2398 JS_ReportOutOfMemory(cx
);
2402 char *bytes
= JS_smprintf("(new %s(%s))", js_Date_str
, numStr
);
2404 JS_ReportOutOfMemory(cx
);
2408 JSString
*str
= JS_NewStringCopyZ(cx
, bytes
);
2418 date_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
2421 if (!GetThisUTCTime(cx
, vp
, &utctime
))
2424 return date_format(cx
, utctime
, FORMATSPEC_FULL
, vp
);
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. */
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]);
2445 /* Convert to number only if the hint was given, otherwise favor string. */
2446 JSString
*str
= js_ValueToString(cx
, vp
[2]);
2449 JSLinearString
*linear_str
= str
->ensureLinear(cx
);
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
),
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),
2515 JS_FN(js_toSource_str
, date_toSource
, 0,0),
2517 JS_FN(js_toString_str
, date_toString
, 0,0),
2518 JS_FN(js_valueOf_str
, date_valueOf
, 0,0),
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. */
2535 } else if (argc
== 1) {
2536 if (!argv
[0].isString()) {
2537 /* the argument is a millisecond number */
2538 if (!ValueToNumber(cx
, argv
[0], &d
))
2542 /* the argument is a string; parse it. */
2543 JSString
*str
= js_ValueToString(cx
, argv
[0]);
2546 argv
[0].setString(str
);
2547 JSLinearString
*linearStr
= str
->ensureLinear(cx
);
2551 if (!date_parseString(linearStr
, &d
, cx
))
2558 if (!date_msecFromArgs(cx
, argc
, argv
, &msec_time
))
2561 if (JSDOUBLE_IS_FINITE(msec_time
)) {
2562 msec_time
= UTC(msec_time
, cx
);
2563 msec_time
= TIMECLIP(msec_time
);
2568 JSObject
*obj
= js_NewDateObjectMsec(cx
, d
);
2571 vp
->setObject(*obj
);
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
);
2586 AutoObjectRooter
tvr(cx
, proto
);
2588 SetDateToNaN(cx
, proto
);
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)) {
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
))
2615 if (!SetUTCTime(cx
, obj
, msec_time
))
2620 JS_FRIEND_API(JSObject
*)
2621 js_NewDateObject(JSContext
* cx
, int year
, int mon
, int mday
,
2622 int hour
, int min
, int sec
)
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
));
2633 JS_FRIEND_API(JSBool
)
2634 js_DateIsValid(JSContext
*cx
, JSObject
* obj
)
2637 return GetUTCTime(cx
, obj
, NULL
, &utctime
) && !JSDOUBLE_IS_NaN(utctime
);
2641 js_DateGetYear(JSContext
*cx
, JSObject
* obj
)
2645 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2646 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2647 JSDOUBLE_IS_NaN(localtime
)) {
2651 return (int) YearFromTime(localtime
);
2655 js_DateGetMonth(JSContext
*cx
, JSObject
* obj
)
2659 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2660 JSDOUBLE_IS_NaN(localtime
)) {
2664 return (int) MonthFromTime(localtime
);
2668 js_DateGetDate(JSContext
*cx
, JSObject
* obj
)
2672 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2673 JSDOUBLE_IS_NaN(localtime
)) {
2677 return (int) DateFromTime(localtime
);
2681 js_DateGetHours(JSContext
*cx
, JSObject
* obj
)
2685 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2686 JSDOUBLE_IS_NaN(localtime
)) {
2690 return (int) HourFromTime(localtime
);
2694 js_DateGetMinutes(JSContext
*cx
, JSObject
* obj
)
2698 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2699 JSDOUBLE_IS_NaN(localtime
)) {
2703 return (int) MinFromTime(localtime
);
2707 js_DateGetSeconds(JSContext
*cx
, JSObject
* obj
)
2711 if (!GetUTCTime(cx
, obj
, NULL
, &utctime
) || JSDOUBLE_IS_NaN(utctime
))
2714 return (int) SecFromTime(utctime
);
2717 JS_FRIEND_API(jsdouble
)
2718 js_DateGetMsecSinceEpoch(JSContext
*cx
, JSObject
*obj
)
2721 if (!GetUTCTime(cx
, obj
, NULL
, &utctime
))
2726 #ifdef JS_THREADSAFE
2727 #include "prinrval.h"
2729 JS_FRIEND_API(uint32
)
2732 return uint32(PR_IntervalToMilliseconds(PR_IntervalNow()));
2735 #else /* !JS_THREADSAFE */
2737 JS_FRIEND_API(uint32
)
2740 return uint32(PRMJ_Now() / PRMJ_USEC_PER_MSEC
);