1 /* -----------------------------------------------------------------------
4 * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.104 2006/02/12 04:44:15 momjian Exp $
7 * Portions Copyright (c) 1999-2005, PostgreSQL Global Development Group
10 * TO_CHAR(); TO_TIMESTAMP(); TO_DATE(); TO_NUMBER();
12 * The PostgreSQL routines for a timestamp/int/float/numeric formatting,
13 * inspired by the 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()
48 * - better number building (formatting) / parsing, now it isn't
51 * - add support for abstime
52 * - add support for roman number to standard number conversion
53 * - add support for number spelling
54 * - add support for string to string formatting (we must be better
56 * to_char('Hello', 'X X X X X') -> 'H e l l o'
58 * -----------------------------------------------------------------------
62 * UnComment me for DEBUG
66 #define DEBUG_TO_FROM_CHAR
67 #define DEBUG_elog_output DEBUG3
78 #include "utils/builtins.h"
79 #include "utils/date.h"
80 #include "utils/datetime.h"
81 #include "utils/formatting.h"
82 #include "utils/int8.h"
83 #include "utils/numeric.h"
84 #include "utils/pg_locale.h"
86 #define _(x) gettext((x))
92 #define DCH_TYPE 1 /* DATE-TIME version */
93 #define NUM_TYPE 2 /* NUMBER version */
96 * KeyWord Index (ascii from position 32 (' ') to 126 (~))
99 #define KeyWord_INDEX_SIZE ('~' - ' ')
100 #define KeyWord_INDEX_FILTER(_c) ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
103 * Maximal length of one node
106 #define DCH_MAX_ITEM_SIZ 9 /* max julian day */
107 #define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
113 #define MAXFLOATWIDTH 64
114 #define MAXDOUBLEWIDTH 128
117 * External (defined in PgSQL datetime.c (timestamp utils))
120 extern char *months[], /* month abbreviation */
121 *days[]; /* full days */
124 * Format parser structs
129 char *name; /* suffix string */
130 int len, /* suffix length */
131 id, /* used in node->suffix */
132 type; /* prefix / postfix */
135 typedef struct FormatNode FormatNode;
139 const char *name; /* keyword */
140 int len; /* keyword length */
141 int (*action) (int arg, char *inout, /* action for keyword */
142 int suf, bool is_to_char, bool is_interval,
143 FormatNode *node, void *data);
144 int id; /* keyword id */
145 bool isitdigit; /* is expected output/input digit */
150 int type; /* node type */
151 const KeyWord *key; /* if node type is KEYWORD */
152 int character, /* if node type is CHAR */
153 suffix; /* keyword suffix */
156 #define NODE_TYPE_END 1
157 #define NODE_TYPE_ACTION 2
158 #define NODE_TYPE_CHAR 3
160 #define SUFFTYPE_PREFIX 1
161 #define SUFFTYPE_POSTFIX 2
168 static char *months_full[] = {
169 "January", "February", "March", "April", "May", "June", "July",
170 "August", "September", "October", "November", "December", NULL
173 static char *days_short[] = {
174 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
182 * There is no 0 AD. Years go from 1 BC to 1 AD, so we make it
183 * positive and map year == -1 to year zero, and shift all negative
184 * years up one. For interval years, we just return the year.
186 #define ADJUST_YEAR(year, is_interval) ((is_interval) ? (year) : ((year) <= 0 ? -((year) - 1) : (year)))
187 #define BC_STR_ORIG " BC"
189 #define A_D_STR "A.D."
190 #define a_d_STR "a.d."
194 #define B_C_STR "B.C."
195 #define b_c_STR "b.c."
204 #define A_M_STR "A.M."
205 #define a_m_STR "a.m."
209 #define P_M_STR "P.M."
210 #define p_m_STR "p.m."
216 * Months in roman-numeral
217 * (Must be conversely for seq_search (in FROM_CHAR), because
218 * 'VIII' must be over 'V')
221 static char *rm_months_upper[] =
222 {"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL};
224 static char *rm_months_lower[] =
225 {"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL};
231 static char *rm1[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL};
232 static char *rm10[] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL};
233 static char *rm100[] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL};
239 static char *numTH[] = {"ST", "ND", "RD", "TH", NULL};
240 static char *numth[] = {"st", "nd", "rd", "th", NULL};
246 #define ONE_UPPER 1 /* Name */
247 #define ALL_UPPER 2 /* NAME */
248 #define ALL_LOWER 3 /* name */
252 #define MAX_MON_LEN 3
259 * Flags for DCH version
262 static bool DCH_global_fx = false;
266 * Number description struct
271 int pre, /* (count) numbers before decimal */
272 post, /* (count) numbers after decimal */
273 lsign, /* want locales sign */
274 flag, /* number parameters */
275 pre_lsign_num, /* tmp value for lsign */
276 multi, /* multiplier for 'V' */
277 zero_start, /* position of first zero */
278 zero_end, /* position of last zero */
279 need_locale; /* needs it locale */
283 * Flags for NUMBER version
286 #define NUM_F_DECIMAL (1 << 1)
287 #define NUM_F_LDECIMAL (1 << 2)
288 #define NUM_F_ZERO (1 << 3)
289 #define NUM_F_BLANK (1 << 4)
290 #define NUM_F_FILLMODE (1 << 5)
291 #define NUM_F_LSIGN (1 << 6)
292 #define NUM_F_BRACKET (1 << 7)
293 #define NUM_F_MINUS (1 << 8)
294 #define NUM_F_PLUS (1 << 9)
295 #define NUM_F_ROMAN (1 << 10)
296 #define NUM_F_MULTI (1 << 11)
297 #define NUM_F_PLUS_POST (1 << 12)
298 #define NUM_F_MINUS_POST (1 << 13)
300 #define NUM_LSIGN_PRE (-1)
301 #define NUM_LSIGN_POST 1
302 #define NUM_LSIGN_NONE 0
308 #define IS_DECIMAL(_f) ((_f)->flag & NUM_F_DECIMAL)
309 #define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL)
310 #define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO)
311 #define IS_BLANK(_f) ((_f)->flag & NUM_F_BLANK)
312 #define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE)
313 #define IS_BRACKET(_f) ((_f)->flag & NUM_F_BRACKET)
314 #define IS_MINUS(_f) ((_f)->flag & NUM_F_MINUS)
315 #define IS_LSIGN(_f) ((_f)->flag & NUM_F_LSIGN)
316 #define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
317 #define IS_ROMAN(_f) ((_f)->flag & NUM_F_ROMAN)
318 #define IS_MULTI(_f) ((_f)->flag & NUM_F_MULTI)
321 * Format picture cache
323 * Number part = NUM_CACHE_SIZE * NUM_CACHE_FIELDS
324 * Date-time part = DCH_CACHE_SIZE * DCH_CACHE_FIELDS
328 #define NUM_CACHE_SIZE 64
329 #define NUM_CACHE_FIELDS 16
330 #define DCH_CACHE_SIZE 128
331 #define DCH_CACHE_FIELDS 16
335 FormatNode format[DCH_CACHE_SIZE + 1];
336 char str[DCH_CACHE_SIZE + 1];
342 FormatNode format[NUM_CACHE_SIZE + 1];
343 char str[NUM_CACHE_SIZE + 1];
348 /* global cache for --- date/time part */
349 static DCHCacheEntry DCHCache[DCH_CACHE_FIELDS + 1];
351 static int n_DCHCache = 0; /* number of entries */
352 static int DCHCounter = 0;
354 /* global cache for --- number part */
355 static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1];
356 static NUMCacheEntry *last_NUMCacheEntry;
358 static int n_NUMCache = 0; /* number of entries */
359 static int NUMCounter = 0;
361 #define MAX_INT32 (2147483600)
364 * For char->date/time conversion
389 yysz; /* is it YY or YYYY ? */
392 #define ZERO_tmfc(_X) memset(_X, 0, sizeof(TmFromChar))
398 #ifdef DEBUG_TO_FROM_CHAR
399 #define DEBUG_TMFC(_X) \
400 elog(DEBUG_elog_output, "TMFC:\nhh %d\nam %d\npm %d\nmi %d\nss %d\nssss %d\nd %d\ndd %d\nddd %d\nmm %d\nms: %d\nyear %d\nbc %d\niw %d\nww %d\nw %d\ncc %d\nq %d\nj %d\nus: %d\nyysz: %d", \
401 (_X)->hh, (_X)->am, (_X)->pm, (_X)->mi, (_X)->ss, \
402 (_X)->ssss, (_X)->d, (_X)->dd, (_X)->ddd, (_X)->mm, (_X)->ms, \
403 (_X)->year, (_X)->bc, (_X)->iw, (_X)->ww, (_X)->w, \
404 (_X)->cc, (_X)->q, (_X)->j, (_X)->us, (_X)->yysz);
405 #define DEBUG_TM(_X) \
406 elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\
407 (_X)->tm_sec, (_X)->tm_year,\
408 (_X)->tm_min, (_X)->tm_wday, (_X)->tm_hour, (_X)->tm_yday,\
409 (_X)->tm_mday, (_X)->tm_isdst, (_X)->tm_mon)
411 #define DEBUG_TMFC(_X)
416 * Datetime to char conversion
419 typedef struct TmToChar
421 struct pg_tm tm; /* classic 'tm' struct */
422 fsec_t fsec; /* fractional seconds */
423 char *tzn; /* timezone */
426 #define tmtcTm(_X) (&(_X)->tm)
427 #define tmtcTzn(_X) ((_X)->tzn)
428 #define tmtcFsec(_X) ((_X)->fsec)
430 #define ZERO_tm(_X) \
432 (_X)->tm_sec = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
433 (_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
434 (_X)->tm_mday = (_X)->tm_mon = 1; \
437 #define ZERO_tmtc(_X) \
439 ZERO_tm( tmtcTm(_X) ); \
441 tmtcTzn(_X) = NULL; \
445 * to_char(time) appears to to_char() as an interval, so this check
446 * is really for interval and time data types.
448 #define INVALID_FOR_INTERVAL \
452 (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
453 errmsg("invalid format specification for an interval value"), \
454 errhint("Intervals are not tied to specific calendar dates."))); \
457 /*****************************************************************************
458 * KeyWords definition & action
459 *****************************************************************************/
461 static int dch_global(int arg, char *inout, int suf, bool is_to_char,
462 bool is_interval, FormatNode *node, void *data);
463 static int dch_time(int arg, char *inout, int suf, bool is_to_char,
464 bool is_interval, FormatNode *node, void *data);
465 static int dch_date(int arg, char *inout, int suf, bool is_to_char,
466 bool is_interval, FormatNode *node, void *data);
472 #define DCH_S_FM 0x01
473 #define DCH_S_TH 0x02
474 #define DCH_S_th 0x04
475 #define DCH_S_SP 0x08
476 #define DCH_S_TM 0x10
482 #define S_THth(_s) ((((_s) & DCH_S_TH) || ((_s) & DCH_S_th)) ? 1 : 0)
483 #define S_TH(_s) (((_s) & DCH_S_TH) ? 1 : 0)
484 #define S_th(_s) (((_s) & DCH_S_th) ? 1 : 0)
485 #define S_TH_TYPE(_s) (((_s) & DCH_S_TH) ? TH_UPPER : TH_LOWER)
487 #define S_FM(_s) (((_s) & DCH_S_FM) ? 1 : 0)
488 #define S_SP(_s) (((_s) & DCH_S_SP) ? 1 : 0)
489 #define S_TM(_s) (((_s) & DCH_S_TM) ? 1 : 0)
492 * Suffixes definition for DATE-TIME TO/FROM CHAR
495 static KeySuffix DCH_suff[] = {
496 {"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
497 {"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
498 {"TM", 2, DCH_S_TM, SUFFTYPE_PREFIX},
499 {"tm", 2, DCH_S_TM, SUFFTYPE_PREFIX},
500 {"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
501 {"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
502 {"SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX},
508 * Format-pictures (KeyWord).
510 * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
511 * complicated -to-> easy:
513 * (example: "DDD","DD","Day","D" )
515 * (this specific sort needs the algorithm for sequential search for strings,
516 * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
517 * or "HH12"? You must first try "HH12", because "HH" is in string, but
521 * - Position for the keyword is similar as position in the enum DCH/NUM_poz.
524 * For fast search is used the 'int index[]', index is ascii table from position
525 * 32 (' ') to 126 (~), in this index is DCH_ / NUM_ enums for each ASCII
526 * position or -1 if char is not used in the KeyWord. Search example for
528 * 1) see in index to index['M' - 32],
529 * 2) take keywords position (enum DCH_MM) from index
530 * 3) run sequential search in keywords[] from this position
551 DCH_FX, /* global suffix */
674 * KeyWords for DATE-TIME version
677 static const KeyWord DCH_keywords[] = {
678 /* keyword, len, func, type, isitdigit is in Index */
679 {"A.D.", 4, dch_date, DCH_A_D, FALSE}, /* A */
680 {"A.M.", 4, dch_time, DCH_A_M, FALSE},
681 {"AD", 2, dch_date, DCH_AD, FALSE},
682 {"AM", 2, dch_time, DCH_AM, FALSE},
683 {"B.C.", 4, dch_date, DCH_B_C, FALSE}, /* B */
684 {"BC", 2, dch_date, DCH_BC, FALSE},
685 {"CC", 2, dch_date, DCH_CC, TRUE}, /* C */
686 {"DAY", 3, dch_date, DCH_DAY, FALSE}, /* D */
687 {"DDD", 3, dch_date, DCH_DDD, TRUE},
688 {"DD", 2, dch_date, DCH_DD, TRUE},
689 {"DY", 2, dch_date, DCH_DY, FALSE},
690 {"Day", 3, dch_date, DCH_Day, FALSE},
691 {"Dy", 2, dch_date, DCH_Dy, FALSE},
692 {"D", 1, dch_date, DCH_D, TRUE},
693 {"FX", 2, dch_global, DCH_FX, FALSE}, /* F */
694 {"HH24", 4, dch_time, DCH_HH24, TRUE}, /* H */
695 {"HH12", 4, dch_time, DCH_HH12, TRUE},
696 {"HH", 2, dch_time, DCH_HH, TRUE},
697 {"IW", 2, dch_date, DCH_IW, TRUE}, /* I */
698 {"IYYY", 4, dch_date, DCH_IYYY, TRUE},
699 {"IYY", 3, dch_date, DCH_IYY, TRUE},
700 {"IY", 2, dch_date, DCH_IY, TRUE},
701 {"I", 1, dch_date, DCH_I, TRUE},
702 {"J", 1, dch_date, DCH_J, TRUE}, /* J */
703 {"MI", 2, dch_time, DCH_MI, TRUE},
704 {"MM", 2, dch_date, DCH_MM, TRUE},
705 {"MONTH", 5, dch_date, DCH_MONTH, FALSE},
706 {"MON", 3, dch_date, DCH_MON, FALSE},
707 {"MS", 2, dch_time, DCH_MS, TRUE},
708 {"Month", 5, dch_date, DCH_Month, FALSE},
709 {"Mon", 3, dch_date, DCH_Mon, FALSE},
710 {"P.M.", 4, dch_time, DCH_P_M, FALSE}, /* P */
711 {"PM", 2, dch_time, DCH_PM, FALSE},
712 {"Q", 1, dch_date, DCH_Q, TRUE}, /* Q */
713 {"RM", 2, dch_date, DCH_RM, FALSE}, /* R */
714 {"SSSS", 4, dch_time, DCH_SSSS, TRUE}, /* S */
715 {"SS", 2, dch_time, DCH_SS, TRUE},
716 {"TZ", 2, dch_time, DCH_TZ, FALSE}, /* T */
717 {"US", 2, dch_time, DCH_US, TRUE}, /* U */
718 {"WW", 2, dch_date, DCH_WW, TRUE}, /* W */
719 {"W", 1, dch_date, DCH_W, TRUE},
720 {"Y,YYY", 5, dch_date, DCH_Y_YYY, TRUE}, /* Y */
721 {"YYYY", 4, dch_date, DCH_YYYY, TRUE},
722 {"YYY", 3, dch_date, DCH_YYY, TRUE},
723 {"YY", 2, dch_date, DCH_YY, TRUE},
724 {"Y", 1, dch_date, DCH_Y, TRUE},
725 {"a.d.", 4, dch_date, DCH_a_d, FALSE}, /* a */
726 {"a.m.", 4, dch_time, DCH_a_m, FALSE},
727 {"ad", 2, dch_date, DCH_ad, FALSE},
728 {"am", 2, dch_time, DCH_am, FALSE},
729 {"b.c.", 4, dch_date, DCH_b_c, FALSE}, /* b */
730 {"bc", 2, dch_date, DCH_bc, FALSE},
731 {"cc", 2, dch_date, DCH_CC, TRUE}, /* c */
732 {"day", 3, dch_date, DCH_day, FALSE}, /* d */
733 {"ddd", 3, dch_date, DCH_DDD, TRUE},
734 {"dd", 2, dch_date, DCH_DD, TRUE},
735 {"dy", 2, dch_date, DCH_dy, FALSE},
736 {"d", 1, dch_date, DCH_D, TRUE},
737 {"fx", 2, dch_global, DCH_FX, FALSE}, /* f */
738 {"hh24", 4, dch_time, DCH_HH24, TRUE}, /* h */
739 {"hh12", 4, dch_time, DCH_HH12, TRUE},
740 {"hh", 2, dch_time, DCH_HH, TRUE},
741 {"iw", 2, dch_date, DCH_IW, TRUE}, /* i */
742 {"iyyy", 4, dch_date, DCH_IYYY, TRUE},
743 {"iyy", 3, dch_date, DCH_IYY, TRUE},
744 {"iy", 2, dch_date, DCH_IY, TRUE},
745 {"i", 1, dch_date, DCH_I, TRUE},
746 {"j", 1, dch_time, DCH_J, TRUE}, /* j */
747 {"mi", 2, dch_time, DCH_MI, TRUE}, /* m */
748 {"mm", 2, dch_date, DCH_MM, TRUE},
749 {"month", 5, dch_date, DCH_month, FALSE},
750 {"mon", 3, dch_date, DCH_mon, FALSE},
751 {"ms", 2, dch_time, DCH_MS, TRUE},
752 {"p.m.", 4, dch_time, DCH_p_m, FALSE}, /* p */
753 {"pm", 2, dch_time, DCH_pm, FALSE},
754 {"q", 1, dch_date, DCH_Q, TRUE}, /* q */
755 {"rm", 2, dch_date, DCH_rm, FALSE}, /* r */
756 {"ssss", 4, dch_time, DCH_SSSS, TRUE}, /* s */
757 {"ss", 2, dch_time, DCH_SS, TRUE},
758 {"tz", 2, dch_time, DCH_tz, FALSE}, /* t */
759 {"us", 2, dch_time, DCH_US, TRUE}, /* u */
760 {"ww", 2, dch_date, DCH_WW, TRUE}, /* w */
761 {"w", 1, dch_date, DCH_W, TRUE},
762 {"y,yyy", 5, dch_date, DCH_Y_YYY, TRUE}, /* y */
763 {"yyyy", 4, dch_date, DCH_YYYY, TRUE},
764 {"yyy", 3, dch_date, DCH_YYY, TRUE},
765 {"yy", 2, dch_date, DCH_YY, TRUE},
766 {"y", 1, dch_date, DCH_Y, TRUE},
771 * KeyWords for NUMBER version (now, isitdigit info is not needful here..)
774 static const KeyWord NUM_keywords[] = {
775 /* keyword, len, func. type is in Index */
776 {",", 1, NULL, NUM_COMMA}, /* , */
777 {".", 1, NULL, NUM_DEC}, /* . */
778 {"0", 1, NULL, NUM_0}, /* 0 */
779 {"9", 1, NULL, NUM_9}, /* 9 */
780 {"B", 1, NULL, NUM_B}, /* B */
781 {"C", 1, NULL, NUM_C}, /* C */
782 {"D", 1, NULL, NUM_D}, /* D */
783 {"E", 1, NULL, NUM_E}, /* E */
784 {"FM", 2, NULL, NUM_FM}, /* F */
785 {"G", 1, NULL, NUM_G}, /* G */
786 {"L", 1, NULL, NUM_L}, /* L */
787 {"MI", 2, NULL, NUM_MI}, /* M */
788 {"PL", 2, NULL, NUM_PL}, /* P */
789 {"PR", 2, NULL, NUM_PR},
790 {"RN", 2, NULL, NUM_RN}, /* R */
791 {"SG", 2, NULL, NUM_SG}, /* S */
792 {"SP", 2, NULL, NUM_SP},
793 {"S", 1, NULL, NUM_S},
794 {"TH", 2, NULL, NUM_TH}, /* T */
795 {"V", 1, NULL, NUM_V}, /* V */
796 {"b", 1, NULL, NUM_B}, /* b */
797 {"c", 1, NULL, NUM_C}, /* c */
798 {"d", 1, NULL, NUM_D}, /* d */
799 {"e", 1, NULL, NUM_E}, /* e */
800 {"fm", 2, NULL, NUM_FM}, /* f */
801 {"g", 1, NULL, NUM_G}, /* g */
802 {"l", 1, NULL, NUM_L}, /* l */
803 {"mi", 2, NULL, NUM_MI}, /* m */
804 {"pl", 2, NULL, NUM_PL}, /* p */
805 {"pr", 2, NULL, NUM_PR},
806 {"rn", 2, NULL, NUM_rn}, /* r */
807 {"sg", 2, NULL, NUM_SG}, /* s */
808 {"sp", 2, NULL, NUM_SP},
809 {"s", 1, NULL, NUM_S},
810 {"th", 2, NULL, NUM_th}, /* t */
811 {"v", 1, NULL, NUM_V}, /* v */
818 * KeyWords index for DATE-TIME version
821 static const int DCH_index[KeyWord_INDEX_SIZE] = {
825 /*---- first 0..31 chars are skipped ----*/
827 -1, -1, -1, -1, -1, -1, -1, -1,
828 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
829 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
830 -1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1,
831 DCH_FX, -1, DCH_HH24, DCH_IW, DCH_J, -1, -1, DCH_MI, -1, -1,
832 DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, DCH_TZ, DCH_US, -1, DCH_WW, -1, DCH_Y_YYY,
833 -1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc,
834 DCH_day, -1, DCH_fx, -1, DCH_hh24, DCH_iw, DCH_j, -1, -1, DCH_mi,
835 -1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, DCH_us, -1, DCH_ww,
836 -1, DCH_y_yyy, -1, -1, -1, -1
838 /*---- chars over 126 are skiped ----*/
842 * KeyWords index for NUMBER version
845 static const int NUM_index[KeyWord_INDEX_SIZE] = {
849 /*---- first 0..31 chars are skiped ----*/
851 -1, -1, -1, -1, -1, -1, -1, -1,
852 -1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1,
853 -1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1,
854 -1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E,
855 NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1,
856 NUM_PL, -1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1,
857 -1, -1, -1, -1, -1, -1, -1, -1, NUM_b, NUM_c,
858 NUM_d, NUM_e, NUM_fm, NUM_g, -1, -1, -1, -1, NUM_l, NUM_mi,
859 -1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1,
860 -1, -1, -1, -1, -1, -1
862 /*---- chars over 126 are skiped ----*/
866 * Number processor struct
869 typedef struct NUMProc
872 NUMDesc *Num; /* number description */
874 int sign, /* '-' or '+' */
875 sign_wrote, /* was sign write */
876 num_count, /* number of write digits */
877 num_in, /* is inside number */
878 num_curr, /* current position in number */
879 num_pre, /* space before first number */
881 read_dec, /* to_number - was read dec. point */
882 read_post, /* to_number - number of dec. digit */
883 read_pre; /* to_number - number non-dec. digit */
885 char *number, /* string with number */
886 *number_p, /* pointer to current number position */
887 *inout, /* in / out buffer */
888 *inout_p, /* pointer to current inout position */
889 *last_relevant, /* last relevant number after decimal point */
891 *L_negative_sign, /* Locale */
903 static const KeyWord *index_seq_search(char *str, const KeyWord *kw,
905 static KeySuffix *suff_search(char *str, KeySuffix *suf, int type);
906 static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
907 static void parse_format(FormatNode *node, char *str, const KeyWord *kw,
908 KeySuffix *suf, const int *index, int ver, NUMDesc *Num);
909 static char *DCH_processor(FormatNode *node, char *inout, bool is_to_char,
910 bool is_interval, void *data);
912 #ifdef DEBUG_TO_FROM_CHAR
913 static void dump_index(const KeyWord *k, const int *index);
914 static void dump_node(FormatNode *node, int max);
917 static char *get_th(char *num, int type);
918 static char *str_numth(char *dest, char *num, int type);
919 static int strdigits_len(char *str);
920 static char *str_toupper(char *buff);
921 static char *str_tolower(char *buff);
923 /* static int is_acdc(char *str, int *len); */
924 static int seq_search(char *name, char **array, int type, int max, int *len);
925 static void do_to_timestamp(text *date_txt, text *fmt,
926 struct pg_tm * tm, fsec_t *fsec);
927 static char *fill_str(char *str, int c, int max);
928 static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, bool *shouldFree);
929 static char *int_to_roman(int number);
930 static void NUM_prepare_locale(NUMProc *Np);
931 static char *get_last_relevant_decnum(char *num);
932 static void NUM_numpart_from_char(NUMProc *Np, int id, int plen);
933 static void NUM_numpart_to_char(NUMProc *Np, int id);
934 static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
935 int plen, int sign, bool is_to_char);
936 static DCHCacheEntry *DCH_cache_search(char *str);
937 static DCHCacheEntry *DCH_cache_getnew(char *str);
939 static NUMCacheEntry *NUM_cache_search(char *str);
940 static NUMCacheEntry *NUM_cache_getnew(char *str);
941 static void NUM_cache_remove(NUMCacheEntry *ent);
943 static char *localize_month_full(int index);
944 static char *localize_month(int index);
945 static char *localize_day_full(int index);
946 static char *localize_day(int index);
949 * Fast sequential search, use index for data selection which
950 * go to seq. cycle (it is very fast for unwanted strings)
951 * (can't be used binary search in format parsing)
954 static const KeyWord *
955 index_seq_search(char *str, const KeyWord *kw, const int *index)
959 if (!KeyWord_INDEX_FILTER(*str))
962 if ((poz = *(index + (*str - ' '))) > -1)
964 const KeyWord *k = kw + poz;
968 if (!strncmp(str, k->name, k->len))
973 } while (*str == *k->name);
979 suff_search(char *str, KeySuffix *suf, int type)
983 for (s = suf; s->name != NULL; s++)
988 if (!strncmp(str, s->name, s->len))
995 * Prepare NUMDesc (number description struct) via FormatNode struct
999 NUMDesc_prepare(NUMDesc *num, FormatNode *n)
1002 if (n->type != NODE_TYPE_ACTION)
1008 if (IS_BRACKET(num))
1010 NUM_cache_remove(last_NUMCacheEntry);
1012 (errcode(ERRCODE_SYNTAX_ERROR),
1013 errmsg("\"9\" must be ahead of \"PR\"")));
1020 if (IS_DECIMAL(num))
1027 if (IS_BRACKET(num))
1029 NUM_cache_remove(last_NUMCacheEntry);
1031 (errcode(ERRCODE_SYNTAX_ERROR),
1032 errmsg("\"0\" must be ahead of \"PR\"")));
1034 if (!IS_ZERO(num) && !IS_DECIMAL(num))
1036 num->flag |= NUM_F_ZERO;
1037 num->zero_start = num->pre + 1;
1039 if (!IS_DECIMAL(num))
1044 num->zero_end = num->pre + num->post;
1048 if (num->pre == 0 && num->post == 0 && (!IS_ZERO(num)))
1049 num->flag |= NUM_F_BLANK;
1053 num->flag |= NUM_F_LDECIMAL;
1054 num->need_locale = TRUE;
1056 if (IS_DECIMAL(num))
1058 NUM_cache_remove(last_NUMCacheEntry);
1060 (errcode(ERRCODE_SYNTAX_ERROR),
1061 errmsg("multiple decimal points")));
1065 NUM_cache_remove(last_NUMCacheEntry);
1067 (errcode(ERRCODE_SYNTAX_ERROR),
1068 errmsg("cannot use \"V\" and decimal point together")));
1070 num->flag |= NUM_F_DECIMAL;
1074 num->flag |= NUM_F_FILLMODE;
1080 NUM_cache_remove(last_NUMCacheEntry);
1082 (errcode(ERRCODE_SYNTAX_ERROR),
1083 errmsg("not unique \"S\"")));
1085 if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
1087 NUM_cache_remove(last_NUMCacheEntry);
1089 (errcode(ERRCODE_SYNTAX_ERROR),
1090 errmsg("cannot use \"S\" and \"PL\"/\"MI\"/\"SG\"/\"PR\" together")));
1092 if (!IS_DECIMAL(num))
1094 num->lsign = NUM_LSIGN_PRE;
1095 num->pre_lsign_num = num->pre;
1096 num->need_locale = TRUE;
1097 num->flag |= NUM_F_LSIGN;
1100 else if (num->lsign == NUM_LSIGN_NONE)
1102 num->lsign = NUM_LSIGN_POST;
1103 num->need_locale = TRUE;
1104 num->flag |= NUM_F_LSIGN;
1111 NUM_cache_remove(last_NUMCacheEntry);
1113 (errcode(ERRCODE_SYNTAX_ERROR),
1114 errmsg("cannot use \"S\" and \"MI\" together")));
1116 num->flag |= NUM_F_MINUS;
1117 if (IS_DECIMAL(num))
1118 num->flag |= NUM_F_MINUS_POST;
1124 NUM_cache_remove(last_NUMCacheEntry);
1126 (errcode(ERRCODE_SYNTAX_ERROR),
1127 errmsg("cannot use \"S\" and \"PL\" together")));
1129 num->flag |= NUM_F_PLUS;
1130 if (IS_DECIMAL(num))
1131 num->flag |= NUM_F_PLUS_POST;
1137 NUM_cache_remove(last_NUMCacheEntry);
1139 (errcode(ERRCODE_SYNTAX_ERROR),
1140 errmsg("cannot use \"S\" and \"SG\" together")));
1142 num->flag |= NUM_F_MINUS;
1143 num->flag |= NUM_F_PLUS;
1147 if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num))
1149 NUM_cache_remove(last_NUMCacheEntry);
1151 (errcode(ERRCODE_SYNTAX_ERROR),
1152 errmsg("cannot use \"PR\" and \"S\"/\"PL\"/\"MI\"/\"SG\" together")));
1154 num->flag |= NUM_F_BRACKET;
1159 num->flag |= NUM_F_ROMAN;
1164 num->need_locale = TRUE;
1168 if (IS_DECIMAL(num))
1170 NUM_cache_remove(last_NUMCacheEntry);
1172 (errcode(ERRCODE_SYNTAX_ERROR),
1173 errmsg("cannot use \"V\" and decimal point together")));
1175 num->flag |= NUM_F_MULTI;
1179 NUM_cache_remove(last_NUMCacheEntry);
1181 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1182 errmsg("\"E\" is not supported")));
1189 * Format parser, search small keywords and keyword's suffixes, and make
1192 * for DATE-TIME & NUMBER version
1196 parse_format(FormatNode *node, char *str, const KeyWord *kw,
1197 KeySuffix *suf, const int *index, int ver, NUMDesc *Num)
1205 #ifdef DEBUG_TO_FROM_CHAR
1206 elog(DEBUG_elog_output, "to_char/number(): run parser");
1218 if (ver == DCH_TYPE && (s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL)
1228 if (*str && (n->key = index_seq_search(str, kw, index)) != NULL)
1231 n->type = NODE_TYPE_ACTION;
1238 * NUM version: Prepare global NUMDesc struct
1240 if (ver == NUM_TYPE)
1241 NUMDesc_prepare(Num, n);
1246 if (ver == DCH_TYPE && *str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL)
1257 * Special characters '\' and '"'
1259 if (*str == '"' && last != '\\')
1266 if (*str == '"' && x != '\\')
1271 else if (*str == '\\' && x != '\\')
1276 n->type = NODE_TYPE_CHAR;
1277 n->character = *str;
1288 else if (*str && *str == '\\' && last != '\\' && *(str + 1) == '"')
1296 n->type = NODE_TYPE_CHAR;
1297 n->character = *str;
1309 if (n->type == NODE_TYPE_ACTION)
1319 n->type = NODE_TYPE_END;
1325 * Call keyword's function for each of (action) node in format-node tree
1329 DCH_processor(FormatNode *node, char *inout, bool is_to_char,
1330 bool is_interval, void *data)
1336 * Zeroing global flags
1338 DCH_global_fx = false;
1340 for (n = node, s = inout; n->type != NODE_TYPE_END; n++)
1342 if (!is_to_char && *s == '\0')
1345 * The input string is shorter than format picture, so it's good
1346 * time to break this loop...
1348 * Note: this isn't relevant for TO_CHAR mode, because it uses
1349 * 'inout' allocated by format picture length.
1353 if (n->type == NODE_TYPE_ACTION)
1358 * Call node action function
1360 len = n->key->action(n->key->id, s, n->suffix, is_to_char,
1361 is_interval, n, data);
1363 s += len - 1; /* s++ is at the end of the loop */
1370 * Remove to output char from input in TO_CHAR
1377 * Skip blank space in FROM_CHAR's input
1379 if (isspace((unsigned char) n->character) && !DCH_global_fx)
1380 while (*s != '\0' && isspace((unsigned char) *(s + 1)))
1394 * DEBUG: Dump the FormatNode Tree (debug)
1397 #ifdef DEBUG_TO_FROM_CHAR
1399 #define DUMP_THth(_suf) (S_TH(_suf) ? "TH" : (S_th(_suf) ? "th" : " "))
1400 #define DUMP_FM(_suf) (S_FM(_suf) ? "FM" : " ")
1403 dump_node(FormatNode *node, int max)
1408 elog(DEBUG_elog_output, "to_from-char(): DUMP FORMAT");
1410 for (a = 0, n = node; a <= max; n++, a++)
1412 if (n->type == NODE_TYPE_ACTION)
1413 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_ACTION '%s'\t(%s,%s)",
1414 a, n->key->name, DUMP_THth(n->suffix), DUMP_FM(n->suffix));
1415 else if (n->type == NODE_TYPE_CHAR)
1416 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_CHAR '%c'", a, n->character);
1417 else if (n->type == NODE_TYPE_END)
1419 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_END", a);
1423 elog(DEBUG_elog_output, "%d:\t unknown NODE!", a);
1429 /*****************************************************************************
1431 *****************************************************************************/
1434 * Return ST/ND/RD/TH for simple (1..9) numbers
1435 * type --> 0 upper, 1 lower
1439 get_th(char *num, int type)
1441 int len = strlen(num),
1445 last = *(num + (len - 1));
1446 if (!isdigit((unsigned char) last))
1448 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1449 errmsg("\"%s\" is not a number", num)));
1452 * All "teens" (<x>1[0-9]) get 'TH/th', while <x>[02-9][123] still get
1453 * 'ST/st', 'ND/nd', 'RD/rd', respectively
1455 if ((len > 1) && ((seclast = num[len - 2]) == '1'))
1461 if (type == TH_UPPER)
1465 if (type == TH_UPPER)
1469 if (type == TH_UPPER)
1473 if (type == TH_UPPER)
1481 * Convert string-number to ordinal string-number
1482 * type --> 0 upper, 1 lower
1486 str_numth(char *dest, char *num, int type)
1490 strcat(dest, get_th(num, type));
1495 * Convert string to upper-string. Input string is modified in place.
1499 str_toupper(char *buff)
1501 char *p_buff = buff;
1508 *p_buff = pg_toupper((unsigned char) *p_buff);
1515 * Convert string to lower-string. Input string is modified in place.
1519 str_tolower(char *buff)
1521 char *p_buff = buff;
1528 *p_buff = pg_tolower((unsigned char) *p_buff);
1535 * Sequential search with to upper/lower conversion
1539 seq_search(char *name, char **array, int type, int max, int *len)
1552 /* set first char */
1553 if (type == ONE_UPPER || type == ALL_UPPER)
1554 *name = pg_toupper((unsigned char) *name);
1555 else if (type == ALL_LOWER)
1556 *name = pg_tolower((unsigned char) *name);
1558 for (last = 0, a = array; *a != NULL; a++)
1561 /* comperate first chars */
1565 for (i = 1, p = *a + 1, n = name + 1;; n++, p++, i++)
1568 /* search fragment (max) only */
1569 if (max && i == max)
1580 /* Not found in array 'a' */
1585 * Convert (but convert new chars only)
1589 if (type == ONE_UPPER || type == ALL_LOWER)
1590 *n = pg_tolower((unsigned char) *n);
1591 else if (type == ALL_UPPER)
1592 *n = pg_toupper((unsigned char) *n);
1596 #ifdef DEBUG_TO_FROM_CHAR
1599 * elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)", *n, *p, *a,
1612 #ifdef DEBUG_TO_FROM_CHAR
1614 * DEBUG: Call for debug and for index checking; (Show ASCII char
1615 * and defined keyword for each used position
1619 dump_index(const KeyWord *k, const int *index)
1625 elog(DEBUG_elog_output, "TO-FROM_CHAR: Dump KeyWord Index:");
1627 for (i = 0; i < KeyWord_INDEX_SIZE; i++)
1631 elog(DEBUG_elog_output, "\t%c: %s, ", i + 32, k[index[i]].name);
1637 elog(DEBUG_elog_output, "\t(%d) %c %d", i, i + 32, index[i]);
1640 elog(DEBUG_elog_output, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d",
1646 * Skip TM / th in FROM_CHAR
1649 #define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
1653 * Global format option for DCH version
1657 dch_global(int arg, char *inout, int suf, bool is_to_char, bool is_interval,
1658 FormatNode *node, void *data)
1661 DCH_global_fx = true;
1666 * Return TRUE if next format picture is not digit value
1670 is_next_separator(FormatNode *n)
1672 if (n->type == NODE_TYPE_END)
1675 if (n->type == NODE_TYPE_ACTION && S_THth(n->suffix))
1683 if (n->type == NODE_TYPE_END)
1686 if (n->type == NODE_TYPE_ACTION)
1688 if (n->key->isitdigit)
1693 else if (isdigit((unsigned char) n->character))
1696 return TRUE; /* some non-digit input (separator) */
1700 strdigits_len(char *str)
1705 while (*p && isdigit((unsigned char) *p) && len <= DCH_MAX_ITEM_SIZ)
1713 #define AMPM_ERROR ereport(ERROR, \
1714 (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
1715 errmsg("invalid AM/PM string")));
1718 * Master function of TIME for:
1719 * TO_CHAR - write (inout) formated string
1720 * FROM_CHAR - scan (inout) string by course of FormatNode
1724 dch_time(int arg, char *inout, int suf, bool is_to_char, bool is_interval,
1725 FormatNode *node, void *data)
1727 char *p_inout = inout;
1728 struct pg_tm *tm = NULL;
1729 TmFromChar *tmfc = NULL;
1730 TmToChar *tmtc = NULL;
1734 tmtc = (TmToChar *) data;
1738 tmfc = (TmFromChar *) data;
1746 strcpy(inout, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
1747 ? P_M_STR : A_M_STR);
1748 return strlen(p_inout);
1752 if (strncmp(inout, P_M_STR, 4) == 0)
1754 else if (strncmp(inout, A_M_STR, 4) == 0)
1758 return strlen(p_inout);
1765 strcpy(inout, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
1767 return strlen(p_inout);
1771 if (strncmp(inout, PM_STR, 2) == 0)
1773 else if (strncmp(inout, AM_STR, 2) == 0)
1777 return strlen(p_inout);
1784 strcpy(inout, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
1785 ? p_m_STR : a_m_STR);
1786 return strlen(p_inout);
1790 if (strncmp(inout, p_m_STR, 4) == 0)
1792 else if (strncmp(inout, a_m_STR, 4) == 0)
1796 return strlen(p_inout);
1803 strcpy(inout, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
1805 return strlen(p_inout);
1809 if (strncmp(inout, pm_STR, 2) == 0)
1811 else if (strncmp(inout, am_STR, 2) == 0)
1815 return strlen(p_inout);
1822 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
1823 tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ? 12 :
1824 tm->tm_hour % (HOURS_PER_DAY / 2));
1826 str_numth(p_inout, inout, 0);
1827 return strlen(p_inout);
1831 if (S_FM(suf) || is_next_separator(node))
1833 sscanf(inout, "%d", &tmfc->hh);
1834 return strdigits_len(inout) + SKIP_THth(suf);
1838 sscanf(inout, "%02d", &tmfc->hh);
1839 return 2 + SKIP_THth(suf);
1846 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_hour);
1848 str_numth(p_inout, inout, S_TH_TYPE(suf));
1849 return strlen(p_inout);
1853 if (S_FM(suf) || is_next_separator(node))
1855 sscanf(inout, "%d", &tmfc->hh);
1856 return strdigits_len(inout) + SKIP_THth(suf);
1860 sscanf(inout, "%02d", &tmfc->hh);
1861 return 2 + SKIP_THth(suf);
1868 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_min);
1870 str_numth(p_inout, inout, S_TH_TYPE(suf));
1871 return strlen(p_inout);
1875 if (S_FM(suf) || is_next_separator(node))
1877 sscanf(inout, "%d", &tmfc->mi);
1878 return strdigits_len(inout) + SKIP_THth(suf);
1882 sscanf(inout, "%02d", &tmfc->mi);
1883 return 2 + SKIP_THth(suf);
1890 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_sec);
1892 str_numth(p_inout, inout, S_TH_TYPE(suf));
1893 return strlen(p_inout);
1897 if (S_FM(suf) || is_next_separator(node))
1899 sscanf(inout, "%d", &tmfc->ss);
1900 return strdigits_len(inout) + SKIP_THth(suf);
1904 sscanf(inout, "%02d", &tmfc->ss);
1905 return 2 + SKIP_THth(suf);
1909 case DCH_MS: /* millisecond */
1912 #ifdef HAVE_INT64_TIMESTAMP
1913 sprintf(inout, "%03d", (int) (tmtc->fsec / INT64CONST(1000)));
1915 sprintf(inout, "%03d", (int) rint(tmtc->fsec * 1000));
1918 str_numth(p_inout, inout, S_TH_TYPE(suf));
1919 return strlen(p_inout);
1926 if (is_next_separator(node))
1928 sscanf(inout, "%d", &tmfc->ms);
1929 len = x = strdigits_len(inout);
1933 sscanf(inout, "%03d", &tmfc->ms);
1934 x = strdigits_len(inout);
1935 len = x = x > 3 ? 3 : x;
1939 * 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
1941 tmfc->ms *= x == 1 ? 100 :
1945 * elog(DEBUG3, "X: %d, MS: %d, LEN: %d", x, tmfc->ms, len);
1947 return len + SKIP_THth(suf);
1950 case DCH_US: /* microsecond */
1953 #ifdef HAVE_INT64_TIMESTAMP
1954 sprintf(inout, "%06d", (int) tmtc->fsec);
1956 sprintf(inout, "%06d", (int) rint(tmtc->fsec * 1000000));
1959 str_numth(p_inout, inout, S_TH_TYPE(suf));
1960 return strlen(p_inout);
1967 if (is_next_separator(node))
1969 sscanf(inout, "%d", &tmfc->us);
1970 len = x = strdigits_len(inout);
1974 sscanf(inout, "%06d", &tmfc->us);
1975 x = strdigits_len(inout);
1976 len = x = x > 6 ? 6 : x;
1979 tmfc->us *= x == 1 ? 100000 :
1986 * elog(DEBUG3, "X: %d, US: %d, LEN: %d", x, tmfc->us, len);
1988 return len + SKIP_THth(suf);
1994 sprintf(inout, "%d", tm->tm_hour * SECS_PER_HOUR +
1995 tm->tm_min * SECS_PER_MINUTE +
1998 str_numth(p_inout, inout, S_TH_TYPE(suf));
1999 return strlen(p_inout);
2003 if (S_FM(suf) || is_next_separator(node))
2005 sscanf(inout, "%d", &tmfc->ssss);
2006 return strdigits_len(inout) + SKIP_THth(suf);
2010 sscanf(inout, "%05d", &tmfc->ssss);
2011 return 5 + SKIP_THth(suf);
2017 INVALID_FOR_INTERVAL;
2018 if (is_to_char && tmtcTzn(tmtc))
2020 int siz = strlen(tmtcTzn(tmtc));
2023 strcpy(inout, tmtcTzn(tmtc));
2026 char *p = palloc(siz);
2028 strcpy(p, tmtcTzn(tmtc));
2029 strcpy(inout, str_tolower(p));
2034 else if (!is_to_char)
2036 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2037 errmsg("\"TZ\"/\"tz\" not supported")));
2042 #define CHECK_SEQ_SEARCH(_l, _s) \
2046 (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
2047 errmsg("invalid value for %s", (_s)))); \
2053 * Master of DATE for:
2054 * TO_CHAR - write (inout) formated string
2055 * FROM_CHAR - scan (inout) string by course of FormatNode
2059 dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval,
2060 FormatNode *node, void *data)
2062 char buff[DCH_CACHE_SIZE],
2067 struct pg_tm *tm = NULL;
2068 TmFromChar *tmfc = NULL;
2069 TmToChar *tmtc = NULL;
2073 tmtc = (TmToChar *) data;
2077 tmfc = (TmFromChar *) data;
2080 * In the FROM-char there is no difference between "January" or "JANUARY" or
2081 * "january", all is before search convert to "first-upper". This
2082 * convention is used for MONTH, MON, DAY, DY
2086 if (arg == DCH_MONTH || arg == DCH_Month || arg == DCH_month)
2088 tmfc->mm = seq_search(inout, months_full, ONE_UPPER, FULL_SIZ, &len) + 1;
2089 CHECK_SEQ_SEARCH(len, "MONTH/Month/month");
2095 else if (arg == DCH_MON || arg == DCH_Mon || arg == DCH_mon)
2097 tmfc->mm = seq_search(inout, months, ONE_UPPER, MAX_MON_LEN, &len) + 1;
2098 CHECK_SEQ_SEARCH(len, "MON/Mon/mon");
2101 else if (arg == DCH_DAY || arg == DCH_Day || arg == DCH_day)
2103 tmfc->d = seq_search(inout, days, ONE_UPPER, FULL_SIZ, &len);
2104 CHECK_SEQ_SEARCH(len, "DAY/Day/day");
2110 else if (arg == DCH_DY || arg == DCH_Dy || arg == DCH_dy)
2112 tmfc->d = seq_search(inout, days, ONE_UPPER, MAX_DY_LEN, &len);
2113 CHECK_SEQ_SEARCH(len, "DY/Dy/dy");
2122 INVALID_FOR_INTERVAL;
2125 strcpy(inout, (tm->tm_year <= 0 ? B_C_STR : A_D_STR));
2126 return strlen(p_inout);
2130 if (strncmp(inout, B_C_STR, 4) == 0)
2137 INVALID_FOR_INTERVAL;
2140 strcpy(inout, (tm->tm_year <= 0 ? BC_STR : AD_STR));
2141 return strlen(p_inout);
2145 if (strncmp(inout, BC_STR, 2) == 0)
2152 INVALID_FOR_INTERVAL;
2155 strcpy(inout, (tm->tm_year <= 0 ? b_c_STR : a_d_STR));
2156 return strlen(p_inout);
2160 if (strncmp(inout, b_c_STR, 4) == 0)
2167 INVALID_FOR_INTERVAL;
2170 strcpy(inout, (tm->tm_year <= 0 ? bc_STR : ad_STR));
2171 return strlen(p_inout);
2175 if (strncmp(inout, bc_STR, 2) == 0)
2181 INVALID_FOR_INTERVAL;
2185 strcpy(workbuff, localize_month_full(tm->tm_mon - 1));
2187 strcpy(workbuff, months_full[tm->tm_mon - 1]);
2188 sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, str_toupper(workbuff));
2189 return strlen(p_inout);
2192 INVALID_FOR_INTERVAL;
2196 sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1));
2198 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
2199 return strlen(p_inout);
2202 INVALID_FOR_INTERVAL;
2206 sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1));
2208 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
2209 *inout = pg_tolower((unsigned char) *inout);
2210 return strlen(p_inout);
2213 INVALID_FOR_INTERVAL;
2217 strcpy(inout, localize_month(tm->tm_mon - 1));
2219 strcpy(inout, months[tm->tm_mon - 1]);
2221 return strlen(p_inout);
2224 INVALID_FOR_INTERVAL;
2228 strcpy(inout, localize_month(tm->tm_mon - 1));
2230 strcpy(inout, months[tm->tm_mon - 1]);
2231 return strlen(p_inout);
2234 INVALID_FOR_INTERVAL;
2238 strcpy(inout, localize_month(tm->tm_mon - 1));
2240 strcpy(inout, months[tm->tm_mon - 1]);
2241 *inout = pg_tolower((unsigned char) *inout);
2242 return strlen(p_inout);
2247 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mon);
2249 str_numth(p_inout, inout, S_TH_TYPE(suf));
2250 return strlen(p_inout);
2254 if (S_FM(suf) || is_next_separator(node))
2256 sscanf(inout, "%d", &tmfc->mm);
2257 return strdigits_len(inout) + SKIP_THth(suf);
2261 sscanf(inout, "%02d", &tmfc->mm);
2262 return 2 + SKIP_THth(suf);
2267 INVALID_FOR_INTERVAL;
2269 strcpy(workbuff, localize_day_full(tm->tm_wday));
2271 strcpy(workbuff, days[tm->tm_wday]);
2272 sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, str_toupper(workbuff));
2273 return strlen(p_inout);
2276 INVALID_FOR_INTERVAL;
2278 sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday));
2280 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
2281 return strlen(p_inout);
2284 INVALID_FOR_INTERVAL;
2286 sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday));
2288 sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
2289 *inout = pg_tolower((unsigned char) *inout);
2290 return strlen(p_inout);
2293 INVALID_FOR_INTERVAL;
2295 strcpy(inout, localize_day(tm->tm_wday));
2297 strcpy(inout, days_short[tm->tm_wday]);
2299 return strlen(p_inout);
2302 INVALID_FOR_INTERVAL;
2304 strcpy(inout, localize_day(tm->tm_wday));
2306 strcpy(inout, days_short[tm->tm_wday]);
2307 return strlen(p_inout);
2310 INVALID_FOR_INTERVAL;
2312 strcpy(inout, localize_day(tm->tm_wday));
2314 strcpy(inout, days_short[tm->tm_wday]);
2315 *inout = pg_tolower((unsigned char) *inout);
2316 return strlen(p_inout);
2321 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 3, tm->tm_yday);
2323 str_numth(p_inout, inout, S_TH_TYPE(suf));
2324 return strlen(p_inout);
2328 if (S_FM(suf) || is_next_separator(node))
2330 sscanf(inout, "%d", &tmfc->ddd);
2331 return strdigits_len(inout) + SKIP_THth(suf);
2335 sscanf(inout, "%03d", &tmfc->ddd);
2336 return 3 + SKIP_THth(suf);
2343 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mday);
2345 str_numth(p_inout, inout, S_TH_TYPE(suf));
2346 return strlen(p_inout);
2350 if (S_FM(suf) || is_next_separator(node))
2352 sscanf(inout, "%d", &tmfc->dd);
2353 return strdigits_len(inout) + SKIP_THth(suf);
2357 sscanf(inout, "%02d", &tmfc->dd);
2358 return 2 + SKIP_THth(suf);
2365 sprintf(inout, "%d", tm->tm_wday + 1);
2367 str_numth(p_inout, inout, S_TH_TYPE(suf));
2368 return strlen(p_inout);
2372 sscanf(inout, "%1d", &tmfc->d);
2373 return 1 + SKIP_THth(suf);
2379 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
2380 (tm->tm_yday - 1) / 7 + 1);
2382 str_numth(p_inout, inout, S_TH_TYPE(suf));
2383 return strlen(p_inout);
2387 if (S_FM(suf) || is_next_separator(node))
2389 sscanf(inout, "%d", &tmfc->ww);
2390 return strdigits_len(inout) + SKIP_THth(suf);
2394 sscanf(inout, "%02d", &tmfc->ww);
2395 return 2 + SKIP_THth(suf);
2402 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
2403 date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
2405 str_numth(p_inout, inout, S_TH_TYPE(suf));
2406 return strlen(p_inout);
2410 if (S_FM(suf) || is_next_separator(node))
2412 sscanf(inout, "%d", &tmfc->iw);
2413 return strdigits_len(inout) + SKIP_THth(suf);
2417 sscanf(inout, "%02d", &tmfc->iw);
2418 return 2 + SKIP_THth(suf);
2427 sprintf(inout, "%d", (tm->tm_mon - 1) / 3 + 1);
2429 str_numth(p_inout, inout, S_TH_TYPE(suf));
2430 return strlen(p_inout);
2434 sscanf(inout, "%1d", &tmfc->q);
2435 return 1 + SKIP_THth(suf);
2441 i = tm->tm_year / 100 + ((is_interval) ? 0 : 1);
2442 if (i <= 99 && i >= -99)
2443 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, i);
2445 sprintf(inout, "%d", i);
2447 str_numth(p_inout, inout, S_TH_TYPE(suf));
2448 return strlen(p_inout);
2452 if (S_FM(suf) || is_next_separator(node))
2454 sscanf(inout, "%d", &tmfc->cc);
2455 return strdigits_len(inout) + SKIP_THth(suf);
2459 sscanf(inout, "%02d", &tmfc->cc);
2460 return 2 + SKIP_THth(suf);
2467 i = ADJUST_YEAR(tm->tm_year, is_interval) / 1000;
2468 sprintf(inout, "%d,%03d", i, ADJUST_YEAR(tm->tm_year, is_interval) - (i * 1000));
2470 str_numth(p_inout, inout, S_TH_TYPE(suf));
2471 return strlen(p_inout);
2477 sscanf(inout, "%d,%03d", &cc, &tmfc->year);
2478 tmfc->year += (cc * 1000);
2480 return strdigits_len(inout) + 4 + SKIP_THth(suf);
2487 if (tm->tm_year <= 9999 && tm->tm_year >= -9998)
2488 sprintf(inout, "%0*d",
2491 ADJUST_YEAR(tm->tm_year, is_interval) :
2492 ADJUST_YEAR(date2isoyear(
2495 tm->tm_mday), is_interval));
2497 sprintf(inout, "%d",
2499 ADJUST_YEAR(tm->tm_year, is_interval) :
2500 ADJUST_YEAR(date2isoyear(
2503 tm->tm_mday), is_interval));
2505 str_numth(p_inout, inout, S_TH_TYPE(suf));
2506 return strlen(p_inout);
2510 if (S_FM(suf) || is_next_separator(node))
2512 sscanf(inout, "%d", &tmfc->year);
2514 return strdigits_len(inout) + SKIP_THth(suf);
2518 sscanf(inout, "%04d", &tmfc->year);
2520 return 4 + SKIP_THth(suf);
2528 snprintf(buff, sizeof(buff), "%03d",
2530 ADJUST_YEAR(tm->tm_year, is_interval) :
2531 ADJUST_YEAR(date2isoyear(tm->tm_year,
2532 tm->tm_mon, tm->tm_mday),
2535 strcpy(inout, buff + (i - 3));
2537 str_numth(p_inout, inout, S_TH_TYPE(suf));
2538 return strlen(p_inout);
2542 sscanf(inout, "%03d", &tmfc->year);
2545 * 3-digit year: '100' ... '999' = 1100 ... 1999 '000' ...
2546 * '099' = 2000 ... 2099
2548 if (tmfc->year >= 100)
2553 return 3 + SKIP_THth(suf);
2560 snprintf(buff, sizeof(buff), "%02d",
2562 ADJUST_YEAR(tm->tm_year, is_interval) :
2563 ADJUST_YEAR(date2isoyear(tm->tm_year,
2564 tm->tm_mon, tm->tm_mday),
2567 strcpy(inout, buff + (i - 2));
2569 str_numth(p_inout, inout, S_TH_TYPE(suf));
2570 return strlen(p_inout);
2574 sscanf(inout, "%02d", &tmfc->year);
2577 * 2-digit year: '00' ... '69' = 2000 ... 2069 '70' ... '99'
2580 if (tmfc->year < 70)
2585 return 2 + SKIP_THth(suf);
2592 snprintf(buff, sizeof(buff), "%1d",
2594 ADJUST_YEAR(tm->tm_year, is_interval) :
2595 ADJUST_YEAR(date2isoyear(tm->tm_year,
2596 tm->tm_mon, tm->tm_mday),
2599 strcpy(inout, buff + (i - 1));
2601 str_numth(p_inout, inout, S_TH_TYPE(suf));
2602 return strlen(p_inout);
2606 sscanf(inout, "%1d", &tmfc->year);
2609 * 1-digit year: always +2000
2613 return 1 + SKIP_THth(suf);
2621 sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
2622 rm_months_upper[12 - tm->tm_mon]);
2623 return strlen(p_inout);
2627 tmfc->mm = 12 - seq_search(inout, rm_months_upper, ALL_UPPER, FULL_SIZ, &len);
2628 CHECK_SEQ_SEARCH(len, "RM");
2640 sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
2641 rm_months_lower[12 - tm->tm_mon]);
2642 return strlen(p_inout);
2646 tmfc->mm = 12 - seq_search(inout, rm_months_lower, ALL_LOWER, FULL_SIZ, &len);
2647 CHECK_SEQ_SEARCH(len, "rm");
2657 sprintf(inout, "%d", (tm->tm_mday - 1) / 7 + 1);
2659 str_numth(p_inout, inout, S_TH_TYPE(suf));
2660 return strlen(p_inout);
2664 sscanf(inout, "%1d", &tmfc->w);
2665 return 1 + SKIP_THth(suf);
2671 sprintf(inout, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
2673 str_numth(p_inout, inout, S_TH_TYPE(suf));
2674 return strlen(p_inout);
2678 sscanf(inout, "%d", &tmfc->j);
2679 return strdigits_len(inout) + SKIP_THth(suf);
2686 static DCHCacheEntry *
2687 DCH_cache_getnew(char *str)
2689 DCHCacheEntry *ent = NULL;
2691 /* counter overload check - paranoia? */
2692 if (DCHCounter + DCH_CACHE_FIELDS >= MAX_INT32)
2696 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2697 ent->age = (++DCHCounter);
2701 * Cache is full - needs remove any older entry
2703 if (n_DCHCache > DCH_CACHE_FIELDS)
2706 DCHCacheEntry *old = DCHCache + 0;
2708 #ifdef DEBUG_TO_FROM_CHAR
2709 elog(DEBUG_elog_output, "cache is full (%d)", n_DCHCache);
2711 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2713 if (ent->age < old->age)
2716 #ifdef DEBUG_TO_FROM_CHAR
2717 elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age);
2719 StrNCpy(old->str, str, DCH_CACHE_SIZE + 1);
2720 /* old->format fill parser */
2721 old->age = (++DCHCounter);
2726 #ifdef DEBUG_TO_FROM_CHAR
2727 elog(DEBUG_elog_output, "NEW (%d)", n_DCHCache);
2729 ent = DCHCache + n_DCHCache;
2730 StrNCpy(ent->str, str, DCH_CACHE_SIZE + 1);
2731 /* ent->format fill parser */
2732 ent->age = (++DCHCounter);
2737 return NULL; /* never */
2740 static DCHCacheEntry *
2741 DCH_cache_search(char *str)
2746 /* counter overload check - paranoia? */
2747 if (DCHCounter + DCH_CACHE_FIELDS >= MAX_INT32)
2751 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2752 ent->age = (++DCHCounter);
2755 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2757 if (i == n_DCHCache)
2759 if (strcmp(ent->str, str) == 0)
2761 ent->age = (++DCHCounter);
2771 datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval)
2777 int fmt_len = VARSIZE(fmt) - VARHDRSZ;
2782 * Convert fmt to C string
2784 fmt_str = (char *) palloc(fmt_len + 1);
2785 memcpy(fmt_str, VARDATA(fmt), fmt_len);
2786 *(fmt_str + fmt_len) = '\0';
2789 * Allocate workspace for result as C string
2791 result = palloc((fmt_len * DCH_MAX_ITEM_SIZ) + 1);
2795 * Allocate new memory if format picture is bigger than static cache and
2796 * not use cache (call parser always)
2798 if (fmt_len > DCH_CACHE_SIZE)
2800 format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
2803 parse_format(format, fmt_str, DCH_keywords,
2804 DCH_suff, DCH_index, DCH_TYPE, NULL);
2806 (format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
2817 if ((ent = DCH_cache_search(fmt_str)) == NULL)
2819 ent = DCH_cache_getnew(fmt_str);
2822 * Not in the cache, must run parser and save a new format-picture
2825 parse_format(ent->format, fmt_str, DCH_keywords,
2826 DCH_suff, DCH_index, DCH_TYPE, NULL);
2828 (ent->format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
2830 #ifdef DEBUG_TO_FROM_CHAR
2831 /* dump_node(ent->format, fmt_len); */
2832 /* dump_index(DCH_keywords, DCH_index); */
2835 format = ent->format;
2838 /* The real work is here */
2839 DCH_processor(format, result, true, is_interval, (void *) tmtc);
2846 /* convert C-string result to TEXT format */
2847 reslen = strlen(result);
2848 res = (text *) palloc(reslen + VARHDRSZ);
2849 memcpy(VARDATA(res), result, reslen);
2850 VARATT_SIZEP(res) = reslen + VARHDRSZ;
2857 localize_month_full(int index)
2905 localize_month(int index)
2953 localize_day_full(int index)
2986 localize_day(int index)
3018 /****************************************************************************
3020 ***************************************************************************/
3022 /* -------------------
3023 * TIMESTAMP to_char()
3024 * -------------------
3027 timestamp_to_char(PG_FUNCTION_ARGS)
3029 Timestamp dt = PG_GETARG_TIMESTAMP(0);
3030 text *fmt = PG_GETARG_TEXT_P(1),
3036 if ((VARSIZE(fmt) - VARHDRSZ) <= 0 || TIMESTAMP_NOT_FINITE(dt))
3042 if (timestamp2tm(dt, NULL, tm, &tmtcFsec(&tmtc), NULL, NULL) != 0)
3044 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3045 errmsg("timestamp out of range")));
3047 thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3048 tm->tm_wday = (thisdate + 1) % 7;
3049 tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
3051 if (!(res = datetime_to_char_body(&tmtc, fmt, false)))
3054 PG_RETURN_TEXT_P(res);
3058 timestamptz_to_char(PG_FUNCTION_ARGS)
3060 TimestampTz dt = PG_GETARG_TIMESTAMP(0);
3061 text *fmt = PG_GETARG_TEXT_P(1),
3068 if ((VARSIZE(fmt) - VARHDRSZ) <= 0 || TIMESTAMP_NOT_FINITE(dt))
3074 if (timestamp2tm(dt, &tz, tm, &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0)
3076 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3077 errmsg("timestamp out of range")));
3079 thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3080 tm->tm_wday = (thisdate + 1) % 7;
3081 tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
3083 if (!(res = datetime_to_char_body(&tmtc, fmt, false)))
3086 PG_RETURN_TEXT_P(res);
3090 /* -------------------
3091 * INTERVAL to_char()
3092 * -------------------
3095 interval_to_char(PG_FUNCTION_ARGS)
3097 Interval *it = PG_GETARG_INTERVAL_P(0);
3098 text *fmt = PG_GETARG_TEXT_P(1),
3103 if ((VARSIZE(fmt) - VARHDRSZ) <= 0)
3109 if (interval2tm(*it, tm, &tmtcFsec(&tmtc)) != 0)
3112 /* wday is meaningless, yday approximates the total span in days */
3113 tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday;
3115 if (!(res = datetime_to_char_body(&tmtc, fmt, true)))
3118 PG_RETURN_TEXT_P(res);
3121 /* ---------------------
3124 * Make Timestamp from date_str which is formatted at argument 'fmt'
3125 * ( to_timestamp is reverse to_char() )
3126 * ---------------------
3129 to_timestamp(PG_FUNCTION_ARGS)
3131 text *date_txt = PG_GETARG_TEXT_P(0);
3132 text *fmt = PG_GETARG_TEXT_P(1);
3138 do_to_timestamp(date_txt, fmt, &tm, &fsec);
3140 tz = DetermineTimeZoneOffset(&tm, global_timezone);
3142 if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
3144 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3145 errmsg("timestamp out of range")));
3147 PG_RETURN_TIMESTAMP(result);
3152 * Make Date from date_str which is formated at argument 'fmt'
3156 to_date(PG_FUNCTION_ARGS)
3158 text *date_txt = PG_GETARG_TEXT_P(0);
3159 text *fmt = PG_GETARG_TEXT_P(1);
3164 do_to_timestamp(date_txt, fmt, &tm, &fsec);
3166 result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
3168 PG_RETURN_DATEADT(result);
3172 * do_to_timestamp: shared code for to_timestamp and to_date
3174 * Parse the 'date_txt' according to 'fmt', return results as a struct pg_tm
3175 * and fractional seconds.
3178 do_to_timestamp(text *date_txt, text *fmt,
3179 struct pg_tm * tm, fsec_t *fsec)
3190 fmt_len = VARSIZE(fmt) - VARHDRSZ;
3199 fmt_str = (char *) palloc(fmt_len + 1);
3200 memcpy(fmt_str, VARDATA(fmt), fmt_len);
3201 *(fmt_str + fmt_len) = '\0';
3204 * Allocate new memory if format picture is bigger than static cache
3205 * and not use cache (call parser always)
3207 if (fmt_len > DCH_CACHE_SIZE)
3209 format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
3212 parse_format(format, fmt_str, DCH_keywords,
3213 DCH_suff, DCH_index, DCH_TYPE, NULL);
3215 (format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
3226 if ((ent = DCH_cache_search(fmt_str)) == NULL)
3229 ent = DCH_cache_getnew(fmt_str);
3232 * Not in the cache, must run parser and save a new
3233 * format-picture to the cache.
3235 parse_format(ent->format, fmt_str, DCH_keywords,
3236 DCH_suff, DCH_index, DCH_TYPE, NULL);
3238 (ent->format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
3239 #ifdef DEBUG_TO_FROM_CHAR
3240 /* dump_node(ent->format, fmt_len); */
3241 /* dump_index(DCH_keywords, DCH_index); */
3244 format = ent->format;
3248 * Call action for each node in FormatNode tree
3250 #ifdef DEBUG_TO_FROM_CHAR
3251 /* dump_node(format, fmt_len); */
3255 * Convert date to C string
3257 date_len = VARSIZE(date_txt) - VARHDRSZ;
3258 date_str = (char *) palloc(date_len + 1);
3259 memcpy(date_str, VARDATA(date_txt), date_len);
3260 *(date_str + date_len) = '\0';
3262 DCH_processor(format, date_str, false, false, (void *) &tmfc);
3273 * Convert values that user define for FROM_CHAR (to_date/to_timestamp) to
3280 tm->tm_hour = x / SECS_PER_HOUR;
3282 tm->tm_min = x / SECS_PER_MINUTE;
3283 x %= SECS_PER_MINUTE;
3288 tm->tm_year = (tmfc.cc - 1) * 100;
3291 tmfc.ddd = (tmfc.ww - 1) * 7 + 1;
3294 tmfc.dd = (tmfc.w - 1) * 7 + 1;
3297 tm->tm_sec = tmfc.ss;
3299 tm->tm_min = tmfc.mi;
3301 tm->tm_hour = tmfc.hh;
3303 if (tmfc.pm || tmfc.am)
3305 if (tm->tm_hour < 1 || tm->tm_hour > 12)
3307 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3308 errmsg("AM/PM hour must be between 1 and 12")));
3310 if (tmfc.pm && tm->tm_hour < 12)
3313 else if (tmfc.am && tm->tm_hour == 12)
3339 if (tmfc.yysz == 2 && tmfc.cc)
3342 * CC and YY defined why -[2000|1900]? See dch_date() DCH_YY code.
3344 tm->tm_year = (tmfc.cc - 1) * 100 + (tmfc.year >= 2000 ? tmfc.year - 2000 : tmfc.year - 1900);
3346 else if (tmfc.yysz == 1 && tmfc.cc)
3351 tm->tm_year = (tmfc.cc - 1) * 100 + tmfc.year - 2000;
3354 /* set year (and ignore CC if defined) */
3355 tm->tm_year = tmfc.year;
3359 if (tm->tm_year > 0)
3360 tm->tm_year = -(tm->tm_year - 1);
3363 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3364 errmsg("inconsistent use of year %04d and \"BC\"",
3369 j2date(tmfc.j, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3372 isoweek2date(tmfc.iw, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3375 tm->tm_wday = tmfc.d;
3377 tm->tm_mday = tmfc.dd;
3379 tm->tm_yday = tmfc.ddd;
3381 tm->tm_mon = tmfc.mm;
3384 * we don't ignore DDD
3386 if (tmfc.ddd && (tm->tm_mon <= 1 || tm->tm_mday <= 1))
3388 /* count mday and mon from yday */
3393 {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0},
3394 {31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 0}};
3398 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3399 errmsg("cannot calculate day of year without year information")));
3401 y = ysum[isleap(tm->tm_year)];
3403 for (i = 0; i <= 11; i++)
3405 if (tm->tm_yday < y[i])
3408 if (tm->tm_mon <= 1)
3411 if (tm->tm_mday <= 1)
3412 tm->tm_mday = i == 0 ? tm->tm_yday :
3413 tm->tm_yday - y[i - 1];
3416 #ifdef HAVE_INT64_TIMESTAMP
3418 *fsec += tmfc.ms * 1000;
3423 *fsec += (double) tmfc.ms / 1000;
3425 *fsec += (double) tmfc.us / 1000000;
3432 /**********************************************************************
3433 * the NUMBER version part
3434 *********************************************************************/
3438 fill_str(char *str, int c, int max)
3440 memset(str, c, max);
3441 *(str + max + 1) = '\0';
3445 #define zeroize_NUM(_n) \
3451 (_n)->pre_lsign_num = 0; \
3452 (_n)->need_locale = 0; \
3454 (_n)->zero_start = 0; \
3455 (_n)->zero_end = 0; \
3458 static NUMCacheEntry *
3459 NUM_cache_getnew(char *str)
3461 NUMCacheEntry *ent = NULL;
3463 /* counter overload check - paranoia? */
3464 if (NUMCounter + NUM_CACHE_FIELDS >= MAX_INT32)
3468 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3469 ent->age = (++NUMCounter);
3473 * Cache is full - needs remove any older entry
3475 if (n_NUMCache > NUM_CACHE_FIELDS)
3478 NUMCacheEntry *old = NUMCache + 0;
3480 #ifdef DEBUG_TO_FROM_CHAR
3481 elog(DEBUG_elog_output, "Cache is full (%d)", n_NUMCache);
3484 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3487 * entry removed via NUM_cache_remove() can be used here
3489 if (*ent->str == '\0')
3494 if (ent->age < old->age)
3497 #ifdef DEBUG_TO_FROM_CHAR
3498 elog(DEBUG_elog_output, "OLD: \"%s\" AGE: %d", old->str, old->age);
3500 StrNCpy(old->str, str, NUM_CACHE_SIZE + 1);
3501 /* old->format fill parser */
3502 old->age = (++NUMCounter);
3509 #ifdef DEBUG_TO_FROM_CHAR
3510 elog(DEBUG_elog_output, "NEW (%d)", n_NUMCache);
3512 ent = NUMCache + n_NUMCache;
3513 StrNCpy(ent->str, str, NUM_CACHE_SIZE + 1);
3514 /* ent->format fill parser */
3515 ent->age = (++NUMCounter);
3519 zeroize_NUM(&ent->Num);
3521 last_NUMCacheEntry = ent;
3522 return ent; /* never */
3525 static NUMCacheEntry *
3526 NUM_cache_search(char *str)
3531 /* counter overload check - paranoia? */
3532 if (NUMCounter + NUM_CACHE_FIELDS >= MAX_INT32)
3536 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3537 ent->age = (++NUMCounter);
3540 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3542 if (i == n_NUMCache)
3544 if (strcmp(ent->str, str) == 0)
3546 ent->age = (++NUMCounter);
3547 last_NUMCacheEntry = ent;
3557 NUM_cache_remove(NUMCacheEntry *ent)
3559 #ifdef DEBUG_TO_FROM_CHAR
3560 elog(DEBUG_elog_output, "REMOVING ENTRY (%s)", ent->str);
3567 * Cache routine for NUM to_char version
3571 NUM_cache(int len, NUMDesc *Num, char *pars_str, bool *shouldFree)
3573 FormatNode *format = NULL;
3577 * Convert VARDATA() to string
3579 str = (char *) palloc(len + 1);
3580 memcpy(str, pars_str, len);
3581 *(str + len) = '\0';
3584 * Allocate new memory if format picture is bigger than static cache and
3585 * not use cache (call parser always). This branches sets shouldFree to
3586 * true, accordingly.
3588 if (len > NUM_CACHE_SIZE)
3590 format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
3596 parse_format(format, str, NUM_keywords,
3597 NULL, NUM_index, NUM_TYPE, Num);
3599 (format + len)->type = NODE_TYPE_END; /* Paranoia? */
3609 *shouldFree = false;
3611 if ((ent = NUM_cache_search(str)) == NULL)
3614 ent = NUM_cache_getnew(str);
3617 * Not in the cache, must run parser and save a new format-picture
3620 parse_format(ent->format, str, NUM_keywords,
3621 NULL, NUM_index, NUM_TYPE, &ent->Num);
3623 (ent->format + len)->type = NODE_TYPE_END; /* Paranoia? */
3627 format = ent->format;
3630 * Copy cache to used struct
3632 Num->flag = ent->Num.flag;
3633 Num->lsign = ent->Num.lsign;
3634 Num->pre = ent->Num.pre;
3635 Num->post = ent->Num.post;
3636 Num->pre_lsign_num = ent->Num.pre_lsign_num;
3637 Num->need_locale = ent->Num.need_locale;
3638 Num->multi = ent->Num.multi;
3639 Num->zero_start = ent->Num.zero_start;
3640 Num->zero_end = ent->Num.zero_end;
3643 #ifdef DEBUG_TO_FROM_CHAR
3644 /* dump_node(format, len); */
3645 dump_index(NUM_keywords, NUM_index);
3654 int_to_roman(int number)
3662 result = (char *) palloc(16);
3665 if (number > 3999 || number < 1)
3667 fill_str(result, '#', 15);
3670 len = snprintf(numstr, sizeof(numstr), "%d", number);
3672 for (p = numstr; *p != '\0'; p++, --len)
3674 num = *p - 49; /* 48 ascii + 1 */
3681 strcat(result, "M");
3686 strcat(result, rm100[num]);
3688 strcat(result, rm10[num]);
3690 strcat(result, rm1[num]);
3703 NUM_prepare_locale(NUMProc *Np)
3705 if (Np->Num->need_locale)
3708 struct lconv *lconv;
3713 lconv = PGLC_localeconv();
3716 * Positive / Negative number sign
3718 if (lconv->negative_sign && *lconv->negative_sign)
3719 Np->L_negative_sign = lconv->negative_sign;
3721 Np->L_negative_sign = "-";
3723 if (lconv->positive_sign && *lconv->positive_sign)
3724 Np->L_positive_sign = lconv->positive_sign;
3726 Np->L_positive_sign = "+";
3729 * Number thousands separator
3731 if (lconv->thousands_sep && *lconv->thousands_sep)
3732 Np->L_thousands_sep = lconv->thousands_sep;
3734 Np->L_thousands_sep = ",";
3737 * Number decimal point
3739 if (lconv->decimal_point && *lconv->decimal_point)
3740 Np->decimal = lconv->decimal_point;
3748 if (lconv->currency_symbol && *lconv->currency_symbol)
3749 Np->L_currency_symbol = lconv->currency_symbol;
3751 Np->L_currency_symbol = " ";
3754 if (!IS_LDECIMAL(Np->Num))
3762 Np->L_negative_sign = "-";
3763 Np->L_positive_sign = "+";
3766 Np->L_thousands_sep = ",";
3767 Np->L_currency_symbol = " ";
3772 * Return pointer of last relevant number after decimal point
3773 * 12.0500 --> last relevant is '5'
3777 get_last_relevant_decnum(char *num)
3780 *p = strchr(num, '.');
3782 #ifdef DEBUG_TO_FROM_CHAR
3783 elog(DEBUG_elog_output, "get_last_relevant_decnum()");
3800 * Number extraction for TO_NUMBER()
3804 NUM_numpart_from_char(NUMProc *Np, int id, int plen)
3806 bool isread = FALSE;
3808 #ifdef DEBUG_TO_FROM_CHAR
3809 elog(DEBUG_elog_output, " --- scan start --- id=%s",
3810 (id == NUM_0 || id == NUM_9) ? "NUM_0/9" : id == NUM_DEC ? "NUM_DEC" : "???");
3813 if (*Np->inout_p == ' ')
3816 #define OVERLOAD_TEST (Np->inout_p >= Np->inout + plen)
3817 #define AMOUNT_TEST(_s) (plen-(Np->inout_p-Np->inout) >= _s)
3819 if (*Np->inout_p == ' ')
3826 * read sign before number
3828 if (*Np->number == ' ' && (id == NUM_0 || id == NUM_9) &&
3829 (Np->read_pre + Np->read_post) == 0)
3832 #ifdef DEBUG_TO_FROM_CHAR
3833 elog(DEBUG_elog_output, "Try read sign (%c), locale positive: %s, negative: %s",
3834 *Np->inout_p, Np->L_positive_sign, Np->L_negative_sign);
3840 if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_PRE)
3844 #ifdef DEBUG_TO_FROM_CHAR
3845 elog(DEBUG_elog_output, "Try read locale pre-sign (%c)", *Np->inout_p);
3847 if ((x = strlen(Np->L_negative_sign)) &&
3849 strncmp(Np->inout_p, Np->L_negative_sign, x) == 0)
3854 else if ((x = strlen(Np->L_positive_sign)) &&
3856 strncmp(Np->inout_p, Np->L_positive_sign, x) == 0)
3864 #ifdef DEBUG_TO_FROM_CHAR
3865 elog(DEBUG_elog_output, "Try read simple sign (%c)", *Np->inout_p);
3871 if (*Np->inout_p == '-' || (IS_BRACKET(Np->Num) &&
3872 *Np->inout_p == '<'))
3875 *Np->number = '-'; /* set - */
3879 else if (*Np->inout_p == '+')
3882 *Np->number = '+'; /* set + */
3891 #ifdef DEBUG_TO_FROM_CHAR
3892 elog(DEBUG_elog_output, "Scan for numbers (%c), current number: '%s'", *Np->inout_p, Np->number);
3898 if (isdigit((unsigned char) *Np->inout_p))
3901 if (Np->read_dec && Np->read_post == Np->Num->post)
3904 *Np->number_p = *Np->inout_p;
3914 #ifdef DEBUG_TO_FROM_CHAR
3915 elog(DEBUG_elog_output, "Read digit (%c)", *Np->inout_p);
3919 * read decimal point
3922 else if (IS_DECIMAL(Np->Num) && Np->read_dec == FALSE)
3925 #ifdef DEBUG_TO_FROM_CHAR
3926 elog(DEBUG_elog_output, "Try read decimal point (%c)", *Np->inout_p);
3928 if (*Np->inout_p == '.')
3931 *Np->number_p = '.';
3933 Np->read_dec = TRUE;
3938 int x = strlen(Np->decimal);
3940 #ifdef DEBUG_TO_FROM_CHAR
3941 elog(DEBUG_elog_output, "Try read locale point (%c)",
3944 if (x && AMOUNT_TEST(x) && strncmp(Np->inout_p, Np->decimal, x) == 0)
3946 Np->inout_p += x - 1;
3947 *Np->number_p = '.';
3949 Np->read_dec = TRUE;
3959 * Read sign behind "last" number
3961 * We need sign detection because determine exact position of post-sign is
3964 * FM9999.9999999S -> 123.001- 9.9S -> .5- FM9.999999MI ->
3967 if (*Np->number == ' ' && Np->read_pre + Np->read_post > 0)
3970 * locale sign (NUM_S) is always anchored behind a last number, if: -
3971 * locale sign expected - last read char was NUM_0/9 or NUM_DEC - and
3972 * next char is not digit
3974 if (IS_LSIGN(Np->Num) && isread &&
3975 (Np->inout_p + 1) <= Np->inout + plen &&
3976 !isdigit((unsigned char) *(Np->inout_p + 1)))
3979 char *tmp = Np->inout_p++;
3981 #ifdef DEBUG_TO_FROM_CHAR
3982 elog(DEBUG_elog_output, "Try read locale post-sign (%c)", *Np->inout_p);
3984 if ((x = strlen(Np->L_negative_sign)) &&
3986 strncmp(Np->inout_p, Np->L_negative_sign, x) == 0)
3988 Np->inout_p += x - 1; /* -1 .. NUM_processor() do inout_p++ */
3991 else if ((x = strlen(Np->L_positive_sign)) &&
3993 strncmp(Np->inout_p, Np->L_positive_sign, x) == 0)
3995 Np->inout_p += x - 1; /* -1 .. NUM_processor() do inout_p++ */
3998 if (*Np->number == ' ')
4004 * try read non-locale sign, it's happen only if format is not exact
4005 * and we cannot determine sign position of MI/PL/SG, an example:
4007 * FM9.999999MI -> 5.01-
4009 * if (.... && IS_LSIGN(Np->Num)==FALSE) prevents read wrong formats
4010 * like to_number('1 -', '9S') where sign is not anchored to last
4013 else if (isread == FALSE && IS_LSIGN(Np->Num) == FALSE &&
4014 (IS_PLUS(Np->Num) || IS_MINUS(Np->Num)))
4016 #ifdef DEBUG_TO_FROM_CHAR
4017 elog(DEBUG_elog_output, "Try read simple post-sign (%c)", *Np->inout_p);
4023 if (*Np->inout_p == '-' || *Np->inout_p == '+')
4024 /* NUM_processor() do inout_p++ */
4025 *Np->number = *Np->inout_p;
4030 #define IS_PREDEC_SPACE(_n) \
4031 (IS_ZERO((_n)->Num)==FALSE && \
4032 (_n)->number == (_n)->number_p && \
4033 *(_n)->number == '0' && \
4034 (_n)->Num->post != 0)
4037 * Add digit or sign to number-string
4041 NUM_numpart_to_char(NUMProc *Np, int id)
4045 if (IS_ROMAN(Np->Num))
4048 /* Note: in this elog() output not set '\0' in 'inout' */
4050 #ifdef DEBUG_TO_FROM_CHAR
4053 * Np->num_curr is number of current item in format-picture, it is not
4054 * current position in inout!
4056 elog(DEBUG_elog_output,
4057 "SIGN_WROTE: %d, CURRENT: %d, NUMBER_P: \"%s\", INOUT: \"%s\"",
4066 * Write sign if real number will write to output Note: IS_PREDEC_SPACE()
4067 * handle "9.9" --> " .1"
4069 if (Np->sign_wrote == FALSE &&
4070 (Np->num_curr >= Np->num_pre || (IS_ZERO(Np->Num) && Np->Num->zero_start == Np->num_curr)) &&
4071 (IS_PREDEC_SPACE(Np) == FALSE || (Np->last_relevant && *Np->last_relevant == '.')))
4073 if (IS_LSIGN(Np->Num))
4075 if (Np->Num->lsign == NUM_LSIGN_PRE)
4077 if (Np->sign == '-')
4078 strcpy(Np->inout_p, Np->L_negative_sign);
4080 strcpy(Np->inout_p, Np->L_positive_sign);
4081 Np->inout_p += strlen(Np->inout_p);
4082 Np->sign_wrote = TRUE;
4085 else if (IS_BRACKET(Np->Num))
4087 *Np->inout_p = Np->sign == '+' ? ' ' : '<';
4089 Np->sign_wrote = TRUE;
4091 else if (Np->sign == '+')
4093 if (!IS_FILLMODE(Np->Num))
4095 *Np->inout_p = ' '; /* Write + */
4098 Np->sign_wrote = TRUE;
4100 else if (Np->sign == '-')
4104 Np->sign_wrote = TRUE;
4110 * digits / FM / Zero / Dec. point
4112 if (id == NUM_9 || id == NUM_0 || id == NUM_D || id == NUM_DEC)
4114 if (Np->num_curr < Np->num_pre &&
4115 (Np->Num->zero_start > Np->num_curr || !IS_ZERO(Np->Num)))
4120 if (!IS_FILLMODE(Np->Num))
4122 *Np->inout_p = ' '; /* Write ' ' */
4127 else if (IS_ZERO(Np->Num) &&
4128 Np->num_curr < Np->num_pre &&
4129 Np->Num->zero_start <= Np->num_curr)
4134 *Np->inout_p = '0'; /* Write '0' */
4142 * Write Decimal point
4144 if (*Np->number_p == '.')
4147 if (!Np->last_relevant || *Np->last_relevant != '.')
4149 strcpy(Np->inout_p, Np->decimal); /* Write DEC/D */
4150 Np->inout_p += strlen(Np->inout_p);
4154 * Ora 'n' -- FM9.9 --> 'n.'
4156 else if (IS_FILLMODE(Np->Num) &&
4157 Np->last_relevant && *Np->last_relevant == '.')
4160 strcpy(Np->inout_p, Np->decimal); /* Write DEC/D */
4161 Np->inout_p += strlen(Np->inout_p);
4170 if (Np->last_relevant && Np->number_p > Np->last_relevant &&
4175 * '0.1' -- 9.9 --> ' .1'
4177 else if (IS_PREDEC_SPACE(Np))
4179 if (!IS_FILLMODE(Np->Num))
4186 * '0' -- FM9.9 --> '0.'
4188 else if (Np->last_relevant && *Np->last_relevant == '.')
4196 *Np->inout_p = *Np->number_p; /* Write DIGIT */
4204 end = Np->num_count + (Np->num_pre ? 1 : 0) + (IS_DECIMAL(Np->Num) ? 1 : 0);
4206 if (Np->last_relevant && Np->last_relevant == Np->number_p)
4209 if (Np->num_curr + 1 == end)
4211 if (Np->sign_wrote == TRUE && IS_BRACKET(Np->Num))
4213 *Np->inout_p = Np->sign == '+' ? ' ' : '>';
4216 else if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_POST)
4218 if (Np->sign == '-')
4219 strcpy(Np->inout_p, Np->L_negative_sign);
4221 strcpy(Np->inout_p, Np->L_positive_sign);
4222 Np->inout_p += strlen(Np->inout_p);
4231 * Note: 'plen' is used in FROM_CHAR conversion and it's length of
4232 * input (inout). In TO_CHAR conversion it's space before first number.
4235 NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
4236 int plen, int sign, bool is_to_char)
4242 MemSet(Np, 0, sizeof(NUMProc));
4245 Np->is_to_char = is_to_char;
4246 Np->number = number;
4248 Np->last_relevant = NULL;
4251 Np->read_dec = FALSE;
4253 if (Np->Num->zero_start)
4254 --Np->Num->zero_start;
4259 if (IS_ROMAN(Np->Num))
4261 if (!Np->is_to_char)
4263 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4264 errmsg("\"RN\" not supported")));
4266 Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->post =
4267 Np->Num->pre = Np->num_pre = Np->sign = 0;
4269 if (IS_FILLMODE(Np->Num))
4272 Np->Num->flag |= NUM_F_FILLMODE;
4276 Np->Num->flag |= NUM_F_ROMAN;
4286 /* MI/PL/SG - write sign itself and not in number */
4287 if (IS_PLUS(Np->Num) || IS_MINUS(Np->Num))
4289 if (IS_PLUS(Np->Num) && IS_MINUS(Np->Num) == FALSE)
4290 Np->sign_wrote = FALSE; /* need sign */
4292 Np->sign_wrote = TRUE; /* needn't sign */
4296 if (Np->sign != '-')
4298 if (IS_BRACKET(Np->Num) && IS_FILLMODE(Np->Num))
4299 Np->Num->flag &= ~NUM_F_BRACKET;
4300 if (IS_MINUS(Np->Num))
4301 Np->Num->flag &= ~NUM_F_MINUS;
4303 else if (Np->sign != '+' && IS_PLUS(Np->Num))
4304 Np->Num->flag &= ~NUM_F_PLUS;
4306 if (Np->sign == '+' && IS_FILLMODE(Np->Num) && IS_LSIGN(Np->Num) == FALSE)
4307 Np->sign_wrote = TRUE; /* needn't sign */
4309 Np->sign_wrote = FALSE; /* need sign */
4311 if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num)
4312 Np->Num->lsign = NUM_LSIGN_POST;
4321 Np->num_count = Np->Num->post + Np->Num->pre - 1;
4327 if (IS_FILLMODE(Np->Num))
4329 if (IS_DECIMAL(Np->Num))
4330 Np->last_relevant = get_last_relevant_decnum(
4332 ((Np->Num->zero_end - Np->num_pre > 0) ?
4333 Np->Num->zero_end - Np->num_pre : 0));
4336 if (Np->sign_wrote == FALSE && Np->num_pre == 0)
4342 *Np->number = ' '; /* sign space */
4343 *(Np->number + 1) = '\0';
4349 #ifdef DEBUG_TO_FROM_CHAR
4350 elog(DEBUG_elog_output,
4351 "\n\tSIGN: '%c'\n\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_WROTE: %s\n\tZERO: %s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s\n\tBRACKET: %s\n\tPLUS: %s\n\tMINUS: %s\n\tFILLMODE: %s\n\tROMAN: %s",
4358 Np->sign_wrote ? "Yes" : "No",
4359 IS_ZERO(Np->Num) ? "Yes" : "No",
4360 Np->Num->zero_start,
4362 Np->last_relevant ? Np->last_relevant : "<not set>",
4363 IS_BRACKET(Np->Num) ? "Yes" : "No",
4364 IS_PLUS(Np->Num) ? "Yes" : "No",
4365 IS_MINUS(Np->Num) ? "Yes" : "No",
4366 IS_FILLMODE(Np->Num) ? "Yes" : "No",
4367 IS_ROMAN(Np->Num) ? "Yes" : "No"
4374 NUM_prepare_locale(Np);
4377 * Processor direct cycle
4380 Np->number_p = Np->number;
4382 Np->number_p = Np->number + 1; /* first char is space for sign */
4384 for (n = node, Np->inout_p = Np->inout; n->type != NODE_TYPE_END; n++)
4386 if (!Np->is_to_char)
4389 * Check non-string inout end
4391 if (Np->inout_p >= Np->inout + plen)
4396 * Format pictures actions
4398 if (n->type == NODE_TYPE_ACTION)
4401 * Create/reading digit/zero/blank/sing
4403 * 'NUM_S' note: The locale sign is anchored to number and we
4404 * read/write it when we work with first or last number
4405 * (NUM_0/NUM_9). This is reason why NUM_S missing in follow
4417 NUM_numpart_to_char(Np, n->key->id);
4418 continue; /* for() */
4422 NUM_numpart_from_char(Np, n->key->id, plen);
4423 break; /* switch() case: */
4431 if (IS_FILLMODE(Np->Num))
4443 if (IS_FILLMODE(Np->Num))
4454 if (IS_FILLMODE(Np->Num))
4458 int x = strlen(Np->L_thousands_sep);
4460 memset(Np->inout_p, ' ', x);
4461 Np->inout_p += x - 1;
4466 strcpy(Np->inout_p, Np->L_thousands_sep);
4467 Np->inout_p += strlen(Np->inout_p) - 1;
4475 if (IS_FILLMODE(Np->Num))
4478 Np->inout_p += strlen(Np->L_thousands_sep) - 1;
4485 strcpy(Np->inout_p, Np->L_currency_symbol);
4486 Np->inout_p += strlen(Np->inout_p) - 1;
4490 Np->inout_p += strlen(Np->L_currency_symbol) - 1;
4494 if (IS_FILLMODE(Np->Num))
4496 strcpy(Np->inout_p, Np->number_p);
4497 Np->inout_p += strlen(Np->inout_p) - 1;
4501 sprintf(Np->inout_p, "%15s", Np->number_p);
4502 Np->inout_p += strlen(Np->inout_p) - 1;
4507 if (IS_FILLMODE(Np->Num))
4509 strcpy(Np->inout_p, str_tolower(Np->number_p));
4510 Np->inout_p += strlen(Np->inout_p) - 1;
4514 sprintf(Np->inout_p, "%15s", str_tolower(Np->number_p));
4515 Np->inout_p += strlen(Np->inout_p) - 1;
4520 if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
4521 Np->sign == '-' || IS_DECIMAL(Np->Num))
4525 strcpy(Np->inout_p, get_th(Np->number, TH_LOWER));
4530 if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
4531 Np->sign == '-' || IS_DECIMAL(Np->Num))
4535 strcpy(Np->inout_p, get_th(Np->number, TH_UPPER));
4542 if (Np->sign == '-')
4544 else if (IS_FILLMODE(Np->Num))
4552 if (*Np->inout_p == '-')
4560 if (Np->sign == '+')
4562 else if (IS_FILLMODE(Np->Num))
4570 if (*Np->inout_p == '+')
4577 *Np->inout_p = Np->sign;
4581 if (*Np->inout_p == '-')
4583 else if (*Np->inout_p == '+')
4598 * Remove to output char from input in TO_CHAR
4601 *Np->inout_p = n->character;
4608 *Np->inout_p = '\0';
4613 if (*(Np->number_p - 1) == '.')
4614 *(Np->number_p - 1) = '\0';
4616 *Np->number_p = '\0';
4619 * Correction - precision of dec. number
4621 Np->Num->post = Np->read_post;
4623 #ifdef DEBUG_TO_FROM_CHAR
4624 elog(DEBUG_elog_output, "TO_NUMBER (number): '%s'", Np->number);
4631 * MACRO: Start part of NUM - for all NUM's to_char variants
4632 * (sorry, but I hate copy same code - macro is better..)
4635 #define NUM_TOCHAR_prepare \
4637 len = VARSIZE(fmt) - VARHDRSZ; \
4639 return DirectFunctionCall1(textin, CStringGetDatum("")); \
4640 result = (text *) palloc( (len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ); \
4641 memset(result, 0, (len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ ); \
4642 format = NUM_cache(len, &Num, VARDATA(fmt), &shouldFree); \
4646 * MACRO: Finish part of NUM
4649 #define NUM_TOCHAR_finish \
4651 NUM_processor(format, &Num, VARDATA(result), \
4652 numstr, plen, sign, true); \
4659 * for result is allocated max memory, which current format-picture\
4660 * needs, now it must be re-allocate to result real size \
4662 if (!(len = strlen(VARDATA(result)))) \
4668 result_tmp = result; \
4669 result = (text *) palloc( len + 1 + VARHDRSZ); \
4671 strcpy( VARDATA(result), VARDATA(result_tmp)); \
4672 VARATT_SIZEP(result) = len + VARHDRSZ; \
4673 pfree(result_tmp); \
4676 /* -------------------
4677 * NUMERIC to_number() (convert string to numeric)
4678 * -------------------
4681 numeric_to_number(PG_FUNCTION_ARGS)
4683 text *value = PG_GETARG_TEXT_P(0);
4684 text *fmt = PG_GETARG_TEXT_P(1);
4694 len = VARSIZE(fmt) - VARHDRSZ;
4699 format = NUM_cache(len, &Num, VARDATA(fmt), &shouldFree);
4701 numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1);
4703 NUM_processor(format, &Num, VARDATA(value), numstr,
4704 VARSIZE(value) - VARHDRSZ, 0, false);
4707 precision = Max(0, Num.pre) + scale;
4712 result = DirectFunctionCall3(numeric_in,
4713 CStringGetDatum(numstr),
4714 ObjectIdGetDatum(InvalidOid),
4715 Int32GetDatum(((precision << 16) | scale) + VARHDRSZ));
4720 /* ------------------
4722 * ------------------
4725 numeric_to_char(PG_FUNCTION_ARGS)
4727 Numeric value = PG_GETARG_NUMERIC(0);
4728 text *fmt = PG_GETARG_TEXT_P(1);
4745 * On DateType depend part (numeric)
4749 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
4750 NumericGetDatum(value),
4753 int_to_roman(DatumGetInt32(DirectFunctionCall1(numeric_int4,
4754 NumericGetDatum(x))));
4759 Numeric val = value;
4763 Numeric a = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
4764 Int32GetDatum(10)));
4765 Numeric b = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
4766 Int32GetDatum(Num.multi)));
4768 x = DatumGetNumeric(DirectFunctionCall2(numeric_power,
4770 NumericGetDatum(b)));
4771 val = DatumGetNumeric(DirectFunctionCall2(numeric_mul,
4772 NumericGetDatum(value),
4773 NumericGetDatum(x)));
4777 Num.pre += Num.multi;
4780 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
4781 NumericGetDatum(val),
4782 Int32GetDatum(Num.post)));
4783 orgnum = DatumGetCString(DirectFunctionCall1(numeric_out,
4784 NumericGetDatum(x)));
4790 numstr = orgnum + 1;
4797 if ((p = strchr(numstr, '.')))
4800 len = strlen(numstr);
4803 plen = Num.pre - len;
4804 else if (len > Num.pre)
4806 fill_str(numstr, '#', Num.pre);
4807 *(numstr + Num.pre) = '.';
4808 fill_str(numstr + 1 + Num.pre, '#', Num.post);
4816 PG_RETURN_TEXT_P(result);
4824 int4_to_char(PG_FUNCTION_ARGS)
4826 int32 value = PG_GETARG_INT32(0);
4827 text *fmt = PG_GETARG_TEXT_P(1);
4842 * On DateType depend part (int32)
4845 numstr = orgnum = int_to_roman(value);
4850 orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4851 Int32GetDatum(value * ((int32) pow((double) 10, (double) Num.multi)))));
4852 Num.pre += Num.multi;
4856 orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4857 Int32GetDatum(value)));
4859 len = strlen(orgnum);
4873 numstr = (char *) palloc(len + Num.post + 2);
4874 strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
4875 *(numstr + len) = '.';
4877 for (i = len + 1; i <= len + Num.post; i++)
4878 *(numstr + i) = '0';
4879 *(numstr + len + Num.post + 1) = '\0';
4884 numstr = orgnum + (*orgnum == '-' ? 1 : 0);
4887 plen = Num.pre - len;
4888 else if (len > Num.pre)
4890 fill_str(numstr, '#', Num.pre);
4891 *(numstr + Num.pre) = '.';
4892 fill_str(numstr + 1 + Num.pre, '#', Num.post);
4897 PG_RETURN_TEXT_P(result);
4905 int8_to_char(PG_FUNCTION_ARGS)
4907 int64 value = PG_GETARG_INT64(0);
4908 text *fmt = PG_GETARG_TEXT_P(1);
4923 * On DateType depend part (int32)
4927 /* Currently don't support int8 conversion to roman... */
4928 numstr = orgnum = int_to_roman(DatumGetInt32(
4929 DirectFunctionCall1(int84, Int64GetDatum(value))));
4935 double multi = pow((double) 10, (double) Num.multi);
4937 value = DatumGetInt64(DirectFunctionCall2(int8mul,
4938 Int64GetDatum(value),
4939 DirectFunctionCall1(dtoi8,
4940 Float8GetDatum(multi))));
4941 Num.pre += Num.multi;
4944 orgnum = DatumGetCString(DirectFunctionCall1(int8out,
4945 Int64GetDatum(value)));
4946 len = strlen(orgnum);
4960 numstr = (char *) palloc(len + Num.post + 2);
4961 strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
4962 *(numstr + len) = '.';
4964 for (i = len + 1; i <= len + Num.post; i++)
4965 *(numstr + i) = '0';
4966 *(numstr + len + Num.post + 1) = '\0';
4971 numstr = orgnum + (*orgnum == '-' ? 1 : 0);
4974 plen = Num.pre - len;
4975 else if (len > Num.pre)
4977 fill_str(numstr, '#', Num.pre);
4978 *(numstr + Num.pre) = '.';
4979 fill_str(numstr + 1 + Num.pre, '#', Num.post);
4984 PG_RETURN_TEXT_P(result);
4987 /* -----------------
4992 float4_to_char(PG_FUNCTION_ARGS)
4994 float4 value = PG_GETARG_FLOAT4(0);
4995 text *fmt = PG_GETARG_TEXT_P(1);
5011 numstr = orgnum = int_to_roman((int) rint(value));
5018 float multi = pow((double) 10, (double) Num.multi);
5020 val = value * multi;
5021 Num.pre += Num.multi;
5024 orgnum = (char *) palloc(MAXFLOATWIDTH + 1);
5025 snprintf(orgnum, MAXFLOATWIDTH + 1, "%.0f", fabs(val));
5026 len = strlen(orgnum);
5028 plen = Num.pre - len;
5031 else if (Num.post + len > FLT_DIG)
5032 Num.post = FLT_DIG - len;
5033 snprintf(orgnum, MAXFLOATWIDTH + 1, "%.*f", Num.post, val);
5038 numstr = orgnum + 1;
5045 if ((p = strchr(numstr, '.')))
5048 len = strlen(numstr);
5051 plen = Num.pre - len;
5052 else if (len > Num.pre)
5054 fill_str(numstr, '#', Num.pre);
5055 *(numstr + Num.pre) = '.';
5056 fill_str(numstr + 1 + Num.pre, '#', Num.post);
5061 PG_RETURN_TEXT_P(result);
5064 /* -----------------
5069 float8_to_char(PG_FUNCTION_ARGS)
5071 float8 value = PG_GETARG_FLOAT8(0);
5072 text *fmt = PG_GETARG_TEXT_P(1);
5088 numstr = orgnum = int_to_roman((int) rint(value));
5095 double multi = pow((double) 10, (double) Num.multi);
5097 val = value * multi;
5098 Num.pre += Num.multi;
5100 orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
5101 len = snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.0f", fabs(val));
5103 plen = Num.pre - len;
5106 else if (Num.post + len > DBL_DIG)
5107 Num.post = DBL_DIG - len;
5108 snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.*f", Num.post, val);
5113 numstr = orgnum + 1;
5120 if ((p = strchr(numstr, '.')))
5123 len = strlen(numstr);
5126 plen = Num.pre - len;
5127 else if (len > Num.pre)
5129 fill_str(numstr, '#', Num.pre);
5130 *(numstr + Num.pre) = '.';
5131 fill_str(numstr + 1 + Num.pre, '#', Num.post);
5136 PG_RETURN_TEXT_P(result);