gecko-dev/js/src/jsdate.c
brendan%mozilla.org 9324b81dee Fix for bug 99663 (for loop resolves properties of the object being enumerated
with JSRESOLVE_ASSIGNING, wrongly), plus a few miscellaneous bugfixes.

- Combine the JSStackFrame members constructing, special, overrides, and
  reserved into a uint32 flags member.

- Separate JOF_ASSIGNING from the JOF_SET bytecode format flag, and impute
  JSRESOLVE_ASSIGNING from the presence of JOF_ASSIGNING among the current
  opcode's format flags.  To handle the for-in loop opcodes, which do more
  than simply assign -- in particular, they do property lookups whose resolve
  hook outcalls should not be flagged with JSRESOLVE_ASSIGNING -- a new frame
  flag, JSFRAME_ASSIGNING, has been added.

- Fix interpreter version selection to respect JS_SetVersion, whose effect on
  cx->version is "sticky".

- Fix js_DecompileValueGenerator to deal with JSOP_ENUMELEM -- it never had,
  as this testcase shows (it crashes without this patch):

    version(120);
    eval("function fe(s) { for (it[s] in this); }");
    try { fe('rdonly'); } catch (e) { print(e); }
2001-10-03 06:39:30 +00:00

2213 lines
57 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
/*
* JS date methods.
*/
/*
* "For example, OS/360 devotes 26 bytes of the permanently
* resident date-turnover routine to the proper handling of
* December 31 on leap years (when it is Day 366). That
* might have been left to the operator."
*
* Frederick Brooks, 'The Second-System Effect'.
*/
#include "jsstddef.h"
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsprf.h"
#include "prmjtime.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsconfig.h"
#include "jscntxt.h"
#include "jsdate.h"
#include "jsinterp.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsstr.h"
/*
* The JS 'Date' object is patterned after the Java 'Date' object.
* Here is an script:
*
* today = new Date();
*
* print(today.toLocaleString());
*
* weekDay = today.getDay();
*
*
* These Java (and ECMA-262) methods are supported:
*
* UTC
* getDate (getUTCDate)
* getDay (getUTCDay)
* getHours (getUTCHours)
* getMinutes (getUTCMinutes)
* getMonth (getUTCMonth)
* getSeconds (getUTCSeconds)
* getMilliseconds (getUTCMilliseconds)
* getTime
* getTimezoneOffset
* getYear
* getFullYear (getUTCFullYear)
* parse
* setDate (setUTCDate)
* setHours (setUTCHours)
* setMinutes (setUTCMinutes)
* setMonth (setUTCMonth)
* setSeconds (setUTCSeconds)
* setMilliseconds (setUTCMilliseconds)
* setTime
* setYear (setFullYear, setUTCFullYear)
* toGMTString (toUTCString)
* toLocaleString
* toString
*
*
* These Java methods are not supported
*
* setDay
* before
* after
* equals
* hashCode
*/
/*
* 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
* definition and reduce dependence on NSPR. NSPR is used to get the current
* time in milliseconds, the time zone offset, and the daylight savings time
* offset for a given time. NSPR is also used for Date.toLocaleString(), for
* locale-specific formatting, and to get a string representing the timezone.
* (Which turns out to be platform-dependent.)
*
* To do:
* (I did some performance tests by timing how long it took to run what
* I had of the js ECMA conformance tests.)
*
* - look at saving results across multiple calls to supporting
* functions; the toString functions compute some of the same values
* multiple times. Although - I took a quick stab at this, and I lost
* rather than gained. (Fractionally.) Hard to tell what compilers/processors
* are doing these days.
*
* - look at tweaking function return types to return double instead
* of int; this seems to make things run slightly faster sometimes.
* (though it could be architecture-dependent.) It'd be good to see
* how this does on win32. (Tried it on irix.) Types could use a
* general going-over.
*/
/*
* Supporting functions - ECMA 15.9.1.*
*/
#define HalfTimeDomain 8.64e15
#define HoursPerDay 24.0
#define MinutesPerDay (HoursPerDay * MinutesPerHour)
#define MinutesPerHour 60.0
#define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
#define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
#define SecondsPerMinute 60.0
#ifdef XP_PC
/* Work around msvc double optimization bug by making these runtime values; if
* they're available at compile time, msvc optimizes division by them by
* computing the reciprocal and multiplying instead of dividing - this loses
* when the reciprocal isn't representable in a double.
*/
static jsdouble msPerSecond = 1000.0;
static jsdouble msPerDay = SecondsPerDay * 1000.0;
static jsdouble msPerHour = SecondsPerHour * 1000.0;
static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
#else
#define msPerDay (SecondsPerDay * msPerSecond)
#define msPerHour (SecondsPerHour * msPerSecond)
#define msPerMinute (SecondsPerMinute * msPerSecond)
#define msPerSecond 1000.0
#endif
#define Day(t) floor((t) / msPerDay)
static jsdouble
TimeWithinDay(jsdouble t)
{
jsdouble result;
result = fmod(t, msPerDay);
if (result < 0)
result += msPerDay;
return result;
}
#define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
? 366 : 365)
/* math here has to be f.p, because we need
* floor((1968 - 1969) / 4) == -1
*/
#define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
- floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
#define TimeFromYear(y) (DayFromYear(y) * msPerDay)
static jsint
YearFromTime(jsdouble t)
{
jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
jsdouble t2 = (jsdouble) TimeFromYear(y);
if (t2 > t) {
y--;
} else {
if (t2 + msPerDay * DaysInYear(y) <= t)
y++;
}
return y;
}
#define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
#define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
/*
* The following array contains the day of year for the first day of
* each month, where index 0 is January, and day 0 is January 1.
*/
static jsdouble firstDayOfMonth[2][12] = {
{0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
{0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
};
#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
static intN
MonthFromTime(jsdouble t)
{
intN d, step;
jsint year = YearFromTime(t);
d = DayWithinYear(t, year);
if (d < (step = 31))
return 0;
step += (InLeapYear(t) ? 29 : 28);
if (d < step)
return 1;
if (d < (step += 31))
return 2;
if (d < (step += 30))
return 3;
if (d < (step += 31))
return 4;
if (d < (step += 30))
return 5;
if (d < (step += 31))
return 6;
if (d < (step += 31))
return 7;
if (d < (step += 30))
return 8;
if (d < (step += 31))
return 9;
if (d < (step += 30))
return 10;
return 11;
}
static intN
DateFromTime(jsdouble t)
{
intN d, step, next;
jsint year = YearFromTime(t);
d = DayWithinYear(t, year);
if (d <= (next = 30))
return d + 1;
step = next;
next += (InLeapYear(t) ? 29 : 28);
if (d <= next)
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 30))
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 30))
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 30))
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 30))
return d - step;
step = next;
return d - step;
}
static intN
WeekDay(jsdouble t)
{
jsint result;
result = (jsint) Day(t) + 4;
result = result % 7;
if (result < 0)
result += 7;
return (intN) result;
}
/* LocalTZA gets set by js_InitDateClass() */
static jsdouble LocalTZA;
static jsdouble
DaylightSavingTA(jsdouble t)
{
volatile int64 PR_t;
int64 ms2us;
int64 offset;
jsdouble result;
/* abort if NaN */
if (JSDOUBLE_IS_NaN(t))
return t;
/* put our t in an LL, and map it to usec for prtime */
JSLL_D2L(PR_t, t);
JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
JSLL_MUL(PR_t, PR_t, ms2us);
offset = PRMJ_DSTOffset(PR_t);
JSLL_DIV(offset, offset, ms2us);
JSLL_L2D(result, offset);
return result;
}
#define LocalTime(t) ((t) + LocalTZA + DaylightSavingTA(t))
static jsdouble
UTC(jsdouble t)
{
return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
}
static intN
HourFromTime(jsdouble t)
{
intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
if (result < 0)
result += (intN)HoursPerDay;
return result;
}
static intN
MinFromTime(jsdouble t)
{
intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
if (result < 0)
result += (intN)MinutesPerHour;
return result;
}
static intN
SecFromTime(jsdouble t)
{
intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
if (result < 0)
result += (intN)SecondsPerMinute;
return result;
}
static intN
msFromTime(jsdouble t)
{
intN result = (intN) fmod(t, msPerSecond);
if (result < 0)
result += (intN)msPerSecond;
return result;
}
#define MakeTime(hour, min, sec, ms) \
(((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms)
static jsdouble
MakeDay(jsdouble year, jsdouble month, jsdouble date)
{
jsdouble result;
JSBool leap;
jsdouble yearday;
jsdouble monthday;
year += floor(month / 12);
month = fmod(month, 12.0);
if (month < 0)
month += 12;
leap = (DaysInYear((jsint) year) == 366);
yearday = floor(TimeFromYear(year) / msPerDay);
monthday = DayFromMonth(month, leap);
result = yearday
+ monthday
+ date - 1;
return result;
}
#define MakeDate(day, time) (day * msPerDay + time)
#define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
&& !((d < 0 ? -d : d) > HalfTimeDomain)) \
? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
/**
* end of ECMA 'support' functions
*/
/*
* Other Support routines and definitions
*/
static JSClass date_class = {
js_Date_str,
JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
/* for use by date_parse */
static char* wtb[] = {
"am", "pm",
"monday", "tuesday", "wednesday", "thursday", "friday",
"saturday", "sunday",
"january", "february", "march", "april", "may", "june",
"july", "august", "september", "october", "november", "december",
"gmt", "ut", "utc",
"est", "edt",
"cst", "cdt",
"mst", "mdt",
"pst", "pdt"
/* time zone table needs to be expanded */
};
static int ttb[] = {
-1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
};
/* helper for date_parse */
static JSBool
date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
int count, int ignoreCase)
{
JSBool result = JS_FALSE;
/* return true if matches, otherwise, false */
while (count > 0 && s1[s1off] && s2[s2off]) {
if (ignoreCase) {
if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
break;
}
} else {
if ((jschar)s1[s1off] != s2[s2off]) {
break;
}
}
s1off++;
s2off++;
count--;
}
if (count == 0) {
result = JS_TRUE;
}
return result;
}
/* find UTC time from given date... no 1900 correction! */
static jsdouble
date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
jsdouble min, jsdouble sec, jsdouble msec)
{
jsdouble day;
jsdouble msec_time;
jsdouble result;
day = MakeDay(year, mon, mday);
msec_time = MakeTime(hour, min, sec, msec);
result = MakeDate(day, msec_time);
return result;
}
/*
* See ECMA 15.9.4.[3-10];
*/
/* XXX this function must be above date_parseString to avoid a
horrid bug in the Win16 1.52 compiler */
#define MAXARGS 7
static JSBool
date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble array[MAXARGS];
uintN loop;
jsdouble d;
for (loop = 0; loop < MAXARGS; loop++) {
if (loop < argc) {
if (!js_ValueToNumber(cx, argv[loop], &d))
return JS_FALSE;
/* return NaN if any arg is NaN */
if (!JSDOUBLE_IS_FINITE(d)) {
return js_NewNumberValue(cx, d, rval);
}
array[loop] = floor(d);
} else {
array[loop] = 0;
}
}
/* adjust 2-digit years into the 20th century */
if (array[0] >= 0 && array[0] <= 99)
array[0] += 1900;
/* if we got a 0 for 'date' (which is out of range)
* pretend it's a 1. (So Date.UTC(1972, 5) works) */
if (array[2] < 1)
array[2] = 1;
d = date_msecFromDate(array[0], array[1], array[2],
array[3], array[4], array[5], array[6]);
d = TIMECLIP(d);
return js_NewNumberValue(cx, d, rval);
}
static JSBool
date_parseString(const jschar *s, jsdouble *result)
{
jsdouble msec;
int year = -1;
int mon = -1;
int mday = -1;
int hour = -1;
int min = -1;
int sec = -1;
int c = -1;
int i = 0;
int n = -1;
jsdouble tzoffset = -1; /* was an int, overflowed on win16!!! */
int prevc = 0;
int limit = 0;
JSBool seenplusminus = JS_FALSE;
if (s == 0)
goto syntax;
limit = js_strlen(s);
while (i < limit) {
c = s[i];
i++;
if (c <= ' ' || c == ',' || c == '-') {
if (c == '-' && '0' <= s[i] && s[i] <= '9') {
prevc = c;
}
continue;
}
if (c == '(') { /* comments) */
int depth = 1;
while (i < limit) {
c = s[i];
i++;
if (c == '(') depth++;
else if (c == ')')
if (--depth <= 0)
break;
}
continue;
}
if ('0' <= c && c <= '9') {
n = c - '0';
while (i < limit && '0' <= (c = s[i]) && c <= '9') {
n = n * 10 + c - '0';
i++;
}
/* allow TZA before the year, so
* 'Wed Nov 05 21:49:11 GMT-0800 1997'
* works */
/* uses of seenplusminus allow : in TZA, so Java
* no-timezone style of GMT+4:30 works
*/
if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
/* make ':' case below change tzoffset */
seenplusminus = JS_TRUE;
/* offset */
if (n < 24)
n = n * 60; /* EG. "GMT-3" */
else
n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
if (prevc == '+') /* plus means east of GMT */
n = -n;
if (tzoffset != 0 && tzoffset != -1)
goto syntax;
tzoffset = n;
} else if (n >= 70 ||
(prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
if (year >= 0)
goto syntax;
else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
year = n < 100 ? n + 1900 : n;
else
goto syntax;
} else if (c == ':') {
if (hour < 0)
hour = /*byte*/ n;
else if (min < 0)
min = /*byte*/ n;
else
goto syntax;
} else if (c == '/') {
if (mon < 0)
mon = /*byte*/ n-1;
else if (mday < 0)
mday = /*byte*/ n;
else
goto syntax;
} else if (i < limit && c != ',' && c > ' ' && c != '-') {
goto syntax;
} else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
if (tzoffset < 0)
tzoffset -= n;
else
tzoffset += n;
} else if (hour >= 0 && min < 0) {
min = /*byte*/ n;
} else if (min >= 0 && sec < 0) {
sec = /*byte*/ n;
} else if (mday < 0) {
mday = /*byte*/ n;
} else {
goto syntax;
}
prevc = 0;
} else if (c == '/' || c == ':' || c == '+' || c == '-') {
prevc = c;
} else {
int st = i - 1;
int k;
while (i < limit) {
c = s[i];
if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
break;
i++;
}
if (i <= st + 1)
goto syntax;
for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
int action = ttb[k];
if (action != 0) {
if (action < 0) {
/*
* AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
* 12:30, instead of blindly adding 12 if PM.
*/
JS_ASSERT(action == -1 || action == -2);
if (hour > 12 || hour < 0) {
goto syntax;
} else {
if (action == -1 && hour == 12) { /* am */
hour = 0;
} else if (action == -2 && hour != 12) { /* pm */
hour += 12;
}
}
} else if (action <= 13) { /* month! */
if (mon < 0) {
mon = /*byte*/ (action - 2);
} else {
goto syntax;
}
} else {
tzoffset = action - 10000;
}
}
break;
}
if (k < 0)
goto syntax;
prevc = 0;
}
}
if (year < 0 || mon < 0 || mday < 0)
goto syntax;
if (sec < 0)
sec = 0;
if (min < 0)
min = 0;
if (hour < 0)
hour = 0;
if (tzoffset == -1) { /* no time zone specified, have to use local */
jsdouble msec_time;
msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
*result = UTC(msec_time);
return JS_TRUE;
}
msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
msec += tzoffset * msPerMinute;
*result = msec;
return JS_TRUE;
syntax:
/* syntax error */
*result = 0;
return JS_FALSE;
}
static JSBool
date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSString *str;
jsdouble result;
str = js_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
if (!date_parseString(str->chars, &result)) {
*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
return JS_TRUE;
}
result = TIMECLIP(result);
return js_NewNumberValue(cx, result, rval);
}
/*
* Check that obj is an object of class Date, and get the date value.
* Return NULL on failure.
*/
static jsdouble *
date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)
{
if (!JS_InstanceOf(cx, obj, &date_class, argv))
return NULL;
return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
}
/*
* See ECMA 15.9.5.4 thru 15.9.5.23
*/
static JSBool
date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
return js_NewNumberValue(cx, *date, rval);
}
static JSBool
date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = YearFromTime(LocalTime(result));
/*
* During the great date rewrite of 1.3, we tried to track the evolving ECMA
* standard, which then had a definition of getYear which always subtracted
* 1900. Which we implemented, not realizing that it was incompatible with
* the old behavior... now, rather than thrash the behavior yet again,
* we've decided to leave it with the - 1900 behavior and point people to
* the getFullYear method. But we try to protect existing scripts that
* have specified a version...
*/
if (cx->version == JSVERSION_1_0 ||
cx->version == JSVERSION_1_1 ||
cx->version == JSVERSION_1_2)
{
if (result >= 1900 && result < 2000)
result -= 1900;
} else {
result -= 1900;
}
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = YearFromTime(LocalTime(result));
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = YearFromTime(result);
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = MonthFromTime(LocalTime(result));
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = MonthFromTime(result);
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = LocalTime(result);
result = DateFromTime(result);
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = DateFromTime(result);
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = LocalTime(result);
result = WeekDay(result);
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = WeekDay(result);
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = HourFromTime(LocalTime(result));
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = HourFromTime(result);
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = MinFromTime(LocalTime(result));
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = MinFromTime(result);
return js_NewNumberValue(cx, result, rval);
}
/* Date.getSeconds is mapped to getUTCSeconds */
static JSBool
date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = SecFromTime(result);
return js_NewNumberValue(cx, result, rval);
}
/* Date.getMilliseconds is mapped to getUTCMilliseconds */
static JSBool
date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
result = msFromTime(result);
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
/*
* Return the time zone offset in minutes for the current locale
* that is appropriate for this time. This value would be a
* constant except for daylight savings time.
*/
result = (result - LocalTime(result)) / msPerMinute;
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
if (!js_ValueToNumber(cx, argv[0], &result))
return JS_FALSE;
result = TIMECLIP(result);
*date = result;
return js_NewNumberValue(cx, result, rval);
}
static JSBool
date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
uintN maxargs, JSBool local, jsval *rval)
{
uintN i;
jsdouble args[4], *argp, *stop;
jsdouble hour, min, sec, msec;
jsdouble lorutime; /* Local or UTC version of *date */
jsdouble msec_time;
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
/* just return NaN if the date is already NaN */
if (!JSDOUBLE_IS_FINITE(result))
return js_NewNumberValue(cx, result, rval);
/* Satisfy the ECMA rule that if a function is called with
* fewer arguments than the specified formal arguments, the
* remaining arguments are set to undefined. Seems like all
* the Date.setWhatever functions in ECMA are only varargs
* beyond the first argument; this should be set to undefined
* if it's not given. This means that "d = new Date();
* d.setMilliseconds()" returns NaN. Blech.
*/
if (argc == 0)
argc = 1; /* should be safe, because length of all settors is 1 */
else if (argc > maxargs)
argc = maxargs; /* clamp argc */
for (i = 0; i < argc; i++) {
if (!js_ValueToNumber(cx, argv[i], &args[i]))
return JS_FALSE;
if (!JSDOUBLE_IS_FINITE(args[i])) {
*date = *cx->runtime->jsNaN;
return js_NewNumberValue(cx, *date, rval);
}
args[i] = js_DoubleToInteger(args[i]);
}
if (local)
lorutime = LocalTime(result);
else
lorutime = result;
argp = args;
stop = argp + argc;
if (maxargs >= 4 && argp < stop)
hour = *argp++;
else
hour = HourFromTime(lorutime);
if (maxargs >= 3 && argp < stop)
min = *argp++;
else
min = MinFromTime(lorutime);
if (maxargs >= 2 && argp < stop)
sec = *argp++;
else
sec = SecFromTime(lorutime);
if (maxargs >= 1 && argp < stop)
msec = *argp;
else
msec = msFromTime(lorutime);
msec_time = MakeTime(hour, min, sec, msec);
result = MakeDate(Day(lorutime), msec_time);
/* fprintf(stderr, "%f\n", result); */
if (local)
result = UTC(result);
/* fprintf(stderr, "%f\n", result); */
*date = TIMECLIP(result);
return js_NewNumberValue(cx, *date, rval);
}
static JSBool
date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
}
static JSBool
date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
}
static JSBool
date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
}
static JSBool
date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
}
static JSBool
date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
}
static JSBool
date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
}
static JSBool
date_setHours(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
}
static JSBool
date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
}
static JSBool
date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, uintN maxargs, JSBool local, jsval *rval)
{
uintN i;
jsdouble lorutime; /* local or UTC version of *date */
jsdouble args[3], *argp, *stop;
jsdouble year, month, day;
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
/* see complaint about ECMA in date_MakeTime */
if (argc == 0)
argc = 1; /* should be safe, because length of all settors is 1 */
else if (argc > maxargs)
argc = maxargs; /* clamp argc */
for (i = 0; i < argc; i++) {
if (!js_ValueToNumber(cx, argv[i], &args[i]))
return JS_FALSE;
if (!JSDOUBLE_IS_FINITE(args[i])) {
*date = *cx->runtime->jsNaN;
return js_NewNumberValue(cx, *date, rval);
}
args[i] = js_DoubleToInteger(args[i]);
}
/* return NaN if date is NaN and we're not setting the year,
* If we are, use 0 as the time. */
if (!(JSDOUBLE_IS_FINITE(result))) {
if (argc < 3)
return js_NewNumberValue(cx, result, rval);
else
lorutime = +0.;
} else {
if (local)
lorutime = LocalTime(result);
else
lorutime = result;
}
argp = args;
stop = argp + argc;
if (maxargs >= 3 && argp < stop)
year = *argp++;
else
year = YearFromTime(lorutime);
if (maxargs >= 2 && argp < stop)
month = *argp++;
else
month = MonthFromTime(lorutime);
if (maxargs >= 1 && argp < stop)
day = *argp++;
else
day = DateFromTime(lorutime);
day = MakeDay(year, month, day); /* day within year */
result = MakeDate(day, TimeWithinDay(lorutime));
if (local)
result = UTC(result);
*date = TIMECLIP(result);
return js_NewNumberValue(cx, *date, rval);
}
static JSBool
date_setDate(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
}
static JSBool
date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
}
static JSBool
date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
}
static JSBool
date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
}
static JSBool
date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
}
static JSBool
date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
}
static JSBool
date_setYear(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
jsdouble t;
jsdouble year;
jsdouble day;
jsdouble result;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
result = *date;
if (!js_ValueToNumber(cx, argv[0], &year))
return JS_FALSE;
if (!JSDOUBLE_IS_FINITE(year)) {
*date = *cx->runtime->jsNaN;
return js_NewNumberValue(cx, *date, rval);
}
year = js_DoubleToInteger(year);
if (!JSDOUBLE_IS_FINITE(result)) {
t = +0.0;
} else {
t = LocalTime(result);
}
if (year >= 0 && year <= 99)
year += 1900;
day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
result = MakeDate(day, TimeWithinDay(t));
result = UTC(result);
*date = TIMECLIP(result);
return js_NewNumberValue(cx, *date, rval);
}
/* constants for toString, toUTCString */
static char js_NaN_date_str[] = "Invalid Date";
static const char* days[] =
{
"Sun","Mon","Tue","Wed","Thu","Fri","Sat"
};
static const char* months[] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static JSBool
date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
char buf[100];
JSString *str;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
if (!JSDOUBLE_IS_FINITE(*date)) {
JS_snprintf(buf, sizeof buf, js_NaN_date_str);
} else {
jsdouble temp = *date;
/* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
* requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
*/
JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
days[WeekDay(temp)],
DateFromTime(temp),
months[MonthFromTime(temp)],
YearFromTime(temp),
HourFromTime(temp),
MinFromTime(temp),
SecFromTime(temp));
}
str = JS_NewStringCopyZ(cx, buf);
if (!str)
return JS_FALSE;
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
/* for Date.toLocaleString; interface to PRMJTime date struct.
* If findEquivalent is true, then try to map the year to an equivalent year
* that's in range.
*/
static void
new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)
{
jsint year = YearFromTime(timeval);
int16 adjustedYear;
/* If the year doesn't fit in a PRMJTime, find something to do about it. */
if (year > 32767 || year < -32768) {
if (findEquivalent) {
/* We're really just trying to get a timezone string; map the year
* to some equivalent year in the range 0 to 2800. Borrowed from
* A. D. Olsen.
*/
jsint cycles;
#define CYCLE_YEARS 2800L
cycles = (year >= 0) ? year / CYCLE_YEARS
: -1 - (-1 - year) / CYCLE_YEARS;
adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
} else {
/* Clamp it to the nearest representable year. */
adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
}
} else {
adjustedYear = (int16)year;
}
split->tm_usec = (int32) msFromTime(timeval) * 1000;
split->tm_sec = (int8) SecFromTime(timeval);
split->tm_min = (int8) MinFromTime(timeval);
split->tm_hour = (int8) HourFromTime(timeval);
split->tm_mday = (int8) DateFromTime(timeval);
split->tm_mon = (int8) MonthFromTime(timeval);
split->tm_wday = (int8) WeekDay(timeval);
split->tm_year = (int16) adjustedYear;
split->tm_yday = (int16) DayWithinYear(timeval, year);
/* not sure how this affects things, but it doesn't seem
to matter. */
split->tm_isdst = (DaylightSavingTA(timeval) != 0);
}
typedef enum formatspec {
FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
} formatspec;
/* helper function */
static JSBool
date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
{
char buf[100];
JSString *str;
char tzbuf[100];
JSBool usetz;
size_t i, tzlen;
PRMJTime split;
if (!JSDOUBLE_IS_FINITE(date)) {
JS_snprintf(buf, sizeof buf, js_NaN_date_str);
} else {
jsdouble local = LocalTime(date);
/* offset from GMT in minutes. The offset includes daylight savings,
if it applies. */
jsint minutes = (jsint) floor((LocalTZA + DaylightSavingTA(date))
/ msPerMinute);
/* map 510 minutes to 0830 hours */
intN offset = (minutes / 60) * 100 + minutes % 60;
/* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
* printed as 'GMT-0800' rather than as 'PST' to avoid
* operating-system dependence on strftime (which
* PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
* PST as 'Pacific Standard Time.' This way we always know
* what we're getting, and can parse it if we produce it.
* The OS TZA string is included as a comment.
*/
/* get a timezone string from the OS to include as a
comment. */
new_explode(date, &split, JS_TRUE);
PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split);
/* Decide whether to use the resulting timezone string.
*
* Reject it if it contains any non-ASCII, non-alphanumeric characters.
* It's then likely in some other character encoding, and we probably
* won't display it correctly.
*/
usetz = JS_TRUE;
tzlen = strlen(tzbuf);
if (tzlen > 100) {
usetz = JS_FALSE;
} else {
for (i = 0; i < tzlen; i++) {
jschar c = tzbuf[i];
if (c > 127 ||
!(isalpha(c) || isdigit(c) ||
c == ' ' || c == '(' || c == ')')) {
usetz = JS_FALSE;
}
}
}
/* Also reject it if it's not parenthesized or if it's '()'. */
if (tzbuf[0] != '(' || tzbuf[1] == ')')
usetz = JS_FALSE;
switch (format) {
case FORMATSPEC_FULL:
/*
* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
* requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
*/
/* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */
JS_snprintf(buf, sizeof buf,
"%s %s %.2d %.2d:%.2d:%.2d GMT%+.4d %s%s%.4d",
days[WeekDay(local)],
months[MonthFromTime(local)],
DateFromTime(local),
HourFromTime(local),
MinFromTime(local),
SecFromTime(local),
offset,
usetz ? tzbuf : "",
usetz ? " " : "",
YearFromTime(local));
break;
case FORMATSPEC_DATE:
/* Tue Oct 31 2000 */
JS_snprintf(buf, sizeof buf,
"%s %s %.2d %.4d",
days[WeekDay(local)],
months[MonthFromTime(local)],
DateFromTime(local),
YearFromTime(local));
break;
case FORMATSPEC_TIME:
/* 09:41:40 GMT-0800 (PST) */
JS_snprintf(buf, sizeof buf,
"%.2d:%.2d:%.2d GMT%+.4d%s%s",
HourFromTime(local),
MinFromTime(local),
SecFromTime(local),
offset,
usetz ? " " : "",
usetz ? tzbuf : "");
break;
}
}
str = JS_NewStringCopyZ(cx, buf);
if (!str)
return JS_FALSE;
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
static JSBool
date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval, char *format)
{
char buf[100];
JSString *str;
PRMJTime split;
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
if (!JSDOUBLE_IS_FINITE(*date)) {
JS_snprintf(buf, sizeof buf, js_NaN_date_str);
} else {
intN result_len;
jsdouble local = LocalTime(*date);
new_explode(local, &split, JS_FALSE);
/* let PRMJTime format it. */
result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
/* If it failed, default to toString. */
if (result_len == 0)
return date_format(cx, *date, FORMATSPEC_FULL, rval);
/* Hacked check against undesired 2-digit year 00/00/00 form. */
if (buf[result_len - 3] == '/' &&
isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) {
JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
"%d", js_DateGetYear(cx, obj));
}
}
str = JS_NewStringCopyZ(cx, buf);
if (!str)
return JS_FALSE;
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
static JSBool
date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
/* Use '%#c' for windows, because '%c' is
* backward-compatible and non-y2k with msvc; '%#c' requests that a
* full year be used in the result string.
*/
return date_toLocaleHelper(cx, obj, argc, argv, rval,
#if defined(_WIN32) && !defined(__MWERKS__)
"%#c"
#else
"%c"
#endif
);
}
static JSBool
date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
/* Use '%#x' for windows, because '%x' is
* backward-compatible and non-y2k with msvc; '%#x' requests that a
* full year be used in the result string.
*/
return date_toLocaleHelper(cx, obj, argc, argv, rval,
#if defined(_WIN32) && !defined(__MWERKS__)
"%#x"
#else
"%x"
#endif
);
}
static JSBool
date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");
}
static JSBool
date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
return date_format(cx, *date, FORMATSPEC_TIME, rval);
}
static JSBool
date_toDateString(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
return date_format(cx, *date, FORMATSPEC_DATE, rval);
}
#if JS_HAS_TOSOURCE
#include <string.h>
#include "jsdtoa.h"
static JSBool
date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble *date;
char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
JSString *str;
date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);
if (!numStr) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr);
if (!bytes) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
str = JS_NewString(cx, bytes, strlen(bytes));
if (!str) {
free(bytes);
return JS_FALSE;
}
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
#endif
static JSBool
date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
jsdouble *date = date_getProlog(cx, obj, argv);
if (!date)
return JS_FALSE;
return date_format(cx, *date, FORMATSPEC_FULL, rval);
}
#if JS_HAS_VALUEOF_HINT
static JSBool
date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
/* It is an error to call date_valueOf on a non-date object, but we don't
* need to check for that explicitly here because every path calls
* date_getProlog, which does the check.
*/
/* If called directly with no arguments, convert to a time number. */
if (argc == 0)
return date_getTime(cx, obj, argc, argv, rval);
/* Convert to number only if the hint was given, otherwise favor string. */
if (argc == 1) {
JSString *str, *str2;
str = js_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
if (!js_CompareStrings(str, str2))
return date_getTime(cx, obj, argc, argv, rval);
}
return date_toString(cx, obj, argc, argv, rval);
}
#else
#define date_valueOf date_getTime
#endif
/*
* creation and destruction
*/
static JSFunctionSpec date_static_methods[] = {
{"UTC", date_UTC, MAXARGS,0,0 },
{"parse", date_parse, 1,0,0 },
{0,0,0,0,0}
};
static JSFunctionSpec date_methods[] = {
{"getTime", date_getTime, 0,0,0 },
{"getTimezoneOffset", date_getTimezoneOffset, 0,0,0 },
{"getYear", date_getYear, 0,0,0 },
{"getFullYear", date_getFullYear, 0,0,0 },
{"getUTCFullYear", date_getUTCFullYear, 0,0,0 },
{"getMonth", date_getMonth, 0,0,0 },
{"getUTCMonth", date_getUTCMonth, 0,0,0 },
{"getDate", date_getDate, 0,0,0 },
{"getUTCDate", date_getUTCDate, 0,0,0 },
{"getDay", date_getDay, 0,0,0 },
{"getUTCDay", date_getUTCDay, 0,0,0 },
{"getHours", date_getHours, 0,0,0 },
{"getUTCHours", date_getUTCHours, 0,0,0 },
{"getMinutes", date_getMinutes, 0,0,0 },
{"getUTCMinutes", date_getUTCMinutes, 0,0,0 },
{"getSeconds", date_getUTCSeconds, 0,0,0 },
{"getUTCSeconds", date_getUTCSeconds, 0,0,0 },
{"getMilliseconds", date_getUTCMilliseconds,0,0,0 },
{"getUTCMilliseconds", date_getUTCMilliseconds,0,0,0 },
{"setTime", date_setTime, 1,0,0 },
{"setYear", date_setYear, 1,0,0 },
{"setFullYear", date_setFullYear, 3,0,0 },
{"setUTCFullYear", date_setUTCFullYear, 3,0,0 },
{"setMonth", date_setMonth, 2,0,0 },
{"setUTCMonth", date_setUTCMonth, 2,0,0 },
{"setDate", date_setDate, 1,0,0 },
{"setUTCDate", date_setUTCDate, 1,0,0 },
{"setHours", date_setHours, 4,0,0 },
{"setUTCHours", date_setUTCHours, 4,0,0 },
{"setMinutes", date_setMinutes, 3,0,0 },
{"setUTCMinutes", date_setUTCMinutes, 3,0,0 },
{"setSeconds", date_setSeconds, 2,0,0 },
{"setUTCSeconds", date_setUTCSeconds, 2,0,0 },
{"setMilliseconds", date_setMilliseconds, 1,0,0 },
{"setUTCMilliseconds", date_setUTCMilliseconds,1,0,0 },
{"toUTCString", date_toGMTString, 0,0,0 },
{js_toLocaleString_str, date_toLocaleString, 0,0,0 },
{"toLocaleDateString", date_toLocaleDateString,0,0,0 },
{"toLocaleTimeString", date_toLocaleTimeString,0,0,0 },
{"toDateString", date_toDateString, 0,0,0 },
{"toTimeString", date_toTimeString, 0,0,0 },
#if JS_HAS_TOSOURCE
{js_toSource_str, date_toSource, 0,0,0 },
#endif
{js_toString_str, date_toString, 0,0,0 },
{js_valueOf_str, date_valueOf, 0,0,0 },
{0,0,0,0,0}
};
static jsdouble *
date_constructor(JSContext *cx, JSObject* obj)
{
jsdouble *date;
date = js_NewDouble(cx, 0.0);
if (!date)
return NULL;
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
return date;
}
static JSBool
Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble *date;
JSString *str;
jsdouble d;
/* Date called as function */
if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
int64 us, ms, us2ms;
jsdouble msec_time;
/* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
* so compute ms from PRMJ_Now.
*/
us = PRMJ_Now();
JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
JSLL_DIV(ms, us, us2ms);
JSLL_L2D(msec_time, ms);
return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
}
/* Date called as constructor */
if (argc == 0) {
int64 us, ms, us2ms;
jsdouble msec_time;
date = date_constructor(cx, obj);
if (!date)
return JS_FALSE;
us = PRMJ_Now();
JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
JSLL_DIV(ms, us, us2ms);
JSLL_L2D(msec_time, ms);
*date = msec_time;
} else if (argc == 1) {
if (!JSVAL_IS_STRING(argv[0])) {
/* the argument is a millisecond number */
if (!js_ValueToNumber(cx, argv[0], &d))
return JS_FALSE;
date = date_constructor(cx, obj);
if (!date)
return JS_FALSE;
*date = TIMECLIP(d);
} else {
/* the argument is a string; parse it. */
date = date_constructor(cx, obj);
if (!date)
return JS_FALSE;
str = js_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
if (!date_parseString(str->chars, date))
*date = *cx->runtime->jsNaN;
*date = TIMECLIP(*date);
}
} else {
jsdouble array[MAXARGS];
uintN loop;
jsdouble double_arg;
jsdouble day;
jsdouble msec_time;
for (loop = 0; loop < MAXARGS; loop++) {
if (loop < argc) {
if (!js_ValueToNumber(cx, argv[loop], &double_arg))
return JS_FALSE;
/* if any arg is NaN, make a NaN date object
and return */
if (!JSDOUBLE_IS_FINITE(double_arg)) {
date = date_constructor(cx, obj);
if (!date)
return JS_FALSE;
*date = *cx->runtime->jsNaN;
return JS_TRUE;
}
array[loop] = js_DoubleToInteger(double_arg);
} else {
if (loop == 2) {
array[loop] = 1; /* Default the date argument to 1. */
} else {
array[loop] = 0;
}
}
}
date = date_constructor(cx, obj);
if (!date)
return JS_FALSE;
/* adjust 2-digit years into the 20th century */
if (array[0] >= 0 && array[0] <= 99)
array[0] += 1900;
day = MakeDay(array[0], array[1], array[2]);
msec_time = MakeTime(array[3], array[4], array[5], array[6]);
msec_time = MakeDate(day, msec_time);
msec_time = UTC(msec_time);
*date = TIMECLIP(msec_time);
}
return JS_TRUE;
}
JSObject *
js_InitDateClass(JSContext *cx, JSObject *obj)
{
JSObject *proto;
jsdouble *proto_date;
/* set static LocalTZA */
LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS,
NULL, date_methods, NULL, date_static_methods);
if (!proto)
return NULL;
/* Alias toUTCString with toGMTString. (ECMA B.2.6) */
if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
return NULL;
/* Set the value of the Date.prototype date to NaN */
proto_date = date_constructor(cx, proto);
if (!proto_date)
return NULL;
*proto_date = *cx->runtime->jsNaN;
return proto;
}
JS_FRIEND_API(JSObject *)
js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
{
JSObject *obj;
jsdouble *date;
obj = js_NewObject(cx, &date_class, NULL, NULL);
if (!obj)
return NULL;
JS_DefineFunctions(cx, obj, date_methods);
date = date_constructor(cx, obj);
if (!date)
return NULL;
*date = msec_time;
return obj;
}
JS_FRIEND_API(JSObject *)
js_NewDateObject(JSContext* cx, int year, int mon, int mday,
int hour, int min, int sec)
{
JSObject *obj;
jsdouble msec_time;
msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
obj = js_NewDateObjectMsec(cx, UTC(msec_time));
return obj;
}
JS_FRIEND_API(JSBool)
js_DateIsValid(JSContext *cx, JSObject* obj)
{
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date || JSDOUBLE_IS_NaN(*date))
return JS_FALSE;
else
return JS_TRUE;
}
JS_FRIEND_API(int)
js_DateGetYear(JSContext *cx, JSObject* obj)
{
jsdouble *date = date_getProlog(cx, obj, NULL);
/* Preserve legacy API behavior of returning 0 for invalid dates. */
if (!date || JSDOUBLE_IS_NaN(*date))
return 0;
return (int) YearFromTime(LocalTime(*date));
}
JS_FRIEND_API(int)
js_DateGetMonth(JSContext *cx, JSObject* obj)
{
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date || JSDOUBLE_IS_NaN(*date))
return 0;
return (int) MonthFromTime(LocalTime(*date));
}
JS_FRIEND_API(int)
js_DateGetDate(JSContext *cx, JSObject* obj)
{
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date || JSDOUBLE_IS_NaN(*date))
return 0;
return (int) DateFromTime(LocalTime(*date));
}
JS_FRIEND_API(int)
js_DateGetHours(JSContext *cx, JSObject* obj)
{
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date || JSDOUBLE_IS_NaN(*date))
return 0;
return (int) HourFromTime(LocalTime(*date));
}
JS_FRIEND_API(int)
js_DateGetMinutes(JSContext *cx, JSObject* obj)
{
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date || JSDOUBLE_IS_NaN(*date))
return 0;
return (int) MinFromTime(LocalTime(*date));
}
JS_FRIEND_API(int)
js_DateGetSeconds(JSContext *cx, JSObject* obj)
{
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date || JSDOUBLE_IS_NaN(*date))
return 0;
return (int) SecFromTime(*date);
}
extern JS_FRIEND_API(void)
js_DateSetYear(JSContext *cx, JSObject *obj, int year)
{
jsdouble local;
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date)
return;
local = LocalTime(*date);
/* reset date if it was NaN */
if (JSDOUBLE_IS_NaN(local))
local = 0;
local = date_msecFromDate(year,
MonthFromTime(local),
DateFromTime(local),
HourFromTime(local),
MinFromTime(local),
SecFromTime(local),
msFromTime(local));
*date = UTC(local);
}
extern JS_FRIEND_API(void)
js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
{
jsdouble local;
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date)
return;
local = LocalTime(*date);
/* bail if date was NaN */
if (JSDOUBLE_IS_NaN(local))
return;
local = date_msecFromDate(YearFromTime(local),
month,
DateFromTime(local),
HourFromTime(local),
MinFromTime(local),
SecFromTime(local),
msFromTime(local));
*date = UTC(local);
}
extern JS_FRIEND_API(void)
js_DateSetDate(JSContext *cx, JSObject *obj, int date)
{
jsdouble local;
jsdouble *datep = date_getProlog(cx, obj, NULL);
if (!datep)
return;
local = LocalTime(*datep);
if (JSDOUBLE_IS_NaN(local))
return;
local = date_msecFromDate(YearFromTime(local),
MonthFromTime(local),
date,
HourFromTime(local),
MinFromTime(local),
SecFromTime(local),
msFromTime(local));
*datep = UTC(local);
}
extern JS_FRIEND_API(void)
js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
{
jsdouble local;
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date)
return;
local = LocalTime(*date);
if (JSDOUBLE_IS_NaN(local))
return;
local = date_msecFromDate(YearFromTime(local),
MonthFromTime(local),
DateFromTime(local),
hours,
MinFromTime(local),
SecFromTime(local),
msFromTime(local));
*date = UTC(local);
}
extern JS_FRIEND_API(void)
js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
{
jsdouble local;
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date)
return;
local = LocalTime(*date);
if (JSDOUBLE_IS_NaN(local))
return;
local = date_msecFromDate(YearFromTime(local),
MonthFromTime(local),
DateFromTime(local),
HourFromTime(local),
minutes,
SecFromTime(local),
msFromTime(local));
*date = UTC(local);
}
extern JS_FRIEND_API(void)
js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
{
jsdouble local;
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date)
return;
local = LocalTime(*date);
if (JSDOUBLE_IS_NaN(local))
return;
local = date_msecFromDate(YearFromTime(local),
MonthFromTime(local),
DateFromTime(local),
HourFromTime(local),
MinFromTime(local),
seconds,
msFromTime(local));
*date = UTC(local);
}
extern JS_FRIEND_API(jsdouble)
js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
{
jsdouble *date = date_getProlog(cx, obj, NULL);
if (!date || JSDOUBLE_IS_NaN(*date))
return 0;
return (*date);
}