X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/ba379fdc102753d6be2c4d937058fe40257329fe..14957cd040308e3eeec43d26bae5d76da13fcd85:/wtf/DateMath.cpp diff --git a/wtf/DateMath.cpp b/wtf/DateMath.cpp index 1d480ee..70c0cf4 100644 --- a/wtf/DateMath.cpp +++ b/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. @@ -39,6 +40,33 @@ * other provisions required by the MPL or the GPL, as the case may be. * If you do not delete the provisions above, a recipient may use your * version of this file under any of the LGPL, the MPL or the GPL. + + * Copyright 2006-2008 the V8 project authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -47,7 +75,14 @@ #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 @@ -61,8 +96,9 @@ #include #endif -#if PLATFORM(DARWIN) -#include +#if OS(WINCE) +extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t); +extern "C" struct tm * localtime(const time_t *timer); #endif #if HAVE(SYS_TIME_H) @@ -73,8 +109,14 @@ #include #endif +#if USE(JSC) +#include "CallFrame.h" +#endif + #define NaN std::numeric_limits::quiet_NaN() +using namespace WTF; + namespace WTF { /* Constants */ @@ -86,6 +128,10 @@ static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0; static const double usecPerSec = 1000000.0; static const double maxUnixTime = 2145859200.0; // 12/31/2037 +// ECMAScript asks not to support for a date of which total +// millisecond value is larger than the following value. +// See 15.9.1.14 of ECMA-262 5th edition. +static const double maxECMAScriptTime = 8.64E15; // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1. // First for non-leap years, then for leap years. @@ -134,7 +180,7 @@ static inline double msToDays(double ms) return floor(ms / msPerDay); } -static inline int msToYear(double ms) +int msToYear(double ms) { int approxYear = static_cast(floor(ms / (msPerDay * 365.2425)) + 1970); double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear); @@ -145,7 +191,7 @@ static inline int msToYear(double ms) return approxYear; } -static inline int dayInYear(double ms, int year) +int dayInYear(double ms, int year) { return static_cast(msToDays(ms) - daysFrom1970ToYear(year)); } @@ -191,7 +237,7 @@ static inline int msToHours(double ms) return static_cast(result); } -static inline int monthFromDayInYear(int dayInYear, bool leapYear) +int monthFromDayInYear(int dayInYear, bool leapYear) { const int d = dayInYear; int step; @@ -229,7 +275,7 @@ static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& star return (dayInYear <= startDayOfNextMonth); } -static inline int dayInMonthFromDayInYear(int dayInYear, bool leapYear) +int dayInMonthFromDayInYear(int dayInYear, bool leapYear) { const int d = dayInYear; int step; @@ -272,7 +318,7 @@ static inline double timeToMS(double hour, double min, double sec, double ms) return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms); } -static int dateToDayInYear(int year, int month, int day) +double dateToDaysFrom1970(int year, int month, int day) { year += month / 12; @@ -282,34 +328,13 @@ static int dateToDayInYear(int year, int month, int day) --year; } - int yearday = static_cast(floor(daysFrom1970ToYear(year))); + double yearday = floor(daysFrom1970ToYear(year)); + ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0)); int monthday = monthToDayInYear(month, isLeapYear(year)); return yearday + monthday + day - 1; } -double getCurrentUTCTime() -{ - return floor(getCurrentUTCTimeWithMicroseconds()); -} - -// Returns current time in milliseconds since 1 Jan 1970. -double getCurrentUTCTimeWithMicroseconds() -{ - return currentTime() * 1000.0; -} - -void getLocalTime(const time_t* localTime, struct tm* localTM) -{ -#if COMPILER(MSVC7) || COMPILER(MINGW) || PLATFORM(WINCE) - *localTM = *localtime(localTime); -#elif COMPILER(MSVC) - localtime_s(localTM, localTime); -#else - localtime_r(localTime, localTM); -#endif -} - // There is a hard limit at 2038 that we currently do not have a workaround // for (rdar://problem/5052975). static inline int maximumYearForDST() @@ -323,7 +348,7 @@ static inline int minimumYearForDST() // greater than the max year minus 27 (2010), we want to use the max year // minus 27 instead, to ensure there is a range of 28 years that all years // can map to. - return std::min(msToYear(getCurrentUTCTime()), maximumYearForDST() - 27) ; + return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ; } /* @@ -360,9 +385,13 @@ int equivalentYearForDST(int year) return year; } -static int32_t calculateUTCOffset() +int32_t calculateUTCOffset() { +#if PLATFORM(BREWMP) + time_t localTime = static_cast(currentTime()); +#else time_t localTime = time(0); +#endif tm localt; getLocalTime(&localTime, &localt); @@ -376,57 +405,28 @@ static int32_t calculateUTCOffset() localt.tm_wday = 0; localt.tm_yday = 0; localt.tm_isdst = 0; -#if PLATFORM(WIN_OS) || PLATFORM(SOLARIS) || COMPILER(RVCT) +#if HAVE(TM_GMTOFF) + localt.tm_gmtoff = 0; +#endif +#if HAVE(TM_ZONE) + localt.tm_zone = 0; +#endif + +#if HAVE(TIMEGM) + time_t utcOffset = timegm(&localt) - mktime(&localt); +#else // Using a canned date of 01/01/2009 on platforms with weaker date-handling foo. localt.tm_year = 109; time_t utcOffset = 1230768000 - mktime(&localt); -#else - localt.tm_zone = 0; - localt.tm_gmtoff = 0; - time_t utcOffset = timegm(&localt) - mktime(&localt); #endif return static_cast(utcOffset * 1000); } -#if PLATFORM(DARWIN) -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. -static bool s_haveCachedUTCOffset; -static int s_notificationToken; -#endif - /* - * Get the difference in milliseconds between this time zone and UTC (GMT) - * NOT including DST. + * Get the DST offset for the time passed in. */ -double getUTCOffset() -{ -#if PLATFORM(DARWIN) - if (s_haveCachedUTCOffset) { - int notified; - uint32_t status = notify_check(s_notificationToken, ¬ified); - if (status == NOTIFY_STATUS_OK && !notified) - return s_cachedUTCOffset; - } -#endif - - int32_t utcOffset = calculateUTCOffset(); - -#if PLATFORM(DARWIN) - // Theoretically, it is possible that several threads will be executing this code at once, in which case we will have a race condition, - // and a newer value may be overwritten. In practice, time zones don't change that often. - s_cachedUTCOffset = utcOffset; -#endif - - return utcOffset; -} - -/* - * Get the DST offset for the time passed in. Takes - * seconds (not milliseconds) and cannot handle dates before 1970 - * on some OS' - */ -static double getDSTOffsetSimple(double localTimeSeconds, double utcOffset) +static double calculateDSTOffsetSimple(double localTimeSeconds, double utcOffset) { if (localTimeSeconds > maxUnixTime) localTimeSeconds = maxUnixTime; @@ -455,9 +455,9 @@ static double getDSTOffsetSimple(double localTimeSeconds, double utcOffset) } // Get the DST offset, given a time in UTC -static double getDSTOffset(double ms, double utcOffset) +double calculateDSTOffset(double ms, double utcOffset) { - // On Mac OS X, the call to localtime (see getDSTOffsetSimple) will return historically accurate + // 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 // standard explicitly dictates that historical information should not be considered when // determining DST. For this reason we shift away from years that localtime can handle but would @@ -469,73 +469,25 @@ static double getDSTOffset(double ms, double utcOffset) int dayInYearLocal = dayInYear(ms, year); int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear); int month = monthFromDayInYear(dayInYearLocal, leapYear); - int day = dateToDayInYear(equivalentYear, month, dayInMonth); + double day = dateToDaysFrom1970(equivalentYear, month, dayInMonth); ms = (day * msPerDay) + msToMilliseconds(ms); } - return getDSTOffsetSimple(ms / msPerSecond, utcOffset); -} - -double gregorianDateTimeToMS(const GregorianDateTime& t, double milliSeconds, bool inputIsUTC) -{ - int day = dateToDayInYear(t.year + 1900, t.month, t.monthDay); - double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds); - double result = (day * msPerDay) + ms; - - if (!inputIsUTC) { // convert to UTC - double utcOffset = getUTCOffset(); - result -= utcOffset; - result -= getDSTOffset(result, utcOffset); - } - - return result; -} - -void msToGregorianDateTime(double ms, bool outputIsUTC, GregorianDateTime& tm) -{ - // input is UTC - double dstOff = 0.0; - const double utcOff = getUTCOffset(); - - if (!outputIsUTC) { // convert to local time - dstOff = getDSTOffset(ms, utcOff); - ms += dstOff + utcOff; - } - - const int year = msToYear(ms); - tm.second = msToSeconds(ms); - tm.minute = msToMinutes(ms); - tm.hour = msToHours(ms); - tm.weekDay = msToWeekDay(ms); - tm.yearDay = dayInYear(ms, year); - tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year)); - tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year)); - tm.year = year - 1900; - tm.isDST = dstOff != 0.0; - - tm.utcOffset = outputIsUTC ? 0 : static_cast((dstOff + utcOff) / msPerSecond); - tm.timeZone = NULL; + return calculateDSTOffsetSimple(ms / msPerSecond, utcOffset); } void initializeDates() { #ifndef NDEBUG static bool alreadyInitialized; - ASSERT(!alreadyInitialized++); + ASSERT(!alreadyInitialized); + alreadyInitialized = true; #endif equivalentYearForDST(2000); // Need to call once to initialize a static used in this function. -#if PLATFORM(DARWIN) - // Register for a notification whenever the time zone changes. - uint32_t status = notify_register_check("com.apple.system.timezone", &s_notificationToken); - if (status == NOTIFY_STATUS_OK) { - s_cachedUTCOffset = calculateUTCOffset(); - s_haveCachedUTCOffset = true; - } -#endif } -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) @@ -548,7 +500,7 @@ static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int // We follow the recommendation of RFC 2822 to consider all // obsolete time zones not listed here equivalent to "-0000". static const struct KnownZone { -#if !PLATFORM(WIN_OS) +#if !OS(WINDOWS) const #endif char tzName[4]; @@ -613,8 +565,168 @@ static bool parseLong(const char* string, char** stopPosition, int base, long* r return true; } -double parseDateFromNullTerminatedCharacters(const char* dateString) +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(-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) { + haveTZ = false; + offset = 0; + // This parses a date in the form: // Tuesday, 09-Nov-99 23:12:40 GMT // or @@ -814,9 +926,16 @@ double parseDateFromNullTerminatedCharacters(const char* dateString) } } } - - bool haveTZ = false; - int offset = 0; + + // 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). @@ -836,7 +955,7 @@ double parseDateFromNullTerminatedCharacters(const char* dateString) return NaN; int sgn = (o < 0) ? -1 : 1; - o = abs(o); + o = labs(o); if (*dateString != ':') { offset = ((o / 100) * 60 + (o % 100)) * sgn; } else { // GMT+05:00 @@ -848,7 +967,7 @@ double parseDateFromNullTerminatedCharacters(const char* dateString) } 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); @@ -880,33 +999,172 @@ double parseDateFromNullTerminatedCharacters(const char* dateString) else year += 1900; } + + return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond; +} + +double parseDateFromNullTerminatedCharacters(const char* dateString) +{ + bool haveTZ; + int offset; + double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); + if (isnan(ms)) + return NaN; // fall back to local timezone if (!haveTZ) { - GregorianDateTime t; - t.monthDay = day; - t.month = month; - t.year = year - 1900; - t.isDST = -1; - t.second = second; - t.minute = minute; - t.hour = hour; - - // Use our gregorianDateTimeToMS() rather than mktime() as the latter can't handle the full year range. - return gregorianDateTimeToMS(t, 0, false); + double utcOffset = calculateUTCOffset(); + double dstOffset = calculateDSTOffset(ms, utcOffset); + offset = static_cast((utcOffset + dstOffset) / msPerMinute); } - - return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond; + return ms - (offset * msPerMinute); } double timeClip(double t) { if (!isfinite(t)) return NaN; - if (fabs(t) > 8.64E15) + if (fabs(t) > maxECMAScriptTime) return NaN; return trunc(t); } +} // namespace WTF +#if USE(JSC) +namespace JSC { -} // namespace WTF +// Get the DST offset for the time passed in. +// +// NOTE: The implementation relies on the fact that no time zones have +// more than one daylight savings offset change per month. +// If this function is called with NaN it returns NaN. +static double getDSTOffset(ExecState* exec, double ms, double utcOffset) +{ + DSTOffsetCache& cache = exec->globalData().dstOffsetCache; + double start = cache.start; + double end = cache.end; + + if (start <= ms) { + // If the time fits in the cached interval, return the cached offset. + if (ms <= end) return cache.offset; + + // Compute a possible new interval end. + double newEnd = end + cache.increment; + + if (ms <= newEnd) { + double endOffset = calculateDSTOffset(newEnd, utcOffset); + if (cache.offset == endOffset) { + // If the offset at the end of the new interval still matches + // the offset in the cache, we grow the cached time interval + // and return the offset. + cache.end = newEnd; + cache.increment = msPerMonth; + return endOffset; + } else { + double offset = calculateDSTOffset(ms, utcOffset); + if (offset == endOffset) { + // The offset at the given time is equal to the offset at the + // new end of the interval, so that means that we've just skipped + // the point in time where the DST offset change occurred. Updated + // the interval to reflect this and reset the increment. + cache.start = ms; + cache.end = newEnd; + cache.increment = msPerMonth; + } else { + // The interval contains a DST offset change and the given time is + // before it. Adjust the increment to avoid a linear search for + // the offset change point and change the end of the interval. + cache.increment /= 3; + cache.end = ms; + } + // Update the offset in the cache and return it. + cache.offset = offset; + return offset; + } + } + } + + // Compute the DST offset for the time and shrink the cache interval + // to only contain the time. This allows fast repeated DST offset + // computations for the same time. + double offset = calculateDSTOffset(ms, utcOffset); + cache.offset = offset; + cache.start = ms; + cache.end = ms; + cache.increment = msPerMonth; + return offset; +} + +/* + * Get the difference in milliseconds between this time zone and UTC (GMT) + * NOT including DST. + */ +double getUTCOffset(ExecState* exec) +{ + double utcOffset = exec->globalData().cachedUTCOffset; + if (!isnan(utcOffset)) + return utcOffset; + exec->globalData().cachedUTCOffset = calculateUTCOffset(); + return exec->globalData().cachedUTCOffset; +} + +double gregorianDateTimeToMS(ExecState* exec, const GregorianDateTime& t, double milliSeconds, bool inputIsUTC) +{ + double day = dateToDaysFrom1970(t.year + 1900, t.month, t.monthDay); + double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds); + double result = (day * WTF::msPerDay) + ms; + + if (!inputIsUTC) { // convert to UTC + double utcOffset = getUTCOffset(exec); + result -= utcOffset; + result -= getDSTOffset(exec, result, utcOffset); + } + + return result; +} + +// input is UTC +void msToGregorianDateTime(ExecState* exec, double ms, bool outputIsUTC, GregorianDateTime& tm) +{ + double dstOff = 0.0; + double utcOff = 0.0; + if (!outputIsUTC) { + utcOff = getUTCOffset(exec); + dstOff = getDSTOffset(exec, ms, utcOff); + ms += dstOff + utcOff; + } + + const int year = msToYear(ms); + tm.second = msToSeconds(ms); + tm.minute = msToMinutes(ms); + tm.hour = msToHours(ms); + tm.weekDay = msToWeekDay(ms); + tm.yearDay = dayInYear(ms, year); + tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year)); + tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year)); + tm.year = year - 1900; + tm.isDST = dstOff != 0.0; + tm.utcOffset = static_cast((dstOff + utcOff) / WTF::msPerSecond); + tm.timeZone = nullptr; +} + +double parseDateFromNullTerminatedCharacters(ExecState* exec, const char* dateString) +{ + ASSERT(exec); + bool haveTZ; + int offset; + double ms = WTF::parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); + if (isnan(ms)) + return NaN; + + // fall back to local timezone + if (!haveTZ) { + double utcOffset = getUTCOffset(exec); + double dstOffset = getDSTOffset(exec, ms, utcOffset); + offset = static_cast((utcOffset + dstOffset) / WTF::msPerMinute); + } + return ms - (offset * WTF::msPerMinute); +} + +} // namespace JSC +#endif // USE(JSC)