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
26 * $FreeBSD: src/bin/date/vary.c,v 1.8.2.2 2000/12/08 11:42:53 brian Exp $
27 * $DragonFly: src/bin/date/vary.c,v 1.2 2003/06/17 04:22:49 dillon Exp $
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)
157 if (val <= t->tm_mon)
158 val += 11 - t->tm_mon; /* early next year */
160 val -= t->tm_mon + 1; /* later this year */
163 if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
174 if (val-1 > t->tm_mon)
175 val = 13 - val + t->tm_mon; /* later last year */
177 val = t->tm_mon - val + 1; /* early this year */
180 if (!adjyear(t, '-', val / 12, 0))
183 if (val > t->tm_mon) {
184 if (!adjyear(t, '-', 1, 0))
193 if (val > 12 || val < 1)
198 return !mk || domktime(t, type) != -1;
202 adjday(struct tm *t, char type, int val, int mk)
209 mdays = daysinmonth(t);
210 if (val > mdays - t->tm_mday) {
211 val -= mdays - t->tm_mday + 1;
213 if (!adjmon(t, '+', 1, 0, 0))
223 if (val >= t->tm_mday) {
226 if (!adjmon(t, '-', 1, 0, 0))
228 t->tm_mday = daysinmonth(t);
235 if (val > 0 && val <= daysinmonth(t))
242 return !mk || domktime(t, type) != -1;
246 adjwday(struct tm *t, char type, int val, int istext, int mk)
254 if (val < t->tm_wday)
255 val = 7 - t->tm_wday + val; /* early next week */
257 val -= t->tm_wday; /* later this week */
259 val *= 7; /* "-v+5w" == "5 weeks in the future" */
260 return !val || adjday(t, '+', val, mk);
263 if (val > t->tm_wday)
264 val = 7 - val + t->tm_wday; /* later last week */
266 val = t->tm_wday - val; /* early this week */
268 val *= 7; /* "-v-5w" == "5 weeks ago" */
269 return !val || adjday(t, '-', val, mk);
271 if (val < t->tm_wday)
272 return adjday(t, '-', t->tm_wday - val, mk);
275 else if (val > t->tm_wday)
276 return adjday(t, '+', val - t->tm_wday, mk);
282 adjhour(struct tm *t, char type, int val, int mk)
292 days = (t->tm_hour + val) / 24;
296 if (!adjday(t, '+', days, 0))
307 if (val > t->tm_hour) {
312 if (!adjday(t, '-', days, 0))
323 return !mk || domktime(t, type) != -1;
327 adjmin(struct tm *t, char type, int val, int mk)
335 if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
346 if (!adjhour(t, '-', val / 60, 0))
349 if (val > t->tm_min) {
350 if (!adjhour(t, '-', 1, 0))
364 return !mk || domktime(t, type) != -1;
368 adjsec(struct tm *t, char type, int val, int mk)
376 if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
387 if (!adjmin(t, '-', val / 60, 0))
390 if (val > t->tm_sec) {
391 if (!adjmin(t, '-', 1, 0))
405 return !mk || domktime(t, type) != -1;
409 vary_apply(const struct vary *v, struct tm *t)
417 for (; v; v = v->next) {
420 if (type == '+' || type == '-')
431 if (strspn(arg, digits) != len-1) {
432 val = trans(trans_wday, arg);
434 if (!adjwday(t, type, val, 1, 1))
437 val = trans(trans_mon, arg);
439 if (!adjmon(t, type, val, 1, 1))
450 if (!adjsec(t, type, val, 1))
454 if (!adjmin(t, type, val, 1))
458 if (!adjhour(t, type, val, 1))
463 if (!adjday(t, type, val, 1))
468 if (!adjwday(t, type, val, 0, 1))
473 if (!adjmon(t, type, val, 0, 1))
478 if (!adjyear(t, type, val, 1))
490 vary_destroy(struct vary *v)