/* Copyright (C) 1995 NetWin World, New Zealand, All rights reserved. */
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include "emsg.h"
#include "lib.h"

int parsedate(char *rec);
int get_hour(char *rec, int *hour, int *min, int *sec);
int get_zone(char *rec); 
int tzone_find(char *s);
int month_find(char *s);

/* Could use the routine in INN, it's public domain, but it's also crap
(it's not really crap, I just don't like using yacc to do something 
this simple, it means people have to have and understand yacc to modify it.
which includes most unix guru's, and virtually no-one else	

Doesn't implement relative times, e.g.  2 days,  3 hours, 1 week.

Don't try and be nice with dates, if it isn't valid, chuck it, it should
never have been accepted this far.
*/

/*
	Date: Fri, 19 Nov 82 16:14:55 EST
	Date: Sun, 27 Mar 83 20:39:37 -0500  -02:00:00
	Date: Fri, 19 Nov 82 16:14:55 + 22:00:12
	Date: Fri, 19 Nov 82 16:14:55 -22:00:12
	Date: Fri, 19 Nov 82 16:14:55 GMT
	Date: 1 Oct 86 11:26:15 GMT
Added to allow 
	Date: Fri, 1999 Nov 82 16:14:55 GMT
	Date: 1 Oct 1986 11:26:15 GMT
*/
int parsedate(char *rec)
{
	struct tm t;
	char bf[200]; 
	char *s;
	int tzone;
	int y;
	int rt;

	ncpy(bf,rec,199); s = bf;
	memset(&t,0,sizeof(t));
	s = strtok(bf,", \n\t");
	if (s==NULL) return 0;
	if (!isdigit(*s)) { 
		/* Must be optional Day, ignore it!! */
		s = strtok(NULL," \n\t"); if (s==NULL) return 0;
	} 
	t.tm_mday = atoi(s); 
	if (t.tm_mday==0) return 0;
	s = strtok(NULL," \n\t"); if (s==NULL) return 0;
	t.tm_mon = month_find(s); /* returns 0-11 */
	if (t.tm_mon<0) return 0;
	s = strtok(NULL," \n\t"); if (s==NULL) return 0;
	y = atoi(s);
	if (y<50) y += 100;  /* Allow 00 = 2000*/
	if (y>200) y -= 1900; /* Allow 2010 = 110 */
	t.tm_year = y;
	if (t.tm_year==0) return 0;
	s = strtok(NULL," \n\t"); if (s==NULL) return 0;
	if (!get_hour(s,&t.tm_hour,&t.tm_min,&t.tm_sec)) return 0;

	s = strtok(NULL," \n\t"); 
	if (s==NULL) { /* Should have been a time zone, lets assume GMT */ 	
		tzone = 0;
	} else {
		tzone = get_zone(s);
	}

	if (s!=NULL) s = strtok(NULL," \n\t"); 
	if (s!=NULL) tzone += get_zone(s); /*Offset from time zone*/
	if (tzone > (60*60*27) ) tzone = 0;
	if (tzone < (-60*60*27) ) tzone = 0;
        t.tm_hour += (tzone/(60*60));
        t.tm_min += ((tzone % (60*60))/60);
/*	t.tm_sec += tzone;*/
#ifdef VAX
	return mymktime(&t);
#else
	rt = mktime(&t);
	if (rt == -1) if (t.tm_year>99)  { /* Try and skip past daylight saving black holes */
		t.tm_hour+=2;
		rt = mktime(&t);
	}
	return lib_fixtime(rt);
#endif
}
int get_hour(char *rec, int *hour, int *min, int *sec)
{
	char hours[200],bf[50];
	char *s2,*s3;

	*hour = 0; *min = 0; *sec = 0;
	ncpy(hours,rec,190);
	s2 = strchr(hours,':');
	if (s2==NULL) { /* Maybe it's 4 digit format, no colon's */
		if (strlen(hours)==2) {
			*hour = atoi(hours); return TRUE;
		}
		if (strlen(hours)==3) {
			ncpy(bf,hours,1); 
		 	*hour = atoi(bf); return TRUE;
		}
		if (strlen(hours)!=4) return FALSE; 
		ncpy(bf,hours,2); *hour = atoi(bf);
		ncpy(bf,hours+2,2); *min = atoi(bf);
		*sec = 0;
		return TRUE;
	} 
	s3 = strchr(s2+1,':');
	*hour = atoi(hours);
	*min = atoi(s2+1);
	*sec = 0;	
	if (s3!=NULL) *sec = atoi(s3+1);
	return TRUE;
}
int get_zone(char *rec) /* return time zone offset in seconds, GMT = 0 */
{
	int tsign = 1;
	int h,m,sec;

	if (*rec=='+') rec++;
	if (*rec=='-') {tsign = -1; rec++;}
	if (isdigit(*rec)) {
		if (!get_hour(rec,&h,&m,&sec)) return 0;
		return tsign*(h*60*60+m*60+sec);
	} else {
		return tzone_find(rec);
	}	
}

