2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
4 * Copyright (C) 2009 Google Inc. All rights reserved.
5 * Copyright (C) 2007-2009 Torch Mobile, Inc.
6 * Copyright (C) 2010 &yet, LLC. (nate@andyet.net)
8 * The Original Code is Mozilla Communicator client code, released
11 * The Initial Developer of the Original Code is
12 * Netscape Communications Corporation.
13 * Portions created by the Initial Developer are Copyright (C) 1998
14 * the Initial Developer. All Rights Reserved.
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 * Alternatively, the contents of this file may be used under the terms
31 * of either the Mozilla Public License Version 1.1, found at
32 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
33 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
34 * (the "GPL"), in which case the provisions of the MPL or the GPL are
35 * applicable instead of those above. If you wish to allow use of your
36 * version of this file only under the terms of one of those two
37 * licenses (the MPL or the GPL) and not to allow others to use your
38 * version of this file under the LGPL, indicate your decision by
39 * deletingthe provisions above and replace them with the notice and
40 * other provisions required by the MPL or the GPL, as the case may be.
41 * If you do not delete the provisions above, a recipient may use your
42 * version of this file under any of the LGPL, the MPL or the GPL.
44 * Copyright 2006-2008 the V8 project authors. All rights reserved.
45 * Redistribution and use in source and binary forms, with or without
46 * modification, are permitted provided that the following conditions are
49 * * Redistributions of source code must retain the above copyright
50 * notice, this list of conditions and the following disclaimer.
51 * * Redistributions in binary form must reproduce the above
52 * copyright notice, this list of conditions and the following
53 * disclaimer in the documentation and/or other materials provided
54 * with the distribution.
55 * * Neither the name of Google Inc. nor the names of its
56 * contributors may be used to endorse or promote products derived
57 * from this software without specific prior written permission.
59 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
60 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
61 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
62 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
63 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
64 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
65 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
66 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
67 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
68 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
69 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75 #include "Assertions.h"
76 #include "ASCIICType.h"
77 #include "CurrentTime.h"
78 #include "MathExtras.h"
79 #include "StdLibExtras.h"
80 #include "StringExtras.h"
87 #include <wtf/text/StringBuilder.h>
101 #if HAVE(SYS_TIMEB_H)
102 #include <sys/timeb.h>
111 static const double maxUnixTime = 2145859200.0; // 12/31/2037
112 // ECMAScript asks not to support for a date of which total
113 // millisecond value is larger than the following value.
114 // See 15.9.1.14 of ECMA-262 5th edition.
115 static const double maxECMAScriptTime = 8.64E15;
117 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1.
118 // First for non-leap years, then for leap years.
119 static const int firstDayOfMonth[2][12] = {
120 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
121 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
125 static inline void getLocalTime(const time_t* localTime, struct tm* localTM)
128 *localTM = *localtime(localTime);
130 localtime_s(localTM, localTime);
132 localtime_r(localTime, localTM);
137 bool isLeapYear(int year)
148 static inline int daysInYear(int year)
150 return 365 + isLeapYear(year);
153 static inline double daysFrom1970ToYear(int year)
155 // The Gregorian Calendar rules for leap years:
156 // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.
157 // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years.
158 // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years.
160 static const int leapDaysBefore1971By4Rule = 1970 / 4;
161 static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100;
162 static const int leapDaysBefore1971By400Rule = 1970 / 400;
164 const double yearMinusOne = year - 1;
165 const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule;
166 const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule;
167 const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule;
169 return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;
172 double msToDays(double ms)
174 return floor(ms / msPerDay);
177 static void appendTwoDigitNumber(StringBuilder& builder, int number)
180 ASSERT(number < 100);
181 builder.append(static_cast<LChar>('0' + number / 10));
182 builder.append(static_cast<LChar>('0' + number % 10));
185 int msToYear(double ms)
187 int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970);
188 double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear);
189 if (msFromApproxYearTo1970 > ms)
190 return approxYear - 1;
191 if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms)
192 return approxYear + 1;
196 int dayInYear(double ms, int year)
198 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
201 static inline double msToMilliseconds(double ms)
203 double result = fmod(ms, msPerDay);
209 int msToMinutes(double ms)
211 double result = fmod(floor(ms / msPerMinute), minutesPerHour);
213 result += minutesPerHour;
214 return static_cast<int>(result);
217 int msToHours(double ms)
219 double result = fmod(floor(ms/msPerHour), hoursPerDay);
221 result += hoursPerDay;
222 return static_cast<int>(result);
225 int monthFromDayInYear(int dayInYear, bool leapYear)
227 const int d = dayInYear;
232 step += (leapYear ? 29 : 28);
235 if (d < (step += 31))
237 if (d < (step += 30))
239 if (d < (step += 31))
241 if (d < (step += 30))
243 if (d < (step += 31))
245 if (d < (step += 31))
247 if (d < (step += 30))
249 if (d < (step += 31))
251 if (d < (step += 30))
256 static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth)
258 startDayOfThisMonth = startDayOfNextMonth;
259 startDayOfNextMonth += daysInThisMonth;
260 return (dayInYear <= startDayOfNextMonth);
263 int dayInMonthFromDayInYear(int dayInYear, bool leapYear)
265 const int d = dayInYear;
271 const int daysInFeb = (leapYear ? 29 : 28);
272 if (checkMonth(d, step, next, daysInFeb))
274 if (checkMonth(d, step, next, 31))
276 if (checkMonth(d, step, next, 30))
278 if (checkMonth(d, step, next, 31))
280 if (checkMonth(d, step, next, 30))
282 if (checkMonth(d, step, next, 31))
284 if (checkMonth(d, step, next, 31))
286 if (checkMonth(d, step, next, 30))
288 if (checkMonth(d, step, next, 31))
290 if (checkMonth(d, step, next, 30))
296 int dayInYear(int year, int month, int day)
298 return firstDayOfMonth[isLeapYear(year)][month] + day - 1;
301 double dateToDaysFrom1970(int year, int month, int day)
311 double yearday = floor(daysFrom1970ToYear(year));
312 ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0));
313 return yearday + dayInYear(year, month, day);
316 // There is a hard limit at 2038 that we currently do not have a workaround
317 // for (rdar://problem/5052975).
318 static inline int maximumYearForDST()
323 static inline int minimumYearForDST()
325 // Because of the 2038 issue (see maximumYearForDST) if the current year is
326 // greater than the max year minus 27 (2010), we want to use the max year
327 // minus 27 instead, to ensure there is a range of 28 years that all years
329 return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ;
333 * Find an equivalent year for the one given, where equivalence is deterined by
334 * the two years having the same leapness and the first day of the year, falling
335 * on the same day of the week.
337 * This function returns a year between this current year and 2037, however this
338 * function will potentially return incorrect results if the current year is after
339 * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after
340 * 2100, (rdar://problem/5055038).
342 int equivalentYearForDST(int year)
344 // It is ok if the cached year is not the current year as long as the rules
345 // for DST did not change between the two years; if they did the app would need
347 static int minYear = minimumYearForDST();
348 int maxYear = maximumYearForDST();
352 difference = minYear - year;
353 else if (year < minYear)
354 difference = maxYear - year;
358 int quotient = difference / 28;
359 int product = (quotient) * 28;
362 ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(std::numeric_limits<double>::quiet_NaN())));
368 static int32_t calculateUTCOffset()
371 TIME_ZONE_INFORMATION timeZoneInformation;
372 GetTimeZoneInformation(&timeZoneInformation);
373 int32_t bias = timeZoneInformation.Bias + timeZoneInformation.StandardBias;
374 return -bias * 60 * 1000;
376 time_t localTime = time(0);
378 getLocalTime(&localTime, &localt);
380 // Get the difference between this time zone and UTC on the 1st of January of this year.
386 // Not setting localt.tm_year!
391 localt.tm_gmtoff = 0;
398 time_t utcOffset = timegm(&localt) - mktime(&localt);
400 // Using a canned date of 01/01/2009 on platforms with weaker date-handling foo.
401 localt.tm_year = 109;
402 time_t utcOffset = 1230768000 - mktime(&localt);
405 return static_cast<int32_t>(utcOffset * 1000);
410 // Code taken from http://support.microsoft.com/kb/167296
411 static void UnixTimeToFileTime(time_t t, LPFILETIME pft)
413 // Note that LONGLONG is a 64-bit value
416 ll = Int32x32To64(t, 10000000) + 116444736000000000;
417 pft->dwLowDateTime = (DWORD)ll;
418 pft->dwHighDateTime = ll >> 32;
423 * Get the DST offset for the time passed in.
425 static double calculateDSTOffset(time_t localTime, double utcOffset)
428 UNUSED_PARAM(localTime);
429 UNUSED_PARAM(utcOffset);
432 FILETIME utcFileTime;
433 UnixTimeToFileTime(localTime, &utcFileTime);
434 SYSTEMTIME utcSystemTime, localSystemTime;
435 FileTimeToSystemTime(&utcFileTime, &utcSystemTime);
436 SystemTimeToTzSpecificLocalTime(0, &utcSystemTime, &localSystemTime);
438 double offsetTime = (localTime * msPerSecond) + utcOffset;
440 // Offset from UTC but doesn't include DST obviously
441 int offsetHour = msToHours(offsetTime);
442 int offsetMinute = msToMinutes(offsetTime);
444 double diff = ((localSystemTime.wHour - offsetHour) * secondsPerHour) + ((localSystemTime.wMinute - offsetMinute) * 60);
446 return diff * msPerSecond;
448 //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset()
449 double offsetTime = (localTime * msPerSecond) + utcOffset;
451 // Offset from UTC but doesn't include DST obviously
452 int offsetHour = msToHours(offsetTime);
453 int offsetMinute = msToMinutes(offsetTime);
456 getLocalTime(&localTime, &localTM);
458 double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60);
461 diff += secondsPerDay;
463 return (diff * msPerSecond);
469 // Returns combined offset in millisecond (UTC + DST).
470 LocalTimeOffset calculateLocalTimeOffset(double ms)
472 // On Mac OS X, the call to localtime (see calculateDSTOffset) will return historically accurate
473 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
474 // standard explicitly dictates that historical information should not be considered when
475 // determining DST. For this reason we shift away from years that localtime can handle but would
476 // return historically accurate information.
477 int year = msToYear(ms);
478 int equivalentYear = equivalentYearForDST(year);
479 if (year != equivalentYear) {
480 bool leapYear = isLeapYear(year);
481 int dayInYearLocal = dayInYear(ms, year);
482 int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear);
483 int month = monthFromDayInYear(dayInYearLocal, leapYear);
484 double day = dateToDaysFrom1970(equivalentYear, month, dayInMonth);
485 ms = (day * msPerDay) + msToMilliseconds(ms);
488 double localTimeSeconds = ms / msPerSecond;
489 if (localTimeSeconds > maxUnixTime)
490 localTimeSeconds = maxUnixTime;
491 else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0).
492 localTimeSeconds += secondsPerDay;
493 // FIXME: time_t has a potential problem in 2038.
494 time_t localTime = static_cast<time_t>(localTimeSeconds);
498 getLocalTime(&localTime, &localTM);
499 return LocalTimeOffset(localTM.tm_isdst, localTM.tm_gmtoff * msPerSecond);
501 double utcOffset = calculateUTCOffset();
502 double dstOffset = calculateDSTOffset(localTime, utcOffset);
503 return LocalTimeOffset(dstOffset, utcOffset + dstOffset);
507 void initializeDates()
510 static bool alreadyInitialized;
511 ASSERT(!alreadyInitialized);
512 alreadyInitialized = true;
515 equivalentYearForDST(2000); // Need to call once to initialize a static used in this function.
518 static inline double ymdhmsToSeconds(int year, long mon, long day, long hour, long minute, double second)
520 int mday = firstDayOfMonth[isLeapYear(year)][mon - 1];
521 double ydays = daysFrom1970ToYear(year);
523 return (second + minute * secondsPerMinute + hour * secondsPerHour + (mday + day - 1 + ydays) * secondsPerDay);
526 // We follow the recommendation of RFC 2822 to consider all
527 // obsolete time zones not listed here equivalent to "-0000".
528 static const struct KnownZone {
547 inline static void skipSpacesAndComments(const char*& s)
552 if (!isASCIISpace(ch)) {
555 else if (ch == ')' && nesting > 0)
557 else if (nesting == 0)
564 // returns 0-11 (Jan-Dec); -1 on failure
565 static int findMonth(const char* monthStr)
569 for (int i = 0; i < 3; ++i) {
572 needle[i] = static_cast<char>(toASCIILower(*monthStr++));
575 const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
576 const char *str = strstr(haystack, needle);
578 int position = static_cast<int>(str - haystack);
579 if (position % 3 == 0)
585 static bool parseInt(const char* string, char** stopPosition, int base, int* result)
587 long longResult = strtol(string, stopPosition, base);
588 // Avoid the use of errno as it is not available on Windows CE
589 if (string == *stopPosition || longResult <= std::numeric_limits<int>::min() || longResult >= std::numeric_limits<int>::max())
591 *result = static_cast<int>(longResult);
595 static bool parseLong(const char* string, char** stopPosition, int base, long* result)
597 *result = strtol(string, stopPosition, base);
598 // Avoid the use of errno as it is not available on Windows CE
599 if (string == *stopPosition || *result == std::numeric_limits<long>::min() || *result == std::numeric_limits<long>::max())
604 // Parses a date with the format YYYY[-MM[-DD]].
605 // Year parsing is lenient, allows any number of digits, and +/-.
606 // Returns 0 if a parse error occurs, else returns the end of the parsed portion of the string.
607 static char* parseES5DatePortion(const char* currentPosition, int& year, long& month, long& day)
609 char* postParsePosition;
611 // This is a bit more lenient on the year string than ES5 specifies:
612 // instead of restricting to 4 digits (or 6 digits with mandatory +/-),
613 // it accepts any integer value. Consider this an implementation fallback.
614 if (!parseInt(currentPosition, &postParsePosition, 10, &year))
617 // Check for presence of -MM portion.
618 if (*postParsePosition != '-')
619 return postParsePosition;
620 currentPosition = postParsePosition + 1;
622 if (!isASCIIDigit(*currentPosition))
624 if (!parseLong(currentPosition, &postParsePosition, 10, &month))
626 if ((postParsePosition - currentPosition) != 2)
629 // Check for presence of -DD portion.
630 if (*postParsePosition != '-')
631 return postParsePosition;
632 currentPosition = postParsePosition + 1;
634 if (!isASCIIDigit(*currentPosition))
636 if (!parseLong(currentPosition, &postParsePosition, 10, &day))
638 if ((postParsePosition - currentPosition) != 2)
640 return postParsePosition;
643 // Parses a time with the format HH:mm[:ss[.sss]][Z|(+|-)00:00].
644 // Fractional seconds parsing is lenient, allows any number of digits.
645 // Returns 0 if a parse error occurs, else returns the end of the parsed portion of the string.
646 static char* parseES5TimePortion(char* currentPosition, long& hours, long& minutes, double& seconds, long& timeZoneSeconds)
648 char* postParsePosition;
649 if (!isASCIIDigit(*currentPosition))
651 if (!parseLong(currentPosition, &postParsePosition, 10, &hours))
653 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
655 currentPosition = postParsePosition + 1;
657 if (!isASCIIDigit(*currentPosition))
659 if (!parseLong(currentPosition, &postParsePosition, 10, &minutes))
661 if ((postParsePosition - currentPosition) != 2)
663 currentPosition = postParsePosition;
665 // Seconds are optional.
666 if (*currentPosition == ':') {
670 if (!isASCIIDigit(*currentPosition))
672 if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds))
674 if ((postParsePosition - currentPosition) != 2)
676 seconds = intSeconds;
677 if (*postParsePosition == '.') {
678 currentPosition = postParsePosition + 1;
680 // In ECMA-262-5 it's a bit unclear if '.' can be present without milliseconds, but
681 // a reasonable interpretation guided by the given examples and RFC 3339 says "no".
682 // We check the next character to avoid reading +/- timezone hours after an invalid decimal.
683 if (!isASCIIDigit(*currentPosition))
686 // We are more lenient than ES5 by accepting more or less than 3 fraction digits.
688 if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds))
691 long numFracDigits = postParsePosition - currentPosition;
692 seconds += fracSeconds * pow(10.0, static_cast<double>(-numFracDigits));
694 currentPosition = postParsePosition;
697 if (*currentPosition == 'Z')
698 return currentPosition + 1;
701 if (*currentPosition == '-')
703 else if (*currentPosition == '+')
706 return currentPosition; // no timezone
713 if (!isASCIIDigit(*currentPosition))
715 if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours))
717 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
719 tzHoursAbs = labs(tzHours);
720 currentPosition = postParsePosition + 1;
722 if (!isASCIIDigit(*currentPosition))
724 if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes))
726 if ((postParsePosition - currentPosition) != 2)
728 currentPosition = postParsePosition;
732 if (tzMinutes < 0 || tzMinutes > 59)
735 timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs));
737 timeZoneSeconds = -timeZoneSeconds;
739 return currentPosition;
742 double parseES5DateFromNullTerminatedCharacters(const char* dateString)
744 // This parses a date of the form defined in ECMA-262-5, section 15.9.1.15
745 // (similar to RFC 3339 / ISO 8601: YYYY-MM-DDTHH:mm:ss[.sss]Z).
746 // In most cases it is intentionally strict (e.g. correct field widths, no stray whitespace).
748 static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
750 // The year must be present, but the other fields may be omitted - see ES5.1 15.9.1.15.
757 long timeZoneSeconds = 0;
759 // Parse the date YYYY[-MM[-DD]]
760 char* currentPosition = parseES5DatePortion(dateString, year, month, day);
761 if (!currentPosition)
762 return std::numeric_limits<double>::quiet_NaN();
763 // Look for a time portion.
764 if (*currentPosition == 'T') {
765 // Parse the time HH:mm[:ss[.sss]][Z|(+|-)00:00]
766 currentPosition = parseES5TimePortion(currentPosition + 1, hours, minutes, seconds, timeZoneSeconds);
767 if (!currentPosition)
768 return std::numeric_limits<double>::quiet_NaN();
770 // Check that we have parsed all characters in the string.
771 if (*currentPosition)
772 return std::numeric_limits<double>::quiet_NaN();
774 // A few of these checks could be done inline above, but since many of them are interrelated
775 // we would be sacrificing readability to "optimize" the (presumably less common) failure path.
776 if (month < 1 || month > 12)
777 return std::numeric_limits<double>::quiet_NaN();
778 if (day < 1 || day > daysPerMonth[month - 1])
779 return std::numeric_limits<double>::quiet_NaN();
780 if (month == 2 && day > 28 && !isLeapYear(year))
781 return std::numeric_limits<double>::quiet_NaN();
782 if (hours < 0 || hours > 24)
783 return std::numeric_limits<double>::quiet_NaN();
784 if (hours == 24 && (minutes || seconds))
785 return std::numeric_limits<double>::quiet_NaN();
786 if (minutes < 0 || minutes > 59)
787 return std::numeric_limits<double>::quiet_NaN();
788 if (seconds < 0 || seconds >= 61)
789 return std::numeric_limits<double>::quiet_NaN();
791 // Discard leap seconds by clamping to the end of a minute.
795 double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds;
796 return dateSeconds * msPerSecond;
799 // Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore.
800 double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset)
805 // This parses a date in the form:
806 // Tuesday, 09-Nov-99 23:12:40 GMT
808 // Sat, 01-Jan-2000 08:00:00 GMT
810 // Sat, 01 Jan 2000 08:00:00 GMT
812 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
813 // ### non RFC formats, added for Javascript:
814 // [Wednesday] January 09 1999 23:12:40 GMT
815 // [Wednesday] January 09 23:12:40 GMT 1999
817 // We ignore the weekday.
819 // Skip leading space
820 skipSpacesAndComments(dateString);
823 const char *wordStart = dateString;
824 // Check contents of first words if not number
825 while (*dateString && !isASCIIDigit(*dateString)) {
826 if (isASCIISpace(*dateString) || *dateString == '(') {
827 if (dateString - wordStart >= 3)
828 month = findMonth(wordStart);
829 skipSpacesAndComments(dateString);
830 wordStart = dateString;
835 // Missing delimiter between month and day (like "January29")?
836 if (month == -1 && wordStart != dateString)
837 month = findMonth(wordStart);
839 skipSpacesAndComments(dateString);
842 return std::numeric_limits<double>::quiet_NaN();
844 // ' 09-Nov-99 23:12:40 GMT'
847 if (!parseLong(dateString, &newPosStr, 10, &day))
848 return std::numeric_limits<double>::quiet_NaN();
849 dateString = newPosStr;
852 return std::numeric_limits<double>::quiet_NaN();
855 return std::numeric_limits<double>::quiet_NaN();
859 // ### where is the boundary and what happens below?
860 if (*dateString != '/')
861 return std::numeric_limits<double>::quiet_NaN();
862 // looks like a YYYY/MM/DD date
864 return std::numeric_limits<double>::quiet_NaN();
865 if (day <= std::numeric_limits<int>::min() || day >= std::numeric_limits<int>::max())
866 return std::numeric_limits<double>::quiet_NaN();
867 year = static_cast<int>(day);
868 if (!parseLong(dateString, &newPosStr, 10, &month))
869 return std::numeric_limits<double>::quiet_NaN();
871 dateString = newPosStr;
872 if (*dateString++ != '/' || !*dateString)
873 return std::numeric_limits<double>::quiet_NaN();
874 if (!parseLong(dateString, &newPosStr, 10, &day))
875 return std::numeric_limits<double>::quiet_NaN();
876 dateString = newPosStr;
877 } else if (*dateString == '/' && month == -1) {
879 // This looks like a MM/DD/YYYY date, not an RFC date.
880 month = day - 1; // 0-based
881 if (!parseLong(dateString, &newPosStr, 10, &day))
882 return std::numeric_limits<double>::quiet_NaN();
883 if (day < 1 || day > 31)
884 return std::numeric_limits<double>::quiet_NaN();
885 dateString = newPosStr;
886 if (*dateString == '/')
889 return std::numeric_limits<double>::quiet_NaN();
891 if (*dateString == '-')
894 skipSpacesAndComments(dateString);
896 if (*dateString == ',')
899 if (month == -1) { // not found yet
900 month = findMonth(dateString);
902 return std::numeric_limits<double>::quiet_NaN();
904 while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString))
908 return std::numeric_limits<double>::quiet_NaN();
910 // '-99 23:12:40 GMT'
911 if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString))
912 return std::numeric_limits<double>::quiet_NaN();
917 if (month < 0 || month > 11)
918 return std::numeric_limits<double>::quiet_NaN();
921 if (year <= 0 && *dateString) {
922 if (!parseInt(dateString, &newPosStr, 10, &year))
923 return std::numeric_limits<double>::quiet_NaN();
926 // Don't fail if the time is missing.
931 dateString = newPosStr;
934 if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) {
935 if (*newPosStr != ':')
936 return std::numeric_limits<double>::quiet_NaN();
937 // There was no year; the number was the hour.
940 // in the normal case (we parsed the year), advance to the next number
941 dateString = ++newPosStr;
942 skipSpacesAndComments(dateString);
945 parseLong(dateString, &newPosStr, 10, &hour);
946 // Do not check for errno here since we want to continue
947 // even if errno was set becasue we are still looking
950 // Read a number? If not, this might be a timezone name.
951 if (newPosStr != dateString) {
952 dateString = newPosStr;
954 if (hour < 0 || hour > 23)
955 return std::numeric_limits<double>::quiet_NaN();
958 return std::numeric_limits<double>::quiet_NaN();
961 if (*dateString++ != ':')
962 return std::numeric_limits<double>::quiet_NaN();
964 if (!parseLong(dateString, &newPosStr, 10, &minute))
965 return std::numeric_limits<double>::quiet_NaN();
966 dateString = newPosStr;
968 if (minute < 0 || minute > 59)
969 return std::numeric_limits<double>::quiet_NaN();
972 if (*dateString && *dateString != ':' && !isASCIISpace(*dateString))
973 return std::numeric_limits<double>::quiet_NaN();
975 // seconds are optional in rfc822 + rfc2822
976 if (*dateString ==':') {
979 if (!parseLong(dateString, &newPosStr, 10, &second))
980 return std::numeric_limits<double>::quiet_NaN();
981 dateString = newPosStr;
983 if (second < 0 || second > 59)
984 return std::numeric_limits<double>::quiet_NaN();
987 skipSpacesAndComments(dateString);
989 if (strncasecmp(dateString, "AM", 2) == 0) {
991 return std::numeric_limits<double>::quiet_NaN();
995 skipSpacesAndComments(dateString);
996 } else if (strncasecmp(dateString, "PM", 2) == 0) {
998 return std::numeric_limits<double>::quiet_NaN();
1002 skipSpacesAndComments(dateString);
1007 // The year may be after the time but before the time zone.
1008 if (isASCIIDigit(*dateString) && year == -1) {
1009 if (!parseInt(dateString, &newPosStr, 10, &year))
1010 return std::numeric_limits<double>::quiet_NaN();
1011 dateString = newPosStr;
1012 skipSpacesAndComments(dateString);
1015 // Don't fail if the time zone is missing.
1016 // Some websites omit the time zone (4275206).
1018 if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
1023 if (*dateString == '+' || *dateString == '-') {
1025 if (!parseInt(dateString, &newPosStr, 10, &o))
1026 return std::numeric_limits<double>::quiet_NaN();
1027 dateString = newPosStr;
1029 if (o < -9959 || o > 9959)
1030 return std::numeric_limits<double>::quiet_NaN();
1032 int sgn = (o < 0) ? -1 : 1;
1034 if (*dateString != ':') {
1036 offset = ((o / 100) * 60 + (o % 100)) * sgn;
1038 offset = o * 60 * sgn;
1039 } else { // GMT+05:00
1040 ++dateString; // skip the ':'
1042 if (!parseInt(dateString, &newPosStr, 10, &o2))
1043 return std::numeric_limits<double>::quiet_NaN();
1044 dateString = newPosStr;
1045 offset = (o * 60 + o2) * sgn;
1049 for (size_t i = 0; i < WTF_ARRAY_LENGTH(known_zones); ++i) {
1050 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
1051 offset = known_zones[i].tzOffset;
1052 dateString += strlen(known_zones[i].tzName);
1060 skipSpacesAndComments(dateString);
1062 if (*dateString && year == -1) {
1063 if (!parseInt(dateString, &newPosStr, 10, &year))
1064 return std::numeric_limits<double>::quiet_NaN();
1065 dateString = newPosStr;
1066 skipSpacesAndComments(dateString);
1071 return std::numeric_limits<double>::quiet_NaN();
1073 // Y2K: Handle 2 digit years.
1074 if (year >= 0 && year < 100) {
1081 return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond;
1084 double parseDateFromNullTerminatedCharacters(const char* dateString)
1088 double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
1090 return std::numeric_limits<double>::quiet_NaN();
1092 // fall back to local timezone
1094 offset = calculateLocalTimeOffset(ms).offset / msPerMinute;
1096 return ms - (offset * msPerMinute);
1099 double timeClip(double t)
1101 if (!std::isfinite(t))
1102 return std::numeric_limits<double>::quiet_NaN();
1103 if (fabs(t) > maxECMAScriptTime)
1104 return std::numeric_limits<double>::quiet_NaN();
1108 // See http://tools.ietf.org/html/rfc2822#section-3.3 for more information.
1109 String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset)
1111 StringBuilder stringBuilder;
1112 stringBuilder.append(weekdayName[dayOfWeek]);
1113 stringBuilder.appendLiteral(", ");
1114 stringBuilder.appendNumber(day);
1115 stringBuilder.append(' ');
1116 stringBuilder.append(monthName[month]);
1117 stringBuilder.append(' ');
1118 stringBuilder.appendNumber(year);
1119 stringBuilder.append(' ');
1121 appendTwoDigitNumber(stringBuilder, hours);
1122 stringBuilder.append(':');
1123 appendTwoDigitNumber(stringBuilder, minutes);
1124 stringBuilder.append(':');
1125 appendTwoDigitNumber(stringBuilder, seconds);
1126 stringBuilder.append(' ');
1128 stringBuilder.append(utcOffset > 0 ? '+' : '-');
1129 int absoluteUTCOffset = abs(utcOffset);
1130 appendTwoDigitNumber(stringBuilder, absoluteUTCOffset / 60);
1131 appendTwoDigitNumber(stringBuilder, absoluteUTCOffset % 60);
1133 return stringBuilder.toString();