1 /* -----------------------------------------------------------------------
4 * src/backend/utils/adt/formatting.c
7 * Portions Copyright (c) 1999-2011, 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 buffer and is persistent across transactions. If
20 * the format-picture is bigger than the cache buffer, the parser is called
23 * NOTE for Number version:
24 * All in this version is implemented as keywords ( => not used
25 * suffixes), because a format picture is for *one* item (number)
26 * only. It not is as a timestamp version, where each keyword (can)
29 * NOTE for Timestamp routines:
30 * In this module the POSIX 'struct tm' type is *not* used, but rather
31 * PgSQL type, which has tm_mon based on one (*non* zero) and
32 * year *not* based on 1900, but is used full year number.
33 * Module supports AD / BC / AM / PM.
35 * Supported types for to_char():
37 * Timestamp, Numeric, int4, int8, float4, float8
39 * Supported types for reverse conversion:
41 * Timestamp - to_timestamp()
43 * Numeric - to_number()
49 * - better number building (formatting) / parsing, now it isn't
52 * - add support for abstime
53 * - add support for roman number to standard number conversion
54 * - add support for number spelling
55 * - add support for string to string formatting (we must be better
57 * to_char('Hello', 'X X X X X') -> 'H e l l o'
59 * -----------------------------------------------------------------------
62 #ifdef DEBUG_TO_FROM_CHAR
63 #define DEBUG_elog_output DEBUG3
75 * towlower() and friends should be in <wctype.h>, but some pre-C99 systems
76 * declare them in <wchar.h>.
85 #include "catalog/pg_collation.h"
86 #include "mb/pg_wchar.h"
87 #include "utils/builtins.h"
88 #include "utils/date.h"
89 #include "utils/datetime.h"
90 #include "utils/formatting.h"
91 #include "utils/int8.h"
92 #include "utils/numeric.h"
93 #include "utils/pg_locale.h"
99 #define DCH_TYPE 1 /* DATE-TIME version */
100 #define NUM_TYPE 2 /* NUMBER version */
103 * KeyWord Index (ascii from position 32 (' ') to 126 (~))
106 #define KeyWord_INDEX_SIZE ('~' - ' ')
107 #define KeyWord_INDEX_FILTER(_c) ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
110 * Maximal length of one node
113 #define DCH_MAX_ITEM_SIZ 9 /* max julian day */
114 #define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
120 #define MAXFLOATWIDTH 60
121 #define MAXDOUBLEWIDTH 500
125 * External (defined in PgSQL datetime.c (timestamp utils))
128 extern char *months[], /* month abbreviation */
129 *days[]; /* full days */
132 * Format parser structs
137 char *name; /* suffix string */
138 int len, /* suffix length */
139 id, /* used in node->suffix */
140 type; /* prefix / postfix */
147 * This value is used to nominate one of several distinct (and mutually
148 * exclusive) date conventions that a keyword can belong to.
152 FROM_CHAR_DATE_NONE = 0, /* Value does not affect date mode. */
153 FROM_CHAR_DATE_GREGORIAN, /* Gregorian (day, month, year) style date */
154 FROM_CHAR_DATE_ISOWEEK /* ISO 8601 week date */
157 typedef struct FormatNode FormatNode;
165 FromCharDateMode date_mode;
170 int type; /* node type */
171 const KeyWord *key; /* if node type is KEYWORD */
172 char character; /* if node type is CHAR */
173 int suffix; /* keyword suffix */
176 #define NODE_TYPE_END 1
177 #define NODE_TYPE_ACTION 2
178 #define NODE_TYPE_CHAR 3
180 #define SUFFTYPE_PREFIX 1
181 #define SUFFTYPE_POSTFIX 2
183 #define CLOCK_24_HOUR 0
184 #define CLOCK_12_HOUR 1
191 static char *months_full[] = {
192 "January", "February", "March", "April", "May", "June", "July",
193 "August", "September", "October", "November", "December", NULL
196 static char *days_short[] = {
197 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
203 * There is no 0 AD. Years go from 1 BC to 1 AD, so we make it
204 * positive and map year == -1 to year zero, and shift all negative
205 * years up one. For interval years, we just return the year.
207 #define ADJUST_YEAR(year, is_interval) ((is_interval) ? (year) : ((year) <= 0 ? -((year) - 1) : (year)))
209 #define A_D_STR "A.D."
210 #define a_d_STR "a.d."
214 #define B_C_STR "B.C."
215 #define b_c_STR "b.c."
220 * AD / BC strings for seq_search.
222 * These are given in two variants, a long form with periods and a standard
225 * The array is laid out such that matches for AD have an even index, and
226 * matches for BC have an odd index. So the boolean value for BC is given by
227 * taking the array index of the match, modulo 2.
229 static char *adbc_strings[] = {ad_STR, bc_STR, AD_STR, BC_STR, NULL};
230 static char *adbc_strings_long[] = {a_d_STR, b_c_STR, A_D_STR, B_C_STR, NULL};
236 #define A_M_STR "A.M."
237 #define a_m_STR "a.m."
241 #define P_M_STR "P.M."
242 #define p_m_STR "p.m."
247 * AM / PM strings for seq_search.
249 * These are given in two variants, a long form with periods and a standard
252 * The array is laid out such that matches for AM have an even index, and
253 * matches for PM have an odd index. So the boolean value for PM is given by
254 * taking the array index of the match, modulo 2.
256 static char *ampm_strings[] = {am_STR, pm_STR, AM_STR, PM_STR, NULL};
257 static char *ampm_strings_long[] = {a_m_STR, p_m_STR, A_M_STR, P_M_STR, NULL};
260 * Months in roman-numeral
261 * (Must be in reverse order for seq_search (in FROM_CHAR), because
262 * 'VIII' must have higher precedence than 'V')
265 static char *rm_months_upper[] =
266 {"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL};
268 static char *rm_months_lower[] =
269 {"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL};
275 static char *rm1[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL};
276 static char *rm10[] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL};
277 static char *rm100[] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL};
283 static char *numTH[] = {"ST", "ND", "RD", "TH", NULL};
284 static char *numth[] = {"st", "nd", "rd", "th", NULL};
290 #define ONE_UPPER 1 /* Name */
291 #define ALL_UPPER 2 /* NAME */
292 #define ALL_LOWER 3 /* name */
296 #define MAX_MONTH_LEN 9
297 #define MAX_MON_LEN 3
298 #define MAX_DAY_LEN 9
306 * Number description struct
311 int pre, /* (count) numbers before decimal */
312 post, /* (count) numbers after decimal */
313 lsign, /* want locales sign */
314 flag, /* number parameters */
315 pre_lsign_num, /* tmp value for lsign */
316 multi, /* multiplier for 'V' */
317 zero_start, /* position of first zero */
318 zero_end, /* position of last zero */
319 need_locale; /* needs it locale */
323 * Flags for NUMBER version
326 #define NUM_F_DECIMAL (1 << 1)
327 #define NUM_F_LDECIMAL (1 << 2)
328 #define NUM_F_ZERO (1 << 3)
329 #define NUM_F_BLANK (1 << 4)
330 #define NUM_F_FILLMODE (1 << 5)
331 #define NUM_F_LSIGN (1 << 6)
332 #define NUM_F_BRACKET (1 << 7)
333 #define NUM_F_MINUS (1 << 8)
334 #define NUM_F_PLUS (1 << 9)
335 #define NUM_F_ROMAN (1 << 10)
336 #define NUM_F_MULTI (1 << 11)
337 #define NUM_F_PLUS_POST (1 << 12)
338 #define NUM_F_MINUS_POST (1 << 13)
339 #define NUM_F_EEEE (1 << 14)
341 #define NUM_LSIGN_PRE (-1)
342 #define NUM_LSIGN_POST 1
343 #define NUM_LSIGN_NONE 0
349 #define IS_DECIMAL(_f) ((_f)->flag & NUM_F_DECIMAL)
350 #define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL)
351 #define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO)
352 #define IS_BLANK(_f) ((_f)->flag & NUM_F_BLANK)
353 #define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE)
354 #define IS_BRACKET(_f) ((_f)->flag & NUM_F_BRACKET)
355 #define IS_MINUS(_f) ((_f)->flag & NUM_F_MINUS)
356 #define IS_LSIGN(_f) ((_f)->flag & NUM_F_LSIGN)
357 #define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
358 #define IS_ROMAN(_f) ((_f)->flag & NUM_F_ROMAN)
359 #define IS_MULTI(_f) ((_f)->flag & NUM_F_MULTI)
360 #define IS_EEEE(_f) ((_f)->flag & NUM_F_EEEE)
363 * Format picture cache
365 * Number part = NUM_CACHE_SIZE * NUM_CACHE_FIELDS
366 * Date-time part = DCH_CACHE_SIZE * DCH_CACHE_FIELDS
370 #define NUM_CACHE_SIZE 64
371 #define NUM_CACHE_FIELDS 16
372 #define DCH_CACHE_SIZE 128
373 #define DCH_CACHE_FIELDS 16
377 FormatNode format[DCH_CACHE_SIZE + 1];
378 char str[DCH_CACHE_SIZE + 1];
384 FormatNode format[NUM_CACHE_SIZE + 1];
385 char str[NUM_CACHE_SIZE + 1];
390 /* global cache for --- date/time part */
391 static DCHCacheEntry DCHCache[DCH_CACHE_FIELDS + 1];
393 static int n_DCHCache = 0; /* number of entries */
394 static int DCHCounter = 0;
396 /* global cache for --- number part */
397 static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1];
399 static int n_NUMCache = 0; /* number of entries */
400 static int NUMCounter = 0;
401 static NUMCacheEntry *last_NUMCacheEntry = NUMCache + 0;
404 * For char->date/time conversion
409 FromCharDateMode mode;
427 yysz, /* is it YY or YYYY ? */
428 clock; /* 12 or 24 hour clock? */
431 #define ZERO_tmfc(_X) memset(_X, 0, sizeof(TmFromChar))
437 #ifdef DEBUG_TO_FROM_CHAR
438 #define DEBUG_TMFC(_X) \
439 elog(DEBUG_elog_output, "TMFC:\nmode %d\nhh %d\npm %d\nmi %d\nss %d\nssss %d\nd %d\ndd %d\nddd %d\nmm %d\nms: %d\nyear %d\nbc %d\nww %d\nw %d\ncc %d\nj %d\nus: %d\nyysz: %d\nclock: %d", \
440 (_X)->mode, (_X)->hh, (_X)->pm, (_X)->mi, (_X)->ss, (_X)->ssss, \
441 (_X)->d, (_X)->dd, (_X)->ddd, (_X)->mm, (_X)->ms, (_X)->year, \
442 (_X)->bc, (_X)->ww, (_X)->w, (_X)->cc, (_X)->j, (_X)->us, \
443 (_X)->yysz, (_X)->clock);
444 #define DEBUG_TM(_X) \
445 elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\
446 (_X)->tm_sec, (_X)->tm_year,\
447 (_X)->tm_min, (_X)->tm_wday, (_X)->tm_hour, (_X)->tm_yday,\
448 (_X)->tm_mday, (_X)->tm_isdst, (_X)->tm_mon)
450 #define DEBUG_TMFC(_X)
455 * Datetime to char conversion
458 typedef struct TmToChar
460 struct pg_tm tm; /* classic 'tm' struct */
461 fsec_t fsec; /* fractional seconds */
462 char *tzn; /* timezone */
465 #define tmtcTm(_X) (&(_X)->tm)
466 #define tmtcTzn(_X) ((_X)->tzn)
467 #define tmtcFsec(_X) ((_X)->fsec)
469 #define ZERO_tm(_X) \
471 (_X)->tm_sec = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
472 (_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
473 (_X)->tm_mday = (_X)->tm_mon = 1; \
476 #define ZERO_tmtc(_X) \
478 ZERO_tm( tmtcTm(_X) ); \
480 tmtcTzn(_X) = NULL; \
484 * to_char(time) appears to to_char() as an interval, so this check
485 * is really for interval and time data types.
487 #define INVALID_FOR_INTERVAL \
491 (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
492 errmsg("invalid format specification for an interval value"), \
493 errhint("Intervals are not tied to specific calendar dates."))); \
496 /*****************************************************************************
497 * KeyWord definitions
498 *****************************************************************************/
504 #define DCH_S_FM 0x01
505 #define DCH_S_TH 0x02
506 #define DCH_S_th 0x04
507 #define DCH_S_SP 0x08
508 #define DCH_S_TM 0x10
514 #define S_THth(_s) ((((_s) & DCH_S_TH) || ((_s) & DCH_S_th)) ? 1 : 0)
515 #define S_TH(_s) (((_s) & DCH_S_TH) ? 1 : 0)
516 #define S_th(_s) (((_s) & DCH_S_th) ? 1 : 0)
517 #define S_TH_TYPE(_s) (((_s) & DCH_S_TH) ? TH_UPPER : TH_LOWER)
519 /* Oracle toggles FM behavior, we don't; see docs. */
520 #define S_FM(_s) (((_s) & DCH_S_FM) ? 1 : 0)
521 #define S_SP(_s) (((_s) & DCH_S_SP) ? 1 : 0)
522 #define S_TM(_s) (((_s) & DCH_S_TM) ? 1 : 0)
525 * Suffixes definition for DATE-TIME TO/FROM CHAR
528 static KeySuffix DCH_suff[] = {
529 {"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
530 {"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
531 {"TM", 2, DCH_S_TM, SUFFTYPE_PREFIX},
532 {"tm", 2, DCH_S_TM, SUFFTYPE_PREFIX},
533 {"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
534 {"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
535 {"SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX},
541 * Format-pictures (KeyWord).
543 * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
544 * complicated -to-> easy:
546 * (example: "DDD","DD","Day","D" )
548 * (this specific sort needs the algorithm for sequential search for strings,
549 * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
550 * or "HH12"? You must first try "HH12", because "HH" is in string, but
554 * - Position for the keyword is similar as position in the enum DCH/NUM_poz.
557 * For fast search is used the 'int index[]', index is ascii table from position
558 * 32 (' ') to 126 (~), in this index is DCH_ / NUM_ enums for each ASCII
559 * position or -1 if char is not used in the KeyWord. Search example for
561 * 1) see in index to index['M' - 32],
562 * 2) take keywords position (enum DCH_MI) from index
563 * 3) run sequential search in keywords[] from this position
584 DCH_FX, /* global suffix */
711 * KeyWords for DATE-TIME version
714 static const KeyWord DCH_keywords[] = {
715 /* name, len, id, is_digit, date_mode */
716 {"A.D.", 4, DCH_A_D, FALSE, FROM_CHAR_DATE_NONE}, /* A */
717 {"A.M.", 4, DCH_A_M, FALSE, FROM_CHAR_DATE_NONE},
718 {"AD", 2, DCH_AD, FALSE, FROM_CHAR_DATE_NONE},
719 {"AM", 2, DCH_AM, FALSE, FROM_CHAR_DATE_NONE},
720 {"B.C.", 4, DCH_B_C, FALSE, FROM_CHAR_DATE_NONE}, /* B */
721 {"BC", 2, DCH_BC, FALSE, FROM_CHAR_DATE_NONE},
722 {"CC", 2, DCH_CC, TRUE, FROM_CHAR_DATE_NONE}, /* C */
723 {"DAY", 3, DCH_DAY, FALSE, FROM_CHAR_DATE_NONE}, /* D */
724 {"DDD", 3, DCH_DDD, TRUE, FROM_CHAR_DATE_GREGORIAN},
725 {"DD", 2, DCH_DD, TRUE, FROM_CHAR_DATE_GREGORIAN},
726 {"DY", 2, DCH_DY, FALSE, FROM_CHAR_DATE_NONE},
727 {"Day", 3, DCH_Day, FALSE, FROM_CHAR_DATE_NONE},
728 {"Dy", 2, DCH_Dy, FALSE, FROM_CHAR_DATE_NONE},
729 {"D", 1, DCH_D, TRUE, FROM_CHAR_DATE_GREGORIAN},
730 {"FX", 2, DCH_FX, FALSE, FROM_CHAR_DATE_NONE}, /* F */
731 {"HH24", 4, DCH_HH24, TRUE, FROM_CHAR_DATE_NONE}, /* H */
732 {"HH12", 4, DCH_HH12, TRUE, FROM_CHAR_DATE_NONE},
733 {"HH", 2, DCH_HH, TRUE, FROM_CHAR_DATE_NONE},
734 {"IDDD", 4, DCH_IDDD, TRUE, FROM_CHAR_DATE_ISOWEEK}, /* I */
735 {"ID", 2, DCH_ID, TRUE, FROM_CHAR_DATE_ISOWEEK},
736 {"IW", 2, DCH_IW, TRUE, FROM_CHAR_DATE_ISOWEEK},
737 {"IYYY", 4, DCH_IYYY, TRUE, FROM_CHAR_DATE_ISOWEEK},
738 {"IYY", 3, DCH_IYY, TRUE, FROM_CHAR_DATE_ISOWEEK},
739 {"IY", 2, DCH_IY, TRUE, FROM_CHAR_DATE_ISOWEEK},
740 {"I", 1, DCH_I, TRUE, FROM_CHAR_DATE_ISOWEEK},
741 {"J", 1, DCH_J, TRUE, FROM_CHAR_DATE_NONE}, /* J */
742 {"MI", 2, DCH_MI, TRUE, FROM_CHAR_DATE_NONE}, /* M */
743 {"MM", 2, DCH_MM, TRUE, FROM_CHAR_DATE_GREGORIAN},
744 {"MONTH", 5, DCH_MONTH, FALSE, FROM_CHAR_DATE_GREGORIAN},
745 {"MON", 3, DCH_MON, FALSE, FROM_CHAR_DATE_GREGORIAN},
746 {"MS", 2, DCH_MS, TRUE, FROM_CHAR_DATE_NONE},
747 {"Month", 5, DCH_Month, FALSE, FROM_CHAR_DATE_GREGORIAN},
748 {"Mon", 3, DCH_Mon, FALSE, FROM_CHAR_DATE_GREGORIAN},
749 {"P.M.", 4, DCH_P_M, FALSE, FROM_CHAR_DATE_NONE}, /* P */
750 {"PM", 2, DCH_PM, FALSE, FROM_CHAR_DATE_NONE},
751 {"Q", 1, DCH_Q, TRUE, FROM_CHAR_DATE_NONE}, /* Q */
752 {"RM", 2, DCH_RM, FALSE, FROM_CHAR_DATE_GREGORIAN}, /* R */
753 {"SSSS", 4, DCH_SSSS, TRUE, FROM_CHAR_DATE_NONE}, /* S */
754 {"SS", 2, DCH_SS, TRUE, FROM_CHAR_DATE_NONE},
755 {"TZ", 2, DCH_TZ, FALSE, FROM_CHAR_DATE_NONE}, /* T */
756 {"US", 2, DCH_US, TRUE, FROM_CHAR_DATE_NONE}, /* U */
757 {"WW", 2, DCH_WW, TRUE, FROM_CHAR_DATE_GREGORIAN}, /* W */
758 {"W", 1, DCH_W, TRUE, FROM_CHAR_DATE_GREGORIAN},
759 {"Y,YYY", 5, DCH_Y_YYY, TRUE, FROM_CHAR_DATE_GREGORIAN}, /* Y */
760 {"YYYY", 4, DCH_YYYY, TRUE, FROM_CHAR_DATE_GREGORIAN},
761 {"YYY", 3, DCH_YYY, TRUE, FROM_CHAR_DATE_GREGORIAN},
762 {"YY", 2, DCH_YY, TRUE, FROM_CHAR_DATE_GREGORIAN},
763 {"Y", 1, DCH_Y, TRUE, FROM_CHAR_DATE_GREGORIAN},
764 {"a.d.", 4, DCH_a_d, FALSE, FROM_CHAR_DATE_NONE}, /* a */
765 {"a.m.", 4, DCH_a_m, FALSE, FROM_CHAR_DATE_NONE},
766 {"ad", 2, DCH_ad, FALSE, FROM_CHAR_DATE_NONE},
767 {"am", 2, DCH_am, FALSE, FROM_CHAR_DATE_NONE},
768 {"b.c.", 4, DCH_b_c, FALSE, FROM_CHAR_DATE_NONE}, /* b */
769 {"bc", 2, DCH_bc, FALSE, FROM_CHAR_DATE_NONE},
770 {"cc", 2, DCH_CC, TRUE, FROM_CHAR_DATE_NONE}, /* c */
771 {"day", 3, DCH_day, FALSE, FROM_CHAR_DATE_NONE}, /* d */
772 {"ddd", 3, DCH_DDD, TRUE, FROM_CHAR_DATE_GREGORIAN},
773 {"dd", 2, DCH_DD, TRUE, FROM_CHAR_DATE_GREGORIAN},
774 {"dy", 2, DCH_dy, FALSE, FROM_CHAR_DATE_NONE},
775 {"d", 1, DCH_D, TRUE, FROM_CHAR_DATE_GREGORIAN},
776 {"fx", 2, DCH_FX, FALSE, FROM_CHAR_DATE_NONE}, /* f */
777 {"hh24", 4, DCH_HH24, TRUE, FROM_CHAR_DATE_NONE}, /* h */
778 {"hh12", 4, DCH_HH12, TRUE, FROM_CHAR_DATE_NONE},
779 {"hh", 2, DCH_HH, TRUE, FROM_CHAR_DATE_NONE},
780 {"iddd", 4, DCH_IDDD, TRUE, FROM_CHAR_DATE_ISOWEEK}, /* i */
781 {"id", 2, DCH_ID, TRUE, FROM_CHAR_DATE_ISOWEEK},
782 {"iw", 2, DCH_IW, TRUE, FROM_CHAR_DATE_ISOWEEK},
783 {"iyyy", 4, DCH_IYYY, TRUE, FROM_CHAR_DATE_ISOWEEK},
784 {"iyy", 3, DCH_IYY, TRUE, FROM_CHAR_DATE_ISOWEEK},
785 {"iy", 2, DCH_IY, TRUE, FROM_CHAR_DATE_ISOWEEK},
786 {"i", 1, DCH_I, TRUE, FROM_CHAR_DATE_ISOWEEK},
787 {"j", 1, DCH_J, TRUE, FROM_CHAR_DATE_NONE}, /* j */
788 {"mi", 2, DCH_MI, TRUE, FROM_CHAR_DATE_NONE}, /* m */
789 {"mm", 2, DCH_MM, TRUE, FROM_CHAR_DATE_GREGORIAN},
790 {"month", 5, DCH_month, FALSE, FROM_CHAR_DATE_GREGORIAN},
791 {"mon", 3, DCH_mon, FALSE, FROM_CHAR_DATE_GREGORIAN},
792 {"ms", 2, DCH_MS, TRUE, FROM_CHAR_DATE_NONE},
793 {"p.m.", 4, DCH_p_m, FALSE, FROM_CHAR_DATE_NONE}, /* p */
794 {"pm", 2, DCH_pm, FALSE, FROM_CHAR_DATE_NONE},
795 {"q", 1, DCH_Q, TRUE, FROM_CHAR_DATE_NONE}, /* q */
796 {"rm", 2, DCH_rm, FALSE, FROM_CHAR_DATE_GREGORIAN}, /* r */
797 {"ssss", 4, DCH_SSSS, TRUE, FROM_CHAR_DATE_NONE}, /* s */
798 {"ss", 2, DCH_SS, TRUE, FROM_CHAR_DATE_NONE},
799 {"tz", 2, DCH_tz, FALSE, FROM_CHAR_DATE_NONE}, /* t */
800 {"us", 2, DCH_US, TRUE, FROM_CHAR_DATE_NONE}, /* u */
801 {"ww", 2, DCH_WW, TRUE, FROM_CHAR_DATE_GREGORIAN}, /* w */
802 {"w", 1, DCH_W, TRUE, FROM_CHAR_DATE_GREGORIAN},
803 {"y,yyy", 5, DCH_Y_YYY, TRUE, FROM_CHAR_DATE_GREGORIAN}, /* y */
804 {"yyyy", 4, DCH_YYYY, TRUE, FROM_CHAR_DATE_GREGORIAN},
805 {"yyy", 3, DCH_YYY, TRUE, FROM_CHAR_DATE_GREGORIAN},
806 {"yy", 2, DCH_YY, TRUE, FROM_CHAR_DATE_GREGORIAN},
807 {"y", 1, DCH_Y, TRUE, FROM_CHAR_DATE_GREGORIAN},
814 * KeyWords for NUMBER version
816 * The is_digit and date_mode fields are not relevant here.
819 static const KeyWord NUM_keywords[] = {
820 /* name, len, id is in Index */
821 {",", 1, NUM_COMMA}, /* , */
822 {".", 1, NUM_DEC}, /* . */
823 {"0", 1, NUM_0}, /* 0 */
824 {"9", 1, NUM_9}, /* 9 */
825 {"B", 1, NUM_B}, /* B */
826 {"C", 1, NUM_C}, /* C */
827 {"D", 1, NUM_D}, /* D */
828 {"EEEE", 4, NUM_E}, /* E */
829 {"FM", 2, NUM_FM}, /* F */
830 {"G", 1, NUM_G}, /* G */
831 {"L", 1, NUM_L}, /* L */
832 {"MI", 2, NUM_MI}, /* M */
833 {"PL", 2, NUM_PL}, /* P */
835 {"RN", 2, NUM_RN}, /* R */
836 {"SG", 2, NUM_SG}, /* S */
839 {"TH", 2, NUM_TH}, /* T */
840 {"V", 1, NUM_V}, /* V */
841 {"b", 1, NUM_B}, /* b */
842 {"c", 1, NUM_C}, /* c */
843 {"d", 1, NUM_D}, /* d */
844 {"eeee", 4, NUM_E}, /* e */
845 {"fm", 2, NUM_FM}, /* f */
846 {"g", 1, NUM_G}, /* g */
847 {"l", 1, NUM_L}, /* l */
848 {"mi", 2, NUM_MI}, /* m */
849 {"pl", 2, NUM_PL}, /* p */
851 {"rn", 2, NUM_rn}, /* r */
852 {"sg", 2, NUM_SG}, /* s */
855 {"th", 2, NUM_th}, /* t */
856 {"v", 1, NUM_V}, /* v */
864 * KeyWords index for DATE-TIME version
867 static const int DCH_index[KeyWord_INDEX_SIZE] = {
871 /*---- first 0..31 chars are skipped ----*/
873 -1, -1, -1, -1, -1, -1, -1, -1,
874 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
875 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
876 -1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1,
877 DCH_FX, -1, DCH_HH24, DCH_IDDD, DCH_J, -1, -1, DCH_MI, -1, -1,
878 DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, DCH_TZ, DCH_US, -1, DCH_WW, -1, DCH_Y_YYY,
879 -1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc,
880 DCH_day, -1, DCH_fx, -1, DCH_hh24, DCH_iddd, DCH_j, -1, -1, DCH_mi,
881 -1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, DCH_us, -1, DCH_ww,
882 -1, DCH_y_yyy, -1, -1, -1, -1
884 /*---- chars over 126 are skipped ----*/
888 * KeyWords index for NUMBER version
891 static const int NUM_index[KeyWord_INDEX_SIZE] = {
895 /*---- first 0..31 chars are skipped ----*/
897 -1, -1, -1, -1, -1, -1, -1, -1,
898 -1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1,
899 -1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1,
900 -1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E,
901 NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1,
902 NUM_PL, -1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1,
903 -1, -1, -1, -1, -1, -1, -1, -1, NUM_b, NUM_c,
904 NUM_d, NUM_e, NUM_fm, NUM_g, -1, -1, -1, -1, NUM_l, NUM_mi,
905 -1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1,
906 -1, -1, -1, -1, -1, -1
908 /*---- chars over 126 are skipped ----*/
912 * Number processor struct
915 typedef struct NUMProc
918 NUMDesc *Num; /* number description */
920 int sign, /* '-' or '+' */
921 sign_wrote, /* was sign write */
922 num_count, /* number of write digits */
923 num_in, /* is inside number */
924 num_curr, /* current position in number */
925 num_pre, /* space before first number */
927 read_dec, /* to_number - was read dec. point */
928 read_post, /* to_number - number of dec. digit */
929 read_pre; /* to_number - number non-dec. digit */
931 char *number, /* string with number */
932 *number_p, /* pointer to current number position */
933 *inout, /* in / out buffer */
934 *inout_p, /* pointer to current inout position */
935 *last_relevant, /* last relevant number after decimal point */
937 *L_negative_sign, /* Locale */
949 static const KeyWord *index_seq_search(char *str, const KeyWord *kw,
951 static KeySuffix *suff_search(char *str, KeySuffix *suf, int type);
952 static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
953 static void parse_format(FormatNode *node, char *str, const KeyWord *kw,
954 KeySuffix *suf, const int *index, int ver, NUMDesc *Num);
956 static void DCH_to_char(FormatNode *node, bool is_interval,
957 TmToChar *in, char *out, Oid collid);
958 static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out);
960 #ifdef DEBUG_TO_FROM_CHAR
961 static void dump_index(const KeyWord *k, const int *index);
962 static void dump_node(FormatNode *node, int max);
965 static char *get_th(char *num, int type);
966 static char *str_numth(char *dest, char *num, int type);
967 static int strspace_len(char *str);
968 static int strdigits_len(char *str);
969 static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode);
970 static void from_char_set_int(int *dest, const int value, const FormatNode *node);
971 static int from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node);
972 static int from_char_parse_int(int *dest, char **src, FormatNode *node);
973 static int seq_search(char *name, char **array, int type, int max, int *len);
974 static int from_char_seq_search(int *dest, char **src, char **array, int type, int max, FormatNode *node);
975 static void do_to_timestamp(text *date_txt, text *fmt,
976 struct pg_tm * tm, fsec_t *fsec);
977 static char *fill_str(char *str, int c, int max);
978 static FormatNode *NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree);
979 static char *int_to_roman(int number);
980 static void NUM_prepare_locale(NUMProc *Np);
981 static char *get_last_relevant_decnum(char *num);
982 static void NUM_numpart_from_char(NUMProc *Np, int id, int plen);
983 static void NUM_numpart_to_char(NUMProc *Np, int id);
984 static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
985 int plen, int sign, bool is_to_char, Oid collid);
986 static DCHCacheEntry *DCH_cache_search(char *str);
987 static DCHCacheEntry *DCH_cache_getnew(char *str);
989 static NUMCacheEntry *NUM_cache_search(char *str);
990 static NUMCacheEntry *NUM_cache_getnew(char *str);
991 static void NUM_cache_remove(NUMCacheEntry *ent);
995 * Fast sequential search, use index for data selection which
996 * go to seq. cycle (it is very fast for unwanted strings)
997 * (can't be used binary search in format parsing)
1000 static const KeyWord *
1001 index_seq_search(char *str, const KeyWord *kw, const int *index)
1005 if (!KeyWord_INDEX_FILTER(*str))
1008 if ((poz = *(index + (*str - ' '))) > -1)
1010 const KeyWord *k = kw + poz;
1014 if (!strncmp(str, k->name, k->len))
1019 } while (*str == *k->name);
1025 suff_search(char *str, KeySuffix *suf, int type)
1029 for (s = suf; s->name != NULL; s++)
1031 if (s->type != type)
1034 if (!strncmp(str, s->name, s->len))
1041 * Prepare NUMDesc (number description struct) via FormatNode struct
1045 NUMDesc_prepare(NUMDesc *num, FormatNode *n)
1048 if (n->type != NODE_TYPE_ACTION)
1052 * In case of an error, we need to remove the numeric from the cache. Use
1053 * a PG_TRY block to ensure that this happens.
1057 if (IS_EEEE(num) && n->key->id != NUM_E)
1059 (errcode(ERRCODE_SYNTAX_ERROR),
1060 errmsg("\"EEEE\" must be the last pattern used")));
1065 if (IS_BRACKET(num))
1067 (errcode(ERRCODE_SYNTAX_ERROR),
1068 errmsg("\"9\" must be ahead of \"PR\"")));
1074 if (IS_DECIMAL(num))
1081 if (IS_BRACKET(num))
1083 (errcode(ERRCODE_SYNTAX_ERROR),
1084 errmsg("\"0\" must be ahead of \"PR\"")));
1085 if (!IS_ZERO(num) && !IS_DECIMAL(num))
1087 num->flag |= NUM_F_ZERO;
1088 num->zero_start = num->pre + 1;
1090 if (!IS_DECIMAL(num))
1095 num->zero_end = num->pre + num->post;
1099 if (num->pre == 0 && num->post == 0 && (!IS_ZERO(num)))
1100 num->flag |= NUM_F_BLANK;
1104 num->flag |= NUM_F_LDECIMAL;
1105 num->need_locale = TRUE;
1107 if (IS_DECIMAL(num))
1109 (errcode(ERRCODE_SYNTAX_ERROR),
1110 errmsg("multiple decimal points")));
1113 (errcode(ERRCODE_SYNTAX_ERROR),
1114 errmsg("cannot use \"V\" and decimal point together")));
1115 num->flag |= NUM_F_DECIMAL;
1119 num->flag |= NUM_F_FILLMODE;
1125 (errcode(ERRCODE_SYNTAX_ERROR),
1126 errmsg("cannot use \"S\" twice")));
1127 if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
1129 (errcode(ERRCODE_SYNTAX_ERROR),
1130 errmsg("cannot use \"S\" and \"PL\"/\"MI\"/\"SG\"/\"PR\" together")));
1131 if (!IS_DECIMAL(num))
1133 num->lsign = NUM_LSIGN_PRE;
1134 num->pre_lsign_num = num->pre;
1135 num->need_locale = TRUE;
1136 num->flag |= NUM_F_LSIGN;
1138 else if (num->lsign == NUM_LSIGN_NONE)
1140 num->lsign = NUM_LSIGN_POST;
1141 num->need_locale = TRUE;
1142 num->flag |= NUM_F_LSIGN;
1149 (errcode(ERRCODE_SYNTAX_ERROR),
1150 errmsg("cannot use \"S\" and \"MI\" together")));
1151 num->flag |= NUM_F_MINUS;
1152 if (IS_DECIMAL(num))
1153 num->flag |= NUM_F_MINUS_POST;
1159 (errcode(ERRCODE_SYNTAX_ERROR),
1160 errmsg("cannot use \"S\" and \"PL\" together")));
1161 num->flag |= NUM_F_PLUS;
1162 if (IS_DECIMAL(num))
1163 num->flag |= NUM_F_PLUS_POST;
1169 (errcode(ERRCODE_SYNTAX_ERROR),
1170 errmsg("cannot use \"S\" and \"SG\" together")));
1171 num->flag |= NUM_F_MINUS;
1172 num->flag |= NUM_F_PLUS;
1176 if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num))
1178 (errcode(ERRCODE_SYNTAX_ERROR),
1179 errmsg("cannot use \"PR\" and \"S\"/\"PL\"/\"MI\"/\"SG\" together")));
1180 num->flag |= NUM_F_BRACKET;
1185 num->flag |= NUM_F_ROMAN;
1190 num->need_locale = TRUE;
1194 if (IS_DECIMAL(num))
1196 (errcode(ERRCODE_SYNTAX_ERROR),
1197 errmsg("cannot use \"V\" and decimal point together")));
1198 num->flag |= NUM_F_MULTI;
1204 (errcode(ERRCODE_SYNTAX_ERROR),
1205 errmsg("cannot use \"EEEE\" twice")));
1206 if (IS_BLANK(num) || IS_FILLMODE(num) || IS_LSIGN(num) ||
1207 IS_BRACKET(num) || IS_MINUS(num) || IS_PLUS(num) ||
1208 IS_ROMAN(num) || IS_MULTI(num))
1210 (errcode(ERRCODE_SYNTAX_ERROR),
1211 errmsg("\"EEEE\" is incompatible with other formats"),
1212 errdetail("\"EEEE\" may only be used together with digit and decimal point patterns.")));
1213 num->flag |= NUM_F_EEEE;
1219 NUM_cache_remove(last_NUMCacheEntry);
1229 * Format parser, search small keywords and keyword's suffixes, and make
1232 * for DATE-TIME & NUMBER version
1236 parse_format(FormatNode *node, char *str, const KeyWord *kw,
1237 KeySuffix *suf, const int *index, int ver, NUMDesc *Num)
1245 #ifdef DEBUG_TO_FROM_CHAR
1246 elog(DEBUG_elog_output, "to_char/number(): run parser");
1258 if (ver == DCH_TYPE && (s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL)
1268 if (*str && (n->key = index_seq_search(str, kw, index)) != NULL)
1270 n->type = NODE_TYPE_ACTION;
1277 * NUM version: Prepare global NUMDesc struct
1279 if (ver == NUM_TYPE)
1280 NUMDesc_prepare(Num, n);
1285 if (ver == DCH_TYPE && *str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL)
1295 * Special characters '\' and '"'
1297 if (*str == '"' && last != '\\')
1303 if (*str == '"' && x != '\\')
1308 else if (*str == '\\' && x != '\\')
1313 n->type = NODE_TYPE_CHAR;
1314 n->character = *str;
1324 else if (*str && *str == '\\' && last != '\\' && *(str + 1) == '"')
1331 n->type = NODE_TYPE_CHAR;
1332 n->character = *str;
1343 if (n->type == NODE_TYPE_ACTION)
1352 n->type = NODE_TYPE_END;
1358 * DEBUG: Dump the FormatNode Tree (debug)
1361 #ifdef DEBUG_TO_FROM_CHAR
1363 #define DUMP_THth(_suf) (S_TH(_suf) ? "TH" : (S_th(_suf) ? "th" : " "))
1364 #define DUMP_FM(_suf) (S_FM(_suf) ? "FM" : " ")
1367 dump_node(FormatNode *node, int max)
1372 elog(DEBUG_elog_output, "to_from-char(): DUMP FORMAT");
1374 for (a = 0, n = node; a <= max; n++, a++)
1376 if (n->type == NODE_TYPE_ACTION)
1377 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_ACTION '%s'\t(%s,%s)",
1378 a, n->key->name, DUMP_THth(n->suffix), DUMP_FM(n->suffix));
1379 else if (n->type == NODE_TYPE_CHAR)
1380 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_CHAR '%c'", a, n->character);
1381 else if (n->type == NODE_TYPE_END)
1383 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_END", a);
1387 elog(DEBUG_elog_output, "%d:\t unknown NODE!", a);
1392 /*****************************************************************************
1394 *****************************************************************************/
1397 * Return ST/ND/RD/TH for simple (1..9) numbers
1398 * type --> 0 upper, 1 lower
1402 get_th(char *num, int type)
1404 int len = strlen(num),
1408 last = *(num + (len - 1));
1409 if (!isdigit((unsigned char) last))
1411 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1412 errmsg("\"%s\" is not a number", num)));
1415 * All "teens" (<x>1[0-9]) get 'TH/th', while <x>[02-9][123] still get
1416 * 'ST/st', 'ND/nd', 'RD/rd', respectively
1418 if ((len > 1) && ((seclast = num[len - 2]) == '1'))
1424 if (type == TH_UPPER)
1428 if (type == TH_UPPER)
1432 if (type == TH_UPPER)
1436 if (type == TH_UPPER)
1444 * Convert string-number to ordinal string-number
1445 * type --> 0 upper, 1 lower
1449 str_numth(char *dest, char *num, int type)
1453 strcat(dest, get_th(num, type));
1458 * If the system provides the needed functions for wide-character manipulation
1459 * (which are all standardized by C99), then we implement upper/lower/initcap
1460 * using wide-character functions, if necessary. Otherwise we use the
1461 * traditional <ctype.h> functions, which of course will not work as desired
1462 * in multibyte character sets. Note that in either case we are effectively
1463 * assuming that the database character encoding matches the encoding implied
1468 * wide-character-aware lower function
1470 * We pass the number of bytes so we can pass varlena and char*
1471 * to this function. The result is a palloc'd, null-terminated string.
1474 str_tolower(const char *buff, size_t nbytes, Oid collid)
1477 pg_locale_t mylocale = 0;
1482 if (collid != DEFAULT_COLLATION_OID)
1483 mylocale = pg_newlocale_from_collation(collid);
1485 #ifdef USE_WIDE_UPPER_LOWER
1486 if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid))
1492 /* Overflow paranoia */
1493 if ((nbytes + 1) > (INT_MAX / sizeof(wchar_t)))
1495 (errcode(ERRCODE_OUT_OF_MEMORY),
1496 errmsg("out of memory")));
1498 /* Output workspace cannot have more codes than input bytes */
1499 workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
1501 char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
1503 for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
1504 #ifdef HAVE_LOCALE_T
1506 workspace[curr_char] = towlower_l(workspace[curr_char], mylocale);
1509 workspace[curr_char] = towlower(workspace[curr_char]);
1511 /* Make result large enough; case change might change number of bytes */
1512 result_size = curr_char * pg_database_encoding_max_length() + 1;
1513 result = palloc(result_size);
1515 wchar2char(result, workspace, result_size, collid);
1519 #endif /* USE_WIDE_UPPER_LOWER */
1523 result = pnstrdup(buff, nbytes);
1525 for (p = result; *p; p++)
1526 *p = pg_tolower((unsigned char) *p);
1533 * wide-character-aware upper function
1535 * We pass the number of bytes so we can pass varlena and char*
1536 * to this function. The result is a palloc'd, null-terminated string.
1539 str_toupper(const char *buff, size_t nbytes, Oid collid)
1542 pg_locale_t mylocale = 0;
1547 if (collid != DEFAULT_COLLATION_OID)
1548 mylocale = pg_newlocale_from_collation(collid);
1550 #ifdef USE_WIDE_UPPER_LOWER
1551 if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid))
1557 /* Overflow paranoia */
1558 if ((nbytes + 1) > (INT_MAX / sizeof(wchar_t)))
1560 (errcode(ERRCODE_OUT_OF_MEMORY),
1561 errmsg("out of memory")));
1563 /* Output workspace cannot have more codes than input bytes */
1564 workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
1566 char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
1568 for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
1569 #ifdef HAVE_LOCALE_T
1571 workspace[curr_char] = towupper_l(workspace[curr_char], mylocale);
1574 workspace[curr_char] = towupper(workspace[curr_char]);
1576 /* Make result large enough; case change might change number of bytes */
1577 result_size = curr_char * pg_database_encoding_max_length() + 1;
1578 result = palloc(result_size);
1580 wchar2char(result, workspace, result_size, collid);
1584 #endif /* USE_WIDE_UPPER_LOWER */
1588 result = pnstrdup(buff, nbytes);
1590 for (p = result; *p; p++)
1591 *p = pg_toupper((unsigned char) *p);
1598 * wide-character-aware initcap function
1600 * We pass the number of bytes so we can pass varlena and char*
1601 * to this function. The result is a palloc'd, null-terminated string.
1604 str_initcap(const char *buff, size_t nbytes, Oid collid)
1607 int wasalnum = false;
1608 pg_locale_t mylocale = 0;
1613 if (collid != DEFAULT_COLLATION_OID)
1614 mylocale = pg_newlocale_from_collation(collid);
1616 #ifdef USE_WIDE_UPPER_LOWER
1617 if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid))
1623 /* Overflow paranoia */
1624 if ((nbytes + 1) > (INT_MAX / sizeof(wchar_t)))
1626 (errcode(ERRCODE_OUT_OF_MEMORY),
1627 errmsg("out of memory")));
1629 /* Output workspace cannot have more codes than input bytes */
1630 workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
1632 char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
1634 for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
1636 #ifdef HAVE_LOCALE_T
1640 workspace[curr_char] = towlower_l(workspace[curr_char], mylocale);
1642 workspace[curr_char] = towupper_l(workspace[curr_char], mylocale);
1643 wasalnum = iswalnum_l(workspace[curr_char], mylocale);
1649 workspace[curr_char] = towlower(workspace[curr_char]);
1651 workspace[curr_char] = towupper(workspace[curr_char]);
1652 wasalnum = iswalnum(workspace[curr_char]);
1656 /* Make result large enough; case change might change number of bytes */
1657 result_size = curr_char * pg_database_encoding_max_length() + 1;
1658 result = palloc(result_size);
1660 wchar2char(result, workspace, result_size, collid);
1664 #endif /* USE_WIDE_UPPER_LOWER */
1668 result = pnstrdup(buff, nbytes);
1670 for (p = result; *p; p++)
1673 *p = pg_tolower((unsigned char) *p);
1675 *p = pg_toupper((unsigned char) *p);
1676 wasalnum = isalnum((unsigned char) *p);
1683 /* convenience routines for when the input is null-terminated */
1686 str_tolower_z(const char *buff, Oid collid)
1688 return str_tolower(buff, strlen(buff), collid);
1692 str_toupper_z(const char *buff, Oid collid)
1694 return str_toupper(buff, strlen(buff), collid);
1698 str_initcap_z(const char *buff, Oid collid)
1700 return str_initcap(buff, strlen(buff), collid);
1705 * Skip TM / th in FROM_CHAR
1708 #define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
1710 #ifdef DEBUG_TO_FROM_CHAR
1712 * DEBUG: Call for debug and for index checking; (Show ASCII char
1713 * and defined keyword for each used position
1717 dump_index(const KeyWord *k, const int *index)
1723 elog(DEBUG_elog_output, "TO-FROM_CHAR: Dump KeyWord Index:");
1725 for (i = 0; i < KeyWord_INDEX_SIZE; i++)
1729 elog(DEBUG_elog_output, "\t%c: %s, ", i + 32, k[index[i]].name);
1735 elog(DEBUG_elog_output, "\t(%d) %c %d", i, i + 32, index[i]);
1738 elog(DEBUG_elog_output, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d",
1744 * Return TRUE if next format picture is not digit value
1748 is_next_separator(FormatNode *n)
1750 if (n->type == NODE_TYPE_END)
1753 if (n->type == NODE_TYPE_ACTION && S_THth(n->suffix))
1761 /* end of format string is treated like a non-digit separator */
1762 if (n->type == NODE_TYPE_END)
1765 if (n->type == NODE_TYPE_ACTION)
1767 if (n->key->is_digit)
1772 else if (isdigit((unsigned char) n->character))
1775 return TRUE; /* some non-digit input (separator) */
1779 strspace_len(char *str)
1783 while (*str && isspace((unsigned char) *str))
1792 strdigits_len(char *str)
1797 len = strspace_len(str);
1800 while (*p && isdigit((unsigned char) *p) && len <= DCH_MAX_ITEM_SIZ)
1809 * Set the date mode of a from-char conversion.
1811 * Puke if the date mode has already been set, and the caller attempts to set
1812 * it to a conflicting mode.
1815 from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode)
1817 if (mode != FROM_CHAR_DATE_NONE)
1819 if (tmfc->mode == FROM_CHAR_DATE_NONE)
1821 else if (tmfc->mode != mode)
1823 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1824 errmsg("invalid combination of date conventions"),
1825 errhint("Do not mix Gregorian and ISO week date "
1826 "conventions in a formatting template.")));
1831 * Set the integer pointed to by 'dest' to the given value.
1833 * Puke if the destination integer has previously been set to some other
1837 from_char_set_int(int *dest, const int value, const FormatNode *node)
1839 if (*dest != 0 && *dest != value)
1841 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1842 errmsg("conflicting values for \"%s\" field in formatting string",
1844 errdetail("This value contradicts a previous setting for "
1845 "the same field type.")));
1850 * Read a single integer from the source string, into the int pointed to by
1851 * 'dest'. If 'dest' is NULL, the result is discarded.
1853 * In fixed-width mode (the node does not have the FM suffix), consume at most
1854 * 'len' characters. However, any leading whitespace isn't counted in 'len'.
1856 * We use strtol() to recover the integer value from the source string, in
1857 * accordance with the given FormatNode.
1859 * If the conversion completes successfully, src will have been advanced to
1860 * point at the character immediately following the last character used in the
1863 * Return the number of characters consumed.
1865 * Note that from_char_parse_int() provides a more convenient wrapper where
1866 * the length of the field is the same as the length of the format keyword (as
1870 from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node)
1873 char copy[DCH_MAX_ITEM_SIZ + 1];
1878 * Skip any whitespace before parsing the integer.
1880 *src += strspace_len(*src);
1882 Assert(len <= DCH_MAX_ITEM_SIZ);
1883 used = (int) strlcpy(copy, *src, len + 1);
1885 if (S_FM(node->suffix) || is_next_separator(node))
1888 * This node is in Fill Mode, or the next node is known to be a
1889 * non-digit value, so we just slurp as many characters as we can get.
1892 result = strtol(init, src, 10);
1897 * We need to pull exactly the number of characters given in 'len' out
1898 * of the string, and convert those.
1904 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1905 errmsg("source string too short for \"%s\" formatting field",
1907 errdetail("Field requires %d characters, but only %d "
1910 errhint("If your source string is not fixed-width, try "
1911 "using the \"FM\" modifier.")));
1914 result = strtol(copy, &last, 10);
1917 if (used > 0 && used < len)
1919 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1920 errmsg("invalid value \"%s\" for \"%s\"",
1921 copy, node->key->name),
1922 errdetail("Field requires %d characters, but only %d "
1923 "could be parsed.", len, used),
1924 errhint("If your source string is not fixed-width, try "
1925 "using the \"FM\" modifier.")));
1932 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1933 errmsg("invalid value \"%s\" for \"%s\"",
1934 copy, node->key->name),
1935 errdetail("Value must be an integer.")));
1937 if (errno == ERANGE || result < INT_MIN || result > INT_MAX)
1939 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1940 errmsg("value for \"%s\" in source string is out of range",
1942 errdetail("Value must be in the range %d to %d.",
1943 INT_MIN, INT_MAX)));
1946 from_char_set_int(dest, (int) result, node);
1951 * Call from_char_parse_int_len(), using the length of the format keyword as
1952 * the expected length of the field.
1954 * Don't call this function if the field differs in length from the format
1955 * keyword (as with HH24; the keyword length is 4, but the field length is 2).
1956 * In such cases, call from_char_parse_int_len() instead to specify the
1957 * required length explictly.
1960 from_char_parse_int(int *dest, char **src, FormatNode *node)
1962 return from_char_parse_int_len(dest, src, node->key->len, node);
1966 * Sequential search with to upper/lower conversion
1970 seq_search(char *name, char **array, int type, int max, int *len)
1983 /* set first char */
1984 if (type == ONE_UPPER || type == ALL_UPPER)
1985 *name = pg_toupper((unsigned char) *name);
1986 else if (type == ALL_LOWER)
1987 *name = pg_tolower((unsigned char) *name);
1989 for (last = 0, a = array; *a != NULL; a++)
1991 /* comperate first chars */
1995 for (i = 1, p = *a + 1, n = name + 1;; n++, p++, i++)
1997 /* search fragment (max) only */
1998 if (max && i == max)
2009 /* Not found in array 'a' */
2014 * Convert (but convert new chars only)
2018 if (type == ONE_UPPER || type == ALL_LOWER)
2019 *n = pg_tolower((unsigned char) *n);
2020 else if (type == ALL_UPPER)
2021 *n = pg_toupper((unsigned char) *n);
2025 #ifdef DEBUG_TO_FROM_CHAR
2026 elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)",
2038 * Perform a sequential search in 'array' for text matching the first 'max'
2039 * characters of the source string.
2041 * If a match is found, copy the array index of the match into the integer
2042 * pointed to by 'dest', advance 'src' to the end of the part of the string
2043 * which matched, and return the number of characters consumed.
2045 * If the string doesn't match, throw an error.
2048 from_char_seq_search(int *dest, char **src, char **array, int type, int max,
2053 *dest = seq_search(*src, array, type, max, &len);
2056 char copy[DCH_MAX_ITEM_SIZ + 1];
2058 Assert(max <= DCH_MAX_ITEM_SIZ);
2059 strlcpy(copy, *src, max + 1);
2062 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2063 errmsg("invalid value \"%s\" for \"%s\"",
2064 copy, node->key->name),
2065 errdetail("The given value did not match any of the allowed "
2066 "values for this field.")));
2073 * Process a TmToChar struct as denoted by a list of FormatNodes.
2074 * The formatted data is written to the string pointed to by 'out'.
2078 DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid collid)
2082 struct pg_tm *tm = &in->tm;
2085 /* cache localized days and months */
2086 cache_locale_time();
2089 for (n = node; n->type != NODE_TYPE_END; n++)
2091 if (n->type != NODE_TYPE_ACTION)
2102 strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
2103 ? P_M_STR : A_M_STR);
2108 strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
2114 strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
2115 ? p_m_STR : a_m_STR);
2120 strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
2128 * display time as shown on a 12-hour clock, even for
2131 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
2132 tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ? 12 :
2133 tm->tm_hour % (HOURS_PER_DAY / 2));
2134 if (S_THth(n->suffix))
2135 str_numth(s, s, S_TH_TYPE(n->suffix));
2139 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_hour);
2140 if (S_THth(n->suffix))
2141 str_numth(s, s, S_TH_TYPE(n->suffix));
2145 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_min);
2146 if (S_THth(n->suffix))
2147 str_numth(s, s, S_TH_TYPE(n->suffix));
2151 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_sec);
2152 if (S_THth(n->suffix))
2153 str_numth(s, s, S_TH_TYPE(n->suffix));
2156 case DCH_MS: /* millisecond */
2157 #ifdef HAVE_INT64_TIMESTAMP
2158 sprintf(s, "%03d", (int) (in->fsec / INT64CONST(1000)));
2160 /* No rint() because we can't overflow and we might print US */
2161 sprintf(s, "%03d", (int) (in->fsec * 1000));
2163 if (S_THth(n->suffix))
2164 str_numth(s, s, S_TH_TYPE(n->suffix));
2167 case DCH_US: /* microsecond */
2168 #ifdef HAVE_INT64_TIMESTAMP
2169 sprintf(s, "%06d", (int) in->fsec);
2171 /* don't use rint() because we can't overflow 1000 */
2172 sprintf(s, "%06d", (int) (in->fsec * 1000000));
2174 if (S_THth(n->suffix))
2175 str_numth(s, s, S_TH_TYPE(n->suffix));
2179 sprintf(s, "%d", tm->tm_hour * SECS_PER_HOUR +
2180 tm->tm_min * SECS_PER_MINUTE +
2182 if (S_THth(n->suffix))
2183 str_numth(s, s, S_TH_TYPE(n->suffix));
2187 INVALID_FOR_INTERVAL;
2190 char *p = str_tolower_z(tmtcTzn(in), collid);
2198 INVALID_FOR_INTERVAL;
2201 strcpy(s, tmtcTzn(in));
2207 INVALID_FOR_INTERVAL;
2208 strcpy(s, (tm->tm_year <= 0 ? B_C_STR : A_D_STR));
2213 INVALID_FOR_INTERVAL;
2214 strcpy(s, (tm->tm_year <= 0 ? BC_STR : AD_STR));
2219 INVALID_FOR_INTERVAL;
2220 strcpy(s, (tm->tm_year <= 0 ? b_c_STR : a_d_STR));
2225 INVALID_FOR_INTERVAL;
2226 strcpy(s, (tm->tm_year <= 0 ? bc_STR : ad_STR));
2230 INVALID_FOR_INTERVAL;
2233 if (S_TM(n->suffix))
2234 strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1], collid));
2236 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
2237 str_toupper_z(months_full[tm->tm_mon - 1], collid));
2241 INVALID_FOR_INTERVAL;
2244 if (S_TM(n->suffix))
2245 strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1], collid));
2247 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
2251 INVALID_FOR_INTERVAL;
2254 if (S_TM(n->suffix))
2255 strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1], collid));
2258 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
2259 *s = pg_tolower((unsigned char) *s);
2264 INVALID_FOR_INTERVAL;
2267 if (S_TM(n->suffix))
2268 strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid));
2270 strcpy(s, str_toupper_z(months[tm->tm_mon - 1], collid));
2274 INVALID_FOR_INTERVAL;
2277 if (S_TM(n->suffix))
2278 strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid));
2280 strcpy(s, months[tm->tm_mon - 1]);
2284 INVALID_FOR_INTERVAL;
2287 if (S_TM(n->suffix))
2288 strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid));
2291 strcpy(s, months[tm->tm_mon - 1]);
2292 *s = pg_tolower((unsigned char) *s);
2297 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_mon);
2298 if (S_THth(n->suffix))
2299 str_numth(s, s, S_TH_TYPE(n->suffix));
2303 INVALID_FOR_INTERVAL;
2304 if (S_TM(n->suffix))
2305 strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday], collid));
2307 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
2308 str_toupper_z(days[tm->tm_wday], collid));
2312 INVALID_FOR_INTERVAL;
2313 if (S_TM(n->suffix))
2314 strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday], collid));
2316 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
2320 INVALID_FOR_INTERVAL;
2321 if (S_TM(n->suffix))
2322 strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday], collid));
2325 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
2326 *s = pg_tolower((unsigned char) *s);
2331 INVALID_FOR_INTERVAL;
2332 if (S_TM(n->suffix))
2333 strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday], collid));
2335 strcpy(s, str_toupper_z(days_short[tm->tm_wday], collid));
2339 INVALID_FOR_INTERVAL;
2340 if (S_TM(n->suffix))
2341 strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday], collid));
2343 strcpy(s, days_short[tm->tm_wday]);
2347 INVALID_FOR_INTERVAL;
2348 if (S_TM(n->suffix))
2349 strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday], collid));
2352 strcpy(s, days_short[tm->tm_wday]);
2353 *s = pg_tolower((unsigned char) *s);
2359 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 3,
2360 (n->key->id == DCH_DDD) ?
2362 date2isoyearday(tm->tm_year, tm->tm_mon, tm->tm_mday));
2363 if (S_THth(n->suffix))
2364 str_numth(s, s, S_TH_TYPE(n->suffix));
2368 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_mday);
2369 if (S_THth(n->suffix))
2370 str_numth(s, s, S_TH_TYPE(n->suffix));
2374 INVALID_FOR_INTERVAL;
2375 sprintf(s, "%d", tm->tm_wday + 1);
2376 if (S_THth(n->suffix))
2377 str_numth(s, s, S_TH_TYPE(n->suffix));
2381 INVALID_FOR_INTERVAL;
2382 sprintf(s, "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
2383 if (S_THth(n->suffix))
2384 str_numth(s, s, S_TH_TYPE(n->suffix));
2388 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
2389 (tm->tm_yday - 1) / 7 + 1);
2390 if (S_THth(n->suffix))
2391 str_numth(s, s, S_TH_TYPE(n->suffix));
2395 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
2396 date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
2397 if (S_THth(n->suffix))
2398 str_numth(s, s, S_TH_TYPE(n->suffix));
2404 sprintf(s, "%d", (tm->tm_mon - 1) / 3 + 1);
2405 if (S_THth(n->suffix))
2406 str_numth(s, s, S_TH_TYPE(n->suffix));
2410 if (is_interval) /* straight calculation */
2411 i = tm->tm_year / 100;
2412 else /* century 21 starts in 2001 */
2413 i = (tm->tm_year - 1) / 100 + 1;
2414 if (i <= 99 && i >= -99)
2415 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, i);
2417 sprintf(s, "%d", i);
2418 if (S_THth(n->suffix))
2419 str_numth(s, s, S_TH_TYPE(n->suffix));
2423 i = ADJUST_YEAR(tm->tm_year, is_interval) / 1000;
2424 sprintf(s, "%d,%03d", i,
2425 ADJUST_YEAR(tm->tm_year, is_interval) - (i * 1000));
2426 if (S_THth(n->suffix))
2427 str_numth(s, s, S_TH_TYPE(n->suffix));
2433 S_FM(n->suffix) ? 0 : 4,
2434 (n->key->id == DCH_YYYY ?
2435 ADJUST_YEAR(tm->tm_year, is_interval) :
2436 ADJUST_YEAR(date2isoyear(tm->tm_year,
2440 if (S_THth(n->suffix))
2441 str_numth(s, s, S_TH_TYPE(n->suffix));
2447 S_FM(n->suffix) ? 0 : 3,
2448 (n->key->id == DCH_YYY ?
2449 ADJUST_YEAR(tm->tm_year, is_interval) :
2450 ADJUST_YEAR(date2isoyear(tm->tm_year,
2453 is_interval)) % 1000);
2454 if (S_THth(n->suffix))
2455 str_numth(s, s, S_TH_TYPE(n->suffix));
2461 S_FM(n->suffix) ? 0 : 2,
2462 (n->key->id == DCH_YY ?
2463 ADJUST_YEAR(tm->tm_year, is_interval) :
2464 ADJUST_YEAR(date2isoyear(tm->tm_year,
2467 is_interval)) % 100);
2468 if (S_THth(n->suffix))
2469 str_numth(s, s, S_TH_TYPE(n->suffix));
2475 (n->key->id == DCH_Y ?
2476 ADJUST_YEAR(tm->tm_year, is_interval) :
2477 ADJUST_YEAR(date2isoyear(tm->tm_year,
2480 is_interval)) % 10);
2481 if (S_THth(n->suffix))
2482 str_numth(s, s, S_TH_TYPE(n->suffix));
2488 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4,
2489 rm_months_upper[12 - tm->tm_mon]);
2495 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4,
2496 rm_months_lower[12 - tm->tm_mon]);
2500 sprintf(s, "%d", (tm->tm_mday - 1) / 7 + 1);
2501 if (S_THth(n->suffix))
2502 str_numth(s, s, S_TH_TYPE(n->suffix));
2506 sprintf(s, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
2507 if (S_THth(n->suffix))
2508 str_numth(s, s, S_TH_TYPE(n->suffix));
2518 * Process a string as denoted by a list of FormatNodes.
2519 * The TmFromChar struct pointed to by 'out' is populated with the results.
2521 * Note: we currently don't have any to_interval() function, so there
2522 * is no need here for INVALID_FOR_INTERVAL checks.
2526 DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
2532 bool fx_mode = false;
2534 for (n = node, s = in; n->type != NODE_TYPE_END && *s != '\0'; n++)
2536 if (n->type != NODE_TYPE_ACTION)
2539 /* Ignore spaces when not in FX (fixed width) mode */
2540 if (isspace((unsigned char) n->character) && !fx_mode)
2542 while (*s != '\0' && isspace((unsigned char) *s))
2548 from_char_set_mode(out, n->key->date_mode);
2559 from_char_seq_search(&value, &s, ampm_strings_long,
2560 ALL_UPPER, n->key->len, n);
2561 from_char_set_int(&out->pm, value % 2, n);
2562 out->clock = CLOCK_12_HOUR;
2568 from_char_seq_search(&value, &s, ampm_strings,
2569 ALL_UPPER, n->key->len, n);
2570 from_char_set_int(&out->pm, value % 2, n);
2571 out->clock = CLOCK_12_HOUR;
2575 from_char_parse_int_len(&out->hh, &s, 2, n);
2576 out->clock = CLOCK_12_HOUR;
2577 s += SKIP_THth(n->suffix);
2580 from_char_parse_int_len(&out->hh, &s, 2, n);
2581 s += SKIP_THth(n->suffix);
2584 from_char_parse_int(&out->mi, &s, n);
2585 s += SKIP_THth(n->suffix);
2588 from_char_parse_int(&out->ss, &s, n);
2589 s += SKIP_THth(n->suffix);
2591 case DCH_MS: /* millisecond */
2592 len = from_char_parse_int_len(&out->ms, &s, 3, n);
2595 * 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
2597 out->ms *= len == 1 ? 100 :
2600 s += SKIP_THth(n->suffix);
2602 case DCH_US: /* microsecond */
2603 len = from_char_parse_int_len(&out->us, &s, 6, n);
2605 out->us *= len == 1 ? 100000 :
2611 s += SKIP_THth(n->suffix);
2614 from_char_parse_int(&out->ssss, &s, n);
2615 s += SKIP_THth(n->suffix);
2620 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2621 errmsg("\"TZ\"/\"tz\" format patterns are not supported in to_date")));
2626 from_char_seq_search(&value, &s, adbc_strings_long,
2627 ALL_UPPER, n->key->len, n);
2628 from_char_set_int(&out->bc, value % 2, n);
2634 from_char_seq_search(&value, &s, adbc_strings,
2635 ALL_UPPER, n->key->len, n);
2636 from_char_set_int(&out->bc, value % 2, n);
2641 from_char_seq_search(&value, &s, months_full, ONE_UPPER,
2643 from_char_set_int(&out->mm, value + 1, n);
2648 from_char_seq_search(&value, &s, months, ONE_UPPER,
2650 from_char_set_int(&out->mm, value + 1, n);
2653 from_char_parse_int(&out->mm, &s, n);
2654 s += SKIP_THth(n->suffix);
2659 from_char_seq_search(&value, &s, days, ONE_UPPER,
2661 from_char_set_int(&out->d, value, n);
2666 from_char_seq_search(&value, &s, days, ONE_UPPER,
2668 from_char_set_int(&out->d, value, n);
2671 from_char_parse_int(&out->ddd, &s, n);
2672 s += SKIP_THth(n->suffix);
2675 from_char_parse_int_len(&out->ddd, &s, 3, n);
2676 s += SKIP_THth(n->suffix);
2679 from_char_parse_int(&out->dd, &s, n);
2680 s += SKIP_THth(n->suffix);
2683 from_char_parse_int(&out->d, &s, n);
2685 s += SKIP_THth(n->suffix);
2688 from_char_parse_int_len(&out->d, &s, 1, n);
2689 s += SKIP_THth(n->suffix);
2693 from_char_parse_int(&out->ww, &s, n);
2694 s += SKIP_THth(n->suffix);
2699 * We ignore 'Q' when converting to date because it is unclear
2700 * which date in the quarter to use, and some people specify
2701 * both quarter and month, so if it was honored it might
2702 * conflict with the supplied month. That is also why we don't
2705 * We still parse the source string for an integer, but it
2706 * isn't stored anywhere in 'out'.
2708 from_char_parse_int((int *) NULL, &s, n);
2709 s += SKIP_THth(n->suffix);
2712 from_char_parse_int(&out->cc, &s, n);
2713 s += SKIP_THth(n->suffix);
2721 matched = sscanf(s, "%d,%03d", &millenia, &years);
2724 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2725 errmsg("invalid input string for \"Y,YYY\"")));
2726 years += (millenia * 1000);
2727 from_char_set_int(&out->year, years, n);
2729 s += strdigits_len(s) + 4 + SKIP_THth(n->suffix);
2734 from_char_parse_int(&out->year, &s, n);
2736 s += SKIP_THth(n->suffix);
2740 from_char_parse_int(&out->year, &s, n);
2744 * 3-digit year: '100' ... '999' = 1100 ... 1999 '000' ...
2745 * '099' = 2000 ... 2099
2747 if (out->year >= 100)
2751 s += SKIP_THth(n->suffix);
2755 from_char_parse_int(&out->year, &s, n);
2759 * 2-digit year: '00' ... '69' = 2000 ... 2069 '70' ... '99'
2766 s += SKIP_THth(n->suffix);
2770 from_char_parse_int(&out->year, &s, n);
2774 * 1-digit year: always +2000
2777 s += SKIP_THth(n->suffix);
2780 from_char_seq_search(&value, &s, rm_months_upper,
2781 ALL_UPPER, MAX_RM_LEN, n);
2782 from_char_set_int(&out->mm, 12 - value, n);
2785 from_char_seq_search(&value, &s, rm_months_lower,
2786 ALL_LOWER, MAX_RM_LEN, n);
2787 from_char_set_int(&out->mm, 12 - value, n);
2790 from_char_parse_int(&out->w, &s, n);
2791 s += SKIP_THth(n->suffix);
2794 from_char_parse_int(&out->j, &s, n);
2795 s += SKIP_THth(n->suffix);
2801 static DCHCacheEntry *
2802 DCH_cache_getnew(char *str)
2806 /* counter overflow check - paranoia? */
2807 if (DCHCounter >= (INT_MAX - DCH_CACHE_FIELDS - 1))
2811 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2812 ent->age = (++DCHCounter);
2816 * If cache is full, remove oldest entry
2818 if (n_DCHCache > DCH_CACHE_FIELDS)
2820 DCHCacheEntry *old = DCHCache + 0;
2822 #ifdef DEBUG_TO_FROM_CHAR
2823 elog(DEBUG_elog_output, "cache is full (%d)", n_DCHCache);
2825 for (ent = DCHCache + 1; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2827 if (ent->age < old->age)
2830 #ifdef DEBUG_TO_FROM_CHAR
2831 elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age);
2833 StrNCpy(old->str, str, DCH_CACHE_SIZE + 1);
2834 /* old->format fill parser */
2835 old->age = (++DCHCounter);
2840 #ifdef DEBUG_TO_FROM_CHAR
2841 elog(DEBUG_elog_output, "NEW (%d)", n_DCHCache);
2843 ent = DCHCache + n_DCHCache;
2844 StrNCpy(ent->str, str, DCH_CACHE_SIZE + 1);
2845 /* ent->format fill parser */
2846 ent->age = (++DCHCounter);
2852 static DCHCacheEntry *
2853 DCH_cache_search(char *str)
2858 /* counter overflow check - paranoia? */
2859 if (DCHCounter >= (INT_MAX - DCH_CACHE_FIELDS - 1))
2863 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2864 ent->age = (++DCHCounter);
2867 for (i = 0, ent = DCHCache; i < n_DCHCache; i++, ent++)
2869 if (strcmp(ent->str, str) == 0)
2871 ent->age = (++DCHCounter);
2880 * Format a date/time or interval into a string according to fmt.
2881 * We parse fmt into a list of FormatNodes. This is then passed to DCH_to_char
2885 datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval, Oid collid)
2895 * Convert fmt to C string
2897 fmt_str = text_to_cstring(fmt);
2898 fmt_len = strlen(fmt_str);
2901 * Allocate workspace for result as C string
2903 result = palloc((fmt_len * DCH_MAX_ITEM_SIZ) + 1);
2907 * Allocate new memory if format picture is bigger than static cache and
2908 * not use cache (call parser always)
2910 if (fmt_len > DCH_CACHE_SIZE)
2912 format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
2915 parse_format(format, fmt_str, DCH_keywords,
2916 DCH_suff, DCH_index, DCH_TYPE, NULL);
2918 (format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
2929 if ((ent = DCH_cache_search(fmt_str)) == NULL)
2931 ent = DCH_cache_getnew(fmt_str);
2934 * Not in the cache, must run parser and save a new format-picture
2937 parse_format(ent->format, fmt_str, DCH_keywords,
2938 DCH_suff, DCH_index, DCH_TYPE, NULL);
2940 (ent->format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
2942 #ifdef DEBUG_TO_FROM_CHAR
2943 /* dump_node(ent->format, fmt_len); */
2944 /* dump_index(DCH_keywords, DCH_index); */
2947 format = ent->format;
2950 /* The real work is here */
2951 DCH_to_char(format, is_interval, tmtc, result, collid);
2958 /* convert C-string result to TEXT format */
2959 res = cstring_to_text(result);
2965 /****************************************************************************
2967 ***************************************************************************/
2969 /* -------------------
2970 * TIMESTAMP to_char()
2971 * -------------------
2974 timestamp_to_char(PG_FUNCTION_ARGS)
2976 Timestamp dt = PG_GETARG_TIMESTAMP(0);
2977 text *fmt = PG_GETARG_TEXT_P(1),
2983 if ((VARSIZE(fmt) - VARHDRSZ) <= 0 || TIMESTAMP_NOT_FINITE(dt))
2989 if (timestamp2tm(dt, NULL, tm, &tmtcFsec(&tmtc), NULL, NULL) != 0)
2991 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2992 errmsg("timestamp out of range")));
2994 thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
2995 tm->tm_wday = (thisdate + 1) % 7;
2996 tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
2998 if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
3001 PG_RETURN_TEXT_P(res);
3005 timestamptz_to_char(PG_FUNCTION_ARGS)
3007 TimestampTz dt = PG_GETARG_TIMESTAMP(0);
3008 text *fmt = PG_GETARG_TEXT_P(1),
3015 if ((VARSIZE(fmt) - VARHDRSZ) <= 0 || TIMESTAMP_NOT_FINITE(dt))
3021 if (timestamp2tm(dt, &tz, tm, &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0)
3023 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3024 errmsg("timestamp out of range")));
3026 thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3027 tm->tm_wday = (thisdate + 1) % 7;
3028 tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
3030 if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
3033 PG_RETURN_TEXT_P(res);
3037 /* -------------------
3038 * INTERVAL to_char()
3039 * -------------------
3042 interval_to_char(PG_FUNCTION_ARGS)
3044 Interval *it = PG_GETARG_INTERVAL_P(0);
3045 text *fmt = PG_GETARG_TEXT_P(1),
3050 if ((VARSIZE(fmt) - VARHDRSZ) <= 0)
3056 if (interval2tm(*it, tm, &tmtcFsec(&tmtc)) != 0)
3059 /* wday is meaningless, yday approximates the total span in days */
3060 tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday;
3062 if (!(res = datetime_to_char_body(&tmtc, fmt, true, PG_GET_COLLATION())))
3065 PG_RETURN_TEXT_P(res);
3068 /* ---------------------
3071 * Make Timestamp from date_str which is formatted at argument 'fmt'
3072 * ( to_timestamp is reverse to_char() )
3073 * ---------------------
3076 to_timestamp(PG_FUNCTION_ARGS)
3078 text *date_txt = PG_GETARG_TEXT_P(0);
3079 text *fmt = PG_GETARG_TEXT_P(1);
3085 do_to_timestamp(date_txt, fmt, &tm, &fsec);
3087 tz = DetermineTimeZoneOffset(&tm, session_timezone);
3089 if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
3091 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3092 errmsg("timestamp out of range")));
3094 PG_RETURN_TIMESTAMP(result);
3099 * Make Date from date_str which is formated at argument 'fmt'
3103 to_date(PG_FUNCTION_ARGS)
3105 text *date_txt = PG_GETARG_TEXT_P(0);
3106 text *fmt = PG_GETARG_TEXT_P(1);
3111 do_to_timestamp(date_txt, fmt, &tm, &fsec);
3113 result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
3115 PG_RETURN_DATEADT(result);
3119 * do_to_timestamp: shared code for to_timestamp and to_date
3121 * Parse the 'date_txt' according to 'fmt', return results as a struct pg_tm
3122 * and fractional seconds.
3124 * We parse 'fmt' into a list of FormatNodes, which is then passed to
3125 * DCH_from_char to populate a TmFromChar with the parsed contents of
3128 * The TmFromChar is then analysed and converted into the final results in
3129 * struct 'tm' and 'fsec'.
3131 * This function does very little error checking, e.g.
3132 * to_timestamp('20096040','YYYYMMDD') works
3135 do_to_timestamp(text *date_txt, text *fmt,
3136 struct pg_tm * tm, fsec_t *fsec)
3146 fmt_len = VARSIZE_ANY_EXHDR(fmt);
3154 fmt_str = text_to_cstring(fmt);
3157 * Allocate new memory if format picture is bigger than static cache
3158 * and not use cache (call parser always)
3160 if (fmt_len > DCH_CACHE_SIZE)
3162 format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
3165 parse_format(format, fmt_str, DCH_keywords,
3166 DCH_suff, DCH_index, DCH_TYPE, NULL);
3168 (format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
3179 if ((ent = DCH_cache_search(fmt_str)) == NULL)
3181 ent = DCH_cache_getnew(fmt_str);
3184 * Not in the cache, must run parser and save a new
3185 * format-picture to the cache.
3187 parse_format(ent->format, fmt_str, DCH_keywords,
3188 DCH_suff, DCH_index, DCH_TYPE, NULL);
3190 (ent->format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
3191 #ifdef DEBUG_TO_FROM_CHAR
3192 /* dump_node(ent->format, fmt_len); */
3193 /* dump_index(DCH_keywords, DCH_index); */
3196 format = ent->format;
3199 #ifdef DEBUG_TO_FROM_CHAR
3200 /* dump_node(format, fmt_len); */
3203 date_str = text_to_cstring(date_txt);
3205 DCH_from_char(format, date_str, &tmfc);
3216 * Convert values that user define for FROM_CHAR (to_date/to_timestamp) to
3223 tm->tm_hour = x / SECS_PER_HOUR;
3225 tm->tm_min = x / SECS_PER_MINUTE;
3226 x %= SECS_PER_MINUTE;
3231 tm->tm_sec = tmfc.ss;
3233 tm->tm_min = tmfc.mi;
3235 tm->tm_hour = tmfc.hh;
3237 if (tmfc.clock == CLOCK_12_HOUR)
3239 if (tm->tm_hour < 1 || tm->tm_hour > 12)
3241 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3242 errmsg("hour \"%d\" is invalid for the 12-hour clock",
3244 errhint("Use the 24-hour clock, or give an hour between 1 and 12.")));
3246 if (tmfc.pm && tm->tm_hour < 12)
3248 else if (!tmfc.pm && tm->tm_hour == 12)
3255 * If CC and YY (or Y) are provided, use YY as 2 low-order digits for
3256 * the year in the given century. Keep in mind that the 21st century
3257 * runs from 2001-2100, not 2000-2099.
3259 * If a 4-digit year is provided, we use that and ignore CC.
3261 if (tmfc.cc && tmfc.yysz <= 2)
3263 tm->tm_year = tmfc.year % 100;
3265 tm->tm_year += (tmfc.cc - 1) * 100;
3267 tm->tm_year = tmfc.cc * 100;
3270 tm->tm_year = tmfc.year;
3272 else if (tmfc.cc) /* use first year of century */
3273 tm->tm_year = (tmfc.cc - 1) * 100 + 1;
3277 if (tm->tm_year > 0)
3278 tm->tm_year = -(tm->tm_year - 1);
3281 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3282 errmsg("inconsistent use of year %04d and \"BC\"",
3287 j2date(tmfc.j, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3291 if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
3294 * If tmfc.d is not set, then the date is left at the beginning of
3295 * the ISO week (Monday).
3298 isoweekdate2date(tmfc.ww, tmfc.d, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3300 isoweek2date(tmfc.ww, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3303 tmfc.ddd = (tmfc.ww - 1) * 7 + 1;
3307 tmfc.dd = (tmfc.w - 1) * 7 + 1;
3309 tm->tm_wday = tmfc.d;
3311 tm->tm_mday = tmfc.dd;
3313 tm->tm_yday = tmfc.ddd;
3315 tm->tm_mon = tmfc.mm;
3317 if (tmfc.ddd && (tm->tm_mon <= 1 || tm->tm_mday <= 1))
3320 * The month and day field have not been set, so we use the
3321 * day-of-year field to populate them. Depending on the date mode,
3322 * this field may be interpreted as a Gregorian day-of-year, or an ISO
3323 * week date day-of-year.
3326 if (!tm->tm_year && !tmfc.bc)
3328 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3329 errmsg("cannot calculate day of year without year information")));
3331 if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
3333 int j0; /* zeroth day of the ISO year, in Julian */
3335 j0 = isoweek2j(tm->tm_year, 1) - 1;
3337 j2date(j0 + tmfc.ddd, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3344 static const int ysum[2][13] = {
3345 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
3346 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
3348 y = ysum[isleap(tm->tm_year)];
3350 for (i = 1; i <= 12; i++)
3352 if (tmfc.ddd < y[i])
3355 if (tm->tm_mon <= 1)
3358 if (tm->tm_mday <= 1)
3359 tm->tm_mday = tmfc.ddd - y[i - 1];
3363 #ifdef HAVE_INT64_TIMESTAMP
3365 *fsec += tmfc.ms * 1000;
3370 *fsec += (double) tmfc.ms / 1000;
3372 *fsec += (double) tmfc.us / 1000000;
3379 /**********************************************************************
3380 * the NUMBER version part
3381 *********************************************************************/
3385 fill_str(char *str, int c, int max)
3387 memset(str, c, max);
3388 *(str + max) = '\0';
3392 #define zeroize_NUM(_n) \
3398 (_n)->pre_lsign_num = 0; \
3399 (_n)->need_locale = 0; \
3401 (_n)->zero_start = 0; \
3402 (_n)->zero_end = 0; \
3405 static NUMCacheEntry *
3406 NUM_cache_getnew(char *str)
3410 /* counter overflow check - paranoia? */
3411 if (NUMCounter >= (INT_MAX - NUM_CACHE_FIELDS - 1))
3415 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3416 ent->age = (++NUMCounter);
3420 * If cache is full, remove oldest entry
3422 if (n_NUMCache > NUM_CACHE_FIELDS)
3424 NUMCacheEntry *old = NUMCache + 0;
3426 #ifdef DEBUG_TO_FROM_CHAR
3427 elog(DEBUG_elog_output, "Cache is full (%d)", n_NUMCache);
3429 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3432 * entry removed via NUM_cache_remove() can be used here, which is
3433 * why it's worth scanning first entry again
3435 if (ent->str[0] == '\0')
3440 if (ent->age < old->age)
3443 #ifdef DEBUG_TO_FROM_CHAR
3444 elog(DEBUG_elog_output, "OLD: \"%s\" AGE: %d", old->str, old->age);
3446 StrNCpy(old->str, str, NUM_CACHE_SIZE + 1);
3447 /* old->format fill parser */
3448 old->age = (++NUMCounter);
3453 #ifdef DEBUG_TO_FROM_CHAR
3454 elog(DEBUG_elog_output, "NEW (%d)", n_NUMCache);
3456 ent = NUMCache + n_NUMCache;
3457 StrNCpy(ent->str, str, NUM_CACHE_SIZE + 1);
3458 /* ent->format fill parser */
3459 ent->age = (++NUMCounter);
3463 zeroize_NUM(&ent->Num);
3465 last_NUMCacheEntry = ent;
3469 static NUMCacheEntry *
3470 NUM_cache_search(char *str)
3475 /* counter overflow check - paranoia? */
3476 if (NUMCounter >= (INT_MAX - NUM_CACHE_FIELDS - 1))
3480 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3481 ent->age = (++NUMCounter);
3484 for (i = 0, ent = NUMCache; i < n_NUMCache; i++, ent++)
3486 if (strcmp(ent->str, str) == 0)
3488 ent->age = (++NUMCounter);
3489 last_NUMCacheEntry = ent;
3498 NUM_cache_remove(NUMCacheEntry *ent)
3500 #ifdef DEBUG_TO_FROM_CHAR
3501 elog(DEBUG_elog_output, "REMOVING ENTRY (%s)", ent->str);
3508 * Cache routine for NUM to_char version
3512 NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree)
3514 FormatNode *format = NULL;
3517 str = text_to_cstring(pars_str);
3520 * Allocate new memory if format picture is bigger than static cache and
3521 * not use cache (call parser always). This branches sets shouldFree to
3522 * true, accordingly.
3524 if (len > NUM_CACHE_SIZE)
3526 format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
3532 parse_format(format, str, NUM_keywords,
3533 NULL, NUM_index, NUM_TYPE, Num);
3535 (format + len)->type = NODE_TYPE_END; /* Paranoia? */
3544 *shouldFree = false;
3546 if ((ent = NUM_cache_search(str)) == NULL)
3548 ent = NUM_cache_getnew(str);
3551 * Not in the cache, must run parser and save a new format-picture
3554 parse_format(ent->format, str, NUM_keywords,
3555 NULL, NUM_index, NUM_TYPE, &ent->Num);
3557 (ent->format + len)->type = NODE_TYPE_END; /* Paranoia? */
3560 format = ent->format;
3563 * Copy cache to used struct
3565 Num->flag = ent->Num.flag;
3566 Num->lsign = ent->Num.lsign;
3567 Num->pre = ent->Num.pre;
3568 Num->post = ent->Num.post;
3569 Num->pre_lsign_num = ent->Num.pre_lsign_num;
3570 Num->need_locale = ent->Num.need_locale;
3571 Num->multi = ent->Num.multi;
3572 Num->zero_start = ent->Num.zero_start;
3573 Num->zero_end = ent->Num.zero_end;
3576 #ifdef DEBUG_TO_FROM_CHAR
3577 /* dump_node(format, len); */
3578 dump_index(NUM_keywords, NUM_index);
3587 int_to_roman(int number)
3595 result = (char *) palloc(16);
3598 if (number > 3999 || number < 1)
3600 fill_str(result, '#', 15);
3603 len = snprintf(numstr, sizeof(numstr), "%d", number);
3605 for (p = numstr; *p != '\0'; p++, --len)
3607 num = *p - 49; /* 48 ascii + 1 */
3614 strcat(result, "M");
3619 strcat(result, rm100[num]);
3621 strcat(result, rm10[num]);
3623 strcat(result, rm1[num]);
3636 NUM_prepare_locale(NUMProc *Np)
3638 if (Np->Num->need_locale)
3640 struct lconv *lconv;
3645 lconv = PGLC_localeconv();
3648 * Positive / Negative number sign
3650 if (lconv->negative_sign && *lconv->negative_sign)
3651 Np->L_negative_sign = lconv->negative_sign;
3653 Np->L_negative_sign = "-";
3655 if (lconv->positive_sign && *lconv->positive_sign)
3656 Np->L_positive_sign = lconv->positive_sign;
3658 Np->L_positive_sign = "+";
3661 * Number decimal point
3663 if (lconv->decimal_point && *lconv->decimal_point)
3664 Np->decimal = lconv->decimal_point;
3669 if (!IS_LDECIMAL(Np->Num))
3673 * Number thousands separator
3675 * Some locales (e.g. broken glibc pt_BR), have a comma for decimal,
3676 * but "" for thousands_sep, so we set the thousands_sep too.
3677 * http://archives.postgresql.org/pgsql-hackers/2007-11/msg00772.php
3679 if (lconv->thousands_sep && *lconv->thousands_sep)
3680 Np->L_thousands_sep = lconv->thousands_sep;
3681 /* Make sure thousands separator doesn't match decimal point symbol. */
3682 else if (strcmp(Np->decimal, ",") !=0)
3683 Np->L_thousands_sep = ",";
3685 Np->L_thousands_sep = ".";
3690 if (lconv->currency_symbol && *lconv->currency_symbol)
3691 Np->L_currency_symbol = lconv->currency_symbol;
3693 Np->L_currency_symbol = " ";
3700 Np->L_negative_sign = "-";
3701 Np->L_positive_sign = "+";
3704 Np->L_thousands_sep = ",";
3705 Np->L_currency_symbol = " ";
3710 * Return pointer of last relevant number after decimal point
3711 * 12.0500 --> last relevant is '5'
3715 get_last_relevant_decnum(char *num)
3718 *p = strchr(num, '.');
3720 #ifdef DEBUG_TO_FROM_CHAR
3721 elog(DEBUG_elog_output, "get_last_relevant_decnum()");
3738 * Number extraction for TO_NUMBER()
3742 NUM_numpart_from_char(NUMProc *Np, int id, int plen)
3744 bool isread = FALSE;
3746 #ifdef DEBUG_TO_FROM_CHAR
3747 elog(DEBUG_elog_output, " --- scan start --- id=%s",
3748 (id == NUM_0 || id == NUM_9) ? "NUM_0/9" : id == NUM_DEC ? "NUM_DEC" : "???");
3751 if (*Np->inout_p == ' ')
3754 #define OVERLOAD_TEST (Np->inout_p >= Np->inout + plen)
3755 #define AMOUNT_TEST(_s) (plen-(Np->inout_p-Np->inout) >= _s)
3757 if (*Np->inout_p == ' ')
3764 * read sign before number
3766 if (*Np->number == ' ' && (id == NUM_0 || id == NUM_9) &&
3767 (Np->read_pre + Np->read_post) == 0)
3769 #ifdef DEBUG_TO_FROM_CHAR
3770 elog(DEBUG_elog_output, "Try read sign (%c), locale positive: %s, negative: %s",
3771 *Np->inout_p, Np->L_positive_sign, Np->L_negative_sign);
3777 if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_PRE)
3781 #ifdef DEBUG_TO_FROM_CHAR
3782 elog(DEBUG_elog_output, "Try read locale pre-sign (%c)", *Np->inout_p);
3784 if ((x = strlen(Np->L_negative_sign)) &&
3786 strncmp(Np->inout_p, Np->L_negative_sign, x) == 0)
3791 else if ((x = strlen(Np->L_positive_sign)) &&
3793 strncmp(Np->inout_p, Np->L_positive_sign, x) == 0)
3801 #ifdef DEBUG_TO_FROM_CHAR
3802 elog(DEBUG_elog_output, "Try read simple sign (%c)", *Np->inout_p);
3808 if (*Np->inout_p == '-' || (IS_BRACKET(Np->Num) &&
3809 *Np->inout_p == '<'))
3811 *Np->number = '-'; /* set - */
3814 else if (*Np->inout_p == '+')
3816 *Np->number = '+'; /* set + */
3825 #ifdef DEBUG_TO_FROM_CHAR
3826 elog(DEBUG_elog_output, "Scan for numbers (%c), current number: '%s'", *Np->inout_p, Np->number);
3832 if (isdigit((unsigned char) *Np->inout_p))
3834 if (Np->read_dec && Np->read_post == Np->Num->post)
3837 *Np->number_p = *Np->inout_p;
3847 #ifdef DEBUG_TO_FROM_CHAR
3848 elog(DEBUG_elog_output, "Read digit (%c)", *Np->inout_p);
3852 * read decimal point
3855 else if (IS_DECIMAL(Np->Num) && Np->read_dec == FALSE)
3857 #ifdef DEBUG_TO_FROM_CHAR
3858 elog(DEBUG_elog_output, "Try read decimal point (%c)", *Np->inout_p);
3860 if (*Np->inout_p == '.')
3862 *Np->number_p = '.';
3864 Np->read_dec = TRUE;
3869 int x = strlen(Np->decimal);
3871 #ifdef DEBUG_TO_FROM_CHAR
3872 elog(DEBUG_elog_output, "Try read locale point (%c)",
3875 if (x && AMOUNT_TEST(x) && strncmp(Np->inout_p, Np->decimal, x) == 0)
3877 Np->inout_p += x - 1;
3878 *Np->number_p = '.';
3880 Np->read_dec = TRUE;
3890 * Read sign behind "last" number
3892 * We need sign detection because determine exact position of post-sign is
3895 * FM9999.9999999S -> 123.001- 9.9S -> .5- FM9.999999MI ->
3898 if (*Np->number == ' ' && Np->read_pre + Np->read_post > 0)
3901 * locale sign (NUM_S) is always anchored behind a last number, if: -
3902 * locale sign expected - last read char was NUM_0/9 or NUM_DEC - and
3903 * next char is not digit
3905 if (IS_LSIGN(Np->Num) && isread &&
3906 (Np->inout_p + 1) <= Np->inout + plen &&
3907 !isdigit((unsigned char) *(Np->inout_p + 1)))
3910 char *tmp = Np->inout_p++;
3912 #ifdef DEBUG_TO_FROM_CHAR
3913 elog(DEBUG_elog_output, "Try read locale post-sign (%c)", *Np->inout_p);
3915 if ((x = strlen(Np->L_negative_sign)) &&
3917 strncmp(Np->inout_p, Np->L_negative_sign, x) == 0)
3919 Np->inout_p += x - 1; /* -1 .. NUM_processor() do inout_p++ */
3922 else if ((x = strlen(Np->L_positive_sign)) &&
3924 strncmp(Np->inout_p, Np->L_positive_sign, x) == 0)
3926 Np->inout_p += x - 1; /* -1 .. NUM_processor() do inout_p++ */
3929 if (*Np->number == ' ')
3935 * try read non-locale sign, it's happen only if format is not exact
3936 * and we cannot determine sign position of MI/PL/SG, an example:
3938 * FM9.999999MI -> 5.01-
3940 * if (.... && IS_LSIGN(Np->Num)==FALSE) prevents read wrong formats
3941 * like to_number('1 -', '9S') where sign is not anchored to last
3944 else if (isread == FALSE && IS_LSIGN(Np->Num) == FALSE &&
3945 (IS_PLUS(Np->Num) || IS_MINUS(Np->Num)))
3947 #ifdef DEBUG_TO_FROM_CHAR
3948 elog(DEBUG_elog_output, "Try read simple post-sign (%c)", *Np->inout_p);
3954 if (*Np->inout_p == '-' || *Np->inout_p == '+')
3955 /* NUM_processor() do inout_p++ */
3956 *Np->number = *Np->inout_p;
3961 #define IS_PREDEC_SPACE(_n) \
3962 (IS_ZERO((_n)->Num)==FALSE && \
3963 (_n)->number == (_n)->number_p && \
3964 *(_n)->number == '0' && \
3965 (_n)->Num->post != 0)
3968 * Add digit or sign to number-string
3972 NUM_numpart_to_char(NUMProc *Np, int id)
3976 if (IS_ROMAN(Np->Num))
3979 /* Note: in this elog() output not set '\0' in 'inout' */
3981 #ifdef DEBUG_TO_FROM_CHAR
3984 * Np->num_curr is number of current item in format-picture, it is not
3985 * current position in inout!
3987 elog(DEBUG_elog_output,
3988 "SIGN_WROTE: %d, CURRENT: %d, NUMBER_P: \"%s\", INOUT: \"%s\"",
3997 * Write sign if real number will write to output Note: IS_PREDEC_SPACE()
3998 * handle "9.9" --> " .1"
4000 if (Np->sign_wrote == FALSE &&
4001 (Np->num_curr >= Np->num_pre || (IS_ZERO(Np->Num) && Np->Num->zero_start == Np->num_curr)) &&
4002 (IS_PREDEC_SPACE(Np) == FALSE || (Np->last_relevant && *Np->last_relevant == '.')))
4004 if (IS_LSIGN(Np->Num))
4006 if (Np->Num->lsign == NUM_LSIGN_PRE)
4008 if (Np->sign == '-')
4009 strcpy(Np->inout_p, Np->L_negative_sign);
4011 strcpy(Np->inout_p, Np->L_positive_sign);
4012 Np->inout_p += strlen(Np->inout_p);
4013 Np->sign_wrote = TRUE;
4016 else if (IS_BRACKET(Np->Num))
4018 *Np->inout_p = Np->sign == '+' ? ' ' : '<';
4020 Np->sign_wrote = TRUE;
4022 else if (Np->sign == '+')
4024 if (!IS_FILLMODE(Np->Num))
4026 *Np->inout_p = ' '; /* Write + */
4029 Np->sign_wrote = TRUE;
4031 else if (Np->sign == '-')
4035 Np->sign_wrote = TRUE;
4041 * digits / FM / Zero / Dec. point
4043 if (id == NUM_9 || id == NUM_0 || id == NUM_D || id == NUM_DEC)
4045 if (Np->num_curr < Np->num_pre &&
4046 (Np->Num->zero_start > Np->num_curr || !IS_ZERO(Np->Num)))
4051 if (!IS_FILLMODE(Np->Num))
4053 *Np->inout_p = ' '; /* Write ' ' */
4057 else if (IS_ZERO(Np->Num) &&
4058 Np->num_curr < Np->num_pre &&
4059 Np->Num->zero_start <= Np->num_curr)
4064 *Np->inout_p = '0'; /* Write '0' */
4071 * Write Decimal point
4073 if (*Np->number_p == '.')
4075 if (!Np->last_relevant || *Np->last_relevant != '.')
4077 strcpy(Np->inout_p, Np->decimal); /* Write DEC/D */
4078 Np->inout_p += strlen(Np->inout_p);
4082 * Ora 'n' -- FM9.9 --> 'n.'
4084 else if (IS_FILLMODE(Np->Num) &&
4085 Np->last_relevant && *Np->last_relevant == '.')
4087 strcpy(Np->inout_p, Np->decimal); /* Write DEC/D */
4088 Np->inout_p += strlen(Np->inout_p);
4096 if (Np->last_relevant && Np->number_p > Np->last_relevant &&
4101 * '0.1' -- 9.9 --> ' .1'
4103 else if (IS_PREDEC_SPACE(Np))
4105 if (!IS_FILLMODE(Np->Num))
4112 * '0' -- FM9.9 --> '0.'
4114 else if (Np->last_relevant && *Np->last_relevant == '.')
4122 *Np->inout_p = *Np->number_p; /* Write DIGIT */
4130 end = Np->num_count + (Np->num_pre ? 1 : 0) + (IS_DECIMAL(Np->Num) ? 1 : 0);
4132 if (Np->last_relevant && Np->last_relevant == Np->number_p)
4135 if (Np->num_curr + 1 == end)
4137 if (Np->sign_wrote == TRUE && IS_BRACKET(Np->Num))
4139 *Np->inout_p = Np->sign == '+' ? ' ' : '>';
4142 else if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_POST)
4144 if (Np->sign == '-')
4145 strcpy(Np->inout_p, Np->L_negative_sign);
4147 strcpy(Np->inout_p, Np->L_positive_sign);
4148 Np->inout_p += strlen(Np->inout_p);
4157 * Note: 'plen' is used in FROM_CHAR conversion and it's length of
4158 * input (inout). In TO_CHAR conversion it's space before first number.
4161 NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
4162 int plen, int sign, bool is_to_char, Oid collid)
4168 MemSet(Np, 0, sizeof(NUMProc));
4171 Np->is_to_char = is_to_char;
4172 Np->number = number;
4174 Np->last_relevant = NULL;
4177 Np->read_dec = FALSE;
4179 if (Np->Num->zero_start)
4180 --Np->Num->zero_start;
4182 if (IS_EEEE(Np->Num))
4184 if (!Np->is_to_char)
4186 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4187 errmsg("\"EEEE\" not supported for input")));
4188 return strcpy(inout, number);
4194 if (IS_ROMAN(Np->Num))
4196 if (!Np->is_to_char)
4198 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4199 errmsg("\"RN\" not supported for input")));
4201 Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->post =
4202 Np->Num->pre = Np->num_pre = Np->sign = 0;
4204 if (IS_FILLMODE(Np->Num))
4207 Np->Num->flag |= NUM_F_FILLMODE;
4211 Np->Num->flag |= NUM_F_ROMAN;
4221 /* MI/PL/SG - write sign itself and not in number */
4222 if (IS_PLUS(Np->Num) || IS_MINUS(Np->Num))
4224 if (IS_PLUS(Np->Num) && IS_MINUS(Np->Num) == FALSE)
4225 Np->sign_wrote = FALSE; /* need sign */
4227 Np->sign_wrote = TRUE; /* needn't sign */
4231 if (Np->sign != '-')
4233 if (IS_BRACKET(Np->Num) && IS_FILLMODE(Np->Num))
4234 Np->Num->flag &= ~NUM_F_BRACKET;
4235 if (IS_MINUS(Np->Num))
4236 Np->Num->flag &= ~NUM_F_MINUS;
4238 else if (Np->sign != '+' && IS_PLUS(Np->Num))
4239 Np->Num->flag &= ~NUM_F_PLUS;
4241 if (Np->sign == '+' && IS_FILLMODE(Np->Num) && IS_LSIGN(Np->Num) == FALSE)
4242 Np->sign_wrote = TRUE; /* needn't sign */
4244 Np->sign_wrote = FALSE; /* need sign */
4246 if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num)
4247 Np->Num->lsign = NUM_LSIGN_POST;
4256 Np->num_count = Np->Num->post + Np->Num->pre - 1;
4262 if (IS_FILLMODE(Np->Num))
4264 if (IS_DECIMAL(Np->Num))
4265 Np->last_relevant = get_last_relevant_decnum(
4267 ((Np->Num->zero_end - Np->num_pre > 0) ?
4268 Np->Num->zero_end - Np->num_pre : 0));
4271 if (Np->sign_wrote == FALSE && Np->num_pre == 0)
4277 *Np->number = ' '; /* sign space */
4278 *(Np->number + 1) = '\0';
4284 #ifdef DEBUG_TO_FROM_CHAR
4285 elog(DEBUG_elog_output,
4286 "\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\n\tEEEE: %s",
4293 Np->sign_wrote ? "Yes" : "No",
4294 IS_ZERO(Np->Num) ? "Yes" : "No",
4295 Np->Num->zero_start,
4297 Np->last_relevant ? Np->last_relevant : "<not set>",
4298 IS_BRACKET(Np->Num) ? "Yes" : "No",
4299 IS_PLUS(Np->Num) ? "Yes" : "No",
4300 IS_MINUS(Np->Num) ? "Yes" : "No",
4301 IS_FILLMODE(Np->Num) ? "Yes" : "No",
4302 IS_ROMAN(Np->Num) ? "Yes" : "No",
4303 IS_EEEE(Np->Num) ? "Yes" : "No"
4310 NUM_prepare_locale(Np);
4313 * Processor direct cycle
4316 Np->number_p = Np->number;
4318 Np->number_p = Np->number + 1; /* first char is space for sign */
4320 for (n = node, Np->inout_p = Np->inout; n->type != NODE_TYPE_END; n++)
4322 if (!Np->is_to_char)
4325 * Check non-string inout end
4327 if (Np->inout_p >= Np->inout + plen)
4332 * Format pictures actions
4334 if (n->type == NODE_TYPE_ACTION)
4337 * Create/reading digit/zero/blank/sing
4339 * 'NUM_S' note: The locale sign is anchored to number and we
4340 * read/write it when we work with first or last number
4341 * (NUM_0/NUM_9). This is reason why NUM_S missing in follow
4352 NUM_numpart_to_char(Np, n->key->id);
4353 continue; /* for() */
4357 NUM_numpart_from_char(Np, n->key->id, plen);
4358 break; /* switch() case: */
4366 if (IS_FILLMODE(Np->Num))
4378 if (IS_FILLMODE(Np->Num))
4389 if (IS_FILLMODE(Np->Num))
4393 int x = strlen(Np->L_thousands_sep);
4395 memset(Np->inout_p, ' ', x);
4396 Np->inout_p += x - 1;
4401 strcpy(Np->inout_p, Np->L_thousands_sep);
4402 Np->inout_p += strlen(Np->inout_p) - 1;
4409 if (IS_FILLMODE(Np->Num))
4412 Np->inout_p += strlen(Np->L_thousands_sep) - 1;
4419 strcpy(Np->inout_p, Np->L_currency_symbol);
4420 Np->inout_p += strlen(Np->inout_p) - 1;
4423 Np->inout_p += strlen(Np->L_currency_symbol) - 1;
4427 if (IS_FILLMODE(Np->Num))
4429 strcpy(Np->inout_p, Np->number_p);
4430 Np->inout_p += strlen(Np->inout_p) - 1;
4434 sprintf(Np->inout_p, "%15s", Np->number_p);
4435 Np->inout_p += strlen(Np->inout_p) - 1;
4440 if (IS_FILLMODE(Np->Num))
4442 strcpy(Np->inout_p, str_tolower_z(Np->number_p, collid));
4443 Np->inout_p += strlen(Np->inout_p) - 1;
4447 sprintf(Np->inout_p, "%15s", str_tolower_z(Np->number_p, collid));
4448 Np->inout_p += strlen(Np->inout_p) - 1;
4453 if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
4454 Np->sign == '-' || IS_DECIMAL(Np->Num))
4458 strcpy(Np->inout_p, get_th(Np->number, TH_LOWER));
4463 if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
4464 Np->sign == '-' || IS_DECIMAL(Np->Num))
4468 strcpy(Np->inout_p, get_th(Np->number, TH_UPPER));
4475 if (Np->sign == '-')
4477 else if (IS_FILLMODE(Np->Num))
4484 if (*Np->inout_p == '-')
4492 if (Np->sign == '+')
4494 else if (IS_FILLMODE(Np->Num))
4501 if (*Np->inout_p == '+')
4508 *Np->inout_p = Np->sign;
4512 if (*Np->inout_p == '-')
4514 else if (*Np->inout_p == '+')
4528 * Remove to output char from input in TO_CHAR
4531 *Np->inout_p = n->character;
4538 *Np->inout_p = '\0';
4543 if (*(Np->number_p - 1) == '.')
4544 *(Np->number_p - 1) = '\0';
4546 *Np->number_p = '\0';
4549 * Correction - precision of dec. number
4551 Np->Num->post = Np->read_post;
4553 #ifdef DEBUG_TO_FROM_CHAR
4554 elog(DEBUG_elog_output, "TO_NUMBER (number): '%s'", Np->number);
4561 * MACRO: Start part of NUM - for all NUM's to_char variants
4562 * (sorry, but I hate copy same code - macro is better..)
4565 #define NUM_TOCHAR_prepare \
4567 len = VARSIZE_ANY_EXHDR(fmt); \
4568 if (len <= 0 || len >= (INT_MAX-VARHDRSZ)/NUM_MAX_ITEM_SIZ) \
4569 PG_RETURN_TEXT_P(cstring_to_text("")); \
4570 result = (text *) palloc0((len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ); \
4571 format = NUM_cache(len, &Num, fmt, &shouldFree); \
4575 * MACRO: Finish part of NUM
4578 #define NUM_TOCHAR_finish \
4580 NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true, PG_GET_COLLATION()); \
4586 * Convert null-terminated representation of result to standard text. \
4587 * The result is usually much bigger than it needs to be, but there \
4588 * seems little point in realloc'ing it smaller. \
4590 len = strlen(VARDATA(result)); \
4591 SET_VARSIZE(result, len + VARHDRSZ); \
4594 /* -------------------
4595 * NUMERIC to_number() (convert string to numeric)
4596 * -------------------
4599 numeric_to_number(PG_FUNCTION_ARGS)
4601 text *value = PG_GETARG_TEXT_P(0);
4602 text *fmt = PG_GETARG_TEXT_P(1);
4612 len = VARSIZE(fmt) - VARHDRSZ;
4614 if (len <= 0 || len >= INT_MAX / NUM_MAX_ITEM_SIZ)
4617 format = NUM_cache(len, &Num, fmt, &shouldFree);
4619 numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1);
4621 NUM_processor(format, &Num, VARDATA(value), numstr,
4622 VARSIZE(value) - VARHDRSZ, 0, false, PG_GET_COLLATION());
4625 precision = Max(0, Num.pre) + scale;
4630 result = DirectFunctionCall3(numeric_in,
4631 CStringGetDatum(numstr),
4632 ObjectIdGetDatum(InvalidOid),
4633 Int32GetDatum(((precision << 16) | scale) + VARHDRSZ));
4638 /* ------------------
4640 * ------------------
4643 numeric_to_char(PG_FUNCTION_ARGS)
4645 Numeric value = PG_GETARG_NUMERIC(0);
4646 text *fmt = PG_GETARG_TEXT_P(1);
4662 * On DateType depend part (numeric)
4666 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
4667 NumericGetDatum(value),
4670 int_to_roman(DatumGetInt32(DirectFunctionCall1(numeric_int4,
4671 NumericGetDatum(x))));
4673 else if (IS_EEEE(&Num))
4675 orgnum = numeric_out_sci(value, Num.post);
4678 * numeric_out_sci() does not emit a sign for positive numbers. We
4679 * need to add a space in this case so that positive and negative
4680 * numbers are aligned. We also have to do the right thing for NaN.
4682 if (strcmp(orgnum, "NaN") == 0)
4685 * Allow 6 characters for the leading sign, the decimal point,
4686 * "e", the exponent's sign and two exponent digits.
4688 numstr = (char *) palloc(Num.pre + Num.post + 7);
4689 fill_str(numstr, '#', Num.pre + Num.post + 6);
4691 *(numstr + Num.pre + 1) = '.';
4693 else if (*orgnum != '-')
4695 numstr = (char *) palloc(strlen(orgnum) + 2);
4697 strcpy(numstr + 1, orgnum);
4698 len = strlen(numstr);
4703 len = strlen(orgnum);
4708 Numeric val = value;
4712 Numeric a = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
4713 Int32GetDatum(10)));
4714 Numeric b = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
4715 Int32GetDatum(Num.multi)));
4717 x = DatumGetNumeric(DirectFunctionCall2(numeric_power,
4719 NumericGetDatum(b)));
4720 val = DatumGetNumeric(DirectFunctionCall2(numeric_mul,
4721 NumericGetDatum(value),
4722 NumericGetDatum(x)));
4723 Num.pre += Num.multi;
4726 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
4727 NumericGetDatum(val),
4728 Int32GetDatum(Num.post)));
4729 orgnum = DatumGetCString(DirectFunctionCall1(numeric_out,
4730 NumericGetDatum(x)));
4735 numstr = orgnum + 1;
4742 if ((p = strchr(numstr, '.')))
4745 len = strlen(numstr);
4748 plen = Num.pre - len;
4749 else if (len > Num.pre)
4751 numstr = (char *) palloc(Num.pre + Num.post + 2);
4752 fill_str(numstr, '#', Num.pre + Num.post + 1);
4753 *(numstr + Num.pre) = '.';
4758 PG_RETURN_TEXT_P(result);
4766 int4_to_char(PG_FUNCTION_ARGS)
4768 int32 value = PG_GETARG_INT32(0);
4769 text *fmt = PG_GETARG_TEXT_P(1);
4783 * On DateType depend part (int32)
4786 numstr = orgnum = int_to_roman(value);
4787 else if (IS_EEEE(&Num))
4789 /* we can do it easily because float8 won't lose any precision */
4790 float8 val = (float8) value;
4792 orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
4793 snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, val);
4796 * Swap a leading positive sign for a space.
4801 len = strlen(orgnum);
4808 orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4809 Int32GetDatum(value * ((int32) pow((double) 10, (double) Num.multi)))));
4810 Num.pre += Num.multi;
4814 orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4815 Int32GetDatum(value)));
4825 len = strlen(orgnum);
4829 numstr = (char *) palloc(len + Num.post + 2);
4830 strcpy(numstr, orgnum);
4831 *(numstr + len) = '.';
4832 memset(numstr + len + 1, '0', Num.post);
4833 *(numstr + len + Num.post + 1) = '\0';
4839 plen = Num.pre - len;
4840 else if (len > Num.pre)
4842 numstr = (char *) palloc(Num.pre + Num.post + 2);
4843 fill_str(numstr, '#', Num.pre + Num.post + 1);
4844 *(numstr + Num.pre) = '.';
4849 PG_RETURN_TEXT_P(result);
4857 int8_to_char(PG_FUNCTION_ARGS)
4859 int64 value = PG_GETARG_INT64(0);
4860 text *fmt = PG_GETARG_TEXT_P(1);
4874 * On DateType depend part (int32)
4878 /* Currently don't support int8 conversion to roman... */
4879 numstr = orgnum = int_to_roman(DatumGetInt32(
4880 DirectFunctionCall1(int84, Int64GetDatum(value))));
4882 else if (IS_EEEE(&Num))
4884 /* to avoid loss of precision, must go via numeric not float8 */
4887 val = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
4888 Int64GetDatum(value)));
4889 orgnum = numeric_out_sci(val, Num.post);
4892 * numeric_out_sci() does not emit a sign for positive numbers. We
4893 * need to add a space in this case so that positive and negative
4894 * numbers are aligned. We don't have to worry about NaN here.
4898 numstr = (char *) palloc(strlen(orgnum) + 2);
4900 strcpy(numstr + 1, orgnum);
4901 len = strlen(numstr);
4906 len = strlen(orgnum);
4913 double multi = pow((double) 10, (double) Num.multi);
4915 value = DatumGetInt64(DirectFunctionCall2(int8mul,
4916 Int64GetDatum(value),
4917 DirectFunctionCall1(dtoi8,
4918 Float8GetDatum(multi))));
4919 Num.pre += Num.multi;
4922 orgnum = DatumGetCString(DirectFunctionCall1(int8out,
4923 Int64GetDatum(value)));
4932 len = strlen(orgnum);
4936 numstr = (char *) palloc(len + Num.post + 2);
4937 strcpy(numstr, orgnum);
4938 *(numstr + len) = '.';
4939 memset(numstr + len + 1, '0', Num.post);
4940 *(numstr + len + Num.post + 1) = '\0';
4946 plen = Num.pre - len;
4947 else if (len > Num.pre)
4949 numstr = (char *) palloc(Num.pre + Num.post + 2);
4950 fill_str(numstr, '#', Num.pre + Num.post + 1);
4951 *(numstr + Num.pre) = '.';
4956 PG_RETURN_TEXT_P(result);
4959 /* -----------------
4964 float4_to_char(PG_FUNCTION_ARGS)
4966 float4 value = PG_GETARG_FLOAT4(0);
4967 text *fmt = PG_GETARG_TEXT_P(1);
4982 numstr = orgnum = int_to_roman((int) rint(value));
4983 else if (IS_EEEE(&Num))
4985 numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
4986 if (isnan(value) || is_infinite(value))
4989 * Allow 6 characters for the leading sign, the decimal point,
4990 * "e", the exponent's sign and two exponent digits.
4992 numstr = (char *) palloc(Num.pre + Num.post + 7);
4993 fill_str(numstr, '#', Num.pre + Num.post + 6);
4995 *(numstr + Num.pre + 1) = '.';
4999 snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, value);
5002 * Swap a leading positive sign for a space.
5007 len = strlen(orgnum);
5017 float multi = pow((double) 10, (double) Num.multi);
5019 val = value * multi;
5020 Num.pre += Num.multi;
5023 orgnum = (char *) palloc(MAXFLOATWIDTH + 1);
5024 snprintf(orgnum, MAXFLOATWIDTH + 1, "%.0f", fabs(val));
5025 len = strlen(orgnum);
5027 plen = Num.pre - len;
5030 else if (Num.post + len > FLT_DIG)
5031 Num.post = FLT_DIG - len;
5032 snprintf(orgnum, MAXFLOATWIDTH + 1, "%.*f", Num.post, val);
5037 numstr = orgnum + 1;
5044 if ((p = strchr(numstr, '.')))
5047 len = strlen(numstr);
5050 plen = Num.pre - len;
5051 else if (len > Num.pre)
5053 numstr = (char *) palloc(Num.pre + Num.post + 2);
5054 fill_str(numstr, '#', Num.pre + Num.post + 1);
5055 *(numstr + Num.pre) = '.';
5060 PG_RETURN_TEXT_P(result);
5063 /* -----------------
5068 float8_to_char(PG_FUNCTION_ARGS)
5070 float8 value = PG_GETARG_FLOAT8(0);
5071 text *fmt = PG_GETARG_TEXT_P(1);
5086 numstr = orgnum = int_to_roman((int) rint(value));
5087 else if (IS_EEEE(&Num))
5089 numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
5090 if (isnan(value) || is_infinite(value))
5093 * Allow 6 characters for the leading sign, the decimal point,
5094 * "e", the exponent's sign and two exponent digits.
5096 numstr = (char *) palloc(Num.pre + Num.post + 7);
5097 fill_str(numstr, '#', Num.pre + Num.post + 6);
5099 *(numstr + Num.pre + 1) = '.';
5103 snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, value);
5106 * Swap a leading positive sign for a space.
5111 len = strlen(orgnum);
5121 double multi = pow((double) 10, (double) Num.multi);
5123 val = value * multi;
5124 Num.pre += Num.multi;
5126 orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
5127 len = snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.0f", fabs(val));
5129 plen = Num.pre - len;
5132 else if (Num.post + len > DBL_DIG)
5133 Num.post = DBL_DIG - len;
5134 snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.*f", Num.post, val);
5139 numstr = orgnum + 1;
5146 if ((p = strchr(numstr, '.')))
5149 len = strlen(numstr);
5152 plen = Num.pre - len;
5153 else if (len > Num.pre)
5155 numstr = (char *) palloc(Num.pre + Num.post + 2);
5156 fill_str(numstr, '#', Num.pre + Num.post + 1);
5157 *(numstr + Num.pre) = '.';
5162 PG_RETURN_TEXT_P(result);