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$");
41 static struct trans trans_mon[] = {
42 { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
43 { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" },
44 { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" },
48 static struct trans trans_wday[] = {
49 { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
50 { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
54 static char digits[] = "0123456789";
55 static int adjhour(struct tm *, char, int, int);
58 domktime(struct tm *t, char type)
62 while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138)
63 /* While mktime() fails, adjust by an hour */
64 adjhour(t, type == '-' ? type : '+', 1, 0);
70 trans(const struct trans t[], const char *arg)
74 for (f = 0; t[f].val != -1; f++)
75 if (!strncasecmp(t[f].str, arg, 3) ||
76 !strncasecmp(t[f].str, arg, strlen(t[f].str)))
83 vary_append(struct vary *v, char *arg)
85 struct vary *result, **nextp;
95 if ((*nextp = (struct vary *)malloc(sizeof(struct vary))) == NULL)
98 (*nextp)->next = NULL;
102 static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
105 daysinmonth(const struct tm *t)
109 year = t->tm_year + 1900;
114 else if (!(year % 100))
116 else if (!(year % 4))
120 else if (t->tm_mon >= 0 && t->tm_mon < 12)
121 return mdays[t->tm_mon];
128 adjyear(struct tm *t, char type, int val, int mk)
140 t->tm_year += 100; /* as per date.c */
141 else if (t->tm_year > 1900)
142 t->tm_year -= 1900; /* struct tm holds years since 1900 */
145 return !mk || domktime(t, type) != -1;
149 adjmon(struct tm *t, char type, int val, int istext, int mk)
159 if (val <= t->tm_mon)
160 val += 11 - t->tm_mon; /* early next year */
162 val -= t->tm_mon + 1; /* later this year */
165 if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
176 if (val-1 > t->tm_mon)
177 val = 13 - val + t->tm_mon; /* later last year */
179 val = t->tm_mon - val + 1; /* early this year */
182 if (!adjyear(t, '-', val / 12, 0))
185 if (val > t->tm_mon) {
186 if (!adjyear(t, '-', 1, 0))
195 if (val > 12 || val < 1)
200 /* e.g., -v-1m on March, 31 is the last day of February in common sense */
201 lmdays = daysinmonth(t);
202 if (t->tm_mday > lmdays)
205 return !mk || domktime(t, type) != -1;
209 adjday(struct tm *t, char type, int val, int mk)
216 lmdays = daysinmonth(t);
217 if (val > lmdays - t->tm_mday) {
218 val -= lmdays - t->tm_mday + 1;
220 if (!adjmon(t, '+', 1, 0, 0))
230 if (val >= t->tm_mday) {
233 if (!adjmon(t, '-', 1, 0, 0))
235 t->tm_mday = daysinmonth(t);
242 if (val > 0 && val <= daysinmonth(t))
249 return !mk || domktime(t, type) != -1;
253 adjwday(struct tm *t, char type, int val, int istext, int mk)
261 if (val < t->tm_wday)
262 val = 7 - t->tm_wday + val; /* early next week */
264 val -= t->tm_wday; /* later this week */
266 val *= 7; /* "-v+5w" == "5 weeks in the future" */
267 return !val || adjday(t, '+', val, mk);
270 if (val > t->tm_wday)
271 val = 7 - val + t->tm_wday; /* later last week */
273 val = t->tm_wday - val; /* early this week */
275 val *= 7; /* "-v-5w" == "5 weeks ago" */
276 return !val || adjday(t, '-', val, mk);
278 if (val < t->tm_wday)
279 return adjday(t, '-', t->tm_wday - val, mk);
282 else if (val > t->tm_wday)
283 return adjday(t, '+', val - t->tm_wday, mk);
289 adjhour(struct tm *t, char type, int val, int mk)
299 days = (t->tm_hour + val) / 24;
303 if (!adjday(t, '+', days, 0))
314 if (val > t->tm_hour) {
319 if (!adjday(t, '-', days, 0))
330 return !mk || domktime(t, type) != -1;
334 adjmin(struct tm *t, char type, int val, int mk)
342 if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
353 if (!adjhour(t, '-', val / 60, 0))
356 if (val > t->tm_min) {
357 if (!adjhour(t, '-', 1, 0))
371 return !mk || domktime(t, type) != -1;
375 adjsec(struct tm *t, char type, int val, int mk)
383 if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
394 if (!adjmin(t, '-', val / 60, 0))
397 if (val > t->tm_sec) {
398 if (!adjmin(t, '-', 1, 0))
412 return !mk || domktime(t, type) != -1;
416 vary_apply(const struct vary *v, struct tm *t)
424 for (; v; v = v->next) {
427 if (type == '+' || type == '-')
438 if (strspn(arg, digits) != len-1) {
439 val = trans(trans_wday, arg);
441 if (!adjwday(t, type, val, 1, 1))
444 val = trans(trans_mon, arg);
446 if (!adjmon(t, type, val, 1, 1))
457 if (!adjsec(t, type, val, 1))
461 if (!adjmin(t, type, val, 1))
465 if (!adjhour(t, type, val, 1))
470 if (!adjday(t, type, val, 1))
475 if (!adjwday(t, type, val, 0, 1))
480 if (!adjmon(t, type, val, 0, 1))
485 if (!adjyear(t, type, val, 1))
497 vary_destroy(struct vary *v)