Skip to content

Commit a17f2d7

Browse files
committed
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.
1 parent 4b02f3c commit a17f2d7

File tree

1 file changed

+98
-76
lines changed

1 file changed

+98
-76
lines changed

src/backend/utils/adt/formatting.c

Lines changed: 98 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* -----------------------------------------------------------------------
22
* formatting.c
33
*
4-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.66 2003/08/04 23:59:38 tgl Exp $
4+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.67 2003/08/25 16:13:27 tgl Exp $
55
*
66
*
77
* 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);
880880
static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node, void *data);
881881
static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node, void *data);
882882
static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node, void *data);
883+
static void do_to_timestamp(text *date_txt, text *fmt,
884+
struct tm *tm, fsec_t *fsec);
883885
static char *fill_str(char *str, int c, int max);
884886
static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, bool *shouldFree);
885887
static char *int_to_roman(int number);
@@ -2901,21 +2903,65 @@ to_timestamp(PG_FUNCTION_ARGS)
29012903
{
29022904
text *date_txt = PG_GETARG_TEXT_P(0);
29032905
text *fmt = PG_GETARG_TEXT_P(1);
2904-
29052906
Timestamp result;
2907+
int tz;
2908+
struct tm tm;
2909+
fsec_t fsec;
2910+
2911+
do_to_timestamp(date_txt, fmt, &tm, &fsec);
2912+
2913+
tz = DetermineLocalTimeZone(&tm);
2914+
2915+
if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
2916+
ereport(ERROR,
2917+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2918+
errmsg("timestamp out of range")));
2919+
2920+
PG_RETURN_TIMESTAMP(result);
2921+
}
2922+
2923+
/* ----------
2924+
* TO_DATE
2925+
* Make Date from date_str which is formated at argument 'fmt'
2926+
* ----------
2927+
*/
2928+
Datum
2929+
to_date(PG_FUNCTION_ARGS)
2930+
{
2931+
text *date_txt = PG_GETARG_TEXT_P(0);
2932+
text *fmt = PG_GETARG_TEXT_P(1);
2933+
DateADT result;
2934+
struct tm tm;
2935+
fsec_t fsec;
2936+
2937+
do_to_timestamp(date_txt, fmt, &tm, &fsec);
2938+
2939+
result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
2940+
2941+
PG_RETURN_DATEADT(result);
2942+
}
2943+
2944+
/*
2945+
* do_to_timestamp: shared code for to_timestamp and to_date
2946+
*
2947+
* Parse the 'date_txt' according to 'fmt', return results as a struct tm
2948+
* and fractional seconds.
2949+
*/
2950+
static void
2951+
do_to_timestamp(text *date_txt, text *fmt,
2952+
struct tm *tm, fsec_t *fsec)
2953+
{
29062954
FormatNode *format;
29072955
TmFromChar tmfc;
2908-
29092956
bool incache;
29102957
char *str;
29112958
char *date_str;
29122959
int len,
2913-
date_len,
2914-
tz = 0;
2915-
struct tm tm;
2916-
fsec_t fsec = 0;
2960+
date_len;
2961+
2962+
ZERO_tm(tm);
2963+
*fsec = 0;
29172964

2918-
ZERO_tm(&tm);
29192965
ZERO_tmfc(&tmfc);
29202966

29212967
len = VARSIZE(fmt) - VARHDRSZ;
@@ -3004,15 +3050,15 @@ to_timestamp(PG_FUNCTION_ARGS)
30043050
{
30053051
int x = tmfc.ssss;
30063052

3007-
tm.tm_hour = x / 3600;
3053+
tm->tm_hour = x / 3600;
30083054
x %= 3600;
3009-
tm.tm_min = x / 60;
3055+
tm->tm_min = x / 60;
30103056
x %= 60;
3011-
tm.tm_sec = x;
3057+
tm->tm_sec = x;
30123058
}
30133059

30143060
if (tmfc.cc)
3015-
tm.tm_year = (tmfc.cc - 1) * 100;
3061+
tm->tm_year = (tmfc.cc - 1) * 100;
30163062

30173063
if (tmfc.ww)
30183064
tmfc.ddd = (tmfc.ww - 1) * 7 + 1;
@@ -3021,79 +3067,79 @@ to_timestamp(PG_FUNCTION_ARGS)
30213067
tmfc.dd = (tmfc.w - 1) * 7 + 1;
30223068

30233069
if (tmfc.ss)
3024-
tm.tm_sec = tmfc.ss;
3070+
tm->tm_sec = tmfc.ss;
30253071
if (tmfc.mi)
3026-
tm.tm_min = tmfc.mi;
3072+
tm->tm_min = tmfc.mi;
30273073
if (tmfc.hh)
3028-
tm.tm_hour = tmfc.hh;
3074+
tm->tm_hour = tmfc.hh;
30293075

30303076
if (tmfc.pm || tmfc.am)
30313077
{
3032-
if (tm.tm_hour < 1 || tm.tm_hour > 12)
3078+
if (tm->tm_hour < 1 || tm->tm_hour > 12)
30333079
ereport(ERROR,
30343080
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
30353081
errmsg("AM/PM hour must be between 1 and 12")));
30363082

3037-
if (tmfc.pm && tm.tm_hour < 12)
3038-
tm.tm_hour += 12;
3083+
if (tmfc.pm && tm->tm_hour < 12)
3084+
tm->tm_hour += 12;
30393085

3040-
else if (tmfc.am && tm.tm_hour == 12)
3041-
tm.tm_hour = 0;
3086+
else if (tmfc.am && tm->tm_hour == 12)
3087+
tm->tm_hour = 0;
30423088
}
30433089

