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