1 /* -----------------------------------------------------------------------
4 * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.14 2000/06/15 03:32:28 momjian Exp $
7 * Portions Copyright (c) 1999-2000, PostgreSQL, Inc
10 * TO_CHAR(); TO_TIMESTAMP(); TO_DATE(); TO_NUMBER();
12 * The PostgreSQL routines for a timestamp/int/float/numeric formatting,
13 * inspire with Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines.
17 * Routines use (itself) internal cache for format pictures.
19 * The cache uses a static buffers and is persistent across transactions.
20 * If format-picture is bigger than cache buffer, parser is called always.
22 * NOTE for Number version:
23 * All in this version is implemented as keywords ( => not used
24 * suffixes), because a format picture is for *one* item (number)
25 * only. It not is as a timestamp version, where each keyword (can)
28 * NOTE for Timestamp routines:
29 * In this module the POSIX 'struct tm' type is *not* used, but rather
30 * PgSQL type, which has tm_mon based on one (*non* zero) and
31 * year *not* based on 1900, but is used full year number.
32 * Module supports AD / BC / AM / PM.
34 * Supported types for to_char():
36 * Timestamp, Numeric, int4, int8, float4, float8
38 * Supported types for reverse conversion:
40 * Timestamp - to_timestamp()
42 * Numeric - to_number()
47 * -----------------------------------------------------------------------
51 * UnComment me for DEBUG
55 #define DEBUG_TO_FROM_CHAR
56 #define DEBUG_elog_output NOTICE
69 #include "utils/builtins.h"
70 #include "utils/date.h"
71 #include "utils/datetime.h"
72 #include "utils/formatting.h"
73 #include "utils/int8.h"
74 #include "utils/pg_locale.h"
80 #define DCH_TYPE 1 /* DATE-TIME version */
81 #define NUM_TYPE 2 /* NUMBER version */
84 * KeyWord Index (ascii from position 32 (' ') to 126 (~))
87 #define KeyWord_INDEX_SIZE ('~' - ' ')
88 #define KeyWord_INDEX_FILTER(_c) ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
91 * Maximal length of one node
94 #define DCH_MAX_ITEM_SIZ 9 /* max julian day */
95 #define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
101 #define MAXFLOATWIDTH 64
102 #define MAXDOUBLEWIDTH 128
105 * External (defined in PgSQL dt.c (timestamp utils))
108 extern char *months[], /* month abbreviation */
109 *days[]; /* full days */
112 * Format parser structs
117 char *name; /* suffix string */
118 int len, /* suffix length */
119 id, /* used in node->suffix */
120 type; /* prefix / postfix */
125 char *name; /* keyword */
126 /* action for keyword */
127 int len, /* keyword length */
134 int type; /* node type */
135 KeyWord *key; /* if node type is KEYWORD */
136 int character, /* if node type is CHAR */
137 suffix; /* keyword suffix */
140 #define NODE_TYPE_END 1
141 #define NODE_TYPE_ACTION 2
142 #define NODE_TYPE_CHAR 3
144 #define SUFFTYPE_PREFIX 1
145 #define SUFFTYPE_POSTFIX 2
152 static char *months_full[] = {
153 "January", "February", "March", "April", "May", "June", "July",
154 "August", "September", "October", "November", "December", NULL
161 #define YEAR_ABS(_y) (_y < 0 ? -(_y -1) : _y)
162 #define BC_STR_ORIG " BC"
164 #define A_D_STR "A.D."
165 #define a_d_STR "a.d."
169 #define B_C_STR "B.C."
170 #define b_c_STR "b.c."
179 #define A_M_STR "A.M."
180 #define a_m_STR "a.m."
184 #define P_M_STR "P.M."
185 #define p_m_STR "p.m."
191 * Months in roman-numeral
192 * (Must be conversely for seq_search (in FROM_CHAR), because
193 * 'VIII' must be over 'V')
196 static char *rm_months_upper[] =
197 {"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL};
199 static char *rm_months_lower[] =
200 {"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL};
206 static char *rm1[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL};
207 static char *rm10[] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL};
208 static char *rm100[] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL};
214 static char *numTH[] = {"ST", "ND", "RD", "TH", NULL};
215 static char *numth[] = {"st", "nd", "rd", "th", NULL};
224 #define ONE_UPPER 1 /* Name */
225 #define ALL_UPPER 2 /* NAME */
226 #define ALL_LOWER 3 /* name */
230 #define MAX_MON_LEN 3
237 #ifdef DEBUG_TO_FROM_CHAR
239 elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\
240 tm->tm_sec, tm->tm_year,\
241 tm->tm_min, tm->tm_wday, tm->tm_hour, tm->tm_yday,\
242 tm->tm_mday, tm->tm_isdst,tm->tm_mon);\
247 * Flags for DCH version
250 static int DCH_global_flag = 0;
252 #define DCH_F_FX 0x01
254 #define IS_FX (DCH_global_flag & DCH_F_FX)
258 * Number description struct
263 int pre, /* (count) numbers before decimal */
264 post, /* (count) numbers after decimal */
265 lsign, /* want locales sign */
266 flag, /* number parametrs */
267 pre_lsign_num, /* tmp value for lsign */
268 multi, /* multiplier for 'V' */
269 zero_start, /* position of first zero */
270 zero_end, /* position of last zero */
271 need_locale; /* needs it locale */
275 * Flags for NUMBER version
278 #define NUM_F_DECIMAL 0x01
279 #define NUM_F_LDECIMAL 0x02
280 #define NUM_F_ZERO 0x04
281 #define NUM_F_BLANK 0x08
282 #define NUM_F_FILLMODE 0x10
283 #define NUM_F_LSIGN 0x20
284 #define NUM_F_BRACKET 0x40
285 #define NUM_F_MINUS 0x80
286 #define NUM_F_PLUS 0x100
287 #define NUM_F_ROMAN 0x200
288 #define NUM_F_MULTI 0x400
290 #define NUM_LSIGN_PRE -1
291 #define NUM_LSIGN_POST 1
292 #define NUM_LSIGN_NONE 0
298 #define IS_DECIMAL(_f) ((_f)->flag & NUM_F_DECIMAL)
299 #define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL)
300 #define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO)
301 #define IS_BLANK(_f) ((_f)->flag & NUM_F_BLANK)
302 #define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE)
303 #define IS_BRACKET(_f) ((_f)->flag & NUM_F_BRACKET)
304 #define IS_MINUS(_f) ((_f)->flag & NUM_F_MINUS)
305 #define IS_LSIGN(_f) ((_f)->flag & NUM_F_LSIGN)
306 #define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
307 #define IS_ROMAN(_f) ((_f)->flag & NUM_F_ROMAN)
308 #define IS_MULTI(_f) ((_f)->flag & NUM_F_MULTI)
311 * Format picture cache
313 * Number part = NUM_CACHE_SIZE * NUM_CACHE_FIELDS
314 * Date-time part = DCH_CACHE_SIZE * DCH_CACHE_FIELDS
318 #define NUM_CACHE_SIZE 64
319 #define NUM_CACHE_FIELDS 16
320 #define DCH_CACHE_SIZE 128
321 #define DCH_CACHE_FIELDS 16
325 FormatNode format[DCH_CACHE_SIZE + 1];
326 char str[DCH_CACHE_SIZE + 1];
332 FormatNode format[NUM_CACHE_SIZE + 1];
333 char str[NUM_CACHE_SIZE + 1];
338 static DCHCacheEntry DCHCache[DCH_CACHE_FIELDS + 1]; /* global cache for
340 static int n_DCHCache = 0; /* number of entries */
341 static int DCHCounter = 0;
343 static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1]; /* global cache for
345 static int n_NUMCache = 0; /* number of entries */
346 static int NUMCounter = 0;
348 #define MAX_INT32 (2147483640)
351 * Private global-modul definitions
354 static struct tm _tm,
362 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
365 #define MAX(a,b) (((a)>(b)) ? (a) : (b))
368 /*****************************************************************************
369 * KeyWords definition & action
370 *****************************************************************************/
372 static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node);
373 static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node);
374 static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node);
380 #define DCH_S_FM 0x01
381 #define DCH_S_TH 0x02
382 #define DCH_S_th 0x04
383 #define DCH_S_SP 0x08
389 #define S_THth(_s) (((_s & DCH_S_TH) || (_s & DCH_S_th)) ? 1 : 0)
390 #define S_TH(_s) ((_s & DCH_S_TH) ? 1 : 0)
391 #define S_th(_s) ((_s & DCH_S_th) ? 1 : 0)
392 #define S_TH_TYPE(_s) ((_s & DCH_S_TH) ? TH_UPPER : TH_LOWER)
394 #define S_FM(_s) ((_s & DCH_S_FM) ? 1 : 0)
395 #define S_SP(_s) ((_s & DCH_S_SP) ? 1 : 0)
398 * Suffixes definition for DATE-TIME TO/FROM CHAR
401 static KeySuffix DCH_suff[] = {
402 {"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
403 {"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
404 {"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
405 {"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
406 {"SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX},
412 * Format-pictures (KeyWord).
414 * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
415 * complicated -to-> easy:
417 * (example: "DDD","DD","Day","D" )
419 * (this specific sort needs the algorithm for sequential search for strings,
420 * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
421 * or "HH12"? You must first try "HH12", because "HH" is in string, but
425 * Position for the keyword is simular as position in the enum DCH/NUM_poz
428 * For fast search is used the 'int index[]', index is ascii table from position
429 * 32 (' ') to 126 (~), in this index is DCH_ / NUM_ enums for each ASCII
430 * position or -1 if char is not used in the KeyWord. Search example for
432 * 1) see in index to index['M' - 32],
433 * 2) take keywords position (enum DCH_MM) from index
434 * 3) run sequential search in keywords[] from this position
455 DCH_FX, /* global suffix */
562 * KeyWords for DATE-TIME version
565 static KeyWord DCH_keywords[] = {
566 /* keyword, len, func. type is in Index */
567 {"A.D.", 4, dch_date, DCH_A_D}, /* A */
568 {"A.M.", 4, dch_time, DCH_A_M},
569 {"AD", 2, dch_date, DCH_AD},
570 {"AM", 2, dch_time, DCH_AM},
571 {"B.C.", 4, dch_date, DCH_B_C}, /* B */
572 {"BC", 2, dch_date, DCH_BC},
573 {"CC", 2, dch_date, DCH_CC},/* C */
574 {"DAY", 3, dch_date, DCH_DAY}, /* D */
575 {"DDD", 3, dch_date, DCH_DDD},
576 {"DD", 2, dch_date, DCH_DD},
577 {"DY", 2, dch_date, DCH_DY},
578 {"Day", 3, dch_date, DCH_Day},
579 {"Dy", 2, dch_date, DCH_Dy},
580 {"D", 1, dch_date, DCH_D},
581 {"FX", 2, dch_global, DCH_FX}, /* F */
582 {"HH24", 4, dch_time, DCH_HH24}, /* H */
583 {"HH12", 4, dch_time, DCH_HH12},
584 {"HH", 2, dch_time, DCH_HH},
585 {"J", 1, dch_date, DCH_J}, /* J */
586 {"MI", 2, dch_time, DCH_MI},
587 {"MM", 2, dch_date, DCH_MM},
588 {"MONTH", 5, dch_date, DCH_MONTH},
589 {"MON", 3, dch_date, DCH_MON},
590 {"Month", 5, dch_date, DCH_Month},
591 {"Mon", 3, dch_date, DCH_Mon},
592 {"P.M.", 4, dch_time, DCH_P_M}, /* P */
593 {"PM", 2, dch_time, DCH_PM},
594 {"Q", 1, dch_date, DCH_Q}, /* Q */
595 {"RM", 2, dch_date, DCH_RM},/* R */
596 {"SSSS", 4, dch_time, DCH_SSSS}, /* S */
597 {"SS", 2, dch_time, DCH_SS},
598 {"WW", 2, dch_date, DCH_WW},/* W */
599 {"W", 1, dch_date, DCH_W},
600 {"Y,YYY", 5, dch_date, DCH_Y_YYY}, /* Y */
601 {"YYYY", 4, dch_date, DCH_YYYY},
602 {"YYY", 3, dch_date, DCH_YYY},
603 {"YY", 2, dch_date, DCH_YY},
604 {"Y", 1, dch_date, DCH_Y},
605 {"a.d.", 4, dch_date, DCH_a_d}, /* a */
606 {"a.m.", 4, dch_time, DCH_a_m},
607 {"ad", 2, dch_date, DCH_ad},
608 {"am", 2, dch_time, DCH_am},
609 {"b.c.", 4, dch_date, DCH_b_c}, /* b */
610 {"bc", 2, dch_date, DCH_bc},
611 {"cc", 2, dch_date, DCH_CC},/* c */
612 {"day", 3, dch_date, DCH_day}, /* d */
613 {"ddd", 3, dch_date, DCH_DDD},
614 {"dd", 2, dch_date, DCH_DD},
615 {"dy", 2, dch_date, DCH_dy},
616 {"d", 1, dch_date, DCH_D},
617 {"fx", 2, dch_global, DCH_FX}, /* f */
618 {"hh24", 4, dch_time, DCH_HH24}, /* h */
619 {"hh12", 4, dch_time, DCH_HH12},
620 {"hh", 2, dch_time, DCH_HH},
621 {"j", 1, dch_time, DCH_J}, /* j */
622 {"mi", 2, dch_time, DCH_MI},/* m */
623 {"mm", 2, dch_date, DCH_MM},
624 {"month", 5, dch_date, DCH_month},
625 {"mon", 3, dch_date, DCH_mon},
626 {"p.m.", 4, dch_time, DCH_p_m}, /* p */
627 {"pm", 2, dch_time, DCH_pm},
628 {"q", 1, dch_date, DCH_Q}, /* q */
629 {"rm", 2, dch_date, DCH_rm},/* r */
630 {"ssss", 4, dch_time, DCH_SSSS}, /* s */
631 {"ss", 2, dch_time, DCH_SS},
632 {"ww", 2, dch_date, DCH_WW},/* w */
633 {"w", 1, dch_date, DCH_W},
634 {"y,yyy", 5, dch_date, DCH_Y_YYY}, /* y */
635 {"yyyy", 4, dch_date, DCH_YYYY},
636 {"yyy", 3, dch_date, DCH_YYY},
637 {"yy", 2, dch_date, DCH_YY},
638 {"y", 1, dch_date, DCH_Y},
643 * KeyWords for NUMBER version
646 static KeyWord NUM_keywords[] = {
647 /* keyword, len, func. type is in Index */
648 {",", 1, NULL, NUM_COMMA}, /* , */
649 {".", 1, NULL, NUM_DEC}, /* . */
650 {"0", 1, NULL, NUM_0}, /* 0 */
651 {"9", 1, NULL, NUM_9}, /* 9 */
652 {"B", 1, NULL, NUM_B}, /* B */
653 {"C", 1, NULL, NUM_C}, /* C */
654 {"D", 1, NULL, NUM_D}, /* D */
655 {"E", 1, NULL, NUM_E}, /* E */
656 {"FM", 2, NULL, NUM_FM}, /* F */
657 {"G", 1, NULL, NUM_G}, /* G */
658 {"L", 1, NULL, NUM_L}, /* L */
659 {"MI", 2, NULL, NUM_MI}, /* M */
660 {"PL", 2, NULL, NUM_PL}, /* P */
661 {"PR", 2, NULL, NUM_PR},
662 {"RN", 2, NULL, NUM_RN}, /* R */
663 {"SG", 2, NULL, NUM_SG}, /* S */
664 {"SP", 2, NULL, NUM_SP},
665 {"S", 1, NULL, NUM_S},
666 {"TH", 2, NULL, NUM_TH}, /* T */
667 {"V", 1, NULL, NUM_V}, /* V */
668 {"b", 1, NULL, NUM_B}, /* b */
669 {"c", 1, NULL, NUM_C}, /* c */
670 {"d", 1, NULL, NUM_D}, /* d */
671 {"e", 1, NULL, NUM_E}, /* e */
672 {"fm", 2, NULL, NUM_FM}, /* f */
673 {"g", 1, NULL, NUM_G}, /* g */
674 {"l", 1, NULL, NUM_L}, /* l */
675 {"mi", 2, NULL, NUM_MI}, /* m */
676 {"pl", 2, NULL, NUM_PL}, /* p */
677 {"pr", 2, NULL, NUM_PR},
678 {"rn", 2, NULL, NUM_rn}, /* r */
679 {"sg", 2, NULL, NUM_SG}, /* s */
680 {"sp", 2, NULL, NUM_SP},
681 {"s", 1, NULL, NUM_S},
682 {"th", 2, NULL, NUM_th}, /* t */
683 {"v", 1, NULL, NUM_V}, /* v */
690 * KeyWords index for DATE-TIME version
693 static int DCH_index[KeyWord_INDEX_SIZE] = {
697 /*---- first 0..31 chars are skiped ----*/
699 -1, -1, -1, -1, -1, -1, -1, -1,
700 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
701 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
702 -1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1,
703 DCH_FX, -1, DCH_HH24, -1, DCH_J, -1, -1, DCH_MI, -1, -1,
704 DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, -1, -1, -1, DCH_WW, -1, DCH_Y_YYY,
705 -1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc,
706 DCH_day, -1, DCH_fx, -1, DCH_hh24, -1, DCH_j, -1, -1, DCH_mi,
707 -1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, -1, -1, -1, DCH_ww,
708 -1, DCH_y_yyy, -1, -1, -1, -1
710 /*---- chars over 126 are skiped ----*/
714 * KeyWords index for NUMBER version
717 static int NUM_index[KeyWord_INDEX_SIZE] = {
721 /*---- first 0..31 chars are skiped ----*/
723 -1, -1, -1, -1, -1, -1, -1, -1,
724 -1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1,
725 -1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1,
726 -1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E,
727 NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1,
728 NUM_PL, -1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1,
729 -1, -1, -1, -1, -1, -1, -1, -1, NUM_b, NUM_c,
730 NUM_d, NUM_e, NUM_fm, NUM_g, -1, -1, -1, -1, NUM_l, NUM_mi,
731 -1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1,
732 -1, -1, -1, -1, -1, -1
734 /*---- chars over 126 are skiped ----*/
738 * Number processor struct
741 typedef struct NUMProc
743 int type; /* FROM_CHAR (TO_NUMBER) or TO_CHAR */
745 NUMDesc *Num; /* number description */
747 int sign, /* '-' or '+' */
748 sign_wrote, /* was sign write */
749 sign_pos, /* pre number sign position */
750 num_count, /* number of write digits */
751 num_in, /* is inside number */
752 num_curr, /* current position in number */
753 num_pre, /* space before first number */
755 read_dec, /* to_number - was read dec. point */
756 read_post; /* to_number - number of dec. digit */
758 char *number, /* string with number */
759 *number_p, /* pointer to current number pozition */
760 *inout, /* in / out buffer */
761 *inout_p, /* pointer to current inout pozition */
762 *last_relevant, /* last relevant number after decimal
765 *L_negative_sign,/* Locale */
777 static KeyWord *index_seq_search(char *str, KeyWord *kw, int *index);
778 static KeySuffix *suff_search(char *str, KeySuffix *suf, int type);
779 static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
780 static void parse_format(FormatNode *node, char *str, KeyWord *kw,
781 KeySuffix *suf, int *index, int ver, NUMDesc *Num);
782 static char *DCH_processor(FormatNode *node, char *inout, int flag);
784 #ifdef DEBUG_TO_FROM_CHAR
785 static void dump_index(KeyWord *k, int *index);
786 static void dump_node(FormatNode *node, int max);
790 static char *get_th(char *num, int type);
791 static char *str_numth(char *dest, char *num, int type);
792 static int int4len(int4 num);
793 static char *str_toupper(char *buff);
794 static char *str_tolower(char *buff);
796 /* static int is_acdc(char *str, int *len); */
797 static int seq_search(char *name, char **array, int type, int max, int *len);
798 static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node);
799 static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node);
800 static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node);
801 static char *fill_str(char *str, int c, int max);
802 static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, int *flag);
803 static char *int_to_roman(int number);
804 static void NUM_prepare_locale(NUMProc *Np);
805 static char *get_last_relevant_decnum(char *num);
806 static void NUM_numpart_from_char(NUMProc *Np, int id, int plen);
807 static void NUM_numpart_to_char(NUMProc *Np, int id);
808 static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
809 int plen, int sign, int type);
810 static DCHCacheEntry *DCH_cache_search(char *str);
811 static DCHCacheEntry *DCH_cache_getnew(char *str);
812 static NUMCacheEntry *NUM_cache_search(char *str);
813 static NUMCacheEntry *NUM_cache_getnew(char *str);
817 * Fast sequential search, use index for data selection which
818 * go to seq. cycle (it is very fast for non-wanted strings)
819 * (can't be used binary search in format parsing)
823 index_seq_search(char *str, KeyWord *kw, int *index)
827 if (!KeyWord_INDEX_FILTER(*str))
828 return (KeyWord *) NULL;
830 if ((poz = *(index + (*str - ' '))) > -1)
833 KeyWord *k = kw + poz;
837 if (!strncmp(str, k->name, k->len))
841 return (KeyWord *) NULL;
842 } while (*str == *k->name);
844 return (KeyWord *) NULL;
848 suff_search(char *str, KeySuffix *suf, int type)
852 for (s = suf; s->name != NULL; s++)
857 if (!strncmp(str, s->name, s->len))
860 return (KeySuffix *) NULL;
864 * Prepare NUMDesc (number description struct) via FormatNode struct
868 NUMDesc_prepare(NUMDesc *num, FormatNode *n)
871 if (n->type != NODE_TYPE_ACTION)
879 elog(ERROR, "to_char/to_number(): '9' must be ahead of 'PR'.");
894 elog(ERROR, "to_char/to_number(): '0' must be ahead of 'PR'.");
896 if (!IS_ZERO(num) && !IS_DECIMAL(num))
898 num->flag |= NUM_F_ZERO;
899 num->zero_start = num->pre + 1;
901 if (!IS_DECIMAL(num))
906 num->zero_end = num->pre + num->post;
910 if (num->pre == 0 && num->post == 0 && (!IS_ZERO(num)))
911 num->flag |= NUM_F_BLANK;
915 num->flag |= NUM_F_LDECIMAL;
916 num->need_locale = TRUE;
919 elog(ERROR, "to_char/to_number(): not unique decimal poit.");
921 elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together.");
922 num->flag |= NUM_F_DECIMAL;
926 num->flag |= NUM_F_FILLMODE;
931 elog(ERROR, "to_char/to_number(): not unique 'S'.");
933 if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
934 elog(ERROR, "to_char/to_number(): can't use 'S' and 'PL'/'MI'/'SG'/'PR' together.");
936 if (!IS_DECIMAL(num))
938 num->lsign = NUM_LSIGN_PRE;
939 num->pre_lsign_num = num->pre;
940 num->need_locale = TRUE;
941 num->flag |= NUM_F_LSIGN;
944 else if (num->lsign == NUM_LSIGN_NONE)
946 num->lsign = NUM_LSIGN_POST;
947 num->need_locale = TRUE;
948 num->flag |= NUM_F_LSIGN;
954 elog(ERROR, "to_char/to_number(): can't use 'S' and 'MI' together.");
956 num->flag |= NUM_F_MINUS;
961 elog(ERROR, "to_char/to_number(): can't use 'S' and 'PL' together.");
963 num->flag |= NUM_F_PLUS;
968 elog(ERROR, "to_char/to_number(): can't use 'S' and 'SG' together.");
970 num->flag |= NUM_F_MINUS;
971 num->flag |= NUM_F_PLUS;
975 if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num))
976 elog(ERROR, "to_char/to_number(): can't use 'PR' and 'S'/'PL'/'MI'/'SG' together.");
977 num->flag |= NUM_F_BRACKET;
982 num->flag |= NUM_F_ROMAN;
987 num->need_locale = TRUE;
992 elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together.");
993 num->flag |= NUM_F_MULTI;
997 elog(ERROR, "to_char/to_number(): 'E' is not supported.");
1004 * Format parser, search small keywords and keyword's suffixes, and make
1007 * for DATE-TIME & NUMBER version
1011 parse_format(FormatNode *node, char *str, KeyWord *kw,
1012 KeySuffix *suf, int *index, int ver, NUMDesc *Num)
1020 #ifdef DEBUG_TO_FROM_CHAR
1021 elog(DEBUG_elog_output, "to_char/number(): run parser.");
1034 if (ver == DCH_TYPE && (s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL)
1045 if (*str && (n->key = index_seq_search(str, kw, index)) != NULL)
1048 n->type = NODE_TYPE_ACTION;
1055 * NUM version: Prepare global NUMDesc struct
1058 if (ver == NUM_TYPE)
1059 NUMDesc_prepare(Num, n);
1065 if (ver == DCH_TYPE && *str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL)
1077 * Special characters '\' and '"'
1080 if (*str == '"' && last != '\\')
1087 if (*str == '"' && x != '\\')
1092 else if (*str == '\\' && x != '\\')
1097 n->type = NODE_TYPE_CHAR;
1098 n->character = *str;
1099 n->key = (KeyWord *) NULL;
1109 else if (*str && *str == '\\' && last != '\\' && *(str + 1) == '"')
1117 n->type = NODE_TYPE_CHAR;
1118 n->character = *str;
1119 n->key = (KeyWord *) NULL;
1130 if (n->type == NODE_TYPE_ACTION)
1140 n->type = NODE_TYPE_END;
1146 * Call keyword's function for each of (action) node in format-node tree
1150 DCH_processor(FormatNode *node, char *inout, int flag)
1157 * Zeroing global flags
1160 DCH_global_flag = 0;
1162 for (n = node, s = inout; n->type != NODE_TYPE_END; n++)
1164 if (n->type == NODE_TYPE_ACTION)
1170 * Call node action function
1173 len = n->key->action(n->key->id, s, n->suffix, flag, n);
1184 * Remove to output char from input in TO_CHAR
1187 if (flag == TO_CHAR)
1193 * Skip blank space in FROM_CHAR's input
1196 if (isspace(n->character) && IS_FX == 0)
1198 while (*s != '\0' && isspace((int) *(s + 1)))
1208 if (flag == TO_CHAR)
1215 * DEBUG: Dump the FormatNode Tree (debug)
1218 #ifdef DEBUG_TO_FROM_CHAR
1220 #define DUMP_THth(_suf) (S_TH(_suf) ? "TH" : (S_th(_suf) ? "th" : " "))
1221 #define DUMP_FM(_suf) (S_FM(_suf) ? "FM" : " ")
1224 dump_node(FormatNode *node, int max)
1229 elog(DEBUG_elog_output, "to_from-char(): DUMP FORMAT");
1231 for (a = 0, n = node; a <= max; n++, a++)
1233 if (n->type == NODE_TYPE_ACTION)
1234 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_ACTION '%s'\t(%s,%s)",
1235 a, n->key->name, DUMP_THth(n->suffix), DUMP_FM(n->suffix));
1236 else if (n->type == NODE_TYPE_CHAR)
1237 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_CHAR '%c'", a, n->character);
1238 else if (n->type == NODE_TYPE_END)
1240 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_END", a);
1244 elog(DEBUG_elog_output, "%d:\t UnKnown NODE !!!", a);
1251 /*****************************************************************************
1253 *****************************************************************************/
1256 * Return ST/ND/RD/TH for simple (1..9) numbers
1257 * type --> 0 upper, 1 lower
1261 get_th(char *num, int type)
1263 int len = strlen(num),
1266 last = *(num + (len - 1));
1267 if (!isdigit((unsigned char) last))
1268 elog(ERROR, "get_th: '%s' is not number.", num);
1271 * All "teens" (<x>1[0-9]) get 'TH/th',
1272 * while <x>[02-9][123] still get 'ST/st', 'ND/nd', 'RD/rd', respectively
1274 if ((len > 1) && ((seclast = num[len-2]) == '1'))
1280 if (type == TH_UPPER)
1284 if (type == TH_UPPER)
1288 if (type == TH_UPPER)
1292 if (type == TH_UPPER)
1300 * Convert string-number to ordinal string-number
1301 * type --> 0 upper, 1 lower
1305 str_numth(char *dest, char *num, int type)
1307 sprintf(dest, "%s%s", num, get_th(num, type));
1312 * Return length of integer writed in string
1320 return sprintf(b, "%d", num);
1324 * Convert string to upper-string
1328 str_toupper(char *buff)
1330 char *p_buff = buff;
1334 *p_buff = toupper((unsigned char) *p_buff);
1341 * Convert string to lower-string
1345 str_tolower(char *buff)
1347 char *p_buff = buff;
1351 *p_buff = tolower((unsigned char) *p_buff);
1358 * Check if in string is AC or BC (return: 0==none; -1==BC; 1==AC)
1361 /************* not used - use AD/BC format pictures instead **********
1363 is_acdc(char *str, int *len)
1367 for(p=str; *p != '\0'; p++) {
1372 if (toupper(*p)=='B' && toupper(*(++p))=='C') {
1373 *len += (p - str) +1;
1375 } else if (toupper(*p)=='A' && toupper(*(++p))=='C') {
1376 *len += (p - str) +1;
1384 ******************************/
1387 * Sequential search with to upper/lower conversion
1391 seq_search(char *name, char **array, int type, int max, int *len)
1404 /* set first char */
1405 if (type == ONE_UPPER || ALL_UPPER)
1406 *name = toupper((unsigned char) *name);
1407 else if (type == ALL_LOWER)
1408 *name = tolower((unsigned char) *name);
1410 for (last = 0, a = array; *a != NULL; a++)
1413 /* comperate first chars */
1417 for (i = 1, p = *a + 1, n = name + 1;; n++, p++, i++)
1420 /* search fragment (max) only */
1421 if (max && i == max)
1432 /* Not found in array 'a' */
1437 * Convert (but convert new chars only)
1441 if (type == ONE_UPPER || type == ALL_LOWER)
1442 *n = tolower((unsigned char) *n);
1443 else if (type == ALL_UPPER)
1444 *n = toupper((unsigned char) *n);
1448 #ifdef DEBUG_TO_FROM_CHAR
1451 * elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)", *n, *p,
1464 #ifdef DEBUG_TO_FROM_CHAR
1466 * DEBUG: Call for debug and for index checking; (Show ASCII char
1467 * and defined keyword for each used position
1471 dump_index(KeyWord *k, int *index)
1477 elog(DEBUG_elog_output, "TO-FROM_CHAR: Dump KeyWord Index:");
1479 for (i = 0; i < KeyWord_INDEX_SIZE; i++)
1483 elog(DEBUG_elog_output, "\t%c: %s, ", i + 32, k[index[i]].name);
1489 elog(DEBUG_elog_output, "\t(%d) %c %d", i, i + 32, index[i]);
1492 elog(DEBUG_elog_output, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d",
1499 * Skip TM / th in FROM_CHAR
1502 #define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
1506 * Global format option for DCH version
1510 dch_global(int arg, char *inout, int suf, int flag, FormatNode *node)
1516 DCH_global_flag |= DCH_F_FX;
1523 * Master function of TIME for:
1524 * TO_CHAR - write (inout) formated string
1525 * FROM_CHAR - scan (inout) string by course of FormatNode
1529 dch_time(int arg, char *inout, int suf, int flag, FormatNode *node)
1531 char *p_inout = inout;
1538 if (flag == TO_CHAR)
1540 strcpy(inout, (tm->tm_hour > 13 ? P_M_STR : A_M_STR));
1544 else if (flag == FROM_CHAR)
1546 if (strncmp(inout, P_M_STR, 4) == 0 && tm->tm_hour < 13)
1553 if (flag == TO_CHAR)
1555 strcpy(inout, (tm->tm_hour > 13 ? PM_STR : AM_STR));
1559 else if (flag == FROM_CHAR)
1561 if (strncmp(inout, PM_STR, 2) == 0 && tm->tm_hour < 13)
1568 if (flag == TO_CHAR)
1570 strcpy(inout, (tm->tm_hour > 13 ? p_m_STR : a_m_STR));
1574 else if (flag == FROM_CHAR)
1576 if (strncmp(inout, p_m_STR, 4) == 0 && tm->tm_hour < 13)
1583 if (flag == TO_CHAR)
1585 strcpy(inout, (tm->tm_hour > 13 ? pm_STR : am_STR));
1589 else if (flag == FROM_CHAR)
1591 if (strncmp(inout, pm_STR, 2) == 0 && tm->tm_hour < 13)
1598 if (flag == TO_CHAR)
1600 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
1601 tm->tm_hour == 0 ? 12 :
1602 tm->tm_hour < 13 ? tm->tm_hour : tm->tm_hour - 12);
1604 str_numth(p_inout, inout, 0);
1605 if (S_FM(suf) || S_THth(suf))
1606 return strlen(p_inout) - 1;
1611 else if (flag == FROM_CHAR)
1615 sscanf(inout, "%d", &tm->tm_hour);
1616 return int4len((int4) tm->tm_hour) - 1 + SKIP_THth(suf);
1620 sscanf(inout, "%02d", &tm->tm_hour);
1621 return 1 + SKIP_THth(suf);
1626 if (flag == TO_CHAR)
1628 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_hour);
1630 str_numth(p_inout, inout, S_TH_TYPE(suf));
1631 if (S_FM(suf) || S_THth(suf))
1632 return strlen(p_inout) - 1;
1637 else if (flag == FROM_CHAR)
1641 sscanf(inout, "%d", &tm->tm_hour);
1642 return int4len((int4) tm->tm_hour) - 1 + SKIP_THth(suf);
1646 sscanf(inout, "%02d", &tm->tm_hour);
1647 return 1 + SKIP_THth(suf);
1652 if (flag == TO_CHAR)
1654 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_min);
1656 str_numth(p_inout, inout, S_TH_TYPE(suf));
1657 if (S_FM(suf) || S_THth(suf))
1658 return strlen(p_inout) - 1;
1663 else if (flag == FROM_CHAR)
1667 sscanf(inout, "%d", &tm->tm_min);
1668 return int4len((int4) tm->tm_min) - 1 + SKIP_THth(suf);
1672 sscanf(inout, "%02d", &tm->tm_min);
1673 return 1 + SKIP_THth(suf);
1677 if (flag == TO_CHAR)
1679 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_sec);
1681 str_numth(p_inout, inout, S_TH_TYPE(suf));
1682 if (S_FM(suf) || S_THth(suf))
1683 return strlen(p_inout) - 1;
1688 else if (flag == FROM_CHAR)
1692 sscanf(inout, "%d", &tm->tm_sec);
1693 return int4len((int4) tm->tm_sec) - 1 + SKIP_THth(suf);
1697 sscanf(inout, "%02d", &tm->tm_sec);
1698 return 1 + SKIP_THth(suf);
1702 if (flag == TO_CHAR)
1704 sprintf(inout, "%d", tm->tm_hour * 3600 +
1708 str_numth(p_inout, inout, S_TH_TYPE(suf));
1709 return strlen(p_inout) - 1;
1711 else if (flag == FROM_CHAR)
1712 elog(ERROR, "to_timestamp(): SSSS is not supported");
1717 #define CHECK_SEQ_SEARCH(_l, _s) { \
1719 elog(ERROR, "to_timestamp(): bad value for %s", _s); \
1724 * Master of DATE for:
1725 * TO_CHAR - write (inout) formated string
1726 * FROM_CHAR - scan (inout) string by course of FormatNode
1730 dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
1732 char buff[DCH_CACHE_SIZE],
1740 * In the FROM-char is not difference between "January" or "JANUARY"
1741 * or "january", all is before search convert to "first-upper".
1742 * This convention is used for MONTH, MON, DAY, DY
1745 if (flag == FROM_CHAR)
1747 if (arg == DCH_MONTH || arg == DCH_Month || arg == DCH_month)
1750 tm->tm_mon = seq_search(inout, months_full, ONE_UPPER, FULL_SIZ, &len);
1751 CHECK_SEQ_SEARCH(len, "MONTH/Month/month");
1759 else if (arg == DCH_MON || arg == DCH_Mon || arg == DCH_mon)
1762 tm->tm_mon = seq_search(inout, months, ONE_UPPER, MAX_MON_LEN, &len);
1763 CHECK_SEQ_SEARCH(len, "MON/Mon/mon");
1768 else if (arg == DCH_DAY || arg == DCH_Day || arg == DCH_day)
1771 tm->tm_wday = seq_search(inout, days, ONE_UPPER, FULL_SIZ, &len);
1772 CHECK_SEQ_SEARCH(len, "DAY/Day/day");
1779 else if (arg == DCH_DY || arg == DCH_Dy || arg == DCH_dy)
1782 tm->tm_wday = seq_search(inout, days, ONE_UPPER, MAX_DY_LEN, &len);
1783 CHECK_SEQ_SEARCH(len, "DY/Dy/dy");
1794 if (flag == TO_CHAR)
1796 strcpy(inout, (tm->tm_year < 0 ? B_C_STR : A_D_STR));
1800 else if (flag == FROM_CHAR)
1802 if (strncmp(inout, B_C_STR, 4) == 0 && tm->tm_year > 0)
1803 tm->tm_year = -(tm->tm_year);
1804 if (tm->tm_year < 0)
1805 tm->tm_year = tm->tm_year + 1;
1811 if (flag == TO_CHAR)
1813 strcpy(inout, (tm->tm_year < 0 ? BC_STR : AD_STR));
1817 else if (flag == FROM_CHAR)
1819 if (strncmp(inout, BC_STR, 2) == 0 && tm->tm_year > 0)
1820 tm->tm_year = -(tm->tm_year);
1821 if (tm->tm_year < 0)
1822 tm->tm_year = tm->tm_year + 1;
1828 if (flag == TO_CHAR)
1830 strcpy(inout, (tm->tm_year < 0 ? b_c_STR : a_d_STR));
1834 else if (flag == FROM_CHAR)
1836 if (strncmp(inout, b_c_STR, 4) == 0 && tm->tm_year > 0)
1837 tm->tm_year = -(tm->tm_year);
1838 if (tm->tm_year < 0)
1839 tm->tm_year = tm->tm_year + 1;
1845 if (flag == TO_CHAR)
1847 strcpy(inout, (tm->tm_year < 0 ? bc_STR : ad_STR));
1851 else if (flag == FROM_CHAR)
1853 if (strncmp(inout, bc_STR, 2) == 0 && tm->tm_year > 0)
1854 tm->tm_year = -(tm->tm_year);
1855 if (tm->tm_year < 0)
1856 tm->tm_year = tm->tm_year + 1;
1861 strcpy(inout, months_full[tm->tm_mon - 1]);
1862 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout));
1864 return strlen(p_inout) - 1;
1869 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
1871 return strlen(p_inout) - 1;
1876 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
1877 *inout = tolower(*inout);
1879 return strlen(p_inout) - 1;
1884 strcpy(inout, months[tm->tm_mon - 1]);
1885 inout = str_toupper(inout);
1889 strcpy(inout, months[tm->tm_mon - 1]);
1893 strcpy(inout, months[tm->tm_mon - 1]);
1894 *inout = tolower(*inout);
1898 if (flag == TO_CHAR)
1900 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mon);
1902 str_numth(p_inout, inout, S_TH_TYPE(suf));
1903 if (S_FM(suf) || S_THth(suf))
1904 return strlen(p_inout) - 1;
1909 else if (flag == FROM_CHAR)
1913 sscanf(inout, "%d", &tm->tm_mon);
1914 return int4len((int4) tm->tm_mon) - 1 + SKIP_THth(suf);
1918 sscanf(inout, "%02d", &tm->tm_mon);
1919 return 1 + SKIP_THth(suf);
1924 strcpy(inout, days[tm->tm_wday]);
1925 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout));
1927 return strlen(p_inout) - 1;
1932 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
1934 return strlen(p_inout) - 1;
1939 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
1940 *inout = tolower(*inout);
1942 return strlen(p_inout) - 1;
1947 strcpy(inout, days[tm->tm_wday]);
1948 inout = str_toupper(inout);
1952 strcpy(inout, days[tm->tm_wday]);
1956 strcpy(inout, days[tm->tm_wday]);
1957 *inout = tolower(*inout);
1961 if (flag == TO_CHAR)
1963 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 3, tm->tm_yday);
1965 str_numth(p_inout, inout, S_TH_TYPE(suf));
1966 if (S_FM(suf) || S_THth(suf))
1967 return strlen(p_inout) - 1;
1972 else if (flag == FROM_CHAR)
1976 sscanf(inout, "%d", &tm->tm_yday);
1977 return int4len((int4) tm->tm_yday) - 1 + SKIP_THth(suf);
1981 sscanf(inout, "%03d", &tm->tm_yday);
1982 return 2 + SKIP_THth(suf);
1987 if (flag == TO_CHAR)
1989 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mday);
1991 str_numth(p_inout, inout, S_TH_TYPE(suf));
1992 if (S_FM(suf) || S_THth(suf))
1993 return strlen(p_inout) - 1;
1998 else if (flag == FROM_CHAR)
2002 sscanf(inout, "%d", &tm->tm_mday);
2003 return int4len((int4) tm->tm_mday) - 1 + SKIP_THth(suf);
2007 sscanf(inout, "%02d", &tm->tm_mday);
2008 return 1 + SKIP_THth(suf);
2012 if (flag == TO_CHAR)
2014 sprintf(inout, "%d", tm->tm_wday + 1);
2017 str_numth(p_inout, inout, S_TH_TYPE(suf));
2022 else if (flag == FROM_CHAR)
2024 sscanf(inout, "%1d", &tm->tm_wday);
2027 return 0 + SKIP_THth(suf);
2031 if (flag == TO_CHAR)
2033 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
2034 (tm->tm_yday - tm->tm_wday + 7) / 7);
2036 str_numth(p_inout, inout, S_TH_TYPE(suf));
2037 if (S_FM(suf) || S_THth(suf))
2038 return strlen(p_inout) - 1;
2043 else if (flag == FROM_CHAR)
2044 elog(ERROR, "to_datatime(): WW is not supported");
2046 if (flag == TO_CHAR)
2048 sprintf(inout, "%d", (tm->tm_mon - 1) / 3 + 1);
2051 str_numth(p_inout, inout, S_TH_TYPE(suf));
2057 else if (flag == FROM_CHAR)
2058 elog(ERROR, "to_datatime(): Q is not supported");
2061 if (flag == TO_CHAR)
2063 i = tm->tm_year / 100 + 1;
2064 if (i <= 99 && i >= -99)
2065 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, i);
2067 sprintf(inout, "%d", i);
2069 str_numth(p_inout, inout, S_TH_TYPE(suf));
2070 return strlen(p_inout) - 1;
2073 else if (flag == FROM_CHAR)
2074 elog(ERROR, "to_datatime(): CC is not supported");
2076 if (flag == TO_CHAR)
2078 i = YEAR_ABS(tm->tm_year) / 1000;
2079 sprintf(inout, "%d,%03d", i, YEAR_ABS(tm->tm_year) - (i * 1000));
2081 str_numth(p_inout, inout, S_TH_TYPE(suf));
2084 * if (tm->tm_year < 0) strcat(inout, BC_STR_ORIG);
2086 return strlen(p_inout) - 1;
2089 else if (flag == FROM_CHAR)
2094 sscanf(inout, "%d,%03d", &cc, &yy);
2095 tm->tm_year = (cc * 1000) + yy;
2097 if (!S_FM(suf) && tm->tm_year <= 9999 && tm->tm_year >= -9999)
2100 len = int4len((int4) tm->tm_year) + 1;
2101 len += SKIP_THth(suf);
2104 * AC/BC if (is_acdc(inout+len, &len) < 0 && tm->tm_year >
2105 * 0) tm->tm_year = -(tm->tm_year); if (tm->tm_year < 0)
2106 * tm->tm_year = tm->tm_year+1;
2112 if (flag == TO_CHAR)
2114 if (tm->tm_year <= 9999 && tm->tm_year >= -9998)
2115 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 4, YEAR_ABS(tm->tm_year));
2117 sprintf(inout, "%d", YEAR_ABS(tm->tm_year));
2119 str_numth(p_inout, inout, S_TH_TYPE(suf));
2122 * if (tm->tm_year < 0) strcat(inout, BC_STR_ORIG);
2124 return strlen(p_inout) - 1;
2127 else if (flag == FROM_CHAR)
2129 sscanf(inout, "%d", &tm->tm_year);
2130 if (!S_FM(suf) && tm->tm_year <= 9999 && tm->tm_year >= -9999)
2133 len = int4len((int4) tm->tm_year);
2134 len += SKIP_THth(suf);
2137 * AC/BC if (is_acdc(inout+len, &len) < 0 && tm->tm_year >
2138 * 0) tm->tm_year = -(tm->tm_year); if (tm->tm_year < 0)
2139 * tm->tm_year = tm->tm_year+1;
2145 if (flag == TO_CHAR)
2147 sprintf(buff, "%03d", YEAR_ABS(tm->tm_year));
2149 strcpy(inout, buff + (i - 3));
2152 str_numth(p_inout, inout, S_TH_TYPE(suf));
2158 else if (flag == FROM_CHAR)
2162 sscanf(inout, "%03d", &yy);
2163 tm->tm_year = (tm->tm_year / 1000) * 1000 + yy;
2164 return 2 + SKIP_THth(suf);
2168 if (flag == TO_CHAR)
2170 sprintf(buff, "%02d", YEAR_ABS(tm->tm_year));
2172 strcpy(inout, buff + (i - 2));
2175 str_numth(p_inout, inout, S_TH_TYPE(suf));
2181 else if (flag == FROM_CHAR)
2185 sscanf(inout, "%02d", &yy);
2186 tm->tm_year = (tm->tm_year / 100) * 100 + yy;
2187 return 1 + SKIP_THth(suf);
2191 if (flag == TO_CHAR)
2193 sprintf(buff, "%1d", YEAR_ABS(tm->tm_year));
2195 strcpy(inout, buff + (i - 1));
2198 str_numth(p_inout, inout, S_TH_TYPE(suf));
2204 else if (flag == FROM_CHAR)
2208 sscanf(inout, "%1d", &yy);
2209 tm->tm_year = (tm->tm_year / 10) * 10 + yy;
2210 return 0 + SKIP_THth(suf);
2214 if (flag == TO_CHAR)
2216 sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
2217 rm_months_upper[12 - tm->tm_mon]);
2219 return strlen(p_inout) - 1;
2224 else if (flag == FROM_CHAR)
2226 tm->tm_mon = 11 - seq_search(inout, rm_months_upper, ALL_UPPER, FULL_SIZ, &len);
2227 CHECK_SEQ_SEARCH(len, "RM");
2236 if (flag == TO_CHAR)
2238 sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
2239 rm_months_lower[12 - tm->tm_mon]);
2241 return strlen(p_inout) - 1;
2246 else if (flag == FROM_CHAR)
2248 tm->tm_mon = 11 - seq_search(inout, rm_months_lower, ALL_UPPER, FULL_SIZ, &len);
2249 CHECK_SEQ_SEARCH(len, "rm");
2258 if (flag == TO_CHAR)
2260 sprintf(inout, "%d", (tm->tm_mday - tm->tm_wday + 7) / 7);
2263 str_numth(p_inout, inout, S_TH_TYPE(suf));
2269 else if (flag == FROM_CHAR)
2270 elog(ERROR, "to_datatime(): W is not supported");
2273 if (flag == TO_CHAR)
2275 sprintf(inout, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
2277 str_numth(p_inout, inout, S_TH_TYPE(suf));
2278 return strlen(p_inout) - 1;
2280 else if (flag == FROM_CHAR)
2281 elog(ERROR, "to_datatime(): J is not supported");
2286 static DCHCacheEntry *
2287 DCH_cache_getnew(char *str)
2289 DCHCacheEntry *ent = NULL;
2291 /* counter overload check - paranoa? */
2292 if (DCHCounter + DCH_CACHE_FIELDS >= MAX_INT32)
2296 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2297 ent->age = (++DCHCounter);
2301 * Cache is full - needs remove any older entry
2304 if (n_DCHCache > DCH_CACHE_FIELDS)
2307 DCHCacheEntry *old = DCHCache + 0;
2309 #ifdef DEBUG_TO_FROM_CHAR
2310 elog(DEBUG_elog_output, "Cache is full (%d)", n_DCHCache);
2312 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2314 if (ent->age < old->age)
2317 #ifdef DEBUG_TO_FROM_CHAR
2318 elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age);
2320 strcpy(old->str, str); /* check str size before this func. */
2321 /* old->format fill parser */
2322 old->age = (++DCHCounter);
2328 #ifdef DEBUG_TO_FROM_CHAR
2329 elog(DEBUG_elog_output, "NEW (%d)", n_DCHCache);
2331 ent = DCHCache + n_DCHCache;
2332 strcpy(ent->str, str); /* check str size before this func. */
2333 /* ent->format fill parser */
2334 ent->age = (++DCHCounter);
2339 return (DCHCacheEntry *) NULL; /* never */
2342 static DCHCacheEntry *
2343 DCH_cache_search(char *str)
2348 /* counter overload check - paranoa? */
2349 if (DCHCounter + DCH_CACHE_FIELDS >= MAX_INT32)
2353 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2354 ent->age = (++DCHCounter);
2357 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2359 if (i == n_DCHCache)
2361 if (strcmp(ent->str, str) == 0)
2363 ent->age = (++DCHCounter);
2369 return (DCHCacheEntry *) NULL;
2373 /****************************************************************************
2375 ***************************************************************************/
2377 /* -------------------
2378 * TIMESTAMP to_char()
2379 * -------------------
2382 timestamp_to_char(PG_FUNCTION_ARGS)
2384 Timestamp dt = PG_GETARG_TIMESTAMP(0);
2385 text *fmt = PG_GETARG_TEXT_P(1);
2397 len = VARSIZE(fmt) - VARHDRSZ;
2399 if ((!len) || (TIMESTAMP_NOT_FINITE(dt)))
2400 return PointerGetDatum(textin(""));
2412 if (TIMESTAMP_IS_EPOCH(dt))
2414 x = timestamp2tm(SetTimestamp(dt), NULL, tm, &fsec, NULL);
2417 else if (TIMESTAMP_IS_CURRENT(dt))
2419 x = timestamp2tm(SetTimestamp(dt), &tz, tm, &fsec, &tzn);
2423 x = timestamp2tm(dt, &tz, tm, &fsec, &tzn);
2426 elog(ERROR, "to_char(): Unable to convert timestamp to tm");
2428 tm->tm_wday = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1) % 7;
2429 tm->tm_yday = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(tm->tm_year, 1, 1) + 1;
2432 * Convert VARDATA() to string
2435 str = (char *) palloc(len + 1);
2436 memcpy(str, VARDATA(fmt), len);
2437 *(str + len) = '\0';
2443 result = (text *) palloc((len * DCH_MAX_ITEM_SIZ) + 1 + VARHDRSZ);
2446 * Allocate new memory if format picture is bigger than static cache
2447 * and not use cache (call parser always) - flag=1 show this variant
2450 if (len > DCH_CACHE_SIZE)
2453 format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
2456 parse_format(format, str, DCH_keywords,
2457 DCH_suff, DCH_index, DCH_TYPE, NULL);
2459 (format + len)->type = NODE_TYPE_END; /* Paranoa? */
2473 if ((ent = DCH_cache_search(str)) == NULL)
2476 ent = DCH_cache_getnew(str);
2479 * Not in the cache, must run parser and save a new
2480 * format-picture to the cache.
2483 parse_format(ent->format, str, DCH_keywords,
2484 DCH_suff, DCH_index, DCH_TYPE, NULL);
2486 (ent->format + len)->type = NODE_TYPE_END; /* Paranoa? */
2488 #ifdef DEBUG_TO_FROM_CHAR
2489 /* dump_node(ent->format, len); */
2490 /* dump_index(DCH_keywords, DCH_index); */
2493 format = ent->format;
2496 DCH_processor(format, VARDATA(result), TO_CHAR);
2504 * for result is allocated max memory, which current format-picture
2505 * needs, now it must be re-allocate to result real size
2508 len = strlen(VARDATA(result));
2509 result_tmp = result;
2510 result = (text *) palloc(len + 1 + VARHDRSZ);
2512 strcpy(VARDATA(result), VARDATA(result_tmp));
2513 VARSIZE(result) = len + VARHDRSZ;
2516 PG_RETURN_TEXT_P(result);
2520 /* ---------------------
2523 * Make Timestamp from date_str which is formated at argument 'fmt'
2524 * ( to_timestamp is reverse to_char() )
2525 * ---------------------
2528 to_timestamp(PG_FUNCTION_ARGS)
2530 text *date_str = PG_GETARG_TEXT_P(0);
2531 text *fmt = PG_GETARG_TEXT_P(1);
2550 len = VARSIZE(fmt) - VARHDRSZ;
2556 * Convert VARDATA() to string
2559 str = (char *) palloc(len + 1);
2560 memcpy(str, VARDATA(fmt), len);
2561 *(str + len) = '\0';
2564 * Allocate new memory if format picture is bigger than static cache
2565 * and not use cache (call parser always) - flag=1 show this variant
2568 if (len > DCH_CACHE_SIZE)
2571 format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
2574 parse_format(format, str, DCH_keywords,
2575 DCH_suff, DCH_index, DCH_TYPE, NULL);
2577 (format + len)->type = NODE_TYPE_END; /* Paranoa? */
2590 if ((ent = DCH_cache_search(str)) == NULL)
2593 ent = DCH_cache_getnew(str);
2596 * Not in the cache, must run parser and save a new
2597 * format-picture to the cache.
2600 parse_format(ent->format, str, DCH_keywords,
2601 DCH_suff, DCH_index, DCH_TYPE, NULL);
2603 (ent->format + len)->type = NODE_TYPE_END; /* Paranoa? */
2604 #ifdef DEBUG_TO_FROM_CHAR
2605 /* dump_node(ent->format, len); */
2606 /* dump_index(DCH_keywords, DCH_index); */
2609 format = ent->format;
2613 * Call action for each node in FormatNode tree
2616 #ifdef DEBUG_TO_FROM_CHAR
2617 /* dump_node(format, len); */
2619 VARDATA(date_str)[VARSIZE(date_str) - VARHDRSZ] = '\0';
2620 DCH_processor(format, VARDATA(date_str), FROM_CHAR);
2628 #ifdef DEBUG_TO_FROM_CHAR
2631 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
2634 #ifdef USE_POSIX_TIME
2636 tm->tm_year -= 1900;
2639 #ifdef DEBUG_TO_FROM_CHAR
2640 elog(DEBUG_elog_output, "TO-FROM_CHAR: Call mktime()");
2644 tm->tm_year += 1900;
2647 #if defined(HAVE_TM_ZONE)
2648 tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
2649 #elif defined(HAVE_INT_TIMEZONE)
2652 tz = (tm->tm_isdst ? (_timezone - 3600) : _timezone);
2654 tz = (tm->tm_isdst ? (timezone - 3600) : timezone);
2658 #error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined
2661 #else /* !USE_POSIX_TIME */
2670 #ifdef DEBUG_TO_FROM_CHAR
2673 if (tm2timestamp(tm, fsec, &tz, &result) != 0)
2674 elog(ERROR, "to_timestamp(): can't convert 'tm' to timestamp.");
2676 PG_RETURN_TIMESTAMP(result);
2681 * Make Date from date_str which is formated at argument 'fmt'
2685 to_date(PG_FUNCTION_ARGS)
2687 /* Quick hack: since our inputs are just like to_timestamp,
2688 * hand over the whole input info struct...
2690 return DirectFunctionCall1(timestamp_date, to_timestamp(fcinfo));
2693 /**********************************************************************
2694 * the NUMBER version part
2695 *********************************************************************/
2699 fill_str(char *str, int c, int max)
2701 memset(str, c, max);
2702 *(str + max + 1) = '\0';
2706 #define zeroize_NUM(_n) { \
2711 (_n)->pre_lsign_num = 0; \
2712 (_n)->need_locale = 0; \
2714 (_n)->zero_start = 0; \
2715 (_n)->zero_end = 0; \
2718 static NUMCacheEntry *
2719 NUM_cache_getnew(char *str)
2721 NUMCacheEntry *ent = NULL;
2723 /* counter overload check - paranoa? */
2724 if (NUMCounter + NUM_CACHE_FIELDS >= MAX_INT32)
2728 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
2729 ent->age = (++NUMCounter);
2733 * Cache is full - needs remove any older entry
2736 if (n_NUMCache > NUM_CACHE_FIELDS)
2739 NUMCacheEntry *old = NUMCache + 0;
2741 #ifdef DEBUG_TO_FROM_CHAR
2742 elog(DEBUG_elog_output, "Cache is full (%d)", n_NUMCache);
2745 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
2747 if (ent->age < old->age)
2750 #ifdef DEBUG_TO_FROM_CHAR
2751 elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age);
2753 strcpy(old->str, str); /* check str size before this func. */
2754 /* old->format fill parser */
2755 old->age = (++NUMCounter);
2762 #ifdef DEBUG_TO_FROM_CHAR
2763 elog(DEBUG_elog_output, "NEW (%d)", n_NUMCache);
2765 ent = NUMCache + n_NUMCache;
2766 strcpy(ent->str, str); /* check str size before this func. */
2767 /* ent->format fill parser */
2768 ent->age = (++NUMCounter);
2772 zeroize_NUM(&ent->Num);
2774 return ent; /* never */
2777 static NUMCacheEntry *
2778 NUM_cache_search(char *str)
2783 /* counter overload check - paranoa? */
2784 if (NUMCounter + NUM_CACHE_FIELDS >= MAX_INT32)
2788 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
2789 ent->age = (++NUMCounter);
2792 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
2794 if (i == n_NUMCache)
2796 if (strcmp(ent->str, str) == 0)
2798 ent->age = (++NUMCounter);
2804 return (NUMCacheEntry *) NULL;
2808 * Cache routine for NUM to_char version
2812 NUM_cache(int len, NUMDesc *Num, char *pars_str, int *flag)
2814 FormatNode *format = NULL;
2818 * Convert VARDATA() to string
2821 str = (char *) palloc(len + 1);
2822 memcpy(str, pars_str, len);
2823 *(str + len) = '\0';
2826 * Allocate new memory if format picture is bigger than static cache
2827 * and not use cache (call parser always) - flag=1 show this variant
2830 if (len > NUM_CACHE_SIZE)
2833 format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
2838 parse_format(format, str, NUM_keywords,
2839 NULL, NUM_index, NUM_TYPE, Num);
2841 (format + len)->type = NODE_TYPE_END; /* Paranoa? */
2855 if ((ent = NUM_cache_search(str)) == NULL)
2858 ent = NUM_cache_getnew(str);
2861 * Not in the cache, must run parser and save a new
2862 * format-picture to the cache.
2865 parse_format(ent->format, str, NUM_keywords,
2866 NULL, NUM_index, NUM_TYPE, &ent->Num);
2868 (ent->format + len)->type = NODE_TYPE_END; /* Paranoa? */
2872 format = ent->format;
2875 * Copy cache to used struct
2878 Num->flag = ent->Num.flag;
2879 Num->lsign = ent->Num.lsign;
2880 Num->pre = ent->Num.pre;
2881 Num->post = ent->Num.post;
2882 Num->pre_lsign_num = ent->Num.pre_lsign_num;
2883 Num->need_locale = ent->Num.need_locale;
2884 Num->multi = ent->Num.multi;
2885 Num->zero_start = ent->Num.zero_start;
2886 Num->zero_end = ent->Num.zero_end;
2889 #ifdef DEBUG_TO_FROM_CHAR
2890 /* dump_node(format, len); */
2891 dump_index(NUM_keywords, NUM_index);
2900 int_to_roman(int number)
2909 result = (char *) palloc(16);
2912 if (number > 3999 || number < 1)
2914 fill_str(result, '#', 15);
2917 len = sprintf(numstr, "%d", number);
2919 for (p = numstr; *p != '\0'; p++, --len)
2921 num = *p - 49; /* 48 ascii + 1 */
2924 if (num == -1 && set == 0)
2931 strcat(result, "M");
2936 strcat(result, rm100[num]);
2938 strcat(result, rm10[num]);
2940 strcat(result, rm1[num]);
2953 NUM_prepare_locale(NUMProc *Np)
2958 if (Np->Num->need_locale)
2961 struct lconv *lconv;
2967 lconv = PGLC_localeconv();
2970 * Positive / Negative number sign
2973 if (lconv->negative_sign && *lconv->negative_sign)
2974 Np->L_negative_sign = lconv->negative_sign;
2976 Np->L_negative_sign = "-";
2978 if (lconv->positive_sign && *lconv->positive_sign)
2979 Np->L_positive_sign = lconv->positive_sign;
2981 Np->L_positive_sign = "+";
2984 * Number thousands separator
2987 if (lconv->thousands_sep && *lconv->thousands_sep)
2988 Np->L_thousands_sep = lconv->thousands_sep;
2990 Np->L_thousands_sep = ",";
2993 * Number decimal point
2996 if (lconv->decimal_point && *lconv->decimal_point)
2997 Np->decimal = lconv->decimal_point;
3005 if (lconv->currency_symbol && *lconv->currency_symbol)
3006 Np->L_currency_symbol = lconv->currency_symbol;
3008 Np->L_currency_symbol = " ";
3011 if (!IS_LDECIMAL(Np->Num))
3022 Np->L_negative_sign = "-";
3023 Np->L_positive_sign = "+";
3025 Np->L_thousands_sep = ",";
3026 Np->L_currency_symbol = " ";
3034 * Return pointer of last relevant number after decimal point
3035 * 12.0500 --> last relevant is '5'
3039 get_last_relevant_decnum(char *num)
3042 *p = strchr(num, '.');
3044 #ifdef DEBUG_TO_FROM_CHAR
3045 elog(DEBUG_elog_output, "CALL: get_last_relevant_decnum()");
3062 * Number extraction for TO_NUMBER()
3066 NUM_numpart_from_char(NUMProc *Np, int id, int plen)
3069 #ifdef DEBUG_TO_FROM_CHAR
3070 elog(DEBUG_elog_output, " --- scan start --- ");
3073 if (*Np->inout_p == ' ')
3076 #define OVERLOAD_TEST (Np->inout_p >= Np->inout + plen)
3078 if (*Np->inout_p == ' ')
3088 if (*Np->number == ' ' && (id == NUM_0 || id == NUM_9 || NUM_S))
3091 #ifdef DEBUG_TO_FROM_CHAR
3092 elog(DEBUG_elog_output, "Try read sign (%c).", *Np->inout_p);
3098 if (IS_LSIGN(Np->Num))
3101 int x = strlen(Np->L_negative_sign);
3103 #ifdef DEBUG_TO_FROM_CHAR
3104 elog(DEBUG_elog_output, "Try read locale sign (%c).", *Np->inout_p);
3106 if (!strncmp(Np->inout_p, Np->L_negative_sign, x))
3108 Np->inout_p += x - 1;
3113 x = strlen(Np->L_positive_sign);
3114 if (!strncmp(Np->inout_p, Np->L_positive_sign, x))
3116 Np->inout_p += x - 1;
3122 #ifdef DEBUG_TO_FROM_CHAR
3123 elog(DEBUG_elog_output, "Try read sipmle sign (%c).", *Np->inout_p);
3129 if (*Np->inout_p == '-' || (IS_BRACKET(Np->Num) &&
3130 *Np->inout_p == '<'))
3133 *Np->number = '-'; /* set - */
3137 else if (*Np->inout_p == '+')
3140 *Np->number = '+'; /* set + */
3152 if (isdigit((unsigned char) *Np->inout_p))
3155 if (Np->read_dec && Np->read_post == Np->Num->post)
3158 *Np->number_p = *Np->inout_p;
3164 #ifdef DEBUG_TO_FROM_CHAR
3165 elog(DEBUG_elog_output, "Read digit (%c).", *Np->inout_p);
3169 * read decimal point
3173 else if (IS_DECIMAL(Np->Num))
3176 #ifdef DEBUG_TO_FROM_CHAR
3177 elog(DEBUG_elog_output, "Try read decimal point (%c).", *Np->inout_p);
3179 if (*Np->inout_p == '.')
3182 *Np->number_p = '.';
3184 Np->read_dec = TRUE;
3190 int x = strlen(Np->decimal);
3192 #ifdef DEBUG_TO_FROM_CHAR
3193 elog(DEBUG_elog_output, "Try read locale point (%c).", *Np->inout_p);
3195 if (!strncmp(Np->inout_p, Np->decimal, x))
3197 Np->inout_p += x - 1;
3198 *Np->number_p = '.';
3200 Np->read_dec = TRUE;
3207 * Add digit or sign to number-string
3211 NUM_numpart_to_char(NUMProc *Np, int id)
3213 if (IS_ROMAN(Np->Num))
3216 /* Note: in this elog() output not set '\0' in 'inout' */
3218 #ifdef DEBUG_TO_FROM_CHAR
3221 * Np->num_curr is number of current item in format-picture, it is not
3222 * current position in inout!
3224 elog(DEBUG_elog_output,
3226 "SIGN_WROTE: %d, CURRENT: %d, NUMBER_P: '%s', INOUT: '%s'",
3238 if (Np->num_curr == Np->sign_pos && Np->sign_wrote == FALSE)
3241 #ifdef DEBUG_TO_FROM_CHAR
3242 elog(DEBUG_elog_output, "Writing sign to position: %d", Np->num_curr);
3244 if (IS_LSIGN(Np->Num))
3251 if (Np->sign == '-')
3252 strcpy(Np->inout_p, Np->L_negative_sign);
3254 strcpy(Np->inout_p, Np->L_positive_sign);
3255 Np->inout_p += strlen(Np->inout_p);
3258 else if (IS_BRACKET(Np->Num))
3260 *Np->inout_p = '<'; /* Write < */
3264 else if (Np->sign == '+')
3266 *Np->inout_p = ' '; /* Write + */
3270 else if (Np->sign == '-')
3275 Np->sign_wrote = TRUE;
3278 else if (Np->sign_wrote && IS_BRACKET(Np->Num) &&
3279 (Np->num_curr == Np->num_count + (Np->num_pre ? 1 : 0)
3280 + (IS_DECIMAL(Np->Num) ? 1 : 0)))
3283 * Write close BRACKET
3286 #ifdef DEBUG_TO_FROM_CHAR
3287 elog(DEBUG_elog_output, "Writing bracket to position %d", Np->num_curr);
3289 *Np->inout_p = '>'; /* Write '>' */
3294 * digits / FM / Zero / Dec. point
3297 if (id == NUM_9 || id == NUM_0 || id == NUM_D || id == NUM_DEC ||
3298 (id == NUM_S && Np->num_curr < Np->num_pre))
3301 if (Np->num_curr < Np->num_pre &&
3302 (Np->Num->zero_start > Np->num_curr || !IS_ZERO(Np->Num)))
3309 if (!IS_FILLMODE(Np->Num))
3311 #ifdef DEBUG_TO_FROM_CHAR
3312 elog(DEBUG_elog_output, "Writing blank space to position %d", Np->num_curr);
3314 *Np->inout_p = ' '; /* Write ' ' */
3319 else if (IS_ZERO(Np->Num) &&
3320 Np->num_curr < Np->num_pre &&
3321 Np->Num->zero_start <= Np->num_curr)
3328 #ifdef DEBUG_TO_FROM_CHAR
3329 elog(DEBUG_elog_output, "Writing zero to position %d", Np->num_curr);
3331 *Np->inout_p = '0'; /* Write '0' */
3340 * Write Decinal point
3343 if (*Np->number_p == '.')
3346 if (!Np->last_relevant || *Np->last_relevant != '.')
3348 #ifdef DEBUG_TO_FROM_CHAR
3349 elog(DEBUG_elog_output, "Writing decimal point to position %d", Np->num_curr);
3351 strcpy(Np->inout_p, Np->decimal); /* Write DEC/D */
3352 Np->inout_p += strlen(Np->inout_p);
3355 * terrible Ora '0' -- 9.9 --> '0.'
3358 else if (IS_FILLMODE(Np->Num) && *Np->number == '0' &&
3359 Np->last_relevant && *Np->last_relevant == '.')
3362 strcpy(Np->inout_p, Np->decimal); /* Write DEC/D */
3363 Np->inout_p += strlen(Np->inout_p);
3374 if (Np->last_relevant && Np->number_p > Np->last_relevant &&
3379 * terrible Ora format: '0.1' -- 9.9 --> ' .1'
3381 else if (!IS_ZERO(Np->Num) && *Np->number == '0' &&
3382 Np->number == Np->number_p && Np->Num->post != 0)
3385 if (!IS_FILLMODE(Np->Num))
3391 * total terible Ora: '0' -- FM9.9 --> '0.'
3394 else if (Np->last_relevant && *Np->last_relevant == '.')
3403 #ifdef DEBUG_TO_FROM_CHAR
3404 elog(DEBUG_elog_output, "Writing digit '%c' to position %d", *Np->number_p, Np->num_curr);
3406 *Np->inout_p = *Np->number_p; /* Write DIGIT */
3419 NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
3420 int plen, int sign, int type)
3428 Np->number = number;
3430 Np->last_relevant = NULL;
3432 Np->read_dec = FALSE;
3434 if (Np->Num->zero_start)
3435 --Np->Num->zero_start;
3441 if (IS_ROMAN(Np->Num))
3443 if (Np->type == FROM_CHAR)
3444 elog(ERROR, "to_number(): RN is not supported");
3446 Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->post =
3447 Np->Num->pre = Np->num_pre = Np->sign = 0;
3449 if (IS_FILLMODE(Np->Num))
3452 Np->Num->flag |= NUM_F_FILLMODE;
3456 Np->Num->flag |= NUM_F_ROMAN;
3463 if (type == FROM_CHAR)
3472 if (Np->sign != '-')
3474 Np->Num->flag &= ~NUM_F_BRACKET;
3475 Np->Num->flag &= ~NUM_F_MINUS;
3477 else if (Np->sign != '+')
3478 Np->Num->flag &= ~NUM_F_PLUS;
3480 if (Np->sign == '+' && IS_FILLMODE(Np->Num) && !IS_LSIGN(Np->Num))
3481 Np->sign_wrote = TRUE; /* needn't sign */
3483 Np->sign_wrote = FALSE; /* need sign */
3487 if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num)
3488 Np->Num->lsign = NUM_LSIGN_POST;
3490 /* MI/PL/SG - write sign itself and not in number */
3491 if (IS_PLUS(Np->Num) || IS_MINUS(Np->Num))
3492 Np->sign_wrote = TRUE; /* needn't sign */
3499 Np->num_count = Np->Num->post + Np->Num->pre - 1;
3501 if (type == TO_CHAR)
3505 if (IS_FILLMODE(Np->Num))
3507 if (IS_DECIMAL(Np->Num))
3508 Np->last_relevant = get_last_relevant_decnum(
3510 ((Np->Num->zero_end - Np->num_pre > 0) ?
3511 Np->Num->zero_end - Np->num_pre : 0));
3514 if (!Np->sign_wrote && Np->num_pre == 0)
3517 if (!Np->sign_wrote)
3524 if (Np->Num->lsign == NUM_LSIGN_POST)
3526 Np->sign_pos = Np->num_count + (Np->num_pre ? 1 : 0);
3528 if (IS_DECIMAL(Np->Num)) /* decimal point correctio */
3531 else if (IS_ZERO(Np->Num) && Np->num_pre > Np->Num->zero_start)
3532 Np->sign_pos = Np->Num->zero_start ? Np->Num->zero_start : 0;
3535 Np->sign_pos = Np->num_pre && !IS_FILLMODE(Np->Num) ? Np->num_pre : 0;
3538 * terrible Ora format
3541 if (!IS_ZERO(Np->Num) && *Np->number == '0' &&
3542 !IS_FILLMODE(Np->Num) && Np->Num->post != 0)
3547 if (IS_LSIGN(Np->Num))
3549 if (Np->Num->lsign == NUM_LSIGN_PRE)
3561 *Np->number = ' '; /* sign space */
3562 *(Np->number + 1) = '\0';
3568 #ifdef DEBUG_TO_FROM_CHAR
3569 elog(DEBUG_elog_output,
3571 "\n\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_POS: %d\n\tSIGN_WROTE: %s\n\tZERO: %s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s",
3578 Np->sign_wrote ? "Yes" : "No",
3579 IS_ZERO(Np->Num) ? "Yes" : "No",
3580 Np->Num->zero_start,
3582 Np->last_relevant ? Np->last_relevant : "<not set>"
3590 NUM_prepare_locale(Np);
3593 * Processor direct cycle
3596 if (Np->type == FROM_CHAR)
3597 Np->number_p = Np->number + 1; /* first char is space for sign */
3598 else if (Np->type == TO_CHAR)
3599 Np->number_p = Np->number;
3601 for (n = node, Np->inout_p = Np->inout; n->type != NODE_TYPE_END; n++)
3604 if (Np->type == FROM_CHAR)
3607 * Check non-string inout end
3610 if (Np->inout_p >= Np->inout + plen)
3615 * Format pictures actions
3618 if (n->type == NODE_TYPE_ACTION)
3622 * Create/reading digit/zero/blank/sing
3634 if (Np->type == TO_CHAR)
3636 NUM_numpart_to_char(Np, n->key->id);
3637 continue; /* for() */
3641 NUM_numpart_from_char(Np, n->key->id, plen);
3642 break; /* switch() case: */
3646 if (Np->type == TO_CHAR)
3650 if (IS_FILLMODE(Np->Num))
3659 else if (Np->type == FROM_CHAR)
3663 if (IS_FILLMODE(Np->Num))
3670 if (Np->type == TO_CHAR)
3674 if (IS_FILLMODE(Np->Num))
3678 int x = strlen(Np->L_thousands_sep);
3680 memset(Np->inout_p, ' ', x);
3681 Np->inout_p += x - 1;
3686 strcpy(Np->inout_p, Np->L_thousands_sep);
3687 Np->inout_p += strlen(Np->inout_p) - 1;
3691 else if (Np->type == FROM_CHAR)
3695 if (IS_FILLMODE(Np->Num))
3698 Np->inout_p += strlen(Np->L_thousands_sep) - 1;
3703 if (Np->type == TO_CHAR)
3705 strcpy(Np->inout_p, Np->L_currency_symbol);
3706 Np->inout_p += strlen(Np->inout_p) - 1;
3709 else if (Np->type == FROM_CHAR)
3710 Np->inout_p += strlen(Np->L_currency_symbol) - 1;
3714 if (IS_FILLMODE(Np->Num))
3716 strcpy(Np->inout_p, Np->number_p);
3717 Np->inout_p += strlen(Np->inout_p) - 1;
3720 Np->inout_p += sprintf(Np->inout_p, "%15s", Np->number_p) - 1;
3724 if (IS_FILLMODE(Np->Num))
3726 strcpy(Np->inout_p, str_tolower(Np->number_p));
3727 Np->inout_p += strlen(Np->inout_p) - 1;
3730 Np->inout_p += sprintf(Np->inout_p, "%15s", str_tolower(Np->number_p)) - 1;
3734 if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
3735 Np->sign == '-' || IS_DECIMAL(Np->Num))
3738 if (Np->type == TO_CHAR)
3739 strcpy(Np->inout_p, get_th(Np->number, TH_LOWER));
3744 if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
3745 Np->sign == '-' || IS_DECIMAL(Np->Num))
3748 if (Np->type == TO_CHAR)
3749 strcpy(Np->inout_p, get_th(Np->number, TH_UPPER));
3754 if (Np->type == TO_CHAR)
3756 if (Np->sign == '-')
3762 else if (Np->type == FROM_CHAR)
3764 if (*Np->inout_p == '-')
3770 if (Np->type == TO_CHAR)
3772 if (Np->sign == '+')
3778 else if (Np->type == FROM_CHAR)
3780 if (*Np->inout_p == '+')
3786 if (Np->type == TO_CHAR)
3787 *Np->inout_p = Np->sign;
3789 else if (Np->type == FROM_CHAR)
3791 if (*Np->inout_p == '-')
3793 else if (*Np->inout_p == '+')
3808 * Remove to output char from input in TO_CHAR
3811 if (Np->type == TO_CHAR)
3812 *Np->inout_p = n->character;
3817 if (Np->type == TO_CHAR)
3819 *Np->inout_p = '\0';
3823 else if (Np->type == FROM_CHAR)
3826 if (*(Np->number_p - 1) == '.')
3827 *(Np->number_p - 1) = '\0';
3829 *Np->number_p = '\0';
3832 * Correction - precision of dec. number
3835 Np->Num->post = Np->read_post;
3837 #ifdef DEBUG_TO_FROM_CHAR
3838 elog(DEBUG_elog_output, "TO_NUMBER (number): '%s'", Np->number);
3849 * MACRO: Start part of NUM - for all NUM's to_char variants
3850 * (sorry, but I hate copy same code - macro is better..)
3853 #define NUM_TOCHAR_prepare { \
3855 len = VARSIZE(fmt) - VARHDRSZ; \
3858 return PointerGetDatum(textin("")); \
3860 result = (text *) palloc( (len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ); \
3861 format = NUM_cache(len, &Num, VARDATA(fmt), &flag); \
3865 * MACRO: Finish part of NUM
3868 #define NUM_TOCHAR_finish { \
3870 NUM_processor(format, &Num, VARDATA(result), \
3871 numstr, plen, sign, TO_CHAR); \
3878 * for result is allocated max memory, which current format-picture\
3879 * needs, now it must be re-allocate to result real size \
3882 len = strlen(VARDATA(result)); \
3883 result_tmp = result; \
3884 result = (text *) palloc( len + 1 + VARHDRSZ); \
3886 strcpy( VARDATA(result), VARDATA(result_tmp)); \
3887 VARSIZE(result) = len + VARHDRSZ; \
3888 pfree(result_tmp); \
3891 /* -------------------
3892 * NUMERIC to_number() (convert string to numeric)
3893 * -------------------
3896 numeric_to_number(PG_FUNCTION_ARGS)
3898 text *value = PG_GETARG_TEXT_P(0);
3899 text *fmt = PG_GETARG_TEXT_P(1);
3909 len = VARSIZE(fmt) - VARHDRSZ;
3914 format = NUM_cache(len, &Num, VARDATA(fmt), &flag);
3916 numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1);
3918 NUM_processor(format, &Num, VARDATA(value), numstr,
3919 VARSIZE(value) - VARHDRSZ, 0, FROM_CHAR);
3922 precision = MAX(0, Num.pre) + scale;
3927 result = DirectFunctionCall3(numeric_in,
3928 CStringGetDatum(numstr),
3929 ObjectIdGetDatum(InvalidOid),
3930 Int32GetDatum(((precision << 16) | scale) + VARHDRSZ));
3935 /* ------------------
3937 * ------------------
3940 numeric_to_char(PG_FUNCTION_ARGS)
3942 Numeric value = PG_GETARG_NUMERIC(0);
3943 text *fmt = PG_GETARG_TEXT_P(1);
3960 * On DateType depend part (numeric)
3965 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
3966 NumericGetDatum(value),
3968 numstr = orgnum = int_to_roman(numeric_int4(x));
3973 Numeric val = value;
3977 Numeric a = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
3978 Int32GetDatum(10)));
3979 Numeric b = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
3980 Int32GetDatum(Num.multi)));
3982 x = numeric_power(a, b);
3983 val = numeric_mul(value, x);
3987 Num.pre += Num.multi;
3990 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
3991 NumericGetDatum(val),
3992 Int32GetDatum(Num.post)));
3993 orgnum = DatumGetCString(DirectFunctionCall1(numeric_out,
3994 NumericGetDatum(x)));
4000 numstr = orgnum + 1;
4007 if ((p = strchr(numstr, '.')))
4010 len = strlen(numstr);
4013 plen = Num.pre - len;
4015 else if (len > Num.pre)
4017 fill_str(numstr, '#', Num.pre);
4018 *(numstr + Num.pre) = '.';
4019 fill_str(numstr + 1 + Num.pre, '#', Num.post);
4027 PG_RETURN_TEXT_P(result);
4035 int4_to_char(PG_FUNCTION_ARGS)
4037 int32 value = PG_GETARG_INT32(0);
4038 text *fmt = PG_GETARG_TEXT_P(1);
4053 * On DateType depend part (int32)
4058 numstr = orgnum = int_to_roman(value);
4064 orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4065 Int32GetDatum(value * ((int32) pow((double) 10, (double) Num.multi)))));
4066 Num.pre += Num.multi;
4070 orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4071 Int32GetDatum(value)));
4073 len = strlen(orgnum);
4087 numstr = palloc(len + 1 + Num.post);
4088 strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
4089 *(numstr + len) = '.';
4091 for (i = len + 1; i <= Num.post + len + 1; i++)
4092 *(numstr + i) = '0';
4093 *(numstr + Num.post + len + 1) = '\0';
4098 numstr = orgnum + (*orgnum == '-' ? 1 : 0);
4101 plen = Num.pre - len;
4102 else if (len > Num.pre)
4104 fill_str(numstr, '#', Num.pre);
4105 *(numstr + Num.pre) = '.';
4106 fill_str(numstr + 1 + Num.pre, '#', Num.post);
4111 PG_RETURN_TEXT_P(result);
4119 int8_to_char(PG_FUNCTION_ARGS)
4121 int64 value = PG_GETARG_INT64(0);
4122 text *fmt = PG_GETARG_TEXT_P(1);
4137 * On DateType depend part (int32)
4142 /* Currently don't support int8 conversion to roman... */
4143 numstr = orgnum = int_to_roman(DatumGetInt32(
4144 DirectFunctionCall1(int84, Int64GetDatum(value))));
4150 double multi = pow((double) 10, (double) Num.multi);
4152 value = DatumGetInt64(DirectFunctionCall2(int8mul,
4153 Int64GetDatum(value),
4154 DirectFunctionCall1(dtoi8,
4155 Float8GetDatum(multi))));
4156 Num.pre += Num.multi;
4159 orgnum = DatumGetCString(DirectFunctionCall1(int8out,
4160 Int64GetDatum(value)));
4161 len = strlen(orgnum);
4175 numstr = palloc(len + 1 + Num.post);
4176 strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
4177 *(numstr + len) = '.';
4179 for (i = len + 1; i <= Num.post + len + 1; i++)
4180 *(numstr + i) = '0';
4181 *(numstr + Num.post + len + 1) = '\0';
4186 numstr = orgnum + (*orgnum == '-' ? 1 : 0);
4189 plen = Num.pre - len;
4190 else if (len > Num.pre)
4192 fill_str(numstr, '#', Num.pre);
4193 *(numstr + Num.pre) = '.';
4194 fill_str(numstr + 1 + Num.pre, '#', Num.post);
4199 PG_RETURN_TEXT_P(result);
4202 /* -----------------
4207 float4_to_char(PG_FUNCTION_ARGS)
4209 float4 value = PG_GETARG_FLOAT4(0);
4210 text *fmt = PG_GETARG_TEXT_P(1);
4227 numstr = orgnum = int_to_roman((int) rint(value));
4236 float multi = pow((double) 10, (double) Num.multi);
4238 val = value * multi;
4239 Num.pre += Num.multi;
4242 orgnum = (char *) palloc(MAXFLOATWIDTH + 1);
4243 len = sprintf(orgnum, "%.0f", fabs(val));
4245 plen = Num.pre - len;
4248 else if (Num.post + len > FLT_DIG)
4249 Num.post = FLT_DIG - len;
4250 sprintf(orgnum, "%.*f", Num.post, val);
4255 numstr = orgnum + 1;
4262 if ((p = strchr(numstr, '.')))
4265 len = strlen(numstr);
4268 plen = Num.pre - len;
4270 else if (len > Num.pre)
4272 fill_str(numstr, '#', Num.pre);
4273 *(numstr + Num.pre) = '.';
4274 fill_str(numstr + 1 + Num.pre, '#', Num.post);
4279 PG_RETURN_TEXT_P(result);
4282 /* -----------------
4287 float8_to_char(PG_FUNCTION_ARGS)
4289 float8 value = PG_GETARG_FLOAT8(0);
4290 text *fmt = PG_GETARG_TEXT_P(1);
4307 numstr = orgnum = int_to_roman((int) rint(value));
4316 double multi = pow((double) 10, (double) Num.multi);
4318 val = value * multi;
4319 Num.pre += Num.multi;
4321 orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
4322 len = sprintf(orgnum, "%.0f", fabs(val));
4324 plen = Num.pre - len;
4327 else if (Num.post + len > DBL_DIG)
4328 Num.post = DBL_DIG - len;
4329 sprintf(orgnum, "%.*f", Num.post, val);
4334 numstr = orgnum + 1;
4341 if ((p = strchr(numstr, '.')))
4344 len = strlen(numstr);
4347 plen = Num.pre - len;
4349 else if (len > Num.pre)
4351 fill_str(numstr, '#', Num.pre);
4352 *(numstr + Num.pre) = '.';
4353 fill_str(numstr + 1 + Num.pre, '#', Num.post);
4358 PG_RETURN_TEXT_P(result);