1 /* 2 * Copyright (C) 1999-2000 Harri Porten (porten (at) 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) 997