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 #ifndef HAVE_SYS_TIMEB_H
00027 #define HAVE_SYS_TIMEB_H 0
00028 #endif
00029 
00030 #if TIME_WITH_SYS_TIME
00031 # include <sys/time.h>
00032 # include <time.h>
00033 #else
00034 #if HAVE_SYS_TIME_H
00035 #include <sys/time.h>
00036 #else
00037 #  include <time.h>
00038 # endif
00039 #endif
00040 #if HAVE_SYS_TIMEB_H
00041 #include <sys/timeb.h>
00042 #endif
00043 
00044 #ifdef HAVE_SYS_PARAM_H
00045 #  include <sys/param.h>
00046 #endif // HAVE_SYS_PARAM_H
00047 
00048 #include <math.h>
00049 #include <string.h>
00050 #include <stdio.h>
00051 #include <stdlib.h>
00052 #include <locale.h>
00053 #include <ctype.h>
00054 
00055 #include "date_object.h"
00056 #include "error_object.h"
00057 #include "operations.h"
00058 
00059 #include "date_object.lut.h"
00060 
00061 const time_t invalidDate = -1;
00062 
00063 using namespace KJS;
00064 
00065 // ------------------------------ DateInstanceImp ------------------------------
00066 
00067 const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
00068 
00069 DateInstanceImp::DateInstanceImp(ObjectImp *proto)
00070   : ObjectImp(proto)
00071 {
00072 }
00073 
00074 // ------------------------------ DatePrototypeImp -----------------------------
00075 
00076 const ClassInfo DatePrototypeImp::info = {"Date", 0, &dateTable, 0};
00077 
00078 /* Source for date_object.lut.h
00079    We use a negative ID to denote the "UTC" variant.
00080 @begin dateTable 61
00081   toString      DateProtoFuncImp::ToString      DontEnum|Function   0
00082   toUTCString       DateProtoFuncImp::ToUTCString       DontEnum|Function   0
00083   toDateString      DateProtoFuncImp::ToDateString      DontEnum|Function   0
00084   toTimeString      DateProtoFuncImp::ToTimeString      DontEnum|Function   0
00085   toLocaleString    DateProtoFuncImp::ToLocaleString    DontEnum|Function   0
00086   toLocaleDateString    DateProtoFuncImp::ToLocaleDateString    DontEnum|Function   0
00087   toLocaleTimeString    DateProtoFuncImp::ToLocaleTimeString    DontEnum|Function   0
00088   valueOf       DateProtoFuncImp::ValueOf       DontEnum|Function   0
00089   getTime       DateProtoFuncImp::GetTime       DontEnum|Function   0
00090   getFullYear       DateProtoFuncImp::GetFullYear       DontEnum|Function   0
00091   getUTCFullYear    -DateProtoFuncImp::GetFullYear      DontEnum|Function   0
00092   toGMTString       DateProtoFuncImp::ToGMTString       DontEnum|Function   0
00093   getMonth      DateProtoFuncImp::GetMonth      DontEnum|Function   0
00094   getUTCMonth       -DateProtoFuncImp::GetMonth     DontEnum|Function   0
00095   getDate       DateProtoFuncImp::GetDate       DontEnum|Function   0
00096   getUTCDate        -DateProtoFuncImp::GetDate      DontEnum|Function   0
00097   getDay        DateProtoFuncImp::GetDay        DontEnum|Function   0
00098   getUTCDay     -DateProtoFuncImp::GetDay       DontEnum|Function   0
00099   getHours      DateProtoFuncImp::GetHours      DontEnum|Function   0
00100   getUTCHours       -DateProtoFuncImp::GetHours     DontEnum|Function   0
00101   getMinutes        DateProtoFuncImp::GetMinutes        DontEnum|Function   0
00102   getUTCMinutes     -DateProtoFuncImp::GetMinutes       DontEnum|Function   0
00103   getSeconds        DateProtoFuncImp::GetSeconds        DontEnum|Function   0
00104   getUTCSeconds     -DateProtoFuncImp::GetSeconds       DontEnum|Function   0
00105   getMilliseconds   DateProtoFuncImp::GetMilliSeconds   DontEnum|Function   0
00106   getUTCMilliseconds    -DateProtoFuncImp::GetMilliSeconds  DontEnum|Function   0
00107   getTimezoneOffset DateProtoFuncImp::GetTimezoneOffset DontEnum|Function   0
00108   setTime       DateProtoFuncImp::SetTime       DontEnum|Function   1
00109   setMilliseconds   DateProtoFuncImp::SetMilliSeconds   DontEnum|Function   1
00110   setUTCMilliseconds    -DateProtoFuncImp::SetMilliSeconds  DontEnum|Function   1
00111   setSeconds        DateProtoFuncImp::SetSeconds        DontEnum|Function   2
00112   setUTCSeconds     -DateProtoFuncImp::SetSeconds       DontEnum|Function   2
00113   setMinutes        DateProtoFuncImp::SetMinutes        DontEnum|Function   3
00114   setUTCMinutes     -DateProtoFuncImp::SetMinutes       DontEnum|Function   3
00115   setHours      DateProtoFuncImp::SetHours      DontEnum|Function   4
00116   setUTCHours       -DateProtoFuncImp::SetHours     DontEnum|Function   4
00117   setDate       DateProtoFuncImp::SetDate       DontEnum|Function   1
00118   setUTCDate        -DateProtoFuncImp::SetDate      DontEnum|Function   1
00119   setMonth      DateProtoFuncImp::SetMonth      DontEnum|Function   2
00120   setUTCMonth       -DateProtoFuncImp::SetMonth     DontEnum|Function   2
00121   setFullYear       DateProtoFuncImp::SetFullYear       DontEnum|Function   3
00122   setUTCFullYear    -DateProtoFuncImp::SetFullYear      DontEnum|Function   3
00123   setYear       DateProtoFuncImp::SetYear       DontEnum|Function   1
00124   getYear       DateProtoFuncImp::GetYear       DontEnum|Function   0
00125   toGMTString       DateProtoFuncImp::ToGMTString       DontEnum|Function   0
00126 @end
00127 */
00128 // ECMA 15.9.4
00129 
00130 DatePrototypeImp::DatePrototypeImp(ExecState *,
00131                                    ObjectPrototypeImp *objectProto)
00132   : DateInstanceImp(objectProto)
00133 {
00134   Value protect(this);
00135   setInternalValue(Number(NaN));
00136   // The constructor will be added later, after DateObjectImp has been built
00137 }
00138 
00139 Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
00140 {
00141   return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
00142 }
00143 
00144 // ------------------------------ DateProtoFuncImp -----------------------------
00145 
00146 DateProtoFuncImp::DateProtoFuncImp(ExecState *exec, int i, int len)
00147   : InternalFunctionImp(
00148     static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
00149     ), id(abs(i)), utc(i<0)
00150   // We use a negative ID to denote the "UTC" variant.
00151 {
00152   Value protect(this);
00153   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
00154 }
00155 
00156 bool DateProtoFuncImp::implementsCall() const
00157 {
00158   return true;
00159 }
00160 
00161 Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
00162 {
00163   if ((id == ToString || id == ValueOf || id == GetTime || id == SetTime) &&
00164       !thisObj.inherits(&DateInstanceImp::info)) {
00165     // non-generic function called on non-date object
00166 
00167     // ToString and ValueOf are generic according to the spec, but the mozilla
00168     // tests suggest otherwise...
00169     Object err = Error::create(exec,TypeError);
00170     exec->setException(err);
00171     return err;
00172   }
00173 
00174 
00175   Value result;
00176   UString s;
00177   const int bufsize=100;
00178   char timebuffer[bufsize];
00179   CString oldlocale = setlocale(LC_TIME,NULL);
00180   if (!oldlocale.c_str())
00181     oldlocale = setlocale(LC_ALL, NULL);
00182   Value v = thisObj.internalValue();
00183   double milli = v.toNumber(exec);
00184   // special case: time value is NaN
00185   if (isNaN(milli)) {
00186     switch (id) {
00187     case ToString:
00188     case ToDateString:
00189     case ToTimeString:
00190     case ToGMTString:
00191     case ToUTCString:
00192     case ToLocaleString:
00193     case ToLocaleDateString:
00194     case ToLocaleTimeString:
00195       return String("Invalid Date");
00196     case ValueOf:
00197     case GetTime:
00198     case GetYear:
00199     case GetFullYear:
00200     case GetMonth:
00201     case GetDate:
00202     case GetDay:
00203     case GetHours:
00204     case GetMinutes:
00205     case GetSeconds:
00206     case GetMilliSeconds:
00207     case GetTimezoneOffset:
00208       return Number(NaN);
00209     }
00210   }
00211   time_t tv = (time_t) floor(milli / 1000.0);
00212   int ms = int(milli - tv * 1000.0);
00213 
00214   // As long as we're using time_t we need to 'truncate' to avoid 'wrapping'.
00215   // Real long term solutions include: writing our own 64-bit-based date/time class,
00216   // using wxWindow's datetime.cpp (in wxBase), using QDateTime... or shifting
00217   // to a time_t range by substracting a big enough number of years....
00218   if (sizeof(time_t) == 4)
00219   {
00220     // If time_t is signed, the bigger it can be is 2^31-1
00221     if ( (time_t)-1 < 0 ) {
00222       if ( floor(milli / 1000.0) > ((double)((uint)1<<31)-1) ) {
00223 #ifdef KJS_VERBOSE
00224         fprintf(stderr, "date above time_t limit. Year seems to be %d\n", (int)(milli/(1000.0*365.25*86400)+1970));
00225 #endif
00226         tv = ((uint)1<<31)-1;
00227         ms = 0;
00228       }
00229     }
00230     else
00231       // time_t is unsigned, the bigger it can be is 2^32-1, aka (uint)-1
00232       if ( floor(milli / 1000.0) > ((double)(uint)-1) ) {
00233 #ifdef KJS_VERBOSE
00234         fprintf(stderr, "date above time_t limit. Year seems to be %d\n", (int)(milli/(1000.0*365.25*86400)+1970));
00235 #endif
00236         tv = (uint)-1;
00237         ms = 0;
00238       }
00239   }
00240 
00241   struct tm *t;
00242   if (utc)
00243     t = gmtime(&tv);
00244   else
00245     t = localtime(&tv);
00246 
00247   // trick gcc. We don't want the Y2K warnings.
00248   const char xFormat[] = "%x";
00249   const char cFormat[] = "%c";
00250 
00251   switch (id) {
00252   case ToString:
00253   case ToDateString:
00254   case ToTimeString:
00255   case ToGMTString:
00256   case ToUTCString:
00257     setlocale(LC_TIME,"C");
00258     if (id == DateProtoFuncImp::ToDateString) {
00259       strftime(timebuffer, bufsize, xFormat, t);
00260     } else if (id == DateProtoFuncImp::ToTimeString) {
00261       strftime(timebuffer, bufsize, "%X",t);
00262     } else { // ToString, toGMTString & toUTCString
00263       t = (id == ToString ? localtime(&tv) : gmtime(&tv));
00264       strftime(timebuffer, bufsize, "%a, %d %b %Y %H:%M:%S %z", t);
00265     }
00266     setlocale(LC_TIME,oldlocale.c_str());
00267     result = String(timebuffer);
00268     break;
00269   case ToLocaleString:
00270     strftime(timebuffer, bufsize, cFormat, t);
00271     result = String(timebuffer);
00272     break;
00273   case ToLocaleDateString:
00274     strftime(timebuffer, bufsize, xFormat, t);
00275     result = String(timebuffer);
00276     break;
00277   case ToLocaleTimeString:
00278     strftime(timebuffer, bufsize, "%X", t);
00279     result = String(timebuffer);
00280     break;
00281   case ValueOf:
00282     result = Number(milli);
00283     break;
00284   case GetTime:
00285     result = Number(milli);
00286     break;
00287   case GetYear:
00288     // IE returns the full year even in getYear.
00289     if ( exec->interpreter()->compatMode() != Interpreter::IECompat )
00290       result = Number(t->tm_year);
00291     else
00292       result = Number(1900 + t->tm_year);
00293     break;
00294   case GetFullYear:
00295     result = Number(1900 + t->tm_year);
00296     break;
00297   case GetMonth:
00298     result = Number(t->tm_mon);
00299     break;
00300   case GetDate:
00301     result = Number(t->tm_mday);
00302     break;
00303   case GetDay:
00304     result = Number(t->tm_wday);
00305     break;
00306   case GetHours:
00307     result = Number(t->tm_hour);
00308     break;
00309   case GetMinutes:
00310     result = Number(t->tm_min);
00311     break;
00312   case GetSeconds:
00313     result = Number(t->tm_sec);
00314     break;
00315   case GetMilliSeconds:
00316     result = Number(ms);
00317     break;
00318   case GetTimezoneOffset:
00319 #if defined BSD || defined(__APPLE__)
00320     result = Number(-(t->tm_gmtoff / 60) + (t->tm_isdst > 0 ? 60 : 0));
00321 #else
00322 #  if defined(__BORLANDC__)
00323 #error please add daylight savings offset here!
00324     result = Number(_timezone / 60 - (t->tm_isdst > 0 ? 60 : 0));
00325 #  else
00326     result = Number((timezone / 60 - (t->tm_isdst > 0 ? 60 : 0 )));
00327 #  endif
00328 #endif
00329     break;
00330   case SetTime:
00331     milli = roundValue(exec,args[0]);
00332     result = Number(milli);
00333     thisObj.setInternalValue(result);
00334     break;
00335   case SetMilliSeconds:
00336     ms = args[0].toInt32(exec);
00337     break;
00338   case SetSeconds:
00339     t->tm_sec = args[0].toInt32(exec);
00340     if (args.size() >= 2)
00341       ms = args[1].toInt32(exec);
00342     break;
00343   case SetMinutes:
00344     t->tm_min = args[0].toInt32(exec);
00345     if (args.size() >= 2)
00346       t->tm_sec = args[1].toInt32(exec);
00347     if (args.size() >= 3)
00348       ms = args[2].toInt32(exec);
00349     break;
00350   case SetHours:
00351     t->tm_hour = args[0].toInt32(exec);
00352     if (args.size() >= 2)
00353       t->tm_min = args[1].toInt32(exec);
00354     if (args.size() >= 3)
00355       t->tm_sec = args[2].toInt32(exec);
00356     if (args.size() >= 4)
00357       ms = args[3].toInt32(exec);
00358     break;
00359   case SetDate:
00360     t->tm_mday = args[0].toInt32(exec);
00361     break;
00362   case SetMonth:
00363     t->tm_mon = args[0].toInt32(exec);
00364     if (args.size() >= 2)
00365       t->tm_mday = args[1].toInt32(exec);
00366     break;
00367   case SetFullYear:
00368     t->tm_year = args[0].toInt32(exec) - 1900;
00369     if (args.size() >= 2)
00370       t->tm_mon = args[1].toInt32(exec);
00371     if (args.size() >= 3)
00372       t->tm_mday = args[2].toInt32(exec);
00373     break;
00374   case SetYear:
00375     t->tm_year = args[0].toInt32(exec) >= 1900 ? args[0].toInt32(exec) - 1900 : args[0].toInt32(exec);
00376     break;
00377   }
00378 
00379   if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
00380       id == SetMinutes || id == SetHours || id == SetDate ||
00381       id == SetMonth || id == SetFullYear ) {
00382     result = Number(mktime(t) * 1000.0 + ms);
00383     thisObj.setInternalValue(result);
00384   }
00385 
00386   return result;
00387 }
00388 
00389 // ------------------------------ DateObjectImp --------------------------------
00390 
00391 // TODO: MakeTime (15.9.11.1) etc. ?
00392 
00393 DateObjectImp::DateObjectImp(ExecState *exec,
00394                              FunctionPrototypeImp *funcProto,
00395                              DatePrototypeImp *dateProto)
00396   : InternalFunctionImp(funcProto)
00397 {
00398   Value protect(this);
00399 
00400   // ECMA 15.9.4.1 Date.prototype
00401   putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
00402 
00403   static const Identifier parsePropertyName("parse");
00404   putDirect(parsePropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1), DontEnum);
00405   static const Identifier UTCPropertyName("UTC");
00406   putDirect(UTCPropertyName,   new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC,   7),   DontEnum);
00407 
00408   // no. of arguments for constructor
00409   putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
00410 }
00411 
00412 bool DateObjectImp::implementsConstruct() const
00413 {
00414   return true;
00415 }
00416 
00417 // ECMA 15.9.3
00418 Object DateObjectImp::construct(ExecState *exec, const List &args)
00419 {
00420   int numArgs = args.size();
00421 
00422 #ifdef KJS_VERBOSE
00423   fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
00424 #endif
00425   Value value;
00426 
00427   if (numArgs == 0) { // new Date() ECMA 15.9.3.3
00428 #if HAVE_SYS_TIMEB_H
00429 #  if defined(__BORLANDC__)
00430     struct timeb timebuffer;
00431     ftime(&timebuffer);
00432 #  else
00433     struct _timeb timebuffer;
00434     _ftime(&timebuffer);
00435 #  endif
00436     double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
00437 #else
00438     struct timeval tv;
00439     gettimeofday(&tv, 0L);
00440     double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
00441 #endif
00442     value = Number(utc);
00443   } else if (numArgs == 1) {
00444     UString s = args[0].toString(exec);
00445     double d = s.toDouble();
00446     if (isNaN(d))
00447       value = parseDate(s);
00448     else
00449       value = Number(d);
00450   } else {
00451     struct tm t;
00452     memset(&t, 0, sizeof(t));
00453     int year = args[0].toInt32(exec);
00454     // TODO: check for NaN
00455     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
00456     t.tm_mon = args[1].toInt32(exec);
00457     t.tm_mday = (numArgs >= 3) ? args[2].toInt32(exec) : 1;
00458     t.tm_hour = (numArgs >= 4) ? args[3].toInt32(exec) : 0;
00459     t.tm_min = (numArgs >= 5) ? args[4].toInt32(exec) : 0;
00460     t.tm_sec = (numArgs >= 6) ? args[5].toInt32(exec) : 0;
00461     t.tm_isdst = -1;
00462     int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
00463     value = Number(mktime(&t) * 1000.0 + ms);
00464   }
00465 
00466   Object proto = exec->interpreter()->builtinDatePrototype();
00467   Object ret(new DateInstanceImp(proto.imp()));
00468   ret.setInternalValue(timeClip(value));
00469   return ret;
00470 }
00471 
00472 bool DateObjectImp::implementsCall() const
00473 {
00474   return true;
00475 }
00476 
00477 // ECMA 15.9.2
00478 Value DateObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
00479 {
00480 #ifdef KJS_VERBOSE
00481   fprintf(stderr,"DateObjectImp::call - current time\n");
00482 #endif
00483   time_t t = time(0L);
00484   UString s(ctime(&t));
00485 
00486   // return formatted string minus trailing \n
00487   return String(s.substr(0, s.size() - 1));
00488 }
00489 
00490 // ------------------------------ DateObjectFuncImp ----------------------------
00491 
00492 DateObjectFuncImp::DateObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto,
00493                                      int i, int len)
00494   : InternalFunctionImp(funcProto), id(i)
00495 {
00496   Value protect(this);
00497   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
00498 }
00499 
00500 bool DateObjectFuncImp::implementsCall() const
00501 {
00502   return true;
00503 }
00504 
00505 // ECMA 15.9.4.2 - 3
00506 Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
00507 {
00508   if (id == Parse) {
00509     return parseDate(args[0].toString(exec));
00510   } else { // UTC
00511     struct tm t;
00512     memset(&t, 0, sizeof(t));
00513     int n = args.size();
00514     int year = args[0].toInt32(exec);
00515     // TODO: check for NaN
00516     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
00517     t.tm_mon = args[1].toInt32(exec);
00518     t.tm_mday = (n >= 3) ? args[2].toInt32(exec) : 1;
00519     t.tm_hour = (n >= 4) ? args[3].toInt32(exec) : 0;
00520     t.tm_min = (n >= 5) ? args[4].toInt32(exec) : 0;
00521     t.tm_sec = (n >= 6) ? args[5].toInt32(exec) : 0;
00522     int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
00523     return Number(mktime(&t) * 1000.0 + ms);
00524   }
00525 }
00526 
00527 // -----------------------------------------------------------------------------
00528 
00529 
00530 Value KJS::parseDate(const UString &u)
00531 {
00532 #ifdef KJS_VERBOSE
00533   fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
00534 #endif
00535   double /*time_t*/ seconds = KRFCDate_parseDate( u );
00536 #ifdef KJS_VERBOSE
00537   fprintf(stderr,"KRFCDate_parseDate returned seconds=%g\n",seconds);
00538   bool withinLimits = true;
00539   if ( sizeof(time_t) == 4 )
00540   {
00541     int limit = ((time_t)-1 < 0) ? 2038 : 2115;
00542     if ( seconds > (limit-1970) * 365.25 * 86400 ) {
00543       fprintf(stderr, "date above time_t limit. Year seems to be %d\n", (int)(seconds/(365.25*86400)+1970));
00544       withinLimits = false;
00545     }
00546   }
00547   if ( withinLimits ) {
00548     time_t lsec = (time_t)seconds;
00549     fprintf(stderr, "this is: %s\n", ctime(&lsec));
00550   }
00551 #endif
00552 
00553   return Number(seconds == -1 ? NaN : seconds * 1000.0);
00554 }
00555 
00557 
00558 static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
00559 {
00560     //printf("year=%d month=%d day=%d hour=%d minute=%d second=%d\n", year, mon, day, hour, minute, second);
00561 
00562     double ret = (day - 32075)       /* days */
00563             + 1461L * (year + 4800L + (mon - 14) / 12) / 4
00564             + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
00565             - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
00566             - 2440588;
00567     ret = 24*ret + hour;     /* hours   */
00568     ret = 60*ret + minute;   /* minutes */
00569     ret = 60*ret + second;   /* seconds */
00570 
00571     return ret;
00572 }
00573 
00574 static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
00575 
00576 // we follow the recommendation of rfc2822 to consider all
00577 // obsolete time zones not listed here equivalent to "-0000"
00578 static const struct {
00579     const char tzName[4];
00580     int tzOffset;
00581 } known_zones[] = {
00582     { "UT", 0 },
00583     { "GMT", 0 },
00584     { "EST", -300 },
00585     { "EDT", -240 },
00586     { "CST", -360 },
00587     { "CDT", -300 },
00588     { "MST", -420 },
00589     { "MDT", -360 },
00590     { "PST", -480 },
00591     { "PDT", -420 },
00592     { { 0, 0, 0, 0 }, 0 }
00593 };
00594 
00595 int KJS::local_timeoffset()
00596 {
00597      static int local_offset = -1;
00598 
00599      if ( local_offset != -1 ) return local_offset;
00600 
00601      time_t local = time(0);
00602      struct tm* tm_local = gmtime(&local);
00603      local_offset = local-mktime(tm_local);
00604      if(tm_local->tm_isdst)
00605        local_offset += 3600;
00606 
00607      return local_offset;
00608 }
00609 
00610 double KJS::KRFCDate_parseDate(const UString &_date)
00611 {
00612      // This parse a date in the form:
00613      //     Wednesday, 09-Nov-99 23:12:40 GMT
00614      // or
00615      //     Sat, 01-Jan-2000 08:00:00 GMT
00616      // or
00617      //     Sat, 01 Jan 2000 08:00:00 GMT
00618      // or
00619      //     01 Jan 99 22:00 +0100    (exceptions in rfc822/rfc2822)
00620      // ### non RFC formats, added for Javascript:
00621      //     [Wednesday] January 09 1999 23:12:40 GMT
00622      //     [Wednesday] January 09 23:12:40 GMT 1999
00623      //
00624      // We ignore the weekday
00625      //
00626      double result = -1;
00627      int offset = 0;
00628      bool have_tz = false;
00629      char *newPosStr;
00630      const char *dateString = _date.ascii();
00631      int day = 0;
00632      char monthStr[4];
00633      int month = -1; // not set yet
00634      int year = 0;
00635      int hour = 0;
00636      int minute = 0;
00637      int second = 0;
00638      bool have_time = false;
00639 
00640      // Skip leading space
00641      while(*dateString && isspace(*dateString))
00642         dateString++;
00643 
00644      const char *wordStart = dateString;
00645      // Check contents of first words if not number
00646      while(*dateString && !isdigit(*dateString))
00647      {
00648         if ( isspace(*dateString) && dateString - wordStart >= 3 )
00649         {
00650           monthStr[0] = tolower(*wordStart++);
00651           monthStr[1] = tolower(*wordStart++);
00652           monthStr[2] = tolower(*wordStart++);
00653           monthStr[3] = '\0';
00654           //fprintf(stderr,"KJS::parseDate found word starting with '%s'\n", monthStr);
00655           const char *str = strstr(haystack, monthStr);
00656           if (str) {
00657             int position = str - haystack;
00658             if (position % 3 == 0) {
00659               month = position / 3; // Jan=00, Feb=01, Mar=02, ..
00660             }
00661           }
00662           while(*dateString && isspace(*dateString))
00663              dateString++;
00664           wordStart = dateString;
00665         }
00666         else
00667            dateString++;
00668      }
00669 
00670      while(*dateString && isspace(*dateString))
00671         dateString++;
00672 
00673      if (!*dateString)
00674         return invalidDate;
00675 
00676      // ' 09-Nov-99 23:12:40 GMT'
00677      day = strtol(dateString, &newPosStr, 10);
00678      dateString = newPosStr;
00679 
00680      if ((day < 1) || (day > 31))
00681         return invalidDate;
00682      if (!*dateString)
00683         return invalidDate;
00684 
00685      if (*dateString == '/' && day <= 12 && month == -1)
00686      {
00687         dateString++;
00688         // This looks like a MM/DD/YYYY date, not an RFC date.....
00689         month = day - 1; // 0-based
00690         day = strtol(dateString, &newPosStr, 10);
00691         dateString = newPosStr;
00692         if (*dateString == '/')
00693           dateString++;
00694         if (!*dateString)
00695           return invalidDate;
00696         //printf("month=%d day=%d dateString=%s\n", month, day, dateString);
00697      }
00698      else
00699      {
00700        if (*dateString == '-')
00701          dateString++;
00702 
00703        while(*dateString && isspace(*dateString))
00704          dateString++;
00705 
00706        if (*dateString == ',')
00707          dateString++;
00708 
00709        if ( month == -1 ) // not found yet
00710        {
00711          for(int i=0; i < 3;i++)
00712          {
00713            if (!*dateString || (*dateString == '-') || isspace(*dateString))
00714              return invalidDate;
00715            monthStr[i] = tolower(*dateString++);
00716          }
00717          monthStr[3] = '\0';
00718 
00719          newPosStr = (char*)strstr(haystack, monthStr);
00720 
00721          if (!newPosStr || (newPosStr - haystack) % 3 != 0)
00722            return invalidDate;
00723 
00724          month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
00725 
00726          if ((month < 0) || (month > 11))
00727            return invalidDate;
00728 
00729          while(*dateString && (*dateString != '-') && !isspace(*dateString))
00730            dateString++;
00731 
00732          if (!*dateString)
00733            return invalidDate;
00734 
00735          // '-99 23:12:40 GMT'
00736          if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
00737            return invalidDate;
00738          dateString++;
00739        }
00740 
00741        if ((month < 0) || (month > 11))
00742          return invalidDate;
00743      }
00744 
00745      // '99 23:12:40 GMT'
00746      year = strtol(dateString, &newPosStr, 10);
00747 
00748      // Don't fail if the time is missing.
00749      if (*newPosStr)
00750      {
00751         // ' 23:12:40 GMT'
00752         if (!isspace(*newPosStr)) {
00753            if ( *newPosStr == ':' ) // Ah, so there was no year, but the number was the hour
00754                year = -1;
00755            else
00756                return invalidDate;
00757         } else // in the normal case (we parsed the year), advance to the next number
00758             dateString = ++newPosStr;
00759 
00760         have_time = true;
00761         hour = strtol(dateString, &newPosStr, 10);
00762         dateString = newPosStr;
00763 
00764         if ((hour < 0) || (hour > 23))
00765            return invalidDate;
00766 
00767         if (!*dateString)
00768            return invalidDate;
00769 
00770         // ':12:40 GMT'
00771         if (*dateString++ != ':')
00772            return invalidDate;
00773 
00774         minute = strtol(dateString, &newPosStr, 10);
00775         dateString = newPosStr;
00776 
00777         if ((minute < 0) || (minute > 59))
00778            return invalidDate;
00779 
00780         // ':40 GMT'
00781         if (*dateString && *dateString != ':' && !isspace(*dateString))
00782            return invalidDate;
00783 
00784         // seconds are optional in rfc822 + rfc2822
00785         if (*dateString ==':') {
00786            dateString++;
00787 
00788            second = strtol(dateString, &newPosStr, 10);
00789            dateString = newPosStr;
00790 
00791            if ((second < 0) || (second > 59))
00792               return invalidDate;
00793         }
00794 
00795         while(*dateString && isspace(*dateString))
00796            dateString++;
00797      }
00798      else
00799        dateString = newPosStr;
00800 
00801 
00802      // don't fail if the time zone is missing, some
00803      // broken mail-/news-clients omit the time zone
00804      if (*dateString) {
00805 
00806        if ( (dateString[0] == 'G' && dateString[1] == 'M' && dateString[2] == 'T')
00807             || (dateString[0] == 'U' && dateString[1] == 'T' && dateString[2] == 'C') )
00808        {
00809          dateString += 3;
00810          have_tz = true;
00811        }
00812 
00813        while (*dateString && isspace(*dateString))
00814          ++dateString;
00815 
00816        if (strncasecmp(dateString, "GMT", 3) == 0) {
00817          dateString += 3;
00818        }
00819        if ((*dateString == '+') || (*dateString == '-')) {
00820          offset = strtol(dateString, &newPosStr, 10);
00821          dateString = newPosStr;
00822 
00823          if ((offset < -9959) || (offset > 9959))
00824             return invalidDate;
00825 
00826          int sgn = (offset < 0)? -1:1;
00827          offset = abs(offset);
00828          if ( *dateString == ':' ) { // GMT+05:00
00829            int offset2 = strtol(dateString, &newPosStr, 10);
00830            dateString = newPosStr;
00831            offset = (offset*60 + offset2)*sgn;
00832          }
00833          else
00834            offset = ((offset / 100)*60 + (offset % 100))*sgn;
00835          have_tz = true;
00836        } else {
00837          for (int i=0; known_zones[i].tzName != 0; i++) {
00838            if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
00839              offset = known_zones[i].tzOffset;
00840              have_tz = true;
00841              break;
00842            }
00843          }
00844        }
00845      }
00846 
00847      while(*dateString && isspace(*dateString))
00848         dateString++;
00849 
00850      if ( *dateString && year == -1 ) {
00851        year = strtol(dateString, &newPosStr, 10);
00852      }
00853 
00854      // Y2K: Solve 2 digit years
00855      if ((year >= 0) && (year < 50))
00856          year += 2000;
00857 
00858      if ((year >= 50) && (year < 100))
00859          year += 1900;  // Y2K
00860 
00861      if ((year < 1900) || (year > 2500))
00862         return invalidDate;
00863 
00864      if (!have_time && !have_tz) {
00865        // fall back to midnight, local timezone
00866        struct tm t;
00867        memset(&t, 0, sizeof(tm));
00868        t.tm_mday = day;
00869        t.tm_mon = month;
00870        t.tm_year = year - 1900;
00871        t.tm_isdst = -1;
00872        return mktime(&t);
00873      }
00874 
00875      if(!have_tz)
00876        offset = local_timeoffset();
00877      else
00878        offset *= 60;
00879 
00880      result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
00881 
00882      // avoid negative time values
00883      if ((offset > 0) && (offset > result))
00884         offset = 0;
00885 
00886      result -= offset;
00887 
00888      // If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT
00889      // This is so that parse error and valid epoch 0 return values won't
00890      // be the same for sensitive applications...
00891      if (result < 1) result = 1;
00892 
00893      return result;
00894 }
00895 
00896 
00897 Value KJS::timeClip(const Value &t)
00898 {
00899   /* TODO */
00900   return t;
00901 }
00902 
KDE Logo
This file is part of the documentation for kjs Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Sep 30 05:17:56 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2003