/*
 * 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 or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 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.
 */
/* Header file */
#include <stdio.h>
#include <math.h>
#ifdef VX
#include <time.h>
#include <private/mathP.h>
#endif
#ifdef SUNOS5
#include <ieeefp.h>
#endif
#if defined(SUNOS5) || defined(TARGET_OS_LINUX)
#include <sys/timeb.h>
#endif
#include <ctype.h>
#include "js-objectadapter.h"
#include "js-scriptruntime.h"
#include "js-utils.h"
#include "js-exceptionhandler.h"
#include "js-nativeglobal.h"
#include "js-nativedate.h"

#if defined CONF_WPAD
#include "pfdateLib.h"
#if defined(HAKUGIN)
extern "C" {
#include "date_inic_pro.h"
}
#endif
#endif


/* Class data */
const jsString_t JSNativeDate::className = "Date";
const jsString_t JSNativeDate::js_NaN_date_str = "Invalid Date";
const jsString_t JSNativeDate::days[7] = {
	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
const jsString_t JSNativeDate::months[12] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
double JSNativeDate::LocalTZA;

/* Procedures */
void JSNativeDate::finishInit(JSContext * cx, JSScriptable * scope, JSFunctionObject * ctor, JSScriptable * proto)
{
	class DateAdpater:public JSObjectAdapter {
	  public:
#define defineDateFunction(name)                                        \
			static jsVal_t                                              \
				name(JSContext *cx, JSScriptable *thisObj, int numArgs, \
						jsVal_t *args, JSFunction *funObj) {            \
				JSScriptable *scriptable;                               \
																		\
				scriptable = getScriptable(cx, thisObj,                 \
								JSNativeDate::className, funObj);       \
																		\
				JSNativeDate *_thisObj = (JSNativeDate *) scriptable;   \
																		\
				double d = JSScriptRuntime::toNumber(cx, args[0]);      \
																		\
				return _thisObj->jsFunction_##name(cx, d);              \
			}

		JS_OA_DefineFunction(toString, JSNativeDate) JS_OA_DefineFunction(toLocaleString, JSNativeDate) JS_OA_DefineFunction(toUTCString, JSNativeDate) JS_OA_DefineFunction(valueOf, JSNativeDate) JS_OA_DefineFunction(getTime, JSNativeDate) JS_OA_DefineFunction(getYear, JSNativeDate) JS_OA_DefineFunction(getFullYear, JSNativeDate) JS_OA_DefineFunction(getUTCFullYear, JSNativeDate) JS_OA_DefineFunction(getMonth, JSNativeDate) JS_OA_DefineFunction(getUTCMonth, JSNativeDate) JS_OA_DefineFunction(getDate, JSNativeDate) JS_OA_DefineFunction(getUTCDate, JSNativeDate) JS_OA_DefineFunction(getDay, JSNativeDate) JS_OA_DefineFunction(getUTCDay, JSNativeDate) JS_OA_DefineFunction(getHours, JSNativeDate) JS_OA_DefineFunction(getUTCHours, JSNativeDate) JS_OA_DefineFunction(getMinutes, JSNativeDate) JS_OA_DefineFunction(getUTCMinutes, JSNativeDate) JS_OA_DefineFunction(getSeconds, JSNativeDate) JS_OA_DefineFunction(getUTCSeconds, JSNativeDate) JS_OA_DefineFunction(getMilliseconds, JSNativeDate) JS_OA_DefineFunction(getUTCMilliseconds, JSNativeDate) JS_OA_DefineFunction(getTimezoneOffset, JSNativeDate)
		 defineDateFunction(setYear) defineDateFunction(setTime)

		static jsVal_t parse(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj) {
			jsString_t s = JSScriptRuntime::toString(cx, args[0]);
			jsVal_t result = JSNativeDate::jsStaticFunction_parse(cx, s);

			JS_DeleteZoneObj(cx, s);
			return result;
	}};
	static jsFunction_t functions[] = {
#define Item(name, length)      { #name,  jsFunction_##name, length }
#define ItemAdapter(name, clazz, length)        \
								{ #name,  clazz::name, length }
		Item(setMilliseconds, -1),
		Item(setUTCMilliseconds, -1),
		Item(setSeconds, -1),
		Item(setUTCSeconds, -1),
		Item(setMinutes, -1),
		Item(setUTCMinutes, -1),
		Item(setHours, -1),
		Item(setUTCHours, -1),
		Item(setDate, -1),
		Item(setUTCDate, -1),
		Item(setMonth, -1),
		Item(setUTCMonth, -1),
		Item(setFullYear, -1),
		Item(setUTCFullYear, -1),

		ItemAdapter(toString, DateAdpater, 0),
		ItemAdapter(toLocaleString, DateAdpater, 0),
		ItemAdapter(toUTCString, DateAdpater, 0),
		ItemAdapter(valueOf, DateAdpater, 0),
		ItemAdapter(getTime, DateAdpater, 0),
		ItemAdapter(getYear, DateAdpater, 0),
		ItemAdapter(getFullYear, DateAdpater, 0),
		ItemAdapter(getUTCFullYear, DateAdpater, 0),
		ItemAdapter(getMonth, DateAdpater, 0),
		ItemAdapter(getUTCMonth, DateAdpater, 0),
		ItemAdapter(getDate, DateAdpater, 0),
		ItemAdapter(getUTCDate, DateAdpater, 0),
		ItemAdapter(getDay, DateAdpater, 0),
		ItemAdapter(getUTCDay, DateAdpater, 0),
		ItemAdapter(getHours, DateAdpater, 0),
		ItemAdapter(getUTCHours, DateAdpater, 0),
		ItemAdapter(getMinutes, DateAdpater, 0),
		ItemAdapter(getUTCMinutes, DateAdpater, 0),
		ItemAdapter(getSeconds, DateAdpater, 0),
		ItemAdapter(getUTCSeconds, DateAdpater, 0),
		ItemAdapter(getMilliseconds, DateAdpater, 0),
		ItemAdapter(getUTCMilliseconds, DateAdpater, 0),
		ItemAdapter(getTimezoneOffset, DateAdpater, 0),
		ItemAdapter(setTime, DateAdpater, 1),
		ItemAdapter(setYear, DateAdpater, 1),
#undef  ItemAdapter
#undef  Item
	};
	static jsFunction_t ctorFunctions[] = {
#define Item(name, length)      { #name,  jsStaticFunction_##name, length }
#define ItemAdapter(name, clazz, length)        \
								{ #name,  clazz::name, length }
		Item(UTC, -1), ItemAdapter(parse, DateAdpater, 1),
#undef  ItemAdapter
#undef  Item
	};

	((JSNativeDate *) proto)->defineFunctionProperties(cx, className, TableSize(functions), functions, JS_PA_DontEnum);
	ctor->defineFunctionProperties(cx, className, TableSize(ctorFunctions), ctorFunctions, JS_PA_DontEnum);

	jsVal_t val;

	(void)proto->get(cx, "toUTCString", proto, &val);
	((JSScriptableObject *) proto)->defineProperty(cx, "toGMTString", val, JS_PA_DontEnum);

	// Set the value of the prototype Date to NaN ('invalid date');
	((JSNativeDate *) proto)->date = JSScriptRuntime::NaN;

	static const struct {
		jsString_t name;
		short value;
	} specialLengths[] = {
		{
		"setSeconds", 2}, {
		"setUTCSeconds", 2}, {
		"setMinutes", 3}, {
		"setUTCMinutes", 3}, {
		"setHours", 4}, {
		"setUTCHours", 4}, {
		"setMonth", 2}, {
		"setUTCMonth", 2}, {
		"setFullYear", 4}, {
	"setUTCFullYear", 4},};

	for (int i = 0; i < (int)TableSize(specialLengths); ++i) {
		jsVal_t value;

		(void)proto->get(cx, specialLengths[i].name, proto, &value);
		((JSFunctionObject *) JS_VAL_TO_OBJECT(value))
				->setLength(specialLengths[i].value);
	}
}

double JSNativeDate::Day(double t)
{
	return floor(t / msPerDay);
}
double JSNativeDate::TimeWithinDay(double t)
{
	double result;

	result = fmod(t, msPerDay);
	if (result < 0) {
		result += msPerDay;
	}
	return result;
}
int JSNativeDate::DaysInYear(int y)
{
	if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) {
		return 366;
	}
	else {
		return 365;
	}
}
double JSNativeDate::DayFromYear(double y)
{
	/* math here has to be f.p, because we need
	 * floor((1968 - 1969) / 4) == -1
	 */
	return ((365.0 * ((y) - 1970.0) + floor(((y) - 1969.0) / 4.0)
					- floor(((y) - 1901) / 100.0) + floor(((y) - 1601.0) / 400.0)));
}
double JSNativeDate::TimeFromYear(double y)
{
	return DayFromYear(y) * msPerDay;
}
int JSNativeDate::YearFromTime(double t)
{
	int lo = (int)floor((t / msPerDay) / 366.0) + 1970;
	int hi = (int)floor((t / msPerDay) / 365.0) + 1970;
	int mid;

	/* above doesn't work for negative dates... */
	if (hi < lo) {
		int temp = lo;

		lo = hi;
		hi = temp;
	}

	/* Use a simple binary search algorithm to find the right
	   year.  This seems like brute force... but the computation
	   of hi and lo years above lands within one year of the
	   correct answer for years within a thousand years of
	   1970; the loop below only requires six iterations
	   for year 270000. */
	while (hi > lo) {
		mid = (hi + lo) / 2;
		if (TimeFromYear(mid) > t) {
			hi = mid - 1;
		}
		else {
			if (TimeFromYear(mid) <= t) {
				int temp = mid + 1;

				if (TimeFromYear(temp) > t) {
					return mid;
				}
				lo = mid + 1;
			}
		}
	}
	return lo;
}

jsBool_t JSNativeDate::InLeapYear(double t)
{
	return DaysInYear(YearFromTime(t)) == 366;
}
int JSNativeDate::DayWithinYear(double t)
{
	int year = YearFromTime(t);
	return (int)(Day(t) - DayFromYear(year));
}
double JSNativeDate::DayFromMonth(int m, jsBool_t leap)
{
	/*
	 * 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 double firstDay[] = {
		0.0, 31.0, 59.0, 90.0, 120.0, 151.0,
		181.0, 212.0, 243.0, 273.0, 304.0, 334.0
	};
	static double leapFirstDay[] = {
		0.0, 31.0, 60.0, 91.0, 121.0, 152.0,
		182.0, 213.0, 244.0, 274.0, 305.0, 335.0
	};

	if (leap) {
		return leapFirstDay[m];
	}
	else {
		return firstDay[m];
	}
}
int JSNativeDate::MonthFromTime(double t)
{
	int d, step;

	d = DayWithinYear(t);

	if (d < (step = 31)) {
		return 0;
	}

	// Originally coded as step += (InLeapYear(t) ? 29 : 28);
	// but some jits always returned 28!
	if (InLeapYear(t)) {
		step += 29;
	}
	else {
		step += 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;
}
int JSNativeDate::DateFromTime(double t)
{
	int d, step, next;

	d = DayWithinYear(t);
	if (d <= (next = 30)) {
		return d + 1;
	}
	step = next;

	// Originally coded as next += (InLeapYear(t) ? 29 : 28);
	// but some jits always returned 28!
	if (InLeapYear(t)) {
		next += 29;
	}
	else {
		next += 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;
}
int JSNativeDate::WeekDay(double t)
{
	double result;

	result = Day(t) + 4.0;
	result = fmod(result, 7.0);
	if (result < 0) {
		result += 7.0;
	}
	return (int)result;
}

double JSNativeDate::Now()
{
#if defined CONF_WPAD
	time_t	ltime;
	PfDateGetCurrent(&ltime);
	return msPerSecond * (double)ltime;
#elif defined VX
/*
	time_t ltime;

	time( &ltime );
	return (double) ltime * 1000.0;
*/
	struct timespec timespec;

	clock_gettime(CLOCK_REALTIME, &timespec);
	return ((double)timespec.tv_sec * 1000.0 + (double)timespec.tv_nsec / 1000000.0);
#else
	struct timeb timeb;

	ftime(&timeb);
	return (double)timeb.time * 1000.0 + (double)timeb.millitm;
#endif
}
double JSNativeDate::DaylightSavingTA(double t)
{
	return 0;   // not needed on unix
}
double JSNativeDate::LocalTime(double t)
{
	return t + LocalTZA;
}
double JSNativeDate::internalUTC(double t)
{
	return t - LocalTZA;
}
int JSNativeDate::HourFromTime(double t)
{
	double result;

	result = fmod(floor(t / msPerHour), HoursPerDay);
	if (result < 0) {
		result += HoursPerDay;
	}
	return (int)result;
}
int JSNativeDate::MinFromTime(double t)
{
	double result;

	result = fmod(floor(t / msPerMinute), MinutesPerHour);
	if (result < 0) {
		result += MinutesPerHour;
	}
	return (int)result;
}
int JSNativeDate::SecFromTime(double t)
{
	double result;

	result = fmod(floor(t / msPerSecond), SecondsPerMinute);
	if (result < 0) {
		result += SecondsPerMinute;
	}
	return (int)result;
}
int JSNativeDate::msFromTime(double t)
{
	double result;

	result = fmod(t, msPerSecond);
	if (result < 0) {
		result += msPerSecond;
	}
	return (int)result;
}
double JSNativeDate::MakeTime(double hour, double min, double sec, double ms)
{
	return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
			* msPerSecond + ms;
}
double JSNativeDate::MakeDay(double year, double month, double date)
{
	double result;
	jsBool_t leap;
	double yearday;
	double monthday;

	year += floor(month / 12.0);

	month = fmod(month, 12);
	if (month < 0) {
		month += 12;
	}

	leap = (DaysInYear((int)year) == 366);

	yearday = floor(TimeFromYear(year) / msPerDay);
	monthday = DayFromMonth((int)month, leap);

	result = yearday + monthday + date - 1;
	return result;
}
double JSNativeDate::MakeDate(double day, double time)
{
	return day * msPerDay + time;
}
double JSNativeDate::TimeClip(double d)
{
#ifdef VX
	if (isNan(d) || isInf(d) || fabs(d) > HalfTimeDomain) {
#else
	if (!finite(d) || fabs(d) > HalfTimeDomain) {
#endif
		return JSScriptRuntime::NaN;
	}
	if (d > 0.0) {
		return floor(d + 0.0);
	}
	else {
		return ceil(d + 0.0);
	}
}

/* find UTC time from given date... no 1900 correction! */
double JSNativeDate::date_msecFromDate(double year, double mon, double mday, double hour, double min, double sec, double msec)
{
	double day;
	double time;
	double result;

	day = MakeDay(year, mon, mday);
	time = MakeTime(hour, min, sec, msec);
	result = MakeDate(day, time);
	return result;
}

/* helper for date_parse */
jsBool_t JSNativeDate::date_regionMatches(jsString_t s1, int s1off, jsString_t s2, int s2off, int count)
{
	jsBool_t result = FALSE;
	/* return true if matches, otherwise, FALSE */
	int s1len = strlen(s1);
	int s2len = strlen(s2);

	while (count > 0 && s1off < s1len && s2off < s2len) {
		if (tolower(s1[s1off]) != tolower(s2[s2off])) {
			break;
		}
		s1off++;
		s2off++;
		count--;
	}

	if (count == 0) {
		result = TRUE;
	}
	return result;
}

double JSNativeDate::date_parseString(jsString_t s)
{
	/* replace this with byte arrays?  Cheaper? */
	static jsString_t 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,    /* UT/UTC */
		10000 + 5 * 60, 10000 + 4 * 60, /* EDT */
		10000 + 6 * 60, 10000 + 5 * 60,
		10000 + 7 * 60, 10000 + 6 * 60,
		10000 + 8 * 60, 10000 + 7 * 60
	};
	double msec;

	int year = -1;
	int mon = -1;
	int mday = -1;
	int hour = -1;
	int min = -1;
	int sec = -1;
	jsChar_t c = 0;
	jsChar_t si = 0;
	int i = 0;
	int n = -1;
	double tzoffset = -1;
	jsChar_t prevc = 0;
	int limit = 0;
	jsBool_t seenplusminus = FALSE;

	if (s == NULL) {    // ??? Will s be null?
		return JSScriptRuntime::NaN;
	}
	limit = strlen(s);
	while (i < limit) {
		c = s[i];
		i++;
		if (c <= ' ' || c == ',' || c == '-') {
			if (i < limit) {
				si = s[i];
				if (c == '-' && '0' <= si && si <= '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 = 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) {
					return JSScriptRuntime::NaN;
				}
				tzoffset = n;
			}
			else if (n >= 70 || (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
				if (year >= 0) {
					return JSScriptRuntime::NaN;
				}
				else if (c <= ' ' || c == ',' || c == '/' || i >= limit) {
					year = n < 100 ? n + 1900 : n;
				}
				else {
					return JSScriptRuntime::NaN;
				}
			}
			else if (c == ':') {
				if (hour < 0) {
					hour = /*byte */ n;
				}
				else if (min < 0) {
					min = /*byte */ n;
				}
				else {
					return JSScriptRuntime::NaN;
				}
			}
			else if (c == '/') {
				if (mon < 0) {
					mon = /*byte */ n - 1;
				}
				else if (mday < 0) {
					mday = /*byte */ n;
				}
				else {
					return JSScriptRuntime::NaN;
				}
			}
			else if (c == '/') {
			}
			else if (i < limit && c != ',' && c > ' ' && c != '-') {
				return JSScriptRuntime::NaN;
			}
			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 {
				return JSScriptRuntime::NaN;
			}
			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) {
				return JSScriptRuntime::NaN;
			}
			for (k = TableSize(wtb); --k >= 0;)
				if (date_regionMatches(wtb[k], 0, s, st, i - st)) {
					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.
							 */
							if (hour > 12 || hour < 0) {
								return JSScriptRuntime::NaN;
							}
							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 {
								return JSScriptRuntime::NaN;
							}
						}
						else {
							tzoffset = action - 10000;
						}
					}
					break;
				}
			if (k < 0) {
				return JSScriptRuntime::NaN;
			}
			prevc = 0;
		}
	}
	if (year < 0 || mon < 0 || mday < 0) {
		return JSScriptRuntime::NaN;
	}
	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 */
		double time;
		time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
		return internalUTC(time);
	}

	msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
	msec += tzoffset * msPerMinute;
	return msec;
}

