/*
 * date.c --
 *	Functions for the built-in type "AbsoluteTime".
 *	Functions for the built-in type "RelativeTime".
 *	Functions for the built-in type "TimeInterval".
 *
 * Notes:
 *	This code is actually (almost) unused.
 *	It needs to be integrated with Time and struct trange.
 *
 * XXX	This code needs to be rewritten to work with the "new" definitions
 * XXX	in h/tim.h.  Look for int32's, int, long, etc. in the code.  The
 * XXX	definitions in h/tim.h may need to be rethought also.
 */

#include <ctype.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <strings.h>

#include "tmp/postgres.h"
#include "tmp/miscadmin.h"
#include "utils/log.h"
#include "utils/nabstime.h"

RcsId("$Header: /private/postgres/src/utils/adt/RCS/date.c,v 1.25 1992/06/18 18:16:01 mer Exp $");

#define	TM_YEAR_BASE	1900		/* compatible to UNIX time */
#define	EPOCH_YEAR	1970		/* compatible to UNIX time */
#define	YEAR_MAX	2038		/* otherwise overflow */
#define	YEAR_MIN	1902		/* otherwise overflow */
#define	DAYS_PER_LYEAR	366
#define	DAYS_PER_NYEAR	365
#define	HOURS_PER_DAY	24
#define	MINS_PER_HOUR	60
#define	SECS_PER_MIN	60
#define	MAX_LONG	2147483647	/* 2^31 */

/* absolute time definitions */
#define	TIME_NOW_STR		"now"	   /* represents time now */
#define	TIME_EPOCH_STR		"epoch"	   /* Jan 1 00:00:00 1970 GMT */
#define TIME_EPOCH_STR_LEN	(sizeof(TIME_EPOCH_STR)-1)
#ifndef INVALID_ABSTIME
#define	INVALID_ABSTIME		MAX_LONG
#endif	!INVALID_ABSTIME
	/* -2145916801 invalid time representation */
	/*   Dec 31 23:59:59 1901       */
#define	INVALID_ABSTIME_STR 	"Undefined AbsTime"
#define	INVALID_ABSTIME_STR_LEN	(sizeof(INVALID_ABSTIME_STR)-1)

/*
 *  Unix epoch is Jan  1 00:00:00 1970.  Postgres knows about times
 *  sixty-eight years on either side of that.
 */ 

#define	MAX_ABSTIME		2147483647	/* Jan 19 03:14:07 2038 GMT */
#define	MIN_ABSTIME		(0)		/* Jan  1 00:00:00 1970 */

/* relative time definitions */
#define	MAX_RELTIME		2144448000
	/* about 68 years, compatible to absolute time */
#define	INVALID_RELTIME		MAX_LONG
	/* invalid reltime representation */
#define	INVALID_RELTIME_STR	"Undefined RelTime"
#define	INVALID_RELTIME_STR_LEN (sizeof(INVALID_RELTIME_STR)-1)
#define	RELTIME_LABEL		'@'
#define	RELTIME_PAST		"ago"
#define	DIRMAXLEN		(sizeof(RELTIME_PAST)-1)

#define	InAbsTimeInterval(T) \
	((int32)(T) <= MAX_ABSTIME && (int32)(T) >= MIN_ABSTIME)
#define	InRelTimeInterval(T) \
	((int32)(T) <= MAX_RELTIME && (int32)(T) >= - MAX_RELTIME)
#define	IsCharDigit(C)		isdigit(C)
#define	IsCharA_Z(C)		isalpha(C)		
#define	IsSpace(C)		((C) == ' ')
#define	IsNull(C)		((C) == NULL)

#define	T_INTERVAL_INVAL   0	/* data represents no valid interval */
#define	T_INTERVAL_VALID   1	/* data represents a valid interval */
#define	T_INTERVAL_UNCOM   2	/* data represents an uncomplete interval */
#define	T_INTERVAL_LEN     47	/* 2+20+1+1+1+20+2 : ['...' '...']  */
#define	INVALID_INTERVAL_STR		"Undefined Range"
#define	INVALID_INTERVAL_STR_LEN	(sizeof(INVALID_INTERVAL_STR)-1)

static char	*month_name[] = {
	"Jan","Feb","Mar","Apr","May","Jun","Jul",
	"Aug","Sep","Oct","Nov","Dec" };

static int	day_tab[2][12] = {
	{31,28,31,30,31,30,31,31,30,31,30,31},
	{31,29,31,30,31,30,31,31,30,31,30,31}  };

