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][13] = {
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, 365.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, 366.0}
226 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]
229 DaysInMonth(jsint year
, jsint month
)
231 JSBool leap
= (DaysInYear(year
) == 366);
232 intN result
= intN(DayFromMonth(month
, leap
) - DayFromMonth(month
-1, leap
));
237 MonthFromTime(jsdouble t
)
240 jsint year
= YearFromTime(t
);
241 d
= DayWithinYear(t
, year
);
245 step
+= (InLeapYear(t
) ? 29 : 28);
248 if (d
< (step
+= 31))
250 if (d
< (step
+= 30))
252 if (d
< (step
+= 31))
254 if (d
< (step
+= 30))
256 if (d
< (step
+= 31))
258 if (d
< (step
+= 31))
260 if (d
< (step
+= 30))
262 if (d
< (step
+= 31))
264 if (d
< (step
+= 30))
270 DateFromTime(jsdouble t
)
273 jsint year
= YearFromTime(t
);
274 d
= DayWithinYear(t
, year
);
276 if (d
<= (next
= 30))
279 next
+= (InLeapYear(t
) ? 29 : 28);
283 if (d
<= (next
+= 31))
286 if (d
<= (next
+= 30))
289 if (d
<= (next
+= 31))
292 if (d
<= (next
+= 30))
295 if (d
<= (next
+= 31))
298 if (d
<= (next
+= 31))
301 if (d
<= (next
+= 30))
304 if (d
<= (next
+= 31))
307 if (d
<= (next
+= 30))
317 result
= (jsint
) Day(t
) + 4;
321 return (intN
) result
;
324 #define MakeTime(hour, min, sec, ms) \
325 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
328 MakeDay(jsdouble year
, jsdouble month
, jsdouble date
)
334 year
+= floor(month
/ 12);
336 month
= fmod(month
, 12.0);
340 leap
= (DaysInYear((jsint
) year
) == 366);
342 yearday
= floor(TimeFromYear(year
) / msPerDay
);
343 monthday
= DayFromMonth(month
, leap
);
345 return yearday
+ monthday
+ date
- 1;
348 #define MakeDate(day, time) ((day) * msPerDay + (time))
351 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
353 * yearStartingWith[0][i] is an example non-leap year where
354 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
356 * yearStartingWith[1][i] is an example leap year where
357 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
359 static jsint yearStartingWith
[2][7] = {
360 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
361 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
365 * Find a year for which any given date will fall on the same weekday.
367 * This function should be used with caution when used other than
368 * for determining DST; it hasn't been proven not to produce an
369 * incorrect year for times near year boundaries.
372 EquivalentYearForDST(jsint year
)
377 day
= (jsint
) DayFromYear(year
) + 4;
382 isLeapYear
= (DaysInYear(year
) == 366);
384 return yearStartingWith
[isLeapYear
][day
];
387 /* LocalTZA gets set by js_InitDateClass() */
388 static jsdouble LocalTZA
;
391 DaylightSavingTA(jsdouble t
)
399 if (JSDOUBLE_IS_NaN(t
))
403 * If earlier than 1970 or after 2038, potentially beyond the ken of
404 * many OSes, map it to an equivalent year before asking.
406 if (t
< 0.0 || t
> 2145916800000.0) {
410 year
= EquivalentYearForDST(YearFromTime(t
));
411 day
= MakeDay(year
, MonthFromTime(t
), DateFromTime(t
));
412 t
= MakeDate(day
, TimeWithinDay(t
));
415 /* put our t in an LL, and map it to usec for prtime */
417 JSLL_I2L(ms2us
, PRMJ_USEC_PER_MSEC
);
418 JSLL_MUL(PR_t
, PR_t
, ms2us
);
420 offset
= PRMJ_DSTOffset(PR_t
);
422 JSLL_DIV(offset
, offset
, ms2us
);
423 JSLL_L2D(result
, offset
);
428 AdjustTime(jsdouble date
)
430 jsdouble t
= DaylightSavingTA(date
) + LocalTZA
;
431 t
= (LocalTZA
>= 0) ? fmod(t
, msPerDay
) : -fmod(msPerDay
- t
, msPerDay
);
435 #define LocalTime(t) ((t) + AdjustTime(t))
440 return t
- AdjustTime(t
- LocalTZA
);
444 HourFromTime(jsdouble t
)
446 intN result
= (intN
) fmod(floor(t
/msPerHour
), HoursPerDay
);
448 result
+= (intN
)HoursPerDay
;
453 MinFromTime(jsdouble t
)
455 intN result
= (intN
) fmod(floor(t
/ msPerMinute
), MinutesPerHour
);
457 result
+= (intN
)MinutesPerHour
;
462 SecFromTime(jsdouble t
)
464 intN result
= (intN
) fmod(floor(t
/ msPerSecond
), SecondsPerMinute
);
466 result
+= (intN
)SecondsPerMinute
;
471 msFromTime(jsdouble t
)
473 intN result
= (intN
) fmod(t
, msPerSecond
);
475 result
+= (intN
)msPerSecond
;
479 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
480 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
481 ? js_DoubleToInteger(d + (+0.)) : js_NaN)
484 * end of ECMA 'support' functions
488 * Other Support routines and definitions
492 * We use the first reseved slot to store UTC time, and the second for caching
493 * the local time. The initial value of the cache entry is NaN.
495 const uint32 JSSLOT_UTC_TIME
= JSSLOT_PRIVATE
;
496 const uint32 JSSLOT_LOCAL_TIME
= JSSLOT_PRIVATE
+ 1;
498 const uint32 DATE_RESERVED_SLOTS
= 2;
500 JSClass js_DateClass
= {
502 JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS
) |
503 JSCLASS_HAS_CACHED_PROTO(JSProto_Date
),
504 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
505 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, NULL
,
506 JSCLASS_NO_OPTIONAL_MEMBERS
509 /* for use by date_parse */
511 static const char* wtb
[] = {
513 "monday", "tuesday", "wednesday", "thursday", "friday",
514 "saturday", "sunday",
515 "january", "february", "march", "april", "may", "june",
516 "july", "august", "september", "october", "november", "december",
522 /* time zone table needs to be expanded */
526 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
527 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
528 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
529 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
530 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
531 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
532 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
535 /* helper for date_parse */
537 date_regionMatches(const char* s1
, int s1off
, const jschar
* s2
, int s2off
,
538 int count
, int ignoreCase
)
540 JSBool result
= JS_FALSE
;
541 /* return true if matches, otherwise, false */
543 while (count
> 0 && s1
[s1off
] && s2
[s2off
]) {
545 if (JS_TOLOWER((jschar
)s1
[s1off
]) != JS_TOLOWER(s2
[s2off
])) {
549 if ((jschar
)s1
[s1off
] != s2
[s2off
]) {
565 /* find UTC time from given date... no 1900 correction! */
567 date_msecFromDate(jsdouble year
, jsdouble mon
, jsdouble mday
, jsdouble hour
,
568 jsdouble min
, jsdouble sec
, jsdouble msec
)
574 day
= MakeDay(year
, mon
, mday
);
575 msec_time
= MakeTime(hour
, min
, sec
, msec
);
576 result
= MakeDate(day
, msec_time
);
580 /* compute the time in msec (unclipped) from the given args */
584 date_msecFromArgs(JSContext
*cx
, uintN argc
, jsval
*argv
, jsdouble
*rval
)
587 jsdouble array
[MAXARGS
];
591 for (loop
= 0; loop
< MAXARGS
; loop
++) {
593 d
= js_ValueToNumber(cx
, &argv
[loop
]);
594 if (JSVAL_IS_NULL(argv
[loop
]))
596 /* return NaN if any arg is not finite */
597 if (!JSDOUBLE_IS_FINITE(d
)) {
601 array
[loop
] = js_DoubleToInteger(d
);
604 array
[loop
] = 1; /* Default the date argument to 1. */
611 /* adjust 2-digit years into the 20th century */
612 if (array
[0] >= 0 && array
[0] <= 99)
615 msec_time
= date_msecFromDate(array
[0], array
[1], array
[2],
616 array
[3], array
[4], array
[5], array
[6]);
622 * See ECMA 15.9.4.[3-10];
625 date_UTC(JSContext
*cx
, uintN argc
, jsval
*vp
)
629 if (!date_msecFromArgs(cx
, argc
, vp
+ 2, &msec_time
))
632 msec_time
= TIMECLIP(msec_time
);
634 return js_NewNumberInRootedValue(cx
, msec_time
, vp
);
638 * Read and convert decimal digits from s[*i] into *result
641 * Succeed if any digits are converted. Advance *i only
642 * as digits are consumed.
645 digits(size_t *result
, const jschar
*s
, size_t *i
, size_t limit
)
650 ('0' <= s
[*i
] && s
[*i
] <= '9')) {
652 *result
+= (s
[*i
] - '0');
659 * Read and convert decimal digits to the right of a decimal point,
660 * representing a fractional integer, from s[*i] into *result
663 * Succeed if any digits are converted. Advance *i only
664 * as digits are consumed.
667 fractional(jsdouble
*result
, const jschar
*s
, size_t *i
, size_t limit
)
669 jsdouble factor
= 0.1;
673 ('0' <= s
[*i
] && s
[*i
] <= '9')) {
674 *result
+= (s
[*i
] - '0') * factor
;
682 * Read and convert exactly n decimal digits from s[*i]
683 * to s[min(*i+n,limit)] into *result.
685 * Succeed if exactly n digits are converted. Advance *i only
689 ndigits(size_t n
, size_t *result
, const jschar
*s
, size_t* i
, size_t limit
)
693 if (digits(result
, s
, i
, JS_MIN(limit
, init
+n
)))
694 return ((*i
- init
) == n
);
701 * Parse a string in one of the date-time formats given by the W3C
702 * "NOTE-datetime" specification. These formats make up a restricted
703 * profile of the ISO 8601 format. Quoted here:
705 * The formats are as follows. Exactly the components shown here
706 * must be present, with exactly this punctuation. Note that the "T"
707 * appears literally in the string, to indicate the beginning of the
708 * time element, as specified in ISO 8601.
710 * Any combination of the date formats with the time formats is
711 * allowed, and also either the date or the time can be missing.
713 * The specification is silent on the meaning when fields are
714 * ommitted so the interpretations are a guess, but hopefully a
715 * reasonable one. We default the month to January, the day to the
716 * 1st, and hours minutes and seconds all to 0. If the date is
717 * missing entirely then we assume 1970-01-01 so that the time can
718 * be aded to a date later. If the time is missing then we assume
719 * 00:00 UTC. If the time is present but the time zone field is
720 * missing then we use local time.
728 * YYYY-MM (eg 1997-07)
731 * YYYY-MM-DD (eg 1997-07-16)
736 * Thh:mmTZD (eg T19:20+01:00)
738 * Hours, minutes and seconds:
739 * Thh:mm:ssTZD (eg T19:20:30+01:00)
741 * Hours, minutes, seconds and a decimal fraction of a second:
742 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
746 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
747 * MM = two-digit month (01=January, etc.)
748 * DD = two-digit day of month (01 through 31)
749 * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
750 * mm = two digits of minute (00 through 59)
751 * ss = two digits of second (00 through 59)
752 * s = one or more digits representing a decimal fraction of a second
753 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
757 date_parseISOString(JSString
*str
, jsdouble
*result
)
773 bool isLocalTime
= JS_FALSE
;
777 #define PEEK(ch) (i < limit && s[i] == ch)
781 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
784 #define DONE_DATE_UNLESS(ch) \
786 if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
789 #define DONE_UNLESS(ch) \
791 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
794 #define NEED_NDIGITS(n, field) \
796 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
799 str
->getCharsAndLength(s
, limit
);
801 if (PEEK('+') || PEEK('-')) {
805 NEED_NDIGITS(6, year
);
806 } else if (!PEEK('T')) {
807 NEED_NDIGITS(4, year
);
809 DONE_DATE_UNLESS('-');
810 NEED_NDIGITS(2, month
);
811 DONE_DATE_UNLESS('-');
812 NEED_NDIGITS(2, day
);
816 NEED_NDIGITS(2, hour
);
818 NEED_NDIGITS(2, min
);
822 NEED_NDIGITS(2, sec
);
825 if (!fractional(&frac
, s
, &i
, limit
))
832 } else if (PEEK('+') || PEEK('-')) {
836 NEED_NDIGITS(2, tzHour
);
838 NEED_NDIGITS(2, tzMin
);
840 isLocalTime
= JS_TRUE
;
844 if (year
> 275943 // ceil(1e8/365) + 1970
845 || (month
== 0 || month
> 12)
846 || (day
== 0 || day
> size_t(DaysInMonth(year
,month
)))
848 || ((hour
== 24) && (min
> 0 || sec
> 0))
858 month
-= 1; /* convert month to 0-based */
860 msec
= date_msecFromDate(dateMul
* (jsdouble
)year
, month
, day
,
867 msec
-= ((tzMul
) * ((tzHour
* msPerHour
)
868 + (tzMin
* msPerMinute
)));
871 if (msec
< -8.64e15
|| msec
> 8.64e15
)
890 date_parseString(JSString
*str
, jsdouble
*result
)
907 JSBool seenplusminus
= JS_FALSE
;
909 JSBool seenmonthname
= JS_FALSE
;
911 if (date_parseISOString(str
, result
))
914 str
->getCharsAndLength(s
, limit
);
920 if (c
<= ' ' || c
== ',' || c
== '-') {
921 if (c
== '-' && '0' <= s
[i
] && s
[i
] <= '9') {
926 if (c
== '(') { /* comments) */
931 if (c
== '(') depth
++;
938 if ('0' <= c
&& c
<= '9') {
940 while (i
< limit
&& '0' <= (c
= s
[i
]) && c
<= '9') {
941 n
= n
* 10 + c
- '0';
945 /* allow TZA before the year, so
946 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
949 /* uses of seenplusminus allow : in TZA, so Java
950 * no-timezone style of GMT+4:30 works
953 if ((prevc
== '+' || prevc
== '-')/* && year>=0 */) {
954 /* make ':' case below change tzoffset */
955 seenplusminus
= JS_TRUE
;
959 n
= n
* 60; /* EG. "GMT-3" */
961 n
= n
% 100 + n
/ 100 * 60; /* eg "GMT-0430" */
962 if (prevc
== '+') /* plus means east of GMT */
964 if (tzoffset
!= 0 && tzoffset
!= -1)
967 } else if (prevc
== '/' && mon
>= 0 && mday
>= 0 && year
< 0) {
968 if (c
<= ' ' || c
== ',' || c
== '/' || i
>= limit
)
972 } else if (c
== ':') {
979 } else if (c
== '/') {
980 /* until it is determined that mon is the actual
981 month, keep it as 1-based rather than 0-based */
988 } else if (i
< limit
&& c
!= ',' && c
> ' ' && c
!= '-' && c
!= '(') {
990 } else if (seenplusminus
&& n
< 60) { /* handle GMT-3:30 */
995 } else if (hour
>= 0 && min
< 0) {
997 } else if (prevc
== ':' && min
>= 0 && sec
< 0) {
999 } else if (mon
< 0) {
1001 } else if (mon
>= 0 && mday
< 0) {
1003 } else if (mon
>= 0 && mday
>= 0 && year
< 0) {
1009 } else if (c
== '/' || c
== ':' || c
== '+' || c
== '-') {
1016 if (!(('A' <= c
&& c
<= 'Z') || ('a' <= c
&& c
<= 'z')))
1022 for (k
= JS_ARRAY_LENGTH(wtb
); --k
>= 0;)
1023 if (date_regionMatches(wtb
[k
], 0, s
, st
, i
-st
, 1)) {
1024 int action
= ttb
[k
];
1028 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1029 * 12:30, instead of blindly adding 12 if PM.
1031 JS_ASSERT(action
== -1 || action
== -2);
1032 if (hour
> 12 || hour
< 0) {
1035 if (action
== -1 && hour
== 12) { /* am */
1037 } else if (action
== -2 && hour
!= 12) { /* pm */
1041 } else if (action
<= 13) { /* month! */
1042 /* Adjust mon to be 1-based until the final values
1043 for mon, mday and year are adjusted below */
1044 if (seenmonthname
) {
1047 seenmonthname
= JS_TRUE
;
1048 temp
= /*byte*/ (action
- 2) + 1;
1052 } else if (mday
< 0) {
1055 } else if (year
< 0) {
1062 tzoffset
= action
- 10000;
1072 if (year
< 0 || mon
< 0 || mday
< 0)
1075 Case 1. The input string contains an English month name.
1076 The form of the string can be month f l, or f month l, or
1077 f l month which each evaluate to the same date.
1078 If f and l are both greater than or equal to 70, or
1079 both less than 70, the date is invalid.
1080 The year is taken to be the greater of the values f, l.
1081 If the year is greater than or equal to 70 and less than 100,
1082 it is considered to be the number of years after 1900.
1083 Case 2. The input string is of the form "f/m/l" where f, m and l are
1084 integers, e.g. 7/16/45.
1085 Adjust the mon, mday and year values to achieve 100% MSIE
1087 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1088 i. If year < 100, it is the number of years after 1900
1089 ii. If year >= 100, it is the number of years after 0.
1091 i. If m < 70, f/m/l is interpreted as
1092 year/month/day where year is the number of years after
1094 ii. If m >= 70, the date is invalid.
1096 i. If m < 70, f/m/l is interpreted as
1097 year/month/day where year is the number of years after 0.
1098 ii. If m >= 70, the date is invalid.
1100 if (seenmonthname
) {
1101 if ((mday
>= 70 && year
>= 70) || (mday
< 70 && year
< 70)) {
1109 if (year
>= 70 && year
< 100) {
1112 } else if (mon
< 70) { /* (a) month/day/year */
1116 } else if (mon
< 100) { /* (b) year/month/day */
1125 } else { /* (c) year/month/day */
1135 mon
-= 1; /* convert month to 0-based */
1143 msec
= date_msecFromDate(year
, mon
, mday
, hour
, min
, sec
, 0);
1145 if (tzoffset
== -1) { /* no time zone specified, have to use local */
1148 msec
+= tzoffset
* msPerMinute
;
1161 date_parse(JSContext
*cx
, uintN argc
, jsval
*vp
)
1167 *vp
= cx
->runtime
->NaNValue
;
1170 str
= js_ValueToString(cx
, vp
[2]);
1173 vp
[2] = STRING_TO_JSVAL(str
);
1174 if (!date_parseString(str
, &result
)) {
1175 *vp
= cx
->runtime
->NaNValue
;
1179 result
= TIMECLIP(result
);
1180 return js_NewNumberInRootedValue(cx
, result
, vp
);
1183 static inline jsdouble
1186 return (jsdouble
) (PRMJ_Now() / PRMJ_USEC_PER_MSEC
);
1190 date_now(JSContext
*cx
, uintN argc
, jsval
*vp
)
1192 return js_NewDoubleInRootedValue(cx
, NowAsMillis(), vp
);
1196 static jsdouble FASTCALL
1197 date_now_tn(JSContext
*)
1199 return NowAsMillis();
1204 * Get UTC time from the date object. Returns false if the object is not
1208 GetUTCTime(JSContext
*cx
, JSObject
*obj
, jsval
*vp
, jsdouble
*dp
)
1210 if (!JS_InstanceOf(cx
, obj
, &js_DateClass
, vp
? vp
+ 2 : NULL
))
1212 *dp
= *JSVAL_TO_DOUBLE(obj
->fslots
[JSSLOT_UTC_TIME
]);
1217 SetDateToNaN(JSContext
*cx
, JSObject
*obj
, jsval
*vp
= NULL
)
1219 JS_ASSERT(OBJ_GET_CLASS(cx
, obj
) == &js_DateClass
);
1221 obj
->fslots
[JSSLOT_LOCAL_TIME
] = cx
->runtime
->NaNValue
;
1222 obj
->fslots
[JSSLOT_UTC_TIME
] = cx
->runtime
->NaNValue
;
1224 *vp
= cx
->runtime
->NaNValue
;
1228 * Set UTC time to a given time and invalidate cached local time.
1231 SetUTCTime(JSContext
*cx
, JSObject
*obj
, jsdouble t
, jsval
*vp
= NULL
)
1233 JS_ASSERT(OBJ_GET_CLASS(cx
, obj
) == &js_DateClass
);
1235 obj
->fslots
[JSSLOT_LOCAL_TIME
] = cx
->runtime
->NaNValue
;
1236 if (!js_NewDoubleInRootedValue(cx
, t
, &obj
->fslots
[JSSLOT_UTC_TIME
]))
1239 *vp
= obj
->fslots
[JSSLOT_UTC_TIME
];
1244 * Get the local time, cache it if necessary. If UTC time is not finite
1245 * (e.g., NaN), the local time slot is set to the UTC time without conversion.
1248 GetAndCacheLocalTime(JSContext
*cx
, JSObject
*obj
, jsval
*vp
, jsdouble
*dp
)
1250 if (!obj
|| !JS_InstanceOf(cx
, obj
, &js_DateClass
, vp
? vp
+ 2 : NULL
))
1253 jsval
*slotp
= &obj
->fslots
[JSSLOT_LOCAL_TIME
];
1254 jsdouble result
= *JSVAL_TO_DOUBLE(*slotp
);
1255 if (JSDOUBLE_IS_NaN(result
)) {
1256 result
= *JSVAL_TO_DOUBLE(obj
->fslots
[JSSLOT_UTC_TIME
]);
1258 /* if result is NaN, it couldn't be finite. */
1259 if (JSDOUBLE_IS_FINITE(result
))
1260 result
= LocalTime(result
);
1262 if (!js_NewDoubleInRootedValue(cx
, result
, slotp
))
1271 * See ECMA 15.9.5.4 thru 15.9.5.23
1274 date_getTime(JSContext
*cx
, uintN argc
, jsval
*vp
)
1278 return GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
) &&
1279 js_NewNumberInRootedValue(cx
, result
, vp
);
1283 GetYear(JSContext
*cx
, JSBool fullyear
, jsval
*vp
)
1287 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1290 if (JSDOUBLE_IS_FINITE(result
)) {
1291 result
= YearFromTime(result
);
1293 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1298 return js_NewNumberInRootedValue(cx
, result
, vp
);
1302 date_getYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1304 return GetYear(cx
, JS_FALSE
, vp
);
1308 date_getFullYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1310 return GetYear(cx
, JS_TRUE
, vp
);
1314 date_getUTCFullYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1318 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1321 if (JSDOUBLE_IS_FINITE(result
))
1322 result
= YearFromTime(result
);
1324 return js_NewNumberInRootedValue(cx
, result
, vp
);
1328 date_getMonth(JSContext
*cx
, uintN argc
, jsval
*vp
)
1332 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1335 if (JSDOUBLE_IS_FINITE(result
))
1336 result
= MonthFromTime(result
);
1338 return js_NewNumberInRootedValue(cx
, result
, vp
);
1342 date_getUTCMonth(JSContext
*cx
, uintN argc
, jsval
*vp
)
1346 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1349 if (JSDOUBLE_IS_FINITE(result
))
1350 result
= MonthFromTime(result
);
1352 return js_NewNumberInRootedValue(cx
, result
, vp
);
1356 date_getDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
1360 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1363 if (JSDOUBLE_IS_FINITE(result
))
1364 result
= DateFromTime(result
);
1366 return js_NewNumberInRootedValue(cx
, result
, vp
);
1370 date_getUTCDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
1374 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1377 if (JSDOUBLE_IS_FINITE(result
))
1378 result
= DateFromTime(result
);
1380 return js_NewNumberInRootedValue(cx
, result
, vp
);
1384 date_getDay(JSContext
*cx
, uintN argc
, jsval
*vp
)
1388 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1391 if (JSDOUBLE_IS_FINITE(result
))
1392 result
= WeekDay(result
);
1394 return js_NewNumberInRootedValue(cx
, result
, vp
);
1398 date_getUTCDay(JSContext
*cx
, uintN argc
, jsval
*vp
)
1402 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1405 if (JSDOUBLE_IS_FINITE(result
))
1406 result
= WeekDay(result
);
1408 return js_NewNumberInRootedValue(cx
, result
, vp
);
1412 date_getHours(JSContext
*cx
, uintN argc
, jsval
*vp
)
1416 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1419 if (JSDOUBLE_IS_FINITE(result
))
1420 result
= HourFromTime(result
);
1422 return js_NewNumberInRootedValue(cx
, result
, vp
);
1426 date_getUTCHours(JSContext
*cx
, uintN argc
, jsval
*vp
)
1430 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1433 if (JSDOUBLE_IS_FINITE(result
))
1434 result
= HourFromTime(result
);
1436 return js_NewNumberInRootedValue(cx
, result
, vp
);
1440 date_getMinutes(JSContext
*cx
, uintN argc
, jsval
*vp
)
1444 if (!GetAndCacheLocalTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1447 if (JSDOUBLE_IS_FINITE(result
))
1448 result
= MinFromTime(result
);
1450 return js_NewNumberInRootedValue(cx
, result
, vp
);
1454 date_getUTCMinutes(JSContext
*cx
, uintN argc
, jsval
*vp
)
1458 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1461 if (JSDOUBLE_IS_FINITE(result
))
1462 result
= MinFromTime(result
);
1464 return js_NewNumberInRootedValue(cx
, result
, vp
);
1467 /* Date.getSeconds is mapped to getUTCSeconds */
1470 date_getUTCSeconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1474 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1477 if (JSDOUBLE_IS_FINITE(result
))
1478 result
= SecFromTime(result
);
1480 return js_NewNumberInRootedValue(cx
, result
, vp
);
1483 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1486 date_getUTCMilliseconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1490 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &result
))
1493 if (JSDOUBLE_IS_FINITE(result
))
1494 result
= msFromTime(result
);
1496 return js_NewNumberInRootedValue(cx
, result
, vp
);
1500 date_getTimezoneOffset(JSContext
*cx
, uintN argc
, jsval
*vp
)
1503 jsdouble utctime
, localtime
, result
;
1505 obj
= JS_THIS_OBJECT(cx
, vp
);
1506 if (!GetUTCTime(cx
, obj
, vp
, &utctime
))
1508 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
))
1512 * Return the time zone offset in minutes for the current locale that is
1513 * appropriate for this time. This value would be a constant except for
1514 * daylight savings time.
1516 result
= (utctime
- localtime
) / msPerMinute
;
1517 return js_NewNumberInRootedValue(cx
, result
, vp
);
1521 date_setTime(JSContext
*cx
, uintN argc
, jsval
*vp
)
1523 JSObject
*obj
= JS_THIS_OBJECT(cx
, vp
);
1524 if (!JS_InstanceOf(cx
, obj
, &js_DateClass
, vp
+ 2))
1528 SetDateToNaN(cx
, obj
, vp
);
1532 jsdouble result
= js_ValueToNumber(cx
, &vp
[2]);
1533 if (JSVAL_IS_NULL(vp
[2]))
1536 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1540 date_makeTime(JSContext
*cx
, uintN maxargs
, JSBool local
, uintN argc
, jsval
*vp
)
1545 jsdouble args
[4], *argp
, *stop
;
1546 jsdouble hour
, min
, sec
, msec
;
1547 jsdouble lorutime
; /* Local or UTC version of *date */
1552 obj
= JS_THIS_OBJECT(cx
, vp
);
1553 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1556 /* just return NaN if the date is already NaN */
1557 if (!JSDOUBLE_IS_FINITE(result
))
1558 return js_NewNumberInRootedValue(cx
, result
, vp
);
1561 * Satisfy the ECMA rule that if a function is called with
1562 * fewer arguments than the specified formal arguments, the
1563 * remaining arguments are set to undefined. Seems like all
1564 * the Date.setWhatever functions in ECMA are only varargs
1565 * beyond the first argument; this should be set to undefined
1566 * if it's not given. This means that "d = new Date();
1567 * d.setMilliseconds()" returns NaN. Blech.
1570 SetDateToNaN(cx
, obj
, vp
);
1574 argc
= maxargs
; /* clamp argc */
1575 JS_ASSERT(argc
<= 4);
1578 for (i
= 0; i
< argc
; i
++) {
1579 args
[i
] = js_ValueToNumber(cx
, &argv
[i
]);
1580 if (JSVAL_IS_NULL(argv
[i
]))
1582 if (!JSDOUBLE_IS_FINITE(args
[i
])) {
1583 SetDateToNaN(cx
, obj
, vp
);
1586 args
[i
] = js_DoubleToInteger(args
[i
]);
1590 lorutime
= LocalTime(result
);
1596 if (maxargs
>= 4 && argp
< stop
)
1599 hour
= HourFromTime(lorutime
);
1601 if (maxargs
>= 3 && argp
< stop
)
1604 min
= MinFromTime(lorutime
);
1606 if (maxargs
>= 2 && argp
< stop
)
1609 sec
= SecFromTime(lorutime
);
1611 if (maxargs
>= 1 && argp
< stop
)
1614 msec
= msFromTime(lorutime
);
1616 msec_time
= MakeTime(hour
, min
, sec
, msec
);
1617 result
= MakeDate(Day(lorutime
), msec_time
);
1619 /* fprintf(stderr, "%f\n", result); */
1622 result
= UTC(result
);
1624 /* fprintf(stderr, "%f\n", result); */
1626 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1630 date_setMilliseconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1632 return date_makeTime(cx
, 1, JS_TRUE
, argc
, vp
);
1636 date_setUTCMilliseconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1638 return date_makeTime(cx
, 1, JS_FALSE
, argc
, vp
);
1642 date_setSeconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1644 return date_makeTime(cx
, 2, JS_TRUE
, argc
, vp
);
1648 date_setUTCSeconds(JSContext
*cx
, uintN argc
, jsval
*vp
)
1650 return date_makeTime(cx
, 2, JS_FALSE
, argc
, vp
);
1654 date_setMinutes(JSContext
*cx
, uintN argc
, jsval
*vp
)
1656 return date_makeTime(cx
, 3, JS_TRUE
, argc
, vp
);
1660 date_setUTCMinutes(JSContext
*cx
, uintN argc
, jsval
*vp
)
1662 return date_makeTime(cx
, 3, JS_FALSE
, argc
, vp
);
1666 date_setHours(JSContext
*cx
, uintN argc
, jsval
*vp
)
1668 return date_makeTime(cx
, 4, JS_TRUE
, argc
, vp
);
1672 date_setUTCHours(JSContext
*cx
, uintN argc
, jsval
*vp
)
1674 return date_makeTime(cx
, 4, JS_FALSE
, argc
, vp
);
1678 date_makeDate(JSContext
*cx
, uintN maxargs
, JSBool local
, uintN argc
, jsval
*vp
)
1683 jsdouble lorutime
; /* local or UTC version of *date */
1684 jsdouble args
[3], *argp
, *stop
;
1685 jsdouble year
, month
, day
;
1688 obj
= JS_THIS_OBJECT(cx
, vp
);
1689 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1692 /* see complaint about ECMA in date_MakeTime */
1694 SetDateToNaN(cx
, obj
, vp
);
1698 argc
= maxargs
; /* clamp argc */
1699 JS_ASSERT(1 <= argc
&& argc
<= 3);
1702 for (i
= 0; i
< argc
; i
++) {
1703 args
[i
] = js_ValueToNumber(cx
, &argv
[i
]);
1704 if (JSVAL_IS_NULL(argv
[i
]))
1706 if (!JSDOUBLE_IS_FINITE(args
[i
])) {
1707 SetDateToNaN(cx
, obj
, vp
);
1710 args
[i
] = js_DoubleToInteger(args
[i
]);
1713 /* return NaN if date is NaN and we're not setting the year,
1714 * If we are, use 0 as the time. */
1715 if (!(JSDOUBLE_IS_FINITE(result
))) {
1717 return js_NewNumberInRootedValue(cx
, result
, vp
);
1720 lorutime
= local
? LocalTime(result
) : result
;
1725 if (maxargs
>= 3 && argp
< stop
)
1728 year
= YearFromTime(lorutime
);
1730 if (maxargs
>= 2 && argp
< stop
)
1733 month
= MonthFromTime(lorutime
);
1735 if (maxargs
>= 1 && argp
< stop
)
1738 day
= DateFromTime(lorutime
);
1740 day
= MakeDay(year
, month
, day
); /* day within year */
1741 result
= MakeDate(day
, TimeWithinDay(lorutime
));
1744 result
= UTC(result
);
1746 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1750 date_setDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
1752 return date_makeDate(cx
, 1, JS_TRUE
, argc
, vp
);
1756 date_setUTCDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
1758 return date_makeDate(cx
, 1, JS_FALSE
, argc
, vp
);
1762 date_setMonth(JSContext
*cx
, uintN argc
, jsval
*vp
)
1764 return date_makeDate(cx
, 2, JS_TRUE
, argc
, vp
);
1768 date_setUTCMonth(JSContext
*cx
, uintN argc
, jsval
*vp
)
1770 return date_makeDate(cx
, 2, JS_FALSE
, argc
, vp
);
1774 date_setFullYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1776 return date_makeDate(cx
, 3, JS_TRUE
, argc
, vp
);
1780 date_setUTCFullYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1782 return date_makeDate(cx
, 3, JS_FALSE
, argc
, vp
);
1786 date_setYear(JSContext
*cx
, uintN argc
, jsval
*vp
)
1788 JSObject
*obj
= JS_THIS_OBJECT(cx
, vp
);
1791 if (!GetUTCTime(cx
, obj
, vp
, &result
))
1795 /* Call this only after GetUTCTime has verified that obj is Date. */
1796 SetDateToNaN(cx
, obj
, vp
);
1800 jsdouble year
= js_ValueToNumber(cx
, &vp
[2]);
1801 if (JSVAL_IS_NULL(vp
[2]))
1803 if (!JSDOUBLE_IS_FINITE(year
)) {
1804 SetDateToNaN(cx
, obj
, vp
);
1807 year
= js_DoubleToInteger(year
);
1808 if (year
>= 0 && year
<= 99)
1811 jsdouble t
= JSDOUBLE_IS_FINITE(result
) ? LocalTime(result
) : +0.0;
1812 jsdouble day
= MakeDay(year
, MonthFromTime(t
), DateFromTime(t
));
1813 result
= MakeDate(day
, TimeWithinDay(t
));
1814 result
= UTC(result
);
1816 return SetUTCTime(cx
, obj
, TIMECLIP(result
), vp
);
1819 /* constants for toString, toUTCString */
1820 static char js_NaN_date_str
[] = "Invalid Date";
1821 static const char* days
[] =
1823 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1825 static const char* months
[] =
1827 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1831 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1832 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1834 print_gmt_string(char* buf
, size_t size
, jsdouble utctime
)
1836 JS_snprintf(buf
, size
, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1837 days
[WeekDay(utctime
)],
1838 DateFromTime(utctime
),
1839 months
[MonthFromTime(utctime
)],
1840 YearFromTime(utctime
),
1841 HourFromTime(utctime
),
1842 MinFromTime(utctime
),
1843 SecFromTime(utctime
));
1847 print_iso_string(char* buf
, size_t size
, jsdouble utctime
)
1849 JS_snprintf(buf
, size
, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
1850 YearFromTime(utctime
),
1851 MonthFromTime(utctime
) + 1,
1852 DateFromTime(utctime
),
1853 HourFromTime(utctime
),
1854 MinFromTime(utctime
),
1855 SecFromTime(utctime
),
1856 msFromTime(utctime
));
1860 date_utc_format(JSContext
*cx
, jsval
*vp
,
1861 void (*printFunc
)(char*, size_t, jsdouble
))
1867 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
1870 if (!JSDOUBLE_IS_FINITE(utctime
)) {
1871 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
1873 (*printFunc
)(buf
, sizeof buf
, utctime
);
1875 str
= JS_NewStringCopyZ(cx
, buf
);
1878 *vp
= STRING_TO_JSVAL(str
);
1883 date_toGMTString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1885 return date_utc_format(cx
, vp
, print_gmt_string
);
1889 date_toISOString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1891 return date_utc_format(cx
, vp
, print_iso_string
);
1894 /* for Date.toLocaleString; interface to PRMJTime date struct.
1897 new_explode(jsdouble timeval
, PRMJTime
*split
)
1899 jsint year
= YearFromTime(timeval
);
1901 split
->tm_usec
= (int32
) msFromTime(timeval
) * 1000;
1902 split
->tm_sec
= (int8
) SecFromTime(timeval
);
1903 split
->tm_min
= (int8
) MinFromTime(timeval
);
1904 split
->tm_hour
= (int8
) HourFromTime(timeval
);
1905 split
->tm_mday
= (int8
) DateFromTime(timeval
);
1906 split
->tm_mon
= (int8
) MonthFromTime(timeval
);
1907 split
->tm_wday
= (int8
) WeekDay(timeval
);
1908 split
->tm_year
= year
;
1909 split
->tm_yday
= (int16
) DayWithinYear(timeval
, year
);
1911 /* not sure how this affects things, but it doesn't seem
1913 split
->tm_isdst
= (DaylightSavingTA(timeval
) != 0);
1916 typedef enum formatspec
{
1917 FORMATSPEC_FULL
, FORMATSPEC_DATE
, FORMATSPEC_TIME
1920 /* helper function */
1922 date_format(JSContext
*cx
, jsdouble date
, formatspec format
, jsval
*rval
)
1931 if (!JSDOUBLE_IS_FINITE(date
)) {
1932 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
1934 jsdouble local
= LocalTime(date
);
1936 /* offset from GMT in minutes. The offset includes daylight savings,
1938 jsint minutes
= (jsint
) floor(AdjustTime(date
) / msPerMinute
);
1940 /* map 510 minutes to 0830 hours */
1941 intN offset
= (minutes
/ 60) * 100 + minutes
% 60;
1943 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1944 * printed as 'GMT-0800' rather than as 'PST' to avoid
1945 * operating-system dependence on strftime (which
1946 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1947 * PST as 'Pacific Standard Time.' This way we always know
1948 * what we're getting, and can parse it if we produce it.
1949 * The OS TZA string is included as a comment.
1952 /* get a timezone string from the OS to include as a
1954 new_explode(date
, &split
);
1955 if (PRMJ_FormatTime(tzbuf
, sizeof tzbuf
, "(%Z)", &split
) != 0) {
1957 /* Decide whether to use the resulting timezone string.
1959 * Reject it if it contains any non-ASCII, non-alphanumeric
1960 * characters. It's then likely in some other character
1961 * encoding, and we probably won't display it correctly.
1964 tzlen
= strlen(tzbuf
);
1968 for (i
= 0; i
< tzlen
; i
++) {
1969 jschar c
= tzbuf
[i
];
1971 !(isalpha(c
) || isdigit(c
) ||
1972 c
== ' ' || c
== '(' || c
== ')')) {
1978 /* Also reject it if it's not parenthesized or if it's '()'. */
1979 if (tzbuf
[0] != '(' || tzbuf
[1] == ')')
1985 case FORMATSPEC_FULL
:
1987 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1988 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1990 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1991 JS_snprintf(buf
, sizeof buf
,
1992 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1993 days
[WeekDay(local
)],
1994 months
[MonthFromTime(local
)],
1995 DateFromTime(local
),
1996 YearFromTime(local
),
1997 HourFromTime(local
),
2002 usetz
? tzbuf
: "");
2004 case FORMATSPEC_DATE
:
2005 /* Tue Oct 31 2000 */
2006 JS_snprintf(buf
, sizeof buf
,
2008 days
[WeekDay(local
)],
2009 months
[MonthFromTime(local
)],
2010 DateFromTime(local
),
2011 YearFromTime(local
));
2013 case FORMATSPEC_TIME
:
2014 /* 09:41:40 GMT-0800 (PST) */
2015 JS_snprintf(buf
, sizeof buf
,
2016 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2017 HourFromTime(local
),
2022 usetz
? tzbuf
: "");
2027 str
= JS_NewStringCopyZ(cx
, buf
);
2030 *rval
= STRING_TO_JSVAL(str
);
2035 date_toLocaleHelper(JSContext
*cx
, const char *format
, jsval
*vp
)
2043 obj
= JS_THIS_OBJECT(cx
, vp
);
2044 if (!GetUTCTime(cx
, obj
, vp
, &utctime
))
2047 if (!JSDOUBLE_IS_FINITE(utctime
)) {
2048 JS_snprintf(buf
, sizeof buf
, js_NaN_date_str
);
2051 jsdouble local
= LocalTime(utctime
);
2052 new_explode(local
, &split
);
2054 /* let PRMJTime format it. */
2055 result_len
= PRMJ_FormatTime(buf
, sizeof buf
, format
, &split
);
2057 /* If it failed, default to toString. */
2058 if (result_len
== 0)
2059 return date_format(cx
, utctime
, FORMATSPEC_FULL
, vp
);
2061 /* Hacked check against undesired 2-digit year 00/00/00 form. */
2062 if (strcmp(format
, "%x") == 0 && result_len
>= 6 &&
2063 /* Format %x means use OS settings, which may have 2-digit yr, so
2064 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2065 !isdigit(buf
[result_len
- 3]) &&
2066 isdigit(buf
[result_len
- 2]) && isdigit(buf
[result_len
- 1]) &&
2067 /* ...but not if starts with 4-digit year, like 2022/3/11. */
2068 !(isdigit(buf
[0]) && isdigit(buf
[1]) &&
2069 isdigit(buf
[2]) && isdigit(buf
[3]))) {
2070 JS_snprintf(buf
+ (result_len
- 2), (sizeof buf
) - (result_len
- 2),
2071 "%d", js_DateGetYear(cx
, obj
));
2076 if (cx
->localeCallbacks
&& cx
->localeCallbacks
->localeToUnicode
)
2077 return cx
->localeCallbacks
->localeToUnicode(cx
, buf
, vp
);
2079 str
= JS_NewStringCopyZ(cx
, buf
);
2082 *vp
= STRING_TO_JSVAL(str
);
2087 date_toLocaleString(JSContext
*cx
, uintN argc
, jsval
*vp
)
2089 /* Use '%#c' for windows, because '%c' is
2090 * backward-compatible and non-y2k with msvc; '%#c' requests that a
2091 * full year be used in the result string.
2093 return date_toLocaleHelper(cx
,
2094 #if defined(_WIN32) && !defined(__MWERKS__)
2103 date_toLocaleDateString(JSContext
*cx
, uintN argc
, jsval
*vp
)
2105 /* Use '%#x' for windows, because '%x' is
2106 * backward-compatible and non-y2k with msvc; '%#x' requests that a
2107 * full year be used in the result string.
2109 return date_toLocaleHelper(cx
,
2110 #if defined(_WIN32) && !defined(__MWERKS__)
2119 date_toLocaleTimeString(JSContext
*cx
, uintN argc
, jsval
*vp
)
2121 return date_toLocaleHelper(cx
, "%X", vp
);
2125 date_toLocaleFormat(JSContext
*cx
, uintN argc
, jsval
*vp
)
2128 const char *fmtbytes
;
2131 return date_toLocaleString(cx
, argc
, vp
);
2133 fmt
= js_ValueToString(cx
, vp
[2]);
2136 vp
[2] = STRING_TO_JSVAL(fmt
);
2137 fmtbytes
= js_GetStringBytes(cx
, fmt
);
2141 return date_toLocaleHelper(cx
, fmtbytes
, vp
);
2145 date_toTimeString(JSContext
*cx
, uintN argc
, jsval
*vp
)
2149 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
2151 return date_format(cx
, utctime
, FORMATSPEC_TIME
, vp
);
2155 date_toDateString(JSContext
*cx
, uintN argc
, jsval
*vp
)
2159 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
2161 return date_format(cx
, utctime
, FORMATSPEC_DATE
, vp
);
2169 date_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
2172 char buf
[DTOSTR_STANDARD_BUFFER_SIZE
], *numStr
, *bytes
;
2175 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
2178 numStr
= JS_dtostr(buf
, sizeof buf
, DTOSTR_STANDARD
, 0, utctime
);
2180 JS_ReportOutOfMemory(cx
);
2184 bytes
= JS_smprintf("(new %s(%s))", js_Date_str
, numStr
);
2186 JS_ReportOutOfMemory(cx
);
2190 str
= JS_NewString(cx
, bytes
, strlen(bytes
));
2195 *vp
= STRING_TO_JSVAL(str
);
2201 date_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
2205 if (!GetUTCTime(cx
, JS_THIS_OBJECT(cx
, vp
), vp
, &utctime
))
2207 return date_format(cx
, utctime
, FORMATSPEC_FULL
, vp
);
2211 static jsval FASTCALL
2212 date_valueOf_tn(JSContext
* cx
, JSObject
* obj
, JSString
* str
)
2214 JS_ASSERT(JS_InstanceOf(cx
, obj
, &js_DateClass
, NULL
));
2215 jsdouble t
= *JSVAL_TO_DOUBLE(obj
->fslots
[JSSLOT_UTC_TIME
]);
2217 JSString
* number_str
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_NUMBER
]);
2219 if (js_EqualStrings(str
, number_str
)) {
2220 if (!js_NewNumberInRootedValue(cx
, t
, &v
))
2221 return JSVAL_ERROR_COOKIE
;
2223 if (!date_format(cx
, t
, FORMATSPEC_FULL
, &v
))
2224 return JSVAL_ERROR_COOKIE
;
2231 date_valueOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
2233 JSString
*str
, *number_str
;
2235 /* It is an error to call date_valueOf on a non-date object, but we don't
2236 * need to check for that explicitly here because every path calls
2237 * GetUTCTime, which does the check.
2240 /* If called directly with no arguments, convert to a time number. */
2242 return date_getTime(cx
, argc
, vp
);
2244 /* Convert to number only if the hint was given, otherwise favor string. */
2245 str
= js_ValueToString(cx
, vp
[2]);
2248 number_str
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_NUMBER
]);
2249 if (js_EqualStrings(str
, number_str
))
2250 return date_getTime(cx
, argc
, vp
);
2251 return date_toString(cx
, argc
, vp
);
2254 // Don't really need an argument here, but we don't support arg-less builtins
2255 JS_DEFINE_TRCINFO_1(date_now
,
2256 (1, (static, DOUBLE
, date_now_tn
, CONTEXT
, 0, 0)))
2258 static JSFunctionSpec date_static_methods
[] = {
2259 JS_FN("UTC", date_UTC
, MAXARGS
,0),
2260 JS_FN("parse", date_parse
, 1,0),
2261 JS_TN("now", date_now
, 0,0, &date_now_trcinfo
),
2265 JS_DEFINE_TRCINFO_1(date_valueOf
,
2266 (3, (static, JSVAL_RETRY
, date_valueOf_tn
, CONTEXT
, THIS
, STRING
, 0, 0)))
2268 static JSFunctionSpec date_methods
[] = {
2269 JS_FN("getTime", date_getTime
, 0,0),
2270 JS_FN("getTimezoneOffset", date_getTimezoneOffset
, 0,0),
2271 JS_FN("getYear", date_getYear
, 0,0),
2272 JS_FN("getFullYear", date_getFullYear
, 0,0),
2273 JS_FN("getUTCFullYear", date_getUTCFullYear
, 0,0),
2274 JS_FN("getMonth", date_getMonth
, 0,0),
2275 JS_FN("getUTCMonth", date_getUTCMonth
, 0,0),
2276 JS_FN("getDate", date_getDate
, 0,0),
2277 JS_FN("getUTCDate", date_getUTCDate
, 0,0),
2278 JS_FN("getDay", date_getDay
, 0,0),
2279 JS_FN("getUTCDay", date_getUTCDay
, 0,0),
2280 JS_FN("getHours", date_getHours
, 0,0),
2281 JS_FN("getUTCHours", date_getUTCHours
, 0,0),
2282 JS_FN("getMinutes", date_getMinutes
, 0,0),
2283 JS_FN("getUTCMinutes", date_getUTCMinutes
, 0,0),
2284 JS_FN("getSeconds", date_getUTCSeconds
, 0,0),
2285 JS_FN("getUTCSeconds", date_getUTCSeconds
, 0,0),
2286 JS_FN("getMilliseconds", date_getUTCMilliseconds
, 0,0),
2287 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds
, 0,0),
2288 JS_FN("setTime", date_setTime
, 1,0),
2289 JS_FN("setYear", date_setYear
, 1,0),
2290 JS_FN("setFullYear", date_setFullYear
, 3,0),
2291 JS_FN("setUTCFullYear", date_setUTCFullYear
, 3,0),
2292 JS_FN("setMonth", date_setMonth
, 2,0),
2293 JS_FN("setUTCMonth", date_setUTCMonth
, 2,0),
2294 JS_FN("setDate", date_setDate
, 1,0),
2295 JS_FN("setUTCDate", date_setUTCDate
, 1,0),
2296 JS_FN("setHours", date_setHours
, 4,0),
2297 JS_FN("setUTCHours", date_setUTCHours
, 4,0),
2298 JS_FN("setMinutes", date_setMinutes
, 3,0),
2299 JS_FN("setUTCMinutes", date_setUTCMinutes
, 3,0),
2300 JS_FN("setSeconds", date_setSeconds
, 2,0),
2301 JS_FN("setUTCSeconds", date_setUTCSeconds
, 2,0),
2302 JS_FN("setMilliseconds", date_setMilliseconds
, 1,0),
2303 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds
, 1,0),
2304 JS_FN("toUTCString", date_toGMTString
, 0,0),
2305 JS_FN(js_toLocaleString_str
, date_toLocaleString
, 0,0),
2306 JS_FN("toLocaleDateString", date_toLocaleDateString
, 0,0),
2307 JS_FN("toLocaleTimeString", date_toLocaleTimeString
, 0,0),
2308 JS_FN("toLocaleFormat", date_toLocaleFormat
, 0,0),
2309 JS_FN("toDateString", date_toDateString
, 0,0),
2310 JS_FN("toTimeString", date_toTimeString
, 0,0),
2311 JS_FN("toISOString", date_toISOString
, 0,0),
2312 JS_FN(js_toJSON_str
, date_toISOString
, 0,0),
2315 JS_FN(js_toSource_str
, date_toSource
, 0,0),
2317 JS_FN(js_toString_str
, date_toString
, 0,0),
2318 JS_TN(js_valueOf_str
, date_valueOf
, 0,0, &date_valueOf_trcinfo
),
2323 js_Date(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2325 /* Date called as function. */
2326 if (!JS_IsConstructing(cx
))
2327 return date_format(cx
, NowAsMillis(), FORMATSPEC_FULL
, rval
);
2329 /* Date called as constructor. */
2333 } else if (argc
== 1) {
2334 if (!JSVAL_IS_STRING(argv
[0])) {
2335 /* the argument is a millisecond number */
2336 d
= js_ValueToNumber(cx
, &argv
[0]);
2337 if (JSVAL_IS_NULL(argv
[0]))
2341 /* the argument is a string; parse it. */
2342 JSString
*str
= js_ValueToString(cx
, argv
[0]);
2345 argv
[0] = STRING_TO_JSVAL(str
);
2347 if (!date_parseString(str
, &d
))
2354 if (!date_msecFromArgs(cx
, argc
, argv
, &msec_time
))
2357 if (JSDOUBLE_IS_FINITE(msec_time
)) {
2358 msec_time
= UTC(msec_time
);
2359 msec_time
= TIMECLIP(msec_time
);
2363 return SetUTCTime(cx
, obj
, d
);
2367 js_InitDateClass(JSContext
*cx
, JSObject
*obj
)
2371 /* set static LocalTZA */
2372 LocalTZA
= -(PRMJ_LocalGMTDifference() * msPerSecond
);
2373 proto
= js_InitClass(cx
, obj
, NULL
, &js_DateClass
, js_Date
, MAXARGS
,
2374 NULL
, date_methods
, NULL
, date_static_methods
);
2378 SetDateToNaN(cx
, proto
);
2380 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2381 if (!JS_AliasProperty(cx
, proto
, "toUTCString", "toGMTString"))
2387 JS_FRIEND_API(JSObject
*)
2388 js_NewDateObjectMsec(JSContext
*cx
, jsdouble msec_time
)
2390 JSObject
*obj
= js_NewObject(cx
, &js_DateClass
, NULL
, NULL
);
2391 if (!obj
|| !SetUTCTime(cx
, obj
, msec_time
))
2396 JS_FRIEND_API(JSObject
*)
2397 js_NewDateObject(JSContext
* cx
, int year
, int mon
, int mday
,
2398 int hour
, int min
, int sec
)
2403 JS_ASSERT(mon
< 12);
2404 msec_time
= date_msecFromDate(year
, mon
, mday
, hour
, min
, sec
, 0);
2405 obj
= js_NewDateObjectMsec(cx
, UTC(msec_time
));
2409 JS_FRIEND_API(JSBool
)
2410 js_DateIsValid(JSContext
*cx
, JSObject
* obj
)
2413 return GetUTCTime(cx
, obj
, NULL
, &utctime
) && !JSDOUBLE_IS_NaN(utctime
);
2417 js_DateGetYear(JSContext
*cx
, JSObject
* obj
)
2421 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2422 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2423 JSDOUBLE_IS_NaN(localtime
)) {
2427 return (int) YearFromTime(localtime
);
2431 js_DateGetMonth(JSContext
*cx
, JSObject
* obj
)
2435 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2436 JSDOUBLE_IS_NaN(localtime
)) {
2440 return (int) MonthFromTime(localtime
);
2444 js_DateGetDate(JSContext
*cx
, JSObject
* obj
)
2448 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2449 JSDOUBLE_IS_NaN(localtime
)) {
2453 return (int) DateFromTime(localtime
);
2457 js_DateGetHours(JSContext
*cx
, JSObject
* obj
)
2461 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2462 JSDOUBLE_IS_NaN(localtime
)) {
2466 return (int) HourFromTime(localtime
);
2470 js_DateGetMinutes(JSContext
*cx
, JSObject
* obj
)
2474 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &localtime
) ||
2475 JSDOUBLE_IS_NaN(localtime
)) {
2479 return (int) MinFromTime(localtime
);
2483 js_DateGetSeconds(JSContext
*cx
, JSObject
* obj
)
2487 if (!GetUTCTime(cx
, obj
, NULL
, &utctime
) || JSDOUBLE_IS_NaN(utctime
))
2490 return (int) SecFromTime(utctime
);
2494 js_DateSetYear(JSContext
*cx
, JSObject
*obj
, int year
)
2498 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2501 /* reset date if it was NaN */
2502 if (JSDOUBLE_IS_NaN(local
))
2505 local
= date_msecFromDate(year
,
2506 MonthFromTime(local
),
2507 DateFromTime(local
),
2508 HourFromTime(local
),
2513 /* SetUTCTime also invalidates local time cache. */
2514 SetUTCTime(cx
, obj
, UTC(local
));
2518 js_DateSetMonth(JSContext
*cx
, JSObject
*obj
, int month
)
2522 JS_ASSERT(month
< 12);
2524 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2527 /* bail if date was NaN */
2528 if (JSDOUBLE_IS_NaN(local
))
2531 local
= date_msecFromDate(YearFromTime(local
),
2533 DateFromTime(local
),
2534 HourFromTime(local
),
2538 SetUTCTime(cx
, obj
, UTC(local
));
2542 js_DateSetDate(JSContext
*cx
, JSObject
*obj
, int date
)
2546 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2549 if (JSDOUBLE_IS_NaN(local
))
2552 local
= date_msecFromDate(YearFromTime(local
),
2553 MonthFromTime(local
),
2555 HourFromTime(local
),
2559 SetUTCTime(cx
, obj
, UTC(local
));
2563 js_DateSetHours(JSContext
*cx
, JSObject
*obj
, int hours
)
2567 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2570 if (JSDOUBLE_IS_NaN(local
))
2572 local
= date_msecFromDate(YearFromTime(local
),
2573 MonthFromTime(local
),
2574 DateFromTime(local
),
2579 SetUTCTime(cx
, obj
, UTC(local
));
2583 js_DateSetMinutes(JSContext
*cx
, JSObject
*obj
, int minutes
)
2587 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2590 if (JSDOUBLE_IS_NaN(local
))
2592 local
= date_msecFromDate(YearFromTime(local
),
2593 MonthFromTime(local
),
2594 DateFromTime(local
),
2595 HourFromTime(local
),
2599 SetUTCTime(cx
, obj
, UTC(local
));
2603 js_DateSetSeconds(JSContext
*cx
, JSObject
*obj
, int seconds
)
2607 if (!GetAndCacheLocalTime(cx
, obj
, NULL
, &local
))
2610 if (JSDOUBLE_IS_NaN(local
))
2612 local
= date_msecFromDate(YearFromTime(local
),
2613 MonthFromTime(local
),
2614 DateFromTime(local
),
2615 HourFromTime(local
),
2619 SetUTCTime(cx
, obj
, UTC(local
));
2622 JS_FRIEND_API(jsdouble
)
2623 js_DateGetMsecSinceEpoch(JSContext
*cx
, JSObject
*obj
)
2626 if (!GetUTCTime(cx
, obj
, NULL
, &utctime
))
2631 #ifdef JS_THREADSAFE
2632 #include "prinrval.h"
2634 JS_FRIEND_API(uint32
)
2637 return uint32(PR_IntervalToMilliseconds(PR_IntervalNow()));
2640 #else /* !JS_THREADSAFE */
2642 JS_FRIEND_API(uint32
)
2645 return uint32(PRMJ_Now() / PRMJ_USEC_PER_MSEC
);