kjs Library API Documentation

date_object.cpp

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