static	char 	*unit_tab[] = {
	"second", "seconds", "minute", "minutes",
	"hour", "hours", "day", "days", "week", "weeks",
	"month", "months", "year", "years"};
#define UNITMAXLEN 7	/* max length of a unit name */

/* table of seconds per unit (month = 30 days, year = 365 days)  */
static	int	sec_tab[] = { 
	1,1, 60, 60,
	3600, 3600, 86400, 86400, 604800,  604800,
	2592000,  2592000,  31536000,  31536000 };

/* maximal values (in seconds) per unit which can be represented */
static	int	unit_max_quantity[] = {
	2144448000, 2144448000, 35740800, 35740800,
	595680, 595680, 24820, 24820, 3545, 3545,
	827, 827, 68, 68 };


#ifdef linux
struct tm *CurrentLocalTime = 0;
#else
struct timeb *TimeDifferenceFromGMT = NULL;
#endif
static bool TimeDiffIsInited = false;
static char *timezonename = NULL;

/*
 * Function prototypes -- internal to this file only
 */

int isabstime ARGS((char *datestring , struct tm *brokentime ));
int correct_month ARGS((char month [], int *num ));
int isleap ARGS((int year ));
int timeinsec ARGS((struct tm *t , AbsoluteTime *seconds ));
int isreltime ARGS((char *timestring , int *sign , long *quantity , int *unitnr ));
int correct_unit ARGS((char unit [], int *unptr ));
int correct_dir ARGS((char direction [], int *signptr ));
int istinterval ARGS((char *i_string , AbsoluteTime *i_start , AbsoluteTime *i_end ));
char *timenowout ARGS((void ));
AbsoluteTime timenow ARGS((void ));
RelativeTime intervalrel ARGS((TimeInterval interval ));
int ininterval ARGS((int32 t , TimeInterval interval ));
AbsoluteTime timemi ARGS((AbsoluteTime AbsTime_t1 , RelativeTime RelTime_t2 ));
char *tintervalout ARGS((TimeInterval interval ));
TimeInterval tintervalin ARGS((char *intervalstr ));
int32 intervaleq ARGS((TimeInterval i1 , TimeInterval i2 ));
int32 intervalleneq ARGS((TimeInterval i , RelativeTime t ));
int32 intervallenne ARGS((TimeInterval i , RelativeTime t ));
int32 intervallenlt ARGS((TimeInterval i , RelativeTime t ));
int32 intervallengt ARGS((TimeInterval i , RelativeTime t ));
int32 intervallenle ARGS((TimeInterval i , RelativeTime t ));
int32 intervallenge ARGS((TimeInterval i , RelativeTime t ));
int32 intervalct ARGS((TimeInterval i1 , TimeInterval i2 ));
int32 intervalov ARGS((TimeInterval i1 , TimeInterval i2 ));
AbsoluteTime intervalstart ARGS((TimeInterval i ));
AbsoluteTime intervalend ARGS((TimeInterval i ));

	    /* ========== USER I/O ROUTINES ========== */

/*
 *	abstimein	- converts a time string to an internal format
 *			  checks syntax and range of datetime,
 *			  returns time as long integer in respect of GMT
 */

AbsoluteTime	
abstimein(datetime)
	char	*datetime;
{
	int		which;
	AbsoluteTime	time_;
	struct tm	brokentime;

	if ( TimeDiffIsInited == false ) {
#ifdef linux
	    time_t ti;
	    CurrentLocalTime = (struct tm *) malloc (sizeof(struct tm));
            time(&ti);
            memcpy(CurrentLocalTime, localtime(&ti), sizeof(struct tm));
	    timezonename = tzname[CurrentLocalTime -> tm_isdst];
#else
	    TimeDifferenceFromGMT = (struct timeb *)
	      malloc( sizeof(struct timeb));
	    ftime(TimeDifferenceFromGMT);
	    timezonename = (char *) timezone ( TimeDifferenceFromGMT->timezone,
			        TimeDifferenceFromGMT->dstflag ) ;
#endif
	    TimeDiffIsInited = true;
	}

	which = isabstime(datetime, &brokentime);

	switch (which) {

	case 3:							/* epoch */
		time_ = MIN_ABSTIME;
		break;

	case 2:							/* now */
		time_ = GetCurrentTransactionStartTime();
		break;

	case 1:							/* from user */
		(void) timeinsec(&brokentime, &time_);

		/* user inputs in local time, we need to store things
		 * in GMT so that moving databases across timezones
		 * (amongst other things) behave predictably
		 */

		time_ = time_ +
#ifdef linux
		  timezone - (CurrentLocalTime -> tm_isdst) * 3600;
#else
		  (TimeDifferenceFromGMT->timezone * 60 ) -
		    ( TimeDifferenceFromGMT->dstflag * 3600 ) ;
#endif
		  
		
		break;

	case 0:							/* error */
		time_ = INVALID_ABSTIME;
		break;
	}

	return(time_);
}

