diff options
author | Thomas G. Lockhart <lockhart@fourpalms.org> | 2000-02-16 17:26:26 +0000 |
---|---|---|
committer | Thomas G. Lockhart <lockhart@fourpalms.org> | 2000-02-16 17:26:26 +0000 |
commit | 41f1f5b76ad8e177a2b19116cbf41384f93f3851 (patch) | |
tree | 2165e6c833bc96f32e2d2b45c1d40b71e9c82ab7 /src/backend/utils | |
parent | c97672b08325d722cf5807ffede9fd31ee070f72 (diff) | |
download | postgresql-41f1f5b76ad8e177a2b19116cbf41384f93f3851.tar.gz postgresql-41f1f5b76ad8e177a2b19116cbf41384f93f3851.zip |
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.
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/Makefile | 4 | ||||
-rw-r--r-- | src/backend/utils/adt/dt.c | 4270 | ||||
-rw-r--r-- | src/backend/utils/adt/formatting.c | 97 | ||||
-rw-r--r-- | src/backend/utils/adt/nabstime.c | 1328 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 2214 |
5 files changed, 3498 insertions, 4415 deletions
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 192928db097..030ca951789 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -4,7 +4,7 @@ # Makefile for utils/adt # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.33 2000/01/25 23:53:51 momjian Exp $ +# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.34 2000/02/16 17:24:46 thomas Exp $ # #------------------------------------------------------------------------- @@ -24,7 +24,7 @@ endif endif OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o chunk.o \ - date.o datetime.o datum.o dt.o filename.o float.o \ + date.o datetime.o datum.o filename.o float.o \ geo_ops.o geo_selfuncs.o int.o int8.o like.o \ misc.o nabstime.o name.o not_in.o numeric.o numutils.o \ oid.o oracle_compat.o \ diff --git a/src/backend/utils/adt/dt.c b/src/backend/utils/adt/dt.c deleted file mode 100644 index 90a5c5c9db9..00000000000 --- a/src/backend/utils/adt/dt.c +++ /dev/null @@ -1,4270 +0,0 @@ -/*------------------------------------------------------------------------- - * - * dt.c - * Functions for the built-in type "dt". - * - * Portions Copyright (c) 1996-2000, PostgreSQL, Inc - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/dt.c,v 1.83 2000/02/15 03:17:09 thomas Exp $ - * - *------------------------------------------------------------------------- - */ -#include <ctype.h> -#include <math.h> -#include <sys/types.h> -#include <errno.h> - -#include "postgres.h" -#ifdef HAVE_FLOAT_H -#include <float.h> -#endif -#ifdef HAVE_LIMITS_H -#include <limits.h> -#endif -#ifndef USE_POSIX_TIME -#include <sys/timeb.h> -#endif - -#include "miscadmin.h" -#include "utils/builtins.h" - -static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm); -static int DecodeNumber(int flen, char *field, - int fmask, int *tmask, struct tm * tm, double *fsec, int *is2digits); -static int DecodeNumberField(int len, char *str, - int fmask, int *tmask, struct tm * tm, double *fsec, int *is2digits); -static int DecodeSpecial(int field, char *lowtoken, int *val); -static int DecodeTime(char *str, int fmask, int *tmask, - struct tm * tm, double *fsec); -static int DecodeTimezone(char *str, int *tzp); -static int DecodeUnits(int field, char *lowtoken, int *val); -static int EncodeSpecialDateTime(DateTime dt, char *str); -static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel); -static DateTime dt2local(DateTime dt, int timezone); -static void dt2time(DateTime dt, int *hour, int *min, double *sec); -static int j2day(int jd); -static double time2t(const int hour, const int min, const double sec); -static int timespan2tm(TimeSpan span, struct tm * tm, float8 *fsec); -static int tm2timespan(struct tm * tm, double fsec, TimeSpan *span); - - -#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}; - -/* TMODULO() - * Macro to replace modf(), which is broken on some platforms. - */ -#define TMODULO(t,q,u) \ -do { \ - q = ((t < 0)? ceil(t / u): floor(t / u)); \ - if (q != 0) \ - t -= rint(q * u); \ -} while(0) - -static void GetEpochTime(struct tm * tm); - -#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)))))) - - -/***************************************************************************** - * USER I/O ROUTINES * - *****************************************************************************/ - -/* datetime_in() - * Convert a string to internal form. - */ -DateTime * -datetime_in(char *str) -{ - DateTime *result; - - double fsec; - struct tm tt, - *tm = &tt; - int tz; - int dtype; - int nf; - char *field[MAXDATEFIELDS]; - int ftype[MAXDATEFIELDS]; - char lowstr[MAXDATELEN + 1]; - - if (!PointerIsValid(str)) - elog(ERROR, "Bad (null) datetime external representation"); - - if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) - || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0)) - elog(ERROR, "Bad datetime external representation '%s'", str); - - result = palloc(sizeof(DateTime)); - - switch (dtype) - { - case DTK_DATE: - if (tm2datetime(tm, fsec, &tz, result) != 0) - elog(ERROR, "Datetime out of range '%s'", str); - break; - - case DTK_EPOCH: - DATETIME_EPOCH(*result); - break; - - case DTK_CURRENT: - DATETIME_CURRENT(*result); - break; - - case DTK_LATE: - DATETIME_NOEND(*result); - break; - - case DTK_EARLY: - DATETIME_NOBEGIN(*result); - break; - - case DTK_INVALID: - DATETIME_INVALID(*result); - break; - - default: - elog(ERROR, "Internal coding error, can't input datetime '%s'", str); - } - - return result; -} /* datetime_in() */ - -/* datetime_out() - * Convert a datetime to external form. - */ -char * -datetime_out(DateTime *dt) -{ - char *result; - int tz; - struct tm tt, - *tm = &tt; - double fsec; - char *tzn; - char buf[MAXDATELEN + 1]; - - if (!PointerIsValid(dt)) - return NULL; - - if (DATETIME_IS_RESERVED(*dt)) - { - EncodeSpecialDateTime(*dt, buf); - - } - else if (datetime2tm(*dt, &tz, tm, &fsec, &tzn) == 0) - { - EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); - - } - else - EncodeSpecialDateTime(DT_INVALID, buf); - - result = palloc(strlen(buf) + 1); - - strcpy(result, buf); - - return result; -} /* datetime_out() */ - - -/* timespan_in() - * Convert a string to internal form. - * - * External format(s): - * Uses the generic date/time parsing and decoding routines. - */ -TimeSpan * -timespan_in(char *str) -{ - TimeSpan *span; - - double fsec; - struct tm tt, - *tm = &tt; - int dtype; - int nf; - char *field[MAXDATEFIELDS]; - int ftype[MAXDATEFIELDS]; - char lowstr[MAXDATELEN + 1]; - - 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; - - if (!PointerIsValid(str)) - elog(ERROR, "Bad (null) timespan external representation"); - - if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) - || (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) - elog(ERROR, "Bad timespan external representation '%s'", str); - - span = palloc(sizeof(TimeSpan)); - - switch (dtype) - { - case DTK_DELTA: - if (tm2timespan(tm, fsec, span) != 0) - { -#if NOT_USED - TIMESPAN_INVALID(span); -#endif - elog(ERROR, "Bad timespan external representation '%s'", str); - } - break; - - default: - elog(ERROR, "Internal coding error, can't input timespan '%s'", str); - } - - return span; -} /* timespan_in() */ - -/* timespan_out() - * Convert a time span to external form. - */ -char * -timespan_out(TimeSpan *span) -{ - char *result; - - struct tm tt, - *tm = &tt; - double fsec; - char buf[MAXDATELEN + 1]; - - if (!PointerIsValid(span)) - return NULL; - - if (timespan2tm(*span, tm, &fsec) != 0) - return NULL; - - if (EncodeTimeSpan(tm, fsec, DateStyle, buf) != 0) - elog(ERROR, "Unable to format timespan"); - - result = palloc(strlen(buf) + 1); - - strcpy(result, buf); - return result; -} /* timespan_out() */ - - -/***************************************************************************** - * PUBLIC ROUTINES * - *****************************************************************************/ - - -bool -datetime_finite(DateTime *datetime) -{ - if (!PointerIsValid(datetime)) - return FALSE; - - return !DATETIME_NOT_FINITE(*datetime); -} /* datetime_finite() */ - -bool -timespan_finite(TimeSpan *timespan) -{ - if (!PointerIsValid(timespan)) - return FALSE; - - return !TIMESPAN_NOT_FINITE(*timespan); -} /* timespan_finite() */ - - -/*---------------------------------------------------------- - * Relational operators for datetime. - *---------------------------------------------------------*/ - -static void -GetEpochTime(struct tm * tm) -{ - struct tm *t0; - time_t epoch = 0; - - t0 = gmtime(&epoch); - - tm->tm_year = t0->tm_year; - tm->tm_mon = t0->tm_mon; - tm->tm_mday = t0->tm_mday; - tm->tm_hour = t0->tm_hour; - tm->tm_min = t0->tm_min; - tm->tm_sec = t0->tm_sec; - - if (tm->tm_year < 1900) - tm->tm_year += 1900; - tm->tm_mon++; - - return; -} /* GetEpochTime() */ - -DateTime -SetDateTime(DateTime dt) -{ - struct tm tt; - - if (DATETIME_IS_CURRENT(dt)) - { - GetCurrentTime(&tt); - tm2datetime(&tt, 0, NULL, &dt); - dt = dt2local(dt, -CTimeZone); - } - else - { /* if (DATETIME_IS_EPOCH(dt1)) */ - GetEpochTime(&tt); - tm2datetime(&tt, 0, NULL, &dt); - } - - return dt; -} /* SetDateTime() */ - -/* datetime_relop - is datetime1 relop datetime2 - */ -bool -datetime_eq(DateTime *datetime1, DateTime *datetime2) -{ - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return FALSE; - - dt1 = *datetime1; - dt2 = *datetime2; - - if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2)) - return FALSE; - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - return dt1 == dt2; -} /* datetime_eq() */ - -bool -datetime_ne(DateTime *datetime1, DateTime *datetime2) -{ - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return FALSE; - - dt1 = *datetime1; - dt2 = *datetime2; - - if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2)) - return FALSE; - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - return dt1 != dt2; -} /* datetime_ne() */ - -bool -datetime_lt(DateTime *datetime1, DateTime *datetime2) -{ - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return FALSE; - - dt1 = *datetime1; - dt2 = *datetime2; - - if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2)) - return FALSE; - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - return dt1 < dt2; -} /* datetime_lt() */ - -bool -datetime_gt(DateTime *datetime1, DateTime *datetime2) -{ - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return FALSE; - - dt1 = *datetime1; - dt2 = *datetime2; - - if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2)) - return FALSE; - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - return dt1 > dt2; -} /* datetime_gt() */ - -bool -datetime_le(DateTime *datetime1, DateTime *datetime2) -{ - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return FALSE; - - dt1 = *datetime1; - dt2 = *datetime2; - - if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2)) - return FALSE; - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - return dt1 <= dt2; -} /* datetime_le() */ - -bool -datetime_ge(DateTime *datetime1, DateTime *datetime2) -{ - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return FALSE; - - dt1 = *datetime1; - dt2 = *datetime2; - - if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2)) - return FALSE; - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - return dt1 >= dt2; -} /* datetime_ge() */ - - -/* datetime_cmp - 3-state comparison for datetime - * collate invalid datetime at the end - */ -int -datetime_cmp(DateTime *datetime1, DateTime *datetime2) -{ - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return 0; - - dt1 = *datetime1; - dt2 = *datetime2; - - if (DATETIME_IS_INVALID(dt1)) - { - return (DATETIME_IS_INVALID(dt2) ? 0 : 1); - - } - else if (DATETIME_IS_INVALID(dt2)) - { - return -1; - - } - else - { - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - } - - return ((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0)); -} /* datetime_cmp() */ - - -/* timespan_relop - is timespan1 relop timespan2 - */ -bool -timespan_eq(TimeSpan *timespan1, TimeSpan *timespan2) -{ - if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2)) - return FALSE; - - if (TIMESPAN_IS_INVALID(*timespan1) || TIMESPAN_IS_INVALID(*timespan2)) - return FALSE; - - return ((timespan1->time == timespan2->time) - && (timespan1->month == timespan2->month)); -} /* timespan_eq() */ - -bool -timespan_ne(TimeSpan *timespan1, TimeSpan *timespan2) -{ - if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2)) - return FALSE; - - if (TIMESPAN_IS_INVALID(*timespan1) || TIMESPAN_IS_INVALID(*timespan2)) - return FALSE; - - return ((timespan1->time != timespan2->time) - || (timespan1->month != timespan2->month)); -} /* timespan_ne() */ - -bool -timespan_lt(TimeSpan *timespan1, TimeSpan *timespan2) -{ - double span1, - span2; - - if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2)) - return FALSE; - - if (TIMESPAN_IS_INVALID(*timespan1) || TIMESPAN_IS_INVALID(*timespan2)) - return FALSE; - - span1 = timespan1->time; - if (timespan1->month != 0) - span1 += (timespan1->month * (30.0 * 86400)); - span2 = timespan2->time; - if (timespan2->month != 0) - span2 += (timespan2->month * (30.0 * 86400)); - - return span1 < span2; -} /* timespan_lt() */ - -bool -timespan_gt(TimeSpan *timespan1, TimeSpan *timespan2) -{ - double span1, - span2; - - if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2)) - return FALSE; - - if (TIMESPAN_IS_INVALID(*timespan1) || TIMESPAN_IS_INVALID(*timespan2)) - return FALSE; - - span1 = timespan1->time; - if (timespan1->month != 0) - span1 += (timespan1->month * (30.0 * 86400)); - span2 = timespan2->time; - if (timespan2->month != 0) - span2 += (timespan2->month * (30.0 * 86400)); - - return span1 > span2; -} /* timespan_gt() */ - -bool -timespan_le(TimeSpan *timespan1, TimeSpan *timespan2) -{ - double span1, - span2; - - if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2)) - return FALSE; - - if (TIMESPAN_IS_INVALID(*timespan1) || TIMESPAN_IS_INVALID(*timespan2)) - return FALSE; - - span1 = timespan1->time; - if (timespan1->month != 0) - span1 += (timespan1->month * (30.0 * 86400)); - span2 = timespan2->time; - if (timespan2->month != 0) - span2 += (timespan2->month * (30.0 * 86400)); - - return span1 <= span2; -} /* timespan_le() */ - -bool -timespan_ge(TimeSpan *timespan1, TimeSpan *timespan2) -{ - double span1, - span2; - - if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2)) - return FALSE; - - if (TIMESPAN_IS_INVALID(*timespan1) || TIMESPAN_IS_INVALID(*timespan2)) - return FALSE; - - span1 = timespan1->time; - if (timespan1->month != 0) - span1 += (timespan1->month * (30.0 * 86400)); - span2 = timespan2->time; - if (timespan2->month != 0) - span2 += (timespan2->month * (30.0 * 86400)); - - return span1 >= span2; -} /* timespan_ge() */ - - -/* timespan_cmp - 3-state comparison for timespan - */ -int -timespan_cmp(TimeSpan *timespan1, TimeSpan *timespan2) -{ - double span1, - span2; - - if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2)) - return 0; - - if (TIMESPAN_IS_INVALID(*timespan1)) - { - return TIMESPAN_IS_INVALID(*timespan2) ? 0 : 1; - - } - else if (TIMESPAN_IS_INVALID(*timespan2)) - return -1; - - span1 = timespan1->time; - if (timespan1->month != 0) - span1 += (timespan1->month * (30.0 * 86400)); - span2 = timespan2->time; - if (timespan2->month != 0) - span2 += (timespan2->month * (30.0 * 86400)); - - return (span1 < span2) ? -1 : (span1 > span2) ? 1 : 0; -} /* timespan_cmp() */ - - -/*---------------------------------------------------------- - * "Arithmetic" operators on date/times. - * datetime_foo returns foo as an object (pointer) that - * can be passed between languages. - * datetime_xx is an internal routine which returns the - * actual value. - *---------------------------------------------------------*/ - -DateTime * -datetime_smaller(DateTime *datetime1, DateTime *datetime2) -{ - DateTime *result; - - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return NULL; - - dt1 = *datetime1; - dt2 = *datetime2; - - result = palloc(sizeof(DateTime)); - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - if (DATETIME_IS_INVALID(dt1)) - *result = dt2; - else if (DATETIME_IS_INVALID(dt2)) - *result = dt1; - else - *result = ((dt2 < dt1) ? dt2 : dt1); - - return result; -} /* datetime_smaller() */ - -DateTime * -datetime_larger(DateTime *datetime1, DateTime *datetime2) -{ - DateTime *result; - - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return NULL; - - dt1 = *datetime1; - dt2 = *datetime2; - - result = palloc(sizeof(DateTime)); - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - if (DATETIME_IS_INVALID(dt1)) - *result = dt2; - else if (DATETIME_IS_INVALID(dt2)) - *result = dt1; - else - *result = ((dt2 > dt1) ? dt2 : dt1); - - return result; -} /* datetime_larger() */ - - -TimeSpan * -datetime_mi(DateTime *datetime1, DateTime *datetime2) -{ - TimeSpan *result; - - DateTime dt1, - dt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return NULL; - - dt1 = *datetime1; - dt2 = *datetime2; - - result = palloc(sizeof(TimeSpan)); - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - if (DATETIME_IS_INVALID(dt1) - || DATETIME_IS_INVALID(dt2)) - { - DATETIME_INVALID(result->time); - - } - else - result->time = JROUND(dt1 - dt2); - result->month = 0; - - return result; -} /* datetime_mi() */ - - -/* datetime_pl_span() - * Add a timespan to a datetime data type. - * Note that timespan has provisions for qualitative year/month - * units, so try to do the right thing with them. - * To add a month, increment the month, and use the same day of month. - * Then, if the next month has fewer days, set the day of month - * to the last day of month. - * Lastly, add in the "quantitative time". - */ -DateTime * -datetime_pl_span(DateTime *datetime, TimeSpan *span) -{ - DateTime *result; - DateTime dt; - int tz; - char *tzn; - - if ((!PointerIsValid(datetime)) || (!PointerIsValid(span))) - return NULL; - - result = palloc(sizeof(DateTime)); - - if (DATETIME_NOT_FINITE(*datetime)) - { - *result = *datetime; - - } - else if (TIMESPAN_IS_INVALID(*span)) - { - DATETIME_INVALID(*result); - - } - else - { - dt = (DATETIME_IS_RELATIVE(*datetime) ? SetDateTime(*datetime) : *datetime); - - if (span->month != 0) - { - struct tm tt, - *tm = &tt; - double fsec; - - if (datetime2tm(dt, &tz, tm, &fsec, &tzn) == 0) - { - tm->tm_mon += span->month; - if (tm->tm_mon > 12) - { - tm->tm_year += ((tm->tm_mon - 1) / 12); - tm->tm_mon = (((tm->tm_mon - 1) % 12) + 1); - } - else if (tm->tm_mon < 1) - { - tm->tm_year += ((tm->tm_mon / 12) - 1); - tm->tm_mon = ((tm->tm_mon % 12) + 12); - } - - /* adjust for end of month boundary problems... */ - if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) - tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]); - - if (tm2datetime(tm, fsec, &tz, &dt) != 0) - elog(ERROR, "Unable to add datetime and timespan"); - - } - else - DATETIME_INVALID(dt); - } - -#ifdef ROUND_ALL - dt = JROUND(dt + span->time); -#else - dt += span->time; -#endif - - *result = dt; - } - - return result; -} /* datetime_pl_span() */ - -DateTime * -datetime_mi_span(DateTime *datetime, TimeSpan *span) -{ - DateTime *result; - TimeSpan tspan; - - if (!PointerIsValid(datetime) || !PointerIsValid(span)) - return NULL; - - tspan.month = -span->month; - tspan.time = -span->time; - - result = datetime_pl_span(datetime, &tspan); - - return result; -} /* datetime_mi_span() */ - - -TimeSpan * -timespan_um(TimeSpan *timespan) -{ - TimeSpan *result; - - if (!PointerIsValid(timespan)) - return NULL; - - result = palloc(sizeof(TimeSpan)); - - result->time = -(timespan->time); - result->month = -(timespan->month); - - return result; -} /* timespan_um() */ - - -TimeSpan * -timespan_smaller(TimeSpan *timespan1, TimeSpan *timespan2) -{ - TimeSpan *result; - - double span1, - span2; - - if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2)) - return NULL; - - result = palloc(sizeof(TimeSpan)); - - if (TIMESPAN_IS_INVALID(*timespan1)) - { - result->time = timespan2->time; - result->month = timespan2->month; - - } - else if (TIMESPAN_IS_INVALID(*timespan2)) - { - result->time = timespan1->time; - result->month = timespan1->month; - - } - else - { - span1 = timespan1->time; - if (timespan1->month != 0) - span1 += (timespan1->month * (30.0 * 86400)); - span2 = timespan2->time; - if (timespan2->month != 0) - span2 += (timespan2->month * (30.0 * 86400)); - - if (span2 < span1) - { - result->time = timespan2->time; - result->month = timespan2->month; - - } - else - { - result->time = timespan1->time; - result->month = timespan1->month; - } - } - - return result; -} /* timespan_smaller() */ - -TimeSpan * -timespan_larger(TimeSpan *timespan1, TimeSpan *timespan2) -{ - TimeSpan *result; - - double span1, - span2; - - if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2)) - return NULL; - - result = palloc(sizeof(TimeSpan)); - - if (TIMESPAN_IS_INVALID(*timespan1)) - { - result->time = timespan2->time; - result->month = timespan2->month; - - } - else if (TIMESPAN_IS_INVALID(*timespan2)) - { - result->time = timespan1->time; - result->month = timespan1->month; - - } - else - { - span1 = timespan1->time; - if (timespan1->month != 0) - span1 += (timespan1->month * (30.0 * 86400)); - span2 = timespan2->time; - if (timespan2->month != 0) - span2 += (timespan2->month * (30.0 * 86400)); - - if (span2 > span1) - { - result->time = timespan2->time; - result->month = timespan2->month; - - } - else - { - result->time = timespan1->time; - result->month = timespan1->month; - } - } - - return result; -} /* timespan_larger() */ - - -TimeSpan * -timespan_pl(TimeSpan *span1, TimeSpan *span2) -{ - TimeSpan *result; - - if ((!PointerIsValid(span1)) || (!PointerIsValid(span2))) - return NULL; - - result = palloc(sizeof(TimeSpan)); - - result->month = (span1->month + span2->month); - result->time = JROUND(span1->time + span2->time); - - return result; -} /* timespan_pl() */ - -TimeSpan * -timespan_mi(TimeSpan *span1, TimeSpan *span2) -{ - TimeSpan *result; - - if ((!PointerIsValid(span1)) || (!PointerIsValid(span2))) - return NULL; - - result = palloc(sizeof(TimeSpan)); - - result->month = (span1->month - span2->month); - result->time = JROUND(span1->time - span2->time); - - return result; -} /* timespan_mi() */ - -TimeSpan * -timespan_div(TimeSpan *span1, float8 *arg2) -{ - TimeSpan *result; - - if ((!PointerIsValid(span1)) || (!PointerIsValid(arg2))) - return NULL; - - if (!PointerIsValid(result = palloc(sizeof(TimeSpan)))) - elog(ERROR, "Memory allocation failed, can't divide timespans"); - - if (*arg2 == 0.0) - elog(ERROR, "timespan_div: divide by 0.0 error"); - - result->month = rint(span1->month / *arg2); - result->time = JROUND(span1->time / *arg2); - - return result; -} /* timespan_div() */ - -/* datetime_age() - * Calculate time difference while retaining year/month fields. - * Note that this does not result in an accurate absolute time span - * since year and month are out of context once the arithmetic - * is done. - */ -TimeSpan * -datetime_age(DateTime *datetime1, DateTime *datetime2) -{ - TimeSpan *result; - - DateTime dt1, - dt2; - double fsec, - fsec1, - fsec2; - struct tm tt, - *tm = &tt; - struct tm tt1, - *tm1 = &tt1; - struct tm tt2, - *tm2 = &tt2; - - if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2)) - return NULL; - - result = palloc(sizeof(TimeSpan)); - - dt1 = *datetime1; - dt2 = *datetime2; - - if (DATETIME_IS_RELATIVE(dt1)) - dt1 = SetDateTime(dt1); - if (DATETIME_IS_RELATIVE(dt2)) - dt2 = SetDateTime(dt2); - - if (DATETIME_IS_INVALID(dt1) - || DATETIME_IS_INVALID(dt2)) - { - DATETIME_INVALID(result->time); - - } - else if ((datetime2tm(dt1, NULL, tm1, &fsec1, NULL) == 0) - && (datetime2tm(dt2, NULL, tm2, &fsec2, NULL) == 0)) - { - fsec = (fsec1 - fsec2); - tm->tm_sec = (tm1->tm_sec - tm2->tm_sec); - tm->tm_min = (tm1->tm_min - tm2->tm_min); - tm->tm_hour = (tm1->tm_hour - tm2->tm_hour); - tm->tm_mday = (tm1->tm_mday - tm2->tm_mday); - tm->tm_mon = (tm1->tm_mon - tm2->tm_mon); - tm->tm_year = (tm1->tm_year - tm2->tm_year); - - /* flip sign if necessary... */ - if (dt1 < dt2) - { - 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; - } - - if (tm->tm_sec < 0) - { - tm->tm_sec += 60; - tm->tm_min--; - } - - if (tm->tm_min < 0) - { - tm->tm_min += 60; - tm->tm_hour--; - } - - if (tm->tm_hour < 0) - { - tm->tm_hour += 24; - tm->tm_mday--; - } - - if (tm->tm_mday < 0) - { - if (dt1 < dt2) - { - tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1]; - tm->tm_mon--; - } - else - { - tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1]; - tm->tm_mon--; - } - } - - if (tm->tm_mon < 0) - { - tm->tm_mon += 12; - tm->tm_year--; - } - - /* recover sign if necessary... */ - if (dt1 < dt2) - { - 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; - } - - if (tm2timespan(tm, fsec, result) != 0) - elog(ERROR, "Unable to decode datetime"); - - } - else - elog(ERROR, "Unable to decode datetime"); - - return result; -} /* datetime_age() */ - - -/*---------------------------------------------------------- - * Conversion operators. - *---------------------------------------------------------*/ - - -/* datetime_text() - * Convert datetime to text data type. - */ -text * -datetime_text(DateTime *datetime) -{ - text *result; - char *str; - int len; - - if (!PointerIsValid(datetime)) - return NULL; - - str = datetime_out(datetime); - - if (!PointerIsValid(str)) - return NULL; - - len = (strlen(str) + VARHDRSZ); - - result = palloc(len); - - VARSIZE(result) = len; - memmove(VARDATA(result), str, (len - VARHDRSZ)); - - pfree(str); - - return result; -} /* datetime_text() */ - - -/* text_datetime() - * Convert text string to datetime. - * Text type is not null terminated, so use temporary string - * then call the standard input routine. - */ -DateTime * -text_datetime(text *str) -{ - DateTime *result; - int i; - char *sp, - *dp, - dstr[MAXDATELEN + 1]; - - if (!PointerIsValid(str)) - return NULL; - - sp = VARDATA(str); - dp = dstr; - for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++) - *dp++ = *sp++; - *dp = '\0'; - - result = datetime_in(dstr); - - return result; -} /* text_datetime() */ - - -/* timespan_text() - * Convert timespan to text data type. - */ -text * -timespan_text(TimeSpan *timespan) -{ - text *result; - char *str; - int len; - - if (!PointerIsValid(timespan)) - return NULL; - - str = timespan_out(timespan); - - if (!PointerIsValid(str)) - return NULL; - - len = (strlen(str) + VARHDRSZ); - - result = palloc(len); - - VARSIZE(result) = len; - memmove(VARDATA(result), str, (len - VARHDRSZ)); - - pfree(str); - - return result; -} /* timespan_text() */ - - -/* text_timespan() - * Convert text string to timespan. - * Text type may not be null terminated, so copy to temporary string - * then call the standard input routine. - */ -TimeSpan * -text_timespan(text *str) -{ - TimeSpan *result; - int i; - char *sp, - *dp, - dstr[MAXDATELEN + 1]; - - if (!PointerIsValid(str)) - return NULL; - - sp = VARDATA(str); - dp = dstr; - for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++) - *dp++ = *sp++; - *dp = '\0'; - - result = timespan_in(dstr); - - return result; -} /* text_timespan() */ - -/* datetime_trunc() - * Extract specified field from datetime. - */ -DateTime * -datetime_trunc(text *units, DateTime *datetime) -{ - DateTime *result; - - DateTime dt; - int tz; - int type, - val; - int i; - char *up, - *lp, - lowunits[MAXDATELEN + 1]; - double fsec; - char *tzn; - struct tm tt, - *tm = &tt; - - if ((!PointerIsValid(units)) || (!PointerIsValid(datetime))) - return NULL; - - result = palloc(sizeof(DateTime)); - - up = VARDATA(units); - lp = lowunits; - for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) - *lp++ = tolower(*up++); - *lp = '\0'; - - type = DecodeUnits(0, lowunits, &val); - - if (DATETIME_NOT_FINITE(*datetime)) - { -#if NOT_USED -/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */ - elog(ERROR, "Datetime is not finite", NULL); -#endif - *result = 0; - - } - else - { - dt = (DATETIME_IS_RELATIVE(*datetime) ? SetDateTime(*datetime) : *datetime); - - if ((type == UNITS) && (datetime2tm(dt, &tz, tm, &fsec, &tzn) == 0)) - { - switch (val) - { - case DTK_MILLENIUM: - tm->tm_year = (tm->tm_year / 1000) * 1000; - case DTK_CENTURY: - tm->tm_year = (tm->tm_year / 100) * 100; - case DTK_DECADE: - tm->tm_year = (tm->tm_year / 10) * 10; - case DTK_YEAR: - tm->tm_mon = 1; - case DTK_QUARTER: - tm->tm_mon = (3 * (tm->tm_mon / 4)) + 1; - case DTK_MONTH: - tm->tm_mday = 1; - case DTK_DAY: - tm->tm_hour = 0; - case DTK_HOUR: - tm->tm_min = 0; - case DTK_MINUTE: - tm->tm_sec = 0; - case DTK_SECOND: - fsec = 0; - break; - - case DTK_MILLISEC: - fsec = rint(fsec * 1000) / 1000; - break; - - case DTK_MICROSEC: - fsec = rint(fsec * 1000000) / 1000000; - break; - - default: - elog(ERROR, "Datetime units '%s' not supported", lowunits); - result = NULL; - } - - if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) - { -#ifdef USE_POSIX_TIME - tm->tm_isdst = -1; - 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) - tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ -#elif defined(HAVE_INT_TIMEZONE) - -#ifdef __CYGWIN__ - tz = (tm->tm_isdst ? (_timezone - 3600) : _timezone); -#else - tz = (tm->tm_isdst ? (timezone - 3600) : timezone); -#endif - -#else -#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined -#endif - -#else /* !USE_POSIX_TIME */ - tz = CTimeZone; -#endif - } - else - { - tm->tm_isdst = 0; - tz = 0; - } - - if (tm2datetime(tm, fsec, &tz, result) != 0) - elog(ERROR, "Unable to truncate datetime to '%s'", lowunits); - -#if NOT_USED - } - else if ((type == RESERV) && (val == DTK_EPOCH)) - { - DATETIME_EPOCH(*result); - *result = dt - SetDateTime(*result); -#endif - - } - else - { - elog(ERROR, "Datetime units '%s' not recognized", lowunits); - result = NULL; - } - } - - return result; -} /* datetime_trunc() */ - -/* timespan_trunc() - * Extract specified field from timespan. - */ -TimeSpan * -timespan_trunc(text *units, TimeSpan *timespan) -{ - TimeSpan *result; - - int type, - val; - int i; - char *up, - *lp, - lowunits[MAXDATELEN + 1]; - double fsec; - struct tm tt, - *tm = &tt; - - if ((!PointerIsValid(units)) || (!PointerIsValid(timespan))) - return NULL; - - result = palloc(sizeof(TimeSpan)); - - up = VARDATA(units); - lp = lowunits; - for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) - *lp++ = tolower(*up++); - *lp = '\0'; - - type = DecodeUnits(0, lowunits, &val); - - if (TIMESPAN_IS_INVALID(*timespan)) - { -#if NOT_USED - elog(ERROR, "Timespan is not finite", NULL); -#endif - result = NULL; - - } - else if (type == UNITS) - { - - if (timespan2tm(*timespan, tm, &fsec) == 0) - { - switch (val) - { - case DTK_MILLENIUM: - tm->tm_year = (tm->tm_year / 1000) * 1000; - case DTK_CENTURY: - tm->tm_year = (tm->tm_year / 100) * 100; - case DTK_DECADE: - tm->tm_year = (tm->tm_year / 10) * 10; - case DTK_YEAR: - tm->tm_mon = 0; - case DTK_QUARTER: - tm->tm_mon = (3 * (tm->tm_mon / 4)); - case DTK_MONTH: - tm->tm_mday = 0; - case DTK_DAY: - tm->tm_hour = 0; - case DTK_HOUR: - tm->tm_min = 0; - case DTK_MINUTE: - tm->tm_sec = 0; - case DTK_SECOND: - fsec = 0; - break; - - case DTK_MILLISEC: - fsec = rint(fsec * 1000) / 1000; - break; - - case DTK_MICROSEC: - fsec = rint(fsec * 1000000) / 1000000; - break; - - default: - elog(ERROR, "Timespan units '%s' not supported", lowunits); - result = NULL; - } - - if (tm2timespan(tm, fsec, result) != 0) - elog(ERROR, "Unable to truncate timespan to '%s'", lowunits); - - } - else - { - elog(NOTICE, "Timespan out of range"); - result = NULL; - } - -#if NOT_USED - } - else if ((type == RESERV) && (val == DTK_EPOCH)) - { - *result = timespan->time; - if (timespan->month != 0) - { - *result += ((365.25 * 86400) * (timespan->month / 12)); - *result += ((30 * 86400) * (timespan->month % 12)); - } -#endif - - } - else - { - elog(ERROR, "Timespan units '%s' not recognized", textout(units)); - result = NULL; - } - - return result; -} /* timespan_trunc() */ - - -/* datetime_part() - * Extract specified field from datetime. - */ -float64 -datetime_part(text *units, DateTime *datetime) -{ - float64 result; - - DateTime dt; - int tz; - int type, - val; - int i; - char *up, - *lp, - lowunits[MAXDATELEN + 1]; - double dummy; - double fsec; - char *tzn; - struct tm tt, - *tm = &tt; - - if ((!PointerIsValid(units)) || (!PointerIsValid(datetime))) - return NULL; - - result = palloc(sizeof(float64data)); - - up = VARDATA(units); - lp = lowunits; - for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) - *lp++ = tolower(*up++); - *lp = '\0'; - - type = DecodeUnits(0, lowunits, &val); - if (type == IGNORE) - type = DecodeSpecial(0, lowunits, &val); - - if (DATETIME_NOT_FINITE(*datetime)) - { -#if NOT_USED -/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */ - elog(ERROR, "Datetime is not finite", NULL); -#endif - *result = 0; - - } - else - { - dt = (DATETIME_IS_RELATIVE(*datetime) ? SetDateTime(*datetime) : *datetime); - - if ((type == UNITS) && (datetime2tm(dt, &tz, tm, &fsec, &tzn) == 0)) - { - switch (val) - { - case DTK_TZ: - *result = tz; - break; - - case DTK_TZ_MINUTE: - *result = tz / 60; - TMODULO(*result, dummy, 60e0); - break; - - case DTK_TZ_HOUR: - dummy = tz; - TMODULO(dummy, *result, 3600e0); - break; - - case DTK_MICROSEC: - *result = (fsec * 1000000); - break; - - case DTK_MILLISEC: - *result = (fsec * 1000); - break; - - case DTK_SECOND: - *result = (tm->tm_sec + fsec); - break; - - case DTK_MINUTE: - *result = tm->tm_min; - break; - - case DTK_HOUR: - *result = tm->tm_hour; - break; - - case DTK_DAY: - *result = tm->tm_mday; - break; - - case DTK_MONTH: - *result = tm->tm_mon; - break; - - case DTK_QUARTER: - *result = (tm->tm_mon / 4) + 1; - break; - - case DTK_YEAR: - *result = tm->tm_year; - break; - - case DTK_DECADE: - *result = (tm->tm_year / 10); - break; - - case DTK_CENTURY: - *result = (tm->tm_year / 100); - break; - - case DTK_MILLENIUM: - *result = (tm->tm_year / 1000); - break; - - default: - elog(ERROR, "Datetime units '%s' not supported", lowunits); - *result = 0; - } - - } - else if (type == RESERV) - { - switch (val) - { - case DTK_EPOCH: - DATETIME_EPOCH(*result); - *result = dt - SetDateTime(*result); - break; - - case DTK_DOW: - if (datetime2tm(dt, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to encode datetime"); - - *result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); - break; - - case DTK_DOY: - if (datetime2tm(dt, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to encode datetime"); - - *result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - - date2j(tm->tm_year, 1, 1) + 1); - break; - - default: - elog(ERROR, "Datetime units '%s' not supported", lowunits); - *result = 0; - } - - } - else - { - elog(ERROR, "Datetime units '%s' not recognized", lowunits); - *result = 0; - } - } - - return result; -} /* datetime_part() */ - - -/* timespan_part() - * Extract specified field from timespan. - */ -float64 -timespan_part(text *units, TimeSpan *timespan) -{ - float64 result; - - int type, - val; - int i; - char *up, - *lp, - lowunits[MAXDATELEN + 1]; - double fsec; - struct tm tt, - *tm = &tt; - - if ((!PointerIsValid(units)) || (!PointerIsValid(timespan))) - return NULL; - - result = palloc(sizeof(float64data)); - - up = VARDATA(units); - lp = lowunits; - for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) - *lp++ = tolower(*up++); - *lp = '\0'; - - type = DecodeUnits(0, lowunits, &val); - if (type == IGNORE) - type = DecodeSpecial(0, lowunits, &val); - - if (TIMESPAN_IS_INVALID(*timespan)) - { -#if NOT_USED - elog(ERROR, "Timespan is not finite"); -#endif - *result = 0; - - } - else if (type == UNITS) - { - - if (timespan2tm(*timespan, tm, &fsec) == 0) - { - switch (val) - { - case DTK_MICROSEC: - *result = (fsec * 1000000); - break; - - case DTK_MILLISEC: - *result = (fsec * 1000); - break; - - case DTK_SECOND: - *result = (tm->tm_sec + fsec); - break; - - case DTK_MINUTE: - *result = tm->tm_min; - break; - - case DTK_HOUR: - *result = tm->tm_hour; - break; - - case DTK_DAY: - *result = tm->tm_mday; - break; - - case DTK_MONTH: - *result = tm->tm_mon; - break; - - case DTK_QUARTER: - *result = (tm->tm_mon / 4) + 1; - break; - - case DTK_YEAR: - *result = tm->tm_year; - break; - - case DTK_DECADE: - *result = (tm->tm_year / 10); - break; - - case DTK_CENTURY: - *result = (tm->tm_year / 100); - break; - - case DTK_MILLENIUM: - *result = (tm->tm_year / 1000); - break; - - default: - elog(ERROR, "Timespan units '%s' not yet supported", textout(units)); - result = NULL; - } - - } - else - { - elog(NOTICE, "Timespan out of range"); - *result = 0; - } - - } - else if ((type == RESERV) && (val == DTK_EPOCH)) - { - *result = timespan->time; - if (timespan->month != 0) - { - *result += ((365.25 * 86400) * (timespan->month / 12)); - *result += ((30 * 86400) * (timespan->month % 12)); - } - - } - else - { - elog(ERROR, "Timespan units '%s' not recognized", textout(units)); - *result = 0; - } - - return result; -} /* timespan_part() */ - - -/* datetime_zone() - * Encode datetime type with specified time zone. - */ -text * -datetime_zone(text *zone, DateTime *datetime) -{ - text *result; - - DateTime dt; - int tz; - int type, - val; - int i; - char *up, - *lp, - lowzone[MAXDATELEN + 1]; - char *tzn, - upzone[MAXDATELEN + 1]; - double fsec; - struct tm tt, - *tm = &tt; - char buf[MAXDATELEN + 1]; - int len; - - if ((!PointerIsValid(zone)) || (!PointerIsValid(datetime))) - return NULL; - - up = VARDATA(zone); - lp = lowzone; - for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++) - *lp++ = tolower(*up++); - *lp = '\0'; - - type = DecodeSpecial(0, lowzone, &val); - - if (DATETIME_NOT_FINITE(*datetime)) - { - - /* - * could return null but Postgres doesn't like that currently. - - * tgl 97/06/12 - */ - elog(ERROR, "Datetime is not finite"); - result = NULL; - - } - else if ((type == TZ) || (type == DTZ)) - { - tm->tm_isdst = ((type == DTZ) ? 1 : 0); - tz = val * 60; - - dt = (DATETIME_IS_RELATIVE(*datetime) ? SetDateTime(*datetime) : *datetime); - dt = dt2local(dt, tz); - - if (datetime2tm(dt, NULL, tm, &fsec, NULL) != 0) - elog(ERROR, "Datetime not legal"); - - up = upzone; - lp = lowzone; - for (i = 0; *lp != '\0'; i++) - *up++ = toupper(*lp++); - *up = '\0'; - - tzn = upzone; - EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); - - len = (strlen(buf) + VARHDRSZ); - - result = palloc(len); - - VARSIZE(result) = len; - memmove(VARDATA(result), buf, (len - VARHDRSZ)); - - } - else - { - elog(ERROR, "Time zone '%s' not recognized", lowzone); - result = NULL; - } - - return result; -} /* datetime_zone() */ - - -/***************************************************************************** - * 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)) - -/* - * to keep this table reasonably small, we divide the lexval for TZ and DTZ - * entries by 10 and truncate the text field at MAXTOKLEN characters. - * the text field is not guaranteed to be NULL-terminated. - */ -static datetkn datetktbl[] = { -/* text token lexval */ - {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */ - {"acsst", DTZ, 63}, /* Cent. Australia */ - {"acst", TZ, 57}, /* Cent. Australia */ - {DA_D, ADBC, AD}, /* "ad" for years >= 0 */ - {"abstime", IGNORE, 0}, /* "abstime" for pre-v6.1 "Invalid - * Abstime" */ - {"adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */ - {"aesst", DTZ, 66}, /* E. Australia */ - {"aest", TZ, 60}, /* Australia Eastern Std Time */ - {"ahst", TZ, NEG(60)}, /* Alaska-Hawaii Std Time */ - {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */ - {"am", AMPM, AM}, - {"apr", MONTH, 4}, - {"april", MONTH, 4}, - {"ast", TZ, NEG(24)}, /* Atlantic Std Time (Canada) */ - {"at", IGNORE, 0}, /* "at" (throwaway) */ - {"aug", MONTH, 8}, - {"august", MONTH, 8}, - {"awsst", DTZ, 54}, /* W. Australia */ - {"awst", TZ, 48}, /* W. Australia */ - {DB_C, ADBC, BC}, /* "bc" for years < 0 */ - {"bst", TZ, 6}, /* British Summer Time */ - {"bt", TZ, 18}, /* Baghdad Time */ - {"cadt", DTZ, 63}, /* Central Australian DST */ - {"cast", TZ, 57}, /* Central Australian ST */ - {"cat", TZ, NEG(60)}, /* Central Alaska Time */ - {"cct", TZ, 48}, /* China Coast */ - {"cdt", DTZ, NEG(30)}, /* Central Daylight Time */ - {"cet", TZ, 6}, /* Central European Time */ - {"cetdst", DTZ, 12}, /* Central European Dayl.Time */ -#if USE_AUSTRALIAN_RULES - {"cst", TZ, 63}, /* Australia Eastern Std Time */ -#else - {"cst", TZ, NEG(36)}, /* Central Standard Time */ -#endif - {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */ - {"dec", MONTH, 12}, - {"december", MONTH, 12}, - {"dnt", TZ, 6}, /* Dansk Normal Tid */ - {"dow", RESERV, DTK_DOW}, /* day of week */ - {"doy", RESERV, DTK_DOY}, /* day of year */ - {"dst", DTZMOD, 6}, - {"east", TZ, 60}, /* East Australian Std Time */ - {"edt", DTZ, NEG(24)}, /* Eastern Daylight Time */ - {"eet", TZ, 12}, /* East. Europe, USSR Zone 1 */ - {"eetdst", DTZ, 18}, /* Eastern Europe */ - {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */ -#if USE_AUSTRALIAN_RULES - {"est", TZ, 60}, /* Australia Eastern Std Time */ -#else - {"est", TZ, NEG(30)}, /* Eastern Standard Time */ -#endif - {"feb", MONTH, 2}, - {"february", MONTH, 2}, - {"fri", DOW, 5}, - {"friday", DOW, 5}, - {"fst", TZ, 6}, /* French Summer Time */ - {"fwt", DTZ, 12}, /* French Winter Time */ - {"gmt", TZ, 0}, /* Greenwish Mean Time */ - {"gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */ - {"hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */ - {"hmt", DTZ, 18}, /* Hellas ? ? */ - {"hst", TZ, NEG(60)}, /* Hawaii Std Time */ - {"idle", TZ, 72}, /* Intl. Date Line, East */ - {"idlw", TZ, NEG(72)}, /* Intl. Date Line, West */ - {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */ - {INVALID, RESERV, DTK_INVALID}, - /* "invalid" reserved for invalid time */ - {"ist", TZ, 12}, /* Israel */ - {"it", TZ, 21}, /* Iran Time */ - {"jan", MONTH, 1}, - {"january", MONTH, 1}, - {"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */ - {"jt", TZ, 45}, /* Java Time */ - {"jul", MONTH, 7}, - {"july", MONTH, 7}, - {"jun", MONTH, 6}, - {"june", MONTH, 6}, - {"kst", TZ, 54}, /* Korea Standard Time */ - {"ligt", TZ, 60}, /* From Melbourne, Australia */ - {"mar", MONTH, 3}, - {"march", MONTH, 3}, - {"may", MONTH, 5}, - {"mdt", DTZ, NEG(36)}, /* Mountain Daylight Time */ - {"mest", DTZ, 12}, /* Middle Europe Summer Time */ - {"met", TZ, 6}, /* Middle Europe Time */ - {"metdst", DTZ, 12}, /* Middle Europe Daylight Time */ - {"mewt", TZ, 6}, /* Middle Europe Winter Time */ - {"mez", TZ, 6}, /* Middle Europe Zone */ - {"mon", DOW, 1}, - {"monday", DOW, 1}, - {"mst", TZ, NEG(42)}, /* Mountain Standard Time */ - {"mt", TZ, 51}, /* Moluccas Time */ - {"ndt", DTZ, NEG(15)}, /* Nfld. Daylight Time */ - {"nft", TZ, NEG(21)}, /* Newfoundland Standard Time */ - {"nor", TZ, 6}, /* Norway Standard Time */ - {"nov", MONTH, 11}, - {"november", MONTH, 11}, - {NOW, RESERV, DTK_NOW}, /* current transaction time */ - {"nst", TZ, NEG(21)}, /* Nfld. Standard Time */ - {"nt", TZ, NEG(66)}, /* Nome Time */ - {"nzdt", DTZ, 78}, /* New Zealand Daylight Time */ - {"nzst", TZ, 72}, /* New Zealand Standard Time */ - {"nzt", TZ, 72}, /* New Zealand Time */ - {"oct", MONTH, 10}, - {"october", MONTH, 10}, - {"on", IGNORE, 0}, /* "on" (throwaway) */ - {"pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */ - {"pm", AMPM, PM}, - {"pst", TZ, NEG(48)}, /* Pacific Standard Time */ - {"sadt", DTZ, 63}, /* S. Australian Dayl. Time */ - {"sast", TZ, 57}, /* South Australian Std Time */ - {"sat", DOW, 6}, - {"saturday", DOW, 6}, - {"sep", MONTH, 9}, - {"sept", MONTH, 9}, - {"september", MONTH, 9}, - {"set", TZ, NEG(6)}, /* Seychelles Time ?? */ - {"sst", DTZ, 12}, /* Swedish Summer Time */ - {"sun", DOW, 0}, - {"sunday", DOW, 0}, - {"swt", TZ, 6}, /* Swedish Winter Time */ - {"thu", DOW, 4}, - {"thur", DOW, 4}, - {"thurs", DOW, 4}, - {"thursday", DOW, 4}, - {TODAY, RESERV, DTK_TODAY}, /* midnight */ - {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */ - {"tue", DOW, 2}, - {"tues", DOW, 2}, - {"tuesday", DOW, 2}, - {"undefined", RESERV, DTK_INVALID}, /* "undefined" pre-v6.1 invalid - * time */ - {"ut", TZ, 0}, - {"utc", TZ, 0}, - {"wadt", DTZ, 48}, /* West Australian DST */ - {"wast", TZ, 42}, /* West Australian Std Time */ - {"wat", TZ, NEG(6)}, /* West Africa Time */ - {"wdt", DTZ, 54}, /* West Australian DST */ - {"wed", DOW, 3}, - {"wednesday", DOW, 3}, - {"weds", DOW, 3}, - {"wet", TZ, 0}, /* Western Europe */ - {"wetdst", DTZ, 6}, /* Western Europe */ - {"wst", TZ, 48}, /* West Australian Std Time */ - {"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */ - {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */ - {"yst", TZ, NEG(54)}, /* Yukon Standard Time */ - {"zp4", TZ, NEG(24)}, /* GMT +4 hours. */ - {"zp5", TZ, NEG(30)}, /* GMT +5 hours. */ - {"zp6", TZ, NEG(36)}, /* GMT +6 hours. */ - {"z", RESERV, DTK_ZULU}, /* 00:00:00 */ - {ZULU, RESERV, DTK_ZULU}, /* 00:00:00 */ -}; - -static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0]; - -static datetkn deltatktbl[] = { -/* text token lexval */ - {"@", IGNORE, 0}, /* postgres relative time prefix */ - {DAGO, AGO, 0}, /* "ago" indicates negative time offset */ - {"c", UNITS, DTK_CENTURY}, /* "century" relative time units */ - {"cent", UNITS, DTK_CENTURY}, /* "century" relative time units */ - {"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative time units */ - {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative time units */ - {"d", UNITS, DTK_DAY}, /* "day" relative time units */ - {DDAY, UNITS, DTK_DAY}, /* "day" relative time units */ - {"days", UNITS, DTK_DAY}, /* "days" relative time units */ - {"dec", UNITS, DTK_DECADE}, /* "decade" relative time units */ - {"decs", UNITS, DTK_DECADE},/* "decades" relative time units */ - {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative time units */ - {"decades", UNITS, DTK_DECADE}, /* "decades" relative time units */ - {"h", UNITS, DTK_HOUR}, /* "hour" relative time units */ - {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative time units */ - {"hours", UNITS, DTK_HOUR}, /* "hours" relative time units */ - {"hr", UNITS, DTK_HOUR}, /* "hour" relative time units */ - {"hrs", UNITS, DTK_HOUR}, /* "hours" relative time units */ - {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for invalid - * time */ - {"m", UNITS, DTK_MINUTE}, /* "minute" relative time units */ - {"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative - * time units */ - {"mil", UNITS, DTK_MILLENIUM}, /* "millenium" relative time units */ - {"mils", UNITS, DTK_MILLENIUM}, /* "millenia" relative time units */ - {"millenia", UNITS, DTK_MILLENIUM}, /* "millenia" relative time units */ - {DMILLENIUM, UNITS, DTK_MILLENIUM}, /* "millenium" relative time units */ - {"millisecon", UNITS, DTK_MILLISEC}, /* relative time units */ - {"min", UNITS, DTK_MINUTE}, /* "minute" relative time units */ - {"mins", UNITS, DTK_MINUTE},/* "minutes" relative time units */ - {"mins", UNITS, DTK_MINUTE},/* "minutes" relative time units */ - {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative time units */ - {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative time units */ - {"mon", UNITS, DTK_MONTH}, /* "months" relative time units */ - {"mons", UNITS, DTK_MONTH}, /* "months" relative time units */ - {DMONTH, UNITS, DTK_MONTH}, /* "month" relative time units */ - {"months", UNITS, DTK_MONTH}, - {"ms", UNITS, DTK_MILLISEC}, - {"msec", UNITS, DTK_MILLISEC}, - {DMILLISEC, UNITS, DTK_MILLISEC}, - {"mseconds", UNITS, DTK_MILLISEC}, - {"msecs", UNITS, DTK_MILLISEC}, - {"qtr", UNITS, DTK_QUARTER},/* "quarter" relative time */ - {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative time */ - {"reltime", IGNORE, 0}, /* for pre-v6.1 "Undefined Reltime" */ - {"s", UNITS, DTK_SECOND}, - {"sec", UNITS, DTK_SECOND}, - {DSECOND, UNITS, DTK_SECOND}, - {"seconds", UNITS, DTK_SECOND}, - {"secs", UNITS, DTK_SECOND}, - {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */ - {"tz", UNITS, DTK_TZ}, /* "timezone" time offset */ - {"tz_hour", UNITS, DTK_TZ_HOUR}, /* timezone hour units */ - {"tz_minute", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */ - {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */ - {"us", UNITS, DTK_MICROSEC},/* "microsecond" relative time units */ - {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative time - * units */ - {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative time - * units */ - {"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative time - * units */ - {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative time - * units */ - {"w", UNITS, DTK_WEEK}, /* "week" relative time units */ - {DWEEK, UNITS, DTK_WEEK}, /* "week" relative time units */ - {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative time units */ - {"y", UNITS, DTK_YEAR}, /* "year" relative time units */ - {DYEAR, UNITS, DTK_YEAR}, /* "year" relative time units */ - {"years", UNITS, DTK_YEAR}, /* "years" relative time units */ - {"yr", UNITS, DTK_YEAR}, /* "year" relative time units */ - {"yrs", UNITS, DTK_YEAR}, /* "years" relative time units */ -}; - -static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0]; - -#if USE_DATE_CACHE -datetkn *datecache[MAXDATEFIELDS] = {NULL}; - -datetkn *deltacache[MAXDATEFIELDS] = {NULL}; - -#endif - - -/* - * 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 - */ - -int -date2j(int y, int m, int d) -{ - int m12 = (m - 14) / 12; - - 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) -{ - int j, - y, - m, - d; - - int i, - l, - n; - - l = jd + 68569; - n = (4 * l) / 146097; - l -= (146097 * n + 3) / 4; - i = (4000 * (l + 1)) / 1461001; - l += 31 - (1461 * i) / 4; - j = (80 * l) / 2447; - d = l - (2447 * j) / 80; - l = j / 11; - m = (j + 2) - (12 * l); - y = 100 * (n - 49) + i + l; - - *year = y; - *month = m; - *day = d; - return; -} /* j2date() */ - -static int -j2day(int date) -{ - int day; - - day = (date + 1) % 7; - - return day; -} /* j2day() */ - - -/* datetime2tm() - * Convert datetime data type to POSIX time structure. - * Note that year is _not_ 1900-based, but is an explicit full value. - * Also, month is one-based, _not_ zero-based. - * Returns: - * 0 on success - * -1 on out of range - * - * For dates within the system-supported time_t range, convert to the - * local time zone. If out of this range, leave as GMT. - tgl 97/05/27 - */ -int -datetime2tm(DateTime dt, int *tzp, struct tm * tm, double *fsec, char **tzn) -{ - double date, - date0, - time, - sec; - time_t utime; - -#ifdef USE_POSIX_TIME - struct tm *tx; - -#endif - - date0 = date2j(2000, 1, 1); - - time = dt; - TMODULO(time, date, 86400e0); - - if (time < 0) - { - time += 86400; - date -= 1; - } - - /* Julian day routine does not work for negative Julian days */ - if (date < -date0) - return -1; - - /* add offset to go from J2000 back to standard Julian date */ - date += date0; - - j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - dt2time(time, &tm->tm_hour, &tm->tm_min, &sec); - - *fsec = JROUND(sec); - TMODULO(*fsec, tm->tm_sec, 1e0); - - if (tzp != NULL) - { - if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) - { - utime = (dt + (date0 - date2j(1970, 1, 1)) * 86400); - -#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_hour = tx->tm_hour; - tm->tm_min = tx->tm_min; -#if NOT_USED -/* XXX HACK - * Argh! My Linux box puts in a 1 second offset for dates less than 1970 - * but only if the seconds field was non-zero. So, don't copy the seconds - * field and instead carry forward from the original - tgl 97/06/18 - * Note that GNU/Linux uses the standard freeware zic package as do - * many other platforms so this may not be GNU/Linux/ix86-specific. - */ - tm->tm_sec = tx->tm_sec; -#endif - tm->tm_isdst = tx->tm_isdst; - -#if defined(HAVE_TM_ZONE) - tm->tm_gmtoff = tx->tm_gmtoff; - tm->tm_zone = tx->tm_zone; - - *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ - 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 - - } - else - { - *tzp = 0; - tm->tm_isdst = 0; - if (tzn != NULL) - *tzn = NULL; - } - - dt = dt2local(dt, *tzp); - - } - else - { - tm->tm_isdst = 0; - if (tzn != NULL) - *tzn = NULL; - } - - return 0; -} /* datetime2tm() */ - - -/* tm2datetime() - * Convert a tm structure to a datetime data type. - * Note that year is _not_ 1900-based, but is an explicit full value. - * Also, month is one-based, _not_ zero-based. - */ -int -tm2datetime(struct tm * tm, double fsec, int *tzp, DateTime *result) -{ - - double date, - time; - - /* Julian day routines are not correct for negative Julian days */ - if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) - return -1; - - date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); - time = time2t(tm->tm_hour, tm->tm_min, (tm->tm_sec + fsec)); - *result = (date * 86400 + time); - if (tzp != NULL) - *result = dt2local(*result, -(*tzp)); - - return 0; -} /* tm2datetime() */ - - -/* timespan2tm() - * Convert a timespan data type to a tm structure. - */ -static int -timespan2tm(TimeSpan span, struct tm * tm, float8 *fsec) -{ - double time; - - if (span.month != 0) - { - tm->tm_year = span.month / 12; - tm->tm_mon = span.month % 12; - - } - else - { - tm->tm_year = 0; - tm->tm_mon = 0; - } - -#ifdef ROUND_ALL - time = JROUND(span.time); -#else - time = span.time; -#endif - - TMODULO(time, tm->tm_mday, 86400e0); - TMODULO(time, tm->tm_hour, 3600e0); - TMODULO(time, tm->tm_min, 60e0); - TMODULO(time, tm->tm_sec, 1e0); - *fsec = time; - - return 0; -} /* timespan2tm() */ - -static int -tm2timespan(struct tm * tm, double fsec, TimeSpan *span) -{ - span->month = ((tm->tm_year * 12) + tm->tm_mon); - span->time = ((((((tm->tm_mday * 24.0) - + tm->tm_hour) * 60.0) - + tm->tm_min) * 60.0) - + tm->tm_sec); - span->time = JROUND(span->time + fsec); - - return 0; -} /* tm2timespan() */ - - -static DateTime -dt2local(DateTime dt, int tz) -{ - dt -= tz; - dt = JROUND(dt); - return dt; -} /* dt2local() */ - -static double -time2t(const int hour, const int min, const double sec) -{ - return (((hour * 60) + min) * 60) + sec; -} /* time2t() */ - -static void -dt2time(DateTime 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() */ - - -/* - * parse and convert date in timestr (the normal interface) - * - * Returns the number of seconds since epoch (J2000) - */ - -/* ParseDateTime() - * Break string into tokens based on a date/time context. - */ -int -ParseDateTime(char *timestr, char *lowstr, - char **field, int *ftype, int maxfields, int *numfields) -{ - int nf = 0; - char *cp = timestr; - char *lp = lowstr; - - /* outer loop through fields */ - while (*cp != '\0') - { - field[nf] = lp; - - /* 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++; - - } - /* date field? allow embedded text month */ - else if ((*cp == '-') || (*cp == '/') || (*cp == '.')) - { - ftype[nf] = DTK_DATE; - while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.')) - *lp++ = tolower(*cp++); - - } - - /* - * otherwise, number only and will determine year, month, or - * day later - */ - else - ftype[nf] = DTK_NUMBER; - - } - - /* - * 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++); - - /* 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; - } - - *numfields = nf; - - return 0; -} /* ParseDateTime() */ - - -/* DecodeDateTime() - * Interpret previously parsed fields for general date and time. - * Return 0 if full date, 1 if only time, and -1 if problems. - * External format(s): - * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>" - * "Fri Feb-7-1997 15:23:27" - * "Feb-7-1997 15:23:27" - * "2-7-1997 15:23:27" - * "1997-2-7 15:23:27" - * "1997.038 15:23:27" (day of year 1-366) - * Also supports input in compact time: - * "970207 152327" - * "97038 152327" - * - * Use the system-provided functions to get the current time zone - * if not specified in the input string. - * If the date is outside the time_t system-supported time range, - * then assume GMT time zone. - tgl 97/05/27 - */ -int -DecodeDateTime(char **field, int *ftype, int nf, - int *dtype, struct tm * tm, double *fsec, int *tzp) -{ - int fmask = 0, - tmask, - type; - int i; - int flen, - val; - int mer = HR24; - int haveTextMonth = FALSE; - int is2digits = FALSE; - int bc = FALSE; - - *dtype = DTK_DATE; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - *fsec = 0; - tm->tm_isdst = -1; /* don't know daylight savings time status - * apriori */ - if (tzp != NULL) - *tzp = 0; - - for (i = 0; i < nf; i++) - { - switch (ftype[i]) - { - case DTK_DATE: - if (DecodeDate(field[i], fmask, &tmask, tm) != 0) - return -1; - break; - - 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 - tm->tm_year -= 1900; - tm->tm_mon -= 1; - tm->tm_isdst = -1; - mktime(tm); - tm->tm_year += 1900; - tm->tm_mon += 1; - -#if defined(HAVE_TM_ZONE) - *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is - * Sun/DEC-ism */ -#elif defined(HAVE_INT_TIMEZONE) -#ifdef __CYGWIN__ - *tzp = ((tm->tm_isdst > 0) ? (_timezone - 3600) : _timezone); -#else - *tzp = ((tm->tm_isdst > 0) ? (timezone - 3600) : timezone); -#endif -#else -#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined -#endif - -#else /* !USE_POSIX_TIME */ - *tzp = CTimeZone; -#endif - } - else - { - tm->tm_isdst = 0; - *tzp = 0; - } - } - } - - return 0; -} /* DecodeDateTime() */ - - -/* DecodeTimeOnly() - * Interpret parsed string as time fields only. - */ -int -DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec) -{ - int fmask, - tmask, - type; - int i; - int flen, - val; - int is2digits = FALSE; - int mer = HR24; - - *dtype = DTK_TIME; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - tm->tm_isdst = -1; /* don't know daylight savings time status - * apriori */ - *fsec = 0; - - fmask = DTK_DATE_M; - - for (i = 0; i < nf; i++) - { - switch (ftype[i]) - { - case DTK_TIME: - if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) - return -1; - break; - - case DTK_NUMBER: - flen = strlen(field[i]); - - if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) - return -1; - break; - - case DTK_STRING: - case DTK_SPECIAL: - type = DecodeSpecial(i, field[i], &val); - if (type == IGNORE) - continue; - - tmask = DTK_M(type); - switch (type) - { - case RESERV: - switch (val) - { - case DTK_NOW: - tmask = DTK_TIME_M; - *dtype = DTK_TIME; - GetCurrentTime(tm); - break; - - case DTK_ZULU: - tmask = (DTK_TIME_M | DTK_M(TZ)); - *dtype = DTK_TIME; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - tm->tm_isdst = 0; - break; - - default: - return -1; - } - - break; - - case IGNORE: - break; - - case AMPM: - mer = val; - break; - - default: - return -1; - } - break; - - default: - return -1; - } - - if (tmask & fmask) - return -1; - fmask |= tmask; - } - - if ((mer != HR24) && (tm->tm_hour > 12)) - return -1; - if ((mer == AM) && (tm->tm_hour == 12)) - tm->tm_hour = 0; - else if ((mer == PM) && (tm->tm_hour != 12)) - tm->tm_hour += 12; - - if (((tm->tm_hour < 0) || (tm->tm_hour > 23)) - || ((tm->tm_min < 0) || (tm->tm_min > 59)) - || ((tm->tm_sec < 0) || ((tm->tm_sec + *fsec) >= 60))) - return -1; - - if ((fmask & DTK_TIME_M) != DTK_TIME_M) - return -1; - - return 0; -} /* DecodeTimeOnly() */ - - -/* DecodeDate() - * Decode date string which includes delimiters. - * Insist on a complete set of fields. - */ -static int -DecodeDate(char *str, int fmask, int *tmask, struct tm * tm) -{ - double fsec; - - int nf = 0; - int i, - len; - int bc = FALSE; - int is2digits = FALSE; - int type, - val, - dmask = 0; - char *field[MAXDATEFIELDS]; - - /* parse this string... */ - while ((*str != '\0') && (nf < MAXDATEFIELDS)) - { - /* skip field separators */ - while (!isalnum(*str)) - str++; - - field[nf] = str; - if (isdigit(*str)) - { - while (isdigit(*str)) - str++; - } - else if (isalpha(*str)) - { - while (isalpha(*str)) - str++; - } - - if (*str != '\0') - *str++ = '\0'; - nf++; - } - -#if 0 - /* don't allow too many fields */ - if (nf > 3) - return -1; -#endif - - *tmask = 0; - - /* look first for text fields, since that will be unambiguous month */ - for (i = 0; i < nf; i++) - { - if (isalpha(*field[i])) - { - type = DecodeSpecial(i, field[i], &val); - if (type == IGNORE) - continue; - - dmask = DTK_M(type); - switch (type) - { - case MONTH: - tm->tm_mon = val; - break; - - case ADBC: - bc = (val == BC); - break; - - default: - return -1; - } - if (fmask & dmask) - return -1; - - fmask |= dmask; - *tmask |= dmask; - - /* mark this field as being completed */ - field[i] = NULL; - } - } - - /* now pick up remaining numeric fields */ - for (i = 0; i < nf; i++) - { - if (field[i] == NULL) - continue; - - if ((len = strlen(field[i])) <= 0) - return -1; - - if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits) != 0) - return -1; - - if (fmask & dmask) - return -1; - - fmask |= dmask; - *tmask |= dmask; - } - - if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M) - return -1; - - /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */ - if (bc) - { - if (tm->tm_year > 0) - tm->tm_year = -(tm->tm_year - 1); - else - elog(ERROR, "Inconsistant use of year %04d and 'BC'", tm->tm_year); - } - else if (is2digits) - { - if (tm->tm_year < 70) - tm->tm_year += 2000; - else if (tm->tm_year < 100) - tm->tm_year += 1900; - } - - return 0; -} /* DecodeDate() */ - - -/* DecodeTime() - * Decode time string which includes delimiters. - * Only check the lower limit on hours, since this same code - * can be used to represent time spans. - */ -static int -DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec) -{ - char *cp; - - *tmask = DTK_TIME_M; - - tm->tm_hour = strtol(str, &cp, 10); - if (*cp != ':') - return -1; - str = cp + 1; - tm->tm_min = strtol(str, &cp, 10); - if (*cp == '\0') - { - tm->tm_sec = 0; - *fsec = 0; - - } - else if (*cp != ':') - { - return -1; - - } - else - { - str = cp + 1; - tm->tm_sec = strtol(str, &cp, 10); - if (*cp == '\0') - *fsec = 0; - else if (*cp == '.') - { - str = cp; - *fsec = strtod(str, &cp); - if (cp == str) - return -1; - } - else - return -1; - } - - /* do a sanity check */ - if ((tm->tm_hour < 0) - || (tm->tm_min < 0) || (tm->tm_min > 59) - || (tm->tm_sec < 0) || (tm->tm_sec > 59)) - return -1; - - return 0; -} /* DecodeTime() */ - - -/* DecodeNumber() - * Interpret numeric field as a date value in context. - */ -static int -DecodeNumber(int flen, char *str, int fmask, - int *tmask, struct tm * tm, double *fsec, int *is2digits) -{ - int val; - char *cp; - - *tmask = 0; - - val = strtol(str, &cp, 10); - if (cp == str) - return -1; - if (*cp == '.') - { - *fsec = strtod(cp, &cp); - if (*cp != '\0') - return -1; - } - - /* Special case day of year? */ - if ((flen == 3) && (fmask & DTK_M(YEAR)) - && ((val >= 1) && (val <= 366))) - { - *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY)); - tm->tm_yday = val; - j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1), - &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - - } - - /* - * Enough digits to be unequivocal year? Used to test for 4 digits or - * more, but we now test first for a three-digit doy so anything - * bigger than two digits had better be an explicit year. - thomas - * 1999-01-09 - */ - else if (flen > 2) - { - *tmask = DTK_M(YEAR); - - /* already have a year? then see if we can substitute... */ - if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY))) - && ((tm->tm_year >= 1) && (tm->tm_year <= 31))) - { - tm->tm_mday = tm->tm_year; - *tmask = DTK_M(DAY); - } - - tm->tm_year = val; - } - /* already have year? then could be month */ - else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH))) - && ((val >= 1) && (val <= 12))) - { - *tmask = DTK_M(MONTH); - tm->tm_mon = val; - /* no year and EuroDates enabled? then could be day */ - } - else if ((EuroDates || (fmask & DTK_M(MONTH))) - && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY))) - && ((val >= 1) && (val <= 31))) - { - *tmask = DTK_M(DAY); - tm->tm_mday = val; - } - else if ((!(fmask & DTK_M(MONTH))) - && ((val >= 1) && (val <= 12))) - { - *tmask = DTK_M(MONTH); - tm->tm_mon = val; - } - else if ((!(fmask & DTK_M(DAY))) - && ((val >= 1) && (val <= 31))) - { - *tmask = DTK_M(DAY); - tm->tm_mday = val; - } - else if (!(fmask & DTK_M(YEAR))) - { - *tmask = DTK_M(YEAR); - tm->tm_year = val; - - /* adjust ONLY if exactly two digits... */ - *is2digits = (flen == 2); - } - else - return -1; - - return 0; -} /* DecodeNumber() */ - - -/* DecodeNumberField() - * Interpret numeric string as a concatenated date field. - */ -static int -DecodeNumberField(int len, char *str, int fmask, - int *tmask, struct tm * tm, double *fsec, int *is2digits) -{ - char *cp; - - /* yyyymmdd? */ - if (len == 8) - { - *tmask = DTK_DATE_M; - - tm->tm_mday = atoi(str + 6); - *(str + 6) = '\0'; - tm->tm_mon = atoi(str + 4); - *(str + 4) = '\0'; - tm->tm_year = atoi(str + 0); - /* yymmdd or hhmmss? */ - } - else if (len == 6) - { - if (fmask & DTK_DATE_M) - { - *tmask = DTK_TIME_M; - tm->tm_sec = atoi(str + 4); - *(str + 4) = '\0'; - tm->tm_min = atoi(str + 2); - *(str + 2) = '\0'; - tm->tm_hour = atoi(str + 0); - } - else - { - *tmask = DTK_DATE_M; - tm->tm_mday = atoi(str + 4); - *(str + 4) = '\0'; - tm->tm_mon = atoi(str + 2); - *(str + 2) = '\0'; - tm->tm_year = atoi(str + 0); - *is2digits = TRUE; - } - - } - else if ((len == 5) && !(fmask & DTK_DATE_M)) - { - *tmask = DTK_DATE_M; - tm->tm_mday = atoi(str + 2); - *(str + 2) = '\0'; - tm->tm_mon = 1; - tm->tm_year = atoi(str + 0); - *is2digits = TRUE; - } - else if (strchr(str, '.') != NULL) - { - *tmask = DTK_TIME_M; - tm->tm_sec = strtod((str + 4), &cp); - if (cp == (str + 4)) - return -1; - if (*cp == '.') - *fsec = strtod(cp, NULL); - *(str + 4) = '\0'; - tm->tm_min = strtod((str + 2), &cp); - *(str + 2) = '\0'; - tm->tm_hour = strtod((str + 0), &cp); - - } - else - return -1; - - return 0; -} /* DecodeNumberField() */ - - -/* DecodeTimezone() - * Interpret string as a numeric timezone. - */ -static int -DecodeTimezone(char *str, int *tzp) -{ - int tz; - int hr, - min; - char *cp; - int len; - - /* assume leading character is "+" or "-" */ - hr = strtol((str + 1), &cp, 10); - - /* explicit delimiter? */ - if (*cp == ':') - { - min = strtol((cp + 1), &cp, 10); - - /* otherwise, might have run things together... */ - } - else if ((*cp == '\0') && ((len = strlen(str)) > 3)) - { - min = strtol((str + len - 2), &cp, 10); - *(str + len - 2) = '\0'; - hr = strtol((str + 1), &cp, 10); - - } - else - min = 0; - - tz = (hr * 60 + min) * 60; - if (*str == '-') - tz = -tz; - - *tzp = -tz; - return *cp != '\0'; -} /* DecodeTimezone() */ - - -/* DecodeSpecial() - * Decode text string using lookup table. - * Implement a cache lookup since it is likely that dates - * will be related in format. - */ -static int -DecodeSpecial(int field, char *lowtoken, int *val) -{ - int type; - datetkn *tp; - -#if USE_DATE_CACHE - if ((datecache[field] != NULL) - && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0)) - tp = datecache[field]; - else - { -#endif - tp = datebsearch(lowtoken, datetktbl, szdatetktbl); -#if USE_DATE_CACHE - } - datecache[field] = tp; -#endif - if (tp == NULL) - { - type = IGNORE; - *val = 0; - } - else - { - type = tp->type; - switch (type) - { - case TZ: - case DTZ: - case DTZMOD: - *val = FROMVAL(tp); - break; - - default: - *val = tp->value; - break; - } - } - - return type; -} /* DecodeSpecial() */ - - -/* DecodeDateDelta() - * Interpret previously parsed fields for general time interval. - * Return 0 if decoded and -1 if problems. - * - * Allow "date" field DTK_DATE since this could be just - * an unsigned floating point number. - thomas 1997-11-16 - * - * Allow ISO-style time span, with implicit units on number of days - * preceeding an hh:mm:ss field. - thomas 1998-04-30 - */ -int -DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec) -{ - int is_before = FALSE; - - char *cp; - int fmask = 0, - tmask, - type; - int i; - int flen, - val; - double fval; - double sec; - - *dtype = DTK_DELTA; - - type = DTK_SECOND; - tm->tm_year = 0; - tm->tm_mon = 0; - tm->tm_mday = 0; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - *fsec = 0; - - /* read through list backwards to pick up units before values */ - for (i = nf - 1; i >= 0; i--) - { - switch (ftype[i]) - { - case DTK_TIME: - if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) - return -1; - type = DTK_DAY; - break; - - case DTK_TZ: - - /* - * Timezone is a token with a leading sign character and - * otherwise the same as a non-signed numeric field - */ - case DTK_DATE: - case DTK_NUMBER: - val = strtol(field[i], &cp, 10); - if (*cp == '.') - { - fval = strtod(cp, &cp); - if (*cp != '\0') - return -1; - - if (val < 0) - fval = -(fval); - } - else if (*cp == '\0') - fval = 0; - else - return -1; - - flen = strlen(field[i]); - tmask = 0; /* DTK_M(type); */ - - switch (type) - { - case DTK_MICROSEC: - *fsec += ((val + fval) * 1e-6); - break; - - case DTK_MILLISEC: - *fsec += ((val + fval) * 1e-3); - break; - - case DTK_SECOND: - tm->tm_sec += val; - *fsec += fval; - tmask = DTK_M(SECOND); - break; - - case DTK_MINUTE: - tm->tm_min += val; - if (fval != 0) - tm->tm_sec += (fval * 60); - tmask = DTK_M(MINUTE); - break; - - case DTK_HOUR: - tm->tm_hour += val; - if (fval != 0) - tm->tm_sec += (fval * 3600); - tmask = DTK_M(HOUR); - break; - - case DTK_DAY: - tm->tm_mday += val; - if (fval != 0) - tm->tm_sec += (fval * 86400); - tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY)); - break; - - case DTK_WEEK: - tm->tm_mday += val * 7; - if (fval != 0) - tm->tm_sec += (fval * (7 * 86400)); - tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY)); - break; - - case DTK_MONTH: - tm->tm_mon += val; - if (fval != 0) - tm->tm_sec += (fval * (30 * 86400)); - tmask = DTK_M(MONTH); - break; - - case DTK_YEAR: - tm->tm_year += val; - if (fval != 0) - tm->tm_mon += (fval * 12); - tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); - break; - - case DTK_DECADE: - tm->tm_year += val * 10; - if (fval != 0) - tm->tm_mon += (fval * 120); - tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); - break; - - case DTK_CENTURY: - tm->tm_year += val * 100; - if (fval != 0) - tm->tm_mon += (fval * 1200); - tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); - break; - - case DTK_MILLENIUM: - tm->tm_year += val * 1000; - if (fval != 0) - tm->tm_mon += (fval * 12000); - tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); - break; - - default: - return -1; - } - break; - - case DTK_STRING: - case DTK_SPECIAL: - type = DecodeUnits(i, field[i], &val); - if (type == IGNORE) - continue; - - tmask = 0; /* DTK_M(type); */ - switch (type) - { - case UNITS: - type = val; - break; - - case AGO: - is_before = TRUE; - type = val; - break; - - case RESERV: - tmask = (DTK_DATE_M || DTK_TIME_M); - *dtype = val; - break; - - default: - return -1; - } - break; - - default: - return -1; - } - - if (tmask & fmask) - return -1; - fmask |= tmask; - } - - if (*fsec != 0) - { - TMODULO(*fsec, sec, 1e0); - tm->tm_sec += sec; - } - - if (is_before) - { - *fsec = -(*fsec); - tm->tm_sec = -(tm->tm_sec); - tm->tm_min = -(tm->tm_min); - tm->tm_hour = -(tm->tm_hour); - tm->tm_mday = -(tm->tm_mday); - tm->tm_mon = -(tm->tm_mon); - tm->tm_year = -(tm->tm_year); - } - - /* ensure that at least one time field has been found */ - return (fmask != 0) ? 0 : -1; -} /* DecodeDateDelta() */ - - -/* DecodeUnits() - * Decode text string using lookup table. - * This routine supports time interval decoding. - */ -static int -DecodeUnits(int field, char *lowtoken, int *val) -{ - int type; - datetkn *tp; - -#if USE_DATE_CACHE - if ((deltacache[field] != NULL) - && (strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0)) - tp = deltacache[field]; - else - { -#endif - tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl); -#if USE_DATE_CACHE - } - deltacache[field] = tp; -#endif - if (tp == NULL) - { - type = IGNORE; - *val = 0; - } - else - { - type = tp->type; - if ((type == TZ) || (type == DTZ)) - *val = FROMVAL(tp); - else - *val = tp->value; - } - - return type; -} /* DecodeUnits() */ - - -/* datebsearch() - * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this - * is WAY faster than the generic bsearch(). - */ -static 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; -} - - -/* EncodeSpecialDateTime() - * Convert reserved datetime data type to string. - */ -static int -EncodeSpecialDateTime(DateTime dt, char *str) -{ - if (DATETIME_IS_RESERVED(dt)) - { - if (DATETIME_IS_INVALID(dt)) - strcpy(str, INVALID); - else if (DATETIME_IS_NOBEGIN(dt)) - strcpy(str, EARLY); - else if (DATETIME_IS_NOEND(dt)) - strcpy(str, LATE); - else if (DATETIME_IS_CURRENT(dt)) - strcpy(str, DCURRENT); - else if (DATETIME_IS_EPOCH(dt)) - strcpy(str, EPOCH); - else - strcpy(str, INVALID); - return TRUE; - } - - return FALSE; -} /* EncodeSpecialDateTime() */ - - -/* 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 -datetime_is_epoch(double j) -{ - static union - { - double epoch; - unsigned char c[8]; - } u; - - u.c[0] = 0x80; /* sign bit */ - u.c[1] = 0x10; /* DBL_MIN */ - - return j == u.epoch; -} -int -datetime_is_current(double j) -{ - static union - { - double current; - unsigned char c[8]; - } u; - - u.c[1] = 0x10; /* DBL_MIN */ - - return j == u.current; -} - -#endif diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index e8f6d2cd477..7281d4f4dce 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -1,45 +1,45 @@ /* ----------------------------------------------------------------------- * formatting.c * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.3 2000/02/08 15:56:55 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.4 2000/02/16 17:24:48 thomas Exp $ * * * Portions Copyright (c) 1999-2000, PostgreSQL, Inc * * - * TO_CHAR(); TO_DATETIME(); TO_DATE(); TO_NUMBER(); + * TO_CHAR(); TO_TIMESTAMP(); TO_DATE(); TO_NUMBER(); * - * The PostgreSQL routines for a DateTime/int/float/numeric formatting, + * The PostgreSQL routines for a timestamp/int/float/numeric formatting, * inspire with Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines. * * * Cache & Memory: * Routines use (itself) internal cache for format pictures. If - * new format arg is same as a last format string, routines not + * new format arg is same as a last format string, routines do not * call the format-parser. * - * The cache use static buffer and is persistent across transactions. If - * format-picture is bigger than cache buffer, parser is called always. + * The cache uses a static buffer and is persistent across transactions. + * If format-picture is bigger than cache buffer, parser is called always. * * NOTE for Number version: * All in this version is implemented as keywords ( => not used * suffixes), because a format picture is for *one* item (number) - * only. It not is as a datetime version, where each keyword (can) + * only. It not is as a timestamp version, where each keyword (can) * has suffix. * - * NOTE for DateTime version: - * In this modul is *not* used POSIX 'struct tm' type, but + * NOTE for Timestamp routines: + * In this module the POSIX 'struct tm' type is *not* used, but rather * PgSQL type, which has tm_mon based on one (*non* zero) and * year *not* based on 1900, but is used full year number. - * Modul support AC / BC years. + * Module supports AC / BC years. * * Supported types for to_char(): * - * Timestamp, DateTime, Numeric, int4, int8, float4, float8 + * Timestamp, Numeric, int4, int8, float4, float8 * * Supported types for reverse conversion: * - * Datetime - to_datetime() + * Timestamp - to_timestamp() * Date - to_date() * Numeric - to_number() * @@ -108,7 +108,7 @@ #define MAXDOUBLEWIDTH 128 /* ---------- - * External (defined in PgSQL dt.c (datetime utils)) + * External (defined in PgSQL dt.c (timestamp utils)) * ---------- */ extern char *months[], /* month abbreviation */ @@ -1377,14 +1377,14 @@ dch_time(int arg, char *inout, int suf, int flag, FormatNode *node) str_numth(p_inout, inout, S_TH_TYPE(suf)); return strlen(p_inout)-1; } else if (flag == FROM_CHAR) - elog(ERROR, "to_datatime()/to_timestamp(): SSSS is not supported"); + elog(ERROR, "to_datatime(): SSSS is not supported"); } return -1; } #define CHECK_SEQ_SEARCH(_l, _s) { \ if (_l <= 0) { \ - elog(ERROR, "to_datatime()/to_timestamp(): bad value for %s", _s); \ + elog(ERROR, "to_datatime(): bad value for %s", _s); \ } \ } @@ -1600,7 +1600,7 @@ dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) return 1; } else if (flag == FROM_CHAR) - elog(ERROR, "to_datatime()/to_timestamp(): WW is not supported"); + elog(ERROR, "to_datatime(): WW is not supported"); case DCH_Q: if (flag == TO_CHAR) { sprintf(inout, "%d", (tm->tm_mon-1)/3+1); @@ -1611,7 +1611,7 @@ dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) return 0; } else if (flag == FROM_CHAR) - elog(ERROR, "to_datatime()/to_timestamp(): Q is not supported"); + elog(ERROR, "to_datatime(): Q is not supported"); case DCH_CC: if (flag == TO_CHAR) { @@ -1625,7 +1625,7 @@ dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) return strlen(p_inout)-1; } else if (flag == FROM_CHAR) - elog(ERROR, "to_datatime()/to_timestamp(): CC is not supported"); + elog(ERROR, "to_datatime(): CC is not supported"); case DCH_Y_YYY: if (flag == TO_CHAR) { i= YEAR_ABS(tm->tm_year) / 1000; @@ -1764,7 +1764,7 @@ dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) return 0; } else if (flag == FROM_CHAR) - elog(ERROR, "to_datatime()/to_timestamp(): W is not supported"); + elog(ERROR, "to_datatime(): W is not supported"); case DCH_J: if (flag == TO_CHAR) { @@ -1773,7 +1773,7 @@ dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) str_numth(p_inout, inout, S_TH_TYPE(suf)); return strlen(p_inout)-1; } else if (flag == FROM_CHAR) - elog(ERROR, "to_datatime()/to_timestamp(): J is not supported"); + elog(ERROR, "to_datatime(): J is not supported"); } return -1; } @@ -1783,11 +1783,11 @@ dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) ***************************************************************************/ /* ------------------- - * DATETIME to_char() + * TIMESTAMP to_char() * ------------------- */ text * -datetime_to_char(DateTime *dt, text *fmt) +timestamp_to_char(Timestamp *dt, text *fmt) { static FormatNode CacheFormat[ DCH_CACHE_SIZE +1]; static char CacheStr[ DCH_CACHE_SIZE +1]; @@ -1814,14 +1814,14 @@ datetime_to_char(DateTime *dt, text *fmt) tm->tm_mday =1; tm->tm_isdst =0; tm->tm_mon =1; - if (DATETIME_IS_EPOCH(*dt)) + if (TIMESTAMP_IS_EPOCH(*dt)) { - datetime2tm(SetDateTime(*dt), NULL, tm, &fsec, NULL); - } else if (DATETIME_IS_CURRENT(*dt)) { - datetime2tm(SetDateTime(*dt), &tz, tm, &fsec, &tzn); + timestamp2tm(SetTimestamp(*dt), NULL, tm, &fsec, NULL); + } else if (TIMESTAMP_IS_CURRENT(*dt)) { + timestamp2tm(SetTimestamp(*dt), &tz, tm, &fsec, &tzn); } else { - if (datetime2tm(*dt, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "to_char(): Unable to convert datetime to tm"); + if (timestamp2tm(*dt, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "to_char(): Unable to convert timestamp to tm"); } tm->tm_wday = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1) % 7; @@ -1902,32 +1902,22 @@ datetime_to_char(DateTime *dt, text *fmt) } -/* ------------------- - * TIMESTAMP to_char() - * ------------------- - */ -text * -timestamp_to_char(time_t dt, text *fmt) -{ - return datetime_to_char( timestamp_datetime(dt), fmt); -} - /* --------------------- - * TO_DATETIME() + * TO_TIMESTAMP() * - * Make DateTime from date_str which is formated at argument 'fmt' - * ( to_datetime is reverse to_char() ) + * Make Timestamp from date_str which is formated at argument 'fmt' + * ( to_timestamp is reverse to_char() ) * --------------------- */ -DateTime * -to_datetime(text *date_str, text *fmt) +Timestamp * +to_timestamp(text *date_str, text *fmt) { static FormatNode CacheFormat[ DCH_CACHE_SIZE +1]; static char CacheStr[ DCH_CACHE_SIZE +1]; FormatNode *format; int flag=0; - DateTime *result; + Timestamp *result; char *str; int len=0, fsec=0, @@ -1942,7 +1932,7 @@ to_datetime(text *date_str, text *fmt) tm->tm_mday =1; tm->tm_isdst =0; tm->tm_mon =1; - result = palloc(sizeof(DateTime)); + result = palloc(sizeof(Timestamp)); len = VARSIZE(fmt) - VARHDRSZ; @@ -2060,8 +2050,8 @@ to_datetime(text *date_str, text *fmt) #ifdef DEBUG_TO_FROM_CHAR NOTICE_TM; #endif - if (tm2datetime(tm, fsec, &tz, result) != 0) - elog(ERROR, "to_datatime()/to_timestamp(): can't convert 'tm' to datetime."); + if (tm2timestamp(tm, fsec, &tz, result) != 0) + elog(ERROR, "to_datatime(): can't convert 'tm' to timestamp."); return result; } @@ -2074,18 +2064,7 @@ to_datetime(text *date_str, text *fmt) DateADT to_date(text *date_str, text *fmt) { - return datetime_date( to_datetime(date_str, fmt) ); -} - -/* ---------- - * TO_TIMESTAMP - * Make timestamp from date_str which is formated at argument 'fmt' - * ---------- - */ -time_t -to_timestamp(text *date_str, text *fmt) -{ - return datetime_timestamp( to_datetime(date_str, fmt) ); + return timestamp_date( to_timestamp(date_str, fmt) ); } /********************************************************************** diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c index 71a636feeb3..28a94f5e168 100644 --- a/src/backend/utils/adt/nabstime.c +++ b/src/backend/utils/adt/nabstime.c @@ -1,23 +1,41 @@ /* * nabstime.c - * parse almost any absolute date getdate(3) can (& some it can't) + * Utilities for the built-in type "AbsoluteTime". + * Functions for the built-in type "RelativeTime". + * Functions for the built-in type "TimeInterval". * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: nabstime.c,v 1.65 2000/01/26 05:57:14 momjian Exp $ * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.66 2000/02/16 17:24:48 thomas Exp $ + * + * NOTES + * + *------------------------------------------------------------------------- */ #include <ctype.h> +#include <time.h> +#include <sys/time.h> #include <sys/types.h> #include "postgres.h" #ifdef HAVE_FLOAT_H #include <float.h> #endif + #ifdef HAVE_LIMITS_H #include <limits.h> +#ifndef MAXINT +#define MAXINT INT_MAX +#endif +#else +#ifdef HAVE_VALUES_H +#include <values.h> #endif +#endif + #ifndef USE_POSIX_TIME #include <sys/timeb.h> #endif @@ -26,11 +44,80 @@ #include "miscadmin.h" #include "utils/builtins.h" + +#if 0 static AbsoluteTime tm2abstime(struct tm * tm, int tz); +#endif + #define MIN_DAYNUM -24856 /* December 13, 1901 */ #define MAX_DAYNUM 24854 /* January 18, 2038 */ +#define INVALID_RELTIME_STR "Undefined RelTime" +#define INVALID_RELTIME_STR_LEN (sizeof(INVALID_RELTIME_STR)-1) +#define RELTIME_LABEL '@' +#define RELTIME_PAST "ago" +#define DIRMAXLEN (sizeof(RELTIME_PAST)-1) + +/* + * Unix epoch is Jan 1 00:00:00 1970. + * Postgres knows about times sixty-eight years on either side of that + * for these 4-byte types. + * + * "tinterval" is two 4-byte fields. + * Definitions for parsing tinterval. + */ + +#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); /* GetCurrentAbsoluteTime() * Get the current system time. Set timezone parameters if not specified elsewhere. @@ -491,10 +578,10 @@ abstimege(AbsoluteTime t1, AbsoluteTime t2) /* datetime_abstime() - * Convert datetime to abstime. + * Convert timestamp to abstime. */ AbsoluteTime -datetime_abstime(DateTime *datetime) +timestamp_abstime(Timestamp *timestamp) { AbsoluteTime result; @@ -502,35 +589,35 @@ datetime_abstime(DateTime *datetime) struct tm tt, *tm = &tt; - if (!PointerIsValid(datetime)) + if (!PointerIsValid(timestamp)) { result = INVALID_ABSTIME; } - else if (DATETIME_IS_INVALID(*datetime)) + else if (TIMESTAMP_IS_INVALID(*timestamp)) { result = INVALID_ABSTIME; } - else if (DATETIME_IS_NOBEGIN(*datetime)) + else if (TIMESTAMP_IS_NOBEGIN(*timestamp)) { result = NOSTART_ABSTIME; } - else if (DATETIME_IS_NOEND(*datetime)) + else if (TIMESTAMP_IS_NOEND(*timestamp)) { result = NOEND_ABSTIME; } else { - if (DATETIME_IS_RELATIVE(*datetime)) + if (TIMESTAMP_IS_RELATIVE(*timestamp)) { - datetime2tm(SetDateTime(*datetime), NULL, tm, &fsec, NULL); + timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL); result = tm2abstime(tm, 0); } - else if (datetime2tm(*datetime, NULL, tm, &fsec, NULL) == 0) + else if (timestamp2tm(*timestamp, NULL, tm, &fsec, NULL) == 0) { result = tm2abstime(tm, 0); @@ -542,39 +629,39 @@ datetime_abstime(DateTime *datetime) }; return result; -} /* datetime_abstime() */ +} /* timestamp_abstime() */ -/* abstime_datetime() - * Convert abstime to datetime. +/* abstime_timestamp() + * Convert abstime to timestamp. */ -DateTime * -abstime_datetime(AbsoluteTime abstime) +Timestamp * +abstime_timestamp(AbsoluteTime abstime) { - DateTime *result; + Timestamp *result; - if (!PointerIsValid(result = palloc(sizeof(DateTime)))) - elog(ERROR, "Unable to allocate space to convert abstime to datetime"); + if (!PointerIsValid(result = palloc(sizeof(Timestamp)))) + elog(ERROR, "Unable to allocate space to convert abstime to timestamp"); switch (abstime) { case INVALID_ABSTIME: - DATETIME_INVALID(*result); + TIMESTAMP_INVALID(*result); break; case NOSTART_ABSTIME: - DATETIME_NOBEGIN(*result); + TIMESTAMP_NOBEGIN(*result); break; case NOEND_ABSTIME: - DATETIME_NOEND(*result); + TIMESTAMP_NOEND(*result); break; case EPOCH_ABSTIME: - DATETIME_EPOCH(*result); + TIMESTAMP_EPOCH(*result); break; case CURRENT_ABSTIME: - DATETIME_CURRENT(*result); + TIMESTAMP_CURRENT(*result); break; default: @@ -583,4 +670,1195 @@ abstime_datetime(AbsoluteTime abstime) }; return result; -} /* abstime_datetime() */ +} /* abstime_timestamp() */ + + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * reltimein - converts a reltime string in an internal format + */ +RelativeTime +reltimein(char *str) +{ + RelativeTime result; + + struct tm tt, + *tm = &tt; + double fsec; + int dtype; + char *field[MAXDATEFIELDS]; + int nf, + 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); + + 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; + + default: + return INVALID_RELTIME; + } + + elog(ERROR, "Bad reltime (internal coding error) '%s'", str); + return INVALID_RELTIME; +} /* reltimein() */ + + +/* + * reltimeout - converts the internal format to a reltime string + */ +char * +reltimeout(RelativeTime time) +{ + char *result; + struct tm tt, + *tm = &tt; + char buf[MAXDATELEN + 1]; + + if (time == INVALID_RELTIME) + { + strcpy(buf, INVALID_RELTIME_STR); + + } + else + { + reltime2tm(time, tm); + EncodeTimeSpan(tm, 0, DateStyle, buf); + } + + result = palloc(strlen(buf) + 1); + strcpy(result, buf); + + return result; +} /* reltimeout() */ + + +static void +reltime2tm(RelativeTime time, struct tm * tm) +{ + 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; +} /* reltime2tm() */ + +#ifdef NOT_USED +int +dummyfunc() +{ + char *timestring; + long quantity; + int i; + int unitnr; + + timestring = (char *) palloc(Max(strlen(INVALID_RELTIME_STR), + UNITMAXLEN) + 1); + if (timevalue == INVALID_RELTIME) + { + strcpy(timestring, INVALID_RELTIME_STR); + return timestring; + } + + if (timevalue == 0) + i = 1; /* unit = 'seconds' */ + else + for (i = 12; i >= 0; i = i - 2) + if ((timevalue % sec_tab[i]) == 0) + break; /* appropriate unit found */ + unitnr = i; + quantity = (timevalue / sec_tab[unitnr]); + if (quantity > 1 || quantity < -1) + unitnr++; /* adjust index for PLURAL of unit */ + if (quantity >= 0) + sprintf(timestring, "%c %lu %s", RELTIME_LABEL, + quantity, unit_tab[unitnr]); + else + sprintf(timestring, "%c %lu %s %s", RELTIME_LABEL, + (quantity * -1), unit_tab[unitnr], RELTIME_PAST); + return timestring; +} + +#endif + + +/* + * tintervalin - converts an interval string to an internal format + */ +TimeInterval +tintervalin(char *intervalstr) +{ + int error; + AbsoluteTime i_start, + i_end, + t1, + t2; + TimeInterval interval; + + interval = (TimeInterval) palloc(sizeof(TimeIntervalData)); + error = istinterval(intervalstr, &t1, &t2); + if (error == 0) + interval->status = T_INTERVAL_INVAL; + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + interval->status = T_INTERVAL_INVAL; /* undefined */ + else + { + i_start = ABSTIMEMIN(t1, t2); + i_end = ABSTIMEMAX(t1, t2); + interval->data[0] = i_start; + interval->data[1] = i_end; + interval->status = T_INTERVAL_VALID; + } + return interval; +} + + +/* + * tintervalout - converts an internal interval format to a string + * + */ +char * +tintervalout(TimeInterval interval) +{ + char *i_str, + *p; + + i_str = (char *) palloc(T_INTERVAL_LEN); /* ['...' '...'] */ + strcpy(i_str, "[\""); + if (interval->status == T_INTERVAL_INVAL) + strcat(i_str, INVALID_INTERVAL_STR); + else + { + p = nabstimeout(interval->data[0]); + strcat(i_str, p); + pfree(p); + strcat(i_str, "\" \""); + p = nabstimeout(interval->data[1]); + strcat(i_str, p); + pfree(p); + } + strcat(i_str, "\"]\0"); + return i_str; +} + + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + +RelativeTime +interval_reltime(Interval *interval) +{ + RelativeTime time; + int year, + month; + double span; + + if (!PointerIsValid(interval)) + time = INVALID_RELTIME; + + if (INTERVAL_IS_INVALID(*interval)) + { + time = INVALID_RELTIME; + + } + else + { + if (interval->month == 0) + { + year = 0; + month = 0; + + } + else if (abs(interval->month) >= 12) + { + year = (interval->month / 12); + month = (interval->month % 12); + + } + else + { + year = 0; + month = interval->month; + } + + span = (((((double) 365 * year) + ((double) 30 * month)) * 86400) + interval->time); + + time = (((span > INT_MIN) && (span < INT_MAX)) ? span : INVALID_RELTIME); + } + + return time; +} /* interval_reltime() */ + + +Interval * +reltime_interval(RelativeTime reltime) +{ + Interval *result; + int year, + month; + + if (!PointerIsValid(result = palloc(sizeof(Interval)))) + elog(ERROR, "Memory allocation failed, can't convert reltime to interval"); + + switch (reltime) + { + case INVALID_RELTIME: + INTERVAL_INVALID(*result); + break; + + default: + TMODULO(reltime, year, 31536000); + TMODULO(reltime, month, 2592000); + + result->time = reltime; + result->month = ((12 * year) + month); + } + + return result; +} /* reltime_interval() */ + + +/* + * mktinterval - creates a time interval with endpoints t1 and t2 + */ +TimeInterval +mktinterval(AbsoluteTime t1, AbsoluteTime t2) +{ + AbsoluteTime tstart = ABSTIMEMIN(t1, t2), + tend = ABSTIMEMAX(t1, t2); + TimeInterval interval; + + interval = (TimeInterval) palloc(sizeof(TimeIntervalData)); + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + interval->status = T_INTERVAL_INVAL; + else + { + interval->status = T_INTERVAL_VALID; + interval->data[0] = tstart; + interval->data[1] = tend; + } + + 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; +} + + +/* + * intinterval - returns 1, iff absolute date is in the interval + */ +int +intinterval(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; +} + +/* + * tintervalrel - returns relative time corresponding to interval + */ +RelativeTime +tintervalrel(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; +} + + +/* + * tintervalsame - returns 1, iff interval i1 is same as interval i2 + * Check begin and end time. + */ +bool +tintervalsame(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])); +} /* tintervalsame() */ + + +/* + * tintervaleq - returns 1, iff interval i1 is equal to interval i2 + * Check length of intervals. + */ +bool +tintervaleq(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); +} /* tintervaleq() */ + +/* + * tintervalne - returns 1, iff interval i1 is not equal to interval i2 + * Check length of intervals. + */ +bool +tintervalne(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); +} /* tintervalne() */ + +/* + * tintervallt - returns TRUE, iff interval i1 is less than interval i2 + * Check length of intervals. + */ +bool +tintervallt(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); +} /* tintervallt() */ + +/* + * tintervalle - returns TRUE, iff interval i1 is less than or equal to interval i2 + * Check length of intervals. + */ +bool +tintervalle(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); +} /* tintervalle() */ + +/* + * tintervalgt - returns TRUE, iff interval i1 is less than interval i2 + * Check length of intervals. + */ +bool +tintervalgt(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); +} /* tintervalgt() */ + +/* + * tintervalge - returns TRUE, iff interval i1 is less than or equal to interval i2 + * Check length of intervals. + */ +bool +tintervalge(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); +} /* tintervalge() */ + + +/* + * tintervalleneq - returns 1, iff length of interval i is equal to + * reltime t + */ +bool +tintervalleneq(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return 0; + rt = tintervalrel(i); + return rt != INVALID_RELTIME && rt == t; +} + +/* + * tintervallenne - returns 1, iff length of interval i is not equal + * to reltime t + */ +bool +tintervallenne(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return 0; + rt = tintervalrel(i); + return rt != INVALID_RELTIME && rt != t; +} + +/* + * tintervallenlt - returns 1, iff length of interval i is less than + * reltime t + */ +bool +tintervallenlt(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return 0; + rt = tintervalrel(i); + return rt != INVALID_RELTIME && rt < t; +} + +/* + * tintervallengt - returns 1, iff length of interval i is greater than + * reltime t + */ +bool +tintervallengt(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return 0; + rt = tintervalrel(i); + return rt != INVALID_RELTIME && rt > t; +} + +/* + * tintervallenle - returns 1, iff length of interval i is less or equal + * than reltime t + */ +bool +tintervallenle(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return 0; + rt = tintervalrel(i); + return rt != INVALID_RELTIME && rt <= t; +} + +/* + * tintervallenge - returns 1, iff length of interval i is greater or + * equal than reltime t + */ +bool +tintervallenge(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return 0; + rt = tintervalrel(i); + return rt != INVALID_RELTIME && rt >= t; +} + +/* + * tintervalct - returns 1, iff interval i1 contains interval i2 + */ +bool +tintervalct(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])); +} + +/* + * tintervalov - returns 1, iff interval i1 (partially) overlaps i2 + */ +bool +tintervalov(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]))); +} + +/* + * tintervalstart - returns the start of interval i + */ +AbsoluteTime +tintervalstart(TimeInterval i) +{ + if (i->status == T_INTERVAL_INVAL) + return INVALID_ABSTIME; + return i->data[0]; +} + +/* + * tintervalend - returns the end of interval i + */ +AbsoluteTime +tintervalend(TimeInterval i) +{ + if (i->status == T_INTERVAL_INVAL) + return INVALID_ABSTIME; + return i->data[1]; +} + + +/***************************************************************************** + * PRIVATE ROUTINES * + *****************************************************************************/ + +#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) +{ + struct tm tt, + *tm = &tt; + double fsec; + int dtype; + char *field[MAXDATEFIELDS]; + int nf, + ftype[MAXDATEFIELDS]; + char lowstr[MAXDATELEN + 1]; + + if (!PointerIsValid(str)) + return 0; + + if (strlen(str) > MAXDATELEN) + return 0; + + if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) + || (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) + return 0; + + switch (dtype) + { + case (DTK_DELTA): + return (abs(tm->tm_year) <= 68) ? 1 : 0; + break; + + case (DTK_INVALID): + return 2; + break; + + default: + return 0; + break; + } + + return 0; +} /* isreltime() */ + +#endif + +#ifdef NOT_USED +int +dummyfunc() +{ + char *p; + char c; + int i; + char unit[UNITMAXLEN]; + char direction[DIRMAXLEN]; + int localSign; + int localUnitNumber; + long localQuantity; + + if (!PointerIsValid(sign)) + sign = &localSign; + + if (!PointerIsValid(unitnr)) + unitnr = &localUnitNumber; + + if (!PointerIsValid(quantity)) + quantity = &localQuantity; + + unit[0] = '\0'; + direction[0] = '\0'; + p = timestring; + /* skip leading blanks */ + while ((c = *p) != '\0') + { + if (c != ' ') + break; + p++; + } + + /* Test whether 'invalid time' identifier or not */ + if (!strncmp(INVALID_RELTIME_STR, p, strlen(INVALID_RELTIME_STR) + 1)) + return 2; /* correct 'invalid time' identifier found */ + + /* handle label of relative time */ + if (c != RELTIME_LABEL) + return 0; /* syntax error */ + c = *++p; + if (c != ' ') + return 0; /* syntax error */ + p++; + /* handle the quantity */ + *quantity = 0; + for (;;) + { + c = *p; + if (isdigit(c)) + { + *quantity = *quantity * 10 + (c - '0'); + p++; + } + else + { + if (c == ' ') + break; /* correct quantity found */ + else + return 0; /* syntax error */ + } + } + + /* handle unit */ + p++; + i = 0; + for (;;) + { + c = *p; + if (c >= 'a' && c <= 'z' && i <= (UNITMAXLEN - 1)) + { + unit[i] = c; + p++; + i++; + } + else + { + 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()) + */ +static int +correct_unit(char *unit, int *unptr) +{ + int j = 0; + + while (j < NUNITS) + { + if (strncmp(unit, unit_tab[j], strlen(unit_tab[j])) == 0) + { + *unptr = j; + return 1; + } + j++; + } + return 0; /* invalid unit descriptor */ +} + +/* + * correct_dir - returns 1, iff direction is a correct identifier + * + * output parameter: + * signptr: points to -1 if dir corresponds to past tense + * else to 1 + */ +static int +correct_dir(char *direction, int *signptr) +{ + *signptr = 1; + if (strncmp(RELTIME_PAST, direction, strlen(RELTIME_PAST) + 1) == 0) + { + *signptr = -1; + return 1; + } + else + return 0; /* invalid direction descriptor */ +} + +#endif + +/* + * istinterval - returns 1, iff i_string is a valid interval descr. + * 0, iff i_string is NOT a valid interval desc. + * 2, iff any time is INVALID_ABSTIME + * + * output parameter: + * i_start, i_end: interval margins + * + * Time interval: + * `[' {` '} `'' <AbsTime> `'' {` '} `'' <AbsTime> `'' {` '} `]' + * + * OR `Undefined Range' (see also INVALID_INTERVAL_STR) + * + * where <AbsTime> satisfies the syntax of absolute time. + * + * e.g. [ ' Jan 18 1902' 'Jan 1 00:00:00 1970'] + */ +static int +istinterval(char *i_string, + AbsoluteTime *i_start, + AbsoluteTime *i_end) +{ + char *p, + *p1; + char c; + + p = i_string; + /* skip leading blanks up to '[' */ + while ((c = *p) != '\0') + { + if (IsSpace(c)) + p++; + else if (c != '[') + return 0; /* syntax error */ + else + break; + } + p++; + /* skip leading blanks up to "'" */ + while ((c = *p) != '\0') + { + if (IsSpace(c)) + p++; + else if (c != '"') + return 0; /* syntax error */ + else + break; + } + p++; + if (strncmp(INVALID_INTERVAL_STR, p, strlen(INVALID_INTERVAL_STR)) == 0) + return 0; /* undefined range, handled like a syntax + * err. */ + /* search for the end of the first date and change it to a NULL */ + p1 = p; + while ((c = *p1) != '\0') + { + if (c == '"') + { + *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; +} + + +/***************************************************************************** + * + *****************************************************************************/ + +int32 /* RelativeTime */ +int4reltime(int32 timevalue) +{ + return timevalue; +} + +/* + * 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; +} diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index cdbac4c7f13..77202c8308e 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -1,114 +1,2210 @@ -#include <time.h> +/*------------------------------------------------------------------------- + * + * timestamp.c + * Functions for the built-in SQL92 type "timestamp" and "interval". + * + * Portions Copyright (c) 1996-2000, PostgreSQL, Inc + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.22 2000/02/16 17:24:48 thomas Exp $ + * + *------------------------------------------------------------------------- + */ #include <ctype.h> +#include <math.h> +#include <sys/types.h> +#include <errno.h> #include "postgres.h" -#include "access/xact.h" +#ifdef HAVE_FLOAT_H +#include <float.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifndef USE_POSIX_TIME +#include <sys/timeb.h> +#endif + #include "miscadmin.h" #include "utils/builtins.h" -time_t -timestamp_in(const char *timestamp_str) + +#if 0 + + +static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm); +static int DecodeNumber(int flen, char *field, + int fmask, int *tmask, struct tm * tm, double *fsec, int *is2digits); +static int DecodeNumberField(int len, char *str, + int fmask, int *tmask, struct tm * tm, double *fsec, int *is2digits); +static int DecodeSpecial(int field, char *lowtoken, int *val); +static int DecodeTime(char *str, int fmask, int *tmask, + struct tm * tm, double *fsec); +static int DecodeTimezone(char *str, int *tzp); +static int DecodeUnits(int field, char *lowtoken, int *val); +static int EncodeSpecialTimestamp(Timestamp dt, char *str); +static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel); +static Timestamp dt2local(Timestamp dt, int timezone); +static int j2day(int jd); +static double time2t(const int hour, const int min, const double sec); +static int interval2tm(Interval span, struct tm * tm, float8 *fsec); +static int tm2interval(struct tm * tm, double fsec, Interval *span); + + +#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}; + +/* TMODULO() + * Macro to replace modf(), which is broken on some platforms. + */ +#define TMODULO(t,q,u) \ +do { \ + q = ((t < 0)? ceil(t / u): floor(t / u)); \ + if (q != 0) \ + t -= rint(q * u); \ +} while(0) + +static void GetEpochTime(struct tm * tm); + +#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)))))) + + +#endif + + +static double time2t(const int hour, const int min, const double sec); + + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* timestamp_in() + * Convert a string to internal form. + */ +Timestamp * +timestamp_in(char *str) { - int4 result; + Timestamp *result; + + double fsec; + struct tm tt, + *tm = &tt; + int tz; + int dtype; + int nf; + char *field[MAXDATEFIELDS]; + int ftype[MAXDATEFIELDS]; + char lowstr[MAXDATELEN + 1]; + + if (!PointerIsValid(str)) + elog(ERROR, "Bad (null) timestamp external representation"); + + if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) + || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0)) + elog(ERROR, "Bad timestamp external representation '%s'", str); + + result = palloc(sizeof(Timestamp)); + + switch (dtype) + { + case DTK_DATE: + if (tm2timestamp(tm, fsec, &tz, result) != 0) + elog(ERROR, "Timestamp out of range '%s'", str); + break; + + case DTK_EPOCH: + TIMESTAMP_EPOCH(*result); + break; + + case DTK_CURRENT: + TIMESTAMP_CURRENT(*result); + break; + + case DTK_LATE: + TIMESTAMP_NOEND(*result); + break; + + case DTK_EARLY: + TIMESTAMP_NOBEGIN(*result); + break; + + case DTK_INVALID: + TIMESTAMP_INVALID(*result); + break; - result = nabstimein((char *) timestamp_str); + default: + elog(ERROR, "Internal coding error, can't input timestamp '%s'", str); + } return result; -} +} /* timestamp_in() */ +/* timestamp_out() + * Convert a timestamp to external form. + */ char * -timestamp_out(time_t timestamp) +timestamp_out(Timestamp *dt) { char *result; int tz; - double fsec = 0; struct tm tt, *tm = &tt; + double fsec; + char *tzn; char buf[MAXDATELEN + 1]; - char zone[MAXDATELEN + 1], - *tzn = zone; - switch (timestamp) + if (!PointerIsValid(dt)) + return NULL; + + if (TIMESTAMP_IS_RESERVED(*dt)) { - case EPOCH_ABSTIME: - strcpy(buf, EPOCH); - break; - case INVALID_ABSTIME: - strcpy(buf, INVALID); - break; - case CURRENT_ABSTIME: - strcpy(buf, DCURRENT); - break; - case NOEND_ABSTIME: - strcpy(buf, LATE); - break; - case NOSTART_ABSTIME: - strcpy(buf, EARLY); + EncodeSpecialTimestamp(*dt, buf); + + } + else if (timestamp2tm(*dt, &tz, tm, &fsec, &tzn) == 0) + { + EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); + + } + else + EncodeSpecialTimestamp(DT_INVALID, buf); + + result = palloc(strlen(buf) + 1); + + strcpy(result, buf); + + return result; +} /* timestamp_out() */ + + +/* interval_in() + * Convert a string to internal form. + * + * External format(s): + * Uses the generic date/time parsing and decoding routines. + */ +Interval * +interval_in(char *str) +{ + Interval *span; + + double fsec; + struct tm tt, + *tm = &tt; + int dtype; + int nf; + char *field[MAXDATEFIELDS]; + int ftype[MAXDATEFIELDS]; + char lowstr[MAXDATELEN + 1]; + + 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; + + if (!PointerIsValid(str)) + elog(ERROR, "Bad (null) interval external representation"); + + if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) + || (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) + elog(ERROR, "Bad interval external representation '%s'", str); + + span = palloc(sizeof(Interval)); + + switch (dtype) + { + case DTK_DELTA: + if (tm2interval(tm, fsec, span) != 0) + { +#if NOT_USED + INTERVAL_INVALID(span); +#endif + elog(ERROR, "Bad interval external representation '%s'", str); + } break; + default: - abstime2tm(timestamp, &tz, tm, tzn); - EncodeDateTime(tm, fsec, &tz, &tzn, USE_ISO_DATES, buf); - break; + elog(ERROR, "Internal coding error, can't input interval '%s'", str); } + return span; +} /* interval_in() */ + +/* interval_out() + * Convert a time span to external form. + */ +char * +interval_out(Interval *span) +{ + char *result; + + struct tm tt, + *tm = &tt; + double fsec; + char buf[MAXDATELEN + 1]; + + if (!PointerIsValid(span)) + return NULL; + + if (interval2tm(*span, tm, &fsec) != 0) + return NULL; + + if (EncodeTimeSpan(tm, fsec, DateStyle, buf) != 0) + elog(ERROR, "Unable to format interval"); + result = palloc(strlen(buf) + 1); + strcpy(result, buf); return result; -} /* timestamp_out() */ +} /* interval_out() */ + -time_t +/* EncodeSpecialTimestamp() + * Convert reserved timestamp data type to string. + */ +int +EncodeSpecialTimestamp(Timestamp dt, char *str) +{ + if (TIMESTAMP_IS_RESERVED(dt)) + { + if (TIMESTAMP_IS_INVALID(dt)) + strcpy(str, INVALID); + else if (TIMESTAMP_IS_NOBEGIN(dt)) + strcpy(str, EARLY); + else if (TIMESTAMP_IS_NOEND(dt)) + strcpy(str, LATE); + else if (TIMESTAMP_IS_CURRENT(dt)) + strcpy(str, DCURRENT); + else if (TIMESTAMP_IS_EPOCH(dt)) + strcpy(str, EPOCH); + else + strcpy(str, INVALID); + return TRUE; + } + + return FALSE; +} /* EncodeSpecialTimestamp() */ + +Timestamp * now(void) { - time_t sec; + Timestamp *result; + AbsoluteTime sec; + + result = palloc(sizeof(Timestamp)); sec = GetCurrentTransactionStartTime(); - return sec; + + *result = (sec - ((date2j(2000, 1, 1) - date2j(1970, 1, 1)) * 86400)); + + return result; } +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() */ + + +/* timestamp2tm() + * Convert timestamp data type to POSIX time structure. + * Note that year is _not_ 1900-based, but is an explicit full value. + * Also, month is one-based, _not_ zero-based. + * Returns: + * 0 on success + * -1 on out of range + * + * For dates within the system-supported time_t range, convert to the + * local time zone. If out of this range, leave as GMT. - tgl 97/05/27 + */ +int +timestamp2tm(Timestamp dt, int *tzp, struct tm * tm, double *fsec, char **tzn) +{ + double date, + date0, + time, + sec; + time_t utime; + +#ifdef USE_POSIX_TIME + struct tm *tx; + +#endif + + date0 = date2j(2000, 1, 1); + + time = dt; + TMODULO(time, date, 86400e0); + + if (time < 0) + { + time += 86400; + date -= 1; + } + + /* Julian day routine does not work for negative Julian days */ + if (date < -date0) + return -1; + + /* add offset to go from J2000 back to standard Julian date */ + date += date0; + + j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + dt2time(time, &tm->tm_hour, &tm->tm_min, &sec); + + *fsec = JROUND(sec); + TMODULO(*fsec, tm->tm_sec, 1e0); + + if (tzp != NULL) + { + if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) + { + utime = (dt + (date0 - date2j(1970, 1, 1)) * 86400); + +#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_hour = tx->tm_hour; + tm->tm_min = tx->tm_min; +#if NOT_USED +/* XXX HACK + * Argh! My Linux box puts in a 1 second offset for dates less than 1970 + * but only if the seconds field was non-zero. So, don't copy the seconds + * field and instead carry forward from the original - tgl 97/06/18 + * Note that GNU/Linux uses the standard freeware zic package as do + * many other platforms so this may not be GNU/Linux/ix86-specific. + */ + tm->tm_sec = tx->tm_sec; +#endif + tm->tm_isdst = tx->tm_isdst; + +#if defined(HAVE_TM_ZONE) + tm->tm_gmtoff = tx->tm_gmtoff; + tm->tm_zone = tx->tm_zone; + + *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ + 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 + + } + else + { + *tzp = 0; + tm->tm_isdst = 0; + if (tzn != NULL) + *tzn = NULL; + } + + dt = dt2local(dt, *tzp); + + } + else + { + tm->tm_isdst = 0; + if (tzn != NULL) + *tzn = NULL; + } + + return 0; +} /* timestamp2tm() */ + + +/* tm2timestamp() + * Convert a tm structure to a timestamp data type. + * Note that year is _not_ 1900-based, but is an explicit full value. + * Also, month is one-based, _not_ zero-based. + */ +int +tm2timestamp(struct tm * tm, double fsec, int *tzp, Timestamp *result) +{ + + double date, + time; + + /* Julian day routines are not correct for negative Julian days */ + if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) + return -1; + + date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); + time = time2t(tm->tm_hour, tm->tm_min, (tm->tm_sec + fsec)); + *result = (date * 86400 + time); + if (tzp != NULL) + *result = dt2local(*result, -(*tzp)); + + return 0; +} /* tm2timestamp() */ + + +/* interval2tm() + * Convert a interval data type to a tm structure. + */ +int +interval2tm(Interval span, struct tm * tm, float8 *fsec) +{ + double time; + + if (span.month != 0) + { + tm->tm_year = span.month / 12; + tm->tm_mon = span.month % 12; + + } + else + { + tm->tm_year = 0; + tm->tm_mon = 0; + } + +#ifdef ROUND_ALL + time = JROUND(span.time); +#else + time = span.time; +#endif + + TMODULO(time, tm->tm_mday, 86400e0); + TMODULO(time, tm->tm_hour, 3600e0); + TMODULO(time, tm->tm_min, 60e0); + TMODULO(time, tm->tm_sec, 1e0); + *fsec = time; + + return 0; +} /* interval2tm() */ + +int +tm2interval(struct tm * tm, double fsec, Interval *span) +{ + span->month = ((tm->tm_year * 12) + tm->tm_mon); + span->time = ((((((tm->tm_mday * 24.0) + + tm->tm_hour) * 60.0) + + tm->tm_min) * 60.0) + + tm->tm_sec); + span->time = JROUND(span->time + fsec); + + return 0; +} /* tm2interval() */ + +static double +time2t(const int hour, const int min, const double sec) +{ + return (((hour * 60) + min) * 60) + sec; +} /* time2t() */ + +Timestamp +dt2local(Timestamp dt, int tz) +{ + dt -= tz; + dt = JROUND(dt); + return dt; +} /* dt2local() */ + + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + + bool -timestampeq(time_t t1, time_t t2) +timestamp_finite(Timestamp *timestamp) { - return abstimeeq(t1, t2); -} + if (!PointerIsValid(timestamp)) + return FALSE; + + return !TIMESTAMP_NOT_FINITE(*timestamp); +} /* timestamp_finite() */ bool -timestampne(time_t t1, time_t t2) +interval_finite(Interval *interval) { - return abstimene(t1, t2); -} + if (!PointerIsValid(interval)) + return FALSE; + return !INTERVAL_NOT_FINITE(*interval); +} /* interval_finite() */ + + +/*---------------------------------------------------------- + * Relational operators for timestamp. + *---------------------------------------------------------*/ + +static void +GetEpochTime(struct tm * tm) +{ + struct tm *t0; + time_t epoch = 0; + + t0 = gmtime(&epoch); + + tm->tm_year = t0->tm_year; + tm->tm_mon = t0->tm_mon; + tm->tm_mday = t0->tm_mday; + tm->tm_hour = t0->tm_hour; + tm->tm_min = t0->tm_min; + tm->tm_sec = t0->tm_sec; + + if (tm->tm_year < 1900) + tm->tm_year += 1900; + tm->tm_mon++; + + return; +} /* GetEpochTime() */ + +Timestamp +SetTimestamp(Timestamp dt) +{ + struct tm tt; + + if (TIMESTAMP_IS_CURRENT(dt)) + { + GetCurrentTime(&tt); + tm2timestamp(&tt, 0, NULL, &dt); + dt = dt2local(dt, -CTimeZone); + } + else + { /* if (TIMESTAMP_IS_EPOCH(dt1)) */ + GetEpochTime(&tt); + tm2timestamp(&tt, 0, NULL, &dt); + } + + return dt; +} /* SetTimestamp() */ + +/* timestamp_relop - is timestamp1 relop timestamp2 + */ bool -timestamplt(time_t t1, time_t t2) +timestamp_eq(Timestamp *timestamp1, Timestamp *timestamp2) { - return abstimelt(t1, t2); -} + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return FALSE; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2)) + return FALSE; + + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + + return dt1 == dt2; +} /* timestamp_eq() */ bool -timestampgt(time_t t1, time_t t2) +timestamp_ne(Timestamp *timestamp1, Timestamp *timestamp2) { - return abstimegt(t1, t2); -} + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return FALSE; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2)) + return FALSE; + + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + + return dt1 != dt2; +} /* timestamp_ne() */ bool -timestample(time_t t1, time_t t2) +timestamp_lt(Timestamp *timestamp1, Timestamp *timestamp2) { - return abstimele(t1, t2); -} + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return FALSE; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2)) + return FALSE; + + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + + return dt1 < dt2; +} /* timestamp_lt() */ bool -timestampge(time_t t1, time_t t2) +timestamp_gt(Timestamp *timestamp1, Timestamp *timestamp2) { - return abstimege(t1, t2); -} + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return FALSE; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2)) + return FALSE; + + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + + return dt1 > dt2; +} /* timestamp_gt() */ + +bool +timestamp_le(Timestamp *timestamp1, Timestamp *timestamp2) +{ + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return FALSE; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2)) + return FALSE; + + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); -DateTime * -timestamp_datetime(time_t timestamp) + return dt1 <= dt2; +} /* timestamp_le() */ + +bool +timestamp_ge(Timestamp *timestamp1, Timestamp *timestamp2) { - return abstime_datetime((AbsoluteTime) timestamp); -} /* timestamp_datetime() */ + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return FALSE; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2)) + return FALSE; -time_t -datetime_timestamp(DateTime *datetime) + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + + return dt1 >= dt2; +} /* timestamp_ge() */ + + +/* timestamp_cmp - 3-state comparison for timestamp + * collate invalid timestamp at the end + */ +int +timestamp_cmp(Timestamp *timestamp1, Timestamp *timestamp2) { - return (AbsoluteTime) datetime_abstime(datetime); -} /* datetime_timestamp() */ + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return 0; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + if (TIMESTAMP_IS_INVALID(dt1)) + { + return (TIMESTAMP_IS_INVALID(dt2) ? 0 : 1); + + } + else if (TIMESTAMP_IS_INVALID(dt2)) + { + return -1; + + } + else + { + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + } + + return ((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0)); +} /* timestamp_cmp() */ + + +/* interval_relop - is interval1 relop interval2 + */ +bool +interval_eq(Interval *interval1, Interval *interval2) +{ + if (!PointerIsValid(interval1) || !PointerIsValid(interval2)) + return FALSE; + + if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2)) + return FALSE; + + return ((interval1->time == interval2->time) + && (interval1->month == interval2->month)); +} /* interval_eq() */ + +bool +interval_ne(Interval *interval1, Interval *interval2) +{ + if (!PointerIsValid(interval1) || !PointerIsValid(interval2)) + return FALSE; + + if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2)) + return FALSE; + + return ((interval1->time != interval2->time) + || (interval1->month != interval2->month)); +} /* interval_ne() */ + +bool +interval_lt(Interval *interval1, Interval *interval2) +{ + double span1, + span2; + + if (!PointerIsValid(interval1) || !PointerIsValid(interval2)) + return FALSE; + + if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2)) + return FALSE; + + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); + + return span1 < span2; +} /* interval_lt() */ + +bool +interval_gt(Interval *interval1, Interval *interval2) +{ + double span1, + span2; + + if (!PointerIsValid(interval1) || !PointerIsValid(interval2)) + return FALSE; + + if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2)) + return FALSE; + + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); + + return span1 > span2; +} /* interval_gt() */ + +bool +interval_le(Interval *interval1, Interval *interval2) +{ + double span1, + span2; + + if (!PointerIsValid(interval1) || !PointerIsValid(interval2)) + return FALSE; + + if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2)) + return FALSE; + + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); + + return span1 <= span2; +} /* interval_le() */ + +bool +interval_ge(Interval *interval1, Interval *interval2) +{ + double span1, + span2; + + if (!PointerIsValid(interval1) || !PointerIsValid(interval2)) + return FALSE; + + if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2)) + return FALSE; + + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); + + return span1 >= span2; +} /* interval_ge() */ + + +/* interval_cmp - 3-state comparison for interval + */ +int +interval_cmp(Interval *interval1, Interval *interval2) +{ + double span1, + span2; + + if (!PointerIsValid(interval1) || !PointerIsValid(interval2)) + return 0; + + if (INTERVAL_IS_INVALID(*interval1)) + { + return INTERVAL_IS_INVALID(*interval2) ? 0 : 1; + + } + else if (INTERVAL_IS_INVALID(*interval2)) + return -1; + + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); + + return (span1 < span2) ? -1 : (span1 > span2) ? 1 : 0; +} /* interval_cmp() */ + + +/*---------------------------------------------------------- + * "Arithmetic" operators on date/times. + * timestamp_foo returns foo as an object (pointer) that + * can be passed between languages. + * timestamp_xx is an internal routine which returns the + * actual value. + *---------------------------------------------------------*/ + +Timestamp * +timestamp_smaller(Timestamp *timestamp1, Timestamp *timestamp2) +{ + Timestamp *result; + + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return NULL; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + result = palloc(sizeof(Timestamp)); + + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + + if (TIMESTAMP_IS_INVALID(dt1)) + *result = dt2; + else if (TIMESTAMP_IS_INVALID(dt2)) + *result = dt1; + else + *result = ((dt2 < dt1) ? dt2 : dt1); + + return result; +} /* timestamp_smaller() */ + +Timestamp * +timestamp_larger(Timestamp *timestamp1, Timestamp *timestamp2) +{ + Timestamp *result; + + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return NULL; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + result = palloc(sizeof(Timestamp)); + + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + + if (TIMESTAMP_IS_INVALID(dt1)) + *result = dt2; + else if (TIMESTAMP_IS_INVALID(dt2)) + *result = dt1; + else + *result = ((dt2 > dt1) ? dt2 : dt1); + + return result; +} /* timestamp_larger() */ + + +Interval * +timestamp_mi(Timestamp *timestamp1, Timestamp *timestamp2) +{ + Interval *result; + + Timestamp dt1, + dt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return NULL; + + dt1 = *timestamp1; + dt2 = *timestamp2; + + result = palloc(sizeof(Interval)); + + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + + if (TIMESTAMP_IS_INVALID(dt1) + || TIMESTAMP_IS_INVALID(dt2)) + { + TIMESTAMP_INVALID(result->time); + + } + else + result->time = JROUND(dt1 - dt2); + result->month = 0; + + return result; +} /* timestamp_mi() */ + + +/* timestamp_pl_span() + * Add a interval to a timestamp data type. + * Note that interval has provisions for qualitative year/month + * units, so try to do the right thing with them. + * To add a month, increment the month, and use the same day of month. + * Then, if the next month has fewer days, set the day of month + * to the last day of month. + * Lastly, add in the "quantitative time". + */ +Timestamp * +timestamp_pl_span(Timestamp *timestamp, Interval *span) +{ + Timestamp *result; + Timestamp dt; + int tz; + char *tzn; + + if ((!PointerIsValid(timestamp)) || (!PointerIsValid(span))) + return NULL; + + result = palloc(sizeof(Timestamp)); + + if (TIMESTAMP_NOT_FINITE(*timestamp)) + { + *result = *timestamp; + + } + else if (INTERVAL_IS_INVALID(*span)) + { + TIMESTAMP_INVALID(*result); + + } + else + { + dt = (TIMESTAMP_IS_RELATIVE(*timestamp) ? SetTimestamp(*timestamp) : *timestamp); + + if (span->month != 0) + { + struct tm tt, + *tm = &tt; + double fsec; + + if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0) + { + tm->tm_mon += span->month; + if (tm->tm_mon > 12) + { + tm->tm_year += ((tm->tm_mon - 1) / 12); + tm->tm_mon = (((tm->tm_mon - 1) % 12) + 1); + } + else if (tm->tm_mon < 1) + { + tm->tm_year += ((tm->tm_mon / 12) - 1); + tm->tm_mon = ((tm->tm_mon % 12) + 12); + } + + /* adjust for end of month boundary problems... */ + if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) + tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]); + + if (tm2timestamp(tm, fsec, &tz, &dt) != 0) + elog(ERROR, "Unable to add timestamp and interval"); + + } + else + TIMESTAMP_INVALID(dt); + } + +#ifdef ROUND_ALL + dt = JROUND(dt + span->time); +#else + dt += span->time; +#endif + + *result = dt; + } + + return result; +} /* timestamp_pl_span() */ + +Timestamp * +timestamp_mi_span(Timestamp *timestamp, Interval *span) +{ + Timestamp *result; + Interval tspan; + + if (!PointerIsValid(timestamp) || !PointerIsValid(span)) + return NULL; + + tspan.month = -span->month; + tspan.time = -span->time; + + result = timestamp_pl_span(timestamp, &tspan); + + return result; +} /* timestamp_mi_span() */ + + +Interval * +interval_um(Interval *interval) +{ + Interval *result; + + if (!PointerIsValid(interval)) + return NULL; + + result = palloc(sizeof(Interval)); + + result->time = -(interval->time); + result->month = -(interval->month); + + return result; +} /* interval_um() */ + + +Interval * +interval_smaller(Interval *interval1, Interval *interval2) +{ + Interval *result; + + double span1, + span2; + + if (!PointerIsValid(interval1) || !PointerIsValid(interval2)) + return NULL; + + result = palloc(sizeof(Interval)); + + if (INTERVAL_IS_INVALID(*interval1)) + { + result->time = interval2->time; + result->month = interval2->month; + + } + else if (INTERVAL_IS_INVALID(*interval2)) + { + result->time = interval1->time; + result->month = interval1->month; + + } + else + { + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); + + if (span2 < span1) + { + result->time = interval2->time; + result->month = interval2->month; + + } + else + { + result->time = interval1->time; + result->month = interval1->month; + } + } + + return result; +} /* interval_smaller() */ + +Interval * +interval_larger(Interval *interval1, Interval *interval2) +{ + Interval *result; + + double span1, + span2; + + if (!PointerIsValid(interval1) || !PointerIsValid(interval2)) + return NULL; + + result = palloc(sizeof(Interval)); + + if (INTERVAL_IS_INVALID(*interval1)) + { + result->time = interval2->time; + result->month = interval2->month; + + } + else if (INTERVAL_IS_INVALID(*interval2)) + { + result->time = interval1->time; + result->month = interval1->month; + + } + else + { + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); + + if (span2 > span1) + { + result->time = interval2->time; + result->month = interval2->month; + + } + else + { + result->time = interval1->time; + result->month = interval1->month; + } + } + + return result; +} /* interval_larger() */ + + +Interval * +interval_pl(Interval *span1, Interval *span2) +{ + Interval *result; + + if ((!PointerIsValid(span1)) || (!PointerIsValid(span2))) + return NULL; + + result = palloc(sizeof(Interval)); + + result->month = (span1->month + span2->month); + result->time = JROUND(span1->time + span2->time); + + return result; +} /* interval_pl() */ + +Interval * +interval_mi(Interval *span1, Interval *span2) +{ + Interval *result; + + if ((!PointerIsValid(span1)) || (!PointerIsValid(span2))) + return NULL; + + result = palloc(sizeof(Interval)); + + result->month = (span1->month - span2->month); + result->time = JROUND(span1->time - span2->time); + + return result; +} /* interval_mi() */ + +Interval * +interval_div(Interval *span1, float8 *arg2) +{ + Interval *result; + + if ((!PointerIsValid(span1)) || (!PointerIsValid(arg2))) + return NULL; + + if (!PointerIsValid(result = palloc(sizeof(Interval)))) + elog(ERROR, "Memory allocation failed, can't divide intervals"); + + if (*arg2 == 0.0) + elog(ERROR, "interval_div: divide by 0.0 error"); + + result->month = rint(span1->month / *arg2); + result->time = JROUND(span1->time / *arg2); + + return result; +} /* interval_div() */ + +/* timestamp_age() + * Calculate time difference while retaining year/month fields. + * Note that this does not result in an accurate absolute time span + * since year and month are out of context once the arithmetic + * is done. + */ +Interval * +timestamp_age(Timestamp *timestamp1, Timestamp *timestamp2) +{ + Interval *result; + + Timestamp dt1, + dt2; + double fsec, + fsec1, + fsec2; + struct tm tt, + *tm = &tt; + struct tm tt1, + *tm1 = &tt1; + struct tm tt2, + *tm2 = &tt2; + + if (!PointerIsValid(timestamp1) || !PointerIsValid(timestamp2)) + return NULL; + + result = palloc(sizeof(Interval)); + + dt1 = *timestamp1; + dt2 = *timestamp2; + + if (TIMESTAMP_IS_RELATIVE(dt1)) + dt1 = SetTimestamp(dt1); + if (TIMESTAMP_IS_RELATIVE(dt2)) + dt2 = SetTimestamp(dt2); + + if (TIMESTAMP_IS_INVALID(dt1) + || TIMESTAMP_IS_INVALID(dt2)) + { + TIMESTAMP_INVALID(result->time); + + } + else if ((timestamp2tm(dt1, NULL, tm1, &fsec1, NULL) == 0) + && (timestamp2tm(dt2, NULL, tm2, &fsec2, NULL) == 0)) + { + fsec = (fsec1 - fsec2); + tm->tm_sec = (tm1->tm_sec - tm2->tm_sec); + tm->tm_min = (tm1->tm_min - tm2->tm_min); + tm->tm_hour = (tm1->tm_hour - tm2->tm_hour); + tm->tm_mday = (tm1->tm_mday - tm2->tm_mday); + tm->tm_mon = (tm1->tm_mon - tm2->tm_mon); + tm->tm_year = (tm1->tm_year - tm2->tm_year); + + /* flip sign if necessary... */ + if (dt1 < dt2) + { + 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; + } + + if (tm->tm_sec < 0) + { + tm->tm_sec += 60; + tm->tm_min--; + } + + if (tm->tm_min < 0) + { + tm->tm_min += 60; + tm->tm_hour--; + } + + if (tm->tm_hour < 0) + { + tm->tm_hour += 24; + tm->tm_mday--; + } + + if (tm->tm_mday < 0) + { + if (dt1 < dt2) + { + tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1]; + tm->tm_mon--; + } + else + { + tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1]; + tm->tm_mon--; + } + } + + if (tm->tm_mon < 0) + { + tm->tm_mon += 12; + tm->tm_year--; + } + + /* recover sign if necessary... */ + if (dt1 < dt2) + { + 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; + } + + if (tm2interval(tm, fsec, result) != 0) + elog(ERROR, "Unable to decode timestamp"); + + } + else + elog(ERROR, "Unable to decode timestamp"); + + return result; +} /* timestamp_age() */ + + +/*---------------------------------------------------------- + * Conversion operators. + *---------------------------------------------------------*/ + + +/* timestamp_text() + * Convert timestamp to text data type. + */ +text * +timestamp_text(Timestamp *timestamp) +{ + text *result; + char *str; + int len; + + if (!PointerIsValid(timestamp)) + return NULL; + + str = timestamp_out(timestamp); + + if (!PointerIsValid(str)) + return NULL; + + len = (strlen(str) + VARHDRSZ); + + result = palloc(len); + + VARSIZE(result) = len; + memmove(VARDATA(result), str, (len - VARHDRSZ)); + + pfree(str); + + return result; +} /* timestamp_text() */ + + +/* text_timestamp() + * Convert text string to timestamp. + * Text type is not null terminated, so use temporary string + * then call the standard input routine. + */ +Timestamp * +text_timestamp(text *str) +{ + Timestamp *result; + int i; + char *sp, + *dp, + dstr[MAXDATELEN + 1]; + + if (!PointerIsValid(str)) + return NULL; + + sp = VARDATA(str); + dp = dstr; + for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++) + *dp++ = *sp++; + *dp = '\0'; + + result = timestamp_in(dstr); + + return result; +} /* text_timestamp() */ + + +/* interval_text() + * Convert interval to text data type. + */ +text * +interval_text(Interval *interval) +{ + text *result; + char *str; + int len; + + if (!PointerIsValid(interval)) + return NULL; + + str = interval_out(interval); + + if (!PointerIsValid(str)) + return NULL; + + len = (strlen(str) + VARHDRSZ); + + result = palloc(len); + + VARSIZE(result) = len; + memmove(VARDATA(result), str, (len - VARHDRSZ)); + + pfree(str); + + return result; +} /* interval_text() */ + + +/* text_interval() + * Convert text string to interval. + * Text type may not be null terminated, so copy to temporary string + * then call the standard input routine. + */ +Interval * +text_interval(text *str) +{ + Interval *result; + int i; + char *sp, + *dp, + dstr[MAXDATELEN + 1]; + + if (!PointerIsValid(str)) + return NULL; + + sp = VARDATA(str); + dp = dstr; + for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++) + *dp++ = *sp++; + *dp = '\0'; + + result = interval_in(dstr); + + return result; +} /* text_interval() */ + +/* timestamp_trunc() + * Extract specified field from timestamp. + */ +Timestamp * +timestamp_trunc(text *units, Timestamp *timestamp) +{ + Timestamp *result; + + Timestamp dt; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowunits[MAXDATELEN + 1]; + double fsec; + char *tzn; + struct tm tt, + *tm = &tt; + + if ((!PointerIsValid(units)) || (!PointerIsValid(timestamp))) + return NULL; + + result = palloc(sizeof(Timestamp)); + + up = VARDATA(units); + lp = lowunits; + for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) + *lp++ = tolower(*up++); + *lp = '\0'; + + type = DecodeUnits(0, lowunits, &val); + + if (TIMESTAMP_NOT_FINITE(*timestamp)) + { +#if NOT_USED +/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */ + elog(ERROR, "Timestamp is not finite", NULL); +#endif + *result = 0; + + } + else + { + dt = (TIMESTAMP_IS_RELATIVE(*timestamp) ? SetTimestamp(*timestamp) : *timestamp); + + if ((type == UNITS) && (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0)) + { + switch (val) + { + case DTK_MILLENIUM: + tm->tm_year = (tm->tm_year / 1000) * 1000; + case DTK_CENTURY: + tm->tm_year = (tm->tm_year / 100) * 100; + case DTK_DECADE: + tm->tm_year = (tm->tm_year / 10) * 10; + case DTK_YEAR: + tm->tm_mon = 1; + case DTK_QUARTER: + tm->tm_mon = (3 * (tm->tm_mon / 4)) + 1; + case DTK_MONTH: + tm->tm_mday = 1; + case DTK_DAY: + tm->tm_hour = 0; + case DTK_HOUR: + tm->tm_min = 0; + case DTK_MINUTE: + tm->tm_sec = 0; + case DTK_SECOND: + fsec = 0; + break; + + case DTK_MILLISEC: + fsec = rint(fsec * 1000) / 1000; + break; + + case DTK_MICROSEC: + fsec = rint(fsec * 1000000) / 1000000; + break; + + default: + elog(ERROR, "Timestamp units '%s' not supported", lowunits); + result = NULL; + } + + if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) + { +#ifdef USE_POSIX_TIME + tm->tm_isdst = -1; + 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) + tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ +#elif defined(HAVE_INT_TIMEZONE) + +#ifdef __CYGWIN__ + tz = (tm->tm_isdst ? (_timezone - 3600) : _timezone); +#else + tz = (tm->tm_isdst ? (timezone - 3600) : timezone); +#endif + +#else +#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined +#endif + +#else /* !USE_POSIX_TIME */ + tz = CTimeZone; +#endif + } + else + { + tm->tm_isdst = 0; + tz = 0; + } + + if (tm2timestamp(tm, fsec, &tz, result) != 0) + elog(ERROR, "Unable to truncate timestamp to '%s'", lowunits); + +#if NOT_USED + } + else if ((type == RESERV) && (val == DTK_EPOCH)) + { + TIMESTAMP_EPOCH(*result); + *result = dt - SetTimestamp(*result); +#endif + + } + else + { + elog(ERROR, "Timestamp units '%s' not recognized", lowunits); + result = NULL; + } + } + + return result; +} /* timestamp_trunc() */ + +/* interval_trunc() + * Extract specified field from interval. + */ +Interval * +interval_trunc(text *units, Interval *interval) +{ + Interval *result; + + int type, + val; + int i; + char *up, + *lp, + lowunits[MAXDATELEN + 1]; + double fsec; + struct tm tt, + *tm = &tt; + + if ((!PointerIsValid(units)) || (!PointerIsValid(interval))) + return NULL; + + result = palloc(sizeof(Interval)); + + up = VARDATA(units); + lp = lowunits; + for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) + *lp++ = tolower(*up++); + *lp = '\0'; + + type = DecodeUnits(0, lowunits, &val); + + if (INTERVAL_IS_INVALID(*interval)) + { +#if NOT_USED + elog(ERROR, "Interval is not finite", NULL); +#endif + result = NULL; + + } + else if (type == UNITS) + { + + if (interval2tm(*interval, tm, &fsec) == 0) + { + switch (val) + { + case DTK_MILLENIUM: + tm->tm_year = (tm->tm_year / 1000) * 1000; + case DTK_CENTURY: + tm->tm_year = (tm->tm_year / 100) * 100; + case DTK_DECADE: + tm->tm_year = (tm->tm_year / 10) * 10; + case DTK_YEAR: + tm->tm_mon = 0; + case DTK_QUARTER: + tm->tm_mon = (3 * (tm->tm_mon / 4)); + case DTK_MONTH: + tm->tm_mday = 0; + case DTK_DAY: + tm->tm_hour = 0; + case DTK_HOUR: + tm->tm_min = 0; + case DTK_MINUTE: + tm->tm_sec = 0; + case DTK_SECOND: + fsec = 0; + break; + + case DTK_MILLISEC: + fsec = rint(fsec * 1000) / 1000; + break; + + case DTK_MICROSEC: + fsec = rint(fsec * 1000000) / 1000000; + break; + + default: + elog(ERROR, "Interval units '%s' not supported", lowunits); + result = NULL; + } + + if (tm2interval(tm, fsec, result) != 0) + elog(ERROR, "Unable to truncate interval to '%s'", lowunits); + + } + else + { + elog(NOTICE, "Interval out of range"); + result = NULL; + } + +#if NOT_USED + } + else if ((type == RESERV) && (val == DTK_EPOCH)) + { + *result = interval->time; + if (interval->month != 0) + { + *result += ((365.25 * 86400) * (interval->month / 12)); + *result += ((30 * 86400) * (interval->month % 12)); + } +#endif + + } + else + { + elog(ERROR, "Interval units '%s' not recognized", textout(units)); + result = NULL; + } + + return result; +} /* interval_trunc() */ + + +/* timestamp_part() + * Extract specified field from timestamp. + */ +float64 +timestamp_part(text *units, Timestamp *timestamp) +{ + float64 result; + + Timestamp dt; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowunits[MAXDATELEN + 1]; + double dummy; + double fsec; + char *tzn; + struct tm tt, + *tm = &tt; + + if ((!PointerIsValid(units)) || (!PointerIsValid(timestamp))) + return NULL; + + result = palloc(sizeof(float64data)); + + up = VARDATA(units); + lp = lowunits; + for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) + *lp++ = tolower(*up++); + *lp = '\0'; + + type = DecodeUnits(0, lowunits, &val); + if (type == IGNORE) + type = DecodeSpecial(0, lowunits, &val); + + if (TIMESTAMP_NOT_FINITE(*timestamp)) + { +#if NOT_USED +/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */ + elog(ERROR, "Timestamp is not finite", NULL); +#endif + *result = 0; + + } + else + { + dt = (TIMESTAMP_IS_RELATIVE(*timestamp) ? SetTimestamp(*timestamp) : *timestamp); + + if ((type == UNITS) && (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0)) + { + switch (val) + { + case DTK_TZ: + *result = tz; + break; + + case DTK_TZ_MINUTE: + *result = tz / 60; + TMODULO(*result, dummy, 60e0); + break; + + case DTK_TZ_HOUR: + dummy = tz; + TMODULO(dummy, *result, 3600e0); + break; + + case DTK_MICROSEC: + *result = (fsec * 1000000); + break; + + case DTK_MILLISEC: + *result = (fsec * 1000); + break; + + case DTK_SECOND: + *result = (tm->tm_sec + fsec); + break; + + case DTK_MINUTE: + *result = tm->tm_min; + break; + + case DTK_HOUR: + *result = tm->tm_hour; + break; + + case DTK_DAY: + *result = tm->tm_mday; + break; + + case DTK_MONTH: + *result = tm->tm_mon; + break; + + case DTK_QUARTER: + *result = (tm->tm_mon / 4) + 1; + break; + + case DTK_YEAR: + *result = tm->tm_year; + break; + + case DTK_DECADE: + *result = (tm->tm_year / 10); + break; + + case DTK_CENTURY: + *result = (tm->tm_year / 100); + break; + + case DTK_MILLENIUM: + *result = (tm->tm_year / 1000); + break; + + default: + elog(ERROR, "Timestamp units '%s' not supported", lowunits); + *result = 0; + } + + } + else if (type == RESERV) + { + switch (val) + { + case DTK_EPOCH: + TIMESTAMP_EPOCH(*result); + *result = dt - SetTimestamp(*result); + break; + + case DTK_DOW: + if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp"); + + *result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); + break; + + case DTK_DOY: + if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp"); + + *result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + - date2j(tm->tm_year, 1, 1) + 1); + break; + + default: + elog(ERROR, "Timestamp units '%s' not supported", lowunits); + *result = 0; + } + + } + else + { + elog(ERROR, "Timestamp units '%s' not recognized", lowunits); + *result = 0; + } + } + + return result; +} /* timestamp_part() */ + + +/* interval_part() + * Extract specified field from interval. + */ +float64 +interval_part(text *units, Interval *interval) +{ + float64 result; + + int type, + val; + int i; + char *up, + *lp, + lowunits[MAXDATELEN + 1]; + double fsec; + struct tm tt, + *tm = &tt; + + if ((!PointerIsValid(units)) || (!PointerIsValid(interval))) + return NULL; + + result = palloc(sizeof(float64data)); + + up = VARDATA(units); + lp = lowunits; + for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) + *lp++ = tolower(*up++); + *lp = '\0'; + + type = DecodeUnits(0, lowunits, &val); + if (type == IGNORE) + type = DecodeSpecial(0, lowunits, &val); + + if (INTERVAL_IS_INVALID(*interval)) + { +#if NOT_USED + elog(ERROR, "Interval is not finite"); +#endif + *result = 0; + + } + else if (type == UNITS) + { + + if (interval2tm(*interval, tm, &fsec) == 0) + { + switch (val) + { + case DTK_MICROSEC: + *result = (fsec * 1000000); + break; + + case DTK_MILLISEC: + *result = (fsec * 1000); + break; + + case DTK_SECOND: + *result = (tm->tm_sec + fsec); + break; + + case DTK_MINUTE: + *result = tm->tm_min; + break; + + case DTK_HOUR: + *result = tm->tm_hour; + break; + + case DTK_DAY: + *result = tm->tm_mday; + break; + + case DTK_MONTH: + *result = tm->tm_mon; + break; + + case DTK_QUARTER: + *result = (tm->tm_mon / 4) + 1; + break; + + case DTK_YEAR: + *result = tm->tm_year; + break; + + case DTK_DECADE: + *result = (tm->tm_year / 10); + break; + + case DTK_CENTURY: + *result = (tm->tm_year / 100); + break; + + case DTK_MILLENIUM: + *result = (tm->tm_year / 1000); + break; + + default: + elog(ERROR, "Interval units '%s' not yet supported", textout(units)); + result = NULL; + } + + } + else + { + elog(NOTICE, "Interval out of range"); + *result = 0; + } + + } + else if ((type == RESERV) && (val == DTK_EPOCH)) + { + *result = interval->time; + if (interval->month != 0) + { + *result += ((365.25 * 86400) * (interval->month / 12)); + *result += ((30 * 86400) * (interval->month % 12)); + } + + } + else + { + elog(ERROR, "Interval units '%s' not recognized", textout(units)); + *result = 0; + } + + return result; +} /* interval_part() */ + + +/* timestamp_zone() + * Encode timestamp type with specified time zone. + */ +text * +timestamp_zone(text *zone, Timestamp *timestamp) +{ + text *result; + + Timestamp dt; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowzone[MAXDATELEN + 1]; + char *tzn, + upzone[MAXDATELEN + 1]; + double fsec; + struct tm tt, + *tm = &tt; + char buf[MAXDATELEN + 1]; + int len; + + if ((!PointerIsValid(zone)) || (!PointerIsValid(timestamp))) + return NULL; + + up = VARDATA(zone); + lp = lowzone; + for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++) + *lp++ = tolower(*up++); + *lp = '\0'; + + type = DecodeSpecial(0, lowzone, &val); + + if (TIMESTAMP_NOT_FINITE(*timestamp)) + { + + /* + * could return null but Postgres doesn't like that currently. - + * tgl 97/06/12 + */ + elog(ERROR, "Timestamp is not finite"); + result = NULL; + + } + else if ((type == TZ) || (type == DTZ)) + { + tm->tm_isdst = ((type == DTZ) ? 1 : 0); + tz = val * 60; + + dt = (TIMESTAMP_IS_RELATIVE(*timestamp) ? SetTimestamp(*timestamp) : *timestamp); + dt = dt2local(dt, tz); + + if (timestamp2tm(dt, NULL, tm, &fsec, NULL) != 0) + elog(ERROR, "Timestamp not legal"); + + up = upzone; + lp = lowzone; + for (i = 0; *lp != '\0'; i++) + *up++ = toupper(*lp++); + *up = '\0'; + + tzn = upzone; + EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); + + len = (strlen(buf) + VARHDRSZ); + + result = palloc(len); + + VARSIZE(result) = len; + memmove(VARDATA(result), buf, (len - VARHDRSZ)); + + } + else + { + elog(ERROR, "Time zone '%s' not recognized", lowzone); + result = NULL; + } + + return result; +} /* timestamp_zone() */ |