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 minutesPerDay = 24.0 * 60.0;
112 static const double secondsPerDay = 24.0 * 60.0 * 60.0;
113 static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0;
115 static const double usecPerSec = 1000000.0;
117 static const double maxUnixTime = 2145859200.0; // 12/31/2037
118 // ECMAScript asks not to support for a date of which total
119 // millisecond value is larger than the following value.
120 // See 15.9.1.14 of ECMA-262 5th edition.
121 static const double maxECMAScriptTime = 8.64E15;
123 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1.
124 // First for non-leap years, then for leap years.
125 static const int firstDayOfMonth[2][12] = {
126 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
127 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
130 bool isLeapYear(int year)
141 static inline int daysInYear(int year)
143 return 365 + isLeapYear(year);
146 static inline double daysFrom1970ToYear(int year)
148 // The Gregorian Calendar rules for leap years:
149 // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.
150 // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years.
151 // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years.
153 static const int leapDaysBefore1971By4Rule = 1970 / 4;
154 static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100;
155 static const int leapDaysBefore1971By400Rule = 1970 / 400;
157 const double yearMinusOne = year - 1;
158 const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule;
159 const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule;
160 const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule;
162 return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;
165 double msToDays(double ms)
167 return floor(ms / msPerDay);
170 static String twoDigitStringFromNumber(int number)
172 ASSERT(number >= 0 && number < 100);
174 return String::number(number);
175 return makeString("0", String::number(number));
178 int msToYear(double ms)
180 int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970);
181 double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear);
182 if (msFromApproxYearTo1970 > ms)
183 return approxYear - 1;
184 if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms)
185 return approxYear + 1;
189 int dayInYear(double ms, int year)
191 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
194 static inline double msToMilliseconds(double ms)
196 double result = fmod(ms, msPerDay);
202 int msToMinutes(double ms)
204 double result = fmod(floor(ms / msPerMinute), minutesPerHour);
206 result += minutesPerHour;
207 return static_cast<int>(result);
210 int msToHours(double ms)
212 double result = fmod(floor(ms/msPerHour), hoursPerDay);
214 result += hoursPerDay;
215 return static_cast<int>(result);
218 int monthFromDayInYear(int dayInYear, bool leapYear)
220 const int d = dayInYear;
225 step += (leapYear ? 29 : 28);
228 if (d < (step += 31))
230 if (d < (step += 30))
232 if (d < (step += 31))
234 if (d < (step += 30))
236 if (d < (step += 31))
238 if (d < (step += 31))
240 if (d < (step += 30))
242 if (d < (step += 31))
244 if (d < (step += 30))
249 static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth)
251 startDayOfThisMonth = startDayOfNextMonth;
252 startDayOfNextMonth += daysInThisMonth;
253 return (dayInYear <= startDayOfNextMonth);
256 int dayInMonthFromDayInYear(int dayInYear, bool leapYear)
258 const int d = dayInYear;
264 const int daysInFeb = (leapYear ? 29 : 28);
265 if (checkMonth(d, step, next, daysInFeb))
267 if (checkMonth(d, step, next, 31))
269 if (checkMonth(d, step, next, 30))
271 if (checkMonth(d, step, next, 31))
273 if (checkMonth(d, step, next, 30))
275 if (checkMonth(d, step, next, 31))
277 if (checkMonth(d, step, next, 31))
279 if (checkMonth(d, step, next, 30))
281 if (checkMonth(d, step, next, 31))
283 if (checkMonth(d, step, next, 30))
289 int dayInYear(int year, int month, int day)
291 return firstDayOfMonth[isLeapYear(year)][month] + day - 1;
294 double dateToDaysFrom1970(int year, int month, int day)
304 double yearday = floor(daysFrom1970ToYear(year));
305 ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0));
306 return yearday + dayInYear(year, month, day);
309 // There is a hard limit at 2038 that we currently do not have a workaround
310 // for (rdar://problem/5052975).
311 static inline int maximumYearForDST()
316 static inline int minimumYearForDST()
318 // Because of the 2038 issue (see maximumYearForDST) if the current year is
319 // greater than the max year minus 27 (2010), we want to use the max year
320 // minus 27 instead, to ensure there is a range of 28 years that all years
322 return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ;
326 * Find an equivalent year for the one given, where equivalence is deterined by
327 * the two years having the same leapness and the first day of the year, falling
328 * on the same day of the week.
330 * This function returns a year between this current year and 2037, however this
331 * function will potentially return incorrect results if the current year is after
332 * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after
333 * 2100, (rdar://problem/5055038).
335 int equivalentYearForDST(int year)
337 // It is ok if the cached year is not the current year as long as the rules
338 // for DST did not change between the two years; if they did the app would need
340 static int minYear = minimumYearForDST();
341 int maxYear = maximumYearForDST();
345 difference = minYear - year;
346 else if (year < minYear)
347 difference = maxYear - year;
351 int quotient = difference / 28;
352 int product = (quotient) * 28;
355 ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(std::numeric_limits<double>::quiet_NaN())));
359 int32_t calculateUTCOffset()
362 TIME_ZONE_INFORMATION timeZoneInformation;
363 GetTimeZoneInformation(&timeZoneInformation);
364 int32_t bias = timeZoneInformation.Bias + timeZoneInformation.StandardBias;
365 return -bias * 60 * 1000;
367 time_t localTime = time(0);
369 getLocalTime(&localTime, &localt);
371 // Get the difference between this time zone and UTC on the 1st of January of this year.
377 // Not setting localt.tm_year!
382 localt.tm_gmtoff = 0;
389 time_t utcOffset = timegm(&localt) - mktime(&localt);
391 // Using a canned date of 01/01/2009 on platforms with weaker date-handling foo.
392 localt.tm_year = 109;
393 time_t utcOffset = 1230768000 - mktime(&localt);
396 return static_cast<int32_t>(utcOffset * 1000);
401 * Get the DST offset for the time passed in.
403 static double calculateDSTOffsetSimple(double localTimeSeconds, double utcOffset)
405 if (localTimeSeconds > maxUnixTime)
406 localTimeSeconds = maxUnixTime;
407 else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0)
408 localTimeSeconds += secondsPerDay;
410 //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset()
411 double offsetTime = (localTimeSeconds * msPerSecond) + utcOffset;
413 // Offset from UTC but doesn't include DST obviously
414 int offsetHour = msToHours(offsetTime);
415 int offsetMinute = msToMinutes(offsetTime);
417 // FIXME: time_t has a potential problem in 2038
418 time_t localTime = static_cast<time_t>(localTimeSeconds);
421 getLocalTime(&localTime, &localTM);
423 double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60);
426 diff += secondsPerDay;
428 return (diff * msPerSecond);
431 // Get the DST offset, given a time in UTC
432 double calculateDSTOffset(double ms, double utcOffset)
434 // On Mac OS X, the call to localtime (see calculateDSTOffsetSimple) will return historically accurate
435 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
436 // standard explicitly dictates that historical information should not be considered when
437 // determining DST. For this reason we shift away from years that localtime can handle but would
438 // return historically accurate information.
439 int year = msToYear(ms);
440 int equivalentYear = equivalentYearForDST(year);
441 if (year != equivalentYear) {
442 bool leapYear = isLeapYear(year);
443 int dayInYearLocal = dayInYear(ms, year);
444 int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear);
445 int month = monthFromDayInYear(dayInYearLocal, leapYear);
446 double day = dateToDaysFrom1970(equivalentYear, month, dayInMonth);
447 ms = (day * msPerDay) + msToMilliseconds(ms);
450 return calculateDSTOffsetSimple(ms / msPerSecond, utcOffset);
453 void initializeDates()
456 static bool alreadyInitialized;
457 ASSERT(!alreadyInitialized);
458 alreadyInitialized = true;
461 equivalentYearForDST(2000); // Need to call once to initialize a static used in this function.
464 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second)
466 double days = (day - 32075)
467 + floor(1461 * (year + 4800 + (mon - 14) / 12) / 4)
468 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
469 - floor(3 * ((year + 4900 + (mon - 14) / 12) / 100) / 4)
471 return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
474 // We follow the recommendation of RFC 2822 to consider all
475 // obsolete time zones not listed here equivalent to "-0000".
476 static const struct KnownZone {
495 inline static void skipSpacesAndComments(const char*& s)
500 if (!isASCIISpace(ch)) {
503 else if (ch == ')' && nesting > 0)
505 else if (nesting == 0)
512 // returns 0-11 (Jan-Dec); -1 on failure
513 static int findMonth(const char* monthStr)
517 for (int i = 0; i < 3; ++i) {
520 needle[i] = static_cast<char>(toASCIILower(*monthStr++));
523 const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
524 const char *str = strstr(haystack, needle);
526 int position = static_cast<int>(str - haystack);
527 if (position % 3 == 0)
533 static bool parseLong(const char* string, char** stopPosition, int base, long* result)
535 *result = strtol(string, stopPosition, base);
536 // Avoid the use of errno as it is not available on Windows CE
537 if (string == *stopPosition || *result == LONG_MIN || *result == LONG_MAX)
542 // Parses a date with the format YYYY[-MM[-DD]].
543 // Year parsing is lenient, allows any number of digits, and +/-.
544 // Returns 0 if a parse error occurs, else returns the end of the parsed portion of the string.
545 static char* parseES5DatePortion(const char* currentPosition, long& year, long& month, long& day)
547 char* postParsePosition;
549 // This is a bit more lenient on the year string than ES5 specifies:
550 // instead of restricting to 4 digits (or 6 digits with mandatory +/-),
551 // it accepts any integer value. Consider this an implementation fallback.
552 if (!parseLong(currentPosition, &postParsePosition, 10, &year))
555 // Check for presence of -MM portion.
556 if (*postParsePosition != '-')
557 return postParsePosition;
558 currentPosition = postParsePosition + 1;
560 if (!isASCIIDigit(*currentPosition))
562 if (!parseLong(currentPosition, &postParsePosition, 10, &month))
564 if ((postParsePosition - currentPosition) != 2)
567 // Check for presence of -DD portion.
568 if (*postParsePosition != '-')
569 return postParsePosition;
570 currentPosition = postParsePosition + 1;
572 if (!isASCIIDigit(*currentPosition))
574 if (!parseLong(currentPosition, &postParsePosition, 10, &day))
576 if ((postParsePosition - currentPosition) != 2)
578 return postParsePosition;
581 // Parses a time with the format HH:mm[:ss[.sss]][Z|(+|-)00:00].
582 // Fractional seconds parsing is lenient, allows any number of digits.
583 // Returns 0 if a parse error occurs, else returns the end of the parsed portion of the string.
584 static char* parseES5TimePortion(char* currentPosition, long& hours, long& minutes, double& seconds, long& timeZoneSeconds)
586 char* postParsePosition;
587 if (!isASCIIDigit(*currentPosition))
589 if (!parseLong(currentPosition, &postParsePosition, 10, &hours))
591 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
593 currentPosition = postParsePosition + 1;
595 if (!isASCIIDigit(*currentPosition))
597 if (!parseLong(currentPosition, &postParsePosition, 10, &minutes))
599 if ((postParsePosition - currentPosition) != 2)
601 currentPosition = postParsePosition;
603 // Seconds are optional.
604 if (*currentPosition == ':') {
608 if (!isASCIIDigit(*currentPosition))
610 if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds))
612 if ((postParsePosition - currentPosition) != 2)
614 seconds = intSeconds;
615 if (*postParsePosition == '.') {
616 currentPosition = postParsePosition + 1;
618 // In ECMA-262-5 it's a bit unclear if '.' can be present without milliseconds, but
619 // a reasonable interpretation guided by the given examples and RFC 3339 says "no".
620 // We check the next character to avoid reading +/- timezone hours after an invalid decimal.
621 if (!isASCIIDigit(*currentPosition))
624 // We are more lenient than ES5 by accepting more or less than 3 fraction digits.
626 if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds))
629 long numFracDigits = postParsePosition - currentPosition;
630 seconds += fracSeconds * pow(10.0, static_cast<double>(-numFracDigits));
632 currentPosition = postParsePosition;
635 if (*currentPosition == 'Z')
636 return currentPosition + 1;
639 if (*currentPosition == '-')
641 else if (*currentPosition == '+')
644 return currentPosition; // no timezone
651 if (!isASCIIDigit(*currentPosition))
653 if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours))
655 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
657 tzHoursAbs = labs(tzHours);
658 currentPosition = postParsePosition + 1;
660 if (!isASCIIDigit(*currentPosition))
662 if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes))
664 if ((postParsePosition - currentPosition) != 2)
666 currentPosition = postParsePosition;
670 if (tzMinutes < 0 || tzMinutes > 59)
673 timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs));
675 timeZoneSeconds = -timeZoneSeconds;
677 return currentPosition;
680 double parseES5DateFromNullTerminatedCharacters(const char* dateString)
682 // This parses a date of the form defined in ECMA-262-5, section 15.9.1.15
683 // (similar to RFC 3339 / ISO 8601: YYYY-MM-DDTHH:mm:ss[.sss]Z).
684 // In most cases it is intentionally strict (e.g. correct field widths, no stray whitespace).
686 static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
688 // The year must be present, but the other fields may be omitted - see ES5.1 15.9.1.15.
695 long timeZoneSeconds = 0;
697 // Parse the date YYYY[-MM[-DD]]
698 char* currentPosition = parseES5DatePortion(dateString, year, month, day);
699 if (!currentPosition)
700 return std::numeric_limits<double>::quiet_NaN();
701 // Look for a time portion.
702 if (*currentPosition == 'T') {
703 // Parse the time HH:mm[:ss[.sss]][Z|(+|-)00:00]
704 currentPosition = parseES5TimePortion(currentPosition + 1, hours, minutes, seconds, timeZoneSeconds);
705 if (!currentPosition)
706 return std::numeric_limits<double>::quiet_NaN();
708 // Check that we have parsed all characters in the string.
709 if (*currentPosition)
710 return std::numeric_limits<double>::quiet_NaN();
712 // A few of these checks could be done inline above, but since many of them are interrelated
713 // we would be sacrificing readability to "optimize" the (presumably less common) failure path.
714 if (month < 1 || month > 12)
715 return std::numeric_limits<double>::quiet_NaN();
716 if (day < 1 || day > daysPerMonth[month - 1])
717 return std::numeric_limits<double>::quiet_NaN();
718 if (month == 2 && day > 28 && !isLeapYear(year))
719 return std::numeric_limits<double>::quiet_NaN();
720 if (hours < 0 || hours > 24)
721 return std::numeric_limits<double>::quiet_NaN();
722 if (hours == 24 && (minutes || seconds))
723 return std::numeric_limits<double>::quiet_NaN();
724 if (minutes < 0 || minutes > 59)
725 return std::numeric_limits<double>::quiet_NaN();
726 if (seconds < 0 || seconds >= 61)
727 return std::numeric_limits<double>::quiet_NaN();
729 // Discard leap seconds by clamping to the end of a minute.
733 double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds;
734 return dateSeconds * msPerSecond;
737 // Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore.
738 double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset)
743 // This parses a date in the form:
744 // Tuesday, 09-Nov-99 23:12:40 GMT
746 // Sat, 01-Jan-2000 08:00:00 GMT
748 // Sat, 01 Jan 2000 08:00:00 GMT
750 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
751 // ### non RFC formats, added for Javascript:
752 // [Wednesday] January 09 1999 23:12:40 GMT
753 // [Wednesday] January 09 23:12:40 GMT 1999
755 // We ignore the weekday.
757 // Skip leading space
758 skipSpacesAndComments(dateString);
761 const char *wordStart = dateString;
762 // Check contents of first words if not number
763 while (*dateString && !isASCIIDigit(*dateString)) {
764 if (isASCIISpace(*dateString) || *dateString == '(') {
765 if (dateString - wordStart >= 3)
766 month = findMonth(wordStart);
767 skipSpacesAndComments(dateString);
768 wordStart = dateString;
773 // Missing delimiter between month and day (like "January29")?
774 if (month == -1 && wordStart != dateString)
775 month = findMonth(wordStart);
777 skipSpacesAndComments(dateString);
780 return std::numeric_limits<double>::quiet_NaN();
782 // ' 09-Nov-99 23:12:40 GMT'
785 if (!parseLong(dateString, &newPosStr, 10, &day))
786 return std::numeric_limits<double>::quiet_NaN();
787 dateString = newPosStr;
790 return std::numeric_limits<double>::quiet_NaN();
793 return std::numeric_limits<double>::quiet_NaN();
797 // ### where is the boundary and what happens below?
798 if (*dateString != '/')
799 return std::numeric_limits<double>::quiet_NaN();
800 // looks like a YYYY/MM/DD date
802 return std::numeric_limits<double>::quiet_NaN();
804 if (!parseLong(dateString, &newPosStr, 10, &month))
805 return std::numeric_limits<double>::quiet_NaN();
807 dateString = newPosStr;
808 if (*dateString++ != '/' || !*dateString)
809 return std::numeric_limits<double>::quiet_NaN();
810 if (!parseLong(dateString, &newPosStr, 10, &day))
811 return std::numeric_limits<double>::quiet_NaN();
812 dateString = newPosStr;
813 } else if (*dateString == '/' && month == -1) {
815 // This looks like a MM/DD/YYYY date, not an RFC date.
816 month = day - 1; // 0-based
817 if (!parseLong(dateString, &newPosStr, 10, &day))
818 return std::numeric_limits<double>::quiet_NaN();
819 if (day < 1 || day > 31)
820 return std::numeric_limits<double>::quiet_NaN();
821 dateString = newPosStr;
822 if (*dateString == '/')
825 return std::numeric_limits<double>::quiet_NaN();
827 if (*dateString == '-')
830 skipSpacesAndComments(dateString);
832 if (*dateString == ',')
835 if (month == -1) { // not found yet
836 month = findMonth(dateString);
838 return std::numeric_limits<double>::quiet_NaN();
840 while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString))
844 return std::numeric_limits<double>::quiet_NaN();
846 // '-99 23:12:40 GMT'
847 if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString))
848 return std::numeric_limits<double>::quiet_NaN();
853 if (month < 0 || month > 11)
854 return std::numeric_limits<double>::quiet_NaN();
857 if (year <= 0 && *dateString) {
858 if (!parseLong(dateString, &newPosStr, 10, &year))
859 return std::numeric_limits<double>::quiet_NaN();
862 // Don't fail if the time is missing.
867 dateString = newPosStr;
870 if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) {
871 if (*newPosStr != ':')
872 return std::numeric_limits<double>::quiet_NaN();
873 // There was no year; the number was the hour.
876 // in the normal case (we parsed the year), advance to the next number
877 dateString = ++newPosStr;
878 skipSpacesAndComments(dateString);
881 parseLong(dateString, &newPosStr, 10, &hour);
882 // Do not check for errno here since we want to continue
883 // even if errno was set becasue we are still looking
886 // Read a number? If not, this might be a timezone name.
887 if (newPosStr != dateString) {
888 dateString = newPosStr;
890 if (hour < 0 || hour > 23)
891 return std::numeric_limits<double>::quiet_NaN();
894 return std::numeric_limits<double>::quiet_NaN();
897 if (*dateString++ != ':')
898 return std::numeric_limits<double>::quiet_NaN();
900 if (!parseLong(dateString, &newPosStr, 10, &minute))
901 return std::numeric_limits<double>::quiet_NaN();
902 dateString = newPosStr;
904 if (minute < 0 || minute > 59)
905 return std::numeric_limits<double>::quiet_NaN();
908 if (*dateString && *dateString != ':' && !isASCIISpace(*dateString))
909 return std::numeric_limits<double>::quiet_NaN();
911 // seconds are optional in rfc822 + rfc2822
912 if (*dateString ==':') {
915 if (!parseLong(dateString, &newPosStr, 10, &second))
916 return std::numeric_limits<double>::quiet_NaN();
917 dateString = newPosStr;
919 if (second < 0 || second > 59)
920 return std::numeric_limits<double>::quiet_NaN();
923 skipSpacesAndComments(dateString);
925 if (strncasecmp(dateString, "AM", 2) == 0) {
927 return std::numeric_limits<double>::quiet_NaN();
931 skipSpacesAndComments(dateString);
932 } else if (strncasecmp(dateString, "PM", 2) == 0) {
934 return std::numeric_limits<double>::quiet_NaN();
938 skipSpacesAndComments(dateString);
943 // The year may be after the time but before the time zone.
944 if (isASCIIDigit(*dateString) && year == -1) {
945 if (!parseLong(dateString, &newPosStr, 10, &year))
946 return std::numeric_limits<double>::quiet_NaN();
947 dateString = newPosStr;
948 skipSpacesAndComments(dateString);
951 // Don't fail if the time zone is missing.
952 // Some websites omit the time zone (4275206).
954 if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
959 if (*dateString == '+' || *dateString == '-') {
961 if (!parseLong(dateString, &newPosStr, 10, &o))
962 return std::numeric_limits<double>::quiet_NaN();
963 dateString = newPosStr;
965 if (o < -9959 || o > 9959)
966 return std::numeric_limits<double>::quiet_NaN();
968 int sgn = (o < 0) ? -1 : 1;
970 if (*dateString != ':') {
972 offset = ((o / 100) * 60 + (o % 100)) * sgn;
974 offset = o * 60 * sgn;
975 } else { // GMT+05:00
976 ++dateString; // skip the ':'
978 if (!parseLong(dateString, &newPosStr, 10, &o2))
979 return std::numeric_limits<double>::quiet_NaN();
980 dateString = newPosStr;
981 offset = (o * 60 + o2) * sgn;
985 for (size_t i = 0; i < WTF_ARRAY_LENGTH(known_zones); ++i) {
986 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
987 offset = known_zones[i].tzOffset;
988 dateString += strlen(known_zones[i].tzName);
996 skipSpacesAndComments(dateString);
998 if (*dateString && year == -1) {
999 if (!parseLong(dateString, &newPosStr, 10, &year))
1000 return std::numeric_limits<double>::quiet_NaN();
1001 dateString = newPosStr;
1002 skipSpacesAndComments(dateString);
1007 return std::numeric_limits<double>::quiet_NaN();
1009 // Y2K: Handle 2 digit years.
1010 if (year >= 0 && year < 100) {
1017 return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond;
1020 double parseDateFromNullTerminatedCharacters(const char* dateString)
1024 double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
1026 return std::numeric_limits<double>::quiet_NaN();
1028 // fall back to local timezone
1030 double utcOffset = calculateUTCOffset();
1031 double dstOffset = calculateDSTOffset(ms, utcOffset);
1032 offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute);
1034 return ms - (offset * msPerMinute);
1037 double timeClip(double t)
1040 return std::numeric_limits<double>::quiet_NaN();
1041 if (fabs(t) > maxECMAScriptTime)
1042 return std::numeric_limits<double>::quiet_NaN();
1046 // See http://tools.ietf.org/html/rfc2822#section-3.3 for more information.
1047 String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset)
1049 StringBuilder stringBuilder;
1050 stringBuilder.append(weekdayName[dayOfWeek]);
1051 stringBuilder.append(", ");
1052 stringBuilder.append(String::number(day));
1053 stringBuilder.append(" ");
1054 stringBuilder.append(monthName[month]);
1055 stringBuilder.append(" ");
1056 stringBuilder.append(String::number(year));
1057 stringBuilder.append(" ");
1059 stringBuilder.append(twoDigitStringFromNumber(hours));
1060 stringBuilder.append(':');
1061 stringBuilder.append(twoDigitStringFromNumber(minutes));
1062 stringBuilder.append(':');
1063 stringBuilder.append(twoDigitStringFromNumber(seconds));
1064 stringBuilder.append(' ');
1066 stringBuilder.append(utcOffset > 0 ? "+" : "-");
1067 int absoluteUTCOffset = abs(utcOffset);
1068 stringBuilder.append(twoDigitStringFromNumber(absoluteUTCOffset / 60));
1069 stringBuilder.append(twoDigitStringFromNumber(absoluteUTCOffset % 60));
1071 return stringBuilder.toString();