/*
 *	abstimeout	- converts the internal time format to a string
 */
char	*
abstimeout(datetime)
	AbsoluteTime	datetime;
	/* seconds in respect of Greenwich Mean Time GMT!! */
{
	extern	struct tm	*gmtime();
	extern	char		*asctime();
	extern	char		*index();
	extern	char		*strcpy();
	struct tm		*brokentime;
	/*points to static structure in gmtime*/
	char			*datestring, *p;
	int			i;

	if ( TimeDiffIsInited == false ) {
#ifdef linux
	    time_t ti;
	    CurrentLocalTime = (struct tm *) malloc (sizeof(struct tm));
            time(&ti);
            memcpy(CurrentLocalTime, localtime(&ti), sizeof(struct tm));
	    timezonename = tzname[CurrentLocalTime -> tm_isdst];
#else
	    TimeDifferenceFromGMT = (struct timeb *)
	      malloc( sizeof(struct timeb));
	    ftime(TimeDifferenceFromGMT);
	    timezonename = (char *) timezone ( TimeDifferenceFromGMT->timezone,
			        TimeDifferenceFromGMT->dstflag ) ;
#endif
	    TimeDiffIsInited = true;
	}

	if (datetime == INVALID_ABSTIME) {
	    datestring = (char *) palloc ( INVALID_ABSTIME_STR_LEN +1 );
	    (void) strcpy(datestring,INVALID_ABSTIME_STR);
	} else if ( datetime == 0 ) {
	    datestring = (char *)palloc ( TIME_EPOCH_STR_LEN + 1 );
	    (void) strcpy(datestring, TIME_EPOCH_STR );
	} else   {
	    /* Using localtime instead of gmtime
	     * does the correct conversion !!!
	     */
	    char *temp_date_string = NULL;

	    brokentime = localtime((long *) &datetime);
	    temp_date_string = asctime(brokentime);

	    /* add ten to the length because we want to append
	     * the local timezone spec which might be up to GMT+xx:xx
	     */

	    datestring = (char *)palloc ( strlen ( temp_date_string ) + 10 );

	    (void) strcpy(datestring, 
			  temp_date_string + 4 );    /* skip name of day */

	    /* change new-line character "\n" at the end to timezone 
	       followed by a null */
	    p = index(datestring,'\n');
	    *p = ' ';

	    strcpy ( p+1 , timezonename );
	}
	return(datestring);
}


/*
 *	reltimein	- converts a reltime string in an internal format
 */
int32	/* RelativeTime */
reltimein(timestring)
	char	*timestring;
{
	int		error;
	int32 /* RelativeTime */	timeinsec;
	int		sign, unitnr;
	long		quantity;

	error = isreltime(timestring, &sign, &quantity, &unitnr);

#ifdef	DATEDEBUG
	elog(DEBUG, "reltimein: isreltime(%s) returns error=%d, %d, %d, %d",
		timestring, error, sign, quantity, unitnr);
#endif	/* !DATEDEBUG */

	if (error != 1) {
		timeinsec = INVALID_RELTIME;  /*invalid time representation */
	} else {
		/* this check is necessary, while no control on overflow */
		if (quantity > unit_max_quantity[unitnr] || quantity < 0) {
#ifdef	DATEDEBUG
			elog(DEBUG, "reltimein: illegal quantity %d (< %d)",
				quantity, unit_max_quantity[unitnr]);
#endif	/* DATEDEBUG */
			timeinsec = INVALID_RELTIME; /* illegal quantity */
		} else {
			timeinsec = sign * quantity * sec_tab[unitnr];
#ifdef	DATEDEBUG
			elog(DEBUG, "reltimein: computed timeinsec %d",
				timeinsec);
#endif	/* DATEDEBUG */
			if (timeinsec > MAX_RELTIME || 
					timeinsec < - MAX_RELTIME) {
				timeinsec = INVALID_RELTIME;
			}
		}
	}
/*
	if (timeinsec == INVALID_RELTIME)
		(void) printf("\n\tInvalid RelTime");
*/
	if (timeinsec == InvalidTime) {
		elog(WARN, "reltimein: cannot handle reltime of 0 secs, yet");
		return(0);
	} else if (timeinsec == INVALID_ABSTIME) {
		timeinsec = InvalidTime;
	}
	return(timeinsec);
}


