File indexing completed on 2024-05-12 15:43:17
0001 /* 0002 * This file is part of the KDE libraries 0003 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) 0004 * Copyright (C) 2004 Apple Computer, Inc. 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Lesser General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Lesser General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Lesser General Public 0017 * License along with this library; if not, write to the Free Software 0018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0019 * 0020 */ 0021 0022 #include "date_object.h" 0023 #include "global.h" 0024 #include "date_object.lut.h" 0025 #include "internal.h" 0026 0027 #if HAVE_ERRNO_H 0028 #include <errno.h> 0029 #endif 0030 0031 #if HAVE_SYS_PARAM_H 0032 #include <sys/param.h> 0033 #endif 0034 0035 #if HAVE_SYS_TIME_H 0036 #include <sys/time.h> 0037 #endif 0038 0039 #if HAVE_SYS_TIMEB_H 0040 #include <sys/timeb.h> 0041 #endif 0042 0043 #include <float.h> 0044 #include <limits.h> 0045 #include <locale.h> 0046 #include <math.h> 0047 #include <stdio.h> 0048 #include <stdlib.h> 0049 #include <cstring> 0050 #include <time.h> 0051 0052 #if PLATFORM(SOLARIS_OS) 0053 #include <strings.h> 0054 #endif 0055 0056 #include "error_object.h" 0057 #include "operations.h" 0058 0059 #if PLATFORM(MAC) 0060 #include <CoreFoundation/CoreFoundation.h> 0061 #endif 0062 0063 #if PLATFORM(WIN_OS) 0064 #include <windows.h> 0065 #if defined(WTF_COMPILER_MSVC) 0066 #define copysign(x, y) _copysign(x, y) 0067 #define snprintf _snprintf 0068 #endif 0069 #if !defined(WTF_COMPILER_GCC) 0070 #ifndef strncasecmp 0071 #define strncasecmp(x, y, z) strnicmp(x, y, z) 0072 #endif 0073 #endif 0074 #endif 0075 0076 #include "wtf/DisallowCType.h" 0077 #include "wtf/ASCIICType.h" 0078 0079 // GCC cstring uses these automatically, but not all implementations do. 0080 using std::strlen; 0081 using std::strcpy; 0082 using std::strncpy; 0083 using std::memset; 0084 using std::memcpy; 0085 0086 using namespace WTF; 0087 0088 inline int gmtoffset(const tm &t) 0089 { 0090 #if PLATFORM(WIN_OS) 0091 // Time is supposed to be in the current timezone. 0092 // FIXME: Use undocumented _dstbias? 0093 return -(_timezone / 60 - (t.tm_isdst > 0 ? 60 : 0)) * 60; 0094 #else 0095 #if HAVE_TM_GMTOFF 0096 return t.tm_gmtoff; 0097 #else 0098 return - timezone; 0099 #endif 0100 #endif 0101 } 0102 0103 namespace KJS 0104 { 0105 0106 /** 0107 * @internal 0108 * 0109 * Class to implement all methods that are properties of the 0110 * Date object 0111 */ 0112 class DateObjectFuncImp : public InternalFunctionImp 0113 { 0114 public: 0115 DateObjectFuncImp(ExecState *, FunctionPrototype *, int i, int len, const Identifier &); 0116 0117 JSValue *callAsFunction(ExecState *, JSObject *thisObj, const List &args) override; 0118 0119 enum { Parse, UTC, Now }; 0120 0121 private: 0122 int id; 0123 }; 0124 0125 // some constants 0126 const double hoursPerDay = 24; 0127 const double minutesPerHour = 60; 0128 const double secondsPerMinute = 60; 0129 const double msPerSecond = 1000; 0130 const double msPerMinute = 60 * 1000; 0131 const double msPerHour = 60 * 60 * 1000; 0132 const double msPerDay = 24 * 60 * 60 * 1000; 0133 0134 static const char *const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; 0135 static const char *const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 0136 0137 static double makeTime(tm *, double ms, bool utc); 0138 static double parseDate(const UString &); 0139 static double timeClip(double); 0140 static void millisecondsToTM(double milli, bool utc, tm *t); 0141 0142 #if PLATFORM(MAC) 0143 0144 static CFDateFormatterStyle styleFromArgString(const UString &string, CFDateFormatterStyle defaultStyle) 0145 { 0146 if (string == "short") { 0147 return kCFDateFormatterShortStyle; 0148 } 0149 if (string == "medium") { 0150 return kCFDateFormatterMediumStyle; 0151 } 0152 if (string == "long") { 0153 return kCFDateFormatterLongStyle; 0154 } 0155 if (string == "full") { 0156 return kCFDateFormatterFullStyle; 0157 } 0158 return defaultStyle; 0159 } 0160 0161 static UString formatLocaleDate(ExecState *exec, double time, bool includeDate, bool includeTime, const List &args) 0162 { 0163 CFDateFormatterStyle dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); 0164 CFDateFormatterStyle timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); 0165 0166 bool useCustomFormat = false; 0167 UString customFormatString; 0168 0169 JSValue *arg0 = args[0]; 0170 JSValue *arg1 = args[1]; 0171 UString arg0String = arg0->toString(exec); 0172 if (arg0String == "custom" && !arg1->isUndefined()) { 0173 useCustomFormat = true; 0174 customFormatString = arg1->toString(exec); 0175 } else if (includeDate && includeTime && !arg1->isUndefined()) { 0176 dateStyle = styleFromArgString(arg0String, dateStyle); 0177 timeStyle = styleFromArgString(arg1->toString(exec), timeStyle); 0178 } else if (includeDate && !arg0->isUndefined()) { 0179 dateStyle = styleFromArgString(arg0String, dateStyle); 0180 } else if (includeTime && !arg0->isUndefined()) { 0181 timeStyle = styleFromArgString(arg0String, timeStyle); 0182 } 0183 0184 CFLocaleRef locale = CFLocaleCopyCurrent(); 0185 CFDateFormatterRef formatter = CFDateFormatterCreate(0, locale, dateStyle, timeStyle); 0186 CFRelease(locale); 0187 0188 if (useCustomFormat) { 0189 CFStringRef customFormatCFString = CFStringCreateWithCharacters(0, (UniChar *)customFormatString.data(), customFormatString.size()); 0190 CFDateFormatterSetFormat(formatter, customFormatCFString); 0191 CFRelease(customFormatCFString); 0192 } 0193 0194 CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(0, formatter, time - kCFAbsoluteTimeIntervalSince1970); 0195 0196 CFRelease(formatter); 0197 0198 // We truncate the string returned from CFDateFormatter if it's absurdly long (> 200 characters). 0199 // That's not great error handling, but it just won't happen so it doesn't matter. 0200 UChar buffer[200]; 0201 const size_t bufferLength = sizeof(buffer) / sizeof(buffer[0]); 0202 size_t length = CFStringGetLength(string); 0203 assert(length <= bufferLength); 0204 if (length > bufferLength) { 0205 length = bufferLength; 0206 } 0207 CFStringGetCharacters(string, CFRangeMake(0, length), reinterpret_cast<UniChar *>(buffer)); 0208 0209 CFRelease(string); 0210 0211 return UString(buffer, length); 0212 } 0213 0214 #endif // PLATFORM(MAC) 0215 0216 static UString formatDate(const tm &t) 0217 { 0218 char buffer[100]; 0219 int len = snprintf(buffer, sizeof(buffer), "%s %s %02d %04d", 0220 weekdayName[(t.tm_wday + 6) % 7], 0221 monthName[t.tm_mon], t.tm_mday, t.tm_year + 1900); 0222 return UString(buffer, len); 0223 } 0224 0225 static UString formatDateUTCVariant(const tm &t) 0226 { 0227 char buffer[100]; 0228 int len = snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d", 0229 weekdayName[(t.tm_wday + 6) % 7], 0230 t.tm_mday, monthName[t.tm_mon], t.tm_year + 1900); 0231 return UString(buffer, len); 0232 } 0233 0234 static UString formatDateISOVariant(const tm &t, bool utc, double absoluteMS) 0235 { 0236 char buffer[100]; 0237 // YYYY-MM-DD 0238 int len; 0239 if (utc) { 0240 len = snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d", 0241 t.tm_year + 1900, t.tm_mon + 1, t.tm_mday); 0242 } else { 0243 int offset = gmtoffset(t); 0244 tm t_fixed; 0245 millisecondsToTM(absoluteMS - offset * 1000, true, &t_fixed); 0246 len = snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d", 0247 t_fixed.tm_year + 1900, t_fixed.tm_mon + 1, t_fixed.tm_mday); 0248 } 0249 return UString(buffer, len); 0250 } 0251 0252 static UString formatTime(const tm &t, bool utc) 0253 { 0254 char buffer[100]; 0255 int len; 0256 if (utc) { 0257 // FIXME: why not on windows? 0258 #if !PLATFORM(WIN_OS) 0259 ASSERT(gmtoffset(t) == 0); 0260 #endif 0261 len = snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.tm_hour, t.tm_min, t.tm_sec); 0262 } else { 0263 int offset = abs(gmtoffset(t)); 0264 len = snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d", 0265 t.tm_hour, t.tm_min, t.tm_sec, 0266 gmtoffset(t) < 0 ? '-' : '+', offset / (60 * 60), (offset / 60) % 60); 0267 } 0268 return UString(buffer, len); 0269 } 0270 0271 static UString formatTimeISOVariant(const tm &t, bool utc, double absoluteMS, double ms) 0272 { 0273 char buffer[100]; 0274 // HH:mm:ss.sss 0275 int len; 0276 if (utc) { 0277 len = snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d.%03d", 0278 t.tm_hour, t.tm_min, t.tm_sec, int(ms)); 0279 } else { 0280 int offset = gmtoffset(t); 0281 tm t_fixed; 0282 millisecondsToTM(absoluteMS - offset * 1000, true, &t_fixed); 0283 len = snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d.%03d", 0284 t_fixed.tm_hour, t_fixed.tm_min, t_fixed.tm_sec, int(ms)); 0285 } 0286 return UString(buffer, len); 0287 } 0288 0289 static int day(double t) 0290 { 0291 return int(floor(t / msPerDay)); 0292 } 0293 0294 static double dayFromYear(int year) 0295 { 0296 return 365.0 * (year - 1970) 0297 + floor((year - 1969) / 4.0) 0298 - floor((year - 1901) / 100.0) 0299 + floor((year - 1601) / 400.0); 0300 } 0301 0302 // based on the rule for whether it's a leap year or not 0303 static int daysInYear(int year) 0304 { 0305 if (year % 4 != 0) { 0306 return 365; 0307 } 0308 if (year % 400 == 0) { 0309 return 366; 0310 } 0311 if (year % 100 == 0) { 0312 return 365; 0313 } 0314 return 366; 0315 } 0316 0317 // time value of the start of a year 0318 static double timeFromYear(int year) 0319 { 0320 return msPerDay * dayFromYear(year); 0321 } 0322 0323 // year determined by time value 0324 static int yearFromTime(double t) 0325 { 0326 // ### there must be an easier way 0327 0328 // initial guess 0329 int y = 1970 + int(t / (365.25 * msPerDay)); 0330 0331 // adjustment 0332 if (timeFromYear(y) > t) { 0333 do { 0334 --y; 0335 } while (timeFromYear(y) > t); 0336 } else { 0337 while (timeFromYear(y + 1) < t) { 0338 ++y; 0339 } 0340 } 0341 0342 return y; 0343 } 0344 0345 // 0: Sunday, 1: Monday, etc. 0346 static int weekDay(double t) 0347 { 0348 int wd = (day(t) + 4) % 7; 0349 if (wd < 0) { 0350 wd += 7; 0351 } 0352 return wd; 0353 } 0354 0355 // Converts a list of arguments sent to a Date member function into milliseconds, updating 0356 // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately. 0357 // 0358 // Format of member function: f([hour,] [min,] [sec,] [ms]) 0359 static double setTimeFields(ExecState *exec, const List &args, int id, double ms, tm *t) 0360 { 0361 assert(DateProtoFunc::SetSeconds - DateProtoFunc::SetMilliSeconds + 1 == 2); 0362 assert(DateProtoFunc::SetMinutes - DateProtoFunc::SetMilliSeconds + 1 == 3); 0363 assert(DateProtoFunc::SetHours - DateProtoFunc::SetMilliSeconds + 1 == 4); 0364 0365 assert(id == DateProtoFunc::SetMilliSeconds || id == DateProtoFunc::SetSeconds || 0366 id == DateProtoFunc::SetMinutes || id == DateProtoFunc::SetHours); 0367 0368 int maxArgs = id - DateProtoFunc::SetMilliSeconds + 1; 0369 double milliseconds = 0; 0370 int idx = 0; 0371 int numArgs = args.size(); 0372 0373 // JS allows extra trailing arguments -- ignore them 0374 if (numArgs > maxArgs) { 0375 numArgs = maxArgs; 0376 } 0377 0378 // hours 0379 if (maxArgs >= 4 && idx < numArgs) { 0380 t->tm_hour = 0; 0381 milliseconds += JSValue::toInt32(args[idx++], exec) * msPerHour; 0382 } 0383 0384 // minutes 0385 if (maxArgs >= 3 && idx < numArgs) { 0386 t->tm_min = 0; 0387 milliseconds += JSValue::toInt32(args[idx++], exec) * msPerMinute; 0388 } 0389 0390 // seconds 0391 if (maxArgs >= 2 && idx < numArgs) { 0392 t->tm_sec = 0; 0393 milliseconds += JSValue::toInt32(args[idx++], exec) * msPerSecond; 0394 } 0395 0396 // milliseconds 0397 if (idx < numArgs) { 0398 milliseconds += roundValue(exec, args[idx]); 0399 } else { 0400 milliseconds += ms; 0401 } 0402 0403 return milliseconds; 0404 } 0405 0406 // Converts a list of arguments sent to a Date member function into years, months, and milliseconds, updating 0407 // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately. 0408 // 0409 // Format of member function: f([years,] [months,] [days]) 0410 static double setDateFields(ExecState *exec, const List &args, int id, double ms, tm *t) 0411 { 0412 assert(DateProtoFunc::SetMonth - DateProtoFunc::SetDate + 1 == 2); 0413 assert(DateProtoFunc::SetFullYear - DateProtoFunc::SetDate + 1 == 3); 0414 0415 assert(id == DateProtoFunc::SetDate || id == DateProtoFunc::SetMonth || id == DateProtoFunc::SetFullYear); 0416 0417 int maxArgs = id - DateProtoFunc::SetDate + 1; 0418 int idx = 0; 0419 int numArgs = args.size(); 0420 0421 // JS allows extra trailing arguments -- ignore them 0422 if (numArgs > maxArgs) { 0423 numArgs = maxArgs; 0424 } 0425 0426 // years 0427 if (maxArgs >= 3 && idx < numArgs) { 0428 t->tm_year = JSValue::toInt32(args[idx++], exec) - 1900; 0429 } 0430 0431 // months 0432 if (maxArgs >= 2 && idx < numArgs) { 0433 t->tm_mon = JSValue::toInt32(args[idx++], exec); 0434 } 0435 0436 // days 0437 if (idx < numArgs) { 0438 t->tm_mday = 0; 0439 ms += JSValue::toInt32(args[idx], exec) * msPerDay; 0440 } 0441 0442 return ms; 0443 } 0444 0445 // ------------------------------ DateInstance ------------------------------ 0446 0447 const ClassInfo DateInstance::info = {"Date", nullptr, nullptr, nullptr}; 0448 0449 DateInstance::DateInstance(JSObject *proto) 0450 : JSWrapperObject(proto) 0451 { 0452 } 0453 0454 JSObject *DateInstance::valueClone(Interpreter *targetCtx) const 0455 { 0456 DateInstance *copy = new DateInstance(targetCtx->builtinDatePrototype()); 0457 copy->setInternalValue(internalValue()); 0458 return copy; 0459 } 0460 0461 bool DateInstance::getTime(tm &t, int &offset) const 0462 { 0463 double milli = JSValue::getNumber(internalValue()); 0464 if (isNaN(milli)) { 0465 return false; 0466 } 0467 0468 millisecondsToTM(milli, false, &t); 0469 offset = gmtoffset(t); 0470 return true; 0471 } 0472 0473 bool DateInstance::getUTCTime(tm &t) const 0474 { 0475 double milli = JSValue::getNumber(internalValue()); 0476 if (isNaN(milli)) { 0477 return false; 0478 } 0479 0480 millisecondsToTM(milli, true, &t); 0481 return true; 0482 } 0483 0484 bool DateInstance::getTime(double &milli, int &offset) const 0485 { 0486 milli = JSValue::getNumber(internalValue()); 0487 if (isNaN(milli)) { 0488 return false; 0489 } 0490 0491 tm t; 0492 millisecondsToTM(milli, false, &t); 0493 offset = gmtoffset(t); 0494 return true; 0495 } 0496 0497 bool DateInstance::getUTCTime(double &milli) const 0498 { 0499 milli = JSValue::getNumber(internalValue()); 0500 if (isNaN(milli)) { 0501 return false; 0502 } 0503 0504 return true; 0505 } 0506 0507 static inline bool isTime_tSigned() 0508 { 0509 time_t minusOne = (time_t)(-1); 0510 return minusOne < 0; 0511 } 0512 0513 static void millisecondsToTM(double milli, bool utc, tm *t) 0514 { 0515 // check whether time value is outside time_t's usual range 0516 // make the necessary transformations if necessary 0517 static bool time_tIsSigned = isTime_tSigned(); 0518 #if PLATFORM(WIN_OS) 0519 static double time_tMin = 0; //on windows localtime/gmtime returns NULL for pre 1970 dates 0520 #else 0521 static double time_tMin = (time_tIsSigned ? - (double)(1ULL << (8 * sizeof(time_t) - 1)) : 0); 0522 #endif 0523 static double time_tMax = (time_tIsSigned ? (1ULL << (8 * sizeof(time_t) - 1)) - 1 : 2 * (double)(1ULL << (8 * sizeof(time_t) - 1)) - 1); 0524 int realYearOffset = 0; 0525 double milliOffset = 0.0; 0526 double secs = floor(milli / msPerSecond); 0527 0528 if (secs < time_tMin || secs > time_tMax) { 0529 // ### ugly and probably not very precise 0530 int realYear = yearFromTime(milli); 0531 int base = daysInYear(realYear) == 365 ? 2001 : 2000; 0532 milliOffset = timeFromYear(base) - timeFromYear(realYear); 0533 milli += milliOffset; 0534 realYearOffset = realYear - base; 0535 } 0536 0537 time_t tv = (time_t) floor(milli / msPerSecond); 0538 0539 *t = *(utc ? gmtime(&tv) : localtime(&tv)); 0540 // We had an out of range year. Restore the year (plus/minus offset 0541 // found by calculating tm_year) and fix the week day calculation. 0542 if (realYearOffset != 0) { 0543 t->tm_year += realYearOffset; 0544 milli -= milliOffset; 0545 // Do our own weekday calculation. Use time zone offset to handle local time. 0546 double m = milli; 0547 if (!utc) { 0548 m += gmtoffset(*t) * msPerSecond; 0549 } 0550 t->tm_wday = weekDay(m); 0551 } 0552 } 0553 0554 static bool isNaNorInf(double value) 0555 { 0556 return isNaN(value) || isInf(value); 0557 } 0558 0559 // ------------------------------ DatePrototype ----------------------------- 0560 0561 const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, &dateTable, nullptr}; 0562 0563 /* Source for date_object.lut.h 0564 We use a negative ID to denote the "UTC" variant. 0565 @begin dateTable 61 0566 toString DateProtoFunc::ToString DontEnum|Function 0 0567 toUTCString -DateProtoFunc::ToUTCString DontEnum|Function 0 0568 toDateString DateProtoFunc::ToDateString DontEnum|Function 0 0569 toTimeString DateProtoFunc::ToTimeString DontEnum|Function 0 0570 toISOString DateProtoFunc::ToISOString DontEnum|Function 0 0571 toJSON DateProtoFunc::ToJSON DontEnum|Function 1 0572 toLocaleString DateProtoFunc::ToLocaleString DontEnum|Function 0 0573 toLocaleDateString DateProtoFunc::ToLocaleDateString DontEnum|Function 0 0574 toLocaleTimeString DateProtoFunc::ToLocaleTimeString DontEnum|Function 0 0575 valueOf DateProtoFunc::ValueOf DontEnum|Function 0 0576 getTime DateProtoFunc::GetTime DontEnum|Function 0 0577 getFullYear DateProtoFunc::GetFullYear DontEnum|Function 0 0578 getUTCFullYear -DateProtoFunc::GetFullYear DontEnum|Function 0 0579 toGMTString -DateProtoFunc::ToGMTString DontEnum|Function 0 0580 getMonth DateProtoFunc::GetMonth DontEnum|Function 0 0581 getUTCMonth -DateProtoFunc::GetMonth DontEnum|Function 0 0582 getDate DateProtoFunc::GetDate DontEnum|Function 0 0583 getUTCDate -DateProtoFunc::GetDate DontEnum|Function 0 0584 getDay DateProtoFunc::GetDay DontEnum|Function 0 0585 getUTCDay -DateProtoFunc::GetDay DontEnum|Function 0 0586 getHours DateProtoFunc::GetHours DontEnum|Function 0 0587 getUTCHours -DateProtoFunc::GetHours DontEnum|Function 0 0588 getMinutes DateProtoFunc::GetMinutes DontEnum|Function 0 0589 getUTCMinutes -DateProtoFunc::GetMinutes DontEnum|Function 0 0590 getSeconds DateProtoFunc::GetSeconds DontEnum|Function 0 0591 getUTCSeconds -DateProtoFunc::GetSeconds DontEnum|Function 0 0592 getMilliseconds DateProtoFunc::GetMilliSeconds DontEnum|Function 0 0593 getUTCMilliseconds -DateProtoFunc::GetMilliSeconds DontEnum|Function 0 0594 getTimezoneOffset DateProtoFunc::GetTimezoneOffset DontEnum|Function 0 0595 setTime DateProtoFunc::SetTime DontEnum|Function 1 0596 setMilliseconds DateProtoFunc::SetMilliSeconds DontEnum|Function 1 0597 setUTCMilliseconds -DateProtoFunc::SetMilliSeconds DontEnum|Function 1 0598 setSeconds DateProtoFunc::SetSeconds DontEnum|Function 2 0599 setUTCSeconds -DateProtoFunc::SetSeconds DontEnum|Function 2 0600 setMinutes DateProtoFunc::SetMinutes DontEnum|Function 3 0601 setUTCMinutes -DateProtoFunc::SetMinutes DontEnum|Function 3 0602 setHours DateProtoFunc::SetHours DontEnum|Function 4 0603 setUTCHours -DateProtoFunc::SetHours DontEnum|Function 4 0604 setDate DateProtoFunc::SetDate DontEnum|Function 1 0605 setUTCDate -DateProtoFunc::SetDate DontEnum|Function 1 0606 setMonth DateProtoFunc::SetMonth DontEnum|Function 2 0607 setUTCMonth -DateProtoFunc::SetMonth DontEnum|Function 2 0608 setFullYear DateProtoFunc::SetFullYear DontEnum|Function 3 0609 setUTCFullYear -DateProtoFunc::SetFullYear DontEnum|Function 3 0610 setYear DateProtoFunc::SetYear DontEnum|Function 1 0611 getYear DateProtoFunc::GetYear DontEnum|Function 0 0612 @end 0613 */ 0614 // ECMA 15.9.4 0615 0616 DatePrototype::DatePrototype(ExecState *, ObjectPrototype *objectProto) 0617 : DateInstance(objectProto) 0618 { 0619 setInternalValue(jsNaN()); 0620 // The constructor will be added later, after DateObjectImp has been built. 0621 } 0622 0623 bool DatePrototype::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot) 0624 { 0625 return getStaticFunctionSlot<DateProtoFunc, JSObject>(exec, &dateTable, this, propertyName, slot); 0626 } 0627 0628 // ------------------------------ DateProtoFunc ----------------------------- 0629 0630 DateProtoFunc::DateProtoFunc(ExecState *exec, int i, int len, const Identifier &name) 0631 : InternalFunctionImp(static_cast<FunctionPrototype *>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name) 0632 , id(abs(i)) 0633 , utc(i < 0) 0634 // We use a negative ID to denote the "UTC" variant. 0635 { 0636 putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum); 0637 } 0638 0639 JSValue *DateProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) 0640 { 0641 if (id == ToJSON) { 0642 JSValue *tv = thisObj->toPrimitive(exec, NumberType); 0643 if (JSValue::isNumber(tv)) { 0644 double ms = JSValue::toNumber(tv, exec); 0645 if (isNaNorInf(ms)) { 0646 return jsNull(); 0647 } 0648 } 0649 0650 JSValue *toISO = thisObj->get(exec, exec->propertyNames().toISOString); 0651 if (!JSValue::implementsCall(toISO)) { 0652 return throwError(exec, TypeError, "toISOString is not callable"); 0653 } 0654 JSObject *toISOobj = JSValue::toObject(toISO, exec); 0655 if (!toISOobj) { 0656 return throwError(exec, TypeError, "toISOString is not callable"); 0657 } 0658 return toISOobj->call(exec, thisObj, List::empty()); 0659 } 0660 0661 if (!thisObj->inherits(&DateInstance::info)) { 0662 if (id == ToString) 0663 return jsString("Invalid Date"); 0664 return throwError(exec, TypeError, "Incompatible object"); 0665 } 0666 0667 DateInstance *thisDateObj = static_cast<DateInstance *>(thisObj); 0668 0669 JSValue *result = nullptr; 0670 #if !PLATFORM(MAC) 0671 const int bufsize = 100; 0672 char timebuffer[bufsize]; 0673 CString oldlocale = setlocale(LC_TIME, nullptr); 0674 if (!oldlocale.size()) { 0675 oldlocale = setlocale(LC_ALL, nullptr); 0676 } 0677 // FIXME: Where's the code to set the locale back to oldlocale? 0678 #endif 0679 JSValue *v = thisDateObj->internalValue(); 0680 double milli = JSValue::toNumber(v, exec); 0681 if (isNaN(milli)) { 0682 switch (id) { 0683 case ToString: 0684 case ToDateString: 0685 case ToTimeString: 0686 case ToGMTString: 0687 case ToUTCString: 0688 case ToLocaleString: 0689 case ToLocaleDateString: 0690 case ToLocaleTimeString: 0691 return jsString("Invalid Date", 12); 0692 case ValueOf: 0693 case GetTime: 0694 case GetYear: 0695 case GetFullYear: 0696 case GetMonth: 0697 case GetDate: 0698 case GetDay: 0699 case GetHours: 0700 case GetMinutes: 0701 case GetSeconds: 0702 case GetMilliSeconds: 0703 case GetTimezoneOffset: 0704 case SetMilliSeconds: 0705 case SetSeconds: 0706 case SetMinutes: 0707 case SetHours: 0708 case SetDate: 0709 case SetMonth: 0710 case SetFullYear: 0711 return jsNaN(); 0712 case ToISOString: 0713 return throwError(exec, RangeError, "Invalid Date"); 0714 } 0715 } 0716 0717 if (id == SetTime) { 0718 double milli = roundValue(exec, args[0]); 0719 result = jsNumber(timeClip(milli)); 0720 thisDateObj->setInternalValue(result); 0721 return result; 0722 } 0723 0724 double secs = floor(milli / msPerSecond); 0725 double ms = milli - secs * msPerSecond; 0726 0727 tm t; 0728 millisecondsToTM(milli, utc, &t); 0729 0730 switch (id) { 0731 case ToString: 0732 return jsString(formatDate(t).append(' ').append(formatTime(t, utc))); 0733 0734 case ToDateString: 0735 return jsString(formatDate(t)); 0736 0737 case ToTimeString: 0738 return jsString(formatTime(t, utc)); 0739 0740 case ToGMTString: 0741 case ToUTCString: 0742 return jsString(formatDateUTCVariant(t).append(' ').append(formatTime(t, utc))); 0743 case ToISOString: 0744 return jsString(formatDateISOVariant(t, utc, milli).append('T').append(formatTimeISOVariant(t, utc, milli, ms)).append('Z')); 0745 0746 #if PLATFORM(MAC) 0747 case ToLocaleString: 0748 return jsString(formatLocaleDate(exec, secs, true, true, args)); 0749 0750 case ToLocaleDateString: 0751 return jsString(formatLocaleDate(exec, secs, true, false, args)); 0752 0753 case ToLocaleTimeString: 0754 return jsString(formatLocaleDate(exec, secs, false, true, args)); 0755 0756 #else 0757 case ToLocaleString: 0758 return jsString(timebuffer, strftime(timebuffer, bufsize, "%c", &t)); 0759 0760 case ToLocaleDateString: 0761 return jsString(timebuffer, strftime(timebuffer, bufsize, "%x", &t)); 0762 0763 case ToLocaleTimeString: 0764 return jsString(timebuffer, strftime(timebuffer, bufsize, "%X", &t)); 0765 0766 #endif 0767 case ValueOf: 0768 case GetTime: 0769 return jsNumber(milli); 0770 case GetYear: 0771 // IE returns the full year even in getYear. 0772 if (exec->dynamicInterpreter()->compatMode() == Interpreter::IECompat) { 0773 return jsNumber(1900 + t.tm_year); 0774 } 0775 return jsNumber(t.tm_year); 0776 case GetFullYear: 0777 return jsNumber(1900 + t.tm_year); 0778 case GetMonth: 0779 return jsNumber(t.tm_mon); 0780 case GetDate: 0781 return jsNumber(t.tm_mday); 0782 case GetDay: 0783 return jsNumber(t.tm_wday); 0784 case GetHours: 0785 return jsNumber(t.tm_hour); 0786 case GetMinutes: 0787 return jsNumber(t.tm_min); 0788 case GetSeconds: 0789 return jsNumber(t.tm_sec); 0790 case GetMilliSeconds: 0791 return jsNumber(ms); 0792 case GetTimezoneOffset: 0793 return jsNumber(-gmtoffset(t) / 60); 0794 0795 case SetMilliSeconds: 0796 case SetSeconds: 0797 case SetMinutes: 0798 case SetHours: 0799 ms = args.size() > 0 ? setTimeFields(exec, args, id, ms, &t) : NaN; 0800 break; 0801 0802 case SetDate: 0803 case SetMonth: 0804 case SetFullYear: 0805 ms = args.size() > 0 ? setDateFields(exec, args, id, ms, &t) : NaN; 0806 break; 0807 0808 case SetYear: { 0809 int32_t year = JSValue::toInt32(args[0], exec); 0810 t.tm_year = (year > 99 || year < 0) ? year - 1900 : year; 0811 break; 0812 } 0813 } 0814 0815 if (id == SetYear || id == SetMilliSeconds || id == SetSeconds || 0816 id == SetMinutes || id == SetHours || id == SetDate || 0817 id == SetMonth || id == SetFullYear) { 0818 result = jsNumber(isNaN(ms) ? ms : timeClip(makeTime(&t, ms, utc))); 0819 thisDateObj->setInternalValue(result); 0820 } 0821 0822 return result; 0823 } 0824 0825 // ------------------------------ DateObjectImp -------------------------------- 0826 0827 DateObjectImp::DateObjectImp(ExecState *exec, 0828 FunctionPrototype *funcProto, 0829 DatePrototype *dateProto) 0830 : InternalFunctionImp(funcProto) 0831 { 0832 // ECMA 15.9.4.1 Date.prototype 0833 static const Identifier *parsePropertyName = new Identifier("parse"); 0834 static const Identifier *UTCPropertyName = new Identifier("UTC"); 0835 static const Identifier *nowPropertyName = new Identifier("now"); 0836 0837 putDirect(exec->propertyNames().prototype, dateProto, DontEnum | DontDelete | ReadOnly); 0838 putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Parse, 1, *parsePropertyName), DontEnum); 0839 putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::UTC, 7, *UTCPropertyName), DontEnum); 0840 putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Now, 0, *nowPropertyName), DontEnum); 0841 0842 // no. of arguments for constructor 0843 putDirect(exec->propertyNames().length, 7, ReadOnly | DontDelete | DontEnum); 0844 } 0845 0846 bool DateObjectImp::implementsConstruct() const 0847 { 0848 return true; 0849 } 0850 0851 static double getCurrentUTCTime() 0852 { 0853 #if PLATFORM(WIN_OS) 0854 #if defined(WTF_COMPILER_BORLAND) 0855 struct timeb timebuffer; 0856 ftime(&timebuffer); 0857 #else 0858 struct _timeb timebuffer; 0859 _ftime(&timebuffer); 0860 #endif 0861 double utc = timebuffer.time * msPerSecond + timebuffer.millitm; 0862 #else 0863 struct timeval tv; 0864 gettimeofday(&tv, nullptr); 0865 double utc = floor(tv.tv_sec * msPerSecond + tv.tv_usec / 1000); 0866 #endif 0867 return utc; 0868 } 0869 0870 static double makeTimeFromList(ExecState *exec, const List &args, bool utc) 0871 { 0872 const int numArgs = args.size(); 0873 if (isNaNorInf(JSValue::toNumber(args[0], exec)) 0874 || isNaNorInf(JSValue::toNumber(args[1], exec)) 0875 || (numArgs >= 3 && isNaNorInf(JSValue::toNumber(args[2], exec))) 0876 || (numArgs >= 4 && isNaNorInf(JSValue::toNumber(args[3], exec))) 0877 || (numArgs >= 5 && isNaNorInf(JSValue::toNumber(args[4], exec))) 0878 || (numArgs >= 6 && isNaNorInf(JSValue::toNumber(args[5], exec))) 0879 || (numArgs >= 7 && isNaNorInf(JSValue::toNumber(args[6], exec)))) { 0880 return NaN; 0881 } 0882 0883 tm t; 0884 memset(&t, 0, sizeof(t)); 0885 int year = JSValue::toInt32(args[0], exec); 0886 t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900; 0887 t.tm_mon = JSValue::toInt32(args[1], exec); 0888 t.tm_mday = (numArgs >= 3) ? JSValue::toInt32(args[2], exec) : 1; 0889 t.tm_hour = (numArgs >= 4) ? JSValue::toInt32(args[3], exec) : 0; 0890 t.tm_min = (numArgs >= 5) ? JSValue::toInt32(args[4], exec) : 0; 0891 t.tm_sec = (numArgs >= 6) ? JSValue::toInt32(args[5], exec) : 0; 0892 if (!utc) { 0893 t.tm_isdst = -1; 0894 } 0895 double ms = (numArgs >= 7) ? roundValue(exec, args[6]) : 0; 0896 return makeTime(&t, ms, utc); 0897 } 0898 0899 // ECMA 15.9.3 0900 JSObject *DateObjectImp::construct(ExecState *exec, const List &args) 0901 { 0902 int numArgs = args.size(); 0903 double value; 0904 0905 if (numArgs == 0) { // new Date() ECMA 15.9.3.3 0906 value = getCurrentUTCTime(); 0907 } else if (numArgs == 1) { 0908 JSValue *arg0 = args[0]; 0909 if (JSValue::isObject(arg0, &DateInstance::info)) { 0910 value = JSValue::toNumber(static_cast<DateInstance *>(arg0)->internalValue(), exec); 0911 } else { 0912 JSValue *primitive = JSValue::toPrimitive(arg0, exec); 0913 if (JSValue::isString(primitive)) { 0914 value = parseDate(JSValue::getString(primitive)); 0915 } else { 0916 value = JSValue::toNumber(primitive, exec); 0917 } 0918 } 0919 } else { 0920 value = makeTimeFromList(exec, args, false); 0921 } 0922 0923 DateInstance *ret = new DateInstance(exec->lexicalInterpreter()->builtinDatePrototype()); 0924 ret->setInternalValue(jsNumber(timeClip(value))); 0925 return ret; 0926 } 0927 0928 // ECMA 15.9.2 0929 JSValue *DateObjectImp::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/) 0930 { 0931 time_t t = time(nullptr); 0932 tm ts = *localtime(&t); 0933 return jsString(formatDate(ts).append(' ').append(formatTime(ts, false))); 0934 } 0935 0936 // ------------------------------ DateObjectFuncImp ---------------------------- 0937 0938 DateObjectFuncImp::DateObjectFuncImp(ExecState *exec, FunctionPrototype *funcProto, int i, int len, const Identifier &name) 0939 : InternalFunctionImp(funcProto, name), id(i) 0940 { 0941 putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum); 0942 } 0943 0944 // ECMA 15.9.4.2 - 3 0945 JSValue *DateObjectFuncImp::callAsFunction(ExecState *exec, JSObject *, const List &args) 0946 { 0947 if (id == Parse) { 0948 return jsNumber(parseDate(JSValue::toString(args[0], exec))); 0949 } else if (id == Now) { 0950 return jsNumber(getCurrentUTCTime()); 0951 } else { // UTC 0952 return jsNumber(makeTimeFromList(exec, args, true)); 0953 } 0954 } 0955 0956 // ----------------------------------------------------------------------------- 0957 0958 // Code originally from krfcdate.cpp, but we don't want to use kdecore, and we want double range. 0959 0960 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second) 0961 { 0962 // in which case is the floor() needed? breaks day value of 0963 // "new Date('Thu Nov 5 2065 18:15:30 GMT+0500')" 0964 #if 0 0965 double days = (day - 32075) 0966 + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4) 0967 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12 0968 - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4) 0969 - 2440588; 0970 #else 0971 double days = (day - 32075) 0972 + 1461 * (year + 4800 + (mon - 14) / 12) / 4 0973 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12 0974 - 3 * ((year + 4900 + (mon - 14) / 12) / 100) / 4 0975 - 2440588; 0976 #endif 0977 return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second; 0978 } 0979 0980 // We follow the recommendation of RFC 2822 to consider all 0981 // obsolete time zones not listed here equivalent to "-0000". 0982 static const struct KnownZone { 0983 #if !PLATFORM(WIN_OS) 0984 const 0985 #endif 0986 char tzName[4]; 0987 int tzOffset; 0988 } known_zones[] = { 0989 { "UT", 0 }, 0990 { "GMT", 0 }, 0991 { "EST", -300 }, 0992 { "EDT", -240 }, 0993 { "CST", -360 }, 0994 { "CDT", -300 }, 0995 { "MST", -420 }, 0996 { "MDT", -360 }, 0997 { "PST", -480 }, 0998 { "PDT", -420 } 0999 }; 1000 1001 #if PLATFORM(WIN_OS) 1002 void FileTimeToUnixTime(LPFILETIME pft, double *pt) 1003 { 1004 ULARGE_INTEGER ull; 1005 ull.LowPart = pft->dwLowDateTime; 1006 ull.HighPart = pft->dwHighDateTime; 1007 *pt = (double)(ull.QuadPart / 10000000ULL) - 11644473600ULL; 1008 } 1009 1010 void SystemTimeToUnixTime(LPSYSTEMTIME pst, double *pt) 1011 { 1012 FILETIME ft; 1013 SystemTimeToFileTime(pst, &ft); 1014 FileTimeToUnixTime(&ft, pt); 1015 } 1016 #endif 1017 1018 static double makeTime(tm *t, double ms, bool utc) 1019 { 1020 int utcOffset; 1021 if (utc) { 1022 time_t zero = 0; 1023 #if PLATFORM(WIN_OS) 1024 // FIXME: not thread safe 1025 (void)localtime(&zero); 1026 #if defined(WTF_COMPILER_BORLAND) || defined(WTF_COMPILER_CYGWIN) || defined(WTF_COMPILER_MSVC) 1027 utcOffset = - _timezone; 1028 #else 1029 utcOffset = - timezone; 1030 #endif 1031 t->tm_isdst = 0; 1032 #elif PLATFORM(DARWIN) 1033 utcOffset = 0; 1034 t->tm_isdst = 0; 1035 #else 1036 tm t3; 1037 localtime_r(&zero, &t3); 1038 utcOffset = gmtoffset(t3); 1039 t->tm_isdst = t3.tm_isdst; 1040 #endif 1041 } else { 1042 utcOffset = 0; 1043 t->tm_isdst = -1; 1044 } 1045 1046 #if !PLATFORM(WIN_OS) 1047 double yearOffset = 0.0; 1048 if (t->tm_year < (1971 - 1900) || t->tm_year > (2037 - 1900)) { 1049 // we'll fool mktime() into believing that this year is within 1050 // its normal, portable range (1970-2038) by setting tm_year to 1051 // 2000 or 2001 and adding the difference in milliseconds later. 1052 // choice between offset will depend on whether the year is a 1053 // leap year or not. 1054 int y = t->tm_year + 1900; 1055 int baseYear = daysInYear(y) == 365 ? 2001 : 2000; 1056 double baseTime = timeFromYear(baseYear); 1057 yearOffset = timeFromYear(y) - baseTime; 1058 t->tm_year = baseYear - 1900; 1059 } 1060 1061 // Determine whether DST is in effect. mktime() can't do this for us because 1062 // it doesn't know about ms and yearOffset. 1063 // NOTE: Casting values of large magnitude to time_t (long) will 1064 // produce incorrect results, but there's no other option when calling localtime_r(). 1065 if (!utc) { 1066 time_t tval = mktime(t) + (time_t)((ms + yearOffset) / 1000); 1067 tm t3 = *localtime(&tval); 1068 t->tm_isdst = t3.tm_isdst; 1069 } 1070 1071 return (mktime(t) + utcOffset) * msPerSecond + ms + yearOffset; 1072 #else 1073 SYSTEMTIME st, dt; 1074 double tval; 1075 1076 st.wYear = 1900 + t->tm_year; 1077 st.wMonth = t->tm_mon + 1; 1078 st.wDayOfWeek = t->tm_wday; 1079 st.wDay = t->tm_mday; 1080 st.wHour = t->tm_hour; 1081 st.wMinute = t->tm_min; 1082 st.wSecond = t->tm_sec; 1083 st.wMilliseconds = 0; 1084 1085 TzSpecificLocalTimeToSystemTime(0, &st, &dt); 1086 SystemTimeToUnixTime(&dt, &tval); 1087 1088 return (tval + utcOffset) * msPerSecond + ms; 1089 #endif 1090 } 1091 1092 inline static bool isSpaceLike(char c) 1093 { 1094 return isASCIISpace(c) || c == ',' || c == ':' || c == '-'; 1095 } 1096 1097 static const char *skipSpacesAndComments(const char *s) 1098 { 1099 int nesting = 0; 1100 char ch; 1101 while ((ch = *s)) { 1102 // interpret - before a number as a sign rather than a comment char 1103 if (ch == '-' && isASCIIDigit(*(s + 1))) { 1104 break; 1105 } 1106 if (!isSpaceLike(ch)) { 1107 if (ch == '(') { 1108 nesting++; 1109 } else if (ch == ')' && nesting > 0) { 1110 nesting--; 1111 } else if (nesting == 0) { 1112 break; 1113 } 1114 } 1115 s++; 1116 } 1117 return s; 1118 } 1119 1120 // returns 0-11 (Jan-Dec); -1 on failure 1121 static int findMonth(const char *monthStr) 1122 { 1123 assert(monthStr); 1124 char needle[4]; 1125 for (int i = 0; i < 3; ++i) { 1126 if (!*monthStr) { 1127 return -1; 1128 } 1129 needle[i] = toASCIILower(*monthStr++); 1130 } 1131 needle[3] = '\0'; 1132 const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec"; 1133 const char *str = strstr(haystack, needle); 1134 if (str) { 1135 int position = str - haystack; 1136 if (position % 3 == 0) { 1137 return position / 3; 1138 } 1139 } 1140 return -1; 1141 } 1142 1143 static bool isTwoDigits(const char *str) 1144 { 1145 return isASCIIDigit(str[0]) && isASCIIDigit(str[1]); 1146 } 1147 1148 static int twoDigit(const char *str) 1149 { 1150 return (str[0] - '0') * 10 + str[1] - '0'; 1151 } 1152 1153 static double parseDate(const UString &date) 1154 { 1155 // This parses a date in the form: 1156 // Tuesday, 09-Nov-99 23:12:40 GMT 1157 // or 1158 // Sat, 01-Jan-2000 08:00:00 GMT 1159 // or 1160 // Sat, 01 Jan 2000 08:00:00 GMT 1161 // or 1162 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822) 1163 // ### non RFC formats, added for Javascript: 1164 // [Wednesday] January 09 1999 23:12:40 GMT 1165 // [Wednesday] January 09 23:12:40 GMT 1999 1166 // 1167 // We ignore the weekday. 1168 1169 CString dateCString = date.UTF8String(); 1170 const char *dateString = dateCString.c_str(); 1171 if (!dateString) { 1172 return NaN; 1173 } 1174 1175 // Skip leading space 1176 dateString = skipSpacesAndComments(dateString); 1177 1178 // ISO 8601: "YYYY-MM-DD('T'|'t')hh:mm:ss[.S+]['Z']" 1179 // e.g. "2006-06-15T23:12:10.207830Z" 1180 if (isTwoDigits(dateString) && 1181 isTwoDigits(dateString + 2) && 1182 dateString[4] == '-' && 1183 isTwoDigits(dateString + 5) && 1184 dateString[7] == '-' && 1185 isTwoDigits(dateString + 8)) { 1186 int year = twoDigit(dateString) * 100 + twoDigit(dateString + 2); 1187 int month = twoDigit(dateString + 5) - 1; 1188 int day = twoDigit(dateString + 8); 1189 if (month > 11 || day < 1 || day > 31) { 1190 return NaN; 1191 } 1192 int hour = 0, minute = 0; 1193 double second = 0; 1194 dateString += 10; 1195 if ((dateString[0] | 0x20) == 't' && 1196 isTwoDigits(dateString + 1) && 1197 dateString[3] == ':' && 1198 isTwoDigits(dateString + 4)) { 1199 hour = twoDigit(dateString + 1); 1200 minute = twoDigit(dateString + 4); 1201 if (hour > 23 || minute > 59) { 1202 return NaN; 1203 } 1204 dateString += 6; 1205 if (dateString[0] == ':' && 1206 isTwoDigits(dateString + 1)) { 1207 second = twoDigit(dateString + 1); 1208 if (second > 59) { 1209 return NaN; 1210 } 1211 dateString += 3; 1212 if (dateString[0] == '.' && 1213 isASCIIDigit(dateString[1])) { 1214 dateString++; 1215 double div = 10; 1216 do { 1217 second += (dateString[0] - '0') / div; 1218 div *= 10; 1219 } while (isASCIIDigit(*++dateString)); 1220 } 1221 } 1222 } 1223 1224 if (dateString[0] == 'Z') { 1225 tm t; 1226 memset(&t, 0, sizeof(tm)); 1227 int secs = int(second); 1228 t.tm_sec = secs; 1229 t.tm_min = minute; 1230 t.tm_hour = hour; 1231 t.tm_mday = day; 1232 t.tm_mon = month; 1233 t.tm_year = year - 1900; 1234 // t.tm_isdst = -1; 1235 1236 // Use our makeTime() rather than mktime() as the latter can't handle the full year range. 1237 return makeTime(&t, (second - secs) * 1000, true); 1238 } 1239 1240 int offset = 0; 1241 return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond; 1242 } 1243 1244 long month = -1; 1245 const char *wordStart = dateString; 1246 // Check contents of first words if not number 1247 while (*dateString && !isASCIIDigit(*dateString)) { 1248 if (isASCIISpace(*dateString) || *dateString == '(') { 1249 if (dateString - wordStart >= 3) { 1250 month = findMonth(wordStart); 1251 } 1252 dateString = skipSpacesAndComments(dateString); 1253 wordStart = dateString; 1254 } else { 1255 dateString++; 1256 } 1257 } 1258 1259 // Missing delimiter between month and day (like "January29")? 1260 if (month == -1 && wordStart != dateString) { 1261 month = findMonth(wordStart); 1262 } 1263 1264 dateString = skipSpacesAndComments(dateString); 1265 1266 if (!*dateString) { 1267 return NaN; 1268 } 1269 1270 // ' 09-Nov-99 23:12:40 GMT' 1271 char *newPosStr; 1272 errno = 0; 1273 long day = strtol(dateString, &newPosStr, 10); 1274 dateString = newPosStr; 1275 1276 if (errno || day < 0 || !*dateString) { 1277 return NaN; 1278 } 1279 1280 long year = 0; 1281 if (day > 31) { 1282 // ### where is the boundary and what happens below? 1283 if (*dateString != '/') { 1284 return NaN; 1285 } 1286 // looks like a YYYY/MM/DD date 1287 if (!*++dateString) { 1288 return NaN; 1289 } 1290 year = day; 1291 month = strtol(dateString, &newPosStr, 10) - 1; 1292 if (errno) { 1293 return NaN; 1294 } 1295 dateString = newPosStr; 1296 if (*dateString++ != '/' || !*dateString) { 1297 return NaN; 1298 } 1299 day = strtol(dateString, &newPosStr, 10); 1300 if (errno) { 1301 return NaN; 1302 } 1303 dateString = newPosStr; 1304 } else if (*dateString == '/' && month == -1) { 1305 dateString++; 1306 // This looks like a MM/DD/YYYY date, not an RFC date. 1307 month = day - 1; // 0-based 1308 day = strtol(dateString, &newPosStr, 10); 1309 if (errno) { 1310 return NaN; 1311 } 1312 dateString = newPosStr; 1313 if (*dateString == '/') { 1314 dateString++; 1315 } 1316 if (!*dateString) { 1317 return NaN; 1318 } 1319 } else { 1320 if (*dateString == '-') { 1321 dateString++; 1322 } 1323 1324 dateString = skipSpacesAndComments(dateString); 1325 1326 if (*dateString == ',') { 1327 dateString++; 1328 } 1329 1330 if (month == -1) { // not found yet 1331 month = findMonth(dateString); 1332 if (month == -1) { 1333 return NaN; 1334 } 1335 1336 while (*dateString && (*dateString != '-') && !isASCIISpace(*dateString)) { 1337 dateString++; 1338 } 1339 1340 if (!*dateString) { 1341 return NaN; 1342 } 1343 1344 // '-99 23:12:40 GMT' 1345 if (*dateString != '-' && *dateString != '/' && !isASCIISpace(*dateString)) { 1346 return NaN; 1347 } 1348 dateString++; 1349 } 1350 1351 if (month < 0 || month > 11) { 1352 return NaN; 1353 } 1354 } 1355 1356 // '99 23:12:40 GMT' 1357 if (year <= 0 && *dateString) { 1358 year = strtol(dateString, &newPosStr, 10); 1359 if (errno) { 1360 return NaN; 1361 } 1362 } 1363 1364 // Don't fail if the time is missing. 1365 long hour = 0; 1366 long minute = 0; 1367 long second = 0; 1368 if (!*newPosStr) { 1369 dateString = newPosStr; 1370 } else { 1371 // ' 23:12:40 GMT' 1372 if (*newPosStr == ':') { 1373 // There was no year; the number was the hour. 1374 year = -1; 1375 } else if (isSpaceLike(*newPosStr)) { 1376 // in the normal case (we parsed the year), advance to the next number 1377 dateString = skipSpacesAndComments(newPosStr + 1); 1378 } else { 1379 return NaN; 1380 } 1381 1382 hour = strtol(dateString, &newPosStr, 10); 1383 // Do not check for errno here since we want to continue 1384 // even if errno was set because we are still looking 1385 // for the timezone! 1386 1387 // Read a number? If not, this might be a timezone name. 1388 if (newPosStr != dateString) { 1389 dateString = newPosStr; 1390 1391 if (hour < 0 || hour > 23) { 1392 return NaN; 1393 } 1394 1395 if (!*dateString) { 1396 return NaN; 1397 } 1398 1399 // ':12:40 GMT' 1400 if (*dateString++ != ':') { 1401 return NaN; 1402 } 1403 1404 minute = strtol(dateString, &newPosStr, 10); 1405 if (errno) { 1406 return NaN; 1407 } 1408 dateString = newPosStr; 1409 1410 if (minute < 0 || minute > 59) { 1411 return NaN; 1412 } 1413 1414 // ':40 GMT' 1415 if (*dateString && *dateString != ':' && !isASCIISpace(*dateString)) { 1416 return NaN; 1417 } 1418 1419 // seconds are optional in rfc822 + rfc2822 1420 if (*dateString == ':') { 1421 dateString++; 1422 1423 second = strtol(dateString, &newPosStr, 10); 1424 if (errno) { 1425 return NaN; 1426 } 1427 dateString = newPosStr; 1428 1429 if (second < 0 || second > 59) { 1430 return NaN; 1431 } 1432 1433 // disallow trailing colon seconds 1434 if (*dateString == ':') { 1435 return NaN; 1436 } 1437 } 1438 1439 dateString = skipSpacesAndComments(dateString); 1440 1441 if (strncasecmp(dateString, "AM", 2) == 0) { 1442 if (hour > 12) { 1443 return NaN; 1444 } 1445 if (hour == 12) { 1446 hour = 0; 1447 } 1448 dateString = skipSpacesAndComments(dateString + 2); 1449 } else if (strncasecmp(dateString, "PM", 2) == 0) { 1450 if (hour > 12) { 1451 return NaN; 1452 } 1453 if (hour != 12) { 1454 hour += 12; 1455 } 1456 dateString = skipSpacesAndComments(dateString + 2); 1457 } 1458 } 1459 } 1460 1461 bool haveTZ = false; 1462 int offset = 0; 1463 1464 // Don't fail if the time zone is missing. 1465 // Some websites omit the time zone (4275206). 1466 if (*dateString) { 1467 if (strncasecmp(dateString, "GMT", 3) == 0 || 1468 strncasecmp(dateString, "UTC", 3) == 0) { 1469 dateString += 3; 1470 haveTZ = true; 1471 } 1472 1473 if (*dateString == '+' || *dateString == '-') { 1474 long o = strtol(dateString, &newPosStr, 10); 1475 if (errno) { 1476 return NaN; 1477 } 1478 dateString = newPosStr; 1479 1480 if (o < -9959 || o > 9959) { 1481 return NaN; 1482 } 1483 1484 int sgn = (o < 0) ? -1 : 1; 1485 o = abs(o); 1486 if (*dateString != ':') { 1487 offset = ((o / 100) * 60 + (o % 100)) * sgn; 1488 } else { // GMT+05:00 1489 dateString++; 1490 long o2 = strtol(dateString, &newPosStr, 10); 1491 if (errno) { 1492 return NaN; 1493 } 1494 dateString = newPosStr; 1495 offset = (o * 60 + o2) * sgn; 1496 } 1497 haveTZ = true; 1498 } else { 1499 for (int i = 0; i < int(sizeof(known_zones) / sizeof(KnownZone)); i++) { 1500 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) { 1501 offset = known_zones[i].tzOffset; 1502 dateString += strlen(known_zones[i].tzName); 1503 haveTZ = true; 1504 break; 1505 } 1506 } 1507 } 1508 } 1509 1510 dateString = skipSpacesAndComments(dateString); 1511 1512 if (*dateString && year == -1) { 1513 year = strtol(dateString, &newPosStr, 10); 1514 if (errno) { 1515 return NaN; 1516 } 1517 dateString = newPosStr; 1518 } 1519 1520 dateString = skipSpacesAndComments(dateString); 1521 1522 // Trailing garbage 1523 if (*dateString) { 1524 return NaN; 1525 } 1526 1527 // Y2K: Handle 2 digit years. 1528 if (year >= 0 && year < 100) { 1529 if (year < 50) { 1530 year += 2000; 1531 } else { 1532 year += 1900; 1533 } 1534 } 1535 1536 // fall back to local timezone 1537 if (!haveTZ) { 1538 tm t; 1539 memset(&t, 0, sizeof(tm)); 1540 t.tm_mday = day; 1541 t.tm_mon = month; 1542 t.tm_year = year - 1900; 1543 t.tm_isdst = -1; 1544 t.tm_sec = second; 1545 t.tm_min = minute; 1546 t.tm_hour = hour; 1547 1548 // Use our makeTime() rather than mktime() as the latter can't handle the full year range. 1549 return makeTime(&t, 0, false); 1550 } 1551 1552 return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond; 1553 } 1554 1555 double timeClip(double t) 1556 { 1557 if (isNaN(t) || isInf(t)) { 1558 return NaN; 1559 } 1560 double at = fabs(t); 1561 if (at > 8.64E15) { 1562 return NaN; 1563 } 1564 return copysign(floor(at), t); 1565 } 1566 1567 } // namespace KJS