1 | /* |
2 | * Copyright (C) 1999-2000 Harri Porten ([email protected]) |
3 | * Copyright (C) 2006-2017 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. ([email protected]) |
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 <wtf/DateMath.h> |
74 | |
75 | #include <algorithm> |
76 | #include <limits.h> |
77 | #include <limits> |
78 | #include <stdint.h> |
79 | #include <time.h> |
80 | #include <wtf/Assertions.h> |
81 | #include <wtf/ASCIICType.h> |
82 | #include <wtf/MathExtras.h> |
83 | #include <wtf/StdLibExtras.h> |
84 | #include <wtf/text/StringBuilder.h> |
85 | |
86 | #if OS(WINDOWS) |
87 | #include <windows.h> |
88 | #endif |
89 | |
90 | #if HAVE(ERRNO_H) |
91 | #include <errno.h> |
92 | #endif |
93 | |
94 | #if HAVE(SYS_TIME_H) |
95 | #include <sys/time.h> |
96 | #endif |
97 | |
98 | #if HAVE(SYS_TIMEB_H) |
99 | #include <sys/timeb.h> |
100 | #endif |
101 | |
102 | namespace WTF { |
103 | |
104 | // FIXME: Should this function go into StringCommon.h or some other header? |
105 | template<unsigned length> inline bool startsWithLettersIgnoringASCIICase(const char* string, const char (&lowercaseLetters)[length]) |
106 | { |
107 | return equalLettersIgnoringASCIICase(string, lowercaseLetters, length - 1); |
108 | } |
109 | |
110 | /* Constants */ |
111 | |
112 | static const double maxUnixTime = 2145859200.0; // 12/31/2037 |
113 | // ECMAScript asks not to support for a date of which total |
114 | // millisecond value is larger than the following value. |
115 | // See 15.9.1.14 of ECMA-262 5th edition. |
116 | static const double maxECMAScriptTime = 8.64E15; |
117 | |
118 | // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1. |
119 | // First for non-leap years, then for leap years. |
120 | static const int firstDayOfMonth[2][12] = { |
121 | {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, |
122 | {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} |
123 | }; |
124 | |
125 | #if !OS(WINDOWS) || HAVE(TM_GMTOFF) |
126 | static inline void getLocalTime(const time_t* localTime, struct tm* localTM) |
127 | { |
128 | #if HAVE(LOCALTIME_R) |
129 | localtime_r(localTime, localTM); |
130 | #else |
131 | localtime_s(localTime, localTM); |
132 | #endif |
133 | } |
134 | #endif |
135 | |
136 | bool isLeapYear(int year) |
137 | { |
138 | if (year % 4 != 0) |
139 | return false; |
140 | if (year % 400 == 0) |
141 | return true; |
142 | if (year % 100 == 0) |
143 | return false; |
144 | return true; |
145 | } |
146 | |
147 | static inline int daysInYear(int year) |
148 | { |
149 | return 365 + isLeapYear(year); |
150 | } |
151 | |
152 | static inline double daysFrom1970ToYear(int year) |
153 | { |
154 | // The Gregorian Calendar rules for leap years: |
155 | // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years. |
156 | // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years. |
157 | // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years. |
158 | |
159 | static const int leapDaysBefore1971By4Rule = 1970 / 4; |
160 | static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100; |
161 | static const int leapDaysBefore1971By400Rule = 1970 / 400; |
162 | |
163 | const double yearMinusOne = year - 1; |
164 | const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule; |
165 | const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule; |
166 | const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule; |
167 | |
168 | return 365.0 * (year - 1970.0) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule; |
169 | } |
170 | |
171 | double msToDays(double ms) |
172 | { |
173 | return floor(ms / msPerDay); |
174 | } |
175 | |
176 | static void appendTwoDigitNumber(StringBuilder& builder, int number) |
177 | { |
178 | ASSERT(number >= 0); |
179 | ASSERT(number < 100); |
180 | builder.append(static_cast<LChar>('0' + number / 10)); |
181 | builder.append(static_cast<LChar>('0' + number % 10)); |
182 | } |
183 | |
184 | int msToYear(double ms) |
185 | { |
186 | int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970); |
187 | double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear); |
188 | if (msFromApproxYearTo1970 > ms) |
189 | return approxYear - 1; |
190 | if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms) |
191 | return approxYear + 1; |
192 | return approxYear; |
193 | } |
194 | |
195 | int dayInYear(double ms, int year) |
196 | { |
197 | return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year)); |
198 | } |
199 | |
200 | static inline double msToMilliseconds(double ms) |
201 | { |
202 | double result = fmod(ms, msPerDay); |
203 | if (result < 0) |
204 | result += msPerDay; |
205 | return result; |
206 | } |
207 | |
208 | int msToMinutes(double ms) |
209 | { |
210 | double result = fmod(floor(ms / msPerMinute), minutesPerHour); |
211 | if (result < 0) |
212 | result += minutesPerHour; |
213 | return static_cast<int>(result); |
214 | } |
215 | |
216 | int msToHours(double ms) |
217 | { |
218 | double result = fmod(floor(ms/msPerHour), hoursPerDay); |
219 | if (result < 0) |
220 | result += hoursPerDay; |
221 | return static_cast<int>(result); |
222 | } |
223 | |
224 | int monthFromDayInYear(int dayInYear, bool leapYear) |
225 | { |
226 | const int d = dayInYear; |
227 | int step; |
228 | |
229 | if (d < (step = 31)) |
230 | return 0; |
231 | step += (leapYear ? 29 : 28); |
232 | if (d < step) |
233 | return 1; |
234 | if (d < (step += 31)) |
235 | return 2; |
236 | if (d < (step += 30)) |
237 | return 3; |
238 | if (d < (step += 31)) |
239 | return 4; |
240 | if (d < (step += 30)) |
241 | return 5; |
242 | if (d < (step += 31)) |
243 | return 6; |
244 | if (d < (step += 31)) |
245 | return 7; |
246 | if (d < (step += 30)) |
247 | return 8; |
248 | if (d < (step += 31)) |
249 | return 9; |
250 | if (d < (step += 30)) |
251 | return 10; |
252 | return 11; |
253 | } |
254 | |
255 | static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth) |
256 | { |
257 | startDayOfThisMonth = startDayOfNextMonth; |
258 | startDayOfNextMonth += daysInThisMonth; |
259 | return (dayInYear <= startDayOfNextMonth); |
260 | } |
261 | |
262 | int dayInMonthFromDayInYear(int dayInYear, bool leapYear) |
263 | { |
264 | const int d = dayInYear; |
265 | int step; |
266 | int next = 30; |
267 | |
268 | if (d <= next) |
269 | return d + 1; |
270 | const int daysInFeb = (leapYear ? 29 : 28); |
271 | if (checkMonth(d, step, next, daysInFeb)) |
272 | return d - step; |
273 | if (checkMonth(d, step, next, 31)) |
274 | return d - step; |
275 | if (checkMonth(d, step, next, 30)) |
276 | return d - step; |
277 | if (checkMonth(d, step, next, 31)) |
278 | return d - step; |
279 | if (checkMonth(d, step, next, 30)) |
280 | return d - step; |
281 | if (checkMonth(d, step, next, 31)) |
282 | return d - step; |
283 | if (checkMonth(d, step, next, 31)) |
284 | return d - step; |
285 | if (checkMonth(d, step, next, 30)) |
286 | return d - step; |
287 | if (checkMonth(d, step, next, 31)) |
288 | return d - step; |
289 | if (checkMonth(d, step, next, 30)) |
290 | return d - step; |
291 | step = next; |
292 | return d - step; |
293 | } |
294 | |
295 | int dayInYear(int year, int month, int day) |
296 | { |
297 | return firstDayOfMonth[isLeapYear(year)][month] + day - 1; |
298 | } |
299 | |
300 | double dateToDaysFrom1970(int year, int month, int day) |
301 | { |
302 | year += month / 12; |
303 | |
304 | month %= 12; |
305 | if (month < 0) { |
306 | month += 12; |
307 | --year; |
308 | } |
309 | |
310 | double yearday = floor(daysFrom1970ToYear(year)); |
311 | ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0)); |
312 | return yearday + dayInYear(year, month, day); |
313 | } |
314 | |
315 | // There is a hard limit at 2038 that we currently do not have a workaround |
316 | // for (rdar://problem/5052975). |
317 | static inline int maximumYearForDST() |
318 | { |
319 | return 2037; |
320 | } |
321 | |
322 | static inline int minimumYearForDST() |
323 | { |
324 | // Because of the 2038 issue (see maximumYearForDST) if the current year is |
325 | // greater than the max year minus 27 (2010), we want to use the max year |
326 | // minus 27 instead, to ensure there is a range of 28 years that all years |
327 | // can map to. |
328 | return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ; |
329 | } |
330 | |
331 | /* |
332 | * Find an equivalent year for the one given, where equivalence is deterined by |
333 | * the two years having the same leapness and the first day of the year, falling |
334 | * on the same day of the week. |
335 | * |
336 | * This function returns a year between this current year and 2037, however this |
337 | * function will potentially return incorrect results if the current year is after |
338 | * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after |
339 | * 2100, (rdar://problem/5055038). |
340 | */ |
341 | int equivalentYearForDST(int year) |
342 | { |
343 | // It is ok if the cached year is not the current year as long as the rules |
344 | // for DST did not change between the two years; if they did the app would need |
345 | // to be restarted. |
346 | static int minYear = minimumYearForDST(); |
347 | int maxYear = maximumYearForDST(); |
348 | |
349 | int difference; |
350 | if (year > maxYear) |
351 | difference = minYear - year; |
352 | else if (year < minYear) |
353 | difference = maxYear - year; |
354 | else |
355 | return year; |
356 | |
357 | int quotient = difference / 28; |
358 | int product = (quotient) * 28; |
359 | |
360 | year += product; |
361 | return year; |
362 | } |
363 | |
364 | #if OS(WINDOWS) |
365 | typedef BOOL(WINAPI* callGetTimeZoneInformationForYear_t)(USHORT, PDYNAMIC_TIME_ZONE_INFORMATION, LPTIME_ZONE_INFORMATION); |
366 | |
367 | static callGetTimeZoneInformationForYear_t timeZoneInformationForYearFunction() |
368 | { |
369 | static callGetTimeZoneInformationForYear_t getTimeZoneInformationForYear = nullptr; |
370 | |
371 | if (getTimeZoneInformationForYear) |
372 | return getTimeZoneInformationForYear; |
373 | |
374 | HMODULE module = ::GetModuleHandleW(L"kernel32.dll" ); |
375 | if (!module) |
376 | return nullptr; |
377 | |
378 | getTimeZoneInformationForYear = reinterpret_cast<callGetTimeZoneInformationForYear_t>(::GetProcAddress(module, "GetTimeZoneInformationForYear" )); |
379 | |
380 | return getTimeZoneInformationForYear; |
381 | } |
382 | #endif |
383 | |
384 | static int32_t calculateUTCOffset() |
385 | { |
386 | #if OS(WINDOWS) |
387 | TIME_ZONE_INFORMATION timeZoneInformation; |
388 | DWORD rc = 0; |
389 | |
390 | if (callGetTimeZoneInformationForYear_t timeZoneFunction = timeZoneInformationForYearFunction()) { |
391 | // If available, use the Windows API call that takes into account the varying DST from |
392 | // year to year. |
393 | SYSTEMTIME systemTime; |
394 | ::GetSystemTime(&systemTime); |
395 | rc = timeZoneFunction(systemTime.wYear, nullptr, &timeZoneInformation); |
396 | if (rc == TIME_ZONE_ID_INVALID) |
397 | return 0; |
398 | } else { |
399 | rc = ::GetTimeZoneInformation(&timeZoneInformation); |
400 | if (rc == TIME_ZONE_ID_INVALID) |
401 | return 0; |
402 | } |
403 | |
404 | int32_t bias = timeZoneInformation.Bias; |
405 | |
406 | if (rc == TIME_ZONE_ID_DAYLIGHT) |
407 | bias += timeZoneInformation.DaylightBias; |
408 | else if (rc == TIME_ZONE_ID_STANDARD || rc == TIME_ZONE_ID_UNKNOWN) |
409 | bias += timeZoneInformation.StandardBias; |
410 | |
411 | return -bias * 60 * 1000; |
412 | #else |
413 | time_t localTime = time(0); |
414 | tm localt; |
415 | getLocalTime(&localTime, &localt); |
416 | |
417 | // Get the difference between this time zone and UTC on the 1st of January of this year. |
418 | localt.tm_sec = 0; |
419 | localt.tm_min = 0; |
420 | localt.tm_hour = 0; |
421 | localt.tm_mday = 1; |
422 | localt.tm_mon = 0; |
423 | // Not setting localt.tm_year! |
424 | localt.tm_wday = 0; |
425 | localt.tm_yday = 0; |
426 | localt.tm_isdst = 0; |
427 | #if HAVE(TM_GMTOFF) |
428 | localt.tm_gmtoff = 0; |
429 | #endif |
430 | #if HAVE(TM_ZONE) |
431 | localt.tm_zone = 0; |
432 | #endif |
433 | |
434 | #if HAVE(TIMEGM) |
435 | time_t utcOffset = timegm(&localt) - mktime(&localt); |
436 | #else |
437 | // Using a canned date of 01/01/2009 on platforms with weaker date-handling foo. |
438 | localt.tm_year = 109; |
439 | time_t utcOffset = 1230768000 - mktime(&localt); |
440 | #endif |
441 | |
442 | return static_cast<int32_t>(utcOffset * 1000); |
443 | #endif |
444 | } |
445 | |
446 | #if !HAVE(TM_GMTOFF) |
447 | |
448 | #if OS(WINDOWS) |
449 | // Code taken from http://support.microsoft.com/kb/167296 |
450 | static void UnixTimeToFileTime(time_t t, LPFILETIME pft) |
451 | { |
452 | // Note that LONGLONG is a 64-bit value |
453 | LONGLONG ll; |
454 | |
455 | ll = Int32x32To64(t, 10000000) + 116444736000000000; |
456 | pft->dwLowDateTime = (DWORD)ll; |
457 | pft->dwHighDateTime = ll >> 32; |
458 | } |
459 | #endif |
460 | |
461 | /* |
462 | * Get the DST offset for the time passed in. |
463 | */ |
464 | static double calculateDSTOffset(time_t localTime, double utcOffset) |
465 | { |
466 | // input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset() |
467 | double offsetTime = (localTime * msPerSecond) + utcOffset; |
468 | |
469 | // Offset from UTC but doesn't include DST obviously |
470 | int offsetHour = msToHours(offsetTime); |
471 | int offsetMinute = msToMinutes(offsetTime); |
472 | |
473 | #if OS(WINDOWS) |
474 | FILETIME utcFileTime; |
475 | UnixTimeToFileTime(localTime, &utcFileTime); |
476 | SYSTEMTIME utcSystemTime, localSystemTime; |
477 | if (!::FileTimeToSystemTime(&utcFileTime, &utcSystemTime)) |
478 | return 0; |
479 | if (!::SystemTimeToTzSpecificLocalTime(nullptr, &utcSystemTime, &localSystemTime)) |
480 | return 0; |
481 | |
482 | double diff = ((localSystemTime.wHour - offsetHour) * secondsPerHour) + ((localSystemTime.wMinute - offsetMinute) * 60); |
483 | #else |
484 | tm localTM; |
485 | getLocalTime(&localTime, &localTM); |
486 | |
487 | double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60); |
488 | #endif |
489 | |
490 | if (diff < 0) |
491 | diff += secondsPerDay; |
492 | |
493 | return (diff * msPerSecond); |
494 | } |
495 | |
496 | #endif |
497 | |
498 | // Returns combined offset in millisecond (UTC + DST). |
499 | LocalTimeOffset calculateLocalTimeOffset(double ms, TimeType inputTimeType) |
500 | { |
501 | #if HAVE(TM_GMTOFF) |
502 | double localToUTCTimeOffset = inputTimeType == LocalTime ? calculateUTCOffset() : 0; |
503 | #else |
504 | double localToUTCTimeOffset = calculateUTCOffset(); |
505 | #endif |
506 | if (inputTimeType == LocalTime) |
507 | ms -= localToUTCTimeOffset; |
508 | |
509 | // On Mac OS X, the call to localtime (see calculateDSTOffset) will return historically accurate |
510 | // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript |
511 | // standard explicitly dictates that historical information should not be considered when |
512 | // determining DST. For this reason we shift away from years that localtime can handle but would |
513 | // return historically accurate information. |
514 | int year = msToYear(ms); |
515 | int equivalentYear = equivalentYearForDST(year); |
516 | if (year != equivalentYear) { |
517 | bool leapYear = isLeapYear(year); |
518 | int dayInYearLocal = dayInYear(ms, year); |
519 | int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear); |
520 | int month = monthFromDayInYear(dayInYearLocal, leapYear); |
521 | double day = dateToDaysFrom1970(equivalentYear, month, dayInMonth); |
522 | ms = (day * msPerDay) + msToMilliseconds(ms); |
523 | } |
524 | |
525 | double localTimeSeconds = ms / msPerSecond; |
526 | if (localTimeSeconds > maxUnixTime) |
527 | localTimeSeconds = maxUnixTime; |
528 | else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0). |
529 | localTimeSeconds += secondsPerDay; |
530 | // FIXME: time_t has a potential problem in 2038. |
531 | time_t localTime = static_cast<time_t>(localTimeSeconds); |
532 | |
533 | #if HAVE(TM_GMTOFF) |
534 | tm localTM; |
535 | getLocalTime(&localTime, &localTM); |
536 | return LocalTimeOffset(localTM.tm_isdst, localTM.tm_gmtoff * msPerSecond); |
537 | #else |
538 | double dstOffset = calculateDSTOffset(localTime, localToUTCTimeOffset); |
539 | return LocalTimeOffset(dstOffset, localToUTCTimeOffset + dstOffset); |
540 | #endif |
541 | } |
542 | |
543 | void initializeDates() |
544 | { |
545 | #if !ASSERT_DISABLED |
546 | static bool alreadyInitialized; |
547 | ASSERT(!alreadyInitialized); |
548 | alreadyInitialized = true; |
549 | #endif |
550 | |
551 | equivalentYearForDST(2000); // Need to call once to initialize a static used in this function. |
552 | } |
553 | |
554 | static inline double ymdhmsToSeconds(int year, long mon, long day, long hour, long minute, double second) |
555 | { |
556 | int mday = firstDayOfMonth[isLeapYear(year)][mon - 1]; |
557 | double ydays = daysFrom1970ToYear(year); |
558 | |
559 | double dateSeconds = second + minute * secondsPerMinute + hour * secondsPerHour + (mday + day - 1 + ydays) * secondsPerDay; |
560 | |
561 | // Clamp to EcmaScript standard (ecma262/#sec-time-values-and-time-range) of |
562 | // +/- 100,000,000 days from 01 January, 1970. |
563 | if (dateSeconds < -8640000000000.0 || dateSeconds > 8640000000000.0) |
564 | return std::numeric_limits<double>::quiet_NaN(); |
565 | |
566 | return dateSeconds; |
567 | } |
568 | |
569 | // We follow the recommendation of RFC 2822 to consider all |
570 | // obsolete time zones not listed here equivalent to "-0000". |
571 | static const struct KnownZone { |
572 | #if !OS(WINDOWS) |
573 | const |
574 | #endif |
575 | char tzName[4]; |
576 | int tzOffset; |
577 | } knownZones[] = { |
578 | { "ut" , 0 }, |
579 | { "gmt" , 0 }, |
580 | { "est" , -300 }, |
581 | { "edt" , -240 }, |
582 | { "cst" , -360 }, |
583 | { "cdt" , -300 }, |
584 | { "mst" , -420 }, |
585 | { "mdt" , -360 }, |
586 | { "pst" , -480 }, |
587 | { "pdt" , -420 } |
588 | }; |
589 | |
590 | inline static void skipSpacesAndComments(const char*& s) |
591 | { |
592 | int nesting = 0; |
593 | char ch; |
594 | while ((ch = *s)) { |
595 | if (!isASCIISpace(ch)) { |
596 | if (ch == '(') |
597 | nesting++; |
598 | else if (ch == ')' && nesting > 0) |
599 | nesting--; |
600 | else if (nesting == 0) |
601 | break; |
602 | } |
603 | s++; |
604 | } |
605 | } |
606 | |
607 | // returns 0-11 (Jan-Dec); -1 on failure |
608 | static int findMonth(const char* monthStr) |
609 | { |
610 | ASSERT(monthStr); |
611 | char needle[4]; |
612 | for (int i = 0; i < 3; ++i) { |
613 | if (!*monthStr) |
614 | return -1; |
615 | needle[i] = static_cast<char>(toASCIILower(*monthStr++)); |
616 | } |
617 | needle[3] = '\0'; |
618 | const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec" ; |
619 | const char *str = strstr(haystack, needle); |
620 | if (str) { |
621 | int position = static_cast<int>(str - haystack); |
622 | if (position % 3 == 0) |
623 | return position / 3; |
624 | } |
625 | return -1; |
626 | } |
627 | |
628 | static bool parseInt(const char* string, char** stopPosition, int base, int* result) |
629 | { |
630 | long longResult = strtol(string, stopPosition, base); |
631 | // Avoid the use of errno as it is not available on Windows CE |
632 | if (string == *stopPosition || longResult <= std::numeric_limits<int>::min() || longResult >= std::numeric_limits<int>::max()) |
633 | return false; |
634 | *result = static_cast<int>(longResult); |
635 | return true; |
636 | } |
637 | |
638 | static bool parseLong(const char* string, char** stopPosition, int base, long* result) |
639 | { |
640 | *result = strtol(string, stopPosition, base); |
641 | // Avoid the use of errno as it is not available on Windows CE |
642 | if (string == *stopPosition || *result == std::numeric_limits<long>::min() || *result == std::numeric_limits<long>::max()) |
643 | return false; |
644 | return true; |
645 | } |
646 | |
647 | // Parses a date with the format YYYY[-MM[-DD]]. |
648 | // Year parsing is lenient, allows any number of digits, and +/-. |
649 | // Returns 0 if a parse error occurs, else returns the end of the parsed portion of the string. |
650 | static char* parseES5DatePortion(const char* currentPosition, int& year, long& month, long& day) |
651 | { |
652 | char* postParsePosition; |
653 | |
654 | // This is a bit more lenient on the year string than ES5 specifies: |
655 | // instead of restricting to 4 digits (or 6 digits with mandatory +/-), |
656 | // it accepts any integer value. Consider this an implementation fallback. |
657 | if (!parseInt(currentPosition, &postParsePosition, 10, &year)) |
658 | return 0; |
659 | |
660 | // Check for presence of -MM portion. |
661 | if (*postParsePosition != '-') |
662 | return postParsePosition; |
663 | currentPosition = postParsePosition + 1; |
664 | |
665 | if (!isASCIIDigit(*currentPosition)) |
666 | return 0; |
667 | if (!parseLong(currentPosition, &postParsePosition, 10, &month)) |
668 | return 0; |
669 | if ((postParsePosition - currentPosition) != 2) |
670 | return 0; |
671 | |
672 | // Check for presence of -DD portion. |
673 | if (*postParsePosition != '-') |
674 | return postParsePosition; |
675 | currentPosition = postParsePosition + 1; |
676 | |
677 | if (!isASCIIDigit(*currentPosition)) |
678 | return 0; |
679 | if (!parseLong(currentPosition, &postParsePosition, 10, &day)) |
680 | return 0; |
681 | if ((postParsePosition - currentPosition) != 2) |
682 | return 0; |
683 | return postParsePosition; |
684 | } |
685 | |
686 | // Parses a time with the format HH:mm[:ss[.sss]][Z|(+|-)00:00]. |
687 | // Fractional seconds parsing is lenient, allows any number of digits. |
688 | // Returns 0 if a parse error occurs, else returns the end of the parsed portion of the string. |
689 | static char* parseES5TimePortion(char* currentPosition, long& hours, long& minutes, double& seconds, long& timeZoneSeconds) |
690 | { |
691 | char* postParsePosition; |
692 | if (!isASCIIDigit(*currentPosition)) |
693 | return 0; |
694 | if (!parseLong(currentPosition, &postParsePosition, 10, &hours)) |
695 | return 0; |
696 | if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2) |
697 | return 0; |
698 | currentPosition = postParsePosition + 1; |
699 | |
700 | if (!isASCIIDigit(*currentPosition)) |
701 | return 0; |
702 | if (!parseLong(currentPosition, &postParsePosition, 10, &minutes)) |
703 | return 0; |
704 | if ((postParsePosition - currentPosition) != 2) |
705 | return 0; |
706 | currentPosition = postParsePosition; |
707 | |
708 | // Seconds are optional. |
709 | if (*currentPosition == ':') { |
710 | ++currentPosition; |
711 | |
712 | long intSeconds; |
713 | if (!isASCIIDigit(*currentPosition)) |
714 | return 0; |
715 | if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds)) |
716 | return 0; |
717 | if ((postParsePosition - currentPosition) != 2) |
718 | return 0; |
719 | seconds = intSeconds; |
720 | if (*postParsePosition == '.') { |
721 | currentPosition = postParsePosition + 1; |
722 | |
723 | // In ECMA-262-5 it's a bit unclear if '.' can be present without milliseconds, but |
724 | // a reasonable interpretation guided by the given examples and RFC 3339 says "no". |
725 | // We check the next character to avoid reading +/- timezone hours after an invalid decimal. |
726 | if (!isASCIIDigit(*currentPosition)) |
727 | return 0; |
728 | |
729 | // We are more lenient than ES5 by accepting more or less than 3 fraction digits. |
730 | long fracSeconds; |
731 | if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds)) |
732 | return 0; |
733 | |
734 | long numFracDigits = postParsePosition - currentPosition; |
735 | seconds += fracSeconds * pow(10.0, static_cast<double>(-numFracDigits)); |
736 | } |
737 | currentPosition = postParsePosition; |
738 | } |
739 | |
740 | if (*currentPosition == 'Z') |
741 | return currentPosition + 1; |
742 | |
743 | bool tzNegative; |
744 | if (*currentPosition == '-') |
745 | tzNegative = true; |
746 | else if (*currentPosition == '+') |
747 | tzNegative = false; |
748 | else |
749 | return currentPosition; // no timezone |
750 | ++currentPosition; |
751 | |
752 | long tzHours; |
753 | long tzHoursAbs; |
754 | long tzMinutes; |
755 | |
756 | if (!isASCIIDigit(*currentPosition)) |
757 | return 0; |
758 | if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours)) |
759 | return 0; |
760 | if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2) |
761 | return 0; |
762 | tzHoursAbs = labs(tzHours); |
763 | currentPosition = postParsePosition + 1; |
764 | |
765 | if (!isASCIIDigit(*currentPosition)) |
766 | return 0; |
767 | if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes)) |
768 | return 0; |
769 | if ((postParsePosition - currentPosition) != 2) |
770 | return 0; |
771 | currentPosition = postParsePosition; |
772 | |
773 | if (tzHoursAbs > 24) |
774 | return 0; |
775 | if (tzMinutes < 0 || tzMinutes > 59) |
776 | return 0; |
777 | |
778 | timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs)); |
779 | if (tzNegative) |
780 | timeZoneSeconds = -timeZoneSeconds; |
781 | |
782 | return currentPosition; |
783 | } |
784 | |
785 | double parseES5DateFromNullTerminatedCharacters(const char* dateString) |
786 | { |
787 | // This parses a date of the form defined in ecma262/#sec-date-time-string-format |
788 | // (similar to RFC 3339 / ISO 8601: YYYY-MM-DDTHH:mm:ss[.sss]Z). |
789 | // In most cases it is intentionally strict (e.g. correct field widths, no stray whitespace). |
790 | |
791 | static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
792 | |
793 | // The year must be present, but the other fields may be omitted - see ES5.1 15.9.1.15. |
794 | int year = 0; |
795 | long month = 1; |
796 | long day = 1; |
797 | long hours = 0; |
798 | long minutes = 0; |
799 | double seconds = 0; |
800 | long timeZoneSeconds = 0; |
801 | |
802 | // Parse the date YYYY[-MM[-DD]] |
803 | char* currentPosition = parseES5DatePortion(dateString, year, month, day); |
804 | if (!currentPosition) |
805 | return std::numeric_limits<double>::quiet_NaN(); |
806 | // Look for a time portion. |
807 | if (*currentPosition == 'T') { |
808 | // Parse the time HH:mm[:ss[.sss]][Z|(+|-)00:00] |
809 | currentPosition = parseES5TimePortion(currentPosition + 1, hours, minutes, seconds, timeZoneSeconds); |
810 | if (!currentPosition) |
811 | return std::numeric_limits<double>::quiet_NaN(); |
812 | } |
813 | // Check that we have parsed all characters in the string. |
814 | if (*currentPosition) |
815 | return std::numeric_limits<double>::quiet_NaN(); |
816 | |
817 | // A few of these checks could be done inline above, but since many of them are interrelated |
818 | // we would be sacrificing readability to "optimize" the (presumably less common) failure path. |
819 | if (month < 1 || month > 12) |
820 | return std::numeric_limits<double>::quiet_NaN(); |
821 | if (day < 1 || day > daysPerMonth[month - 1]) |
822 | return std::numeric_limits<double>::quiet_NaN(); |
823 | if (month == 2 && day > 28 && !isLeapYear(year)) |
824 | return std::numeric_limits<double>::quiet_NaN(); |
825 | if (hours < 0 || hours > 24) |
826 | return std::numeric_limits<double>::quiet_NaN(); |
827 | if (hours == 24 && (minutes || seconds)) |
828 | return std::numeric_limits<double>::quiet_NaN(); |
829 | if (minutes < 0 || minutes > 59) |
830 | return std::numeric_limits<double>::quiet_NaN(); |
831 | if (seconds < 0 || seconds >= 61) |
832 | return std::numeric_limits<double>::quiet_NaN(); |
833 | if (seconds > 60) { |
834 | // Discard leap seconds by clamping to the end of a minute. |
835 | seconds = 60; |
836 | } |
837 | |
838 | double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds; |
839 | return dateSeconds * msPerSecond; |
840 | } |
841 | |
842 | // Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore. |
843 | double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset) |
844 | { |
845 | haveTZ = false; |
846 | offset = 0; |
847 | |
848 | // This parses a date in the form: |
849 | // Tuesday, 09-Nov-99 23:12:40 GMT |
850 | // or |
851 | // Sat, 01-Jan-2000 08:00:00 GMT |
852 | // or |
853 | // Sat, 01 Jan 2000 08:00:00 GMT |
854 | // or |
855 | // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822) |
856 | // ### non RFC formats, added for Javascript: |
857 | // [Wednesday] January 09 1999 23:12:40 GMT |
858 | // [Wednesday] January 09 23:12:40 GMT 1999 |
859 | // |
860 | // We ignore the weekday. |
861 | |
862 | // Skip leading space |
863 | skipSpacesAndComments(dateString); |
864 | |
865 | long month = -1; |
866 | const char *wordStart = dateString; |
867 | // Check contents of first words if not number |
868 | while (*dateString && !isASCIIDigit(*dateString)) { |
869 | if (isASCIISpace(*dateString) || *dateString == '(') { |
870 | if (dateString - wordStart >= 3) |
871 | month = findMonth(wordStart); |
872 | skipSpacesAndComments(dateString); |
873 | wordStart = dateString; |
874 | } else |
875 | dateString++; |
876 | } |
877 | |
878 | // Missing delimiter between month and day (like "January29")? |
879 | if (month == -1 && wordStart != dateString) |
880 | month = findMonth(wordStart); |
881 | |
882 | skipSpacesAndComments(dateString); |
883 | |
884 | if (!*dateString) |
885 | return std::numeric_limits<double>::quiet_NaN(); |
886 | |
887 | // ' 09-Nov-99 23:12:40 GMT' |
888 | char* newPosStr; |
889 | long day; |
890 | if (!parseLong(dateString, &newPosStr, 10, &day)) |
891 | return std::numeric_limits<double>::quiet_NaN(); |
892 | dateString = newPosStr; |
893 | |
894 | if (day < 0) |
895 | return std::numeric_limits<double>::quiet_NaN(); |
896 | |
897 | Optional<int> year; |
898 | if (day > 31) { |
899 | // ### where is the boundary and what happens below? |
900 | if (*dateString != '/') |
901 | return std::numeric_limits<double>::quiet_NaN(); |
902 | // looks like a YYYY/MM/DD date |
903 | if (!*++dateString) |
904 | return std::numeric_limits<double>::quiet_NaN(); |
905 | if (day <= std::numeric_limits<int>::min() || day >= std::numeric_limits<int>::max()) |
906 | return std::numeric_limits<double>::quiet_NaN(); |
907 | year = static_cast<int>(day); |
908 | if (!parseLong(dateString, &newPosStr, 10, &month)) |
909 | return std::numeric_limits<double>::quiet_NaN(); |
910 | month -= 1; |
911 | dateString = newPosStr; |
912 | if (*dateString++ != '/' || !*dateString) |
913 | return std::numeric_limits<double>::quiet_NaN(); |
914 | if (!parseLong(dateString, &newPosStr, 10, &day)) |
915 | return std::numeric_limits<double>::quiet_NaN(); |
916 | dateString = newPosStr; |
917 | } else if (*dateString == '/' && month == -1) { |
918 | dateString++; |
919 | // This looks like a MM/DD/YYYY date, not an RFC date. |
920 | month = day - 1; // 0-based |
921 | if (!parseLong(dateString, &newPosStr, 10, &day)) |
922 | return std::numeric_limits<double>::quiet_NaN(); |
923 | if (day < 1 || day > 31) |
924 | return std::numeric_limits<double>::quiet_NaN(); |
925 | dateString = newPosStr; |
926 | if (*dateString == '/') |
927 | dateString++; |
928 | if (!*dateString) |
929 | return std::numeric_limits<double>::quiet_NaN(); |
930 | } else { |
931 | if (*dateString == '-') |
932 | dateString++; |
933 | |
934 | skipSpacesAndComments(dateString); |
935 | |
936 | if (*dateString == ',') |
937 | dateString++; |
938 | |
939 | if (month == -1) { // not found yet |
940 | month = findMonth(dateString); |
941 | if (month == -1) |
942 | return std::numeric_limits<double>::quiet_NaN(); |
943 | |
944 | while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString)) |
945 | dateString++; |
946 | |
947 | if (!*dateString) |
948 | return std::numeric_limits<double>::quiet_NaN(); |
949 | |
950 | // '-99 23:12:40 GMT' |
951 | if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString)) |
952 | return std::numeric_limits<double>::quiet_NaN(); |
953 | dateString++; |
954 | } |
955 | } |
956 | |
957 | if (month < 0 || month > 11) |
958 | return std::numeric_limits<double>::quiet_NaN(); |
959 | |
960 | // '99 23:12:40 GMT' |
961 | if (*dateString && !year) { |
962 | int result = 0; |
963 | if (!parseInt(dateString, &newPosStr, 10, &result)) |
964 | return std::numeric_limits<double>::quiet_NaN(); |
965 | year = result; |
966 | } |
967 | |
968 | // Don't fail if the time is missing. |
969 | long hour = 0; |
970 | long minute = 0; |
971 | long second = 0; |
972 | if (!*newPosStr) |
973 | dateString = newPosStr; |
974 | else { |
975 | // ' 23:12:40 GMT' |
976 | if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) { |
977 | if (*newPosStr != ':') |
978 | return std::numeric_limits<double>::quiet_NaN(); |
979 | // There was no year; the number was the hour. |
980 | year = WTF::nullopt; |
981 | } else { |
982 | // in the normal case (we parsed the year), advance to the next number |
983 | dateString = ++newPosStr; |
984 | skipSpacesAndComments(dateString); |
985 | } |
986 | |
987 | parseLong(dateString, &newPosStr, 10, &hour); |
988 | // Do not check for errno here since we want to continue |
989 | // even if errno was set becasue we are still looking |
990 | // for the timezone! |
991 | |
992 | // Read a number? If not, this might be a timezone name. |
993 | if (newPosStr != dateString) { |
994 | dateString = newPosStr; |
995 | |
996 | if (hour < 0 || hour > 23) |
997 | return std::numeric_limits<double>::quiet_NaN(); |
998 | |
999 | if (!*dateString) |
1000 | return std::numeric_limits<double>::quiet_NaN(); |
1001 | |
1002 | // ':12:40 GMT' |
1003 | if (*dateString++ != ':') |
1004 | return std::numeric_limits<double>::quiet_NaN(); |
1005 | |
1006 | if (!parseLong(dateString, &newPosStr, 10, &minute)) |
1007 | return std::numeric_limits<double>::quiet_NaN(); |
1008 | dateString = newPosStr; |
1009 | |
1010 | if (minute < 0 || minute > 59) |
1011 | return std::numeric_limits<double>::quiet_NaN(); |
1012 | |
1013 | // ':40 GMT' |
1014 | if (*dateString && *dateString != ':' && !isASCIISpace(*dateString)) |
1015 | return std::numeric_limits<double>::quiet_NaN(); |
1016 | |
1017 | // seconds are optional in rfc822 + rfc2822 |
1018 | if (*dateString ==':') { |
1019 | dateString++; |
1020 | |
1021 | if (!parseLong(dateString, &newPosStr, 10, &second)) |
1022 | return std::numeric_limits<double>::quiet_NaN(); |
1023 | dateString = newPosStr; |
1024 | |
1025 | if (second < 0 || second > 59) |
1026 | return std::numeric_limits<double>::quiet_NaN(); |
1027 | } |
1028 | |
1029 | skipSpacesAndComments(dateString); |
1030 | |
1031 | if (startsWithLettersIgnoringASCIICase(dateString, "am" )) { |
1032 | if (hour > 12) |
1033 | return std::numeric_limits<double>::quiet_NaN(); |
1034 | if (hour == 12) |
1035 | hour = 0; |
1036 | dateString += 2; |
1037 | skipSpacesAndComments(dateString); |
1038 | } else if (startsWithLettersIgnoringASCIICase(dateString, "pm" )) { |
1039 | if (hour > 12) |
1040 | return std::numeric_limits<double>::quiet_NaN(); |
1041 | if (hour != 12) |
1042 | hour += 12; |
1043 | dateString += 2; |
1044 | skipSpacesAndComments(dateString); |
1045 | } |
1046 | } |
1047 | } |
1048 | |
1049 | // The year may be after the time but before the time zone. |
1050 | if (isASCIIDigit(*dateString) && !year) { |
1051 | int result = 0; |
1052 | if (!parseInt(dateString, &newPosStr, 10, &result)) |
1053 | return std::numeric_limits<double>::quiet_NaN(); |
1054 | year = result; |
1055 | dateString = newPosStr; |
1056 | skipSpacesAndComments(dateString); |
1057 | } |
1058 | |
1059 | // Don't fail if the time zone is missing. |
1060 | // Some websites omit the time zone (4275206). |
1061 | if (*dateString) { |
1062 | if (startsWithLettersIgnoringASCIICase(dateString, "gmt" ) || startsWithLettersIgnoringASCIICase(dateString, "utc" )) { |
1063 | dateString += 3; |
1064 | haveTZ = true; |
1065 | } |
1066 | |
1067 | if (*dateString == '+' || *dateString == '-') { |
1068 | int o; |
1069 | if (!parseInt(dateString, &newPosStr, 10, &o)) |
1070 | return std::numeric_limits<double>::quiet_NaN(); |
1071 | dateString = newPosStr; |
1072 | |
1073 | if (o < -9959 || o > 9959) |
1074 | return std::numeric_limits<double>::quiet_NaN(); |
1075 | |
1076 | int sgn = (o < 0) ? -1 : 1; |
1077 | o = abs(o); |
1078 | if (*dateString != ':') { |
1079 | if (o >= 24) |
1080 | offset = ((o / 100) * 60 + (o % 100)) * sgn; |
1081 | else |
1082 | offset = o * 60 * sgn; |
1083 | } else { // GMT+05:00 |
1084 | ++dateString; // skip the ':' |
1085 | int o2; |
1086 | if (!parseInt(dateString, &newPosStr, 10, &o2)) |
1087 | return std::numeric_limits<double>::quiet_NaN(); |
1088 | dateString = newPosStr; |
1089 | offset = (o * 60 + o2) * sgn; |
1090 | } |
1091 | haveTZ = true; |
1092 | } else { |
1093 | for (auto& knownZone : knownZones) { |
1094 | // Since the passed-in length is used for both strings, the following checks that |
1095 | // dateString has the time zone name as a prefix, not that it is equal. |
1096 | auto length = strlen(knownZone.tzName); |
1097 | if (equalLettersIgnoringASCIICase(dateString, knownZone.tzName, length)) { |
1098 | offset = knownZone.tzOffset; |
1099 | dateString += length; |
1100 | haveTZ = true; |
1101 | break; |
1102 | } |
1103 | } |
1104 | } |
1105 | } |
1106 | |
1107 | skipSpacesAndComments(dateString); |
1108 | |
1109 | if (*dateString && !year) { |
1110 | int result = 0; |
1111 | if (!parseInt(dateString, &newPosStr, 10, &result)) |
1112 | return std::numeric_limits<double>::quiet_NaN(); |
1113 | year = result; |
1114 | dateString = newPosStr; |
1115 | skipSpacesAndComments(dateString); |
1116 | } |
1117 | |
1118 | // Trailing garbage |
1119 | if (*dateString) |
1120 | return std::numeric_limits<double>::quiet_NaN(); |
1121 | |
1122 | // Y2K: Handle 2 digit years. |
1123 | if (year) { |
1124 | int yearValue = year.value(); |
1125 | if (yearValue >= 0 && yearValue < 100) { |
1126 | if (yearValue < 50) |
1127 | yearValue += 2000; |
1128 | else |
1129 | yearValue += 1900; |
1130 | } |
1131 | year = yearValue; |
1132 | } else { |
1133 | // We select 2000 as default value. This is because of the following reasons. |
1134 | // 1. Year 2000 was used for the initial value of the variable `year`. While it won't be posed to users in WebKit, |
1135 | // V8 used this 2000 as its default value. (As of April 2017, V8 is using the year 2001 and Spider Monkey is |
1136 | // not doing this kind of fallback.) |
1137 | // 2. It is a leap year. When using `new Date("Feb 29")`, we assume that people want to save month and day. |
1138 | // Leap year can save user inputs if they is valid. If we use the current year instead, the current year |
1139 | // may not be a leap year. In that case, `new Date("Feb 29").getMonth()` becomes 2 (March). |
1140 | year = 2000; |
1141 | } |
1142 | ASSERT(year); |
1143 | |
1144 | return ymdhmsToSeconds(year.value(), month + 1, day, hour, minute, second) * msPerSecond; |
1145 | } |
1146 | |
1147 | double parseDateFromNullTerminatedCharacters(const char* dateString) |
1148 | { |
1149 | bool haveTZ; |
1150 | int offset; |
1151 | double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); |
1152 | if (std::isnan(ms)) |
1153 | return std::numeric_limits<double>::quiet_NaN(); |
1154 | |
1155 | // fall back to local timezone |
1156 | if (!haveTZ) |
1157 | offset = calculateLocalTimeOffset(ms, LocalTime).offset / msPerMinute; // ms value is in local time milliseconds. |
1158 | |
1159 | return ms - (offset * msPerMinute); |
1160 | } |
1161 | |
1162 | double timeClip(double t) |
1163 | { |
1164 | if (std::abs(t) > maxECMAScriptTime) |
1165 | return std::numeric_limits<double>::quiet_NaN(); |
1166 | return std::trunc(t) + 0.0; |
1167 | } |
1168 | |
1169 | // See http://tools.ietf.org/html/rfc2822#section-3.3 for more information. |
1170 | String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset) |
1171 | { |
1172 | StringBuilder stringBuilder; |
1173 | stringBuilder.append(weekdayName[dayOfWeek]); |
1174 | stringBuilder.appendLiteral(", " ); |
1175 | stringBuilder.appendNumber(day); |
1176 | stringBuilder.append(' '); |
1177 | stringBuilder.append(monthName[month]); |
1178 | stringBuilder.append(' '); |
1179 | stringBuilder.appendNumber(year); |
1180 | stringBuilder.append(' '); |
1181 | |
1182 | appendTwoDigitNumber(stringBuilder, hours); |
1183 | stringBuilder.append(':'); |
1184 | appendTwoDigitNumber(stringBuilder, minutes); |
1185 | stringBuilder.append(':'); |
1186 | appendTwoDigitNumber(stringBuilder, seconds); |
1187 | stringBuilder.append(' '); |
1188 | |
1189 | stringBuilder.append(utcOffset > 0 ? '+' : '-'); |
1190 | int absoluteUTCOffset = abs(utcOffset); |
1191 | appendTwoDigitNumber(stringBuilder, absoluteUTCOffset / 60); |
1192 | appendTwoDigitNumber(stringBuilder, absoluteUTCOffset % 60); |
1193 | |
1194 | return stringBuilder.toString(); |
1195 | } |
1196 | |
1197 | } // namespace WTF |
1198 | |