/*
 *	reltimeout	- converts the internal format to a reltime string
 */
char	*
reltimeout(timevalue)
	int32 /* RelativeTime */	timevalue;
{	extern char	*sprintf();
	char		*timestring;
	long		quantity;
	register int	i;
	int		unitnr;

	timestring = (char *) palloc(Max(strlen(INVALID_RELTIME_STR),
					 UNITMAXLEN));
	(void) strcpy(timestring,INVALID_RELTIME_STR);
	if (timevalue == INVALID_RELTIME)
		return(timestring);
	if (timevalue == 0)
		i = 1; /* unit = 'seconds' */
	else
		for (i = 12; i >= 0; i = i-2)
			if ((timevalue % sec_tab[i]) == 0)
				break;  /* appropriate unit found */
	unitnr = i;
	quantity = (timevalue / sec_tab[unitnr]);
	if (quantity > 1 || quantity < -1)
		unitnr++;  /* adjust index for PLURAL of unit */
	timestring = (char *) palloc(Max(strlen(INVALID_RELTIME_STR),
					 UNITMAXLEN));
	if (quantity >= 0)
		(void) sprintf( timestring, "%c %u %s", RELTIME_LABEL,
			       quantity, unit_tab[unitnr]);
	else
		(void) sprintf( timestring, "%c %u %s %s", RELTIME_LABEL,
			       (quantity * -1), unit_tab[unitnr], RELTIME_PAST);
	return(timestring);
}


/*
 *	tintervalin	- converts an interval string to an internal format
 */
TimeInterval	
tintervalin(intervalstr)
	char	*intervalstr;
{	
	int 		error;
	AbsoluteTime	i_start, i_end;
	TimeInterval	interval;

	interval = (TimeInterval) palloc(sizeof(TimeIntervalData));
	error = istinterval(intervalstr, &i_start, &i_end);
	if (error == 0)	/* not a valid interval NOT IMPLEMENTED */
		interval->status = T_INTERVAL_INVAL;
	else {
		interval->data[0] = Min(i_start, i_end);
		interval->data[1] = Max(i_start, i_end);
		interval->status = T_INTERVAL_VALID;
		/* suppose valid range*/
		if (interval->data[0] == INVALID_ABSTIME ||
		    interval->data[1] == INVALID_ABSTIME)
			interval->status = T_INTERVAL_UNCOM;  /* uncomplete */
		if (interval->data[0] == INVALID_ABSTIME &&
		    interval->data[1] == INVALID_ABSTIME)
			interval->status = T_INTERVAL_INVAL;  /* undefined  */
	}
	return(interval);
}


/*
 *	tintervalout	- converts an internal interval format to a string
 *
 */
char	*
tintervalout(interval)
	TimeInterval	interval;
{	extern	char	*abstimeout();
	extern	char	*strcat();
	extern	char	*strcpy();
	char	*i_str, *p;

	i_str = (char	*) palloc( T_INTERVAL_LEN );  /* ['...' '...'] */
	p = (char *) palloc(T_INTERVAL_LEN);
	(void) strcpy(i_str,"[\'");
	if (interval->status == T_INTERVAL_INVAL)
		(void) strcat(i_str,INVALID_INTERVAL_STR);
	else {
		p = nabstimeout(interval->data[0]);
		(void) strcat(i_str,p);
		(void) strcat(i_str,"\' \'");
		p = nabstimeout(interval->data[1]);
		(void) strcat(i_str,nabstimeout(interval->data[1]));
	}
	(void) strcat(i_str,"\']\0");
	return(i_str);
}


	     /* ========== PUBLIC ROUTINES ========== */


/*
 *	timemi		- returns the value of (AbsTime_t1 - RelTime_t2)
 */
AbsoluteTime
timemi(AbsTime_t1, RelTime_t2)
	AbsoluteTime	AbsTime_t1;
	RelativeTime	RelTime_t2;
{
       AbsoluteTime	t;

       if (InAbsTimeInterval(AbsTime_t1) && InRelTimeInterval(RelTime_t2)) {
	       t = AbsTime_t1 - RelTime_t2;
	       if (InAbsTimeInterval(t))
		       return(t);
       }
       return(INVALID_ABSTIME);
}


/*
 *	timepl		- returns the avlue of (AbsTime_t1 + RelTime_t2)
 */
