Refactor code so that to_date() does not call to_timestamp() and then

perform a timestamp-to-date coercion.  Instead both routines share a
subroutine that delivers the parsing result as a struct tm.  This avoids
problems with timezone dependency of to_date's result, and should be
at least marginally faster too.
This commit is contained in:
Tom Lane 2003-08-25 16:13:27 +00:00
parent 4b02f3c4eb
commit a17f2d76cc
1 changed files with 98 additions and 76 deletions

View File

@ -1,7 +1,7 @@
/* ----------------------------------------------------------------------- /* -----------------------------------------------------------------------
* formatting.c * formatting.c
* *
* $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.66 2003/08/04 23:59:38 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.67 2003/08/25 16:13:27 tgl Exp $
* *
* *
* Portions Copyright (c) 1999-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1999-2003, PostgreSQL Global Development Group
@ -880,6 +880,8 @@ static int seq_search(char *name, char **array, int type, int max, int *len);
static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node, void *data); static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node, void *data);
static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node, void *data); static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node, void *data);
static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node, void *data); static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node, void *data);
static void do_to_timestamp(text *date_txt, text *fmt,
struct tm *tm, fsec_t *fsec);
static char *fill_str(char *str, int c, int max); static char *fill_str(char *str, int c, int max);
static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, bool *shouldFree); static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, bool *shouldFree);
static char *int_to_roman(int number); static char *int_to_roman(int number);
@ -2901,21 +2903,65 @@ to_timestamp(PG_FUNCTION_ARGS)
{ {
text *date_txt = PG_GETARG_TEXT_P(0); text *date_txt = PG_GETARG_TEXT_P(0);
text *fmt = PG_GETARG_TEXT_P(1); text *fmt = PG_GETARG_TEXT_P(1);
Timestamp result; Timestamp result;
int tz;
struct tm tm;
fsec_t fsec;
do_to_timestamp(date_txt, fmt, &tm, &fsec);
tz = DetermineLocalTimeZone(&tm);
if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
PG_RETURN_TIMESTAMP(result);
}
/* ----------
* TO_DATE
* Make Date from date_str which is formated at argument 'fmt'
* ----------
*/
Datum
to_date(PG_FUNCTION_ARGS)
{
text *date_txt = PG_GETARG_TEXT_P(0);
text *fmt = PG_GETARG_TEXT_P(1);
DateADT result;
struct tm tm;
fsec_t fsec;
do_to_timestamp(date_txt, fmt, &tm, &fsec);
result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
PG_RETURN_DATEADT(result);
}
/*
* do_to_timestamp: shared code for to_timestamp and to_date
*
* Parse the 'date_txt' according to 'fmt', return results as a struct tm
* and fractional seconds.
*/
static void
do_to_timestamp(text *date_txt, text *fmt,
struct tm *tm, fsec_t *fsec)
{
FormatNode *format; FormatNode *format;
TmFromChar tmfc; TmFromChar tmfc;
bool incache; bool incache;
char *str; char *str;
char *date_str; char *date_str;
int len, int len,
date_len, date_len;
tz = 0;
struct tm tm; ZERO_tm(tm);
fsec_t fsec = 0; *fsec = 0;
ZERO_tm(&tm);
ZERO_tmfc(&tmfc); ZERO_tmfc(&tmfc);
len = VARSIZE(fmt) - VARHDRSZ; len = VARSIZE(fmt) - VARHDRSZ;
@ -3004,15 +3050,15 @@ to_timestamp(PG_FUNCTION_ARGS)
{ {
int x = tmfc.ssss; int x = tmfc.ssss;
tm.tm_hour = x / 3600; tm->tm_hour = x / 3600;
x %= 3600; x %= 3600;
tm.tm_min = x / 60; tm->tm_min = x / 60;
x %= 60; x %= 60;
tm.tm_sec = x; tm->tm_sec = x;
} }
if (tmfc.cc) if (tmfc.cc)
tm.tm_year = (tmfc.cc - 1) * 100; tm->tm_year = (tmfc.cc - 1) * 100;
if (tmfc.ww) if (tmfc.ww)
tmfc.ddd = (tmfc.ww - 1) * 7 + 1; tmfc.ddd = (tmfc.ww - 1) * 7 + 1;
@ -3021,79 +3067,79 @@ to_timestamp(PG_FUNCTION_ARGS)
tmfc.dd = (tmfc.w - 1) * 7 + 1; tmfc.dd = (tmfc.w - 1) * 7 + 1;
if (tmfc.ss) if (tmfc.ss)
tm.tm_sec = tmfc.ss; tm->tm_sec = tmfc.ss;
if (tmfc.mi) if (tmfc.mi)
tm.tm_min = tmfc.mi; tm->tm_min = tmfc.mi;
if (tmfc.hh) if (tmfc.hh)
tm.tm_hour = tmfc.hh; tm->tm_hour = tmfc.hh;
if (tmfc.pm || tmfc.am) if (tmfc.pm || tmfc.am)
{ {
if (tm.tm_hour < 1 || tm.tm_hour > 12) if (tm->tm_hour < 1 || tm->tm_hour > 12)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT), (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("AM/PM hour must be between 1 and 12"))); errmsg("AM/PM hour must be between 1 and 12")));
if (tmfc.pm && tm.tm_hour < 12) if (tmfc.pm && tm->tm_hour < 12)
tm.tm_hour += 12; tm->tm_hour += 12;
else if (tmfc.am && tm.tm_hour == 12) else if (tmfc.am && tm->tm_hour == 12)
tm.tm_hour = 0; tm->tm_hour = 0;
} }
switch (tmfc.q) switch (tmfc.q)
{ {
case 1: case 1:
tm.tm_mday = 1; tm->tm_mday = 1;
tm.tm_mon = 1; tm->tm_mon = 1;
break; break;
case 2: case 2:
tm.tm_mday = 1; tm->tm_mday = 1;
tm.tm_mon = 4; tm->tm_mon = 4;
break; break;
case 3: case 3:
tm.tm_mday = 1; tm->tm_mday = 1;
tm.tm_mon = 7; tm->tm_mon = 7;
break; break;
case 4: case 4:
tm.tm_mday = 1; tm->tm_mday = 1;
tm.tm_mon = 10; tm->tm_mon = 10;
break; break;
} }
if (tmfc.year) if (tmfc.year)
tm.tm_year = tmfc.year; tm->tm_year = tmfc.year;
if (tmfc.bc) if (tmfc.bc)
{ {
if (tm.tm_year > 0) if (tm->tm_year > 0)
tm.tm_year = -(tm.tm_year - 1); tm->tm_year = -(tm->tm_year - 1);
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT), (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("inconsistent use of year %04d and \"BC\"", errmsg("inconsistent use of year %04d and \"BC\"",
tm.tm_year))); tm->tm_year)));
} }
if (tmfc.j) if (tmfc.j)
j2date(tmfc.j, &tm.tm_year, &tm.tm_mon, &tm.tm_mday); j2date(tmfc.j, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
if (tmfc.iw) if (tmfc.iw)
isoweek2date(tmfc.iw, &tm.tm_year, &tm.tm_mon, &tm.tm_mday); isoweek2date(tmfc.iw, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
if (tmfc.d) if (tmfc.d)
tm.tm_wday = tmfc.d; tm->tm_wday = tmfc.d;
if (tmfc.dd) if (tmfc.dd)
tm.tm_mday = tmfc.dd; tm->tm_mday = tmfc.dd;
if (tmfc.ddd) if (tmfc.ddd)
tm.tm_yday = tmfc.ddd; tm->tm_yday = tmfc.ddd;
if (tmfc.mm) if (tmfc.mm)
tm.tm_mon = tmfc.mm; tm->tm_mon = tmfc.mm;
/* /*
* we don't ignore DDD * we don't ignore DDD
*/ */
if (tmfc.ddd && (tm.tm_mon <= 1 || tm.tm_mday <= 1)) if (tmfc.ddd && (tm->tm_mon <= 1 || tm->tm_mday <= 1))
{ {
/* count mday and mon from yday */ /* count mday and mon from yday */
int *y, int *y,
@ -3103,65 +3149,41 @@ to_timestamp(PG_FUNCTION_ARGS)
{31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0}, {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0},
{31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 0}}; {31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 0}};
if (!tm.tm_year) if (!tm->tm_year)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT), (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("cannot convert yday without year information"))); errmsg("cannot convert yday without year information")));
y = ysum[isleap(tm.tm_year)]; y = ysum[isleap(tm->tm_year)];
for (i = 0; i <= 11; i++) for (i = 0; i <= 11; i++)
{ {
if (tm.tm_yday < y[i]) if (tm->tm_yday < y[i])
break; break;
} }
if (tm.tm_mon <= 1) if (tm->tm_mon <= 1)
tm.tm_mon = i + 1; tm->tm_mon = i + 1;
if (tm.tm_mday <= 1) if (tm->tm_mday <= 1)
tm.tm_mday = i == 0 ? tm.tm_yday : tm->tm_mday = i == 0 ? tm->tm_yday :
tm.tm_yday - y[i - 1]; tm->tm_yday - y[i - 1];
} }
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
if (tmfc.ms) if (tmfc.ms)
fsec += tmfc.ms * 1000; *fsec += tmfc.ms * 1000;
if (tmfc.us) if (tmfc.us)
fsec += tmfc.us; *fsec += tmfc.us;
#else #else
if (tmfc.ms) if (tmfc.ms)
fsec += (double) tmfc.ms / 1000; *fsec += (double) tmfc.ms / 1000;
if (tmfc.us) if (tmfc.us)
fsec += (double) tmfc.us / 1000000; *fsec += (double) tmfc.us / 1000000;
#endif #endif
/* -------------------------------------------------------------- */ DEBUG_TM(tm);
DEBUG_TM(&tm);
tz = DetermineLocalTimeZone(&tm);
if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
PG_RETURN_TIMESTAMP(result);
} }
/* ----------
* TO_DATE
* Make Date from date_str which is formated at argument 'fmt'
* ----------
*/
Datum
to_date(PG_FUNCTION_ARGS)
{
/*
* Quick hack: since our inputs are just like to_timestamp, hand over
* the whole input info struct...
*/
return DirectFunctionCall1(timestamptz_date, to_timestamp(fcinfo));
}
/********************************************************************** /**********************************************************************
* the NUMBER version part * the NUMBER version part