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.16 2004/08/09 13:43:39 yar Exp $
27 * $DragonFly: src/bin/date/vary.c,v 1.4 2005/07/20 06:10:51 cpressey 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)
158 if (val <= t->tm_mon)
159 val += 11 - t->tm_mon; /* early next year */
161 val -= t->tm_mon + 1; /* later this year */
164 if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
175 if (val-1 > t->tm_mon)
176 val = 13 - val + t->tm_mon; /* later last year */
178 val = t->tm_mon - val + 1; /* early this year */
181 if (!adjyear(t, '-', val / 12, 0))
184 if (val > t->tm_mon) {
185 if (!adjyear(t, '-', 1, 0))
194 if (val > 12 || val < 1)
199 /* e.g., -v-1m on March, 31 is the last day of February in common sense */
200 lmdays = daysinmonth(t);
201 if (t->tm_mday > lmdays)
204 return !mk || domktime(t, type) != -1;
208 adjday(struct tm *t, char type, int val, int mk)
215 daycount = daysinmonth(t);
216 if (val > daycount - t->tm_mday) {
217 val -= daycount - t->tm_mday + 1;
219 if (!adjmon(t, '+', 1, 0, 0))
229 if (val >= t->tm_mday) {
232 if (!adjmon(t, '-', 1, 0, 0))
234 t->tm_mday = daysinmonth(t);
241 if (val > 0 && val <= daysinmonth(t))
248 return !mk || domktime(t, type) != -1;
252 adjwday(struct tm *t, char type, int val, int istext, int mk)
260 if (val < t->tm_wday)
261 val = 7 - t->tm_wday + val; /* early next week */
263 val -= t->tm_wday; /* later this week */
265 val *= 7; /* "-v+5w" == "5 weeks in the future" */
266 return !val || adjday(t, '+', val, mk);
269 if (val > t->tm_wday)
270 val = 7 - val + t->tm_wday; /* later last week */
272 val = t->tm_wday - val; /* early this week */
274 val *= 7; /* "-v-5w" == "5 weeks ago" */
275 return !val || adjday(t, '-', val, mk);
277 if (val < t->tm_wday)
278 return adjday(t, '-', t->tm_wday - val, mk);
281 else if (val > t->tm_wday)
282 return adjday(t, '+', val - t->tm_wday, mk);
288 adjhour(struct tm *t, char type, int val, int mk)
298 days = (t->tm_hour + val) / 24;
302 if (!adjday(t, '+', days, 0))
313 if (val > t->tm_hour) {
318 if (!adjday(t, '-', days, 0))
329 return !mk || domktime(t, type) != -1;
333 adjmin(struct tm *t, char type, int val, int mk)
341 if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
352 if (!adjhour(t, '-', val / 60, 0))
355 if (val > t->tm_min) {
356 if (!adjhour(t, '-', 1, 0))
370 return !mk || domktime(t, type) != -1;
374 adjsec(struct tm *t, char type, int val, int mk)
382 if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
393 if (!adjmin(t, '-', val / 60, 0))
396 if (val > t->tm_sec) {
397 if (!adjmin(t, '-', 1, 0))
411 return !mk || domktime(t, type) != -1;
415 vary_apply(const struct vary *v, struct tm *t)
423 for (; v; v = v->next) {
426 if (type == '+' || type == '-')
437 if (strspn(arg, digits) != len-1) {
438 val = trans(trans_wday, arg);
440 if (!adjwday(t, type, val, 1, 1))
443 val = trans(trans_mon, arg);
445 if (!adjmon(t, type, val, 1, 1))
456 if (!adjsec(t, type, val, 1))
460 if (!adjmin(t, type, val, 1))
464 if (!adjhour(t, type, val, 1))
469 if (!adjday(t, type, val, 1))
474 if (!adjwday(t, type, val, 0, 1))
479 if (!adjmon(t, type, val, 0, 1))
484 if (!adjyear(t, type, val, 1))
496 vary_destroy(struct vary *v)