psdate.c revision 30259
1/*-
2 * Copyright (C) 1996
3 *	David L. Nugent.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
14 * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT 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 DAVID L. NUGENT 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
24 * SUCH DAMAGE.
25 */
26
27#ifndef lint
28static const char rcsid[] =
29	"$Id$";
30#endif /* not lint */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <ctype.h>
36
37#include "psdate.h"
38
39
40static int
41a2i(char const ** str)
42{
43	int             i = 0;
44	char const     *s = *str;
45
46	if (isdigit(*s)) {
47		i = atoi(s);
48		while (isdigit(*s))
49			++s;
50		*str = s;
51	}
52	return i;
53}
54
55static int
56numerics(char const * str)
57{
58	int             rc = isdigit(*str);
59
60	if (rc)
61		while (isdigit(*str) || *str == 'x')
62			++str;
63	return rc && !*str;
64}
65
66static int
67aindex(char const * arr[], char const ** str, int len)
68{
69	int             l, i;
70	char            mystr[32];
71
72	mystr[len] = '\0';
73	l = strlen(strncpy(mystr, *str, len));
74	for (i = 0; i < l; i++)
75		mystr[i] = (char) tolower(mystr[i]);
76	for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
77	if (arr[i] == NULL)
78		i = -1;
79	else {			/* Skip past it */
80		while (**str && isalpha(**str))
81			++(*str);
82		/* And any following whitespace */
83		while (**str && (**str == ',' || isspace(**str)))
84			++(*str);
85	}			/* Return index */
86	return i;
87}
88
89static int
90weekday(char const ** str)
91{
92	static char const *days[] =
93	{"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
94
95	return aindex(days, str, 3);
96}
97
98static int
99month(char const ** str)
100{
101	static char const *months[] =
102	{"jan", "feb", "mar", "apr", "may", "jun", "jul",
103	"aug", "sep", "oct", "nov", "dec", NULL};
104
105	return aindex(months, str, 3);
106}
107
108static void
109parse_time(char const * str, int *hour, int *min, int *sec)
110{
111	*hour = a2i(&str);
112	if ((str = strchr(str, ':')) == NULL)
113		*min = *sec = 0;
114	else {
115		++str;
116		*min = a2i(&str);
117		*sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
118	}
119}
120
121
122static void
123parse_datesub(char const * str, int *day, int *mon, int *year)
124{
125	int             i;
126
127	static char const nchrs[] = "0123456789 \t,/-.";
128
129	if ((i = month(&str)) != -1) {
130		*mon = i;
131		if ((i = a2i(&str)) != 0)
132			*day = i;
133	} else if ((i = a2i(&str)) != 0) {
134		*day = i;
135		while (*str && strchr(nchrs + 10, *str) != NULL)
136			++str;
137		if ((i = month(&str)) != -1)
138			*mon = i;
139		else if ((i = a2i(&str)) != 0)
140			*mon = i - 1;
141	} else
142		return;
143
144	while (*str && strchr(nchrs + 10, *str) != NULL)
145		++str;
146	if (isdigit(*str)) {
147		*year = atoi(str);
148		if (*year > 1900)
149			*year -= 1900;
150		else if (*year < 32)
151			*year += 100;
152	}
153}
154
155
156/*-
157 * Parse time must be flexible, it handles the following formats:
158 * nnnnnnnnnnn		UNIX timestamp (all numeric), 0 = now
159 * 0xnnnnnnnn		UNIX timestamp in hexadecimal
160 * 0nnnnnnnnn		UNIX timestamp in octal
161 * 0			Given time
162 * +nnnn[smhdwoy]	Given time + nnnn hours, mins, days, weeks, months or years
163 * -nnnn[smhdwoy]	Given time - nnnn hours, mins, days, weeks, months or years
164 * dd[ ./-]mmm[ ./-]yy	Date }
165 * hh:mm:ss		Time } May be combined
166 */
167
168time_t
169parse_date(time_t dt, char const * str)
170{
171	char           *p;
172	int             i;
173	long            val;
174	struct tm      *T;
175
176	if (dt == 0)
177		dt = time(NULL);
178
179	while (*str && isspace(*str))
180		++str;
181
182	if (numerics(str)) {
183		val = strtol(str, &p, 0);
184		dt = val ? val : dt;
185	} else if (*str == '+' || *str == '-') {
186		val = strtol(str, &p, 0);
187		switch (*p) {
188		case 'h':
189		case 'H':	/* hours */
190			dt += (val * 3600L);
191			break;
192		case '\0':
193		case 'm':
194		case 'M':	/* minutes */
195			dt += (val * 60L);
196			break;
197		case 's':
198		case 'S':	/* seconds */
199			dt += val;
200			break;
201		case 'd':
202		case 'D':	/* days */
203			dt += (val * 86400L);
204			break;
205		case 'w':
206		case 'W':	/* weeks */
207			dt += (val * 604800L);
208			break;
209		case 'o':
210		case 'O':	/* months */
211			T = localtime(&dt);
212			T->tm_mon += (int) val;
213			i = T->tm_mday;
214			goto fixday;
215		case 'y':
216		case 'Y':	/* years */
217			T = localtime(&dt);
218			T->tm_year += (int) val;
219			i = T->tm_mday;
220	fixday:
221			dt = mktime(T);
222			T = localtime(&dt);
223			if (T->tm_mday != i) {
224				T->tm_mday = 1;
225				dt = mktime(T);
226				dt -= (time_t) 86400L;
227			}
228		default:	/* unknown */
229			break;	/* leave untouched */
230		}
231	} else {
232		char           *q, tmp[64];
233
234		/*
235		 * Skip past any weekday prefix
236		 */
237		weekday(&str);
238		str = strncpy(tmp, str, sizeof tmp - 1);
239		tmp[sizeof tmp - 1] = '\0';
240		T = localtime(&dt);
241
242		/*
243		 * See if we can break off any timezone
244		 */
245		while ((q = strrchr(tmp, ' ')) != NULL) {
246			if (strchr("(+-", q[1]) != NULL)
247				*q = '\0';
248			else {
249				int             j = 1;
250
251				while (q[j] && isupper(q[j]))
252					++j;
253				if (q[j] == '\0')
254					*q = '\0';
255				else
256					break;
257			}
258		}
259
260		/*
261		 * See if there is a time hh:mm[:ss]
262		 */
263		if ((p = strchr(tmp, ':')) == NULL) {
264
265			/*
266			 * No time string involved
267			 */
268			T->tm_hour = T->tm_min = T->tm_sec = 0;
269			parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
270		} else {
271			char            datestr[64], timestr[64];
272
273			/*
274			 * Let's chip off the time string
275			 */
276			if ((q = strpbrk(p, " \t")) != NULL) {	/* Time first? */
277				int             l = q - str;
278
279				strncpy(timestr, str, l);
280				timestr[l] = '\0';
281				strncpy(datestr, q + 1, sizeof datestr);
282				datestr[sizeof datestr - 1] = '\0';
283				parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
284				parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
285			} else if ((q = strrchr(tmp, ' ')) != NULL) {	/* Time last */
286				int             l = q - tmp;
287
288				strncpy(timestr, q + 1, sizeof timestr);
289				timestr[sizeof timestr - 1] = '\0';
290				strncpy(datestr, tmp, l);
291				datestr[l] = '\0';
292			} else	/* Bail out */
293				return dt;
294			parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
295			parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
296		}
297		dt = mktime(T);
298	}
299	return dt;
300}
301