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