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'.
63 #include "jsutil.h" /* Added by JSIFY */
65 #include "jsversion.h"
66 #include "jsbuiltins.h"
75 * The JS 'Date' object is patterned after the Java 'Date' object.
80 * print(today.toLocaleString());
82 * weekDay = today.getDay();
85 * These Java (and ECMA-262) methods are supported:
88 * getDate (getUTCDate)
90 * getHours (getUTCHours)
91 * getMinutes (getUTCMinutes)
92 * getMonth (getUTCMonth)
93 * getSeconds (getUTCSeconds)
94 * getMilliseconds (getUTCMilliseconds)
98 * getFullYear (getUTCFullYear)
100 * setDate (setUTCDate)
101 * setHours (setUTCHours)
102 * setMinutes (setUTCMinutes)
103 * setMonth (setUTCMonth)
104 * setSeconds (setUTCSeconds)
105 * setMilliseconds (setUTCMilliseconds)
107 * setYear (setFullYear, setUTCFullYear)
108 * toGMTString (toUTCString)
113 * These Java methods are not supported
123 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
124 * definition and reduce dependence on NSPR. NSPR is used to get the current
125 * time in milliseconds, the time zone offset, and the daylight savings time
126 * offset for a given time. NSPR is also used for Date.toLocaleString(), for
127 * locale-specific formatting, and to get a string representing the timezone.
128 * (Which turns out to be platform-dependent.)
131 * (I did some performance tests by timing how long it took to run what
132 * I had of the js ECMA conformance tests.)
134 * - look at saving results across multiple calls to supporting
135 * functions; the toString functions compute some of the same values
136 * multiple times. Although - I took a quick stab at this, and I lost
137 * rather than gained. (Fractionally.) Hard to tell what compilers/processors
138 * are doing these days.
140 * - look at tweaking function return types to return double instead
141 * of int; this seems to make things run slightly faster sometimes.
142 * (though it could be architecture-dependent.) It'd be good to see
143 * how this does on win32. (Tried it on irix.) Types could use a
144 * general going-over.
148 * Supporting functions - ECMA 15.9.1.*
151 #define HalfTimeDomain 8.64e15
152 #define HoursPerDay 24.0
153 #define MinutesPerDay (HoursPerDay * MinutesPerHour)
154 #define MinutesPerHour 60.0
155 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
156 #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
157 #define SecondsPerMinute 60.0
159 #if defined(XP_WIN) || defined(XP_OS2)
160 /* Work around msvc double optimization bug by making these runtime values; if
161 * they're available at compile time, msvc optimizes division by them by
162 * computing the reciprocal and multiplying instead of dividing - this loses
163 * when the reciprocal isn't representable in a double.
165 static jsdouble msPerSecond
= 1000.0;
166 static jsdouble msPerDay
= SecondsPerDay
* 1000.0;
167 static jsdouble msPerHour
= SecondsPerHour
* 1000.0;
168 static jsdouble msPerMinute
= SecondsPerMinute
* 1000.0;
170 #define msPerDay (SecondsPerDay * msPerSecond)
171 #define msPerHour (SecondsPerHour * msPerSecond)
172 #define msPerMinute (SecondsPerMinute * msPerSecond)
173 #define msPerSecond 1000.0
176 #define Day(t) floor((t) / msPerDay)
179 TimeWithinDay(jsdouble t
)
182 result
= fmod(t
, msPerDay
);
188 #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
191 /* math here has to be f.p, because we need
192 * floor((1968 - 1969) / 4) == -1
194 #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
195 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
196 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
199 YearFromTime(jsdouble t
)
201 jsint y
= (jsint
) floor(t
/(msPerDay
*365.2425)) + 1970;
202 jsdouble t2
= (jsdouble
) TimeFromYear(y
);
207 if (t2
+ msPerDay
* DaysInYear(y
) <= t
)
213 #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
215 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
218 * The following array contains the day of year for the first day of
219 * each month, where index 0 is January, and day 0 is January 1.
221 static jsdouble firstDayOfMonth
[2][12] = {
222 {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
223 {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
226 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
229 MonthFromTime(jsdouble t
)
232 jsint year
= YearFromTime(t
);
233 d
= DayWithinYear(t
, year
);
237 step
+= (InLeapYear(t
) ? 29 : 28);
240 if (d
< (step
+= 31))
242 if (d
< (step
+= 30))
244 if (d
< (step
+= 31))
246 if (d
< (step
+= 30))
248 if (d
< (step
+= 31))
250 if (d
< (step
+= 31))
252 if (d
< (step
+= 30))
254 if (d
< (step
+= 31))
256 if (d
< (step
+= 30))
262 DateFromTime(jsdouble t
)
265 jsint year
= YearFromTime(t
);
266 d
= DayWithinYear(t
, year
);
268 if (d
<= (next
= 30))
271 next
+= (InLeapYear(t
) ? 29 : 28);
275 if (d
<= (next
+= 31))
278 if (d
<= (next
+= 30))
281 if (d
<= (next
+= 31))
284 if (d
<= (next
+= 30))
287 if (d
<= (next
+= 31))
290 if (d
<= (next
+= 31))
293 if (d
<= (next
+= 30))
296 if (d
<= (next
+= 31))
299 if (d
<= (next
+= 30))
309 result
= (jsint
) Day(t
) + 4;
313 return (intN
) result
;
316 #define MakeTime(hour, min, sec, ms) \
317 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
320 MakeDay(jsdouble year
, jsdouble month
, jsdouble date
)
326 year
+= floor(month
/ 12);
328 month
= fmod(month
, 12.0);
332 leap
= (DaysInYear((jsint
) year
) == 366);
334 yearday
= floor(TimeFromYear(year
) / msPerDay
);
335 monthday
= DayFromMonth(month
, leap
);
337 return yearday
+ monthday
+ date
- 1;
340 #define MakeDate(day, time) ((day) * msPerDay + (time))
343 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
345 * yearStartingWith[0][i] is an example non-leap year where
346 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
348 * yearStartingWith[1][i] is an example leap year where
349 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
351 static jsint yearStartingWith
[2][7] = {
352 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
353 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
357 * Find a year for which any given date will fall on the same weekday.
359 * This function should be used with caution when used other than
360 * for determining DST; it hasn't been proven not to produce an
361 * incorrect year for times near year boundaries.
364 EquivalentYearForDST(jsint year
)
369 day
= (jsint
) DayFromYear(year
) + 4;
374 isLeapYear
= (DaysInYear(year
) == 366);
376 return yearStartingWith
[isLeapYear
][day
];
379 /* LocalTZA gets set by js_InitDateClass() */
380 static jsdouble LocalTZA
;
383 DaylightSavingTA(jsdouble t
)
391 if (JSDOUBLE_IS_NaN(t
))
395 * If earlier than 1970 or after 2038, potentially beyond the ken of
396 * many OSes, map it to an equivalent year before asking.
398 if (t
< 0.0 || t
> 2145916800000.0) {
402 year
= EquivalentYearForDST(YearFromTime(t
));
403 day
= MakeDay(year
, MonthFromTime(t
), DateFromTime(t
));
404 t
= MakeDate(day
, TimeWithinDay(t
));
407 /* put our t in an LL, and map it to usec for prtime */
409 JSLL_I2L(ms2us
, PRMJ_USEC_PER_MSEC
);
410 JSLL_MUL(PR_t
, PR_t
, ms2us
);
412 offset
= PRMJ_DSTOffset(PR_t
);
414 JSLL_DIV(offset
, offset
, ms2us
);
415 JSLL_L2D(result
, offset
);
420 #define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
422 #define LocalTime(t) ((t) + AdjustTime(t))
427 return t
- AdjustTime(t
- LocalTZA
);
431 HourFromTime(jsdouble t
)
433 intN result
= (intN
) fmod(floor(t
/msPerHour
), HoursPerDay
);
435 result
+= (intN
)HoursPerDay
;
440 MinFromTime(jsdouble t
)
442 intN result
= (intN
) fmod(floor(t
/ msPerMinute
), MinutesPerHour
);
444 result
+= (intN
)MinutesPerHour
;
449 SecFromTime(jsdouble t
)
451 intN result
= (intN
) fmod(floor(t
/ msPerSecond
), SecondsPerMinute
);
453 result
+= (intN
)SecondsPerMinute
;
458 msFromTime(jsdouble t
)
460 intN result
= (intN
) fmod(t
, msPerSecond
);
462 result
+= (intN
)msPerSecond
;
466 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
467 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
468 ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
471 * end of ECMA 'support' functions
475 * Other Support routines and definitions
479 * We use the first reseved slot to store UTC time, and the second for caching
480 * the local time. The initial value of the cache entry is NaN.
482 const uint32 JSSLOT_UTC_TIME
= JSSLOT_PRIVATE
;
483 const uint32 JSSLOT_LOCAL_TIME
= JSSLOT_PRIVATE
+ 1;
485 const uint32 DATE_RESERVED_SLOTS
= 2;
487 JSClass js_DateClass
= {
489 JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS
) |
490 JSCLASS_HAS_CACHED_PROTO(JSProto_Date
),
491 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
492 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, JS_FinalizeStub
,
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
, jsval
*argv
, jsdouble
*rval
)
574 jsdouble array
[MAXARGS
];
578 for (loop
= 0; loop
< MAXARGS
; loop
++) {
580 d
= js_ValueToNumber(cx
, &argv
[loop
]);
581 if (JSVAL_IS_NULL(argv
[loop
]))
583 /* return NaN if any arg is not finite */
584 if (!JSDOUBLE_IS_FINITE(d
)) {
585 *rval
= *cx
->runtime
->jsNaN
;
588 array
[loop
] = js_DoubleToInteger(d
);
591 array
[loop
] = 1; /* Default the date argument to 1. */
598 /* adjust 2-digit years into the 20th century */
599 if (array
[0] >= 0 && array
[0] <= 99)
602 msec_time
= date_msecFromDate(array
[0], array
[1], array
[2],
603 array
[3], array
[4], array
[5], array
[6]);
609 * See ECMA 15.9.4.[3-10];
612 date_UTC(JSContext
*cx
, uintN argc
, jsval
*vp
)
616 if (!date_msecFromArgs(cx
, argc
, vp
+ 2, &msec_time
))
619 msec_time
= TIMECLIP(msec_time
);
621 return js_NewNumberInRootedValue(cx
, msec_time
, vp
);
625 date_parseString(JSString
*str
, jsdouble
*result
)
642 JSBool seenplusminus
= JS_FALSE
;
644 JSBool seenmonthname
= JS_FALSE
;
646 JSSTRING_CHARS_AND_LENGTH(str
, s
, limit
);
652 if (c
<= ' ' || c
== ',' || c
== '-') {
653 if (c
== '-' && '0' <= s
[i
] && s
[i
] <= '9') {
658 if (c
== '(') { /* comments) */
663 if (c
== '(') depth
++;
670 if ('0' <= c
&& c
<= '9') {
672 while (i
< limit
&& '0' <= (c
= s
[i
]) && c
<= '9') {
673 n
= n
* 10 + c
- '0';
677 /* allow TZA before the year, so
678 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
681 /* uses of seenplusminus allow : in TZA, so Java
682 * no-timezone style of GMT+4:30 works
685 if ((prevc
== '+' || prevc
== '-')/* && year>=0 */) {
686 /* make ':' case below change tzoffset */
687 seenplusminus
= JS_TRUE
;
691 n
= n
* 60; /* EG. "GMT-3" */
693 n
= n
% 100 + n
/ 100 * 60; /* eg "GMT-0430" */
694 if (prevc
== '+') /* plus means east of GMT */
696 if (tzoffset
!= 0 && tzoffset
!= -1)
699 } else if (prevc
== '/' && mon
>= 0 && mday
>= 0 && year
< 0) {
700 if (c
<= ' ' || c
== ',' || c
== '/' || i
>= limit
)
704 } else if (c
== ':') {
711 } else if (c
== '/') {
712 /* until it is determined that mon is the actual
713 month, keep it as 1-based rather than 0-based */
720 } else if (i
< limit
&& c
!= ',' && c
> ' ' && c
!= '-' && c
!= '(') {
722 } else if (seenplusminus
&& n
< 60) { /* handle GMT-3:30 */
727 } else if (hour
>= 0 && min
< 0) {
729 } else if (prevc
== ':' && min
>= 0 && sec
< 0) {
731 } else if (mon
< 0) {
733 } else if (mon
>= 0 && mday
< 0) {
735 } else if (mon
>= 0 && mday
>= 0 && year
< 0) {
741 } else if (c
== '/' || c
== ':' || c
== '+' || c
== '-') {
748 if (!(('A' <= c
&& c
<= 'Z') || ('a' <= c
&& c
<= 'z')))
754 for (k
= JS_ARRAY_LENGTH(wtb
); --k
>= 0;)
755 if (date_regionMatches(wtb
[k
], 0, s
, st
, i
-st
, 1)) {
760 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
761 * 12:30, instead of blindly adding 12 if PM.
763 JS_ASSERT(action
== -1 || action
== -2);
764 if (hour
> 12 || hour
< 0) {
767 if (action
== -1 && hour
== 12) { /* am */
769 } else if (action
== -2 && hour
!= 12) { /* pm */
773 } else if (action
<= 13) { /* month! */
774 /* Adjust mon to be 1-based until the final values
775 for mon, mday and year are adjusted below */
779 seenmonthname
= JS_TRUE
;
780 temp
= /*byte*/ (action
- 2) + 1;
784 } else if (mday
< 0) {
787 } else if (year
< 0) {
794 tzoffset
= action
- 10000;
804 if (year
< 0 || mon
< 0 || mday
< 0)
807 Case 1. The input string contains an English month name.
808 The form of the string can be month f l, or f month l, or
809 f l month which each evaluate to the same date.
810 If f and l are both greater than or equal to 70, or
811 both less than 70, the date is invalid.
812 The year is taken to be the greater of the values f, l.
813 If the year is greater than or equal to 70 and less than 100,
814 it is considered to be the number of years after 1900.
815 Case 2. The input string is of the form "f/m/l" where f, m and l are
816 integers, e.g. 7/16/45.
817 Adjust the mon, mday and year values to achieve 100% MSIE
819 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
820 i. If year < 100, it is the number of years after 1900
821 ii. If year >= 100, it is the number of years after 0.
823 i. If m < 70, f/m/l is interpreted as
824 year/month/day where year is the number of years after
826 ii. If m >= 70, the date is invalid.
828 i. If m < 70, f/m/l is interpreted as
829 year/month/day where year is the number of years after 0.
830 ii. If m >= 70, the date is invalid.
833 if ((mday
>= 70 && year
>= 70) || (mday
< 70 && year
< 70)) {
841 if (year
>= 70 && year
< 100) {
844 } else if (mon
< 70) { /* (a) month/day/year */
848 } else if (mon
< 100) { /* (b) year/month/day */
857 } else { /* (c) year/month/day */
867 mon
-= 1; /* convert month to 0-based */
874 if (tzoffset
== -1) { /* no time zone specified, have to use local */
876 msec_time
= date_msecFromDate(year
, mon
, mday
, hour
, min
, sec
, 0);
878 *result
= UTC(msec_time
);
882 msec
= date_msecFromDate(year
, mon
, mday
, hour
, min
, sec
, 0);
883 msec
+= tzoffset
* msPerMinute
;
894 date_parse(JSContext
*cx
, uintN argc
, jsval
*vp
)
900 *vp
= DOUBLE_TO_JSVAL(cx
->runtime
->jsNaN
);
903 str
= js_ValueToString(cx
, vp
[2]);
906 vp
[2] = STRING_TO_JSVAL(str
);
907 if (!date_parseString(str
, &result
)) {
908 *vp
= DOUBLE_TO_JSVAL(cx
->runtime
->jsNaN
);
912 result
= TIMECLIP(result
);
913 return js_NewNumberInRootedValue(cx
, result
, vp
);
917 date_now(JSContext
*cx
, uintN argc
, jsval
*vp
)
919 return js_NewDoubleInRootedValue(cx
, PRMJ_Now() / PRMJ_USEC_PER_MSEC
, vp
);
923 static jsdouble FASTCALL
924 date_now_tn(JSContext
*)
926 return PRMJ_Now() / PRMJ_USEC_PER_MSEC
;
931 * Get UTC time from the date object. Returns false if the object is not
935 GetUTCTime(JSContext
*cx
, JSObject
*obj
, jsval
*vp
, jsdouble
*dp
)
937 if (!JS_InstanceOf(cx
, obj
, &js_DateClass
, vp
? vp
+ 2 : NULL
))
939 *dp
= *JSVAL_TO_DOUBLE(obj
->fslots
[JSSLOT_UTC_TIME
]);
944 * Set UTC time slot with a pointer pointing to a jsdouble. This function is
945 * used only for setting UTC time to some predefined values, such as NaN.
947 * It also invalidates cached local time.
950 SetUTCTimePtr(JSContext
*cx
, JSObject
*obj
, jsval
*vp
, jsdouble
*dp
)
952 if (vp
&& !JS_InstanceOf(cx
, obj
, &js_DateClass
, vp
+ 2))
954 JS_ASSERT_IF(!vp
, STOBJ_GET_CLASS(obj
) == &js_DateClass
);
956 /* Invalidate local time cache. */
957 obj
->fslots
[JSSLOT_LOCAL_TIME
] = DOUBLE_TO_JSVAL(cx
->runtime
->jsNaN
);
958 obj
->fslots
[JSSLOT_UTC_TIME
] = DOUBLE_TO_JSVAL(dp
);
963 * Set UTC time to a given time.
966 SetUTCTime(JSContext
*cx
, JSObject
*obj
, jsval
*vp
, jsdouble t
)
968 jsdouble
*dp
= js_NewWeaklyRootedDouble(cx
, t
);
971 return SetUTCTimePtr(cx
, obj
, vp
, dp
);
975 * Get the local time, cache it if necessary. If UTC time is not finite
976 * (e.g., NaN), the local time slot is set to the UTC time without conversion.
979 GetAndCacheLocalTime(JSContext
*cx
, JSObject
*obj
, jsval
*vp
, jsdouble
*dp
)
985 if (!obj
|| !JS_InstanceOf(cx
, obj
, &js_DateClass
, vp
? vp
+ 2 : NULL
))
987 v
= obj
->fslots
[JSSLOT_LOCAL_TIME
];
989 result
= *JSVAL_TO_DOUBLE(v
);
991 if (JSDOUBLE_IS_NaN(result
)) {
992 if (!GetUTCTime(cx
, obj
, vp
, &result
))
995 /* if result is NaN, it couldn't be finite. */
996 if (JSDOUBLE_IS_FINITE(result
))
997 result
= LocalTime(result
);
999 cached
= js_NewWeaklyRootedDouble(cx
, result
);
1003 obj
->fslots
[JSSLOT_LOCAL_TIME
] = DOUBLE_TO_JSVAL(cached
);
1011 * See ECMA 15.9.5.4 thru 15.9.5.23
1014 date_getTime(JSContext
*cx
, uintN argc
, jsval
*vp
)
1018 return GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
) &&
1019 js_NewNumberInRootedValue(cx
, result
, vp
);
1023 GetYear(JSContext
*cx
, JSBool fullyear
, jsval
*vp
)
1027 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1030 if (JSDOUBLE_IS_FINITE(result
)) {
1031 result
= YearFromTime(result
);
1033 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1038 return js_NewNumberInRootedValue(cx
, result
, vp
);
1042 date_getYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1044 return GetYear(cx
, JS_FALSE
, vp
);
1048 date_getFullYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1050 return GetYear(cx
, JS_TRUE
, vp
);
1054 date_getUTCFullYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1058 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1061 if (JSDOUBLE_IS_FINITE(result
))
1062 result
= YearFromTime(result
);
1064 return js_NewNumberInRootedValue(cx
, result
, vp
);
1068 date_getMonth(JSContext
*cx
, uintN argc
, jsval
*vp
)
1072 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1075 if (JSDOUBLE_IS_FINITE(result
))
1076 result
= MonthFromTime(result
);
1078 return js_NewNumberInRootedValue(cx
, result
, vp
);
1082 date_getUTCMonth(JSContext
*cx
, uintN argc
, jsval
*vp
)
1086 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1089 if (JSDOUBLE_IS_FINITE(result
))
1090 result
= MonthFromTime(result
);
1092 return js_NewNumberInRootedValue(cx
, result
, vp
);
1096 date_getDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
1100 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1103 if (JSDOUBLE_IS_FINITE(result
))
1104 result
= DateFromTime(result
);
1106 return js_NewNumberInRootedValue(cx
, result
, vp
);
1110 date_getUTCDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
1114 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1117 if (JSDOUBLE_IS_FINITE(result
))
1118 result
= DateFromTime(result
);
1120 return js_NewNumberInRootedValue(cx
, result
, vp
);
1124 date_getDay(JSContext
*cx
, uintN argc
, jsval
*vp
)
1128 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1131 if (JSDOUBLE_IS_FINITE(result
))
1132 result
= WeekDay(result
);
1134 return js_NewNumberInRootedValue(cx
, result
, vp
);
1138 date_getUTCDay(JSContext
*cx
, uintN argc
, jsval
*vp
)
1142 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1145 if (JSDOUBLE_IS_FINITE(result
))
1146 result
= WeekDay(result
);
1148 return js_NewNumberInRootedValue(cx
, result
, vp
);
1152 date_getHours(JSContext
*cx
, uintN argc
, jsval
*vp
)
1156 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1159 if (JSDOUBLE_IS_FINITE(result
))
1160 result
= HourFromTime(result
);
1162 return js_NewNumberInRootedValue(cx
, result
, vp
);
1166 date_getUTCHours(JSContext
*cx
, uintN argc
, jsval
*vp
)
1170 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1173 if (JSDOUBLE_IS_FINITE(result
))
1174 result
= HourFromTime(result
);
1176 return js_NewNumberInRootedValue(cx
, result
, vp
);
1180 date_getMinutes(JSContext
*cx
, uintN argc
, jsval
*vp
)
1184 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1187 if (JSDOUBLE_IS_FINITE(result
))
1188 result
= MinFromTime(result
);
1190 return js_NewNumberInRootedValue(cx
, result
, vp
);
1194 date_getUTCMinutes(JSContext
*cx
, uintN argc
, jsval
*vp
)
1198 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1201 if (JSDOUBLE_IS_FINITE(result
))
1202 result
= MinFromTime(result
);
1204 return js_NewNumberInRootedValue(cx
, result
, vp
);
1207 /* Date.getSeconds is mapped to getUTCSeconds */
1210 date_getUTCSeconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1214 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1217 if (JSDOUBLE_IS_FINITE(result
))
1218 result
= SecFromTime(result
);
1220 return js_NewNumberInRootedValue(cx
, result
, vp
);
1223 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1226 date_getUTCMilliseconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1230 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1233 if (JSDOUBLE_IS_FINITE(result
))
1234 result
= msFromTime(result
);
1236 return js_NewNumberInRootedValue(cx
, result
, vp
);
1240 date_getTimezoneOffset(JSContext
*cx
, uintN argc
, jsval
*vp
)
1243 jsdouble utctime
, localtime
, result
;
1245 obj
= JS_THIS_OBJECT(cx
, vp
);
1246 if (!GetUTCTime(cx
, obj
, vp
, &utctime
))
1248 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
))
1252 * Return the time zone offset in minutes for the current locale that is
1253 * appropriate for this time. This value would be a constant except for
1254 * daylight savings time.
1256 result
= (utctime
- localtime
) / msPerMinute
;
1257 return js_NewNumberInRootedValue(cx
, result
, vp
);
1261 SetDateToNaN(JSContext
*cx
, jsval
*vp
)
1265 obj
= JS_THIS_OBJECT(cx
, vp
);
1266 if (!SetUTCTimePtr(cx
, obj
, NULL
, cx
->runtime
->jsNaN
))
1268 *vp
= DOUBLE_TO_JSVAL(cx
->runtime
->jsNaN
);
1273 date_setTime(JSContext
*cx
, uintN argc
, jsval
*vp
)
1278 return SetDateToNaN(cx
, vp
);
1279 result
= js_ValueToNumber(cx
, &vp
[2]);
1280 if (JSVAL_IS_NULL(vp
[2]))
1283 result
= TIMECLIP(result
);
1285 if (!SetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, result
))
1288 return js_NewNumberInRootedValue(cx
, result
, vp
);
1292 date_makeTime(JSContext
*cx
, uintN maxargs
, JSBool local
, uintN argc
, jsval
*vp
)
1297 jsdouble args
[4], *argp
, *stop
;
1298 jsdouble hour
, min
, sec
, msec
;
1299 jsdouble lorutime
; /* Local or UTC version of *date */
1304 obj
= JS_THIS_OBJECT(cx
, vp
);
1305 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1308 /* just return NaN if the date is already NaN */
1309 if (!JSDOUBLE_IS_FINITE(result
))
1310 return js_NewNumberInRootedValue(cx
, result
, vp
);
1313 * Satisfy the ECMA rule that if a function is called with
1314 * fewer arguments than the specified formal arguments, the
1315 * remaining arguments are set to undefined. Seems like all
1316 * the Date.setWhatever functions in ECMA are only varargs
1317 * beyond the first argument; this should be set to undefined
1318 * if it's not given. This means that "d = new Date();
1319 * d.setMilliseconds()" returns NaN. Blech.
1322 return SetDateToNaN(cx
, vp
);
1324 argc
= maxargs
; /* clamp argc */
1325 JS_ASSERT(argc
<= 4);
1328 for (i
= 0; i
< argc
; i
++) {
1329 args
[i
] = js_ValueToNumber(cx
, &argv
[i
]);
1330 if (JSVAL_IS_NULL(argv
[i
]))
1332 if (!JSDOUBLE_IS_FINITE(args
[i
]))
1333 return SetDateToNaN(cx
, vp
);
1334 args
[i
] = js_DoubleToInteger(args
[i
]);
1338 lorutime
= LocalTime(result
);
1344 if (maxargs
>= 4 && argp
< stop
)
1347 hour
= HourFromTime(lorutime
);
1349 if (maxargs
>= 3 && argp
< stop
)
1352 min
= MinFromTime(lorutime
);
1354 if (maxargs
>= 2 && argp
< stop
)
1357 sec
= SecFromTime(lorutime
);
1359 if (maxargs
>= 1 && argp
< stop
)
1362 msec
= msFromTime(lorutime
);
1364 msec_time
= MakeTime(hour
, min
, sec
, msec
);
1365 result
= MakeDate(Day(lorutime
), msec_time
);
1367 /* fprintf(stderr, "%f\n", result); */
1370 result
= UTC(result
);
1372 /* fprintf(stderr, "%f\n", result); */
1374 result
= TIMECLIP(result
);
1375 if (!SetUTCTime(cx
, obj
, NULL
, result
))
1378 return js_NewNumberInRootedValue(cx
, result
, vp
);
1382 date_setMilliseconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1384 return date_makeTime(cx
, 1, JS_TRUE
, argc
, vp
);
1388 date_setUTCMilliseconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1390 return date_makeTime(cx
, 1, JS_FALSE
, argc
, vp
);
1394 date_setSeconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1396 return date_makeTime(cx
, 2, JS_TRUE
, argc
, vp
);
1400 date_setUTCSeconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1402 return date_makeTime(cx
, 2, JS_FALSE
, argc
, vp
);
1406 date_setMinutes(JSContext
*cx
, uintN argc
, jsval
*vp
)
1408 return date_makeTime(cx
, 3, JS_TRUE
, argc
, vp
);
1412 date_setUTCMinutes(JSContext
*cx
, uintN argc
, jsval
*vp
)
1414 return date_makeTime(cx
, 3, JS_FALSE
, argc
, vp
);
1418 date_setHours(JSContext
*cx
, uintN argc
, jsval
*vp
)
1420 return date_makeTime(cx
, 4, JS_TRUE
, argc
, vp
);
1424 date_setUTCHours(JSContext
*cx
, uintN argc
, jsval
*vp
)
1426 return date_makeTime(cx
, 4, JS_FALSE
, argc
, vp
);
1430 date_makeDate(JSContext
*cx
, uintN maxargs
, JSBool local
, uintN argc
, jsval
*vp
)
1435 jsdouble lorutime
; /* local or UTC version of *date */
1436 jsdouble args
[3], *argp
, *stop
;
1437 jsdouble year
, month
, day
;
1440 obj
= JS_THIS_OBJECT(cx
, vp
);
1441 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1444 /* see complaint about ECMA in date_MakeTime */
1446 return SetDateToNaN(cx
, vp
);
1448 argc
= maxargs
; /* clamp argc */
1449 JS_ASSERT(1 <= argc
&& argc
<= 3);
1452 for (i
= 0; i
< argc
; i
++) {
1453 args
[i
] = js_ValueToNumber(cx
, &argv
[i
]);
1454 if (JSVAL_IS_NULL(argv
[i
]))
1456 if (!JSDOUBLE_IS_FINITE(args
[i
]))
1457 return SetDateToNaN(cx
, vp
);
1458 args
[i
] = js_DoubleToInteger(args
[i
]);
1461 /* return NaN if date is NaN and we're not setting the year,
1462 * If we are, use 0 as the time. */
1463 if (!(JSDOUBLE_IS_FINITE(result
))) {
1465 return js_NewNumberInRootedValue(cx
, result
, vp
);
1468 lorutime
= local
? LocalTime(result
) : result
;
1473 if (maxargs
>= 3 && argp
< stop
)
1476 year
= YearFromTime(lorutime
);
1478 if (maxargs
>= 2 && argp
< stop
)
1481 month
= MonthFromTime(lorutime
);
1483 if (maxargs
>= 1 && argp
< stop
)
1486 day
= DateFromTime(lorutime
);
1488 day
= MakeDay(year
, month
, day
); /* day within year */
1489 result
= MakeDate(day
, TimeWithinDay(lorutime
));
1492 result
= UTC(result
);
1494 result
= TIMECLIP(result
);
1495 if (!SetUTCTime(cx
, obj
, NULL
, result
))
1498 return js_NewNumberInRootedValue(cx
, result
, vp
);
1502 date_setDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
1504 return date_makeDate(cx
, 1, JS_TRUE
, argc
, vp
);
1508 date_setUTCDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
1510 return date_makeDate(cx
, 1, JS_FALSE
, argc
, vp
);
1514 date_setMonth(JSContext
*cx
, uintN argc
, jsval
*vp
)
1516 return date_makeDate(cx
, 2, JS_TRUE
, argc
, vp
);
1520 date_setUTCMonth(JSContext
*cx
, uintN argc
, jsval
*vp
)
1522 return date_makeDate(cx
, 2, JS_FALSE
, argc
, vp
);
1526 date_setFullYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1528 return date_makeDate(cx
, 3, JS_TRUE
, argc
, vp
);
1532 date_setUTCFullYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1534 return date_makeDate(cx
, 3, JS_FALSE
, argc
, vp
);
1538 date_setYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1546 obj
= JS_THIS_OBJECT(cx
, vp
);
1547 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1551 return SetDateToNaN(cx
, vp
);
1552 year
= js_ValueToNumber(cx
, &vp
[2]);
1553 if (JSVAL_IS_NULL(vp
[2]))
1555 if (!JSDOUBLE_IS_FINITE(year
))
1556 return SetDateToNaN(cx
, vp
);
1558 year
= js_DoubleToInteger(year
);
1560 if (!JSDOUBLE_IS_FINITE(result
)) {
1563 t
= LocalTime(result
);
1566 if (year
>= 0 && year
<= 99)
1569 day
= MakeDay(year
, MonthFromTime(t
), DateFromTime(t
));
1570 result
= MakeDate(day
, TimeWithinDay(t
));
1571 result
= UTC(result
);
1573 result
= TIMECLIP(result
);
1574 if (!SetUTCTime(cx
, obj
, NULL
, result
))
1577 return js_NewNumberInRootedValue(cx
, result
, vp
);
1580 /* constants for toString, toUTCString */
1581 static char js_NaN_date_str
[] = "Invalid Date";
1582 static const char* days
[] =
1584 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1586 static const char* months
[] =
1588 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1592 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1593 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1595 print_gmt_string(char* buf
, size_t size
, jsdouble utctime
)
1597 JS_snprintf(buf
, size
, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1598 days
[WeekDay(utctime
)],
1599 DateFromTime(utctime
),
1600 months
[MonthFromTime(utctime
)],
1601 YearFromTime(utctime
),
1602 HourFromTime(utctime
),
1603 MinFromTime(utctime
),
1604 SecFromTime(utctime
));
1608 print_iso_string(char* buf
, size_t size
, jsdouble utctime
)
1610 JS_snprintf(buf
, size
, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
1611 YearFromTime(utctime
),
1612 MonthFromTime(utctime
) + 1,
1613 DateFromTime(utctime
),
1614 HourFromTime(utctime
),
1615 MinFromTime(utctime
),
1616 SecFromTime(utctime
),
1617 msFromTime(utctime
));
1621 date_utc_format(JSContext
*cx
, jsval
*vp
,
1622 void (*printFunc
)(char*, size_t, jsdouble
))
1628 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
1631 if (!JSDOUBLE_IS_FINITE(utctime
)) {
1632 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
1634 (*printFunc
)(buf
, sizeof buf
, utctime
);
1636 str
= JS_NewStringCopyZ(cx
, buf
);
1639 *vp
= STRING_TO_JSVAL(str
);
1644 date_toGMTString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1646 return date_utc_format(cx
, vp
, print_gmt_string
);
1650 date_toISOString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1652 return date_utc_format(cx
, vp
, print_iso_string
);
1655 /* for Date.toLocaleString; interface to PRMJTime date struct.
1658 new_explode(jsdouble timeval
, PRMJTime
*split
)
1660 jsint year
= YearFromTime(timeval
);
1662 split
->tm_usec
= (int32
) msFromTime(timeval
) * 1000;
1663 split
->tm_sec
= (int8
) SecFromTime(timeval
);
1664 split
->tm_min
= (int8
) MinFromTime(timeval
);
1665 split
->tm_hour
= (int8
) HourFromTime(timeval
);
1666 split
->tm_mday
= (int8
) DateFromTime(timeval
);
1667 split
->tm_mon
= (int8
) MonthFromTime(timeval
);
1668 split
->tm_wday
= (int8
) WeekDay(timeval
);
1669 split
->tm_year
= year
;
1670 split
->tm_yday
= (int16
) DayWithinYear(timeval
, year
);
1672 /* not sure how this affects things, but it doesn't seem
1674 split
->tm_isdst
= (DaylightSavingTA(timeval
) != 0);
1677 typedef enum formatspec
{
1678 FORMATSPEC_FULL
, FORMATSPEC_DATE
, FORMATSPEC_TIME
1681 /* helper function */
1683 date_format(JSContext
*cx
, jsdouble date
, formatspec format
, jsval
*rval
)
1692 if (!JSDOUBLE_IS_FINITE(date
)) {
1693 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
1695 jsdouble local
= LocalTime(date
);
1697 /* offset from GMT in minutes. The offset includes daylight savings,
1699 jsint minutes
= (jsint
) floor(AdjustTime(date
) / msPerMinute
);
1701 /* map 510 minutes to 0830 hours */
1702 intN offset
= (minutes
/ 60) * 100 + minutes
% 60;
1704 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1705 * printed as 'GMT-0800' rather than as 'PST' to avoid
1706 * operating-system dependence on strftime (which
1707 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1708 * PST as 'Pacific Standard Time.' This way we always know
1709 * what we're getting, and can parse it if we produce it.
1710 * The OS TZA string is included as a comment.
1713 /* get a timezone string from the OS to include as a
1715 new_explode(date
, &split
);
1716 if (PRMJ_FormatTime(tzbuf
, sizeof tzbuf
, "(%Z)", &split
) != 0) {
1718 /* Decide whether to use the resulting timezone string.
1720 * Reject it if it contains any non-ASCII, non-alphanumeric
1721 * characters. It's then likely in some other character
1722 * encoding, and we probably won't display it correctly.
1725 tzlen
= strlen(tzbuf
);
1729 for (i
= 0; i
< tzlen
; i
++) {
1730 jschar c
= tzbuf
[i
];
1732 !(isalpha(c
) || isdigit(c
) ||
1733 c
== ' ' || c
== '(' || c
== ')')) {
1739 /* Also reject it if it's not parenthesized or if it's '()'. */
1740 if (tzbuf
[0] != '(' || tzbuf
[1] == ')')
1746 case FORMATSPEC_FULL
:
1748 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1749 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1751 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1752 JS_snprintf(buf
, sizeof buf
,
1753 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1754 days
[WeekDay(local
)],
1755 months
[MonthFromTime(local
)],
1756 DateFromTime(local
),
1757 YearFromTime(local
),
1758 HourFromTime(local
),
1763 usetz
? tzbuf
: "");
1765 case FORMATSPEC_DATE
:
1766 /* Tue Oct 31 2000 */
1767 JS_snprintf(buf
, sizeof buf
,
1769 days
[WeekDay(local
)],
1770 months
[MonthFromTime(local
)],
1771 DateFromTime(local
),
1772 YearFromTime(local
));
1774 case FORMATSPEC_TIME
:
1775 /* 09:41:40 GMT-0800 (PST) */
1776 JS_snprintf(buf
, sizeof buf
,
1777 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
1778 HourFromTime(local
),
1783 usetz
? tzbuf
: "");
1788 str
= JS_NewStringCopyZ(cx
, buf
);
1791 *rval
= STRING_TO_JSVAL(str
);
1796 date_toLocaleHelper(JSContext
*cx
, const char *format
, jsval
*vp
)
1804 obj
= JS_THIS_OBJECT(cx
, vp
);
1805 if (!GetUTCTime(cx
, obj
, vp
, &utctime
))
1808 if (!JSDOUBLE_IS_FINITE(utctime
)) {
1809 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
1812 jsdouble local
= LocalTime(utctime
);
1813 new_explode(local
, &split
);
1815 /* let PRMJTime format it. */
1816 result_len
= PRMJ_FormatTime(buf
, sizeof buf
, format
, &split
);
1818 /* If it failed, default to toString. */
1819 if (result_len
== 0)
1820 return date_format(cx
, utctime
, FORMATSPEC_FULL
, vp
);
1822 /* Hacked check against undesired 2-digit year 00/00/00 form. */
1823 if (strcmp(format
, "%x") == 0 && result_len
>= 6 &&
1824 /* Format %x means use OS settings, which may have 2-digit yr, so
1825 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
1826 !isdigit(buf
[result_len
- 3]) &&
1827 isdigit(buf
[result_len
- 2]) && isdigit(buf
[result_len
- 1]) &&
1828 /* ...but not if starts with 4-digit year, like 2022/3/11. */
1829 !(isdigit(buf
[0]) && isdigit(buf
[1]) &&
1830 isdigit(buf
[2]) && isdigit(buf
[3]))) {
1831 JS_snprintf(buf
+ (result_len
- 2), (sizeof buf
) - (result_len
- 2),
1832 "%d", js_DateGetYear(cx
, obj
));
1837 if (cx
->localeCallbacks
&& cx
->localeCallbacks
->localeToUnicode
)
1838 return cx
->localeCallbacks
->localeToUnicode(cx
, buf
, vp
);
1840 str
= JS_NewStringCopyZ(cx
, buf
);
1843 *vp
= STRING_TO_JSVAL(str
);
1848 date_toLocaleString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1850 /* Use '%#c' for windows, because '%c' is
1851 * backward-compatible and non-y2k with msvc; '%#c' requests that a
1852 * full year be used in the result string.
1854 return date_toLocaleHelper(cx
,
1855 #if defined(_WIN32) && !defined(__MWERKS__)
1864 date_toLocaleDateString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1866 /* Use '%#x' for windows, because '%x' is
1867 * backward-compatible and non-y2k with msvc; '%#x' requests that a
1868 * full year be used in the result string.
1870 return date_toLocaleHelper(cx
,
1871 #if defined(_WIN32) && !defined(__MWERKS__)
1880 date_toLocaleTimeString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1882 return date_toLocaleHelper(cx
, "%X", vp
);
1886 date_toLocaleFormat(JSContext
*cx
, uintN argc
, jsval
*vp
)
1889 const char *fmtbytes
;
1892 return date_toLocaleString(cx
, argc
, vp
);
1894 fmt
= js_ValueToString(cx
, vp
[2]);
1897 vp
[2] = STRING_TO_JSVAL(fmt
);
1898 fmtbytes
= js_GetStringBytes(cx
, fmt
);
1902 return date_toLocaleHelper(cx
, fmtbytes
, vp
);
1906 date_toTimeString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1910 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
1912 return date_format(cx
, utctime
, FORMATSPEC_TIME
, vp
);
1916 date_toDateString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1920 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
1922 return date_format(cx
, utctime
, FORMATSPEC_DATE
, vp
);
1930 date_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
1933 char buf
[DTOSTR_STANDARD_BUFFER_SIZE
], *numStr
, *bytes
;
1936 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
1939 numStr
= JS_dtostr(buf
, sizeof buf
, DTOSTR_STANDARD
, 0, utctime
);
1941 JS_ReportOutOfMemory(cx
);
1945 bytes
= JS_smprintf("(new %s(%s))", js_Date_str
, numStr
);
1947 JS_ReportOutOfMemory(cx
);
1951 str
= JS_NewString(cx
, bytes
, strlen(bytes
));
1956 *vp
= STRING_TO_JSVAL(str
);
1962 date_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1966 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
1968 return date_format(cx
, utctime
, FORMATSPEC_FULL
, vp
);
1972 static jsval FASTCALL
1973 date_valueOf_tn(JSContext
* cx
, JSObject
* obj
, JSString
* str
)
1975 JS_ASSERT(JS_InstanceOf(cx
, obj
, &js_DateClass
, NULL
));
1976 jsdouble t
= *JSVAL_TO_DOUBLE(obj
->fslots
[JSSLOT_UTC_TIME
]);
1978 JSString
* number_str
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_NUMBER
]);
1980 if (js_EqualStrings(str
, number_str
)) {
1981 if (!js_NewNumberInRootedValue(cx
, t
, &v
))
1982 return JSVAL_ERROR_COOKIE
;
1984 if (!date_format(cx
, t
, FORMATSPEC_FULL
, &v
))
1985 return JSVAL_ERROR_COOKIE
;
1992 date_valueOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
1994 JSString
*str
, *number_str
;
1996 /* It is an error to call date_valueOf on a non-date object, but we don't
1997 * need to check for that explicitly here because every path calls
1998 * GetUTCTime, which does the check.
2001 /* If called directly with no arguments, convert to a time number. */
2003 return date_getTime(cx
, argc
, vp
);
2005 /* Convert to number only if the hint was given, otherwise favor string. */
2006 str
= js_ValueToString(cx
, vp
[2]);
2009 number_str
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_NUMBER
]);
2010 if (js_EqualStrings(str
, number_str
))
2011 return date_getTime(cx
, argc
, vp
);
2012 return date_toString(cx
, argc
, vp
);
2015 JS_DEFINE_CALLINFO_2(extern, OBJECT
, js_FastNewDate
, CONTEXT
, OBJECT
, 0, 0)
2017 // Don't really need an argument here, but we don't support arg-less builtins
2018 JS_DEFINE_TRCINFO_1(date_now
,
2019 (1, (static, DOUBLE
, date_now_tn
, CONTEXT
, 0, 0)))
2021 static JSFunctionSpec date_static_methods
[] = {
2022 JS_FN("UTC", date_UTC
, MAXARGS
,0),
2023 JS_FN("parse", date_parse
, 1,0),
2024 JS_TN("now", date_now
, 0,0, date_now_trcinfo
),
2028 JS_DEFINE_TRCINFO_1(date_valueOf
,
2029 (3, (static, JSVAL_FAIL
, date_valueOf_tn
, CONTEXT
, THIS
, STRING
, 0, 0)))
2031 static JSFunctionSpec date_methods
[] = {
2032 JS_FN("getTime", date_getTime
, 0,0),
2033 JS_FN("getTimezoneOffset", date_getTimezoneOffset
, 0,0),
2034 JS_FN("getYear", date_getYear
, 0,0),
2035 JS_FN("getFullYear", date_getFullYear
, 0,0),
2036 JS_FN("getUTCFullYear", date_getUTCFullYear
, 0,0),
2037 JS_FN("getMonth", date_getMonth
, 0,0),
2038 JS_FN("getUTCMonth", date_getUTCMonth
, 0,0),
2039 JS_FN("getDate", date_getDate
, 0,0),
2040 JS_FN("getUTCDate", date_getUTCDate
, 0,0),
2041 JS_FN("getDay", date_getDay
, 0,0),
2042 JS_FN("getUTCDay", date_getUTCDay
, 0,0),
2043 JS_FN("getHours", date_getHours
, 0,0),
2044 JS_FN("getUTCHours", date_getUTCHours
, 0,0),
2045 JS_FN("getMinutes", date_getMinutes
, 0,0),
2046 JS_FN("getUTCMinutes", date_getUTCMinutes
, 0,0),
2047 JS_FN("getSeconds", date_getUTCSeconds
, 0,0),
2048 JS_FN("getUTCSeconds", date_getUTCSeconds
, 0,0),
2049 JS_FN("getMilliseconds", date_getUTCMilliseconds
, 0,0),
2050 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds
, 0,0),
2051 JS_FN("setTime", date_setTime
, 1,0),
2052 JS_FN("setYear", date_setYear
, 1,0),
2053 JS_FN("setFullYear", date_setFullYear
, 3,0),
2054 JS_FN("setUTCFullYear", date_setUTCFullYear
, 3,0),
2055 JS_FN("setMonth", date_setMonth
, 2,0),
2056 JS_FN("setUTCMonth", date_setUTCMonth
, 2,0),
2057 JS_FN("setDate", date_setDate
, 1,0),
2058 JS_FN("setUTCDate", date_setUTCDate
, 1,0),
2059 JS_FN("setHours", date_setHours
, 4,0),
2060 JS_FN("setUTCHours", date_setUTCHours
, 4,0),
2061 JS_FN("setMinutes", date_setMinutes
, 3,0),
2062 JS_FN("setUTCMinutes", date_setUTCMinutes
, 3,0),
2063 JS_FN("setSeconds", date_setSeconds
, 2,0),
2064 JS_FN("setUTCSeconds", date_setUTCSeconds
, 2,0),
2065 JS_FN("setMilliseconds", date_setMilliseconds
, 1,0),
2066 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds
, 1,0),
2067 JS_FN("toUTCString", date_toGMTString
, 0,0),
2068 JS_FN(js_toLocaleString_str
, date_toLocaleString
, 0,0),
2069 JS_FN("toLocaleDateString", date_toLocaleDateString
, 0,0),
2070 JS_FN("toLocaleTimeString", date_toLocaleTimeString
, 0,0),
2071 JS_FN("toLocaleFormat", date_toLocaleFormat
, 0,0),
2072 JS_FN("toDateString", date_toDateString
, 0,0),
2073 JS_FN("toTimeString", date_toTimeString
, 0,0),
2074 JS_FN("toISOString", date_toISOString
, 0,0),
2075 JS_FN(js_toJSON_str
, date_toISOString
, 0,0),
2078 JS_FN(js_toSource_str
, date_toSource
, 0,0),
2080 JS_FN(js_toString_str
, date_toString
, 0,0),
2081 JS_TN(js_valueOf_str
, date_valueOf
, 0,0, date_valueOf_trcinfo
),
2086 date_constructor(JSContext
*cx
, JSObject
* obj
)
2090 date
= js_NewWeaklyRootedDouble(cx
, 0.0);
2094 obj
->fslots
[JSSLOT_UTC_TIME
] = DOUBLE_TO_JSVAL(date
);
2095 obj
->fslots
[JSSLOT_LOCAL_TIME
] = DOUBLE_TO_JSVAL(cx
->runtime
->jsNaN
);
2100 js_Date(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2106 /* Date called as function. */
2107 if (!JS_IsConstructing(cx
)) {
2108 return date_format(cx
, PRMJ_Now() / PRMJ_USEC_PER_MSEC
,
2109 FORMATSPEC_FULL
, rval
);
2112 /* Date called as constructor. */
2114 date
= date_constructor(cx
, obj
);
2117 *date
= PRMJ_Now() / PRMJ_USEC_PER_MSEC
;
2118 } else if (argc
== 1) {
2119 if (!JSVAL_IS_STRING(argv
[0])) {
2120 /* the argument is a millisecond number */
2121 d
= js_ValueToNumber(cx
, &argv
[0]);
2122 if (JSVAL_IS_NULL(argv
[0]))
2124 date
= date_constructor(cx
, obj
);
2127 *date
= TIMECLIP(d
);
2129 /* the argument is a string; parse it. */
2130 date
= date_constructor(cx
, obj
);
2134 str
= js_ValueToString(cx
, argv
[0]);
2138 if (!date_parseString(str
, date
))
2139 *date
= *cx
->runtime
->jsNaN
;
2140 *date
= TIMECLIP(*date
);
2146 if (!date_msecFromArgs(cx
, argc
, argv
, &msec_time
))
2149 date
= date_constructor(cx
, obj
);
2153 if (JSDOUBLE_IS_FINITE(msec_time
)) {
2154 msec_time
= UTC(msec_time
);
2155 msec_time
= TIMECLIP(msec_time
);
2163 JS_STATIC_ASSERT(JSSLOT_PRIVATE
== JSSLOT_UTC_TIME
);
2164 JS_STATIC_ASSERT(JSSLOT_UTC_TIME
+ 1 == JSSLOT_LOCAL_TIME
);
2168 js_FastNewDate(JSContext
* cx
, JSObject
* proto
)
2170 JS_ASSERT(JS_ON_TRACE(cx
));
2171 JSObject
* obj
= (JSObject
*) js_NewGCThing(cx
, GCX_OBJECT
, sizeof(JSObject
));
2175 JSClass
* clasp
= &js_DateClass
;
2176 obj
->classword
= jsuword(clasp
);
2178 obj
->fslots
[JSSLOT_PROTO
] = OBJECT_TO_JSVAL(proto
);
2179 obj
->fslots
[JSSLOT_PARENT
] = proto
->fslots
[JSSLOT_PARENT
];
2181 jsdouble
* date
= js_NewWeaklyRootedDouble(cx
, 0.0);
2184 *date
= date_now_tn(cx
);
2185 obj
->fslots
[JSSLOT_UTC_TIME
] = DOUBLE_TO_JSVAL(date
);
2186 obj
->fslots
[JSSLOT_LOCAL_TIME
] = DOUBLE_TO_JSVAL(cx
->runtime
->jsNaN
);
2187 for (unsigned i
= JSSLOT_LOCAL_TIME
+ 1; i
!= JS_INITIAL_NSLOTS
; ++i
)
2188 obj
->fslots
[i
] = JSVAL_VOID
;
2190 JS_ASSERT(!clasp
->getObjectOps
);
2191 JS_ASSERT(proto
->map
->ops
== &js_ObjectOps
);
2192 obj
->map
= js_HoldObjectMap(cx
, proto
->map
);
2199 js_InitDateClass(JSContext
*cx
, JSObject
*obj
)
2202 jsdouble
*proto_date
;
2204 /* set static LocalTZA */
2205 LocalTZA
= -(PRMJ_LocalGMTDifference() * msPerSecond
);
2206 proto
= JS_InitClass(cx
, obj
, NULL
, &js_DateClass
, js_Date
, MAXARGS
,
2207 NULL
, date_methods
, NULL
, date_static_methods
);
2211 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2212 if (!JS_AliasProperty(cx
, proto
, "toUTCString", "toGMTString"))
2215 /* Set the value of the Date.prototype date to NaN */
2216 proto_date
= date_constructor(cx
, proto
);
2219 *proto_date
= *cx
->runtime
->jsNaN
;
2224 JS_FRIEND_API(JSObject
*)
2225 js_NewDateObjectMsec(JSContext
*cx
, jsdouble msec_time
)
2230 obj
= js_NewObject(cx
, &js_DateClass
, NULL
, NULL
, 0);
2234 date
= date_constructor(cx
, obj
);
2242 JS_FRIEND_API(JSObject
*)
2243 js_NewDateObject(JSContext
* cx
, int year
, int mon
, int mday
,
2244 int hour
, int min
, int sec
)
2249 JS_ASSERT(mon
< 12);
2250 msec_time
= date_msecFromDate(year
, mon
, mday
, hour
, min
, sec
, 0);
2251 obj
= js_NewDateObjectMsec(cx
, UTC(msec_time
));
2255 JS_FRIEND_API(JSBool
)
2256 js_DateIsValid(JSContext
*cx
, JSObject
* obj
)
2259 return GetUTCTime(cx
, obj
, NULL
, &utctime
) && !JSDOUBLE_IS_NaN(utctime
);
2263 js_DateGetYear(JSContext
*cx
, JSObject
* obj
)
2267 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2268 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2269 JSDOUBLE_IS_NaN(localtime
)) {
2273 return (int) YearFromTime(localtime
);
2277 js_DateGetMonth(JSContext
*cx
, JSObject
* obj
)
2281 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2282 JSDOUBLE_IS_NaN(localtime
)) {
2286 return (int) MonthFromTime(localtime
);
2290 js_DateGetDate(JSContext
*cx
, JSObject
* obj
)
2294 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2295 JSDOUBLE_IS_NaN(localtime
)) {
2299 return (int) DateFromTime(localtime
);
2303 js_DateGetHours(JSContext
*cx
, JSObject
* obj
)
2307 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2308 JSDOUBLE_IS_NaN(localtime
)) {
2312 return (int) HourFromTime(localtime
);
2316 js_DateGetMinutes(JSContext
*cx
, JSObject
* obj
)
2320 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2321 JSDOUBLE_IS_NaN(localtime
)) {
2325 return (int) MinFromTime(localtime
);
2329 js_DateGetSeconds(JSContext
*cx
, JSObject
* obj
)
2333 if (!GetUTCTime(cx
, obj
, NULL
, &utctime
) || JSDOUBLE_IS_NaN(utctime
))
2336 return (int) SecFromTime(utctime
);
2340 js_DateSetYear(JSContext
*cx
, JSObject
*obj
, int year
)
2344 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2347 /* reset date if it was NaN */
2348 if (JSDOUBLE_IS_NaN(local
))
2351 local
= date_msecFromDate(year
,
2352 MonthFromTime(local
),
2353 DateFromTime(local
),
2354 HourFromTime(local
),
2359 /* SetUTCTime also invalidates local time cache. */
2360 SetUTCTime(cx
, obj
, NULL
, UTC(local
));
2364 js_DateSetMonth(JSContext
*cx
, JSObject
*obj
, int month
)
2368 JS_ASSERT(month
< 12);
2370 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2373 /* bail if date was NaN */
2374 if (JSDOUBLE_IS_NaN(local
))
2377 local
= date_msecFromDate(YearFromTime(local
),
2379 DateFromTime(local
),
2380 HourFromTime(local
),
2384 SetUTCTime(cx
, obj
, NULL
, UTC(local
));
2388 js_DateSetDate(JSContext
*cx
, JSObject
*obj
, int date
)
2392 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2395 if (JSDOUBLE_IS_NaN(local
))
2398 local
= date_msecFromDate(YearFromTime(local
),
2399 MonthFromTime(local
),
2401 HourFromTime(local
),
2405 SetUTCTime(cx
, obj
, NULL
, UTC(local
));
2409 js_DateSetHours(JSContext
*cx
, JSObject
*obj
, int hours
)
2413 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2416 if (JSDOUBLE_IS_NaN(local
))
2418 local
= date_msecFromDate(YearFromTime(local
),
2419 MonthFromTime(local
),
2420 DateFromTime(local
),
2425 SetUTCTime(cx
, obj
, NULL
, UTC(local
));
2429 js_DateSetMinutes(JSContext
*cx
, JSObject
*obj
, int minutes
)
2433 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2436 if (JSDOUBLE_IS_NaN(local
))
2438 local
= date_msecFromDate(YearFromTime(local
),
2439 MonthFromTime(local
),
2440 DateFromTime(local
),
2441 HourFromTime(local
),
2445 SetUTCTime(cx
, obj
, NULL
, UTC(local
));
2449 js_DateSetSeconds(JSContext
*cx
, JSObject
*obj
, int seconds
)
2453 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2456 if (JSDOUBLE_IS_NaN(local
))
2458 local
= date_msecFromDate(YearFromTime(local
),
2459 MonthFromTime(local
),
2460 DateFromTime(local
),
2461 HourFromTime(local
),
2465 SetUTCTime(cx
, obj
, NULL
, UTC(local
));
2468 JS_FRIEND_API(jsdouble
)
2469 js_DateGetMsecSinceEpoch(JSContext
*cx
, JSObject
*obj
)
2472 if (!GetUTCTime(cx
, obj
, NULL
, &utctime
))