psdate.c revision 20302
120253Sjoerg/*-
220302Sjoerg * Copyright (C) 1996
320302Sjoerg *	David L. Nugent.  All rights reserved.
420253Sjoerg *
520253Sjoerg * Redistribution and use in source and binary forms, with or without
620253Sjoerg * modification, are permitted provided that the following conditions
720253Sjoerg * are met:
820253Sjoerg * 1. Redistributions of source code must retain the above copyright
920302Sjoerg *    notice, this list of conditions and the following disclaimer.
1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1120253Sjoerg *    notice, this list of conditions and the following disclaimer in the
1220253Sjoerg *    documentation and/or other materials provided with the distribution.
1320253Sjoerg *
1420302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
1520253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1620253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1720302Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
1820253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1920253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2020253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2120253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2220253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2320253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2420253Sjoerg * SUCH DAMAGE.
2520253Sjoerg *
2620302Sjoerg *	$Id: psdate.c,v 1.1.1.1 1996/12/09 14:05:35 joerg Exp $
2720253Sjoerg */
2820253Sjoerg
2920253Sjoerg#include <stdio.h>
3020253Sjoerg#include <stdlib.h>
3120253Sjoerg#include <string.h>
3220253Sjoerg#include <ctype.h>
3320253Sjoerg
3420253Sjoerg#include "psdate.h"
3520253Sjoerg
3620253Sjoerg
3720253Sjoergstatic int
3820253Sjoerga2i(char const ** str)
3920253Sjoerg{
4020253Sjoerg	int             i = 0;
4120253Sjoerg	char const     *s = *str;
4220253Sjoerg
4320253Sjoerg	if (isdigit(*s)) {
4420253Sjoerg		i = atoi(s);
4520253Sjoerg		while (isdigit(*s))
4620253Sjoerg			++s;
4720253Sjoerg		*str = s;
4820253Sjoerg	}
4920253Sjoerg	return i;
5020253Sjoerg}
5120253Sjoerg
5220253Sjoergstatic int
5320253Sjoergnumerics(char const * str)
5420253Sjoerg{
5520253Sjoerg	int             rc = isdigit(*str);
5620253Sjoerg
5720253Sjoerg	if (rc)
5820253Sjoerg		while (isdigit(*str) || *str == 'x')
5920253Sjoerg			++str;
6020253Sjoerg	return rc && !*str;
6120253Sjoerg}
6220253Sjoerg
6320253Sjoergstatic int
6420253Sjoergaindex(char const * arr[], char const ** str, int len)
6520253Sjoerg{
6620253Sjoerg	int             l, i;
6720253Sjoerg	char            mystr[32];
6820253Sjoerg
6920253Sjoerg	mystr[len] = '\0';
7020253Sjoerg	l = strlen(strncpy(mystr, *str, len));
7120253Sjoerg	for (i = 0; i < l; i++)
7220253Sjoerg		mystr[i] = (char) tolower(mystr[i]);
7320253Sjoerg	for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
7420253Sjoerg	if (arr[i] == NULL)
7520253Sjoerg		i = -1;
7620253Sjoerg	else {			/* Skip past it */
7720253Sjoerg		while (**str && isalpha(**str))
7820253Sjoerg			++(*str);
7920253Sjoerg		/* And any following whitespace */
8020253Sjoerg		while (**str && (**str == ',' || isspace(**str)))
8120253Sjoerg			++(*str);
8220253Sjoerg	}			/* Return index */
8320253Sjoerg	return i;
8420253Sjoerg}
8520253Sjoerg
8620253Sjoergstatic int
8720253Sjoergweekday(char const ** str)
8820253Sjoerg{
8920253Sjoerg	static char const *days[] =
9020253Sjoerg	{"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
9120253Sjoerg
9220253Sjoerg	return aindex(days, str, 3);
9320253Sjoerg}
9420253Sjoerg
9520253Sjoergstatic int
9620253Sjoergmonth(char const ** str)
9720253Sjoerg{
9820253Sjoerg	static char const *months[] =
9920253Sjoerg	{"jan", "feb", "mar", "apr", "may", "jun", "jul",
10020253Sjoerg	"aug", "sep", "oct", "nov", "dec", NULL};
10120253Sjoerg
10220253Sjoerg	return aindex(months, str, 3);
10320253Sjoerg}
10420253Sjoerg
10520253Sjoergstatic void
10620253Sjoergparse_time(char const * str, int *hour, int *min, int *sec)
10720253Sjoerg{
10820253Sjoerg	*hour = a2i(&str);
10920253Sjoerg	if ((str = strchr(str, ':')) == NULL)
11020253Sjoerg		*min = *sec = 0;
11120253Sjoerg	else {
11220253Sjoerg		++str;
11320253Sjoerg		*min = a2i(&str);
11420253Sjoerg		*sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
11520253Sjoerg	}
11620253Sjoerg}
11720253Sjoerg
11820253Sjoerg
11920253Sjoergstatic void
12020253Sjoergparse_datesub(char const * str, int *day, int *mon, int *year)
12120253Sjoerg{
12220253Sjoerg	int             i;
12320253Sjoerg
12420253Sjoerg	static char const nchrs[] = "0123456789 \t,/-.";
12520253Sjoerg
12620253Sjoerg	if ((i = month(&str)) != -1) {
12720253Sjoerg		*mon = i;
12820253Sjoerg		if ((i = a2i(&str)) != 0)
12920253Sjoerg			*day = i;
13020253Sjoerg	} else if ((i = a2i(&str)) != 0) {
13120253Sjoerg		*day = i;
13220253Sjoerg		while (*str && strchr(nchrs + 10, *str) != NULL)
13320253Sjoerg			++str;
13420253Sjoerg		if ((i = month(&str)) != -1)
13520253Sjoerg			*mon = i;
13620253Sjoerg		else if ((i = a2i(&str)) != 0)
13720253Sjoerg			*mon = i - 1;
13820253Sjoerg	} else
13920253Sjoerg		return;
14020253Sjoerg
14120253Sjoerg	while (*str && strchr(nchrs + 10, *str) != NULL)
14220253Sjoerg		++str;
14320253Sjoerg	if (isdigit(*str)) {
14420253Sjoerg		*year = atoi(str);
14520253Sjoerg		if (*year > 1900)
14620253Sjoerg			*year -= 1900;
14720253Sjoerg		else if (*year < 32)
14820253Sjoerg			*year += 100;
14920253Sjoerg	}
15020253Sjoerg}
15120253Sjoerg
15220253Sjoerg
15320253Sjoerg/*-
15420253Sjoerg * Parse time must be flexible, it handles the following formats:
15520253Sjoerg * nnnnnnnnnnn		UNIX timestamp (all numeric), 0 = now
15620253Sjoerg * 0xnnnnnnnn		UNIX timestamp in hexadecimal
15720253Sjoerg * 0nnnnnnnnn		UNIX timestamp in octal
15820253Sjoerg * 0			Given time
15920253Sjoerg * +nnnn[smhdwoy]	Given time + nnnn hours, mins, days, weeks, months or years
16020253Sjoerg * -nnnn[smhdwoy]	Given time - nnnn hours, mins, days, weeks, months or years
16120253Sjoerg * dd[ ./-]mmm[ ./-]yy	Date }
16220253Sjoerg * hh:mm:ss		Time } May be combined
16320253Sjoerg */
16420253Sjoerg
16520253Sjoergtime_t
16620253Sjoergparse_date(time_t dt, char const * str)
16720253Sjoerg{
16820253Sjoerg	char           *p;
16920253Sjoerg	int             i;
17020253Sjoerg	long            val;
17120253Sjoerg	struct tm      *T;
17220253Sjoerg
17320253Sjoerg	if (dt == 0)
17420253Sjoerg		dt = time(NULL);
17520253Sjoerg
17620253Sjoerg	while (*str && isspace(*str))
17720253Sjoerg		++str;
17820253Sjoerg
17920253Sjoerg	if (numerics(str)) {
18020253Sjoerg		val = strtol(str, &p, 0);
18120253Sjoerg		dt = val ? val : dt;
18220253Sjoerg	} else if (*str == '+' || *str == '-') {
18320253Sjoerg		val = strtol(str, &p, 0);
18420253Sjoerg		switch (*p) {
18520253Sjoerg		case 'h':
18620253Sjoerg		case 'H':	/* hours */
18720253Sjoerg			dt += (val * 3600L);
18820253Sjoerg			break;
18920253Sjoerg		case '\0':
19020253Sjoerg		case 'm':
19120253Sjoerg		case 'M':	/* minutes */
19220253Sjoerg			dt += (val * 60L);
19320253Sjoerg			break;
19420253Sjoerg		case 's':
19520253Sjoerg		case 'S':	/* seconds */
19620253Sjoerg			dt += val;
19720253Sjoerg			break;
19820253Sjoerg		case 'd':
19920253Sjoerg		case 'D':	/* days */
20020253Sjoerg			dt += (val * 86400L);
20120253Sjoerg			break;
20220253Sjoerg		case 'w':
20320253Sjoerg		case 'W':	/* weeks */
20420253Sjoerg			dt += (val * 604800L);
20520253Sjoerg			break;
20620253Sjoerg		case 'o':
20720253Sjoerg		case 'O':	/* months */
20820253Sjoerg			T = localtime(&dt);
20920253Sjoerg			T->tm_mon += (int) val;
21020253Sjoerg			i = T->tm_mday;
21120253Sjoerg			goto fixday;
21220253Sjoerg		case 'y':
21320253Sjoerg		case 'Y':	/* years */
21420253Sjoerg			T = localtime(&dt);
21520253Sjoerg			T->tm_year += (int) val;
21620253Sjoerg			i = T->tm_mday;
21720253Sjoerg	fixday:
21820253Sjoerg			dt = mktime(T);
21920253Sjoerg			T = localtime(&dt);
22020253Sjoerg			if (T->tm_mday != i) {
22120253Sjoerg				T->tm_mday = 1;
22220253Sjoerg				dt = mktime(T);
22320253Sjoerg				dt -= (time_t) 86400L;
22420253Sjoerg			}
22520253Sjoerg		default:	/* unknown */
22620253Sjoerg			break;	/* leave untouched */
22720253Sjoerg		}
22820253Sjoerg	} else {
22920253Sjoerg		char           *q, tmp[64];
23020253Sjoerg
23120253Sjoerg		/*
23220253Sjoerg		 * Skip past any weekday prefix
23320253Sjoerg		 */
23420253Sjoerg		weekday(&str);
23520253Sjoerg		str = strncpy(tmp, str, sizeof tmp - 1);
23620253Sjoerg		tmp[sizeof tmp - 1] = '\0';
23720253Sjoerg		T = localtime(&dt);
23820253Sjoerg
23920253Sjoerg		/*
24020253Sjoerg		 * See if we can break off any timezone
24120253Sjoerg		 */
24220253Sjoerg		while ((q = strrchr(tmp, ' ')) != NULL) {
24320253Sjoerg			if (strchr("(+-", q[1]) != NULL)
24420253Sjoerg				*q = '\0';
24520253Sjoerg			else {
24620253Sjoerg				int             j = 1;
24720253Sjoerg
24820253Sjoerg				while (q[j] && isupper(q[j]))
24920253Sjoerg					++j;
25020253Sjoerg				if (q[j] == '\0')
25120253Sjoerg					*q = '\0';
25220253Sjoerg				else
25320253Sjoerg					break;
25420253Sjoerg			}
25520253Sjoerg		}
25620253Sjoerg
25720253Sjoerg		/*
25820253Sjoerg		 * See if there is a time hh:mm[:ss]
25920253Sjoerg		 */
26020253Sjoerg		if ((p = strchr(tmp, ':')) == NULL) {
26120253Sjoerg
26220253Sjoerg			/*
26320253Sjoerg			 * No time string involved
26420253Sjoerg			 */
26520253Sjoerg			T->tm_hour = T->tm_min = T->tm_sec = 0;
26620253Sjoerg			parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
26720253Sjoerg		} else {
26820253Sjoerg			char            datestr[64], timestr[64];
26920253Sjoerg
27020253Sjoerg			/*
27120253Sjoerg			 * Let's chip off the time string
27220253Sjoerg			 */
27320253Sjoerg			if ((q = strpbrk(p, " \t")) != NULL) {	/* Time first? */
27420253Sjoerg				int             l = q - str;
27520253Sjoerg
27620253Sjoerg				strncpy(timestr, str, l);
27720253Sjoerg				timestr[l] = '\0';
27820253Sjoerg				strncpy(datestr, q + 1, sizeof datestr);
27920253Sjoerg				datestr[sizeof datestr - 1] = '\0';
28020253Sjoerg				parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
28120253Sjoerg				parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
28220253Sjoerg			} else if ((q = strrchr(tmp, ' ')) != NULL) {	/* Time last */
28320253Sjoerg				int             l = q - tmp;
28420253Sjoerg
28520253Sjoerg				strncpy(timestr, q + 1, sizeof timestr);
28620253Sjoerg				timestr[sizeof timestr - 1] = '\0';
28720253Sjoerg				strncpy(datestr, tmp, l);
28820253Sjoerg				datestr[l] = '\0';
28920253Sjoerg			} else	/* Bail out */
29020253Sjoerg				return dt;
29120253Sjoerg			parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
29220253Sjoerg			parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
29320253Sjoerg		}
29420253Sjoerg		dt = mktime(T);
29520253Sjoerg	}
29620253Sjoerg	return dt;
29720253Sjoerg}
298