/*-------------------------------------------------------------------------
 *
 * datetimes.c--
 *    implements DATE and TIME data types specified in SQL-92 standard
 *
 * Copyright (c) 1994-5, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    /usr/local/devel/pglite/cvs/src/backend/utils/adt/datetimes.c,v 1.1 1995/06/23 03:20:57 andrew Exp
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>		/* for sprintf() */
#include <string.h>
#include "postgres.h"
#include "utils/palloc.h"
#include "utils/elog.h"

typedef struct DateADT {
    char	day;
    char	month;
    short	year;
} DateADT;

typedef struct TimeADT {
    short	hr;
    short	min;
    float	sec;
} TimeADT;

#define AMERICAN_STYLE

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 int
isleap(int year)
{
    return
	(((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0);
}

/*****************************************************************************
 *   Date ADT
 *****************************************************************************/

int4
date_in(char *datestr)
{
    int d, m, y;
    int4 result;
    DateADT *date = (DateADT*)&result;

#ifdef AMERICAN_STYLE
    if (sscanf(datestr, "%d%*c%d%*c%d", &m, &d, &y) != 3) {
	elog(WARN, "date_in: date \"%s\" not of the form mm-dd-yy",
	     datestr);
    }
#else
    if (sscanf(datestr, "%d%*c%d%*c%d", &d, &m, &y) != 3) {
	elog(WARN, "date_in: date \"%s\" not of the form dd-mm-yy",
	     datestr);
    }
#endif
    if (m < 1 || m > 12)
	elog(WARN, "date_in: month must be limited to values 1 through 12 in \"%s\"", datestr);
    if (d < 1 || d > day_tab[isleap(y)][m-1])
	elog(WARN, "date_in: day must be limited to values 1 through %d in \"%s\"",
	     day_tab[isleap(y)][m-1], datestr);
    date->day = d;
    date->month = m;
    date->year = y;
    return result;
}

char *
date_out(DateADT date)
{
    char *datestr = palloc(11);
    
#ifdef AMERICAN_STYLE
    sprintf(datestr, "%02d-%02d-%04d",
	    (int)date.month, (int)date.day, (int)date.year);
#else
    sprintf(datestr, "%02d-%02d-%04d",
	    (int)date.day, (int)date.month, (int)date.year);
#endif

    return datestr;
}


int
date_eq(DateADT date1, DateADT date2)
{
    return (date1.day==date2.day && date1.month==date2.month &&
	    date1.year==date2.year);
}

int
date_ne(DateADT date1, DateADT date2)
{
    return (date1.day!=date2.day || date1.month!=date2.month ||
	    date1.year!=date2.year);
}

int
date_lt(DateADT date1, DateADT date2)
{
    if (date1.year!=date2.year)
	return (date1.year<date2.year);
    if (date1.month!=date2.month)
	return (date1.month<date2.month);
    return (date1.day<date2.day);
}

int
date_le(DateADT date1, DateADT date2)
{
    if (date1.year!=date2.year)
	return (date1.year<=date2.year);
    if (date1.month!=date2.month)
	return (date1.month<=date2.month);
    return (date1.day<=date2.day);
}

int
date_gt(DateADT date1, DateADT date2)
{
    if (date1.year!=date2.year)
	return (date1.year>date2.year);
    if (date1.month!=date2.month)
	return (date1.month>date2.month);
    return (date1.day>date2.day);
}

int
date_ge(DateADT date1, DateADT date2)
{
    if (date1.year!=date2.year)
	return (date1.year>=date2.year);
    if (date1.month!=date2.month)
	return (date1.month>=date2.month);
    return (date1.day>=date2.day);
}

int
date_cmp(DateADT date1, DateADT date2)
{
    if (date1.year!=date2.year)
	return ((date1.year<date2.year) ? -1 : 1);
    if (date1.month!=date2.month)
	return ((date1.month<date2.month) ? -1 : 1);
    if (date1.day!=date2.day)
	return ((date1.day<date2.day) ? -1 : 1);
    return 0;
}

/*****************************************************************************
 *   Time ADT
 *****************************************************************************/

char *
time_in(char *timestr)
{
    int h, m;
    float sec;
    TimeADT *time;

    if (sscanf(timestr, "%d%*c%d%*c%f", &h, &m, &sec) != 3) {
	elog(WARN, "time_in: time \"%s\" not of the form hh:mm:ss",
	     timestr);
    }

    if (h < 0 || h > 23)
	elog(WARN, "time_in: hour must be limited to values 0 through 23 in \"%s\"", timestr);
    if (m < 0 || m > 59)
	elog(WARN, "time_in: minute must be limited to values 0 through 59 in \"%s\"", timestr);
    if (sec < 0 || sec >= 62.0)
	elog(WARN, "time_in: second must be limited to values 0 through 61.99 in \"%s\"", timestr);

    time = (TimeADT*)palloc(sizeof(TimeADT));
    time->hr = h;
    time->min = m;
    time->sec = sec;
    return (char*)time;
}

char *
time_out(TimeADT *time)
{
    char *timestr = palloc(16);
    
    sprintf(timestr, "%02d:%02d:%02.6f",
	    (int)time->hr, (int)time->min, time->sec);

    return timestr;
}


int
time_eq(TimeADT *time1, TimeADT *time2)
{
    return (time1->sec==time2->sec && time1->min==time2->min &&
	    time1->hr==time2->hr);
}

int
time_ne(TimeADT *time1, TimeADT *time2)
{
    return (time1->sec!=time2->sec || time1->min!=time2->min ||
	    time1->hr!=time2->hr);
}

int
time_lt(TimeADT *time1, TimeADT *time2)
{
    if (time1->hr!=time2->hr)
	return (time1->hr<time2->hr);
    if (time1->min!=time2->min)
	return (time1->min<time2->min);
    return (time1->sec<time2->sec);
}

int
time_le(TimeADT *time1, TimeADT *time2)
{
    if (time1->hr!=time2->hr)
	return (time1->hr<=time2->hr);
    if (time1->min!=time2->min)
	return (time1->min<=time2->min);
    return (time1->sec<=time2->sec);
}

int
time_gt(TimeADT *time1, TimeADT *time2)
{
    if (time1->hr!=time2->hr)
	return (time1->hr>time2->hr);
    if (time1->min!=time2->min)
	return (time1->min>time2->min);
    return (time1->sec>time2->sec);
}

int
time_ge(TimeADT *time1, TimeADT *time2)
{
    if (time1->hr!=time2->hr)
	return (time1->hr>=time2->hr);
    if (time1->min!=time2->min)
	return (time1->min>=time2->min);
    return (time1->sec>=time2->sec);
}

int
time_cmp(TimeADT *time1, TimeADT *time2)
{
    if (time1->hr!=time2->hr)
	return ((time1->hr<time2->hr) ? -1 : 1);
    if (time1->min!=time2->min)
	return ((time1->min<time2->min) ? -1 : 1);
    if (time1->sec!=time2->sec)
	return ((time1->sec<time2->sec) ? -1 : 1);
    return 0;
}
