|
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 * |
|
7 * The Original Code is Mozilla Communicator client code, released |
|
8 * March 31, 1998. |
|
9 * |
|
10 * The Initial Developer of the Original Code is |
|
11 * Netscape Communications Corporation. |
|
12 * Portions created by the Initial Developer are Copyright (C) 1998 |
|
13 * the Initial Developer. All Rights Reserved. |
|
14 * |
|
15 * This library is free software; you can redistribute it and/or |
|
16 * modify it under the terms of the GNU Lesser General Public |
|
17 * License as published by the Free Software Foundation; either |
|
18 * version 2.1 of the License, or (at your option) any later version. |
|
19 * |
|
20 * This library is distributed in the hope that it will be useful, |
|
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
23 * Lesser General Public License for more details. |
|
24 * |
|
25 * You should have received a copy of the GNU Lesser General Public |
|
26 * License along with this library; if not, write to the Free Software |
|
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
28 * |
|
29 * Alternatively, the contents of this file may be used under the terms |
|
30 * of either the Mozilla Public License Version 1.1, found at |
|
31 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public |
|
32 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html |
|
33 * (the "GPL"), in which case the provisions of the MPL or the GPL are |
|
34 * applicable instead of those above. If you wish to allow use of your |
|
35 * version of this file only under the terms of one of those two |
|
36 * licenses (the MPL or the GPL) and not to allow others to use your |
|
37 * version of this file under the LGPL, indicate your decision by |
|
38 * deletingthe provisions above and replace them with the notice and |
|
39 * other provisions required by the MPL or the GPL, as the case may be. |
|
40 * If you do not delete the provisions above, a recipient may use your |
|
41 * version of this file under any of the LGPL, the MPL or the GPL. |
|
42 |
|
43 * Copyright 2006-2008 the V8 project authors. All rights reserved. |
|
44 * Redistribution and use in source and binary forms, with or without |
|
45 * modification, are permitted provided that the following conditions are |
|
46 * met: |
|
47 * |
|
48 * * Redistributions of source code must retain the above copyright |
|
49 * notice, this list of conditions and the following disclaimer. |
|
50 * * Redistributions in binary form must reproduce the above |
|
51 * copyright notice, this list of conditions and the following |
|
52 * disclaimer in the documentation and/or other materials provided |
|
53 * with the distribution. |
|
54 * * Neither the name of Google Inc. nor the names of its |
|
55 * contributors may be used to endorse or promote products derived |
|
56 * from this software without specific prior written permission. |
|
57 * |
|
58 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
59 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
60 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
61 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
62 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
63 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
64 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
65 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
66 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
67 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
68 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
69 */ |
|
70 |
|
71 #include "config.h" |
|
72 #include "DateMath.h" |
|
73 |
|
74 #include "Assertions.h" |
|
75 #include "ASCIICType.h" |
|
76 #include "CurrentTime.h" |
|
77 #include "MathExtras.h" |
|
78 #include "StringExtras.h" |
|
79 |
|
80 #include <algorithm> |
|
81 #include <limits.h> |
|
82 #include <limits> |
|
83 #include <stdint.h> |
|
84 #include <time.h> |
|
85 |
|
86 |
|
87 #if HAVE(ERRNO_H) |
|
88 #include <errno.h> |
|
89 #endif |
|
90 |
|
91 #if OS(WINCE) |
|
92 extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t); |
|
93 extern "C" struct tm * localtime(const time_t *timer); |
|
94 #endif |
|
95 |
|
96 #if HAVE(SYS_TIME_H) |
|
97 #include <sys/time.h> |
|
98 #endif |
|
99 |
|
100 #if HAVE(SYS_TIMEB_H) |
|
101 #include <sys/timeb.h> |
|
102 #endif |
|
103 |
|
104 #if USE(JSC) |
|
105 #include "CallFrame.h" |
|
106 #endif |
|
107 |
|
108 #define NaN std::numeric_limits<double>::quiet_NaN() |
|
109 |
|
110 using namespace WTF; |
|
111 |
|
112 namespace WTF { |
|
113 |
|
114 /* Constants */ |
|
115 |
|
116 static const double minutesPerDay = 24.0 * 60.0; |
|
117 static const double secondsPerDay = 24.0 * 60.0 * 60.0; |
|
118 static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0; |
|
119 |
|
120 static const double usecPerSec = 1000000.0; |
|
121 |
|
122 static const double maxUnixTime = 2145859200.0; // 12/31/2037 |
|
123 // ECMAScript asks not to support for a date of which total |
|
124 // millisecond value is larger than the following value. |
|
125 // See 15.9.1.14 of ECMA-262 5th edition. |
|
126 static const double maxECMAScriptTime = 8.64E15; |
|
127 |
|
128 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1. |
|
129 // First for non-leap years, then for leap years. |
|
130 static const int firstDayOfMonth[2][12] = { |
|
131 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, |
|
132 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} |
|
133 }; |
|
134 |
|
135 static inline bool isLeapYear(int year) |
|
136 { |
|
137 if (year % 4 != 0) |
|
138 return false; |
|
139 if (year % 400 == 0) |
|
140 return true; |
|
141 if (year % 100 == 0) |
|
142 return false; |
|
143 return true; |
|
144 } |
|
145 |
|
146 static inline int daysInYear(int year) |
|
147 { |
|
148 return 365 + isLeapYear(year); |
|
149 } |
|
150 |
|
151 static inline double daysFrom1970ToYear(int year) |
|
152 { |
|
153 // The Gregorian Calendar rules for leap years: |
|
154 // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years. |
|
155 // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years. |
|
156 // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years. |
|
157 |
|
158 static const int leapDaysBefore1971By4Rule = 1970 / 4; |
|
159 static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100; |
|
160 static const int leapDaysBefore1971By400Rule = 1970 / 400; |
|
161 |
|
162 const double yearMinusOne = year - 1; |
|
163 const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule; |
|
164 const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule; |
|
165 const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule; |
|
166 |
|
167 return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule; |
|
168 } |
|
169 |
|
170 static inline double msToDays(double ms) |
|
171 { |
|
172 return floor(ms / msPerDay); |
|
173 } |
|
174 |
|
175 int msToYear(double ms) |
|
176 { |
|
177 int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970); |
|
178 double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear); |
|
179 if (msFromApproxYearTo1970 > ms) |
|
180 return approxYear - 1; |
|
181 if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms) |
|
182 return approxYear + 1; |
|
183 return approxYear; |
|
184 } |
|
185 |
|
186 int dayInYear(double ms, int year) |
|
187 { |
|
188 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year)); |
|
189 } |
|
190 |
|
191 static inline double msToMilliseconds(double ms) |
|
192 { |
|
193 double result = fmod(ms, msPerDay); |
|
194 if (result < 0) |
|
195 result += msPerDay; |
|
196 return result; |
|
197 } |
|
198 |
|
199 // 0: Sunday, 1: Monday, etc. |
|
200 static inline int msToWeekDay(double ms) |
|
201 { |
|
202 int wd = (static_cast<int>(msToDays(ms)) + 4) % 7; |
|
203 if (wd < 0) |
|
204 wd += 7; |
|
205 return wd; |
|
206 } |
|
207 |
|
208 static inline int msToSeconds(double ms) |
|
209 { |
|
210 double result = fmod(floor(ms / msPerSecond), secondsPerMinute); |
|
211 if (result < 0) |
|
212 result += secondsPerMinute; |
|
213 return static_cast<int>(result); |
|
214 } |
|
215 |
|
216 static inline int msToMinutes(double ms) |
|
217 { |
|
218 double result = fmod(floor(ms / msPerMinute), minutesPerHour); |
|
219 if (result < 0) |
|
220 result += minutesPerHour; |
|
221 return static_cast<int>(result); |
|
222 } |
|
223 |
|
224 static inline int msToHours(double ms) |
|
225 { |
|
226 double result = fmod(floor(ms/msPerHour), hoursPerDay); |
|
227 if (result < 0) |
|
228 result += hoursPerDay; |
|
229 return static_cast<int>(result); |
|
230 } |
|
231 |
|
232 int monthFromDayInYear(int dayInYear, bool leapYear) |
|
233 { |
|
234 const int d = dayInYear; |
|
235 int step; |
|
236 |
|
237 if (d < (step = 31)) |
|
238 return 0; |
|
239 step += (leapYear ? 29 : 28); |
|
240 if (d < step) |
|
241 return 1; |
|
242 if (d < (step += 31)) |
|
243 return 2; |
|
244 if (d < (step += 30)) |
|
245 return 3; |
|
246 if (d < (step += 31)) |
|
247 return 4; |
|
248 if (d < (step += 30)) |
|
249 return 5; |
|
250 if (d < (step += 31)) |
|
251 return 6; |
|
252 if (d < (step += 31)) |
|
253 return 7; |
|
254 if (d < (step += 30)) |
|
255 return 8; |
|
256 if (d < (step += 31)) |
|
257 return 9; |
|
258 if (d < (step += 30)) |
|
259 return 10; |
|
260 return 11; |
|
261 } |
|
262 |
|
263 static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth) |
|
264 { |
|
265 startDayOfThisMonth = startDayOfNextMonth; |
|
266 startDayOfNextMonth += daysInThisMonth; |
|
267 return (dayInYear <= startDayOfNextMonth); |
|
268 } |
|
269 |
|
270 int dayInMonthFromDayInYear(int dayInYear, bool leapYear) |
|
271 { |
|
272 const int d = dayInYear; |
|
273 int step; |
|
274 int next = 30; |
|
275 |
|
276 if (d <= next) |
|
277 return d + 1; |
|
278 const int daysInFeb = (leapYear ? 29 : 28); |
|
279 if (checkMonth(d, step, next, daysInFeb)) |
|
280 return d - step; |
|
281 if (checkMonth(d, step, next, 31)) |
|
282 return d - step; |
|
283 if (checkMonth(d, step, next, 30)) |
|
284 return d - step; |
|
285 if (checkMonth(d, step, next, 31)) |
|
286 return d - step; |
|
287 if (checkMonth(d, step, next, 30)) |
|
288 return d - step; |
|
289 if (checkMonth(d, step, next, 31)) |
|
290 return d - step; |
|
291 if (checkMonth(d, step, next, 31)) |
|
292 return d - step; |
|
293 if (checkMonth(d, step, next, 30)) |
|
294 return d - step; |
|
295 if (checkMonth(d, step, next, 31)) |
|
296 return d - step; |
|
297 if (checkMonth(d, step, next, 30)) |
|
298 return d - step; |
|
299 step = next; |
|
300 return d - step; |
|
301 } |
|
302 |
|
303 static inline int monthToDayInYear(int month, bool isLeapYear) |
|
304 { |
|
305 return firstDayOfMonth[isLeapYear][month]; |
|
306 } |
|
307 |
|
308 static inline double timeToMS(double hour, double min, double sec, double ms) |
|
309 { |
|
310 return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms); |
|
311 } |
|
312 |
|
313 double dateToDaysFrom1970(int year, int month, int day) |
|
314 { |
|
315 year += month / 12; |
|
316 |
|
317 month %= 12; |
|
318 if (month < 0) { |
|
319 month += 12; |
|
320 --year; |
|
321 } |
|
322 |
|
323 double yearday = floor(daysFrom1970ToYear(year)); |
|
324 ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0)); |
|
325 int monthday = monthToDayInYear(month, isLeapYear(year)); |
|
326 |
|
327 return yearday + monthday + day - 1; |
|
328 } |
|
329 |
|
330 // There is a hard limit at 2038 that we currently do not have a workaround |
|
331 // for (rdar://problem/5052975). |
|
332 static inline int maximumYearForDST() |
|
333 { |
|
334 return 2037; |
|
335 } |
|
336 |
|
337 static inline int minimumYearForDST() |
|
338 { |
|
339 // Because of the 2038 issue (see maximumYearForDST) if the current year is |
|
340 // greater than the max year minus 27 (2010), we want to use the max year |
|
341 // minus 27 instead, to ensure there is a range of 28 years that all years |
|
342 // can map to. |
|
343 return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ; |
|
344 } |
|
345 |
|
346 /* |
|
347 * Find an equivalent year for the one given, where equivalence is deterined by |
|
348 * the two years having the same leapness and the first day of the year, falling |
|
349 * on the same day of the week. |
|
350 * |
|
351 * This function returns a year between this current year and 2037, however this |
|
352 * function will potentially return incorrect results if the current year is after |
|
353 * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after |
|
354 * 2100, (rdar://problem/5055038). |
|
355 */ |
|
356 int equivalentYearForDST(int year) |
|
357 { |
|
358 // It is ok if the cached year is not the current year as long as the rules |
|
359 // for DST did not change between the two years; if they did the app would need |
|
360 // to be restarted. |
|
361 static int minYear = minimumYearForDST(); |
|
362 int maxYear = maximumYearForDST(); |
|
363 |
|
364 int difference; |
|
365 if (year > maxYear) |
|
366 difference = minYear - year; |
|
367 else if (year < minYear) |
|
368 difference = maxYear - year; |
|
369 else |
|
370 return year; |
|
371 |
|
372 int quotient = difference / 28; |
|
373 int product = (quotient) * 28; |
|
374 |
|
375 year += product; |
|
376 ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(NaN))); |
|
377 return year; |
|
378 } |
|
379 |
|
380 static int32_t calculateUTCOffset() |
|
381 { |
|
382 #if PLATFORM(BREWMP) |
|
383 time_t localTime = static_cast<time_t>(currentTime()); |
|
384 #else |
|
385 time_t localTime = time(0); |
|
386 #endif |
|
387 tm localt; |
|
388 getLocalTime(&localTime, &localt); |
|
389 |
|
390 // Get the difference between this time zone and UTC on the 1st of January of this year. |
|
391 localt.tm_sec = 0; |
|
392 localt.tm_min = 0; |
|
393 localt.tm_hour = 0; |
|
394 localt.tm_mday = 1; |
|
395 localt.tm_mon = 0; |
|
396 // Not setting localt.tm_year! |
|
397 localt.tm_wday = 0; |
|
398 localt.tm_yday = 0; |
|
399 localt.tm_isdst = 0; |
|
400 #if HAVE(TM_GMTOFF) |
|
401 localt.tm_gmtoff = 0; |
|
402 #endif |
|
403 #if HAVE(TM_ZONE) |
|
404 localt.tm_zone = 0; |
|
405 #endif |
|
406 |
|
407 #if HAVE(TIMEGM) |
|
408 time_t utcOffset = timegm(&localt) - mktime(&localt); |
|
409 #else |
|
410 // Using a canned date of 01/01/2009 on platforms with weaker date-handling foo. |
|
411 localt.tm_year = 109; |
|
412 time_t utcOffset = 1230768000 - mktime(&localt); |
|
413 #endif |
|
414 |
|
415 return static_cast<int32_t>(utcOffset * 1000); |
|
416 } |
|
417 |
|
418 /* |
|
419 * Get the DST offset for the time passed in. |
|
420 */ |
|
421 static double calculateDSTOffsetSimple(double localTimeSeconds, double utcOffset) |
|
422 { |
|
423 if (localTimeSeconds > maxUnixTime) |
|
424 localTimeSeconds = maxUnixTime; |
|
425 else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0) |
|
426 localTimeSeconds += secondsPerDay; |
|
427 |
|
428 //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset() |
|
429 double offsetTime = (localTimeSeconds * msPerSecond) + utcOffset; |
|
430 |
|
431 // Offset from UTC but doesn't include DST obviously |
|
432 int offsetHour = msToHours(offsetTime); |
|
433 int offsetMinute = msToMinutes(offsetTime); |
|
434 |
|
435 // FIXME: time_t has a potential problem in 2038 |
|
436 time_t localTime = static_cast<time_t>(localTimeSeconds); |
|
437 |
|
438 tm localTM; |
|
439 getLocalTime(&localTime, &localTM); |
|
440 |
|
441 double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60); |
|
442 |
|
443 if (diff < 0) |
|
444 diff += secondsPerDay; |
|
445 |
|
446 return (diff * msPerSecond); |
|
447 } |
|
448 |
|
449 // Get the DST offset, given a time in UTC |
|
450 static double calculateDSTOffset(double ms, double utcOffset) |
|
451 { |
|
452 // On Mac OS X, the call to localtime (see calculateDSTOffsetSimple) will return historically accurate |
|
453 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript |
|
454 // standard explicitly dictates that historical information should not be considered when |
|
455 // determining DST. For this reason we shift away from years that localtime can handle but would |
|
456 // return historically accurate information. |
|
457 int year = msToYear(ms); |
|
458 int equivalentYear = equivalentYearForDST(year); |
|
459 if (year != equivalentYear) { |
|
460 bool leapYear = isLeapYear(year); |
|
461 int dayInYearLocal = dayInYear(ms, year); |
|
462 int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear); |
|
463 int month = monthFromDayInYear(dayInYearLocal, leapYear); |
|
464 double day = dateToDaysFrom1970(equivalentYear, month, dayInMonth); |
|
465 ms = (day * msPerDay) + msToMilliseconds(ms); |
|
466 } |
|
467 |
|
468 return calculateDSTOffsetSimple(ms / msPerSecond, utcOffset); |
|
469 } |
|
470 |
|
471 void initializeDates() |
|
472 { |
|
473 #ifndef NDEBUG |
|
474 static bool alreadyInitialized; |
|
475 ASSERT(!alreadyInitialized); |
|
476 alreadyInitialized = true; |
|
477 #endif |
|
478 |
|
479 equivalentYearForDST(2000); // Need to call once to initialize a static used in this function. |
|
480 } |
|
481 |
|
482 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second) |
|
483 { |
|
484 double days = (day - 32075) |
|
485 + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4) |
|
486 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12 |
|
487 - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4) |
|
488 - 2440588; |
|
489 return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second; |
|
490 } |
|
491 |
|
492 // We follow the recommendation of RFC 2822 to consider all |
|
493 // obsolete time zones not listed here equivalent to "-0000". |
|
494 static const struct KnownZone { |
|
495 #if !OS(WINDOWS) |
|
496 const |
|
497 #endif |
|
498 char tzName[4]; |
|
499 int tzOffset; |
|
500 } known_zones[] = { |
|
501 { "UT", 0 }, |
|
502 { "GMT", 0 }, |
|
503 { "EST", -300 }, |
|
504 { "EDT", -240 }, |
|
505 { "CST", -360 }, |
|
506 { "CDT", -300 }, |
|
507 { "MST", -420 }, |
|
508 { "MDT", -360 }, |
|
509 { "PST", -480 }, |
|
510 { "PDT", -420 } |
|
511 }; |
|
512 |
|
513 inline static void skipSpacesAndComments(const char*& s) |
|
514 { |
|
515 int nesting = 0; |
|
516 char ch; |
|
517 while ((ch = *s)) { |
|
518 if (!isASCIISpace(ch)) { |
|
519 if (ch == '(') |
|
520 nesting++; |
|
521 else if (ch == ')' && nesting > 0) |
|
522 nesting--; |
|
523 else if (nesting == 0) |
|
524 break; |
|
525 } |
|
526 s++; |
|
527 } |
|
528 } |
|
529 |
|
530 // returns 0-11 (Jan-Dec); -1 on failure |
|
531 static int findMonth(const char* monthStr) |
|
532 { |
|
533 ASSERT(monthStr); |
|
534 char needle[4]; |
|
535 for (int i = 0; i < 3; ++i) { |
|
536 if (!*monthStr) |
|
537 return -1; |
|
538 needle[i] = static_cast<char>(toASCIILower(*monthStr++)); |
|
539 } |
|
540 needle[3] = '\0'; |
|
541 const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec"; |
|
542 const char *str = strstr(haystack, needle); |
|
543 if (str) { |
|
544 int position = static_cast<int>(str - haystack); |
|
545 if (position % 3 == 0) |
|
546 return position / 3; |
|
547 } |
|
548 return -1; |
|
549 } |
|
550 |
|
551 static bool parseLong(const char* string, char** stopPosition, int base, long* result) |
|
552 { |
|
553 *result = strtol(string, stopPosition, base); |
|
554 // Avoid the use of errno as it is not available on Windows CE |
|
555 if (string == *stopPosition || *result == LONG_MIN || *result == LONG_MAX) |
|
556 return false; |
|
557 return true; |
|
558 } |
|
559 |
|
560 // Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore. |
|
561 static double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset) |
|
562 { |
|
563 haveTZ = false; |
|
564 offset = 0; |
|
565 |
|
566 // This parses a date in the form: |
|
567 // Tuesday, 09-Nov-99 23:12:40 GMT |
|
568 // or |
|
569 // Sat, 01-Jan-2000 08:00:00 GMT |
|
570 // or |
|
571 // Sat, 01 Jan 2000 08:00:00 GMT |
|
572 // or |
|
573 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822) |
|
574 // ### non RFC formats, added for Javascript: |
|
575 // [Wednesday] January 09 1999 23:12:40 GMT |
|
576 // [Wednesday] January 09 23:12:40 GMT 1999 |
|
577 // |
|
578 // We ignore the weekday. |
|
579 |
|
580 // Skip leading space |
|
581 skipSpacesAndComments(dateString); |
|
582 |
|
583 long month = -1; |
|
584 const char *wordStart = dateString; |
|
585 // Check contents of first words if not number |
|
586 while (*dateString && !isASCIIDigit(*dateString)) { |
|
587 if (isASCIISpace(*dateString) || *dateString == '(') { |
|
588 if (dateString - wordStart >= 3) |
|
589 month = findMonth(wordStart); |
|
590 skipSpacesAndComments(dateString); |
|
591 wordStart = dateString; |
|
592 } else |
|
593 dateString++; |
|
594 } |
|
595 |
|
596 // Missing delimiter between month and day (like "January29")? |
|
597 if (month == -1 && wordStart != dateString) |
|
598 month = findMonth(wordStart); |
|
599 |
|
600 skipSpacesAndComments(dateString); |
|
601 |
|
602 if (!*dateString) |
|
603 return NaN; |
|
604 |
|
605 // ' 09-Nov-99 23:12:40 GMT' |
|
606 char* newPosStr; |
|
607 long day; |
|
608 if (!parseLong(dateString, &newPosStr, 10, &day)) |
|
609 return NaN; |
|
610 dateString = newPosStr; |
|
611 |
|
612 if (!*dateString) |
|
613 return NaN; |
|
614 |
|
615 if (day < 0) |
|
616 return NaN; |
|
617 |
|
618 long year = 0; |
|
619 if (day > 31) { |
|
620 // ### where is the boundary and what happens below? |
|
621 if (*dateString != '/') |
|
622 return NaN; |
|
623 // looks like a YYYY/MM/DD date |
|
624 if (!*++dateString) |
|
625 return NaN; |
|
626 year = day; |
|
627 if (!parseLong(dateString, &newPosStr, 10, &month)) |
|
628 return NaN; |
|
629 month -= 1; |
|
630 dateString = newPosStr; |
|
631 if (*dateString++ != '/' || !*dateString) |
|
632 return NaN; |
|
633 if (!parseLong(dateString, &newPosStr, 10, &day)) |
|
634 return NaN; |
|
635 dateString = newPosStr; |
|
636 } else if (*dateString == '/' && month == -1) { |
|
637 dateString++; |
|
638 // This looks like a MM/DD/YYYY date, not an RFC date. |
|
639 month = day - 1; // 0-based |
|
640 if (!parseLong(dateString, &newPosStr, 10, &day)) |
|
641 return NaN; |
|
642 if (day < 1 || day > 31) |
|
643 return NaN; |
|
644 dateString = newPosStr; |
|
645 if (*dateString == '/') |
|
646 dateString++; |
|
647 if (!*dateString) |
|
648 return NaN; |
|
649 } else { |
|
650 if (*dateString == '-') |
|
651 dateString++; |
|
652 |
|
653 skipSpacesAndComments(dateString); |
|
654 |
|
655 if (*dateString == ',') |
|
656 dateString++; |
|
657 |
|
658 if (month == -1) { // not found yet |
|
659 month = findMonth(dateString); |
|
660 if (month == -1) |
|
661 return NaN; |
|
662 |
|
663 while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString)) |
|
664 dateString++; |
|
665 |
|
666 if (!*dateString) |
|
667 return NaN; |
|
668 |
|
669 // '-99 23:12:40 GMT' |
|
670 if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString)) |
|
671 return NaN; |
|
672 dateString++; |
|
673 } |
|
674 } |
|
675 |
|
676 if (month < 0 || month > 11) |
|
677 return NaN; |
|
678 |
|
679 // '99 23:12:40 GMT' |
|
680 if (year <= 0 && *dateString) { |
|
681 if (!parseLong(dateString, &newPosStr, 10, &year)) |
|
682 return NaN; |
|
683 } |
|
684 |
|
685 // Don't fail if the time is missing. |
|
686 long hour = 0; |
|
687 long minute = 0; |
|
688 long second = 0; |
|
689 if (!*newPosStr) |
|
690 dateString = newPosStr; |
|
691 else { |
|
692 // ' 23:12:40 GMT' |
|
693 if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) { |
|
694 if (*newPosStr != ':') |
|
695 return NaN; |
|
696 // There was no year; the number was the hour. |
|
697 year = -1; |
|
698 } else { |
|
699 // in the normal case (we parsed the year), advance to the next number |
|
700 dateString = ++newPosStr; |
|
701 skipSpacesAndComments(dateString); |
|
702 } |
|
703 |
|
704 parseLong(dateString, &newPosStr, 10, &hour); |
|
705 // Do not check for errno here since we want to continue |
|
706 // even if errno was set becasue we are still looking |
|
707 // for the timezone! |
|
708 |
|
709 // Read a number? If not, this might be a timezone name. |
|
710 if (newPosStr != dateString) { |
|
711 dateString = newPosStr; |
|
712 |
|
713 if (hour < 0 || hour > 23) |
|
714 return NaN; |
|
715 |
|
716 if (!*dateString) |
|
717 return NaN; |
|
718 |
|
719 // ':12:40 GMT' |
|
720 if (*dateString++ != ':') |
|
721 return NaN; |
|
722 |
|
723 if (!parseLong(dateString, &newPosStr, 10, &minute)) |
|
724 return NaN; |
|
725 dateString = newPosStr; |
|
726 |
|
727 if (minute < 0 || minute > 59) |
|
728 return NaN; |
|
729 |
|
730 // ':40 GMT' |
|
731 if (*dateString && *dateString != ':' && !isASCIISpace(*dateString)) |
|
732 return NaN; |
|
733 |
|
734 // seconds are optional in rfc822 + rfc2822 |
|
735 if (*dateString ==':') { |
|
736 dateString++; |
|
737 |
|
738 if (!parseLong(dateString, &newPosStr, 10, &second)) |
|
739 return NaN; |
|
740 dateString = newPosStr; |
|
741 |
|
742 if (second < 0 || second > 59) |
|
743 return NaN; |
|
744 } |
|
745 |
|
746 skipSpacesAndComments(dateString); |
|
747 |
|
748 if (strncasecmp(dateString, "AM", 2) == 0) { |
|
749 if (hour > 12) |
|
750 return NaN; |
|
751 if (hour == 12) |
|
752 hour = 0; |
|
753 dateString += 2; |
|
754 skipSpacesAndComments(dateString); |
|
755 } else if (strncasecmp(dateString, "PM", 2) == 0) { |
|
756 if (hour > 12) |
|
757 return NaN; |
|
758 if (hour != 12) |
|
759 hour += 12; |
|
760 dateString += 2; |
|
761 skipSpacesAndComments(dateString); |
|
762 } |
|
763 } |
|
764 } |
|
765 |
|
766 // Don't fail if the time zone is missing. |
|
767 // Some websites omit the time zone (4275206). |
|
768 if (*dateString) { |
|
769 if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) { |
|
770 dateString += 3; |
|
771 haveTZ = true; |
|
772 } |
|
773 |
|
774 if (*dateString == '+' || *dateString == '-') { |
|
775 long o; |
|
776 if (!parseLong(dateString, &newPosStr, 10, &o)) |
|
777 return NaN; |
|
778 dateString = newPosStr; |
|
779 |
|
780 if (o < -9959 || o > 9959) |
|
781 return NaN; |
|
782 |
|
783 int sgn = (o < 0) ? -1 : 1; |
|
784 o = labs(o); |
|
785 if (*dateString != ':') { |
|
786 offset = ((o / 100) * 60 + (o % 100)) * sgn; |
|
787 } else { // GMT+05:00 |
|
788 long o2; |
|
789 if (!parseLong(dateString, &newPosStr, 10, &o2)) |
|
790 return NaN; |
|
791 dateString = newPosStr; |
|
792 offset = (o * 60 + o2) * sgn; |
|
793 } |
|
794 haveTZ = true; |
|
795 } else { |
|
796 for (int i = 0; i < int(sizeof(known_zones) / sizeof(KnownZone)); i++) { |
|
797 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) { |
|
798 offset = known_zones[i].tzOffset; |
|
799 dateString += strlen(known_zones[i].tzName); |
|
800 haveTZ = true; |
|
801 break; |
|
802 } |
|
803 } |
|
804 } |
|
805 } |
|
806 |
|
807 skipSpacesAndComments(dateString); |
|
808 |
|
809 if (*dateString && year == -1) { |
|
810 if (!parseLong(dateString, &newPosStr, 10, &year)) |
|
811 return NaN; |
|
812 dateString = newPosStr; |
|
813 } |
|
814 |
|
815 skipSpacesAndComments(dateString); |
|
816 |
|
817 // Trailing garbage |
|
818 if (*dateString) |
|
819 return NaN; |
|
820 |
|
821 // Y2K: Handle 2 digit years. |
|
822 if (year >= 0 && year < 100) { |
|
823 if (year < 50) |
|
824 year += 2000; |
|
825 else |
|
826 year += 1900; |
|
827 } |
|
828 |
|
829 return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond; |
|
830 } |
|
831 |
|
832 double parseDateFromNullTerminatedCharacters(const char* dateString) |
|
833 { |
|
834 bool haveTZ; |
|
835 int offset; |
|
836 double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); |
|
837 if (isnan(ms)) |
|
838 return NaN; |
|
839 |
|
840 // fall back to local timezone |
|
841 if (!haveTZ) { |
|
842 double utcOffset = calculateUTCOffset(); |
|
843 double dstOffset = calculateDSTOffset(ms, utcOffset); |
|
844 offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute); |
|
845 } |
|
846 return ms - (offset * msPerMinute); |
|
847 } |
|
848 |
|
849 double timeClip(double t) |
|
850 { |
|
851 if (!isfinite(t)) |
|
852 return NaN; |
|
853 if (fabs(t) > maxECMAScriptTime) |
|
854 return NaN; |
|
855 return trunc(t); |
|
856 } |
|
857 } // namespace WTF |
|
858 |
|
859 #if USE(JSC) |
|
860 namespace JSC { |
|
861 |
|
862 // Get the DST offset for the time passed in. |
|
863 // |
|
864 // NOTE: The implementation relies on the fact that no time zones have |
|
865 // more than one daylight savings offset change per month. |
|
866 // If this function is called with NaN it returns NaN. |
|
867 static double getDSTOffset(ExecState* exec, double ms, double utcOffset) |
|
868 { |
|
869 DSTOffsetCache& cache = exec->globalData().dstOffsetCache; |
|
870 double start = cache.start; |
|
871 double end = cache.end; |
|
872 |
|
873 if (start <= ms) { |
|
874 // If the time fits in the cached interval, return the cached offset. |
|
875 if (ms <= end) return cache.offset; |
|
876 |
|
877 // Compute a possible new interval end. |
|
878 double newEnd = end + cache.increment; |
|
879 |
|
880 if (ms <= newEnd) { |
|
881 double endOffset = calculateDSTOffset(newEnd, utcOffset); |
|
882 if (cache.offset == endOffset) { |
|
883 // If the offset at the end of the new interval still matches |
|
884 // the offset in the cache, we grow the cached time interval |
|
885 // and return the offset. |
|
886 cache.end = newEnd; |
|
887 cache.increment = msPerMonth; |
|
888 return endOffset; |
|
889 } else { |
|
890 double offset = calculateDSTOffset(ms, utcOffset); |
|
891 if (offset == endOffset) { |
|
892 // The offset at the given time is equal to the offset at the |
|
893 // new end of the interval, so that means that we've just skipped |
|
894 // the point in time where the DST offset change occurred. Updated |
|
895 // the interval to reflect this and reset the increment. |
|
896 cache.start = ms; |
|
897 cache.end = newEnd; |
|
898 cache.increment = msPerMonth; |
|
899 } else { |
|
900 // The interval contains a DST offset change and the given time is |
|
901 // before it. Adjust the increment to avoid a linear search for |
|
902 // the offset change point and change the end of the interval. |
|
903 cache.increment /= 3; |
|
904 cache.end = ms; |
|
905 } |
|
906 // Update the offset in the cache and return it. |
|
907 cache.offset = offset; |
|
908 return offset; |
|
909 } |
|
910 } |
|
911 } |
|
912 |
|
913 // Compute the DST offset for the time and shrink the cache interval |
|
914 // to only contain the time. This allows fast repeated DST offset |
|
915 // computations for the same time. |
|
916 double offset = calculateDSTOffset(ms, utcOffset); |
|
917 cache.offset = offset; |
|
918 cache.start = ms; |
|
919 cache.end = ms; |
|
920 cache.increment = msPerMonth; |
|
921 return offset; |
|
922 } |
|
923 |
|
924 /* |
|
925 * Get the difference in milliseconds between this time zone and UTC (GMT) |
|
926 * NOT including DST. |
|
927 */ |
|
928 double getUTCOffset(ExecState* exec) |
|
929 { |
|
930 double utcOffset = exec->globalData().cachedUTCOffset; |
|
931 if (!isnan(utcOffset)) |
|
932 return utcOffset; |
|
933 exec->globalData().cachedUTCOffset = calculateUTCOffset(); |
|
934 return exec->globalData().cachedUTCOffset; |
|
935 } |
|
936 |
|
937 double gregorianDateTimeToMS(ExecState* exec, const GregorianDateTime& t, double milliSeconds, bool inputIsUTC) |
|
938 { |
|
939 double day = dateToDaysFrom1970(t.year + 1900, t.month, t.monthDay); |
|
940 double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds); |
|
941 double result = (day * WTF::msPerDay) + ms; |
|
942 |
|
943 if (!inputIsUTC) { // convert to UTC |
|
944 double utcOffset = getUTCOffset(exec); |
|
945 result -= utcOffset; |
|
946 result -= getDSTOffset(exec, result, utcOffset); |
|
947 } |
|
948 |
|
949 return result; |
|
950 } |
|
951 |
|
952 // input is UTC |
|
953 void msToGregorianDateTime(ExecState* exec, double ms, bool outputIsUTC, GregorianDateTime& tm) |
|
954 { |
|
955 double dstOff = 0.0; |
|
956 double utcOff = 0.0; |
|
957 if (!outputIsUTC) { |
|
958 utcOff = getUTCOffset(exec); |
|
959 dstOff = getDSTOffset(exec, ms, utcOff); |
|
960 ms += dstOff + utcOff; |
|
961 } |
|
962 |
|
963 const int year = msToYear(ms); |
|
964 tm.second = msToSeconds(ms); |
|
965 tm.minute = msToMinutes(ms); |
|
966 tm.hour = msToHours(ms); |
|
967 tm.weekDay = msToWeekDay(ms); |
|
968 tm.yearDay = dayInYear(ms, year); |
|
969 tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year)); |
|
970 tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year)); |
|
971 tm.year = year - 1900; |
|
972 tm.isDST = dstOff != 0.0; |
|
973 tm.utcOffset = static_cast<long>((dstOff + utcOff) / WTF::msPerSecond); |
|
974 tm.timeZone = NULL; |
|
975 } |
|
976 |
|
977 double parseDateFromNullTerminatedCharacters(ExecState* exec, const char* dateString) |
|
978 { |
|
979 ASSERT(exec); |
|
980 bool haveTZ; |
|
981 int offset; |
|
982 double ms = WTF::parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); |
|
983 if (isnan(ms)) |
|
984 return NaN; |
|
985 |
|
986 // fall back to local timezone |
|
987 if (!haveTZ) { |
|
988 double utcOffset = getUTCOffset(exec); |
|
989 double dstOffset = getDSTOffset(exec, ms, utcOffset); |
|
990 offset = static_cast<int>((utcOffset + dstOffset) / WTF::msPerMinute); |
|
991 } |
|
992 return ms - (offset * WTF::msPerMinute); |
|
993 } |
|
994 |
|
995 } // namespace JSC |
|
996 #endif // USE(JSC) |