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"
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"
116 #define NaN std::numeric_limits<double>::quiet_NaN()
124 static const double minutesPerDay = 24.0 * 60.0;
125 static const double secondsPerDay = 24.0 * 60.0 * 60.0;
126 static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0;
128 static const double usecPerSec = 1000000.0;
130 static const double maxUnixTime = 2145859200.0; // 12/31/2037
131 // ECMAScript asks not to support for a date of which total
132 // millisecond value is larger than the following value.
133 // See 15.9.1.14 of ECMA-262 5th edition.
134 static const double maxECMAScriptTime = 8.64E15;
136 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1.
137 // First for non-leap years, then for leap years.
138 static const int firstDayOfMonth[2][12] = {
139 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
140 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
143 static inline bool isLeapYear(int year)
154 static inline int daysInYear(int year)
156 return 365 + isLeapYear(year);
159 static inline double daysFrom1970ToYear(int year)
161 // The Gregorian Calendar rules for leap years:
162 // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.
163 // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years.
164 // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years.
166 static const int leapDaysBefore1971By4Rule = 1970 / 4;
167 static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100;
168 static const int leapDaysBefore1971By400Rule = 1970 / 400;
170 const double yearMinusOne = year - 1;
171 const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule;
172 const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule;
173 const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule;
175 return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;
178 static inline double msToDays(double ms)
180 return floor(ms / msPerDay);
183 int msToYear(double ms)
185 int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970);
186 double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear);
187 if (msFromApproxYearTo1970 > ms)
188 return approxYear - 1;
189 if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms)
190 return approxYear + 1;
194 int dayInYear(double ms, int year)
196 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
199 static inline double msToMilliseconds(double ms)
201 double result = fmod(ms, msPerDay);
207 // 0: Sunday, 1: Monday, etc.
208 static inline int msToWeekDay(double ms)
210 int wd = (static_cast<int>(msToDays(ms)) + 4) % 7;
216 static inline int msToSeconds(double ms)
218 double result = fmod(floor(ms / msPerSecond), secondsPerMinute);
220 result += secondsPerMinute;
221 return static_cast<int>(result);
224 static inline int msToMinutes(double ms)
226 double result = fmod(floor(ms / msPerMinute), minutesPerHour);
228 result += minutesPerHour;
229 return static_cast<int>(result);
232 static inline int msToHours(double ms)
234 double result = fmod(floor(ms/msPerHour), hoursPerDay);
236 result += hoursPerDay;
237 return static_cast<int>(result);
240 int monthFromDayInYear(int dayInYear, bool leapYear)
242 const int d = dayInYear;
247 step += (leapYear ? 29 : 28);
250 if (d < (step += 31))
252 if (d < (step += 30))
254 if (d < (step += 31))
256 if (d < (step += 30))
258 if (d < (step += 31))
260 if (d < (step += 31))
262 if (d < (step += 30))
264 if (d < (step += 31))
266 if (d < (step += 30))
271 static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth)
273 startDayOfThisMonth = startDayOfNextMonth;
274 startDayOfNextMonth += daysInThisMonth;
275 return (dayInYear <= startDayOfNextMonth);
278 int dayInMonthFromDayInYear(int dayInYear, bool leapYear)
280 const int d = dayInYear;
286 const int daysInFeb = (leapYear ? 29 : 28);
287 if (checkMonth(d, step, next, daysInFeb))
289 if (checkMonth(d, step, next, 31))
291 if (checkMonth(d, step, next, 30))
293 if (checkMonth(d, step, next, 31))
295 if (checkMonth(d, step, next, 30))
297 if (checkMonth(d, step, next, 31))
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, 30))
311 static inline int monthToDayInYear(int month, bool isLeapYear)
313 return firstDayOfMonth[isLeapYear][month];
316 static inline double timeToMS(double hour, double min, double sec, double ms)
318 return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms);
321 double dateToDaysFrom1970(int year, int month, int day)
331 double yearday = floor(daysFrom1970ToYear(year));
332 ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0));
333 int monthday = monthToDayInYear(month, isLeapYear(year));
335 return yearday + monthday + day - 1;
338 // There is a hard limit at 2038 that we currently do not have a workaround
339 // for (rdar://problem/5052975).
340 static inline int maximumYearForDST()
345 static inline int minimumYearForDST()
347 // Because of the 2038 issue (see maximumYearForDST) if the current year is
348 // greater than the max year minus 27 (2010), we want to use the max year
349 // minus 27 instead, to ensure there is a range of 28 years that all years
351 return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ;
355 * Find an equivalent year for the one given, where equivalence is deterined by
356 * the two years having the same leapness and the first day of the year, falling
357 * on the same day of the week.
359 * This function returns a year between this current year and 2037, however this
360 * function will potentially return incorrect results if the current year is after
361 * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after
362 * 2100, (rdar://problem/5055038).
364 int equivalentYearForDST(int year)
366 // It is ok if the cached year is not the current year as long as the rules
367 // for DST did not change between the two years; if they did the app would need
369 static int minYear = minimumYearForDST();
370 int maxYear = maximumYearForDST();
374 difference = minYear - year;
375 else if (year < minYear)
376 difference = maxYear - year;
380 int quotient = difference / 28;
381 int product = (quotient) * 28;
384 ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(NaN)));
388 int32_t calculateUTCOffset()
391 time_t localTime = static_cast<time_t>(currentTime());
393 time_t localTime = time(0);
396 getLocalTime(&localTime, &localt);
398 // Get the difference between this time zone and UTC on the 1st of January of this year.
404 // Not setting localt.tm_year!
409 localt.tm_gmtoff = 0;
416 time_t utcOffset = timegm(&localt) - mktime(&localt);
418 // Using a canned date of 01/01/2009 on platforms with weaker date-handling foo.
419 localt.tm_year = 109;
420 time_t utcOffset = 1230768000 - mktime(&localt);
423 return static_cast<int32_t>(utcOffset * 1000);
427 * Get the DST offset for the time passed in.
429 static double calculateDSTOffsetSimple(double localTimeSeconds, double utcOffset)
431 if (localTimeSeconds > maxUnixTime)
432 localTimeSeconds = maxUnixTime;
433 else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0)
434 localTimeSeconds += secondsPerDay;
436 //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset()
437 double offsetTime = (localTimeSeconds * msPerSecond) + utcOffset;
439 // Offset from UTC but doesn't include DST obviously
440 int offsetHour = msToHours(offsetTime);
441 int offsetMinute = msToMinutes(offsetTime);
443 // FIXME: time_t has a potential problem in 2038
444 time_t localTime = static_cast<time_t>(localTimeSeconds);
447 getLocalTime(&localTime, &localTM);
449 double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60);
452 diff += secondsPerDay;
454 return (diff * msPerSecond);
457 // Get the DST offset, given a time in UTC
458 double calculateDSTOffset(double ms, double utcOffset)
460 // On Mac OS X, the call to localtime (see calculateDSTOffsetSimple) will return historically accurate
461 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
462 // standard explicitly dictates that historical information should not be considered when
463 // determining DST. For this reason we shift away from years that localtime can handle but would
464 // return historically accurate information.
465 int year = msToYear(ms);
466 int equivalentYear = equivalentYearForDST(year);
467 if (year != equivalentYear) {
468 bool leapYear = isLeapYear(year);
469 int dayInYearLocal = dayInYear(ms, year);
470 int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear);
471 int month = monthFromDayInYear(dayInYearLocal, leapYear);
472 double day = dateToDaysFrom1970(equivalentYear, month, dayInMonth);
473 ms = (day * msPerDay) + msToMilliseconds(ms);
476 return calculateDSTOffsetSimple(ms / msPerSecond, utcOffset);
479 void initializeDates()
482 static bool alreadyInitialized;
483 ASSERT(!alreadyInitialized);
484 alreadyInitialized = true;
487 equivalentYearForDST(2000); // Need to call once to initialize a static used in this function.
490 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second)
492 double days = (day - 32075)
493 + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4)
494 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
495 - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4)
497 return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
500 // We follow the recommendation of RFC 2822 to consider all
501 // obsolete time zones not listed here equivalent to "-0000".
502 static const struct KnownZone {
521 inline static void skipSpacesAndComments(const char*& s)
526 if (!isASCIISpace(ch)) {
529 else if (ch == ')' && nesting > 0)
531 else if (nesting == 0)
538 // returns 0-11 (Jan-Dec); -1 on failure
539 static int findMonth(const char* monthStr)
543 for (int i = 0; i < 3; ++i) {
546 needle[i] = static_cast<char>(toASCIILower(*monthStr++));
549 const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
550 const char *str = strstr(haystack, needle);
552 int position = static_cast<int>(str - haystack);
553 if (position % 3 == 0)
559 static bool parseLong(const char* string, char** stopPosition, int base, long* result)
561 *result = strtol(string, stopPosition, base);
562 // Avoid the use of errno as it is not available on Windows CE
563 if (string == *stopPosition || *result == LONG_MIN || *result == LONG_MAX)
568 double parseES5DateFromNullTerminatedCharacters(const char* dateString)
570 // This parses a date of the form defined in ECMA-262-5, section 15.9.1.15
571 // (similar to RFC 3339 / ISO 8601: YYYY-MM-DDTHH:mm:ss[.sss]Z).
572 // In most cases it is intentionally strict (e.g. correct field widths, no stray whitespace).
574 static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
576 const char* currentPosition = dateString;
577 char* postParsePosition;
579 // This is a bit more lenient on the year string than ES5 specifies:
580 // instead of restricting to 4 digits (or 6 digits with mandatory +/-),
581 // it accepts any integer value. Consider this an implementation fallback.
583 if (!parseLong(currentPosition, &postParsePosition, 10, &year))
585 if (*postParsePosition != '-')
587 currentPosition = postParsePosition + 1;
590 if (!isASCIIDigit(*currentPosition))
592 if (!parseLong(currentPosition, &postParsePosition, 10, &month))
594 if (*postParsePosition != '-' || (postParsePosition - currentPosition) != 2)
596 currentPosition = postParsePosition + 1;
599 if (!isASCIIDigit(*currentPosition))
601 if (!parseLong(currentPosition, &postParsePosition, 10, &day))
603 if (*postParsePosition != 'T' || (postParsePosition - currentPosition) != 2)
605 currentPosition = postParsePosition + 1;
608 if (!isASCIIDigit(*currentPosition))
610 if (!parseLong(currentPosition, &postParsePosition, 10, &hours))
612 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
614 currentPosition = postParsePosition + 1;
617 if (!isASCIIDigit(*currentPosition))
619 if (!parseLong(currentPosition, &postParsePosition, 10, &minutes))
621 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
623 currentPosition = postParsePosition + 1;
626 if (!isASCIIDigit(*currentPosition))
628 if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds))
630 if ((postParsePosition - currentPosition) != 2)
633 double seconds = intSeconds;
634 if (*postParsePosition == '.') {
635 currentPosition = postParsePosition + 1;
637 // In ECMA-262-5 it's a bit unclear if '.' can be present without milliseconds, but
638 // a reasonable interpretation guided by the given examples and RFC 3339 says "no".
639 // We check the next character to avoid reading +/- timezone hours after an invalid decimal.
640 if (!isASCIIDigit(*currentPosition))
643 // We are more lenient than ES5 by accepting more or less than 3 fraction digits.
645 if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds))
648 long numFracDigits = postParsePosition - currentPosition;
649 seconds += fracSeconds * pow(10.0, static_cast<double>(-numFracDigits));
651 currentPosition = postParsePosition;
653 // A few of these checks could be done inline above, but since many of them are interrelated
654 // we would be sacrificing readability to "optimize" the (presumably less common) failure path.
655 if (month < 1 || month > 12)
657 if (day < 1 || day > daysPerMonth[month - 1])
659 if (month == 2 && day > 28 && !isLeapYear(year))
661 if (hours < 0 || hours > 24)
663 if (hours == 24 && (minutes || seconds))
665 if (minutes < 0 || minutes > 59)
667 if (seconds < 0 || seconds >= 61)
670 // Discard leap seconds by clamping to the end of a minute.
674 long timeZoneSeconds = 0;
675 if (*currentPosition != 'Z') {
677 if (*currentPosition == '-')
679 else if (*currentPosition == '+')
683 currentPosition += 1;
689 if (!isASCIIDigit(*currentPosition))
691 if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours))
693 if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
695 tzHoursAbs = labs(tzHours);
696 currentPosition = postParsePosition + 1;
698 if (!isASCIIDigit(*currentPosition))
700 if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes))
702 if ((postParsePosition - currentPosition) != 2)
704 currentPosition = postParsePosition;
708 if (tzMinutes < 0 || tzMinutes > 59)
711 timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs));
713 timeZoneSeconds = -timeZoneSeconds;
715 currentPosition += 1;
717 if (*currentPosition)
720 double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds;
721 return dateSeconds * msPerSecond;
724 // Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore.
725 static double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset)
730 // This parses a date in the form:
731 // Tuesday, 09-Nov-99 23:12:40 GMT
733 // Sat, 01-Jan-2000 08:00:00 GMT
735 // Sat, 01 Jan 2000 08:00:00 GMT
737 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
738 // ### non RFC formats, added for Javascript:
739 // [Wednesday] January 09 1999 23:12:40 GMT
740 // [Wednesday] January 09 23:12:40 GMT 1999
742 // We ignore the weekday.
744 // Skip leading space
745 skipSpacesAndComments(dateString);
748 const char *wordStart = dateString;
749 // Check contents of first words if not number
750 while (*dateString && !isASCIIDigit(*dateString)) {
751 if (isASCIISpace(*dateString) || *dateString == '(') {
752 if (dateString - wordStart >= 3)
753 month = findMonth(wordStart);
754 skipSpacesAndComments(dateString);
755 wordStart = dateString;
760 // Missing delimiter between month and day (like "January29")?
761 if (month == -1 && wordStart != dateString)
762 month = findMonth(wordStart);
764 skipSpacesAndComments(dateString);
769 // ' 09-Nov-99 23:12:40 GMT'
772 if (!parseLong(dateString, &newPosStr, 10, &day))
774 dateString = newPosStr;
784 // ### where is the boundary and what happens below?
785 if (*dateString != '/')
787 // looks like a YYYY/MM/DD date
791 if (!parseLong(dateString, &newPosStr, 10, &month))
794 dateString = newPosStr;
795 if (*dateString++ != '/' || !*dateString)
797 if (!parseLong(dateString, &newPosStr, 10, &day))
799 dateString = newPosStr;
800 } else if (*dateString == '/' && month == -1) {
802 // This looks like a MM/DD/YYYY date, not an RFC date.
803 month = day - 1; // 0-based
804 if (!parseLong(dateString, &newPosStr, 10, &day))
806 if (day < 1 || day > 31)
808 dateString = newPosStr;
809 if (*dateString == '/')
814 if (*dateString == '-')
817 skipSpacesAndComments(dateString);
819 if (*dateString == ',')
822 if (month == -1) { // not found yet
823 month = findMonth(dateString);
827 while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString))
833 // '-99 23:12:40 GMT'
834 if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString))
840 if (month < 0 || month > 11)
844 if (year <= 0 && *dateString) {
845 if (!parseLong(dateString, &newPosStr, 10, &year))
849 // Don't fail if the time is missing.
854 dateString = newPosStr;
857 if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) {
858 if (*newPosStr != ':')
860 // There was no year; the number was the hour.
863 // in the normal case (we parsed the year), advance to the next number
864 dateString = ++newPosStr;
865 skipSpacesAndComments(dateString);
868 parseLong(dateString, &newPosStr, 10, &hour);
869 // Do not check for errno here since we want to continue
870 // even if errno was set becasue we are still looking
873 // Read a number? If not, this might be a timezone name.
874 if (newPosStr != dateString) {
875 dateString = newPosStr;
877 if (hour < 0 || hour > 23)
884 if (*dateString++ != ':')
887 if (!parseLong(dateString, &newPosStr, 10, &minute))
889 dateString = newPosStr;
891 if (minute < 0 || minute > 59)
895 if (*dateString && *dateString != ':' && !isASCIISpace(*dateString))
898 // seconds are optional in rfc822 + rfc2822
899 if (*dateString ==':') {
902 if (!parseLong(dateString, &newPosStr, 10, &second))
904 dateString = newPosStr;
906 if (second < 0 || second > 59)
910 skipSpacesAndComments(dateString);
912 if (strncasecmp(dateString, "AM", 2) == 0) {
918 skipSpacesAndComments(dateString);
919 } else if (strncasecmp(dateString, "PM", 2) == 0) {
925 skipSpacesAndComments(dateString);
930 // Don't fail if the time zone is missing.
931 // Some websites omit the time zone (4275206).
933 if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
938 if (*dateString == '+' || *dateString == '-') {
940 if (!parseLong(dateString, &newPosStr, 10, &o))
942 dateString = newPosStr;
944 if (o < -9959 || o > 9959)
947 int sgn = (o < 0) ? -1 : 1;
949 if (*dateString != ':') {
950 offset = ((o / 100) * 60 + (o % 100)) * sgn;
951 } else { // GMT+05:00
953 if (!parseLong(dateString, &newPosStr, 10, &o2))
955 dateString = newPosStr;
956 offset = (o * 60 + o2) * sgn;
960 for (size_t i = 0; i < WTF_ARRAY_LENGTH(known_zones); ++i) {
961 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
962 offset = known_zones[i].tzOffset;
963 dateString += strlen(known_zones[i].tzName);
971 skipSpacesAndComments(dateString);
973 if (*dateString && year == -1) {
974 if (!parseLong(dateString, &newPosStr, 10, &year))
976 dateString = newPosStr;
979 skipSpacesAndComments(dateString);
985 // Y2K: Handle 2 digit years.
986 if (year >= 0 && year < 100) {
993 return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond;
996 double parseDateFromNullTerminatedCharacters(const char* dateString)
1000 double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
1004 // fall back to local timezone
1006 double utcOffset = calculateUTCOffset();
1007 double dstOffset = calculateDSTOffset(ms, utcOffset);
1008 offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute);
1010 return ms - (offset * msPerMinute);
1013 double timeClip(double t)
1017 if (fabs(t) > maxECMAScriptTime)
1026 // Get the DST offset for the time passed in.
1028 // NOTE: The implementation relies on the fact that no time zones have
1029 // more than one daylight savings offset change per month.
1030 // If this function is called with NaN it returns NaN.
1031 static double getDSTOffset(ExecState* exec, double ms, double utcOffset)
1033 DSTOffsetCache& cache = exec->globalData().dstOffsetCache;
1034 double start = cache.start;
1035 double end = cache.end;
1038 // If the time fits in the cached interval, return the cached offset.
1039 if (ms <= end) return cache.offset;
1041 // Compute a possible new interval end.
1042 double newEnd = end + cache.increment;
1045 double endOffset = calculateDSTOffset(newEnd, utcOffset);
1046 if (cache.offset == endOffset) {
1047 // If the offset at the end of the new interval still matches
1048 // the offset in the cache, we grow the cached time interval
1049 // and return the offset.
1051 cache.increment = msPerMonth;
1054 double offset = calculateDSTOffset(ms, utcOffset);
1055 if (offset == endOffset) {
1056 // The offset at the given time is equal to the offset at the
1057 // new end of the interval, so that means that we've just skipped
1058 // the point in time where the DST offset change occurred. Updated
1059 // the interval to reflect this and reset the increment.
1062 cache.increment = msPerMonth;
1064 // The interval contains a DST offset change and the given time is
1065 // before it. Adjust the increment to avoid a linear search for
1066 // the offset change point and change the end of the interval.
1067 cache.increment /= 3;
1070 // Update the offset in the cache and return it.
1071 cache.offset = offset;
1077 // Compute the DST offset for the time and shrink the cache interval
1078 // to only contain the time. This allows fast repeated DST offset
1079 // computations for the same time.
1080 double offset = calculateDSTOffset(ms, utcOffset);
1081 cache.offset = offset;
1084 cache.increment = msPerMonth;
1089 * Get the difference in milliseconds between this time zone and UTC (GMT)
1090 * NOT including DST.
1092 double getUTCOffset(ExecState* exec)
1094 double utcOffset = exec->globalData().cachedUTCOffset;
1095 if (!isnan(utcOffset))
1097 exec->globalData().cachedUTCOffset = calculateUTCOffset();
1098 return exec->globalData().cachedUTCOffset;
1101 double gregorianDateTimeToMS(ExecState* exec, const GregorianDateTime& t, double milliSeconds, bool inputIsUTC)
1103 double day = dateToDaysFrom1970(t.year + 1900, t.month, t.monthDay);
1104 double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds);
1105 double result = (day * WTF::msPerDay) + ms;
1107 if (!inputIsUTC) { // convert to UTC
1108 double utcOffset = getUTCOffset(exec);
1109 result -= utcOffset;
1110 result -= getDSTOffset(exec, result, utcOffset);
1117 void msToGregorianDateTime(ExecState* exec, double ms, bool outputIsUTC, GregorianDateTime& tm)
1119 double dstOff = 0.0;
1120 double utcOff = 0.0;
1122 utcOff = getUTCOffset(exec);
1123 dstOff = getDSTOffset(exec, ms, utcOff);
1124 ms += dstOff + utcOff;
1127 const int year = msToYear(ms);
1128 tm.second = msToSeconds(ms);
1129 tm.minute = msToMinutes(ms);
1130 tm.hour = msToHours(ms);
1131 tm.weekDay = msToWeekDay(ms);
1132 tm.yearDay = dayInYear(ms, year);
1133 tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year));
1134 tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year));
1135 tm.year = year - 1900;
1136 tm.isDST = dstOff != 0.0;
1137 tm.utcOffset = static_cast<long>((dstOff + utcOff) / WTF::msPerSecond);
1138 tm.timeZone = nullptr;
1141 double parseDateFromNullTerminatedCharacters(ExecState* exec, const char* dateString)
1146 double ms = WTF::parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
1150 // fall back to local timezone
1152 double utcOffset = getUTCOffset(exec);
1153 double dstOffset = getDSTOffset(exec, ms, utcOffset);
1154 offset = static_cast<int>((utcOffset + dstOffset) / WTF::msPerMinute);
1156 return ms - (offset * WTF::msPerMinute);