Ignore:
Timestamp:
Jul 1, 2005, 11:45:13 AM (20 years ago)
Author:
ggaren
Message:

JavaScriptCore:

-landed patch by Carsten Guenther <[email protected]>

-fixes http://bugzilla.opendarwin.org/show_bug.cgi?id=3477
some US-centric date formats not parsed by JavaScript (clock at news8austin.com)

-relevant tests:

mozilla/ecma_3/Date/15.9.5.5.js
layout-tests/fast/js/date-parse-test.html

Reviewed by darin.

  • kjs/date_object.cpp: (formatLocaleDate): (day): (dayFromYear): (daysInYear): (timeFromYear): (yearFromTime): (weekDay): (timeZoneOffset): (DateProtoFuncImp::call): (DateObjectImp::construct): (KJS::parseDate): (ymdhms_to_seconds): (KJS::makeTime): (findMonth): (KJS::KRFCDate_parseDate):
  • kjs/date_object.h:
  • tests/mozilla/expected.html: updated expected results to reflect fix

WebCore:

-landed patch by Carsten Guenther <[email protected]>
http://bugzilla.opendarwin.org/show_bug.cgi?id=3477

-corrects errors in date layout test

Reviewed by darin.

Test cases modified:

  • layout-tests/fast/js/date-parse-test.html:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/date_object.cpp

    r9525 r9559  
    5959#include "date_object.lut.h"
    6060
     61// some constants
    6162const time_t invalidDate = -1;
     63const double hoursPerDay = 24;
     64const double minutesPerHour = 60;
     65const double secondsPerMinute = 60;
     66const double msPerSecond = 1000;
     67const double msPerMinute = msPerSecond * secondsPerMinute;
     68const double msPerHour = msPerMinute * minutesPerHour;
     69const double msPerDay = msPerHour * hoursPerDay;
    6270
    6371#if APPLE_CHANGES
     
    256264}
    257265
    258 static UString formatLocaleDate(KJS::ExecState *exec,time_t tv, bool includeDate, bool includeTime, const KJS::List &args)
    259 {
    260     LongDateTime longDateTime;
    261     UCConvertCFAbsoluteTimeToLongDateTime(tv - kCFAbsoluteTimeIntervalSince1970, &longDateTime);
    262 
     266static UString formatLocaleDate(KJS::ExecState *exec, double time, bool includeDate, bool includeTime, const KJS::List &args)
     267{
    263268    CFLocaleRef locale = CFLocaleCopyCurrent();
    264    
    265269    int argCount = args.size();
    266270   
     
    291295        CFRelease(customFormatCFString);
    292296    }
    293     CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(NULL, formatter, tv - kCFAbsoluteTimeIntervalSince1970);
    294 
     297    CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(NULL, formatter, time - kCFAbsoluteTimeIntervalSince1970);
    295298    // We truncate the string returned from CFDateFormatter if it's absurdly long (> 200 characters).
    296299    // That's not great error handling, but it just won't happen so it doesn't matter.
     
    313316
    314317using namespace KJS;
     318
     319static int day(double t)
     320{
     321  return int(floor(t / msPerDay));
     322}
     323
     324static double dayFromYear(int year)
     325{
     326  return 365.0 * (year - 1970)
     327    + floor((year - 1969) / 4.0)
     328    - floor((year - 1901) / 100.0)
     329    + floor((year - 1601) / 400.0);
     330}
     331
     332// depending on whether it's a leap year or not
     333static int daysInYear(int year)
     334{
     335  if (year % 4 != 0)
     336    return 365;
     337  else if (year % 400 == 0)
     338    return 366;
     339  else if (year % 100 == 0)
     340    return 365;
     341  else
     342    return 366;
     343}
     344
     345// time value of the start of a year
     346static double timeFromYear(int year)
     347{
     348  return msPerDay * dayFromYear(year);
     349}
     350
     351// year determined by time value
     352static int yearFromTime(double t)
     353{
     354  // ### there must be an easier way
     355  // initial guess
     356  int y = 1970 + int(t / (365.25 * msPerDay));
     357  // adjustment
     358  if (timeFromYear(y) > t) {
     359    do {
     360      --y;
     361    } while (timeFromYear(y) > t);
     362  } else {
     363    while (timeFromYear(y + 1) < t)
     364      ++y;
     365  }
     366
     367  return y;
     368}
     369
     370// 0: Sunday, 1: Monday, etc.
     371static int weekDay(double t)
     372{
     373  int wd = (day(t) + 4) % 7;
     374  if (wd < 0)
     375    wd += 7;
     376  return wd;
     377}
     378
     379static double timeZoneOffset(const struct tm *t)
     380{
     381#if defined BSD || defined(__linux__) || defined(__APPLE__)
     382  return -(t->tm_gmtoff / 60);
     383#else
     384#  if defined(__BORLANDC__) || defined(__CYGWIN__)
     385// FIXME consider non one-hour DST change
     386#if !defined(__CYGWIN__)
     387#error please add daylight savings offset here!
     388#endif
     389  return _timezone / 60 - (t->tm_isdst > 0 ? 60 : 0);
     390#  else
     391  return timezone / 60 - (t->tm_isdst > 0 ? 60 : 0 );
     392#  endif
     393#endif
     394}
    315395
    316396// ------------------------------ DateInstanceImp ------------------------------
     
    462542  }
    463543 
    464   time_t tv = (time_t)(milli / 1000.0);
     544  // check whether time value is outside time_t's usual range
     545  // make the necessary transformations if necessary
     546  int realYearOffset = 0;
     547  double milliOffset = 0.0;
     548  double secs = floor(milli / 1000.0);
     549
     550  if (milli < 0 || milli >= timeFromYear(2038)) {
     551    // ### ugly and probably not very precise
     552    int realYear = yearFromTime(milli);
     553    int base = daysInYear(realYear) == 365 ? 2001 : 2000;
     554    milliOffset = timeFromYear(base) - timeFromYear(realYear);
     555    milli += milliOffset;
     556    realYearOffset = realYear - base;
     557  }
     558
     559  time_t tv = (time_t) floor(milli / 1000.0);
    465560  int ms = int(milli - tv * 1000.0);
    466561
    467   struct tm *t;
    468   if (utc)
    469     t = gmtime(&tv);
    470   else
    471     t = localtime(&tv);
    472 
     562  struct tm *t = utc ? gmtime(&tv) : localtime(&tv);
     563  // we had an out of range year. use that one (plus/minus offset
     564  // found by calculating tm_year) and fix the week day calculation
     565  if (realYearOffset != 0) {
     566    t->tm_year += realYearOffset;
     567    milli -= milliOffset;
     568    // our own weekday calculation. beware of need for local time.
     569    double m = milli;
     570    if (!utc)
     571      m -= timeZoneOffset(t) * msPerMinute;
     572    t->tm_wday = weekDay(m);
     573  }
     574 
    473575  switch (id) {
    474576#if APPLE_CHANGES
     
    487589    break;
    488590  case ToLocaleString:
    489     result = String(formatLocaleDate(exec,tv, true, true,args));
     591    result = String(formatLocaleDate(exec, secs, true, true, args));
    490592    break;
    491593  case ToLocaleDateString:
    492     result = String(formatLocaleDate(exec,tv, true, false,args));
     594    result = String(formatLocaleDate(exec, secs, true, false, args));
    493595    break;
    494596  case ToLocaleTimeString:
    495     result = String(formatLocaleDate(exec,tv, false, true,args));
     597    result = String(formatLocaleDate(exec, secs, false, true, args));
    496598    break;
    497599#else
     
    631733      id == SetMinutes || id == SetHours || id == SetDate ||
    632734      id == SetMonth || id == SetFullYear ) {
    633     time_t mktimeResult = utc ? timegm(t) : mktime(t);
    634     if (mktimeResult == invalidDate)
    635       result = Number(NaN);
    636     else
    637       result = Number(mktimeResult * 1000.0 + ms);
     735    result = Number(makeTime(t, ms, utc));
    638736    thisObj.setInternalValue(result);
    639737  }
    640 
     738 
    641739  return result;
    642740}
     
    724822      t.tm_isdst = -1;
    725823      int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
    726       time_t mktimeResult = mktime(&t);
    727       if (mktimeResult == invalidDate)
    728         value = NaN;
    729       else
    730         value = mktimeResult * 1000.0 + ms;
     824      value = makeTime(&t, ms, false);
    731825    }
    732826  }
    733 
     827 
    734828  Object proto = exec->lexicalInterpreter()->builtinDatePrototype();
    735829  Object ret(new DateInstanceImp(proto.imp()));
     
    818912  fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
    819913#endif
    820   int firstSlash = u.find('/');
    821   if ( firstSlash == -1 )
    822   {
    823     time_t seconds = KRFCDate_parseDate( u );
    824 #ifdef KJS_VERBOSE
    825     fprintf(stderr,"KRFCDate_parseDate returned seconds=%d\n",seconds);
    826 #endif
    827     if ( seconds == invalidDate )
    828       return NaN;
    829     else
    830       return seconds * 1000.0;
    831   }
    832   else
    833   {
    834     // Found 12/31/2099 on some website -> obviously MM/DD/YYYY
    835     int month = u.substr(0,firstSlash).toULong();
    836     int secondSlash = u.find('/',firstSlash+1);
    837     //fprintf(stdout,"KJS::parseDate firstSlash=%d, secondSlash=%d\n", firstSlash, secondSlash);
    838     if ( secondSlash == -1 )
    839     {
    840       fprintf(stderr,"KJS::parseDate parsing for this format isn't implemented\n%s", u.ascii());
    841       return NaN;
    842     }
    843     int day = u.substr(firstSlash+1,secondSlash-firstSlash-1).toULong();
    844     int year = u.substr(secondSlash+1).toULong();
    845     //fprintf(stdout,"KJS::parseDate day=%d, month=%d, year=%d\n", day, month, year);
    846     struct tm t;
    847     memset( &t, 0, sizeof(t) );
    848 #if !APPLE_CHANGES
    849     year = (year > 2037) ? 2037 : year; // mktime is limited to 2037 !!!
    850 #endif
    851     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
    852     t.tm_mon = month-1; // mktime wants 0-11 for some reason
    853     t.tm_mday = day;
    854     time_t seconds = mktime(&t);
    855     if ( seconds == invalidDate )
    856     {
    857 #if !APPLE_CHANGES
    858       fprintf(stderr,"KJS::parseDate mktime returned -1.\n%s", u.ascii());
    859 #endif
    860       return NaN;
    861     }
    862     else
    863       return seconds * 1000.0;
    864   }
     914  double /*time_t*/ seconds = KRFCDate_parseDate( u );
     915
     916  return seconds == invalidDate ? NaN : seconds * 1000.0;
    865917}
    866918
    867919///// Awful duplication from krfcdate.cpp - we don't link to kdecore
    868920
    869 static unsigned int ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
    870 {
    871     unsigned int ret = (day - 32075)       /* days */
     921static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
     922{
     923    double ret = (day - 32075)       /* days */
    872924            + 1461L * (year + 4800L + (mon - 14) / 12) / 4
    873925            + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
     
    885937// we follow the recommendation of rfc2822 to consider all
    886938// obsolete time zones not listed here equivalent to "-0000"
    887 static const struct {
    888     const char *tzName;
     939static const struct KnownZone {
     940#ifdef _WIN32
     941    char tzName[4];
     942#else
     943    const char tzName[4];
     944#endif
    889945    int tzOffset;
    890946} known_zones[] = {
     
    898954    { "MDT", -360 },
    899955    { "PST", -480 },
    900     { "PDT", -420 },
    901     { 0, 0 }
     956    { "PDT", -420 }
    902957};
    903958
    904 static inline bool isSpaceOrTab(char c)
    905 {
    906     return c == ' ' || c == '\t';
    907 }
    908 
    909 time_t KJS::KRFCDate_parseDate(const UString &_date)
     959double KJS::makeTime(struct tm *t, int ms, bool utc)
     960{
     961    int utcOffset;
     962    if (utc) {
     963        time_t zero = 0;
     964#if defined BSD || defined(__linux__) || defined(__APPLE__)
     965        struct tm *t3 = localtime(&zero);
     966        utcOffset = t3->tm_gmtoff;
     967        t->tm_isdst = t3->tm_isdst;
     968#else
     969        (void)localtime(&zero);
     970#  if defined(__BORLANDC__) || defined(__CYGWIN__)
     971        utcOffset = - _timezone;
     972#  else
     973        utcOffset = - timezone;
     974#  endif
     975        t->tm_isdst = 0;
     976#endif
     977    } else {
     978        utcOffset = 0;
     979        t->tm_isdst = -1;
     980    }
     981
     982    double yearOffset = 0.0;
     983    if (t->tm_year < (1970 - 1900) || t->tm_year > (2038 - 1900)) {
     984      // we'll fool mktime() into believing that this year is within
     985      // its normal, portable range (1970-2038) by setting tm_year to
     986      // 2000 or 2001 and adding the difference in milliseconds later.
     987      // choice between offset will depend on whether the year is a
     988      // leap year or not.
     989      int y = t->tm_year + 1900;
     990      int baseYear = daysInYear(y) == 365 ? 2001 : 2000;
     991      const double baseTime = timeFromYear(baseYear);
     992      yearOffset = timeFromYear(y) - baseTime;
     993      t->tm_year = baseYear - 1900;
     994    }
     995
     996    return (mktime(t) + utcOffset) * 1000.0 + ms + yearOffset;
     997}
     998
     999// returns 0-11 (Jan-Dec); -1 on failure
     1000static int findMonth(const char *monthStr)
     1001{
     1002  assert(monthStr);
     1003  static const char haystack[37] = "janfebmaraprmayjunjulaugsepoctnovdec";
     1004  char needle[4];
     1005  for (int i = 0; i < 3; ++i) {
     1006    if (!*monthStr)
     1007      return -1;
     1008    needle[i] = tolower(*monthStr++);
     1009  }
     1010  needle[3] = '\0';
     1011  const char *str = strstr(haystack, needle);
     1012  if (str) {
     1013    int position = str - haystack;
     1014    if (position % 3 == 0) {
     1015      return position / 3;
     1016    }
     1017  }
     1018  return -1;
     1019}
     1020
     1021double KJS::KRFCDate_parseDate(const UString &_date)
    9101022{
    9111023     // This parse a date in the form:
    912      //     Wednesday, 09-Nov-99 23:12:40 GMT
     1024     //     Tuesday, 09-Nov-99 23:12:40 GMT
    9131025     // or
    9141026     //     Sat, 01-Jan-2000 08:00:00 GMT
     
    9171029     // or
    9181030     //     01 Jan 99 22:00 +0100    (exceptions in rfc822/rfc2822)
    919      // ### non RFC format, added for Javascript:
     1031     // ### non RFC formats, added for Javascript:
    9201032     //     [Wednesday] January 09 1999 23:12:40 GMT
     1033     //     [Wednesday] January 09 23:12:40 GMT 1999
    9211034     //
    9221035     // We ignore the weekday
    9231036     //
     1037     double result = -1;
    9241038     int offset = 0;
     1039     bool have_tz = false;
    9251040     char *newPosStr;
    9261041     const char *dateString = _date.ascii();
    9271042     int day = 0;
    928      char monthStr[4];
    9291043     int month = -1; // not set yet
    9301044     int year = 0;
     
    9321046     int minute = 0;
    9331047     int second = 0;
    934 
     1048     bool have_time = false;
     1049     
     1050     // for strtol error checking
    9351051     errno = 0;
    9361052
    9371053     // Skip leading space
    938      while (isSpaceOrTab(*dateString))
     1054     while(isspace(*dateString))
    9391055        dateString++;
    9401056
     
    9431059     while(*dateString && !isdigit(*dateString))
    9441060     {
    945         if ( isSpaceOrTab(*dateString) && dateString - wordStart >= 3 )
     1061        if ( isspace(*dateString) && dateString - wordStart >= 3 )
    9461062        {
    947           monthStr[0] = tolower(*wordStart++);
    948           monthStr[1] = tolower(*wordStart++);
    949           monthStr[2] = tolower(*wordStart++);
    950           monthStr[3] = '\0';
    951           //fprintf(stderr,"KJS::parseDate found word starting with '%s'\n", monthStr);
    952           const char *str = strstr(haystack, monthStr);
    953           if (str) {
    954             int position = str - haystack;
    955             if (position % 3 == 0) {
    956               month = position / 3; // Jan=00, Feb=01, Mar=02, ..
    957             }
    958           }
    959           while (isSpaceOrTab(*dateString))
     1063          month = findMonth(wordStart);
     1064          while(isspace(*dateString))
    9601065             dateString++;
    9611066          wordStart = dateString;
     
    9641069           dateString++;
    9651070     }
    966 
    967      while (isSpaceOrTab(*dateString))
     1071     // missing delimiter between month and day (like "January29")?
     1072     if (month == -1 && dateString && wordStart != dateString) {
     1073       month = findMonth(wordStart);
     1074       // TODO: emit warning about dubious format found
     1075     }
     1076
     1077     while(isspace(*dateString))
    9681078        dateString++;
    9691079
     
    9741084     day = strtol(dateString, &newPosStr, 10);
    9751085     if (errno)
    976         return invalidDate;
     1086       return invalidDate;
    9771087     dateString = newPosStr;
    9781088
    979      if ((day < 1) || (day > 31))
    980         return invalidDate;
    9811089     if (!*dateString)
    9821090        return invalidDate;
    9831091
    984      if (*dateString == '-' || *dateString == ',')
     1092     if (day < 1)
     1093       return invalidDate;
     1094     if (day > 31) {
     1095       // ### where is the boundary and what happens below?
     1096       if (*dateString == '/' && day >= 1000) {
     1097         // looks like a YYYY/MM/DD date
     1098         if (!*++dateString)
     1099           return invalidDate;
     1100         year = day;
     1101         month = strtol(dateString, &newPosStr, 10) - 1;
     1102         if (errno)
     1103           return invalidDate;
     1104         dateString = newPosStr;
     1105         if (*dateString++ != '/' || !*dateString)
     1106           return invalidDate;
     1107         day = strtol(dateString, &newPosStr, 10);
     1108         if (errno)
     1109           return invalidDate;
     1110         dateString = newPosStr;
     1111       } else {
     1112         return invalidDate;
     1113       }
     1114     } else if (*dateString == '/' && day <= 12 && month == -1) {
    9851115        dateString++;
    986 
    987      while (isSpaceOrTab(*dateString))
    988         dateString++;
    989 
    990      if ( month == -1 ) // not found yet
     1116        // This looks like a MM/DD/YYYY date, not an RFC date.....
     1117        month = day - 1; // 0-based
     1118        day = strtol(dateString, &newPosStr, 10);
     1119        if (errno)
     1120          return invalidDate;
     1121        dateString = newPosStr;
     1122        if (*dateString == '/')
     1123          dateString++;
     1124        if (!*dateString)
     1125          return invalidDate;
     1126     }
     1127     else
    9911128     {
    992         for(int i=0; i < 3;i++)
    993         {
    994            if (!*dateString || (*dateString == '-') || isSpaceOrTab(*dateString))
    995               return invalidDate;
    996            monthStr[i] = tolower(*dateString++);
    997         }
    998         monthStr[3] = '\0';
    999 
    1000         newPosStr = (char*)strstr(haystack, monthStr);
    1001 
    1002         if (!newPosStr || (newPosStr - haystack) % 3 != 0)
     1129       if (*dateString == '-')
     1130         dateString++;
     1131
     1132       while(isspace(*dateString))
     1133         dateString++;
     1134
     1135       if (*dateString == ',')
     1136         dateString++;
     1137
     1138       if ( month == -1 ) // not found yet
     1139       {
     1140         month = findMonth(dateString);
     1141         if (month == -1)
    10031142           return invalidDate;
    10041143
    1005         month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
    1006 
    1007         if ((month < 0) || (month > 11))
     1144         while(*dateString && (*dateString != '-') && !isspace(*dateString))
     1145           dateString++;
     1146
     1147         if (!*dateString)
    10081148           return invalidDate;
    10091149
    1010         while (*dateString && *dateString != '-' && !isSpaceOrTab(*dateString))
    1011            dateString++;
    1012 
    1013         if (!*dateString)
     1150         // '-99 23:12:40 GMT'
     1151         if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
    10141152           return invalidDate;
    1015 
    1016         // '-99 23:12:40 GMT'
    1017         if (*dateString != '-' && !isSpaceOrTab(*dateString))
    1018            return invalidDate;
    1019         dateString++;
     1153         dateString++;
     1154       }
     1155
     1156       if ((month < 0) || (month > 11))
     1157         return invalidDate;
    10201158     }
    10211159
    1022      if ((month < 0) || (month > 11))
    1023         return invalidDate;
    1024 
    10251160     // '99 23:12:40 GMT'
    1026      bool gotYear = true;
    1027      year = strtol(dateString, &newPosStr, 10);
    1028      if (errno)
    1029         return invalidDate;
    1030      dateString = newPosStr;
    1031 
     1161     if (year <= 0 && *dateString) {
     1162       year = strtol(dateString, &newPosStr, 10);
     1163       if (errno)
     1164         return invalidDate;
     1165    }
     1166   
    10321167     // Don't fail if the time is missing.
    1033      if (*dateString == ':' || (isSpaceOrTab(*dateString) && isdigit(dateString[1])))
     1168     if (*newPosStr)
    10341169     {
    1035         if (*dateString == ':') {
    1036           hour = year;
    1037           gotYear = false;
    1038         } else {
    1039           // ' 23:12:40 GMT'
    1040           ++dateString;
    1041        
    1042           hour = strtol(dateString, &newPosStr, 10);
     1170        // ' 23:12:40 GMT'
     1171        if (!isspace(*newPosStr)) {
     1172           if ( *newPosStr == ':' ) // Ah, so there was no year, but the number was the hour
     1173               year = -1;
     1174           else
     1175               return invalidDate;
     1176        } else // in the normal case (we parsed the year), advance to the next number
     1177            dateString = ++newPosStr;
     1178
     1179        hour = strtol(dateString, &newPosStr, 10);
     1180
     1181        // Do not check for errno here since we want to continue
     1182        // even if errno was set becasue we are still looking
     1183        // for the timezone!
     1184        // read a number? if not this might be a timezone name
     1185        if (newPosStr != dateString) {
     1186          have_time = true;
     1187          dateString = newPosStr;
     1188
     1189          if ((hour < 0) || (hour > 23))
     1190            return invalidDate;
     1191
     1192          if (!*dateString)
     1193            return invalidDate;
     1194
     1195          // ':12:40 GMT'
     1196          if (*dateString++ != ':')
     1197            return invalidDate;
     1198
     1199          minute = strtol(dateString, &newPosStr, 10);
    10431200          if (errno)
    10441201            return invalidDate;
    10451202          dateString = newPosStr;
     1203
     1204          if ((minute < 0) || (minute > 59))
     1205            return invalidDate;
     1206
     1207          // ':40 GMT'
     1208          if (*dateString && *dateString != ':' && !isspace(*dateString))
     1209            return invalidDate;
     1210
     1211          // seconds are optional in rfc822 + rfc2822
     1212          if (*dateString ==':') {
     1213            dateString++;
     1214
     1215            second = strtol(dateString, &newPosStr, 10);
     1216            if (errno)
     1217              return invalidDate;
     1218            dateString = newPosStr;
     1219           
     1220            if ((second < 0) || (second > 59))
     1221              return invalidDate;
     1222          }
     1223
     1224          while(isspace(*dateString))
     1225            dateString++;
     1226
     1227          if (strncasecmp(dateString, "AM", 2) == 0) {
     1228            if (hour > 12)
     1229              return invalidDate;
     1230            if (hour == 12)
     1231              hour = 0;
     1232            dateString += 2;
     1233            while (isspace(*dateString))
     1234              dateString++;
     1235          } else if (strncasecmp(dateString, "PM", 2) == 0) {
     1236            if (hour > 12)
     1237              return invalidDate;
     1238            if (hour != 12)
     1239              hour += 12;
     1240            dateString += 2;
     1241            while (isspace(*dateString))
     1242              dateString++;
     1243          }
    10461244        }
    1047 
    1048         if ((hour < 0) || (hour > 23))
     1245     } else {
     1246       dateString = newPosStr;
     1247     }
     1248
     1249     // don't fail if the time zone is missing, some
     1250     // broken mail-/news-clients omit the time zone
     1251     if (*dateString) {
     1252       if (strncasecmp(dateString, "GMT", 3) == 0 ||
     1253           strncasecmp(dateString, "UTC", 3) == 0) {
     1254         dateString += 3;
     1255         have_tz = true;
     1256       }
     1257
     1258       while (isspace(*dateString))
     1259         ++dateString;
     1260
     1261       if (strncasecmp(dateString, "GMT", 3) == 0) {
     1262         dateString += 3;
     1263       }
     1264       if ((*dateString == '+') || (*dateString == '-')) {
     1265         offset = strtol(dateString, &newPosStr, 10);
     1266         if (errno)
    10491267           return invalidDate;
    1050 
    1051         if (!*dateString)
    1052            return invalidDate;
    1053 
    1054         // ':12:40 GMT'
    1055         if (*dateString++ != ':')
    1056            return invalidDate;
    1057 
    1058         minute = strtol(dateString, &newPosStr, 10);
    1059         if (errno)
    1060           return invalidDate;
    1061         dateString = newPosStr;
    1062 
    1063         if ((minute < 0) || (minute > 59))
    1064            return invalidDate;
    1065 
    1066         // seconds are optional in rfc822 + rfc2822
    1067         if (*dateString ==':') {
    1068            dateString++;
    1069 
    1070            second = strtol(dateString, &newPosStr, 10);
     1268         dateString = newPosStr;
     1269
     1270         if ((offset < -9959) || (offset > 9959))
     1271            return invalidDate;
     1272
     1273         int sgn = (offset < 0)? -1:1;
     1274         offset = abs(offset);
     1275         if ( *dateString == ':' ) { // GMT+05:00
     1276           int offset2 = strtol(dateString, &newPosStr, 10);
    10711277           if (errno)
    10721278             return invalidDate;
    10731279           dateString = newPosStr;
    1074 
    1075            if ((second < 0) || (second > 59))
    1076               return invalidDate;
    1077         }
     1280           offset = (offset*60 + offset2)*sgn;
     1281         }
     1282         else
     1283           offset = ((offset / 100)*60 + (offset % 100))*sgn;
     1284         have_tz = true;
     1285       } else {
     1286         for (int i=0; i < int(sizeof(known_zones)/sizeof(KnownZone)); i++) {
     1287           if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
     1288             offset = known_zones[i].tzOffset;
     1289             have_tz = true;
     1290             break;
     1291           }
     1292         }
     1293         // Bail out if we found an unknown timezone
     1294         if (!have_tz)
     1295             return invalidDate;
     1296       }
    10781297     }
    1079      
    1080      while (isSpaceOrTab(*dateString))
     1298
     1299     while(isspace(*dateString))
    10811300        dateString++;
    10821301
    1083      if (!gotYear) {
    1084         year = strtol(dateString, &newPosStr, 10);
    1085         if (errno)
    1086           return invalidDate;
    1087         while (isSpaceOrTab(*dateString))
    1088            dateString++;
     1302     if ( *dateString && year == -1 ) {
     1303       year = strtol(dateString, &newPosStr, 10);
     1304       if (errno)
     1305         return invalidDate;
    10891306     }
    10901307
     
    10961313         year += 1900;  // Y2K
    10971314
    1098      if ((year < 1900) || (year > 2500))
    1099         return invalidDate;
    1100 
    1101      if (strncasecmp(dateString, "AM", 2) == 0) {
    1102         if (hour < 1 || hour > 12)
    1103             return invalidDate;
    1104         if (hour == 12)
    1105             hour = 0;
    1106         dateString += 2;
    1107         while (isSpaceOrTab(*dateString))
    1108            dateString++;
    1109      } else if (strncasecmp(dateString, "PM", 2) == 0) {
    1110         if (hour < 1 || hour > 12)
    1111             return invalidDate;
    1112         if (hour != 12)
    1113             hour += 12;
    1114         dateString += 2;
    1115         while (isSpaceOrTab(*dateString))
    1116            dateString++;
     1315     if (!have_tz) {
     1316       // fall back to midnight, local timezone
     1317       struct tm t;
     1318       memset(&t, 0, sizeof(tm));
     1319       t.tm_mday = day;
     1320       t.tm_mon = month;
     1321       t.tm_year = year - 1900;
     1322       t.tm_isdst = -1;
     1323       if (have_time) {
     1324         t.tm_sec = second;
     1325         t.tm_min = minute;
     1326         t.tm_hour = hour;
     1327       }
     1328
     1329       // better not use mktime() as it can't handle the full year range
     1330       return makeTime(&t, 0, false) / 1000.0;
    11171331     }
    1118 
    1119      // don't fail if the time zone is missing, some
    1120      // broken mail-/news-clients omit the time zone
    1121      bool localTime;
    1122      if (*dateString == 0) {
    1123         // Other web browsers interpret missing time zone as "current time zone".
    1124         localTime = true;
    1125      } else {
    1126         localTime = false;
    1127         if (strncasecmp(dateString, "GMT", 3) == 0) {
    1128             dateString += 3;
    1129         }
    1130         if ((*dateString == '+') || (*dateString == '-')) {
    1131            offset = strtol(dateString, &newPosStr, 10);
    1132 
    1133            if (errno || (offset < -9959) || (offset > 9959))
    1134               return invalidDate;
    1135 
    1136            int sgn = (offset < 0)? -1:1;
    1137            offset = abs(offset);
    1138            offset = ((offset / 100)*60 + (offset % 100))*sgn;
    1139         } else {
    1140            for (int i=0; known_zones[i].tzName != 0; i++) {
    1141               if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
    1142                  offset = known_zones[i].tzOffset;
    1143                  break;
    1144               }
    1145            }
    1146         }
    1147      }
    1148      if (sizeof(time_t) == 4)
    1149      {
    1150          if ((time_t)-1 < 0)
    1151          {
    1152             if (year >= 2038)
    1153             {
    1154                year = 2038;
    1155                month = 0;
    1156                day = 1;
    1157                hour = 0;
    1158                minute = 0;
    1159                second = 0;
    1160             }
    1161          }
    1162          else
    1163          {
    1164             if (year >= 2115)
    1165             {
    1166                year = 2115;
    1167                month = 0;
    1168                day = 1;
    1169                hour = 0;
    1170                minute = 0;
    1171                second = 0;
    1172             }
    1173          }
    1174      }
    1175 
    1176     time_t result;
    11771332     
    1178     if (localTime) {
    1179       struct tm tm;
    1180       tm.tm_year = year - 1900;
    1181       tm.tm_mon = month;
    1182       tm.tm_mday = day;
    1183       tm.tm_hour = hour;
    1184       tm.tm_min = minute;
    1185       tm.tm_sec = second;
    1186       tm.tm_isdst = -1;
    1187       result = mktime(&tm);
    1188     } else {
    1189      result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
    1190 
    1191      // avoid negative time values
    1192      if ((offset > 0) && (offset > result))
    1193         offset = 0;
    1194 
    1195      result -= offset*60;
    1196     }
    1197 
     1333     result = ymdhms_to_seconds(year, month+1, day, hour, minute, second) - (offset*60);
    11981334     return result;
    11991335}
Note: See TracChangeset for help on using the changeset viewer.