From a17f2d76ccc8ba0801f6d92595cf3adbc3f0c54f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 25 Aug 2003 16:13:27 +0000 Subject: [PATCH] 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. --- src/backend/utils/adt/formatting.c | 174 ++++++++++++++++------------- 1 file changed, 98 insertions(+), 76 deletions(-) diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 1e9b49ea8d..cc31115fa6 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.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 @@ -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_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 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 FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, bool *shouldFree); 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 *fmt = PG_GETARG_TEXT_P(1); - 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; TmFromChar tmfc; - bool incache; char *str; char *date_str; int len, - date_len, - tz = 0; - struct tm tm; - fsec_t fsec = 0; + date_len; + + ZERO_tm(tm); + *fsec = 0; - ZERO_tm(&tm); ZERO_tmfc(&tmfc); len = VARSIZE(fmt) - VARHDRSZ; @@ -3004,15 +3050,15 @@ to_timestamp(PG_FUNCTION_ARGS) { int x = tmfc.ssss; - tm.tm_hour = x / 3600; + tm->tm_hour = x / 3600; x %= 3600; - tm.tm_min = x / 60; + tm->tm_min = x / 60; x %= 60; - tm.tm_sec = x; + tm->tm_sec = x; } if (tmfc.cc) - tm.tm_year = (tmfc.cc - 1) * 100; + tm->tm_year = (tmfc.cc - 1) * 100; if (tmfc.ww) tmfc.ddd = (tmfc.ww - 1) * 7 + 1; @@ -3021,79 +3067,79 @@ to_timestamp(PG_FUNCTION_ARGS) tmfc.dd = (tmfc.w - 1) * 7 + 1; if (tmfc.ss) - tm.tm_sec = tmfc.ss; + tm->tm_sec = tmfc.ss; if (tmfc.mi) - tm.tm_min = tmfc.mi; + tm->tm_min = tmfc.mi; if (tmfc.hh) - tm.tm_hour = tmfc.hh; + tm->tm_hour = tmfc.hh; 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, (errcode(ERRCODE_INVALID_DATETIME_FORMAT), errmsg("AM/PM hour must be between 1 and 12"))); - if (tmfc.pm && tm.tm_hour < 12) - tm.tm_hour += 12; + if (tmfc.pm && tm->tm_hour < 12) + tm->tm_hour += 12; - else if (tmfc.am && tm.tm_hour == 12) - tm.tm_hour = 0; + else if (tmfc.am && tm->tm_hour == 12) + tm->tm_hour = 0; } switch (tmfc.q) { case 1: - tm.tm_mday = 1; - tm.tm_mon = 1; + tm->tm_mday = 1; + tm->tm_mon = 1; break; case 2: - tm.tm_mday = 1; - tm.tm_mon = 4; + tm->tm_mday = 1; + tm->tm_mon = 4; break; case 3: - tm.tm_mday = 1; - tm.tm_mon = 7; + tm->tm_mday = 1; + tm->tm_mon = 7; break; case 4: - tm.tm_mday = 1; - tm.tm_mon = 10; + tm->tm_mday = 1; + tm->tm_mon = 10; break; } if (tmfc.year) - tm.tm_year = tmfc.year; + tm->tm_year = tmfc.year; if (tmfc.bc) { - if (tm.tm_year > 0) - tm.tm_year = -(tm.tm_year - 1); + if (tm->tm_year > 0) + tm->tm_year = -(tm->tm_year - 1); else ereport(ERROR, (errcode(ERRCODE_INVALID_DATETIME_FORMAT), errmsg("inconsistent use of year %04d and \"BC\"", - tm.tm_year))); + tm->tm_year))); } 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) - 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) - tm.tm_wday = tmfc.d; + tm->tm_wday = tmfc.d; if (tmfc.dd) - tm.tm_mday = tmfc.dd; + tm->tm_mday = tmfc.dd; if (tmfc.ddd) - tm.tm_yday = tmfc.ddd; + tm->tm_yday = tmfc.ddd; if (tmfc.mm) - tm.tm_mon = tmfc.mm; + tm->tm_mon = tmfc.mm; /* * 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 */ 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, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 0}}; - if (!tm.tm_year) + if (!tm->tm_year) ereport(ERROR, (errcode(ERRCODE_INVALID_DATETIME_FORMAT), 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++) { - if (tm.tm_yday < y[i]) + if (tm->tm_yday < y[i]) break; } - if (tm.tm_mon <= 1) - tm.tm_mon = i + 1; + if (tm->tm_mon <= 1) + tm->tm_mon = i + 1; - if (tm.tm_mday <= 1) - tm.tm_mday = i == 0 ? tm.tm_yday : - tm.tm_yday - y[i - 1]; + if (tm->tm_mday <= 1) + tm->tm_mday = i == 0 ? tm->tm_yday : + tm->tm_yday - y[i - 1]; } #ifdef HAVE_INT64_TIMESTAMP if (tmfc.ms) - fsec += tmfc.ms * 1000; + *fsec += tmfc.ms * 1000; if (tmfc.us) - fsec += tmfc.us; + *fsec += tmfc.us; #else if (tmfc.ms) - fsec += (double) tmfc.ms / 1000; + *fsec += (double) tmfc.ms / 1000; if (tmfc.us) - fsec += (double) tmfc.us / 1000000; + *fsec += (double) tmfc.us / 1000000; #endif - /* -------------------------------------------------------------- */ - - 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); + DEBUG_TM(tm); } -/* ---------- - * 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