summaryrefslogtreecommitdiffstats
path: root/kjs/date_object.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kjs/date_object.cpp
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kjs/date_object.cpp')
-rw-r--r--kjs/date_object.cpp1214
1 files changed, 1214 insertions, 0 deletions
diff --git a/kjs/date_object.cpp b/kjs/date_object.cpp
new file mode 100644
index 000000000..26635611d
--- /dev/null
+++ b/kjs/date_object.cpp
@@ -0,0 +1,1214 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2005 Harri Porten (porten@kde.org)
+ * Copyright (C) 2004 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+# include <time.h>
+# endif
+#endif
+#ifdef HAVE_SYS_TIMEB_H
+#include <sys/timeb.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif // HAVE_SYS_PARAM_H
+
+#include <math.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "date_object.h"
+#include "error_object.h"
+#include "operations.h"
+
+#include "date_object.lut.h"
+
+#ifdef _MSC_VER
+# define strncasecmp(a,b,c) _strnicmp(a,b,c)
+#endif
+
+using namespace KJS;
+
+// come constants
+const time_t invalidDate = LONG_MIN;
+const double hoursPerDay = 24;
+const double minutesPerHour = 60;
+const double secondsPerMinute = 60;
+const double msPerSecond = 1000;
+const double msPerMinute = msPerSecond * secondsPerMinute;
+const double msPerHour = msPerMinute * minutesPerHour;
+const double msPerDay = msPerHour * hoursPerDay;
+static const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
+static const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+static UString formatDate(struct tm &tm)
+{
+ char buffer[100];
+ snprintf(buffer, sizeof(buffer), "%s %s %02d %04d",
+ weekdayName[(tm.tm_wday + 6) % 7],
+ monthName[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
+ return buffer;
+}
+
+static UString formatDateUTCVariant(struct tm &tm)
+{
+ char buffer[100];
+ snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d",
+ weekdayName[(tm.tm_wday + 6) % 7],
+ tm.tm_mday, monthName[tm.tm_mon], tm.tm_year + 1900);
+ return buffer;
+}
+
+static UString formatTime(struct tm &tm)
+{
+ int tz;
+ char buffer[100];
+#if defined BSD || defined(__linux__) || defined(__APPLE__)
+ tz = tm.tm_gmtoff;
+#else
+# if defined(__BORLANDC__) || defined (__CYGWIN__)
+ tz = - _timezone;
+# else
+ tz = - timezone;
+# endif
+#endif
+ if (tz == 0) {
+ snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", tm.tm_hour, tm.tm_min, tm.tm_sec);
+ } else {
+ int offset = tz;
+ if (offset < 0) {
+ offset = -offset;
+ }
+ snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d",
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tz < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
+ }
+ return UString(buffer);
+}
+
+static int day(double t)
+{
+ return int(floor(t / msPerDay));
+}
+
+static double dayFromYear(int year)
+{
+ return 365.0 * (year - 1970)
+ + floor((year - 1969) / 4.0)
+ - floor((year - 1901) / 100.0)
+ + floor((year - 1601) / 400.0);
+}
+
+// depending on whether it's a leap year or not
+static int daysInYear(int year)
+{
+ if (year % 4 != 0)
+ return 365;
+ else if (year % 400 == 0)
+ return 366;
+ else if (year % 100 == 0)
+ return 365;
+ else
+ return 366;
+}
+
+// time value of the start of a year
+double timeFromYear(int year)
+{
+ return msPerDay * dayFromYear(year);
+}
+
+// year determined by time value
+int yearFromTime(double t)
+{
+ // ### there must be an easier way
+ // initial guess
+ int y = 1970 + int(t / (365.25 * msPerDay));
+ // adjustment
+ if (timeFromYear(y) > t) {
+ do {
+ --y;
+ } while (timeFromYear(y) > t);
+ } else {
+ while (timeFromYear(y + 1) < t)
+ ++y;
+ }
+
+ return y;
+}
+
+// 0: Sunday, 1: Monday, etc.
+int weekDay(double t)
+{
+ int wd = (day(t) + 4) % 7;
+ if (wd < 0)
+ wd += 7;
+ return wd;
+}
+
+static double timeZoneOffset(const struct tm *t)
+{
+#if defined BSD || defined(__linux__) || defined(__APPLE__)
+ return -(t->tm_gmtoff / 60);
+#else
+# if defined(__BORLANDC__) || defined(__CYGWIN__)
+// FIXME consider non one-hour DST change
+#if !defined(__CYGWIN__)
+#error please add daylight savings offset here!
+#endif
+ return _timezone / 60 - (t->tm_isdst > 0 ? 60 : 0);
+# else
+ return timezone / 60 - (t->tm_isdst > 0 ? 60 : 0 );
+# endif
+#endif
+}
+
+// Converts a list of arguments sent to a Date member function into milliseconds, updating
+// ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
+//
+// Format of member function: f([hour,] [min,] [sec,] [ms])
+static void fillStructuresUsingTimeArgs(ExecState *exec, const List &args, int maxArgs, double *ms, struct tm *t)
+{
+ double milliseconds = 0;
+ int idx = 0;
+ int numArgs = args.size();
+
+ // JS allows extra trailing arguments -- ignore them
+ if (numArgs > maxArgs)
+ numArgs = maxArgs;
+
+ // hours
+ if (maxArgs >= 4 && idx < numArgs) {
+ t->tm_hour = 0;
+ milliseconds += args[idx++].toInt32(exec) * msPerHour;
+ }
+
+ // minutes
+ if (maxArgs >= 3 && idx < numArgs) {
+ t->tm_min = 0;
+ milliseconds += args[idx++].toInt32(exec) * msPerMinute;
+ }
+
+ // seconds
+ if (maxArgs >= 2 && idx < numArgs) {
+ t->tm_sec = 0;
+ milliseconds += args[idx++].toInt32(exec) * msPerSecond;
+ }
+
+ // milliseconds
+ if (idx < numArgs) {
+ milliseconds += roundValue(exec, args[idx]);
+ } else {
+ milliseconds += *ms;
+ }
+
+ *ms = milliseconds;
+}
+
+// Converts a list of arguments sent to a Date member function into years, months, and milliseconds, updating
+// ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
+//
+// Format of member function: f([years,] [months,] [days])
+static void fillStructuresUsingDateArgs(ExecState *exec, const List &args, int maxArgs, double *ms, struct tm *t)
+{
+ int idx = 0;
+ int numArgs = args.size();
+
+ // JS allows extra trailing arguments -- ignore them
+ if (numArgs > maxArgs)
+ numArgs = maxArgs;
+
+ // years
+ if (maxArgs >= 3 && idx < numArgs) {
+ t->tm_year = args[idx++].toInt32(exec) - 1900;
+ }
+
+ // months
+ if (maxArgs >= 2 && idx < numArgs) {
+ t->tm_mon = args[idx++].toInt32(exec);
+ }
+
+ // days
+ if (idx < numArgs) {
+ t->tm_mday = 0;
+ *ms += args[idx].toInt32(exec) * msPerDay;
+ }
+}
+
+// ------------------------------ DateInstanceImp ------------------------------
+
+const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
+
+DateInstanceImp::DateInstanceImp(ObjectImp *proto)
+ : ObjectImp(proto)
+{
+}
+
+// ------------------------------ DatePrototypeImp -----------------------------
+
+const ClassInfo DatePrototypeImp::info = {"Date", &DateInstanceImp::info, &dateTable, 0};
+
+/* Source for date_object.lut.h
+ We use a negative ID to denote the "UTC" variant.
+@begin dateTable 61
+ toString DateProtoFuncImp::ToString DontEnum|Function 0
+ toUTCString DateProtoFuncImp::ToUTCString DontEnum|Function 0
+ toDateString DateProtoFuncImp::ToDateString DontEnum|Function 0
+ toTimeString DateProtoFuncImp::ToTimeString DontEnum|Function 0
+ toLocaleString DateProtoFuncImp::ToLocaleString DontEnum|Function 0
+ toLocaleDateString DateProtoFuncImp::ToLocaleDateString DontEnum|Function 0
+ toLocaleTimeString DateProtoFuncImp::ToLocaleTimeString DontEnum|Function 0
+ valueOf DateProtoFuncImp::ValueOf DontEnum|Function 0
+ getTime DateProtoFuncImp::GetTime DontEnum|Function 0
+ getFullYear DateProtoFuncImp::GetFullYear DontEnum|Function 0
+ getUTCFullYear -DateProtoFuncImp::GetFullYear DontEnum|Function 0
+ toGMTString DateProtoFuncImp::ToGMTString DontEnum|Function 0
+ getMonth DateProtoFuncImp::GetMonth DontEnum|Function 0
+ getUTCMonth -DateProtoFuncImp::GetMonth DontEnum|Function 0
+ getDate DateProtoFuncImp::GetDate DontEnum|Function 0
+ getUTCDate -DateProtoFuncImp::GetDate DontEnum|Function 0
+ getDay DateProtoFuncImp::GetDay DontEnum|Function 0
+ getUTCDay -DateProtoFuncImp::GetDay DontEnum|Function 0
+ getHours DateProtoFuncImp::GetHours DontEnum|Function 0
+ getUTCHours -DateProtoFuncImp::GetHours DontEnum|Function 0
+ getMinutes DateProtoFuncImp::GetMinutes DontEnum|Function 0
+ getUTCMinutes -DateProtoFuncImp::GetMinutes DontEnum|Function 0
+ getSeconds DateProtoFuncImp::GetSeconds DontEnum|Function 0
+ getUTCSeconds -DateProtoFuncImp::GetSeconds DontEnum|Function 0
+ getMilliseconds DateProtoFuncImp::GetMilliSeconds DontEnum|Function 0
+ getUTCMilliseconds -DateProtoFuncImp::GetMilliSeconds DontEnum|Function 0
+ getTimezoneOffset DateProtoFuncImp::GetTimezoneOffset DontEnum|Function 0
+ setTime DateProtoFuncImp::SetTime DontEnum|Function 1
+ setMilliseconds DateProtoFuncImp::SetMilliSeconds DontEnum|Function 1
+ setUTCMilliseconds -DateProtoFuncImp::SetMilliSeconds DontEnum|Function 1
+ setSeconds DateProtoFuncImp::SetSeconds DontEnum|Function 2
+ setUTCSeconds -DateProtoFuncImp::SetSeconds DontEnum|Function 2
+ setMinutes DateProtoFuncImp::SetMinutes DontEnum|Function 3
+ setUTCMinutes -DateProtoFuncImp::SetMinutes DontEnum|Function 3
+ setHours DateProtoFuncImp::SetHours DontEnum|Function 4
+ setUTCHours -DateProtoFuncImp::SetHours DontEnum|Function 4
+ setDate DateProtoFuncImp::SetDate DontEnum|Function 1
+ setUTCDate -DateProtoFuncImp::SetDate DontEnum|Function 1
+ setMonth DateProtoFuncImp::SetMonth DontEnum|Function 2
+ setUTCMonth -DateProtoFuncImp::SetMonth DontEnum|Function 2
+ setFullYear DateProtoFuncImp::SetFullYear DontEnum|Function 3
+ setUTCFullYear -DateProtoFuncImp::SetFullYear DontEnum|Function 3
+ setYear DateProtoFuncImp::SetYear DontEnum|Function 1
+ getYear DateProtoFuncImp::GetYear DontEnum|Function 0
+ toGMTString DateProtoFuncImp::ToGMTString DontEnum|Function 0
+@end
+*/
+// ECMA 15.9.4
+
+DatePrototypeImp::DatePrototypeImp(ExecState *,
+ ObjectPrototypeImp *objectProto)
+ : DateInstanceImp(objectProto)
+{
+ Value protect(this);
+ setInternalValue(Number(NaN));
+ // The constructor will be added later, after DateObjectImp has been built
+}
+
+Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
+}
+
+// ------------------------------ DateProtoFuncImp -----------------------------
+
+DateProtoFuncImp::DateProtoFuncImp(ExecState *exec, int i, int len)
+ : InternalFunctionImp(
+ static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
+ ), id(abs(i)), utc(i<0)
+ // We use a negative ID to denote the "UTC" variant.
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+}
+
+bool DateProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ if (!thisObj.inherits(&DateInstanceImp::info)) {
+ // non-generic function called on non-date object
+
+ // ToString and ValueOf are generic according to the spec, but the mozilla
+ // tests suggest otherwise...
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+
+
+ Value result;
+ UString s;
+ const int bufsize=100;
+ char timebuffer[bufsize];
+ CString oldlocale = setlocale(LC_TIME,NULL);
+ if (!oldlocale.c_str())
+ oldlocale = setlocale(LC_ALL, NULL);
+ Value v = thisObj.internalValue();
+ double milli = v.toNumber(exec);
+ // special case: time value is NaN
+ if (isNaN(milli)) {
+ switch (id) {
+ case ToString:
+ case ToDateString:
+ case ToTimeString:
+ case ToGMTString:
+ case ToUTCString:
+ case ToLocaleString:
+ case ToLocaleDateString:
+ case ToLocaleTimeString:
+ return String("Invalid Date");
+ case ValueOf:
+ case GetTime:
+ case GetYear:
+ case GetFullYear:
+ case GetMonth:
+ case GetDate:
+ case GetDay:
+ case GetHours:
+ case GetMinutes:
+ case GetSeconds:
+ case GetMilliSeconds:
+ case GetTimezoneOffset:
+ case SetMilliSeconds:
+ case SetSeconds:
+ case SetMinutes:
+ case SetHours:
+ case SetDate:
+ case SetMonth:
+ case SetFullYear:
+ return Number(NaN);
+ }
+ }
+
+ if (id == SetTime) {
+ result = Number(roundValue(exec,args[0]));
+ thisObj.setInternalValue(result);
+ return result;
+ }
+
+ // check whether time value is outside time_t's usual range
+ // make the necessary transformations if necessary
+ int realYearOffset = 0;
+ double milliOffset = 0.0;
+ if (milli < 0 || milli >= timeFromYear(2038)) {
+ // ### ugly and probably not very precise
+ int realYear = yearFromTime(milli);
+ int base = daysInYear(realYear) == 365 ? 2001 : 2000;
+ milliOffset = timeFromYear(base) - timeFromYear(realYear);
+ milli += milliOffset;
+ realYearOffset = realYear - base;
+ }
+
+ time_t tv = (time_t) floor(milli / 1000.0);
+ double ms = milli - tv * 1000.0;
+
+ struct tm *t;
+ if ( (id == DateProtoFuncImp::ToGMTString) ||
+ (id == DateProtoFuncImp::ToUTCString) )
+ t = gmtime(&tv);
+ else if (id == DateProtoFuncImp::ToString)
+ t = localtime(&tv);
+ else if (utc)
+ t = gmtime(&tv);
+ else
+ t = localtime(&tv);
+
+ // we had an out of range year. use that one (plus/minus offset
+ // found by calculating tm_year) and fix the week day calculation
+ if (realYearOffset != 0) {
+ t->tm_year += realYearOffset;
+ milli -= milliOffset;
+ // our own weekday calculation. beware of need for local time.
+ double m = milli;
+ if (!utc)
+ m -= timeZoneOffset(t) * msPerMinute;
+ t->tm_wday = weekDay(m);
+ }
+
+ // trick gcc. We don't want the Y2K warnings.
+ const char xFormat[] = "%x";
+ const char cFormat[] = "%c";
+
+ switch (id) {
+ case ToString:
+ result = String(formatDate(*t) + " " + formatTime(*t));
+ break;
+ case ToDateString:
+ result = String(formatDate(*t));
+ break;
+ case ToTimeString:
+ result = String(formatTime(*t));
+ break;
+ case ToGMTString:
+ case ToUTCString:
+ result = String(formatDateUTCVariant(*t) + " " + formatTime(*t));
+ break;
+ case ToLocaleString:
+ strftime(timebuffer, bufsize, cFormat, t);
+ result = String(timebuffer);
+ break;
+ case ToLocaleDateString:
+ strftime(timebuffer, bufsize, xFormat, t);
+ result = String(timebuffer);
+ break;
+ case ToLocaleTimeString:
+ strftime(timebuffer, bufsize, "%X", t);
+ result = String(timebuffer);
+ break;
+ case ValueOf:
+ result = Number(milli);
+ break;
+ case GetTime:
+ result = Number(milli);
+ break;
+ case GetYear:
+ // IE returns the full year even in getYear.
+ if ( exec->dynamicInterpreter()->compatMode() != Interpreter::IECompat )
+ result = Number(t->tm_year);
+ else
+ result = Number(1900 + t->tm_year);
+ break;
+ case GetFullYear:
+ result = Number(1900 + t->tm_year);
+ break;
+ case GetMonth:
+ result = Number(t->tm_mon);
+ break;
+ case GetDate:
+ result = Number(t->tm_mday);
+ break;
+ case GetDay:
+ result = Number(t->tm_wday);
+ break;
+ case GetHours:
+ result = Number(t->tm_hour);
+ break;
+ case GetMinutes:
+ result = Number(t->tm_min);
+ break;
+ case GetSeconds:
+ result = Number(t->tm_sec);
+ break;
+ case GetMilliSeconds:
+ result = Number(ms);
+ break;
+ case GetTimezoneOffset:
+ result = Number(timeZoneOffset(t));
+ break;
+ case SetMilliSeconds:
+ fillStructuresUsingTimeArgs(exec, args, 1, &ms, t);
+ break;
+ case SetSeconds:
+ fillStructuresUsingTimeArgs(exec, args, 2, &ms, t);
+ break;
+ case SetMinutes:
+ fillStructuresUsingTimeArgs(exec, args, 3, &ms, t);
+ break;
+ case SetHours:
+ fillStructuresUsingTimeArgs(exec, args, 4, &ms, t);
+ break;
+ case SetDate:
+ fillStructuresUsingDateArgs(exec, args, 1, &ms, t);
+ break;
+ case SetMonth:
+ fillStructuresUsingDateArgs(exec, args, 2, &ms, t);
+ break;
+ case SetFullYear:
+ fillStructuresUsingDateArgs(exec, args, 3, &ms, t);
+ break;
+ case SetYear:
+ int y = args[0].toInt32(exec);
+ if (y < 1900) {
+ if (y >= 0 && y <= 99) {
+ t->tm_year = y;
+ } else {
+ fillStructuresUsingDateArgs(exec, args, 3, &ms, t);
+ }
+ } else {
+ t->tm_year = y - 1900;
+ }
+ break;
+ }
+
+ if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
+ id == SetMinutes || id == SetHours || id == SetDate ||
+ id == SetMonth || id == SetFullYear ) {
+ result = Number(makeTime(t, ms, utc));
+ thisObj.setInternalValue(result);
+ }
+
+ return result;
+}
+
+// ------------------------------ DateObjectImp --------------------------------
+
+// TODO: MakeTime (15.9.11.1) etc. ?
+
+DateObjectImp::DateObjectImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto,
+ DatePrototypeImp *dateProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+
+ // ECMA 15.9.4.1 Date.prototype
+ putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
+
+ static const Identifier parsePropertyName("parse");
+ putDirect(parsePropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1), DontEnum);
+ static const Identifier UTCPropertyName("UTC");
+ putDirect(UTCPropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC, 7), DontEnum);
+
+ // no. of arguments for constructor
+ putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
+}
+
+bool DateObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+// ECMA 15.9.3
+Object DateObjectImp::construct(ExecState *exec, const List &args)
+{
+ int numArgs = args.size();
+
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
+#endif
+ double value;
+
+ if (numArgs == 0) { // new Date() ECMA 15.9.3.3
+#ifdef HAVE_SYS_TIMEB_H
+# if defined(__BORLANDC__)
+ struct timeb timebuffer;
+ ftime(&timebuffer);
+# else
+ struct _timeb timebuffer;
+ _ftime(&timebuffer);
+# endif
+ double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
+#else
+ struct timeval tv;
+ gettimeofday(&tv, 0L);
+ double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
+#endif
+ value = utc;
+ } else if (numArgs == 1) {
+ Value prim = args[0].toPrimitive(exec);
+ if (prim.isA(StringType))
+ value = parseDate(prim.toString(exec));
+ else
+ value = prim.toNumber(exec);
+ } else {
+ if (isNaN(args[0].toNumber(exec))
+ || isNaN(args[1].toNumber(exec))
+ || (numArgs >= 3 && isNaN(args[2].toNumber(exec)))
+ || (numArgs >= 4 && isNaN(args[3].toNumber(exec)))
+ || (numArgs >= 5 && isNaN(args[4].toNumber(exec)))
+ || (numArgs >= 6 && isNaN(args[5].toNumber(exec)))
+ || (numArgs >= 7 && isNaN(args[6].toNumber(exec)))) {
+ value = NaN;
+ } else {
+ struct tm t;
+ memset(&t, 0, sizeof(t));
+ int year = args[0].toInt32(exec);
+ t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
+ t.tm_mon = args[1].toInt32(exec);
+ t.tm_mday = (numArgs >= 3) ? args[2].toInt32(exec) : 1;
+ t.tm_hour = (numArgs >= 4) ? args[3].toInt32(exec) : 0;
+ t.tm_min = (numArgs >= 5) ? args[4].toInt32(exec) : 0;
+ t.tm_sec = (numArgs >= 6) ? args[5].toInt32(exec) : 0;
+ t.tm_isdst = -1;
+ int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
+ value = makeTime(&t, ms, false);
+ }
+ }
+
+ Object proto = exec->lexicalInterpreter()->builtinDatePrototype();
+ Object ret(new DateInstanceImp(proto.imp()));
+ ret.setInternalValue(Number(timeClip(value)));
+ return ret;
+}
+
+bool DateObjectImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.9.2
+Value DateObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
+{
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"DateObjectImp::call - current time\n");
+#endif
+ time_t t = time(0L);
+ // FIXME: not threadsafe
+ struct tm *tm = localtime(&t);
+ return String(formatDate(*tm) + " " + formatTime(*tm));
+}
+
+// ------------------------------ DateObjectFuncImp ----------------------------
+
+DateObjectFuncImp::DateObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto,
+ int i, int len)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+}
+
+bool DateObjectFuncImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.9.4.2 - 3
+Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ if (id == Parse) {
+ return Number(parseDate(args[0].toString(exec)));
+ } else { // UTC
+ int n = args.size();
+ if (isNaN(args[0].toNumber(exec))
+ || isNaN(args[1].toNumber(exec))
+ || (n >= 3 && isNaN(args[2].toNumber(exec)))
+ || (n >= 4 && isNaN(args[3].toNumber(exec)))
+ || (n >= 5 && isNaN(args[4].toNumber(exec)))
+ || (n >= 6 && isNaN(args[5].toNumber(exec)))
+ || (n >= 7 && isNaN(args[6].toNumber(exec)))) {
+ return Number(NaN);
+ }
+
+ struct tm t;
+ memset(&t, 0, sizeof(t));
+ int year = args[0].toInt32(exec);
+ t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
+ t.tm_mon = args[1].toInt32(exec);
+ t.tm_mday = (n >= 3) ? args[2].toInt32(exec) : 1;
+ t.tm_hour = (n >= 4) ? args[3].toInt32(exec) : 0;
+ t.tm_min = (n >= 5) ? args[4].toInt32(exec) : 0;
+ t.tm_sec = (n >= 6) ? args[5].toInt32(exec) : 0;
+ int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
+ return Number(makeTime(&t, ms, true));
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+
+double KJS::parseDate(const UString &u)
+{
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
+#endif
+ double /*time_t*/ seconds = KRFCDate_parseDate( u );
+
+ return seconds == invalidDate ? NaN : seconds * 1000.0;
+}
+
+///// Awful duplication from krfcdate.cpp - we don't link to kdecore
+
+static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
+{
+ //printf("year=%d month=%d day=%d hour=%d minute=%d second=%d\n", year, mon, day, hour, minute, second);
+
+ double ret = (day - 32075) /* days */
+ + 1461L * (year + 4800L + (mon - 14) / 12) / 4
+ + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
+ - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
+ - 2440588;
+ ret = 24*ret + hour; /* hours */
+ ret = 60*ret + minute; /* minutes */
+ ret = 60*ret + second; /* seconds */
+
+ return ret;
+}
+
+// we follow the recommendation of rfc2822 to consider all
+// obsolete time zones not listed here equivalent to "-0000"
+static const struct KnownZone {
+#ifdef _WIN32
+ char tzName[4];
+#else
+ const char tzName[4];
+#endif
+ int tzOffset;
+} known_zones[] = {
+ { "UT", 0 },
+ { "GMT", 0 },
+ { "EST", -300 },
+ { "EDT", -240 },
+ { "CST", -360 },
+ { "CDT", -300 },
+ { "MST", -420 },
+ { "MDT", -360 },
+ { "PST", -480 },
+ { "PDT", -420 }
+};
+
+double KJS::makeTime(struct tm *t, double ms, bool utc)
+{
+ int utcOffset;
+ if (utc) {
+ time_t zero = 0;
+#if defined BSD || defined(__linux__) || defined(__APPLE__)
+ struct tm t3;
+ localtime_r(&zero, &t3);
+ utcOffset = t3.tm_gmtoff;
+ t->tm_isdst = t3.tm_isdst;
+#else
+ (void)localtime(&zero);
+# if defined(__BORLANDC__) || defined(__CYGWIN__)
+ utcOffset = - _timezone;
+# else
+ utcOffset = - timezone;
+# endif
+ t->tm_isdst = 0;
+#endif
+ } else {
+ utcOffset = 0;
+ t->tm_isdst = -1;
+ }
+
+ double yearOffset = 0.0;
+ if (t->tm_year < (1970 - 1900) || t->tm_year > (2038 - 1900)) {
+ // we'll fool mktime() into believing that this year is within
+ // its normal, portable range (1970-2038) by setting tm_year to
+ // 2000 or 2001 and adding the difference in milliseconds later.
+ // choice between offset will depend on whether the year is a
+ // leap year or not.
+ int y = t->tm_year + 1900;
+ int baseYear = daysInYear(y) == 365 ? 2001 : 2000;
+ const double baseTime = timeFromYear(baseYear);
+ yearOffset = timeFromYear(y) - baseTime;
+ t->tm_year = baseYear - 1900;
+ }
+
+ // Determine if we passed over a DST change boundary
+ if (!utc) {
+ time_t tval = mktime(t) + utcOffset + int((ms + yearOffset)/1000);
+ struct tm t3;
+ localtime_r(&tval, &t3);
+ t->tm_isdst = t3.tm_isdst;
+ }
+
+ return (mktime(t) + utcOffset) * 1000.0 + ms + yearOffset;
+}
+
+// returns 0-11 (Jan-Dec); -1 on failure
+static int findMonth(const char *monthStr)
+{
+ assert(monthStr);
+ static const char haystack[37] = "janfebmaraprmayjunjulaugsepoctnovdec";
+ char needle[4];
+ for (int i = 0; i < 3; ++i) {
+ if (!*monthStr)
+ return -1;
+ needle[i] = tolower(*monthStr++);
+ }
+ needle[3] = '\0';
+ const char *str = strstr(haystack, needle);
+ if (str) {
+ int position = str - haystack;
+ if (position % 3 == 0) {
+ return position / 3;
+ }
+ }
+ return -1;
+}
+
+// maybe this should be more often than just isspace() but beware of
+// conflicts with : in time strings.
+static bool isSpaceLike(char c)
+{
+ return isspace(c) || c == ',' || c == ':' || c == '-';
+}
+
+double KJS::KRFCDate_parseDate(const UString &_date)
+{
+ // This parse a date in the form:
+ // Wednesday, 09-Nov-99 23:12:40 GMT
+ // or
+ // Sat, 01-Jan-2000 08:00:00 GMT
+ // or
+ // Sat, 01 Jan 2000 08:00:00 GMT
+ // or
+ // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
+ // ### non RFC formats, added for Javascript:
+ // [Wednesday] January 09 1999 23:12:40 GMT
+ // [Wednesday] January 09 23:12:40 GMT 1999
+ //
+ // We ignore the weekday
+ //
+ double result = -1;
+ int offset = 0;
+ bool have_tz = false;
+ char *newPosStr;
+ const char *dateString = _date.ascii();
+ int day = 0;
+ int month = -1; // not set yet
+ int year = 0;
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+ bool have_time = false;
+
+ // Skip leading space
+ while(*dateString && isSpaceLike(*dateString))
+ dateString++;
+
+ const char *wordStart = dateString;
+ // Check contents of first words if not number
+ while(*dateString && !isdigit(*dateString))
+ {
+ if (isSpaceLike(*dateString) && dateString - wordStart >= 3)
+ {
+ month = findMonth(wordStart);
+ while(*dateString && isSpaceLike(*dateString))
+ dateString++;
+ wordStart = dateString;
+ }
+ else
+ dateString++;
+ }
+ // missing delimiter between month and day (like "January29")?
+ if (month == -1 && dateString && wordStart != dateString) {
+ month = findMonth(wordStart);
+ // TODO: emit warning about dubious format found
+ }
+
+ while(*dateString && isSpaceLike(*dateString))
+ dateString++;
+
+ if (!*dateString)
+ return invalidDate;
+
+ // ' 09-Nov-99 23:12:40 GMT'
+ errno = 0;
+ day = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+
+ if (!*dateString)
+ return invalidDate;
+
+ if (day < 0)
+ return invalidDate;
+ if (day > 31) {
+ // ### where is the boundary and what happens below?
+ if (*dateString == '/') {
+ // looks like a YYYY/MM/DD date
+ if (!*++dateString)
+ return invalidDate;
+ year = day;
+ month = strtol(dateString, &newPosStr, 10) - 1;
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+ if (*dateString++ != '/' || !*dateString)
+ return invalidDate;
+ day = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+ } else {
+ return invalidDate;
+ }
+ } else if (*dateString == '/' && month == -1)
+ {
+ dateString++;
+ // This looks like a MM/DD/YYYY date, not an RFC date.....
+ month = day - 1; // 0-based
+ day = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+ if (*dateString == '/')
+ dateString++;
+ if (!*dateString)
+ return invalidDate;
+ //printf("month=%d day=%d dateString=%s\n", month, day, dateString);
+ }
+ else
+ {
+ if (*dateString == '-')
+ dateString++;
+
+ while(*dateString && isSpaceLike(*dateString))
+ dateString++;
+
+ if (*dateString == ',')
+ dateString++;
+
+ if ( month == -1 ) // not found yet
+ {
+ month = findMonth(dateString);
+ if (month == -1)
+ return invalidDate;
+
+ while(*dateString && (*dateString != '-') && !isSpaceLike(*dateString))
+ dateString++;
+
+ if (!*dateString)
+ return invalidDate;
+
+ // '-99 23:12:40 GMT'
+ if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
+ return invalidDate;
+ dateString++;
+ }
+
+ if ((month < 0) || (month > 11))
+ return invalidDate;
+ }
+
+ // '99 23:12:40 GMT'
+ if (year <= 0 && *dateString) {
+ year = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ }
+
+ // Don't fail if the time is missing.
+ if (*newPosStr)
+ {
+ // ' 23:12:40 GMT'
+ if (*newPosStr == ':') // Ah, so there was no year, but the number was the hour
+ year = -1;
+ else if (isSpaceLike(*newPosStr)) // we parsed the year
+ dateString = ++newPosStr;
+ else
+ return invalidDate;
+
+ hour = strtol(dateString, &newPosStr, 10);
+
+ // Do not check for errno here since we want to continue
+ // even if errno was set becasue we are still looking
+ // for the timezone!
+ // read a number? if not this might be a timezone name
+ if (newPosStr != dateString) {
+ have_time = true;
+ dateString = newPosStr;
+
+ if ((hour < 0) || (hour > 23))
+ return invalidDate;
+
+ if (!*dateString)
+ return invalidDate;
+
+ // ':12:40 GMT'
+ if (*dateString++ != ':')
+ return invalidDate;
+
+ minute = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+
+ if ((minute < 0) || (minute > 59))
+ return invalidDate;
+
+ // ':40 GMT'
+ if (*dateString && *dateString != ':' && !isspace(*dateString))
+ return invalidDate;
+
+ // seconds are optional in rfc822 + rfc2822
+ if (*dateString ==':') {
+ dateString++;
+
+ second = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+
+ if ((second < 0) || (second > 59))
+ return invalidDate;
+
+ // disallow trailing colon seconds
+ if (*dateString == ':')
+ return invalidDate;
+ }
+
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ if (strncasecmp(dateString, "AM", 2) == 0) {
+ if (hour > 12)
+ return invalidDate;
+ if (hour == 12)
+ hour = 0;
+ dateString += 2;
+ while (isspace(*dateString))
+ dateString++;
+ } else if (strncasecmp(dateString, "PM", 2) == 0) {
+ if (hour > 12)
+ return invalidDate;
+ if (hour != 12)
+ hour += 12;
+ dateString += 2;
+ while (isspace(*dateString))
+ dateString++;
+ }
+ }
+ } else {
+ dateString = newPosStr;
+ }
+
+ // don't fail if the time zone is missing, some
+ // broken mail-/news-clients omit the time zone
+ if (*dateString) {
+
+ if (strncasecmp(dateString, "GMT", 3) == 0 ||
+ strncasecmp(dateString, "UTC", 3) == 0)
+ {
+ dateString += 3;
+ have_tz = true;
+ }
+
+ while (*dateString && isspace(*dateString))
+ ++dateString;
+
+ if (strncasecmp(dateString, "GMT", 3) == 0) {
+ dateString += 3;
+ }
+ if ((*dateString == '+') || (*dateString == '-')) {
+ offset = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+
+ if ((offset < -9959) || (offset > 9959))
+ return invalidDate;
+
+ int sgn = (offset < 0)? -1:1;
+ offset = abs(offset);
+ if ( *dateString == ':' ) { // GMT+05:00
+ int offset2 = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+ offset = (offset*60 + offset2)*sgn;
+ }
+ else
+ offset = ((offset / 100)*60 + (offset % 100))*sgn;
+ have_tz = true;
+ } else {
+ for (int i=0; i < int(sizeof(known_zones)/sizeof(KnownZone)); i++) {
+ if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
+ offset = known_zones[i].tzOffset;
+ dateString += strlen(known_zones[i].tzName);
+ have_tz = true;
+ break;
+ }
+ }
+ }
+ }
+
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ if ( *dateString && year == -1 ) {
+ year = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+ }
+
+ while (isspace(*dateString))
+ dateString++;
+
+#if 0
+ // Trailing garbage
+ if (*dateString != '\0')
+ return invalidDate;
+#endif
+
+ // Y2K: Solve 2 digit years
+ if ((year >= 0) && (year < 50))
+ year += 2000;
+
+ if ((year >= 50) && (year < 100))
+ year += 1900; // Y2K
+
+ if (!have_tz) {
+ // fall back to midnight, local timezone
+ struct tm t;
+ memset(&t, 0, sizeof(tm));
+ t.tm_mday = day;
+ t.tm_mon = month;
+ t.tm_year = year - 1900;
+ t.tm_isdst = -1;
+ if (have_time) {
+ t.tm_sec = second;
+ t.tm_min = minute;
+ t.tm_hour = hour;
+ }
+
+ // better not use mktime() as it can't handle the full year range
+ return makeTime(&t, 0, false) / 1000.0;
+ }
+
+ result = ymdhms_to_seconds(year, month+1, day, hour, minute, second) - offset*60;
+ return result;
+}
+
+
+double KJS::timeClip(double t)
+{
+ if (isInf(t))
+ return NaN;
+ double at = fabs(t);
+ if (at > 8.64E15)
+ return NaN;
+ return floor(at) * (t != at ? -1 : 1);
+}
+