#line 28 "date.c-nw" #include #include "equal.e" #include "skip-sp.e" /* Ignoring commas and hyphens: o-+-wday-+-+-date-month-+-+-H:M:S-zone-year-----+-garbage-o | | | | | | +------+ +-month-date-+ +-year-----H:M:S-zone-+ zone = o-+-zone-+------+-o | | | | +-diff-+ | | +-diff--------+ | | +-------------+ H:M:S = o-hour-":"-min-":"-sec-+---------------+-o | | +-"."-hundreths-+ */ /* This is from RFC822: 5. DATE AND TIME SPECIFICATION 5.1. SYNTAX date-time = [ day "," ] date time ; dd mm yy ; hh:mm:ss zzz day = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" date = 1*2DIGIT month 2DIGIT ; day month year ; e.g. 20 Jun 82 month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" time = hour zone ; ANSI and Military hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59 zone = "GMT" ; Universal Time */ #define SKIP(s) while (isspace(*(s)) || *(s) == ',') (s)++ static void try_weekday(char **s) { char *t = *s; switch (upcase[*t]) { case 'F': /* Fri or Feb */ t++; if (upcase[*t] != 'R') return; /* Not a weekday */ break; case 'M': /* Mon, Mar or May */ t++; if (upcase[*t] != 'O') return; /* Not a weekday */ break; case 'S': /* Sat, Sun or Sep */ t++; if (upcase[*t] != 'A' && upcase[*t] != 'U') return; /* Not a weekday */ break; case 'T': /* Tue or Thu */ break; case 'W': /* Wed */ break; default: return; /* Not a weekday */ } while (isalpha(*t)) t++; SKIP(t); *s = t; } static Bool try_number(char **s, int *n) { if (!isdigit(**s)) return FALSE; for (*n = 0; isdigit(**s); (*s)++) *n = 10 * *n + **s - '0'; SKIP(*s); return TRUE; } static Bool try_month(char **s, int *n) { char *t = *s; switch (upcase[*t]) { case 'A': /* Apr or Aug */ *n = upcase[*(++t)] == 'P' ? 3 : 7; /* Assume Apr, resp. Aug */ break; case 'D': *n = 11; break; /* Assume Dec */ case 'F': /* Feb or Fri */ if (upcase[*(++t)] != 'E') return FALSE; /* Error */ *n = 1; /* Assume Feb */ break; case 'J': /* Jan, Jun or Jul */ t++; switch (upcase[*t]) { case 'A': *n = 0; break; /* Assume Jan */ case 'U': /* Jun or Jul */ *n = upcase[*(++t)] == 'N' ? 5 : 6; /* Assume Jun, resp. Jul */ break; default: return FALSE; /* Error */ } break; case 'M': /* Mar, May or Mon */ if (upcase[*(++t)] != 'A') return FALSE; /* Error */ *n = upcase[*(++t)] == 'R' ? 2 : 4; /* Assume Mar, resp. May */ break; case 'N': *n = 10; break; /* Assume Nov */ case 'O': *n = 9; break; /* Assume Oct */ case 'S': /* Sep, Sat or Sun */ if (upcase[*(++t)] != 'E') return FALSE; /* Error */ *n = 8; break; default: return FALSE; /* Error */ } while (isalpha(*t)) t++; SKIP(t); *s = t; return TRUE; } static void try_numeric_zone(char **s, int *zone) { char *t = *s; if (*t == '-') { t++; if (isdigit(*t)) { *zone = 60 * 60 * (*t - '0'); t++; if (isdigit(*t)) { *zone = 10 * *zone + 60 * 60 * (*t - '0'); t++; if (isdigit(*t)) { *zone += 10 * 60 * (*t - '0'); t++; if (isdigit(*t)) { *zone += 60 * (*t - '0'); t++; } } } } else *zone = 0; /* A '-' and no digits? */ } else if (*t == '+') { t++; if (isdigit(*t)) { *zone = -60 * 60 * (*t - '0'); t++; if (isdigit(*t)) { *zone = 10 * *zone - 60 * 60 * (*t - '0'); t++; if (isdigit(*t)) { *zone -= 10 * 60 * (*t - '0'); t++; if (isdigit(*t)) { *zone -= 60 * (*t - '0'); t++; } } } } else *zone = 0; /* A '+' and no digits? */ } else *zone = 0; /* No '-' and no '+' */ SKIP(t); *s = t; } static void try_zone(char **s, int *zone) { char *t; int diff; if (**s == '-' || **s == '+') { try_numeric_zone(s, zone); } else { t = *s; switch (upcase[*t]) { case 'U': *zone = 0; break; /* Assume UTC */ case 'A': if (upcase[*(++t)] == 'S') *zone = 4 * 60 * 60; /* Assume AST */ else *zone = 3 * 60 * 60; /* Assume ADT */ break; case 'B': *zone = 0; break; /* Assume BST */ case 'C': if (upcase[*(++t)] == 'S') *zone = 6 * 60 * 60; /* Assume CST */ else *zone = 5 * 60 * 60; /* Assume CDT */ break; case 'E': if (upcase[*(++t)] == 'S') *zone = 5 * 60 * 60; /* Assume EST */ else *zone = 4 * 60 * 60; /* Assume EDT */ break; case 'G': *zone = 0; break; /* Assume GMT */ case 'M': if (upcase[*(++t)] == 'S') *zone = 7 * 60 * 60; /* Assume MST */ else if (upcase[*t] == 'D') *zone = 6 * 60 * 60; /* Assume MDT */ else if (upcase[*t] != 'E') ; /* Unknown */ else if (upcase[*(++t)] == 'Z') *zone = -60 * 60; /* Assume MEZ */ else if (upcase[*t] == 'S') *zone = -2 * 60 * 60; /* Assume MESZ */ else if (upcase[*t] != 'T') ; /* Unknown */ else if (upcase[*(++t)] == 'D') *zone = -2 * 60 * 60; /* Assume METDST */ else *zone = -60 * 60; /* Assume MET */ break; case 'N': if (upcase[*(++t)] == 'S') *zone = 3 * 60 * 60 + 30 * 60; /* Assume NST */ else if (upcase[*(++t)] == 'D') *zone = 2 * 60 * 60 + 30 * 60; /* Assume NDT */ else if (upcase[*t] != 'Z') ; /* Unknown */ if (upcase[*(++t)] == 'S') *zone = -12 * 60 * 60; /* Assume NZST */ else *zone = -13 * 60 * 60; /* Assume NZDT */ break; case 'P': if (upcase[*(++t)] == 'W') *zone = 0; /* Assume PWT */ else if (upcase[*t] == 'S') *zone = 8 * 60 * 60; /* Assume PST */ else *zone = 7 * 60 * 60; /* Assume PDT */ break; case 'S': if (upcase[*(++t)] != 'A') ; /* Unknown */ else if (upcase[*(++t)] == 'S') *zone = -2 * 60 * 60; /* Assume SAST */ else *zone = -3 * 60 * 60; /* Assume SADT */ break; case 'W': *zone = 0; break; /* Assume WET */ case 'Y': if (upcase[*(++t)] == 'S') *zone = 9 * 60 * 60; /* Assume YST */ else *zone = 8 * 60 * 60; /* Assume YDT */ break; default: return; /* Unknown */ } while (isalpha(*t)) t++; SKIP(t); if (*t == '-' || *t == '+') { try_numeric_zone(&t, &diff); *zone += diff; } SKIP(t); *s = t; } } static Bool try_time(char **s, int *hour, int *min, int *sec) { char *t = *s; if (!isdigit(*t)) return FALSE; for (*hour = 0; isdigit(*t); t++) /* Hours */ *hour = 10 * *hour + *t - '0'; if (*hour > 24 || *t != ':') return FALSE; t++; if (!isdigit(*t)) return FALSE; for (*min = 0; isdigit(*t); t++) /* Minutes */ *min = 10 * *min + *t - '0'; if (*min > 59 || *t != ':') return FALSE; t++; if (!isdigit(*t)) return FALSE; for (*sec = 0; isdigit(*t); t++) /* Seconds */ *sec = 10 * *sec + *t - '0'; if (*sec > 59) return FALSE; if (*t == '.') do t++; while (isdigit(*t)); /* Skip hundreths */ SKIP(t); *s = t; return TRUE; } #define CENTURY_CUTOFF 70 EXPORT Bool parse_date(char *s, time_t *t) { struct tm h, *timeptr; int n, zone = 0; *t = time(NULL); timeptr = gmtime(t); h = *timeptr; SKIP(s); try_weekday(&s); if (isdigit(*s)) { (void) try_number(&s, &h.tm_mday); if (*s == '-') { s++; SKIP(s); } if (!try_month(&s, &h.tm_mon)) return FALSE; if (*s == '-') { s++; SKIP(s); } } else { if (!try_month(&s, &h.tm_mon)) return FALSE; if (!try_number(&s, &h.tm_mday)) return FALSE; } if (try_time(&s, &h.tm_hour, &h.tm_min, &h.tm_sec)) { try_zone(&s, &zone); if (!try_number(&s, &n)) return FALSE; if (n > 1900) h.tm_year = n - 1900; else if (n < CENTURY_CUTOFF) h.tm_year = 100 + n; /* 21st century? */ else h.tm_year = n; } else { if (!try_number(&s, &n)) return FALSE; if (n > 1900) h.tm_year = n - 1900; else if (n < CENTURY_CUTOFF) h.tm_year = 100 + n; /* 21st century? */ else h.tm_year = n; if (!try_time(&s, &h.tm_hour, &h.tm_min, &h.tm_sec)) return FALSE; try_zone(&s, &zone); } tzset(); h.tm_sec += zone; /* Convert to GMT */ h.tm_sec -= timezone; /* Convert to local time */ *t = mktime(&h); /* Compute secs since epoch */ return TRUE; }