summaryrefslogtreecommitdiff
path: root/vtime.h
diff options
context:
space:
mode:
Diffstat (limited to 'vtime.h')
-rw-r--r--vtime.h194
1 files changed, 194 insertions, 0 deletions
diff --git a/vtime.h b/vtime.h
new file mode 100644
index 0000000..011241f
--- /dev/null
+++ b/vtime.h
@@ -0,0 +1,194 @@
+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