30443090
switch (tmfc.q)
30453091
{
30463092
case 1:
3047-
tm.tm_mday = 1;
3048-
tm.tm_mon = 1;
3093+
tm->tm_mday = 1;
3094+
tm->tm_mon = 1;
30493095
break;
30503096
case 2:
3051-
tm.tm_mday = 1;
3052-
tm.tm_mon = 4;
3097+
tm->tm_mday = 1;
3098+
tm->tm_mon = 4;
30533099
break;
30543100
case 3:
3055-
tm.tm_mday = 1;
3056-
tm.tm_mon = 7;
3101+
tm->tm_mday = 1;
3102+
tm->tm_mon = 7;
30573103
break;
30583104
case 4:
3059-
tm.tm_mday = 1;
3060-
tm.tm_mon = 10;
3105+
tm->tm_mday = 1;
3106+
tm->tm_mon = 10;
30613107
break;
30623108
}
30633109

30643110
if (tmfc.year)
3065-
tm.tm_year = tmfc.year;
3111+
tm->tm_year = tmfc.year;
30663112

30673113
if (tmfc.bc)
30683114
{
3069-
if (tm.tm_year > 0)
3070-
tm.tm_year = -(tm.tm_year - 1);
3115+
if (tm->tm_year > 0)
3116+
tm->tm_year = -(tm->tm_year - 1);
30713117
else
30723118
ereport(ERROR,
30733119
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
30743120
errmsg("inconsistent use of year %04d and \"BC\"",
3075-
tm.tm_year)));
3121+
tm->tm_year)));
30763122
}
30773123

30783124
if (tmfc.j)
3079-
j2date(tmfc.j, &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
3125+
j2date(tmfc.j, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
30803126

30813127
if (tmfc.iw)
3082-
isoweek2date(tmfc.iw, &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
3128+
isoweek2date(tmfc.iw, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
30833129

30843130
if (tmfc.d)
3085-
tm.tm_wday = tmfc.d;
3131+
tm->tm_wday = tmfc.d;
30863132
if (tmfc.dd)
3087-
tm.tm_mday = tmfc.dd;
3133+
tm->tm_mday = tmfc.dd;
30883134
if (tmfc.ddd)
3089-
tm.tm_yday = tmfc.ddd;
3135+
tm->tm_yday = tmfc.ddd;
30903136
if (tmfc.mm)
3091-
tm.tm_mon = tmfc.mm;
3137+
tm->tm_mon = tmfc.mm;
30923138

30933139
/*
30943140
* we don't ignore DDD
30953141
*/
3096-
if (tmfc.ddd && (tm.tm_mon <= 1 || tm.tm_mday <= 1))
3142+
if (tmfc.ddd && (tm->tm_mon <= 1 || tm->tm_mday <= 1))
30973143
{
30983144
/* count mday and mon from yday */
30993145
int *y,
@@ -3103,65 +3149,41 @@ to_timestamp(PG_FUNCTION_ARGS)
31033149
{31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0},
31043150
{31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 0}};
31053151

3106-
if (!tm.tm_year)
3152+
if (!tm->tm_year)
31073153
ereport(ERROR,
31083154
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
31093155
errmsg("cannot convert yday without year information")));
31103156

3111-
y = ysum[isleap(tm.tm_year)];
3157+
y = ysum[isleap(tm->tm_year)];
31123158

31133159
for (i = 0; i <= 11; i++)
31143160
{
3115-
if (tm.tm_yday < y[i])
3161+
if (tm->tm_yday < y[i])
31163162
break;
31173163
}
3118-
if (tm.tm_mon <= 1)
3119-
tm.tm_mon = i + 1;
3164+
if (tm->tm_mon <= 1)
3165+
tm->tm_mon = i + 1;
31203166

3121-
if (tm.tm_mday <= 1)
3122-
tm.tm_mday = i == 0 ? tm.tm_yday :
3123-
tm.tm_yday - y[i - 1];
3167+
if (tm->tm_mday <= 1)
3168+
tm->tm_mday = i == 0 ? tm->tm_yday :
3169+
tm->tm_yday - y[i - 1];
31243170
}
31253171

31263172
#ifdef HAVE_INT64_TIMESTAMP
31273173
if (tmfc.ms)
3128-
fsec += tmfc.ms * 1000;
3174+
*fsec += tmfc.ms * 1000;
31293175
if (tmfc.us)
3130-
fsec += tmfc.us;
3176+
*fsec += tmfc.us;
31313177
#else
31323178
if (tmfc.ms)
3133-
fsec += (double) tmfc.ms / 1000;
3179+
*fsec += (double) tmfc.ms / 1000;
31343180
if (tmfc.us)
3135-
fsec += (double) tmfc.us / 1000000;
3181+
*fsec += (double) tmfc.us / 1000000;
31363182
#endif
31373183

3138-
/* -------------------------------------------------------------- */
3139-
3140-
DEBUG_TM(&tm);
3141-
tz = DetermineLocalTimeZone(&tm);
3142-
3143-
if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
3144-
ereport(ERROR,
3145-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3146-
errmsg("timestamp out of range")));
3147-
3148-
PG_RETURN_TIMESTAMP(result);
3184+
DEBUG_TM(tm);
31493185
}
31503186

3151-
/* ----------
3152-
* TO_DATE
3153-
* Make Date from date_str which is formated at argument 'fmt'
3154-
* ----------
3155-
*/
3156-
Datum
3157-
to_date(PG_FUNCTION_ARGS)
3158-
{
3159-
/*
3160-
* Quick hack: since our inputs are just like to_timestamp, hand over
3161-
* the whole input info struct...
3162-
*/
3163-
return DirectFunctionCall1(timestamptz_date, to_timestamp(fcinfo));
3164-
}
31653187

31663188
/**********************************************************************
31673189
* the NUMBER version part

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy