diff options
Diffstat (limited to 'JavaScriptCore/wtf/DateMath.cpp')
-rw-r--r-- | JavaScriptCore/wtf/DateMath.cpp | 159 |
1 files changed, 158 insertions, 1 deletions
diff --git a/JavaScriptCore/wtf/DateMath.cpp b/JavaScriptCore/wtf/DateMath.cpp index b9a0207..d005859 100644 --- a/JavaScriptCore/wtf/DateMath.cpp +++ b/JavaScriptCore/wtf/DateMath.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) 2010 &yet, LLC. (nate@andyet.net) * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. @@ -479,7 +480,7 @@ void initializeDates() equivalentYearForDST(2000); // Need to call once to initialize a static used in this function. } -static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second) +static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second) { double days = (day - 32075) + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4) @@ -557,6 +558,162 @@ static bool parseLong(const char* string, char** stopPosition, int base, long* r return true; } +double parseES5DateFromNullTerminatedCharacters(const char* dateString) +{ + // This parses a date of the form defined in ECMA-262-5, section 15.9.1.15 + // (similar to RFC 3339 / ISO 8601: YYYY-MM-DDTHH:mm:ss[.sss]Z). + // In most cases it is intentionally strict (e.g. correct field widths, no stray whitespace). + + static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + const char* currentPosition = dateString; + char* postParsePosition; + + // This is a bit more lenient on the year string than ES5 specifies: + // instead of restricting to 4 digits (or 6 digits with mandatory +/-), + // it accepts any integer value. Consider this an implementation fallback. + long year; + if (!parseLong(currentPosition, &postParsePosition, 10, &year)) + return NaN; + if (*postParsePosition != '-') + return NaN; + currentPosition = postParsePosition + 1; + + long month; + if (!isASCIIDigit(*currentPosition)) + return NaN; + if (!parseLong(currentPosition, &postParsePosition, 10, &month)) + return NaN; + if (*postParsePosition != '-' || (postParsePosition - currentPosition) != 2) + return NaN; + currentPosition = postParsePosition + 1; + + long day; + if (!isASCIIDigit(*currentPosition)) + return NaN; + if (!parseLong(currentPosition, &postParsePosition, 10, &day)) + return NaN; + if (*postParsePosition != 'T' || (postParsePosition - currentPosition) != 2) + return NaN; + currentPosition = postParsePosition + 1; + + long hours; + if (!isASCIIDigit(*currentPosition)) + return NaN; + if (!parseLong(currentPosition, &postParsePosition, 10, &hours)) + return NaN; + if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2) + return NaN; + currentPosition = postParsePosition + 1; + + long minutes; + if (!isASCIIDigit(*currentPosition)) + return NaN; + if (!parseLong(currentPosition, &postParsePosition, 10, &minutes)) + return NaN; + if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2) + return NaN; + currentPosition = postParsePosition + 1; + + long intSeconds; + if (!isASCIIDigit(*currentPosition)) + return NaN; + if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds)) + return NaN; + if ((postParsePosition - currentPosition) != 2) + return NaN; + + double seconds = intSeconds; + if (*postParsePosition == '.') { + currentPosition = postParsePosition + 1; + + // In ECMA-262-5 it's a bit unclear if '.' can be present without milliseconds, but + // a reasonable interpretation guided by the given examples and RFC 3339 says "no". + // We check the next character to avoid reading +/- timezone hours after an invalid decimal. + if (!isASCIIDigit(*currentPosition)) + return NaN; + + // We are more lenient than ES5 by accepting more or less than 3 fraction digits. + long fracSeconds; + if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds)) + return NaN; + + long numFracDigits = postParsePosition - currentPosition; + seconds += fracSeconds * pow(10.0, static_cast<double>(-numFracDigits)); + } + currentPosition = postParsePosition; + + // A few of these checks could be done inline above, but since many of them are interrelated + // we would be sacrificing readability to "optimize" the (presumably less common) failure path. + if (month < 1 || month > 12) + return NaN; + if (day < 1 || day > daysPerMonth[month - 1]) + return NaN; + if (month == 2 && day > 28 && !isLeapYear(year)) + return NaN; + if (hours < 0 || hours > 24) + return NaN; + if (hours == 24 && (minutes || seconds)) + return NaN; + if (minutes < 0 || minutes > 59) + return NaN; + if (seconds < 0 || seconds >= 61) + return NaN; + if (seconds > 60) { + // Discard leap seconds by clamping to the end of a minute. + seconds = 60; + } + + long timeZoneSeconds = 0; + if (*currentPosition != 'Z') { + bool tzNegative; + if (*currentPosition == '-') + tzNegative = true; + else if (*currentPosition == '+') + tzNegative = false; + else + return NaN; + currentPosition += 1; + + long tzHours; + long tzHoursAbs; + long tzMinutes; + + if (!isASCIIDigit(*currentPosition)) + return NaN; + if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours)) + return NaN; + if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2) + return NaN; + tzHoursAbs = abs(tzHours); + currentPosition = postParsePosition + 1; + + if (!isASCIIDigit(*currentPosition)) + return NaN; + if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes)) + return NaN; + if ((postParsePosition - currentPosition) != 2) + return NaN; + currentPosition = postParsePosition; + + if (tzHoursAbs > 24) + return NaN; + if (tzMinutes < 0 || tzMinutes > 59) + return NaN; + + timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs)); + if (tzNegative) + timeZoneSeconds = -timeZoneSeconds; + } else { + currentPosition += 1; + } + if (*currentPosition) + return NaN; + + double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds; + return dateSeconds * msPerSecond; +} + // Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore. static double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset) { |