summaryrefslogtreecommitdiffstats
path: root/bin/date/vary.c
diff options
context:
space:
mode:
authorbrian <brian@FreeBSD.org>1997-08-04 03:37:07 +0000
committerbrian <brian@FreeBSD.org>1997-08-04 03:37:07 +0000
commitfdc5ce4a45ba240d51b1446cd73859d43e9a11c7 (patch)
treeba21dc6c79ca9c2ec10d7ae68044d956d485f7dd /bin/date/vary.c
parenteb599a86b687c502745bbc36a4b17d788cdc28c5 (diff)
downloadFreeBSD-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.c337
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;
+ }
+}
OpenPOWER on IntegriCloud