kjs Library: date_object.cpp Source File (kjs Library)

| # kjs Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# date_object.cpp

    00001 // -*- c-basic-offset: 2 -*-
    00002 /*
    00003  *  This file is part of the KDE libraries
    00004  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
    00005  *  Copyright (C) 2003 Apple Computer, Inc.
    00006  *
    00007  *  This library is free software; you can redistribute it and/or
    00008  *  modify it under the terms of the GNU Lesser General Public
    00009  *  License as published by the Free Software Foundation; either
    00010  *  version 2 of the License, or (at your option) any later version.
    00011  *
    00012  *  This library is distributed in the hope that it will be useful,
    00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00015  *  Lesser General Public License for more details.
    00016  *
    00017  *  You should have received a copy of the GNU Lesser General Public
    00018  *  License along with this library; if not, write to the Free Software
    00019  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    00020  *
    00021  */
    00022
    00023 #ifdef HAVE_CONFIG_H
    00024 #include <config.h>
    00025 #endif
    00026
    00027 #if TIME_WITH_SYS_TIME
    00028 # include <sys/time.h>
    00029 # include <time.h>
    00030 #else
    00031 #if HAVE_SYS_TIME_H
    00032 #include <sys/time.h>
    00033 #else
    00034 #  include <time.h>
    00035 # endif
    00036 #endif
    00037 #ifdef HAVE_SYS_TIMEB_H
    00038 #include <sys/timeb.h>
    00039 #endif
    00040
    00041 #ifdef HAVE_SYS_PARAM_H
    00042 #  include <sys/param.h>
    00043 #endif // HAVE_SYS_PARAM_H
    00044
    00045 #include <math.h>
    00046 #include <string.h>
    00047 #ifdef HAVE_STRINGS_H
    00048 #  include <strings.h>
    00049 #endif
    00050 #include <stdio.h>
    00051 #include <stdlib.h>
    00052 #include <locale.h>
    00053 #include <ctype.h>
    00054 #include <assert.h>
    00055
    00056 #include "date_object.h"
    00057 #include "error_object.h"
    00058 #include "operations.h"
    00059
    00060 #include "date_object.lut.h"
    00061
    00062 using namespace KJS;
    00063
    00064 // come constants
    00065 const time_t invalidDate = -1;
    00066 const double hoursPerDay = 24;
    00067 const double minutesPerHour = 60;
    00068 const double secondsPerMinute = 60;
    00069 const double msPerSecond = 1000;
    00070 const double msPerMinute = msPerSecond * secondsPerMinute;
    00071 const double msPerHour = msPerMinute * minutesPerHour;
    00072 const double msPerDay = msPerHour * hoursPerDay;
    00073
    00074 static int day(double t)
    00075 {
    00076   return int(floor(t / msPerDay));
    00077 }
    00078
    00079 static double dayFromYear(int year)
    00080 {
    00081   return 365.0 * (year - 1970)
    00082     + floor((year - 1969) / 4.0)
    00083     - floor((year - 1901) / 100.0)
    00084     + floor((year - 1601) / 400.0);
    00085 }
    00086
    00087 // depending on whether it's a leap year or not
    00088 static int daysInYear(int year)
    00089 {
    00090   if (year % 4 != 0)
    00091     return 365;
    00092   else if (year % 400 == 0)
    00093     return 366;
    00094   else if (year % 100 == 0)
    00095     return 365;
    00096   else
    00097     return 366;
    00098 }
    00099
    00100 // time value of the start of a year
    00101 double timeFromYear(int year)
    00102 {
    00103   return msPerDay * dayFromYear(year);
    00104 }
    00105
    00106 // year determined by time value
    00107 int yearFromTime(double t)
    00108 {
    00109   // ### there must be an easier way
    00110   // initial guess
    00111   int y = 1970 + int(t / (365.25 * msPerDay));
    00112   // adjustment
    00113   if (timeFromYear(y) > t) {
    00114     do {
    00115       --y;
    00116     } while (timeFromYear(y) > t);
    00117   } else {
    00118     while (timeFromYear(y + 1) < t)
    00119       ++y;
    00120   }
    00121
    00122   return y;
    00123 }
    00124
    00125 // 0: Sunday, 1: Monday, etc.
    00126 int weekDay(double t)
    00127 {
    00128   int wd = (day(t) + 4) % 7;
    00129   if (wd < 0)
    00130     wd += 7;
    00131   return wd;
    00132 }
    00133
    00134 static double timeZoneOffset(const struct tm *t)
    00135 {
    00136 #if defined BSD || defined(__linux__) || defined(__APPLE__)
    00137   return -(t->tm_gmtoff / 60);
    00138 #else
    00139 #  if defined(__BORLANDC__)
    00140 // FIXME consider non one-hour DST change
    00141 #error please add daylight savings offset here!
    00142   return _timezone / 60 - (t->tm_isdst > 0 ? 60 : 0);
    00143 #  else
    00144   return timezone / 60 - (t->tm_isdst > 0 ? 60 : 0 );
    00145 #  endif
    00146 #endif
    00147 }
    00148
    00149 // ------------------------------ DateInstanceImp ------------------------------
    00150
    00151 const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
    00152
    00153 DateInstanceImp::DateInstanceImp(ObjectImp *proto)
    00154   : ObjectImp(proto)
    00155 {
    00156 }
    00157
    00158 // ------------------------------ DatePrototypeImp -----------------------------
    00159
    00160 const ClassInfo DatePrototypeImp::info = {"Date", 0, &dateTable, 0};
    00161
    00162 /* Source for date_object.lut.h
    00163    We use a negative ID to denote the "UTC" variant.
    00164 @begin dateTable 61
    00165   toString      DateProtoFuncImp::ToString      DontEnum|Function   0
    00166   toUTCString       DateProtoFuncImp::ToUTCString       DontEnum|Function   0
    00167   toDateString      DateProtoFuncImp::ToDateString      DontEnum|Function   0
    00168   toTimeString      DateProtoFuncImp::ToTimeString      DontEnum|Function   0
    00169   toLocaleString    DateProtoFuncImp::ToLocaleString    DontEnum|Function   0
    00170   toLocaleDateString    DateProtoFuncImp::ToLocaleDateString    DontEnum|Function   0
    00171   toLocaleTimeString    DateProtoFuncImp::ToLocaleTimeString    DontEnum|Function   0
    00172   valueOf       DateProtoFuncImp::ValueOf       DontEnum|Function   0
    00173   getTime       DateProtoFuncImp::GetTime       DontEnum|Function   0
    00174   getFullYear       DateProtoFuncImp::GetFullYear       DontEnum|Function   0
    00175   getUTCFullYear    -DateProtoFuncImp::GetFullYear      DontEnum|Function   0
    00176   toGMTString       DateProtoFuncImp::ToGMTString       DontEnum|Function   0
    00177   getMonth      DateProtoFuncImp::GetMonth      DontEnum|Function   0
    00178   getUTCMonth       -DateProtoFuncImp::GetMonth     DontEnum|Function   0
    00179   getDate       DateProtoFuncImp::GetDate       DontEnum|Function   0
    00180   getUTCDate        -DateProtoFuncImp::GetDate      DontEnum|Function   0
    00181   getDay        DateProtoFuncImp::GetDay        DontEnum|Function   0
    00182   getUTCDay     -DateProtoFuncImp::GetDay       DontEnum|Function   0
    00183   getHours      DateProtoFuncImp::GetHours      DontEnum|Function   0
    00184   getUTCHours       -DateProtoFuncImp::GetHours     DontEnum|Function   0
    00185   getMinutes        DateProtoFuncImp::GetMinutes        DontEnum|Function   0
    00186   getUTCMinutes     -DateProtoFuncImp::GetMinutes       DontEnum|Function   0
    00187   getSeconds        DateProtoFuncImp::GetSeconds        DontEnum|Function   0
    00188   getUTCSeconds     -DateProtoFuncImp::GetSeconds       DontEnum|Function   0
    00189   getMilliseconds   DateProtoFuncImp::GetMilliSeconds   DontEnum|Function   0
    00190   getUTCMilliseconds    -DateProtoFuncImp::GetMilliSeconds  DontEnum|Function   0
    00191   getTimezoneOffset DateProtoFuncImp::GetTimezoneOffset DontEnum|Function   0
    00192   setTime       DateProtoFuncImp::SetTime       DontEnum|Function   1
    00193   setMilliseconds   DateProtoFuncImp::SetMilliSeconds   DontEnum|Function   1
    00194   setUTCMilliseconds    -DateProtoFuncImp::SetMilliSeconds  DontEnum|Function   1
    00195   setSeconds        DateProtoFuncImp::SetSeconds        DontEnum|Function   2
    00196   setUTCSeconds     -DateProtoFuncImp::SetSeconds       DontEnum|Function   2
    00197   setMinutes        DateProtoFuncImp::SetMinutes        DontEnum|Function   3
    00198   setUTCMinutes     -DateProtoFuncImp::SetMinutes       DontEnum|Function   3
    00199   setHours      DateProtoFuncImp::SetHours      DontEnum|Function   4
    00200   setUTCHours       -DateProtoFuncImp::SetHours     DontEnum|Function   4
    00201   setDate       DateProtoFuncImp::SetDate       DontEnum|Function   1
    00202   setUTCDate        -DateProtoFuncImp::SetDate      DontEnum|Function   1
    00203   setMonth      DateProtoFuncImp::SetMonth      DontEnum|Function   2
    00204   setUTCMonth       -DateProtoFuncImp::SetMonth     DontEnum|Function   2
    00205   setFullYear       DateProtoFuncImp::SetFullYear       DontEnum|Function   3
    00206   setUTCFullYear    -DateProtoFuncImp::SetFullYear      DontEnum|Function   3
    00207   setYear       DateProtoFuncImp::SetYear       DontEnum|Function   1
    00208   getYear       DateProtoFuncImp::GetYear       DontEnum|Function   0
    00209   toGMTString       DateProtoFuncImp::ToGMTString       DontEnum|Function   0
    00210 @end
    00211 */
    00212 // ECMA 15.9.4
    00213
    00214 DatePrototypeImp::DatePrototypeImp(ExecState *,
    00215                                    ObjectPrototypeImp *objectProto)
    00216   : DateInstanceImp(objectProto)
    00217 {
    00218   Value protect(this);
    00219   setInternalValue(Number(NaN));
    00220   // The constructor will be added later, after DateObjectImp has been built
    00221 }
    00222
    00223 Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
    00224 {
    00225   return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
    00226 }
    00227
    00228 // ------------------------------ DateProtoFuncImp -----------------------------
    00229
    00230 DateProtoFuncImp::DateProtoFuncImp(ExecState *exec, int i, int len)
    00231   : InternalFunctionImp(
    00232     static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
    00233     ), id(abs(i)), utc(i<0)
    00234   // We use a negative ID to denote the "UTC" variant.
    00235 {
    00236   Value protect(this);
    00237   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
    00238 }
    00239
    00240 bool DateProtoFuncImp::implementsCall() const
    00241 {
    00242   return true;
    00243 }
    00244
    00245 Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
    00246 {
    00247   if ((id == ToString || id == ValueOf || id == GetTime || id == SetTime) &&
    00248       !thisObj.inherits(&DateInstanceImp::info)) {
    00249     // non-generic function called on non-date object
    00250
    00251     // ToString and ValueOf are generic according to the spec, but the mozilla
    00252     // tests suggest otherwise...
    00253     Object err = Error::create(exec,TypeError);
    00254     exec->setException(err);
    00255     return err;
    00256   }
    00257
    00258
    00259   Value result;
    00260   UString s;
    00261   const int bufsize=100;
    00262   char timebuffer[bufsize];
    00263   CString oldlocale = setlocale(LC_TIME,NULL);
    00264   if (!oldlocale.c_str())
    00265     oldlocale = setlocale(LC_ALL, NULL);
    00266   Value v = thisObj.internalValue();
    00267   double milli = v.toNumber(exec);
    00268   // special case: time value is NaN
    00269   if (isNaN(milli)) {
    00270     switch (id) {
    00271     case ToString:
    00272     case ToDateString:
    00273     case ToTimeString:
    00274     case ToGMTString:
    00275     case ToUTCString:
    00276     case ToLocaleString:
    00277     case ToLocaleDateString:
    00278     case ToLocaleTimeString:
    00279       return String("Invalid Date");
    00280     case ValueOf:
    00281     case GetTime:
    00282     case GetYear:
    00283     case GetFullYear:
    00284     case GetMonth:
    00285     case GetDate:
    00286     case GetDay:
    00287     case GetHours:
    00288     case GetMinutes:
    00289     case GetSeconds:
    00290     case GetMilliSeconds:
    00291     case GetTimezoneOffset:
    00292       return Number(NaN);
    00293     }
    00294   }
    00295
    00296   // check whether time value is outside time_t's usual range
    00297   // make the necessary transformations if necessary
    00298   int realYearOffset = 0;
    00299   double milliOffset = 0.0;
    00300   if (milli < 0 || milli >= timeFromYear(2038)) {
    00301     // ### ugly and probably not very precise
    00302     int realYear = yearFromTime(milli);
    00303     int base = daysInYear(realYear) == 365 ? 2001 : 2000;
    00304     milliOffset = timeFromYear(base) - timeFromYear(realYear);
    00305     milli += milliOffset;
    00306     realYearOffset = realYear - base;
    00307   }
    00308
    00309   time_t tv = (time_t) floor(milli / 1000.0);
    00310   int ms = int(milli - tv * 1000.0);
    00311
    00312   struct tm *t;
    00313   if ( (id == DateProtoFuncImp::ToGMTString) ||
    00314        (id == DateProtoFuncImp::ToUTCString) )
    00315     t = gmtime(&tv);
    00316   else if (id == DateProtoFuncImp::ToString)
    00317     t = localtime(&tv);
    00318   else if (utc)
    00319     t = gmtime(&tv);
    00320   else
    00321     t = localtime(&tv);
    00322
    00323   // we had an out of range year. use that one (plus/minus offset
    00324   // found by calculating tm_year) and fix the week day calculation
    00325   if (realYearOffset != 0) {
    00326     t->tm_year += realYearOffset;
    00327     milli -= milliOffset;
    00328     // our own weekday calculation. beware of need for local time.
    00329     double m = milli;
    00330     if (!utc)
    00331       m -= timeZoneOffset(t) * msPerMinute;
    00332     t->tm_wday = weekDay(m);
    00333   }
    00334
    00335   // trick gcc. We don't want the Y2K warnings.
    00336   const char xFormat[] = "%x";
    00337   const char cFormat[] = "%c";
    00338
    00339   switch (id) {
    00340   case ToString:
    00341   case ToDateString:
    00342   case ToTimeString:
    00343   case ToGMTString:
    00344   case ToUTCString:
    00345     setlocale(LC_TIME,"C");
    00346     if (id == DateProtoFuncImp::ToDateString) {
    00347       strftime(timebuffer, bufsize, xFormat, t);
    00348     } else if (id == DateProtoFuncImp::ToTimeString) {
    00349       strftime(timebuffer, bufsize, "%X",t);
    00350     } else { // ToString, toGMTString & toUTCString
    00351       strftime(timebuffer, bufsize, "%a, %d %b %Y %H:%M:%S %z", t);
    00352     }
    00353     setlocale(LC_TIME,oldlocale.c_str());
    00354     result = String(timebuffer);
    00355     break;
    00356   case ToLocaleString:
    00357     strftime(timebuffer, bufsize, cFormat, t);
    00358     result = String(timebuffer);
    00359     break;
    00360   case ToLocaleDateString:
    00361     strftime(timebuffer, bufsize, xFormat, t);
    00362     result = String(timebuffer);
    00363     break;
    00364   case ToLocaleTimeString:
    00365     strftime(timebuffer, bufsize, "%X", t);
    00366     result = String(timebuffer);
    00367     break;
    00368   case ValueOf:
    00369     result = Number(milli);
    00370     break;
    00371   case GetTime:
    00372     result = Number(milli);
    00373     break;
    00374   case GetYear:
    00375     // IE returns the full year even in getYear.
    00376     if ( exec->interpreter()->compatMode() != Interpreter::IECompat )
    00377       result = Number(t->tm_year);
    00378     else
    00379       result = Number(1900 + t->tm_year);
    00380     break;
    00381   case GetFullYear:
    00382     result = Number(1900 + t->tm_year);
    00383     break;
    00384   case GetMonth:
    00385     result = Number(t->tm_mon);
    00386     break;
    00387   case GetDate:
    00388     result = Number(t->tm_mday);
    00389     break;
    00390   case GetDay:
    00391     result = Number(t->tm_wday);
    00392     break;
    00393   case GetHours:
    00394     result = Number(t->tm_hour);
    00395     break;
    00396   case GetMinutes:
    00397     result = Number(t->tm_min);
    00398     break;
    00399   case GetSeconds:
    00400     result = Number(t->tm_sec);
    00401     break;
    00402   case GetMilliSeconds:
    00403     result = Number(ms);
    00404     break;
    00405   case GetTimezoneOffset:
    00406     result = Number(timeZoneOffset(t));
    00407     break;
    00408   case SetTime:
    00409     milli = roundValue(exec,args[0]);
    00410     result = Number(milli);
    00411     thisObj.setInternalValue(result);
    00412     break;
    00413   case SetMilliSeconds:
    00414     ms = args[0].toInt32(exec);
    00415     break;
    00416   case SetSeconds:
    00417     t->tm_sec = args[0].toInt32(exec);
    00418     if (args.size() >= 2)
    00419       ms = args[1].toInt32(exec);
    00420     break;
    00421   case SetMinutes:
    00422     t->tm_min = args[0].toInt32(exec);
    00423     if (args.size() >= 2)
    00424       t->tm_sec = args[1].toInt32(exec);
    00425     if (args.size() >= 3)
    00426       ms = args[2].toInt32(exec);
    00427     break;
    00428   case SetHours:
    00429     t->tm_hour = args[0].toInt32(exec);
    00430     if (args.size() >= 2)
    00431       t->tm_min = args[1].toInt32(exec);
    00432     if (args.size() >= 3)
    00433       t->tm_sec = args[2].toInt32(exec);
    00434     if (args.size() >= 4)
    00435       ms = args[3].toInt32(exec);
    00436     break;
    00437   case SetDate:
    00438     t->tm_mday = args[0].toInt32(exec);
    00439     break;
    00440   case SetMonth:
    00441     t->tm_mon = args[0].toInt32(exec);
    00442     if (args.size() >= 2)
    00443       t->tm_mday = args[1].toInt32(exec);
    00444     break;
    00445   case SetFullYear:
    00446     t->tm_year = args[0].toInt32(exec) - 1900;
    00447     if (args.size() >= 2)
    00448       t->tm_mon = args[1].toInt32(exec);
    00449     if (args.size() >= 3)
    00450       t->tm_mday = args[2].toInt32(exec);
    00451     break;
    00452   case SetYear: {
    00453     int a0 = args[0].toInt32(exec);
    00454     if (a0 >= 0 && a0 <= 99)
    00455       a0 += 1900;
    00456     t->tm_year = a0 - 1900;
    00457     break;
    00458   }
    00459   }
    00460
    00461   if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
    00462       id == SetMinutes || id == SetHours || id == SetDate ||
    00463       id == SetMonth || id == SetFullYear ) {
    00464     result = Number(makeTime(t, ms, utc));
    00465     thisObj.setInternalValue(result);
    00466   }
    00467
    00468   return result;
    00469 }
    00470
    00471 // ------------------------------ DateObjectImp --------------------------------
    00472
    00473 // TODO: MakeTime (15.9.11.1) etc. ?
    00474
    00475 DateObjectImp::DateObjectImp(ExecState *exec,
    00476                              FunctionPrototypeImp *funcProto,
    00477                              DatePrototypeImp *dateProto)
    00478   : InternalFunctionImp(funcProto)
    00479 {
    00480   Value protect(this);
    00481
    00482   // ECMA 15.9.4.1 Date.prototype
    00483   putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
    00484
    00485   static const Identifier parsePropertyName("parse");
    00486   putDirect(parsePropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1), DontEnum);
    00487   static const Identifier UTCPropertyName("UTC");
    00488   putDirect(UTCPropertyName,   new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC,   7),   DontEnum);
    00489
    00490   // no. of arguments for constructor
    00491   putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
    00492 }
    00493
    00494 bool DateObjectImp::implementsConstruct() const
    00495 {
    00496   return true;
    00497 }
    00498
    00499 // ECMA 15.9.3
    00500 Object DateObjectImp::construct(ExecState *exec, const List &args)
    00501 {
    00502   int numArgs = args.size();
    00503
    00504 #ifdef KJS_VERBOSE
    00505   fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
    00506 #endif
    00507   double value;
    00508
    00509   if (numArgs == 0) { // new Date() ECMA 15.9.3.3
    00510 #ifdef HAVE_SYS_TIMEB_H
    00511 #  if defined(__BORLANDC__)
    00512     struct timeb timebuffer;
    00513     ftime(&timebuffer);
    00514 #  else
    00515     struct _timeb timebuffer;
    00516     _ftime(&timebuffer);
    00517 #  endif
    00518     double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
    00519 #else
    00520     struct timeval tv;
    00521     gettimeofday(&tv, 0L);
    00522     double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
    00523 #endif
    00524     value = utc;
    00525   } else if (numArgs == 1) {
    00526     Value prim = args[0].toPrimitive(exec);
    00527     if (prim.isA(StringType))
    00528       value = parseDate(prim.toString(exec));
    00529     else
    00530       value = prim.toNumber(exec);
    00531   } else {
    00532     struct tm t;
    00533     memset(&t, 0, sizeof(t));
    00534     int year = args[0].toInt32(exec);
    00535     // TODO: check for NaN
    00536     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
    00537     t.tm_mon = args[1].toInt32(exec);
    00538     t.tm_mday = (numArgs >= 3) ? args[2].toInt32(exec) : 1;
    00539     t.tm_hour = (numArgs >= 4) ? args[3].toInt32(exec) : 0;
    00540     t.tm_min = (numArgs >= 5) ? args[4].toInt32(exec) : 0;
    00541     t.tm_sec = (numArgs >= 6) ? args[5].toInt32(exec) : 0;
    00542     t.tm_isdst = -1;
    00543     int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
    00544     value = makeTime(&t, ms, false);
    00545   }
    00546
    00547   Object proto = exec->interpreter()->builtinDatePrototype();
    00548   Object ret(new DateInstanceImp(proto.imp()));
    00549   ret.setInternalValue(Number(timeClip(value)));
    00550   return ret;
    00551 }
    00552
    00553 bool DateObjectImp::implementsCall() const
    00554 {
    00555   return true;
    00556 }
    00557
    00558 // ECMA 15.9.2
    00559 Value DateObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
    00560 {
    00561 #ifdef KJS_VERBOSE
    00562   fprintf(stderr,"DateObjectImp::call - current time\n");
    00563 #endif
    00564   time_t t = time(0L);
    00565   UString s(ctime(&t));
    00566
    00567   // return formatted string minus trailing \n
    00568   return String(s.substr(0, s.size() - 1));
    00569 }
    00570
    00571 // ------------------------------ DateObjectFuncImp ----------------------------
    00572
    00573 DateObjectFuncImp::DateObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto,
    00574                                      int i, int len)
    00575   : InternalFunctionImp(funcProto), id(i)
    00576 {
    00577   Value protect(this);
    00578   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
    00579 }
    00580
    00581 bool DateObjectFuncImp::implementsCall() const
    00582 {
    00583   return true;
    00584 }
    00585
    00586 // ECMA 15.9.4.2 - 3
    00587 Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
    00588 {
    00589   if (id == Parse) {
    00590     return Number(parseDate(args[0].toString(exec)));
    00591   } else { // UTC
    00592     struct tm t;
    00593     memset(&t, 0, sizeof(t));
    00594     int n = args.size();
    00595     int year = args[0].toInt32(exec);
    00596     // TODO: check for NaN
    00597     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
    00598     t.tm_mon = args[1].toInt32(exec);
    00599     t.tm_mday = (n >= 3) ? args[2].toInt32(exec) : 1;
    00600     t.tm_hour = (n >= 4) ? args[3].toInt32(exec) : 0;
    00601     t.tm_min = (n >= 5) ? args[4].toInt32(exec) : 0;
    00602     t.tm_sec = (n >= 6) ? args[5].toInt32(exec) : 0;
    00603     int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
    00604     return Number(makeTime(&t, ms, true));
    00605   }
    00606 }
    00607
    00608 // -----------------------------------------------------------------------------
    00609
    00610
    00611 double KJS::parseDate(const UString &u)
    00612 {
    00613 #ifdef KJS_VERBOSE
    00614   fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
    00615 #endif
    00616   double /*time_t*/ seconds = KRFCDate_parseDate( u );
    00617
    00618   return seconds == -1 ? NaN : seconds * 1000.0;
    00619 }
    00620
    00622
    00623 static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
    00624 {
    00625     //printf("year=%d month=%d day=%d hour=%d minute=%d second=%d\n", year, mon, day, hour, minute, second);
    00626
    00627     double ret = (day - 32075)       /* days */
    00628             + 1461L * (year + 4800L + (mon - 14) / 12) / 4
    00629             + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
    00630             - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
    00631             - 2440588;
    00632     ret = 24*ret + hour;     /* hours   */
    00633     ret = 60*ret + minute;   /* minutes */
    00634     ret = 60*ret + second;   /* seconds */
    00635
    00636     return ret;
    00637 }
    00638
    00639 // we follow the recommendation of rfc2822 to consider all
    00640 // obsolete time zones not listed here equivalent to "-0000"
    00641 static const struct {
    00642 #ifdef _WIN32
    00643     char tzName[4];
    00644 #else
    00645     const char tzName[4];
    00646 #endif
    00647     int tzOffset;
    00648 } known_zones[] = {
    00649     { "UT", 0 },
    00650     { "GMT", 0 },
    00651     { "EST", -300 },
    00652     { "EDT", -240 },
    00653     { "CST", -360 },
    00654     { "CDT", -300 },
    00655     { "MST", -420 },
    00656     { "MDT", -360 },
    00657     { "PST", -480 },
    00658     { "PDT", -420 },
    00659     { { 0, 0, 0, 0 }, 0 }
    00660 };
    00661
    00662 double KJS::makeTime(struct tm *t, int ms, bool utc)
    00663 {
    00664     int utcOffset;
    00665     if (utc) {
    00666     time_t zero = 0;
    00667 #if defined BSD || defined(__linux__) || defined(__APPLE__)
    00668     struct tm t3;
    00669         localtime_r(&zero, &t3);
    00670         utcOffset = t3.tm_gmtoff;
    00671         t->tm_isdst = t3.tm_isdst;
    00672 #else
    00673         (void)localtime(&zero);
    00674 #  if defined(__BORLANDC__)
    00675         utcOffset = - _timezone;
    00676 #  else
    00677         utcOffset = - timezone;
    00678 #  endif
    00679         t->tm_isdst = 0;
    00680 #endif
    00681     } else {
    00682     utcOffset = 0;
    00683     t->tm_isdst = -1;
    00684     }
    00685
    00686     double yearOffset = 0.0;
    00687     if (t->tm_year < (1970 - 1900) || t->tm_year > (2038 - 1900)) {
    00688       // we'll fool mktime() into believing that this year is within
    00689       // its normal, portable range (1970-2038) by setting tm_year to
    00690       // 2000 or 2001 and adding the difference in milliseconds later.
    00691       // choice between offset will depend on whether the year is a
    00692       // leap year or not.
    00693       int y = t->tm_year + 1900;
    00694       int baseYear = daysInYear(y) == 365 ? 2001 : 2000;
    00695       const double baseTime = timeFromYear(baseYear);
    00696       yearOffset = timeFromYear(y) - baseTime;
    00697       t->tm_year = baseYear - 1900;
    00698     }
    00699
    00700     return (mktime(t) + utcOffset) * 1000.0 + ms + yearOffset;
    00701 }
    00702
    00703 // returns 0-11 (Jan-Dec); -1 on failure
    00704 static int findMonth(const char *monthStr)
    00705 {
    00706   assert(monthStr);
    00707   static const char haystack[37] = "janfebmaraprmayjunjulaugsepoctnovdec";
    00708   char needle[4];
    00709   for (int i = 0; i < 3; ++i) {
    00710     if (!*monthStr)
    00711       return -1;
    00712     needle[i] = tolower(*monthStr++);
    00713   }
    00714   needle[3] = '\0';
    00715   const char *str = strstr(haystack, needle);
    00716   if (str) {
    00717     int position = str - haystack;
    00718     if (position % 3 == 0) {
    00719       return position / 3;
    00720     }
    00721   }
    00722   return -1;
    00723 }
    00724
    00725 double KJS::KRFCDate_parseDate(const UString &_date)
    00726 {
    00727      // This parse a date in the form:
    00728      //     Wednesday, 09-Nov-99 23:12:40 GMT
    00729      // or
    00730      //     Sat, 01-Jan-2000 08:00:00 GMT
    00731      // or
    00732      //     Sat, 01 Jan 2000 08:00:00 GMT
    00733      // or
    00734      //     01 Jan 99 22:00 +0100    (exceptions in rfc822/rfc2822)
    00735      // ### non RFC formats, added for Javascript:
    00736      //     [Wednesday] January 09 1999 23:12:40 GMT
    00737      //     [Wednesday] January 09 23:12:40 GMT 1999
    00738      //
    00739      // We ignore the weekday
    00740      //
    00741      double result = -1;
    00742      int offset = 0;
    00743      bool have_tz = false;
    00744      char *newPosStr;
    00745      const char *dateString = _date.ascii();
    00746      int day = 0;
    00747      int month = -1; // not set yet
    00748      int year = 0;
    00749      int hour = 0;
    00750      int minute = 0;
    00751      int second = 0;
    00752      bool have_time = false;
    00753
    00754      // Skip leading space
    00755      while(*dateString && isspace(*dateString))
    00756         dateString++;
    00757
    00758      const char *wordStart = dateString;
    00759      // Check contents of first words if not number
    00760      while(*dateString && !isdigit(*dateString))
    00761      {
    00762         if ( isspace(*dateString) && dateString - wordStart >= 3 )
    00763         {
    00764           month = findMonth(wordStart);
    00765           while(*dateString && isspace(*dateString))
    00766              dateString++;
    00767           wordStart = dateString;
    00768         }
    00769         else
    00770            dateString++;
    00771      }
    00772      // missing delimiter between month and day (like "January29")?
    00773      if (month == -1 && dateString && wordStart != dateString) {
    00774        month = findMonth(wordStart);
    00775        // TODO: emit warning about dubious format found
    00776      }
    00777
    00778      while(*dateString && isspace(*dateString))
    00779         dateString++;
    00780
    00781      if (!*dateString)
    00782         return invalidDate;
    00783
    00784      // ' 09-Nov-99 23:12:40 GMT'
    00785      day = strtol(dateString, &newPosStr, 10);
    00786      dateString = newPosStr;
    00787
    00788      if (!*dateString)
    00789         return invalidDate;
    00790
    00791      if (day < 1)
    00792        return invalidDate;
    00793      if (day > 31) {
    00794        // ### where is the boundary and what happens below?
    00795        if (*dateString == '/' && day >= 1000) {
    00796          // looks like a YYYY/MM/DD date
    00797          if (!*++dateString)
    00798            return invalidDate;
    00799          year = day;
    00800          month = strtol(dateString, &newPosStr, 10) - 1;
    00801          dateString = newPosStr;
    00802          if (*dateString++ != '/' || !*dateString)
    00803            return invalidDate;
    00804          day = strtol(dateString, &newPosStr, 10);
    00805          dateString = newPosStr;
    00806        } else {
    00807          return invalidDate;
    00808        }
    00809      } else if (*dateString == '/' && day <= 12 && month == -1)
    00810      {
    00811         dateString++;
    00812         // This looks like a MM/DD/YYYY date, not an RFC date.....
    00813         month = day - 1; // 0-based
    00814         day = strtol(dateString, &newPosStr, 10);
    00815         dateString = newPosStr;
    00816         if (*dateString == '/')
    00817           dateString++;
    00818         if (!*dateString)
    00819           return invalidDate;
    00820         //printf("month=%d day=%d dateString=%s\n", month, day, dateString);
    00821      }
    00822      else
    00823      {
    00824        if (*dateString == '-')
    00825          dateString++;
    00826
    00827        while(*dateString && isspace(*dateString))
    00828          dateString++;
    00829
    00830        if (*dateString == ',')
    00831          dateString++;
    00832
    00833        if ( month == -1 ) // not found yet
    00834        {
    00835          month = findMonth(dateString);
    00836          if (month == -1)
    00837            return invalidDate;
    00838
    00839          while(*dateString && (*dateString != '-') && !isspace(*dateString))
    00840            dateString++;
    00841
    00842          if (!*dateString)
    00843            return invalidDate;
    00844
    00845          // '-99 23:12:40 GMT'
    00846          if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
    00847            return invalidDate;
    00848          dateString++;
    00849        }
    00850
    00851        if ((month < 0) || (month > 11))
    00852          return invalidDate;
    00853      }
    00854
    00855      // '99 23:12:40 GMT'
    00856      if (year <= 0 && *dateString)
    00857        year = strtol(dateString, &newPosStr, 10);
    00858
    00859      // Don't fail if the time is missing.
    00860      if (*newPosStr)
    00861      {
    00862         // ' 23:12:40 GMT'
    00863         if (!isspace(*newPosStr)) {
    00864            if ( *newPosStr == ':' ) // Ah, so there was no year, but the number was the hour
    00865                year = -1;
    00866            else
    00867                return invalidDate;
    00868         } else // in the normal case (we parsed the year), advance to the next number
    00869             dateString = ++newPosStr;
    00870
    00871         hour = strtol(dateString, &newPosStr, 10);
    00872         // read a number? if not this might be a timezone name
    00873         if (newPosStr != dateString) {
    00874           have_time = true;
    00875           dateString = newPosStr;
    00876
    00877           if ((hour < 0) || (hour > 23))
    00878             return invalidDate;
    00879
    00880           if (!*dateString)
    00881             return invalidDate;
    00882
    00883           // ':12:40 GMT'
    00884           if (*dateString++ != ':')
    00885             return invalidDate;
    00886
    00887           minute = strtol(dateString, &newPosStr, 10);
    00888           dateString = newPosStr;
    00889
    00890           if ((minute < 0) || (minute > 59))
    00891             return invalidDate;
    00892
    00893           // ':40 GMT'
    00894           if (*dateString && *dateString != ':' && !isspace(*dateString))
    00895             return invalidDate;
    00896
    00897           // seconds are optional in rfc822 + rfc2822
    00898           if (*dateString ==':') {
    00899             dateString++;
    00900
    00901             second = strtol(dateString, &newPosStr, 10);
    00902             dateString = newPosStr;
    00903
    00904             if ((second < 0) || (second > 59))
    00905               return invalidDate;
    00906           }
    00907
    00908           while(*dateString && isspace(*dateString))
    00909             dateString++;
    00910         }
    00911      } else {
    00912        dateString = newPosStr;
    00913      }
    00914
    00915      // don't fail if the time zone is missing, some
    00916      // broken mail-/news-clients omit the time zone
    00917      if (*dateString) {
    00918
    00919        if ( (dateString[0] == 'G' && dateString[1] == 'M' && dateString[2] == 'T')
    00920             || (dateString[0] == 'U' && dateString[1] == 'T' && dateString[2] == 'C') )
    00921        {
    00922          dateString += 3;
    00923          have_tz = true;
    00924        }
    00925
    00926        while (*dateString && isspace(*dateString))
    00927          ++dateString;
    00928
    00929        if (strncasecmp(dateString, "GMT", 3) == 0) {
    00930          dateString += 3;
    00931        }
    00932        if ((*dateString == '+') || (*dateString == '-')) {
    00933          offset = strtol(dateString, &newPosStr, 10);
    00934          dateString = newPosStr;
    00935
    00936          if ((offset < -9959) || (offset > 9959))
    00937             return invalidDate;
    00938
    00939          int sgn = (offset < 0)? -1:1;
    00940          offset = abs(offset);
    00941          if ( *dateString == ':' ) { // GMT+05:00
    00942            int offset2 = strtol(dateString, &newPosStr, 10);
    00943            dateString = newPosStr;
    00944            offset = (offset*60 + offset2)*sgn;
    00945          }
    00946          else
    00947            offset = ((offset / 100)*60 + (offset % 100))*sgn;
    00948          have_tz = true;
    00949        } else {
    00950          for (int i=0; known_zones[i].tzName != 0; i++) {
    00951            if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
    00952              offset = known_zones[i].tzOffset;
    00953              have_tz = true;
    00954              break;
    00955            }
    00956          }
    00957        }
    00958      }
    00959
    00960      while(*dateString && isspace(*dateString))
    00961         dateString++;
    00962
    00963      if ( *dateString && year == -1 ) {
    00964        year = strtol(dateString, &newPosStr, 10);
    00965      }
    00966
    00967      // Y2K: Solve 2 digit years
    00968      if ((year >= 0) && (year < 50))
    00969          year += 2000;
    00970
    00971      if ((year >= 50) && (year < 100))
    00972          year += 1900;  // Y2K
    00973
    00974      if ((year < 1900) || (year > 2500))
    00975         return invalidDate;
    00976
    00977      if (!have_tz) {
    00978        // fall back to midnight, local timezone
    00979        struct tm t;
    00980        memset(&t, 0, sizeof(tm));
    00981        t.tm_mday = day;
    00982        t.tm_mon = month;
    00983        t.tm_year = year - 1900;
    00984        t.tm_isdst = -1;
    00985        if (have_time) {
    00986          t.tm_sec = second;
    00987          t.tm_min = minute;
    00988          t.tm_hour = hour;
    00989        }
    00990
    00991        // better not use mktime() as it can't handle the full year range
    00992        return makeTime(&t, 0, false) / 1000.0;
    00993      }
    00994
    00995      offset *= 60;
    00996
    00997      result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
    00998
    00999      // avoid negative time values
    01000      if ((offset > 0) && (offset > result))
    01001         offset = 0;
    01002
    01003      result -= offset;
    01004
    01005      // If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT
    01006      // This is so that parse error and valid epoch 0 return values won't
    01007      // be the same for sensitive applications...
    01008      if (result < 1) result = 1;
    01009
    01010      return result;
    01011 }
    01012
    01013
    01014 double KJS::timeClip(double t)
    01015 {
    01016   if (isInf(t) || fabs(t) > 8.64E15)
    01017     return NaN;
    01018   return t;
    01019 }
    01020

This file is part of the documentation for kjs Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:23:05 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003