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 $
43 static struct trans trans_mon
[] = {
44 { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
45 { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" },
46 { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" },
50 static struct trans trans_wday
[] = {
51 { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
52 { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
56 static int adjhour(struct tm
*, char, long, 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
, long 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
, long 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
, long val
, int mk
)
216 daycount
= daysinmonth(t
);
217 if (val
> daycount
- t
->tm_mday
) {
218 val
-= daycount
- 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
, long 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
, long 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
, long 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
, long 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 * WARNING! Severely deficient relative to gnu date's -d option
418 * - timezone handling is all wrong (see also TZ= test in date.c)
419 * - doesn't check for 'Z' suffix
420 * - doesn't handle a multitude of formats that gnu date handles
421 * - is generally a mess
425 vary_apply(const struct vary
*vb
, time_t tval
, struct tm
*t
)
427 const struct vary
*v
;
434 *t
= *localtime(&tval
);
436 for (v
= vb
; v
; v
= v
->next
) {
452 tt
= strtoul(arg
+ 1, NULL
, 10);
458 * Delta verses absolute
460 if (type
== '+' || type
== '-') {
462 } else if (strncmp(arg
, "next", 4) == 0) {
465 } else if (strncmp(arg
, "last", 4) == 0) {
475 while (isspace(arg
[0]))
482 * Reset dst calculation for absolute specifications so
483 * it gets recalculated.
491 val
= strtoul(arg
, &tmp
, 10);
492 if (tmp
!= arg
&& isalpha(tmp
[0])) {
493 if (strcmp(tmp
, "S") == 0 || strncmp(tmp
, "sec", 3) == 0) {
494 if (!adjsec(t
, type
, val
, 1))
496 } else if (strcmp(tmp
, "M") == 0 || strncmp(tmp
, "min", 3) == 0) {
497 if (!adjmin(t
, type
, val
, 1))
499 } else if (strcmp(tmp
, "H") == 0 || strncmp(tmp
, "hour", 4) == 0) {
500 if (!adjhour(t
, type
, val
, 1))
502 } else if (strcmp(tmp
, "d") == 0 || strncmp(tmp
, "day", 3) == 0) {
504 if (!adjday(t
, type
, val
, 1))
506 } else if (strcmp(tmp
, "w") == 0 || strncmp(tmp
, "week", 4) == 0) {
508 if (!adjwday(t
, type
, val
, 0, 1))
510 } else if (strcmp(tmp
, "m") == 0 || strncmp(tmp
, "mon", 3) == 0) {
512 if (!adjmon(t
, type
, val
, 0, 1))
514 } else if (strcmp(tmp
, "y") == 0 || strncmp(tmp
, "year", 4) == 0) {
516 if (!adjyear(t
, type
, val
, 1))
527 * [wdayname[,]] monthname day h:m:s tzone yyyy
528 * [wdayname[,]] monthname day h:m:s YYYY tzone
529 * year-month-day h:m:s[Z]
535 if ((val
= trans(trans_wday
, arg
)) != -1) {
536 if (!adjwday(t
, type
, val
, 1, 1))
538 while (isalpha(*arg
))
540 while (isspace(arg
[0]))
545 * Month_string [day ]
547 if ((val
= trans(trans_mon
, arg
)) != -1) {
548 if (!adjmon(t
, type
, val
, 1, 1))
551 while (isalpha(*arg
))
553 while (isspace(*arg
))
556 val
= strtoul(arg
, &tmp
, 10);
557 if (tmp
!= arg
&& (isspace(*tmp
) || *tmp
== 0)) {
559 if (!adjday(t
, 0, val
, 1))
563 while (isspace(arg
[0]))
573 val
= strtol(arg
, &tmp
, 10);
574 if (tmp
!= arg
&& (*tmp
== 0 || isspace(*tmp
))) {
575 if (!adjyear(t
, type
, val
, 1))
579 while (isspace(arg
[0]))
584 val
= strtol(arg
, &tmp
, 10);
585 if (dohms
&& tmp
!= arg
&& *tmp
== ':') {
591 sscanf(arg
, "%d:%d:%d%c", &hr
, &mi
, &se
, &zflag
);
593 if (!adjhour(t
, type
, hr
, 1))
597 if (!adjmin(t
, type
, mi
, 1))
601 if (!adjsec(t
, type
, se
, 1))
609 while (arg
[0] && !isspace(arg
[0]))
611 while (isspace(arg
[0]))
616 val
= strtol(arg
, &tmp
, 10);
617 if (tmp
!= arg
&& *tmp
== 0) {
618 if (!adjyear(t
, type
, val
, 1))
622 while (isspace(arg
[0]))
636 val
= strtol(arg
, NULL
, 10);
645 vary_destroy(struct vary
*v
)