1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-16 06:01:02 +03:00

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.
This commit is contained in:
Thomas G. Lockhart
2000-11-06 15:57:00 +00:00
parent df9462ac05
commit 2cf1642461
2 changed files with 212 additions and 110 deletions

View File

@ -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() */