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"
81 #include "MathExtras.h"
83 #include "ScopeChain.h"
85 #include "StdLibExtras.h"
86 #include "StringExtras.h"
93 #include <wtf/text/StringBuilder.h>
100 extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t);
101 extern "C" struct tm * localtime(const time_t *timer);
105 #include <sys/time.h>
108 #if HAVE(SYS_TIMEB_H)
109 #include <sys/timeb.h>
113 #include "CallFrame.h"
122 static const double minutesPerDay = 24.0 * 60.0;
123 static const double secondsPerDay = 24.0 * 60.0 * 60.0;
124 static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0;
126 static const double usecPerSec = 1000000.0;
128 static const double maxUnixTime = 2145859200.0; // 12/31/2037
129 // ECMAScript asks not to support for a date of which total
130 // millisecond value is larger than the following value.
131 // See 15.9.1.14 of ECMA-262 5th edition.
132 static const double maxECMAScriptTime = 8.64E15;
134 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1.
135 // First for non-leap years, then for leap years.
136 static const int firstDayOfMonth[2][12] = {
137 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
138 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
141 static inline bool isLeapYear(int year)
152 static inline int daysInYear(int year)
154 return 365 + isLeapYear(year);
157 static inline double daysFrom1970ToYear(int year)
159 // The Gregorian Calendar rules for leap years:
160 // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.
161 // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years.
162 // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years.
164 static const int leapDaysBefore1971By4Rule = 1970 / 4;
165 static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100;
166 static const int leapDaysBefore1971By400Rule = 1970 / 400;
168 const double yearMinusOne = year - 1;
169 const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule;
170 const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule;
171 const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule;
173 return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;
176 static inline double msToDays(double ms)
178 return floor(ms / msPerDay);
181 static String twoDigitStringFromNumber(int number)
183 ASSERT(number >= 0 && number < 100);
185 return String::number(number);
186 return makeString("0", String::number(number));
189 int msToYear(double ms)
191 int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970);
192 double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear);
193 if (msFromApproxYearTo1970 > ms)
194 return approxYear - 1;
195 if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms)
196 return approxYear + 1;
200 int dayInYear(double ms, int year)
202 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
205 static inline double msToMilliseconds(double ms)
207 double result = fmod(ms, msPerDay);
213 // 0: Sunday, 1: Monday, etc.
214 static inline int msToWeekDay(double ms)
216 int wd = (static_cast<int>(msToDays(ms)) + 4) % 7;
222 static inline int msToSeconds(double ms)
224 double result = fmod(floor(ms / msPerSecond), secondsPerMinute);
226 result += secondsPerMinute;
227 return static_cast<int>(result);
230 static inline int msToMinutes(double ms)
232 double result = fmod(floor(ms / msPerMinute), minutesPerHour);
234 result += minutesPerHour;
235 return static_cast<int>(result);
238 static inline int msToHours(double ms)
240 double result = fmod(floor(ms/msPerHour), hoursPerDay);
242 result += hoursPerDay;
243 return static_cast<int>(result);
246 int monthFromDayInYear(int dayInYear, bool leapYear)
248 const int d = dayInYear;
253 step += (leapYear ? 29 : 28);
256 if (d < (step += 31))
258 if (d < (step += 30))
260 if (d < (step += 31))
262 if (d < (step += 30))
264 if (d < (step += 31))
266 if (d < (step += 31))
268 if (d < (step += 30))
270 if (d < (step += 31))
272 if (d < (step += 30))
277 static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth)
279 startDayOfThisMonth = startDayOfNextMonth;
280 startDayOfNextMonth += daysInThisMonth;
281 return (dayInYear <= startDayOfNextMonth);
284 int dayInMonthFromDayInYear(int dayInYear, bool leapYear)
286 const int d = dayInYear;
292 const int daysInFeb = (leapYear ? 29 : 28);
293 if (checkMonth(d, step, next, daysInFeb))
295 if (checkMonth(d, step, next, 31))
297 if (checkMonth(d, step, next, 30))
299 if (checkMonth(d, step, next, 31))
301 if (checkMonth(d, step, next, 30))
303 if (checkMonth(d, step, next, 31))
305 if (checkMonth(d, step, next, 31))
307 if (checkMonth(d, step, next, 30))
309 if (checkMonth(d, step, next, 31))
311 if (checkMonth(d, step, next, 30))
317 static inline int monthToDayInYear(int month, bool isLeapYear)
319 return firstDayOfMonth[isLeapYear][month];
322 static inline double timeToMS(double hour, double min, double sec, double ms)
324 return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms);
327 double dateToDaysFrom1970(int year, int month, int day)
337 double yearday = floor(daysFrom1970ToYear(year));
338 ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0));
339 int monthday = monthToDayInYear(month, isLeapYear(year));
341 return yearday + monthday + day - 1;
344 // There is a hard limit at 2038 that we currently do not have a workaround
345 // for (rdar://problem/5052975).
346 static inline int maximumYearForDST()
351 static inline int minimumYearForDST()
353 // Because of the 2038 issue (see maximumYearForDST) if the current year is
354 // greater than the max year minus 27 (2010), we want to use the max year
355 // minus 27 instead, to ensure there is a range of 28 years that all years
357 return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ;
361 * Find an equivalent year for the one given, where equivalence is deterined by
362 * the two years having the same leapness and the first day of the year, falling
363 * on the same day of the week.
365 * This function returns a year between this current year and 2037, however this
366 * function will potentially return incorrect results if the current year is after
367 * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after
368 * 2100, (rdar://problem/5055038).
370 int equivalentYearForDST(int year)
372 // It is ok if the cached year is not the current year as long as the rules
373 // for DST did not change between the two years; if they did the app would need
375 static int minYear = minimumYearForDST();
376 int maxYear = maximumYearForDST();
380 difference = minYear - year;
381 else if (year < minYear)
382 difference = maxYear - year;
386 int quotient = difference / 28;
387 int product = (quotient) * 28;
390 ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(std::numeric_limits<double>::quiet_NaN())));
394 int32_t calculateUTCOffset()
397 time_t localTime = static_cast<time_t>(currentTime());
399 time_t localTime = time(0);
402 getLocalTime(&localTime, &localt);
404 // Get the difference between this time zone and UTC on the 1st of January of this year.
410 // Not setting localt.tm_year!
415 localt.tm_gmtoff = 0;
422 time_t utcOffset = timegm(&localt) - mktime(&localt);
424 // Using a canned date of 01/01/2009 on platforms with weaker date-handling foo.
425 localt.tm_year = 109;
426 time_t utcOffset = 1230768000 - mktime(&localt);
429 return static_cast<int32_t>(utcOffset * 1000);
433 * Get the DST offset for the time passed in.
435 static double calculateDSTOffsetSimple(double localTimeSeconds, double utcOffset)
437 if (localTimeSeconds > maxUnixTime)
438 localTimeSeconds = maxUnixTime;
439 else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0)
440 localTimeSeconds += secondsPerDay;
442 //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset()
443 double offsetTime = (localTimeSeconds * msPerSecond) + utcOffset;
445 // Offset from UTC but doesn't include DST obviously
446 int offsetHour = msToHours(offsetTime);
447 int offsetMinute = msToMinutes(offsetTime);
449 // FIXME: time_t has a potential problem in 2038
450 time_t localTime = static_cast<time_t>(localTimeSeconds);
453 getLocalTime(&localTime, &localTM);
455 double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60);
458 diff += secondsPerDay;
460 return (diff * msPerSecond);
463 // Get the DST offset, given a time in UTC
464 double calculateDSTOffset(double ms, double utcOffset)
466 // On Mac OS X, the call to localtime (see calculateDSTOffsetSimple) will return historically accurate
467 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
468 // standard explicitly dictates that historical information should not be considered when
469 // determining DST. For this reason we shift away from years that localtime can handle but would
470 // return historically accurate information.
471 int year = msToYear(ms);
472 int equivalentYear = equivalentYearForDST(year);
473 if (year != equivalentYear) {
474 bool leapYear = isLeapYear(year);
475 int dayInYearLocal = dayInYear(ms, year);
476 int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear);
477 int month = monthFromDayInYear(dayInYearLocal, leapYear);
478 double day = dateToDaysFrom1970(equivalentYear, month, dayInMonth);
479 ms = (day * msPerDay) + msToMilliseconds(ms);
482 return calculateDSTOffsetSimple(ms / msPerSecond, utcOffset);
485 void initializeDates()
488 static bool alreadyInitialized;
489 ASSERT(!alreadyInitialized);
490 alreadyInitialized = true;
493 equivalentYearForDST(2000); // Need to call once to initialize a static used in this function.
496 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second)
498 double days = (day - 32075)
499 + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4)
500 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
501 - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4)
503 return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
506 // We follow the recommendation of RFC 2822 to consider all
507 // obsolete time zones not listed here equivalent to "-0000".
508 static const struct KnownZone {
527 inline static void skipSpacesAndComments(const char*& s)
532 if (!isASCIISpace(ch)) {
535 else if (ch == ')' && nesting > 0)
537 else if (nesting == 0)
544 // returns 0-11 (Jan-Dec); -1 on failure
545 static int findMonth(const char* monthStr)
549 for (int i = 0; i < 3; ++i) {
552 needle[i] = static_cast<char>(toASCIILower(*monthStr++));
555 const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
556 const char *str = strstr(haystack, needle);
558 int position = static_cast<int>(str - haystack);
559 if (position % 3 == 0)
565 static bool parseLong(const char* string, char** stopPosition, int base, long* result)
567 *result = strtol(string, stopPosition, base);
568 // Avoid the use of errno as it is not available on Windows CE
569 if (string == *stopPosition || *result == LONG_MIN || *result == LONG_MAX)
574 double parseES5DateFromNullTerminatedCharacters(const char* dateString)
576 // This parses a date of the form defined in ECMA-262-5, section 15.9.1.15
577 // (similar to RFC 3339 / ISO 8601: YYYY-MM-DDTHH:mm:ss[.sss]Z).
578 // In most cases it is intentionally strict (e.g. correct field widths, no stray whitespace).
580 static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
582 const char* currentPosition = dateString;
583 char* postParsePosition;
585 // This is a bit more lenient on the year string than ES5 specifies:
586 // instead of restricting to 4 digits (or 6 digits with mandatory +/-),
587 // it accepts any integer value. Consider this an implementation fallback.
589 if (!parseLong(currentPosition, &postParsePosition, 10, &year))
590 return std::numeric_limits<double>::quiet_NaN();
591 if (*postParsePosition != '-')
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, &month))
599 return std::numeric_limits<double>::quiet_NaN();
600 if (*postParsePosition != '-' || (postParsePosition - currentPosition) != 2)
601 return std::numeric_limits<double>::quiet_NaN();
602 currentPosition = postParsePosition + 1;
605 if (!isASCIIDigit(*currentPosition))
606 return std::numeric_limits<double>::quiet_NaN();
607 if (!parseLong(currentPosition, &postParsePosition, 10, &day))
608 return std::numeric_limits<double>::quiet_NaN();
609 if (*postParsePosition != 'T' || (postParsePosition - currentPosition) != 2)
610 return std::numeric_limits<double>::quiet_NaN();
611 currentPosition = postParsePosition + 1;
614 if (!isASCIIDigit(*currentPosition))
615 return std::numeric_limits<double>::quiet_NaN();
616 if (!parseLong(currentPosition, &postParsePosition, 10, &hours))
617 return std::numeric_limits<double>::quiet_NaN();
618 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
619 return std::numeric_limits<double>::quiet_NaN();
620 currentPosition = postParsePosition + 1;
623 if (!isASCIIDigit(*currentPosition))
624 return std::numeric_limits<double>::quiet_NaN();
625 if (!parseLong(currentPosition, &postParsePosition, 10, &minutes))
626 return std::numeric_limits<double>::quiet_NaN();
627 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
628 return std::numeric_limits<double>::quiet_NaN();
629 currentPosition = postParsePosition + 1;
632 if (!isASCIIDigit(*currentPosition))
633 return std::numeric_limits<double>::quiet_NaN();
634 if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds))
635 return std::numeric_limits<double>::quiet_NaN();
636 if ((postParsePosition - currentPosition) != 2)
637 return std::numeric_limits<double>::quiet_NaN();
639 double seconds = intSeconds;
640 if (*postParsePosition == '.') {
641 currentPosition = postParsePosition + 1;
643 // In ECMA-262-5 it's a bit unclear if '.' can be present without milliseconds, but
644 // a reasonable interpretation guided by the given examples and RFC 3339 says "no".
645 // We check the next character to avoid reading +/- timezone hours after an invalid decimal.
646 if (!isASCIIDigit(*currentPosition))
647 return std::numeric_limits<double>::quiet_NaN();
649 // We are more lenient than ES5 by accepting more or less than 3 fraction digits.
651 if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds))
652 return std::numeric_limits<double>::quiet_NaN();
654 long numFracDigits = postParsePosition - currentPosition;
655 seconds += fracSeconds * pow(10.0, static_cast<double>(-numFracDigits));
657 currentPosition = postParsePosition;
659 // A few of these checks could be done inline above, but since many of them are interrelated
660 // we would be sacrificing readability to "optimize" the (presumably less common) failure path.
661 if (month < 1 || month > 12)
662 return std::numeric_limits<double>::quiet_NaN();
663 if (day < 1 || day > daysPerMonth[month - 1])
664 return std::numeric_limits<double>::quiet_NaN();
665 if (month == 2 && day > 28 && !isLeapYear(year))
666 return std::numeric_limits<double>::quiet_NaN();
667 if (hours < 0 || hours > 24)
668 return std::numeric_limits<double>::quiet_NaN();
669 if (hours == 24 && (minutes || seconds))
670 return std::numeric_limits<double>::quiet_NaN();
671 if (minutes < 0 || minutes > 59)
672 return std::numeric_limits<double>::quiet_NaN();
673 if (seconds < 0 || seconds >= 61)
674 return std::numeric_limits<double>::quiet_NaN();
676 // Discard leap seconds by clamping to the end of a minute.
680 long timeZoneSeconds = 0;
681 if (*currentPosition != 'Z') {
683 if (*currentPosition == '-')
685 else if (*currentPosition == '+')
688 return std::numeric_limits<double>::quiet_NaN();
689 currentPosition += 1;
695 if (!isASCIIDigit(*currentPosition))
696 return std::numeric_limits<double>::quiet_NaN();
697 if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours))
698 return std::numeric_limits<double>::quiet_NaN();
699 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
700 return std::numeric_limits<double>::quiet_NaN();
701 tzHoursAbs = labs(tzHours);
702 currentPosition = postParsePosition + 1;
704 if (!isASCIIDigit(*currentPosition))
705 return std::numeric_limits<double>::quiet_NaN();
706 if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes))
707 return std::numeric_limits<double>::quiet_NaN();
708 if ((postParsePosition - currentPosition) != 2)
709 return std::numeric_limits<double>::quiet_NaN();
710 currentPosition = postParsePosition;
713 return std::numeric_limits<double>::quiet_NaN();
714 if (tzMinutes < 0 || tzMinutes > 59)
715 return std::numeric_limits<double>::quiet_NaN();
717 timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs));
719 timeZoneSeconds = -timeZoneSeconds;
721 currentPosition += 1;
723 if (*currentPosition)
724 return std::numeric_limits<double>::quiet_NaN();
726 double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds;
727 return dateSeconds * msPerSecond;
730 // Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore.
731 static double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset)
736 // This parses a date in the form:
737 // Tuesday, 09-Nov-99 23:12:40 GMT
739 // Sat, 01-Jan-2000 08:00:00 GMT
741 // Sat, 01 Jan 2000 08:00:00 GMT
743 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
744 // ### non RFC formats, added for Javascript:
745 // [Wednesday] January 09 1999 23:12:40 GMT
746 // [Wednesday] January 09 23:12:40 GMT 1999
748 // We ignore the weekday.
750 // Skip leading space
751 skipSpacesAndComments(dateString);
754 const char *wordStart = dateString;
755 // Check contents of first words if not number
756 while (*dateString && !isASCIIDigit(*dateString)) {
757 if (isASCIISpace(*dateString) || *dateString == '(') {
758 if (dateString - wordStart >= 3)
759 month = findMonth(wordStart);
760 skipSpacesAndComments(dateString);
761 wordStart = dateString;
766 // Missing delimiter between month and day (like "January29")?
767 if (month == -1 && wordStart != dateString)
768 month = findMonth(wordStart);
770 skipSpacesAndComments(dateString);
773 return std::numeric_limits<double>::quiet_NaN();
775 // ' 09-Nov-99 23:12:40 GMT'
778 if (!parseLong(dateString, &newPosStr, 10, &day))
779 return std::numeric_limits<double>::quiet_NaN();
780 dateString = newPosStr;
783 return std::numeric_limits<double>::quiet_NaN();
786 return std::numeric_limits<double>::quiet_NaN();
790 // ### where is the boundary and what happens below?
791 if (*dateString != '/')
792 return std::numeric_limits<double>::quiet_NaN();
793 // looks like a YYYY/MM/DD date
795 return std::numeric_limits<double>::quiet_NaN();
797 if (!parseLong(dateString, &newPosStr, 10, &month))
798 return std::numeric_limits<double>::quiet_NaN();
800 dateString = newPosStr;
801 if (*dateString++ != '/' || !*dateString)
802 return std::numeric_limits<double>::quiet_NaN();
803 if (!parseLong(dateString, &newPosStr, 10, &day))
804 return std::numeric_limits<double>::quiet_NaN();
805 dateString = newPosStr;
806 } else if (*dateString == '/' && month == -1) {
808 // This looks like a MM/DD/YYYY date, not an RFC date.
809 month = day - 1; // 0-based
810 if (!parseLong(dateString, &newPosStr, 10, &day))
811 return std::numeric_limits<double>::quiet_NaN();
812 if (day < 1 || day > 31)
813 return std::numeric_limits<double>::quiet_NaN();
814 dateString = newPosStr;
815 if (*dateString == '/')
818 return std::numeric_limits<double>::quiet_NaN();
820 if (*dateString == '-')
823 skipSpacesAndComments(dateString);
825 if (*dateString == ',')
828 if (month == -1) { // not found yet
829 month = findMonth(dateString);
831 return std::numeric_limits<double>::quiet_NaN();
833 while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString))
837 return std::numeric_limits<double>::quiet_NaN();
839 // '-99 23:12:40 GMT'
840 if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString))
841 return std::numeric_limits<double>::quiet_NaN();
846 if (month < 0 || month > 11)
847 return std::numeric_limits<double>::quiet_NaN();
850 if (year <= 0 && *dateString) {
851 if (!parseLong(dateString, &newPosStr, 10, &year))
852 return std::numeric_limits<double>::quiet_NaN();
855 // Don't fail if the time is missing.
860 dateString = newPosStr;
863 if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) {
864 if (*newPosStr != ':')
865 return std::numeric_limits<double>::quiet_NaN();
866 // There was no year; the number was the hour.
869 // in the normal case (we parsed the year), advance to the next number
870 dateString = ++newPosStr;
871 skipSpacesAndComments(dateString);
874 parseLong(dateString, &newPosStr, 10, &hour);
875 // Do not check for errno here since we want to continue
876 // even if errno was set becasue we are still looking
879 // Read a number? If not, this might be a timezone name.
880 if (newPosStr != dateString) {
881 dateString = newPosStr;
883 if (hour < 0 || hour > 23)
884 return std::numeric_limits<double>::quiet_NaN();
887 return std::numeric_limits<double>::quiet_NaN();
890 if (*dateString++ != ':')
891 return std::numeric_limits<double>::quiet_NaN();
893 if (!parseLong(dateString, &newPosStr, 10, &minute))
894 return std::numeric_limits<double>::quiet_NaN();
895 dateString = newPosStr;
897 if (minute < 0 || minute > 59)
898 return std::numeric_limits<double>::quiet_NaN();
901 if (*dateString && *dateString != ':' && !isASCIISpace(*dateString))
902 return std::numeric_limits<double>::quiet_NaN();
904 // seconds are optional in rfc822 + rfc2822
905 if (*dateString ==':') {
908 if (!parseLong(dateString, &newPosStr, 10, &second))
909 return std::numeric_limits<double>::quiet_NaN();
910 dateString = newPosStr;
912 if (second < 0 || second > 59)
913 return std::numeric_limits<double>::quiet_NaN();
916 skipSpacesAndComments(dateString);
918 if (strncasecmp(dateString, "AM", 2) == 0) {
920 return std::numeric_limits<double>::quiet_NaN();
924 skipSpacesAndComments(dateString);
925 } else if (strncasecmp(dateString, "PM", 2) == 0) {
927 return std::numeric_limits<double>::quiet_NaN();
931 skipSpacesAndComments(dateString);
936 // Don't fail if the time zone is missing.
937 // Some websites omit the time zone (4275206).
939 if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
944 if (*dateString == '+' || *dateString == '-') {
946 if (!parseLong(dateString, &newPosStr, 10, &o))
947 return std::numeric_limits<double>::quiet_NaN();
948 dateString = newPosStr;
950 if (o < -9959 || o > 9959)
951 return std::numeric_limits<double>::quiet_NaN();
953 int sgn = (o < 0) ? -1 : 1;
955 if (*dateString != ':') {
956 offset = ((o / 100) * 60 + (o % 100)) * sgn;
957 } else { // GMT+05:00
959 if (!parseLong(dateString, &newPosStr, 10, &o2))
960 return std::numeric_limits<double>::quiet_NaN();
961 dateString = newPosStr;
962 offset = (o * 60 + o2) * sgn;
966 for (size_t i = 0; i < WTF_ARRAY_LENGTH(known_zones); ++i) {
967 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
968 offset = known_zones[i].tzOffset;
969 dateString += strlen(known_zones[i].tzName);
977 skipSpacesAndComments(dateString);
979 if (*dateString && year == -1) {
980 if (!parseLong(dateString, &newPosStr, 10, &year))
981 return std::numeric_limits<double>::quiet_NaN();
982 dateString = newPosStr;
985 skipSpacesAndComments(dateString);
989 return std::numeric_limits<double>::quiet_NaN();
991 // Y2K: Handle 2 digit years.
992 if (year >= 0 && year < 100) {
999 return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond;
1002 double parseDateFromNullTerminatedCharacters(const char* dateString)
1006 double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
1008 return std::numeric_limits<double>::quiet_NaN();
1010 // fall back to local timezone
1012 double utcOffset = calculateUTCOffset();
1013 double dstOffset = calculateDSTOffset(ms, utcOffset);
1014 offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute);
1016 return ms - (offset * msPerMinute);
1019 double timeClip(double t)
1022 return std::numeric_limits<double>::quiet_NaN();
1023 if (fabs(t) > maxECMAScriptTime)
1024 return std::numeric_limits<double>::quiet_NaN();
1028 // See http://tools.ietf.org/html/rfc2822#section-3.3 for more information.
1029 String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset)
1031 StringBuilder stringBuilder;
1032 stringBuilder.append(weekdayName[dayOfWeek]);
1033 stringBuilder.append(", ");
1034 stringBuilder.append(String::number(day));
1035 stringBuilder.append(" ");
1036 stringBuilder.append(monthName[month]);
1037 stringBuilder.append(" ");
1038 stringBuilder.append(String::number(year));
1039 stringBuilder.append(" ");
1041 stringBuilder.append(twoDigitStringFromNumber(hours));
1042 stringBuilder.append(':');
1043 stringBuilder.append(twoDigitStringFromNumber(minutes));
1044 stringBuilder.append(':');
1045 stringBuilder.append(twoDigitStringFromNumber(seconds));
1046 stringBuilder.append(' ');
1048 stringBuilder.append(utcOffset > 0 ? "+" : "-");
1049 int absoluteUTCOffset = abs(utcOffset);
1050 stringBuilder.append(twoDigitStringFromNumber(absoluteUTCOffset / 60));
1051 stringBuilder.append(twoDigitStringFromNumber(absoluteUTCOffset % 60));
1053 return stringBuilder.toString();
1060 // Get the DST offset for the time passed in.
1062 // NOTE: The implementation relies on the fact that no time zones have
1063 // more than one daylight savings offset change per month.
1064 // If this function is called with NaN it returns NaN.
1065 static double getDSTOffset(ExecState* exec, double ms, double utcOffset)
1067 DSTOffsetCache& cache = exec->globalData().dstOffsetCache;
1068 double start = cache.start;
1069 double end = cache.end;
1072 // If the time fits in the cached interval, return the cached offset.
1073 if (ms <= end) return cache.offset;
1075 // Compute a possible new interval end.
1076 double newEnd = end + cache.increment;
1079 double endOffset = calculateDSTOffset(newEnd, utcOffset);
1080 if (cache.offset == endOffset) {
1081 // If the offset at the end of the new interval still matches
1082 // the offset in the cache, we grow the cached time interval
1083 // and return the offset.
1085 cache.increment = msPerMonth;
1088 double offset = calculateDSTOffset(ms, utcOffset);
1089 if (offset == endOffset) {
1090 // The offset at the given time is equal to the offset at the
1091 // new end of the interval, so that means that we've just skipped
1092 // the point in time where the DST offset change occurred. Updated
1093 // the interval to reflect this and reset the increment.
1096 cache.increment = msPerMonth;
1098 // The interval contains a DST offset change and the given time is
1099 // before it. Adjust the increment to avoid a linear search for
1100 // the offset change point and change the end of the interval.
1101 cache.increment /= 3;
1104 // Update the offset in the cache and return it.
1105 cache.offset = offset;
1111 // Compute the DST offset for the time and shrink the cache interval
1112 // to only contain the time. This allows fast repeated DST offset
1113 // computations for the same time.
1114 double offset = calculateDSTOffset(ms, utcOffset);
1115 cache.offset = offset;
1118 cache.increment = msPerMonth;
1123 * Get the difference in milliseconds between this time zone and UTC (GMT)
1124 * NOT including DST.
1126 double getUTCOffset(ExecState* exec)
1128 double utcOffset = exec->globalData().cachedUTCOffset;
1129 if (!isnan(utcOffset))
1131 exec->globalData().cachedUTCOffset = calculateUTCOffset();
1132 return exec->globalData().cachedUTCOffset;
1135 double gregorianDateTimeToMS(ExecState* exec, const GregorianDateTime& t, double milliSeconds, bool inputIsUTC)
1137 double day = dateToDaysFrom1970(t.year + 1900, t.month, t.monthDay);
1138 double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds);
1139 double result = (day * WTF::msPerDay) + ms;
1141 if (!inputIsUTC) { // convert to UTC
1142 double utcOffset = getUTCOffset(exec);
1143 result -= utcOffset;
1144 result -= getDSTOffset(exec, result, utcOffset);
1151 void msToGregorianDateTime(ExecState* exec, double ms, bool outputIsUTC, GregorianDateTime& tm)
1153 double dstOff = 0.0;
1154 double utcOff = 0.0;
1156 utcOff = getUTCOffset(exec);
1157 dstOff = getDSTOffset(exec, ms, utcOff);
1158 ms += dstOff + utcOff;
1161 const int year = msToYear(ms);
1162 tm.second = msToSeconds(ms);
1163 tm.minute = msToMinutes(ms);
1164 tm.hour = msToHours(ms);
1165 tm.weekDay = msToWeekDay(ms);
1166 tm.yearDay = dayInYear(ms, year);
1167 tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year));
1168 tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year));
1169 tm.year = year - 1900;
1170 tm.isDST = dstOff != 0.0;
1171 tm.utcOffset = static_cast<long>((dstOff + utcOff) / WTF::msPerSecond);
1172 tm.timeZone = nullptr;
1175 double parseDateFromNullTerminatedCharacters(ExecState* exec, const char* dateString)
1180 double ms = WTF::parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
1182 return std::numeric_limits<double>::quiet_NaN();
1184 // fall back to local timezone
1186 double utcOffset = getUTCOffset(exec);
1187 double dstOffset = getDSTOffset(exec, ms, utcOffset);
1188 offset = static_cast<int>((utcOffset + dstOffset) / WTF::msPerMinute);
1190 return ms - (offset * WTF::msPerMinute);