diff options
Diffstat (limited to 'src/backend/utils/adt')
-rw-r--r-- | src/backend/utils/adt/date.c | 268 | ||||
-rw-r--r-- | src/backend/utils/adt/datetime.c | 210 | ||||
-rw-r--r-- | src/backend/utils/adt/format_type.c | 6 | ||||
-rw-r--r-- | src/backend/utils/adt/formatting.c | 35 | ||||
-rw-r--r-- | src/backend/utils/adt/nabstime.c | 752 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 1370 |
6 files changed, 1665 insertions, 976 deletions
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 4504f5f043c..36b3a52de25 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -8,21 +8,24 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.57 2001/05/03 19:00:36 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.58 2001/09/28 08:09:10 thomas Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include <ctype.h> #include <limits.h> #include <time.h> #include <float.h> #include "access/hash.h" #include "miscadmin.h" +#include "utils/builtins.h" #include "utils/date.h" #include "utils/nabstime.h" +#include "utils/timestamp.h" /***************************************************************************** @@ -58,13 +61,13 @@ date_in(PG_FUNCTION_ARGS) break; case DTK_CURRENT: + elog(ERROR, "Date CURRENT no longer supported" + "\n\tdate_in() internal coding error"); GetCurrentTime(tm); break; case DTK_EPOCH: - tm->tm_year = 1970; - tm->tm_mon = 1; - tm->tm_mday = 1; + GetEpochTime(tm); break; default: @@ -224,6 +227,46 @@ date_timestamp(PG_FUNCTION_ARGS) { DateADT dateVal = PG_GETARG_DATEADT(0); Timestamp result; + + /* date is days since 2000, timestamp is seconds since same... */ + result = dateVal * 86400.0; + + PG_RETURN_TIMESTAMP(result); +} + + +/* timestamp_date() + * Convert timestamp to date data type. + */ +Datum +timestamp_date(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + DateADT result; + struct tm tt, + *tm = &tt; + double fsec; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_NULL(); + + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0) + elog(ERROR, "Unable to convert timestamp to date"); + + result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); + + PG_RETURN_DATEADT(result); +} + + +/* date_timestamptz() + * Convert date to timestamp with time zone data type. + */ +Datum +date_timestamptz(PG_FUNCTION_ARGS) +{ + DateADT dateVal = PG_GETARG_DATEADT(0); + TimestampTz result; struct tm tt, *tm = &tt; time_t utime; @@ -259,32 +302,25 @@ date_timestamp(PG_FUNCTION_ARGS) } -/* timestamp_date() - * Convert timestamp to date data type. +/* timestamptz_date() + * Convert timestamp with time zone to date data type. */ Datum -timestamp_date(PG_FUNCTION_ARGS) +timestamptz_date(PG_FUNCTION_ARGS) { - Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); DateADT result; struct tm tt, *tm = &tt; - int tz; double fsec; + int tz; char *tzn; if (TIMESTAMP_NOT_FINITE(timestamp)) - elog(ERROR, "Unable to convert timestamp to date"); + PG_RETURN_NULL(); - if (TIMESTAMP_IS_EPOCH(timestamp)) - timestamp2tm(SetTimestamp(timestamp), NULL, tm, &fsec, NULL); - else if (TIMESTAMP_IS_CURRENT(timestamp)) - timestamp2tm(SetTimestamp(timestamp), &tz, tm, &fsec, &tzn); - else - { - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to convert timestamp to date"); - } + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to convert timestamp to date"); result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); @@ -316,15 +352,6 @@ abstime_date(PG_FUNCTION_ARGS) * will be set */ - case EPOCH_ABSTIME: - result = date2j(1970, 1, 1) - date2j(2000, 1, 1); - break; - - case CURRENT_ABSTIME: - GetCurrentTime(tm); - result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); - break; - default: abstime2tm(abstime, &tz, tm, NULL); result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); @@ -664,22 +691,13 @@ timestamp_time(PG_FUNCTION_ARGS) TimeADT result; struct tm tt, *tm = &tt; - int tz; double fsec; - char *tzn; if (TIMESTAMP_NOT_FINITE(timestamp)) - elog(ERROR, "Unable to convert timestamp to date"); + PG_RETURN_NULL(); - if (TIMESTAMP_IS_EPOCH(timestamp)) - timestamp2tm(SetTimestamp(timestamp), NULL, tm, &fsec, NULL); - else if (TIMESTAMP_IS_CURRENT(timestamp)) - timestamp2tm(SetTimestamp(timestamp), &tz, tm, &fsec, &tzn); - else - { - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to convert timestamp to date"); - } + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0) + elog(ERROR, "Unable to convert timestamp to date"); result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec); @@ -736,6 +754,24 @@ interval_time(PG_FUNCTION_ARGS) PG_RETURN_TIMEADT(result); } +/* time_mi_time() + * Subtract two times to produce an interval. + */ +Datum +time_mi_time(PG_FUNCTION_ARGS) +{ + TimeADT time1 = PG_GETARG_TIMEADT(0); + TimeADT time2 = PG_GETARG_TIMEADT(1); + Interval *result; + + result = (Interval *) palloc(sizeof(Interval)); + + result->time = time2 - time1; + result->month = 0; + + PG_RETURN_INTERVAL_P(result); +} + /* time_pl_interval() * Add interval to time. */ @@ -918,7 +954,12 @@ timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2) * If same GMT time, sort by timezone; we only want to say that two * timetz's are equal if both the time and zone parts are equal. */ - return time1->zone - time2->zone; + if (time1->zone > time2->zone) + return 1; + if (time1->zone < time2->zone) + return -1; + + return 0; } Datum @@ -1199,13 +1240,48 @@ overlaps_timetz(PG_FUNCTION_ARGS) #undef TIMETZ_LT } -/* timestamp_timetz() + +Datum +timetz_time(PG_FUNCTION_ARGS) +{ + TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0); + TimeADT result; + + /* swallow the time zone and just return the time */ + result = timetz->time; + + PG_RETURN_TIMEADT(result); +} + + +Datum +time_timetz(PG_FUNCTION_ARGS) +{ + TimeADT time = PG_GETARG_TIMEADT(0); + TimeTzADT *result; + struct tm tt, + *tm = &tt; + int tz; + + GetCurrentTime(tm); + tz = DetermineLocalTimeZone(tm); + + result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); + + result->time = time; + result->zone = tz; + + PG_RETURN_TIMETZADT_P(result); +} + + +/* timestamptz_timetz() * Convert timestamp to timetz data type. */ Datum -timestamp_timetz(PG_FUNCTION_ARGS) +timestamptz_timetz(PG_FUNCTION_ARGS) { - Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); TimeTzADT *result; struct tm tt, *tm = &tt; @@ -1214,20 +1290,10 @@ timestamp_timetz(PG_FUNCTION_ARGS) char *tzn; if (TIMESTAMP_NOT_FINITE(timestamp)) - elog(ERROR, "Unable to convert timestamp to date"); + PG_RETURN_NULL(); - if (TIMESTAMP_IS_EPOCH(timestamp)) - { - timestamp2tm(SetTimestamp(timestamp), NULL, tm, &fsec, NULL); - tz = 0; - } - else if (TIMESTAMP_IS_CURRENT(timestamp)) - timestamp2tm(SetTimestamp(timestamp), &tz, tm, &fsec, &tzn); - else - { - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to convert timestamp to date"); - } + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to convert timestamp to date"); result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); @@ -1238,18 +1304,18 @@ timestamp_timetz(PG_FUNCTION_ARGS) } -/* datetimetz_timestamp() - * Convert date and timetz to timestamp data type. +/* datetimetz_timestamptz() + * Convert date and timetz to timestamp with time zone data type. * Timestamp is stored in GMT, so add the time zone * stored with the timetz to the result. * - thomas 2000-03-10 */ Datum -datetimetz_timestamp(PG_FUNCTION_ARGS) +datetimetz_timestamptz(PG_FUNCTION_ARGS) { DateADT date = PG_GETARG_DATEADT(0); TimeTzADT *time = PG_GETARG_TIMETZADT_P(1); - Timestamp result; + TimestampTz result; result = date * 86400.0 + time->time + time->zone; @@ -1310,3 +1376,83 @@ text_timetz(PG_FUNCTION_ARGS) return DirectFunctionCall1(timetz_in, CStringGetDatum(dstr)); } + +/* timetz_zone() + * Encode time with time zone type with specified time zone. + */ +Datum +timetz_zone(PG_FUNCTION_ARGS) +{ + text *zone = PG_GETARG_TEXT_P(0); + TimeTzADT *time = PG_GETARG_TIMETZADT_P(1); + TimeTzADT *result; + TimeADT time1; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowzone[MAXDATELEN + 1]; + + if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN) + elog(ERROR, "Time zone '%s' not recognized", + DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(zone)))); + up = VARDATA(zone); + lp = lowzone; + for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++) + *lp++ = tolower((unsigned char) *up++); + *lp = '\0'; + + type = DecodeSpecial(0, lowzone, &val); + + result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); + + if ((type == TZ) || (type == DTZ)) + { + tz = val * 60; + time1 = time->time - time->zone + tz; + TMODULO(result->time, time1, 86400e0); + if (result->time < 0) + result->time += 86400; + result->zone = tz; + } + else + { + elog(ERROR, "Time zone '%s' not recognized", lowzone); + PG_RETURN_NULL(); + } + + PG_RETURN_TIMETZADT_P(result); +} /* timetz_zone() */ + +/* timetz_izone() + * Encode time with time zone type with specified time interval as time zone. + */ +Datum +timetz_izone(PG_FUNCTION_ARGS) +{ + Interval *zone = PG_GETARG_INTERVAL_P(0); + TimeTzADT *time = PG_GETARG_TIMETZADT_P(1); + TimeTzADT *result; + TimeADT time1; + int tz; + + if (zone->month != 0) + elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)", + DatumGetCString(DirectFunctionCall1(interval_out, + PointerGetDatum(zone)))); + + tz = -(zone->time); + + result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); + + time1 = time->time - time->zone + tz; + TMODULO(result->time, time1, 86400e0); + if (result->time < 0) + result->time += 86400; + result->zone = tz; + + PG_RETURN_TIMETZADT_P(result); +} /* timetz_izone() */ diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 7095f24de73..28ca77b64ea 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.66 2001/07/10 01:41:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.67 2001/09/28 08:09:10 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -29,13 +29,13 @@ #define ROUND_ALL 1 static int DecodeNumber(int flen, char *field, - int fmask, int *tmask, - struct tm * tm, double *fsec, int *is2digits); + 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); + int fmask, int *tmask, + struct tm * tm, double *fsec, int *is2digits); static int DecodeTime(char *str, int fmask, int *tmask, - struct tm * tm, double *fsec); + struct tm * tm, double *fsec); static int DecodeTimezone(char *str, int *tzp); static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel); static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm); @@ -47,10 +47,10 @@ int day_tab[2][13] = { {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}; + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}; char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", -"Thursday", "Friday", "Saturday", NULL}; + "Thursday", "Friday", "Saturday", NULL}; /***************************************************************************** @@ -71,7 +71,7 @@ char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", * the text field is not guaranteed to be NULL-terminated. */ static datetkn datetktbl[] = { -/* text token lexval */ +/* text, token, lexval */ {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */ {"acsst", DTZ, 63}, /* Cent. Australia */ {"acst", TZ, 57}, /* Cent. Australia */ @@ -104,6 +104,7 @@ static datetkn datetktbl[] = { {"cetdst", DTZ, 12}, /* Central European Dayl.Time */ {"cst", TZ, NEG(36)}, /* Central Standard Time */ {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */ + {"d", UNITS, DAY}, /* "day of month" for ISO input */ {"dec", MONTH, 12}, {"december", MONTH, 12}, {"dnt", TZ, 6}, /* Dansk Normal Tid */ @@ -124,6 +125,7 @@ static datetkn datetktbl[] = { {"fwt", DTZ, 12}, /* French Winter Time */ {"gmt", TZ, 0}, /* Greenwish Mean Time */ {"gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */ + {"h", UNITS, HOUR}, /* "hour" */ {"hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */ {"hmt", DTZ, 18}, /* Hellas ? ? */ {"hst", TZ, NEG(60)}, /* Hawaii Std Time */ @@ -134,16 +136,19 @@ static datetkn datetktbl[] = { /* "invalid" reserved for invalid time */ {"ist", TZ, 12}, /* Israel */ {"it", TZ, 21}, /* Iran Time */ + {"j", UNITS, JULIAN}, {"jan", MONTH, 1}, {"january", MONTH, 1}, + {"jd", UNITS, JULIAN}, {"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */ {"jt", TZ, 45}, /* Java Time */ {"jul", MONTH, 7}, - {"july", MONTH, 7}, + {"julian", UNITS, JULIAN}, {"jun", MONTH, 6}, {"june", MONTH, 6}, {"kst", TZ, 54}, /* Korea Standard Time */ {"ligt", TZ, 60}, /* From Melbourne, Australia */ + {"m", UNITS, MONTH}, /* "month" for ISO input */ {"mar", MONTH, 3}, {"march", MONTH, 3}, {"may", MONTH, 5}, @@ -153,6 +158,7 @@ static datetkn datetktbl[] = { {"metdst", DTZ, 12}, /* Middle Europe Daylight Time */ {"mewt", TZ, 6}, /* Middle Europe Winter Time */ {"mez", TZ, 6}, /* Middle Europe Zone */ + {"mm", UNITS, MINUTE}, /* "minute" for ISO input */ {"mon", DOW, 1}, {"monday", DOW, 1}, {"mst", TZ, NEG(42)}, /* Mountain Standard Time */ @@ -174,6 +180,7 @@ static datetkn datetktbl[] = { {"pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */ {"pm", AMPM, PM}, {"pst", TZ, NEG(48)}, /* Pacific Standard Time */ + {"s", UNITS, SECOND}, /* "seconds" for ISO input */ {"sadt", DTZ, 63}, /* S. Australian Dayl. Time */ {"sast", TZ, 57}, /* South Australian Std Time */ {"sat", DOW, 6}, @@ -186,6 +193,7 @@ static datetkn datetktbl[] = { {"sun", DOW, 0}, {"sunday", DOW, 0}, {"swt", TZ, 6}, /* Swedish Winter Time */ + {"t", DTK_ISO_TIME, 0}, /* Filler for ISO time fields */ {"thu", DOW, 4}, {"thur", DOW, 4}, {"thurs", DOW, 4}, @@ -208,6 +216,7 @@ static datetkn datetktbl[] = { {"wet", TZ, 0}, /* Western Europe */ {"wetdst", DTZ, 6}, /* Western Europe */ {"wst", TZ, 48}, /* West Australian Std Time */ + {"y", UNITS, YEAR}, /* "year" for ISO input */ {"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */ {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */ {"yst", TZ, NEG(54)}, /* Yukon Standard Time */ @@ -222,7 +231,7 @@ static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0]; /* Used for SET australian_timezones to override North American ones */ static datetkn australian_datetktbl[] = { - {"cst", TZ, 63}, /* Australia Eastern Std Time */ + {"cst", TZ, 63}, /* Australia Central Std Time */ {"est", TZ, 60}, /* Australia Eastern Std Time */ {"sat", TZ, 57}, }; @@ -231,7 +240,7 @@ static unsigned int australian_szdatetktbl = sizeof australian_datetktbl / sizeof australian_datetktbl[0]; static datetkn deltatktbl[] = { -/* text token lexval */ +/* text, token, lexval */ {"@", IGNORE, 0}, /* postgres relative time prefix */ {DAGO, AGO, 0}, /* "ago" indicates negative time offset */ {"c", UNITS, DTK_CENTURY}, /* "century" relative time units */ @@ -329,7 +338,8 @@ datetkn *deltacache[MAXDATEFIELDS] = {NULL}; * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague * now at Aerospace Corp. (hi, Henry!) * - * These routines will be used by other date/time packages - tgl 97/02/25 + * These routines will be used by other date/time packages + * - thomas 97/02/25 */ int @@ -413,6 +423,7 @@ ParseDateTime(char *timestr, char *lowstr, if (*cp == ':') { ftype[nf] = DTK_TIME; + *lp++ = *cp++; while (isdigit((unsigned char) *cp) || (*cp == ':') || (*cp == '.')) *lp++ = *cp++; @@ -422,10 +433,20 @@ ParseDateTime(char *timestr, char *lowstr, else if ((*cp == '-') || (*cp == '/') || (*cp == '.')) { ftype[nf] = DTK_DATE; - while (isalnum((unsigned char) *cp) || (*cp == '-') || - (*cp == '/') || (*cp == '.')) - *lp++ = tolower((unsigned char) *cp++); - + *lp++ = *cp++; + /* second field is all digits? then no embedded text month */ + if (isdigit((unsigned char) *cp)) + { + while (isdigit((unsigned char) *cp) || (*cp == '-') || + (*cp == '/') || (*cp == '.')) + *lp++ = *cp++; + } + else + { + while (isalnum((unsigned char) *cp) || (*cp == '-') || + (*cp == '/') || (*cp == '.')) + *lp++ = tolower((unsigned char) *cp++); + } } /* @@ -539,7 +560,7 @@ ParseDateTime(char *timestr, char *lowstr, * Use the system-provided functions to get the current time zone * if not specified in the input string. * If the date is outside the time_t system-supported time range, - * then assume GMT time zone. - tgl 97/05/27 + * then assume GMT time zone. - thomas 1997/05/27 */ int DecodeDateTime(char **field, int *ftype, int nf, @@ -548,6 +569,7 @@ DecodeDateTime(char **field, int *ftype, int nf, int fmask = 0, tmask, type; + int ptype = 0; /* "prefix type" for ISO y2001m02d04 format */ int i; int flen, val; @@ -556,13 +578,16 @@ DecodeDateTime(char **field, int *ftype, int nf, int is2digits = FALSE; int bc = FALSE; + /* We'll insist on at least all of the date fields, + * but initialize the remaining fields in case they are not + * set later... + */ *dtype = DTK_DATE; tm->tm_hour = 0; tm->tm_min = 0; tm->tm_sec = 0; *fsec = 0; - tm->tm_isdst = -1; /* don't know daylight savings time status - * apriori */ + tm->tm_isdst = -1; /* don't know daylight savings time status apriori */ if (tzp != NULL) *tzp = 0; @@ -571,13 +596,32 @@ DecodeDateTime(char **field, int *ftype, int nf, switch (ftype[i]) { case DTK_DATE: + /* Previous field was a label for "julian date"? + * then this should be a julian date with fractional day... + */ + if (ptype == JULIAN) + { + char *cp; + double dt, date, time; - /* - * Already have a date? Then this might be a POSIX time - * zone with an embedded dash (e.g. "PST-3" == "EST") - - * thomas 2000-03-15 + dt = strtod(field[i], &cp); + if (*cp != '\0') + return -1; + + time = dt * 86400; + TMODULO(time, date, 86400e0); + j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + dt2time(time, &tm->tm_hour, &tm->tm_min, fsec); + + tmask = DTK_DATE_M | DTK_TIME_M; + *dtype = DTK_DATE; + } + + /* Already have a date? Then this might be a POSIX time + * zone with an embedded dash (e.g. "PST-3" == "EST") + * - thomas 2000-03-15 */ - if ((fmask & DTK_DATE_M) == DTK_DATE_M) + else if ((fmask & DTK_DATE_M) == DTK_DATE_M) { if ((tzp == NULL) || (DecodePosixTimezone(field[i], tzp) != 0)) @@ -587,15 +631,16 @@ DecodeDateTime(char **field, int *ftype, int nf, tmask = DTK_M(TZ); } else if (DecodeDate(field[i], fmask, &tmask, tm) != 0) + { return -1; + } break; case DTK_TIME: if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) return -1; - /* - * check upper limit on hours; other limits checked in + /* Check upper limit on hours; other limits checked in * DecodeTime() */ if (tm->tm_hour > 23) @@ -618,7 +663,8 @@ DecodeDateTime(char **field, int *ftype, int nf, * PST) */ if ((i > 0) && ((fmask & DTK_M(TZ)) != 0) - && (ftype[i - 1] == DTK_TZ) && (isalpha((unsigned char) *field[i - 1]))) + && (ftype[i - 1] == DTK_TZ) + && (isalpha((unsigned char) *field[i - 1]))) { *tzp -= tz; tmask = 0; @@ -634,21 +680,81 @@ DecodeDateTime(char **field, int *ftype, int nf, case DTK_NUMBER: flen = strlen(field[i]); + /* Was this an "ISO date" with embedded field labels? + * An example is "y2001m02d04" - thomas 2001-02-04 + */ + if (ptype != 0) + { + char *cp; + int val; + + val = strtol(field[i], &cp, 10); + if (*cp != '\0') + return -1; + + switch (ptype) { + case YEAR: + tm->tm_year = val; + tmask = DTK_M(ptype); + break; + + case MONTH: + tm->tm_mon = val; + tmask = DTK_M(ptype); + break; + + case DAY: + tm->tm_mday = val; + tmask = DTK_M(ptype); + break; + + case HOUR: + tm->tm_hour = val; + tmask = DTK_M(ptype); + break; + + case MINUTE: + tm->tm_min = val; + tmask = DTK_M(ptype); + break; + + case SECOND: + tm->tm_sec = val; + tmask = DTK_M(ptype); + break; + + case JULIAN: + /* previous field was a label for "julian date"? + * then this is a julian day with no fractional part + * (see DTK_DATE for cases involving fractional parts) + */ + j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + + tmask = DTK_DATE_M; + break; + + default: + return -1; + break; + } + + ptype = 0; + *dtype = DTK_DATE; + } /* * long numeric string and either no date or no time read * yet? then interpret as a concatenated date or time... */ - if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M))) + else if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M))) { if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) return -1; } /* otherwise it is a single date/time field... */ - else + else if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) { - if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) - return -1; + return -1; } break; @@ -664,10 +770,15 @@ DecodeDateTime(char **field, int *ftype, int nf, case RESERV: switch (val) { + case DTK_CURRENT: case DTK_NOW: tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ)); *dtype = DTK_DATE; +#if NOT_USED GetCurrentTime(tm); +#else + GetCurrentTimeUsec(tm, fsec); +#endif if (tzp != NULL) *tzp = CTimeZone; break; @@ -786,6 +897,18 @@ DecodeDateTime(char **field, int *ftype, int nf, tm->tm_wday = val; break; + case UNITS: + ptype = val; + tmask = 0; + break; + + case DTK_ISO_TIME: + if ((i < 1) || (i >= (nf-1)) + || (ftype[i-1] != DTK_DATE) + || (ftype[i+1] != DTK_TIME)) + return -1; + break; + default: return -1; } @@ -1182,6 +1305,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm) str++; } + /* Just get rid of any non-digit, non-alpha characters... */ if (*str != '\0') *str++ = '\0'; nf++; @@ -1362,8 +1486,9 @@ DecodeNumber(int flen, char *str, int fmask, /* * Enough digits to be unequivocal year? Used to test for 4 digits or * more, but we now test first for a three-digit doy so anything - * bigger than two digits had better be an explicit year. - thomas - * 1999-01-09 Back to requiring a 4 digit year. We accept a two digit + * bigger than two digits had better be an explicit year. + * - thomas 1999-01-09 + * Back to requiring a 4 digit year. We accept a two digit * year farther down. - thomas 2000-03-28 */ else if (flen >= 4) @@ -1613,7 +1738,7 @@ DecodeSpecial(int field, char *lowtoken, int *val) datecache[field] = tp; if (tp == NULL) { - type = IGNORE; + type = UNKNOWN_FIELD; *val = 0; } else @@ -1747,10 +1872,11 @@ DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, do case DTK_NUMBER: val = strtol(field[i], &cp, 10); + if (type == IGNORE) + type = DTK_SECOND; + if (*cp == '.') { - if (type == IGNORE) - type = DTK_SECOND; fval = strtod(cp, &cp); if (*cp != '\0') return -1; @@ -1928,7 +2054,7 @@ DecodeUnits(int field, char *lowtoken, int *val) deltacache[field] = tp; if (tp == NULL) { - type = IGNORE; + type = UNKNOWN_FIELD; *val = 0; } else @@ -1985,8 +2111,8 @@ EncodeDateOnly(struct tm * tm, int style, char *str) switch (style) { - /* compatible with ISO date formats */ case USE_ISO_DATES: + /* compatible with ISO date formats */ if (tm->tm_year > 0) sprintf(str, "%04d-%02d-%02d", tm->tm_year, tm->tm_mon, tm->tm_mday); @@ -1995,8 +2121,8 @@ EncodeDateOnly(struct tm * tm, int style, char *str) -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC"); break; - /* compatible with Oracle/Ingres date formats */ case USE_SQL_DATES: + /* compatible with Oracle/Ingres date formats */ if (EuroDates) sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon); else @@ -2007,8 +2133,8 @@ EncodeDateOnly(struct tm * tm, int style, char *str) sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC"); break; - /* German-style date format */ case USE_GERMAN_DATES: + /* German-style date format */ sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon); if (tm->tm_year > 0) sprintf((str + 5), ".%04d", tm->tm_year); @@ -2016,9 +2142,9 @@ EncodeDateOnly(struct tm * tm, int style, char *str) sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC"); break; - /* traditional date-only style for Postgres */ case USE_POSTGRES_DATES: default: + /* traditional date-only style for Postgres */ if (EuroDates) sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon); else diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index 59b516556c9..c80b5c429b6 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.15 2001/09/21 15:27:38 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.16 2001/09/28 08:09:10 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -205,6 +205,10 @@ format_type_internal(Oid type_oid, int32 typemod, bool allow_invalid) break; case TIMESTAMPOID: + buf = pstrdup("timestamp without time zone"); + break; + + case TIMESTAMPTZOID: buf = pstrdup("timestamp with time zone"); break; diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 532f3eb1d49..d517eb68183 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * formatting.c * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.40 2001/09/12 04:01:57 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.41 2001/09/28 08:09:11 thomas Exp $ * * * Portions Copyright (c) 1999-2000, PostgreSQL Global Development Group @@ -2753,6 +2753,30 @@ timestamp_to_char(PG_FUNCTION_ARGS) Timestamp dt = PG_GETARG_TIMESTAMP(0); text *fmt = PG_GETARG_TEXT_P(1), *res; TmToChar tmtc; + int r = 0; + + if ((VARSIZE(fmt) - VARHDRSZ) <=0 || TIMESTAMP_NOT_FINITE(dt)) + PG_RETURN_NULL(); + + ZERO_tmtc(&tmtc); + + r = timestamp2tm(dt, NULL, tmtcTm(&tmtc), &tmtcFsec(&tmtc), NULL); + + if (r != 0) + elog(ERROR, "to_char(): Unable to convert timestamp to tm"); + + if (!(res=datetime_to_char_body(&tmtc, fmt))) + PG_RETURN_NULL(); + + PG_RETURN_TEXT_P(res); +} + +Datum +timestamptz_to_char(PG_FUNCTION_ARGS) +{ + TimestampTz dt = PG_GETARG_TIMESTAMP(0); + text *fmt = PG_GETARG_TEXT_P(1), *res; + TmToChar tmtc; int tz, r = 0; if ((VARSIZE(fmt) - VARHDRSZ) <=0 || TIMESTAMP_NOT_FINITE(dt)) @@ -2760,12 +2784,7 @@ timestamp_to_char(PG_FUNCTION_ARGS) ZERO_tmtc(&tmtc); - if (TIMESTAMP_IS_EPOCH(dt)) - r = timestamp2tm(SetTimestamp(dt), NULL, tmtcTm(&tmtc), &tmtcFsec(&tmtc), NULL); - else if (TIMESTAMP_IS_CURRENT(dt)) - r = timestamp2tm(SetTimestamp(dt), &tz, tmtcTm(&tmtc), &tmtcFsec(&tmtc), &tmtcTzn(&tmtc)); - else - r = timestamp2tm(dt, &tz, tmtcTm(&tmtc), &tmtcFsec(&tmtc), &tmtcTzn(&tmtc)); + r = timestamp2tm(dt, &tz, tmtcTm(&tmtc), &tmtcFsec(&tmtc), &tmtcTzn(&tmtc)); if (r != 0) elog(ERROR, "to_char(): Unable to convert timestamp to tm"); @@ -2805,7 +2824,7 @@ interval_to_char(PG_FUNCTION_ARGS) /* --------------------- * TO_TIMESTAMP() * - * Make Timestamp from date_str which is formated at argument 'fmt' + * Make Timestamp from date_str which is formatted at argument 'fmt' * ( to_timestamp is reverse to_char() ) * --------------------- */ diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c index 0f8ed87e5ba..ca8d728454b 100644 --- a/src/backend/utils/adt/nabstime.c +++ b/src/backend/utils/adt/nabstime.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.85 2001/05/03 19:00:36 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.86 2001/09/28 08:09:11 thomas Exp $ * * NOTES * @@ -179,6 +179,84 @@ GetCurrentAbsoluteTime(void) } /* GetCurrentAbsoluteTime() */ +/* GetCurrentAbsoluteTime() + * Get the current system time. Set timezone parameters if not specified elsewhere. + * Define HasTZSet to allow clients to specify the default timezone. + * + * Returns the number of seconds since epoch (January 1 1970 GMT) + */ +AbsoluteTime +GetCurrentAbsoluteTimeUsec(int *usec) +{ + time_t now; + struct timeval tp; +// struct timezone tpz; +#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) + struct tm *tm; +#else + struct timeb tb; /* the old V7-ism */ +#endif + + gettimeofday(&tp, NULL); + + now = tp.tv_sec; + *usec = tp.tv_usec; + +#ifdef NOT_USED +#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) + now = time(NULL); +#else + ftime(&tb); + now = tb.time; +#endif +#endif + + if (!HasCTZSet) + { +#if defined(HAVE_TM_ZONE) + tm = localtime(&now); + + CTimeZone = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */ + CDayLight = (tm->tm_isdst > 0); + +#ifdef NOT_USED + + /* + * XXX is there a better way to get local timezone string w/o + * tzname? - tgl 97/03/18 + */ + strftime(CTZName, MAXTZLEN, "%Z", tm); +#endif + + /* + * XXX FreeBSD man pages indicate that this should work - thomas + * 1998-12-12 + */ + strcpy(CTZName, tm->tm_zone); + +#elif defined(HAVE_INT_TIMEZONE) + tm = localtime(&now); + + CDayLight = tm->tm_isdst; + CTimeZone = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL); + strcpy(CTZName, tzname[tm->tm_isdst]); +#else /* neither HAVE_TM_ZONE nor + * HAVE_INT_TIMEZONE */ + CTimeZone = tb.timezone * 60; + CDayLight = (tb.dstflag != 0); + + /* + * XXX does this work to get the local timezone string in V7? - + * tgl 97/03/18 + */ + strftime(CTZName, MAXTZLEN, "%Z", localtime(&now)); +#endif + }; + + return (AbsoluteTime) now; +} /* GetCurrentAbsoluteTime() */ + + void GetCurrentTime(struct tm * tm) { @@ -191,6 +269,19 @@ GetCurrentTime(struct tm * tm) void +GetCurrentTimeUsec(struct tm *tm, double *fsec) +{ + int tz; + int usec; + + abstime2tm(GetCurrentTransactionStartTimeUsec(&usec), &tz, tm, NULL); + *fsec = usec * 1.0e-6; + + return; +} /* GetCurrentTimeUsec() */ + + +void abstime2tm(AbsoluteTime _time, int *tzp, struct tm * tm, char *tzn) { time_t time = (time_t) _time; @@ -357,11 +448,9 @@ nabstimein(PG_FUNCTION_ARGS) break; case DTK_EPOCH: - result = EPOCH_ABSTIME; - break; - - case DTK_CURRENT: - result = CURRENT_ABSTIME; + /* Don't bother retaining this as a reserved value, + * but instead just set to the actual epoch time (1970-01-01) */ + result = 0; break; case DTK_LATE: @@ -404,15 +493,12 @@ nabstimeout(PG_FUNCTION_ARGS) switch (time) { - case EPOCH_ABSTIME: - strcpy(buf, EPOCH); - break; + /* Note that timestamp no longer supports 'invalid'. + * Retain 'invalid' for abstime for now, but dump it someday. + */ case INVALID_ABSTIME: strcpy(buf, INVALID); break; - case CURRENT_ABSTIME: - strcpy(buf, DCURRENT); - break; case NOEND_ABSTIME: strcpy(buf, LATE); break; @@ -449,37 +535,37 @@ abstime_finite(PG_FUNCTION_ARGS) static int abstime_cmp_internal(AbsoluteTime a, AbsoluteTime b) { - /* - * We consider all INVALIDs to be equal and larger than any non-INVALID. - * This is somewhat arbitrary; the important thing is to have a - * consistent sort order. - */ +/* + * We consider all INVALIDs to be equal and larger than any non-INVALID. + * This is somewhat arbitrary; the important thing is to have a + * consistent sort order. + */ if (a == INVALID_ABSTIME) { - if (b == INVALID_ABSTIME) - return 0; /* INVALID = INVALID */ - else - return 1; /* INVALID > non-INVALID */ - } - else if (b == INVALID_ABSTIME) - { - return -1; /* non-INVALID < INVALID */ + if (b == INVALID_ABSTIME) + return 0; /* INVALID = INVALID */ + else + return 1; /* INVALID > non-INVALID */ } + + if (b == INVALID_ABSTIME) + return -1; /* non-INVALID < INVALID */ + +#if 0 +/* CURRENT is no longer stored internally... */ + /* XXX this is broken, should go away: */ + if (a == CURRENT_ABSTIME) + a = GetCurrentTransactionStartTime(); + if (b == CURRENT_ABSTIME) + b = GetCurrentTransactionStartTime(); +#endif + + if (a > b) + return 1; + else if (a == b) + return 0; else - { - /* XXX this is broken, should go away: */ - if (a == CURRENT_ABSTIME) - a = GetCurrentTransactionStartTime(); - if (b == CURRENT_ABSTIME) - b = GetCurrentTransactionStartTime(); - - if (a > b) - return 1; - else if (a == b) - return 0; - else - return -1; - } + return -1; } Datum @@ -546,7 +632,7 @@ btabstimecmp(PG_FUNCTION_ARGS) } -/* datetime_abstime() +/* timestamp_abstime() * Convert timestamp to abstime. */ Datum @@ -555,26 +641,23 @@ timestamp_abstime(PG_FUNCTION_ARGS) Timestamp timestamp = PG_GETARG_TIMESTAMP(0); AbsoluteTime result; double fsec; + int tz; struct tm tt, *tm = &tt; - if (TIMESTAMP_IS_INVALID(timestamp)) - result = INVALID_ABSTIME; - else if (TIMESTAMP_IS_NOBEGIN(timestamp)) + if (TIMESTAMP_IS_NOBEGIN(timestamp)) result = NOSTART_ABSTIME; else if (TIMESTAMP_IS_NOEND(timestamp)) result = NOEND_ABSTIME; + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0) + { + tz = DetermineLocalTimeZone(tm); + result = tm2abstime(tm, tz); + } else { - if (TIMESTAMP_IS_RELATIVE(timestamp)) - { - timestamp2tm(SetTimestamp(timestamp), NULL, tm, &fsec, NULL); - result = tm2abstime(tm, 0); - } - else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0) - result = tm2abstime(tm, 0); - else - result = INVALID_ABSTIME; + elog(ERROR, "Unable to convert timestamp to abstime"); + result = INVALID_ABSTIME; } PG_RETURN_ABSOLUTETIME(result); @@ -588,11 +671,16 @@ abstime_timestamp(PG_FUNCTION_ARGS) { AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0); Timestamp result; + struct tm tt, + *tm = &tt; + int tz; + char tzn[MAXTZLEN]; switch (abstime) { case INVALID_ABSTIME: - TIMESTAMP_INVALID(result); + elog(ERROR, "Unable to convert abstime 'invalid' to timestamp"); + TIMESTAMP_NOBEGIN(result); break; case NOSTART_ABSTIME: @@ -603,12 +691,65 @@ abstime_timestamp(PG_FUNCTION_ARGS) TIMESTAMP_NOEND(result); break; - case EPOCH_ABSTIME: - TIMESTAMP_EPOCH(result); + default: + abstime2tm(abstime, &tz, tm, tzn); + result = abstime + ((date2j(1970, 1, 1) - date2j(2000, 1, 1)) * 86400) + tz; break; + }; + + PG_RETURN_TIMESTAMP(result); +} + + +/* timestamptz_abstime() + * Convert timestamp with time zone to abstime. + */ +Datum +timestamptz_abstime(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); + AbsoluteTime result; + double fsec; + struct tm tt, + *tm = &tt; - case CURRENT_ABSTIME: - TIMESTAMP_CURRENT(result); + if (TIMESTAMP_IS_NOBEGIN(timestamp)) + result = NOSTART_ABSTIME; + else if (TIMESTAMP_IS_NOEND(timestamp)) + result = NOEND_ABSTIME; + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0) + result = tm2abstime(tm, 0); + else + { + elog(ERROR, "Unable to convert timestamp to abstime"); + result = INVALID_ABSTIME; + } + + PG_RETURN_ABSOLUTETIME(result); +} + +/* abstime_timestamptz() + * Convert abstime to timestamp. + */ +Datum +abstime_timestamptz(PG_FUNCTION_ARGS) +{ + AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0); + TimestampTz result; + + switch (abstime) + { + case INVALID_ABSTIME: + elog(ERROR, "Unable to convert abstime 'invalid' to timestamptz"); + TIMESTAMP_NOBEGIN(result); + break; + + case NOSTART_ABSTIME: + TIMESTAMP_NOBEGIN(result); + break; + + case NOEND_ABSTIME: + TIMESTAMP_NOEND(result); break; default: @@ -653,14 +794,15 @@ reltimein(PG_FUNCTION_ARGS) case DTK_DELTA: result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec); result += (((tm->tm_year * 365) + (tm->tm_mon * 30) + tm->tm_mday) * (24 * 60 * 60)); - PG_RETURN_RELATIVETIME(result); + break; default: - PG_RETURN_RELATIVETIME(INVALID_RELTIME); + elog(ERROR, "Bad reltime (internal coding error) '%s'", str); + result = INVALID_RELTIME; + break; } - elog(ERROR, "Bad reltime (internal coding error) '%s'", str); - PG_RETURN_RELATIVETIME(INVALID_RELTIME); + PG_RETURN_RELATIVETIME(result); } @@ -676,13 +818,8 @@ reltimeout(PG_FUNCTION_ARGS) *tm = &tt; char buf[MAXDATELEN + 1]; - if (time == INVALID_RELTIME) - strcpy(buf, INVALID_RELTIME_STR); - else - { - reltime2tm(time, tm); - EncodeTimeSpan(tm, 0, DateStyle, buf); - } + reltime2tm(time, tm); + EncodeTimeSpan(tm, 0, DateStyle, buf); result = pstrdup(buf); PG_RETURN_CSTRING(result); @@ -702,44 +839,6 @@ reltime2tm(RelativeTime time, struct tm * tm) return; } /* reltime2tm() */ -#ifdef NOT_USED -int -dummyfunc() -{ - char *timestring; - long quantity; - int i; - int unitnr; - - timestring = (char *) palloc(Max(strlen(INVALID_RELTIME_STR), - UNITMAXLEN) + 1); - if (timevalue == INVALID_RELTIME) - { - strcpy(timestring, INVALID_RELTIME_STR); - return timestring; - } - - if (timevalue == 0) - i = 1; /* unit = 'seconds' */ - else - for (i = 12; i >= 0; i = i - 2) - if ((timevalue % sec_tab[i]) == 0) - break; /* appropriate unit found */ - unitnr = i; - quantity = (timevalue / sec_tab[unitnr]); - if (quantity > 1 || quantity < -1) - unitnr++; /* adjust index for PLURAL of unit */ - if (quantity >= 0) - sprintf(timestring, "%c %lu %s", RELTIME_LABEL, - quantity, unit_tab[unitnr]); - else - sprintf(timestring, "%c %lu %s %s", RELTIME_LABEL, - (quantity * -1), unit_tab[unitnr], RELTIME_PAST); - return timestring; -} - -#endif - /* * tintervalin - converts an interval string to internal format @@ -749,26 +848,25 @@ tintervalin(PG_FUNCTION_ARGS) { char *intervalstr = PG_GETARG_CSTRING(0); TimeInterval interval; - int error; AbsoluteTime i_start, i_end, t1, t2; interval = (TimeInterval) palloc(sizeof(TimeIntervalData)); - error = istinterval(intervalstr, &t1, &t2); - if (error == 0) - interval->status = T_INTERVAL_INVAL; + if (istinterval(intervalstr, &t1, &t2) == 0) + elog(ERROR, "Unable to decode tinterval '%s'", intervalstr); + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) - interval->status = T_INTERVAL_INVAL; /* undefined */ + interval->status = T_INTERVAL_INVAL; /* undefined */ else - { - i_start = ABSTIMEMIN(t1, t2); - i_end = ABSTIMEMAX(t1, t2); - interval->data[0] = i_start; - interval->data[1] = i_end; interval->status = T_INTERVAL_VALID; - } + + i_start = ABSTIMEMIN(t1, t2); + i_end = ABSTIMEMAX(t1, t2); + interval->data[0] = i_start; + interval->data[1] = i_end; + PG_RETURN_TIMEINTERVAL(interval); } @@ -818,30 +916,28 @@ interval_reltime(PG_FUNCTION_ARGS) month; double span; - if (INTERVAL_IS_INVALID(*interval)) - time = INVALID_RELTIME; + if (interval->month == 0) + { + year = 0; + month = 0; + } + else if (abs(interval->month) >= 12) + { + year = (interval->month / 12); + month = (interval->month % 12); + } else { - if (interval->month == 0) - { - year = 0; - month = 0; - } - else if (abs(interval->month) >= 12) - { - year = (interval->month / 12); - month = (interval->month % 12); - } - else - { - year = 0; - month = interval->month; - } + year = 0; + month = interval->month; + } - span = (((((double) 365 * year) + ((double) 30 * month)) * 86400) + interval->time); + span = (((((double) 365 * year) + ((double) 30 * month)) * 86400) + interval->time); - time = (((span > INT_MIN) && (span < INT_MAX)) ? span : INVALID_RELTIME); - } + if ((span < INT_MIN) || (span > INT_MAX)) + time = INVALID_RELTIME; + else + time = span; PG_RETURN_RELATIVETIME(time); } @@ -860,7 +956,9 @@ reltime_interval(PG_FUNCTION_ARGS) switch (reltime) { case INVALID_RELTIME: - INTERVAL_INVALID(*result); + elog(ERROR, "Unable to convert reltime 'invalid' to interval"); + result->time = 0; + result->month = 0; break; default: @@ -884,11 +982,12 @@ mktinterval(PG_FUNCTION_ARGS) { AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0); AbsoluteTime t2 = PG_GETARG_ABSOLUTETIME(1); - AbsoluteTime tstart = ABSTIMEMIN(t1, t2), - tend = ABSTIMEMAX(t1, t2); + AbsoluteTime tstart = ABSTIMEMIN(t1, t2); + AbsoluteTime tend = ABSTIMEMAX(t1, t2); TimeInterval interval; interval = (TimeInterval) palloc(sizeof(TimeIntervalData)); + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) interval->status = T_INTERVAL_INVAL; else @@ -909,7 +1008,7 @@ mktinterval(PG_FUNCTION_ARGS) */ /* - * timepl - returns the value of (abstime t1 + relime t2) + * timepl - returns the value of (abstime t1 + reltime t2) */ Datum timepl(PG_FUNCTION_ARGS) @@ -917,8 +1016,10 @@ timepl(PG_FUNCTION_ARGS) AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0); RelativeTime t2 = PG_GETARG_RELATIVETIME(1); +#if 0 if (t1 == CURRENT_ABSTIME) t1 = GetCurrentTransactionStartTime(); +#endif if (AbsoluteTimeIsReal(t1) && RelativeTimeIsValid(t2) && @@ -939,8 +1040,10 @@ timemi(PG_FUNCTION_ARGS) AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0); RelativeTime t2 = PG_GETARG_RELATIVETIME(1); +#if 0 if (t1 == CURRENT_ABSTIME) t1 = GetCurrentTransactionStartTime(); +#endif if (AbsoluteTimeIsReal(t1) && RelativeTimeIsValid(t2) && @@ -953,27 +1056,6 @@ timemi(PG_FUNCTION_ARGS) /* - * abstimemi - returns the value of (abstime t1 - abstime t2) - * - * This is not exported, so it's not been made fmgr-compatible. - */ -static RelativeTime -abstimemi(AbsoluteTime t1, AbsoluteTime t2) -{ - if (t1 == CURRENT_ABSTIME) - t1 = GetCurrentTransactionStartTime(); - if (t2 == CURRENT_ABSTIME) - t2 = GetCurrentTransactionStartTime(); - - if (AbsoluteTimeIsReal(t1) && - AbsoluteTimeIsReal(t2)) - return t1 - t2; - - return INVALID_RELTIME; -} - - -/* * intinterval - returns true iff absolute date is in the interval */ Datum @@ -1002,13 +1084,20 @@ Datum tintervalrel(PG_FUNCTION_ARGS) { TimeInterval interval = PG_GETARG_TIMEINTERVAL(0); + AbsoluteTime t1 = interval->data[0]; + AbsoluteTime t2 = interval->data[1]; if (interval->status != T_INTERVAL_VALID) PG_RETURN_RELATIVETIME(INVALID_RELTIME); - PG_RETURN_RELATIVETIME(abstimemi(interval->data[1], interval->data[0])); + if (AbsoluteTimeIsReal(t1) && + AbsoluteTimeIsReal(t2)) + PG_RETURN_RELATIVETIME(t2 - t1); + + PG_RETURN_RELATIVETIME(INVALID_RELTIME); } + /* * timenow - returns time "now", internal format * @@ -1021,6 +1110,7 @@ timenow(PG_FUNCTION_ARGS) if (time(&sec) < 0) PG_RETURN_ABSOLUTETIME(INVALID_ABSTIME); + PG_RETURN_ABSOLUTETIME((AbsoluteTime) sec); } @@ -1113,11 +1203,11 @@ tintervalsame(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); if (DatumGetBool(DirectFunctionCall2(abstimeeq, - AbsoluteTimeGetDatum(i1->data[0]), - AbsoluteTimeGetDatum(i2->data[0]))) && + AbsoluteTimeGetDatum(i1->data[0]), + AbsoluteTimeGetDatum(i2->data[0]))) && DatumGetBool(DirectFunctionCall2(abstimeeq, - AbsoluteTimeGetDatum(i1->data[1]), - AbsoluteTimeGetDatum(i2->data[1])))) + AbsoluteTimeGetDatum(i1->data[1]), + AbsoluteTimeGetDatum(i2->data[1])))) PG_RETURN_BOOL(true); PG_RETURN_BOOL(false); } @@ -1133,9 +1223,9 @@ tintervaleq(PG_FUNCTION_ARGS) TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0); TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1); AbsoluteTime t10, - t11, - t20, - t21; + t11, + t20, + t21; if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) PG_RETURN_BOOL(false); @@ -1145,19 +1235,10 @@ tintervaleq(PG_FUNCTION_ARGS) t20 = i2->data[0]; t21 = i2->data[1]; - if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) + if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) PG_RETURN_BOOL(false); - if (t10 == CURRENT_ABSTIME) - t10 = GetCurrentTransactionStartTime(); - if (t11 == CURRENT_ABSTIME) - t11 = GetCurrentTransactionStartTime(); - if (t20 == CURRENT_ABSTIME) - t20 = GetCurrentTransactionStartTime(); - if (t21 == CURRENT_ABSTIME) - t21 = GetCurrentTransactionStartTime(); - PG_RETURN_BOOL((t11 - t10) == (t21 - t20)); } @@ -1167,9 +1248,9 @@ tintervalne(PG_FUNCTION_ARGS) TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0); TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1); AbsoluteTime t10, - t11, - t20, - t21; + t11, + t20, + t21; if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) PG_RETURN_BOOL(false); @@ -1179,19 +1260,10 @@ tintervalne(PG_FUNCTION_ARGS) t20 = i2->data[0]; t21 = i2->data[1]; - if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) + if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) PG_RETURN_BOOL(false); - if (t10 == CURRENT_ABSTIME) - t10 = GetCurrentTransactionStartTime(); - if (t11 == CURRENT_ABSTIME) - t11 = GetCurrentTransactionStartTime(); - if (t20 == CURRENT_ABSTIME) - t20 = GetCurrentTransactionStartTime(); - if (t21 == CURRENT_ABSTIME) - t21 = GetCurrentTransactionStartTime(); - PG_RETURN_BOOL((t11 - t10) != (t21 - t20)); } @@ -1201,9 +1273,9 @@ tintervallt(PG_FUNCTION_ARGS) TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0); TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1); AbsoluteTime t10, - t11, - t20, - t21; + t11, + t20, + t21; if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) PG_RETURN_BOOL(false); @@ -1213,19 +1285,10 @@ tintervallt(PG_FUNCTION_ARGS) t20 = i2->data[0]; t21 = i2->data[1]; - if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) + if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) PG_RETURN_BOOL(false); - if (t10 == CURRENT_ABSTIME) - t10 = GetCurrentTransactionStartTime(); - if (t11 == CURRENT_ABSTIME) - t11 = GetCurrentTransactionStartTime(); - if (t20 == CURRENT_ABSTIME) - t20 = GetCurrentTransactionStartTime(); - if (t21 == CURRENT_ABSTIME) - t21 = GetCurrentTransactionStartTime(); - PG_RETURN_BOOL((t11 - t10) < (t21 - t20)); } @@ -1235,9 +1298,9 @@ tintervalle(PG_FUNCTION_ARGS) TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0); TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1); AbsoluteTime t10, - t11, - t20, - t21; + t11, + t20, + t21; if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) PG_RETURN_BOOL(false); @@ -1247,19 +1310,10 @@ tintervalle(PG_FUNCTION_ARGS) t20 = i2->data[0]; t21 = i2->data[1]; - if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) + if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) PG_RETURN_BOOL(false); - if (t10 == CURRENT_ABSTIME) - t10 = GetCurrentTransactionStartTime(); - if (t11 == CURRENT_ABSTIME) - t11 = GetCurrentTransactionStartTime(); - if (t20 == CURRENT_ABSTIME) - t20 = GetCurrentTransactionStartTime(); - if (t21 == CURRENT_ABSTIME) - t21 = GetCurrentTransactionStartTime(); - PG_RETURN_BOOL((t11 - t10) <= (t21 - t20)); } @@ -1281,19 +1335,10 @@ tintervalgt(PG_FUNCTION_ARGS) t20 = i2->data[0]; t21 = i2->data[1]; - if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) + if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) PG_RETURN_BOOL(false); - if (t10 == CURRENT_ABSTIME) - t10 = GetCurrentTransactionStartTime(); - if (t11 == CURRENT_ABSTIME) - t11 = GetCurrentTransactionStartTime(); - if (t20 == CURRENT_ABSTIME) - t20 = GetCurrentTransactionStartTime(); - if (t21 == CURRENT_ABSTIME) - t21 = GetCurrentTransactionStartTime(); - PG_RETURN_BOOL((t11 - t10) > (t21 - t20)); } @@ -1315,19 +1360,10 @@ tintervalge(PG_FUNCTION_ARGS) t20 = i2->data[0]; t21 = i2->data[1]; - if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) + if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) PG_RETURN_BOOL(false); - if (t10 == CURRENT_ABSTIME) - t10 = GetCurrentTransactionStartTime(); - if (t11 == CURRENT_ABSTIME) - t11 = GetCurrentTransactionStartTime(); - if (t20 == CURRENT_ABSTIME) - t20 = GetCurrentTransactionStartTime(); - if (t21 == CURRENT_ABSTIME) - t21 = GetCurrentTransactionStartTime(); - PG_RETURN_BOOL((t11 - t10) >= (t21 - t20)); } @@ -1356,8 +1392,8 @@ tintervalleneq(PG_FUNCTION_ARGS) if (i->status == T_INTERVAL_INVAL || t == INVALID_RELTIME) PG_RETURN_BOOL(false); rt = DatumGetRelativeTime(DirectFunctionCall1(tintervalrel, - TimeIntervalGetDatum(i))); - PG_RETURN_BOOL(rt != INVALID_RELTIME && rt == t); + TimeIntervalGetDatum(i))); + PG_RETURN_BOOL((rt != INVALID_RELTIME) && (rt == t)); } Datum @@ -1371,7 +1407,7 @@ tintervallenne(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); rt = DatumGetRelativeTime(DirectFunctionCall1(tintervalrel, TimeIntervalGetDatum(i))); - PG_RETURN_BOOL(rt != INVALID_RELTIME && rt != t); + PG_RETURN_BOOL((rt != INVALID_RELTIME) && (rt != t)); } Datum @@ -1385,7 +1421,7 @@ tintervallenlt(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); rt = DatumGetRelativeTime(DirectFunctionCall1(tintervalrel, TimeIntervalGetDatum(i))); - PG_RETURN_BOOL(rt != INVALID_RELTIME && rt < t); + PG_RETURN_BOOL((rt != INVALID_RELTIME) && (rt < t)); } Datum @@ -1399,7 +1435,7 @@ tintervallengt(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); rt = DatumGetRelativeTime(DirectFunctionCall1(tintervalrel, TimeIntervalGetDatum(i))); - PG_RETURN_BOOL(rt != INVALID_RELTIME && rt > t); + PG_RETURN_BOOL((rt != INVALID_RELTIME) && (rt > t)); } Datum @@ -1413,7 +1449,7 @@ tintervallenle(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); rt = DatumGetRelativeTime(DirectFunctionCall1(tintervalrel, TimeIntervalGetDatum(i))); - PG_RETURN_BOOL(rt != INVALID_RELTIME && rt <= t); + PG_RETURN_BOOL((rt != INVALID_RELTIME) && (rt <= t)); } Datum @@ -1427,7 +1463,7 @@ tintervallenge(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); rt = DatumGetRelativeTime(DirectFunctionCall1(tintervalrel, TimeIntervalGetDatum(i))); - PG_RETURN_BOOL(rt != INVALID_RELTIME && rt >= t); + PG_RETURN_BOOL((rt != INVALID_RELTIME) && (rt >= t)); } /* @@ -1463,11 +1499,11 @@ tintervalov(PG_FUNCTION_ARGS) if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) PG_RETURN_BOOL(false); if (DatumGetBool(DirectFunctionCall2(abstimelt, - AbsoluteTimeGetDatum(i1->data[1]), - AbsoluteTimeGetDatum(i2->data[0]))) || + AbsoluteTimeGetDatum(i1->data[1]), + AbsoluteTimeGetDatum(i2->data[0]))) || DatumGetBool(DirectFunctionCall2(abstimegt, - AbsoluteTimeGetDatum(i1->data[0]), - AbsoluteTimeGetDatum(i2->data[1])))) + AbsoluteTimeGetDatum(i1->data[0]), + AbsoluteTimeGetDatum(i2->data[1])))) PG_RETURN_BOOL(false); PG_RETURN_BOOL(true); } @@ -1503,222 +1539,6 @@ tintervalend(PG_FUNCTION_ARGS) * PRIVATE ROUTINES * *****************************************************************************/ -#ifdef NOT_USED -/* - * isreltime - returns 1, iff datestring is of type reltime - * 2, iff datestring is 'invalid time' identifier - * 0, iff datestring contains a syntax error - * VALID time less or equal +/- `@ 68 years' - * - */ -int -isreltime(char *str) -{ - struct tm tt, - *tm = &tt; - double fsec; - int dtype; - char *field[MAXDATEFIELDS]; - int nf, - ftype[MAXDATEFIELDS]; - char lowstr[MAXDATELEN + 1]; - - if (!PointerIsValid(str)) - return 0; - - if (strlen(str) > MAXDATELEN) - return 0; - - if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) - || (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) - return 0; - - switch (dtype) - { - case (DTK_DELTA): - return (abs(tm->tm_year) <= 68) ? 1 : 0; - break; - - case (DTK_INVALID): - return 2; - break; - - default: - return 0; - break; - } - - return 0; -} /* isreltime() */ - -#endif - -#ifdef NOT_USED -int -dummyfunc() -{ - char *p; - char c; - int i; - char unit[UNITMAXLEN]; - char direction[DIRMAXLEN]; - int localSign; - int localUnitNumber; - long localQuantity; - - if (!PointerIsValid(sign)) - sign = &localSign; - - if (!PointerIsValid(unitnr)) - unitnr = &localUnitNumber; - - if (!PointerIsValid(quantity)) - quantity = &localQuantity; - - unit[0] = '\0'; - direction[0] = '\0'; - p = timestring; - /* skip leading blanks */ - while ((c = *p) != '\0') - { - if (c != ' ') - break; - p++; - } - - /* Test whether 'invalid time' identifier or not */ - if (!strncmp(INVALID_RELTIME_STR, p, strlen(INVALID_RELTIME_STR) + 1)) - return 2; /* correct 'invalid time' identifier found */ - - /* handle label of relative time */ - if (c != RELTIME_LABEL) - return 0; /* syntax error */ - c = *++p; - if (c != ' ') - return 0; /* syntax error */ - p++; - /* handle the quantity */ - *quantity = 0; - for (;;) - { - c = *p; - if (isdigit((unsigned char) c)) - { - *quantity = *quantity * 10 + (c - '0'); - p++; - } - else - { - if (c == ' ') - break; /* correct quantity found */ - else - return 0; /* syntax error */ - } - } - - /* handle unit */ - p++; - i = 0; - for (;;) - { - c = *p; - if (c >= 'a' && c <= 'z' && i <= (UNITMAXLEN - 1)) - { - unit[i] = c; - p++; - i++; - } - else - { - if ((c == ' ' || c == '\0') - && correct_unit(unit, unitnr)) - break; /* correct unit found */ - else - return 0; /* syntax error */ - } - } - - /* handle optional direction */ - if (c == ' ') - p++; - i = 0; - *sign = 1; - for (;;) - { - c = *p; - if (c >= 'a' && c <= 'z' && i <= (DIRMAXLEN - 1)) - { - direction[i] = c; - p++; - i++; - } - else - { - if ((c == ' ' || c == '\0') && i == 0) - { - *sign = 1; - break; /* no direction specified */ - } - if ((c == ' ' || c == '\0') && i != 0) - { - direction[i] = '\0'; - correct_dir(direction, sign); - break; /* correct direction found */ - } - else - return 0; /* syntax error */ - } - } - - return 1; -} - -/* - * correct_unit - returns 1, iff unit is a correct unit description - * - * output parameter: - * unptr: points to an integer which is the appropriate unit number - * (see function isreltime()) - */ -static int -correct_unit(char *unit, int *unptr) -{ - int j = 0; - - while (j < NUNITS) - { - if (strncmp(unit, unit_tab[j], strlen(unit_tab[j])) == 0) - { - *unptr = j; - return 1; - } - j++; - } - return 0; /* invalid unit descriptor */ -} - -/* - * correct_dir - returns 1, iff direction is a correct identifier - * - * output parameter: - * signptr: points to -1 if dir corresponds to past tense - * else to 1 - */ -static int -correct_dir(char *direction, int *signptr) -{ - *signptr = 1; - if (strncmp(RELTIME_PAST, direction, strlen(RELTIME_PAST) + 1) == 0) - { - *signptr = -1; - return 1; - } - else - return 0; /* invalid direction descriptor */ -} - -#endif - /* * istinterval - returns 1, iff i_string is a valid interval descr. * 0, iff i_string is NOT a valid interval desc. diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 824faae152b..1bd42689659 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.50 2001/09/06 03:22:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.51 2001/09/28 08:09:11 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -32,7 +32,7 @@ static double time2t(const int hour, const int min, const double sec); static int EncodeSpecialTimestamp(Timestamp dt, char *str); static Timestamp dt2local(Timestamp dt, int timezone); -static void dt2time(Timestamp dt, int *hour, int *min, double *sec); + /***************************************************************************** * USER I/O ROUTINES * @@ -63,16 +63,12 @@ timestamp_in(PG_FUNCTION_ARGS) switch (dtype) { case DTK_DATE: - if (tm2timestamp(tm, fsec, &tz, &result) != 0) + if (tm2timestamp(tm, fsec, NULL, &result) != 0) elog(ERROR, "Timestamp out of range '%s'", str); break; case DTK_EPOCH: - TIMESTAMP_EPOCH(result); - break; - - case DTK_CURRENT: - TIMESTAMP_CURRENT(result); + result = SetEpochTimestamp(); break; case DTK_LATE: @@ -84,12 +80,13 @@ timestamp_in(PG_FUNCTION_ARGS) break; case DTK_INVALID: - TIMESTAMP_INVALID(result); + elog(ERROR, "Timestamp '%s' no longer supported", str); + TIMESTAMP_NOEND(result); break; default: - elog(ERROR, "Internal coding error, can't input timestamp '%s'", str); - TIMESTAMP_INVALID(result); /* keep compiler quiet */ + elog(ERROR, "Timestamp '%s' not parsed; internal coding error", str); + TIMESTAMP_NOEND(result); } PG_RETURN_TIMESTAMP(result); @@ -103,6 +100,86 @@ timestamp_out(PG_FUNCTION_ARGS) { Timestamp dt = PG_GETARG_TIMESTAMP(0); char *result; + struct tm tt, + *tm = &tt; + double fsec; + char *tzn = NULL; + char buf[MAXDATELEN + 1]; + + if (TIMESTAMP_NOT_FINITE(dt)) + EncodeSpecialTimestamp(dt, buf); + else if (timestamp2tm(dt, NULL, tm, &fsec, NULL) == 0) + EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf); + else + elog(ERROR, "Unable to format timestamp; internal coding error"); + + result = pstrdup(buf); + PG_RETURN_CSTRING(result); +} + + +/* timestamptz_in() + * Convert a string to internal form. + */ +Datum +timestamptz_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + TimestampTz 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 ((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); + + switch (dtype) + { + case DTK_DATE: + if (tm2timestamp(tm, fsec, &tz, &result) != 0) + elog(ERROR, "Timestamp out of range '%s'", str); + break; + + case DTK_EPOCH: + result = SetEpochTimestamp(); + break; + + case DTK_LATE: + TIMESTAMP_NOEND(result); + break; + + case DTK_EARLY: + TIMESTAMP_NOBEGIN(result); + break; + + case DTK_INVALID: + elog(ERROR, "Timestamp with time zone '%s' no longer supported", str); + TIMESTAMP_NOEND(result); + break; + + default: + elog(ERROR, "Timestamp with time zone '%s' not parsed; internal coding error", str); + TIMESTAMP_NOEND(result); + } + + PG_RETURN_TIMESTAMPTZ(result); +} + +/* timestamptz_out() + * Convert a timestamp to external form. + */ +Datum +timestamptz_out(PG_FUNCTION_ARGS) +{ + TimestampTz dt = PG_GETARG_TIMESTAMP(0); + char *result; int tz; struct tm tt, *tm = &tt; @@ -110,12 +187,12 @@ timestamp_out(PG_FUNCTION_ARGS) char *tzn; char buf[MAXDATELEN + 1]; - if (TIMESTAMP_IS_RESERVED(dt)) + if (TIMESTAMP_NOT_FINITE(dt)) EncodeSpecialTimestamp(dt, buf); else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0) EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); else - EncodeSpecialTimestamp(DT_INVALID, buf); + elog(ERROR, "Unable to format timestamp with time zone; internal coding error"); result = pstrdup(buf); PG_RETURN_CSTRING(result); @@ -132,7 +209,7 @@ Datum interval_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); - Interval *span; + Interval *result; double fsec; struct tm tt, *tm = &tt; @@ -154,25 +231,24 @@ interval_in(PG_FUNCTION_ARGS) || (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) elog(ERROR, "Bad interval external representation '%s'", str); - span = (Interval *) palloc(sizeof(Interval)); + result = (Interval *) palloc(sizeof(Interval)); switch (dtype) { case DTK_DELTA: - if (tm2interval(tm, fsec, span) != 0) - { -#if NOT_USED - INTERVAL_INVALID(span); -#endif + if (tm2interval(tm, fsec, result) != 0) elog(ERROR, "Bad interval external representation '%s'", str); - } + break; + + case DTK_INVALID: + elog(ERROR, "Interval '%s' no longer supported", str); break; default: - elog(ERROR, "Internal coding error, can't input interval '%s'", str); + elog(ERROR, "Interval '%s' not parsed; internal coding error", str); } - PG_RETURN_INTERVAL_P(span); + PG_RETURN_INTERVAL_P(result); } /* interval_out() @@ -189,10 +265,10 @@ interval_out(PG_FUNCTION_ARGS) char buf[MAXDATELEN + 1]; if (interval2tm(*span, tm, &fsec) != 0) - PG_RETURN_NULL(); + elog(ERROR, "Unable to encode interval; internal coding error"); if (EncodeTimeSpan(tm, fsec, DateStyle, buf) != 0) - elog(ERROR, "Unable to format interval"); + elog(ERROR, "Unable to format interval; internal coding error"); result = pstrdup(buf); PG_RETURN_CSTRING(result); @@ -205,40 +281,31 @@ interval_out(PG_FUNCTION_ARGS) static 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; - } + if (TIMESTAMP_IS_NOBEGIN(dt)) + strcpy(str, EARLY); + else if (TIMESTAMP_IS_NOEND(dt)) + strcpy(str, LATE); + else + return FALSE; - return FALSE; + return TRUE; } /* EncodeSpecialTimestamp() */ Datum now(PG_FUNCTION_ARGS) { - Timestamp result; + TimestampTz result; AbsoluteTime sec; + int usec; - sec = GetCurrentTransactionStartTime(); + sec = GetCurrentTransactionStartTimeUsec(&usec); - result = (sec - ((date2j(2000, 1, 1) - date2j(1970, 1, 1)) * 86400)); + result = (sec + (usec * 1.0e-6) - ((date2j(2000, 1, 1) - date2j(1970, 1, 1)) * 86400)); - PG_RETURN_TIMESTAMP(result); + PG_RETURN_TIMESTAMPTZ(result); } -static void +void dt2time(Timestamp jd, int *hour, int *min, double *sec) { double time; @@ -485,9 +552,7 @@ timestamp_finite(PG_FUNCTION_ARGS) Datum interval_finite(PG_FUNCTION_ARGS) { - Interval *interval = PG_GETARG_INTERVAL_P(0); - - PG_RETURN_BOOL(!INTERVAL_NOT_FINITE(*interval)); + PG_RETURN_BOOL(true); } @@ -495,7 +560,7 @@ interval_finite(PG_FUNCTION_ARGS) * Relational operators for timestamp. *---------------------------------------------------------*/ -static void +void GetEpochTime(struct tm * tm) { struct tm *t0; @@ -518,24 +583,17 @@ GetEpochTime(struct tm * tm) } /* GetEpochTime() */ Timestamp -SetTimestamp(Timestamp dt) +SetEpochTimestamp(void) { - struct tm tt; + Timestamp dt; + struct tm tt, + *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); - } + GetEpochTime(tm); + tm2timestamp(tm, 0, NULL, &dt); return dt; -} /* SetTimestamp() */ +} /* SetEpochTimestamp() */ /* * timestamp_relop - is timestamp1 relop timestamp2 @@ -545,19 +603,7 @@ SetTimestamp(Timestamp dt) static int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2) { - 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)); - } + return ((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0)); } Datum @@ -632,24 +678,17 @@ timestamp_cmp(PG_FUNCTION_ARGS) static int interval_cmp_internal(Interval *interval1, Interval *interval2) { - if (INTERVAL_IS_INVALID(*interval1)) - return (INTERVAL_IS_INVALID(*interval2) ? 0 : 1); - else if (INTERVAL_IS_INVALID(*interval2)) - return -1; - else - { - double span1, - span2; + double span1, + span2; - 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)); + 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); - } + return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0); } Datum @@ -866,6 +905,9 @@ overlaps_timestamp(PG_FUNCTION_ARGS) * "Arithmetic" operators on date/times. *---------------------------------------------------------*/ +/* We are currently sharing some code between timestamp and timestamptz. + * The comparison functions are among them. - thomas 2001-09-25 + */ Datum timestamp_smaller(PG_FUNCTION_ARGS) { @@ -873,17 +915,7 @@ timestamp_smaller(PG_FUNCTION_ARGS) Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp result; - 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); + result = ((dt2 < dt1) ? dt2 : dt1); PG_RETURN_TIMESTAMP(result); } @@ -895,17 +927,7 @@ timestamp_larger(PG_FUNCTION_ARGS) Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp result; - 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); + result = ((dt2 > dt1) ? dt2 : dt1); PG_RETURN_TIMESTAMP(result); } @@ -920,16 +942,14 @@ timestamp_mi(PG_FUNCTION_ARGS) result = (Interval *) 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); + if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2)) + { + elog(ERROR, "Unable to subtract non-finite timestamps"); + result->time = 0; + } else result->time = JROUND(dt1 - dt2); + result->month = 0; PG_RETURN_INTERVAL_P(result); @@ -951,25 +971,111 @@ timestamp_pl_span(PG_FUNCTION_ARGS) Timestamp timestamp = PG_GETARG_TIMESTAMP(0); Interval *span = PG_GETARG_INTERVAL_P(1); Timestamp result; - Timestamp dt; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = timestamp; + } + else + { + if (span->month != 0) + { + struct tm tt, + *tm = &tt; + double fsec; + + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 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, NULL, ×tamp) != 0) + { + elog(ERROR, "Unable to add timestamp and interval" + "\n\ttimestamp_pl_span() internal error encoding timestamp"); + PG_RETURN_NULL(); + } + } + else + { + elog(ERROR, "Unable to add timestamp and interval" + "\n\ttimestamp_pl_span() internal error decoding timestamp"); + PG_RETURN_NULL(); + } + } + +#ifdef ROUND_ALL + timestamp = JROUND(timestamp + span->time); +#else + timestamp += span->time; +#endif + + result = timestamp; + } + + PG_RETURN_TIMESTAMP(result); +} + +Datum +timestamp_mi_span(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + Interval *span = PG_GETARG_INTERVAL_P(1); + Interval tspan; + + tspan.month = -span->month; + tspan.time = -span->time; + + return DirectFunctionCall2(timestamp_pl_span, + TimestampGetDatum(timestamp), + PointerGetDatum(&tspan)); +} + + +/* timestamp_pl_span() + * Add a interval to a timestamp with time zone 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". + */ +Datum +timestamptz_pl_span(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); + Interval *span = PG_GETARG_INTERVAL_P(1); + TimestampTz result; int tz; char *tzn; 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) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0) { tm->tm_mon += span->month; if (tm->tm_mon > 12) @@ -989,30 +1095,33 @@ timestamp_pl_span(PG_FUNCTION_ARGS) tz = DetermineLocalTimeZone(tm); - if (tm2timestamp(tm, fsec, &tz, &dt) != 0) - elog(ERROR, "Unable to add timestamp and interval"); - + if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0) + elog(ERROR, "Unable to add timestamp and interval" + "\n\ttimestamptz_pl_span() internal error encoding timestamp"); } else - TIMESTAMP_INVALID(dt); + { + elog(ERROR, "Unable to add timestamp and interval" + "\n\ttimestamptz_pl_span() internal error decoding timestamp"); + } } #ifdef ROUND_ALL - dt = JROUND(dt + span->time); + timestamp = JROUND(timestamp + span->time); #else - dt += span->time; + timestamp += span->time; #endif - result = dt; + result = timestamp; } PG_RETURN_TIMESTAMP(result); } Datum -timestamp_mi_span(PG_FUNCTION_ARGS) +timestamptz_mi_span(PG_FUNCTION_ARGS) { - Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); Interval *span = PG_GETARG_INTERVAL_P(1); Interval tspan; @@ -1051,36 +1160,23 @@ interval_smaller(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (INTERVAL_IS_INVALID(*interval1)) + 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 if (INTERVAL_IS_INVALID(*interval2)) + else { 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; - } - } PG_RETURN_INTERVAL_P(result); } @@ -1096,36 +1192,23 @@ interval_larger(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (INTERVAL_IS_INVALID(*interval1)) + 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 if (INTERVAL_IS_INVALID(*interval2)) + else { 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; - } - } PG_RETURN_INTERVAL_P(result); } @@ -1200,7 +1283,7 @@ interval_div(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); if (factor == 0.0) - elog(ERROR, "interval_div: divide by 0.0 error"); + elog(ERROR, "interval_div: divide by 0.0 error"); months = (span1->month / factor); result->month = rint(months); @@ -1321,16 +1404,117 @@ timestamp_age(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (TIMESTAMP_IS_RELATIVE(dt1)) - dt1 = SetTimestamp(dt1); - if (TIMESTAMP_IS_RELATIVE(dt2)) - dt2 = SetTimestamp(dt2); + 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--; + } - 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)) + /* 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 encode interval" + "\n\ttimestamp_age() internal coding error"); + } + else + elog(ERROR, "Unable to decode timestamp" + "\n\ttimestamp_age() internal coding error"); + + PG_RETURN_INTERVAL_P(result); +} + + +/* timestamptz_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. + */ +Datum +timestamptz_age(PG_FUNCTION_ARGS) +{ + TimestampTz dt1 = PG_GETARG_TIMESTAMP(0); + TimestampTz dt2 = PG_GETARG_TIMESTAMP(1); + Interval *result; + double fsec, + fsec1, + fsec2; + struct tm tt, + *tm = &tt; + struct tm tt1, + *tm1 = &tt1; + struct tm tt2, + *tm2 = &tt2; + + result = (Interval *) palloc(sizeof(Interval)); + + 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); @@ -1472,6 +1656,60 @@ text_timestamp(PG_FUNCTION_ARGS) } +/* timestamptz_text() + * Convert timestamp with time zone to text data type. + */ +Datum +timestamptz_text(PG_FUNCTION_ARGS) +{ + /* Input is a Timestamp, but may as well leave it in Datum form */ + Datum timestamp = PG_GETARG_DATUM(0); + text *result; + char *str; + int len; + + str = DatumGetCString(DirectFunctionCall1(timestamptz_out, timestamp)); + + len = (strlen(str) + VARHDRSZ); + + result = palloc(len); + + VARATT_SIZEP(result) = len; + memmove(VARDATA(result), str, (len - VARHDRSZ)); + + pfree(str); + + PG_RETURN_TEXT_P(result); +} + +/* text_timestamptz() + * Convert text string to timestamp with time zone. + * Text type is not null terminated, so use temporary string + * then call the standard input routine. + */ +Datum +text_timestamptz(PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_P(0); + int i; + char *sp, + *dp, + dstr[MAXDATELEN + 1]; + + if (VARSIZE(str) - VARHDRSZ > MAXDATELEN) + elog(ERROR, "Bad timestamp with time zone external representation (too long)"); + + sp = VARDATA(str); + dp = dstr; + for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++) + *dp++ = *sp++; + *dp = '\0'; + + return DirectFunctionCall1(timestamptz_in, + CStringGetDatum(dstr)); +} + + /* interval_text() * Convert interval to text data type. */ @@ -1484,7 +1722,7 @@ interval_text(PG_FUNCTION_ARGS) int len; str = DatumGetCString(DirectFunctionCall1(interval_out, - IntervalPGetDatum(interval))); + IntervalPGetDatum(interval))); len = (strlen(str) + VARHDRSZ); @@ -1525,7 +1763,7 @@ text_interval(PG_FUNCTION_ARGS) } /* timestamp_trunc() - * Extract specified field from timestamp. + * Truncate timestamp to specified units. */ Datum timestamp_trunc(PG_FUNCTION_ARGS) @@ -1533,8 +1771,6 @@ timestamp_trunc(PG_FUNCTION_ARGS) text *units = PG_GETARG_TEXT_P(0); Timestamp timestamp = PG_GETARG_TIMESTAMP(1); Timestamp result; - Timestamp dt; - int tz; int type, val; int i; @@ -1542,7 +1778,6 @@ timestamp_trunc(PG_FUNCTION_ARGS) *lp, lowunits[MAXDATELEN + 1]; double fsec; - char *tzn; struct tm tt, *tm = &tt; @@ -1559,70 +1794,146 @@ timestamp_trunc(PG_FUNCTION_ARGS) type = DecodeUnits(0, lowunits, &val); if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_NULL(); + PG_RETURN_TIMESTAMP(timestamp); + + if ((type == UNITS) && (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0)) + { + switch (val) + { + case DTK_MILLENNIUM: + 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 = 0; + } + + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + elog(ERROR, "Unable to truncate timestamp to '%s'", lowunits); + } else { - dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp); + elog(ERROR, "Timestamp units '%s' not recognized", lowunits); + result = 0; + } - if ((type == UNITS) && (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0)) - { - switch (val) - { - case DTK_MILLENNIUM: - 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; + PG_RETURN_TIMESTAMP(result); +} - case DTK_MILLISEC: - fsec = rint(fsec * 1000) / 1000; - break; +/* timestamptz_trunc() + * Truncate timestamp to specified units. + */ +Datum +timestamptz_trunc(PG_FUNCTION_ARGS) +{ + text *units = PG_GETARG_TEXT_P(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); + TimestampTz result; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowunits[MAXDATELEN + 1]; + double fsec; + char *tzn; + struct tm tt, + *tm = &tt; - case DTK_MICROSEC: - fsec = rint(fsec * 1000000) / 1000000; - break; + if (VARSIZE(units) - VARHDRSZ > MAXDATELEN) + elog(ERROR, "Interval units '%s' not recognized", + DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(units)))); + up = VARDATA(units); + lp = lowunits; + for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) + *lp++ = tolower((unsigned char) *up++); + *lp = '\0'; - default: - elog(ERROR, "Timestamp units '%s' not supported", lowunits); - result = 0; - } + type = DecodeUnits(0, lowunits, &val); - tz = DetermineLocalTimeZone(tm); + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMPTZ(timestamp); - 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 + if ((type == UNITS) && (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0)) + { + switch (val) { - elog(ERROR, "Timestamp units '%s' not recognized", lowunits); - result = 0; + case DTK_MILLENNIUM: + 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 = 0; } + + tz = DetermineLocalTimeZone(tm); + + if (tm2timestamp(tm, fsec, &tz, &result) != 0) + elog(ERROR, "Unable to truncate timestamp to '%s'", lowunits); + } + else + { + elog(ERROR, "Timestamp units '%s' not recognized", lowunits); + PG_RETURN_NULL(); } - PG_RETURN_TIMESTAMP(result); + PG_RETURN_TIMESTAMPTZ(result); } /* interval_trunc() @@ -1658,14 +1969,7 @@ interval_trunc(PG_FUNCTION_ARGS) type = DecodeUnits(0, lowunits, &val); - if (INTERVAL_IS_INVALID(*interval)) - { -#if NOT_USED - elog(ERROR, "Interval is not finite"); -#endif - PG_RETURN_NULL(); - } - else if (type == UNITS) + if (type == UNITS) { if (interval2tm(*interval, tm, &fsec) == 0) { @@ -1703,7 +2007,6 @@ interval_trunc(PG_FUNCTION_ARGS) default: elog(ERROR, "Interval units '%s' not supported", lowunits); - PG_RETURN_NULL(); } if (tm2interval(tm, fsec, result) != 0) @@ -1712,28 +2015,16 @@ interval_trunc(PG_FUNCTION_ARGS) } else { - elog(NOTICE, "Interval out of range"); - PG_RETURN_NULL(); + elog(NOTICE, "Unable to decode interval; internal coding error"); + *result = *interval; } - } -#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", DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(units)))); - PG_RETURN_NULL(); + PointerGetDatum(units)))); + *result = *interval; } PG_RETURN_INTERVAL_P(result); @@ -1828,7 +2119,6 @@ timestamp_part(PG_FUNCTION_ARGS) text *units = PG_GETARG_TEXT_P(0); Timestamp timestamp = PG_GETARG_TIMESTAMP(1); float8 result; - Timestamp dt; int tz; int type, val; @@ -1845,7 +2135,7 @@ timestamp_part(PG_FUNCTION_ARGS) if (VARSIZE(units) - VARHDRSZ > MAXDATELEN) elog(ERROR, "Interval units '%s' not recognized", DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(units)))); + PointerGetDatum(units)))); up = VARDATA(units); lp = lowunits; for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) @@ -1857,123 +2147,276 @@ timestamp_part(PG_FUNCTION_ARGS) type = DecodeSpecial(0, lowunits, &val); if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_NULL(); - else { - dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp); + result = 0; + PG_RETURN_FLOAT8(result); + } - if ((type == UNITS) && (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0)) + if ((type == UNITS) && (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0)) + { + switch (val) { - 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_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 - 1) / 3) + 1; + break; + + case DTK_WEEK: + result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); + 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_MILLENNIUM: + result = (tm->tm_year / 1000); + break; + + default: + elog(ERROR, "Timestamp units '%s' not supported", lowunits); + result = 0; + } - case DTK_SECOND: - result = (tm->tm_sec + fsec); - break; + } + else if (type == RESERV) + { + switch (val) + { + case DTK_EPOCH: + result = timestamp - SetEpochTimestamp(); + break; - case DTK_MINUTE: - result = tm->tm_min; - break; + case DTK_DOW: + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp"); - case DTK_HOUR: - result = tm->tm_hour; - break; + result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); + break; - case DTK_DAY: - result = tm->tm_mday; - break; + case DTK_DOY: + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp"); - case DTK_MONTH: - result = tm->tm_mon; - break; + result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + - date2j(tm->tm_year, 1, 1) + 1); + break; - case DTK_QUARTER: - result = ((tm->tm_mon - 1) / 3) + 1; - break; + default: + elog(ERROR, "Timestamp units '%s' not supported", lowunits); + result = 0; + } - case DTK_WEEK: - result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); - break; + } + else + { + elog(ERROR, "Timestamp units '%s' not recognized", lowunits); + result = 0; + } - case DTK_YEAR: - result = tm->tm_year; - break; + PG_RETURN_FLOAT8(result); +} - case DTK_DECADE: - result = (tm->tm_year / 10); - break; +/* timestamptz_part() + * Extract specified field from timestamp with time zone. + */ +Datum +timestamptz_part(PG_FUNCTION_ARGS) +{ + text *units = PG_GETARG_TEXT_P(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); + float8 result; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowunits[MAXDATELEN + 1]; + double dummy; + double fsec; + char *tzn; + struct tm tt, + *tm = &tt; - case DTK_CENTURY: - result = (tm->tm_year / 100); - break; + if (VARSIZE(units) - VARHDRSZ > MAXDATELEN) + elog(ERROR, "Interval units '%s' not recognized", + DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(units)))); + up = VARDATA(units); + lp = lowunits; + for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) + *lp++ = tolower((unsigned char) *up++); + *lp = '\0'; - case DTK_MILLENNIUM: - result = (tm->tm_year / 1000); - break; + type = DecodeUnits(0, lowunits, &val); + if (type == IGNORE) + type = DecodeSpecial(0, lowunits, &val); - default: - elog(ERROR, "Timestamp units '%s' not supported", lowunits); - result = 0; - } + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = 0; + PG_RETURN_FLOAT8(result); + } - } - else if (type == RESERV) + if ((type == UNITS) && (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0)) + { + switch (val) { - switch (val) - { - case DTK_EPOCH: - TIMESTAMP_EPOCH(result); - result = dt - SetTimestamp(result); - break; + 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 - 1) / 3) + 1; + break; + + case DTK_WEEK: + result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); + 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_MILLENNIUM: + result = (tm->tm_year / 1000); + break; + + default: + elog(ERROR, "Timestamp with time zone units '%s' not supported", lowunits); + result = 0; + } - case DTK_DOW: - if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to encode timestamp"); + } + else if (type == RESERV) + { + switch (val) + { + case DTK_EPOCH: + result = timestamp - SetEpochTimestamp(); + break; - result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); - break; + case DTK_DOW: + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp with time zone"); - case DTK_DOY: - 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; - result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - - date2j(tm->tm_year, 1, 1) + 1); - break; + case DTK_DOY: + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp with time zone"); - default: - elog(ERROR, "Timestamp units '%s' not supported", lowunits); - result = 0; - } + result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + - date2j(tm->tm_year, 1, 1) + 1); + break; - } - else - { - elog(ERROR, "Timestamp units '%s' not recognized", lowunits); - result = 0; + default: + elog(ERROR, "Timestamp with time zone units '%s' not supported", lowunits); + result = 0; } } + else + { + elog(ERROR, "Timestamp with time zone units '%s' not recognized", lowunits); + result = 0; + } PG_RETURN_FLOAT8(result); } @@ -2012,14 +2455,7 @@ interval_part(PG_FUNCTION_ARGS) 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 (type == UNITS) { if (interval2tm(*interval, tm, &fsec) == 0) { @@ -2074,7 +2510,7 @@ interval_part(PG_FUNCTION_ARGS) break; default: - elog(ERROR, "Interval units '%s' not yet supported", + elog(ERROR, "Interval units '%s' not supported", DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(units)))); result = 0; @@ -2083,7 +2519,8 @@ interval_part(PG_FUNCTION_ARGS) } else { - elog(NOTICE, "Interval out of range"); + elog(NOTICE, "Unable to decode interval" + "\n\tinterval_part() internal coding error"); result = 0; } } @@ -2116,8 +2553,142 @@ timestamp_zone(PG_FUNCTION_ARGS) { text *zone = PG_GETARG_TEXT_P(0); Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + TimestampTz result; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowzone[MAXDATELEN + 1]; + + if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN) + elog(ERROR, "Time zone '%s' not recognized", + DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(zone)))); + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMPTZ(timestamp); + + up = VARDATA(zone); + lp = lowzone; + for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++) + *lp++ = tolower((unsigned char) *up++); + *lp = '\0'; + + type = DecodeSpecial(0, lowzone, &val); + + if ((type == TZ) || (type == DTZ)) + { + tz = val * 60; + result = timestamp - tz; + } + else + { + elog(ERROR, "Time zone '%s' not recognized", lowzone); + PG_RETURN_NULL(); + } + + PG_RETURN_TIMESTAMPTZ(result); +} /* timestamp_zone() */ + +/* timestamp_izone() + * Encode timestamp type with specified time interval as time zone. + * Require ISO-formatted result, since character-string time zone is not available. + */ +Datum +timestamp_izone(PG_FUNCTION_ARGS) +{ + Interval *zone = PG_GETARG_INTERVAL_P(0); + Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + TimestampTz result; + int tz; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMPTZ(timestamp); + + if (zone->month != 0) + elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)", + DatumGetCString(DirectFunctionCall1(interval_out, + PointerGetDatum(zone)))); + + tz = -(zone->time); + result = timestamp - tz; + + PG_RETURN_TIMESTAMPTZ(result); +} /* timestamp_izone() */ + +/* timestamp_timestamptz() + * Convert local timestamp to timestamp at GMT + */ +Datum +timestamp_timestamptz(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + TimestampTz result; + struct tm tt, + *tm = &tt; + double fsec; + int tz; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = timestamp; + } + else + { + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0) + elog(ERROR, "Unable to convert timestamp to timestamp with time zone (tm)"); + + tz = DetermineLocalTimeZone(tm); + + if (tm2timestamp(tm, fsec, &tz, &result) != 0) + elog(ERROR, "Unable to convert timestamp to timestamp with time zone"); + } + + PG_RETURN_TIMESTAMPTZ(result); +} + +/* timestamptz_timestamp() + * Convert timestamp at GMT to local timestamp + */ +Datum +timestamptz_timestamp(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); + Timestamp result; + struct tm tt, + *tm = &tt; + double fsec; + char *tzn; + int tz; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = timestamp; + } + else + { + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to convert timestamp with time zone to timestamp (tm)"); + + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + elog(ERROR, "Unable to convert timestamp with time zone to timestamp"); + } + + PG_RETURN_TIMESTAMP(result); +} + +/* timestamptz_zone() + * Encode timestamp with time zone type with specified time zone. + */ +Datum +timestamptz_zone(PG_FUNCTION_ARGS) +{ + text *zone = PG_GETARG_TEXT_P(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); text *result; - Timestamp dt; + TimestampTz dt; int tz; int type, val; @@ -2146,17 +2717,18 @@ timestamp_zone(PG_FUNCTION_ARGS) type = DecodeSpecial(0, lowzone, &val); if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_NULL(); - else if ((type == TZ) || (type == DTZ)) + PG_RETURN_TEXT_P(pstrdup("")); + + 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); + dt = dt2local(timestamp, tz); if (timestamp2tm(dt, NULL, tm, &fsec, NULL) != 0) - elog(ERROR, "Timestamp not legal"); + elog(ERROR, "Unable to decode timestamp" + "\n\ttimestamp_zone() internal coding error"); up = upzone; lp = lowzone; @@ -2177,23 +2749,23 @@ timestamp_zone(PG_FUNCTION_ARGS) else { elog(ERROR, "Time zone '%s' not recognized", lowzone); - result = NULL; + PG_RETURN_TEXT_P(pstrdup("")); } PG_RETURN_TEXT_P(result); -} /* timestamp_zone() */ +} /* timestamptz_zone() */ -/* timestamp_izone() - * Encode timestamp type with specified time interval as time zone. +/* timestamptz_izone() + * Encode timestamp with time zone type with specified time interval as time zone. * Require ISO-formatted result, since character-string time zone is not available. */ Datum -timestamp_izone(PG_FUNCTION_ARGS) +timestamptz_izone(PG_FUNCTION_ARGS) { Interval *zone = PG_GETARG_INTERVAL_P(0); - Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); text *result; - Timestamp dt; + TimestampTz dt; int tz; char *tzn = ""; double fsec; @@ -2203,19 +2775,21 @@ timestamp_izone(PG_FUNCTION_ARGS) int len; if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_NULL(); + PG_RETURN_TEXT_P(pstrdup("")); if (zone->month != 0) - elog(ERROR, "INTERVAL time zone not legal (month specified)"); + elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)", + DatumGetCString(DirectFunctionCall1(interval_out, + PointerGetDatum(zone)))); tm->tm_isdst = -1; tz = -(zone->time); - dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp); - dt = dt2local(dt, tz); + dt = dt2local(timestamp, tz); if (timestamp2tm(dt, NULL, tm, &fsec, NULL) != 0) - elog(ERROR, "Timestamp not legal"); + elog(ERROR, "Unable to decode timestamp" + "\n\ttimestamp_izone() internal coding error"); EncodeDateTime(tm, fsec, &tz, &tzn, USE_ISO_DATES, buf); len = (strlen(buf) + VARHDRSZ); @@ -2225,4 +2799,4 @@ timestamp_izone(PG_FUNCTION_ARGS) memmove(VARDATA(result), buf, (len - VARHDRSZ)); PG_RETURN_TEXT_P(result); -} /* timestamp_izone() */ +} /* timestamptz_izone() */ |