diff options
author | brian <brian@FreeBSD.org> | 1997-08-04 03:37:07 +0000 |
---|---|---|
committer | brian <brian@FreeBSD.org> | 1997-08-04 03:37:07 +0000 |
commit | fdc5ce4a45ba240d51b1446cd73859d43e9a11c7 (patch) | |
tree | ba21dc6c79ca9c2ec10d7ae68044d956d485f7dd /bin/date/vary.c | |
parent | eb599a86b687c502745bbc36a4b17d788cdc28c5 (diff) | |
download | FreeBSD-src-fdc5ce4a45ba240d51b1446cd73859d43e9a11c7.zip FreeBSD-src-fdc5ce4a45ba240d51b1446cd73859d43e9a11c7.tar.gz |
Add [-DWMY] flags for date adjustment.
Discussed with: freebsd-hackers
Diffstat (limited to 'bin/date/vary.c')
-rw-r--r-- | bin/date/vary.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/bin/date/vary.c b/bin/date/vary.c new file mode 100644 index 0000000..5df3a0c --- /dev/null +++ b/bin/date/vary.c @@ -0,0 +1,337 @@ +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include "vary.h" + +struct trans { + int val; + char *str; +}; + +static struct trans trans_mon[] = { + { 1, "jan" }, { 2, "feb" }, { 3, "mar" }, { 4, "apr" }, { 5, "may" }, + { 6, "jun" }, { 7, "jul" }, { 8, "aug" }, { 9, "sep" }, { 10, "oct" }, + { 11, "nov" }, { 12, "dec" }, + { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, + { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" }, + { 10, "october" }, { 11, "november" }, { 12, "december" }, + { -1, NULL } +}; + +static struct trans trans_wday[] = { + { 0, "sun" }, { 1, "mon" }, { 2, "tue" }, { 3, "wed" }, { 4, "thr" }, + { 4, "thu" }, { 5, "fri" }, { 6, "sat" }, + { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, + { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, + { -1, NULL } +}; + +static char digits[] = "0123456789"; + +static int +trans(const struct trans t[], const char *arg) +{ + int f; + + if (strspn(arg, digits) == strlen(arg)) + return atoi(arg); + + for (f = 0; t[f].val != -1; f++) + if (!strcasecmp(t[f].str, arg)) + return t[f].val; + + return -1; +} + +struct vary * +vary_append(struct vary *v, char flag, char *arg) +{ + struct vary *result, **nextp; + + if (!strchr("DWMY", flag)) + return 0; + + if (v) { + result = v; + while (v->next) + v = v->next; + nextp = &v->next; + } else + nextp = &result; + + *nextp = (struct vary *)malloc(sizeof(struct vary)); + (*nextp)->flag = flag; + (*nextp)->arg = arg; + (*nextp)->next = NULL; + return result; +} + +static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +static int +daysinmonth(const struct tm *t) +{ + int year; + + year = t->tm_year + 1900; + + if (t->tm_mon == 1) + if (!(year % 400)) + return 29; + else if (!(year % 100)) + return 28; + else if (!(year % 4)) + return 29; + else + return 28; + else if (t->tm_mon >= 0 && t->tm_mon < 12) + return mdays[t->tm_mon]; + + return 0; +} + + +static int +adjyear(struct tm *t, char type, int val) +{ + switch (type) { + case '+': + t->tm_year += val; + break; + case '-': + t->tm_year -= val; + break; + default: + t->tm_year = val; + if (t->tm_year < 69) + t->tm_year += 100; /* as per date.c */ + else if (t->tm_year > 1900) + t->tm_year -= 1900; /* struct tm holds years since 1900 */ + break; + } + return mktime(t) != -1; +} + +static int +sadjyear(struct tm *t, char *arg) +{ + switch (*arg) { + case '+': + case '-': + return adjyear(t, *arg, atoi(arg+1)); + default: + return adjyear(t, '\0', atoi(arg)); + } +} + +static int +adjmon(struct tm *t, char type, int val, int istext) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (istext) + if (val <= t->tm_mon) + val += 11 - t->tm_mon; /* early next year */ + else + val -= t->tm_mon + 1; /* later this year */ + if (!adjyear(t, '+', (t->tm_mon + val) / 12)) + return 0; + val %= 12; + t->tm_mon += val; + if (t->tm_mon > 11) + t->tm_mon -= 12; + break; + + case '-': + if (istext) + if (val-1 > t->tm_mon) + val = 13 - val + t->tm_mon; /* later last year */ + else + val = t->tm_mon - val + 1; /* early this year */ + if (!adjyear(t, '-', val / 12)) + return 0; + val %= 12; + if (val > t->tm_mon) { + if (!adjyear(t, '-', 1)) + return 0; + val -= 12; + } + t->tm_mon -= val; + break; + + default: + if (val > 12 || val < 1) + return 0; + t->tm_mon = --val; + } + + return mktime(t) != -1; +} + +static int +sadjmon(struct tm *t, char *arg) +{ + int istext; + int val; + + switch (*arg) { + case '+': + case '-': + istext = strspn(arg+1, digits) != strlen(arg+1); + val = trans(trans_mon, arg+1); + return adjmon(t, *arg, val, istext); + default: + istext = strspn(arg, digits) != strlen(arg); + val = trans(trans_mon, arg); + return adjmon(t, '\0', val, istext); + } +} + +static int +adjday(struct tm *t, char type, int val) +{ + int mdays; + switch (type) { + case '+': + while (val) { + mdays = daysinmonth(t); + if (val > mdays - t->tm_mday) { + val -= mdays - t->tm_mday + 1; + t->tm_mday = 1; + if (!adjmon(t, '+', 1, 0)) + return 0; + } else { + t->tm_mday += val; + val = 0; + } + } + break; + case '-': + while (val) + if (val >= t->tm_mday) { + val -= t->tm_mday; + t->tm_mday = 1; + if (!adjmon(t, '-', 1, 0)) + return 0; + t->tm_mday = daysinmonth(t); + } else { + t->tm_mday -= val; + val = 0; + } + break; + default: + if (val > 0 && val <= daysinmonth(t)) + t->tm_mday = val; + else + return 0; + break; + } + + return mktime(t) != -1; +} + +static int +sadjwday(struct tm *t, char *arg) +{ + int istext; + int val; + + switch (*arg) { + case '+': + case '-': + istext = strspn(arg+1, digits) != strlen(arg+1); + val = trans(trans_wday, arg+1); + break; + default: + istext = 0; + val = trans(trans_wday, arg); + break; + } + + if (val < 0) + return 0; + + switch (*arg) { + case '+': + if (istext) + if (val < t->tm_wday) + val = 7 - t->tm_wday + val; /* early next week */ + else + val -= t->tm_wday; /* later this week */ + else + val *= 7; /* "-W +5" == "5 weeks in the future" */ + return adjday(t, '+', val); + case '-': + if (istext) + if (val > t->tm_wday) + val = 7 - val + t->tm_wday; /* later last week */ + else + val = t->tm_wday - val; /* early this week */ + else + val *= 7; /* "-W -5" == "5 weeks ago" */ + return adjday(t, '-', val); + default: + if (val < t->tm_wday) + return adjday(t, '-', t->tm_wday - val); + else if (val > 6) + return 0; + else if (val > t->tm_wday) + return adjday(t, '+', val - t->tm_wday); + } + return 1; +} + +static int +sadjday(struct tm *t, char *arg) +{ + switch (*arg) { + case '+': + case '-': + return adjday(t, *arg, atoi(arg+1)); + default: + return adjday(t, '\0', atoi(arg)); + } +} + +const struct vary * +vary_apply(const struct vary *v, struct tm *t) +{ + for (; v; v = v->next) { + switch (v->flag) { + case 'D': + if (!sadjday(t, v->arg)) + return v; + break; + case 'W': + if (!sadjwday(t, v->arg)) + return v; + break; + case 'M': + if (!sadjmon(t, v->arg)) + return v; + break; + case 'Y': + if (!sadjyear(t, v->arg)) + return v; + break; + default: + return v; + } + } + return 0; +} + +void +vary_destroy(struct vary *v) +{ + struct vary *n; + + while (v) { + n = v->next; + free(v); + v = n; + } +} |