int vtime(struct tm *, const char *, int); enum vtime_flags { VTIME_START_SUNDAY = 1 }; #ifdef VTIME_IMPL static const char * vtime_next_word(const char *); static const char * vtime_relative3(struct tm *, const char *, const int); static const char * vtime_relative2(struct tm *, const char *); static const char* vtime_day_names[] = { "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" }; int vtime(struct tm *tm, const char *fmt, int flags) { int i, j, k; const char *pnt; for (; *fmt; fmt++) { if (isblank(*fmt)) continue; /* Do relative 1 */ if (*fmt == '+' || *fmt == '-') { i = isdigit(fmt[1]) ? 0 : 1; pnt = fmt+1; while (isdigit(*pnt)) i = i * 10 + (*(pnt++) - '0'); if (fmt[0] == '-') i = -i; switch (tolower(*pnt)) { case 'w': i *= 7; /* FALLTHROUGH */ case 'd': tm->tm_mday += i; break; case 'm': tm->tm_mon += i; break; case 'y': tm->tm_year += i; break; default: return 1; } fmt = pnt; /* No day-time savings. We want the same relative hh:mm */ tm->tm_isdst = -1; mktime(tm); continue; } /* Absolute 1 */ if (isdigit(fmt[0])) { i = j = k = -1; vtime_abs: k = j; j = i; for (i = 0; isdigit(*fmt); fmt++) i = i * 10 + (*fmt - '0'); if (!isblank(*fmt) && fmt[0]) { fmt++; goto vtime_abs; } if (k != -1) tm->tm_year = k + (k < 100 ? (tm->tm_year / 100 * 100) : -1900); tm->tm_mon = (j == -1) ? tm->tm_mon : j-1; tm->tm_mday = i; /* No day-time savings. We want the same relative hh:mm */ tm->tm_isdst = -1; mktime(tm); continue; } switch (tolower(fmt[0])) { case 'f': case 's': case 'b': case 'e': fmt = vtime_relative3(tm, fmt, flags & VTIME_START_SUNDAY); if (fmt == NULL) return 1; break; case 'p': case 'l': case 'n': case 't': fmt = vtime_relative2(tm, fmt); if (fmt == NULL) return 1; break; default: return 1; } } return 0; } static const char * vtime_relative2(struct tm *tm, const char *s) { size_t len; int i, next = (s[0] == 'n') || (s[0] == 't'); const char *day = vtime_next_word(s); if (!day[0]) return NULL; for (len = 0; day[len] && !isblank(day[len]); len++); for (i = 0; i < 7; i++) { if (!strncasecmp(day, vtime_day_names[i], len)) break; } /* TODO: Could change to words like month/week/year/day */ if (i == 7) return NULL; if (next) i = ((i + 13 - tm->tm_wday) % 7) + 1; else i = - (((tm->tm_wday - i + 13) % 7) + 1); tm->tm_mday += i; /* No day-time savings. We want the same relative hh:mm */ tm->tm_isdst = -1; mktime(tm); for (s = day; s[1] && !isblank(s[1]); s++); return s; } static const char * vtime_relative3(struct tm *tm, const char *s, const int startSun) { int i, toend = tolower(s[0]) == 'e'; const char *type = vtime_next_word(s); switch (tolower(*type)) { case 'w': i = tm->tm_wday; if (!startSun) i = (i + 6) % 7; tm->tm_mday += - i + 6 * toend; break; case 'm': tm->tm_mday = 1; if (toend) { tm->tm_mon++; tm->tm_mday = 0; } break; case 'y': tm->tm_mday = 1; tm->tm_mon = 0; if (toend) { tm->tm_year++; tm->tm_mday = 0; } break; default: return NULL; } /* No day-time savings. We want the same relative hh:mm */ tm->tm_isdst = -1; mktime(tm); for (s = type; s[1] && !isblank(s[1]); s++); return s; } static const char * vtime_next_word(const char *s) { while (*s && !isblank(*s)) s++; while (isblank(*s)) s++; return s; } #endif