Ignore:
Timestamp:
Jun 8, 2009, 3:37:06 PM (16 years ago)
Author:
Dimitri Glazkov
Message:

JavaScriptCore:

2009-06-08 Dimitri Glazkov <Dimitri Glazkov>

Reviewed by Eric Seidel.

https://bugs.webkit.org/show_bug.cgi?id=26238
Move most of runtime/DateMath functions to wtf/DateMath, and split off conversion-related
helpers to DateConversion.

  • AllInOneFile.cpp: Changed DateMath->DateConversion.
  • GNUmakefile.am: Ditto and added DateMath.
  • JavaScriptCore.exp: Ditto.
  • JavaScriptCore.pri: Ditto.
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: Ditto.
  • JavaScriptCore.vcproj/WTF/WTF.vcproj: Added DateMath.
  • JavaScriptCore.xcodeproj/project.pbxproj: Ditto.
  • JavaScriptCoreSources.bkl: Ditto.
  • pcre/pcre_exec.cpp: Changed to use DateMath.
  • profiler/ProfileNode.cpp: (JSC::getCount): Changed to use DateConversion.
  • runtime/DateConstructor.cpp: Ditto.
  • runtime/DateConversion.cpp: Copied from JavaScriptCore/runtime/DateMath.cpp. (JSC::parseDate): Refactored to use null-terminated characters as input.
  • runtime/DateConversion.h: Copied from JavaScriptCore/runtime/DateMath.h.
  • runtime/DateInstance.cpp: Changed to use wtf/DateMath.
  • runtime/DateInstance.h: Ditto.
  • runtime/DateMath.cpp: Removed.
  • runtime/DateMath.h: Removed.
  • runtime/DatePrototype.cpp: Ditto.
  • runtime/InitializeThreading.cpp: Ditto.
  • wtf/DateMath.cpp: Copied from JavaScriptCore/runtime/DateMath.cpp.
  • wtf/DateMath.h: Copied from JavaScriptCore/runtime/DateMath.h.

WebCore:

2009-06-08 Dimitri Glazkov <Dimitri Glazkov>

Reviewed by Eric Seidel.

https://bugs.webkit.org/show_bug.cgi?id=26238
Add parseDate helper to HTTPParsers, which uses WTF::parseDateFromNullTerminatedCharacters.

  • ForwardingHeaders/runtime/DateMath.h: Removed.
  • ForwardingHeaders/wtf/DateMath.h: Copied from WebCore/ForwardingHeaders/runtime/DateMath.h.
  • platform/network/HTTPParsers.cpp: (WebCore::parseDate): Added.
  • platform/network/HTTPParsers.h:
  • platform/network/ResourceResponseBase.cpp: (WebCore::parseDateValueInHeader): Changed to use the new helper.