AbsoluteTime
timepl(AbsTime_t1,RelTime_t2)
	AbsoluteTime	AbsTime_t1;
	RelativeTime	RelTime_t2;
{
	AbsoluteTime	t;

	if (InAbsTimeInterval(AbsTime_t1) && InRelTimeInterval(RelTime_t2)) {
		t = AbsTime_t1 + RelTime_t2;
		if (InAbsTimeInterval(t))
			return(t);
	}
	return(INVALID_ABSTIME);
}


/*
 *	ininterval	- returns 1, iff absolute date is in the interval
 */
int
ininterval(t, interval)
	int32	/* AbsoluteTime */	t;
	TimeInterval	interval;
{
	if (interval->status == T_INTERVAL_VALID) {
		if (t >= (int32)interval->data[0] &&
				t <= (int32)interval->data[1]) {

			return(1);	/* t in valid interval */
		}
	}
	return(0);
}

/*
 *	intervalrel	- returns  relative time corresponding to interval
 */
RelativeTime
intervalrel(interval)
	TimeInterval	interval;
{
	if (interval->status == T_INTERVAL_VALID)
		return(timemi(interval->data[1], interval->data[0]));
	else
		return(INVALID_RELTIME);
}


/*
 *	timenow		- returns  time "now", internal format
 *
 *      Now AbsoluteTime is time since Jan 1 1970 -mer 7 Feb 1992
 */
AbsoluteTime	
timenow()
{
	struct timeval tp;
	struct timezone tzp;

	(void) gettimeofday(&tp, &tzp);
	return((AbsoluteTime)tp.tv_sec);
}


/*
 *	timenowout	- returns a pointer to "<current_date_and_time>"
 */
char	*
timenowout()
{
	extern			gettimeofday();
	extern	struct tm 	*localtime();
	extern	char		*asctime();

	struct timeval	*tp;
	struct timezone *tzp;
	struct tm	*brokentime;
	long		sec;
	int		i;
	char		*datestring;

	tp = (struct timeval *) palloc(sizeof(struct timeval));
	tzp= (struct timezone *) palloc(sizeof(struct timezone));
	(void) gettimeofday(tp, tzp);
	sec = tp->tv_sec;
	brokentime = (struct tm *) palloc(sizeof(struct tm));
	brokentime = localtime(&sec);
	datestring = asctime(brokentime);
	for (i=0; i<=3; i++)
		datestring=datestring++;
	return(datestring);
}


/*
 *	reltimeeq	- returns 1, iff arguments are equal
 *	reltimene	- returns 1, iff arguments are not equal
 *	reltimelt	- returns 1, iff t1 less than t2
 *	reltimegt	- returns 1, iff t1 greater than t2
 *	reltimele	- returns 1, iff t1 less than or equal to t2
 *	reltimege	- returns 1, iff t1 greater than or equal to t2
 */
int32
reltimeeq(t1,t2)
	RelativeTime	t1,t2;
{ return(t1 == t2); }

int32
reltimene(t1, t2)
	RelativeTime	t1,t2;
{ return(t1 != t2); }

int32
reltimelt(t1, t2)
	int32 /* RelativeTime */	t1,t2;
{ return(t1 < t2); }

int32
reltimegt(t1, t2)
	int32 /* RelativeTime */	t1,t2;
{ return(t1 > t2); }

int32
reltimele(t1, t2)
	int32 /* RelativeTime */	t1,t2;
{ return(t1 <= t2); }

int32
reltimege(t1, t2)
	int32 /* RelativeTime */	t1,t2;
{ return(t1 >= t2); }


/*
 *	intervaleq	- returns 1, iff interval i1 is equal to interval i2
 */
int32
intervaleq(i1, i2)
	TimeInterval	i1, i2;
{
	Assert(0);	/* XXX */

	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
		return(0);	/* invalid interval */
	return((i1->data[0] == i2->data[0]) &&
	       (i1->data[1] == i2->data[1]) );
}

/*
 *	intervalleneq	- returns 1, iff length of interval i is equal to
 *				reltime t
 */
int32
intervalleneq(i,t)
	TimeInterval	i;
	RelativeTime	t;
{
	Assert(0);	/* XXX */

	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
		return(0);
	return ( (intervalrel(i) == t));
}

/*
 *	intervallenne	- returns 1, iff length of interval i is not equal
 *				to reltime t
 */
int32
intervallenne(i,t)
	TimeInterval	i;
	RelativeTime	t;
{
	Assert(0);	/* XXX */

	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
		return(0);
	return ( (intervalrel(i) != t));
}

/*
 *	intervallenlt	- returns 1, iff length of interval i is less than
 *				reltime t
 */
int32
intervallenlt(i,t)
	TimeInterval	i;
	RelativeTime	t;
{
	Assert(0);	/* XXX */

	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
		return(0);
	return ( (intervalrel(i) < t));
}