jsVal_t JSNativeDate::date_format(JSContext * cx, double t)
{
	double local = LocalTime(t);
	jsChar_t buf[128], offsetStr[16], yearStr[8];

	/* offset from GMT in minutes.  The offset includes daylight savings,
	   if it applies. */
	int minutes = (int)floor(LocalTZA / msPerMinute);
	/* map 510 minutes to 0830 hours */
	int offset = (minutes / 60) * 100 + minutes % 60;
	int year = YearFromTime(local);

	sprintf(offsetStr, offset > 0 ? "GMT+%04d" : "GMT-%04d", offset);
	sprintf(yearStr, year > 0 ? "%04d" : "%+05d", year);
#ifdef VX
	sprintf(buf, "%s %s %02d %02d:%02d:%02d %s %s\n", days[WeekDay(local)], months[MonthFromTime(local)], DateFromTime(local), HourFromTime(local), MinFromTime(local), SecFromTime(local), offsetStr, yearStr);
#else
	sprintf(buf, "%s %s %02d %02d:%02d:%02d %s (%s) %s\n", days[WeekDay(local)], months[MonthFromTime(local)], DateFromTime(local), HourFromTime(local), MinFromTime(local), SecFromTime(local), offsetStr, tzname[0], yearStr);
#endif

	return JS_STRING_TO_VAL(JS_NewGCString(cx, buf));
}

