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