diff options
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 2214 |
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() */ |