Commit bf566b20 authored by Thomas G. Lockhart's avatar Thomas G. Lockhart

All regression tests pass except for rules.sql (unrelated).

Implement "date/time grand unification".
 Transform datetime and timespan into timestamp and interval.
 Deprecate datetime and timespan, though translate to new types in gram.y.
 Transform all datetime and timespan catalog entries into new types.
 Make "INTERVAL" reserved word allowed as a column identifier in gram.y.
 Remove dt.h, dt.c files, and retarget datetime.h, datetime.c as utility
  routines for all date/time types.
 date.{h,c} now deals with date, time types.
 timestamp.{h,c} now deals with timestamp, interval types.
 nabstime.{h,c} now deals with abstime, reltime, tinterval types.
Make NUMERIC a known native type for purposes of type coersion. Not tested.
parent 8997675c
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* date.c * date.c
* Utilities for the built-in type "AbsoluteTime" (defined in nabstime). * implements DATE and TIME data types specified in SQL-92 standard
* Functions for the built-in type "RelativeTime".
* Functions for the built-in type "TimeInterval".
* *
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.41 2000/01/26 05:57:13 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.42 2000/02/16 18:17:02 thomas Exp $
*
* 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.
*
* XXX This code has been cleaned up some - avi 07/07/93
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include <ctype.h> #include <limits.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include "postgres.h" #include "postgres.h"
#ifdef HAVE_FLOAT_H #ifdef HAVE_FLOAT_H
#include <float.h> #include <float.h>
#endif #endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#ifndef MAXINT
#define MAXINT INT_MAX
#endif
#else
#ifdef HAVE_VALUES_H
#include <values.h>
#endif
#endif
#include "access/xact.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/dt.h"
#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)
/*
* Unix epoch is Jan 1 00:00:00 1970. Postgres knows about times
* sixty-eight years on either side of that.
*/
#define IsSpace(C) ((C) == ' ')
#define T_INTERVAL_INVAL 0 /* data represents no valid interval */
#define T_INTERVAL_VALID 1 /* data represents a valid interval */
/*
* ['Mon May 10 23:59:12 1943 PST' 'Sun Jan 14 03:14:21 1973 PST']
* 0 1 2 3 4 5 6
* 1234567890123456789012345678901234567890123456789012345678901234
*
* we allocate some extra -- timezones are usually 3 characters but
* this is not in the POSIX standard...
*/
#define T_INTERVAL_LEN 80
#define INVALID_INTERVAL_STR "Undefined Range"
#define INVALID_INTERVAL_STR_LEN (sizeof(INVALID_INTERVAL_STR)-1)
#define ABSTIMEMIN(t1, t2) abstimele((t1),(t2)) ? (t1) : (t2)
#define ABSTIMEMAX(t1, t2) abstimelt((t1),(t2)) ? (t2) : (t1)
#ifdef NOT_USED
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 */
#define NUNITS 14 /* number of different units */
/* 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};
#endif
/*
* Function prototypes -- internal to this file only
*/
static void reltime2tm(RelativeTime time, struct tm * tm);
#ifdef NOT_USED static int
static int correct_unit(char *unit, int *unptr); date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn);
static int correct_dir(char *direction, int *signptr);
#endif
static int istinterval(char *i_string,
AbsoluteTime *i_start,
AbsoluteTime *i_end);
/***************************************************************************** /*****************************************************************************
* USER I/O ROUTINES * * Date ADT
*****************************************************************************/ *****************************************************************************/
/*
* reltimein - converts a reltime string in an internal format /* date_in()
* Given date text string, convert to internal date format.
*/ */
RelativeTime DateADT
reltimein(char *str) date_in(char *str)
{ {
RelativeTime result; DateADT date;
double fsec;
struct tm tt, struct tm tt,
*tm = &tt; *tm = &tt;
double fsec; int tzp;
int dtype; int dtype;
int nf;
char *field[MAXDATEFIELDS]; char *field[MAXDATEFIELDS];
int nf, int ftype[MAXDATEFIELDS];
ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1]; char lowstr[MAXDATELEN + 1];
if (!PointerIsValid(str)) if (!PointerIsValid(str))
elog(ERROR, "Bad (null) date external representation"); elog(ERROR, "Bad (null) date external representation");
if (strlen(str) > MAXDATELEN)
elog(ERROR, "Bad (length) reltime external representation '%s'", str);
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
elog(ERROR, "Bad reltime external representation '%s'", str); elog(ERROR, "Bad date external representation '%s'", str);
switch (dtype) switch (dtype)
{ {
case DTK_DELTA: case DTK_DATE:
result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec); break;
result += (((tm->tm_year * 365) + (tm->tm_mon * 30) + tm->tm_mday) * (24 * 60 * 60));
return result; case DTK_CURRENT:
GetCurrentTime(tm);
break;
case DTK_EPOCH:
tm->tm_year = 1970;
tm->tm_mon = 1;
tm->tm_mday = 1;
break;
default: default:
return INVALID_RELTIME; elog(ERROR, "Unrecognized date external representation '%s'", str);
} }
elog(ERROR, "Bad reltime (internal coding error) '%s'", str); date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
return INVALID_RELTIME;
} /* reltimein() */
return date;
} /* date_in() */
/* /* date_out()
* reltimeout - converts the internal format to a reltime string * Given internal format date, convert to text string.
*/ */
char * char *
reltimeout(RelativeTime time) date_out(DateADT date)
{ {
char *result; char *result;
struct tm tt, struct tm tt,
*tm = &tt; *tm = &tt;
char buf[MAXDATELEN + 1]; char buf[MAXDATELEN + 1];
if (time == INVALID_RELTIME) j2date((date + date2j(2000, 1, 1)),
{ &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
strcpy(buf, INVALID_RELTIME_STR);
} EncodeDateOnly(tm, DateStyle, buf);
else
{
reltime2tm(time, tm);
EncodeTimeSpan(tm, 0, DateStyle, buf);
}
result = palloc(strlen(buf) + 1); result = palloc(strlen(buf) + 1);
strcpy(result, buf); strcpy(result, buf);
return result; return result;
} /* reltimeout() */ } /* date_out() */
bool
date_eq(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 == dateVal2;
}
#define TMODULO(t,q,u) \ bool
do { \ date_ne(DateADT dateVal1, DateADT dateVal2)
q = (t / u); \ {
if (q != 0) t -= (q * u); \ return dateVal1 != dateVal2;
} while(0) }
static void bool
reltime2tm(RelativeTime time, struct tm * tm) date_lt(DateADT dateVal1, DateADT dateVal2)
{ {
TMODULO(time, tm->tm_year, 31536000); return dateVal1 < dateVal2;
TMODULO(time, tm->tm_mon, 2592000); } /* date_lt() */
TMODULO(time, tm->tm_mday, 86400);
TMODULO(time, tm->tm_hour, 3600);
TMODULO(time, tm->tm_min, 60);
TMODULO(time, tm->tm_sec, 1);
return; bool
} /* reltime2tm() */ date_le(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 <= dateVal2;
} /* date_le() */
bool
date_gt(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 > dateVal2;
} /* date_gt() */
bool
date_ge(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 >= dateVal2;
} /* date_ge() */
#ifdef NOT_USED
int int
dummyfunc() date_cmp(DateADT dateVal1, DateADT dateVal2)
{ {
char *timestring; if (dateVal1 < dateVal2)
long quantity; return -1;
int i; else if (dateVal1 > dateVal2)
int unitnr; return 1;
return 0;
timestring = (char *) palloc(Max(strlen(INVALID_RELTIME_STR), } /* date_cmp() */
UNITMAXLEN) + 1);
if (timevalue == INVALID_RELTIME)
{
strcpy(timestring, INVALID_RELTIME_STR);
return timestring;
}
if (timevalue == 0) DateADT
i = 1; /* unit = 'seconds' */ date_larger(DateADT dateVal1, DateADT dateVal2)
else {
for (i = 12; i >= 0; i = i - 2) return date_gt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
if ((timevalue % sec_tab[i]) == 0) } /* date_larger() */
break; /* appropriate unit found */
unitnr = i;
quantity = (timevalue / sec_tab[unitnr]);
if (quantity > 1 || quantity < -1)
unitnr++; /* adjust index for PLURAL of unit */
if (quantity >= 0)
sprintf(timestring, "%c %lu %s", RELTIME_LABEL,
quantity, unit_tab[unitnr]);
else
sprintf(timestring, "%c %lu %s %s", RELTIME_LABEL,
(quantity * -1), unit_tab[unitnr], RELTIME_PAST);
return timestring;
}
#endif DateADT
date_smaller(DateADT dateVal1, DateADT dateVal2)
{
return date_lt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
} /* date_smaller() */
/* Compute difference between two dates in days.
*/
int4
date_mi(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 - dateVal2;
} /* date_mi() */
/* /* Add a number of days to a date, giving a new date.
* tintervalin - converts an interval string to an internal format * Must handle both positive and negative numbers of days.
*/ */
TimeInterval DateADT
tintervalin(char *intervalstr) date_pli(DateADT dateVal, int4 days)
{ {
int error; return dateVal + days;
AbsoluteTime i_start, } /* date_pli() */
i_end,
t1,
t2;
TimeInterval interval;
interval = (TimeInterval) palloc(sizeof(TimeIntervalData));
error = istinterval(intervalstr, &t1, &t2);
if (error == 0)
interval->status = T_INTERVAL_INVAL;
if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
interval->status = T_INTERVAL_INVAL; /* undefined */
else
{
i_start = ABSTIMEMIN(t1, t2);
i_end = ABSTIMEMAX(t1, t2);
interval->data[0] = i_start;
interval->data[1] = i_end;
interval->status = T_INTERVAL_VALID;
}
return interval;
}
/* Subtract a number of days from a date, giving a new date.
*/
DateADT
date_mii(DateADT dateVal, int4 days)
{
return date_pli(dateVal, -days);
} /* date_mii() */
/*
* tintervalout - converts an internal interval format to a string /* date_timestamp()
* * Convert date to timestamp data type.
*/ */
char * Timestamp *
tintervalout(TimeInterval interval) date_timestamp(DateADT dateVal)
{ {
char *i_str, Timestamp *result;
*p; struct tm tt,
*tm = &tt;
int tz;
double fsec = 0;
char *tzn;
i_str = (char *) palloc(T_INTERVAL_LEN); /* ['...' '...'] */ result = palloc(sizeof(Timestamp));
strcpy(i_str, "[\"");
if (interval->status == T_INTERVAL_INVAL)
strcat(i_str, INVALID_INTERVAL_STR);
else
{
p = nabstimeout(interval->data[0]);
strcat(i_str, p);
pfree(p);
strcat(i_str, "\" \"");
p = nabstimeout(interval->data[1]);
strcat(i_str, p);
pfree(p);
}
strcat(i_str, "\"]\0");
return i_str;
}
if (date2tm(dateVal, &tz, tm, &fsec, &tzn) != 0)
elog(ERROR, "Unable to convert date to timestamp");
/***************************************************************************** if (tm2timestamp(tm, fsec, &tz, result) != 0)
* PUBLIC ROUTINES * elog(ERROR, "Timestamp out of range");
*****************************************************************************/
return result;
} /* date_timestamp() */
RelativeTime
timespan_reltime(TimeSpan *timespan) /* timestamp_date()
* Convert timestamp to date data type.
*/
DateADT
timestamp_date(Timestamp *timestamp)
{ {
RelativeTime time; DateADT result;
int year, struct tm tt,
month; *tm = &tt;
double span; int tz;
double fsec;
char *tzn;
if (!PointerIsValid(timespan)) if (!PointerIsValid(timestamp))
time = INVALID_RELTIME; elog(ERROR, "Unable to convert null timestamp to date");
if (TIMESPAN_IS_INVALID(*timespan)) if (TIMESTAMP_NOT_FINITE(*timestamp))
{ elog(ERROR, "Unable to convert timestamp to date");
time = INVALID_RELTIME;
} if (TIMESTAMP_IS_EPOCH(*timestamp))
else
{
if (timespan->month == 0)
{ {
year = 0; timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL);
month = 0;
} }
else if (abs(timespan->month) >= 12) else if (TIMESTAMP_IS_CURRENT(*timestamp))
{ {
year = (timespan->month / 12); timestamp2tm(SetTimestamp(*timestamp), &tz, tm, &fsec, &tzn);
month = (timespan->month % 12);
} }
else else
{ {
year = 0; if (timestamp2tm(*timestamp, &tz, tm, &fsec, &tzn) != 0)
month = timespan->month; elog(ERROR, "Unable to convert timestamp to date");
} }
span = (((((double) 365 * year) + ((double) 30 * month)) * 86400) + timespan->time); result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
time = (((span > INT_MIN) && (span < INT_MAX)) ? span : INVALID_RELTIME);
}
return time;
} /* timespan_reltime() */
TimeSpan *
reltime_timespan(RelativeTime reltime)
{
TimeSpan *result;
int year,
month;
if (!PointerIsValid(result = palloc(sizeof(TimeSpan))))
elog(ERROR, "Memory allocation failed, can't convert reltime to timespan");
switch (reltime)
{
case INVALID_RELTIME:
TIMESPAN_INVALID(*result);
break;
default:
TMODULO(reltime, year, 31536000);
TMODULO(reltime, month, 2592000);
result->time = reltime;
result->month = ((12 * year) + month);
}
return result; return result;
} /* reltime_timespan() */ } /* timestamp_date() */
/* /* abstime_date()
* mktinterval - creates a time interval with endpoints t1 and t2 * Convert abstime to date data type.
*/ */
TimeInterval DateADT
mktinterval(AbsoluteTime t1, AbsoluteTime t2) abstime_date(AbsoluteTime abstime)
{ {
AbsoluteTime tstart = ABSTIMEMIN(t1, t2), DateADT result;
tend = ABSTIMEMAX(t1, t2); struct tm tt,
TimeInterval interval; *tm = &tt;
int tz;
interval = (TimeInterval) palloc(sizeof(TimeIntervalData)); switch (abstime)
if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
interval->status = T_INTERVAL_INVAL;
else
{ {
interval->status = T_INTERVAL_VALID; case INVALID_ABSTIME:
interval->data[0] = tstart; case NOSTART_ABSTIME:
interval->data[1] = tend; case NOEND_ABSTIME:
} elog(ERROR, "Unable to convert reserved abstime value to date");
return interval; /*
} * pretend to drop through to make compiler think that result
* will be set
/*
* timepl, timemi and abstimemi use the formula
* abstime + reltime = abstime
* so abstime - reltime = abstime
* and abstime - abstime = reltime
*/
/*
* timepl - returns the value of (abstime t1 + relime t2)
*/ */
AbsoluteTime
timepl(AbsoluteTime t1, RelativeTime t2)
{
if (t1 == CURRENT_ABSTIME)
t1 = GetCurrentTransactionStartTime();
if (AbsoluteTimeIsReal(t1) &&
RelativeTimeIsValid(t2) &&
((t2 > 0) ? (t1 < NOEND_ABSTIME - t2)
: (t1 > NOSTART_ABSTIME - t2))) /* prevent overflow */
return t1 + t2;
return INVALID_ABSTIME;
}
case EPOCH_ABSTIME:
result = date2j(1970, 1, 1) - date2j(2000, 1, 1);
break;
/* case CURRENT_ABSTIME:
* timemi - returns the value of (abstime t1 - reltime t2) GetCurrentTime(tm);
*/ result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
AbsoluteTime break;
timemi(AbsoluteTime t1, RelativeTime t2)
{
if (t1 == CURRENT_ABSTIME)
t1 = GetCurrentTransactionStartTime();
if (AbsoluteTimeIsReal(t1) && default:
RelativeTimeIsValid(t2) && abstime2tm(abstime, &tz, tm, NULL);
((t2 > 0) ? (t1 > NOSTART_ABSTIME + t2) result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
: (t1 < NOEND_ABSTIME + t2))) /* prevent overflow */ break;
return t1 - t2; }
return INVALID_ABSTIME; return result;
} } /* abstime_date() */
/* /* date2tm()
* abstimemi - returns the value of (abstime t1 - abstime t2) * Convert date to time structure.
* Note that date is an implicit local time, but the system calls assume
* that everything is GMT. So, convert to GMT, rotate to local time,
* and then convert again to try to get the time zones correct.
*/ */
static RelativeTime static int
abstimemi(AbsoluteTime t1, AbsoluteTime t2) date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn)
{ {
if (t1 == CURRENT_ABSTIME) struct tm *tx;
t1 = GetCurrentTransactionStartTime(); time_t utime;
if (t2 == CURRENT_ABSTIME)
t2 = GetCurrentTransactionStartTime();
if (AbsoluteTimeIsReal(t1) && *fsec = 0;
AbsoluteTimeIsReal(t2))
return t1 - t2;
return INVALID_RELTIME; j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
} tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
tm->tm_isdst = -1;
if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
{
/* /* convert to system time */
* ininterval - returns 1, iff absolute date is in the interval utime = ((dateVal + (date2j(2000, 1, 1) - date2j(1970, 1, 1))) * 86400);
*/ /* rotate to noon to get the right day in time zone */
int utime += (12 * 60 * 60);
ininterval(AbsoluteTime t, TimeInterval interval)
{
if (interval->status == T_INTERVAL_VALID && t != INVALID_ABSTIME)
return (abstimege(t, interval->data[0]) &&
abstimele(t, interval->data[1]));
return 0;
}
/*
* intervalrel - returns relative time corresponding to interval
*/
RelativeTime
intervalrel(TimeInterval interval)
{
if (interval->status == T_INTERVAL_VALID)
return abstimemi(interval->data[1], interval->data[0]);
else
return INVALID_RELTIME;
}
/* #ifdef USE_POSIX_TIME
* timenow - returns time "now", internal format tx = localtime(&utime);
*
* Now AbsoluteTime is time since Jan 1 1970 -mer 7 Feb 1992
*/
AbsoluteTime
timenow()
{
time_t sec;
if (time(&sec) < 0) tm->tm_year = tx->tm_year + 1900;
return INVALID_ABSTIME; tm->tm_mon = tx->tm_mon + 1;
return (AbsoluteTime) sec; tm->tm_mday = tx->tm_mday;
} tm->tm_isdst = tx->tm_isdst;
/* #if defined(HAVE_TM_ZONE)
* reltimeeq - returns 1, iff arguments are equal tm->tm_gmtoff = tx->tm_gmtoff;
* reltimene - returns 1, iff arguments are not equal tm->tm_zone = tx->tm_zone;
* 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
*/
bool
reltimeeq(RelativeTime t1, RelativeTime t2)
{
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
return 0;
return t1 == t2;
}
bool /* tm_gmtoff is Sun/DEC-ism */
reltimene(RelativeTime t1, RelativeTime t2) *tzp = -(tm->tm_gmtoff);
{ if (tzn != NULL)
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) *tzn = (char *) tm->tm_zone;
return 0; #elif defined(HAVE_INT_TIMEZONE)
return t1 != t2; #ifdef __CYGWIN__
} *tzp = (tm->tm_isdst ? (_timezone - 3600) : _timezone);
#else
*tzp = (tm->tm_isdst ? (timezone - 3600) : timezone);
#endif
if (tzn != NULL)
*tzn = tzname[(tm->tm_isdst > 0)];
#else
#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined
#endif
#else /* !USE_POSIX_TIME */
*tzp = CTimeZone; /* V7 conventions; don't know timezone? */
if (tzn != NULL)
*tzn = CTZName;
#endif
bool /* otherwise, outside of timezone range so convert to GMT... */
reltimelt(RelativeTime t1, RelativeTime t2) }
{ else
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) {
return 0; *tzp = 0;
return t1 < t2; tm->tm_isdst = 0;
} if (tzn != NULL)
*tzn = NULL;
}
bool
reltimegt(RelativeTime t1, RelativeTime t2)
{
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
return 0; return 0;
return t1 > t2; } /* date2tm() */
}
bool
reltimele(RelativeTime t1, RelativeTime t2)
{
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
return 0;
return t1 <= t2;
}
bool /*****************************************************************************
reltimege(RelativeTime t1, RelativeTime t2) * Time ADT
{ *****************************************************************************/
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
return 0;
return t1 >= t2;
}
/* TimeADT *
* intervalsame - returns 1, iff interval i1 is same as interval i2 time_in(char *str)
* Check begin and end time.
*/
bool
intervalsame(TimeInterval i1, TimeInterval i2)
{ {
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) TimeADT *time;
return FALSE; /* invalid interval */
return (abstimeeq(i1->data[0], i2->data[0]) &&
abstimeeq(i1->data[1], i2->data[1]));
} /* intervalsame() */
/* double fsec;
* intervaleq - returns 1, iff interval i1 is equal to interval i2 struct tm tt,
* Check length of intervals. *tm = &tt;
*/
bool
intervaleq(TimeInterval i1, TimeInterval i2)
{
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) int nf;
return FALSE; /* invalid interval */ char lowstr[MAXDATELEN + 1];
char *field[MAXDATEFIELDS];
int dtype;
int ftype[MAXDATEFIELDS];
t10 = i1->data[0]; if (!PointerIsValid(str))
t11 = i1->data[1]; elog(ERROR, "Bad (null) time external representation");
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) || (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec) != 0))
return FALSE; elog(ERROR, "Bad time external representation '%s'", str);
if (t10 == CURRENT_ABSTIME) time = palloc(sizeof(TimeADT));
t10 = GetCurrentTransactionStartTime();
if (t11 == CURRENT_ABSTIME)
t11 = GetCurrentTransactionStartTime();
if (t20 == CURRENT_ABSTIME)
t20 = GetCurrentTransactionStartTime();
if (t21 == CURRENT_ABSTIME)
t21 = GetCurrentTransactionStartTime();
return (t11 - t10) == (t21 - t20);
} /* intervaleq() */
/*
* intervalne - returns 1, iff interval i1 is not equal to interval i2
* Check length of intervals.
*/
bool
intervalne(TimeInterval i1, TimeInterval i2)
{
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) *time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
return FALSE; /* invalid interval */
t10 = i1->data[0]; return time;
t11 = i1->data[1]; } /* time_in() */
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
return FALSE;
if (t10 == CURRENT_ABSTIME) char *
t10 = GetCurrentTransactionStartTime(); time_out(TimeADT *time)
if (t11 == CURRENT_ABSTIME)
t11 = GetCurrentTransactionStartTime();
if (t20 == CURRENT_ABSTIME)
t20 = GetCurrentTransactionStartTime();
if (t21 == CURRENT_ABSTIME)
t21 = GetCurrentTransactionStartTime();
return (t11 - t10) != (t21 - t20);
} /* intervalne() */
/*
* intervallt - returns TRUE, iff interval i1 is less than interval i2
* Check length of intervals.
*/
bool
intervallt(TimeInterval i1, TimeInterval i2)
{ {
AbsoluteTime t10, char *result;
t11, struct tm tt,
t20, *tm = &tt;
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
return FALSE; /* invalid interval */
t10 = i1->data[0];
t11 = i1->data[1];
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) double fsec;
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) char buf[MAXDATELEN + 1];
return FALSE;
if (t10 == CURRENT_ABSTIME) if (!PointerIsValid(time))
t10 = GetCurrentTransactionStartTime(); return NULL;
if (t11 == CURRENT_ABSTIME)
t11 = GetCurrentTransactionStartTime();
if (t20 == CURRENT_ABSTIME)
t20 = GetCurrentTransactionStartTime();
if (t21 == CURRENT_ABSTIME)
t21 = GetCurrentTransactionStartTime();
return (t11 - t10) < (t21 - t20);
} /* intervallt() */
/*
* intervalle - returns TRUE, iff interval i1 is less than or equal to interval i2
* Check length of intervals.
*/
bool
intervalle(TimeInterval i1, TimeInterval i2)
{
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) tm->tm_hour = (*time / (60 * 60));
return FALSE; /* invalid interval */ tm->tm_min = (((int) (*time / 60)) % 60);
tm->tm_sec = (((int) *time) % 60);
t10 = i1->data[0]; fsec = 0;
t11 = i1->data[1];
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) EncodeTimeOnly(tm, fsec, DateStyle, buf);
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
return FALSE;
if (t10 == CURRENT_ABSTIME) result = palloc(strlen(buf) + 1);
t10 = GetCurrentTransactionStartTime();
if (t11 == CURRENT_ABSTIME)
t11 = GetCurrentTransactionStartTime();
if (t20 == CURRENT_ABSTIME)
t20 = GetCurrentTransactionStartTime();
if (t21 == CURRENT_ABSTIME)
t21 = GetCurrentTransactionStartTime();
return (t11 - t10) <= (t21 - t20);
} /* intervalle() */
/*
* intervalgt - returns TRUE, iff interval i1 is less than interval i2
* Check length of intervals.
*/
bool
intervalgt(TimeInterval i1, TimeInterval i2)
{
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) strcpy(result, buf);
return FALSE; /* invalid interval */
t10 = i1->data[0]; return result;
t11 = i1->data[1]; } /* time_out() */
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
return FALSE;
if (t10 == CURRENT_ABSTIME)
t10 = GetCurrentTransactionStartTime();
if (t11 == CURRENT_ABSTIME)
t11 = GetCurrentTransactionStartTime();
if (t20 == CURRENT_ABSTIME)
t20 = GetCurrentTransactionStartTime();
if (t21 == CURRENT_ABSTIME)
t21 = GetCurrentTransactionStartTime();
return (t11 - t10) > (t21 - t20);
} /* intervalgt() */
/*
* intervalge - returns TRUE, iff interval i1 is less than or equal to interval i2
* Check length of intervals.
*/
bool bool
intervalge(TimeInterval i1, TimeInterval i2) time_eq(TimeADT *time1, TimeADT *time2)
{ {
AbsoluteTime t10, if (!PointerIsValid(time1) || !PointerIsValid(time2))
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
return FALSE; /* invalid interval */
t10 = i1->data[0];
t11 = i1->data[1];
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
return FALSE; return FALSE;
if (t10 == CURRENT_ABSTIME) return *time1 == *time2;
t10 = GetCurrentTransactionStartTime(); } /* time_eq() */
if (t11 == CURRENT_ABSTIME)
t11 = GetCurrentTransactionStartTime();
if (t20 == CURRENT_ABSTIME)
t20 = GetCurrentTransactionStartTime();
if (t21 == CURRENT_ABSTIME)
t21 = GetCurrentTransactionStartTime();
return (t11 - t10) >= (t21 - t20);
} /* intervalge() */
/*
* intervalleneq - returns 1, iff length of interval i is equal to
* reltime t
*/
bool
intervalleneq(TimeInterval i, RelativeTime t)
{
RelativeTime rt;
if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
return 0;
rt = intervalrel(i);
return rt != INVALID_RELTIME && rt == t;
}
/*
* intervallenne - returns 1, iff length of interval i is not equal
* to reltime t
*/
bool
intervallenne(TimeInterval i, RelativeTime t)
{
RelativeTime rt;
if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
return 0;
rt = intervalrel(i);
return rt != INVALID_RELTIME && rt != t;
}
/*
* intervallenlt - returns 1, iff length of interval i is less than
* reltime t
*/
bool bool
intervallenlt(TimeInterval i, RelativeTime t) time_ne(TimeADT *time1, TimeADT *time2)
{ {
RelativeTime rt; if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) return *time1 != *time2;
return 0; } /* time_eq() */
rt = intervalrel(i);
return rt != INVALID_RELTIME && rt < t;
}
/*
* intervallengt - returns 1, iff length of interval i is greater than
* reltime t
*/
bool bool
intervallengt(TimeInterval i, RelativeTime t) time_lt(TimeADT *time1, TimeADT *time2)
{ {
RelativeTime rt; if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) return *time1 < *time2;
return 0; } /* time_eq() */
rt = intervalrel(i);
return rt != INVALID_RELTIME && rt > t;
}
/*
* intervallenle - returns 1, iff length of interval i is less or equal
* than reltime t
*/
bool bool
intervallenle(TimeInterval i, RelativeTime t) time_le(TimeADT *time1, TimeADT *time2)
{ {
RelativeTime rt; if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) return *time1 <= *time2;
return 0; } /* time_eq() */
rt = intervalrel(i);
return rt != INVALID_RELTIME && rt <= t;
}
/*
* intervallenge - returns 1, iff length of interval i is greater or
* equal than reltime t
*/
bool bool
intervallenge(TimeInterval i, RelativeTime t) time_gt(TimeADT *time1, TimeADT *time2)
{ {
RelativeTime rt; if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
return 0;
rt = intervalrel(i);
return rt != INVALID_RELTIME && rt >= t;
}
/* return *time1 > *time2;
* intervalct - returns 1, iff interval i1 contains interval i2 } /* time_eq() */
*/
bool
intervalct(TimeInterval i1, TimeInterval i2)
{
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
return 0;
return (abstimele(i1->data[0], i2->data[0]) &&
abstimege(i1->data[1], i2->data[1]));
}
/*
* intervalov - returns 1, iff interval i1 (partially) overlaps i2
*/
bool bool
intervalov(TimeInterval i1, TimeInterval i2) time_ge(TimeADT *time1, TimeADT *time2)
{ {
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) if (!PointerIsValid(time1) || !PointerIsValid(time2))
return 0; return FALSE;
return (!(abstimelt(i1->data[1], i2->data[0]) ||
abstimegt(i1->data[0], i2->data[1])));
}
/* return *time1 >= *time2;
* intervalstart - returns the start of interval i } /* time_eq() */
*/
AbsoluteTime
intervalstart(TimeInterval i)
{
if (i->status == T_INTERVAL_INVAL)
return INVALID_ABSTIME;
return i->data[0];
}
/* int
* intervalend - returns the end of interval i time_cmp(TimeADT *time1, TimeADT *time2)
*/
AbsoluteTime
intervalend(TimeInterval i)
{ {
if (i->status == T_INTERVAL_INVAL) return (*time1 < *time2) ? -1 : (((*time1 > *time2) ? 1 : 0));
return INVALID_ABSTIME; } /* time_cmp() */
return i->data[1];
}
/***************************************************************************** /* timestamp_time()
* PRIVATE ROUTINES * * Convert timestamp to time data type.
*****************************************************************************/
#ifdef NOT_USED
/*
* isreltime - returns 1, iff datestring is of type reltime
* 2, iff datestring is 'invalid time' identifier
* 0, iff datestring contains a syntax error
* VALID time less or equal +/- `@ 68 years'
*
*/ */
int TimeADT *
isreltime(char *str) timestamp_time(Timestamp *timestamp)
{ {
TimeADT *result;
struct tm tt, struct tm tt,
*tm = &tt; *tm = &tt;
int tz;
double fsec; double fsec;
int dtype; char *tzn;
char *field[MAXDATEFIELDS];
int nf,
ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
if (!PointerIsValid(str))
return 0;
if (strlen(str) > MAXDATELEN) if (!PointerIsValid(timestamp))
return 0; elog(ERROR, "Unable to convert null timestamp to date");
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) if (TIMESTAMP_NOT_FINITE(*timestamp))
|| (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) elog(ERROR, "Unable to convert timestamp to date");
return 0;
switch (dtype) if (TIMESTAMP_IS_EPOCH(*timestamp))
{ {
case (DTK_DELTA): timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL);
return (abs(tm->tm_year) <= 68) ? 1 : 0;
break;
case (DTK_INVALID):
return 2;
break;
default:
return 0;
break;
}
return 0;
} /* isreltime() */
#endif
#ifdef NOT_USED
int
dummyfunc()
{
char *p;
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) != '\0')
{
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 else if (TIMESTAMP_IS_CURRENT(*timestamp))
{ {
if ((c == ' ' || c == '\0') timestamp2tm(SetTimestamp(*timestamp), &tz, tm, &fsec, &tzn);
&& 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 else
{ {
if ((c == ' ' || c == '\0') && i == 0) if (timestamp2tm(*timestamp, &tz, tm, &fsec, &tzn) != 0)
{ elog(ERROR, "Unable to convert timestamp to date");
*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; result = palloc(sizeof(TimeADT));
}
/*
* 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())
*/
static int
correct_unit(char *unit, int *unptr)
{
int j = 0;
while (j < NUNITS) *result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
{
if (strncmp(unit, unit_tab[j], strlen(unit_tab[j])) == 0)
{
*unptr = j;
return 1;
}
j++;
}
return 0; /* invalid unit descriptor */
}
/* return result;
* correct_dir - returns 1, iff direction is a correct identifier } /* timestamp_time() */
*
* output parameter:
* signptr: points to -1 if dir corresponds to past tense
* else to 1
*/
static int
correct_dir(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 */
}
#endif
/* /* datetime_timestamp()
* istinterval - returns 1, iff i_string is a valid interval descr. * Convert date and time to timestamp data type.
* 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']
*/ */
static int Timestamp *
istinterval(char *i_string, datetime_timestamp(DateADT date, TimeADT *time)
AbsoluteTime *i_start,
AbsoluteTime *i_end)
{ {
char *p, Timestamp *result;
*p1;
char c;
p = i_string; if (!PointerIsValid(time))
/* skip leading blanks up to '[' */
while ((c = *p) != '\0')
{
if (IsSpace(c))
p++;
else if (c != '[')
return 0; /* syntax error */
else
break;
}
p++;
/* skip leading blanks up to "'" */
while ((c = *p) != '\0')
{
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) != '\0')
{ {
if (c == '"') result = palloc(sizeof(Timestamp));
{ TIMESTAMP_INVALID(*result);
*p1 = '\0';
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) != '\0')
{
if (IsSpace(c))
p++;
else if (c != '"')
return 0; /* syntax error */
else else
break;
}
p++;
/* search for the end of the second date and change it to a NULL */
p1 = p;
while ((c = *p1) != '\0')
{
if (c == '"')
{ {
*p1 = '\0'; result = date_timestamp(date);
break; *result += *time;
}
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) != '\0')
{
if (IsSpace(c))
p++;
else if (c != ']')
return 0; /* syntax error */
else
break;
} }
p++;
c = *p;
if (c != '\0')
return 0; /* syntax error */
/* it seems to be a valid interval */
return 1;
}
/*****************************************************************************
*
*****************************************************************************/
/*
* timeofday -
* returns the current time as a text. similar to timenow() but returns
* seconds with more precision (up to microsecs). (I need this to compare
* the Wisconsin benchmark with Illustra whose TimeNow() shows current
* time with precision up to microsecs.) - ay 3/95
*/
text *
timeofday(void)
{
struct timeval tp; return result;
struct timezone tpz; } /* datetime_timestamp() */
char templ[500];
char buf[500];
text *tm;
int len = 0;
gettimeofday(&tp, &tpz);
strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%d %Y %Z",
localtime((time_t *) &tp.tv_sec));
sprintf(buf, templ, tp.tv_usec);
len = VARHDRSZ + strlen(buf);
tm = (text *) palloc(len);
VARSIZE(tm) = len;
strncpy(VARDATA(tm), buf, strlen(buf));
return tm;
}
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* datetime.c * datetime.c
* implements DATE and TIME data types specified in SQL-92 standard * Support functions for date/time types.
* *
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.41 2000/01/26 05:57:13 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.42 2000/02/16 18:17:02 thomas Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include <limits.h> #include <ctype.h>
#include <math.h>
#include <sys/types.h>
#include <errno.h>
#include "postgres.h" #include "postgres.h"
#ifdef HAVE_FLOAT_H #ifdef HAVE_FLOAT_H
#include <float.h> #include <float.h>
#endif #endif
#include "miscadmin.h" #ifdef HAVE_LIMITS_H
#include "utils/builtins.h" #include <limits.h>
#endif
#ifndef USE_POSIX_TIME
#include <sys/timeb.h>
#endif
static int date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn); #include "miscadmin.h"
#include "utils/datetime.h"
/***************************************************************************** #define USE_DATE_CACHE 1
* Date ADT #define ROUND_ALL 0
*****************************************************************************/
int day_tab[2][13] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
/* date_in()
* Given date text string, convert to internal date format.
*/
DateADT
date_in(char *str)
{
DateADT date;
double fsec;
struct tm tt,
*tm = &tt;
int tzp;
int dtype;
int nf;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
if (!PointerIsValid(str)) char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
elog(ERROR, "Bad (null) date external representation"); "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0)) "Thursday", "Friday", "Saturday", NULL};
elog(ERROR, "Bad date external representation '%s'", str);
switch (dtype)
{
case DTK_DATE:
break;
case DTK_CURRENT: #if 0
GetCurrentTime(tm);
break;
case DTK_EPOCH:
tm->tm_year = 1970;
tm->tm_mon = 1;
tm->tm_mday = 1;
break;
default: static void GetEpochTime(struct tm * tm);
elog(ERROR, "Unrecognized date external representation '%s'", str);
}
date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
return date; #endif
} /* date_in() */
/* date_out()
* Given internal format date, convert to text string.
*/
char *
date_out(DateADT date)
{
char *result;
struct tm tt,
*tm = &tt;
char buf[MAXDATELEN + 1];
j2date((date + date2j(2000, 1, 1)), #define UTIME_MINYEAR (1901)
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); #define UTIME_MINMONTH (12)
#define UTIME_MINDAY (14)
#define UTIME_MAXYEAR (2038)
#define UTIME_MAXMONTH (01)
#define UTIME_MAXDAY (18)
EncodeDateOnly(tm, DateStyle, buf); #define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
|| ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
|| ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
&& ((y < UTIME_MAXYEAR) \
|| ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
|| ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
result = palloc(strlen(buf) + 1);
strcpy(result, buf); /*****************************************************************************
* PRIVATE ROUTINES *
*****************************************************************************/
return result; /* definitions for squeezing values into "value" */
} /* date_out() */ #define ABS_SIGNBIT (char) 0200
#define VALMASK (char) 0177
#define NEG(n) ((n)|ABS_SIGNBIT)
#define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
#define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
#define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
/*
* to keep this table reasonably small, we divide the lexval for TZ and DTZ
* entries by 10 and truncate the text field at MAXTOKLEN characters.
* the text field is not guaranteed to be NULL-terminated.
*/
static datetkn datetktbl[] = {
/* text token lexval */
{EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
{"acsst", DTZ, 63}, /* Cent. Australia */
{"acst", TZ, 57}, /* Cent. Australia */
{DA_D, ADBC, AD}, /* "ad" for years >= 0 */
{"abstime", IGNORE, 0}, /* "abstime" for pre-v6.1 "Invalid
* Abstime" */
{"adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */
{"aesst", DTZ, 66}, /* E. Australia */
{"aest", TZ, 60}, /* Australia Eastern Std Time */
{"ahst", TZ, NEG(60)}, /* Alaska-Hawaii Std Time */
{"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
{"am", AMPM, AM},
{"apr", MONTH, 4},
{"april", MONTH, 4},
{"ast", TZ, NEG(24)}, /* Atlantic Std Time (Canada) */
{"at", IGNORE, 0}, /* "at" (throwaway) */
{"aug", MONTH, 8},
{"august", MONTH, 8},
{"awsst", DTZ, 54}, /* W. Australia */
{"awst", TZ, 48}, /* W. Australia */
{DB_C, ADBC, BC}, /* "bc" for years < 0 */
{"bst", TZ, 6}, /* British Summer Time */
{"bt", TZ, 18}, /* Baghdad Time */
{"cadt", DTZ, 63}, /* Central Australian DST */
{"cast", TZ, 57}, /* Central Australian ST */
{"cat", TZ, NEG(60)}, /* Central Alaska Time */
{"cct", TZ, 48}, /* China Coast */
{"cdt", DTZ, NEG(30)}, /* Central Daylight Time */
{"cet", TZ, 6}, /* Central European Time */
{"cetdst", DTZ, 12}, /* Central European Dayl.Time */
#if USE_AUSTRALIAN_RULES
{"cst", TZ, 63}, /* Australia Eastern Std Time */
#else
{"cst", TZ, NEG(36)}, /* Central Standard Time */
#endif
{DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
{"dec", MONTH, 12},
{"december", MONTH, 12},
{"dnt", TZ, 6}, /* Dansk Normal Tid */
{"dow", RESERV, DTK_DOW}, /* day of week */
{"doy", RESERV, DTK_DOY}, /* day of year */
{"dst", DTZMOD, 6},
{"east", TZ, 60}, /* East Australian Std Time */
{"edt", DTZ, NEG(24)}, /* Eastern Daylight Time */
{"eet", TZ, 12}, /* East. Europe, USSR Zone 1 */
{"eetdst", DTZ, 18}, /* Eastern Europe */
{EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
#if USE_AUSTRALIAN_RULES
{"est", TZ, 60}, /* Australia Eastern Std Time */
#else
{"est", TZ, NEG(30)}, /* Eastern Standard Time */
#endif
{"feb", MONTH, 2},
{"february", MONTH, 2},
{"fri", DOW, 5},
{"friday", DOW, 5},
{"fst", TZ, 6}, /* French Summer Time */
{"fwt", DTZ, 12}, /* French Winter Time */
{"gmt", TZ, 0}, /* Greenwish Mean Time */
{"gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */
{"hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */
{"hmt", DTZ, 18}, /* Hellas ? ? */
{"hst", TZ, NEG(60)}, /* Hawaii Std Time */
{"idle", TZ, 72}, /* Intl. Date Line, East */
{"idlw", TZ, NEG(72)}, /* Intl. Date Line, West */
{LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
{INVALID, RESERV, DTK_INVALID},
/* "invalid" reserved for invalid time */
{"ist", TZ, 12}, /* Israel */
{"it", TZ, 21}, /* Iran Time */
{"jan", MONTH, 1},
{"january", MONTH, 1},
{"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */
{"jt", TZ, 45}, /* Java Time */
{"jul", MONTH, 7},
{"july", MONTH, 7},
{"jun", MONTH, 6},
{"june", MONTH, 6},
{"kst", TZ, 54}, /* Korea Standard Time */
{"ligt", TZ, 60}, /* From Melbourne, Australia */
{"mar", MONTH, 3},
{"march", MONTH, 3},
{"may", MONTH, 5},
{"mdt", DTZ, NEG(36)}, /* Mountain Daylight Time */
{"mest", DTZ, 12}, /* Middle Europe Summer Time */
{"met", TZ, 6}, /* Middle Europe Time */
{"metdst", DTZ, 12}, /* Middle Europe Daylight Time */
{"mewt", TZ, 6}, /* Middle Europe Winter Time */
{"mez", TZ, 6}, /* Middle Europe Zone */
{"mon", DOW, 1},
{"monday", DOW, 1},
{"mst", TZ, NEG(42)}, /* Mountain Standard Time */
{"mt", TZ, 51}, /* Moluccas Time */
{"ndt", DTZ, NEG(15)}, /* Nfld. Daylight Time */
{"nft", TZ, NEG(21)}, /* Newfoundland Standard Time */
{"nor", TZ, 6}, /* Norway Standard Time */
{"nov", MONTH, 11},
{"november", MONTH, 11},
{NOW, RESERV, DTK_NOW}, /* current transaction time */
{"nst", TZ, NEG(21)}, /* Nfld. Standard Time */
{"nt", TZ, NEG(66)}, /* Nome Time */
{"nzdt", DTZ, 78}, /* New Zealand Daylight Time */
{"nzst", TZ, 72}, /* New Zealand Standard Time */
{"nzt", TZ, 72}, /* New Zealand Time */
{"oct", MONTH, 10},
{"october", MONTH, 10},
{"on", IGNORE, 0}, /* "on" (throwaway) */
{"pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */
{"pm", AMPM, PM},
{"pst", TZ, NEG(48)}, /* Pacific Standard Time */
{"sadt", DTZ, 63}, /* S. Australian Dayl. Time */
{"sast", TZ, 57}, /* South Australian Std Time */
{"sat", DOW, 6},
{"saturday", DOW, 6},
{"sep", MONTH, 9},
{"sept", MONTH, 9},
{"september", MONTH, 9},
{"set", TZ, NEG(6)}, /* Seychelles Time ?? */
{"sst", DTZ, 12}, /* Swedish Summer Time */
{"sun", DOW, 0},
{"sunday", DOW, 0},
{"swt", TZ, 6}, /* Swedish Winter Time */
{"thu", DOW, 4},
{"thur", DOW, 4},
{"thurs", DOW, 4},
{"thursday", DOW, 4},
{TODAY, RESERV, DTK_TODAY}, /* midnight */
{TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
{"tue", DOW, 2},
{"tues", DOW, 2},
{"tuesday", DOW, 2},
{"undefined", RESERV, DTK_INVALID}, /* "undefined" pre-v6.1 invalid
* time */
{"ut", TZ, 0},
{"utc", TZ, 0},
{"wadt", DTZ, 48}, /* West Australian DST */
{"wast", TZ, 42}, /* West Australian Std Time */
{"wat", TZ, NEG(6)}, /* West Africa Time */
{"wdt", DTZ, 54}, /* West Australian DST */
{"wed", DOW, 3},
{"wednesday", DOW, 3},
{"weds", DOW, 3},
{"wet", TZ, 0}, /* Western Europe */
{"wetdst", DTZ, 6}, /* Western Europe */
{"wst", TZ, 48}, /* West Australian Std Time */
{"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */
{YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
{"yst", TZ, NEG(54)}, /* Yukon Standard Time */
{"zp4", TZ, NEG(24)}, /* GMT +4 hours. */
{"zp5", TZ, NEG(30)}, /* GMT +5 hours. */
{"zp6", TZ, NEG(36)}, /* GMT +6 hours. */
{"z", RESERV, DTK_ZULU}, /* 00:00:00 */
{ZULU, RESERV, DTK_ZULU}, /* 00:00:00 */
};
static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
static datetkn deltatktbl[] = {
/* text token lexval */
{"@", IGNORE, 0}, /* postgres relative time prefix */
{DAGO, AGO, 0}, /* "ago" indicates negative time offset */
{"c", UNITS, DTK_CENTURY}, /* "century" relative time units */
{"cent", UNITS, DTK_CENTURY}, /* "century" relative time units */
{"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative time units */
{DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative time units */
{"d", UNITS, DTK_DAY}, /* "day" relative time units */
{DDAY, UNITS, DTK_DAY}, /* "day" relative time units */
{"days", UNITS, DTK_DAY}, /* "days" relative time units */
{"dec", UNITS, DTK_DECADE}, /* "decade" relative time units */
{"decs", UNITS, DTK_DECADE},/* "decades" relative time units */
{DDECADE, UNITS, DTK_DECADE}, /* "decade" relative time units */
{"decades", UNITS, DTK_DECADE}, /* "decades" relative time units */
{"h", UNITS, DTK_HOUR}, /* "hour" relative time units */
{DHOUR, UNITS, DTK_HOUR}, /* "hour" relative time units */
{"hours", UNITS, DTK_HOUR}, /* "hours" relative time units */
{"hr", UNITS, DTK_HOUR}, /* "hour" relative time units */
{"hrs", UNITS, DTK_HOUR}, /* "hours" relative time units */
{INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for invalid
* time */
{"m", UNITS, DTK_MINUTE}, /* "minute" relative time units */
{"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative
* time units */
{"mil", UNITS, DTK_MILLENIUM}, /* "millenium" relative time units */
{"mils", UNITS, DTK_MILLENIUM}, /* "millenia" relative time units */
{"millenia", UNITS, DTK_MILLENIUM}, /* "millenia" relative time units */
{DMILLENIUM, UNITS, DTK_MILLENIUM}, /* "millenium" relative time units */
{"millisecon", UNITS, DTK_MILLISEC}, /* relative time units */
{"min", UNITS, DTK_MINUTE}, /* "minute" relative time units */
{"mins", UNITS, DTK_MINUTE},/* "minutes" relative time units */
{"mins", UNITS, DTK_MINUTE},/* "minutes" relative time units */
{DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative time units */
{"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative time units */
{"mon", UNITS, DTK_MONTH}, /* "months" relative time units */
{"mons", UNITS, DTK_MONTH}, /* "months" relative time units */
{DMONTH, UNITS, DTK_MONTH}, /* "month" relative time units */
{"months", UNITS, DTK_MONTH},
{"ms", UNITS, DTK_MILLISEC},
{"msec", UNITS, DTK_MILLISEC},
{DMILLISEC, UNITS, DTK_MILLISEC},
{"mseconds", UNITS, DTK_MILLISEC},
{"msecs", UNITS, DTK_MILLISEC},
{"qtr", UNITS, DTK_QUARTER},/* "quarter" relative time */
{DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative time */
{"reltime", IGNORE, 0}, /* for pre-v6.1 "Undefined Reltime" */
{"s", UNITS, DTK_SECOND},
{"sec", UNITS, DTK_SECOND},
{DSECOND, UNITS, DTK_SECOND},
{"seconds", UNITS, DTK_SECOND},
{"secs", UNITS, DTK_SECOND},
{DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
{"tz", UNITS, DTK_TZ}, /* "timezone" time offset */
{"tz_hour", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
{"tz_minute", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
{"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
{"us", UNITS, DTK_MICROSEC},/* "microsecond" relative time units */
{"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative time
* units */
{DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative time
* units */
{"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative time
* units */
{"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative time
* units */
{"w", UNITS, DTK_WEEK}, /* "week" relative time units */
{DWEEK, UNITS, DTK_WEEK}, /* "week" relative time units */
{"weeks", UNITS, DTK_WEEK}, /* "weeks" relative time units */
{"y", UNITS, DTK_YEAR}, /* "year" relative time units */
{DYEAR, UNITS, DTK_YEAR}, /* "year" relative time units */
{"years", UNITS, DTK_YEAR}, /* "years" relative time units */
{"yr", UNITS, DTK_YEAR}, /* "year" relative time units */
{"yrs", UNITS, DTK_YEAR}, /* "years" relative time units */
};
static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
#if USE_DATE_CACHE
datetkn *datecache[MAXDATEFIELDS] = {NULL};
datetkn *deltacache[MAXDATEFIELDS] = {NULL};
bool #endif
date_eq(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 == dateVal2;
}
bool
date_ne(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 != dateVal2;
}
bool /*
date_lt(DateADT dateVal1, DateADT dateVal2) * Calendar time to Julian date conversions.
{ * Julian date is commonly used in astronomical applications,
return dateVal1 < dateVal2; * since it is numerically accurate and computationally simple.
} /* date_lt() */ * The algorithms here will accurately convert between Julian day
* and calendar date for all non-negative Julian days
* (i.e. from Nov 23, -4713 on).
*
* Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
* University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
*
* Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
* now at Aerospace Corp. (hi, Henry!)
*
* These routines will be used by other date/time packages - tgl 97/02/25
*/
bool int
date_le(DateADT dateVal1, DateADT dateVal2) date2j(int y, int m, int d)
{ {
return dateVal1 <= dateVal2; int m12 = (m - 14) / 12;
} /* date_le() */
bool return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12
date_gt(DateADT dateVal1, DateADT dateVal2) - (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075);
{ } /* date2j() */
return dateVal1 > dateVal2;
} /* date_gt() */
bool void
date_ge(DateADT dateVal1, DateADT dateVal2) j2date(int jd, int *year, int *month, int *day)
{ {
return dateVal1 >= dateVal2; int j,
} /* date_ge() */ y,
m,
d;
int i,
l,
n;
l = jd + 68569;
n = (4 * l) / 146097;
l -= (146097 * n + 3) / 4;
i = (4000 * (l + 1)) / 1461001;
l += 31 - (1461 * i) / 4;
j = (80 * l) / 2447;
d = l - (2447 * j) / 80;
l = j / 11;
m = (j + 2) - (12 * l);
y = 100 * (n - 49) + i + l;
*year = y;
*month = m;
*day = d;
return;
} /* j2date() */
int int
date_cmp(DateADT dateVal1, DateADT dateVal2) j2day(int date)
{ {
if (dateVal1 < dateVal2) int day;
return -1;
else if (dateVal1 > dateVal2)
return 1;
return 0;
} /* date_cmp() */
DateADT day = (date + 1) % 7;
date_larger(DateADT dateVal1, DateADT dateVal2)
{
return date_gt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
} /* date_larger() */
DateADT return day;
date_smaller(DateADT dateVal1, DateADT dateVal2) } /* j2day() */
{
return date_lt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
} /* date_smaller() */
/* Compute difference between two dates in days.
*/
int4
date_mi(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 - dateVal2;
} /* date_mi() */
/* Add a number of days to a date, giving a new date. #if 0
* Must handle both positive and negative numbers of days.
*/
DateADT static double
date_pli(DateADT dateVal, int4 days) time2t(const int hour, const int min, const double sec)
{ {
return dateVal + days; return (((hour * 60) + min) * 60) + sec;
} /* date_pli() */ } /* time2t() */
/* Subtract a number of days from a date, giving a new date. static void
*/ dt2time(Timestamp jd, int *hour, int *min, double *sec)
DateADT
date_mii(DateADT dateVal, int4 days)
{ {
return date_pli(dateVal, -days); double time;
} /* date_mii() */
time = jd;
/* date_datetime() *hour = (time / 3600);
* Convert date to datetime data type. time -= ((*hour) * 3600);
*/ *min = (time / 60);
DateTime * time -= ((*min) * 60);
date_datetime(DateADT dateVal) *sec = JROUND(time);
{
DateTime *result;
struct tm tt,
*tm = &tt;
int tz;
double fsec = 0;
char *tzn;
result = palloc(sizeof(DateTime)); return;
} /* dt2time() */
if (date2tm(dateVal, &tz, tm, &fsec, &tzn) != 0)
elog(ERROR, "Unable to convert date to datetime");
if (tm2datetime(tm, fsec, &tz, result) != 0) #endif
elog(ERROR, "Datetime out of range");
return result;
} /* date_datetime() */
/*
* parse and convert date in timestr (the normal interface)
*
* Returns the number of seconds since epoch (J2000)
*/
/* datetime_date() /* ParseDateTime()
* Convert datetime to date data type. * Break string into tokens based on a date/time context.
*/ */
DateADT int
datetime_date(DateTime *datetime) ParseDateTime(char *timestr, char *lowstr,
char **field, int *ftype, int maxfields, int *numfields)
{ {
DateADT result; int nf = 0;
struct tm tt, char *cp = timestr;
*tm = &tt; char *lp = lowstr;
int tz;
double fsec;
char *tzn;
if (!PointerIsValid(datetime)) /* outer loop through fields */
elog(ERROR, "Unable to convert null datetime to date"); while (*cp != '\0')
{
if (DATETIME_NOT_FINITE(*datetime)) field[nf] = lp;
elog(ERROR, "Unable to convert datetime to date");
if (DATETIME_IS_EPOCH(*datetime)) /* leading digit? then date or time */
if (isdigit(*cp) || (*cp == '.'))
{ {
datetime2tm(SetDateTime(*datetime), NULL, tm, &fsec, NULL); *lp++ = *cp++;
while (isdigit(*cp))
*lp++ = *cp++;
/* time field? */
if (*cp == ':')
{
ftype[nf] = DTK_TIME;
while (isdigit(*cp) || (*cp == ':') || (*cp == '.'))
*lp++ = *cp++;
} }
else if (DATETIME_IS_CURRENT(*datetime)) /* date field? allow embedded text month */
else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
{ {
datetime2tm(SetDateTime(*datetime), &tz, tm, &fsec, &tzn); ftype[nf] = DTK_DATE;
while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
*lp++ = tolower(*cp++);
} }
/*
* otherwise, number only and will determine year, month, or
* day later
*/
else else
{ ftype[nf] = DTK_NUMBER;
if (datetime2tm(*datetime, &tz, tm, &fsec, &tzn) != 0)
elog(ERROR, "Unable to convert datetime to date");
} }
result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1)); /*
* text? then date string, month, day of week, special, or
* timezone
*/
else if (isalpha(*cp))
{
ftype[nf] = DTK_STRING;
*lp++ = tolower(*cp++);
while (isalpha(*cp))
*lp++ = tolower(*cp++);
return result; /* full date string with leading text month? */
} /* datetime_date() */ if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
{
/*
* special case of Posix timezone "GMT-0800"
* Note that other sign (e.g. "GMT+0800"
* is recognized as two separate fields and handled later.
* XXX There is no room for a delimiter between
* the "GMT" and the "-0800", so we are going to just swallow the "GMT".
* But this leads to other troubles with the definition of signs,
* so we have to flip
* - thomas 2000-02-06
*/
if ((*cp == '-') && isdigit(*(cp+1))
&& (strncmp(field[nf], "gmt", 3) == 0))
{
*cp = '+';
continue;
}
ftype[nf] = DTK_DATE;
while (isdigit(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
*lp++ = tolower(*cp++);
}
/* abstime_date() /* skip leading spaces */
* Convert abstime to date data type. }
*/ else if (isspace(*cp))
DateADT {
abstime_date(AbsoluteTime abstime) cp++;
{ continue;
DateADT result;
struct tm tt,
*tm = &tt;
int tz;
switch (abstime) /* sign? then special or numeric timezone */
}
else if ((*cp == '+') || (*cp == '-'))
{
*lp++ = *cp++;
/* soak up leading whitespace */
while (isspace(*cp))
cp++;
/* numeric timezone? */
if (isdigit(*cp))
{ {
case INVALID_ABSTIME: ftype[nf] = DTK_TZ;
case NOSTART_ABSTIME: *lp++ = *cp++;
case NOEND_ABSTIME: while (isdigit(*cp) || (*cp == ':'))
elog(ERROR, "Unable to convert reserved abstime value to date"); *lp++ = *cp++;
/* /* special? */
* pretend to drop through to make compiler think that result }
* will be set else if (isalpha(*cp))
*/ {
ftype[nf] = DTK_SPECIAL;
*lp++ = tolower(*cp++);
while (isalpha(*cp))
*lp++ = tolower(*cp++);
case EPOCH_ABSTIME: /* otherwise something wrong... */
result = date2j(1970, 1, 1) - date2j(2000, 1, 1); }
break; else
return -1;
case CURRENT_ABSTIME: /* ignore punctuation but use as delimiter */
GetCurrentTime(tm); }
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); else if (ispunct(*cp))
break; {
cp++;
continue;
default:
abstime2tm(abstime, &tz, tm, NULL);
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
break;
} }
else
return -1;
return result; /* force in a delimiter */
} /* abstime_date() */ *lp++ = '\0';
nf++;
if (nf > MAXDATEFIELDS)
return -1;
}
*numfields = nf;
/* date2tm() return 0;
* Convert date to time structure. } /* ParseDateTime() */
* Note that date is an implicit local time, but the system calls assume
* that everything is GMT. So, convert to GMT, rotate to local time,
* and then convert again to try to get the time zones correct. /* DecodeDateTime()
* Interpret previously parsed fields for general date and time.
* Return 0 if full date, 1 if only time, and -1 if problems.
* External format(s):
* "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
* "Fri Feb-7-1997 15:23:27"
* "Feb-7-1997 15:23:27"
* "2-7-1997 15:23:27"
* "1997-2-7 15:23:27"
* "1997.038 15:23:27" (day of year 1-366)
* Also supports input in compact time:
* "970207 152327"
* "97038 152327"
*
* Use the system-provided functions to get the current time zone
* if not specified in the input string.
* If the date is outside the time_t system-supported time range,
* then assume GMT time zone. - tgl 97/05/27
*/ */
static int int
date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn) DecodeDateTime(char **field, int *ftype, int nf,
int *dtype, struct tm * tm, double *fsec, int *tzp)
{ {
struct tm *tx; int fmask = 0,
time_t utime; tmask,
type;
*fsec = 0; int i;
int flen,
j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); val;
int mer = HR24;
int haveTextMonth = FALSE;
int is2digits = FALSE;
int bc = FALSE;
*dtype = DTK_DATE;
tm->tm_hour = 0; tm->tm_hour = 0;
tm->tm_min = 0; tm->tm_min = 0;
tm->tm_sec = 0; tm->tm_sec = 0;
tm->tm_isdst = -1; *fsec = 0;
tm->tm_isdst = -1; /* don't know daylight savings time status
* apriori */
if (tzp != NULL)
*tzp = 0;
if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) for (i = 0; i < nf; i++)
{ {
switch (ftype[i])
{
case DTK_DATE:
if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
return -1;
break;
/* convert to system time */ case DTK_TIME:
utime = ((dateVal + (date2j(2000, 1, 1) - date2j(1970, 1, 1))) * 86400); if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
/* rotate to noon to get the right day in time zone */ return -1;
utime += (12 * 60 * 60);
#ifdef USE_POSIX_TIME /*
tx = localtime(&utime); * check upper limit on hours; other limits checked in
* DecodeTime()
*/
if (tm->tm_hour > 23)
return -1;
break;
tm->tm_year = tx->tm_year + 1900; case DTK_TZ:
tm->tm_mon = tx->tm_mon + 1; if (tzp == NULL)
tm->tm_mday = tx->tm_mday; return -1;
tm->tm_isdst = tx->tm_isdst; if (DecodeTimezone(field[i], tzp) != 0)
return -1;
tmask = DTK_M(TZ);
break;
#if defined(HAVE_TM_ZONE) case DTK_NUMBER:
tm->tm_gmtoff = tx->tm_gmtoff; flen = strlen(field[i]);
tm->tm_zone = tx->tm_zone;
/* tm_gmtoff is Sun/DEC-ism */ /*
*tzp = -(tm->tm_gmtoff); * long numeric string and either no date or no time read
if (tzn != NULL) * yet? then interpret as a concatenated date or time...
*tzn = (char *) tm->tm_zone; */
#elif defined(HAVE_INT_TIMEZONE) if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M)))
#ifdef __CYGWIN__ {
*tzp = (tm->tm_isdst ? (_timezone - 3600) : _timezone); if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
#else return -1;
*tzp = (tm->tm_isdst ? (timezone - 3600) : timezone);
#endif
if (tzn != NULL)
*tzn = tzname[(tm->tm_isdst > 0)];
#else
#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined
#endif
#else /* !USE_POSIX_TIME */
*tzp = CTimeZone; /* V7 conventions; don't know timezone? */
if (tzn != NULL)
*tzn = CTZName;
#endif
/* otherwise, outside of timezone range so convert to GMT... */
} }
/* otherwise it is a single date/time field... */
else else
{ {
*tzp = 0; if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
tm->tm_isdst = 0; return -1;
if (tzn != NULL)
*tzn = NULL;
} }
break;
return 0; case DTK_STRING:
} /* date2tm() */ case DTK_SPECIAL:
type = DecodeSpecial(i, field[i], &val);
if (type == IGNORE)
/***************************************************************************** continue;
* Time ADT
*****************************************************************************/
TimeADT *
time_in(char *str)
{
TimeADT *time;
double fsec;
struct tm tt,
*tm = &tt;
int nf;
char lowstr[MAXDATELEN + 1];
char *field[MAXDATEFIELDS];
int dtype;
int ftype[MAXDATEFIELDS];
if (!PointerIsValid(str))
elog(ERROR, "Bad (null) time external representation");
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec) != 0))
elog(ERROR, "Bad time external representation '%s'", str);
time = palloc(sizeof(TimeADT));
*time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
return time;
} /* time_in() */
char *
time_out(TimeADT *time)
{
char *result;
struct tm tt,
*tm = &tt;
double fsec;
char buf[MAXDATELEN + 1];
if (!PointerIsValid(time))
return NULL;
tm->tm_hour = (*time / (60 * 60));
tm->tm_min = (((int) (*time / 60)) % 60);
tm->tm_sec = (((int) *time) % 60);
fsec = 0;
EncodeTimeOnly(tm, fsec, DateStyle, buf);
result = palloc(strlen(buf) + 1); tmask = DTK_M(type);
switch (type)
{
case RESERV:
switch (val)
{
case DTK_NOW:
tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
*dtype = DTK_DATE;
GetCurrentTime(tm);
if (tzp != NULL)
*tzp = CTimeZone;
break;
strcpy(result, buf); case DTK_YESTERDAY:
tmask = DTK_DATE_M;
*dtype = DTK_DATE;
GetCurrentTime(tm);
j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1),
&tm->tm_year, &tm->tm_mon, &tm->tm_mday);
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
break;
return result; case DTK_TODAY:
} /* time_out() */ tmask = DTK_DATE_M;
*dtype = DTK_DATE;
GetCurrentTime(tm);
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
break;
case DTK_TOMORROW:
tmask = DTK_DATE_M;
*dtype = DTK_DATE;
GetCurrentTime(tm);
j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1),
&tm->tm_year, &tm->tm_mon, &tm->tm_mday);
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
break;
bool case DTK_ZULU:
time_eq(TimeADT *time1, TimeADT *time2) tmask = (DTK_TIME_M | DTK_M(TZ));
{ *dtype = DTK_DATE;
if (!PointerIsValid(time1) || !PointerIsValid(time2)) tm->tm_hour = 0;
return FALSE; tm->tm_min = 0;
tm->tm_sec = 0;
if (tzp != NULL)
*tzp = 0;
break;
return *time1 == *time2; default:
} /* time_eq() */ *dtype = val;
}
bool break;
time_ne(TimeADT *time1, TimeADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return *time1 != *time2; case MONTH:
} /* time_eq() */ /*
* already have a (numeric) month? then see if we
* can substitute...
*/
if ((fmask & DTK_M(MONTH)) && (!haveTextMonth)
&& (!(fmask & DTK_M(DAY)))
&& ((tm->tm_mon >= 1) && (tm->tm_mon <= 31)))
{
tm->tm_mday = tm->tm_mon;
tmask = DTK_M(DAY);
}
haveTextMonth = TRUE;
tm->tm_mon = val;
break;
bool case DTZMOD:
time_lt(TimeADT *time1, TimeADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return *time1 < *time2; /*
} /* time_eq() */ * daylight savings time modifier (solves "MET
* DST" syntax)
*/
tmask |= DTK_M(DTZ);
tm->tm_isdst = 1;
if (tzp == NULL)
return -1;
*tzp += val * 60;
break;
bool case DTZ:
time_le(TimeADT *time1, TimeADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return *time1 <= *time2; /*
} /* time_eq() */ * set mask for TZ here _or_ check for DTZ later
* when getting default timezone
*/
tmask |= DTK_M(TZ);
tm->tm_isdst = 1;
if (tzp == NULL)
return -1;
*tzp = val * 60;
break;
bool case TZ:
time_gt(TimeADT *time1, TimeADT *time2) tm->tm_isdst = 0;
{ if (tzp == NULL)
if (!PointerIsValid(time1) || !PointerIsValid(time2)) return -1;
return FALSE; *tzp = val * 60;
return *time1 > *time2; /* Swallow an immediately succeeding timezone if this is GMT
} /* time_eq() */ * This handles the odd case in FreeBSD of "GMT+0800"
* but note that we need to flip the sign on this too.
* Claims to be some sort of POSIX standard format :(
* - thomas 2000-01-20
*/
if ((i < (nf-1)) && (ftype[i+1] == DTK_TZ)
&& (strcmp(field[i], "gmt") == 0))
{
i++;
if (DecodeTimezone(field[i], tzp) != 0)
return -1;
bool /* flip the sign per POSIX standard */
time_ge(TimeADT *time1, TimeADT *time2) *tzp = -(*tzp);
{ }
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return *time1 >= *time2;
} /* time_eq() */
int break;
time_cmp(TimeADT *time1, TimeADT *time2)
{
return (*time1 < *time2) ? -1 : (((*time1 > *time2) ? 1 : 0));
} /* time_cmp() */
case IGNORE:
break;
/* datetime_time() case AMPM:
* Convert datetime to time data type. mer = val;
*/ break;
TimeADT *
datetime_time(DateTime *datetime)
{
TimeADT *result;
struct tm tt,
*tm = &tt;
int tz;
double fsec;
char *tzn;
if (!PointerIsValid(datetime)) case ADBC:
elog(ERROR, "Unable to convert null datetime to date"); bc = (val == BC);
break;
if (DATETIME_NOT_FINITE(*datetime)) case DOW:
elog(ERROR, "Unable to convert datetime to date"); tm->tm_wday = val;
break;
if (DATETIME_IS_EPOCH(*datetime)) default:
{ return -1;
datetime2tm(SetDateTime(*datetime), NULL, tm, &fsec, NULL); }
break;
default:
return -1;
} }
else if (DATETIME_IS_CURRENT(*datetime))
{
datetime2tm(SetDateTime(*datetime), &tz, tm, &fsec, &tzn);
if (tmask & fmask)
return -1;
fmask |= tmask;
} }
/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
if (bc)
{
if (tm->tm_year > 0)
tm->tm_year = -(tm->tm_year - 1);
else else
elog(ERROR, "Inconsistant use of year %04d and 'BC'", tm->tm_year);
}
else if (is2digits)
{ {
if (datetime2tm(*datetime, &tz, tm, &fsec, &tzn) != 0) if (tm->tm_year < 70)
elog(ERROR, "Unable to convert datetime to date"); tm->tm_year += 2000;
else if (tm->tm_year < 100)
tm->tm_year += 1900;
} }
result = palloc(sizeof(TimeADT)); if ((mer != HR24) && (tm->tm_hour > 12))
return -1;
*result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec); if ((mer == AM) && (tm->tm_hour == 12))
tm->tm_hour = 0;
return result; else if ((mer == PM) && (tm->tm_hour != 12))
} /* datetime_time() */ tm->tm_hour += 12;
/* do additional checking for full date specs... */
if (*dtype == DTK_DATE)
{
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
/* datetime_datetime() /*
* Convert date and time to datetime data type. * check for valid day of month, now that we know for sure the
* month and year...
*/ */
DateTime * if ((tm->tm_mday < 1)
datetime_datetime(DateADT date, TimeADT *time) || (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]))
{ return -1;
DateTime *result;
if (!PointerIsValid(time)) /* timezone not specified? then find local timezone if possible */
if (((fmask & DTK_DATE_M) == DTK_DATE_M)
&& (tzp != NULL) && (!(fmask & DTK_M(TZ))))
{ {
result = palloc(sizeof(DateTime));
DATETIME_INVALID(*result); /*
} * daylight savings time modifier but no standard timezone?
* then error
*/
if (fmask & DTK_M(DTZMOD))
return -1;
if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
{
#ifdef USE_POSIX_TIME
tm->tm_year -= 1900;
tm->tm_mon -= 1;
tm->tm_isdst = -1;
mktime(tm);
tm->tm_year += 1900;
tm->tm_mon += 1;
#if defined(HAVE_TM_ZONE)
*tzp = -(tm->tm_gmtoff); /* tm_gmtoff is
* Sun/DEC-ism */
#elif defined(HAVE_INT_TIMEZONE)
#ifdef __CYGWIN__
*tzp = ((tm->tm_isdst > 0) ? (_timezone - 3600) : _timezone);
#else
*tzp = ((tm->tm_isdst > 0) ? (timezone - 3600) : timezone);
#endif
#else
#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined
#endif
#else /* !USE_POSIX_TIME */
*tzp = CTimeZone;
#endif
}
else
{
tm->tm_isdst = 0;
*tzp = 0;
}
}
}
return 0;
} /* DecodeDateTime() */
/* DecodeTimeOnly()
* Interpret parsed string as time fields only.
*/
int
DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
{
int fmask,
tmask,
type;
int i;
int flen,
val;
int is2digits = FALSE;
int mer = HR24;
*dtype = DTK_TIME;
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
tm->tm_isdst = -1; /* don't know daylight savings time status
* apriori */
*fsec = 0;
fmask = DTK_DATE_M;
for (i = 0; i < nf; i++)
{
switch (ftype[i])
{
case DTK_TIME:
if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
return -1;
break;
case DTK_NUMBER:
flen = strlen(field[i]);
if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
return -1;
break;
case DTK_STRING:
case DTK_SPECIAL:
type = DecodeSpecial(i, field[i], &val);
if (type == IGNORE)
continue;
tmask = DTK_M(type);
switch (type)
{
case RESERV:
switch (val)
{
case DTK_NOW:
tmask = DTK_TIME_M;
*dtype = DTK_TIME;
GetCurrentTime(tm);
break;
case DTK_ZULU:
tmask = (DTK_TIME_M | DTK_M(TZ));
*dtype = DTK_TIME;
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
tm->tm_isdst = 0;
break;
default:
return -1;
}
break;
case IGNORE:
break;
case AMPM:
mer = val;
break;
default:
return -1;
}
break;
default:
return -1;
}
if (tmask & fmask)
return -1;
fmask |= tmask;
}
if ((mer != HR24) && (tm->tm_hour > 12))
return -1;
if ((mer == AM) && (tm->tm_hour == 12))
tm->tm_hour = 0;
else if ((mer == PM) && (tm->tm_hour != 12))
tm->tm_hour += 12;
if (((tm->tm_hour < 0) || (tm->tm_hour > 23))
|| ((tm->tm_min < 0) || (tm->tm_min > 59))
|| ((tm->tm_sec < 0) || ((tm->tm_sec + *fsec) >= 60)))
return -1;
if ((fmask & DTK_TIME_M) != DTK_TIME_M)
return -1;
return 0;
} /* DecodeTimeOnly() */
/* DecodeDate()
* Decode date string which includes delimiters.
* Insist on a complete set of fields.
*/
int
DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
{
double fsec;
int nf = 0;
int i,
len;
int bc = FALSE;
int is2digits = FALSE;
int type,
val,
dmask = 0;
char *field[MAXDATEFIELDS];
/* parse this string... */
while ((*str != '\0') && (nf < MAXDATEFIELDS))
{
/* skip field separators */
while (!isalnum(*str))
str++;
field[nf] = str;
if (isdigit(*str))
{
while (isdigit(*str))
str++;
}
else if (isalpha(*str))
{
while (isalpha(*str))
str++;
}
if (*str != '\0')
*str++ = '\0';
nf++;
}
#if 0
/* don't allow too many fields */
if (nf > 3)
return -1;
#endif
*tmask = 0;
/* look first for text fields, since that will be unambiguous month */
for (i = 0; i < nf; i++)
{
if (isalpha(*field[i]))
{
type = DecodeSpecial(i, field[i], &val);
if (type == IGNORE)
continue;
dmask = DTK_M(type);
switch (type)
{
case MONTH:
tm->tm_mon = val;
break;
case ADBC:
bc = (val == BC);
break;
default:
return -1;
}
if (fmask & dmask)
return -1;
fmask |= dmask;
*tmask |= dmask;
/* mark this field as being completed */
field[i] = NULL;
}
}
/* now pick up remaining numeric fields */
for (i = 0; i < nf; i++)
{
if (field[i] == NULL)
continue;
if ((len = strlen(field[i])) <= 0)
return -1;
if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits) != 0)
return -1;
if (fmask & dmask)
return -1;
fmask |= dmask;
*tmask |= dmask;
}
if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
return -1;
/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
if (bc)
{
if (tm->tm_year > 0)
tm->tm_year = -(tm->tm_year - 1);
else
elog(ERROR, "Inconsistant use of year %04d and 'BC'", tm->tm_year);
}
else if (is2digits)
{
if (tm->tm_year < 70)
tm->tm_year += 2000;
else if (tm->tm_year < 100)
tm->tm_year += 1900;
}
return 0;
} /* DecodeDate() */
/* DecodeTime()
* Decode time string which includes delimiters.
* Only check the lower limit on hours, since this same code
* can be used to represent time spans.
*/
int
DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
{
char *cp;
*tmask = DTK_TIME_M;
tm->tm_hour = strtol(str, &cp, 10);
if (*cp != ':')
return -1;
str = cp + 1;
tm->tm_min = strtol(str, &cp, 10);
if (*cp == '\0')
{
tm->tm_sec = 0;
*fsec = 0;
}
else if (*cp != ':')
{
return -1;
}
else
{
str = cp + 1;
tm->tm_sec = strtol(str, &cp, 10);
if (*cp == '\0')
*fsec = 0;
else if (*cp == '.')
{
str = cp;
*fsec = strtod(str, &cp);
if (cp == str)
return -1;
}
else
return -1;
}
/* do a sanity check */
if ((tm->tm_hour < 0)
|| (tm->tm_min < 0) || (tm->tm_min > 59)
|| (tm->tm_sec < 0) || (tm->tm_sec > 59))
return -1;
return 0;
} /* DecodeTime() */
/* DecodeNumber()
* Interpret numeric field as a date value in context.
*/
int
DecodeNumber(int flen, char *str, int fmask,
int *tmask, struct tm * tm, double *fsec, int *is2digits)
{
int val;
char *cp;
*tmask = 0;
val = strtol(str, &cp, 10);
if (cp == str)
return -1;
if (*cp == '.')
{
*fsec = strtod(cp, &cp);
if (*cp != '\0')
return -1;
}
/* Special case day of year? */
if ((flen == 3) && (fmask & DTK_M(YEAR))
&& ((val >= 1) && (val <= 366)))
{
*tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
tm->tm_yday = val;
j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
&tm->tm_year, &tm->tm_mon, &tm->tm_mday);
}
/*
* Enough digits to be unequivocal year? Used to test for 4 digits or
* more, but we now test first for a three-digit doy so anything
* bigger than two digits had better be an explicit year. - thomas
* 1999-01-09
*/
else if (flen > 2)
{
*tmask = DTK_M(YEAR);
/* already have a year? then see if we can substitute... */
if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
&& ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
{
tm->tm_mday = tm->tm_year;
*tmask = DTK_M(DAY);
}
tm->tm_year = val;
}
/* already have year? then could be month */
else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
&& ((val >= 1) && (val <= 12)))
{
*tmask = DTK_M(MONTH);
tm->tm_mon = val;
/* no year and EuroDates enabled? then could be day */
}
else if ((EuroDates || (fmask & DTK_M(MONTH)))
&& (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
&& ((val >= 1) && (val <= 31)))
{
*tmask = DTK_M(DAY);
tm->tm_mday = val;
}
else if ((!(fmask & DTK_M(MONTH)))
&& ((val >= 1) && (val <= 12)))
{
*tmask = DTK_M(MONTH);
tm->tm_mon = val;
}
else if ((!(fmask & DTK_M(DAY)))
&& ((val >= 1) && (val <= 31)))
{
*tmask = DTK_M(DAY);
tm->tm_mday = val;
}
else if (!(fmask & DTK_M(YEAR)))
{
*tmask = DTK_M(YEAR);
tm->tm_year = val;
/* adjust ONLY if exactly two digits... */
*is2digits = (flen == 2);
}
else
return -1;
return 0;
} /* DecodeNumber() */
/* DecodeNumberField()
* Interpret numeric string as a concatenated date field.
*/
int
DecodeNumberField(int len, char *str, int fmask,
int *tmask, struct tm * tm, double *fsec, int *is2digits)
{
char *cp;
/* yyyymmdd? */
if (len == 8)
{
*tmask = DTK_DATE_M;
tm->tm_mday = atoi(str + 6);
*(str + 6) = '\0';
tm->tm_mon = atoi(str + 4);
*(str + 4) = '\0';
tm->tm_year = atoi(str + 0);
/* yymmdd or hhmmss? */
}
else if (len == 6)
{
if (fmask & DTK_DATE_M)
{
*tmask = DTK_TIME_M;
tm->tm_sec = atoi(str + 4);
*(str + 4) = '\0';
tm->tm_min = atoi(str + 2);
*(str + 2) = '\0';
tm->tm_hour = atoi(str + 0);
}
else
{
*tmask = DTK_DATE_M;
tm->tm_mday = atoi(str + 4);
*(str + 4) = '\0';
tm->tm_mon = atoi(str + 2);
*(str + 2) = '\0';
tm->tm_year = atoi(str + 0);
*is2digits = TRUE;
}
}
else if ((len == 5) && !(fmask & DTK_DATE_M))
{
*tmask = DTK_DATE_M;
tm->tm_mday = atoi(str + 2);
*(str + 2) = '\0';
tm->tm_mon = 1;
tm->tm_year = atoi(str + 0);
*is2digits = TRUE;
}
else if (strchr(str, '.') != NULL)
{
*tmask = DTK_TIME_M;
tm->tm_sec = strtod((str + 4), &cp);
if (cp == (str + 4))
return -1;
if (*cp == '.')
*fsec = strtod(cp, NULL);
*(str + 4) = '\0';
tm->tm_min = strtod((str + 2), &cp);
*(str + 2) = '\0';
tm->tm_hour = strtod((str + 0), &cp);
}
else
return -1;
return 0;
} /* DecodeNumberField() */
/* DecodeTimezone()
* Interpret string as a numeric timezone.
*/
int
DecodeTimezone(char *str, int *tzp)
{
int tz;
int hr,
min;
char *cp;
int len;
/* assume leading character is "+" or "-" */
hr = strtol((str + 1), &cp, 10);
/* explicit delimiter? */
if (*cp == ':')
{
min = strtol((cp + 1), &cp, 10);
/* otherwise, might have run things together... */
}
else if ((*cp == '\0') && ((len = strlen(str)) > 3))
{
min = strtol((str + len - 2), &cp, 10);
*(str + len - 2) = '\0';
hr = strtol((str + 1), &cp, 10);
}
else
min = 0;
tz = (hr * 60 + min) * 60;
if (*str == '-')
tz = -tz;
*tzp = -tz;
return *cp != '\0';
} /* DecodeTimezone() */
/* DecodeSpecial()
* Decode text string using lookup table.
* Implement a cache lookup since it is likely that dates
* will be related in format.
*/
int
DecodeSpecial(int field, char *lowtoken, int *val)
{
int type;
datetkn *tp;
#if USE_DATE_CACHE
if ((datecache[field] != NULL)
&& (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
tp = datecache[field];
else
{
#endif
tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
#if USE_DATE_CACHE
}
datecache[field] = tp;
#endif
if (tp == NULL)
{
type = IGNORE;
*val = 0;
}
else
{
type = tp->type;
switch (type)
{
case TZ:
case DTZ:
case DTZMOD:
*val = FROMVAL(tp);
break;
default:
*val = tp->value;
break;
}
}
return type;
} /* DecodeSpecial() */
/* DecodeDateDelta()
* Interpret previously parsed fields for general time interval.
* Return 0 if decoded and -1 if problems.
*
* Allow "date" field DTK_DATE since this could be just
* an unsigned floating point number. - thomas 1997-11-16
*
* Allow ISO-style time span, with implicit units on number of days
* preceeding an hh:mm:ss field. - thomas 1998-04-30
*/
int
DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
{
int is_before = FALSE;
char *cp;
int fmask = 0,
tmask,
type;
int i;
int flen,
val;
double fval;
double sec;
*dtype = DTK_DELTA;
type = DTK_SECOND;
tm->tm_year = 0;
tm->tm_mon = 0;
tm->tm_mday = 0;
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
*fsec = 0;
/* read through list backwards to pick up units before values */
for (i = nf - 1; i >= 0; i--)
{
switch (ftype[i])
{
case DTK_TIME:
if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
return -1;
type = DTK_DAY;
break;
case DTK_TZ:
/*
* Timezone is a token with a leading sign character and
* otherwise the same as a non-signed numeric field
*/
case DTK_DATE:
case DTK_NUMBER:
val = strtol(field[i], &cp, 10);
if (*cp == '.')
{
fval = strtod(cp, &cp);
if (*cp != '\0')
return -1;
if (val < 0)
fval = -(fval);
}
else if (*cp == '\0')
fval = 0;
else
return -1;
flen = strlen(field[i]);
tmask = 0; /* DTK_M(type); */
switch (type)
{
case DTK_MICROSEC:
*fsec += ((val + fval) * 1e-6);
break;
case DTK_MILLISEC:
*fsec += ((val + fval) * 1e-3);
break;
case DTK_SECOND:
tm->tm_sec += val;
*fsec += fval;
tmask = DTK_M(SECOND);
break;
case DTK_MINUTE:
tm->tm_min += val;
if (fval != 0)
tm->tm_sec += (fval * 60);
tmask = DTK_M(MINUTE);
break;
case DTK_HOUR:
tm->tm_hour += val;
if (fval != 0)
tm->tm_sec += (fval * 3600);
tmask = DTK_M(HOUR);
break;
case DTK_DAY:
tm->tm_mday += val;
if (fval != 0)
tm->tm_sec += (fval * 86400);
tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
break;
case DTK_WEEK:
tm->tm_mday += val * 7;
if (fval != 0)
tm->tm_sec += (fval * (7 * 86400));
tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
break;
case DTK_MONTH:
tm->tm_mon += val;
if (fval != 0)
tm->tm_sec += (fval * (30 * 86400));
tmask = DTK_M(MONTH);
break;
case DTK_YEAR:
tm->tm_year += val;
if (fval != 0)
tm->tm_mon += (fval * 12);
tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
break;
case DTK_DECADE:
tm->tm_year += val * 10;
if (fval != 0)
tm->tm_mon += (fval * 120);
tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
break;
case DTK_CENTURY:
tm->tm_year += val * 100;
if (fval != 0)
tm->tm_mon += (fval * 1200);
tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
break;
case DTK_MILLENIUM:
tm->tm_year += val * 1000;
if (fval != 0)
tm->tm_mon += (fval * 12000);
tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
break;
default:
return -1;
}
break;
case DTK_STRING:
case DTK_SPECIAL:
type = DecodeUnits(i, field[i], &val);
if (type == IGNORE)
continue;
tmask = 0; /* DTK_M(type); */
switch (type)
{
case UNITS:
type = val;
break;
case AGO:
is_before = TRUE;
type = val;
break;
case RESERV:
tmask = (DTK_DATE_M || DTK_TIME_M);
*dtype = val;
break;
default:
return -1;
}
break;
default:
return -1;
}
if (tmask & fmask)
return -1;
fmask |= tmask;
}
if (*fsec != 0)
{
TMODULO(*fsec, sec, 1e0);
tm->tm_sec += sec;
}
if (is_before)
{
*fsec = -(*fsec);
tm->tm_sec = -(tm->tm_sec);
tm->tm_min = -(tm->tm_min);
tm->tm_hour = -(tm->tm_hour);
tm->tm_mday = -(tm->tm_mday);
tm->tm_mon = -(tm->tm_mon);
tm->tm_year = -(tm->tm_year);
}
/* ensure that at least one time field has been found */
return (fmask != 0) ? 0 : -1;
} /* DecodeDateDelta() */
/* DecodeUnits()
* Decode text string using lookup table.
* This routine supports time interval decoding.
*/
int
DecodeUnits(int field, char *lowtoken, int *val)
{
int type;
datetkn *tp;
#if USE_DATE_CACHE
if ((deltacache[field] != NULL)
&& (strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0))
tp = deltacache[field];
else
{
#endif
tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
#if USE_DATE_CACHE
}
deltacache[field] = tp;
#endif
if (tp == NULL)
{
type = IGNORE;
*val = 0;
}
else
{
type = tp->type;
if ((type == TZ) || (type == DTZ))
*val = FROMVAL(tp);
else
*val = tp->value;
}
return type;
} /* DecodeUnits() */
/* datebsearch()
* Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
* is WAY faster than the generic bsearch().
*/
datetkn *
datebsearch(char *key, datetkn *base, unsigned int nel)
{
datetkn *last = base + nel - 1,
*position;
int result;
while (last >= base)
{
position = base + ((last - base) >> 1);
result = key[0] - position->token[0];
if (result == 0)
{
result = strncmp(key, position->token, TOKMAXLEN);
if (result == 0)
return position;
}
if (result < 0)
last = position - 1;
else
base = position + 1;
}
return NULL;
}
/* EncodeDateOnly()
* Encode date as local time.
*/
int
EncodeDateOnly(struct tm * tm, int style, char *str)
{
if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
return -1;
switch (style)
{
/* compatible with ISO date formats */
case USE_ISO_DATES:
if (tm->tm_year > 0)
sprintf(str, "%04d-%02d-%02d",
tm->tm_year, tm->tm_mon, tm->tm_mday);
else
sprintf(str, "%04d-%02d-%02d %s",
-(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
break;
/* compatible with Oracle/Ingres date formats */
case USE_SQL_DATES:
if (EuroDates)
sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
else
sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
if (tm->tm_year > 0)
sprintf((str + 5), "/%04d", tm->tm_year);
else
sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC");
break;
/* German-style date format */
case USE_GERMAN_DATES:
sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
if (tm->tm_year > 0)
sprintf((str + 5), ".%04d", tm->tm_year);
else
sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC");
break;
/* traditional date-only style for Postgres */
case USE_POSTGRES_DATES:
default:
if (EuroDates)
sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
else
sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
if (tm->tm_year > 0)
sprintf((str + 5), "-%04d", tm->tm_year);
else
sprintf((str + 5), "-%04d %s", -(tm->tm_year - 1), "BC");
break;
}
return TRUE;
} /* EncodeDateOnly() */
/* EncodeTimeOnly()
* Encode time fields only.
*/
int
EncodeTimeOnly(struct tm * tm, double fsec, int style, char *str)
{
double sec;
if ((tm->tm_hour < 0) || (tm->tm_hour > 24))
return -1;
sec = (tm->tm_sec + fsec);
sprintf(str, "%02d:%02d:", tm->tm_hour, tm->tm_min);
sprintf((str + 6), ((fsec != 0) ? "%05.2f" : "%02.0f"), sec);
return TRUE;
} /* EncodeTimeOnly() */
/* EncodeDateTime()
* Encode date and time interpreted as local time.
* Support several date styles:
* Postgres - day mon hh:mm:ss yyyy tz
* SQL - mm/dd/yyyy hh:mm:ss.ss tz
* ISO - yyyy-mm-dd hh:mm:ss+/-tz
* German - dd.mm/yyyy hh:mm:ss tz
* Variants (affects order of month and day for Postgres and SQL styles):
* US - mm/dd/yyyy
* European - dd/mm/yyyy
*/
int
EncodeDateTime(struct tm * tm, double fsec, int *tzp, char **tzn, int style, char *str)
{
int day,
hour,
min;
double sec;
if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
return -1;
sec = (tm->tm_sec + fsec);
switch (style)
{
/* compatible with ISO date formats */
case USE_ISO_DATES:
if (tm->tm_year > 0)
{
sprintf(str, "%04d-%02d-%02d %02d:%02d:",
tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
sprintf((str + 17), ((fsec != 0) ? "%05.2f" : "%02.0f"), sec);
if ((*tzn != NULL) && (tm->tm_isdst >= 0))
{
if (tzp != NULL)
{
hour = -(*tzp / 3600);
min = ((abs(*tzp) / 60) % 60);
}
else
{
hour = 0;
min = 0;
}
sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
}
}
else
{
if (tm->tm_hour || tm->tm_min)
sprintf(str, "%04d-%02d-%02d %02d:%02d %s",
-(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, "BC");
else
sprintf(str, "%04d-%02d-%02d %s",
-(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
}
break;
/* compatible with Oracle/Ingres date formats */
case USE_SQL_DATES:
if (EuroDates)
sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
else
sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
if (tm->tm_year > 0)
{
sprintf((str + 5), "/%04d %02d:%02d:%05.2f",
tm->tm_year, tm->tm_hour, tm->tm_min, sec);
if ((*tzn != NULL) && (tm->tm_isdst >= 0))
{
strcpy((str + 22), " ");
strcpy((str + 23), *tzn);
}
}
else
sprintf((str + 5), "/%04d %02d:%02d %s",
-(tm->tm_year - 1), tm->tm_hour, tm->tm_min, "BC");
break;
/* German variant on European style */
case USE_GERMAN_DATES:
sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
if (tm->tm_year > 0)
{
sprintf((str + 5), ".%04d %02d:%02d:%05.2f",
tm->tm_year, tm->tm_hour, tm->tm_min, sec);
if ((*tzn != NULL) && (tm->tm_isdst >= 0))
{
strcpy((str + 22), " ");
strcpy((str + 23), *tzn);
}
}
else
sprintf((str + 5), ".%04d %02d:%02d %s",
-(tm->tm_year - 1), tm->tm_hour, tm->tm_min, "BC");
break;
/* backward-compatible with traditional Postgres abstime dates */
case USE_POSTGRES_DATES:
default:
day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
tm->tm_wday = j2day(day);
strncpy(str, days[tm->tm_wday], 3);
strcpy((str + 3), " ");
if (EuroDates)
sprintf((str + 4), "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
else
sprintf((str + 4), "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
if (tm->tm_year > 0)
{
sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min);
if (fsec != 0)
{
sprintf((str + 16), ":%05.2f %04d", sec, tm->tm_year);
if ((*tzn != NULL) && (tm->tm_isdst >= 0))
{
strcpy((str + 27), " ");
strncpy((str + 28), *tzn, MAXTZLEN);
}
}
else
{
sprintf((str + 16), ":%02.0f %04d", sec, tm->tm_year);
if ((*tzn != NULL) && (tm->tm_isdst >= 0))
{
strcpy((str + 24), " ");
strncpy((str + 25), *tzn, MAXTZLEN);
}
}
}
else else
{ {
result = date_datetime(date); sprintf((str + 10), " %02d:%02d %04d %s",
*result += *time; tm->tm_hour, tm->tm_min, -(tm->tm_year - 1), "BC");
}
break;
}
return TRUE;
} /* EncodeDateTime() */
/* EncodeTimeSpan()
* Interpret time structure as a delta time and convert to string.
*
* Support "traditional Postgres" and ISO-8601 styles.
* Actually, afaik ISO does not address time interval formatting,
* but this looks similar to the spec for absolute date/time.
* - thomas 1998-04-30
*/
int
EncodeTimeSpan(struct tm * tm, double fsec, int style, char *str)
{
int is_before = FALSE;
int is_nonzero = FALSE;
char *cp = str;
switch (style)
{
/* compatible with ISO date formats */
case USE_ISO_DATES:
break;
default:
strcpy(cp, "@ ");
cp += strlen(cp);
break;
}
if (tm->tm_year != 0)
{
is_before |= (tm->tm_year < 0);
sprintf(cp, "%d year%s",
abs(tm->tm_year), ((abs(tm->tm_year) != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
if (tm->tm_mon != 0)
{
is_before |= (tm->tm_mon < 0);
sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""),
abs(tm->tm_mon), ((abs(tm->tm_mon) != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
switch (style)
{
/* compatible with ISO date formats */
case USE_ISO_DATES:
if (tm->tm_mday != 0)
{
is_before |= (tm->tm_mday < 0);
sprintf(cp, "%s%d", (is_nonzero ? " " : ""), abs(tm->tm_mday));
cp += strlen(cp);
is_nonzero = TRUE;
}
is_before |= ((tm->tm_hour < 0) || (tm->tm_min < 0));
sprintf(cp, "%s%02d:%02d", (is_nonzero ? " " : ""),
abs(tm->tm_hour), abs(tm->tm_min));
cp += strlen(cp);
if ((tm->tm_hour != 0) || (tm->tm_min != 0))
is_nonzero = TRUE;
/* fractional seconds? */
if (fsec != 0)
{
fsec += tm->tm_sec;
is_before |= (fsec < 0);
sprintf(cp, ":%05.2f", fabs(fsec));
cp += strlen(cp);
is_nonzero = TRUE;
/* otherwise, integer seconds only? */
}
else if (tm->tm_sec != 0)
{
is_before |= (tm->tm_sec < 0);
sprintf(cp, ":%02d", abs(tm->tm_sec));
cp += strlen(cp);
is_nonzero = TRUE;
}
break;
case USE_POSTGRES_DATES:
default:
if (tm->tm_mday != 0)
{
is_before |= (tm->tm_mday < 0);
sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""),
abs(tm->tm_mday), ((abs(tm->tm_mday) != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
if (tm->tm_hour != 0)
{
is_before |= (tm->tm_hour < 0);
sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""),
abs(tm->tm_hour), ((abs(tm->tm_hour) != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
if (tm->tm_min != 0)
{
is_before |= (tm->tm_min < 0);
sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""),
abs(tm->tm_min), ((abs(tm->tm_min) != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
/* fractional seconds? */
if (fsec != 0)
{
fsec += tm->tm_sec;
is_before |= (fsec < 0);
sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), fabs(fsec));
cp += strlen(cp);
is_nonzero = TRUE;
/* otherwise, integer seconds only? */
}
else if (tm->tm_sec != 0)
{
is_before |= (tm->tm_sec < 0);
sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""),
abs(tm->tm_sec), ((abs(tm->tm_sec) != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
break;
}
/* identically zero? then put in a unitless zero... */
if (!is_nonzero)
{
strcat(cp, "0");
cp += strlen(cp);
}
if (is_before)
{
strcat(cp, " ago");
cp += strlen(cp);
} }
return result; return 0;
} /* datetime_datetime() */ } /* EncodeTimeSpan() */
#if defined(linux) && defined(__powerpc__)
int
timestamp_is_epoch(double j)
{
static union
{
double epoch;
unsigned char c[8];
} u;
u.c[0] = 0x80; /* sign bit */
u.c[1] = 0x10; /* DBL_MIN */
int32 /* RelativeTime */ return j == u.epoch;
int4reltime(int32 timevalue) }
int
timestamp_is_current(double j)
{ {
return timevalue; static union
{
double current;
unsigned char c[8];
} u;
u.c[1] = 0x10; /* DBL_MIN */
return j == u.current;
} }
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment