diff options
author | Thomas G. Lockhart <lockhart@fourpalms.org> | 2000-11-06 15:57:00 +0000 |
---|---|---|
committer | Thomas G. Lockhart <lockhart@fourpalms.org> | 2000-11-06 15:57:00 +0000 |
commit | 2cf1642461536d0d8f3a1cf124ead0eac04eb760 (patch) | |
tree | 2722d9209e289ea8688fc3c956e8fb9ccff89a67 /src/backend/utils/adt/timestamp.c | |
parent | df9462ac052a63a87d0999fa26f78c6893e5b687 (diff) | |
download | postgresql-2cf1642461536d0d8f3a1cf124ead0eac04eb760.tar.gz postgresql-2cf1642461536d0d8f3a1cf124ead0eac04eb760.zip |
Fix INTERVAL output when year/month has different sign as day/hour etc.
Previously, all fields were unsigned, with only a trailing "ago" to
indicate negative intervals. Now, ISO format does not use "ago", and
and the traditional PostgreSQL format has the first numeric field unsigned
with "ago" supporting that field. So "1 month - 2 days ago" is two days
less than a month in the past.
Fix interval arithmetic across daylight savings time boundaries.
Previously, most math across boundaries introduced a one hour offset.
Allow some date/time functions to return NULL if called with NULL args.
Implement functions for AT TIME ZONE support.
Support "SAT" as an Australian time zone if USE_AUSTRALIAN_RULES
is defined.
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 102 |
1 files changed, 81 insertions, 21 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 21fd9d76188..ab147d295b5 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.36 2000/10/29 13:17:34 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.37 2000/11/06 15:57:00 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -1000,6 +1000,39 @@ timestamp_pl_span(PG_FUNCTION_ARGS) 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 (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) + { +#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) + tm->tm_isdst = -1; + tm->tm_year -= 1900; + tm->tm_mon -= 1; + tm->tm_isdst = -1; + mktime(tm); + tm->tm_year += 1900; + tm->tm_mon += 1; + +# if defined(HAVE_TM_ZONE) + tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ +# elif defined(HAVE_INT_TIMEZONE) + +# ifdef __CYGWIN__ + tz = (tm->tm_isdst ? (_timezone - 3600) : _timezone); +# else + tz = (tm->tm_isdst ? (timezone - 3600) : timezone); +# endif + +# endif + +#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */ + tz = CTimeZone; +#endif + } + else + { + tm->tm_isdst = 0; + tz = 0; + } + if (tm2timestamp(tm, fsec, &tz, &dt) != 0) elog(ERROR, "Unable to add timestamp and interval"); @@ -1571,12 +1604,7 @@ timestamp_trunc(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) { -#if NOT_USED -/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */ - elog(ERROR, "Timestamp is not finite"); -#endif - result = 0; - + PG_RETURN_NULL(); } else { @@ -1902,10 +1930,6 @@ timestamp_part(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) { -#if NOT_USED -/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */ - elog(ERROR, "Timestamp is not finite", NULL); -#endif PG_RETURN_NULL(); } else @@ -2197,15 +2221,7 @@ timestamp_zone(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) { - - /* - * could return null but Postgres doesn't like that currently. - - * tgl 97/06/12 - * - * Could do it now if you wanted ... the other tgl 2000/06/08 - */ - elog(ERROR, "Timestamp is not finite"); - result = NULL; + PG_RETURN_NULL(); } else if ((type == TZ) || (type == DTZ)) { @@ -2241,4 +2257,48 @@ timestamp_zone(PG_FUNCTION_ARGS) } PG_RETURN_TEXT_P(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); + text *result; + Timestamp dt; + int tz; + char *tzn = ""; + double fsec; + struct tm tt, + *tm = &tt; + char buf[MAXDATELEN + 1]; + int len; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_NULL(); + + if (zone->month != 0) + elog(ERROR, "INTERVAL time zone not legal (month specified)"); + + tm->tm_isdst = -1; + tz = -(zone->time); + + dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp); + dt = dt2local(dt, tz); + + if (timestamp2tm(dt, NULL, tm, &fsec, NULL) != 0) + elog(ERROR, "Timestamp not legal"); + + EncodeDateTime(tm, fsec, &tz, &tzn, USE_ISO_DATES, buf); + len = (strlen(buf) + VARHDRSZ); + + result = palloc(len); + VARATT_SIZEP(result) = len; + memmove(VARDATA(result), buf, (len - VARHDRSZ)); + + PG_RETURN_TEXT_P(result); +} /* timestamp_izone() */ |