kjs Library API Documentation

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 #ifdef KJS_VERBOSE
00618   fprintf(stderr,"KRFCDate_parseDate returned seconds=%g\n",seconds);
00619   bool withinLimits = true;
00620   if ( sizeof(time_t) == 4 )
00621   {
00622     int limit = ((time_t)-1 < 0) ? 2038 : 2115;
00623     if ( seconds > (limit-1970) * 365.25 * 86400 ) {
00624       fprintf(stderr, "date above time_t limit. Year seems to be %d\n", (int)(seconds/(365.25*86400)+1970));
00625       withinLimits = false;
00626     }
00627   }
00628   if ( withinLimits ) {
00629     time_t lsec = (time_t)seconds;
00630     fprintf(stderr, "this is: %s\n", ctime(&lsec));
00631   }
00632 #endif
00633 
00634   return seconds == -1 ? NaN : seconds * 1000.0;
00635 }
00636 
00638 
00639 static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
00640 {
00641     //printf("year=%d month=%d day=%d hour=%d minute=%d second=%d\n", year, mon, day, hour, minute, second);
00642 
00643     double ret = (day - 32075)       /* days */
00644             + 1461L * (year + 4800L + (mon - 14) / 12) / 4
00645             + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
00646             - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
00647             - 2440588;
00648     ret = 24*ret + hour;     /* hours   */
00649     ret = 60*ret + minute;   /* minutes */
00650     ret = 60*ret + second;   /* seconds */
00651 
00652     return ret;
00653 }
00654 
00655 static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
00656 
00657 // we follow the recommendation of rfc2822 to consider all
00658 // obsolete time zones not listed here equivalent to "-0000"
00659 static const struct {
00660 #ifdef _WIN32
00661     char tzName[4];
00662 #else
00663     const char tzName[4];
00664 #endif
00665     int tzOffset;
00666 } known_zones[] = {
00667     { "UT", 0 },
00668     { "GMT", 0 },
00669     { "EST", -300 },
00670     { "EDT", -240 },
00671     { "CST", -360 },
00672     { "CDT", -300 },
00673     { "MST", -420 },
00674     { "MDT", -360 },
00675     { "PST", -480 },
00676     { "PDT", -420 },
00677     { { 0, 0, 0, 0 }, 0 }
00678 };
00679 
00680 double KJS::makeTime(struct tm *t, int ms, bool utc)
00681 {
00682     int utcOffset;
00683     if (utc) {
00684     time_t zero = 0;
00685 #if defined BSD || defined(__linux__) || defined(__APPLE__)
00686     struct tm t3;
00687         localtime_r(&zero, &t3);
00688         utcOffset = t3.tm_gmtoff;
00689         t->tm_isdst = t3.tm_isdst;
00690 #else
00691         (void)localtime(&zero);
00692 #  if defined(__BORLANDC__)
00693         utcOffset = - _timezone;
00694 #  else
00695         utcOffset = - timezone;
00696 #  endif
00697         t->tm_isdst = 0;
00698 #endif
00699     } else {
00700     utcOffset = 0;
00701     t->tm_isdst = -1;
00702     }
00703 
00704     double yearOffset = 0.0;
00705     if (t->tm_year < (1970 - 1900) || t->tm_year > (2038 - 1900)) {
00706       // we'll fool mktime() into believing that this year is within
00707       // its normal, portable range (1970-2038) by setting tm_year to
00708       // 2000 or 2001 and adding the difference in milliseconds later.
00709       // choice between offset will depend on whether the year is a
00710       // leap year or not.
00711       int y = t->tm_year + 1900;
00712       int baseYear = daysInYear(y) == 365 ? 2001 : 2000;
00713       const double baseTime = timeFromYear(baseYear);
00714       yearOffset = timeFromYear(y) - baseTime;
00715       t->tm_year = baseYear - 1900;
00716     }
00717 
00718     return (mktime(t) + utcOffset) * 1000.0 + ms + yearOffset;
00719 }
00720 
00721 double KJS::KRFCDate_parseDate(const UString &_date)
00722 {
00723      // This parse a date in the form:
00724      //     Wednesday, 09-Nov-99 23:12:40 GMT
00725      // or
00726      //     Sat, 01-Jan-2000 08:00:00 GMT
00727      // or
00728      //     Sat, 01 Jan 2000 08:00:00 GMT
00729      // or
00730      //     01 Jan 99 22:00 +0100    (exceptions in rfc822/rfc2822)
00731      // ### non RFC formats, added for Javascript:
00732      //     [Wednesday] January 09 1999 23:12:40 GMT
00733      //     [Wednesday] January 09 23:12:40 GMT 1999
00734      //
00735      // We ignore the weekday
00736      //
00737      double result = -1;
00738      int offset = 0;
00739      bool have_tz = false;
00740      char *newPosStr;
00741      const char *dateString = _date.ascii();
00742      int day = 0;
00743      char monthStr[4];
00744      int month = -1; // not set yet
00745      int year = 0;
00746      int hour = 0;
00747      int minute = 0;
00748      int second = 0;
00749      bool have_time = false;
00750 
00751      // Skip leading space
00752      while(*dateString && isspace(*dateString))
00753         dateString++;
00754 
00755      const char *wordStart = dateString;
00756      // Check contents of first words if not number
00757      while(*dateString && !isdigit(*dateString))
00758      {
00759         if ( isspace(*dateString) && dateString - wordStart >= 3 )
00760         {
00761           monthStr[0] = tolower(*wordStart++);
00762           monthStr[1] = tolower(*wordStart++);
00763           monthStr[2] = tolower(*wordStart++);
00764           monthStr[3] = '\0';
00765           //fprintf(stderr,"KJS::parseDate found word starting with '%s'\n", monthStr);
00766           const char *str = strstr(haystack, monthStr);
00767           if (str) {
00768             int position = str - haystack;
00769             if (position % 3 == 0) {
00770               month = position / 3; // Jan=00, Feb=01, Mar=02, ..
00771             }
00772           }
00773           while(*dateString && isspace(*dateString))
00774              dateString++;
00775           wordStart = dateString;
00776         }
00777         else
00778            dateString++;
00779      }
00780 
00781      while(*dateString && isspace(*dateString))
00782         dateString++;
00783 
00784      if (!*dateString)
00785         return invalidDate;
00786 
00787      // ' 09-Nov-99 23:12:40 GMT'
00788      day = strtol(dateString, &newPosStr, 10);
00789      dateString = newPosStr;
00790 
00791      if (!*dateString)
00792         return invalidDate;
00793 
00794      if (day < 1)
00795        return invalidDate;
00796      if (day > 31) {
00797        // ### where is the boundary and what happens below?
00798        if (*dateString == '/' && day >= 1000) {
00799          // looks like a YYYY/MM/DD date
00800          if (!*++dateString)
00801            return invalidDate;
00802          year = day;
00803          month = strtol(dateString, &newPosStr, 10) - 1;
00804          dateString = newPosStr;
00805          if (*dateString++ != '/' || !*dateString)
00806            return invalidDate;
00807          day = strtol(dateString, &newPosStr, 10);
00808          dateString = newPosStr;
00809        } else {
00810          return invalidDate;
00811        }
00812      } else if (*dateString == '/' && day <= 12 && month == -1)
00813      {
00814         dateString++;
00815         // This looks like a MM/DD/YYYY date, not an RFC date.....
00816         month = day - 1; // 0-based
00817         day = strtol(dateString, &newPosStr, 10);
00818         dateString = newPosStr;
00819         if (*dateString == '/')
00820           dateString++;
00821         if (!*dateString)
00822           return invalidDate;
00823         //printf("month=%d day=%d dateString=%s\n", month, day, dateString);
00824      }
00825      else
00826      {
00827        if (*dateString == '-')
00828          dateString++;
00829 
00830        while(*dateString && isspace(*dateString))
00831          dateString++;
00832 
00833        if (*dateString == ',')
00834          dateString++;
00835 
00836        if ( month == -1 ) // not found yet
00837        {
00838          for(int i=0; i < 3;i++)
00839          {
00840            if (!*dateString || (*dateString == '-') || isspace(*dateString))
00841              return invalidDate;
00842            monthStr[i] = tolower(*dateString++);
00843          }
00844          monthStr[3] = '\0';
00845 
00846          newPosStr = (char*)strstr(haystack, monthStr);
00847 
00848          if (!newPosStr || (newPosStr - haystack) % 3 != 0)
00849            return invalidDate;
00850 
00851          month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
00852 
00853          if ((month < 0) || (month > 11))
00854            return invalidDate;
00855 
00856          while(*dateString && (*dateString != '-') && !isspace(*dateString))
00857            dateString++;
00858 
00859          if (!*dateString)
00860            return invalidDate;
00861 
00862          // '-99 23:12:40 GMT'
00863          if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
00864            return invalidDate;
00865          dateString++;
00866        }
00867 
00868        if ((month < 0) || (month > 11))
00869          return invalidDate;
00870      }
00871 
00872      // '99 23:12:40 GMT'
00873      if (year <= 0 && *dateString)
00874        year = strtol(dateString, &newPosStr, 10);
00875 
00876      // Don't fail if the time is missing.
00877      if (*newPosStr)
00878      {
00879         // ' 23:12:40 GMT'
00880         if (!isspace(*newPosStr)) {
00881            if ( *newPosStr == ':' ) // Ah, so there was no year, but the number was the hour
00882                year = -1;
00883            else
00884                return invalidDate;
00885         } else // in the normal case (we parsed the year), advance to the next number
00886             dateString = ++newPosStr;
00887 
00888         have_time = true;
00889         hour = strtol(dateString, &newPosStr, 10);
00890         dateString = newPosStr;
00891 
00892         if ((hour < 0) || (hour > 23))
00893            return invalidDate;
00894 
00895         if (!*dateString)
00896            return invalidDate;
00897 
00898         // ':12:40 GMT'
00899         if (*dateString++ != ':')
00900            return invalidDate;
00901 
00902         minute = strtol(dateString, &newPosStr, 10);
00903         dateString = newPosStr;
00904 
00905         if ((minute < 0) || (minute > 59))
00906            return invalidDate;
00907 
00908         // ':40 GMT'
00909         if (*dateString && *dateString != ':' && !isspace(*dateString))
00910            return invalidDate;
00911 
00912         // seconds are optional in rfc822 + rfc2822
00913         if (*dateString ==':') {
00914            dateString++;
00915 
00916            second = strtol(dateString, &newPosStr, 10);
00917            dateString = newPosStr;
00918 
00919            if ((second < 0) || (second > 59))
00920               return invalidDate;
00921         }
00922 
00923         while(*dateString && isspace(*dateString))
00924            dateString++;
00925      }
00926      else
00927        dateString = newPosStr;
00928 
00929 
00930      // don't fail if the time zone is missing, some
00931      // broken mail-/news-clients omit the time zone
00932      if (*dateString) {
00933 
00934        if ( (dateString[0] == 'G' && dateString[1] == 'M' && dateString[2] == 'T')
00935             || (dateString[0] == 'U' && dateString[1] == 'T' && dateString[2] == 'C') )
00936        {
00937          dateString += 3;
00938          have_tz = true;
00939        }
00940 
00941        while (*dateString && isspace(*dateString))
00942          ++dateString;
00943 
00944        if (strncasecmp(dateString, "GMT", 3) == 0) {
00945          dateString += 3;
00946        }
00947        if ((*dateString == '+') || (*dateString == '-')) {
00948          offset = strtol(dateString, &newPosStr, 10);
00949          dateString = newPosStr;
00950 
00951          if ((offset < -9959) || (offset > 9959))
00952             return invalidDate;
00953 
00954          int sgn = (offset < 0)? -1:1;
00955          offset = abs(offset);
00956          if ( *dateString == ':' ) { // GMT+05:00
00957            int offset2 = strtol(dateString, &newPosStr, 10);
00958            dateString = newPosStr;
00959            offset = (offset*60 + offset2)*sgn;
00960          }
00961          else
00962            offset = ((offset / 100)*60 + (offset % 100))*sgn;
00963          have_tz = true;
00964        } else {
00965          for (int i=0; known_zones[i].tzName != 0; i++) {
00966            if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
00967              offset = known_zones[i].tzOffset;
00968              have_tz = true;
00969              break;
00970            }
00971          }
00972        }
00973      }
00974 
00975      while(*dateString && isspace(*dateString))
00976         dateString++;
00977 
00978      if ( *dateString && year == -1 ) {
00979        year = strtol(dateString, &newPosStr, 10);
00980      }
00981 
00982      // Y2K: Solve 2 digit years
00983      if ((year >= 0) && (year < 50))
00984          year += 2000;
00985 
00986      if ((year >= 50) && (year < 100))
00987          year += 1900;  // Y2K
00988 
00989      if ((year < 1900) || (year > 2500))
00990         return invalidDate;
00991 
00992      if (!have_tz) {
00993        // fall back to midnight, local timezone
00994        struct tm t;
00995        memset(&t, 0, sizeof(tm));
00996        t.tm_mday = day;
00997        t.tm_mon = month;
00998        t.tm_year = year - 1900;
00999        t.tm_isdst = -1;
01000        if (have_time) {
01001          t.tm_sec = second;
01002          t.tm_min = minute;
01003          t.tm_hour = hour;
01004        }
01005 
01006        return mktime(&t);
01007      }
01008 
01009      offset *= 60;
01010 
01011      result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
01012 
01013      // avoid negative time values
01014      if ((offset > 0) && (offset > result))
01015         offset = 0;
01016 
01017      result -= offset;
01018 
01019      // If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT
01020      // This is so that parse error and valid epoch 0 return values won't
01021      // be the same for sensitive applications...
01022      if (result < 1) result = 1;
01023 
01024      return result;
01025 }
01026 
01027 
01028 double KJS::timeClip(double t)
01029 {
01030   if (isInf(t) || fabs(t) > 8.64E15)
01031     return NaN;
01032   return t;
01033 }
01034 
KDE Logo
This file is part of the documentation for kjs Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed May 4 06:54:58 2005 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003