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(__linux__) || defined(__APPLE__)
00320     result = Number(-(t->tm_gmtoff / 60) );
00321 #else
00322 #  if defined(__BORLANDC__)
00323 // FIXME consider non one-hour DST change
00324 #error please add daylight savings offset here!
00325     result = Number(_timezone / 60 - (t->tm_isdst > 0 ? 60 : 0));
00326 #  else
00327     result = Number((timezone / 60 - (t->tm_isdst > 0 ? 60 : 0 )));
00328 #  endif
00329 #endif
00330     break;
00331   case SetTime:
00332     milli = roundValue(exec,args[0]);
00333     result = Number(milli);
00334     thisObj.setInternalValue(result);
00335     break;
00336   case SetMilliSeconds:
00337     ms = args[0].toInt32(exec);
00338     break;
00339   case SetSeconds:
00340     t->tm_sec = args[0].toInt32(exec);
00341     if (args.size() >= 2)
00342       ms = args[1].toInt32(exec);
00343     break;
00344   case SetMinutes:
00345     t->tm_min = args[0].toInt32(exec);
00346     if (args.size() >= 2)
00347       t->tm_sec = args[1].toInt32(exec);
00348     if (args.size() >= 3)
00349       ms = args[2].toInt32(exec);
00350     break;
00351   case SetHours:
00352     t->tm_hour = args[0].toInt32(exec);
00353     if (args.size() >= 2)
00354       t->tm_min = args[1].toInt32(exec);
00355     if (args.size() >= 3)
00356       t->tm_sec = args[2].toInt32(exec);
00357     if (args.size() >= 4)
00358       ms = args[3].toInt32(exec);
00359     break;
00360   case SetDate:
00361     t->tm_mday = args[0].toInt32(exec);
00362     break;
00363   case SetMonth:
00364     t->tm_mon = args[0].toInt32(exec);
00365     if (args.size() >= 2)
00366       t->tm_mday = args[1].toInt32(exec);
00367     break;
00368   case SetFullYear:
00369     t->tm_year = args[0].toInt32(exec) - 1900;
00370     if (args.size() >= 2)
00371       t->tm_mon = args[1].toInt32(exec);
00372     if (args.size() >= 3)
00373       t->tm_mday = args[2].toInt32(exec);
00374     break;
00375   case SetYear:
00376     t->tm_year = args[0].toInt32(exec) >= 1900 ? args[0].toInt32(exec) - 1900 : args[0].toInt32(exec);
00377     break;
00378   }
00379 
00380   if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
00381       id == SetMinutes || id == SetHours || id == SetDate ||
00382       id == SetMonth || id == SetFullYear ) {
00383     result = makeTime(t, ms, utc);
00384     thisObj.setInternalValue(result);
00385   }
00386 
00387   return result;
00388 }
00389 
00390 // ------------------------------ DateObjectImp --------------------------------
00391 
00392 // TODO: MakeTime (15.9.11.1) etc. ?
00393 
00394 DateObjectImp::DateObjectImp(ExecState *exec,
00395                              FunctionPrototypeImp *funcProto,
00396                              DatePrototypeImp *dateProto)
00397   : InternalFunctionImp(funcProto)
00398 {
00399   Value protect(this);
00400 
00401   // ECMA 15.9.4.1 Date.prototype
00402   putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
00403 
00404   static const Identifier parsePropertyName("parse");
00405   putDirect(parsePropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1), DontEnum);
00406   static const Identifier UTCPropertyName("UTC");
00407   putDirect(UTCPropertyName,   new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC,   7),   DontEnum);
00408 
00409   // no. of arguments for constructor
00410   putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
00411 }
00412 
00413 bool DateObjectImp::implementsConstruct() const
00414 {
00415   return true;
00416 }
00417 
00418 // ECMA 15.9.3
00419 Object DateObjectImp::construct(ExecState *exec, const List &args)
00420 {
00421   int numArgs = args.size();
00422 
00423 #ifdef KJS_VERBOSE
00424   fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
00425 #endif
00426   Value value;
00427 
00428   if (numArgs == 0) { // new Date() ECMA 15.9.3.3
00429 #if HAVE_SYS_TIMEB_H
00430 #  if defined(__BORLANDC__)
00431     struct timeb timebuffer;
00432     ftime(&timebuffer);
00433 #  else
00434     struct _timeb timebuffer;
00435     _ftime(&timebuffer);
00436 #  endif
00437     double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
00438 #else
00439     struct timeval tv;
00440     gettimeofday(&tv, 0L);
00441     double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
00442 #endif
00443     value = Number(utc);
00444   } else if (numArgs == 1) {
00445     UString s = args[0].toString(exec);
00446     double d = s.toDouble();
00447     if (isNaN(d))
00448       value = parseDate(s);
00449     else
00450       value = Number(d);
00451   } else {
00452     struct tm t;
00453     memset(&t, 0, sizeof(t));
00454     int year = args[0].toInt32(exec);
00455     // TODO: check for NaN
00456     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
00457     t.tm_mon = args[1].toInt32(exec);
00458     t.tm_mday = (numArgs >= 3) ? args[2].toInt32(exec) : 1;
00459     t.tm_hour = (numArgs >= 4) ? args[3].toInt32(exec) : 0;
00460     t.tm_min = (numArgs >= 5) ? args[4].toInt32(exec) : 0;
00461     t.tm_sec = (numArgs >= 6) ? args[5].toInt32(exec) : 0;
00462     t.tm_isdst = -1;
00463     int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
00464     value = makeTime(&t, ms, false);
00465   }
00466 
00467   Object proto = exec->interpreter()->builtinDatePrototype();
00468   Object ret(new DateInstanceImp(proto.imp()));
00469   ret.setInternalValue(timeClip(value));
00470   return ret;
00471 }
00472 
00473 bool DateObjectImp::implementsCall() const
00474 {
00475   return true;
00476 }
00477 
00478 // ECMA 15.9.2
00479 Value DateObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
00480 {
00481 #ifdef KJS_VERBOSE
00482   fprintf(stderr,"DateObjectImp::call - current time\n");
00483 #endif
00484   time_t t = time(0L);
00485   UString s(ctime(&t));
00486 
00487   // return formatted string minus trailing \n
00488   return String(s.substr(0, s.size() - 1));
00489 }
00490 
00491 // ------------------------------ DateObjectFuncImp ----------------------------
00492 
00493 DateObjectFuncImp::DateObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto,
00494                                      int i, int len)
00495   : InternalFunctionImp(funcProto), id(i)
00496 {
00497   Value protect(this);
00498   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
00499 }
00500 
00501 bool DateObjectFuncImp::implementsCall() const
00502 {
00503   return true;
00504 }
00505 
00506 // ECMA 15.9.4.2 - 3
00507 Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
00508 {
00509   if (id == Parse) {
00510     return parseDate(args[0].toString(exec));
00511   } else { // UTC
00512     struct tm t;
00513     memset(&t, 0, sizeof(t));
00514     int n = args.size();
00515     int year = args[0].toInt32(exec);
00516     // TODO: check for NaN
00517     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
00518     t.tm_mon = args[1].toInt32(exec);
00519     t.tm_mday = (n >= 3) ? args[2].toInt32(exec) : 1;
00520     t.tm_hour = (n >= 4) ? args[3].toInt32(exec) : 0;
00521     t.tm_min = (n >= 5) ? args[4].toInt32(exec) : 0;
00522     t.tm_sec = (n >= 6) ? args[5].toInt32(exec) : 0;
00523     int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
00524     return makeTime(&t, ms, true);
00525   }
00526 }
00527 
00528 // -----------------------------------------------------------------------------
00529 
00530 
00531 Value KJS::parseDate(const UString &u)
00532 {
00533 #ifdef KJS_VERBOSE
00534   fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
00535 #endif
00536   double /*time_t*/ seconds = KRFCDate_parseDate( u );
00537 #ifdef KJS_VERBOSE
00538   fprintf(stderr,"KRFCDate_parseDate returned seconds=%g\n",seconds);
00539   bool withinLimits = true;
00540   if ( sizeof(time_t) == 4 )
00541   {
00542     int limit = ((time_t)-1 < 0) ? 2038 : 2115;
00543     if ( seconds > (limit-1970) * 365.25 * 86400 ) {
00544       fprintf(stderr, "date above time_t limit. Year seems to be %d\n", (int)(seconds/(365.25*86400)+1970));
00545       withinLimits = false;
00546     }
00547   }
00548   if ( withinLimits ) {
00549     time_t lsec = (time_t)seconds;
00550     fprintf(stderr, "this is: %s\n", ctime(&lsec));
00551   }
00552 #endif
00553 
00554   return Number(seconds == -1 ? NaN : seconds * 1000.0);
00555 }
00556 
00558 
00559 static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
00560 {
00561     //printf("year=%d month=%d day=%d hour=%d minute=%d second=%d\n", year, mon, day, hour, minute, second);
00562 
00563     double ret = (day - 32075)       /* days */
00564             + 1461L * (year + 4800L + (mon - 14) / 12) / 4
00565             + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
00566             - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
00567             - 2440588;
00568     ret = 24*ret + hour;     /* hours   */
00569     ret = 60*ret + minute;   /* minutes */
00570     ret = 60*ret + second;   /* seconds */
00571 
00572     return ret;
00573 }
00574 
00575 static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
00576 
00577 // we follow the recommendation of rfc2822 to consider all
00578 // obsolete time zones not listed here equivalent to "-0000"
00579 static const struct {
00580     const char tzName[4];
00581     int tzOffset;
00582 } known_zones[] = {
00583     { "UT", 0 },
00584     { "GMT", 0 },
00585     { "EST", -300 },
00586     { "EDT", -240 },
00587     { "CST", -360 },
00588     { "CDT", -300 },
00589     { "MST", -420 },
00590     { "MDT", -360 },
00591     { "PST", -480 },
00592     { "PDT", -420 },
00593     { { 0, 0, 0, 0 }, 0 }
00594 };
00595 
00596 Number KJS::makeTime(struct tm *t, int ms, bool utc)
00597 {
00598     int utcOffset;
00599     if (utc) {
00600     time_t zero = 0;
00601     struct tm t3;
00602         localtime_r(&zero, &t3);
00603 #if defined BSD || defined(__linux__) || defined(__APPLE__)
00604         utcOffset = t3.tm_gmtoff;
00605         t->tm_isdst = t3.tm_isdst;
00606 #else
00607 #  if defined(__BORLANDC__)
00608         utcOffset = - _timezone;
00609 #  else
00610         utcOffset = - timezone;
00611 #  endif
00612         t->tm_isdst = 0;
00613 #endif
00614     } else {
00615     utcOffset = 0;
00616     t->tm_isdst = -1;
00617     }
00618 
00619     return Number( ( mktime(t) + utcOffset ) * 1000.0 + ms );
00620 }
00621 
00622 double KJS::KRFCDate_parseDate(const UString &_date)
00623 {
00624      // This parse a date in the form:
00625      //     Wednesday, 09-Nov-99 23:12:40 GMT
00626      // or
00627      //     Sat, 01-Jan-2000 08:00:00 GMT
00628      // or
00629      //     Sat, 01 Jan 2000 08:00:00 GMT
00630      // or
00631      //     01 Jan 99 22:00 +0100    (exceptions in rfc822/rfc2822)
00632      // ### non RFC formats, added for Javascript:
00633      //     [Wednesday] January 09 1999 23:12:40 GMT
00634      //     [Wednesday] January 09 23:12:40 GMT 1999
00635      //
00636      // We ignore the weekday
00637      //
00638      double result = -1;
00639      int offset = 0;
00640      bool have_tz = false;
00641      char *newPosStr;
00642      const char *dateString = _date.ascii();
00643      int day = 0;
00644      char monthStr[4];
00645      int month = -1; // not set yet
00646      int year = 0;
00647      int hour = 0;
00648      int minute = 0;
00649      int second = 0;
00650      bool have_time = false;
00651 
00652      // Skip leading space
00653      while(*dateString && isspace(*dateString))
00654         dateString++;
00655 
00656      const char *wordStart = dateString;
00657      // Check contents of first words if not number
00658      while(*dateString && !isdigit(*dateString))
00659      {
00660         if ( isspace(*dateString) && dateString - wordStart >= 3 )
00661         {
00662           monthStr[0] = tolower(*wordStart++);
00663           monthStr[1] = tolower(*wordStart++);
00664           monthStr[2] = tolower(*wordStart++);
00665           monthStr[3] = '\0';
00666           //fprintf(stderr,"KJS::parseDate found word starting with '%s'\n", monthStr);
00667           const char *str = strstr(haystack, monthStr);
00668           if (str) {
00669             int position = str - haystack;
00670             if (position % 3 == 0) {
00671               month = position / 3; // Jan=00, Feb=01, Mar=02, ..
00672             }
00673           }
00674           while(*dateString && isspace(*dateString))
00675              dateString++;
00676           wordStart = dateString;
00677         }
00678         else
00679            dateString++;
00680      }
00681 
00682      while(*dateString && isspace(*dateString))
00683         dateString++;
00684 
00685      if (!*dateString)
00686         return invalidDate;
00687 
00688      // ' 09-Nov-99 23:12:40 GMT'
00689      day = strtol(dateString, &newPosStr, 10);
00690      dateString = newPosStr;
00691 
00692      if (!*dateString)
00693         return invalidDate;
00694 
00695      if (day < 1)
00696        return invalidDate;
00697      if (day > 31) {
00698        // ### where is the boundary and what happens below?
00699        if (*dateString == '/' && day >= 1000) {
00700          // looks like a YYYY/MM/DD date
00701          if (!*++dateString)
00702            return invalidDate;
00703          year = day;
00704          month = strtol(dateString, &newPosStr, 10) - 1;
00705          dateString = newPosStr;
00706          if (*dateString++ != '/' || !*dateString)
00707            return invalidDate;
00708          day = strtol(dateString, &newPosStr, 10);
00709          dateString = newPosStr;
00710        } else {
00711          return invalidDate;
00712        }
00713      } else if (*dateString == '/' && day <= 12 && month == -1)
00714      {
00715         dateString++;
00716         // This looks like a MM/DD/YYYY date, not an RFC date.....
00717         month = day - 1; // 0-based
00718         day = strtol(dateString, &newPosStr, 10);
00719         dateString = newPosStr;
00720         if (*dateString == '/')
00721           dateString++;
00722         if (!*dateString)
00723           return invalidDate;
00724         //printf("month=%d day=%d dateString=%s\n", month, day, dateString);
00725      }
00726      else
00727      {
00728        if (*dateString == '-')
00729          dateString++;
00730 
00731        while(*dateString && isspace(*dateString))
00732          dateString++;
00733 
00734        if (*dateString == ',')
00735          dateString++;
00736 
00737        if ( month == -1 ) // not found yet
00738        {
00739          for(int i=0; i < 3;i++)
00740          {
00741            if (!*dateString || (*dateString == '-') || isspace(*dateString))
00742              return invalidDate;
00743            monthStr[i] = tolower(*dateString++);
00744          }
00745          monthStr[3] = '\0';
00746 
00747          newPosStr = (char*)strstr(haystack, monthStr);
00748 
00749          if (!newPosStr || (newPosStr - haystack) % 3 != 0)
00750            return invalidDate;
00751 
00752          month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
00753 
00754          if ((month < 0) || (month > 11))
00755            return invalidDate;
00756 
00757          while(*dateString && (*dateString != '-') && !isspace(*dateString))
00758            dateString++;
00759 
00760          if (!*dateString)
00761            return invalidDate;
00762 
00763          // '-99 23:12:40 GMT'
00764          if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
00765            return invalidDate;
00766          dateString++;
00767        }
00768 
00769        if ((month < 0) || (month > 11))
00770          return invalidDate;
00771      }
00772 
00773      // '99 23:12:40 GMT'
00774      if (year <= 0 && *dateString)
00775        year = strtol(dateString, &newPosStr, 10);
00776 
00777      // Don't fail if the time is missing.
00778      if (*newPosStr)
00779      {
00780         // ' 23:12:40 GMT'
00781         if (!isspace(*newPosStr)) {
00782            if ( *newPosStr == ':' ) // Ah, so there was no year, but the number was the hour
00783                year = -1;
00784            else
00785                return invalidDate;
00786         } else // in the normal case (we parsed the year), advance to the next number
00787             dateString = ++newPosStr;
00788 
00789         have_time = true;
00790         hour = strtol(dateString, &newPosStr, 10);
00791         dateString = newPosStr;
00792 
00793         if ((hour < 0) || (hour > 23))
00794            return invalidDate;
00795 
00796         if (!*dateString)
00797            return invalidDate;
00798 
00799         // ':12:40 GMT'
00800         if (*dateString++ != ':')
00801            return invalidDate;
00802 
00803         minute = strtol(dateString, &newPosStr, 10);
00804         dateString = newPosStr;
00805 
00806         if ((minute < 0) || (minute > 59))
00807            return invalidDate;
00808 
00809         // ':40 GMT'
00810         if (*dateString && *dateString != ':' && !isspace(*dateString))
00811            return invalidDate;
00812 
00813         // seconds are optional in rfc822 + rfc2822
00814         if (*dateString ==':') {
00815            dateString++;
00816 
00817            second = strtol(dateString, &newPosStr, 10);
00818            dateString = newPosStr;
00819 
00820            if ((second < 0) || (second > 59))
00821               return invalidDate;
00822         }
00823 
00824         while(*dateString && isspace(*dateString))
00825            dateString++;
00826      }
00827      else
00828        dateString = newPosStr;
00829 
00830 
00831      // don't fail if the time zone is missing, some
00832      // broken mail-/news-clients omit the time zone
00833      if (*dateString) {
00834 
00835        if ( (dateString[0] == 'G' && dateString[1] == 'M' && dateString[2] == 'T')
00836             || (dateString[0] == 'U' && dateString[1] == 'T' && dateString[2] == 'C') )
00837        {
00838          dateString += 3;
00839          have_tz = true;
00840        }
00841 
00842        while (*dateString && isspace(*dateString))
00843          ++dateString;
00844 
00845        if (strncasecmp(dateString, "GMT", 3) == 0) {
00846          dateString += 3;
00847        }
00848        if ((*dateString == '+') || (*dateString == '-')) {
00849          offset = strtol(dateString, &newPosStr, 10);
00850          dateString = newPosStr;
00851 
00852          if ((offset < -9959) || (offset > 9959))
00853             return invalidDate;
00854 
00855          int sgn = (offset < 0)? -1:1;
00856          offset = abs(offset);
00857          if ( *dateString == ':' ) { // GMT+05:00
00858            int offset2 = strtol(dateString, &newPosStr, 10);
00859            dateString = newPosStr;
00860            offset = (offset*60 + offset2)*sgn;
00861          }
00862          else
00863            offset = ((offset / 100)*60 + (offset % 100))*sgn;
00864          have_tz = true;
00865        } else {
00866          for (int i=0; known_zones[i].tzName != 0; i++) {
00867            if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
00868              offset = known_zones[i].tzOffset;
00869              have_tz = true;
00870              break;
00871            }
00872          }
00873        }
00874      }
00875 
00876      while(*dateString && isspace(*dateString))
00877         dateString++;
00878 
00879      if ( *dateString && year == -1 ) {
00880        year = strtol(dateString, &newPosStr, 10);
00881      }
00882 
00883      // Y2K: Solve 2 digit years
00884      if ((year >= 0) && (year < 50))
00885          year += 2000;
00886 
00887      if ((year >= 50) && (year < 100))
00888          year += 1900;  // Y2K
00889 
00890      if ((year < 1900) || (year > 2500))
00891         return invalidDate;
00892 
00893      if (!have_tz) {
00894        // fall back to midnight, local timezone
00895        struct tm t;
00896        memset(&t, 0, sizeof(tm));
00897        t.tm_mday = day;
00898        t.tm_mon = month;
00899        t.tm_year = year - 1900;
00900        t.tm_isdst = -1;
00901        if (have_time) {
00902          t.tm_sec = second;
00903          t.tm_min = minute;
00904          t.tm_hour = hour;
00905        }
00906 
00907        return mktime(&t);
00908      }
00909 
00910        offset *= 60;
00911 
00912      result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
00913 
00914      // avoid negative time values
00915      if ((offset > 0) && (offset > result))
00916         offset = 0;
00917 
00918      result -= offset;
00919 
00920      // If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT
00921      // This is so that parse error and valid epoch 0 return values won't
00922      // be the same for sensitive applications...
00923      if (result < 1) result = 1;
00924 
00925      return result;
00926 }
00927 
00928 
00929 Value KJS::timeClip(const Value &t)
00930 {
00931   /* TODO */
00932   return t;
00933 }
00934 
KDE Logo
This file is part of the documentation for kjs Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Feb 18 15:10:29 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003