Changeset 44508 in webkit for trunk/JavaScriptCore/runtime/DateConversion.cpp
- Timestamp:
- Jun 8, 2009, 3:37:06 PM (16 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/JavaScriptCore/runtime/DateConversion.cpp
r44501 r44508 2 2 * Copyright (C) 1999-2000 Harri Porten ([email protected]) 3 3 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 4 * 4 * Copyright (C) 2009 Google Inc. All rights reserved. 5 * 5 6 * The Original Code is Mozilla Communicator client code, released 6 7 * March 31, 1998. … … 41 42 42 43 #include "config.h" 43 #include "Date Math.h"44 #include "DateConversion.h" 44 45 45 #include "JSNumberCell.h" 46 #include <math.h> 47 #include <stdint.h> 48 #include <time.h> 49 #include <wtf/ASCIICType.h> 50 #include <wtf/Assertions.h> 51 #include <wtf/CurrentTime.h> 52 #include <wtf/MathExtras.h> 53 #include <wtf/StringExtras.h> 54 55 #if HAVE(ERRNO_H) 56 #include <errno.h> 57 #endif 58 59 #if PLATFORM(DARWIN) 60 #include <notify.h> 61 #endif 62 63 #if HAVE(SYS_TIME_H) 64 #include <sys/time.h> 65 #endif 66 67 #if HAVE(SYS_TIMEB_H) 68 #include <sys/timeb.h> 69 #endif 46 #include <wtf/DateMath.h> 70 47 71 48 using namespace WTF; … … 73 50 namespace JSC { 74 51 75 /* Constants */76 77 static const double minutesPerDay = 24.0 * 60.0;78 static const double secondsPerDay = 24.0 * 60.0 * 60.0;79 static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0;80 81 static const double usecPerSec = 1000000.0;82 83 static const double maxUnixTime = 2145859200.0; // 12/31/203784 85 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1.86 // First for non-leap years, then for leap years.87 static const int firstDayOfMonth[2][12] = {88 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},89 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}90 };91 92 static inline bool isLeapYear(int year)93 {94 if (year % 4 != 0)95 return false;96 if (year % 400 == 0)97 return true;98 if (year % 100 == 0)99 return false;100 return true;101 }102 103 static inline int daysInYear(int year)104 {105 return 365 + isLeapYear(year);106 }107 108 static inline double daysFrom1970ToYear(int year)109 {110 // The Gregorian Calendar rules for leap years:111 // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.112 // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years.113 // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years.114 115 static const int leapDaysBefore1971By4Rule = 1970 / 4;116 static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100;117 static const int leapDaysBefore1971By400Rule = 1970 / 400;118 119 const double yearMinusOne = year - 1;120 const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule;121 const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule;122 const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule;123 124 return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;125 }126 127 static inline double msToDays(double ms)128 {129 return floor(ms / msPerDay);130 }131 132 static inline int msToYear(double ms)133 {134 int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970);135 double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear);136 if (msFromApproxYearTo1970 > ms)137 return approxYear - 1;138 if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms)139 return approxYear + 1;140 return approxYear;141 }142 143 static inline int dayInYear(double ms, int year)144 {145 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));146 }147 148 static inline double msToMilliseconds(double ms)149 {150 double result = fmod(ms, msPerDay);151 if (result < 0)152 result += msPerDay;153 return result;154 }155 156 // 0: Sunday, 1: Monday, etc.157 static inline int msToWeekDay(double ms)158 {159 int wd = (static_cast<int>(msToDays(ms)) + 4) % 7;160 if (wd < 0)161 wd += 7;162 return wd;163 }164 165 static inline int msToSeconds(double ms)166 {167 double result = fmod(floor(ms / msPerSecond), secondsPerMinute);168 if (result < 0)169 result += secondsPerMinute;170 return static_cast<int>(result);171 }172 173 static inline int msToMinutes(double ms)174 {175 double result = fmod(floor(ms / msPerMinute), minutesPerHour);176 if (result < 0)177 result += minutesPerHour;178 return static_cast<int>(result);179 }180 181 static inline int msToHours(double ms)182 {183 double result = fmod(floor(ms/msPerHour), hoursPerDay);184 if (result < 0)185 result += hoursPerDay;186 return static_cast<int>(result);187 }188 189 static inline int monthFromDayInYear(int dayInYear, bool leapYear)190 {191 const int d = dayInYear;192 int step;193 194 if (d < (step = 31))195 return 0;196 step += (leapYear ? 29 : 28);197 if (d < step)198 return 1;199 if (d < (step += 31))200 return 2;201 if (d < (step += 30))202 return 3;203 if (d < (step += 31))204 return 4;205 if (d < (step += 30))206 return 5;207 if (d < (step += 31))208 return 6;209 if (d < (step += 31))210 return 7;211 if (d < (step += 30))212 return 8;213 if (d < (step += 31))214 return 9;215 if (d < (step += 30))216 return 10;217 return 11;218 }219 220 static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth)221 {222 startDayOfThisMonth = startDayOfNextMonth;223 startDayOfNextMonth += daysInThisMonth;224 return (dayInYear <= startDayOfNextMonth);225 }226 227 static inline int dayInMonthFromDayInYear(int dayInYear, bool leapYear)228 {229 const int d = dayInYear;230 int step;231 int next = 30;232 233 if (d <= next)234 return d + 1;235 const int daysInFeb = (leapYear ? 29 : 28);236 if (checkMonth(d, step, next, daysInFeb))237 return d - step;238 if (checkMonth(d, step, next, 31))239 return d - step;240 if (checkMonth(d, step, next, 30))241 return d - step;242 if (checkMonth(d, step, next, 31))243 return d - step;244 if (checkMonth(d, step, next, 30))245 return d - step;246 if (checkMonth(d, step, next, 31))247 return d - step;248 if (checkMonth(d, step, next, 31))249 return d - step;250 if (checkMonth(d, step, next, 30))251 return d - step;252 if (checkMonth(d, step, next, 31))253 return d - step;254 if (checkMonth(d, step, next, 30))255 return d - step;256 step = next;257 return d - step;258 }259 260 static inline int monthToDayInYear(int month, bool isLeapYear)261 {262 return firstDayOfMonth[isLeapYear][month];263 }264 265 static inline double timeToMS(double hour, double min, double sec, double ms)266 {267 return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms);268 }269 270 static int dateToDayInYear(int year, int month, int day)271 {272 year += month / 12;273 274 month %= 12;275 if (month < 0) {276 month += 12;277 --year;278 }279 280 int yearday = static_cast<int>(floor(daysFrom1970ToYear(year)));281 int monthday = monthToDayInYear(month, isLeapYear(year));282 283 return yearday + monthday + day - 1;284 }285 286 double getCurrentUTCTime()287 {288 return floor(getCurrentUTCTimeWithMicroseconds());289 }290 291 // Returns current time in milliseconds since 1 Jan 1970.292 double getCurrentUTCTimeWithMicroseconds()293 {294 return currentTime() * 1000.0;295 }296 297 void getLocalTime(const time_t* localTime, struct tm* localTM)298 {299 #if COMPILER(MSVC7) || COMPILER(MINGW) || PLATFORM(WIN_CE)300 *localTM = *localtime(localTime);301 #elif COMPILER(MSVC)302 localtime_s(localTM, localTime);303 #else304 localtime_r(localTime, localTM);305 #endif306 }307 308 // There is a hard limit at 2038 that we currently do not have a workaround309 // for (rdar://problem/5052975).310 static inline int maximumYearForDST()311 {312 return 2037;313 }314 315 static inline int minimumYearForDST()316 {317 // Because of the 2038 issue (see maximumYearForDST) if the current year is318 // greater than the max year minus 27 (2010), we want to use the max year319 // minus 27 instead, to ensure there is a range of 28 years that all years320 // can map to.321 return std::min(msToYear(getCurrentUTCTime()), maximumYearForDST() - 27) ;322 }323 324 /*325 * Find an equivalent year for the one given, where equivalence is deterined by326 * the two years having the same leapness and the first day of the year, falling327 * on the same day of the week.328 *329 * This function returns a year between this current year and 2037, however this330 * function will potentially return incorrect results if the current year is after331 * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after332 * 2100, (rdar://problem/5055038).333 */334 int equivalentYearForDST(int year)335 {336 // It is ok if the cached year is not the current year as long as the rules337 // for DST did not change between the two years; if they did the app would need338 // to be restarted.339 static int minYear = minimumYearForDST();340 int maxYear = maximumYearForDST();341 342 int difference;343 if (year > maxYear)344 difference = minYear - year;345 else if (year < minYear)346 difference = maxYear - year;347 else348 return year;349 350 int quotient = difference / 28;351 int product = (quotient) * 28;352 353 year += product;354 ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(NaN)));355 return year;356 }357 358 static int32_t calculateUTCOffset()359 {360 tm localt;361 memset(&localt, 0, sizeof(localt));362 363 // get the difference between this time zone and UTC on Jan 01, 2000 12:00:00 AM364 localt.tm_mday = 1;365 localt.tm_year = 100;366 time_t utcOffset = 946684800 - mktime(&localt);367 368 return static_cast<int32_t>(utcOffset * 1000);369 }370 371 #if PLATFORM(DARWIN)372 static int32_t s_cachedUTCOffset; // In milliseconds. An assumption here is that access to an int32_t variable is atomic on platforms that take this code path.373 static bool s_haveCachedUTCOffset;374 static int s_notificationToken;375 #endif376 377 /*378 * Get the difference in milliseconds between this time zone and UTC (GMT)379 * NOT including DST.380 */381 double getUTCOffset()382 {383 #if PLATFORM(DARWIN)384 if (s_haveCachedUTCOffset) {385 int notified;386 uint32_t status = notify_check(s_notificationToken, ¬ified);387 if (status == NOTIFY_STATUS_OK && !notified)388 return s_cachedUTCOffset;389 }390 #endif391 392 int32_t utcOffset = calculateUTCOffset();393 394 #if PLATFORM(DARWIN)395 // Theoretically, it is possible that several threads will be executing this code at once, in which case we will have a race condition,396 // and a newer value may be overwritten. In practice, time zones don't change that often.397 s_cachedUTCOffset = utcOffset;398 #endif399 400 return utcOffset;401 }402 403 /*404 * Get the DST offset for the time passed in. Takes405 * seconds (not milliseconds) and cannot handle dates before 1970406 * on some OS'407 */408 static double getDSTOffsetSimple(double localTimeSeconds, double utcOffset)409 {410 if (localTimeSeconds > maxUnixTime)411 localTimeSeconds = maxUnixTime;412 else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0)413 localTimeSeconds += secondsPerDay;414 415 //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset()416 double offsetTime = (localTimeSeconds * msPerSecond) + utcOffset;417 418 // Offset from UTC but doesn't include DST obviously419 int offsetHour = msToHours(offsetTime);420 int offsetMinute = msToMinutes(offsetTime);421 422 // FIXME: time_t has a potential problem in 2038423 time_t localTime = static_cast<time_t>(localTimeSeconds);424 425 tm localTM;426 getLocalTime(&localTime, &localTM);427 428 double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60);429 430 if (diff < 0)431 diff += secondsPerDay;432 433 return (diff * msPerSecond);434 }435 436 // Get the DST offset, given a time in UTC437 static double getDSTOffset(double ms, double utcOffset)438 {439 // On Mac OS X, the call to localtime (see getDSTOffsetSimple) will return historically accurate440 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript441 // standard explicitly dictates that historical information should not be considered when442 // determining DST. For this reason we shift away from years that localtime can handle but would443 // return historically accurate information.444 int year = msToYear(ms);445 int equivalentYear = equivalentYearForDST(year);446 if (year != equivalentYear) {447 bool leapYear = isLeapYear(year);448 int dayInYearLocal = dayInYear(ms, year);449 int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear);450 int month = monthFromDayInYear(dayInYearLocal, leapYear);451 int day = dateToDayInYear(equivalentYear, month, dayInMonth);452 ms = (day * msPerDay) + msToMilliseconds(ms);453 }454 455 return getDSTOffsetSimple(ms / msPerSecond, utcOffset);456 }457 458 double gregorianDateTimeToMS(const GregorianDateTime& t, double milliSeconds, bool inputIsUTC)459 {460 int day = dateToDayInYear(t.year + 1900, t.month, t.monthDay);461 double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds);462 double result = (day * msPerDay) + ms;463 464 if (!inputIsUTC) { // convert to UTC465 double utcOffset = getUTCOffset();466 result -= utcOffset;467 result -= getDSTOffset(result, utcOffset);468 }469 470 return result;471 }472 473 void msToGregorianDateTime(double ms, bool outputIsUTC, GregorianDateTime& tm)474 {475 // input is UTC476 double dstOff = 0.0;477 const double utcOff = getUTCOffset();478 479 if (!outputIsUTC) { // convert to local time480 dstOff = getDSTOffset(ms, utcOff);481 ms += dstOff + utcOff;482 }483 484 const int year = msToYear(ms);485 tm.second = msToSeconds(ms);486 tm.minute = msToMinutes(ms);487 tm.hour = msToHours(ms);488 tm.weekDay = msToWeekDay(ms);489 tm.yearDay = dayInYear(ms, year);490 tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year));491 tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year));492 tm.year = year - 1900;493 tm.isDST = dstOff != 0.0;494 495 tm.utcOffset = static_cast<long>((dstOff + utcOff) / msPerSecond);496 tm.timeZone = NULL;497 }498 499 void initDateMath()500 {501 #ifndef NDEBUG502 static bool alreadyInitialized;503 ASSERT(!alreadyInitialized++);504 #endif505 506 equivalentYearForDST(2000); // Need to call once to initialize a static used in this function.507 #if PLATFORM(DARWIN)508 // Register for a notification whenever the time zone changes.509 uint32_t status = notify_register_check("com.apple.system.timezone", &s_notificationToken);510 if (status == NOTIFY_STATUS_OK) {511 s_cachedUTCOffset = calculateUTCOffset();512 s_haveCachedUTCOffset = true;513 }514 #endif515 }516 517 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second)518 {519 double days = (day - 32075)520 + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4)521 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12522 - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4)523 - 2440588;524 return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;525 }526 527 // We follow the recommendation of RFC 2822 to consider all528 // obsolete time zones not listed here equivalent to "-0000".529 static const struct KnownZone {530 #if !PLATFORM(WIN_OS)531 const532 #endif533 char tzName[4];534 int tzOffset;535 } known_zones[] = {536 { "UT", 0 },537 { "GMT", 0 },538 { "EST", -300 },539 { "EDT", -240 },540 { "CST", -360 },541 { "CDT", -300 },542 { "MST", -420 },543 { "MDT", -360 },544 { "PST", -480 },545 { "PDT", -420 }546 };547 548 inline static void skipSpacesAndComments(const char*& s)549 {550 int nesting = 0;551 char ch;552 while ((ch = *s)) {553 if (!isASCIISpace(ch)) {554 if (ch == '(')555 nesting++;556 else if (ch == ')' && nesting > 0)557 nesting--;558 else if (nesting == 0)559 break;560 }561 s++;562 }563 }564 565 // returns 0-11 (Jan-Dec); -1 on failure566 static int findMonth(const char* monthStr)567 {568 ASSERT(monthStr);569 char needle[4];570 for (int i = 0; i < 3; ++i) {571 if (!*monthStr)572 return -1;573 needle[i] = static_cast<char>(toASCIILower(*monthStr++));574 }575 needle[3] = '\0';576 const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";577 const char *str = strstr(haystack, needle);578 if (str) {579 int position = static_cast<int>(str - haystack);580 if (position % 3 == 0)581 return position / 3;582 }583 return -1;584 }585 586 static bool parseLong(const char* string, char** stopPosition, int base, long* result)587 {588 *result = strtol(string, stopPosition, base);589 // Avoid the use of errno as it is not available on Windows CE590 if (string == *stopPosition || *result == LONG_MIN || *result == LONG_MAX)591 return false;592 return true;593 }594 595 52 double parseDate(const UString &date) 596 53 { 597 // This parses a date in the form: 598 // Tuesday, 09-Nov-99 23:12:40 GMT 599 // or 600 // Sat, 01-Jan-2000 08:00:00 GMT 601 // or 602 // Sat, 01 Jan 2000 08:00:00 GMT 603 // or 604 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822) 605 // ### non RFC formats, added for Javascript: 606 // [Wednesday] January 09 1999 23:12:40 GMT 607 // [Wednesday] January 09 23:12:40 GMT 1999 608 // 609 // We ignore the weekday. 610 611 CString dateCString = date.UTF8String(); 612 const char *dateString = dateCString.c_str(); 613 614 // Skip leading space 615 skipSpacesAndComments(dateString); 616 617 long month = -1; 618 const char *wordStart = dateString; 619 // Check contents of first words if not number 620 while (*dateString && !isASCIIDigit(*dateString)) { 621 if (isASCIISpace(*dateString) || *dateString == '(') { 622 if (dateString - wordStart >= 3) 623 month = findMonth(wordStart); 624 skipSpacesAndComments(dateString); 625 wordStart = dateString; 626 } else 627 dateString++; 628 } 629 630 // Missing delimiter between month and day (like "January29")? 631 if (month == -1 && wordStart != dateString) 632 month = findMonth(wordStart); 633 634 skipSpacesAndComments(dateString); 635 636 if (!*dateString) 637 return NaN; 638 639 // ' 09-Nov-99 23:12:40 GMT' 640 char* newPosStr; 641 long day; 642 if (!parseLong(dateString, &newPosStr, 10, &day)) 643 return NaN; 644 dateString = newPosStr; 645 646 if (!*dateString) 647 return NaN; 648 649 if (day < 0) 650 return NaN; 651 652 long year = 0; 653 if (day > 31) { 654 // ### where is the boundary and what happens below? 655 if (*dateString != '/') 656 return NaN; 657 // looks like a YYYY/MM/DD date 658 if (!*++dateString) 659 return NaN; 660 year = day; 661 if (!parseLong(dateString, &newPosStr, 10, &month)) 662 return NaN; 663 month -= 1; 664 dateString = newPosStr; 665 if (*dateString++ != '/' || !*dateString) 666 return NaN; 667 if (!parseLong(dateString, &newPosStr, 10, &day)) 668 return NaN; 669 dateString = newPosStr; 670 } else if (*dateString == '/' && month == -1) { 671 dateString++; 672 // This looks like a MM/DD/YYYY date, not an RFC date. 673 month = day - 1; // 0-based 674 if (!parseLong(dateString, &newPosStr, 10, &day)) 675 return NaN; 676 if (day < 1 || day > 31) 677 return NaN; 678 dateString = newPosStr; 679 if (*dateString == '/') 680 dateString++; 681 if (!*dateString) 682 return NaN; 683 } else { 684 if (*dateString == '-') 685 dateString++; 686 687 skipSpacesAndComments(dateString); 688 689 if (*dateString == ',') 690 dateString++; 691 692 if (month == -1) { // not found yet 693 month = findMonth(dateString); 694 if (month == -1) 695 return NaN; 696 697 while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString)) 698 dateString++; 699 700 if (!*dateString) 701 return NaN; 702 703 // '-99 23:12:40 GMT' 704 if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString)) 705 return NaN; 706 dateString++; 707 } 708 } 709 710 if (month < 0 || month > 11) 711 return NaN; 712 713 // '99 23:12:40 GMT' 714 if (year <= 0 && *dateString) { 715 if (!parseLong(dateString, &newPosStr, 10, &year)) 716 return NaN; 717 } 718 719 // Don't fail if the time is missing. 720 long hour = 0; 721 long minute = 0; 722 long second = 0; 723 if (!*newPosStr) 724 dateString = newPosStr; 725 else { 726 // ' 23:12:40 GMT' 727 if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) { 728 if (*newPosStr != ':') 729 return NaN; 730 // There was no year; the number was the hour. 731 year = -1; 732 } else { 733 // in the normal case (we parsed the year), advance to the next number 734 dateString = ++newPosStr; 735 skipSpacesAndComments(dateString); 736 } 737 738 parseLong(dateString, &newPosStr, 10, &hour); 739 // Do not check for errno here since we want to continue 740 // even if errno was set becasue we are still looking 741 // for the timezone! 742 743 // Read a number? If not, this might be a timezone name. 744 if (newPosStr != dateString) { 745 dateString = newPosStr; 746 747 if (hour < 0 || hour > 23) 748 return NaN; 749 750 if (!*dateString) 751 return NaN; 752 753 // ':12:40 GMT' 754 if (*dateString++ != ':') 755 return NaN; 756 757 if (!parseLong(dateString, &newPosStr, 10, &minute)) 758 return NaN; 759 dateString = newPosStr; 760 761 if (minute < 0 || minute > 59) 762 return NaN; 763 764 // ':40 GMT' 765 if (*dateString && *dateString != ':' && !isASCIISpace(*dateString)) 766 return NaN; 767 768 // seconds are optional in rfc822 + rfc2822 769 if (*dateString ==':') { 770 dateString++; 771 772 if (!parseLong(dateString, &newPosStr, 10, &second)) 773 return NaN; 774 dateString = newPosStr; 775 776 if (second < 0 || second > 59) 777 return NaN; 778 } 779 780 skipSpacesAndComments(dateString); 781 782 if (strncasecmp(dateString, "AM", 2) == 0) { 783 if (hour > 12) 784 return NaN; 785 if (hour == 12) 786 hour = 0; 787 dateString += 2; 788 skipSpacesAndComments(dateString); 789 } else if (strncasecmp(dateString, "PM", 2) == 0) { 790 if (hour > 12) 791 return NaN; 792 if (hour != 12) 793 hour += 12; 794 dateString += 2; 795 skipSpacesAndComments(dateString); 796 } 797 } 798 } 799 800 bool haveTZ = false; 801 int offset = 0; 802 803 // Don't fail if the time zone is missing. 804 // Some websites omit the time zone (4275206). 805 if (*dateString) { 806 if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) { 807 dateString += 3; 808 haveTZ = true; 809 } 810 811 if (*dateString == '+' || *dateString == '-') { 812 long o; 813 if (!parseLong(dateString, &newPosStr, 10, &o)) 814 return NaN; 815 dateString = newPosStr; 816 817 if (o < -9959 || o > 9959) 818 return NaN; 819 820 int sgn = (o < 0) ? -1 : 1; 821 o = abs(o); 822 if (*dateString != ':') { 823 offset = ((o / 100) * 60 + (o % 100)) * sgn; 824 } else { // GMT+05:00 825 long o2; 826 if (!parseLong(dateString, &newPosStr, 10, &o2)) 827 return NaN; 828 dateString = newPosStr; 829 offset = (o * 60 + o2) * sgn; 830 } 831 haveTZ = true; 832 } else { 833 for (int i = 0; i < int(sizeof(known_zones) / sizeof(KnownZone)); i++) { 834 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) { 835 offset = known_zones[i].tzOffset; 836 dateString += strlen(known_zones[i].tzName); 837 haveTZ = true; 838 break; 839 } 840 } 841 } 842 } 843 844 skipSpacesAndComments(dateString); 845 846 if (*dateString && year == -1) { 847 if (!parseLong(dateString, &newPosStr, 10, &year)) 848 return NaN; 849 dateString = newPosStr; 850 } 851 852 skipSpacesAndComments(dateString); 853 854 // Trailing garbage 855 if (*dateString) 856 return NaN; 857 858 // Y2K: Handle 2 digit years. 859 if (year >= 0 && year < 100) { 860 if (year < 50) 861 year += 2000; 862 else 863 year += 1900; 864 } 865 866 // fall back to local timezone 867 if (!haveTZ) { 868 GregorianDateTime t; 869 t.monthDay = day; 870 t.month = month; 871 t.year = year - 1900; 872 t.isDST = -1; 873 t.second = second; 874 t.minute = minute; 875 t.hour = hour; 876 877 // Use our gregorianDateTimeToMS() rather than mktime() as the latter can't handle the full year range. 878 return gregorianDateTimeToMS(t, 0, false); 879 } 880 881 return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond; 882 } 883 884 double timeClip(double t) 885 { 886 if (!isfinite(t)) 887 return NaN; 888 if (fabs(t) > 8.64E15) 889 return NaN; 890 return trunc(t); 54 return parseDateFromNullTerminatedCharacters(date.UTF8String().c_str()); 891 55 } 892 56
Note:
See TracChangeset
for help on using the changeset viewer.