/*ident "@(#)Time:tm/tmdate.c 3.1" */ /****************************************************************************** * * C++ Standard Components, Release 3.0. * * Copyright (c) 1991, 1992 AT&T and Unix System Laboratories, Inc. * Copyright (c) 1988, 1989, 1990 AT&T. All Rights Reserved. * * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T and Unix System * Laboratories, Inc. The copyright notice above does not evidence * any actual or intended publication of such source code. * ******************************************************************************/ #include #include #define twodig(s,n) (n = (*s++ - '0') * 10, n += *s++ - '0') #define BREAK (1<<0) #define DAY (1<<1) #define HOLD (1<<2) #define HOUR (1<<3) #define LAST (1<<4) #define MDAY (1<<5) #define MONTH (1<<6) #define NEXT (1<<7) #define THIS (1<<8) #define YEAR (1<<9) #define YYMMDDHHMMSS (1<<10) #define ZONE (1<<11) #define EXACT (1<<12) /* * parse date expression in s and return time_t value * * if non-null, e points to the first invalid sequence in s * clock provides default values */ time_t tmdate_ATTLC(register const char* sss, char** e, time_t* clock) { register struct tm* tm; register int n; int state; time_t now; char* t; char* last; char* type; int dst; int zone; int i; int j; int k; int l; int m; char* s=(char*)sss; reset: /* * use clock for defaults */ tm = tmmake_ATTLC(clock); dst = TM_DST; state = HOLD|EXACT; // cludge for EXACT type = 0; zone = 0; /* * get */ for (;;) { state &= (state & HOLD) ? ~(HOLD) : ~(EXACT|LAST|NEXT|THIS); n = -1; while (isspace(*s) || *s == ',' || *s == '-') s++; if (!*(last = (char*)s)) break; if (*s == '#') { if (!isdigit(*++s)) break; now = strtol(s, &t, 0); clock = &now; s = t; goto reset; } if (isdigit(*s)) { n = (int)strtol(s, &t, 10); if (!(state & (LAST|NEXT|THIS)) && (!*t && (t - s) > 4 || *t == '.' && (t - s) == 4)) { /* * old date(1) format * * [yy[mm]]ddhhmm[.ss] * hhmm.ss */ if (state & YYMMDDHHMMSS) break; state |= YYMMDDHHMMSS; if ((t - s) < 10) m = tm->tm_year; else if (twodig(s, m) < 38) m += 100; if ((t - s) < 8) l = tm->tm_mon; else if (twodig(s, l) <= 0 || l > 12) break; if ((t - s) < 6) k = tm->tm_mon; else if (twodig(s, k) < 1 || k > 31) break; if ((t - s) < 4) break; if (twodig(s, j) > 24) break; if (twodig(s, i) > 59) break; if ((t - s) == 2) twodig(s, n); else if (t - s) break; else if (*t != '.') n = 0; else n = (int)strtol(t + 1, &t, 10); if (n > (59 + TM_MAXLEAP)) break; tm->tm_year = m; tm->tm_mon = l - 1; tm->tm_mday = k; tm->tm_hour = j; tm->tm_min = i; tm->tm_sec = n; s = t; continue; } s = t; if (*s == ':') { if ((state & HOUR) || n > 24 || *s++ != ':' || !isdigit(*s)) break; i = n; n = (int)strtol(s, &t, 10); s = t; if (n > 59) break; j = n; if (*s == ':') { if (!isdigit(*++s)) break; n = (int)strtol(s, &t, 10); s = t; if (n > (59 + TM_MAXLEAP)) break; } else n = 0; tm->tm_sec = n; tm->tm_min = j; tm->tm_hour = i; continue; } } while (isspace(*s) || *s == ',' || *s == '-') s++; if (isalpha(*s)) { if ((j = tmlex_ATTLC(s, &t, tm_form_ATTLC, TM_NFORM, tm_form_ATTLC + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0) { s = t; switch (tm_lex_ATTLC[j]) { /* case TM_EXACT: state |= HOLD|EXACT; continue; */ case TM_LAST: state |= HOLD|LAST; continue; case TM_THIS: state |= HOLD|THIS; n = 0; continue; case TM_NEXT: state |= HOLD|NEXT; continue; case TM_MERIDIAN: if (n > 0) { if (n > 24) goto done; tm->tm_hour = n; } if (j == TM_MERIDIAN) { if (tm->tm_hour == 12) tm->tm_hour = 0; } else if (tm->tm_hour < 12) tm->tm_hour += 12; if (n > 0) goto clear_min; continue; case TM_DAY_3: j += TM_DAY - TM_DAY_3; /*FALLTHROUGH*/ case TM_DAY: case TM_PARTS: case TM_HOURS: case TM_DAYS: if (n == -1) n = 1; if (!(state & (LAST|NEXT|THIS))) for (;;) { while (isspace(*s) || *s == ',') s++; if ((k = tmlex_ATTLC(s, &t, tm_form_ATTLC + TM_LAST, TM_NOISE - TM_LAST, (char**)0, 0)) >= 0) { s = t; if (k <= 2) state |= LAST; else if (k <= 5) state |= THIS; else if (k <= 8) state |= NEXT; else state |= EXACT; } else break; } if (state & LAST) n = -n; else if (!(state & NEXT)) n--; switch (j) { case TM_DAYS+0: tm->tm_mday--; goto clear_hour; case TM_DAYS+1: goto clear_hour; case TM_DAYS+2: tm->tm_mday++; goto clear_hour; case TM_PARTS+0: tm->tm_sec += n; continue; case TM_PARTS+1: tm->tm_min += n; goto clear_sec; case TM_PARTS+2: tm->tm_hour += n; goto clear_min; case TM_PARTS+3: tm->tm_mday += n; goto clear_hour; case TM_PARTS+4: tm->tm_mday += 7 * n - tm->tm_wday + 1; goto clear_hour; case TM_PARTS+5: tm->tm_mon += n; goto clear_mday; case TM_PARTS+6: tm->tm_year += n; goto clear_mon; case TM_HOURS+0: tm->tm_mday += n; goto clear_hour; case TM_HOURS+1: tm->tm_mday += n; tm->tm_hour = 6; goto clear_min; case TM_HOURS+2: tm->tm_mday += n; tm->tm_hour = 12; goto clear_min; case TM_HOURS+3: tm->tm_mday += n; tm->tm_hour = 18; goto clear_min; default: j -= tm->tm_wday + TM_DAY; if (state & (LAST|NEXT|THIS)) { if (j < 0) j += 7; } else if (j > 0) j -= 7; tm->tm_mday += j + n * 7; if (state & (LAST|NEXT|THIS)) goto clear_hour; continue; } /*break;*/ /* unreachable */ case TM_MONTH_3: j += TM_MONTH - TM_MONTH_3; /*FALLTHROUGH*/ case TM_MONTH: if (state & MONTH) goto done; state |= MONTH; i = tm->tm_mon; tm->tm_mon = j - TM_MONTH; if (n < 0) { while (isspace(*s) || *s == ',' || *s == '-') s++; if (isdigit(*s)) { n = (int)strtol(s, &t, 10); if (n <= 31) s = t; else n = -1; } } if (n >= 0) { if (n > 31) goto done; state |= MDAY; tm->tm_mday = n; } if (state & (LAST|NEXT|THIS)) { n = i; goto rel_month; } continue; case TM_UT: if (state & ZONE) goto done; state |= ZONE; zone += tmgoff_ATTLC(s, &t, 0); s = t; continue; case TM_DT: if (!dst) goto done; if (!(state & ZONE)) { dst = tm_local_ATTLC->dst; zone = tm_local_ATTLC->west; } zone += tmgoff_ATTLC(s, &t, dst); s = t; dst = 0; state |= ZONE; continue; case TM_NOISE: continue; } } if (!(state & ZONE) && (j = tmzone_ATTLC(s, &t, type, &dst)) != TM_LOCALZONE) { s = t; zone += j; state |= ZONE; continue; } if (!type && (type = tmtype_ATTLC(s, &t))) { s = t; continue; } state |= BREAK; } else if (*s == '/') { if (!isdigit(*++s) || (state & MONTH) || n == 0 || n > 12) break; i = n - 1; n = (int)strtol(s, &t, 10); s = t; if (n <= 0 || n > 31) break; if (*s == '/' && !isdigit(*(s + 1))) break; state |= MONTH|MDAY; tm->tm_mday = n; n = tm->tm_mon; tm->tm_mon = i; if (*s == '/') { n = (int)strtol(s + 1, &t, 10); s = t; } else { if (state & (LAST|NEXT|THIS)) { rel_month: if (state & LAST) tm->tm_year -= (tm->tm_mon < n) ? 0 : 1; else tm->tm_year += ((state & NEXT) ? 1 : 0) + ((tm->tm_mon < n) ? 1 : 0); if (state & MDAY) goto clear_hour; goto clear_mday; } continue; } } if (n < 0 || (state & YEAR)) break; state |= YEAR; if (n > 1900) n -= 1900; if (n < 38) n += 100; tm->tm_year = n; if (state & BREAK) { last = t; break; } continue; clear_mon: if (state & EXACT) continue; tm->tm_mon = 0; clear_mday: if (state & EXACT) continue; tm->tm_mday = 1; clear_hour: if (state & EXACT) continue; tm->tm_hour = 0; clear_min: if (state & EXACT) continue; tm->tm_min = 0; clear_sec: if (state & EXACT) continue; tm->tm_sec = 0; } done: if (e) *e = last; return(tmtime_ATTLC(tmfix_ATTLC(tm), (state & ZONE) ? zone : TM_LOCALZONE)); }