Ignore:
Timestamp:
Oct 14, 2010, 7:45:03 PM (15 years ago)
Author:
[email protected]
Message:

2010-10-14 Nathan Vander Wilt <[email protected]>

Reviewed by Darin Adler.

Added parser for ECMAScript 5 standard date format, so Date.parse can handle RFC 3339 timestamps: https://bugs.webkit.org/show_bug.cgi?id=44632

  • runtime/DateConversion.cpp: (JSC::parseDate):
  • wtf/DateMath.cpp: (WTF::ymdhmsToSeconds): (WTF::parseES5DateFromNullTerminatedCharacters):
  • wtf/DateMath.h:

2010-10-14 Nathan Vander Wilt <[email protected]>

Reviewed by Darin Adler.

Added tests for https://bugs.webkit.org/show_bug.cgi?id=44632

  • fast/js/date-parse-test-expected.txt:
  • fast/js/script-tests/date-parse-test.js: (testDateParseExact):
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/wtf/DateMath.cpp

    r53921 r69833  
    44 * Copyright (C) 2009 Google Inc. All rights reserved.
    55 * Copyright (C) 2007-2009 Torch Mobile, Inc.
     6 * Copyright (C) 2010 &yet, LLC. ([email protected])
    67 *
    78 * The Original Code is Mozilla Communicator client code, released
     
    480481}
    481482
    482 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second)
     483static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second)
    483484{
    484485    double days = (day - 32075)
     
    556557        return false;
    557558    return true;
     559}
     560
     561double parseES5DateFromNullTerminatedCharacters(const char* dateString)
     562{
     563    // This parses a date of the form defined in ECMA-262-5, section 15.9.1.15
     564    // (similar to RFC 3339 / ISO 8601: YYYY-MM-DDTHH:mm:ss[.sss]Z).
     565    // In most cases it is intentionally strict (e.g. correct field widths, no stray whitespace).
     566   
     567    static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
     568   
     569    const char* currentPosition = dateString;
     570    char* postParsePosition;
     571   
     572    // This is a bit more lenient on the year string than ES5 specifies:
     573    // instead of restricting to 4 digits (or 6 digits with mandatory +/-),
     574    // it accepts any integer value. Consider this an implementation fallback.
     575    long year;
     576    if (!parseLong(currentPosition, &postParsePosition, 10, &year))
     577        return NaN;
     578    if (*postParsePosition != '-')
     579        return NaN;
     580    currentPosition = postParsePosition + 1;
     581   
     582    long month;
     583    if (!isASCIIDigit(*currentPosition))
     584        return NaN;
     585    if (!parseLong(currentPosition, &postParsePosition, 10, &month))
     586        return NaN;
     587    if (*postParsePosition != '-' || (postParsePosition - currentPosition) != 2)
     588        return NaN;
     589    currentPosition = postParsePosition + 1;
     590   
     591    long day;
     592    if (!isASCIIDigit(*currentPosition))
     593        return NaN;
     594    if (!parseLong(currentPosition, &postParsePosition, 10, &day))
     595        return NaN;
     596    if (*postParsePosition != 'T' || (postParsePosition - currentPosition) != 2)
     597        return NaN;
     598    currentPosition = postParsePosition + 1;
     599   
     600    long hours;
     601    if (!isASCIIDigit(*currentPosition))
     602        return NaN;
     603    if (!parseLong(currentPosition, &postParsePosition, 10, &hours))
     604        return NaN;
     605    if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
     606        return NaN;
     607    currentPosition = postParsePosition + 1;
     608   
     609    long minutes;
     610    if (!isASCIIDigit(*currentPosition))
     611        return NaN;
     612    if (!parseLong(currentPosition, &postParsePosition, 10, &minutes))
     613        return NaN;
     614    if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
     615        return NaN;
     616    currentPosition = postParsePosition + 1;
     617   
     618    long intSeconds;
     619    if (!isASCIIDigit(*currentPosition))
     620        return NaN;
     621    if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds))
     622        return NaN;
     623    if ((postParsePosition - currentPosition) != 2)
     624        return NaN;
     625   
     626    double seconds = intSeconds;
     627    if (*postParsePosition == '.') {
     628        currentPosition = postParsePosition + 1;
     629       
     630        // In ECMA-262-5 it's a bit unclear if '.' can be present without milliseconds, but
     631        // a reasonable interpretation guided by the given examples and RFC 3339 says "no".
     632        // We check the next character to avoid reading +/- timezone hours after an invalid decimal.
     633        if (!isASCIIDigit(*currentPosition))
     634            return NaN;
     635       
     636        // We are more lenient than ES5 by accepting more or less than 3 fraction digits.
     637        long fracSeconds;
     638        if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds))
     639            return NaN;
     640       
     641        long numFracDigits = postParsePosition - currentPosition;
     642        seconds += fracSeconds * pow(10, -numFracDigits);
     643    }
     644    currentPosition = postParsePosition;
     645   
     646    // A few of these checks could be done inline above, but since many of them are interrelated
     647    // we would be sacrificing readability to "optimize" the (presumably less common) failure path.
     648    if (month < 1 || month > 12)
     649        return NaN;
     650    if (day < 1 || day > daysPerMonth[month - 1])
     651        return NaN;
     652    if (month == 2 && day > 28 && !isLeapYear(year))
     653        return NaN;
     654    if (hours < 0 || hours > 24)
     655        return NaN;
     656    if (hours == 24 && (minutes || seconds))
     657        return NaN;
     658    if (minutes < 0 || minutes > 59)
     659        return NaN;
     660    if (seconds < 0 || seconds >= 61)
     661        return NaN;
     662    if (seconds > 60) {
     663        // Discard leap seconds by clamping to the end of a minute.
     664        seconds = 60;
     665    }
     666   
     667    long timeZoneSeconds = 0;
     668    if (*currentPosition != 'Z') {
     669        bool tzNegative;
     670        if (*currentPosition == '-')
     671            tzNegative = true;
     672        else if (*currentPosition == '+')
     673            tzNegative = false;
     674        else
     675            return NaN;
     676        currentPosition += 1;
     677       
     678        long tzHours;
     679        long tzHoursAbs;
     680        long tzMinutes;
     681       
     682        if (!isASCIIDigit(*currentPosition))
     683            return NaN;
     684        if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours))
     685            return NaN;
     686        if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
     687            return NaN;
     688        tzHoursAbs = abs(tzHours);
     689        currentPosition = postParsePosition + 1;
     690       
     691        if (!isASCIIDigit(*currentPosition))
     692            return NaN;
     693        if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes))
     694            return NaN;
     695        if ((postParsePosition - currentPosition) != 2)
     696            return NaN;
     697        currentPosition = postParsePosition;
     698       
     699        if (tzHoursAbs > 24)
     700            return NaN;
     701        if (tzMinutes < 0 || tzMinutes > 59)
     702            return NaN;
     703       
     704        timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs));
     705        if (tzNegative)
     706            timeZoneSeconds = -timeZoneSeconds;
     707    } else {
     708        currentPosition += 1;
     709    }
     710    if (*currentPosition)
     711        return NaN;
     712   
     713    double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds;
     714    return dateSeconds * msPerSecond;
    558715}
    559716
Note: See TracChangeset for help on using the changeset viewer.