2 * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
42 static struct trans trans_mon[] = {
43 { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
44 { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" },
45 { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" },
49 static struct trans trans_wday[] = {
50 { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
51 { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
55 static char digits[] = "0123456789";
56 static int adjhour(struct tm *, char, int64_t, int);
59 domktime(struct tm *t, char type)
63 while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138)
64 /* While mktime() fails, adjust by an hour */
65 adjhour(t, type == '-' ? type : '+', 1, 0);
71 trans(const struct trans t[], const char *arg)
75 for (f = 0; t[f].val != -1; f++)
76 if (!strncasecmp(t[f].str, arg, 3) ||
77 !strncasecmp(t[f].str, arg, strlen(t[f].str)))
84 vary_append(struct vary *v, char *arg)
86 struct vary *result, **nextp;
96 if ((*nextp = (struct vary *)malloc(sizeof(struct vary))) == NULL)
99 (*nextp)->next = NULL;
103 static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
106 daysinmonth(const struct tm *t)
110 year = t->tm_year + 1900;
115 else if (!(year % 100))
117 else if (!(year % 4))
121 else if (t->tm_mon >= 0 && t->tm_mon < 12)
122 return mdays[t->tm_mon];
129 adjyear(struct tm *t, char type, int64_t val, int mk)
141 t->tm_year += 100; /* as per date.c */
142 else if (t->tm_year > 1900)
143 t->tm_year -= 1900; /* struct tm holds years since 1900 */
146 return !mk || domktime(t, type) != -1;
150 adjmon(struct tm *t, char type, int64_t val, int istext, int mk)
160 if (val <= t->tm_mon)
161 val += 11 - t->tm_mon; /* early next year */
163 val -= t->tm_mon + 1; /* later this year */
166 if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
177 if (val-1 > t->tm_mon)
178 val = 13 - val + t->tm_mon; /* later last year */
180 val = t->tm_mon - val + 1; /* early this year */
183 if (!adjyear(t, '-', val / 12, 0))
186 if (val > t->tm_mon) {
187 if (!adjyear(t, '-', 1, 0))
196 if (val > 12 || val < 1)
201 /* e.g., -v-1m on March, 31 is the last day of February in common sense */
202 lmdays = daysinmonth(t);
203 if (t->tm_mday > lmdays)
206 return !mk || domktime(t, type) != -1;
210 adjday(struct tm *t, char type, int64_t val, int mk)
217 lmdays = daysinmonth(t);
218 if (val > lmdays - t->tm_mday) {
219 val -= lmdays - t->tm_mday + 1;
221 if (!adjmon(t, '+', 1, 0, 0))
231 if (val >= t->tm_mday) {
234 if (!adjmon(t, '-', 1, 0, 0))
236 t->tm_mday = daysinmonth(t);
243 if (val > 0 && val <= daysinmonth(t))
250 return !mk || domktime(t, type) != -1;
254 adjwday(struct tm *t, char type, int64_t val, int istext, int mk)
262 if (val < t->tm_wday)
263 val = 7 - t->tm_wday + val; /* early next week */
265 val -= t->tm_wday; /* later this week */
267 val *= 7; /* "-v+5w" == "5 weeks in the future" */
268 return !val || adjday(t, '+', val, mk);
271 if (val > t->tm_wday)
272 val = 7 - val + t->tm_wday; /* later last week */
274 val = t->tm_wday - val; /* early this week */
276 val *= 7; /* "-v-5w" == "5 weeks ago" */
277 return !val || adjday(t, '-', val, mk);
279 if (val < t->tm_wday)
280 return adjday(t, '-', t->tm_wday - val, mk);
283 else if (val > t->tm_wday)
284 return adjday(t, '+', val - t->tm_wday, mk);
290 adjhour(struct tm *t, char type, int64_t val, int mk)
300 days = (t->tm_hour + val) / 24;
304 if (!adjday(t, '+', days, 0))
315 if (val > t->tm_hour) {
320 if (!adjday(t, '-', days, 0))
331 return !mk || domktime(t, type) != -1;
335 adjmin(struct tm *t, char type, int64_t val, int mk)
343 if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
354 if (!adjhour(t, '-', val / 60, 0))
357 if (val > t->tm_min) {
358 if (!adjhour(t, '-', 1, 0))
372 return !mk || domktime(t, type) != -1;
376 adjsec(struct tm *t, char type, int64_t val, int mk)
384 if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
395 if (!adjmin(t, '-', val / 60, 0))
398 if (val > t->tm_sec) {
399 if (!adjmin(t, '-', 1, 0))
413 return !mk || domktime(t, type) != -1;
417 vary_apply(const struct vary *v, struct tm *t)
425 for (; v; v = v->next) {
428 if (type == '+' || type == '-')
439 if (strspn(arg, digits) != len-1) {
440 val = trans(trans_wday, arg);
442 if (!adjwday(t, type, val, 1, 1))
445 val = trans(trans_mon, arg);
447 if (!adjmon(t, type, val, 1, 1))
458 if (!adjsec(t, type, val, 1))
462 if (!adjmin(t, type, val, 1))
466 if (!adjhour(t, type, val, 1))
471 if (!adjday(t, type, val, 1))
476 if (!adjwday(t, type, val, 0, 1))
481 if (!adjmon(t, type, val, 0, 1))
486 if (!adjyear(t, type, val, 1))
498 vary_destroy(struct vary *v)