/* $Id: date.c,v 1.5 2015/09/29 13:17:42 onoe Exp $ */

/*-
 * Copyright (c) 1999 Atsushi Onoe
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>

#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "cue.h"

struct nameval {
	char *name;
	int  val;
};

static const struct nameval month[] = {
	{ "Jan", 31 }, { "Feb", 28 }, { "Mar", 31}, { "Apr", 30 },
	{ "May", 31 }, { "Jun", 30 }, { "Jul", 31}, { "Aug", 31 },
	{ "Sep", 30 }, { "Oct", 31 }, { "Nov", 30}, { "Dec", 31 },
	{ NULL, 0 }
};
static const struct nameval zone[] = {
	{ "GMT", 0 },
	{ "JST", +9*60*60 },
	{ "EST", -5*60*60 }, { "EDT", -4*60*60 },
	{ "CST", -6*60*60 }, { "CDT", -5*60*60 },
	{ "MST", -7*60*60 }, { "MDT", -6*60*60 },
	{ "PST", -8*60*60 }, { "PDT", -7*60*60 },
	{ NULL, 0 }
};

time_t
parse_date(ccbuf_t *cb)
{
	const char *s, *ep;
	char *p;
	int d, m, n, val;
	const struct nameval *nv;

	val = 0;
	s = CP(cb);
	ep = CE(cb);

	/* skip the name of day */
	while (*s && !isdigit((u_char)*s)) {
		if (s == ep)
			return -1;
		s++;
	}

	/* day of the month */
	if ((d = strtol(s, &p, 10)) == 0 || d < 1 || p > ep)
		return -1;
	s = p;
	val += (d - 1) * 24*60*60;

	/* month */
	while (*s && isspace((u_char)*s)) {
		if (s == ep)
			return -1;
		s++;
	}
	for (m = 0, nv = month; ; m++, nv++) {
		if (nv->name == NULL)
			return -1;
		if (strncasecmp(nv->name, s, 3) == 0)
			break;
		val += nv->val * 24*60*60;
	}
	while (*s && isalpha((u_char)*s)) {
		if (s == ep)
			return -1;
		s++;
	}

	/* year */
	n = strtol(s, &p, 10);
	if (s == p || n < 0 || p > ep)
		return -1;
	s = p;
	n = (n + 100 - 70) % 100 + 70;		/* 1970..2038 -> 70..138 */
	if (n < 70 || n > 138)
		return -1;
	val += (n - 70) * 365*24*60*60;
	val += (n / 4 - 70 / 4) * 24*60*60;	/* leap year */
	if (n % 4 == 0 && m < 2)
		val -= 24*60*60;
	if (d > (n % 4 == 0 && m == 1 ? nv->val + 1 : nv->val))
		return -1;

	/* hour */
	n = strtoul(s, &p, 10);
	if (s == p || p > ep || *p != ':' || n < 0 || n >= 24)
		return -1;
	s = p + 1;
	val += n * 60*60;

	/* minute */
	n = strtoul(s, &p, 10);
	if (s == p || p > ep || n < 0 || n >= 60)
		return -1;
	s = p;
	val += n * 60;

	/* [second] */
	if (*s == ':') {
		s++;
		n = strtoul(s, &p, 10);
		if (s == p || p > ep || n < 0 || n >= 60)
			return -1;
		s = p;
		val += n;
	}

	/* [zone] */
	while (*s && isspace((u_char)*s)) {
		if (s == ep)
			return val;
		s++;
	}
	if (*s == '+' || *s == '-') {
		n = strtol(s, &p, 10);
		n = ((n / 100) * 60 + n % 100) * 60;	/* +0900 */
	} else {
		n = 0;
		for (nv = zone; nv->name; nv++) {
			if (strncasecmp(nv->name, s, 3) == 0) {
				n = nv->val;
				break;
			}
		}
	}
	val -= n;
	return val;
}

#ifdef TEST

void
main(int argc, char **argv)
{
	int t;
	cbuf_t cb;

	while (*++argv) {
		cb.ptr = *argv;
		cb.len = strlen(cb.ptr);
		t = parse_date(&cb);
		printf("%d: %s", t, ctime(&t));
	}
	exit(0);
}
#endif