File:
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/runtime/DateConversion.cpp

    r44501 r44508  
    22 * Copyright (C) 1999-2000 Harri Porten ([email protected])
    33 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
    4  *
     4 * Copyright (C) 2009 Google Inc. All rights reserved.
     5 *
    56 * The Original Code is Mozilla Communicator client code, released
    67 * March 31, 1998.
     
    4142
    4243#include "config.h"
    43 #include "DateMath.h"
     44#include "DateConversion.h"
    4445
    45 #include "JSNumberCell.h"
    46 #include <math.h>
    47 #include <stdint.h>
    48 #include <time.h>
    49 #include <wtf/ASCIICType.h>
    50 #include <wtf/Assertions.h>
    51 #include <wtf/CurrentTime.h>
    52 #include <wtf/MathExtras.h>
    53 #include <wtf/StringExtras.h>
    54 
    55 #if HAVE(ERRNO_H)
    56 #include <errno.h>
    57 #endif
    58 
    59 #if PLATFORM(DARWIN)
    60 #include <notify.h>
    61 #endif
    62 
    63 #if HAVE(SYS_TIME_H)
    64 #include <sys/time.h>
    65 #endif
    66 
    67 #if HAVE(SYS_TIMEB_H)
    68 #include <sys/timeb.h>
    69 #endif
     46#include <wtf/DateMath.h>
    7047
    7148using namespace WTF;
     
    7350namespace JSC {
    7451
    75 /* Constants */
    76 
    77 static const double minutesPerDay = 24.0 * 60.0;
    78 static const double secondsPerDay = 24.0 * 60.0 * 60.0;
    79 static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0;
    80 
    81 static const double usecPerSec = 1000000.0;
    82 
    83 static const double maxUnixTime = 2145859200.0; // 12/31/2037
    84 
    85 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1.
    86 // First for non-leap years, then for leap years.
    87 static const int firstDayOfMonth[2][12] = {
    88     {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
    89     {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
    90 };
    91 
    92 static inline bool isLeapYear(int year)
    93 {
    94     if (year % 4 != 0)
    95         return false;
    96     if (year % 400 == 0)
    97         return true;
    98     if (year % 100 == 0)
    99         return false;
    100     return true;
    101 }
    102 
    103 static inline int daysInYear(int year)
    104 {
    105     return 365 + isLeapYear(year);
    106 }
    107 
    108 static inline double daysFrom1970ToYear(int year)
    109 {
    110     // The Gregorian Calendar rules for leap years:
    111     // Every fourth year is a leap year.  2004, 2008, and 2012 are leap years.
    112     // However, every hundredth year is not a leap year.  1900 and 2100 are not leap years.
    113     // Every four hundred years, there's a leap year after all.  2000 and 2400 are leap years.
    114 
    115     static const int leapDaysBefore1971By4Rule = 1970 / 4;
    116     static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100;
    117     static const int leapDaysBefore1971By400Rule = 1970 / 400;
    118 
    119     const double yearMinusOne = year - 1;
    120     const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule;
    121     const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule;
    122     const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule;
    123 
    124     return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;
    125 }
    126 
    127 static inline double msToDays(double ms)
    128 {
    129     return floor(ms / msPerDay);
    130 }
    131 
    132 static inline int msToYear(double ms)
    133 {
    134     int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970);
    135     double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear);
    136     if (msFromApproxYearTo1970 > ms)
    137         return approxYear - 1;
    138     if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms)
    139         return approxYear + 1;
    140     return approxYear;
    141 }
    142 
    143 static inline int dayInYear(double ms, int year)
    144 {
    145     return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
    146 }
    147 
    148 static inline double msToMilliseconds(double ms)
    149 {
    150     double result = fmod(ms, msPerDay);
    151     if (result < 0)
    152         result += msPerDay;
    153     return result;
    154 }
    155 
    156 // 0: Sunday, 1: Monday, etc.
    157 static inline int msToWeekDay(double ms)
    158 {
    159     int wd = (static_cast<int>(msToDays(ms)) + 4) % 7;
    160     if (wd < 0)
    161         wd += 7;
    162     return wd;
    163 }
    164 
    165 static inline int msToSeconds(double ms)
    166 {
    167     double result = fmod(floor(ms / msPerSecond), secondsPerMinute);
    168     if (result < 0)
    169         result += secondsPerMinute;
    170     return static_cast<int>(result);
    171 }
    172 
    173 static inline int msToMinutes(double ms)
    174 {
    175     double result = fmod(floor(ms / msPerMinute), minutesPerHour);
    176     if (result < 0)
    177         result += minutesPerHour;
    178     return static_cast<int>(result);
    179 }
    180 
    181 static inline int msToHours(double ms)
    182 {
    183     double result = fmod(floor(ms/msPerHour), hoursPerDay);
    184     if (result < 0)
    185         result += hoursPerDay;
    186     return static_cast<int>(result);
    187 }
    188 
    189 static inline int monthFromDayInYear(int dayInYear, bool leapYear)
    190 {
    191     const int d = dayInYear;
    192     int step;
    193 
    194     if (d < (step = 31))
    195         return 0;
    196     step += (leapYear ? 29 : 28);
    197     if (d < step)
    198         return 1;
    199     if (d < (step += 31))
    200         return 2;
    201     if (d < (step += 30))
    202         return 3;
    203     if (d < (step += 31))
    204         return 4;
    205     if (d < (step += 30))
    206         return 5;
    207     if (d < (step += 31))
    208         return 6;
    209     if (d < (step += 31))
    210         return 7;
    211     if (d < (step += 30))
    212         return 8;
    213     if (d < (step += 31))
    214         return 9;
    215     if (d < (step += 30))
    216         return 10;
    217     return 11;
    218 }
    219 
    220 static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth)
    221 {
    222     startDayOfThisMonth = startDayOfNextMonth;
    223     startDayOfNextMonth += daysInThisMonth;
    224     return (dayInYear <= startDayOfNextMonth);
    225 }
    226 
    227 static inline int dayInMonthFromDayInYear(int dayInYear, bool leapYear)
    228 {
    229     const int d = dayInYear;
    230     int step;
    231     int next = 30;
    232 
    233     if (d <= next)
    234         return d + 1;
    235     const int daysInFeb = (leapYear ? 29 : 28);
    236     if (checkMonth(d, step, next, daysInFeb))
    237         return d - step;
    238     if (checkMonth(d, step, next, 31))
    239         return d - step;
    240     if (checkMonth(d, step, next, 30))
    241         return d - step;
    242     if (checkMonth(d, step, next, 31))
    243         return d - step;
    244     if (checkMonth(d, step, next, 30))
    245         return d - step;
    246     if (checkMonth(d, step, next, 31))
    247         return d - step;
    248     if (checkMonth(d, step, next, 31))
    249         return d - step;
    250     if (checkMonth(d, step, next, 30))
    251         return d - step;
    252     if (checkMonth(d, step, next, 31))
    253         return d - step;
    254     if (checkMonth(d, step, next, 30))
    255         return d - step;
    256     step = next;
    257     return d - step;
    258 }
    259 
    260 static inline int monthToDayInYear(int month, bool isLeapYear)
    261 {
    262     return firstDayOfMonth[isLeapYear][month];
    263 }
    264 
    265 static inline double timeToMS(double hour, double min, double sec, double ms)
    266 {
    267     return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms);
    268 }
    269 
    270 static int dateToDayInYear(int year, int month, int day)
    271 {
    272     year += month / 12;
    273 
    274     month %= 12;
    275     if (month < 0) {
    276         month += 12;
    277         --year;
    278     }
    279 
    280     int yearday = static_cast<int>(floor(daysFrom1970ToYear(year)));
    281     int monthday = monthToDayInYear(month, isLeapYear(year));
    282 
    283     return yearday + monthday + day - 1;
    284 }
    285 
    286 double getCurrentUTCTime()
    287 {
    288     return floor(getCurrentUTCTimeWithMicroseconds());
    289 }
    290 
    291 // Returns current time in milliseconds since 1 Jan 1970.
    292 double getCurrentUTCTimeWithMicroseconds()
    293 {
    294     return currentTime() * 1000.0;
    295 }
    296 
    297 void getLocalTime(const time_t* localTime, struct tm* localTM)
    298 {
    299 #if COMPILER(MSVC7) || COMPILER(MINGW) || PLATFORM(WIN_CE)
    300     *localTM = *localtime(localTime);
    301 #elif COMPILER(MSVC)
    302     localtime_s(localTM, localTime);
    303 #else
    304     localtime_r(localTime, localTM);
    305 #endif
    306 }
    307 
    308 // There is a hard limit at 2038 that we currently do not have a workaround
    309 // for (rdar://problem/5052975).
    310 static inline int maximumYearForDST()
    311 {
    312     return 2037;
    313 }
    314 
    315 static inline int minimumYearForDST()
    316 {
    317     // Because of the 2038 issue (see maximumYearForDST) if the current year is
    318     // greater than the max year minus 27 (2010), we want to use the max year
    319     // minus 27 instead, to ensure there is a range of 28 years that all years
    320     // can map to.
    321     return std::min(msToYear(getCurrentUTCTime()), maximumYearForDST() - 27) ;
    322 }
    323 
    324 /*
    325  * Find an equivalent year for the one given, where equivalence is deterined by
    326  * the two years having the same leapness and the first day of the year, falling
    327  * on the same day of the week.
    328  *
    329  * This function returns a year between this current year and 2037, however this
    330  * function will potentially return incorrect results if the current year is after
    331  * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after
    332  * 2100, (rdar://problem/5055038).
    333  */
    334 int equivalentYearForDST(int year)
    335 {
    336     // It is ok if the cached year is not the current year as long as the rules
    337     // for DST did not change between the two years; if they did the app would need
    338     // to be restarted.
    339     static int minYear = minimumYearForDST();
    340     int maxYear = maximumYearForDST();
    341 
    342     int difference;
    343     if (year > maxYear)
    344         difference = minYear - year;
    345     else if (year < minYear)
    346         difference = maxYear - year;
    347     else
    348         return year;
    349 
    350     int quotient = difference / 28;
    351     int product = (quotient) * 28;
    352 
    353     year += product;
    354     ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(NaN)));
    355     return year;
    356 }
    357 
    358 static int32_t calculateUTCOffset()
    359 {
    360     tm localt;
    361     memset(&localt, 0, sizeof(localt));
    362  
    363     // get the difference between this time zone and UTC on Jan 01, 2000 12:00:00 AM
    364     localt.tm_mday = 1;
    365     localt.tm_year = 100;
    366     time_t utcOffset = 946684800 - mktime(&localt);
    367 
    368     return static_cast<int32_t>(utcOffset * 1000);
    369 }
    370 
    371 #if PLATFORM(DARWIN)
    372 static int32_t s_cachedUTCOffset; // In milliseconds. An assumption here is that access to an int32_t variable is atomic on platforms that take this code path.
    373 static bool s_haveCachedUTCOffset;
    374 static int s_notificationToken;
    375 #endif
    376 
    377 /*
    378  * Get the difference in milliseconds between this time zone and UTC (GMT)
    379  * NOT including DST.
    380  */
    381 double getUTCOffset()
    382 {
    383 #if PLATFORM(DARWIN)
    384     if (s_haveCachedUTCOffset) {
    385         int notified;
    386         uint32_t status = notify_check(s_notificationToken, &notified);
    387         if (status == NOTIFY_STATUS_OK && !notified)
    388             return s_cachedUTCOffset;
    389     }
    390 #endif
    391 
    392     int32_t utcOffset = calculateUTCOffset();
    393 
    394 #if PLATFORM(DARWIN)
    395     // Theoretically, it is possible that several threads will be executing this code at once, in which case we will have a race condition,
    396     // and a newer value may be overwritten. In practice, time zones don't change that often.
    397     s_cachedUTCOffset = utcOffset;
    398 #endif
    399 
    400     return utcOffset;
    401 }
    402 
    403 /*
    404  * Get the DST offset for the time passed in.  Takes
    405  * seconds (not milliseconds) and cannot handle dates before 1970
    406  * on some OS'
    407  */
    408 static double getDSTOffsetSimple(double localTimeSeconds, double utcOffset)
    409 {
    410     if (localTimeSeconds > maxUnixTime)
    411         localTimeSeconds = maxUnixTime;
    412     else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0)
    413         localTimeSeconds += secondsPerDay;
    414 
    415     //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset()
    416     double offsetTime = (localTimeSeconds * msPerSecond) + utcOffset;
    417 
    418     // Offset from UTC but doesn't include DST obviously
    419     int offsetHour =  msToHours(offsetTime);
    420     int offsetMinute =  msToMinutes(offsetTime);
    421 
    422     // FIXME: time_t has a potential problem in 2038
    423     time_t localTime = static_cast<time_t>(localTimeSeconds);
    424 
    425     tm localTM;
    426     getLocalTime(&localTime, &localTM);
    427 
    428     double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60);
    429 
    430     if (diff < 0)
    431         diff += secondsPerDay;
    432 
    433     return (diff * msPerSecond);
    434 }
    435 
    436 // Get the DST offset, given a time in UTC
    437 static double getDSTOffset(double ms, double utcOffset)
    438 {
    439     // On Mac OS X, the call to localtime (see getDSTOffsetSimple) will return historically accurate
    440     // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
    441     // standard explicitly dictates that historical information should not be considered when
    442     // determining DST. For this reason we shift away from years that localtime can handle but would
    443     // return historically accurate information.
    444     int year = msToYear(ms);
    445     int equivalentYear = equivalentYearForDST(year);
    446     if (year != equivalentYear) {
    447         bool leapYear = isLeapYear(year);
    448         int dayInYearLocal = dayInYear(ms, year);
    449         int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear);
    450         int month = monthFromDayInYear(dayInYearLocal, leapYear);
    451         int day = dateToDayInYear(equivalentYear, month, dayInMonth);
    452         ms = (day * msPerDay) + msToMilliseconds(ms);
    453     }
    454 
    455     return getDSTOffsetSimple(ms / msPerSecond, utcOffset);
    456 }
    457 
    458 double gregorianDateTimeToMS(const GregorianDateTime& t, double milliSeconds, bool inputIsUTC)
    459 {
    460     int day = dateToDayInYear(t.year + 1900, t.month, t.monthDay);
    461     double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds);
    462     double result = (day * msPerDay) + ms;
    463 
    464     if (!inputIsUTC) { // convert to UTC
    465         double utcOffset = getUTCOffset();
    466         result -= utcOffset;
    467         result -= getDSTOffset(result, utcOffset);
    468     }
    469 
    470     return result;
    471 }
    472 
    473 void msToGregorianDateTime(double ms, bool outputIsUTC, GregorianDateTime& tm)
    474 {
    475     // input is UTC
    476     double dstOff = 0.0;
    477     const double utcOff = getUTCOffset();
    478 
    479     if (!outputIsUTC) {  // convert to local time
    480         dstOff = getDSTOffset(ms, utcOff);
    481         ms += dstOff + utcOff;
    482     }
    483 
    484     const int year = msToYear(ms);
    485     tm.second   =  msToSeconds(ms);
    486     tm.minute   =  msToMinutes(ms);
    487     tm.hour     =  msToHours(ms);
    488     tm.weekDay  =  msToWeekDay(ms);
    489     tm.yearDay  =  dayInYear(ms, year);
    490     tm.monthDay =  dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year));
    491     tm.month    =  monthFromDayInYear(tm.yearDay, isLeapYear(year));
    492     tm.year     =  year - 1900;
    493     tm.isDST    =  dstOff != 0.0;
    494 
    495     tm.utcOffset = static_cast<long>((dstOff + utcOff) / msPerSecond);
    496     tm.timeZone = NULL;
    497 }
    498 
    499 void initDateMath()
    500 {
    501 #ifndef NDEBUG
    502     static bool alreadyInitialized;
    503     ASSERT(!alreadyInitialized++);
    504 #endif
    505 
    506     equivalentYearForDST(2000); // Need to call once to initialize a static used in this function.
    507 #if PLATFORM(DARWIN)
    508     // Register for a notification whenever the time zone changes.
    509     uint32_t status = notify_register_check("com.apple.system.timezone", &s_notificationToken);
    510     if (status == NOTIFY_STATUS_OK) {
    511         s_cachedUTCOffset = calculateUTCOffset();
    512         s_haveCachedUTCOffset = true;
    513     }
    514 #endif
    515 }
    516 
    517 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second)
    518 {
    519     double days = (day - 32075)
    520         + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4)
    521         + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
    522         - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4)
    523         - 2440588;
    524     return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
    525 }
    526 
    527 // We follow the recommendation of RFC 2822 to consider all
    528 // obsolete time zones not listed here equivalent to "-0000".
    529 static const struct KnownZone {
    530 #if !PLATFORM(WIN_OS)
    531     const
    532 #endif
    533         char tzName[4];
    534     int tzOffset;
    535 } known_zones[] = {
    536     { "UT", 0 },
    537     { "GMT", 0 },
    538     { "EST", -300 },
    539     { "EDT", -240 },
    540     { "CST", -360 },
    541     { "CDT", -300 },
    542     { "MST", -420 },
    543     { "MDT", -360 },
    544     { "PST", -480 },
    545     { "PDT", -420 }
    546 };
    547 
    548 inline static void skipSpacesAndComments(const char*& s)
    549 {
    550     int nesting = 0;
    551     char ch;
    552     while ((ch = *s)) {
    553         if (!isASCIISpace(ch)) {
    554             if (ch == '(')
    555                 nesting++;
    556             else if (ch == ')' && nesting > 0)
    557                 nesting--;
    558             else if (nesting == 0)
    559                 break;
    560         }
    561         s++;
    562     }
    563 }
    564 
    565 // returns 0-11 (Jan-Dec); -1 on failure
    566 static int findMonth(const char* monthStr)
    567 {
    568     ASSERT(monthStr);
    569     char needle[4];
    570     for (int i = 0; i < 3; ++i) {
    571         if (!*monthStr)
    572             return -1;
    573         needle[i] = static_cast<char>(toASCIILower(*monthStr++));
    574     }
    575     needle[3] = '\0';
    576     const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
    577     const char *str = strstr(haystack, needle);
    578     if (str) {
    579         int position = static_cast<int>(str - haystack);
    580         if (position % 3 == 0)
    581             return position / 3;
    582     }
    583     return -1;
    584 }
    585 
    586 static bool parseLong(const char* string, char** stopPosition, int base, long* result)
    587 {
    588     *result = strtol(string, stopPosition, base);
    589     // Avoid the use of errno as it is not available on Windows CE
    590     if (string == *stopPosition || *result == LONG_MIN || *result == LONG_MAX)
    591         return false;
    592     return true;
    593 }
    594 
    59552double parseDate(const UString &date)
    59653{
    597     // This parses a date in the form:
    598     //     Tuesday, 09-Nov-99 23:12:40 GMT
    599     // or
    600     //     Sat, 01-Jan-2000 08:00:00 GMT
    601     // or
    602     //     Sat, 01 Jan 2000 08:00:00 GMT
    603     // or
    604     //     01 Jan 99 22:00 +0100    (exceptions in rfc822/rfc2822)
    605     // ### non RFC formats, added for Javascript:
    606     //     [Wednesday] January 09 1999 23:12:40 GMT
    607     //     [Wednesday] January 09 23:12:40 GMT 1999
    608     //
    609     // We ignore the weekday.
    610 
    611     CString dateCString = date.UTF8String();
    612     const char *dateString = dateCString.c_str();
    613      
    614     // Skip leading space
    615     skipSpacesAndComments(dateString);
    616 
    617     long month = -1;
    618     const char *wordStart = dateString;
    619     // Check contents of first words if not number
    620     while (*dateString && !isASCIIDigit(*dateString)) {
    621         if (isASCIISpace(*dateString) || *dateString == '(') {
    622             if (dateString - wordStart >= 3)
    623                 month = findMonth(wordStart);
    624             skipSpacesAndComments(dateString);
    625             wordStart = dateString;
    626         } else
    627            dateString++;
    628     }
    629 
    630     // Missing delimiter between month and day (like "January29")?
    631     if (month == -1 && wordStart != dateString)
    632         month = findMonth(wordStart);
    633 
    634     skipSpacesAndComments(dateString);
    635 
    636     if (!*dateString)
    637         return NaN;
    638 
    639     // ' 09-Nov-99 23:12:40 GMT'
    640     char* newPosStr;
    641     long day;
    642     if (!parseLong(dateString, &newPosStr, 10, &day))
    643         return NaN;
    644     dateString = newPosStr;
    645 
    646     if (!*dateString)
    647         return NaN;
    648 
    649     if (day < 0)
    650         return NaN;
    651 
    652     long year = 0;
    653     if (day > 31) {
    654         // ### where is the boundary and what happens below?
    655         if (*dateString != '/')
    656             return NaN;
    657         // looks like a YYYY/MM/DD date
    658         if (!*++dateString)
    659             return NaN;
    660         year = day;
    661         if (!parseLong(dateString, &newPosStr, 10, &month))
    662             return NaN;
    663         month -= 1;
    664         dateString = newPosStr;
    665         if (*dateString++ != '/' || !*dateString)
    666             return NaN;
    667         if (!parseLong(dateString, &newPosStr, 10, &day))
    668             return NaN;
    669         dateString = newPosStr;
    670     } else if (*dateString == '/' && month == -1) {
    671         dateString++;
    672         // This looks like a MM/DD/YYYY date, not an RFC date.
    673         month = day - 1; // 0-based
    674         if (!parseLong(dateString, &newPosStr, 10, &day))
    675             return NaN;
    676         if (day < 1 || day > 31)
    677             return NaN;
    678         dateString = newPosStr;
    679         if (*dateString == '/')
    680             dateString++;
    681         if (!*dateString)
    682             return NaN;
    683      } else {
    684         if (*dateString == '-')
    685             dateString++;
    686 
    687         skipSpacesAndComments(dateString);
    688 
    689         if (*dateString == ',')
    690             dateString++;
    691 
    692         if (month == -1) { // not found yet
    693             month = findMonth(dateString);
    694             if (month == -1)
    695                 return NaN;
    696 
    697             while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString))
    698                 dateString++;
    699 
    700             if (!*dateString)
    701                 return NaN;
    702 
    703             // '-99 23:12:40 GMT'
    704             if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString))
    705                 return NaN;
    706             dateString++;
    707         }
    708     }
    709 
    710     if (month < 0 || month > 11)
    711         return NaN;
    712 
    713     // '99 23:12:40 GMT'
    714     if (year <= 0 && *dateString) {
    715         if (!parseLong(dateString, &newPosStr, 10, &year))
    716             return NaN;
    717     }
    718    
    719     // Don't fail if the time is missing.
    720     long hour = 0;
    721     long minute = 0;
    722     long second = 0;
    723     if (!*newPosStr)
    724         dateString = newPosStr;
    725     else {
    726         // ' 23:12:40 GMT'
    727         if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) {
    728             if (*newPosStr != ':')
    729                 return NaN;
    730             // There was no year; the number was the hour.
    731             year = -1;
    732         } else {
    733             // in the normal case (we parsed the year), advance to the next number
    734             dateString = ++newPosStr;
    735             skipSpacesAndComments(dateString);
    736         }
    737 
    738         parseLong(dateString, &newPosStr, 10, &hour);
    739         // Do not check for errno here since we want to continue
    740         // even if errno was set becasue we are still looking
    741         // for the timezone!
    742 
    743         // Read a number? If not, this might be a timezone name.
    744         if (newPosStr != dateString) {
    745             dateString = newPosStr;
    746 
    747             if (hour < 0 || hour > 23)
    748                 return NaN;
    749 
    750             if (!*dateString)
    751                 return NaN;
    752 
    753             // ':12:40 GMT'
    754             if (*dateString++ != ':')
    755                 return NaN;
    756 
    757             if (!parseLong(dateString, &newPosStr, 10, &minute))
    758                 return NaN;
    759             dateString = newPosStr;
    760 
    761             if (minute < 0 || minute > 59)
    762                 return NaN;
    763 
    764             // ':40 GMT'
    765             if (*dateString && *dateString != ':' && !isASCIISpace(*dateString))
    766                 return NaN;
    767 
    768             // seconds are optional in rfc822 + rfc2822
    769             if (*dateString ==':') {
    770                 dateString++;
    771 
    772                 if (!parseLong(dateString, &newPosStr, 10, &second))
    773                     return NaN;
    774                 dateString = newPosStr;
    775            
    776                 if (second < 0 || second > 59)
    777                     return NaN;
    778             }
    779 
    780             skipSpacesAndComments(dateString);
    781 
    782             if (strncasecmp(dateString, "AM", 2) == 0) {
    783                 if (hour > 12)
    784                     return NaN;
    785                 if (hour == 12)
    786                     hour = 0;
    787                 dateString += 2;
    788                 skipSpacesAndComments(dateString);
    789             } else if (strncasecmp(dateString, "PM", 2) == 0) {
    790                 if (hour > 12)
    791                     return NaN;
    792                 if (hour != 12)
    793                     hour += 12;
    794                 dateString += 2;
    795                 skipSpacesAndComments(dateString);
    796             }
    797         }
    798     }
    799 
    800     bool haveTZ = false;
    801     int offset = 0;
    802 
    803     // Don't fail if the time zone is missing.
    804     // Some websites omit the time zone (4275206).
    805     if (*dateString) {
    806         if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
    807             dateString += 3;
    808             haveTZ = true;
    809         }
    810 
    811         if (*dateString == '+' || *dateString == '-') {
    812             long o;
    813             if (!parseLong(dateString, &newPosStr, 10, &o))
    814                 return NaN;
    815             dateString = newPosStr;
    816 
    817             if (o < -9959 || o > 9959)
    818                 return NaN;
    819 
    820             int sgn = (o < 0) ? -1 : 1;
    821             o = abs(o);
    822             if (*dateString != ':') {
    823                 offset = ((o / 100) * 60 + (o % 100)) * sgn;
    824             } else { // GMT+05:00
    825                 long o2;
    826                 if (!parseLong(dateString, &newPosStr, 10, &o2))
    827                     return NaN;
    828                 dateString = newPosStr;
    829                 offset = (o * 60 + o2) * sgn;
    830             }
    831             haveTZ = true;
    832         } else {
    833             for (int i = 0; i < int(sizeof(known_zones) / sizeof(KnownZone)); i++) {
    834                 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
    835                     offset = known_zones[i].tzOffset;
    836                     dateString += strlen(known_zones[i].tzName);
    837                     haveTZ = true;
    838                     break;
    839                 }
    840             }
    841         }
    842     }
    843 
    844     skipSpacesAndComments(dateString);
    845 
    846     if (*dateString && year == -1) {
    847         if (!parseLong(dateString, &newPosStr, 10, &year))
    848             return NaN;
    849         dateString = newPosStr;
    850     }
    851      
    852     skipSpacesAndComments(dateString);
    853      
    854     // Trailing garbage
    855     if (*dateString)
    856         return NaN;
    857 
    858     // Y2K: Handle 2 digit years.
    859     if (year >= 0 && year < 100) {
    860         if (year < 50)
    861             year += 2000;
    862         else
    863             year += 1900;
    864     }
    865 
    866     // fall back to local timezone
    867     if (!haveTZ) {
    868         GregorianDateTime t;
    869         t.monthDay = day;
    870         t.month = month;
    871         t.year = year - 1900;
    872         t.isDST = -1;
    873         t.second = second;
    874         t.minute = minute;
    875         t.hour = hour;
    876 
    877         // Use our gregorianDateTimeToMS() rather than mktime() as the latter can't handle the full year range.
    878         return gregorianDateTimeToMS(t, 0, false);
    879     }
    880 
    881     return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond;
    882 }
    883 
    884 double timeClip(double t)
    885 {
    886     if (!isfinite(t))
    887         return NaN;
    888     if (fabs(t) > 8.64E15)
    889         return NaN;
    890     return trunc(t);
     54    return parseDateFromNullTerminatedCharacters(date.UTF8String().c_str());
    89155}
    89256
Note: See TracChangeset for help on using the changeset viewer.