JSNativeDate *JSNativeDate::checkInstance(JSContext * cx, JSScriptable * obj, JSFunction * funObj)
{
	if (obj == NULL || !obj->isInstanceOf(JSNativeDate::className)) {
		jsString_t msg = JSContext::getMessage(cx, "msg.incompat.call %s",
				((JSNativeFunction *) funObj)->names[0]);

		cx->exceptionHandler->throwEx(JSNativeGlobal::constructError(cx, "TypeError", msg, (JSNativeFunction *) funObj));
	}
	return (JSNativeDate *) obj;
}
double JSNativeDate::makeTime(JSContext * cx, JSScriptable * dateObj, int numArgs, jsVal_t * args, int maxargs, jsBool_t local, JSFunction * funObj)
{
	int i;
	double conv[4];
	double hour, min, sec, msec;
	double lorutime;    /* Local or UTC version of date */

	double time;
	double result;

	JSNativeDate *d = checkInstance(cx, dateObj, funObj);
	double date = d->date;

	/* just return NaN if the date is already NaN */
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return date;
	}

	/* 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.
	 */
	jsVal_t *padArgs;

	if (numArgs == 0) {
		args = padArgs = JSScriptRuntime::padArguments(cx, numArgs, args, 1);
		numArgs = 1;
	}
	else {
		padArgs = NULL;
	}

	for (i = 0; i < numArgs && i < maxargs; i++) {
		conv[i] = JSScriptRuntime::toNumber(cx, args[i]);

		// limit checks that happen in MakeTime in ECMA.
#ifdef VX
		if (isNan(conv[i]) || isInf(conv[i])) {
#else
		if (!finite(conv[i])) {
#endif
			d->date = JSScriptRuntime::NaN;
			if (padArgs != NULL) {
				JS_DeleteZoneObj(cx, padArgs);
			}
			return d->date;
		}
		conv[i] = JSScriptRuntime::toInteger(conv[i]);
	}

	if (local) {
		lorutime = LocalTime(date);
	}
	else {
		lorutime = date;
	}

	i = 0;
	int stop = numArgs;

	if (maxargs >= 4 && i < stop) {
		hour = conv[i++];
	}
	else {
		hour = HourFromTime(lorutime);
	}

	if (maxargs >= 3 && i < stop) {
		min = conv[i++];
	}
	else {
		min = MinFromTime(lorutime);
	}

	if (maxargs >= 2 && i < stop) {
		sec = conv[i++];
	}
	else {
		sec = SecFromTime(lorutime);
	}

	if (maxargs >= 1 && i < stop) {
		msec = conv[i++];
	}
	else {
		msec = msFromTime(lorutime);
	}

	time = MakeTime(hour, min, sec, msec);
	result = MakeDate(Day(lorutime), time);

	if (local) {
		result = internalUTC(result);
	}
	date = TimeClip(result);

	d->date = date;

	if (padArgs != NULL) {
		JS_DeleteZoneObj(cx, padArgs);
	}
	return date;
}
double JSNativeDate::makeDate(JSContext * cx, JSScriptable * dateObj, int numArgs, jsVal_t * args, int maxargs, jsBool_t local, JSFunction * funObj)
{
	int i;
	double conv[3];
	double year, month, day;
	double lorutime;    /* local or UTC version of date */
	double result;

	JSNativeDate *d = checkInstance(cx, dateObj, funObj);
	double date = d->date;

	/* See arg padding comment in makeTime. */
	jsVal_t *padArgs;

	if (numArgs == 0) {
		args = padArgs = JSScriptRuntime::padArguments(cx, numArgs, args, 1);
		numArgs = 1;
	}
	else {
		padArgs = NULL;
	}

	for (i = 0; i < numArgs && i < maxargs; i++) {
		conv[i] = JSScriptRuntime::toNumber(cx, args[i]);

		// limit checks that happen in MakeDate in ECMA.
#ifdef VX
		if (isNan(conv[i]) || isInf(conv[i])) {
#else
		if (!finite(conv[i])) {
#endif
			d->date = JSScriptRuntime::NaN;
			if (padArgs != NULL) {
				JS_DeleteZoneObj(cx, padArgs);
			}
			return d->date;
		}
		conv[i] = JSScriptRuntime::toInteger(conv[i]);
	}

	/* return NaN if date is NaN and we're not setting the year,
	 * If we are, use 0 as the time. */
	if (date != date) {
		if (numArgs < 3) {
			if (padArgs != NULL) {
				JS_DeleteZoneObj(cx, padArgs);
			}
			return JSScriptRuntime::NaN;
		}
		else {
			lorutime = 0;
		}
	}
	else {
		if (local)
			lorutime = LocalTime(date);
		else
			lorutime = date;
	}

	i = 0;
	int stop = numArgs;

	if (maxargs >= 3 && i < stop) {
		year = conv[i++];
	}
	else {
		year = YearFromTime(lorutime);
	}

	if (maxargs >= 2 && i < stop) {
		month = conv[i++];
	}
	else {
		month = MonthFromTime(lorutime);
	}

	if (maxargs >= 1 && i < stop) {
		day = conv[i++];
	}
	else {
		day = DateFromTime(lorutime);
	}

	day = MakeDay(year, month, day);    /* day within year */
	result = MakeDate(day, TimeWithinDay(lorutime));

	if (local) {
		result = internalUTC(result);
	}

	date = TimeClip(result);

	d->date = date;
	if (padArgs != NULL) {
		JS_DeleteZoneObj(cx, padArgs);
	}
	return date;
}

jsVal_t JSNativeDate::jsConstructor(JSContext * cx, int numArgs, jsVal_t * args, JSFunction * ctorObj, jsBool_t inNewExpr)
{
	// if called as a function, just return a string
	// representing the current time.
	if (!inNewExpr) {
		return date_format(cx, Now());
	}

	JSNativeDate *obj = new(cx) JSNativeDate();

	// if called as a constructor with no args,
	// return a new Date with the current time.
	if (numArgs == 0) {
		obj->date = Now();
		return JS_OBJECT_TO_VAL(obj);
	}

	// if called with just one arg -
	if (numArgs == 1) {
		double date;

		// if it's not a string, use it as a millisecond date
		if (!JS_VAL_IS_STRING(args[0])) {
			date = JSScriptRuntime::toNumber(cx, args[0]);
		}
		else {
			// it's a string; parse it.
			jsString_t str = JS_VAL_TO_STRING(args[0]);

			date = date_parseString(str);
		}
		obj->date = TimeClip(date);
		return JS_OBJECT_TO_VAL(obj);
	}

	// multiple arguments; year, month, day etc.
	double *array = (double *)JS_NewZoneObj(cx, sizeof(*array) * MAXARGS);
	int loop;
	double d;

	for (loop = 0; loop < MAXARGS; loop++) {
		if (loop < numArgs) {
			d = JSScriptRuntime::toNumber(cx, args[loop]);

#ifdef VX
			if (isNan(d) || isInf(d)) {
#else
			if (!finite(d)) {
#endif
				obj->date = JSScriptRuntime::NaN;
				return JS_OBJECT_TO_VAL(obj);
			}
			array[loop] = JSScriptRuntime::toInteger(cx, args[loop]);
		}
		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 */
	if (array[2] < 1) {
		array[2] = 1;
	}

	double day = MakeDay(array[0], array[1], array[2]);
	double time = MakeTime(array[3], array[4], array[5], array[6]);

	time = MakeDate(day, time);
	time = internalUTC(time);
	obj->date = TimeClip(time);

	JS_DeleteZoneObj(cx, array);
	return JS_OBJECT_TO_VAL(obj);
}

jsVal_t JSNativeDate::jsFunction_setMilliseconds(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeTime(cx, thisObj, numArgs, args, 1, TRUE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setUTCMilliseconds(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeTime(cx, thisObj, numArgs, args, 1, FALSE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setSeconds(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeTime(cx, thisObj, numArgs, args, 2, TRUE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setUTCSeconds(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeTime(cx, thisObj, numArgs, args, 2, FALSE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setMinutes(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeTime(cx, thisObj, numArgs, args, 3, TRUE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setUTCMinutes(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeTime(cx, thisObj, numArgs, args, 3, FALSE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setHours(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeTime(cx, thisObj, numArgs, args, 4, TRUE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setUTCHours(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeTime(cx, thisObj, numArgs, args, 4, FALSE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setDate(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeDate(cx, thisObj, numArgs, args, 1, TRUE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setUTCDate(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeDate(cx, thisObj, numArgs, args, 1, FALSE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setMonth(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeDate(cx, thisObj, numArgs, args, 2, TRUE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setUTCMonth(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeDate(cx, thisObj, numArgs, args, 2, FALSE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setFullYear(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeDate(cx, thisObj, numArgs, args, 3, TRUE, funObj)));
}

jsVal_t JSNativeDate::jsFunction_setUTCFullYear(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, makeDate(cx, thisObj, numArgs, args, 3, FALSE, funObj)));
}

jsVal_t JSNativeDate::jsStaticFunction_UTC(JSContext * cx, JSScriptable * thisObj, int numArgs, jsVal_t * args, JSFunction * funObj)
{
	double array[MAXARGS];
	int loop;
	double d;

	for (loop = 0; loop < MAXARGS; loop++) {
		if (loop < numArgs) {
			d = JSScriptRuntime::toNumber(cx, args[loop]);
#ifdef VX
			if (isNan(d) || isInf(d)) {
#else
			if (!finite(d)) {
#endif
				return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, JSScriptRuntime::NaN));
			}
			array[loop] = JSScriptRuntime::toInteger(cx, args[loop]);
		}
		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_DOUBLE_TO_VAL(JS_NewGCDouble(cx, d));
}

void JSNativeDate::defineClass(JSContext * cx, JSScriptable * scope)
{
	JSScriptable *proto = new(cx) JSNativeDate();

	JSScriptableObject::defineClass(cx, scope, proto, jsConstructor, finishInit);
}

jsVal_t JSNativeDate::jsStaticFunction_parse(JSContext * cx, jsString_t s)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date_parseString(s)));
}

jsVal_t JSNativeDate::jsFunction_toString(JSContext * cx)
{
	/* all for want of printf.  All of this garbage seems necessary
	 * because Java carefully avoids providing non-localized
	 * string formatting.  We need to avoid localization to ensure
	 * that we generate parseable output.
	 */
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_STRING_TO_VAL(JS_NewGCString(cx, js_NaN_date_str));
	}

	return date_format(cx, date);
}

jsVal_t JSNativeDate::jsFunction_toLocaleString(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_STRING_TO_VAL(JS_NewGCString(cx, js_NaN_date_str));
	}

	// the string this returns doesn't have much resemblance
	// to what jsdate.c returns... does this matter?
#if defined CONF_WPAD
	jsChar_t	buf[64];
	size_t		buf_len = sizeof buf;

	time_t		lt = (time_t)(date / msPerMinute);
	jsString_t	result = ctime_r(&lt, buf, &buf_len);
#else
	time_t lt = (time_t) (date / 1000.0);
	jsString_t result = (jsString_t) ctime(&lt);
#endif

	return JS_STRING_TO_VAL(JS_NewGCString(cx, result));
}

jsVal_t JSNativeDate::jsFunction_toUTCString(JSContext * cx)
{
#ifdef VX
	if (isNan(this->date)) {
#else
	if (isnan(this->date)) {
#endif
		return JS_STRING_TO_VAL(JS_NewGCString(cx, js_NaN_date_str));
	}

	jsChar_t buf[64], yearStr[8];
	int year = YearFromTime(this->date);

	sprintf(yearStr, year > 0 ? "%04d" : "%+05d", year);
	sprintf(buf, "%s, %02d %s %s %02d:%02d:%02d GMT", days[WeekDay(date)], DateFromTime(date), months[MonthFromTime(date)], yearStr, HourFromTime(date), MinFromTime(date), SecFromTime(date));

	return JS_STRING_TO_VAL(JS_NewGCString(cx, buf));
}

jsVal_t JSNativeDate::jsFunction_valueOf(JSContext * cx)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
}

jsVal_t JSNativeDate::jsFunction_getTime(JSContext * cx)
{
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
}

jsVal_t JSNativeDate::jsFunction_getYear(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}

	int result = YearFromTime(LocalTime(date));

	/*
	 * 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 defined CONF_WPAD
	result %= 100;
#else
	int version = cx->getLanguageVersion();
	if (version == JSContext::VERSION_1_0 || version == JSContext::VERSION_1_1 || version == JSContext::VERSION_1_2) {
		if (result >= 1900 && result < 2000) {
			result -= 1900;
		}
	}
	else {
		result -= 1900;
	}
#endif
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)result));
}

jsVal_t JSNativeDate::jsFunction_getFullYear(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)YearFromTime(LocalTime(date))));
}

jsVal_t JSNativeDate::jsFunction_getUTCFullYear(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)YearFromTime(date)));
}

jsVal_t JSNativeDate::jsFunction_getMonth(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)MonthFromTime(LocalTime(date))));
}

jsVal_t JSNativeDate::jsFunction_getUTCMonth(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)MonthFromTime(date)));
}

jsVal_t JSNativeDate::jsFunction_getDate(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)DateFromTime(LocalTime(date))));
}

jsVal_t JSNativeDate::jsFunction_getUTCDate(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)DateFromTime(date)));
}

jsVal_t JSNativeDate::jsFunction_getDay(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)WeekDay(LocalTime(date))));
}

jsVal_t JSNativeDate::jsFunction_getUTCDay(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)WeekDay(date)));
}

jsVal_t JSNativeDate::jsFunction_getHours(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)HourFromTime(LocalTime(date))));
}

jsVal_t JSNativeDate::jsFunction_getUTCHours(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)HourFromTime(date)));
}

jsVal_t JSNativeDate::jsFunction_getMinutes(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)MinFromTime(LocalTime(date))));
}

jsVal_t JSNativeDate::jsFunction_getUTCMinutes(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)MinFromTime(date)));
}

jsVal_t JSNativeDate::jsFunction_getSeconds(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)SecFromTime(LocalTime(date))));
}

jsVal_t JSNativeDate::jsFunction_getUTCSeconds(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)SecFromTime(date)));
}

jsVal_t JSNativeDate::jsFunction_getMilliseconds(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)msFromTime(LocalTime(date))));
}

jsVal_t JSNativeDate::jsFunction_getUTCMilliseconds(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)msFromTime(date)));
}

jsVal_t JSNativeDate::jsFunction_getTimezoneOffset(JSContext * cx)
{
#ifdef VX
	if (isNan(date)) {
#else
	if (isnan(date)) {
#endif
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
	}
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double)((date - LocalTime(date)) / msPerMinute)));
}
jsVal_t JSNativeDate::jsFunction_setTime(JSContext * cx, double time)
{
	date = TimeClip(time);
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, date));
}

jsVal_t JSNativeDate::jsFunction_setYear(JSContext * cx, double year)
{
	double day, result;

#ifdef VX
	if (isNan(year) || isInf(year)) {
#else
	if (!finite(year)) {
#endif
		this->date = JSScriptRuntime::NaN;
		return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, this->date));
	}

#ifdef VX
	if (isNan(this->date)) {
#else
	if (isnan(this->date)) {
#endif
		this->date = 0.0;
	}

	if (year >= 0 && year <= 99) {
		year += 1900;
	}

	day = MakeDay(year, MonthFromTime(this->date), DateFromTime(this->date));
	result = MakeDate(day, TimeWithinDay(this->date));
	result = internalUTC(result);

	this->date = TimeClip(result);
	return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, this->date));
}

jsBool_t JSNativeDate::isInstanceOf(const jsString_t className)
{
	if (className == JSNativeDate::className)
		return TRUE;
	return JSScriptableObject::isInstanceOf(className);
}

jsString_t JSNativeDate::getClassName(JSContext * cx)
{
	return JSNativeDate::className;
}

jsVal_t JSNativeDate::getDefaultValue(JSContext * cx, jsType_t typeHint)
{
	if (typeHint == JS_T_Null) {
		typeHint = JS_T_String;
	}
	return JSScriptableObject::getDefaultValue(cx, typeHint);
}

JSNativeDate::JSNativeDate()
{
	date = 0.0;
}

void JSNativeDate::classInitialize()
{
#if defined CONF_WPAD
	LocalTZA = msPerMinute * (double)PfDateGetGMTDiff(NULL);
#else
	time_t ltime = 0;

	LocalTZA = 1000.0 * (mktime(localtime(&ltime)) - mktime(gmtime(&ltime)));
#endif
}
