From bf566b202ec879d54e411627fb96d5e620d3c246 Mon Sep 17 00:00:00 2001 From: "Thomas G. Lockhart" Date: Wed, 16 Feb 2000 18:17:02 +0000 Subject: [PATCH] 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. --- src/backend/utils/adt/date.c | 1574 +++++-------------- src/backend/utils/adt/datetime.c | 2487 ++++++++++++++++++++++++------ 2 files changed, 2468 insertions(+), 1593 deletions(-) diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 4ca3850f79..7102f64a44 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -1,1303 +1,559 @@ /*------------------------------------------------------------------------- * * date.c - * Utilities for the built-in type "AbsoluteTime" (defined in nabstime). - * Functions for the built-in type "RelativeTime". - * Functions for the built-in type "TimeInterval". + * implements DATE and TIME data types specified in SQL-92 standard * * 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 - * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.41 2000/01/26 05:57:13 momjian 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 + * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.42 2000/02/16 18:17:02 thomas Exp $ * *------------------------------------------------------------------------- */ -#include -#include -#include -#include +#include #include "postgres.h" #ifdef HAVE_FLOAT_H #include #endif -#ifdef HAVE_LIMITS_H -#include -#ifndef MAXINT -#define MAXINT INT_MAX -#endif -#else -#ifdef HAVE_VALUES_H -#include -#endif -#endif - -#include "access/xact.h" #include "miscadmin.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) +static int +date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn); -/* - * 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 correct_unit(char *unit, int *unptr); -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 - */ -RelativeTime -reltimein(char *str) -{ - RelativeTime result; +/* 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; - double fsec; + int tzp; int dtype; + int nf; char *field[MAXDATEFIELDS]; - int nf, - ftype[MAXDATEFIELDS]; + int ftype[MAXDATEFIELDS]; char lowstr[MAXDATELEN + 1]; if (!PointerIsValid(str)) 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) - || (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) - elog(ERROR, "Bad reltime external representation '%s'", str); + || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0)) + elog(ERROR, "Bad date external representation '%s'", str); switch (dtype) { - case DTK_DELTA: - result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec); - result += (((tm->tm_year * 365) + (tm->tm_mon * 30) + tm->tm_mday) * (24 * 60 * 60)); - return result; + case DTK_DATE: + break; + + case DTK_CURRENT: + GetCurrentTime(tm); + break; + + case DTK_EPOCH: + tm->tm_year = 1970; + tm->tm_mon = 1; + tm->tm_mday = 1; + break; default: - return INVALID_RELTIME; + elog(ERROR, "Unrecognized date external representation '%s'", str); } - elog(ERROR, "Bad reltime (internal coding error) '%s'", str); - return INVALID_RELTIME; -} /* reltimein() */ + date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1)); + return date; +} /* date_in() */ -/* - * reltimeout - converts the internal format to a reltime string +/* date_out() + * Given internal format date, convert to text string. */ char * -reltimeout(RelativeTime time) +date_out(DateADT date) { char *result; struct tm tt, *tm = &tt; char buf[MAXDATELEN + 1]; - if (time == INVALID_RELTIME) - { - strcpy(buf, INVALID_RELTIME_STR); + j2date((date + date2j(2000, 1, 1)), + &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); - } - else - { - reltime2tm(time, tm); - EncodeTimeSpan(tm, 0, DateStyle, buf); - } + EncodeDateOnly(tm, DateStyle, buf); result = palloc(strlen(buf) + 1); + strcpy(result, buf); return result; -} /* reltimeout() */ +} /* date_out() */ - -#define TMODULO(t,q,u) \ -do { \ - q = (t / u); \ - if (q != 0) t -= (q * u); \ -} while(0) - -static void -reltime2tm(RelativeTime time, struct tm * tm) +bool +date_eq(DateADT dateVal1, DateADT dateVal2) { - TMODULO(time, tm->tm_year, 31536000); - TMODULO(time, tm->tm_mon, 2592000); - 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 dateVal1 == dateVal2; +} - return; -} /* reltime2tm() */ +bool +date_ne(DateADT dateVal1, DateADT dateVal2) +{ + return dateVal1 != dateVal2; +} + +bool +date_lt(DateADT dateVal1, DateADT dateVal2) +{ + return dateVal1 < dateVal2; +} /* date_lt() */ + +bool +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 -dummyfunc() +date_cmp(DateADT dateVal1, DateADT dateVal2) { - char *timestring; - long quantity; - int i; - int unitnr; + if (dateVal1 < dateVal2) + return -1; + else if (dateVal1 > dateVal2) + return 1; + return 0; +} /* date_cmp() */ - timestring = (char *) palloc(Max(strlen(INVALID_RELTIME_STR), - UNITMAXLEN) + 1); - if (timevalue == INVALID_RELTIME) - { - strcpy(timestring, INVALID_RELTIME_STR); - return timestring; - } +DateADT +date_larger(DateADT dateVal1, DateADT dateVal2) +{ + return date_gt(dateVal1, dateVal2) ? dateVal1 : dateVal2; +} /* date_larger() */ - if (timevalue == 0) - i = 1; /* unit = 'seconds' */ - else - for (i = 12; i >= 0; i = i - 2) - if ((timevalue % sec_tab[i]) == 0) - break; /* appropriate unit found */ - unitnr = i; - quantity = (timevalue / sec_tab[unitnr]); - if (quantity > 1 || quantity < -1) - unitnr++; /* adjust index for PLURAL of unit */ - 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; -} +DateADT +date_smaller(DateADT dateVal1, DateADT dateVal2) +{ + return date_lt(dateVal1, dateVal2) ? dateVal1 : dateVal2; +} /* date_smaller() */ -#endif - - -/* - * tintervalin - converts an interval string to an internal format +/* Compute difference between two dates in days. */ -TimeInterval -tintervalin(char *intervalstr) +int4 +date_mi(DateADT dateVal1, DateADT dateVal2) { - int error; - AbsoluteTime i_start, - i_end, - t1, - t2; - TimeInterval interval; + return dateVal1 - dateVal2; +} /* date_mi() */ - 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; -} - - -/* - * tintervalout - converts an internal interval format to a string - * +/* Add a number of days to a date, giving a new date. + * Must handle both positive and negative numbers of days. */ -char * -tintervalout(TimeInterval interval) +DateADT +date_pli(DateADT dateVal, int4 days) { - char *i_str, - *p; + return dateVal + days; +} /* date_pli() */ - i_str = (char *) palloc(T_INTERVAL_LEN); /* ['...' '...'] */ - strcpy(i_str, "[\""); - if (interval->status == T_INTERVAL_INVAL) - strcat(i_str, INVALID_INTERVAL_STR); - else +/* 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() */ + + +/* date_timestamp() + * Convert date to timestamp data type. + */ +Timestamp * +date_timestamp(DateADT dateVal) +{ + Timestamp *result; + struct tm tt, + *tm = &tt; + int tz; + double fsec = 0; + char *tzn; + + result = palloc(sizeof(Timestamp)); + + if (date2tm(dateVal, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to convert date to timestamp"); + + if (tm2timestamp(tm, fsec, &tz, result) != 0) + elog(ERROR, "Timestamp out of range"); + + return result; +} /* date_timestamp() */ + + +/* timestamp_date() + * Convert timestamp to date data type. + */ +DateADT +timestamp_date(Timestamp *timestamp) +{ + DateADT result; + struct tm tt, + *tm = &tt; + int tz; + double fsec; + char *tzn; + + if (!PointerIsValid(timestamp)) + elog(ERROR, "Unable to convert null timestamp to date"); + + if (TIMESTAMP_NOT_FINITE(*timestamp)) + elog(ERROR, "Unable to convert timestamp to date"); + + if (TIMESTAMP_IS_EPOCH(*timestamp)) { - 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); + timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL); + } - strcat(i_str, "\"]\0"); - return i_str; -} - - -/***************************************************************************** - * PUBLIC ROUTINES * - *****************************************************************************/ - -RelativeTime -timespan_reltime(TimeSpan *timespan) -{ - RelativeTime time; - int year, - month; - double span; - - if (!PointerIsValid(timespan)) - time = INVALID_RELTIME; - - if (TIMESPAN_IS_INVALID(*timespan)) + else if (TIMESTAMP_IS_CURRENT(*timestamp)) { - time = INVALID_RELTIME; + timestamp2tm(SetTimestamp(*timestamp), &tz, tm, &fsec, &tzn); } else { - if (timespan->month == 0) - { - year = 0; - month = 0; - - } - else if (abs(timespan->month) >= 12) - { - year = (timespan->month / 12); - month = (timespan->month % 12); - - } - else - { - year = 0; - month = timespan->month; - } - - span = (((((double) 365 * year) + ((double) 30 * month)) * 86400) + timespan->time); - - time = (((span > INT_MIN) && (span < INT_MAX)) ? span : INVALID_RELTIME); + if (timestamp2tm(*timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to convert timestamp to date"); } - return time; -} /* timespan_reltime() */ + result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1)); + + return result; +} /* timestamp_date() */ -TimeSpan * -reltime_timespan(RelativeTime reltime) +/* abstime_date() + * Convert abstime to date data type. + */ +DateADT +abstime_date(AbsoluteTime abstime) { - TimeSpan *result; - int year, - month; + DateADT result; + struct tm tt, + *tm = &tt; + int tz; - if (!PointerIsValid(result = palloc(sizeof(TimeSpan)))) - elog(ERROR, "Memory allocation failed, can't convert reltime to timespan"); - - switch (reltime) + switch (abstime) { - case INVALID_RELTIME: - TIMESPAN_INVALID(*result); + case INVALID_ABSTIME: + case NOSTART_ABSTIME: + case NOEND_ABSTIME: + elog(ERROR, "Unable to convert reserved abstime value to date"); + + /* + * pretend to drop through to make compiler think that result + * will be set + */ + + case EPOCH_ABSTIME: + result = date2j(1970, 1, 1) - date2j(2000, 1, 1); + break; + + case CURRENT_ABSTIME: + GetCurrentTime(tm); + result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); break; default: - TMODULO(reltime, year, 31536000); - TMODULO(reltime, month, 2592000); - - result->time = reltime; - result->month = ((12 * year) + month); + abstime2tm(abstime, &tz, tm, NULL); + result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); + break; } return result; -} /* reltime_timespan() */ +} /* abstime_date() */ -/* - * mktinterval - creates a time interval with endpoints t1 and t2 +/* date2tm() + * 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. */ -TimeInterval -mktinterval(AbsoluteTime t1, AbsoluteTime t2) +static int +date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn) { - AbsoluteTime tstart = ABSTIMEMIN(t1, t2), - tend = ABSTIMEMAX(t1, t2); - TimeInterval interval; + struct tm *tx; + time_t utime; - interval = (TimeInterval) palloc(sizeof(TimeIntervalData)); - if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) - interval->status = T_INTERVAL_INVAL; + *fsec = 0; + + 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 */ + utime = ((dateVal + (date2j(2000, 1, 1) - date2j(1970, 1, 1))) * 86400); + /* rotate to noon to get the right day in time zone */ + utime += (12 * 60 * 60); + +#ifdef USE_POSIX_TIME + tx = localtime(&utime); + + tm->tm_year = tx->tm_year + 1900; + tm->tm_mon = tx->tm_mon + 1; + tm->tm_mday = tx->tm_mday; + tm->tm_isdst = tx->tm_isdst; + +#if defined(HAVE_TM_ZONE) + tm->tm_gmtoff = tx->tm_gmtoff; + tm->tm_zone = tx->tm_zone; + + /* tm_gmtoff is Sun/DEC-ism */ + *tzp = -(tm->tm_gmtoff); + if (tzn != NULL) + *tzn = (char *) tm->tm_zone; +#elif defined(HAVE_INT_TIMEZONE) +#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 + + /* otherwise, outside of timezone range so convert to GMT... */ + } else { - interval->status = T_INTERVAL_VALID; - interval->data[0] = tstart; - interval->data[1] = tend; + *tzp = 0; + tm->tm_isdst = 0; + if (tzn != NULL) + *tzn = NULL; } - return interval; -} - -/* - * 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; -} - - -/* - * timemi - returns the value of (abstime t1 - reltime t2) - */ -AbsoluteTime -timemi(AbsoluteTime t1, RelativeTime t2) -{ - if (t1 == CURRENT_ABSTIME) - t1 = GetCurrentTransactionStartTime(); - - if (AbsoluteTimeIsReal(t1) && - RelativeTimeIsValid(t2) && - ((t2 > 0) ? (t1 > NOSTART_ABSTIME + t2) - : (t1 < NOEND_ABSTIME + t2))) /* prevent overflow */ - return t1 - t2; - - return INVALID_ABSTIME; -} - - -/* - * abstimemi - returns the value of (abstime t1 - abstime t2) - */ -static RelativeTime -abstimemi(AbsoluteTime t1, AbsoluteTime t2) -{ - if (t1 == CURRENT_ABSTIME) - t1 = GetCurrentTransactionStartTime(); - if (t2 == CURRENT_ABSTIME) - t2 = GetCurrentTransactionStartTime(); - - if (AbsoluteTimeIsReal(t1) && - AbsoluteTimeIsReal(t2)) - return t1 - t2; - - return INVALID_RELTIME; -} - - -/* - * ininterval - returns 1, iff absolute date is in the interval - */ -int -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; -} - -/* - * timenow - returns time "now", internal format - * - * Now AbsoluteTime is time since Jan 1 1970 -mer 7 Feb 1992 - */ -AbsoluteTime -timenow() -{ - time_t sec; - - if (time(&sec) < 0) - return INVALID_ABSTIME; - return (AbsoluteTime) sec; -} - -/* - * reltimeeq - returns 1, iff arguments are equal - * reltimene - returns 1, iff arguments are not equal - * reltimelt - returns 1, iff t1 less than t2 - * reltimegt - returns 1, iff t1 greater than t2 - * reltimele - returns 1, iff t1 less than or equal to t2 - * reltimege - returns 1, iff t1 greater than or equal to t2 - */ -bool -reltimeeq(RelativeTime t1, RelativeTime t2) -{ - if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) - return 0; - return t1 == t2; -} - -bool -reltimene(RelativeTime t1, RelativeTime t2) -{ - if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) - return 0; - return t1 != t2; -} - -bool -reltimelt(RelativeTime t1, RelativeTime t2) -{ - if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) - return 0; - return t1 < t2; -} - -bool -reltimegt(RelativeTime t1, RelativeTime t2) -{ - if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) - return 0; - return t1 > t2; -} - -bool -reltimele(RelativeTime t1, RelativeTime t2) -{ - if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) - return 0; - return t1 <= t2; -} - -bool -reltimege(RelativeTime t1, RelativeTime t2) -{ - if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) - return 0; - return t1 >= t2; -} - - -/* - * intervalsame - returns 1, iff interval i1 is same as interval i2 - * Check begin and end time. - */ -bool -intervalsame(TimeInterval i1, TimeInterval i2) -{ - if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) - return FALSE; /* invalid interval */ - return (abstimeeq(i1->data[0], i2->data[0]) && - abstimeeq(i1->data[1], i2->data[1])); -} /* intervalsame() */ - - -/* - * intervaleq - returns 1, iff interval i1 is equal to interval i2 - * Check length of intervals. - */ -bool -intervaleq(TimeInterval i1, TimeInterval i2) -{ - AbsoluteTime t10, - 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; - - 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); -} /* 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) - 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; - - 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); -} /* intervalne() */ - -/* - * intervallt - returns TRUE, iff interval i1 is less than interval i2 - * Check length of intervals. - */ -bool -intervallt(TimeInterval i1, TimeInterval i2) -{ - AbsoluteTime t10, - 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; - - 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); -} /* 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) - 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; - - 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); -} /* 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) - 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; - - 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 -intervalge(TimeInterval i1, TimeInterval i2) -{ - AbsoluteTime t10, - 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; - - 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); -} /* 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 -intervallenlt(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; -} - -/* - * intervallengt - returns 1, iff length of interval i is greater than - * reltime t - */ -bool -intervallengt(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; -} - -/* - * intervallenle - returns 1, iff length of interval i is less or equal - * than reltime t - */ -bool -intervallenle(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; -} - -/* - * intervallenge - returns 1, iff length of interval i is greater or - * equal than reltime t - */ -bool -intervallenge(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; -} - -/* - * intervalct - returns 1, iff interval i1 contains interval i2 - */ -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 -intervalov(TimeInterval i1, TimeInterval i2) -{ - if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) - return 0; - return (!(abstimelt(i1->data[1], i2->data[0]) || - abstimegt(i1->data[0], i2->data[1]))); -} - -/* - * intervalstart - returns the start of interval i - */ -AbsoluteTime -intervalstart(TimeInterval i) -{ - if (i->status == T_INTERVAL_INVAL) - return INVALID_ABSTIME; - return i->data[0]; -} - -/* - * intervalend - returns the end of interval i - */ -AbsoluteTime -intervalend(TimeInterval i) -{ - if (i->status == T_INTERVAL_INVAL) - return INVALID_ABSTIME; - return i->data[1]; -} +} /* date2tm() */ /***************************************************************************** - * PRIVATE ROUTINES * + * Time ADT *****************************************************************************/ -#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 -isreltime(char *str) + +TimeADT * +time_in(char *str) { + TimeADT *time; + + double fsec; struct tm tt, *tm = &tt; - double fsec; - int dtype; - char *field[MAXDATEFIELDS]; - int nf, - ftype[MAXDATEFIELDS]; + + int nf; char lowstr[MAXDATELEN + 1]; + char *field[MAXDATEFIELDS]; + int dtype; + int ftype[MAXDATEFIELDS]; if (!PointerIsValid(str)) - return 0; - - if (strlen(str) > MAXDATELEN) - return 0; + elog(ERROR, "Bad (null) time external representation"); if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) - || (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) - return 0; + || (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec) != 0)) + elog(ERROR, "Bad time external representation '%s'", str); - switch (dtype) - { - case (DTK_DELTA): - return (abs(tm->tm_year) <= 68) ? 1 : 0; - break; + time = palloc(sizeof(TimeADT)); - case (DTK_INVALID): - return 2; - break; + *time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec); - default: - return 0; - break; - } + return time; +} /* time_in() */ - return 0; -} /* isreltime() */ -#endif +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); + + strcpy(result, buf); + + return result; +} /* time_out() */ + + +bool +time_eq(TimeADT *time1, TimeADT *time2) +{ + if (!PointerIsValid(time1) || !PointerIsValid(time2)) + return FALSE; + + return *time1 == *time2; +} /* time_eq() */ + +bool +time_ne(TimeADT *time1, TimeADT *time2) +{ + if (!PointerIsValid(time1) || !PointerIsValid(time2)) + return FALSE; + + return *time1 != *time2; +} /* time_eq() */ + +bool +time_lt(TimeADT *time1, TimeADT *time2) +{ + if (!PointerIsValid(time1) || !PointerIsValid(time2)) + return FALSE; + + return *time1 < *time2; +} /* time_eq() */ + +bool +time_le(TimeADT *time1, TimeADT *time2) +{ + if (!PointerIsValid(time1) || !PointerIsValid(time2)) + return FALSE; + + return *time1 <= *time2; +} /* time_eq() */ + +bool +time_gt(TimeADT *time1, TimeADT *time2) +{ + if (!PointerIsValid(time1) || !PointerIsValid(time2)) + return FALSE; + + return *time1 > *time2; +} /* time_eq() */ + +bool +time_ge(TimeADT *time1, TimeADT *time2) +{ + if (!PointerIsValid(time1) || !PointerIsValid(time2)) + return FALSE; + + return *time1 >= *time2; +} /* time_eq() */ -#ifdef NOT_USED int -dummyfunc() +time_cmp(TimeADT *time1, TimeADT *time2) { - char *p; - char c; - int i; - char unit[UNITMAXLEN]; - char direction[DIRMAXLEN]; - int localSign; - int localUnitNumber; - long localQuantity; + return (*time1 < *time2) ? -1 : (((*time1 > *time2) ? 1 : 0)); +} /* time_cmp() */ - 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 - { - if ((c == ' ' || c == '\0') - && correct_unit(unit, unitnr)) - break; /* correct unit found */ - else - return 0; /* syntax error */ - } - } - - /* handle optional direction */ - if (c == ' ') - p++; - i = 0; - *sign = 1; - for (;;) - { - c = *p; - if (c >= 'a' && c <= 'z' && i <= (DIRMAXLEN - 1)) - { - direction[i] = c; - p++; - i++; - } - else - { - if ((c == ' ' || c == '\0') && i == 0) - { - *sign = 1; - break; /* no direction specified */ - } - if ((c == ' ' || c == '\0') && i != 0) - { - direction[i] = '\0'; - correct_dir(direction, sign); - break; /* correct direction found */ - } - else - return 0; /* syntax error */ - } - } - - return 1; -} - -/* - * correct_unit - returns 1, iff unit is a correct unit description - * - * output parameter: - * unptr: points to an integer which is the appropriate unit number - * (see function isreltime()) +/* timestamp_time() + * Convert timestamp to time data type. */ -static int -correct_unit(char *unit, int *unptr) +TimeADT * +timestamp_time(Timestamp *timestamp) { - int j = 0; + TimeADT *result; + struct tm tt, + *tm = &tt; + int tz; + double fsec; + char *tzn; - while (j < NUNITS) + if (!PointerIsValid(timestamp)) + elog(ERROR, "Unable to convert null timestamp to date"); + + if (TIMESTAMP_NOT_FINITE(*timestamp)) + elog(ERROR, "Unable to convert timestamp to date"); + + if (TIMESTAMP_IS_EPOCH(*timestamp)) { - if (strncmp(unit, unit_tab[j], strlen(unit_tab[j])) == 0) - { - *unptr = j; - return 1; - } - j++; + timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL); + } - return 0; /* invalid unit descriptor */ -} - -/* - * correct_dir - returns 1, iff direction is a correct identifier - * - * output parameter: - * signptr: points to -1 if dir corresponds to past tense - * else to 1 - */ -static int -correct_dir(char *direction, int *signptr) -{ - *signptr = 1; - if (strncmp(RELTIME_PAST, direction, strlen(RELTIME_PAST) + 1) == 0) + else if (TIMESTAMP_IS_CURRENT(*timestamp)) { - *signptr = -1; - return 1; + timestamp2tm(SetTimestamp(*timestamp), &tz, tm, &fsec, &tzn); + } else - return 0; /* invalid direction descriptor */ -} + { + if (timestamp2tm(*timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to convert timestamp to date"); + } -#endif + result = palloc(sizeof(TimeADT)); -/* - * istinterval - returns 1, iff i_string is a valid interval descr. - * 0, iff i_string is NOT a valid interval desc. - * 2, iff any time is INVALID_ABSTIME - * - * output parameter: - * i_start, i_end: interval margins - * - * Time interval: - * `[' {` '} `'' `'' {` '} `'' `'' {` '} `]' - * - * OR `Undefined Range' (see also INVALID_INTERVAL_STR) - * - * where satisfies the syntax of absolute time. - * - * e.g. [ ' Jan 18 1902' 'Jan 1 00:00:00 1970'] + *result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec); + + return result; +} /* timestamp_time() */ + + +/* datetime_timestamp() + * Convert date and time to timestamp data type. */ -static int -istinterval(char *i_string, - AbsoluteTime *i_start, - AbsoluteTime *i_end) +Timestamp * +datetime_timestamp(DateADT date, TimeADT *time) { - char *p, - *p1; - char c; + Timestamp *result; - p = i_string; - /* skip leading blanks up to '[' */ - while ((c = *p) != '\0') + if (!PointerIsValid(time)) { - if (IsSpace(c)) - p++; - else if (c != '[') - return 0; /* syntax error */ - else - break; + result = palloc(sizeof(Timestamp)); + TIMESTAMP_INVALID(*result); } - p++; - /* skip leading blanks up to "'" */ - while ((c = *p) != '\0') + else { - if (IsSpace(c)) - p++; - else if (c != '"') - return 0; /* syntax error */ - else - break; + result = date_timestamp(date); + *result += *time; } - 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 == '"') - { - *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 - 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'; - break; - } - p1++; - } - /* get the second date */ - *i_end = nabstimein(p); /* second absolute date */ - /* rechange NULL at the end of the first date to a ''' */ - *p1 = '"'; - p = ++p1; - /* skip blanks up to ']' */ - while ((c = *p) != '\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; - struct timezone tpz; - 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; -} + return result; +} /* datetime_timestamp() */ diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 7a9df275fa..1418a0c34c 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -1,565 +1,2184 @@ /*------------------------------------------------------------------------- * * 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) 1994-5, Regents of the University of California + * Portions Copyright (c) 1994, Regents of the University of California * * * 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 +#include +#include +#include +#include #include "postgres.h" #ifdef HAVE_FLOAT_H #include #endif -#include "miscadmin.h" -#include "utils/builtins.h" +#ifdef HAVE_LIMITS_H +#include +#endif +#ifndef USE_POSIX_TIME +#include +#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 +#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}}; + + +char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", +"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}; + +char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", +"Thursday", "Friday", "Saturday", NULL}; + + +#if 0 + + +static void GetEpochTime(struct tm * tm); + + +#endif + + +#define UTIME_MINYEAR (1901) +#define UTIME_MINMONTH (12) +#define UTIME_MINDAY (14) +#define UTIME_MAXYEAR (2038) +#define UTIME_MAXMONTH (01) +#define UTIME_MAXDAY (18) + +#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)))))) /***************************************************************************** - * Date ADT + * PRIVATE ROUTINES * *****************************************************************************/ +/* definitions for squeezing values into "value" */ +#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)) -/* date_in() - * Given date text string, convert to internal date format. +/* + * 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. */ -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]; +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 */ +}; - if (!PointerIsValid(str)) - elog(ERROR, "Bad (null) date external representation"); +static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0]; - if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) - || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0)) - elog(ERROR, "Bad date external representation '%s'", str); +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 */ +}; - switch (dtype) - { - case DTK_DATE: - break; +static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0]; - case DTK_CURRENT: - GetCurrentTime(tm); - break; +#if USE_DATE_CACHE +datetkn *datecache[MAXDATEFIELDS] = {NULL}; - case DTK_EPOCH: - tm->tm_year = 1970; - tm->tm_mon = 1; - tm->tm_mday = 1; - break; +datetkn *deltacache[MAXDATEFIELDS] = {NULL}; - default: - elog(ERROR, "Unrecognized date external representation '%s'", str); - } +#endif - date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1)); - return date; -} /* date_in() */ - -/* date_out() - * Given internal format date, convert to text string. +/* + * Calendar time to Julian date conversions. + * Julian date is commonly used in astronomical applications, + * since it is numerically accurate and computationally simple. + * 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 */ -char * -date_out(DateADT date) -{ - char *result; - struct tm tt, - *tm = &tt; - char buf[MAXDATELEN + 1]; - - j2date((date + date2j(2000, 1, 1)), - &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); - - EncodeDateOnly(tm, DateStyle, buf); - - result = palloc(strlen(buf) + 1); - - strcpy(result, buf); - - return result; -} /* date_out() */ - -bool -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) -{ - return dateVal1 < dateVal2; -} /* date_lt() */ - -bool -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() */ int -date_cmp(DateADT dateVal1, DateADT dateVal2) +date2j(int y, int m, int d) { - if (dateVal1 < dateVal2) - return -1; - else if (dateVal1 > dateVal2) - return 1; - return 0; -} /* date_cmp() */ + int m12 = (m - 14) / 12; -DateADT -date_larger(DateADT dateVal1, DateADT dateVal2) + return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12 + - (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075); +} /* date2j() */ + +void +j2date(int jd, int *year, int *month, int *day) { - return date_gt(dateVal1, dateVal2) ? dateVal1 : dateVal2; -} /* date_larger() */ + int j, + y, + m, + d; -DateADT -date_smaller(DateADT dateVal1, DateADT dateVal2) + 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 +j2day(int date) { - return date_lt(dateVal1, dateVal2) ? dateVal1 : dateVal2; -} /* date_smaller() */ + int day; -/* Compute difference between two dates in days. + day = (date + 1) % 7; + + return day; +} /* j2day() */ + + +#if 0 + + +static double +time2t(const int hour, const int min, const double sec) +{ + return (((hour * 60) + min) * 60) + sec; +} /* time2t() */ + +static void +dt2time(Timestamp jd, int *hour, int *min, double *sec) +{ + double time; + + time = jd; + + *hour = (time / 3600); + time -= ((*hour) * 3600); + *min = (time / 60); + time -= ((*min) * 60); + *sec = JROUND(time); + + return; +} /* dt2time() */ + + +#endif + + +/* + * parse and convert date in timestr (the normal interface) + * + * Returns the number of seconds since epoch (J2000) */ -int4 -date_mi(DateADT dateVal1, DateADT dateVal2) -{ - return dateVal1 - dateVal2; -} /* date_mi() */ -/* Add a number of days to a date, giving a new date. - * Must handle both positive and negative numbers of days. +/* ParseDateTime() + * Break string into tokens based on a date/time context. */ -DateADT -date_pli(DateADT dateVal, int4 days) +int +ParseDateTime(char *timestr, char *lowstr, + char **field, int *ftype, int maxfields, int *numfields) { - return dateVal + days; -} /* date_pli() */ + int nf = 0; + char *cp = timestr; + char *lp = lowstr; -/* 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() */ - - -/* date_datetime() - * Convert date to datetime data type. - */ -DateTime * -date_datetime(DateADT dateVal) -{ - DateTime *result; - struct tm tt, - *tm = &tt; - int tz; - double fsec = 0; - char *tzn; - - result = palloc(sizeof(DateTime)); - - if (date2tm(dateVal, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to convert date to datetime"); - - if (tm2datetime(tm, fsec, &tz, result) != 0) - elog(ERROR, "Datetime out of range"); - - return result; -} /* date_datetime() */ - - -/* datetime_date() - * Convert datetime to date data type. - */ -DateADT -datetime_date(DateTime *datetime) -{ - DateADT result; - struct tm tt, - *tm = &tt; - int tz; - double fsec; - char *tzn; - - if (!PointerIsValid(datetime)) - elog(ERROR, "Unable to convert null datetime to date"); - - if (DATETIME_NOT_FINITE(*datetime)) - elog(ERROR, "Unable to convert datetime to date"); - - if (DATETIME_IS_EPOCH(*datetime)) + /* outer loop through fields */ + while (*cp != '\0') { - datetime2tm(SetDateTime(*datetime), NULL, tm, &fsec, NULL); + field[nf] = lp; - } - else if (DATETIME_IS_CURRENT(*datetime)) - { - datetime2tm(SetDateTime(*datetime), &tz, tm, &fsec, &tzn); + /* leading digit? then date or time */ + if (isdigit(*cp) || (*cp == '.')) + { + *lp++ = *cp++; + while (isdigit(*cp)) + *lp++ = *cp++; + /* time field? */ + if (*cp == ':') + { + ftype[nf] = DTK_TIME; + while (isdigit(*cp) || (*cp == ':') || (*cp == '.')) + *lp++ = *cp++; - } - else - { - if (datetime2tm(*datetime, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to convert datetime to date"); - } + } + /* date field? allow embedded text month */ + else if ((*cp == '-') || (*cp == '/') || (*cp == '.')) + { + ftype[nf] = DTK_DATE; + while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.')) + *lp++ = tolower(*cp++); - result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1)); - - return result; -} /* datetime_date() */ - - -/* abstime_date() - * Convert abstime to date data type. - */ -DateADT -abstime_date(AbsoluteTime abstime) -{ - DateADT result; - struct tm tt, - *tm = &tt; - int tz; - - switch (abstime) - { - case INVALID_ABSTIME: - case NOSTART_ABSTIME: - case NOEND_ABSTIME: - elog(ERROR, "Unable to convert reserved abstime value to date"); + } /* - * pretend to drop through to make compiler think that result - * will be set + * otherwise, number only and will determine year, month, or + * day later */ + else + ftype[nf] = DTK_NUMBER; - case EPOCH_ABSTIME: - result = date2j(1970, 1, 1) - date2j(2000, 1, 1); - break; + } - case CURRENT_ABSTIME: - GetCurrentTime(tm); - result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); - break; + /* + * 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++); - default: - abstime2tm(abstime, &tz, tm, NULL); - result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); - break; + /* full date string with leading text month? */ + 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++); + } + + /* skip leading spaces */ + } + else if (isspace(*cp)) + { + cp++; + continue; + + /* 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)) + { + ftype[nf] = DTK_TZ; + *lp++ = *cp++; + while (isdigit(*cp) || (*cp == ':')) + *lp++ = *cp++; + + /* special? */ + } + else if (isalpha(*cp)) + { + ftype[nf] = DTK_SPECIAL; + *lp++ = tolower(*cp++); + while (isalpha(*cp)) + *lp++ = tolower(*cp++); + + /* otherwise something wrong... */ + } + else + return -1; + + /* ignore punctuation but use as delimiter */ + } + else if (ispunct(*cp)) + { + cp++; + continue; + + } + else + return -1; + + /* force in a delimiter */ + *lp++ = '\0'; + nf++; + if (nf > MAXDATEFIELDS) + return -1; } - return result; -} /* abstime_date() */ + *numfields = nf; + + return 0; +} /* ParseDateTime() */ -/* date2tm() - * 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. +/* 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): + * " -- ::" + * "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 -date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn) +int +DecodeDateTime(char **field, int *ftype, int nf, + int *dtype, struct tm * tm, double *fsec, int *tzp) { - struct tm *tx; - time_t utime; + int fmask = 0, + tmask, + type; + int i; + int flen, + val; + int mer = HR24; + int haveTextMonth = FALSE; + int is2digits = FALSE; + int bc = FALSE; - *fsec = 0; - - j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); + *dtype = DTK_DATE; tm->tm_hour = 0; tm->tm_min = 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 */ - utime = ((dateVal + (date2j(2000, 1, 1) - date2j(1970, 1, 1))) * 86400); - /* rotate to noon to get the right day in time zone */ - utime += (12 * 60 * 60); + case DTK_TIME: + if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) + return -1; + /* + * check upper limit on hours; other limits checked in + * DecodeTime() + */ + if (tm->tm_hour > 23) + return -1; + break; + + case DTK_TZ: + if (tzp == NULL) + return -1; + if (DecodeTimezone(field[i], tzp) != 0) + return -1; + tmask = DTK_M(TZ); + break; + + case DTK_NUMBER: + flen = strlen(field[i]); + + /* + * long numeric string and either no date or no time read + * yet? then interpret as a concatenated date or time... + */ + if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M))) + { + if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) + return -1; + + } + /* otherwise it is a single date/time field... */ + else + { + if (DecodeNumber(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_DATE_M | DTK_TIME_M | DTK_M(TZ)); + *dtype = DTK_DATE; + GetCurrentTime(tm); + if (tzp != NULL) + *tzp = CTimeZone; + break; + + 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; + + case DTK_TODAY: + 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; + + case DTK_ZULU: + tmask = (DTK_TIME_M | DTK_M(TZ)); + *dtype = DTK_DATE; + tm->tm_hour = 0; + tm->tm_min = 0; + tm->tm_sec = 0; + if (tzp != NULL) + *tzp = 0; + break; + + default: + *dtype = val; + } + + break; + + case MONTH: + /* + * 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; + + case DTZMOD: + + /* + * 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; + + case DTZ: + + /* + * 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; + + case TZ: + tm->tm_isdst = 0; + if (tzp == NULL) + return -1; + *tzp = val * 60; + + /* Swallow an immediately succeeding timezone if this is GMT + * 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; + + /* flip the sign per POSIX standard */ + *tzp = -(*tzp); + } + + + break; + + case IGNORE: + break; + + case AMPM: + mer = val; + break; + + case ADBC: + bc = (val == BC); + break; + + case DOW: + tm->tm_wday = val; + break; + + default: + return -1; + } + break; + + default: + return -1; + } + + 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 + 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; + } + + 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; + + /* 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; + + /* + * check for valid day of month, now that we know for sure the + * month and year... + */ + if ((tm->tm_mday < 1) + || (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])) + return -1; + + /* timezone not specified? then find local timezone if possible */ + if (((fmask & DTK_DATE_M) == DTK_DATE_M) + && (tzp != NULL) && (!(fmask & DTK_M(TZ)))) + { + + /* + * 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 - tx = localtime(&utime); - - tm->tm_year = tx->tm_year + 1900; - tm->tm_mon = tx->tm_mon + 1; - tm->tm_mday = tx->tm_mday; - tm->tm_isdst = tx->tm_isdst; + 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) - tm->tm_gmtoff = tx->tm_gmtoff; - tm->tm_zone = tx->tm_zone; - - /* tm_gmtoff is Sun/DEC-ism */ - *tzp = -(tm->tm_gmtoff); - if (tzn != NULL) - *tzn = (char *) tm->tm_zone; + *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is + * Sun/DEC-ism */ #elif defined(HAVE_INT_TIMEZONE) #ifdef __CYGWIN__ - *tzp = (tm->tm_isdst ? (_timezone - 3600) : _timezone); + *tzp = ((tm->tm_isdst > 0) ? (_timezone - 3600) : _timezone); #else - *tzp = (tm->tm_isdst ? (timezone - 3600) : timezone); + *tzp = ((tm->tm_isdst > 0) ? (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... */ - } - else - { - *tzp = 0; - tm->tm_isdst = 0; - if (tzn != NULL) - *tzn = NULL; +#else /* !USE_POSIX_TIME */ + *tzp = CTimeZone; +#endif + } + else + { + tm->tm_isdst = 0; + *tzp = 0; + } + } } return 0; -} /* date2tm() */ +} /* DecodeDateTime() */ -/***************************************************************************** - * Time ADT - *****************************************************************************/ - - -TimeADT * -time_in(char *str) +/* DecodeTimeOnly() + * Interpret parsed string as time fields only. + */ +int +DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec) { - TimeADT *time; + 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; - struct tm tt, - *tm = &tt; - int nf; - char lowstr[MAXDATELEN + 1]; + int nf = 0; + int i, + len; + int bc = FALSE; + int is2digits = FALSE; + int type, + val, + dmask = 0; char *field[MAXDATEFIELDS]; - int dtype; - int ftype[MAXDATEFIELDS]; - if (!PointerIsValid(str)) - elog(ERROR, "Bad (null) time external representation"); + /* parse this string... */ + while ((*str != '\0') && (nf < MAXDATEFIELDS)) + { + /* skip field separators */ + while (!isalnum(*str)) + str++; - 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); + field[nf] = str; + if (isdigit(*str)) + { + while (isdigit(*str)) + str++; + } + else if (isalpha(*str)) + { + while (isalpha(*str)) + str++; + } - time = palloc(sizeof(TimeADT)); + if (*str != '\0') + *str++ = '\0'; + nf++; + } - *time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec); +#if 0 + /* don't allow too many fields */ + if (nf > 3) + return -1; +#endif - return time; -} /* time_in() */ + *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() */ -char * -time_out(TimeADT *time) +/* 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 *result; - struct tm tt, - *tm = &tt; + char *cp; - double fsec; - char buf[MAXDATELEN + 1]; + *tmask = DTK_TIME_M; - if (!PointerIsValid(time)) - return NULL; + 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; - tm->tm_hour = (*time / (60 * 60)); - tm->tm_min = (((int) (*time / 60)) % 60); - tm->tm_sec = (((int) *time) % 60); + } + else if (*cp != ':') + { + return -1; - fsec = 0; + } + 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; + } - EncodeTimeOnly(tm, fsec, DateStyle, buf); + /* 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; - result = palloc(strlen(buf) + 1); - - strcpy(result, buf); - - return result; -} /* time_out() */ + return 0; +} /* DecodeTime() */ -bool -time_eq(TimeADT *time1, TimeADT *time2) +/* 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) { - if (!PointerIsValid(time1) || !PointerIsValid(time2)) - return FALSE; + int val; + char *cp; - return *time1 == *time2; -} /* time_eq() */ + *tmask = 0; -bool -time_ne(TimeADT *time1, TimeADT *time2) + 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) { - if (!PointerIsValid(time1) || !PointerIsValid(time2)) - return FALSE; + char *cp; - return *time1 != *time2; -} /* time_eq() */ + /* yyyymmdd? */ + if (len == 8) + { + *tmask = DTK_DATE_M; -bool -time_lt(TimeADT *time1, TimeADT *time2) + 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) { - if (!PointerIsValid(time1) || !PointerIsValid(time2)) - return FALSE; + int tz; + int hr, + min; + char *cp; + int len; - return *time1 < *time2; -} /* time_eq() */ + /* assume leading character is "+" or "-" */ + hr = strtol((str + 1), &cp, 10); -bool -time_le(TimeADT *time1, TimeADT *time2) + /* 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) { - if (!PointerIsValid(time1) || !PointerIsValid(time2)) - return FALSE; + int type; + datetkn *tp; - return *time1 <= *time2; -} /* time_eq() */ +#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; -bool -time_gt(TimeADT *time1, TimeADT *time2) + 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) { - if (!PointerIsValid(time1) || !PointerIsValid(time2)) - return FALSE; + int is_before = FALSE; - return *time1 > *time2; -} /* time_eq() */ + char *cp; + int fmask = 0, + tmask, + type; + int i; + int flen, + val; + double fval; + double sec; -bool -time_ge(TimeADT *time1, TimeADT *time2) + *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) { - if (!PointerIsValid(time1) || !PointerIsValid(time2)) - return FALSE; + int type; + datetkn *tp; - return *time1 >= *time2; -} /* time_eq() */ +#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 + { + sprintf((str + 10), " %02d:%02d %04d %s", + 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 0; +} /* EncodeTimeSpan() */ + + +#if defined(linux) && defined(__powerpc__) int -time_cmp(TimeADT *time1, TimeADT *time2) +timestamp_is_epoch(double j) { - return (*time1 < *time2) ? -1 : (((*time1 > *time2) ? 1 : 0)); -} /* time_cmp() */ - - -/* datetime_time() - * Convert datetime to time data type. - */ -TimeADT * -datetime_time(DateTime *datetime) -{ - TimeADT *result; - struct tm tt, - *tm = &tt; - int tz; - double fsec; - char *tzn; - - if (!PointerIsValid(datetime)) - elog(ERROR, "Unable to convert null datetime to date"); - - if (DATETIME_NOT_FINITE(*datetime)) - elog(ERROR, "Unable to convert datetime to date"); - - if (DATETIME_IS_EPOCH(*datetime)) + static union { - datetime2tm(SetDateTime(*datetime), NULL, tm, &fsec, NULL); + double epoch; + unsigned char c[8]; + } u; - } - else if (DATETIME_IS_CURRENT(*datetime)) - { - datetime2tm(SetDateTime(*datetime), &tz, tm, &fsec, &tzn); + u.c[0] = 0x80; /* sign bit */ + u.c[1] = 0x10; /* DBL_MIN */ - } - else - { - if (datetime2tm(*datetime, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to convert datetime to date"); - } - - result = palloc(sizeof(TimeADT)); - - *result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec); - - return result; -} /* datetime_time() */ - - -/* datetime_datetime() - * Convert date and time to datetime data type. - */ -DateTime * -datetime_datetime(DateADT date, TimeADT *time) -{ - DateTime *result; - - if (!PointerIsValid(time)) - { - result = palloc(sizeof(DateTime)); - DATETIME_INVALID(*result); - } - else - { - result = date_datetime(date); - *result += *time; - } - - return result; -} /* datetime_datetime() */ - - -int32 /* RelativeTime */ -int4reltime(int32 timevalue) -{ - return timevalue; + return j == u.epoch; } +int +timestamp_is_current(double j) +{ + static union + { + double current; + unsigned char c[8]; + } u; + + u.c[1] = 0x10; /* DBL_MIN */ + + return j == u.current; +} + +#endif