1/*
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)
7 *
8 * The Original Code is Mozilla Communicator client code, released
9 * March 31, 1998.
10 *
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.
15 *
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.
20 *
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.
25 *
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
29 *
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.
43
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
47 * met:
48 *
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.
58 *
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.
70 */
71
72#include "config.h"
73#include "DateMath.h"
74
75#include "Assertions.h"
76#include "ASCIICType.h"
77#include "CurrentTime.h"
78#if USE(JSC)
79#include "JSObject.h"
80#endif
81#include "MathExtras.h"
82#if USE(JSC)
83#include "ScopeChain.h"
84#endif
85#include "StdLibExtras.h"
86#include "StringExtras.h"
87
88#include <algorithm>
89#include <limits.h>
90#include <limits>
91#include <stdint.h>
92#include <time.h>
93
94
95#if HAVE(ERRNO_H)
96#include <errno.h>
97#endif
98
99#if OS(WINCE)
100extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t);
101extern "C" struct tm * localtime(const time_t *timer);
102#endif
103
104#if HAVE(SYS_TIME_H)
105#include <sys/time.h>
106#endif
107
108#if HAVE(SYS_TIMEB_H)
109#include <sys/timeb.h>
110#endif
111
112#if USE(JSC)
113#include "CallFrame.h"
114#endif
115
116#define NaN std::numeric_limits<double>::quiet_NaN()
117
118using namespace WTF;
119
120namespace WTF {
121
122/* Constants */
123
124static const double minutesPerDay = 24.0 * 60.0;
125static const double secondsPerDay = 24.0 * 60.0 * 60.0;
126static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0;
127
128static const double usecPerSec = 1000000.0;
129
130static 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.
134static const double maxECMAScriptTime = 8.64E15;
135
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.
138static 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}
141};
142
143static inline bool isLeapYear(int year)
144{
145    if (year % 4 != 0)
146        return false;
147    if (year % 400 == 0)
148        return true;
149    if (year % 100 == 0)
150        return false;
151    return true;
152}
153
154static inline int daysInYear(int year)
155{
156    return 365 + isLeapYear(year);
157}
158
159static inline double daysFrom1970ToYear(int year)
160{
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.
165
166    static const int leapDaysBefore1971By4Rule = 1970 / 4;
167    static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100;
168    static const int leapDaysBefore1971By400Rule = 1970 / 400;
169
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;
174
175    return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;
176}
177
178static inline double msToDays(double ms)
179{
180    return floor(ms / msPerDay);
181}
182
183int msToYear(double ms)
184{
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;
191    return approxYear;
192}
193
194int dayInYear(double ms, int year)
195{
196    return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
197}
198
199static inline double msToMilliseconds(double ms)
200{
201    double result = fmod(ms, msPerDay);
202    if (result < 0)
203        result += msPerDay;
204    return result;
205}
206
207// 0: Sunday, 1: Monday, etc.
208static inline int msToWeekDay(double ms)
209{
210    int wd = (static_cast<int>(msToDays(ms)) + 4) % 7;
211    if (wd < 0)
212        wd += 7;
213    return wd;
214}
215
216static inline int msToSeconds(double ms)
217{
218    double result = fmod(floor(ms / msPerSecond), secondsPerMinute);
219    if (result < 0)
220        result += secondsPerMinute;
221    return static_cast<int>(result);
222}
223
224static inline int msToMinutes(double ms)
225{
226    double result = fmod(floor(ms / msPerMinute), minutesPerHour);
227    if (result < 0)
228        result += minutesPerHour;
229    return static_cast<int>(result);
230}
231
232static inline int msToHours(double ms)
233{
234    double result = fmod(floor(ms/msPerHour), hoursPerDay);
235    if (result < 0)
236        result += hoursPerDay;
237    return static_cast<int>(result);
238}
239
240int monthFromDayInYear(int dayInYear, bool leapYear)
241{
242    const int d = dayInYear;
243    int step;
244
245    if (d < (step = 31))
246        return 0;
247    step += (leapYear ? 29 : 28);
248    if (d < step)
249        return 1;
250    if (d < (step += 31))
251        return 2;
252    if (d < (step += 30))
253        return 3;
254    if (d < (step += 31))
255        return 4;
256    if (d < (step += 30))
257        return 5;
258    if (d < (step += 31))
259        return 6;
260    if (d < (step += 31))
261        return 7;
262    if (d < (step += 30))
263        return 8;
264    if (d < (step += 31))
265        return 9;
266    if (d < (step += 30))
267        return 10;
268    return 11;
269}
270
271static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth)
272{
273    startDayOfThisMonth = startDayOfNextMonth;
274    startDayOfNextMonth += daysInThisMonth;
275    return (dayInYear <= startDayOfNextMonth);
276}
277
278int dayInMonthFromDayInYear(int dayInYear, bool leapYear)
279{
280    const int d = dayInYear;
281    int step;
282    int next = 30;
283
284    if (d <= next)
285        return d + 1;
286    const int daysInFeb = (leapYear ? 29 : 28);
287    if (checkMonth(d, step, next, daysInFeb))
288        return d - step;
289    if (checkMonth(d, step, next, 31))
290        return d - step;
291    if (checkMonth(d, step, next, 30))
292        return d - step;
293    if (checkMonth(d, step, next, 31))
294        return d - step;
295    if (checkMonth(d, step, next, 30))
296        return d - step;
297    if (checkMonth(d, step, next, 31))
298        return d - step;
299    if (checkMonth(d, step, next, 31))
300        return d - step;
301    if (checkMonth(d, step, next, 30))
302        return d - step;
303    if (checkMonth(d, step, next, 31))
304        return d - step;
305    if (checkMonth(d, step, next, 30))
306        return d - step;
307    step = next;
308    return d - step;
309}
310
311static inline int monthToDayInYear(int month, bool isLeapYear)
312{
313    return firstDayOfMonth[isLeapYear][month];
314}
315
316static inline double timeToMS(double hour, double min, double sec, double ms)
317{
318    return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms);
319}
320
321double dateToDaysFrom1970(int year, int month, int day)
322{
323    year += month / 12;
324
325    month %= 12;
326    if (month < 0) {
327        month += 12;
328        --year;
329    }
330
331    double yearday = floor(daysFrom1970ToYear(year));
332    ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0));
333    int monthday = monthToDayInYear(month, isLeapYear(year));
334
335    return yearday + monthday + day - 1;
336}
337
338// There is a hard limit at 2038 that we currently do not have a workaround
339// for (rdar://problem/5052975).
340static inline int maximumYearForDST()
341{
342    return 2037;
343}
344
345static inline int minimumYearForDST()
346{
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
350    // can map to.
351    return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ;
352}
353
354/*
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.
358 *
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).
363 */
364int equivalentYearForDST(int year)
365{
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
368    // to be restarted.
369    static int minYear = minimumYearForDST();
370    int maxYear = maximumYearForDST();
371
372    int difference;
373    if (year > maxYear)
374        difference = minYear - year;
375    else if (year < minYear)
376        difference = maxYear - year;
377    else
378        return year;
379
380    int quotient = difference / 28;
381    int product = (quotient) * 28;
382
383    year += product;
384    ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(NaN)));
385    return year;
386}
387
388int32_t calculateUTCOffset()
389{
390#if PLATFORM(BREWMP)
391    time_t localTime = static_cast<time_t>(currentTime());
392#else
393    time_t localTime = time(0);
394#endif
395    tm localt;
396    getLocalTime(&localTime, &localt);
397
398    // Get the difference between this time zone and UTC on the 1st of January of this year.
399    localt.tm_sec = 0;
400    localt.tm_min = 0;
401    localt.tm_hour = 0;
402    localt.tm_mday = 1;
403    localt.tm_mon = 0;
404    // Not setting localt.tm_year!
405    localt.tm_wday = 0;
406    localt.tm_yday = 0;
407    localt.tm_isdst = 0;
408#if HAVE(TM_GMTOFF)
409    localt.tm_gmtoff = 0;
410#endif
411#if HAVE(TM_ZONE)
412    localt.tm_zone = 0;
413#endif
414
415#if HAVE(TIMEGM)
416    time_t utcOffset = timegm(&localt) - mktime(&localt);
417#else
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);
421#endif
422
423    return static_cast<int32_t>(utcOffset * 1000);
424}
425
426/*
427 * Get the DST offset for the time passed in.
428 */
429static double calculateDSTOffsetSimple(double localTimeSeconds, double utcOffset)
430{
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;
435
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;
438
439    // Offset from UTC but doesn't include DST obviously
440    int offsetHour =  msToHours(offsetTime);
441    int offsetMinute =  msToMinutes(offsetTime);
442
443    // FIXME: time_t has a potential problem in 2038
444    time_t localTime = static_cast<time_t>(localTimeSeconds);
445
446    tm localTM;
447    getLocalTime(&localTime, &localTM);
448
449    double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60);
450
451    if (diff < 0)
452        diff += secondsPerDay;
453
454    return (diff * msPerSecond);
455}
456
457// Get the DST offset, given a time in UTC
458double calculateDSTOffset(double ms, double utcOffset)
459{
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);
474    }
475
476    return calculateDSTOffsetSimple(ms / msPerSecond, utcOffset);
477}
478
479void initializeDates()
480{
481#ifndef NDEBUG
482    static bool alreadyInitialized;
483    ASSERT(!alreadyInitialized);
484    alreadyInitialized = true;
485#endif
486
487    equivalentYearForDST(2000); // Need to call once to initialize a static used in this function.
488}
489
490static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second)
491{
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)
496        - 2440588;
497    return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
498}
499
500// We follow the recommendation of RFC 2822 to consider all
501// obsolete time zones not listed here equivalent to "-0000".
502static const struct KnownZone {
503#if !OS(WINDOWS)
504    const
505#endif
506        char tzName[4];
507    int tzOffset;
508} known_zones[] = {
509    { "UT", 0 },
510    { "GMT", 0 },
511    { "EST", -300 },
512    { "EDT", -240 },
513    { "CST", -360 },
514    { "CDT", -300 },
515    { "MST", -420 },
516    { "MDT", -360 },
517    { "PST", -480 },
518    { "PDT", -420 }
519};
520
521inline static void skipSpacesAndComments(const char*& s)
522{
523    int nesting = 0;
524    char ch;
525    while ((ch = *s)) {
526        if (!isASCIISpace(ch)) {
527            if (ch == '(')
528                nesting++;
529            else if (ch == ')' && nesting > 0)
530                nesting--;
531            else if (nesting == 0)
532                break;
533        }
534        s++;
535    }
536}
537
538// returns 0-11 (Jan-Dec); -1 on failure
539static int findMonth(const char* monthStr)
540{
541    ASSERT(monthStr);
542    char needle[4];
543    for (int i = 0; i < 3; ++i) {
544        if (!*monthStr)
545            return -1;
546        needle[i] = static_cast<char>(toASCIILower(*monthStr++));
547    }
548    needle[3] = '\0';
549    const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
550    const char *str = strstr(haystack, needle);
551    if (str) {
552        int position = static_cast<int>(str - haystack);
553        if (position % 3 == 0)
554            return position / 3;
555    }
556    return -1;
557}
558
559static bool parseLong(const char* string, char** stopPosition, int base, long* result)
560{
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)
564        return false;
565    return true;
566}
567
568double parseES5DateFromNullTerminatedCharacters(const char* dateString)
569{
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).
573
574    static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
575
576    const char* currentPosition = dateString;
577    char* postParsePosition;
578
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.
582    long year;
583    if (!parseLong(currentPosition, &postParsePosition, 10, &year))
584        return NaN;
585    if (*postParsePosition != '-')
586        return NaN;
587    currentPosition = postParsePosition + 1;
588
589    long month;
590    if (!isASCIIDigit(*currentPosition))
591        return NaN;
592    if (!parseLong(currentPosition, &postParsePosition, 10, &month))
593        return NaN;
594    if (*postParsePosition != '-' || (postParsePosition - currentPosition) != 2)
595        return NaN;
596    currentPosition = postParsePosition + 1;
597
598    long day;
599    if (!isASCIIDigit(*currentPosition))
600        return NaN;
601    if (!parseLong(currentPosition, &postParsePosition, 10, &day))
602        return NaN;
603    if (*postParsePosition != 'T' || (postParsePosition - currentPosition) != 2)
604        return NaN;
605    currentPosition = postParsePosition + 1;
606
607    long hours;
608    if (!isASCIIDigit(*currentPosition))
609        return NaN;
610    if (!parseLong(currentPosition, &postParsePosition, 10, &hours))
611        return NaN;
612    if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
613        return NaN;
614    currentPosition = postParsePosition + 1;
615
616    long minutes;
617    if (!isASCIIDigit(*currentPosition))
618        return NaN;
619    if (!parseLong(currentPosition, &postParsePosition, 10, &minutes))
620        return NaN;
621    if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
622        return NaN;
623    currentPosition = postParsePosition + 1;
624
625    long intSeconds;
626    if (!isASCIIDigit(*currentPosition))
627        return NaN;
628    if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds))
629        return NaN;
630    if ((postParsePosition - currentPosition) != 2)
631        return NaN;
632
633    double seconds = intSeconds;
634    if (*postParsePosition == '.') {
635        currentPosition = postParsePosition + 1;
636
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))
641            return NaN;
642
643        // We are more lenient than ES5 by accepting more or less than 3 fraction digits.
644        long fracSeconds;
645        if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds))
646            return NaN;
647
648        long numFracDigits = postParsePosition - currentPosition;
649        seconds += fracSeconds * pow(10.0, static_cast<double>(-numFracDigits));
650    }
651    currentPosition = postParsePosition;
652
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)
656        return NaN;
657    if (day < 1 || day > daysPerMonth[month - 1])
658        return NaN;
659    if (month == 2 && day > 28 && !isLeapYear(year))
660        return NaN;
661    if (hours < 0 || hours > 24)
662        return NaN;
663    if (hours == 24 && (minutes || seconds))
664        return NaN;
665    if (minutes < 0 || minutes > 59)
666        return NaN;
667    if (seconds < 0 || seconds >= 61)
668        return NaN;
669    if (seconds > 60) {
670        // Discard leap seconds by clamping to the end of a minute.
671        seconds = 60;
672    }
673
674    long timeZoneSeconds = 0;
675    if (*currentPosition != 'Z') {
676        bool tzNegative;
677        if (*currentPosition == '-')
678            tzNegative = true;
679        else if (*currentPosition == '+')
680            tzNegative = false;
681        else
682            return NaN;
683        currentPosition += 1;
684
685        long tzHours;
686        long tzHoursAbs;
687        long tzMinutes;
688
689        if (!isASCIIDigit(*currentPosition))
690            return NaN;
691        if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours))
692            return NaN;
693        if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
694            return NaN;
695        tzHoursAbs = abs(tzHours);
696        currentPosition = postParsePosition + 1;
697
698        if (!isASCIIDigit(*currentPosition))
699            return NaN;
700        if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes))
701            return NaN;
702        if ((postParsePosition - currentPosition) != 2)
703            return NaN;
704        currentPosition = postParsePosition;
705
706        if (tzHoursAbs > 24)
707            return NaN;
708        if (tzMinutes < 0 || tzMinutes > 59)
709            return NaN;
710
711        timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs));
712        if (tzNegative)
713            timeZoneSeconds = -timeZoneSeconds;
714    } else {
715        currentPosition += 1;
716    }
717    if (*currentPosition)
718        return NaN;
719
720    double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds;
721    return dateSeconds * msPerSecond;
722}
723
724// Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore.
725static double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset)
726{
727    haveTZ = false;
728    offset = 0;
729
730    // This parses a date in the form:
731    //     Tuesday, 09-Nov-99 23:12:40 GMT
732    // or
733    //     Sat, 01-Jan-2000 08:00:00 GMT
734    // or
735    //     Sat, 01 Jan 2000 08:00:00 GMT
736    // or
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
741    //
742    // We ignore the weekday.
743
744    // Skip leading space
745    skipSpacesAndComments(dateString);
746
747    long month = -1;
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;
756        } else
757           dateString++;
758    }
759
760    // Missing delimiter between month and day (like "January29")?
761    if (month == -1 && wordStart != dateString)
762        month = findMonth(wordStart);
763
764    skipSpacesAndComments(dateString);
765
766    if (!*dateString)
767        return NaN;
768
769    // ' 09-Nov-99 23:12:40 GMT'
770    char* newPosStr;
771    long day;
772    if (!parseLong(dateString, &newPosStr, 10, &day))
773        return NaN;
774    dateString = newPosStr;
775
776    if (!*dateString)
777        return NaN;
778
779    if (day < 0)
780        return NaN;
781
782    long year = 0;
783    if (day > 31) {
784        // ### where is the boundary and what happens below?
785        if (*dateString != '/')
786            return NaN;
787        // looks like a YYYY/MM/DD date
788        if (!*++dateString)
789            return NaN;
790        year = day;
791        if (!parseLong(dateString, &newPosStr, 10, &month))
792            return NaN;
793        month -= 1;
794        dateString = newPosStr;
795        if (*dateString++ != '/' || !*dateString)
796            return NaN;
797        if (!parseLong(dateString, &newPosStr, 10, &day))
798            return NaN;
799        dateString = newPosStr;
800    } else if (*dateString == '/' && month == -1) {
801        dateString++;
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))
805            return NaN;
806        if (day < 1 || day > 31)
807            return NaN;
808        dateString = newPosStr;
809        if (*dateString == '/')
810            dateString++;
811        if (!*dateString)
812            return NaN;
813     } else {
814        if (*dateString == '-')
815            dateString++;
816
817        skipSpacesAndComments(dateString);
818
819        if (*dateString == ',')
820            dateString++;
821
822        if (month == -1) { // not found yet
823            month = findMonth(dateString);
824            if (month == -1)
825                return NaN;
826
827            while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString))
828                dateString++;
829
830            if (!*dateString)
831                return NaN;
832
833            // '-99 23:12:40 GMT'
834            if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString))
835                return NaN;
836            dateString++;
837        }
838    }
839
840    if (month < 0 || month > 11)
841        return NaN;
842
843    // '99 23:12:40 GMT'
844    if (year <= 0 && *dateString) {
845        if (!parseLong(dateString, &newPosStr, 10, &year))
846            return NaN;
847    }
848
849    // Don't fail if the time is missing.
850    long hour = 0;
851    long minute = 0;
852    long second = 0;
853    if (!*newPosStr)
854        dateString = newPosStr;
855    else {
856        // ' 23:12:40 GMT'
857        if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) {
858            if (*newPosStr != ':')
859                return NaN;
860            // There was no year; the number was the hour.
861            year = -1;
862        } else {
863            // in the normal case (we parsed the year), advance to the next number
864            dateString = ++newPosStr;
865            skipSpacesAndComments(dateString);
866        }
867
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
871        // for the timezone!
872
873        // Read a number? If not, this might be a timezone name.
874        if (newPosStr != dateString) {
875            dateString = newPosStr;
876
877            if (hour < 0 || hour > 23)
878                return NaN;
879
880            if (!*dateString)
881                return NaN;
882
883            // ':12:40 GMT'
884            if (*dateString++ != ':')
885                return NaN;
886
887            if (!parseLong(dateString, &newPosStr, 10, &minute))
888                return NaN;
889            dateString = newPosStr;
890
891            if (minute < 0 || minute > 59)
892                return NaN;
893
894            // ':40 GMT'
895            if (*dateString && *dateString != ':' && !isASCIISpace(*dateString))
896                return NaN;
897
898            // seconds are optional in rfc822 + rfc2822
899            if (*dateString ==':') {
900                dateString++;
901
902                if (!parseLong(dateString, &newPosStr, 10, &second))
903                    return NaN;
904                dateString = newPosStr;
905
906                if (second < 0 || second > 59)
907                    return NaN;
908            }
909
910            skipSpacesAndComments(dateString);
911
912            if (strncasecmp(dateString, "AM", 2) == 0) {
913                if (hour > 12)
914                    return NaN;
915                if (hour == 12)
916                    hour = 0;
917                dateString += 2;
918                skipSpacesAndComments(dateString);
919            } else if (strncasecmp(dateString, "PM", 2) == 0) {
920                if (hour > 12)
921                    return NaN;
922                if (hour != 12)
923                    hour += 12;
924                dateString += 2;
925                skipSpacesAndComments(dateString);
926            }
927        }
928    }
929
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
932    // four-digit year.
933    if (year <= 0 && *dateString != '+' && *dateString != '-') {
934       if (!parseLong(dateString, &newPosStr, 10, &year))
935          year = 0;
936       dateString = newPosStr;
937       skipSpacesAndComments(dateString);
938    }
939
940    // Don't fail if the time zone is missing.
941    // Some websites omit the time zone (4275206).
942    if (*dateString) {
943        if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
944            dateString += 3;
945            haveTZ = true;
946        }
947
948        if (*dateString == '+' || *dateString == '-') {
949            long o;
950            if (!parseLong(dateString, &newPosStr, 10, &o))
951                return NaN;
952            dateString = newPosStr;
953
954            if (o < -9959 || o > 9959)
955                return NaN;
956
957            int sgn = (o < 0) ? -1 : 1;
958            o = labs(o);
959            if (*dateString != ':') {
960                offset = ((o / 100) * 60 + (o % 100)) * sgn;
961            } else { // GMT+05:00
962                long o2;
963                if (!parseLong(dateString, &newPosStr, 10, &o2))
964                    return NaN;
965                dateString = newPosStr;
966                offset = (o * 60 + o2) * sgn;
967            }
968            haveTZ = true;
969        } else {
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);
974                    haveTZ = true;
975                    break;
976                }
977            }
978        }
979    }
980
981    skipSpacesAndComments(dateString);
982
983    if (*dateString && year == -1) {
984        if (!parseLong(dateString, &newPosStr, 10, &year))
985            return NaN;
986        dateString = newPosStr;
987    }
988
989    skipSpacesAndComments(dateString);
990
991    // Trailing garbage
992    if (*dateString)
993        return NaN;
994
995    // Y2K: Handle 2 digit years.
996    if (year >= 0 && year < 100) {
997        if (year < 50)
998            year += 2000;
999        else
1000            year += 1900;
1001    }
1002
1003    return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond;
1004}
1005
1006double parseDateFromNullTerminatedCharacters(const char* dateString)
1007{
1008    bool haveTZ;
1009    int offset;
1010    double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
1011    if (isnan(ms))
1012        return NaN;
1013
1014    // fall back to local timezone
1015    if (!haveTZ) {
1016        double utcOffset = calculateUTCOffset();
1017        double dstOffset = calculateDSTOffset(ms, utcOffset);
1018        offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute);
1019    }
1020    return ms - (offset * msPerMinute);
1021}
1022
1023double timeClip(double t)
1024{
1025    if (!isfinite(t))
1026        return NaN;
1027    if (fabs(t) > maxECMAScriptTime)
1028        return NaN;
1029    return trunc(t);
1030}
1031} // namespace WTF
1032
1033#if USE(JSC)
1034namespace JSC {
1035
1036// Get the DST offset for the time passed in.
1037//
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.
1041static double getDSTOffset(ExecState* exec, double ms, double utcOffset)
1042{
1043    DSTOffsetCache& cache = exec->globalData().dstOffsetCache;
1044    double start = cache.start;
1045    double end = cache.end;
1046
1047    if (start <= ms) {
1048        // If the time fits in the cached interval, return the cached offset.
1049        if (ms <= end) return cache.offset;
1050
1051        // Compute a possible new interval end.
1052        double newEnd = end + cache.increment;
1053
1054        if (ms <= newEnd) {
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.
1060                cache.end = newEnd;
1061                cache.increment = msPerMonth;
1062                return endOffset;
1063            } else {
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.
1070                    cache.start = ms;
1071                    cache.end = newEnd;
1072                    cache.increment = msPerMonth;
1073                } else {
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;
1078                    cache.end = ms;
1079                }
1080                // Update the offset in the cache and return it.
1081                cache.offset = offset;
1082                return offset;
1083            }
1084        }
1085    }
1086
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;
1092    cache.start = ms;
1093    cache.end = ms;
1094    cache.increment = msPerMonth;
1095    return offset;
1096}
1097
1098/*
1099 * Get the difference in milliseconds between this time zone and UTC (GMT)
1100 * NOT including DST.
1101 */
1102double getUTCOffset(ExecState* exec)
1103{
1104    double utcOffset = exec->globalData().cachedUTCOffset;
1105    if (!isnan(utcOffset))
1106        return utcOffset;
1107    exec->globalData().cachedUTCOffset = calculateUTCOffset();
1108    return exec->globalData().cachedUTCOffset;
1109}
1110
1111double gregorianDateTimeToMS(ExecState* exec, const GregorianDateTime& t, double milliSeconds, bool inputIsUTC)
1112{
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;
1116
1117    if (!inputIsUTC) { // convert to UTC
1118        double utcOffset = getUTCOffset(exec);
1119        result -= utcOffset;
1120        result -= getDSTOffset(exec, result, utcOffset);
1121    }
1122
1123    return result;
1124}
1125
1126// input is UTC
1127void msToGregorianDateTime(ExecState* exec, double ms, bool outputIsUTC, GregorianDateTime& tm)
1128{
1129    double dstOff = 0.0;
1130    double utcOff = 0.0;
1131    if (!outputIsUTC) {
1132        utcOff = getUTCOffset(exec);
1133        dstOff = getDSTOffset(exec, ms, utcOff);
1134        ms += dstOff + utcOff;
1135    }
1136
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;
1149}
1150
1151double parseDateFromNullTerminatedCharacters(ExecState* exec, const char* dateString)
1152{
1153    ASSERT(exec);
1154    bool haveTZ;
1155    int offset;
1156    double ms = WTF::parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
1157    if (isnan(ms))
1158        return NaN;
1159
1160    // fall back to local timezone
1161    if (!haveTZ) {
1162        double utcOffset = getUTCOffset(exec);
1163        double dstOffset = getDSTOffset(exec, ms, utcOffset);
1164        offset = static_cast<int>((utcOffset + dstOffset) / WTF::msPerMinute);
1165    }
1166    return ms - (offset * WTF::msPerMinute);
1167}
1168
1169} // namespace JSC
1170#endif // USE(JSC)
1171