/*
 *	intervallengt	- returns 1, iff length of interval i is greater than
 *				reltime t
 */
int32
intervallengt(i,t)
	TimeInterval	i;
	RelativeTime	t;
{
	Assert(0);	/* XXX */

	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
		return(0);
	return ( (intervalrel(i) > t));
}

/*
 *	intervallenle	- returns 1, iff length of interval i is less or equal
 *				    than reltime t
 */
int32
intervallenle(i,t)
	TimeInterval	i;
	RelativeTime	t;
{	
	Assert(0);	/* XXX */

	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
		return(0);
	return ( (intervalrel(i) <= t));
}

/*
 *	intervallenge	- returns 1, iff length of interval i is greater or
 * 				equal than reltime t
 */
int32
intervallenge(i,t)
	TimeInterval	i;
	RelativeTime	t;
{
	Assert(0);	/* XXX */

	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
		return(0);
	return ( (intervalrel(i) >= t));
}

/*
 *	intervalct	- returns 1, iff interval i1 contains interval i2
 */
int32
intervalct(i1,i2)
	TimeInterval	i1, i2;
{
	Assert(0);	/* XXX */

	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
		return(0);	/* invalid interval */
	return((i1->data[0] <= i2->data[0]) &&
	       (i1->data[1] >= i2->data[1]) );
}

/*
 *	intervalov	- returns 1, iff interval i1 (partially) overlaps i2
 */
int32
intervalov(i1, i2)
	TimeInterval	i1, i2;
{
	Assert(0);	/* XXX */

	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
		return(0);	/* invalid interval */
	return(ininterval(i2->data[0], i1) ||
	       ininterval(i2->data[1], i1) );
}

/*
 *	intervalstart	- returns  the start of interval i
 */
AbsoluteTime
intervalstart(i)
	TimeInterval	i;
{
	return(i->data[0]);
}

/*
 *	intervalend	- returns  the end of interval i
 */
AbsoluteTime
intervalend(i)
	TimeInterval	i;
{
	return(i->data[1]);
}


	     /* ========== PRIVATE ROUTINES ========== */


/*
 * These are support routines for the old abstime implementation.  As
 * far as I know they are no longer used...
 *
 *	correct_month	- returns 1, iff month is a correct month descriptor
 *				     else 0.
 *	output parameter:
 *		num:	points to an integer which is an index 
 *			in table month_name[]
 */
int
correct_month(month, num)
	char	month[];
	int	*num;			/* number of month 0,...,11 */
{
	extern	int	strncmp();
	extern	char	*month_name[];
	int		i = 0;

	while (i <= 11) {
		if (strncmp(month, month_name[i],3) == 0) {
			*num = i;
			return(1);
		}
		i++;
	}
	return(0);  /* invalid month describtion */
}

/*
 *	isleap		- returns 1, iff year is a leap year
 */
int
isleap(year)
	int	year;
{
	return(year%4 == 0 && year%100 != 0 || year%400 == 0);
}


/*
 *	timeinsec	- returns 1, iff a valid date is given, otherwise 0
 *
 *	output parameter
 *		seconds:	date in seconds (in respect of GMT!)
 */
int
timeinsec(t, seconds)
	struct tm	*t;
	AbsoluteTime	*seconds;	/* absolute time in seconds */
{
	register long	sec;
	register int	year, mon;

	if (t == NULL)	
		return(0);
	sec = 0;
	*seconds = INVALID_ABSTIME;
	year = t->tm_year + TM_YEAR_BASE;
	mon = t->tm_mon + 1;  /* mon  1,...,12 */
	if (year >= EPOCH_YEAR) {
		/* collect days, maybe also the Feb 29. */
		if (isleap(year) && mon > 2)
			++sec;
		for (--year; year >= EPOCH_YEAR; --year)
			sec += isleap(year) ? DAYS_PER_LYEAR : DAYS_PER_NYEAR;
		--mon;  /* mon  0,...,11 */
		while(--mon >= 0)
			sec += day_tab[0][mon];
		sec += t->tm_mday - 1;
		sec = HOURS_PER_DAY * sec + t->tm_hour;
		sec = MINS_PER_HOUR * sec + t->tm_min;
		sec = SECS_PER_MIN * sec + t->tm_sec;
		/* year >= EPOCH_YEAR ==> sec >= 0 , otherwise overflow!! */
		if (sec >= 0 && sec <= MAX_ABSTIME) {
			*seconds = sec;	/* seconds in respect of */
			return(1);  	/*  Greenwich Mean Time GMT */
		} else
			return(0);
	} else {	/* collect days, maby also the Feb 29. */
		if(isleap(year) && mon <= 2)
			++sec;
		for (++year; year < EPOCH_YEAR; ++year)
			sec += isleap(year) ? DAYS_PER_LYEAR : DAYS_PER_NYEAR;
		--mon;  /* mon  0,...,11 */
		while(++mon <= 11)
			sec += day_tab[0][mon];
		sec += day_tab[isleap(t->tm_year)][t->tm_mon] - t->tm_mday;
		sec = HOURS_PER_DAY * sec + (24 - (t->tm_hour + 1));
		sec = MINS_PER_HOUR * sec + (60 - (t->tm_min + 1));
		sec = SECS_PER_MIN * sec + (60 - t->tm_sec);
		sec = -sec; /* year was less then EPOCH_YEAR !! */
		/* year < EPOCH_YEAR ==> sec < 0 , otherwise overflow!! */
		if (sec < 0 && sec >= MIN_ABSTIME) {
			*seconds = sec;	/* seconds in respect of */
			return(1);  	/*  Greenwich Mean Time GMT */
		} else
			return(0);
	}
}


