kjs Library API Documentation

date_object.cpp

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