typedef struct {char *name; int type; int value;} TABLE;
#define tZONE 0
#define tDAYZONE 1
#define HOUR(x) (x*60)

static TABLE	tzone_table[] = {
    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
    { "ut",	tZONE,     HOUR( 0) },	/* Universal */
    { "utc",	tZONE,     HOUR( 0) },	/* Universal Coordinated */
    { "cut",	tZONE,     HOUR( 0) },	/* Coordinated Universal */
    { "z",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
    { "nst",	tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
    { "ndt",	tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
    { "akst",	tZONE,     HOUR( 9) },	/* Alaska Standard */
    { "akdt",	tDAYZONE,  HOUR( 9) },	/* Alaska Daylight */
    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
    { "hast",	tZONE,     HOUR(10) },	/* Hawaii-Aleutian Standard */
    { "hadt",	tDAYZONE,  HOUR(10) },	/* Hawaii-Aleutian Daylight */
    { "ces",	tDAYZONE,  -HOUR(2) },	/* Central European Summer */
    { "cest",	tDAYZONE,  -HOUR(2) },	/* Central European Summer */
    { "mez",	tZONE,     -HOUR(1) },	/* Middle European */
    { "mezt",	tDAYZONE,  -HOUR(2) },	/* Middle European Summer */
    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe */
    { "msk",	tZONE,     -HOUR(3) },	/* Moscow Winter */
    { "msd",	tDAYZONE,  -HOUR(4) },	/* Moscow Summer */
    { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
    { "wadt",	tDAYZONE,  -HOUR(9) },	/* West Australian Daylight */
    { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
    { "cct",	tZONE,     -HOUR(8) },	/* China Coast */
    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard */
    { "kst",	tZONE,     -HOUR(9) },	/* Korean Standard */
    { "kdt",	tZONE,     -HOUR(10) },	/* Korean Daylight */
    { "cast",	tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
    { "cadt",	tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
    { "nzdt",	tDAYZONE,  -HOUR(13) },	/* New Zealand Daylight */
    { NULL,	tDAYZONE,  0 }	/* End of table */
};

int tzone_find(char *s)
{
	char bf[100];
	int i;
	ncpy(bf,s,30);
	strlwr(bf);
	if (strlen(bf)==1) { /* Military time zones, could have sign wrong*/
		if (*bf>='a' && *bf<='m') return -(bf[0]-'a'+1)*60;
		if (*bf>='n' && *bf<='y') return (bf[0]-'n'+1)*60;
		return 0;
	}
	for (i=0; tzone_table[i].name!=NULL; i++) {
		if (strcmp(bf,tzone_table[i].name)==0) 
			return tzone_table[i].value*60;
	}
	return 0;
}
static char *months[]={
	"jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"
};
int month_find(char *s)
{
	char bf[100];
	int i;
	ncpy(bf,s,30);
	strlwr(bf);
	
	for (i=0; i<12; i++) if (strncmp(bf,months[i],3)==0) return i;
	return -1;
}	
#ifdef TESTIT
main()
{
	char bf[200];
	time_t t;
	struct tm *tt;
	for (;;) {
		gets(bf);
		printf("Input %s \n",bf);
		t = (time_t) parsedate(bf);
		tt = localtime(&t);
		printf("Time %d {%s} \n",t,asctime(tt));
	}
}
char *strlwr(char *s)
{
	char *ss=s;
	for (;*s!=0;s++) *s = tolower(*s);
	return ss;
}
char *ncpy(char *dst, char *src, int len)
{
	strncpy(dst,src,len);
	dst[len] = 0;
	return dst;
}

#endif