/*
 *	isreltime	- returns 1, iff datestring is of type reltime
 *				  2, iff datestring is 'invalid time' identifier
 *				  0, iff datestring contains a syntax error
 *
 *	output parameter:
 *		sign = -1, iff direction is 'ago'
 *			       else sign = 1.
 *		quantity   : quantity of unit
 *		unitnr     :	0 or 1 ... sec
 *				2 or 3 ... min
 *				4 or 5 ... hour
 *				6 or 7 ... day
 *				8 or 9 ... week
 *			       10 or 11... month
 *			       12 or 13... year
 *			  
 *
 *	Relative time:
 *
 *	`@' ` ' Quantity ` ' Unit [ ` ' Direction]
 *
 *	OR  `Undefined RelTime' 	(see also INVALID_RELTIME_STR)
 *
 *	where
 *	Quantity is	`1', `2', ...
 *	Unit is		`second', `minute', `hour', `day', `week',
 *			`month' (30-days), or `year' (365-days),
 *     			or PLURAL of these units.
 *	Direction is	`ago'
 *
 *	VALID time  less or equal   `@ 68 years' 
 *
 */
int
isreltime(timestring, sign, quantity, unitnr)
	char	*timestring;
	int	*sign, *unitnr;
	long	*quantity;
{
	extern	int	strncmp();
	extern 	char	*strcpy();
	register char	*p;
	register char	c;
	int		i;
	char		unit[UNITMAXLEN] ;
	char		direction[DIRMAXLEN];
	int		localSign;
	int		localUnitNumber;
	long		localQuantity;

	if (!PointerIsValid(sign)) {
		sign = &localSign;
	}
	if (!PointerIsValid(unitnr)) {
		unitnr = &localUnitNumber;
	}
	if (!PointerIsValid(quantity)) {
		quantity = &localQuantity;
	}
	unit[0] = '\0';
	direction[0] = '\0';
	p = timestring;
	/* skip leading blanks */
	while (c = *p) {
		if (c != ' ')
			break;
		p++;
	}
	/* Test whether 'invalid time' identifier or not */
	if (!strncmp(INVALID_RELTIME_STR,p,strlen(INVALID_RELTIME_STR) + 1))
		return(2);	/* correct 'invalid time' identifier found */

	/* handle label of relative time */
	if (c != RELTIME_LABEL)
		return(0);	/*syntax error*/
	c = *++p;
	if (c != ' ')	return(0);	/*syntax error*/
	p++;
	/* handle the quantity */
	*quantity = 0;
	for (;;) {
		c = *p;
		if (isdigit(c)) {
			*quantity = *quantity * 10 + (c -'0');
			p++;
		} else {
			if (c == ' ' )
				break; 		/* correct quantity found */
			else
				return(0); 	/* syntax error */
		}
	}
	/* handle unit */
	p++;
	i = 0;
	for (;;) {
		c = *p;
		if (c >= 'a' && c <= 'z' && i <= (UNITMAXLEN - 1)) {
			unit[i] = c;
			p++;
			i++;
		} else {
			if ((c == ' ' || c == '\0')
			    && correct_unit(unit, unitnr))
				break;		/* correct unit found */
			else
				return(0);	/* syntax error */
		}
	}
	/* handle optional direction */
	if (c == ' ')
		p++;
	i = 0;
	*sign = 1;
	for (;;) {
		c = *p;
		if (c >= 'a' && c <= 'z' && i <= (DIRMAXLEN - 1)) {
			direction[i] = c;
			p++;
			i++;
		} else {
			if ((c == ' ' || c == NULL) && i == 0) {
				*sign = 1;
				break;  /* no direction specified */
			}
			if ((c == ' ' || c == '\0') && i != 0)
			{
			    direction[i] = '\0';
			    correct_dir(direction, sign);
			    break;	  /* correct direction found */
			}
			else
				return(0); 	/* syntax error*/
		}
	}
	return(1);
}

