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
= abs(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 // The year may be after the time but before the time zone, but don't
931 // confuse a time zone specificed as an offset from UTC (e.g. +0100) with a
933 if (year
<= 0 && *dateString
!= '+' && *dateString
!= '-') {
934 if (!parseLong(dateString
, &newPosStr
, 10, &year
))
936 dateString
= newPosStr
;
937 skipSpacesAndComments(dateString
);
940 // Don't fail if the time zone is missing.
941 // Some websites omit the time zone (4275206).
943 if (strncasecmp(dateString
, "GMT", 3) == 0 || strncasecmp(dateString
, "UTC", 3) == 0) {
948 if (*dateString
== '+' || *dateString
== '-') {
950 if (!parseLong(dateString
, &newPosStr
, 10, &o
))
952 dateString
= newPosStr
;
954 if (o
< -9959 || o
> 9959)
957 int sgn
= (o
< 0) ? -1 : 1;
959 if (*dateString
!= ':') {
960 offset
= ((o
/ 100) * 60 + (o
% 100)) * sgn
;
961 } else { // GMT+05:00
963 if (!parseLong(dateString
, &newPosStr
, 10, &o2
))
965 dateString
= newPosStr
;
966 offset
= (o
* 60 + o2
) * sgn
;
970 for (size_t i
= 0; i
< WTF_ARRAY_LENGTH(known_zones
); ++i
) {
971 if (0 == strncasecmp(dateString
, known_zones
[i
].tzName
, strlen(known_zones
[i
].tzName
))) {
972 offset
= known_zones
[i
].tzOffset
;
973 dateString
+= strlen(known_zones
[i
].tzName
);
981 skipSpacesAndComments(dateString
);
983 if (*dateString
&& year
== -1) {
984 if (!parseLong(dateString
, &newPosStr
, 10, &year
))
986 dateString
= newPosStr
;
989 skipSpacesAndComments(dateString
);
995 // Y2K: Handle 2 digit years.
996 if (year
>= 0 && year
< 100) {
1003 return ymdhmsToSeconds(year
, month
+ 1, day
, hour
, minute
, second
) * msPerSecond
;
1006 double parseDateFromNullTerminatedCharacters(const char* dateString
)
1010 double ms
= parseDateFromNullTerminatedCharacters(dateString
, haveTZ
, offset
);
1014 // fall back to local timezone
1016 double utcOffset
= calculateUTCOffset();
1017 double dstOffset
= calculateDSTOffset(ms
, utcOffset
);
1018 offset
= static_cast<int>((utcOffset
+ dstOffset
) / msPerMinute
);
1020 return ms
- (offset
* msPerMinute
);
1023 double timeClip(double t
)
1027 if (fabs(t
) > maxECMAScriptTime
)
1036 // Get the DST offset for the time passed in.
1038 // NOTE: The implementation relies on the fact that no time zones have
1039 // more than one daylight savings offset change per month.
1040 // If this function is called with NaN it returns NaN.
1041 static double getDSTOffset(ExecState
* exec
, double ms
, double utcOffset
)
1043 DSTOffsetCache
& cache
= exec
->globalData().dstOffsetCache
;
1044 double start
= cache
.start
;
1045 double end
= cache
.end
;
1048 // If the time fits in the cached interval, return the cached offset.
1049 if (ms
<= end
) return cache
.offset
;
1051 // Compute a possible new interval end.
1052 double newEnd
= end
+ cache
.increment
;
1055 double endOffset
= calculateDSTOffset(newEnd
, utcOffset
);
1056 if (cache
.offset
== endOffset
) {
1057 // If the offset at the end of the new interval still matches
1058 // the offset in the cache, we grow the cached time interval
1059 // and return the offset.
1061 cache
.increment
= msPerMonth
;
1064 double offset
= calculateDSTOffset(ms
, utcOffset
);
1065 if (offset
== endOffset
) {
1066 // The offset at the given time is equal to the offset at the
1067 // new end of the interval, so that means that we've just skipped
1068 // the point in time where the DST offset change occurred. Updated
1069 // the interval to reflect this and reset the increment.
1072 cache
.increment
= msPerMonth
;
1074 // The interval contains a DST offset change and the given time is
1075 // before it. Adjust the increment to avoid a linear search for
1076 // the offset change point and change the end of the interval.
1077 cache
.increment
/= 3;
1080 // Update the offset in the cache and return it.
1081 cache
.offset
= offset
;
1087 // Compute the DST offset for the time and shrink the cache interval
1088 // to only contain the time. This allows fast repeated DST offset
1089 // computations for the same time.
1090 double offset
= calculateDSTOffset(ms
, utcOffset
);
1091 cache
.offset
= offset
;
1094 cache
.increment
= msPerMonth
;
1099 * Get the difference in milliseconds between this time zone and UTC (GMT)
1100 * NOT including DST.
1102 double getUTCOffset(ExecState
* exec
)
1104 double utcOffset
= exec
->globalData().cachedUTCOffset
;
1105 if (!isnan(utcOffset
))
1107 exec
->globalData().cachedUTCOffset
= calculateUTCOffset();
1108 return exec
->globalData().cachedUTCOffset
;
1111 double gregorianDateTimeToMS(ExecState
* exec
, const GregorianDateTime
& t
, double milliSeconds
, bool inputIsUTC
)
1113 double day
= dateToDaysFrom1970(t
.year
+ 1900, t
.month
, t
.monthDay
);
1114 double ms
= timeToMS(t
.hour
, t
.minute
, t
.second
, milliSeconds
);
1115 double result
= (day
* WTF::msPerDay
) + ms
;
1117 if (!inputIsUTC
) { // convert to UTC
1118 double utcOffset
= getUTCOffset(exec
);
1119 result
-= utcOffset
;
1120 result
-= getDSTOffset(exec
, result
, utcOffset
);
1127 void msToGregorianDateTime(ExecState
* exec
, double ms
, bool outputIsUTC
, GregorianDateTime
& tm
)
1129 double dstOff
= 0.0;
1130 double utcOff
= 0.0;
1132 utcOff
= getUTCOffset(exec
);
1133 dstOff
= getDSTOffset(exec
, ms
, utcOff
);
1134 ms
+= dstOff
+ utcOff
;
1137 const int year
= msToYear(ms
);
1138 tm
.second
= msToSeconds(ms
);
1139 tm
.minute
= msToMinutes(ms
);
1140 tm
.hour
= msToHours(ms
);
1141 tm
.weekDay
= msToWeekDay(ms
);
1142 tm
.yearDay
= dayInYear(ms
, year
);
1143 tm
.monthDay
= dayInMonthFromDayInYear(tm
.yearDay
, isLeapYear(year
));
1144 tm
.month
= monthFromDayInYear(tm
.yearDay
, isLeapYear(year
));
1145 tm
.year
= year
- 1900;
1146 tm
.isDST
= dstOff
!= 0.0;
1147 tm
.utcOffset
= static_cast<long>((dstOff
+ utcOff
) / WTF::msPerSecond
);
1148 tm
.timeZone
= nullptr;
1151 double parseDateFromNullTerminatedCharacters(ExecState
* exec
, const char* dateString
)
1156 double ms
= WTF::parseDateFromNullTerminatedCharacters(dateString
, haveTZ
, offset
);
1160 // fall back to local timezone
1162 double utcOffset
= getUTCOffset(exec
);
1163 double dstOffset
= getDSTOffset(exec
, ms
, utcOffset
);
1164 offset
= static_cast<int>((utcOffset
+ dstOffset
) / WTF::msPerMinute
);
1166 return ms
- (offset
* WTF::msPerMinute
);