* 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.
#include "Assertions.h"
#include "ASCIICType.h"
#include "CurrentTime.h"
+#if USE(JSC)
+#include "JSObject.h"
+#endif
#include "MathExtras.h"
+#if USE(JSC)
+#include "ScopeChain.h"
+#endif
+#include "StdLibExtras.h"
#include "StringExtras.h"
#include <algorithm>
return year;
}
-static int32_t calculateUTCOffset()
+int32_t calculateUTCOffset()
{
#if PLATFORM(BREWMP)
time_t localTime = static_cast<time_t>(currentTime());
}
// Get the DST offset, given a time in UTC
-static double calculateDSTOffset(double ms, double utcOffset)
+double calculateDSTOffset(double ms, double utcOffset)
{
// On Mac OS X, the call to localtime (see calculateDSTOffsetSimple) will return historically accurate
// DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
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)
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)
{
}
}
}
+
+ // The year may be after the time but before the time zone, but don't
+ // confuse a time zone specificed as an offset from UTC (e.g. +0100) with a
+ // four-digit year.
+ if (year <= 0 && *dateString != '+' && *dateString != '-') {
+ if (!parseLong(dateString, &newPosStr, 10, &year))
+ year = 0;
+ dateString = newPosStr;
+ skipSpacesAndComments(dateString);
+ }
// Don't fail if the time zone is missing.
// Some websites omit the time zone (4275206).
}
haveTZ = true;
} else {
- for (int i = 0; i < int(sizeof(known_zones) / sizeof(KnownZone)); i++) {
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(known_zones); ++i) {
if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
offset = known_zones[i].tzOffset;
dateString += strlen(known_zones[i].tzName);
tm.year = year - 1900;
tm.isDST = dstOff != 0.0;
tm.utcOffset = static_cast<long>((dstOff + utcOff) / WTF::msPerSecond);
- tm.timeZone = NULL;
+ tm.timeZone = nullptr;
}
double parseDateFromNullTerminatedCharacters(ExecState* exec, const char* dateString)