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>
94 extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t);
95 extern "C" struct tm * localtime(const time_t *timer);
102 #if HAVE(SYS_TIMEB_H)
103 #include <sys/timeb.h>
112 static const double minutesPerDay = 24.0 * 60.0;
113 static const double secondsPerDay = 24.0 * 60.0 * 60.0;
114 static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0;
116 static const double usecPerSec = 1000000.0;
118 static const double maxUnixTime = 2145859200.0; // 12/31/2037
119 // ECMAScript asks not to support for a date of which total
120 // millisecond value is larger than the following value.
121 // See 15.9.1.14 of ECMA-262 5th edition.
122 static const double maxECMAScriptTime = 8.64E15;
124 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1.
125 // First for non-leap years, then for leap years.
126 static const int firstDayOfMonth[2][12] = {
127 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
128 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
131 bool isLeapYear(int year)
142 static inline int daysInYear(int year)
144 return 365 + isLeapYear(year);
147 static inline double daysFrom1970ToYear(int year)
149 // The Gregorian Calendar rules for leap years:
150 // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.
151 // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years.
152 // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years.
154 static const int leapDaysBefore1971By4Rule = 1970 / 4;
155 static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100;
156 static const int leapDaysBefore1971By400Rule = 1970 / 400;
158 const double yearMinusOne = year - 1;
159 const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule;
160 const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule;
161 const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule;
163 return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;
166 double msToDays(double ms)
168 return floor(ms / msPerDay);
171 static String twoDigitStringFromNumber(int number)
173 ASSERT(number >= 0 && number < 100);
175 return String::number(number);
176 return makeString("0", String::number(number));
179 int msToYear(double ms)
181 int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970);
182 double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear);
183 if (msFromApproxYearTo1970 > ms)
184 return approxYear - 1;
185 if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms)
186 return approxYear + 1;
190 int dayInYear(double ms, int year)
192 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
195 static inline double msToMilliseconds(double ms)
197 double result = fmod(ms, msPerDay);
203 int msToMinutes(double ms)
205 double result = fmod(floor(ms / msPerMinute), minutesPerHour);
207 result += minutesPerHour;
208 return static_cast<int>(result);
211 int msToHours(double ms)
213 double result = fmod(floor(ms/msPerHour), hoursPerDay);
215 result += hoursPerDay;
216 return static_cast<int>(result);
219 int monthFromDayInYear(int dayInYear, bool leapYear)
221 const int d = dayInYear;
226 step += (leapYear ? 29 : 28);
229 if (d < (step += 31))
231 if (d < (step += 30))
233 if (d < (step += 31))
235 if (d < (step += 30))
237 if (d < (step += 31))
239 if (d < (step += 31))
241 if (d < (step += 30))
243 if (d < (step += 31))
245 if (d < (step += 30))
250 static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth)
252 startDayOfThisMonth = startDayOfNextMonth;
253 startDayOfNextMonth += daysInThisMonth;
254 return (dayInYear <= startDayOfNextMonth);
257 int dayInMonthFromDayInYear(int dayInYear, bool leapYear)
259 const int d = dayInYear;
265 const int daysInFeb = (leapYear ? 29 : 28);
266 if (checkMonth(d, step, next, daysInFeb))
268 if (checkMonth(d, step, next, 31))
270 if (checkMonth(d, step, next, 30))
272 if (checkMonth(d, step, next, 31))
274 if (checkMonth(d, step, next, 30))
276 if (checkMonth(d, step, next, 31))
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, 30))
290 static inline int monthToDayInYear(int month, bool isLeapYear)
292 return firstDayOfMonth[isLeapYear][month];
295 double dateToDaysFrom1970(int year, int month, int day)
305 double yearday = floor(daysFrom1970ToYear(year));
306 ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0));
307 int monthday = monthToDayInYear(month, isLeapYear(year));
309 return yearday + monthday + day - 1;
312 // There is a hard limit at 2038 that we currently do not have a workaround
313 // for (rdar://problem/5052975).
314 static inline int maximumYearForDST()
319 static inline int minimumYearForDST()
321 // Because of the 2038 issue (see maximumYearForDST) if the current year is
322 // greater than the max year minus 27 (2010), we want to use the max year
323 // minus 27 instead, to ensure there is a range of 28 years that all years
325 return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ;
329 * Find an equivalent year for the one given, where equivalence is deterined by
330 * the two years having the same leapness and the first day of the year, falling
331 * on the same day of the week.
333 * This function returns a year between this current year and 2037, however this
334 * function will potentially return incorrect results if the current year is after
335 * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after
336 * 2100, (rdar://problem/5055038).
338 int equivalentYearForDST(int year)
340 // It is ok if the cached year is not the current year as long as the rules
341 // for DST did not change between the two years; if they did the app would need
343 static int minYear = minimumYearForDST();
344 int maxYear = maximumYearForDST();
348 difference = minYear - year;
349 else if (year < minYear)
350 difference = maxYear - year;
354 int quotient = difference / 28;
355 int product = (quotient) * 28;
358 ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(std::numeric_limits<double>::quiet_NaN())));
362 int32_t calculateUTCOffset()
364 time_t localTime = time(0);
366 getLocalTime(&localTime, &localt);
368 // Get the difference between this time zone and UTC on the 1st of January of this year.
374 // Not setting localt.tm_year!
379 localt.tm_gmtoff = 0;
386 time_t utcOffset = timegm(&localt) - mktime(&localt);
388 // Using a canned date of 01/01/2009 on platforms with weaker date-handling foo.
389 localt.tm_year = 109;
390 time_t utcOffset = 1230768000 - mktime(&localt);
393 return static_cast<int32_t>(utcOffset * 1000);
397 * Get the DST offset for the time passed in.
399 static double calculateDSTOffsetSimple(double localTimeSeconds, double utcOffset)
401 if (localTimeSeconds > maxUnixTime)
402 localTimeSeconds = maxUnixTime;
403 else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0)
404 localTimeSeconds += secondsPerDay;
406 //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset()
407 double offsetTime = (localTimeSeconds * msPerSecond) + utcOffset;
409 // Offset from UTC but doesn't include DST obviously
410 int offsetHour = msToHours(offsetTime);
411 int offsetMinute = msToMinutes(offsetTime);
413 // FIXME: time_t has a potential problem in 2038
414 time_t localTime = static_cast<time_t>(localTimeSeconds);
417 getLocalTime(&localTime, &localTM);
419 double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60);
422 diff += secondsPerDay;
424 return (diff * msPerSecond);
427 // Get the DST offset, given a time in UTC
428 double calculateDSTOffset(double ms, double utcOffset)
430 // On Mac OS X, the call to localtime (see calculateDSTOffsetSimple) will return historically accurate
431 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
432 // standard explicitly dictates that historical information should not be considered when
433 // determining DST. For this reason we shift away from years that localtime can handle but would
434 // return historically accurate information.
435 int year = msToYear(ms);
436 int equivalentYear = equivalentYearForDST(year);
437 if (year != equivalentYear) {
438 bool leapYear = isLeapYear(year);
439 int dayInYearLocal = dayInYear(ms, year);
440 int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear);
441 int month = monthFromDayInYear(dayInYearLocal, leapYear);
442 double day = dateToDaysFrom1970(equivalentYear, month, dayInMonth);
443 ms = (day * msPerDay) + msToMilliseconds(ms);
446 return calculateDSTOffsetSimple(ms / msPerSecond, utcOffset);
449 void initializeDates()
452 static bool alreadyInitialized;
453 ASSERT(!alreadyInitialized);
454 alreadyInitialized = true;
457 equivalentYearForDST(2000); // Need to call once to initialize a static used in this function.
460 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second)
462 double days = (day - 32075)
463 + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4)
464 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
465 - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4)
467 return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
470 // We follow the recommendation of RFC 2822 to consider all
471 // obsolete time zones not listed here equivalent to "-0000".
472 static const struct KnownZone {
491 inline static void skipSpacesAndComments(const char*& s)
496 if (!isASCIISpace(ch)) {
499 else if (ch == ')' && nesting > 0)
501 else if (nesting == 0)
508 // returns 0-11 (Jan-Dec); -1 on failure
509 static int findMonth(const char* monthStr)
513 for (int i = 0; i < 3; ++i) {
516 needle[i] = static_cast<char>(toASCIILower(*monthStr++));
519 const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
520 const char *str = strstr(haystack, needle);
522 int position = static_cast<int>(str - haystack);
523 if (position % 3 == 0)
529 static bool parseLong(const char* string, char** stopPosition, int base, long* result)
531 *result = strtol(string, stopPosition, base);
532 // Avoid the use of errno as it is not available on Windows CE
533 if (string == *stopPosition || *result == LONG_MIN || *result == LONG_MAX)
538 double parseES5DateFromNullTerminatedCharacters(const char* dateString)
540 // This parses a date of the form defined in ECMA-262-5, section 15.9.1.15
541 // (similar to RFC 3339 / ISO 8601: YYYY-MM-DDTHH:mm:ss[.sss]Z).
542 // In most cases it is intentionally strict (e.g. correct field widths, no stray whitespace).
544 static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
546 const char* currentPosition = dateString;
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.
553 if (!parseLong(currentPosition, &postParsePosition, 10, &year))
554 return std::numeric_limits<double>::quiet_NaN();
555 if (*postParsePosition != '-')
556 return std::numeric_limits<double>::quiet_NaN();
557 currentPosition = postParsePosition + 1;
560 if (!isASCIIDigit(*currentPosition))
561 return std::numeric_limits<double>::quiet_NaN();
562 if (!parseLong(currentPosition, &postParsePosition, 10, &month))
563 return std::numeric_limits<double>::quiet_NaN();
564 if (*postParsePosition != '-' || (postParsePosition - currentPosition) != 2)
565 return std::numeric_limits<double>::quiet_NaN();
566 currentPosition = postParsePosition + 1;
569 if (!isASCIIDigit(*currentPosition))
570 return std::numeric_limits<double>::quiet_NaN();
571 if (!parseLong(currentPosition, &postParsePosition, 10, &day))
572 return std::numeric_limits<double>::quiet_NaN();
573 if (*postParsePosition != 'T' || (postParsePosition - currentPosition) != 2)
574 return std::numeric_limits<double>::quiet_NaN();
575 currentPosition = postParsePosition + 1;
578 if (!isASCIIDigit(*currentPosition))
579 return std::numeric_limits<double>::quiet_NaN();
580 if (!parseLong(currentPosition, &postParsePosition, 10, &hours))
581 return std::numeric_limits<double>::quiet_NaN();
582 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
583 return std::numeric_limits<double>::quiet_NaN();
584 currentPosition = postParsePosition + 1;
587 if (!isASCIIDigit(*currentPosition))
588 return std::numeric_limits<double>::quiet_NaN();
589 if (!parseLong(currentPosition, &postParsePosition, 10, &minutes))
590 return std::numeric_limits<double>::quiet_NaN();
591 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
592 return std::numeric_limits<double>::quiet_NaN();
593 currentPosition = postParsePosition + 1;
596 if (!isASCIIDigit(*currentPosition))
597 return std::numeric_limits<double>::quiet_NaN();
598 if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds))
599 return std::numeric_limits<double>::quiet_NaN();
600 if ((postParsePosition - currentPosition) != 2)
601 return std::numeric_limits<double>::quiet_NaN();
603 double seconds = intSeconds;
604 if (*postParsePosition == '.') {
605 currentPosition = postParsePosition + 1;
607 // In ECMA-262-5 it's a bit unclear if '.' can be present without milliseconds, but
608 // a reasonable interpretation guided by the given examples and RFC 3339 says "no".
609 // We check the next character to avoid reading +/- timezone hours after an invalid decimal.
610 if (!isASCIIDigit(*currentPosition))
611 return std::numeric_limits<double>::quiet_NaN();
613 // We are more lenient than ES5 by accepting more or less than 3 fraction digits.
615 if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds))
616 return std::numeric_limits<double>::quiet_NaN();
618 long numFracDigits = postParsePosition - currentPosition;
619 seconds += fracSeconds * pow(10.0, static_cast<double>(-numFracDigits));
621 currentPosition = postParsePosition;
623 // A few of these checks could be done inline above, but since many of them are interrelated
624 // we would be sacrificing readability to "optimize" the (presumably less common) failure path.
625 if (month < 1 || month > 12)
626 return std::numeric_limits<double>::quiet_NaN();
627 if (day < 1 || day > daysPerMonth[month - 1])
628 return std::numeric_limits<double>::quiet_NaN();
629 if (month == 2 && day > 28 && !isLeapYear(year))
630 return std::numeric_limits<double>::quiet_NaN();
631 if (hours < 0 || hours > 24)
632 return std::numeric_limits<double>::quiet_NaN();
633 if (hours == 24 && (minutes || seconds))
634 return std::numeric_limits<double>::quiet_NaN();
635 if (minutes < 0 || minutes > 59)
636 return std::numeric_limits<double>::quiet_NaN();
637 if (seconds < 0 || seconds >= 61)
638 return std::numeric_limits<double>::quiet_NaN();
640 // Discard leap seconds by clamping to the end of a minute.
644 long timeZoneSeconds = 0;
645 if (*currentPosition != 'Z') {
647 if (*currentPosition == '-')
649 else if (*currentPosition == '+')
652 return std::numeric_limits<double>::quiet_NaN();
653 currentPosition += 1;
659 if (!isASCIIDigit(*currentPosition))
660 return std::numeric_limits<double>::quiet_NaN();
661 if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours))
662 return std::numeric_limits<double>::quiet_NaN();
663 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
664 return std::numeric_limits<double>::quiet_NaN();
665 tzHoursAbs = labs(tzHours);
666 currentPosition = postParsePosition + 1;
668 if (!isASCIIDigit(*currentPosition))
669 return std::numeric_limits<double>::quiet_NaN();
670 if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes))
671 return std::numeric_limits<double>::quiet_NaN();
672 if ((postParsePosition - currentPosition) != 2)
673 return std::numeric_limits<double>::quiet_NaN();
674 currentPosition = postParsePosition;
677 return std::numeric_limits<double>::quiet_NaN();
678 if (tzMinutes < 0 || tzMinutes > 59)
679 return std::numeric_limits<double>::quiet_NaN();
681 timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs));
683 timeZoneSeconds = -timeZoneSeconds;
685 currentPosition += 1;
687 if (*currentPosition)
688 return std::numeric_limits<double>::quiet_NaN();
690 double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds;
691 return dateSeconds * msPerSecond;
694 // Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore.
695 double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset)
700 // This parses a date in the form:
701 // Tuesday, 09-Nov-99 23:12:40 GMT
703 // Sat, 01-Jan-2000 08:00:00 GMT
705 // Sat, 01 Jan 2000 08:00:00 GMT
707 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
708 // ### non RFC formats, added for Javascript:
709 // [Wednesday] January 09 1999 23:12:40 GMT
710 // [Wednesday] January 09 23:12:40 GMT 1999
712 // We ignore the weekday.
714 // Skip leading space
715 skipSpacesAndComments(dateString);
718 const char *wordStart = dateString;
719 // Check contents of first words if not number
720 while (*dateString && !isASCIIDigit(*dateString)) {
721 if (isASCIISpace(*dateString) || *dateString == '(') {
722 if (dateString - wordStart >= 3)
723 month = findMonth(wordStart);
724 skipSpacesAndComments(dateString);
725 wordStart = dateString;
730 // Missing delimiter between month and day (like "January29")?
731 if (month == -1 && wordStart != dateString)
732 month = findMonth(wordStart);
734 skipSpacesAndComments(dateString);
737 return std::numeric_limits<double>::quiet_NaN();
739 // ' 09-Nov-99 23:12:40 GMT'
742 if (!parseLong(dateString, &newPosStr, 10, &day))
743 return std::numeric_limits<double>::quiet_NaN();
744 dateString = newPosStr;
747 return std::numeric_limits<double>::quiet_NaN();
750 return std::numeric_limits<double>::quiet_NaN();
754 // ### where is the boundary and what happens below?
755 if (*dateString != '/')
756 return std::numeric_limits<double>::quiet_NaN();
757 // looks like a YYYY/MM/DD date
759 return std::numeric_limits<double>::quiet_NaN();
761 if (!parseLong(dateString, &newPosStr, 10, &month))
762 return std::numeric_limits<double>::quiet_NaN();
764 dateString = newPosStr;
765 if (*dateString++ != '/' || !*dateString)
766 return std::numeric_limits<double>::quiet_NaN();
767 if (!parseLong(dateString, &newPosStr, 10, &day))
768 return std::numeric_limits<double>::quiet_NaN();
769 dateString = newPosStr;
770 } else if (*dateString == '/' && month == -1) {
772 // This looks like a MM/DD/YYYY date, not an RFC date.
773 month = day - 1; // 0-based
774 if (!parseLong(dateString, &newPosStr, 10, &day))
775 return std::numeric_limits<double>::quiet_NaN();
776 if (day < 1 || day > 31)
777 return std::numeric_limits<double>::quiet_NaN();
778 dateString = newPosStr;
779 if (*dateString == '/')
782 return std::numeric_limits<double>::quiet_NaN();
784 if (*dateString == '-')
787 skipSpacesAndComments(dateString);
789 if (*dateString == ',')
792 if (month == -1) { // not found yet
793 month = findMonth(dateString);
795 return std::numeric_limits<double>::quiet_NaN();
797 while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString))
801 return std::numeric_limits<double>::quiet_NaN();
803 // '-99 23:12:40 GMT'
804 if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString))
805 return std::numeric_limits<double>::quiet_NaN();
810 if (month < 0 || month > 11)
811 return std::numeric_limits<double>::quiet_NaN();
814 if (year <= 0 && *dateString) {
815 if (!parseLong(dateString, &newPosStr, 10, &year))
816 return std::numeric_limits<double>::quiet_NaN();
819 // Don't fail if the time is missing.
824 dateString = newPosStr;
827 if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) {
828 if (*newPosStr != ':')
829 return std::numeric_limits<double>::quiet_NaN();
830 // There was no year; the number was the hour.
833 // in the normal case (we parsed the year), advance to the next number
834 dateString = ++newPosStr;
835 skipSpacesAndComments(dateString);
838 parseLong(dateString, &newPosStr, 10, &hour);
839 // Do not check for errno here since we want to continue
840 // even if errno was set becasue we are still looking
843 // Read a number? If not, this might be a timezone name.
844 if (newPosStr != dateString) {
845 dateString = newPosStr;
847 if (hour < 0 || hour > 23)
848 return std::numeric_limits<double>::quiet_NaN();
851 return std::numeric_limits<double>::quiet_NaN();
854 if (*dateString++ != ':')
855 return std::numeric_limits<double>::quiet_NaN();
857 if (!parseLong(dateString, &newPosStr, 10, &minute))
858 return std::numeric_limits<double>::quiet_NaN();
859 dateString = newPosStr;
861 if (minute < 0 || minute > 59)
862 return std::numeric_limits<double>::quiet_NaN();
865 if (*dateString && *dateString != ':' && !isASCIISpace(*dateString))
866 return std::numeric_limits<double>::quiet_NaN();
868 // seconds are optional in rfc822 + rfc2822
869 if (*dateString ==':') {
872 if (!parseLong(dateString, &newPosStr, 10, &second))
873 return std::numeric_limits<double>::quiet_NaN();
874 dateString = newPosStr;
876 if (second < 0 || second > 59)
877 return std::numeric_limits<double>::quiet_NaN();
880 skipSpacesAndComments(dateString);
882 if (strncasecmp(dateString, "AM", 2) == 0) {
884 return std::numeric_limits<double>::quiet_NaN();
888 skipSpacesAndComments(dateString);
889 } else if (strncasecmp(dateString, "PM", 2) == 0) {
891 return std::numeric_limits<double>::quiet_NaN();
895 skipSpacesAndComments(dateString);
900 // Don't fail if the time zone is missing.
901 // Some websites omit the time zone (4275206).
903 if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
908 if (*dateString == '+' || *dateString == '-') {
910 if (!parseLong(dateString, &newPosStr, 10, &o))
911 return std::numeric_limits<double>::quiet_NaN();
912 dateString = newPosStr;
914 if (o < -9959 || o > 9959)
915 return std::numeric_limits<double>::quiet_NaN();
917 int sgn = (o < 0) ? -1 : 1;
919 if (*dateString != ':') {
920 offset = ((o / 100) * 60 + (o % 100)) * sgn;
921 } else { // GMT+05:00
923 if (!parseLong(dateString, &newPosStr, 10, &o2))
924 return std::numeric_limits<double>::quiet_NaN();
925 dateString = newPosStr;
926 offset = (o * 60 + o2) * sgn;
930 for (size_t i = 0; i < WTF_ARRAY_LENGTH(known_zones); ++i) {
931 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
932 offset = known_zones[i].tzOffset;
933 dateString += strlen(known_zones[i].tzName);
941 skipSpacesAndComments(dateString);
943 if (*dateString && year == -1) {
944 if (!parseLong(dateString, &newPosStr, 10, &year))
945 return std::numeric_limits<double>::quiet_NaN();
946 dateString = newPosStr;
949 skipSpacesAndComments(dateString);
953 return std::numeric_limits<double>::quiet_NaN();
955 // Y2K: Handle 2 digit years.
956 if (year >= 0 && year < 100) {
963 return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond;
966 double parseDateFromNullTerminatedCharacters(const char* dateString)
970 double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
972 return std::numeric_limits<double>::quiet_NaN();
974 // fall back to local timezone
976 double utcOffset = calculateUTCOffset();
977 double dstOffset = calculateDSTOffset(ms, utcOffset);
978 offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute);
980 return ms - (offset * msPerMinute);
983 double timeClip(double t)
986 return std::numeric_limits<double>::quiet_NaN();
987 if (fabs(t) > maxECMAScriptTime)
988 return std::numeric_limits<double>::quiet_NaN();
992 // See http://tools.ietf.org/html/rfc2822#section-3.3 for more information.
993 String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset)
995 StringBuilder stringBuilder;
996 stringBuilder.append(weekdayName[dayOfWeek]);
997 stringBuilder.append(", ");
998 stringBuilder.append(String::number(day));
999 stringBuilder.append(" ");
1000 stringBuilder.append(monthName[month]);
1001 stringBuilder.append(" ");
1002 stringBuilder.append(String::number(year));
1003 stringBuilder.append(" ");
1005 stringBuilder.append(twoDigitStringFromNumber(hours));
1006 stringBuilder.append(':');
1007 stringBuilder.append(twoDigitStringFromNumber(minutes));
1008 stringBuilder.append(':');
1009 stringBuilder.append(twoDigitStringFromNumber(seconds));
1010 stringBuilder.append(' ');
1012 stringBuilder.append(utcOffset > 0 ? "+" : "-");
1013 int absoluteUTCOffset = abs(utcOffset);
1014 stringBuilder.append(twoDigitStringFromNumber(absoluteUTCOffset / 60));
1015 stringBuilder.append(twoDigitStringFromNumber(absoluteUTCOffset % 60));
1017 return stringBuilder.toString();