/*
 *	correct_unit	- returns 1, iff unit is a correct unit description
 *
 *	output parameter:
 *		unptr: points to an integer which is the appropriate unit number
 *		       (see function isreltime())
 */
int
correct_unit(unit, unptr)
	char	unit[];
	int	*unptr;
{
	int	j = 0;

	while (j >= 0) {
		if (strncmp(unit, unit_tab[j], strlen(unit_tab[j])) == 0) {
			*unptr = j;
			return(1);
		}
		j++;
	}
	return (0); /* invalid unit descriptor */
}

/*
 *	correct_dir	- returns 1, iff direction is a correct identifier
 *
 *	output parameter:
 *		signptr: points to -1 if dir corresponds to past tense
 *			 else  to 1
 */
int
correct_dir(direction,signptr)
	char	direction[];
	int	*signptr;
{	
	*signptr = 1;
	if (strncmp(RELTIME_PAST, direction, strlen(RELTIME_PAST)+1) == 0)
	{
		*signptr = -1;
		return(1);
	} else
		return (0);  /* invalid direction descriptor */
}


/*
 *	istinterval	- returns 1, iff i_string is a valid interval descr.
 *				  0, iff i_string is NOT a valid interval desc.
 *				  2, iff any time is INVALID_ABSTIME
 *
 *	output parameter:
 *		i_start, i_end: interval margins
 *
 *	Time interval:
 *	`[' {` '} `'' <AbsTime> `'' {` '} `'' <AbsTime> `'' {` '} `]'
 *
 *	OR  `Undefined Range'	(see also INVALID_INTERVAL_STR)
 *
 *	where <AbsTime> satisfies the syntax of absolute time.
 *
 *	e.g.  [  '  Jan 18 1902'   'Jan 1 00:00:00 1970']
 */
int
istinterval(i_string, i_start, i_end)
	char	*i_string;
	AbsoluteTime	*i_start, *i_end;
{
	extern	AbsoluteTime	abstimein();
	register char		*p,*p1;
	register char		c;

	p = i_string;
	/* skip leading blanks up to '[' */
	while (c = *p) {
		if ( IsSpace(c))
			p++;
		else if (c != '[')
			return(0); /* syntax error */
		else
			break;
	}
	p++;
	/* skip leading blanks up to "'" */
	while (c = *p) {
		if (IsSpace(c))
			p++;
		else if (c != '\'')
			return (0);	/* syntax error */
		else
			break;
	}
	p++;
	if (strncmp(INVALID_INTERVAL_STR,p,strlen(INVALID_INTERVAL_STR)) == 0)
		return(0);	/* undefined range, handled like a syntax err.*/
	/* search for the end of the first date and change it to a NULL*/
	p1 = p;
	while (c = *p1) {
		if ( c == '\'') {
			*p1 = NULL;
			break;
			}
		p1++;
	}
	/* get the first date */
	*i_start = nabstimein(p);	/* first absolute date */
	/* rechange NULL at the end of the first date to a "'" */
	*p1 = '\'';
	p = ++p1;
	/* skip blanks up to "'", beginning of second date*/
	while (c = *p) {
		if (IsSpace(c))
			p++;
		else if (c != '\'')
			return (0);	/* syntax error */
		else
			break;
	}	
	p++;
	/* search for the end of the second date and change it to a NULL*/
	p1 = p;
	while (c = *p1) {
		if ( c == '\'') {
			*p1 = NULL;
			break;
		}
		p1++;
	}
	/* get the second date */
	*i_end = nabstimein(p);		/* second absolute date */
	/* rechange NULL at the end of the first date to a ''' */
	*p1 = '\'';
	p = ++p1;
	/* skip blanks up to ']'*/
	while (c = *p) {
		if ( IsSpace(c))
			p++;
		else if (c != ']')
			return(0); /*syntax error */
		else
			break;
	}
	p++;
	c = *p;
	if ( ! IsNull(c))
		return (0);	/* syntax error */
	/* it seems to be a valid interval */
	return(1);	
}
