Commit 19595835 authored by Tom Lane's avatar Tom Lane

Refactor to_char/to_date formatting code; primarily, replace DCH_processor

with two new functions DCH_to_char and DCH_from_char that have less confusing
APIs.  Brendan Jurd
parent 6828c392
/* ----------------------------------------------------------------------- /* -----------------------------------------------------------------------
* formatting.c * formatting.c
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.137 2008/01/01 19:45:52 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.138 2008/03/22 22:32:19 tgl Exp $
* *
* *
* Portions Copyright (c) 1999-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1999-2008, PostgreSQL Global Development Group
...@@ -140,21 +140,18 @@ typedef struct FormatNode FormatNode; ...@@ -140,21 +140,18 @@ typedef struct FormatNode FormatNode;
typedef struct typedef struct
{ {
const char *name; /* keyword */ const char *name;
int len; /* keyword length */ int len;
int (*action) (int arg, char *inout, /* action for keyword */ int id;
int suf, bool is_to_char, bool is_interval, bool is_digit;
FormatNode *node, void *data);
int id; /* keyword id */
bool isitdigit; /* is expected output/input digit */
} KeyWord; } KeyWord;
struct FormatNode struct FormatNode
{ {
int type; /* node type */ int type; /* node type */
const KeyWord *key; /* if node type is KEYWORD */ const KeyWord *key; /* if node type is KEYWORD */
int character, /* if node type is CHAR */ char character; /* if node type is CHAR */
suffix; /* keyword suffix */ int suffix; /* keyword suffix */
}; };
#define NODE_TYPE_END 1 #define NODE_TYPE_END 1
...@@ -179,10 +176,8 @@ static char *days_short[] = { ...@@ -179,10 +176,8 @@ static char *days_short[] = {
}; };
/* ---------- /* ----------
* AC / DC * AD / BC
* ---------- * ----------
*/
/*
* There is no 0 AD. Years go from 1 BC to 1 AD, so we make it * There is no 0 AD. Years go from 1 BC to 1 AD, so we make it
* positive and map year == -1 to year zero, and shift all negative * positive and map year == -1 to year zero, and shift all negative
* years up one. For interval years, we just return the year. * years up one. For interval years, we just return the year.
...@@ -218,8 +213,8 @@ static char *days_short[] = { ...@@ -218,8 +213,8 @@ static char *days_short[] = {
/* ---------- /* ----------
* Months in roman-numeral * Months in roman-numeral
* (Must be conversely for seq_search (in FROM_CHAR), because * (Must be in reverse order for seq_search (in FROM_CHAR), because
* 'VIII' must be over 'V') * 'VIII' must have higher precedence than 'V')
* ---------- * ----------
*/ */
static char *rm_months_upper[] = static char *rm_months_upper[] =
...@@ -259,13 +254,6 @@ static char *numth[] = {"st", "nd", "rd", "th", NULL}; ...@@ -259,13 +254,6 @@ static char *numth[] = {"st", "nd", "rd", "th", NULL};
#define TH_UPPER 1 #define TH_UPPER 1
#define TH_LOWER 2 #define TH_LOWER 2
/* ----------
* Flags for DCH version
* ----------
*/
static bool DCH_global_fx = false;
/* ---------- /* ----------
* Number description struct * Number description struct
* ---------- * ----------
...@@ -460,16 +448,9 @@ do { \ ...@@ -460,16 +448,9 @@ do { \
} while(0) } while(0)
/***************************************************************************** /*****************************************************************************
* KeyWords definition & action * KeyWord definitions
*****************************************************************************/ *****************************************************************************/
static int dch_global(int arg, char *inout, int suf, bool is_to_char,
bool is_interval, FormatNode *node, void *data);
static int dch_time(int arg, char *inout, int suf, bool is_to_char,
bool is_interval, FormatNode *node, void *data);
static int dch_date(int arg, char *inout, int suf, bool is_to_char,
bool is_interval, FormatNode *node, void *data);
/* ---------- /* ----------
* Suffixes: * Suffixes:
* ---------- * ----------
...@@ -684,147 +665,150 @@ typedef enum ...@@ -684,147 +665,150 @@ typedef enum
* ---------- * ----------
*/ */
static const KeyWord DCH_keywords[] = { static const KeyWord DCH_keywords[] = {
/* keyword, len, func, type, isitdigit is in Index */ /* name, len, id, is_digit is in Index */
{"A.D.", 4, dch_date, DCH_A_D, FALSE}, /* A */ {"A.D.", 4, DCH_A_D, FALSE}, /* A */
{"A.M.", 4, dch_time, DCH_A_M, FALSE}, {"A.M.", 4, DCH_A_M, FALSE},
{"AD", 2, dch_date, DCH_AD, FALSE}, {"AD", 2, DCH_AD, FALSE},
{"AM", 2, dch_time, DCH_AM, FALSE}, {"AM", 2, DCH_AM, FALSE},
{"B.C.", 4, dch_date, DCH_B_C, FALSE}, /* B */ {"B.C.", 4, DCH_B_C, FALSE}, /* B */
{"BC", 2, dch_date, DCH_BC, FALSE}, {"BC", 2, DCH_BC, FALSE},
{"CC", 2, dch_date, DCH_CC, TRUE}, /* C */ {"CC", 2, DCH_CC, TRUE}, /* C */
{"DAY", 3, dch_date, DCH_DAY, FALSE}, /* D */ {"DAY", 3, DCH_DAY, FALSE}, /* D */
{"DDD", 3, dch_date, DCH_DDD, TRUE}, {"DDD", 3, DCH_DDD, TRUE},
{"DD", 2, dch_date, DCH_DD, TRUE}, {"DD", 2, DCH_DD, TRUE},
{"DY", 2, dch_date, DCH_DY, FALSE}, {"DY", 2, DCH_DY, FALSE},
{"Day", 3, dch_date, DCH_Day, FALSE}, {"Day", 3, DCH_Day, FALSE},
{"Dy", 2, dch_date, DCH_Dy, FALSE}, {"Dy", 2, DCH_Dy, FALSE},
{"D", 1, dch_date, DCH_D, TRUE}, {"D", 1, DCH_D, TRUE},
{"FX", 2, dch_global, DCH_FX, FALSE}, /* F */ {"FX", 2, DCH_FX, FALSE}, /* F */
{"HH24", 4, dch_time, DCH_HH24, TRUE}, /* H */ {"HH24", 4, DCH_HH24, TRUE}, /* H */
{"HH12", 4, dch_time, DCH_HH12, TRUE}, {"HH12", 4, DCH_HH12, TRUE},
{"HH", 2, dch_time, DCH_HH, TRUE}, {"HH", 2, DCH_HH, TRUE},
{"IDDD", 4, dch_date, DCH_IDDD, TRUE}, /* I */ {"IDDD", 4, DCH_IDDD, TRUE}, /* I */
{"ID", 2, dch_date, DCH_ID, TRUE}, {"ID", 2, DCH_ID, TRUE},
{"IW", 2, dch_date, DCH_IW, TRUE}, {"IW", 2, DCH_IW, TRUE},
{"IYYY", 4, dch_date, DCH_IYYY, TRUE}, {"IYYY", 4, DCH_IYYY, TRUE},
{"IYY", 3, dch_date, DCH_IYY, TRUE}, {"IYY", 3, DCH_IYY, TRUE},
{"IY", 2, dch_date, DCH_IY, TRUE}, {"IY", 2, DCH_IY, TRUE},
{"I", 1, dch_date, DCH_I, TRUE}, {"I", 1, DCH_I, TRUE},
{"J", 1, dch_date, DCH_J, TRUE}, /* J */ {"J", 1, DCH_J, TRUE}, /* J */
{"MI", 2, dch_time, DCH_MI, TRUE}, /* M */ {"MI", 2, DCH_MI, TRUE}, /* M */
{"MM", 2, dch_date, DCH_MM, TRUE}, {"MM", 2, DCH_MM, TRUE},
{"MONTH", 5, dch_date, DCH_MONTH, FALSE}, {"MONTH", 5, DCH_MONTH, FALSE},
{"MON", 3, dch_date, DCH_MON, FALSE}, {"MON", 3, DCH_MON, FALSE},
{"MS", 2, dch_time, DCH_MS, TRUE}, {"MS", 2, DCH_MS, TRUE},
{"Month", 5, dch_date, DCH_Month, FALSE}, {"Month", 5, DCH_Month, FALSE},
{"Mon", 3, dch_date, DCH_Mon, FALSE}, {"Mon", 3, DCH_Mon, FALSE},
{"P.M.", 4, dch_time, DCH_P_M, FALSE}, /* P */ {"P.M.", 4, DCH_P_M, FALSE}, /* P */
{"PM", 2, dch_time, DCH_PM, FALSE}, {"PM", 2, DCH_PM, FALSE},
{"Q", 1, dch_date, DCH_Q, TRUE}, /* Q */ {"Q", 1, DCH_Q, TRUE}, /* Q */
{"RM", 2, dch_date, DCH_RM, FALSE}, /* R */ {"RM", 2, DCH_RM, FALSE}, /* R */
{"SSSS", 4, dch_time, DCH_SSSS, TRUE}, /* S */ {"SSSS", 4, DCH_SSSS, TRUE}, /* S */
{"SS", 2, dch_time, DCH_SS, TRUE}, {"SS", 2, DCH_SS, TRUE},
{"TZ", 2, dch_time, DCH_TZ, FALSE}, /* T */ {"TZ", 2, DCH_TZ, FALSE}, /* T */
{"US", 2, dch_time, DCH_US, TRUE}, /* U */ {"US", 2, DCH_US, TRUE}, /* U */
{"WW", 2, dch_date, DCH_WW, TRUE}, /* W */ {"WW", 2, DCH_WW, TRUE}, /* W */
{"W", 1, dch_date, DCH_W, TRUE}, {"W", 1, DCH_W, TRUE},
{"Y,YYY", 5, dch_date, DCH_Y_YYY, TRUE}, /* Y */ {"Y,YYY", 5, DCH_Y_YYY, TRUE}, /* Y */
{"YYYY", 4, dch_date, DCH_YYYY, TRUE}, {"YYYY", 4, DCH_YYYY, TRUE},
{"YYY", 3, dch_date, DCH_YYY, TRUE}, {"YYY", 3, DCH_YYY, TRUE},
{"YY", 2, dch_date, DCH_YY, TRUE}, {"YY", 2, DCH_YY, TRUE},
{"Y", 1, dch_date, DCH_Y, TRUE}, {"Y", 1, DCH_Y, TRUE},
{"a.d.", 4, dch_date, DCH_a_d, FALSE}, /* a */ {"a.d.", 4, DCH_a_d, FALSE}, /* a */
{"a.m.", 4, dch_time, DCH_a_m, FALSE}, {"a.m.", 4, DCH_a_m, FALSE},
{"ad", 2, dch_date, DCH_ad, FALSE}, {"ad", 2, DCH_ad, FALSE},
{"am", 2, dch_time, DCH_am, FALSE}, {"am", 2, DCH_am, FALSE},
{"b.c.", 4, dch_date, DCH_b_c, FALSE}, /* b */ {"b.c.", 4, DCH_b_c, FALSE}, /* b */
{"bc", 2, dch_date, DCH_bc, FALSE}, {"bc", 2, DCH_bc, FALSE},
{"cc", 2, dch_date, DCH_CC, TRUE}, /* c */ {"cc", 2, DCH_CC, TRUE}, /* c */
{"day", 3, dch_date, DCH_day, FALSE}, /* d */ {"day", 3, DCH_day, FALSE}, /* d */
{"ddd", 3, dch_date, DCH_DDD, TRUE}, {"ddd", 3, DCH_DDD, TRUE},
{"dd", 2, dch_date, DCH_DD, TRUE}, {"dd", 2, DCH_DD, TRUE},
{"dy", 2, dch_date, DCH_dy, FALSE}, {"dy", 2, DCH_dy, FALSE},
{"d", 1, dch_date, DCH_D, TRUE}, {"d", 1, DCH_D, TRUE},
{"fx", 2, dch_global, DCH_FX, FALSE}, /* f */ {"fx", 2, DCH_FX, FALSE}, /* f */
{"hh24", 4, dch_time, DCH_HH24, TRUE}, /* h */ {"hh24", 4, DCH_HH24, TRUE}, /* h */
{"hh12", 4, dch_time, DCH_HH12, TRUE}, {"hh12", 4, DCH_HH12, TRUE},
{"hh", 2, dch_time, DCH_HH, TRUE}, {"hh", 2, DCH_HH, TRUE},
{"iddd", 4, dch_date, DCH_IDDD, TRUE}, /* i */ {"iddd", 4, DCH_IDDD, TRUE}, /* i */
{"id", 2, dch_date, DCH_ID, TRUE}, {"id", 2, DCH_ID, TRUE},
{"iw", 2, dch_date, DCH_IW, TRUE}, {"iw", 2, DCH_IW, TRUE},
{"iyyy", 4, dch_date, DCH_IYYY, TRUE}, {"iyyy", 4, DCH_IYYY, TRUE},
{"iyy", 3, dch_date, DCH_IYY, TRUE}, {"iyy", 3, DCH_IYY, TRUE},
{"iy", 2, dch_date, DCH_IY, TRUE}, {"iy", 2, DCH_IY, TRUE},
{"i", 1, dch_date, DCH_I, TRUE}, {"i", 1, DCH_I, TRUE},
{"j", 1, dch_time, DCH_J, TRUE}, /* j */ {"j", 1, DCH_J, TRUE}, /* j */
{"mi", 2, dch_time, DCH_MI, TRUE}, /* m */ {"mi", 2, DCH_MI, TRUE}, /* m */
{"mm", 2, dch_date, DCH_MM, TRUE}, {"mm", 2, DCH_MM, TRUE},
{"month", 5, dch_date, DCH_month, FALSE}, {"month", 5, DCH_month, FALSE},
{"mon", 3, dch_date, DCH_mon, FALSE}, {"mon", 3, DCH_mon, FALSE},
{"ms", 2, dch_time, DCH_MS, TRUE}, {"ms", 2, DCH_MS, TRUE},
{"p.m.", 4, dch_time, DCH_p_m, FALSE}, /* p */ {"p.m.", 4, DCH_p_m, FALSE}, /* p */
{"pm", 2, dch_time, DCH_pm, FALSE}, {"pm", 2, DCH_pm, FALSE},
{"q", 1, dch_date, DCH_Q, TRUE}, /* q */ {"q", 1, DCH_Q, TRUE}, /* q */
{"rm", 2, dch_date, DCH_rm, FALSE}, /* r */ {"rm", 2, DCH_rm, FALSE}, /* r */
{"ssss", 4, dch_time, DCH_SSSS, TRUE}, /* s */ {"ssss", 4, DCH_SSSS, TRUE}, /* s */
{"ss", 2, dch_time, DCH_SS, TRUE}, {"ss", 2, DCH_SS, TRUE},
{"tz", 2, dch_time, DCH_tz, FALSE}, /* t */ {"tz", 2, DCH_tz, FALSE}, /* t */
{"us", 2, dch_time, DCH_US, TRUE}, /* u */ {"us", 2, DCH_US, TRUE}, /* u */
{"ww", 2, dch_date, DCH_WW, TRUE}, /* w */ {"ww", 2, DCH_WW, TRUE}, /* w */
{"w", 1, dch_date, DCH_W, TRUE}, {"w", 1, DCH_W, TRUE},
{"y,yyy", 5, dch_date, DCH_Y_YYY, TRUE}, /* y */ {"y,yyy", 5, DCH_Y_YYY, TRUE}, /* y */
{"yyyy", 4, dch_date, DCH_YYYY, TRUE}, {"yyyy", 4, DCH_YYYY, TRUE},
{"yyy", 3, dch_date, DCH_YYY, TRUE}, {"yyy", 3, DCH_YYY, TRUE},
{"yy", 2, dch_date, DCH_YY, TRUE}, {"yy", 2, DCH_YY, TRUE},
{"y", 1, dch_date, DCH_Y, TRUE}, {"y", 1, DCH_Y, TRUE},
/* last */
{NULL, 0, NULL, 0}}; /* last */
{NULL, 0, 0, 0}
};
/* ---------- /* ----------
* KeyWords for NUMBER version (now, isitdigit info is not needful here..) * KeyWords for NUMBER version (is_digit field is not needful here...)
* ---------- * ----------
*/ */
static const KeyWord NUM_keywords[] = { static const KeyWord NUM_keywords[] = {
/* keyword, len, func. type is in Index */ /* name, len, id is in Index */
{",", 1, NULL, NUM_COMMA}, /* , */ {",", 1, NUM_COMMA}, /* , */
{".", 1, NULL, NUM_DEC}, /* . */ {".", 1, NUM_DEC}, /* . */
{"0", 1, NULL, NUM_0}, /* 0 */ {"0", 1, NUM_0}, /* 0 */
{"9", 1, NULL, NUM_9}, /* 9 */ {"9", 1, NUM_9}, /* 9 */
{"B", 1, NULL, NUM_B}, /* B */ {"B", 1, NUM_B}, /* B */
{"C", 1, NULL, NUM_C}, /* C */ {"C", 1, NUM_C}, /* C */
{"D", 1, NULL, NUM_D}, /* D */ {"D", 1, NUM_D}, /* D */
{"E", 1, NULL, NUM_E}, /* E */ {"E", 1, NUM_E}, /* E */
{"FM", 2, NULL, NUM_FM}, /* F */ {"FM", 2, NUM_FM}, /* F */
{"G", 1, NULL, NUM_G}, /* G */ {"G", 1, NUM_G}, /* G */
{"L", 1, NULL, NUM_L}, /* L */ {"L", 1, NUM_L}, /* L */
{"MI", 2, NULL, NUM_MI}, /* M */ {"MI", 2, NUM_MI}, /* M */
{"PL", 2, NULL, NUM_PL}, /* P */ {"PL", 2, NUM_PL}, /* P */
{"PR", 2, NULL, NUM_PR}, {"PR", 2, NUM_PR},
{"RN", 2, NULL, NUM_RN}, /* R */ {"RN", 2, NUM_RN}, /* R */
{"SG", 2, NULL, NUM_SG}, /* S */ {"SG", 2, NUM_SG}, /* S */
{"SP", 2, NULL, NUM_SP}, {"SP", 2, NUM_SP},
{"S", 1, NULL, NUM_S}, {"S", 1, NUM_S},
{"TH", 2, NULL, NUM_TH}, /* T */ {"TH", 2, NUM_TH}, /* T */
{"V", 1, NULL, NUM_V}, /* V */ {"V", 1, NUM_V}, /* V */
{"b", 1, NULL, NUM_B}, /* b */ {"b", 1, NUM_B}, /* b */
{"c", 1, NULL, NUM_C}, /* c */ {"c", 1, NUM_C}, /* c */
{"d", 1, NULL, NUM_D}, /* d */ {"d", 1, NUM_D}, /* d */
{"e", 1, NULL, NUM_E}, /* e */ {"e", 1, NUM_E}, /* e */
{"fm", 2, NULL, NUM_FM}, /* f */ {"fm", 2, NUM_FM}, /* f */
{"g", 1, NULL, NUM_G}, /* g */ {"g", 1, NUM_G}, /* g */
{"l", 1, NULL, NUM_L}, /* l */ {"l", 1, NUM_L}, /* l */
{"mi", 2, NULL, NUM_MI}, /* m */ {"mi", 2, NUM_MI}, /* m */
{"pl", 2, NULL, NUM_PL}, /* p */ {"pl", 2, NUM_PL}, /* p */
{"pr", 2, NULL, NUM_PR}, {"pr", 2, NUM_PR},
{"rn", 2, NULL, NUM_rn}, /* r */ {"rn", 2, NUM_rn}, /* r */
{"sg", 2, NULL, NUM_SG}, /* s */ {"sg", 2, NUM_SG}, /* s */
{"sp", 2, NULL, NUM_SP}, {"sp", 2, NUM_SP},
{"s", 1, NULL, NUM_S}, {"s", 1, NUM_S},
{"th", 2, NULL, NUM_th}, /* t */ {"th", 2, NUM_th}, /* t */
{"v", 1, NULL, NUM_V}, /* v */ {"v", 1, NUM_V}, /* v */
/* last */ /* last */
{NULL, 0, NULL, 0}}; {NULL, 0, 0}
};
/* ---------- /* ----------
...@@ -848,7 +832,7 @@ static const int DCH_index[KeyWord_INDEX_SIZE] = { ...@@ -848,7 +832,7 @@ static const int DCH_index[KeyWord_INDEX_SIZE] = {
-1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, DCH_us, -1, DCH_ww, -1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, DCH_us, -1, DCH_ww,
-1, DCH_y_yyy, -1, -1, -1, -1 -1, DCH_y_yyy, -1, -1, -1, -1
/*---- chars over 126 are skiped ----*/ /*---- chars over 126 are skipped ----*/
}; };
/* ---------- /* ----------
...@@ -859,7 +843,7 @@ static const int NUM_index[KeyWord_INDEX_SIZE] = { ...@@ -859,7 +843,7 @@ static const int NUM_index[KeyWord_INDEX_SIZE] = {
/* /*
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
*/ */
/*---- first 0..31 chars are skiped ----*/ /*---- first 0..31 chars are skipped ----*/
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1, -1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1,
...@@ -872,7 +856,7 @@ static const int NUM_index[KeyWord_INDEX_SIZE] = { ...@@ -872,7 +856,7 @@ static const int NUM_index[KeyWord_INDEX_SIZE] = {
-1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1, -1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1,
-1, -1, -1, -1, -1, -1 -1, -1, -1, -1, -1, -1
/*---- chars over 126 are skiped ----*/ /*---- chars over 126 are skipped ----*/
}; };
/* ---------- /* ----------
...@@ -919,8 +903,10 @@ static KeySuffix *suff_search(char *str, KeySuffix *suf, int type); ...@@ -919,8 +903,10 @@ static KeySuffix *suff_search(char *str, KeySuffix *suf, int type);
static void NUMDesc_prepare(NUMDesc *num, FormatNode *n); static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
static void parse_format(FormatNode *node, char *str, const KeyWord *kw, static void parse_format(FormatNode *node, char *str, const KeyWord *kw,
KeySuffix *suf, const int *index, int ver, NUMDesc *Num); KeySuffix *suf, const int *index, int ver, NUMDesc *Num);
static char *DCH_processor(FormatNode *node, char *inout, bool is_to_char,
bool is_interval, void *data); static void DCH_to_char(FormatNode *node, bool is_interval,
TmToChar *in, char *out);
static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out);
#ifdef DEBUG_TO_FROM_CHAR #ifdef DEBUG_TO_FROM_CHAR
static void dump_index(const KeyWord *k, const int *index); static void dump_index(const KeyWord *k, const int *index);
...@@ -934,7 +920,6 @@ static int strdigits_len(char *str); ...@@ -934,7 +920,6 @@ static int strdigits_len(char *str);
static char *str_toupper(char *buff); static char *str_toupper(char *buff);
static char *str_tolower(char *buff); static char *str_tolower(char *buff);
/* static int is_acdc(char *str, int *len); */
static int seq_search(char *name, char **array, int type, int max, int *len); static int seq_search(char *name, char **array, int type, int max, int *len);
static void do_to_timestamp(text *date_txt, text *fmt, static void do_to_timestamp(text *date_txt, text *fmt,
struct pg_tm * tm, fsec_t *fsec); struct pg_tm * tm, fsec_t *fsec);
...@@ -1340,75 +1325,6 @@ parse_format(FormatNode *node, char *str, const KeyWord *kw, ...@@ -1340,75 +1325,6 @@ parse_format(FormatNode *node, char *str, const KeyWord *kw,
return; return;
} }
/* ----------
* Call keyword's function for each of (action) node in format-node tree
* ----------
*/
static char *
DCH_processor(FormatNode *node, char *inout, bool is_to_char,
bool is_interval, void *data)
{
FormatNode *n;
char *s;
/*
* Zeroing global flags
*/
DCH_global_fx = false;
for (n = node, s = inout; n->type != NODE_TYPE_END; n++)
{
if (!is_to_char && *s == '\0')
/*
* The input string is shorter than format picture, so it's good
* time to break this loop...
*
* Note: this isn't relevant for TO_CHAR mode, because it uses
* 'inout' allocated by format picture length.
*/
break;
if (n->type == NODE_TYPE_ACTION)
{
int len;
/*
* Call node action function
*/
len = n->key->action(n->key->id, s, n->suffix, is_to_char,
is_interval, n, data);
if (len > 0)
s += len - 1; /* s++ is at the end of the loop */
else if (len == -1)
continue;
}
else
{
/*
* Remove to output char from input in TO_CHAR
*/
if (is_to_char)
*s = n->character;
else
{
/*
* Skip blank space in FROM_CHAR's input
*/
if (isspace((unsigned char) n->character) && !DCH_global_fx)
while (*s != '\0' && isspace((unsigned char) *(s + 1)))
++s;
}
}
++s;
}
if (is_to_char)
*s = '\0';
return inout;
}
/* ---------- /* ----------
* DEBUG: Dump the FormatNode Tree (debug) * DEBUG: Dump the FormatNode Tree (debug)
* ---------- * ----------
...@@ -1722,20 +1638,6 @@ dump_index(const KeyWord *k, const int *index) ...@@ -1722,20 +1638,6 @@ dump_index(const KeyWord *k, const int *index)
*/ */
#define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0) #define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
/* ----------
* Global format option for DCH version
* ----------
*/
static int
dch_global(int arg, char *inout, int suf, bool is_to_char, bool is_interval,
FormatNode *node, void *data)
{
if (arg == DCH_FX)
DCH_global_fx = true;
return -1;
}
/* ---------- /* ----------
* Return TRUE if next format picture is not digit value * Return TRUE if next format picture is not digit value
* ---------- * ----------
...@@ -1759,7 +1661,7 @@ is_next_separator(FormatNode *n) ...@@ -1759,7 +1661,7 @@ is_next_separator(FormatNode *n)
if (n->type == NODE_TYPE_ACTION) if (n->type == NODE_TYPE_ACTION)
{ {
if (n->key->isitdigit) if (n->key->is_digit)
return FALSE; return FALSE;
return TRUE; return TRUE;
...@@ -1804,1031 +1706,912 @@ strdigits_len(char *str) ...@@ -1804,1031 +1706,912 @@ strdigits_len(char *str)
(errcode(ERRCODE_INVALID_DATETIME_FORMAT), \ (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
errmsg("invalid AM/PM string"))); errmsg("invalid AM/PM string")));
#define CHECK_SEQ_SEARCH(_l, _s) \
do { \
if ((_l) <= 0) { \
ereport(ERROR, \
(errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
errmsg("invalid value for %s", (_s)))); \
} \
} while (0)
/* ---------- /* ----------
* Master function of TIME for: * Process a TmToChar struct as denoted by a list of FormatNodes.
* TO_CHAR - write (inout) formated string * The formatted data is written to the string pointed to by 'out'.
* FROM_CHAR - scan (inout) string by course of FormatNode
* ---------- * ----------
*/ */
static int static void
dch_time(int arg, char *inout, int suf, bool is_to_char, bool is_interval, DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
FormatNode *node, void *data)
{ {
char *p_inout = inout; FormatNode *n;
struct pg_tm *tm = NULL; char *s;
TmFromChar *tmfc = NULL; struct pg_tm *tm = &in->tm;
TmToChar *tmtc = NULL; char buff[DCH_CACHE_SIZE],
workbuff[32];
int i;
if (is_to_char) s = out;
for (n = node; n->type != NODE_TYPE_END; n++)
{ {
tmtc = (TmToChar *) data; if (n->type != NODE_TYPE_ACTION)
tm = tmtcTm(tmtc); {
} *s = n->character;
else s++;
tmfc = (TmFromChar *) data; continue;
}
switch (arg) switch (n->key->id)
{ {
case DCH_A_M: case DCH_A_M:
case DCH_P_M: case DCH_P_M:
if (is_to_char) strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
{
strcpy(inout, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
? P_M_STR : A_M_STR); ? P_M_STR : A_M_STR);
return strlen(p_inout); s += strlen(s);
} break;
else case DCH_AM:
{ case DCH_PM:
if (strncmp(inout, P_M_STR, 4) == 0) strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
tmfc->pm = TRUE;
else if (strncmp(inout, A_M_STR, 4) == 0)
tmfc->am = TRUE;
else
AMPM_ERROR;
return strlen(P_M_STR);
}
break;
case DCH_AM:
case DCH_PM:
if (is_to_char)
{
strcpy(inout, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
? PM_STR : AM_STR); ? PM_STR : AM_STR);
return strlen(p_inout); s += strlen(s);
} break;
else case DCH_a_m:
{ case DCH_p_m:
if (strncmp(inout, PM_STR, 2) == 0) strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
tmfc->pm = TRUE;
else if (strncmp(inout, AM_STR, 2) == 0)
tmfc->am = TRUE;
else
AMPM_ERROR;
return strlen(PM_STR);
}
break;
case DCH_a_m:
case DCH_p_m:
if (is_to_char)
{
strcpy(inout, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
? p_m_STR : a_m_STR); ? p_m_STR : a_m_STR);
return strlen(p_inout); s += strlen(s);
} break;
else case DCH_am:
{ case DCH_pm:
if (strncmp(inout, p_m_STR, 4) == 0) strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
tmfc->pm = TRUE;
else if (strncmp(inout, a_m_STR, 4) == 0)
tmfc->am = TRUE;
else
AMPM_ERROR;
return strlen(p_m_STR);
}
break;
case DCH_am:
case DCH_pm:
if (is_to_char)
{
strcpy(inout, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
? pm_STR : am_STR); ? pm_STR : am_STR);
return strlen(p_inout); s += strlen(s);
} break;
else case DCH_HH:
{ case DCH_HH12:
if (strncmp(inout, pm_STR, 2) == 0) sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
tmfc->pm = TRUE;
else if (strncmp(inout, am_STR, 2) == 0)
tmfc->am = TRUE;
else
AMPM_ERROR;
return strlen(pm_STR);
}
break;
case DCH_HH:
case DCH_HH12:
if (is_to_char)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ? 12 : tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ? 12 :
tm->tm_hour % (HOURS_PER_DAY / 2)); tm->tm_hour % (HOURS_PER_DAY / 2));
if (S_THth(suf)) if (S_THth(n->suffix))
str_numth(p_inout, inout, 0); str_numth(s, s, 0);
return strlen(p_inout); s += strlen(s);
} break;
else case DCH_HH24:
{ sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_hour);
if (S_FM(suf) || is_next_separator(node)) if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_MI:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_min);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_SS:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_sec);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_MS: /* millisecond */
#ifdef HAVE_INT64_TIMESTAMP
sprintf(s, "%03d", (int) (in->fsec / INT64CONST(1000)));
#else
/* No rint() because we can't overflow and we might print US */
sprintf(s, "%03d", (int) (in->fsec * 1000));
#endif
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_US: /* microsecond */
#ifdef HAVE_INT64_TIMESTAMP
sprintf(s, "%06d", (int) in->fsec);
#else
/* don't use rint() because we can't overflow 1000 */
sprintf(s, "%06d", (int) (in->fsec * 1000000));
#endif
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_SSSS:
sprintf(s, "%d", tm->tm_hour * SECS_PER_HOUR +
tm->tm_min * SECS_PER_MINUTE +
tm->tm_sec);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_tz:
INVALID_FOR_INTERVAL;
if (tmtcTzn(in))
{
char *p = pstrdup(tmtcTzn(in));
strcpy(s, str_tolower(p));
pfree(p);
s += strlen(s);
}
break;
case DCH_TZ:
INVALID_FOR_INTERVAL;
if (tmtcTzn(in))
{
strcpy(s, tmtcTzn(in));
s += strlen(s);
}
break;
case DCH_A_D:
case DCH_B_C:
INVALID_FOR_INTERVAL;
strcpy(s, (tm->tm_year <= 0 ? B_C_STR : A_D_STR));
s += strlen(s);
break;
case DCH_AD:
case DCH_BC:
INVALID_FOR_INTERVAL;
strcpy(s, (tm->tm_year <= 0 ? BC_STR : AD_STR));
s += strlen(s);
break;
case DCH_a_d:
case DCH_b_c:
INVALID_FOR_INTERVAL;
strcpy(s, (tm->tm_year <= 0 ? b_c_STR : a_d_STR));
s += strlen(s);
break;
case DCH_ad:
case DCH_bc:
INVALID_FOR_INTERVAL;
strcpy(s, (tm->tm_year <= 0 ? bc_STR : ad_STR));
s += strlen(s);
break;
case DCH_MONTH:
INVALID_FOR_INTERVAL;
if (!tm->tm_mon)
break;
if (S_TM(n->suffix))
{ {
sscanf(inout, "%d", &tmfc->hh); strcpy(workbuff, localize_month_full(tm->tm_mon - 1));
return strdigits_len(inout) + SKIP_THth(suf); sprintf(s, "%*s", 0, localized_str_toupper(workbuff));
} }
else else
{ {
sscanf(inout, "%02d", &tmfc->hh); strcpy(workbuff, months_full[tm->tm_mon - 1]);
return strspace_len(inout) + 2 + SKIP_THth(suf); sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, str_toupper(workbuff));
} }
} s += strlen(s);
break; break;
case DCH_HH24: case DCH_Month:
if (is_to_char) INVALID_FOR_INTERVAL;
{ if (!tm->tm_mon)
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_hour); break;
if (S_THth(suf)) if (S_TM(n->suffix))
str_numth(p_inout, inout, S_TH_TYPE(suf)); sprintf(s, "%*s", 0, localize_month_full(tm->tm_mon - 1));
return strlen(p_inout); else
} sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
else s += strlen(s);
{ break;
if (S_FM(suf) || is_next_separator(node)) case DCH_month:
INVALID_FOR_INTERVAL;
if (!tm->tm_mon)
break;
if (S_TM(n->suffix))
{ {
sscanf(inout, "%d", &tmfc->hh); strcpy(workbuff, localize_month_full(tm->tm_mon - 1));
return strdigits_len(inout) + SKIP_THth(suf); sprintf(s, "%*s", 0, localized_str_tolower(workbuff));
} }
else else
{ {
sscanf(inout, "%02d", &tmfc->hh); sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
return strspace_len(inout) + 2 + SKIP_THth(suf); *s = pg_tolower((unsigned char) *s);
} }
} s += strlen(s);
break; break;
case DCH_MI: case DCH_MON:
if (is_to_char) INVALID_FOR_INTERVAL;
{ if (!tm->tm_mon)
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_min); break;
if (S_THth(suf)) if (S_TM(n->suffix))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
if (S_FM(suf) || is_next_separator(node))
{ {
sscanf(inout, "%d", &tmfc->mi); strcpy(workbuff, localize_month(tm->tm_mon - 1));
return strdigits_len(inout) + SKIP_THth(suf); strcpy(s, localized_str_toupper(workbuff));
} }
else else
{ {
sscanf(inout, "%02d", &tmfc->mi); strcpy(s, months[tm->tm_mon - 1]);
return strspace_len(inout) + 2 + SKIP_THth(suf); str_toupper(s);
} }
} s += strlen(s);
break; break;
case DCH_SS: case DCH_Mon:
if (is_to_char) INVALID_FOR_INTERVAL;
{ if (!tm->tm_mon)
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_sec); break;
if (S_THth(suf)) if (S_TM(n->suffix))
str_numth(p_inout, inout, S_TH_TYPE(suf)); strcpy(s, localize_month(tm->tm_mon - 1));
return strlen(p_inout); else
} strcpy(s, months[tm->tm_mon - 1]);
else s += strlen(s);
{ break;
if (S_FM(suf) || is_next_separator(node)) case DCH_mon:
INVALID_FOR_INTERVAL;
if (!tm->tm_mon)
break;
if (S_TM(n->suffix))
{ {
sscanf(inout, "%d", &tmfc->ss); strcpy(workbuff, localize_month(tm->tm_mon - 1));
return strdigits_len(inout) + SKIP_THth(suf); strcpy(s, localized_str_tolower(workbuff));
} }
else else
{ {
sscanf(inout, "%02d", &tmfc->ss); strcpy(s, months[tm->tm_mon - 1]);
return strspace_len(inout) + 2 + SKIP_THth(suf); *s = pg_tolower((unsigned char) *s);
} }
} s += strlen(s);
break; break;
case DCH_MS: /* millisecond */ case DCH_MM:
if (is_to_char) sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_mon);
{ if (S_THth(n->suffix))
#ifdef HAVE_INT64_TIMESTAMP str_numth(s, s, S_TH_TYPE(n->suffix));
sprintf(inout, "%03d", (int) (tmtc->fsec / INT64CONST(1000))); s += strlen(s);
#else break;
/* No rint() because we can't overflow and we might print US */ case DCH_DAY:
sprintf(inout, "%03d", (int) (tmtc->fsec * 1000)); INVALID_FOR_INTERVAL;
#endif if (S_TM(n->suffix))
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
int len,
x;
if (is_next_separator(node))
{ {
sscanf(inout, "%d", &tmfc->ms); strcpy(workbuff, localize_day_full(tm->tm_wday));
len = x = strdigits_len(inout); sprintf(s, "%*s", 0, localized_str_toupper(workbuff));
} }
else else
{ {
sscanf(inout, "%03d", &tmfc->ms); strcpy(workbuff, days[tm->tm_wday]);
x = strdigits_len(inout); sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, str_toupper(workbuff));
len = x = x > 3 ? 3 : x;
} }
s += strlen(s);
/* break;
* 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25 case DCH_Day:
*/ INVALID_FOR_INTERVAL;
tmfc->ms *= x == 1 ? 100 : if (S_TM(n->suffix))
x == 2 ? 10 : 1; sprintf(s, "%*s", 0, localize_day_full(tm->tm_wday));
else
/* sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
* elog(DEBUG3, "X: %d, MS: %d, LEN: %d", x, tmfc->ms, len); s += strlen(s);
*/ break;
return len + SKIP_THth(suf); case DCH_day:
} INVALID_FOR_INTERVAL;
break; if (S_TM(n->suffix))
case DCH_US: /* microsecond */
if (is_to_char)
{
#ifdef HAVE_INT64_TIMESTAMP
sprintf(inout, "%06d", (int) tmtc->fsec);
#else
/* don't use rint() because we can't overflow 1000 */
sprintf(inout, "%06d", (int) (tmtc->fsec * 1000000));
#endif
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
int len,
x;
if (is_next_separator(node))
{ {
sscanf(inout, "%d", &tmfc->us); strcpy(workbuff, localize_day_full(tm->tm_wday));
len = x = strdigits_len(inout); sprintf(s, "%*s", 0, localized_str_tolower(workbuff));
} }
else else
{ {
sscanf(inout, "%06d", &tmfc->us); sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
x = strdigits_len(inout); *s = pg_tolower((unsigned char) *s);
len = x = x > 6 ? 6 : x;
} }
s += strlen(s);
tmfc->us *= x == 1 ? 100000 : break;
x == 2 ? 10000 : case DCH_DY:
x == 3 ? 1000 : INVALID_FOR_INTERVAL;
x == 4 ? 100 : if (S_TM(n->suffix))
x == 5 ? 10 : 1;
/*
* elog(DEBUG3, "X: %d, US: %d, LEN: %d", x, tmfc->us, len);
*/
return len + SKIP_THth(suf);
}
break;
case DCH_SSSS:
if (is_to_char)
{
sprintf(inout, "%d", tm->tm_hour * SECS_PER_HOUR +
tm->tm_min * SECS_PER_MINUTE +
tm->tm_sec);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
if (S_FM(suf) || is_next_separator(node))
{ {
sscanf(inout, "%d", &tmfc->ssss); strcpy(workbuff, localize_day(tm->tm_wday));
return strdigits_len(inout) + SKIP_THth(suf); strcpy(s, localized_str_toupper(workbuff));
} }
else else
{ {
sscanf(inout, "%05d", &tmfc->ssss); strcpy(s, days_short[tm->tm_wday]);
return strspace_len(inout) + 5 + SKIP_THth(suf); str_toupper(s);
} }
} s += strlen(s);
break; break;
case DCH_tz: case DCH_Dy:
case DCH_TZ: INVALID_FOR_INTERVAL;
INVALID_FOR_INTERVAL; if (S_TM(n->suffix))
if (is_to_char && tmtcTzn(tmtc)) strcpy(s, localize_day(tm->tm_wday));
{
if (arg == DCH_TZ)
strcpy(inout, tmtcTzn(tmtc));
else else
strcpy(s, days_short[tm->tm_wday]);
s += strlen(s);
break;
case DCH_dy:
INVALID_FOR_INTERVAL;
if (S_TM(n->suffix))
{ {
char *p = pstrdup(tmtcTzn(tmtc)); strcpy(workbuff, localize_day(tm->tm_wday));
strcpy(s, localized_str_tolower(workbuff));
strcpy(inout, str_tolower(p));
pfree(p);
} }
return strlen(inout); else
} {
else if (!is_to_char) strcpy(s, days_short[tm->tm_wday]);
ereport(ERROR, *s = pg_tolower((unsigned char) *s);
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), }
errmsg("\"TZ\"/\"tz\" not supported"))); s += strlen(s);
break;
case DCH_DDD:
case DCH_IDDD:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 3,
(n->key->id == DCH_DDD) ?
tm->tm_yday :
date2isoyearday(tm->tm_year, tm->tm_mon, tm->tm_mday));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_DD:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_mday);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_D:
INVALID_FOR_INTERVAL;
sprintf(s, "%d", tm->tm_wday + 1);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_ID:
INVALID_FOR_INTERVAL;
sprintf(s, "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_WW:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
(tm->tm_yday - 1) / 7 + 1);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_IW:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_Q:
if (!tm->tm_mon)
break;
sprintf(s, "%d", (tm->tm_mon - 1) / 3 + 1);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_CC:
if (is_interval) /* straight calculation */
i = tm->tm_year / 100;
else /* century 21 starts in 2001 */
i = (tm->tm_year - 1) / 100 + 1;
if (i <= 99 && i >= -99)
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, i);
else
sprintf(s, "%d", i);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_Y_YYY:
i = ADJUST_YEAR(tm->tm_year, is_interval) / 1000;
sprintf(s, "%d,%03d", i,
ADJUST_YEAR(tm->tm_year, is_interval) - (i * 1000));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_YYYY:
case DCH_IYYY:
if (tm->tm_year <= 9999 && tm->tm_year >= -9998)
sprintf(s, "%0*d",
S_FM(n->suffix) ? 0 : 4,
n->key->id == DCH_YYYY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(
tm->tm_year,
tm->tm_mon,
tm->tm_mday), is_interval));
else
sprintf(s, "%d",
n->key->id == DCH_YYYY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(
tm->tm_year,
tm->tm_mon,
tm->tm_mday), is_interval));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_YYY:
case DCH_IYY:
snprintf(buff, sizeof(buff), "%03d",
n->key->id == DCH_YYY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon, tm->tm_mday),
is_interval));
i = strlen(buff);
strcpy(s, buff + (i - 3));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_YY:
case DCH_IY:
snprintf(buff, sizeof(buff), "%02d",
n->key->id == DCH_YY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon, tm->tm_mday),
is_interval));
i = strlen(buff);
strcpy(s, buff + (i - 2));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_Y:
case DCH_I:
snprintf(buff, sizeof(buff), "%1d",
n->key->id == DCH_Y ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon, tm->tm_mday),
is_interval));
i = strlen(buff);
strcpy(s, buff + (i - 1));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_RM:
if (!tm->tm_mon)
break;
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4,
rm_months_upper[12 - tm->tm_mon]);
s += strlen(s);
break;
case DCH_rm:
if (!tm->tm_mon)
break;
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4,
rm_months_lower[12 - tm->tm_mon]);
s += strlen(s);
break;
case DCH_W:
sprintf(s, "%d", (tm->tm_mday - 1) / 7 + 1);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_J:
sprintf(s, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
}
} }
return -1;
}
#define CHECK_SEQ_SEARCH(_l, _s) \
do { \
if ((_l) <= 0) { \
ereport(ERROR, \
(errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
errmsg("invalid value for %s", (_s)))); \
} \
} while (0)
*s = '\0';
}
/* ---------- /* ----------
* Master of DATE for: * Process a string as denoted by a list of FormatNodes.
* TO_CHAR - write (inout) formated string * The TmFromChar struct pointed to by 'out' is populated with the results.
* FROM_CHAR - scan (inout) string by course of FormatNode *
* Note: we currently don't have any to_interval() function, so there
* is no need here for INVALID_FOR_INTERVAL checks.
* ---------- * ----------
*/ */
static int static void
dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
FormatNode *node, void *data)
{ {
char buff[DCH_CACHE_SIZE], FormatNode *n;
workbuff[32], char *s;
*p_inout = inout; int len,
int i, x;
len; int *target;
struct pg_tm *tm = NULL; bool fx_mode = false;
TmFromChar *tmfc = NULL;
TmToChar *tmtc = NULL;
if (is_to_char) for (n = node, s = in; n->type != NODE_TYPE_END && *s != '\0'; n++)
{ {
tmtc = (TmToChar *) data; if (n->type != NODE_TYPE_ACTION)
tm = tmtcTm(tmtc);
}
else
tmfc = (TmFromChar *) data;
/*
* In the FROM-char there is no difference between "January" or "JANUARY"
* or "january", all is before search convert to "first-upper". This
* convention is used for MONTH, MON, DAY, DY
*/
if (!is_to_char)
{
if (arg == DCH_MONTH || arg == DCH_Month || arg == DCH_month)
{
tmfc->mm = seq_search(inout, months_full, ONE_UPPER, FULL_SIZ, &len) + 1;
CHECK_SEQ_SEARCH(len, "MONTH/Month/month");
return len;
}
else if (arg == DCH_MON || arg == DCH_Mon || arg == DCH_mon)
{ {
tmfc->mm = seq_search(inout, months, ONE_UPPER, MAX_MON_LEN, &len) + 1; s++;
CHECK_SEQ_SEARCH(len, "MON/Mon/mon"); /* Ignore spaces when not in FX (fixed width) mode */
return 3; if (isspace((unsigned char) n->character) && !fx_mode)
}
else if (arg == DCH_DAY || arg == DCH_Day || arg == DCH_day)
{
tmfc->d = seq_search(inout, days, ONE_UPPER, FULL_SIZ, &len);
CHECK_SEQ_SEARCH(len, "DAY/Day/day");
return len;
}
else if (arg == DCH_DY || arg == DCH_Dy || arg == DCH_dy)
{
tmfc->d = seq_search(inout, days, ONE_UPPER, MAX_DY_LEN, &len);
CHECK_SEQ_SEARCH(len, "DY/Dy/dy");
return 3;
}
}
switch (arg)
{
case DCH_A_D:
case DCH_B_C:
INVALID_FOR_INTERVAL;
if (is_to_char)
{
strcpy(inout, (tm->tm_year <= 0 ? B_C_STR : A_D_STR));
return strlen(p_inout);
}
else
{
if (strncmp(inout, B_C_STR, 4) == 0)
tmfc->bc = TRUE;
return 4;
}
break;
case DCH_AD:
case DCH_BC:
INVALID_FOR_INTERVAL;
if (is_to_char)
{
strcpy(inout, (tm->tm_year <= 0 ? BC_STR : AD_STR));
return strlen(p_inout);
}
else
{
if (strncmp(inout, BC_STR, 2) == 0)
tmfc->bc = TRUE;
return 2;
}
break;
case DCH_a_d:
case DCH_b_c:
INVALID_FOR_INTERVAL;
if (is_to_char)
{
strcpy(inout, (tm->tm_year <= 0 ? b_c_STR : a_d_STR));
return strlen(p_inout);
}
else
{
if (strncmp(inout, b_c_STR, 4) == 0)
tmfc->bc = TRUE;
return 4;
}
break;
case DCH_ad:
case DCH_bc:
INVALID_FOR_INTERVAL;
if (is_to_char)
{
strcpy(inout, (tm->tm_year <= 0 ? bc_STR : ad_STR));
return strlen(p_inout);
}
else
{
if (strncmp(inout, bc_STR, 2) == 0)
tmfc->bc = TRUE;
return 2;
}
break;
case DCH_MONTH:
INVALID_FOR_INTERVAL;
if (!tm->tm_mon)
return -1;
if (S_TM(suf))
{
strcpy(workbuff, localize_month_full(tm->tm_mon - 1));
sprintf(inout, "%*s", 0, localized_str_toupper(workbuff));
}
else
{
strcpy(workbuff, months_full[tm->tm_mon - 1]);
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(workbuff));
}
return strlen(p_inout);
case DCH_Month:
INVALID_FOR_INTERVAL;
if (!tm->tm_mon)
return -1;
if (S_TM(suf))
sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1));
else
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
return strlen(p_inout);
case DCH_month:
INVALID_FOR_INTERVAL;
if (!tm->tm_mon)
return -1;
if (S_TM(suf))
{
strcpy(workbuff, localize_month_full(tm->tm_mon - 1));
sprintf(inout, "%*s", 0, localized_str_tolower(workbuff));
}
else
{
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
*inout = pg_tolower((unsigned char) *inout);
}
return strlen(p_inout);
case DCH_MON:
INVALID_FOR_INTERVAL;
if (!tm->tm_mon)
return -1;
if (S_TM(suf))
{
strcpy(workbuff, localize_month(tm->tm_mon - 1));
strcpy(inout, localized_str_toupper(workbuff));
}
else
{
strcpy(inout, months[tm->tm_mon - 1]);
str_toupper(inout);
}
return strlen(p_inout);
case DCH_Mon:
INVALID_FOR_INTERVAL;
if (!tm->tm_mon)
return -1;
if (S_TM(suf))
strcpy(inout, localize_month(tm->tm_mon - 1));
else
strcpy(inout, months[tm->tm_mon - 1]);
return strlen(p_inout);
case DCH_mon:
INVALID_FOR_INTERVAL;
if (!tm->tm_mon)
return -1;
if (S_TM(suf))
{ {
strcpy(workbuff, localize_month(tm->tm_mon - 1)); while (*s != '\0' && isspace((unsigned char) *s))
strcpy(inout, localized_str_tolower(workbuff)); s++;
} }
else continue;
{ }
strcpy(inout, months[tm->tm_mon - 1]);
*inout = pg_tolower((unsigned char) *inout);
}
return strlen(p_inout);
case DCH_MM: switch (n->key->id)
if (is_to_char) {
{ case DCH_FX:
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mon); fx_mode = true;
if (S_THth(suf)) break;
str_numth(p_inout, inout, S_TH_TYPE(suf)); case DCH_A_M:
return strlen(p_inout); case DCH_P_M:
} if (strncmp(s, P_M_STR, n->key->len) == 0)
else out->pm = TRUE;
{ else if (strncmp(s, A_M_STR, n->key->len) == 0)
if (S_FM(suf) || is_next_separator(node)) out->am = TRUE;
else
AMPM_ERROR;
s += strlen(P_M_STR);
break;
case DCH_AM:
case DCH_PM:
if (strncmp(s, PM_STR, n->key->len) == 0)
out->pm = TRUE;
else if (strncmp(s, AM_STR, n->key->len) == 0)
out->am = TRUE;
else
AMPM_ERROR;
s += strlen(PM_STR);
break;
case DCH_a_m:
case DCH_p_m:
if (strncmp(s, p_m_STR, n->key->len) == 0)
out->pm = TRUE;
else if (strncmp(s, a_m_STR, n->key->len) == 0)
out->am = TRUE;
else
AMPM_ERROR;
s += strlen(p_m_STR);
break;
case DCH_am:
case DCH_pm:
if (strncmp(s, pm_STR, n->key->len) == 0)
out->pm = TRUE;
else if (strncmp(s, am_STR, n->key->len) == 0)
out->am = TRUE;
else
AMPM_ERROR;
s += strlen(pm_STR);
break;
case DCH_HH:
case DCH_HH12:
if (S_FM(n->suffix) || is_next_separator(n))
{ {
sscanf(inout, "%d", &tmfc->mm); sscanf(s, "%d", &out->hh);
return strdigits_len(inout) + SKIP_THth(suf); s += strdigits_len(s) + SKIP_THth(n->suffix);
} }
else else
{ {
sscanf(inout, "%02d", &tmfc->mm); sscanf(s, "%02d", &out->hh);
return strspace_len(inout) + 2 + SKIP_THth(suf); s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
}
break;
case DCH_HH24:
if (S_FM(n->suffix) || is_next_separator(n))
{
sscanf(s, "%d", &out->hh);
s += strdigits_len(s) + SKIP_THth(n->suffix);
}
else
{
sscanf(s, "%02d", &out->hh);
s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
}
break;
case DCH_MI:
if (S_FM(n->suffix) || is_next_separator(n))
{
sscanf(s, "%d", &out->mi);
s += strdigits_len(s) + SKIP_THth(n->suffix);
}
else
{
sscanf(s, "%02d", &out->mi);
s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
}
break;
case DCH_SS:
if (S_FM(n->suffix) || is_next_separator(n))
{
sscanf(s, "%d", &out->ss);
s += strdigits_len(s) + SKIP_THth(n->suffix);
}
else
{
sscanf(s, "%02d", &out->ss);
s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
}
break;
case DCH_MS: /* millisecond */
if (is_next_separator(n))
{
sscanf(s, "%d", &out->ms);
len = x = strdigits_len(s);
}
else
{
sscanf(s, "%03d", &out->ms);
x = strdigits_len(s);
len = x = x > 3 ? 3 : x;
} }
}
break;
case DCH_DAY:
INVALID_FOR_INTERVAL;
if (S_TM(suf))
{
strcpy(workbuff, localize_day_full(tm->tm_wday));
sprintf(inout, "%*s", 0, localized_str_toupper(workbuff));
}
else
{
strcpy(workbuff, days[tm->tm_wday]);
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(workbuff));
}
return strlen(p_inout);
case DCH_Day:
INVALID_FOR_INTERVAL;
if (S_TM(suf))
sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday));
else
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
return strlen(p_inout);
case DCH_day:
INVALID_FOR_INTERVAL;
if (S_TM(suf))
{
strcpy(workbuff, localize_day_full(tm->tm_wday));
sprintf(inout, "%*s", 0, localized_str_tolower(workbuff));
}
else
{
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
*inout = pg_tolower((unsigned char) *inout);
}
return strlen(p_inout);
case DCH_DY:
INVALID_FOR_INTERVAL;
if (S_TM(suf))
{
strcpy(workbuff, localize_day(tm->tm_wday));
strcpy(inout, localized_str_toupper(workbuff));
}
else
{
strcpy(inout, days_short[tm->tm_wday]);
str_toupper(inout);
}
return strlen(p_inout); /*
* 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
*/
out->ms *= x == 1 ? 100 :
x == 2 ? 10 : 1;
case DCH_Dy: s += len + SKIP_THth(n->suffix);
INVALID_FOR_INTERVAL; break;
if (S_TM(suf)) case DCH_US: /* microsecond */
strcpy(inout, localize_day(tm->tm_wday)); if (is_next_separator(n))
else {
strcpy(inout, days_short[tm->tm_wday]); sscanf(s, "%d", &out->us);
return strlen(p_inout); len = x = strdigits_len(s);
}
else
{
sscanf(s, "%06d", &out->us);
x = strdigits_len(s);
len = x = x > 6 ? 6 : x;
}
case DCH_dy: out->us *= x == 1 ? 100000 :
INVALID_FOR_INTERVAL; x == 2 ? 10000 :
if (S_TM(suf)) x == 3 ? 1000 :
{ x == 4 ? 100 :
strcpy(workbuff, localize_day(tm->tm_wday)); x == 5 ? 10 : 1;
strcpy(inout, localized_str_tolower(workbuff));
}
else
{
strcpy(inout, days_short[tm->tm_wday]);
*inout = pg_tolower((unsigned char) *inout);
}
return strlen(p_inout);
case DCH_DDD: s += len + SKIP_THth(n->suffix);
case DCH_IDDD: break;
if (is_to_char) case DCH_SSSS:
{ if (S_FM(n->suffix) || is_next_separator(n))
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 3,
(arg == DCH_DDD) ?
tm->tm_yday :
date2isoyearday(tm->tm_year, tm->tm_mon, tm->tm_mday));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
if (S_FM(suf) || is_next_separator(node))
{ {
sscanf(inout, "%d", &tmfc->ddd); sscanf(s, "%d", &out->ssss);
return strdigits_len(inout) + SKIP_THth(suf); s += strdigits_len(s) + SKIP_THth(n->suffix);
} }
else else
{ {
sscanf(inout, "%03d", &tmfc->ddd); sscanf(s, "%05d", &out->ssss);
return strspace_len(inout) + 3 + SKIP_THth(suf); s += strspace_len(s) + 5 + SKIP_THth(n->suffix);
} }
} break;
break; case DCH_tz:
case DCH_DD: case DCH_TZ:
if (is_to_char) ereport(ERROR,
{ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mday); errmsg("\"TZ\"/\"tz\" format patterns are not supported in to_date")));
if (S_THth(suf)) case DCH_A_D:
str_numth(p_inout, inout, S_TH_TYPE(suf)); case DCH_B_C:
return strlen(p_inout); if (strncmp(s, B_C_STR, n->key->len) == 0)
} out->bc = TRUE;
else s += n->key->len;
{ break;
if (S_FM(suf) || is_next_separator(node)) case DCH_AD:
case DCH_BC:
if (strncmp(s, BC_STR, n->key->len) == 0)
out->bc = TRUE;
s += n->key->len;
break;
case DCH_a_d:
case DCH_b_c:
if (strncmp(s, b_c_STR, n->key->len) == 0)
out->bc = TRUE;
s += n->key->len;
break;
case DCH_ad:
case DCH_bc:
if (strncmp(s, bc_STR, n->key->len) == 0)
out->bc = TRUE;
s += n->key->len;
break;
case DCH_MONTH:
case DCH_Month:
case DCH_month:
out->mm = seq_search(s, months_full, ONE_UPPER, FULL_SIZ, &len) + 1;
CHECK_SEQ_SEARCH(len, "MONTH/Month/month");
s += len;
break;
case DCH_MON:
case DCH_Mon:
case DCH_mon:
out->mm = seq_search(s, months, ONE_UPPER, MAX_MON_LEN, &len) + 1;
CHECK_SEQ_SEARCH(len, "MON/Mon/mon");
s += len;
break;
case DCH_MM:
if (S_FM(n->suffix) || is_next_separator(n))
{ {
sscanf(inout, "%d", &tmfc->dd); sscanf(s, "%d", &out->mm);
return strdigits_len(inout) + SKIP_THth(suf); s += strdigits_len(s) + SKIP_THth(n->suffix);
} }
else else
{ {
sscanf(inout, "%02d", &tmfc->dd); sscanf(s, "%02d", &out->mm);
return strspace_len(inout) + 2 + SKIP_THth(suf); s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
} }
} break;
break; case DCH_DAY:
case DCH_D: case DCH_Day:
case DCH_ID: case DCH_day:
INVALID_FOR_INTERVAL; out->d = seq_search(s, days, ONE_UPPER, FULL_SIZ, &len);
if (is_to_char) CHECK_SEQ_SEARCH(len, "DAY/Day/day");
{ s += len;
if (arg == DCH_D) break;
sprintf(inout, "%d", tm->tm_wday + 1); case DCH_DY:
else case DCH_Dy:
sprintf(inout, "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); case DCH_dy:
if (S_THth(suf)) out->d = seq_search(s, days, ONE_UPPER, MAX_DY_LEN, &len);
str_numth(p_inout, inout, S_TH_TYPE(suf)); CHECK_SEQ_SEARCH(len, "DY/Dy/dy");
return strlen(p_inout); s += len;
} break;
else case DCH_DDD:
{ case DCH_IDDD:
sscanf(inout, "%1d", &tmfc->d); if (S_FM(n->suffix) || is_next_separator(n))
if (arg == DCH_D)
tmfc->d--;
return strspace_len(inout) + 1 + SKIP_THth(suf);
}
break;
case DCH_WW:
if (is_to_char)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
(tm->tm_yday - 1) / 7 + 1);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
if (S_FM(suf) || is_next_separator(node))
{ {
sscanf(inout, "%d", &tmfc->ww); sscanf(s, "%d", &out->ddd);
return strdigits_len(inout) + SKIP_THth(suf); s += strdigits_len(s) + SKIP_THth(n->suffix);
} }
else else
{ {
sscanf(inout, "%02d", &tmfc->ww); sscanf(s, "%03d", &out->ddd);
return strspace_len(inout) + 2 + SKIP_THth(suf); s += strspace_len(s) + 3 + SKIP_THth(n->suffix);
} }
} break;
break; case DCH_DD:
case DCH_IW: if (S_FM(n->suffix) || is_next_separator(n))
if (is_to_char)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
if (S_FM(suf) || is_next_separator(node))
{ {
sscanf(inout, "%d", &tmfc->iw); sscanf(s, "%d", &out->dd);
return strdigits_len(inout) + SKIP_THth(suf); s += strdigits_len(s) + SKIP_THth(n->suffix);
} }
else else
{ {
sscanf(inout, "%02d", &tmfc->iw); sscanf(s, "%02d", &out->dd);
return strspace_len(inout) + 2 + SKIP_THth(suf); s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
}
break;
case DCH_D:
case DCH_ID:
sscanf(s, "%1d", &out->d);
if (n->key->id == DCH_D)
out->d--;
s += strspace_len(s) + 1 + SKIP_THth(n->suffix);
break;
case DCH_WW:
if (S_FM(n->suffix) || is_next_separator(n))
{
sscanf(s, "%d", &out->ww);
s += strdigits_len(s) + SKIP_THth(n->suffix);
} }
}
break;
case DCH_Q:
if (is_to_char)
{
if (!tm->tm_mon)
return -1;
sprintf(inout, "%d", (tm->tm_mon - 1) / 3 + 1);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
sscanf(inout, "%1d", &tmfc->q);
return strspace_len(inout) + 1 + SKIP_THth(suf);
}
break;
case DCH_CC:
if (is_to_char)
{
if (is_interval) /* straight calculation */
i = tm->tm_year / 100;
else /* century 21 starts in 2001 */
i = (tm->tm_year - 1) / 100 + 1;
if (i <= 99 && i >= -99)
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, i);
else else
sprintf(inout, "%d", i);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
if (S_FM(suf) || is_next_separator(node))
{ {
sscanf(inout, "%d", &tmfc->cc); sscanf(s, "%02d", &out->ww);
return strdigits_len(inout) + SKIP_THth(suf); s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
}
break;
case DCH_IW:
if (S_FM(n->suffix) || is_next_separator(n))
{
sscanf(s, "%d", &out->iw);
s += strdigits_len(s) + SKIP_THth(n->suffix);
} }
else else
{ {
sscanf(inout, "%02d", &tmfc->cc); sscanf(s, "%02d", &out->iw);
return strspace_len(inout) + 2 + SKIP_THth(suf); s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
}
break;
case DCH_Q:
/*
* We ignore Q when converting to date because it is not
* normative.
*/
s += strspace_len(s) + 1 + SKIP_THth(n->suffix);
break;
case DCH_CC:
if (S_FM(n->suffix) || is_next_separator(n))
{
sscanf(s, "%d", &out->cc);
s += strdigits_len(s) + SKIP_THth(n->suffix);
} }
}
break;
case DCH_Y_YYY:
if (is_to_char)
{
i = ADJUST_YEAR(tm->tm_year, is_interval) / 1000;
sprintf(inout, "%d,%03d", i, ADJUST_YEAR(tm->tm_year, is_interval) - (i * 1000));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
int cc;
sscanf(inout, "%d,%03d", &cc, &tmfc->year);
tmfc->year += (cc * 1000);
tmfc->yysz = 4;
return strdigits_len(inout) + 4 + SKIP_THth(suf);
}
break;
case DCH_YYYY:
case DCH_IYYY:
if (is_to_char)
{
if (tm->tm_year <= 9999 && tm->tm_year >= -9998)
sprintf(inout, "%0*d",
S_FM(suf) ? 0 : 4,
arg == DCH_YYYY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(
tm->tm_year,
tm->tm_mon,
tm->tm_mday), is_interval));
else else
sprintf(inout, "%d", {
arg == DCH_YYYY ? sscanf(s, "%02d", &out->cc);
ADJUST_YEAR(tm->tm_year, is_interval) : s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
ADJUST_YEAR(date2isoyear( }
tm->tm_year, break;
tm->tm_mon, case DCH_Y_YYY:
tm->tm_mday), is_interval)); sscanf(s, "%d,%03d", &x, &out->year);
if (S_THth(suf)) out->year += (x * 1000);
str_numth(p_inout, inout, S_TH_TYPE(suf)); out->yysz = 4;
return strlen(p_inout); s += strdigits_len(s) + 4 + SKIP_THth(n->suffix);
} break;
else case DCH_YYYY:
{ case DCH_IYYY:
int *field; target = (n->key->id == DCH_YYYY) ? &out->year : &out->iyear;
field = (arg == DCH_YYYY) ? &tmfc->year : &tmfc->iyear;
if (S_FM(suf) || is_next_separator(node)) if (S_FM(n->suffix) || is_next_separator(n))
{ {
sscanf(inout, "%d", field); sscanf(s, "%d", target);
tmfc->yysz = 4; out->yysz = 4;
return strdigits_len(inout) + SKIP_THth(suf); s += strdigits_len(s) + SKIP_THth(n->suffix);
} }
else else
{ {
sscanf(inout, "%04d", field); sscanf(s, "%04d", target);
tmfc->yysz = 4; out->yysz = 4;
return strspace_len(inout) + 4 + SKIP_THth(suf); s += strspace_len(s) + 4 + SKIP_THth(n->suffix);
} }
} break;
break; case DCH_YYY:
case DCH_YYY: case DCH_IYY:
case DCH_IYY: target = (n->key->id == DCH_YYY) ? &out->year : &out->iyear;
if (is_to_char)
{
snprintf(buff, sizeof(buff), "%03d",
arg == DCH_YYY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon, tm->tm_mday),
is_interval));
i = strlen(buff);
strcpy(inout, buff + (i - 3));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
int *field;
field = (arg == DCH_YYY) ? &tmfc->year : &tmfc->iyear;
sscanf(inout, "%03d", field); sscanf(s, "%03d", target);
/* /*
* 3-digit year: '100' ... '999' = 1100 ... 1999 '000' ... * 3-digit year: '100' ... '999' = 1100 ... 1999 '000' ...
* '099' = 2000 ... 2099 * '099' = 2000 ... 2099
*/ */
if (*field >= 100) if (*target >= 100)
*field += 1000; *target += 1000;
else else
*field += 2000; *target += 2000;
tmfc->yysz = 3; out->yysz = 3;
return strspace_len(inout) + 3 + SKIP_THth(suf); s += strspace_len(s) + 3 + SKIP_THth(n->suffix);
} break;
break; case DCH_YY:
case DCH_YY: case DCH_IY:
case DCH_IY: target = (n->key->id == DCH_YY) ? &out->year : &out->iyear;
if (is_to_char)
{
snprintf(buff, sizeof(buff), "%02d",
arg == DCH_YY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon, tm->tm_mday),
is_interval));
i = strlen(buff);
strcpy(inout, buff + (i - 2));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
int *field;
field = (arg == DCH_YY) ? &tmfc->year : &tmfc->iyear;
sscanf(inout, "%02d", field); sscanf(s, "%02d", target);
/* /*
* 2-digit year: '00' ... '69' = 2000 ... 2069 '70' ... '99' * 2-digit year: '00' ... '69' = 2000 ... 2069 '70' ... '99'
* = 1970 ... 1999 * = 1970 ... 1999
*/ */
if (*field < 70) if (*target < 70)
*field += 2000; *target += 2000;
else else
*field += 1900; *target += 1900;
tmfc->yysz = 2; out->yysz = 2;
return strspace_len(inout) + 2 + SKIP_THth(suf); s += strspace_len(s) + 2 + SKIP_THth(n->suffix);
} break;
break; case DCH_Y:
case DCH_Y: case DCH_I:
case DCH_I: target = (n->key->id == DCH_Y) ? &out->year : &out->iyear;
if (is_to_char)
{
snprintf(buff, sizeof(buff), "%1d",
arg == DCH_Y ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon, tm->tm_mday),
is_interval));
i = strlen(buff);
strcpy(inout, buff + (i - 1));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
int *field;
field = (arg == DCH_Y) ? &tmfc->year : &tmfc->iyear;
sscanf(inout, "%1d", field); sscanf(s, "%1d", target);
/* /*
* 1-digit year: always +2000 * 1-digit year: always +2000
*/ */
*field += 2000; *target += 2000;
tmfc->yysz = 1; out->yysz = 1;
return strspace_len(inout) + 1 + SKIP_THth(suf); s += strspace_len(s) + 1 + SKIP_THth(n->suffix);
} break;
break; case DCH_RM:
case DCH_RM: out->mm = 12 - seq_search(s, rm_months_upper, ALL_UPPER, FULL_SIZ, &len);
if (is_to_char)
{
if (!tm->tm_mon)
return -1;
sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
rm_months_upper[12 - tm->tm_mon]);
return strlen(p_inout);
}
else
{
tmfc->mm = 12 - seq_search(inout, rm_months_upper, ALL_UPPER, FULL_SIZ, &len);
CHECK_SEQ_SEARCH(len, "RM"); CHECK_SEQ_SEARCH(len, "RM");
return len; s += len;
} break;
break; case DCH_rm:
case DCH_rm: out->mm = 12 - seq_search(s, rm_months_lower, ALL_LOWER, FULL_SIZ, &len);
if (is_to_char)
{
if (!tm->tm_mon)
return -1;
sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
rm_months_lower[12 - tm->tm_mon]);
return strlen(p_inout);
}
else
{
tmfc->mm = 12 - seq_search(inout, rm_months_lower, ALL_LOWER, FULL_SIZ, &len);
CHECK_SEQ_SEARCH(len, "rm"); CHECK_SEQ_SEARCH(len, "rm");
return len; s += len;
} break;
break; case DCH_W:
case DCH_W: sscanf(s, "%1d", &out->w);
if (is_to_char) s += strspace_len(s) + 1 + SKIP_THth(n->suffix);
{ break;
sprintf(inout, "%d", (tm->tm_mday - 1) / 7 + 1); case DCH_J:
if (S_THth(suf)) sscanf(s, "%d", &out->j);
str_numth(p_inout, inout, S_TH_TYPE(suf)); s += strdigits_len(s) + SKIP_THth(n->suffix);
return strlen(p_inout); break;
} }
else
{
sscanf(inout, "%1d", &tmfc->w);
return strspace_len(inout) + 1 + SKIP_THth(suf);
}
break;
case DCH_J:
if (is_to_char)
{
sprintf(inout, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout);
}
else
{
sscanf(inout, "%d", &tmfc->j);
return strdigits_len(inout) + SKIP_THth(suf);
}
break;
} }
return -1;
} }
static DCHCacheEntry * static DCHCacheEntry *
...@@ -2914,6 +2697,11 @@ DCH_cache_search(char *str) ...@@ -2914,6 +2697,11 @@ DCH_cache_search(char *str)
return NULL; return NULL;
} }
/*
* Format a date/time or interval into a string according to fmt.
* We parse fmt into a list of FormatNodes. This is then passed to DCH_to_char
* for formatting.
*/
static text * static text *
datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval) datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval)
{ {
...@@ -2983,7 +2771,7 @@ datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval) ...@@ -2983,7 +2771,7 @@ datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval)
} }
/* The real work is here */ /* The real work is here */
DCH_processor(format, result, true, is_interval, (void *) tmtc); DCH_to_char(format, is_interval, tmtc, result);
if (!incache) if (!incache)
pfree(format); pfree(format);
...@@ -3326,6 +3114,13 @@ to_date(PG_FUNCTION_ARGS) ...@@ -3326,6 +3114,13 @@ to_date(PG_FUNCTION_ARGS)
* *
* Parse the 'date_txt' according to 'fmt', return results as a struct pg_tm * Parse the 'date_txt' according to 'fmt', return results as a struct pg_tm
* and fractional seconds. * and fractional seconds.
*
* We parse 'fmt' into a list of FormatNodes, which is then passed to
* DCH_from_char to populate a TmFromChar with the parsed contents of
* 'date_txt'.
*
* The TmFromChar is then analysed and converted into the final results in
* struct 'tm' and 'fsec'.
*/ */
static void static void
do_to_timestamp(text *date_txt, text *fmt, do_to_timestamp(text *date_txt, text *fmt,
...@@ -3336,11 +3131,10 @@ do_to_timestamp(text *date_txt, text *fmt, ...@@ -3336,11 +3131,10 @@ do_to_timestamp(text *date_txt, text *fmt,
int fmt_len, int fmt_len,
year; year;
ZERO_tmfc(&tmfc);
ZERO_tm(tm); ZERO_tm(tm);
*fsec = 0; *fsec = 0;
ZERO_tmfc(&tmfc);
fmt_len = VARSIZE(fmt) - VARHDRSZ; fmt_len = VARSIZE(fmt) - VARHDRSZ;
if (fmt_len) if (fmt_len)
...@@ -3397,9 +3191,6 @@ do_to_timestamp(text *date_txt, text *fmt, ...@@ -3397,9 +3191,6 @@ do_to_timestamp(text *date_txt, text *fmt,
format = ent->format; format = ent->format;
} }
/*
* Call action for each node in FormatNode tree
*/
#ifdef DEBUG_TO_FROM_CHAR #ifdef DEBUG_TO_FROM_CHAR
/* dump_node(format, fmt_len); */ /* dump_node(format, fmt_len); */
#endif #endif
...@@ -3412,7 +3203,7 @@ do_to_timestamp(text *date_txt, text *fmt, ...@@ -3412,7 +3203,7 @@ do_to_timestamp(text *date_txt, text *fmt,
memcpy(date_str, VARDATA(date_txt), date_len); memcpy(date_str, VARDATA(date_txt), date_len);
*(date_str + date_len) = '\0'; *(date_str + date_len) = '\0';
DCH_processor(format, date_str, false, false, (void *) &tmfc); DCH_from_char(format, date_str, &tmfc);
pfree(date_str); pfree(date_str);
pfree(fmt_str); pfree(fmt_str);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment