int Curl_parsedate(const char *date, time_t *output) { time_t t = 0; int wdaynum=-1; /* day of the week number, 0-6 (mon-sun) */ int monnum=-1; /* month of the year number, 0-11 */ int mdaynum=-1; /* day of month, 1 - 31 */ int hournum=-1; int minnum=-1; int secnum=-1; int yearnum=-1; int tzoff=-1; struct my_tm tm; enum assume dignext = DATE_MDAY; const char *indate = date; /* save the original pointer */ int part = 0; /* max 6 parts */ while(*date && (part < 6)) { bool found=FALSE; skip(&date); if(ISALPHA(*date)) { /* a name coming up */ char buf[32]=""; size_t len; sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]", buf); len = strlen(buf); if(wdaynum == -1) { wdaynum = checkday(buf, len); if(wdaynum != -1) found = TRUE; } if(!found && (monnum == -1)) { monnum = checkmonth(buf); if(monnum != -1) found = TRUE; } if(!found && (tzoff == -1)) { /* this just must be a time zone string */ tzoff = checktz(buf); if(tzoff != -1) found = TRUE; } if(!found) return PARSEDATE_FAIL; /* bad string */ date += len; } else if(ISDIGIT(*date)) { /* a digit */ int val; char *end; if((secnum == -1) && (3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) { /* time stamp! */ date += 8; } else if((secnum == -1) && (2 == sscanf(date, "%02d:%02d", &hournum, &minnum))) { /* time stamp without seconds */ date += 5; secnum = 0; } else { val = curlx_sltosi(strtol(date, &end, 10)); if((tzoff == -1) && ((end - date) == 4) && (val <= 1400) && (indate< date) && ((date[-1] == '+' || date[-1] == '-'))) { /* four digits and a value less than or equal to 1400 (to take into account all sorts of funny time zone diffs) and it is preceeded with a plus or minus. This is a time zone indication. 1400 is picked since +1300 is frequently used and +1400 is mentioned as an edge number in the document "ISO C 200X Proposal: Timezone Functions" at http://david.tribble.com/text/c0xtimezone.html If anyone has a more authoritative source for the exact maximum time zone offsets, please speak up! */ found = TRUE; tzoff = (val/100 * 60 + val%100)*60; /* the + and - prefix indicates the local time compared to GMT, this we need ther reversed math to get what we want */ tzoff = date[-1]=='+'?-tzoff:tzoff; } if(((end - date) == 8) && (yearnum == -1) && (monnum == -1) && (mdaynum == -1)) { /* 8 digits, no year, month or day yet. This is YYYYMMDD */ found = TRUE; yearnum = val/10000; monnum = (val%10000)/100-1; /* month is 0 - 11 */ mdaynum = val%100; } if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { if((val > 0) && (val<32)) { mdaynum = val; found = TRUE; } dignext = DATE_YEAR; } if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) { yearnum = val; found = TRUE; if(yearnum < 1900) { if(yearnum > 70) yearnum += 1900; else yearnum += 2000; } if(mdaynum == -1) dignext = DATE_MDAY; } if(!found) return PARSEDATE_FAIL; date = end; } } part++; } if(-1 == secnum) secnum = minnum = hournum = 0; /* no time, make it zero */ if((-1 == mdaynum) || (-1 == monnum) || (-1 == yearnum)) /* lacks vital info, fail */ return PARSEDATE_FAIL; #if SIZEOF_TIME_T < 5 /* 32 bit time_t can only hold dates to the beginning of 2038 */ if(yearnum > 2037) { *output = 0x7fffffff; return PARSEDATE_LATER; } #endif if(yearnum < 1970) { *output = 0; return PARSEDATE_SOONER; } tm.tm_sec = secnum; tm.tm_min = minnum; tm.tm_hour = hournum; tm.tm_mday = mdaynum; tm.tm_mon = monnum; tm.tm_year = yearnum - 1900; /* my_timegm() returns a time_t. time_t is often 32 bits, even on many architectures that feature 64 bit 'long'. Some systems have 64 bit time_t and deal with years beyond 2038. However, even on some of the systems with 64 bit time_t mktime() returns -1 for dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06) */ t = my_timegm(&tm); /* time zone adjust (cast t to int to compare to negative one) */ if(-1 != (int)t) { /* Add the time zone diff between local time zone and GMT. */ long delta = (long)(tzoff!=-1?tzoff:0); if((delta>0) && (t + delta < t)) return -1; /* time_t overflow */ t += delta; } *output = t; return PARSEDATE_OK; }
/* * Expected styles: * * Date ::= Month . ' ' . DayOfMonth | * Month . ' ' . DayOfWeek . ModifierIndex | * Month . '/' . DayOfMonth | * Month . '/' . DayOfWeek . ModifierIndex | * DayOfMonth . ' ' . Month | * DayOfMonth . '/' . Month | * DayOfWeek . ModifierIndex . ' ' .Month | * DayOfWeek . ModifierIndex . '/' .Month | * DayOfWeek . ModifierIndex | * SpecialDay . ModifierOffset * * Month ::= MonthName | MonthNumber | '*' * MonthNumber ::= '0' ... '9' | '00' ... '09' | '10' ... '12' * MonthName ::= MonthNameShort | MonthNameLong * MonthNameLong ::= 'January' ... 'December' * MonthNameShort ::= 'Jan' ... 'Dec' | 'Jan.' ... 'Dec.' * * DayOfWeek ::= DayOfWeekShort | DayOfWeekLong * DayOfWeekShort ::= 'Mon' .. 'Sun' * DayOfWeekLong ::= 'Monday' .. 'Sunday' * DayOfMonth ::= '0' ... '9' | '00' ... '09' | '10' ... '29' | * '30' ... '31' | '*' * * ModifierOffset ::= '' | '+' . ModifierNumber | '-' . ModifierNumber * ModifierNumber ::= '0' ... '9' | '00' ... '99' | '000' ... '299' | * '300' ... '359' | '360' ... '365' * ModifierIndex ::= 'Second' | 'Third' | 'Fourth' | 'Fifth' | * 'First' | 'Last' * * SpecialDay ::= 'Easter' | 'Paskha' | 'ChineseNewYear' * */ static int determinestyle(char *date, int *flags, char *month, int *imonth, char *dayofmonth, int *idayofmonth, char *dayofweek, int *idayofweek, char *modifieroffset, char *modifierindex, char *specialday, char *year, int *iyear) { char *p, *p1, *p2, *py; const char *dow, *pmonth; char pold; size_t len, offset; *flags = F_NONE; *month = '\0'; *imonth = 0; *year = '\0'; *iyear = 0; *dayofmonth = '\0'; *idayofmonth = 0; *dayofweek = '\0'; *idayofweek = 0; *modifieroffset = '\0'; *modifierindex = '\0'; *specialday = '\0'; #define CHECKSPECIAL(s1, s2, lens2, type) \ if (s2 != NULL && strncmp(s1, s2, lens2) == 0) { \ *flags |= F_SPECIALDAY; \ *flags |= type; \ *flags |= F_VARIABLE; \ if (strlen(s1) == lens2) { \ strcpy(specialday, s1); \ return (1); \ } \ strncpy(specialday, s1, lens2); \ specialday[lens2] = '\0'; \ strcpy(modifieroffset, s1 + lens2); \ *flags |= F_MODIFIEROFFSET; \ return (1); \ } if ((p = strchr(date, ' ')) == NULL) { if ((p = strchr(date, '/')) == NULL) { CHECKSPECIAL(date, STRING_CNY, strlen(STRING_CNY), F_CNY); CHECKSPECIAL(date, ncny.name, ncny.len, F_CNY); CHECKSPECIAL(date, STRING_NEWMOON, strlen(STRING_NEWMOON), F_NEWMOON); CHECKSPECIAL(date, nnewmoon.name, nnewmoon.len, F_NEWMOON); CHECKSPECIAL(date, STRING_FULLMOON, strlen(STRING_FULLMOON), F_FULLMOON); CHECKSPECIAL(date, nfullmoon.name, nfullmoon.len, F_FULLMOON); CHECKSPECIAL(date, STRING_PASKHA, strlen(STRING_PASKHA), F_PASKHA); CHECKSPECIAL(date, npaskha.name, npaskha.len, F_PASKHA); CHECKSPECIAL(date, STRING_EASTER, strlen(STRING_EASTER), F_EASTER); CHECKSPECIAL(date, neaster.name, neaster.len, F_EASTER); CHECKSPECIAL(date, STRING_MAREQUINOX, strlen(STRING_MAREQUINOX), F_MAREQUINOX); CHECKSPECIAL(date, nmarequinox.name, nmarequinox.len, F_SEPEQUINOX); CHECKSPECIAL(date, STRING_SEPEQUINOX, strlen(STRING_SEPEQUINOX), F_SEPEQUINOX); CHECKSPECIAL(date, nsepequinox.name, nsepequinox.len, F_SEPEQUINOX); CHECKSPECIAL(date, STRING_JUNSOLSTICE, strlen(STRING_JUNSOLSTICE), F_JUNSOLSTICE); CHECKSPECIAL(date, njunsolstice.name, njunsolstice.len, F_JUNSOLSTICE); CHECKSPECIAL(date, STRING_DECSOLSTICE, strlen(STRING_DECSOLSTICE), F_DECSOLSTICE); CHECKSPECIAL(date, ndecsolstice.name, ndecsolstice.len, F_DECSOLSTICE); if (checkdayofweek(date, &len, &offset, &dow) != 0) { *flags |= F_DAYOFWEEK; *flags |= F_VARIABLE; *idayofweek = offset; if (strlen(date) == len) { strcpy(dayofweek, date); return (1); } strncpy(dayofweek, date, len); dayofweek[len] = '\0'; strcpy(modifierindex, date + len); *flags |= F_MODIFIERINDEX; return (1); } if (isonlydigits(date, 1)) { /* Assume month number only */ *flags |= F_MONTH; *imonth = (int)strtol(date, (char **)NULL, 10); strcpy(month, getmonthname(*imonth)); return(1); } return (0); } } /* * After this, leave by goto-ing to "allfine" or "fail" to restore the * original data in `date'. */ pold = *p; *p = 0; p1 = date; p2 = p + 1; /* Now p2 points to the next field and p1 to the first field */ if ((py = strchr(p2, '/')) != NULL) { /* We have a year in the string. Now this is getting tricky */ strcpy(year, p1); *iyear = (int)strtol(year, NULL, 10); p1 = p2; p2 = py + 1; *py = 0; *flags |= F_YEAR; } /* Check if there is a month-string in the date */ if ((checkmonth(p1, &len, &offset, &pmonth) != 0) || (checkmonth(p2, &len, &offset, &pmonth) != 0 && (p2 = p1))) { /* p2 is the non-month part */ *flags |= F_MONTH; *imonth = offset; strcpy(month, getmonthname(offset)); if (isonlydigits(p2, 1)) { strcpy(dayofmonth, p2); *idayofmonth = (int)strtol(p2, (char **)NULL, 10); *flags |= F_DAYOFMONTH; goto allfine; } if (strcmp(p2, "*") == 0) { *flags |= F_ALLDAY; goto allfine; } if (checkdayofweek(p2, &len, &offset, &dow) != 0) { *flags |= F_DAYOFWEEK; *flags |= F_VARIABLE; *idayofweek = offset; strcpy(dayofweek, getdayofweekname(offset)); if (strlen(p2) == len) goto allfine; strcpy(modifierindex, p2 + len); *flags |= F_MODIFIERINDEX; goto allfine; } goto fail; } /* Check if there is an every-day or every-month in the string */ if ((strcmp(p1, "*") == 0 && isonlydigits(p2, 1)) || (strcmp(p2, "*") == 0 && isonlydigits(p1, 1) && (p2 = p1))) { int d; *flags |= F_ALLMONTH; *flags |= F_DAYOFMONTH; d = (int)strtol(p2, (char **)NULL, 10); *idayofmonth = d; sprintf(dayofmonth, "%d", d); goto allfine; } /* Month as a number, then a weekday */ if (isonlydigits(p1, 1) && checkdayofweek(p2, &len, &offset, &dow) != 0) { int d; *flags |= F_MONTH; *flags |= F_DAYOFWEEK; *flags |= F_VARIABLE; *idayofweek = offset; d = (int)strtol(p1, (char **)NULL, 10); *imonth = d; strcpy(month, getmonthname(d)); strcpy(dayofweek, getdayofweekname(offset)); if (strlen(p2) == len) goto allfine; strcpy(modifierindex, p2 + len); *flags |= F_MODIFIERINDEX; goto allfine; } /* If both the month and date are specified as numbers */ if (isonlydigits(p1, 1) && isonlydigits(p2, 0)) { /* Now who wants to be this ambigious? :-( */ int m, d; if (strchr(p2, '*') != NULL) *flags |= F_VARIABLE; m = (int)strtol(p1, (char **)NULL, 10); d = (int)strtol(p2, (char **)NULL, 10); *flags |= F_MONTH; *flags |= F_DAYOFMONTH; if (m > 12) { *imonth = d; *idayofmonth = m; strcpy(month, getmonthname(d)); sprintf(dayofmonth, "%d", m); } else { *imonth = m; *idayofmonth = d; strcpy(month, getmonthname(m)); sprintf(dayofmonth, "%d", d); } goto allfine; } /* FALLTHROUGH */ fail: *p = pold; return (0); allfine: *p = pold; return (1); }
time_t strtotime(const char *string){ int sec = -1; /* seconds */ int min = -1; /* minutes */ int hour = -1; /* hours */ int mday = -1; /* day of the month */ int mon = -1; /* month */ int year = -1; /* year */ int wday = -1; /* day of the week */ int tzoff = -1; /* time zone offset */ int part = 0; time_t t = 0; time_t now = 0; struct tm tm; const char *date; const char *indate = string; /* original pointer */ enum assume dignext = DATE_MDAY; BOOLEAN found = FALSE; /* * Make sure we have a string to parse. */ if(!(string && *string)) return(0); date = string; /** * this parser was more or less stolen form libcurl. * Copyright (C) 1998 - 2006, Daniel Stenberg, <*****@*****.**>, et al. * http://curl.haxx.se/ */ while(*date && (part < 6)) { found=FALSE; skip(&date); if(isalpha((unsigned char)*date)) { /* a name coming up */ char buf[32]=""; size_t len; sscanf(date, "%31[A-Za-z]", buf); len = strlen(buf); if(wday == -1) { wday = checkday(buf, len); if(wday != -1) found = TRUE; } if(!found && (mon == -1)) { mon = checkmonth(buf); if(mon != -1) found = TRUE; } if(!found && (tzoff == -1)) { /* this just must be a time zone string */ tzoff = checktz(buf); if(tzoff != -1) found = TRUE; } if(!found) return -1; /* bad string */ date += len; } else if(isdigit((unsigned char)*date)) { /* a digit */ int val; char *end; if((sec == -1) && (3 == sscanf(date, "%02d:%02d:%02d", &hour, &min, &sec))) { /* time stamp! */ date += 8; found = TRUE; } else { val = (int)strtol(date, &end, 10); if((tzoff == -1) && ((end - date) == 4) && (val < 1300) && (indate< date) && ((date[-1] == '+' || date[-1] == '-'))) { /* four digits and a value less than 1300 and it is preceeded with a plus or minus. This is a time zone indication. */ found = TRUE; tzoff = (val/100 * 60 + val%100)*60; /* the + and - prefix indicates the local time compared to GMT, this we need ther reversed math to get what we want */ tzoff = date[-1]=='+'?-tzoff:tzoff; } if(((end - date) == 8) && (year == -1) && (mon == -1) && (mday == -1)) { /* 8 digits, no year, month or day yet. This is YYYYMMDD */ found = TRUE; year = val/10000; mon = (val%10000)/100-1; /* month is 0 - 11 */ mday = val%100; } if(!found && (dignext == DATE_MDAY) && (mday == -1)) { if((val > 0) && (val<32)) { mday = val; found = TRUE; } dignext = DATE_YEAR; } if(!found && (dignext == DATE_YEAR) && (year == -1)) { year = val; found = TRUE; if(year < 1900) { if (year > 70) year += 1900; else year += 2000; } if(mday == -1) dignext = DATE_MDAY; } if(!found) return -1; date = end; } } part++; } if(-1 == sec) sec = min = hour = 0; /* no time, make it zero */ if((-1 == mday) || (-1 == mon) || (-1 == year)) /* lacks vital info, fail */ return -1; /* Y238 'bug' */ if(year > 2037) return 0x7fffffff; tm.tm_sec = sec; tm.tm_min = min; tm.tm_hour = hour; tm.tm_mday = mday; tm.tm_mon = mon; tm.tm_year = year - 1900; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; t = mktime(&tm); /* time zone adjust (cast t to int to compare to negative one) */ if(-1 != (int)t) { struct tm *gmt; long delta; time_t t2; #ifdef HAVE_GMTIME_R /* thread-safe version */ struct tm keeptime2; gmt = (struct tm *)gmtime_r(&t, &keeptime2); if(!gmt) return -1; /* illegal date/time */ t2 = mktime(gmt); #else /* It seems that at least the MSVC version of mktime() doesn't work properly if it gets the 'gmt' pointer passed in (which is a pointer returned from gmtime() pointing to static memory), so instead we copy the tm struct to a local struct and pass a pointer to that struct as input to mktime(). */ struct tm gmt2; gmt = gmtime(&t); /* use gmtime_r() if available */ if(!gmt) return -1; /* illegal date/time */ gmt2 = *gmt; t2 = mktime(&gmt2); #endif /* Add the time zone diff (between the given timezone and GMT) and the diff between the local time zone and GMT. */ delta = (long)((tzoff!=-1?tzoff:0) + (t - t2)); if((delta>0) && (t + delta < t)) return -1; /* time_t overflow */ t += delta; } now = time(NULL); return t; }
static time_t Curl_parsedate(const char *date) { time_t t = 0; int wdaynum=-1; /* day of the week number, 0-6 (mon-sun) */ int monnum=-1; /* month of the year number, 0-11 */ int mdaynum=-1; /* day of month, 1 - 31 */ int hournum=-1; int minnum=-1; int secnum=-1; int yearnum=-1; int tzoff=-1; struct tm tm; enum assume dignext = DATE_MDAY; const char *indate = date; /* save the original pointer */ int part = 0; /* max 6 parts */ #ifdef WIN32 /* * On Windows, we need an odd work-around for the case when no TZ variable * is set. If it isn't set and "automatic DST adjustment" is enabled, the * time functions below will return values one hour off! As reported and * investigated in bug report #1230118. */ const char *env = getenv("TZ"); if(!env) putenv("TZ=GMT"); #endif while(*date && (part < 6)) { int found=FALSE; skip(&date); if(isalpha((int)*date)) { /* a name coming up */ char buf[32]=""; size_t len; sscanf(date, "%31[A-Za-z]", buf); len = strlen(buf); if(wdaynum == -1) { wdaynum = checkday(buf, len); if(wdaynum != -1) found = TRUE; } if(!found && (monnum == -1)) { monnum = checkmonth(buf); if(monnum != -1) found = TRUE; } if(!found && (tzoff == -1)) { /* this just must be a time zone string */ tzoff = checktz(buf); if(tzoff != -1) found = TRUE; } if(!found) return -1; /* bad string */ date += len; } else if(isdigit((int)*date)) { /* a digit */ int val; char *end; if((secnum == -1) && (3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) { /* time stamp! */ date += 8; found = TRUE; } else { val = (int)strtol(date, &end, 10); if((tzoff == -1) && ((end - date) == 4) && (val < 1300) && (indate< date) && ((date[-1] == '+' || date[-1] == '-'))) { /* four digits and a value less than 1300 and it is preceeded with a plus or minus. This is a time zone indication. */ found = TRUE; tzoff = (val/100 * 60 + val%100)*60; /* the + and - prefix indicates the local time compared to GMT, this we need ther reversed math to get what we want */ tzoff = date[-1]=='+'?-tzoff:tzoff; } if(((end - date) == 8) && (yearnum == -1) && (monnum == -1) && (mdaynum == -1)) { /* 8 digits, no year, month or day yet. This is YYYYMMDD */ found = TRUE; yearnum = val/10000; monnum = (val%10000)/100-1; /* month is 0 - 11 */ mdaynum = val%100; } if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { if((val > 0) && (val<32)) { mdaynum = val; found = TRUE; } dignext = DATE_YEAR; } if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) { yearnum = val; found = TRUE; if(yearnum < 1900) { if (yearnum > 70) yearnum += 1900; else yearnum += 2000; } if(mdaynum == -1) dignext = DATE_MDAY; } if(!found) return -1; date = end; } } part++; } if(-1 == secnum) secnum = minnum = hournum = 0; /* no time, make it zero */ if((-1 == mdaynum) || (-1 == monnum) || (-1 == yearnum)) /* lacks vital info, fail */ return -1; #if SIZEOF_TIME_T < 5 /* 32 bit time_t can only hold dates to the beginning of 2038 */ if(yearnum > 2037) return 0x7fffffff; #endif tm.tm_sec = secnum; tm.tm_min = minnum; tm.tm_hour = hournum; tm.tm_mday = mdaynum; tm.tm_mon = monnum; tm.tm_year = yearnum - 1900; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; /* mktime() returns a time_t. time_t is often 32 bits, even on many architectures that feature 64 bit 'long'. Some systems have 64 bit time_t and deal with years beyond 2038. However, even some of the systems with 64 bit time_t returns -1 for dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06) */ t = mktime(&tm); /* time zone adjust (cast t to int to compare to negative one) */ if(-1 != (int)t) { struct tm *gmt; long delta; time_t t2; #ifdef HAVE_GMTIME_R /* thread-safe version */ struct tm keeptime2; gmt = (struct tm *)gmtime_r(&t, &keeptime2); #else gmt = gmtime(&t); /* use gmtime_r() if available */ #endif if(!gmt) return -1; /* illegal date/time */ t2 = mktime(gmt); /* Add the time zone diff (between the given timezone and GMT) and the diff between the local time zone and GMT. */ delta = (long)((tzoff!=-1?tzoff:0) + (t - t2)); if((delta>0) && (t + delta < t)) return -1; /* time_t overflow */ t += delta; } return t; }
static int parsedate(const char *date, time_t *output) { time_t t = 0; int wdaynum = -1; /* day of the week number, 0-6 (mon-sun) */ int monnum = -1; /* month of the year number, 0-11 */ int mdaynum = -1; /* day of month, 1 - 31 */ int hournum = -1; int minnum = -1; int secnum = -1; int yearnum = -1; int tzoff = -1; struct my_tm tm; enum assume dignext = DATE_MDAY; const char *indate = date; /* save the original pointer */ int part = 0; /* max 6 parts */ while(*date && (part < 6)) { bool found = FALSE; skip(&date); if(ISALPHA(*date)) { /* a name coming up */ char buf[32]=""; size_t len; if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz]", buf)) len = strlen(buf); else len = 0; if(wdaynum == -1) { wdaynum = checkday(buf, len); if(wdaynum != -1) found = TRUE; } if(!found && (monnum == -1)) { monnum = checkmonth(buf); if(monnum != -1) found = TRUE; } if(!found && (tzoff == -1)) { /* this just must be a time zone string */ tzoff = checktz(buf); if(tzoff != -1) found = TRUE; } if(!found) return PARSEDATE_FAIL; /* bad string */ date += len; } else if(ISDIGIT(*date)) { /* a digit */ int val; char *end; int len = 0; if((secnum == -1) && (3 == sscanf(date, "%02d:%02d:%02d%n", &hournum, &minnum, &secnum, &len))) { /* time stamp! */ date += len; } else if((secnum == -1) && (2 == sscanf(date, "%02d:%02d%n", &hournum, &minnum, &len))) { /* time stamp without seconds */ date += len; secnum = 0; } else { long lval; int error; int old_errno; old_errno = errno; errno = 0; lval = strtol(date, &end, 10); error = errno; if(errno != old_errno) errno = old_errno; if(error) return PARSEDATE_FAIL; #if LONG_MAX != INT_MAX if((lval > (long)INT_MAX) || (lval < (long)INT_MIN)) return PARSEDATE_FAIL; #endif val = curlx_sltosi(lval); if((tzoff == -1) && ((end - date) == 4) && (val <= 1400) && (indate< date) && ((date[-1] == '+' || date[-1] == '-'))) { /* four digits and a value less than or equal to 1400 (to take into account all sorts of funny time zone diffs) and it is preceded with a plus or minus. This is a time zone indication. 1400 is picked since +1300 is frequently used and +1400 is mentioned as an edge number in the document "ISO C 200X Proposal: Timezone Functions" at http://david.tribble.com/text/c0xtimezone.html If anyone has a more authoritative source for the exact maximum time zone offsets, please speak up! */ found = TRUE; tzoff = (val/100 * 60 + val%100)*60; /* the + and - prefix indicates the local time compared to GMT, this we need their reversed math to get what we want */ tzoff = date[-1]=='+'?-tzoff:tzoff; } if(((end - date) == 8) && (yearnum == -1) && (monnum == -1) && (mdaynum == -1)) { /* 8 digits, no year, month or day yet. This is YYYYMMDD */ found = TRUE; yearnum = val/10000; monnum = (val%10000)/100-1; /* month is 0 - 11 */ mdaynum = val%100; } if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { if((val > 0) && (val<32)) { mdaynum = val; found = TRUE; } dignext = DATE_YEAR; } if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) { yearnum = val; found = TRUE; if(yearnum < 100) { if(yearnum > 70) yearnum += 1900; else yearnum += 2000; } if(mdaynum == -1) dignext = DATE_MDAY; } if(!found) return PARSEDATE_FAIL; date = end; } } part++; } if(-1 == secnum) secnum = minnum = hournum = 0; /* no time, make it zero */ if((-1 == mdaynum) || (-1 == monnum) || (-1 == yearnum)) /* lacks vital info, fail */ return PARSEDATE_FAIL; #ifdef HAVE_TIME_T_UNSIGNED if(yearnum < 1970) { /* only positive numbers cannot return earlier */ *output = TIME_T_MIN; return PARSEDATE_SOONER; } #endif #if (SIZEOF_TIME_T < 5) #ifdef HAVE_TIME_T_UNSIGNED /* an unsigned 32 bit time_t can only hold dates to 2106 */ if(yearnum > 2105) { *output = TIME_T_MAX; return PARSEDATE_LATER; } #else /* a signed 32 bit time_t can only hold dates to the beginning of 2038 */ if(yearnum > 2037) { *output = TIME_T_MAX; return PARSEDATE_LATER; } if(yearnum < 1903) { *output = TIME_T_MIN; return PARSEDATE_SOONER; } #endif #else /* The Gregorian calendar was introduced 1582 */ if(yearnum < 1583) return PARSEDATE_FAIL; #endif if((mdaynum > 31) || (monnum > 11) || (hournum > 23) || (minnum > 59) || (secnum > 60)) return PARSEDATE_FAIL; /* clearly an illegal date */ tm.tm_sec = secnum; tm.tm_min = minnum; tm.tm_hour = hournum; tm.tm_mday = mdaynum; tm.tm_mon = monnum; tm.tm_year = yearnum; /* my_timegm() returns a time_t. time_t is often 32 bits, sometimes even on architectures that feature 64 bit 'long' but ultimately time_t is the correct data type to use. */ my_timegm(&tm, &t); /* Add the time zone diff between local time zone and GMT. */ if(tzoff == -1) tzoff = 0; if((tzoff > 0) && (t > TIME_T_MAX - tzoff)) { *output = TIME_T_MAX; return PARSEDATE_LATER; /* time_t overflow */ } t += tzoff; *output = t; return PARSEDATE_OK; }
static time_t parsedate(const char *date) { time_t t = 0; int wdaynum=-1; /* day of the week number, 0-6 (mon-sun) */ int monnum=-1; /* month of the year number, 0-11 */ int mdaynum=-1; /* day of month, 1 - 31 */ int hournum=-1; int minnum=-1; int secnum=-1; int yearnum=-1; int tzoff=-1; struct tm tm; enum assume dignext = DATE_MDAY; const char *indate = date; /* save the original pointer */ int part = 0; /* max 6 parts */ while(*date && (part < 6)) { bool found=FALSE; skip(&date); if(ISALPHA(*date)) { /* a name coming up */ char buf[32]=""; size_t len; sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]", buf); len = strlen(buf); if(wdaynum == -1) { wdaynum = checkday(buf, len); if(wdaynum != -1) found = TRUE; } if(!found && (monnum == -1)) { monnum = checkmonth(buf); if(monnum != -1) found = TRUE; } if(!found && (tzoff == -1)) { /* this just must be a time zone string */ tzoff = checktz(buf); if(tzoff != -1) found = TRUE; } if(!found) return -1; /* bad string */ date += len; } else if(ISDIGIT(*date)) { /* a digit */ int val; char *end; if((secnum == -1) && (3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) { /* time stamp! */ date += 8; found = TRUE; } else { val = (int)strtol(date, &end, 10); if((tzoff == -1) && ((end - date) == 4) && (val <= 1400) && (indate< date) && ((date[-1] == '+' || date[-1] == '-'))) { /* four digits and a value less than or equal to 1400 (to take into account all sorts of funny time zone diffs) and it is preceeded with a plus or minus. This is a time zone indication. 1400 is picked since +1300 is frequently used and +1400 is mentioned as an edge number in the document "ISO C 200X Proposal: Timezone Functions" at http://david.tribble.com/text/c0xtimezone.html If anyone has a more authoritative source for the exact maximum time zone offsets, please speak up! */ found = TRUE; tzoff = (val/100 * 60 + val%100)*60; /* the + and - prefix indicates the local time compared to GMT, this we need ther reversed math to get what we want */ tzoff = date[-1]=='+'?-tzoff:tzoff; } if(((end - date) == 8) && (yearnum == -1) && (monnum == -1) && (mdaynum == -1)) { /* 8 digits, no year, month or day yet. This is YYYYMMDD */ found = TRUE; yearnum = val/10000; monnum = (val%10000)/100-1; /* month is 0 - 11 */ mdaynum = val%100; } if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { if((val > 0) && (val<32)) { mdaynum = val; found = TRUE; } dignext = DATE_YEAR; } if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) { yearnum = val; found = TRUE; if(yearnum < 1900) { if(yearnum > 70) yearnum += 1900; else yearnum += 2000; } if(mdaynum == -1) dignext = DATE_MDAY; } if(!found) return -1; date = end; } } part++; } if(-1 == secnum) secnum = minnum = hournum = 0; /* no time, make it zero */ if((-1 == mdaynum) || (-1 == monnum) || (-1 == yearnum)) /* lacks vital info, fail */ return -1; #if SIZEOF_TIME_T < 5 /* 32 bit time_t can only hold dates to the beginning of 2038 */ if(yearnum > 2037) return 0x7fffffff; #endif tm.tm_sec = secnum; tm.tm_min = minnum; tm.tm_hour = hournum; tm.tm_mday = mdaynum; tm.tm_mon = monnum; tm.tm_year = yearnum - 1900; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; /* mktime() returns a time_t. time_t is often 32 bits, even on many architectures that feature 64 bit 'long'. Some systems have 64 bit time_t and deal with years beyond 2038. However, even some of the systems with 64 bit time_t returns -1 for dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06) */ t = mktime(&tm); /* time zone adjust (cast t to int to compare to negative one) */ if(-1 != (int)t) { struct tm *gmt; long delta; time_t t2; #ifdef HAVE_GMTIME_R /* thread-safe version */ struct tm keeptime2; gmt = (struct tm *)gmtime_r(&t, &keeptime2); if(!gmt) return -1; /* illegal date/time */ t2 = mktime(gmt); #else /* It seems that at least the MSVC version of mktime() doesn't work properly if it gets the 'gmt' pointer passed in (which is a pointer returned from gmtime() pointing to static memory), so instead we copy the tm struct to a local struct and pass a pointer to that struct as input to mktime(). */ struct tm gmt2; gmt = gmtime(&t); /* use gmtime_r() if available */ if(!gmt) return -1; /* illegal date/time */ gmt2 = *gmt; t2 = mktime(&gmt2); #endif /* Add the time zone diff (between the given timezone and GMT) and the diff between the local time zone and GMT. */ delta = (long)((tzoff!=-1?tzoff:0) + (t - t2)); if((delta>0) && (t + delta < t)) return -1; /* time_t overflow */ t += delta; } return t; }