aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/timestamp.c
diff options
context:
space:
mode:
authorThomas G. Lockhart <lockhart@fourpalms.org>2000-02-16 17:26:26 +0000
committerThomas G. Lockhart <lockhart@fourpalms.org>2000-02-16 17:26:26 +0000
commit41f1f5b76ad8e177a2b19116cbf41384f93f3851 (patch)
tree2165e6c833bc96f32e2d2b45c1d40b71e9c82ab7 /src/backend/utils/adt/timestamp.c
parentc97672b08325d722cf5807ffede9fd31ee070f72 (diff)
downloadpostgresql-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/adt/timestamp.c')
-rw-r--r--src/backend/utils/adt/timestamp.c2214
1 files changed, 2155 insertions, 59 deletions
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() */