Fix ancient bug in handling of to_char modifier 'TH', when used with HH.
[PostgreSQL.git] / src / backend / utils / adt / formatting.c
blob73e3c845187a91db8aa292aaaf64f32742326820
1 /* -----------------------------------------------------------------------
2 * formatting.c
4 * $PostgreSQL$
7 * Portions Copyright (c) 1999-2009, 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.
16 * Cache & Memory:
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
21 * always.
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)
27 * has suffix.
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()
42 * Date - to_date()
43 * Numeric - to_number()
46 * Karel Zak
48 * TODO
49 * - better number building (formatting) / parsing, now it isn't
50 * ideal code
51 * - use Assert()
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
56 * than Oracle :-),
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
64 #endif
66 #include "postgres.h"
68 #include <ctype.h>
69 #include <unistd.h>
70 #include <math.h>
71 #include <float.h>
72 #include <limits.h>
75 * towlower() and friends should be in <wctype.h>, but some pre-C99 systems
76 * declare them in <wchar.h>.
78 #ifdef HAVE_WCHAR_H
79 #include <wchar.h>
80 #endif
81 #ifdef HAVE_WCTYPE_H
82 #include <wctype.h>
83 #endif
85 #include "utils/builtins.h"
86 #include "utils/date.h"
87 #include "utils/datetime.h"
88 #include "utils/formatting.h"
89 #include "utils/int8.h"
90 #include "utils/numeric.h"
91 #include "utils/pg_locale.h"
92 #include "mb/pg_wchar.h"
94 /* ----------
95 * Routines type
96 * ----------
98 #define DCH_TYPE 1 /* DATE-TIME version */
99 #define NUM_TYPE 2 /* NUMBER version */
101 /* ----------
102 * KeyWord Index (ascii from position 32 (' ') to 126 (~))
103 * ----------
105 #define KeyWord_INDEX_SIZE ('~' - ' ')
106 #define KeyWord_INDEX_FILTER(_c) ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
108 /* ----------
109 * Maximal length of one node
110 * ----------
112 #define DCH_MAX_ITEM_SIZ 9 /* max julian day */
113 #define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
115 /* ----------
116 * More is in float.c
117 * ----------
119 #define MAXFLOATWIDTH 60
120 #define MAXDOUBLEWIDTH 500
123 /* ----------
124 * External (defined in PgSQL datetime.c (timestamp utils))
125 * ----------
127 extern char *months[], /* month abbreviation */
128 *days[]; /* full days */
130 /* ----------
131 * Format parser structs
132 * ----------
134 typedef struct
136 char *name; /* suffix string */
137 int len, /* suffix length */
138 id, /* used in node->suffix */
139 type; /* prefix / postfix */
140 } KeySuffix;
142 /* ----------
143 * FromCharDateMode
144 * ----------
146 * This value is used to nominate one of several distinct (and mutually
147 * exclusive) date conventions that a keyword can belong to.
149 typedef enum
151 FROM_CHAR_DATE_NONE = 0, /* Value does not affect date mode. */
152 FROM_CHAR_DATE_GREGORIAN, /* Gregorian (day, month, year) style date */
153 FROM_CHAR_DATE_ISOWEEK /* ISO 8601 week date */
154 } FromCharDateMode;
156 typedef struct FormatNode FormatNode;
158 typedef struct
160 const char *name;
161 int len;
162 int id;
163 bool is_digit;
164 FromCharDateMode date_mode;
165 } KeyWord;
167 struct FormatNode
169 int type; /* node type */
170 const KeyWord *key; /* if node type is KEYWORD */
171 char character; /* if node type is CHAR */
172 int suffix; /* keyword suffix */
175 #define NODE_TYPE_END 1
176 #define NODE_TYPE_ACTION 2
177 #define NODE_TYPE_CHAR 3
179 #define SUFFTYPE_PREFIX 1
180 #define SUFFTYPE_POSTFIX 2
182 #define CLOCK_24_HOUR 0
183 #define CLOCK_12_HOUR 1
186 /* ----------
187 * Full months
188 * ----------
190 static char *months_full[] = {
191 "January", "February", "March", "April", "May", "June", "July",
192 "August", "September", "October", "November", "December", NULL
195 static char *days_short[] = {
196 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
199 /* ----------
200 * AD / BC
201 * ----------
202 * There is no 0 AD. Years go from 1 BC to 1 AD, so we make it
203 * positive and map year == -1 to year zero, and shift all negative
204 * years up one. For interval years, we just return the year.
206 #define ADJUST_YEAR(year, is_interval) ((is_interval) ? (year) : ((year) <= 0 ? -((year) - 1) : (year)))
208 #define A_D_STR "A.D."
209 #define a_d_STR "a.d."
210 #define AD_STR "AD"
211 #define ad_STR "ad"
213 #define B_C_STR "B.C."
214 #define b_c_STR "b.c."
215 #define BC_STR "BC"
216 #define bc_STR "bc"
219 * AD / BC strings for seq_search.
221 * These are given in two variants, a long form with periods and a standard
222 * form without.
224 * The array is laid out such that matches for AD have an even index, and
225 * matches for BC have an odd index. So the boolean value for BC is given by
226 * taking the array index of the match, modulo 2.
228 static char *adbc_strings[] = {ad_STR, bc_STR, AD_STR, BC_STR, NULL};
229 static char *adbc_strings_long[] = {a_d_STR, b_c_STR, A_D_STR, B_C_STR, NULL};
231 /* ----------
232 * AM / PM
233 * ----------
235 #define A_M_STR "A.M."
236 #define a_m_STR "a.m."
237 #define AM_STR "AM"
238 #define am_STR "am"
240 #define P_M_STR "P.M."
241 #define p_m_STR "p.m."
242 #define PM_STR "PM"
243 #define pm_STR "pm"
246 * AM / PM strings for seq_search.
248 * These are given in two variants, a long form with periods and a standard
249 * form without.
251 * The array is laid out such that matches for AM have an even index, and
252 * matches for PM have an odd index. So the boolean value for PM is given by
253 * taking the array index of the match, modulo 2.
255 static char *ampm_strings[] = {am_STR, pm_STR, AM_STR, PM_STR, NULL};
256 static char *ampm_strings_long[] = {a_m_STR, p_m_STR, A_M_STR, P_M_STR, NULL};
258 /* ----------
259 * Months in roman-numeral
260 * (Must be in reverse order for seq_search (in FROM_CHAR), because
261 * 'VIII' must have higher precedence than 'V')
262 * ----------
264 static char *rm_months_upper[] =
265 {"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL};
267 static char *rm_months_lower[] =
268 {"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL};
270 /* ----------
271 * Roman numbers
272 * ----------
274 static char *rm1[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL};
275 static char *rm10[] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL};
276 static char *rm100[] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL};
278 /* ----------
279 * Ordinal postfixes
280 * ----------
282 static char *numTH[] = {"ST", "ND", "RD", "TH", NULL};
283 static char *numth[] = {"st", "nd", "rd", "th", NULL};
285 /* ----------
286 * Flags & Options:
287 * ----------
289 #define ONE_UPPER 1 /* Name */
290 #define ALL_UPPER 2 /* NAME */
291 #define ALL_LOWER 3 /* name */
293 #define FULL_SIZ 0
295 #define MAX_MONTH_LEN 9
296 #define MAX_MON_LEN 3
297 #define MAX_DAY_LEN 9
298 #define MAX_DY_LEN 3
299 #define MAX_RM_LEN 4
301 #define TH_UPPER 1
302 #define TH_LOWER 2
304 /* ----------
305 * Number description struct
306 * ----------
308 typedef struct
310 int pre, /* (count) numbers before decimal */
311 post, /* (count) numbers after decimal */
312 lsign, /* want locales sign */
313 flag, /* number parameters */
314 pre_lsign_num, /* tmp value for lsign */
315 multi, /* multiplier for 'V' */
316 zero_start, /* position of first zero */
317 zero_end, /* position of last zero */
318 need_locale; /* needs it locale */
319 } NUMDesc;
321 /* ----------
322 * Flags for NUMBER version
323 * ----------
325 #define NUM_F_DECIMAL (1 << 1)
326 #define NUM_F_LDECIMAL (1 << 2)
327 #define NUM_F_ZERO (1 << 3)
328 #define NUM_F_BLANK (1 << 4)
329 #define NUM_F_FILLMODE (1 << 5)
330 #define NUM_F_LSIGN (1 << 6)
331 #define NUM_F_BRACKET (1 << 7)
332 #define NUM_F_MINUS (1 << 8)
333 #define NUM_F_PLUS (1 << 9)
334 #define NUM_F_ROMAN (1 << 10)
335 #define NUM_F_MULTI (1 << 11)
336 #define NUM_F_PLUS_POST (1 << 12)
337 #define NUM_F_MINUS_POST (1 << 13)
339 #define NUM_LSIGN_PRE (-1)
340 #define NUM_LSIGN_POST 1
341 #define NUM_LSIGN_NONE 0
343 /* ----------
344 * Tests
345 * ----------
347 #define IS_DECIMAL(_f) ((_f)->flag & NUM_F_DECIMAL)
348 #define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL)
349 #define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO)
350 #define IS_BLANK(_f) ((_f)->flag & NUM_F_BLANK)
351 #define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE)
352 #define IS_BRACKET(_f) ((_f)->flag & NUM_F_BRACKET)
353 #define IS_MINUS(_f) ((_f)->flag & NUM_F_MINUS)
354 #define IS_LSIGN(_f) ((_f)->flag & NUM_F_LSIGN)
355 #define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
356 #define IS_ROMAN(_f) ((_f)->flag & NUM_F_ROMAN)
357 #define IS_MULTI(_f) ((_f)->flag & NUM_F_MULTI)
359 /* ----------
360 * Format picture cache
361 * (cache size:
362 * Number part = NUM_CACHE_SIZE * NUM_CACHE_FIELDS
363 * Date-time part = DCH_CACHE_SIZE * DCH_CACHE_FIELDS
365 * ----------
367 #define NUM_CACHE_SIZE 64
368 #define NUM_CACHE_FIELDS 16
369 #define DCH_CACHE_SIZE 128
370 #define DCH_CACHE_FIELDS 16
372 typedef struct
374 FormatNode format[DCH_CACHE_SIZE + 1];
375 char str[DCH_CACHE_SIZE + 1];
376 int age;
377 } DCHCacheEntry;
379 typedef struct
381 FormatNode format[NUM_CACHE_SIZE + 1];
382 char str[NUM_CACHE_SIZE + 1];
383 int age;
384 NUMDesc Num;
385 } NUMCacheEntry;
387 /* global cache for --- date/time part */
388 static DCHCacheEntry DCHCache[DCH_CACHE_FIELDS + 1];
390 static int n_DCHCache = 0; /* number of entries */
391 static int DCHCounter = 0;
393 /* global cache for --- number part */
394 static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1];
396 static int n_NUMCache = 0; /* number of entries */
397 static int NUMCounter = 0;
398 static NUMCacheEntry *last_NUMCacheEntry = NUMCache + 0;
400 /* ----------
401 * For char->date/time conversion
402 * ----------
404 typedef struct
406 FromCharDateMode mode;
407 int hh,
411 ssss,
414 ddd,
417 year,
424 yysz, /* is it YY or YYYY ? */
425 clock; /* 12 or 24 hour clock? */
426 } TmFromChar;
428 #define ZERO_tmfc(_X) memset(_X, 0, sizeof(TmFromChar))
430 /* ----------
431 * Debug
432 * ----------
434 #ifdef DEBUG_TO_FROM_CHAR
435 #define DEBUG_TMFC(_X) \
436 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", \
437 (_X)->mode, (_X)->hh, (_X)->pm, (_X)->mi, (_X)->ss, (_X)->ssss, \
438 (_X)->d, (_X)->dd, (_X)->ddd, (_X)->mm, (_X)->ms, (_X)->year, \
439 (_X)->bc, (_X)->ww, (_X)->w, (_X)->cc, (_X)->j, (_X)->us, \
440 (_X)->yysz, (_X)->clock);
441 #define DEBUG_TM(_X) \
442 elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\
443 (_X)->tm_sec, (_X)->tm_year,\
444 (_X)->tm_min, (_X)->tm_wday, (_X)->tm_hour, (_X)->tm_yday,\
445 (_X)->tm_mday, (_X)->tm_isdst, (_X)->tm_mon)
446 #else
447 #define DEBUG_TMFC(_X)
448 #define DEBUG_TM(_X)
449 #endif
451 /* ----------
452 * Datetime to char conversion
453 * ----------
455 typedef struct TmToChar
457 struct pg_tm tm; /* classic 'tm' struct */
458 fsec_t fsec; /* fractional seconds */
459 char *tzn; /* timezone */
460 } TmToChar;
462 #define tmtcTm(_X) (&(_X)->tm)
463 #define tmtcTzn(_X) ((_X)->tzn)
464 #define tmtcFsec(_X) ((_X)->fsec)
466 #define ZERO_tm(_X) \
467 do { \
468 (_X)->tm_sec = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
469 (_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
470 (_X)->tm_mday = (_X)->tm_mon = 1; \
471 } while(0)
473 #define ZERO_tmtc(_X) \
474 do { \
475 ZERO_tm( tmtcTm(_X) ); \
476 tmtcFsec(_X) = 0; \
477 tmtcTzn(_X) = NULL; \
478 } while(0)
481 * to_char(time) appears to to_char() as an interval, so this check
482 * is really for interval and time data types.
484 #define INVALID_FOR_INTERVAL \
485 do { \
486 if (is_interval) \
487 ereport(ERROR, \
488 (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
489 errmsg("invalid format specification for an interval value"), \
490 errhint("Intervals are not tied to specific calendar dates."))); \
491 } while(0)
493 /*****************************************************************************
494 * KeyWord definitions
495 *****************************************************************************/
497 /* ----------
498 * Suffixes:
499 * ----------
501 #define DCH_S_FM 0x01
502 #define DCH_S_TH 0x02
503 #define DCH_S_th 0x04
504 #define DCH_S_SP 0x08
505 #define DCH_S_TM 0x10
507 /* ----------
508 * Suffix tests
509 * ----------
511 #define S_THth(_s) ((((_s) & DCH_S_TH) || ((_s) & DCH_S_th)) ? 1 : 0)
512 #define S_TH(_s) (((_s) & DCH_S_TH) ? 1 : 0)
513 #define S_th(_s) (((_s) & DCH_S_th) ? 1 : 0)
514 #define S_TH_TYPE(_s) (((_s) & DCH_S_TH) ? TH_UPPER : TH_LOWER)
516 #define S_FM(_s) (((_s) & DCH_S_FM) ? 1 : 0)
517 #define S_SP(_s) (((_s) & DCH_S_SP) ? 1 : 0)
518 #define S_TM(_s) (((_s) & DCH_S_TM) ? 1 : 0)
520 /* ----------
521 * Suffixes definition for DATE-TIME TO/FROM CHAR
522 * ----------
524 static KeySuffix DCH_suff[] = {
525 {"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
526 {"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
527 {"TM", 2, DCH_S_TM, SUFFTYPE_PREFIX},
528 {"tm", 2, DCH_S_TM, SUFFTYPE_PREFIX},
529 {"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
530 {"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
531 {"SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX},
532 /* last */
533 {NULL, 0, 0, 0}
536 /* ----------
537 * Format-pictures (KeyWord).
539 * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
540 * complicated -to-> easy:
542 * (example: "DDD","DD","Day","D" )
544 * (this specific sort needs the algorithm for sequential search for strings,
545 * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
546 * or "HH12"? You must first try "HH12", because "HH" is in string, but
547 * it is not good.
549 * (!)
550 * - Position for the keyword is similar as position in the enum DCH/NUM_poz.
551 * (!)
553 * For fast search is used the 'int index[]', index is ascii table from position
554 * 32 (' ') to 126 (~), in this index is DCH_ / NUM_ enums for each ASCII
555 * position or -1 if char is not used in the KeyWord. Search example for
556 * string "MM":
557 * 1) see in index to index['M' - 32],
558 * 2) take keywords position (enum DCH_MI) from index
559 * 3) run sequential search in keywords[] from this position
561 * ----------
564 typedef enum
566 DCH_A_D,
567 DCH_A_M,
568 DCH_AD,
569 DCH_AM,
570 DCH_B_C,
571 DCH_BC,
572 DCH_CC,
573 DCH_DAY,
574 DCH_DDD,
575 DCH_DD,
576 DCH_DY,
577 DCH_Day,
578 DCH_Dy,
579 DCH_D,
580 DCH_FX, /* global suffix */
581 DCH_HH24,
582 DCH_HH12,
583 DCH_HH,
584 DCH_IDDD,
585 DCH_ID,
586 DCH_IW,
587 DCH_IYYY,
588 DCH_IYY,
589 DCH_IY,
590 DCH_I,
591 DCH_J,
592 DCH_MI,
593 DCH_MM,
594 DCH_MONTH,
595 DCH_MON,
596 DCH_MS,
597 DCH_Month,
598 DCH_Mon,
599 DCH_P_M,
600 DCH_PM,
601 DCH_Q,
602 DCH_RM,
603 DCH_SSSS,
604 DCH_SS,
605 DCH_TZ,
606 DCH_US,
607 DCH_WW,
608 DCH_W,
609 DCH_Y_YYY,
610 DCH_YYYY,
611 DCH_YYY,
612 DCH_YY,
613 DCH_Y,
614 DCH_a_d,
615 DCH_a_m,
616 DCH_ad,
617 DCH_am,
618 DCH_b_c,
619 DCH_bc,
620 DCH_cc,
621 DCH_day,
622 DCH_ddd,
623 DCH_dd,
624 DCH_dy,
625 DCH_d,
626 DCH_fx,
627 DCH_hh24,
628 DCH_hh12,
629 DCH_hh,
630 DCH_iddd,
631 DCH_id,
632 DCH_iw,
633 DCH_iyyy,
634 DCH_iyy,
635 DCH_iy,
636 DCH_i,
637 DCH_j,
638 DCH_mi,
639 DCH_mm,
640 DCH_month,
641 DCH_mon,
642 DCH_ms,
643 DCH_p_m,
644 DCH_pm,
645 DCH_q,
646 DCH_rm,
647 DCH_ssss,
648 DCH_ss,
649 DCH_tz,
650 DCH_us,
651 DCH_ww,
652 DCH_w,
653 DCH_y_yyy,
654 DCH_yyyy,
655 DCH_yyy,
656 DCH_yy,
657 DCH_y,
659 /* last */
660 _DCH_last_
661 } DCH_poz;
663 typedef enum
665 NUM_COMMA,
666 NUM_DEC,
667 NUM_0,
668 NUM_9,
669 NUM_B,
670 NUM_C,
671 NUM_D,
672 NUM_E,
673 NUM_FM,
674 NUM_G,
675 NUM_L,
676 NUM_MI,
677 NUM_PL,
678 NUM_PR,
679 NUM_RN,
680 NUM_SG,
681 NUM_SP,
682 NUM_S,
683 NUM_TH,
684 NUM_V,
685 NUM_b,
686 NUM_c,
687 NUM_d,
688 NUM_e,
689 NUM_fm,
690 NUM_g,
691 NUM_l,
692 NUM_mi,
693 NUM_pl,
694 NUM_pr,
695 NUM_rn,
696 NUM_sg,
697 NUM_sp,
698 NUM_s,
699 NUM_th,
700 NUM_v,
702 /* last */
703 _NUM_last_
704 } NUM_poz;
706 /* ----------
707 * KeyWords for DATE-TIME version
708 * ----------
710 static const KeyWord DCH_keywords[] = {
711 /* name, len, id, is_digit, date_mode */
712 {"A.D.", 4, DCH_A_D, FALSE, FROM_CHAR_DATE_NONE}, /* A */
713 {"A.M.", 4, DCH_A_M, FALSE, FROM_CHAR_DATE_NONE},
714 {"AD", 2, DCH_AD, FALSE, FROM_CHAR_DATE_NONE},
715 {"AM", 2, DCH_AM, FALSE, FROM_CHAR_DATE_NONE},
716 {"B.C.", 4, DCH_B_C, FALSE, FROM_CHAR_DATE_NONE}, /* B */
717 {"BC", 2, DCH_BC, FALSE, FROM_CHAR_DATE_NONE},
718 {"CC", 2, DCH_CC, TRUE, FROM_CHAR_DATE_NONE}, /* C */
719 {"DAY", 3, DCH_DAY, FALSE, FROM_CHAR_DATE_NONE}, /* D */
720 {"DDD", 3, DCH_DDD, TRUE, FROM_CHAR_DATE_GREGORIAN},
721 {"DD", 2, DCH_DD, TRUE, FROM_CHAR_DATE_GREGORIAN},
722 {"DY", 2, DCH_DY, FALSE, FROM_CHAR_DATE_NONE},
723 {"Day", 3, DCH_Day, FALSE, FROM_CHAR_DATE_NONE},
724 {"Dy", 2, DCH_Dy, FALSE, FROM_CHAR_DATE_NONE},
725 {"D", 1, DCH_D, TRUE, FROM_CHAR_DATE_GREGORIAN},
726 {"FX", 2, DCH_FX, FALSE, FROM_CHAR_DATE_NONE}, /* F */
727 {"HH24", 4, DCH_HH24, TRUE, FROM_CHAR_DATE_NONE}, /* H */
728 {"HH12", 4, DCH_HH12, TRUE, FROM_CHAR_DATE_NONE},
729 {"HH", 2, DCH_HH, TRUE, FROM_CHAR_DATE_NONE},
730 {"IDDD", 4, DCH_IDDD, TRUE, FROM_CHAR_DATE_ISOWEEK}, /* I */
731 {"ID", 2, DCH_ID, TRUE, FROM_CHAR_DATE_ISOWEEK},
732 {"IW", 2, DCH_IW, TRUE, FROM_CHAR_DATE_ISOWEEK},
733 {"IYYY", 4, DCH_IYYY, TRUE, FROM_CHAR_DATE_ISOWEEK},
734 {"IYY", 3, DCH_IYY, TRUE, FROM_CHAR_DATE_ISOWEEK},
735 {"IY", 2, DCH_IY, TRUE, FROM_CHAR_DATE_ISOWEEK},
736 {"I", 1, DCH_I, TRUE, FROM_CHAR_DATE_ISOWEEK},
737 {"J", 1, DCH_J, TRUE, FROM_CHAR_DATE_NONE}, /* J */
738 {"MI", 2, DCH_MI, TRUE, FROM_CHAR_DATE_NONE}, /* M */
739 {"MM", 2, DCH_MM, TRUE, FROM_CHAR_DATE_GREGORIAN},
740 {"MONTH", 5, DCH_MONTH, FALSE, FROM_CHAR_DATE_GREGORIAN},
741 {"MON", 3, DCH_MON, FALSE, FROM_CHAR_DATE_GREGORIAN},
742 {"MS", 2, DCH_MS, TRUE, FROM_CHAR_DATE_NONE},
743 {"Month", 5, DCH_Month, FALSE, FROM_CHAR_DATE_GREGORIAN},
744 {"Mon", 3, DCH_Mon, FALSE, FROM_CHAR_DATE_GREGORIAN},
745 {"P.M.", 4, DCH_P_M, FALSE, FROM_CHAR_DATE_NONE}, /* P */
746 {"PM", 2, DCH_PM, FALSE, FROM_CHAR_DATE_NONE},
747 {"Q", 1, DCH_Q, TRUE, FROM_CHAR_DATE_NONE}, /* Q */
748 {"RM", 2, DCH_RM, FALSE, FROM_CHAR_DATE_GREGORIAN}, /* R */
749 {"SSSS", 4, DCH_SSSS, TRUE, FROM_CHAR_DATE_NONE}, /* S */
750 {"SS", 2, DCH_SS, TRUE, FROM_CHAR_DATE_NONE},
751 {"TZ", 2, DCH_TZ, FALSE, FROM_CHAR_DATE_NONE}, /* T */
752 {"US", 2, DCH_US, TRUE, FROM_CHAR_DATE_NONE}, /* U */
753 {"WW", 2, DCH_WW, TRUE, FROM_CHAR_DATE_GREGORIAN}, /* W */
754 {"W", 1, DCH_W, TRUE, FROM_CHAR_DATE_GREGORIAN},
755 {"Y,YYY", 5, DCH_Y_YYY, TRUE, FROM_CHAR_DATE_GREGORIAN}, /* Y */
756 {"YYYY", 4, DCH_YYYY, TRUE, FROM_CHAR_DATE_GREGORIAN},
757 {"YYY", 3, DCH_YYY, TRUE, FROM_CHAR_DATE_GREGORIAN},
758 {"YY", 2, DCH_YY, TRUE, FROM_CHAR_DATE_GREGORIAN},
759 {"Y", 1, DCH_Y, TRUE, FROM_CHAR_DATE_GREGORIAN},
760 {"a.d.", 4, DCH_a_d, FALSE, FROM_CHAR_DATE_NONE}, /* a */
761 {"a.m.", 4, DCH_a_m, FALSE, FROM_CHAR_DATE_NONE},
762 {"ad", 2, DCH_ad, FALSE, FROM_CHAR_DATE_NONE},
763 {"am", 2, DCH_am, FALSE, FROM_CHAR_DATE_NONE},
764 {"b.c.", 4, DCH_b_c, FALSE, FROM_CHAR_DATE_NONE}, /* b */
765 {"bc", 2, DCH_bc, FALSE, FROM_CHAR_DATE_NONE},
766 {"cc", 2, DCH_CC, TRUE, FROM_CHAR_DATE_NONE}, /* c */
767 {"day", 3, DCH_day, FALSE, FROM_CHAR_DATE_NONE}, /* d */
768 {"ddd", 3, DCH_DDD, TRUE, FROM_CHAR_DATE_GREGORIAN},
769 {"dd", 2, DCH_DD, TRUE, FROM_CHAR_DATE_GREGORIAN},
770 {"dy", 2, DCH_dy, FALSE, FROM_CHAR_DATE_NONE},
771 {"d", 1, DCH_D, TRUE, FROM_CHAR_DATE_GREGORIAN},
772 {"fx", 2, DCH_FX, FALSE, FROM_CHAR_DATE_NONE}, /* f */
773 {"hh24", 4, DCH_HH24, TRUE, FROM_CHAR_DATE_NONE}, /* h */
774 {"hh12", 4, DCH_HH12, TRUE, FROM_CHAR_DATE_NONE},
775 {"hh", 2, DCH_HH, TRUE, FROM_CHAR_DATE_NONE},
776 {"iddd", 4, DCH_IDDD, TRUE, FROM_CHAR_DATE_ISOWEEK}, /* i */
777 {"id", 2, DCH_ID, TRUE, FROM_CHAR_DATE_ISOWEEK},
778 {"iw", 2, DCH_IW, TRUE, FROM_CHAR_DATE_ISOWEEK},
779 {"iyyy", 4, DCH_IYYY, TRUE, FROM_CHAR_DATE_ISOWEEK},
780 {"iyy", 3, DCH_IYY, TRUE, FROM_CHAR_DATE_ISOWEEK},
781 {"iy", 2, DCH_IY, TRUE, FROM_CHAR_DATE_ISOWEEK},
782 {"i", 1, DCH_I, TRUE, FROM_CHAR_DATE_ISOWEEK},
783 {"j", 1, DCH_J, TRUE, FROM_CHAR_DATE_NONE}, /* j */
784 {"mi", 2, DCH_MI, TRUE, FROM_CHAR_DATE_NONE}, /* m */
785 {"mm", 2, DCH_MM, TRUE, FROM_CHAR_DATE_GREGORIAN},
786 {"month", 5, DCH_month, FALSE, FROM_CHAR_DATE_GREGORIAN},
787 {"mon", 3, DCH_mon, FALSE, FROM_CHAR_DATE_GREGORIAN},
788 {"ms", 2, DCH_MS, TRUE, FROM_CHAR_DATE_NONE},
789 {"p.m.", 4, DCH_p_m, FALSE, FROM_CHAR_DATE_NONE}, /* p */
790 {"pm", 2, DCH_pm, FALSE, FROM_CHAR_DATE_NONE},
791 {"q", 1, DCH_Q, TRUE, FROM_CHAR_DATE_NONE}, /* q */
792 {"rm", 2, DCH_rm, FALSE, FROM_CHAR_DATE_GREGORIAN}, /* r */
793 {"ssss", 4, DCH_SSSS, TRUE, FROM_CHAR_DATE_NONE}, /* s */
794 {"ss", 2, DCH_SS, TRUE, FROM_CHAR_DATE_NONE},
795 {"tz", 2, DCH_tz, FALSE, FROM_CHAR_DATE_NONE}, /* t */
796 {"us", 2, DCH_US, TRUE, FROM_CHAR_DATE_NONE}, /* u */
797 {"ww", 2, DCH_WW, TRUE, FROM_CHAR_DATE_GREGORIAN}, /* w */
798 {"w", 1, DCH_W, TRUE, FROM_CHAR_DATE_GREGORIAN},
799 {"y,yyy", 5, DCH_Y_YYY, TRUE, FROM_CHAR_DATE_GREGORIAN}, /* y */
800 {"yyyy", 4, DCH_YYYY, TRUE, FROM_CHAR_DATE_GREGORIAN},
801 {"yyy", 3, DCH_YYY, TRUE, FROM_CHAR_DATE_GREGORIAN},
802 {"yy", 2, DCH_YY, TRUE, FROM_CHAR_DATE_GREGORIAN},
803 {"y", 1, DCH_Y, TRUE, FROM_CHAR_DATE_GREGORIAN},
805 /* last */
806 {NULL, 0, 0, 0, 0}
809 /* ----------
810 * KeyWords for NUMBER version
812 * The is_digit and date_mode fields are not relevant here.
813 * ----------
815 static const KeyWord NUM_keywords[] = {
816 /* name, len, id is in Index */
817 {",", 1, NUM_COMMA}, /* , */
818 {".", 1, NUM_DEC}, /* . */
819 {"0", 1, NUM_0}, /* 0 */
820 {"9", 1, NUM_9}, /* 9 */
821 {"B", 1, NUM_B}, /* B */
822 {"C", 1, NUM_C}, /* C */
823 {"D", 1, NUM_D}, /* D */
824 {"E", 1, NUM_E}, /* E */
825 {"FM", 2, NUM_FM}, /* F */
826 {"G", 1, NUM_G}, /* G */
827 {"L", 1, NUM_L}, /* L */
828 {"MI", 2, NUM_MI}, /* M */
829 {"PL", 2, NUM_PL}, /* P */
830 {"PR", 2, NUM_PR},
831 {"RN", 2, NUM_RN}, /* R */
832 {"SG", 2, NUM_SG}, /* S */
833 {"SP", 2, NUM_SP},
834 {"S", 1, NUM_S},
835 {"TH", 2, NUM_TH}, /* T */
836 {"V", 1, NUM_V}, /* V */
837 {"b", 1, NUM_B}, /* b */
838 {"c", 1, NUM_C}, /* c */
839 {"d", 1, NUM_D}, /* d */
840 {"e", 1, NUM_E}, /* e */
841 {"fm", 2, NUM_FM}, /* f */
842 {"g", 1, NUM_G}, /* g */
843 {"l", 1, NUM_L}, /* l */
844 {"mi", 2, NUM_MI}, /* m */
845 {"pl", 2, NUM_PL}, /* p */
846 {"pr", 2, NUM_PR},
847 {"rn", 2, NUM_rn}, /* r */
848 {"sg", 2, NUM_SG}, /* s */
849 {"sp", 2, NUM_SP},
850 {"s", 1, NUM_S},
851 {"th", 2, NUM_th}, /* t */
852 {"v", 1, NUM_V}, /* v */
854 /* last */
855 {NULL, 0, 0}
859 /* ----------
860 * KeyWords index for DATE-TIME version
861 * ----------
863 static const int DCH_index[KeyWord_INDEX_SIZE] = {
865 0 1 2 3 4 5 6 7 8 9
867 /*---- first 0..31 chars are skipped ----*/
869 -1, -1, -1, -1, -1, -1, -1, -1,
870 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
871 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
872 -1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1,
873 DCH_FX, -1, DCH_HH24, DCH_IDDD, DCH_J, -1, -1, DCH_MI, -1, -1,
874 DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, DCH_TZ, DCH_US, -1, DCH_WW, -1, DCH_Y_YYY,
875 -1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc,
876 DCH_day, -1, DCH_fx, -1, DCH_hh24, DCH_iddd, DCH_j, -1, -1, DCH_mi,
877 -1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, DCH_us, -1, DCH_ww,
878 -1, DCH_y_yyy, -1, -1, -1, -1
880 /*---- chars over 126 are skipped ----*/
883 /* ----------
884 * KeyWords index for NUMBER version
885 * ----------
887 static const int NUM_index[KeyWord_INDEX_SIZE] = {
889 0 1 2 3 4 5 6 7 8 9
891 /*---- first 0..31 chars are skipped ----*/
893 -1, -1, -1, -1, -1, -1, -1, -1,
894 -1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1,
895 -1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1,
896 -1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E,
897 NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1,
898 NUM_PL, -1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1,
899 -1, -1, -1, -1, -1, -1, -1, -1, NUM_b, NUM_c,
900 NUM_d, NUM_e, NUM_fm, NUM_g, -1, -1, -1, -1, NUM_l, NUM_mi,
901 -1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1,
902 -1, -1, -1, -1, -1, -1
904 /*---- chars over 126 are skipped ----*/
907 /* ----------
908 * Number processor struct
909 * ----------
911 typedef struct NUMProc
913 bool is_to_char;
914 NUMDesc *Num; /* number description */
916 int sign, /* '-' or '+' */
917 sign_wrote, /* was sign write */
918 num_count, /* number of write digits */
919 num_in, /* is inside number */
920 num_curr, /* current position in number */
921 num_pre, /* space before first number */
923 read_dec, /* to_number - was read dec. point */
924 read_post, /* to_number - number of dec. digit */
925 read_pre; /* to_number - number non-dec. digit */
927 char *number, /* string with number */
928 *number_p, /* pointer to current number position */
929 *inout, /* in / out buffer */
930 *inout_p, /* pointer to current inout position */
931 *last_relevant, /* last relevant number after decimal point */
933 *L_negative_sign, /* Locale */
934 *L_positive_sign,
935 *decimal,
936 *L_thousands_sep,
937 *L_currency_symbol;
938 } NUMProc;
941 /* ----------
942 * Functions
943 * ----------
945 static const KeyWord *index_seq_search(char *str, const KeyWord *kw,
946 const int *index);
947 static KeySuffix *suff_search(char *str, KeySuffix *suf, int type);
948 static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
949 static void parse_format(FormatNode *node, char *str, const KeyWord *kw,
950 KeySuffix *suf, const int *index, int ver, NUMDesc *Num);
952 static void DCH_to_char(FormatNode *node, bool is_interval,
953 TmToChar *in, char *out);
954 static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out);
956 #ifdef DEBUG_TO_FROM_CHAR
957 static void dump_index(const KeyWord *k, const int *index);
958 static void dump_node(FormatNode *node, int max);
959 #endif
961 static char *get_th(char *num, int type);
962 static char *str_numth(char *dest, char *num, int type);
963 static int strspace_len(char *str);
964 static int strdigits_len(char *str);
965 static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode);
966 static void from_char_set_int(int *dest, const int value, const FormatNode *node);
967 static int from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node);
968 static int from_char_parse_int(int *dest, char **src, FormatNode *node);
969 static int seq_search(char *name, char **array, int type, int max, int *len);
970 static int from_char_seq_search(int *dest, char **src, char **array, int type, int max, FormatNode *node);
971 static void do_to_timestamp(text *date_txt, text *fmt,
972 struct pg_tm * tm, fsec_t *fsec);
973 static char *fill_str(char *str, int c, int max);
974 static FormatNode *NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree);
975 static char *int_to_roman(int number);
976 static void NUM_prepare_locale(NUMProc *Np);
977 static char *get_last_relevant_decnum(char *num);
978 static void NUM_numpart_from_char(NUMProc *Np, int id, int plen);
979 static void NUM_numpart_to_char(NUMProc *Np, int id);
980 static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
981 int plen, int sign, bool is_to_char);
982 static DCHCacheEntry *DCH_cache_search(char *str);
983 static DCHCacheEntry *DCH_cache_getnew(char *str);
985 static NUMCacheEntry *NUM_cache_search(char *str);
986 static NUMCacheEntry *NUM_cache_getnew(char *str);
987 static void NUM_cache_remove(NUMCacheEntry *ent);
990 /* ----------
991 * Fast sequential search, use index for data selection which
992 * go to seq. cycle (it is very fast for unwanted strings)
993 * (can't be used binary search in format parsing)
994 * ----------
996 static const KeyWord *
997 index_seq_search(char *str, const KeyWord *kw, const int *index)
999 int poz;
1001 if (!KeyWord_INDEX_FILTER(*str))
1002 return NULL;
1004 if ((poz = *(index + (*str - ' '))) > -1)
1006 const KeyWord *k = kw + poz;
1010 if (!strncmp(str, k->name, k->len))
1011 return k;
1012 k++;
1013 if (!k->name)
1014 return NULL;
1015 } while (*str == *k->name);
1017 return NULL;
1020 static KeySuffix *
1021 suff_search(char *str, KeySuffix *suf, int type)
1023 KeySuffix *s;
1025 for (s = suf; s->name != NULL; s++)
1027 if (s->type != type)
1028 continue;
1030 if (!strncmp(str, s->name, s->len))
1031 return s;
1033 return NULL;
1036 /* ----------
1037 * Prepare NUMDesc (number description struct) via FormatNode struct
1038 * ----------
1040 static void
1041 NUMDesc_prepare(NUMDesc *num, FormatNode *n)
1044 if (n->type != NODE_TYPE_ACTION)
1045 return;
1047 switch (n->key->id)
1049 case NUM_9:
1050 if (IS_BRACKET(num))
1052 NUM_cache_remove(last_NUMCacheEntry);
1053 ereport(ERROR,
1054 (errcode(ERRCODE_SYNTAX_ERROR),
1055 errmsg("\"9\" must be ahead of \"PR\"")));
1057 if (IS_MULTI(num))
1059 ++num->multi;
1060 break;
1062 if (IS_DECIMAL(num))
1063 ++num->post;
1064 else
1065 ++num->pre;
1066 break;
1068 case NUM_0:
1069 if (IS_BRACKET(num))
1071 NUM_cache_remove(last_NUMCacheEntry);
1072 ereport(ERROR,
1073 (errcode(ERRCODE_SYNTAX_ERROR),
1074 errmsg("\"0\" must be ahead of \"PR\"")));
1076 if (!IS_ZERO(num) && !IS_DECIMAL(num))
1078 num->flag |= NUM_F_ZERO;
1079 num->zero_start = num->pre + 1;
1081 if (!IS_DECIMAL(num))
1082 ++num->pre;
1083 else
1084 ++num->post;
1086 num->zero_end = num->pre + num->post;
1087 break;
1089 case NUM_B:
1090 if (num->pre == 0 && num->post == 0 && (!IS_ZERO(num)))
1091 num->flag |= NUM_F_BLANK;
1092 break;
1094 case NUM_D:
1095 num->flag |= NUM_F_LDECIMAL;
1096 num->need_locale = TRUE;
1097 case NUM_DEC:
1098 if (IS_DECIMAL(num))
1100 NUM_cache_remove(last_NUMCacheEntry);
1101 ereport(ERROR,
1102 (errcode(ERRCODE_SYNTAX_ERROR),
1103 errmsg("multiple decimal points")));
1105 if (IS_MULTI(num))
1107 NUM_cache_remove(last_NUMCacheEntry);
1108 ereport(ERROR,
1109 (errcode(ERRCODE_SYNTAX_ERROR),
1110 errmsg("cannot use \"V\" and decimal point together")));
1112 num->flag |= NUM_F_DECIMAL;
1113 break;
1115 case NUM_FM:
1116 num->flag |= NUM_F_FILLMODE;
1117 break;
1119 case NUM_S:
1120 if (IS_LSIGN(num))
1122 NUM_cache_remove(last_NUMCacheEntry);
1123 ereport(ERROR,
1124 (errcode(ERRCODE_SYNTAX_ERROR),
1125 errmsg("cannot use \"S\" twice")));
1127 if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
1129 NUM_cache_remove(last_NUMCacheEntry);
1130 ereport(ERROR,
1131 (errcode(ERRCODE_SYNTAX_ERROR),
1132 errmsg("cannot use \"S\" and \"PL\"/\"MI\"/\"SG\"/\"PR\" together")));
1134 if (!IS_DECIMAL(num))
1136 num->lsign = NUM_LSIGN_PRE;
1137 num->pre_lsign_num = num->pre;
1138 num->need_locale = TRUE;
1139 num->flag |= NUM_F_LSIGN;
1141 else if (num->lsign == NUM_LSIGN_NONE)
1143 num->lsign = NUM_LSIGN_POST;
1144 num->need_locale = TRUE;
1145 num->flag |= NUM_F_LSIGN;
1147 break;
1149 case NUM_MI:
1150 if (IS_LSIGN(num))
1152 NUM_cache_remove(last_NUMCacheEntry);
1153 ereport(ERROR,
1154 (errcode(ERRCODE_SYNTAX_ERROR),
1155 errmsg("cannot use \"S\" and \"MI\" together")));
1157 num->flag |= NUM_F_MINUS;
1158 if (IS_DECIMAL(num))
1159 num->flag |= NUM_F_MINUS_POST;
1160 break;
1162 case NUM_PL:
1163 if (IS_LSIGN(num))
1165 NUM_cache_remove(last_NUMCacheEntry);
1166 ereport(ERROR,
1167 (errcode(ERRCODE_SYNTAX_ERROR),
1168 errmsg("cannot use \"S\" and \"PL\" together")));
1170 num->flag |= NUM_F_PLUS;
1171 if (IS_DECIMAL(num))
1172 num->flag |= NUM_F_PLUS_POST;
1173 break;
1175 case NUM_SG:
1176 if (IS_LSIGN(num))
1178 NUM_cache_remove(last_NUMCacheEntry);
1179 ereport(ERROR,
1180 (errcode(ERRCODE_SYNTAX_ERROR),
1181 errmsg("cannot use \"S\" and \"SG\" together")));
1183 num->flag |= NUM_F_MINUS;
1184 num->flag |= NUM_F_PLUS;
1185 break;
1187 case NUM_PR:
1188 if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num))
1190 NUM_cache_remove(last_NUMCacheEntry);
1191 ereport(ERROR,
1192 (errcode(ERRCODE_SYNTAX_ERROR),
1193 errmsg("cannot use \"PR\" and \"S\"/\"PL\"/\"MI\"/\"SG\" together")));
1195 num->flag |= NUM_F_BRACKET;
1196 break;
1198 case NUM_rn:
1199 case NUM_RN:
1200 num->flag |= NUM_F_ROMAN;
1201 break;
1203 case NUM_L:
1204 case NUM_G:
1205 num->need_locale = TRUE;
1206 break;
1208 case NUM_V:
1209 if (IS_DECIMAL(num))
1211 NUM_cache_remove(last_NUMCacheEntry);
1212 ereport(ERROR,
1213 (errcode(ERRCODE_SYNTAX_ERROR),
1214 errmsg("cannot use \"V\" and decimal point together")));
1216 num->flag |= NUM_F_MULTI;
1217 break;
1219 case NUM_E:
1220 NUM_cache_remove(last_NUMCacheEntry);
1221 ereport(ERROR,
1222 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1223 errmsg("\"E\" is not supported")));
1226 return;
1229 /* ----------
1230 * Format parser, search small keywords and keyword's suffixes, and make
1231 * format-node tree.
1233 * for DATE-TIME & NUMBER version
1234 * ----------
1236 static void
1237 parse_format(FormatNode *node, char *str, const KeyWord *kw,
1238 KeySuffix *suf, const int *index, int ver, NUMDesc *Num)
1240 KeySuffix *s;
1241 FormatNode *n;
1242 int node_set = 0,
1243 suffix,
1244 last = 0;
1246 #ifdef DEBUG_TO_FROM_CHAR
1247 elog(DEBUG_elog_output, "to_char/number(): run parser");
1248 #endif
1250 n = node;
1252 while (*str)
1254 suffix = 0;
1257 * Prefix
1259 if (ver == DCH_TYPE && (s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL)
1261 suffix |= s->id;
1262 if (s->len)
1263 str += s->len;
1267 * Keyword
1269 if (*str && (n->key = index_seq_search(str, kw, index)) != NULL)
1271 n->type = NODE_TYPE_ACTION;
1272 n->suffix = 0;
1273 node_set = 1;
1274 if (n->key->len)
1275 str += n->key->len;
1278 * NUM version: Prepare global NUMDesc struct
1280 if (ver == NUM_TYPE)
1281 NUMDesc_prepare(Num, n);
1284 * Postfix
1286 if (ver == DCH_TYPE && *str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL)
1288 suffix |= s->id;
1289 if (s->len)
1290 str += s->len;
1293 else if (*str)
1296 * Special characters '\' and '"'
1298 if (*str == '"' && last != '\\')
1300 int x = 0;
1302 while (*(++str))
1304 if (*str == '"' && x != '\\')
1306 str++;
1307 break;
1309 else if (*str == '\\' && x != '\\')
1311 x = '\\';
1312 continue;
1314 n->type = NODE_TYPE_CHAR;
1315 n->character = *str;
1316 n->key = NULL;
1317 n->suffix = 0;
1318 ++n;
1319 x = *str;
1321 node_set = 0;
1322 suffix = 0;
1323 last = 0;
1325 else if (*str && *str == '\\' && last != '\\' && *(str + 1) == '"')
1327 last = *str;
1328 str++;
1330 else if (*str)
1332 n->type = NODE_TYPE_CHAR;
1333 n->character = *str;
1334 n->key = NULL;
1335 node_set = 1;
1336 last = 0;
1337 str++;
1341 /* end */
1342 if (node_set)
1344 if (n->type == NODE_TYPE_ACTION)
1345 n->suffix = suffix;
1346 ++n;
1348 n->suffix = 0;
1349 node_set = 0;
1353 n->type = NODE_TYPE_END;
1354 n->suffix = 0;
1355 return;
1358 /* ----------
1359 * DEBUG: Dump the FormatNode Tree (debug)
1360 * ----------
1362 #ifdef DEBUG_TO_FROM_CHAR
1364 #define DUMP_THth(_suf) (S_TH(_suf) ? "TH" : (S_th(_suf) ? "th" : " "))
1365 #define DUMP_FM(_suf) (S_FM(_suf) ? "FM" : " ")
1367 static void
1368 dump_node(FormatNode *node, int max)
1370 FormatNode *n;
1371 int a;
1373 elog(DEBUG_elog_output, "to_from-char(): DUMP FORMAT");
1375 for (a = 0, n = node; a <= max; n++, a++)
1377 if (n->type == NODE_TYPE_ACTION)
1378 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_ACTION '%s'\t(%s,%s)",
1379 a, n->key->name, DUMP_THth(n->suffix), DUMP_FM(n->suffix));
1380 else if (n->type == NODE_TYPE_CHAR)
1381 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_CHAR '%c'", a, n->character);
1382 else if (n->type == NODE_TYPE_END)
1384 elog(DEBUG_elog_output, "%d:\t NODE_TYPE_END", a);
1385 return;
1387 else
1388 elog(DEBUG_elog_output, "%d:\t unknown NODE!", a);
1391 #endif /* DEBUG */
1393 /*****************************************************************************
1394 * Private utils
1395 *****************************************************************************/
1397 /* ----------
1398 * Return ST/ND/RD/TH for simple (1..9) numbers
1399 * type --> 0 upper, 1 lower
1400 * ----------
1402 static char *
1403 get_th(char *num, int type)
1405 int len = strlen(num),
1406 last,
1407 seclast;
1409 last = *(num + (len - 1));
1410 if (!isdigit((unsigned char) last))
1411 ereport(ERROR,
1412 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1413 errmsg("\"%s\" is not a number", num)));
1416 * All "teens" (<x>1[0-9]) get 'TH/th', while <x>[02-9][123] still get
1417 * 'ST/st', 'ND/nd', 'RD/rd', respectively
1419 if ((len > 1) && ((seclast = num[len - 2]) == '1'))
1420 last = 0;
1422 switch (last)
1424 case '1':
1425 if (type == TH_UPPER)
1426 return numTH[0];
1427 return numth[0];
1428 case '2':
1429 if (type == TH_UPPER)
1430 return numTH[1];
1431 return numth[1];
1432 case '3':
1433 if (type == TH_UPPER)
1434 return numTH[2];
1435 return numth[2];
1436 default:
1437 if (type == TH_UPPER)
1438 return numTH[3];
1439 return numth[3];
1441 return NULL;
1444 /* ----------
1445 * Convert string-number to ordinal string-number
1446 * type --> 0 upper, 1 lower
1447 * ----------
1449 static char *
1450 str_numth(char *dest, char *num, int type)
1452 if (dest != num)
1453 strcpy(dest, num);
1454 strcat(dest, get_th(num, type));
1455 return dest;
1459 * If the system provides the needed functions for wide-character manipulation
1460 * (which are all standardized by C99), then we implement upper/lower/initcap
1461 * using wide-character functions, if necessary. Otherwise we use the
1462 * traditional <ctype.h> functions, which of course will not work as desired
1463 * in multibyte character sets. Note that in either case we are effectively
1464 * assuming that the database character encoding matches the encoding implied
1465 * by LC_CTYPE.
1469 * wide-character-aware lower function
1471 * We pass the number of bytes so we can pass varlena and char*
1472 * to this function. The result is a palloc'd, null-terminated string.
1474 char *
1475 str_tolower(const char *buff, size_t nbytes)
1477 char *result;
1479 if (!buff)
1480 return NULL;
1482 #ifdef USE_WIDE_UPPER_LOWER
1483 if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
1485 wchar_t *workspace;
1486 size_t curr_char;
1487 size_t result_size;
1489 /* Overflow paranoia */
1490 if ((nbytes + 1) > (INT_MAX / sizeof(wchar_t)))
1491 ereport(ERROR,
1492 (errcode(ERRCODE_OUT_OF_MEMORY),
1493 errmsg("out of memory")));
1495 /* Output workspace cannot have more codes than input bytes */
1496 workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
1498 char2wchar(workspace, nbytes + 1, buff, nbytes);
1500 for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
1501 workspace[curr_char] = towlower(workspace[curr_char]);
1503 /* Make result large enough; case change might change number of bytes */
1504 result_size = curr_char * pg_database_encoding_max_length() + 1;
1505 result = palloc(result_size);
1507 wchar2char(result, workspace, result_size);
1508 pfree(workspace);
1510 else
1511 #endif /* USE_WIDE_UPPER_LOWER */
1513 char *p;
1515 result = pnstrdup(buff, nbytes);
1517 for (p = result; *p; p++)
1518 *p = pg_tolower((unsigned char) *p);
1521 return result;
1525 * wide-character-aware upper function
1527 * We pass the number of bytes so we can pass varlena and char*
1528 * to this function. The result is a palloc'd, null-terminated string.
1530 char *
1531 str_toupper(const char *buff, size_t nbytes)
1533 char *result;
1535 if (!buff)
1536 return NULL;
1538 #ifdef USE_WIDE_UPPER_LOWER
1539 if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
1541 wchar_t *workspace;
1542 size_t curr_char;
1543 size_t result_size;
1545 /* Overflow paranoia */
1546 if ((nbytes + 1) > (INT_MAX / sizeof(wchar_t)))
1547 ereport(ERROR,
1548 (errcode(ERRCODE_OUT_OF_MEMORY),
1549 errmsg("out of memory")));
1551 /* Output workspace cannot have more codes than input bytes */
1552 workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
1554 char2wchar(workspace, nbytes + 1, buff, nbytes);
1556 for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
1557 workspace[curr_char] = towupper(workspace[curr_char]);
1559 /* Make result large enough; case change might change number of bytes */
1560 result_size = curr_char * pg_database_encoding_max_length() + 1;
1561 result = palloc(result_size);
1563 wchar2char(result, workspace, result_size);
1564 pfree(workspace);
1566 else
1567 #endif /* USE_WIDE_UPPER_LOWER */
1569 char *p;
1571 result = pnstrdup(buff, nbytes);
1573 for (p = result; *p; p++)
1574 *p = pg_toupper((unsigned char) *p);
1577 return result;
1581 * wide-character-aware initcap function
1583 * We pass the number of bytes so we can pass varlena and char*
1584 * to this function. The result is a palloc'd, null-terminated string.
1586 char *
1587 str_initcap(const char *buff, size_t nbytes)
1589 char *result;
1590 int wasalnum = false;
1592 if (!buff)
1593 return NULL;
1595 #ifdef USE_WIDE_UPPER_LOWER
1596 if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
1598 wchar_t *workspace;
1599 size_t curr_char;
1600 size_t result_size;
1602 /* Overflow paranoia */
1603 if ((nbytes + 1) > (INT_MAX / sizeof(wchar_t)))
1604 ereport(ERROR,
1605 (errcode(ERRCODE_OUT_OF_MEMORY),
1606 errmsg("out of memory")));
1608 /* Output workspace cannot have more codes than input bytes */
1609 workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
1611 char2wchar(workspace, nbytes + 1, buff, nbytes);
1613 for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
1615 if (wasalnum)
1616 workspace[curr_char] = towlower(workspace[curr_char]);
1617 else
1618 workspace[curr_char] = towupper(workspace[curr_char]);
1619 wasalnum = iswalnum(workspace[curr_char]);
1622 /* Make result large enough; case change might change number of bytes */
1623 result_size = curr_char * pg_database_encoding_max_length() + 1;
1624 result = palloc(result_size);
1626 wchar2char(result, workspace, result_size);
1627 pfree(workspace);
1629 else
1630 #endif /* USE_WIDE_UPPER_LOWER */
1632 char *p;
1634 result = pnstrdup(buff, nbytes);
1636 for (p = result; *p; p++)
1638 if (wasalnum)
1639 *p = pg_tolower((unsigned char) *p);
1640 else
1641 *p = pg_toupper((unsigned char) *p);
1642 wasalnum = isalnum((unsigned char) *p);
1646 return result;
1649 /* convenience routines for when the input is null-terminated */
1651 static char *
1652 str_tolower_z(const char *buff)
1654 return str_tolower(buff, strlen(buff));
1657 static char *
1658 str_toupper_z(const char *buff)
1660 return str_toupper(buff, strlen(buff));
1663 static char *
1664 str_initcap_z(const char *buff)
1666 return str_initcap(buff, strlen(buff));
1670 /* ----------
1671 * Skip TM / th in FROM_CHAR
1672 * ----------
1674 #define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
1676 #ifdef DEBUG_TO_FROM_CHAR
1677 /* -----------
1678 * DEBUG: Call for debug and for index checking; (Show ASCII char
1679 * and defined keyword for each used position
1680 * ----------
1682 static void
1683 dump_index(const KeyWord *k, const int *index)
1685 int i,
1686 count = 0,
1687 free_i = 0;
1689 elog(DEBUG_elog_output, "TO-FROM_CHAR: Dump KeyWord Index:");
1691 for (i = 0; i < KeyWord_INDEX_SIZE; i++)
1693 if (index[i] != -1)
1695 elog(DEBUG_elog_output, "\t%c: %s, ", i + 32, k[index[i]].name);
1696 count++;
1698 else
1700 free_i++;
1701 elog(DEBUG_elog_output, "\t(%d) %c %d", i, i + 32, index[i]);
1704 elog(DEBUG_elog_output, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d",
1705 count, free_i);
1707 #endif /* DEBUG */
1709 /* ----------
1710 * Return TRUE if next format picture is not digit value
1711 * ----------
1713 static bool
1714 is_next_separator(FormatNode *n)
1716 if (n->type == NODE_TYPE_END)
1717 return FALSE;
1719 if (n->type == NODE_TYPE_ACTION && S_THth(n->suffix))
1720 return TRUE;
1723 * Next node
1725 n++;
1727 /* end of format string is treated like a non-digit separator */
1728 if (n->type == NODE_TYPE_END)
1729 return TRUE;
1731 if (n->type == NODE_TYPE_ACTION)
1733 if (n->key->is_digit)
1734 return FALSE;
1736 return TRUE;
1738 else if (isdigit((unsigned char) n->character))
1739 return FALSE;
1741 return TRUE; /* some non-digit input (separator) */
1744 static int
1745 strspace_len(char *str)
1747 int len = 0;
1749 while (*str && isspace((unsigned char) *str))
1751 str++;
1752 len++;
1754 return len;
1757 static int
1758 strdigits_len(char *str)
1760 char *p = str;
1761 int len;
1763 len = strspace_len(str);
1764 p += len;
1766 while (*p && isdigit((unsigned char) *p) && len <= DCH_MAX_ITEM_SIZ)
1768 len++;
1769 p++;
1771 return len;
1775 * Set the date mode of a from-char conversion.
1777 * Puke if the date mode has already been set, and the caller attempts to set
1778 * it to a conflicting mode.
1780 static void
1781 from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode)
1783 if (mode != FROM_CHAR_DATE_NONE)
1785 if (tmfc->mode == FROM_CHAR_DATE_NONE)
1786 tmfc->mode = mode;
1787 else if (tmfc->mode != mode)
1788 ereport(ERROR,
1789 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1790 errmsg("invalid combination of date conventions"),
1791 errhint("Do not mix Gregorian and ISO week date "
1792 "conventions in a formatting template.")));
1797 * Set the integer pointed to by 'dest' to the given value.
1799 * Puke if the destination integer has previously been set to some other
1800 * non-zero value.
1802 static void
1803 from_char_set_int(int *dest, const int value, const FormatNode *node)
1805 if (*dest != 0 && *dest != value)
1806 ereport(ERROR,
1807 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1808 errmsg("conflicting values for \"%s\" field in formatting string",
1809 node->key->name),
1810 errdetail("This value contradicts a previous setting for "
1811 "the same field type.")));
1812 *dest = value;
1816 * Read a single integer from the source string, into the int pointed to by
1817 * 'dest'. If 'dest' is NULL, the result is discarded.
1819 * In fixed-width mode (the node does not have the FM suffix), consume at most
1820 * 'len' characters. However, any leading whitespace isn't counted in 'len'.
1822 * We use strtol() to recover the integer value from the source string, in
1823 * accordance with the given FormatNode.
1825 * If the conversion completes successfully, src will have been advanced to
1826 * point at the character immediately following the last character used in the
1827 * conversion.
1829 * Return the number of characters consumed.
1831 * Note that from_char_parse_int() provides a more convenient wrapper where
1832 * the length of the field is the same as the length of the format keyword (as
1833 * with DD and MI).
1835 static int
1836 from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node)
1838 long result;
1839 char copy[DCH_MAX_ITEM_SIZ + 1];
1840 char *init = *src;
1841 int used;
1844 * Skip any whitespace before parsing the integer.
1846 *src += strspace_len(*src);
1848 Assert(len <= DCH_MAX_ITEM_SIZ);
1849 used = (int) strlcpy(copy, *src, len + 1);
1851 if (S_FM(node->suffix) || is_next_separator(node))
1854 * This node is in Fill Mode, or the next node is known to be a
1855 * non-digit value, so we just slurp as many characters as we can get.
1857 errno = 0;
1858 result = strtol(init, src, 10);
1860 else
1863 * We need to pull exactly the number of characters given in 'len' out
1864 * of the string, and convert those.
1866 char *last;
1868 if (used < len)
1869 ereport(ERROR,
1870 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1871 errmsg("source string too short for \"%s\" formatting field",
1872 node->key->name),
1873 errdetail("Field requires %d characters, but only %d "
1874 "remain.",
1875 len, used),
1876 errhint("If your source string is not fixed-width, try "
1877 "using the \"FM\" modifier.")));
1879 errno = 0;
1880 result = strtol(copy, &last, 10);
1881 used = last - copy;
1883 if (used > 0 && used < len)
1884 ereport(ERROR,
1885 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1886 errmsg("invalid value \"%s\" for \"%s\"",
1887 copy, node->key->name),
1888 errdetail("Field requires %d characters, but only %d "
1889 "could be parsed.", len, used),
1890 errhint("If your source string is not fixed-width, try "
1891 "using the \"FM\" modifier.")));
1893 *src += used;
1896 if (*src == init)
1897 ereport(ERROR,
1898 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1899 errmsg("invalid value \"%s\" for \"%s\"",
1900 copy, node->key->name),
1901 errdetail("Value must be an integer.")));
1903 if (errno == ERANGE || result < INT_MIN || result > INT_MAX)
1904 ereport(ERROR,
1905 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1906 errmsg("value for \"%s\" in source string is out of range",
1907 node->key->name),
1908 errdetail("Value must be in the range %d to %d.",
1909 INT_MIN, INT_MAX)));
1911 if (dest != NULL)
1912 from_char_set_int(dest, (int) result, node);
1913 return *src - init;
1917 * Call from_char_parse_int_len(), using the length of the format keyword as
1918 * the expected length of the field.
1920 * Don't call this function if the field differs in length from the format
1921 * keyword (as with HH24; the keyword length is 4, but the field length is 2).
1922 * In such cases, call from_char_parse_int_len() instead to specify the
1923 * required length explictly.
1925 static int
1926 from_char_parse_int(int *dest, char **src, FormatNode *node)
1928 return from_char_parse_int_len(dest, src, node->key->len, node);
1931 /* ----------
1932 * Sequential search with to upper/lower conversion
1933 * ----------
1935 static int
1936 seq_search(char *name, char **array, int type, int max, int *len)
1938 char *p,
1940 **a;
1941 int last,
1944 *len = 0;
1946 if (!*name)
1947 return -1;
1949 /* set first char */
1950 if (type == ONE_UPPER || type == ALL_UPPER)
1951 *name = pg_toupper((unsigned char) *name);
1952 else if (type == ALL_LOWER)
1953 *name = pg_tolower((unsigned char) *name);
1955 for (last = 0, a = array; *a != NULL; a++)
1957 /* comperate first chars */
1958 if (*name != **a)
1959 continue;
1961 for (i = 1, p = *a + 1, n = name + 1;; n++, p++, i++)
1963 /* search fragment (max) only */
1964 if (max && i == max)
1966 *len = i;
1967 return a - array;
1969 /* full size */
1970 if (*p == '\0')
1972 *len = i;
1973 return a - array;
1975 /* Not found in array 'a' */
1976 if (*n == '\0')
1977 break;
1980 * Convert (but convert new chars only)
1982 if (i > last)
1984 if (type == ONE_UPPER || type == ALL_LOWER)
1985 *n = pg_tolower((unsigned char) *n);
1986 else if (type == ALL_UPPER)
1987 *n = pg_toupper((unsigned char) *n);
1988 last = i;
1991 #ifdef DEBUG_TO_FROM_CHAR
1992 elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)",
1993 *n, *p, *a, name);
1994 #endif
1995 if (*n != *p)
1996 break;
2000 return -1;
2004 * Perform a sequential search in 'array' for text matching the first 'max'
2005 * characters of the source string.
2007 * If a match is found, copy the array index of the match into the integer
2008 * pointed to by 'dest', advance 'src' to the end of the part of the string
2009 * which matched, and return the number of characters consumed.
2011 * If the string doesn't match, throw an error.
2013 static int
2014 from_char_seq_search(int *dest, char **src, char **array, int type, int max,
2015 FormatNode *node)
2017 int len;
2019 *dest = seq_search(*src, array, type, max, &len);
2020 if (len <= 0)
2022 char copy[DCH_MAX_ITEM_SIZ + 1];
2024 Assert(max <= DCH_MAX_ITEM_SIZ);
2025 strlcpy(copy, *src, max + 1);
2027 ereport(ERROR,
2028 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2029 errmsg("invalid value \"%s\" for \"%s\"",
2030 copy, node->key->name),
2031 errdetail("The given value did not match any of the allowed "
2032 "values for this field.")));
2034 *src += len;
2035 return len;
2038 /* ----------
2039 * Process a TmToChar struct as denoted by a list of FormatNodes.
2040 * The formatted data is written to the string pointed to by 'out'.
2041 * ----------
2043 static void
2044 DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
2046 FormatNode *n;
2047 char *s;
2048 struct pg_tm *tm = &in->tm;
2049 char buff[DCH_CACHE_SIZE];
2050 int i;
2052 /* cache localized days and months */
2053 cache_locale_time();
2055 s = out;
2056 for (n = node; n->type != NODE_TYPE_END; n++)
2058 if (n->type != NODE_TYPE_ACTION)
2060 *s = n->character;
2061 s++;
2062 continue;
2065 switch (n->key->id)
2067 case DCH_A_M:
2068 case DCH_P_M:
2069 strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
2070 ? P_M_STR : A_M_STR);
2071 s += strlen(s);
2072 break;
2073 case DCH_AM:
2074 case DCH_PM:
2075 strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
2076 ? PM_STR : AM_STR);
2077 s += strlen(s);
2078 break;
2079 case DCH_a_m:
2080 case DCH_p_m:
2081 strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
2082 ? p_m_STR : a_m_STR);
2083 s += strlen(s);
2084 break;
2085 case DCH_am:
2086 case DCH_pm:
2087 strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
2088 ? pm_STR : am_STR);
2089 s += strlen(s);
2090 break;
2091 case DCH_HH:
2092 case DCH_HH12:
2093 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
2094 tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ? 12 :
2095 tm->tm_hour % (HOURS_PER_DAY / 2));
2096 if (S_THth(n->suffix))
2097 str_numth(s, s, S_TH_TYPE(n->suffix));
2098 s += strlen(s);
2099 break;
2100 case DCH_HH24:
2101 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_hour);
2102 if (S_THth(n->suffix))
2103 str_numth(s, s, S_TH_TYPE(n->suffix));
2104 s += strlen(s);
2105 break;
2106 case DCH_MI:
2107 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_min);
2108 if (S_THth(n->suffix))
2109 str_numth(s, s, S_TH_TYPE(n->suffix));
2110 s += strlen(s);
2111 break;
2112 case DCH_SS:
2113 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_sec);
2114 if (S_THth(n->suffix))
2115 str_numth(s, s, S_TH_TYPE(n->suffix));
2116 s += strlen(s);
2117 break;
2118 case DCH_MS: /* millisecond */
2119 #ifdef HAVE_INT64_TIMESTAMP
2120 sprintf(s, "%03d", (int) (in->fsec / INT64CONST(1000)));
2121 #else
2122 /* No rint() because we can't overflow and we might print US */
2123 sprintf(s, "%03d", (int) (in->fsec * 1000));
2124 #endif
2125 if (S_THth(n->suffix))
2126 str_numth(s, s, S_TH_TYPE(n->suffix));
2127 s += strlen(s);
2128 break;
2129 case DCH_US: /* microsecond */
2130 #ifdef HAVE_INT64_TIMESTAMP
2131 sprintf(s, "%06d", (int) in->fsec);
2132 #else
2133 /* don't use rint() because we can't overflow 1000 */
2134 sprintf(s, "%06d", (int) (in->fsec * 1000000));
2135 #endif
2136 if (S_THth(n->suffix))
2137 str_numth(s, s, S_TH_TYPE(n->suffix));
2138 s += strlen(s);
2139 break;
2140 case DCH_SSSS:
2141 sprintf(s, "%d", tm->tm_hour * SECS_PER_HOUR +
2142 tm->tm_min * SECS_PER_MINUTE +
2143 tm->tm_sec);
2144 if (S_THth(n->suffix))
2145 str_numth(s, s, S_TH_TYPE(n->suffix));
2146 s += strlen(s);
2147 break;
2148 case DCH_tz:
2149 INVALID_FOR_INTERVAL;
2150 if (tmtcTzn(in))
2152 char *p = str_tolower_z(tmtcTzn(in));
2154 strcpy(s, p);
2155 pfree(p);
2156 s += strlen(s);
2158 break;
2159 case DCH_TZ:
2160 INVALID_FOR_INTERVAL;
2161 if (tmtcTzn(in))
2163 strcpy(s, tmtcTzn(in));
2164 s += strlen(s);
2166 break;
2167 case DCH_A_D:
2168 case DCH_B_C:
2169 INVALID_FOR_INTERVAL;
2170 strcpy(s, (tm->tm_year <= 0 ? B_C_STR : A_D_STR));
2171 s += strlen(s);
2172 break;
2173 case DCH_AD:
2174 case DCH_BC:
2175 INVALID_FOR_INTERVAL;
2176 strcpy(s, (tm->tm_year <= 0 ? BC_STR : AD_STR));
2177 s += strlen(s);
2178 break;
2179 case DCH_a_d:
2180 case DCH_b_c:
2181 INVALID_FOR_INTERVAL;
2182 strcpy(s, (tm->tm_year <= 0 ? b_c_STR : a_d_STR));
2183 s += strlen(s);
2184 break;
2185 case DCH_ad:
2186 case DCH_bc:
2187 INVALID_FOR_INTERVAL;
2188 strcpy(s, (tm->tm_year <= 0 ? bc_STR : ad_STR));
2189 s += strlen(s);
2190 break;
2191 case DCH_MONTH:
2192 INVALID_FOR_INTERVAL;
2193 if (!tm->tm_mon)
2194 break;
2195 if (S_TM(n->suffix))
2196 strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1]));
2197 else
2198 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
2199 str_toupper_z(months_full[tm->tm_mon - 1]));
2200 s += strlen(s);
2201 break;
2202 case DCH_Month:
2203 INVALID_FOR_INTERVAL;
2204 if (!tm->tm_mon)
2205 break;
2206 if (S_TM(n->suffix))
2207 strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1]));
2208 else
2209 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
2210 s += strlen(s);
2211 break;
2212 case DCH_month:
2213 INVALID_FOR_INTERVAL;
2214 if (!tm->tm_mon)
2215 break;
2216 if (S_TM(n->suffix))
2217 strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1]));
2218 else
2220 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
2221 *s = pg_tolower((unsigned char) *s);
2223 s += strlen(s);
2224 break;
2225 case DCH_MON:
2226 INVALID_FOR_INTERVAL;
2227 if (!tm->tm_mon)
2228 break;
2229 if (S_TM(n->suffix))
2230 strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1]));
2231 else
2232 strcpy(s, str_toupper_z(months[tm->tm_mon - 1]));
2233 s += strlen(s);
2234 break;
2235 case DCH_Mon:
2236 INVALID_FOR_INTERVAL;
2237 if (!tm->tm_mon)
2238 break;
2239 if (S_TM(n->suffix))
2240 strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1]));
2241 else
2242 strcpy(s, months[tm->tm_mon - 1]);
2243 s += strlen(s);
2244 break;
2245 case DCH_mon:
2246 INVALID_FOR_INTERVAL;
2247 if (!tm->tm_mon)
2248 break;
2249 if (S_TM(n->suffix))
2250 strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1]));
2251 else
2253 strcpy(s, months[tm->tm_mon - 1]);
2254 *s = pg_tolower((unsigned char) *s);
2256 s += strlen(s);
2257 break;
2258 case DCH_MM:
2259 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_mon);
2260 if (S_THth(n->suffix))
2261 str_numth(s, s, S_TH_TYPE(n->suffix));
2262 s += strlen(s);
2263 break;
2264 case DCH_DAY:
2265 INVALID_FOR_INTERVAL;
2266 if (S_TM(n->suffix))
2267 strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday]));
2268 else
2269 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
2270 str_toupper_z(days[tm->tm_wday]));
2271 s += strlen(s);
2272 break;
2273 case DCH_Day:
2274 INVALID_FOR_INTERVAL;
2275 if (S_TM(n->suffix))
2276 strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday]));
2277 else
2278 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
2279 s += strlen(s);
2280 break;
2281 case DCH_day:
2282 INVALID_FOR_INTERVAL;
2283 if (S_TM(n->suffix))
2284 strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday]));
2285 else
2287 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
2288 *s = pg_tolower((unsigned char) *s);
2290 s += strlen(s);
2291 break;
2292 case DCH_DY:
2293 INVALID_FOR_INTERVAL;
2294 if (S_TM(n->suffix))
2295 strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday]));
2296 else
2297 strcpy(s, str_toupper_z(days_short[tm->tm_wday]));
2298 s += strlen(s);
2299 break;
2300 case DCH_Dy:
2301 INVALID_FOR_INTERVAL;
2302 if (S_TM(n->suffix))
2303 strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday]));
2304 else
2305 strcpy(s, days_short[tm->tm_wday]);
2306 s += strlen(s);
2307 break;
2308 case DCH_dy:
2309 INVALID_FOR_INTERVAL;
2310 if (S_TM(n->suffix))
2311 strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday]));
2312 else
2314 strcpy(s, days_short[tm->tm_wday]);
2315 *s = pg_tolower((unsigned char) *s);
2317 s += strlen(s);
2318 break;
2319 case DCH_DDD:
2320 case DCH_IDDD:
2321 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 3,
2322 (n->key->id == DCH_DDD) ?
2323 tm->tm_yday :
2324 date2isoyearday(tm->tm_year, tm->tm_mon, tm->tm_mday));
2325 if (S_THth(n->suffix))
2326 str_numth(s, s, S_TH_TYPE(n->suffix));
2327 s += strlen(s);
2328 break;
2329 case DCH_DD:
2330 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_mday);
2331 if (S_THth(n->suffix))
2332 str_numth(s, s, S_TH_TYPE(n->suffix));
2333 s += strlen(s);
2334 break;
2335 case DCH_D:
2336 INVALID_FOR_INTERVAL;
2337 sprintf(s, "%d", tm->tm_wday + 1);
2338 if (S_THth(n->suffix))
2339 str_numth(s, s, S_TH_TYPE(n->suffix));
2340 s += strlen(s);
2341 break;
2342 case DCH_ID:
2343 INVALID_FOR_INTERVAL;
2344 sprintf(s, "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
2345 if (S_THth(n->suffix))
2346 str_numth(s, s, S_TH_TYPE(n->suffix));
2347 s += strlen(s);
2348 break;
2349 case DCH_WW:
2350 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
2351 (tm->tm_yday - 1) / 7 + 1);
2352 if (S_THth(n->suffix))
2353 str_numth(s, s, S_TH_TYPE(n->suffix));
2354 s += strlen(s);
2355 break;
2356 case DCH_IW:
2357 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
2358 date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
2359 if (S_THth(n->suffix))
2360 str_numth(s, s, S_TH_TYPE(n->suffix));
2361 s += strlen(s);
2362 break;
2363 case DCH_Q:
2364 if (!tm->tm_mon)
2365 break;
2366 sprintf(s, "%d", (tm->tm_mon - 1) / 3 + 1);
2367 if (S_THth(n->suffix))
2368 str_numth(s, s, S_TH_TYPE(n->suffix));
2369 s += strlen(s);
2370 break;
2371 case DCH_CC:
2372 if (is_interval) /* straight calculation */
2373 i = tm->tm_year / 100;
2374 else /* century 21 starts in 2001 */
2375 i = (tm->tm_year - 1) / 100 + 1;
2376 if (i <= 99 && i >= -99)
2377 sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, i);
2378 else
2379 sprintf(s, "%d", i);
2380 if (S_THth(n->suffix))
2381 str_numth(s, s, S_TH_TYPE(n->suffix));
2382 s += strlen(s);
2383 break;
2384 case DCH_Y_YYY:
2385 i = ADJUST_YEAR(tm->tm_year, is_interval) / 1000;
2386 sprintf(s, "%d,%03d", i,
2387 ADJUST_YEAR(tm->tm_year, is_interval) - (i * 1000));
2388 if (S_THth(n->suffix))
2389 str_numth(s, s, S_TH_TYPE(n->suffix));
2390 s += strlen(s);
2391 break;
2392 case DCH_YYYY:
2393 case DCH_IYYY:
2394 if (tm->tm_year <= 9999 && tm->tm_year >= -9998)
2395 sprintf(s, "%0*d",
2396 S_FM(n->suffix) ? 0 : 4,
2397 n->key->id == DCH_YYYY ?
2398 ADJUST_YEAR(tm->tm_year, is_interval) :
2399 ADJUST_YEAR(date2isoyear(
2400 tm->tm_year,
2401 tm->tm_mon,
2402 tm->tm_mday), is_interval));
2403 else
2404 sprintf(s, "%d",
2405 n->key->id == DCH_YYYY ?
2406 ADJUST_YEAR(tm->tm_year, is_interval) :
2407 ADJUST_YEAR(date2isoyear(
2408 tm->tm_year,
2409 tm->tm_mon,
2410 tm->tm_mday), is_interval));
2411 if (S_THth(n->suffix))
2412 str_numth(s, s, S_TH_TYPE(n->suffix));
2413 s += strlen(s);
2414 break;
2415 case DCH_YYY:
2416 case DCH_IYY:
2417 snprintf(buff, sizeof(buff), "%03d",
2418 n->key->id == DCH_YYY ?
2419 ADJUST_YEAR(tm->tm_year, is_interval) :
2420 ADJUST_YEAR(date2isoyear(tm->tm_year,
2421 tm->tm_mon, tm->tm_mday),
2422 is_interval));
2423 i = strlen(buff);
2424 strcpy(s, buff + (i - 3));
2425 if (S_THth(n->suffix))
2426 str_numth(s, s, S_TH_TYPE(n->suffix));
2427 s += strlen(s);
2428 break;
2429 case DCH_YY:
2430 case DCH_IY:
2431 snprintf(buff, sizeof(buff), "%02d",
2432 n->key->id == DCH_YY ?
2433 ADJUST_YEAR(tm->tm_year, is_interval) :
2434 ADJUST_YEAR(date2isoyear(tm->tm_year,
2435 tm->tm_mon, tm->tm_mday),
2436 is_interval));
2437 i = strlen(buff);
2438 strcpy(s, buff + (i - 2));
2439 if (S_THth(n->suffix))
2440 str_numth(s, s, S_TH_TYPE(n->suffix));
2441 s += strlen(s);
2442 break;
2443 case DCH_Y:
2444 case DCH_I:
2445 snprintf(buff, sizeof(buff), "%1d",
2446 n->key->id == DCH_Y ?
2447 ADJUST_YEAR(tm->tm_year, is_interval) :
2448 ADJUST_YEAR(date2isoyear(tm->tm_year,
2449 tm->tm_mon, tm->tm_mday),
2450 is_interval));
2451 i = strlen(buff);
2452 strcpy(s, buff + (i - 1));
2453 if (S_THth(n->suffix))
2454 str_numth(s, s, S_TH_TYPE(n->suffix));
2455 s += strlen(s);
2456 break;
2457 case DCH_RM:
2458 if (!tm->tm_mon)
2459 break;
2460 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4,
2461 rm_months_upper[12 - tm->tm_mon]);
2462 s += strlen(s);
2463 break;
2464 case DCH_rm:
2465 if (!tm->tm_mon)
2466 break;
2467 sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4,
2468 rm_months_lower[12 - tm->tm_mon]);
2469 s += strlen(s);
2470 break;
2471 case DCH_W:
2472 sprintf(s, "%d", (tm->tm_mday - 1) / 7 + 1);
2473 if (S_THth(n->suffix))
2474 str_numth(s, s, S_TH_TYPE(n->suffix));
2475 s += strlen(s);
2476 break;
2477 case DCH_J:
2478 sprintf(s, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
2479 if (S_THth(n->suffix))
2480 str_numth(s, s, S_TH_TYPE(n->suffix));
2481 s += strlen(s);
2482 break;
2486 *s = '\0';
2489 /* ----------
2490 * Process a string as denoted by a list of FormatNodes.
2491 * The TmFromChar struct pointed to by 'out' is populated with the results.
2493 * Note: we currently don't have any to_interval() function, so there
2494 * is no need here for INVALID_FOR_INTERVAL checks.
2495 * ----------
2497 static void
2498 DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
2500 FormatNode *n;
2501 char *s;
2502 int len,
2503 value;
2504 bool fx_mode = false;
2506 for (n = node, s = in; n->type != NODE_TYPE_END && *s != '\0'; n++)
2508 if (n->type != NODE_TYPE_ACTION)
2510 s++;
2511 /* Ignore spaces when not in FX (fixed width) mode */
2512 if (isspace((unsigned char) n->character) && !fx_mode)
2514 while (*s != '\0' && isspace((unsigned char) *s))
2515 s++;
2517 continue;
2520 from_char_set_mode(out, n->key->date_mode);
2522 switch (n->key->id)
2524 case DCH_FX:
2525 fx_mode = true;
2526 break;
2527 case DCH_A_M:
2528 case DCH_P_M:
2529 case DCH_a_m:
2530 case DCH_p_m:
2531 from_char_seq_search(&value, &s, ampm_strings_long,
2532 ALL_UPPER, n->key->len, n);
2533 from_char_set_int(&out->pm, value % 2, n);
2534 out->clock = CLOCK_12_HOUR;
2535 break;
2536 case DCH_AM:
2537 case DCH_PM:
2538 case DCH_am:
2539 case DCH_pm:
2540 from_char_seq_search(&value, &s, ampm_strings,
2541 ALL_UPPER, n->key->len, n);
2542 from_char_set_int(&out->pm, value % 2, n);
2543 out->clock = CLOCK_12_HOUR;
2544 break;
2545 case DCH_HH:
2546 case DCH_HH12:
2547 from_char_parse_int_len(&out->hh, &s, 2, n);
2548 out->clock = CLOCK_12_HOUR;
2549 s += SKIP_THth(n->suffix);
2550 break;
2551 case DCH_HH24:
2552 from_char_parse_int_len(&out->hh, &s, 2, n);
2553 s += SKIP_THth(n->suffix);
2554 break;
2555 case DCH_MI:
2556 from_char_parse_int(&out->mi, &s, n);
2557 s += SKIP_THth(n->suffix);
2558 break;
2559 case DCH_SS:
2560 from_char_parse_int(&out->ss, &s, n);
2561 s += SKIP_THth(n->suffix);
2562 break;
2563 case DCH_MS: /* millisecond */
2564 len = from_char_parse_int_len(&out->ms, &s, 3, n);
2567 * 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
2569 out->ms *= len == 1 ? 100 :
2570 len == 2 ? 10 : 1;
2572 s += SKIP_THth(n->suffix);
2573 break;
2574 case DCH_US: /* microsecond */
2575 len = from_char_parse_int_len(&out->us, &s, 6, n);
2577 out->us *= len == 1 ? 100000 :
2578 len == 2 ? 10000 :
2579 len == 3 ? 1000 :
2580 len == 4 ? 100 :
2581 len == 5 ? 10 : 1;
2583 s += SKIP_THth(n->suffix);
2584 break;
2585 case DCH_SSSS:
2586 from_char_parse_int(&out->ssss, &s, n);
2587 s += SKIP_THth(n->suffix);
2588 break;
2589 case DCH_tz:
2590 case DCH_TZ:
2591 ereport(ERROR,
2592 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2593 errmsg("\"TZ\"/\"tz\" format patterns are not supported in to_date")));
2594 case DCH_A_D:
2595 case DCH_B_C:
2596 case DCH_a_d:
2597 case DCH_b_c:
2598 from_char_seq_search(&value, &s, adbc_strings_long,
2599 ALL_UPPER, n->key->len, n);
2600 from_char_set_int(&out->bc, value % 2, n);
2601 break;
2602 case DCH_AD:
2603 case DCH_BC:
2604 case DCH_ad:
2605 case DCH_bc:
2606 from_char_seq_search(&value, &s, adbc_strings,
2607 ALL_UPPER, n->key->len, n);
2608 from_char_set_int(&out->bc, value % 2, n);
2609 break;
2610 case DCH_MONTH:
2611 case DCH_Month:
2612 case DCH_month:
2613 from_char_seq_search(&value, &s, months_full, ONE_UPPER,
2614 MAX_MONTH_LEN, n);
2615 from_char_set_int(&out->mm, value + 1, n);
2616 break;
2617 case DCH_MON:
2618 case DCH_Mon:
2619 case DCH_mon:
2620 from_char_seq_search(&value, &s, months, ONE_UPPER,
2621 MAX_MON_LEN, n);
2622 from_char_set_int(&out->mm, value + 1, n);
2623 break;
2624 case DCH_MM:
2625 from_char_parse_int(&out->mm, &s, n);
2626 s += SKIP_THth(n->suffix);
2627 break;
2628 case DCH_DAY:
2629 case DCH_Day:
2630 case DCH_day:
2631 from_char_seq_search(&value, &s, days, ONE_UPPER,
2632 MAX_DAY_LEN, n);
2633 from_char_set_int(&out->d, value, n);
2634 break;
2635 case DCH_DY:
2636 case DCH_Dy:
2637 case DCH_dy:
2638 from_char_seq_search(&value, &s, days, ONE_UPPER,
2639 MAX_DY_LEN, n);
2640 from_char_set_int(&out->d, value, n);
2641 break;
2642 case DCH_DDD:
2643 from_char_parse_int(&out->ddd, &s, n);
2644 s += SKIP_THth(n->suffix);
2645 break;
2646 case DCH_IDDD:
2647 from_char_parse_int_len(&out->ddd, &s, 3, n);
2648 s += SKIP_THth(n->suffix);
2649 break;
2650 case DCH_DD:
2651 from_char_parse_int(&out->dd, &s, n);
2652 s += SKIP_THth(n->suffix);
2653 break;
2654 case DCH_D:
2655 from_char_parse_int(&out->d, &s, n);
2656 out->d--;
2657 s += SKIP_THth(n->suffix);
2658 break;
2659 case DCH_ID:
2660 from_char_parse_int_len(&out->d, &s, 1, n);
2661 s += SKIP_THth(n->suffix);
2662 break;
2663 case DCH_WW:
2664 case DCH_IW:
2665 from_char_parse_int(&out->ww, &s, n);
2666 s += SKIP_THth(n->suffix);
2667 break;
2668 case DCH_Q:
2671 * We ignore Q when converting to date because it is not
2672 * normative.
2674 * We still parse the source string for an integer, but it
2675 * isn't stored anywhere in 'out'.
2677 from_char_parse_int((int *) NULL, &s, n);
2678 s += SKIP_THth(n->suffix);
2679 break;
2680 case DCH_CC:
2681 from_char_parse_int(&out->cc, &s, n);
2682 s += SKIP_THth(n->suffix);
2683 break;
2684 case DCH_Y_YYY:
2686 int matched,
2687 years,
2688 millenia;
2690 matched = sscanf(s, "%d,%03d", &millenia, &years);
2691 if (matched != 2)
2692 ereport(ERROR,
2693 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2694 errmsg("invalid input string for \"Y,YYY\"")));
2695 years += (millenia * 1000);
2696 from_char_set_int(&out->year, years, n);
2697 out->yysz = 4;
2698 s += strdigits_len(s) + 4 + SKIP_THth(n->suffix);
2700 break;
2701 case DCH_YYYY:
2702 case DCH_IYYY:
2703 from_char_parse_int(&out->year, &s, n);
2704 out->yysz = 4;
2705 s += SKIP_THth(n->suffix);
2706 break;
2707 case DCH_YYY:
2708 case DCH_IYY:
2709 from_char_parse_int(&out->year, &s, n);
2710 out->yysz = 3;
2713 * 3-digit year: '100' ... '999' = 1100 ... 1999 '000' ...
2714 * '099' = 2000 ... 2099
2716 if (out->year >= 100)
2717 out->year += 1000;
2718 else
2719 out->year += 2000;
2720 s += SKIP_THth(n->suffix);
2721 break;
2722 case DCH_YY:
2723 case DCH_IY:
2724 from_char_parse_int(&out->year, &s, n);
2725 out->yysz = 2;
2728 * 2-digit year: '00' ... '69' = 2000 ... 2069 '70' ... '99'
2729 * = 1970 ... 1999
2731 if (out->year < 70)
2732 out->year += 2000;
2733 else
2734 out->year += 1900;
2735 s += SKIP_THth(n->suffix);
2736 break;
2737 case DCH_Y:
2738 case DCH_I:
2739 from_char_parse_int(&out->year, &s, n);
2740 out->yysz = 1;
2743 * 1-digit year: always +2000
2745 out->year += 2000;
2746 s += SKIP_THth(n->suffix);
2747 break;
2748 case DCH_RM:
2749 from_char_seq_search(&value, &s, rm_months_upper,
2750 ALL_UPPER, MAX_RM_LEN, n);
2751 from_char_set_int(&out->mm, 12 - value, n);
2752 break;
2753 case DCH_rm:
2754 from_char_seq_search(&value, &s, rm_months_lower,
2755 ALL_LOWER, MAX_RM_LEN, n);
2756 from_char_set_int(&out->mm, 12 - value, n);
2757 break;
2758 case DCH_W:
2759 from_char_parse_int(&out->w, &s, n);
2760 s += SKIP_THth(n->suffix);
2761 break;
2762 case DCH_J:
2763 from_char_parse_int(&out->j, &s, n);
2764 s += SKIP_THth(n->suffix);
2765 break;
2770 static DCHCacheEntry *
2771 DCH_cache_getnew(char *str)
2773 DCHCacheEntry *ent;
2775 /* counter overflow check - paranoia? */
2776 if (DCHCounter >= (INT_MAX - DCH_CACHE_FIELDS - 1))
2778 DCHCounter = 0;
2780 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2781 ent->age = (++DCHCounter);
2785 * If cache is full, remove oldest entry
2787 if (n_DCHCache > DCH_CACHE_FIELDS)
2789 DCHCacheEntry *old = DCHCache + 0;
2791 #ifdef DEBUG_TO_FROM_CHAR
2792 elog(DEBUG_elog_output, "cache is full (%d)", n_DCHCache);
2793 #endif
2794 for (ent = DCHCache + 1; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2796 if (ent->age < old->age)
2797 old = ent;
2799 #ifdef DEBUG_TO_FROM_CHAR
2800 elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age);
2801 #endif
2802 StrNCpy(old->str, str, DCH_CACHE_SIZE + 1);
2803 /* old->format fill parser */
2804 old->age = (++DCHCounter);
2805 return old;
2807 else
2809 #ifdef DEBUG_TO_FROM_CHAR
2810 elog(DEBUG_elog_output, "NEW (%d)", n_DCHCache);
2811 #endif
2812 ent = DCHCache + n_DCHCache;
2813 StrNCpy(ent->str, str, DCH_CACHE_SIZE + 1);
2814 /* ent->format fill parser */
2815 ent->age = (++DCHCounter);
2816 ++n_DCHCache;
2817 return ent;
2821 static DCHCacheEntry *
2822 DCH_cache_search(char *str)
2824 int i;
2825 DCHCacheEntry *ent;
2827 /* counter overflow check - paranoia? */
2828 if (DCHCounter >= (INT_MAX - DCH_CACHE_FIELDS - 1))
2830 DCHCounter = 0;
2832 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2833 ent->age = (++DCHCounter);
2836 for (i = 0, ent = DCHCache; i < n_DCHCache; i++, ent++)
2838 if (strcmp(ent->str, str) == 0)
2840 ent->age = (++DCHCounter);
2841 return ent;
2845 return NULL;
2849 * Format a date/time or interval into a string according to fmt.
2850 * We parse fmt into a list of FormatNodes. This is then passed to DCH_to_char
2851 * for formatting.
2853 static text *
2854 datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval)
2856 FormatNode *format;
2857 char *fmt_str,
2858 *result;
2859 bool incache;
2860 int fmt_len;
2861 text *res;
2864 * Convert fmt to C string
2866 fmt_str = text_to_cstring(fmt);
2867 fmt_len = strlen(fmt_str);
2870 * Allocate workspace for result as C string
2872 result = palloc((fmt_len * DCH_MAX_ITEM_SIZ) + 1);
2873 *result = '\0';
2876 * Allocate new memory if format picture is bigger than static cache and
2877 * not use cache (call parser always)
2879 if (fmt_len > DCH_CACHE_SIZE)
2881 format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
2882 incache = FALSE;
2884 parse_format(format, fmt_str, DCH_keywords,
2885 DCH_suff, DCH_index, DCH_TYPE, NULL);
2887 (format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
2889 else
2892 * Use cache buffers
2894 DCHCacheEntry *ent;
2896 incache = TRUE;
2898 if ((ent = DCH_cache_search(fmt_str)) == NULL)
2900 ent = DCH_cache_getnew(fmt_str);
2903 * Not in the cache, must run parser and save a new format-picture
2904 * to the cache.
2906 parse_format(ent->format, fmt_str, DCH_keywords,
2907 DCH_suff, DCH_index, DCH_TYPE, NULL);
2909 (ent->format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
2911 #ifdef DEBUG_TO_FROM_CHAR
2912 /* dump_node(ent->format, fmt_len); */
2913 /* dump_index(DCH_keywords, DCH_index); */
2914 #endif
2916 format = ent->format;
2919 /* The real work is here */
2920 DCH_to_char(format, is_interval, tmtc, result);
2922 if (!incache)
2923 pfree(format);
2925 pfree(fmt_str);
2927 /* convert C-string result to TEXT format */
2928 res = cstring_to_text(result);
2930 pfree(result);
2931 return res;
2934 /****************************************************************************
2935 * Public routines
2936 ***************************************************************************/
2938 /* -------------------
2939 * TIMESTAMP to_char()
2940 * -------------------
2942 Datum
2943 timestamp_to_char(PG_FUNCTION_ARGS)
2945 Timestamp dt = PG_GETARG_TIMESTAMP(0);
2946 text *fmt = PG_GETARG_TEXT_P(1),
2947 *res;
2948 TmToChar tmtc;
2949 struct pg_tm *tm;
2950 int thisdate;
2952 if ((VARSIZE(fmt) - VARHDRSZ) <= 0 || TIMESTAMP_NOT_FINITE(dt))
2953 PG_RETURN_NULL();
2955 ZERO_tmtc(&tmtc);
2956 tm = tmtcTm(&tmtc);
2958 if (timestamp2tm(dt, NULL, tm, &tmtcFsec(&tmtc), NULL, NULL) != 0)
2959 ereport(ERROR,
2960 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2961 errmsg("timestamp out of range")));
2963 thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
2964 tm->tm_wday = (thisdate + 1) % 7;
2965 tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
2967 if (!(res = datetime_to_char_body(&tmtc, fmt, false)))
2968 PG_RETURN_NULL();
2970 PG_RETURN_TEXT_P(res);
2973 Datum
2974 timestamptz_to_char(PG_FUNCTION_ARGS)
2976 TimestampTz dt = PG_GETARG_TIMESTAMP(0);
2977 text *fmt = PG_GETARG_TEXT_P(1),
2978 *res;
2979 TmToChar tmtc;
2980 int tz;
2981 struct pg_tm *tm;
2982 int thisdate;
2984 if ((VARSIZE(fmt) - VARHDRSZ) <= 0 || TIMESTAMP_NOT_FINITE(dt))
2985 PG_RETURN_NULL();
2987 ZERO_tmtc(&tmtc);
2988 tm = tmtcTm(&tmtc);
2990 if (timestamp2tm(dt, &tz, tm, &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0)
2991 ereport(ERROR,
2992 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2993 errmsg("timestamp out of range")));
2995 thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
2996 tm->tm_wday = (thisdate + 1) % 7;
2997 tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
2999 if (!(res = datetime_to_char_body(&tmtc, fmt, false)))
3000 PG_RETURN_NULL();
3002 PG_RETURN_TEXT_P(res);
3006 /* -------------------
3007 * INTERVAL to_char()
3008 * -------------------
3010 Datum
3011 interval_to_char(PG_FUNCTION_ARGS)
3013 Interval *it = PG_GETARG_INTERVAL_P(0);
3014 text *fmt = PG_GETARG_TEXT_P(1),
3015 *res;
3016 TmToChar tmtc;
3017 struct pg_tm *tm;
3019 if ((VARSIZE(fmt) - VARHDRSZ) <= 0)
3020 PG_RETURN_NULL();
3022 ZERO_tmtc(&tmtc);
3023 tm = tmtcTm(&tmtc);
3025 if (interval2tm(*it, tm, &tmtcFsec(&tmtc)) != 0)
3026 PG_RETURN_NULL();
3028 /* wday is meaningless, yday approximates the total span in days */
3029 tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday;
3031 if (!(res = datetime_to_char_body(&tmtc, fmt, true)))
3032 PG_RETURN_NULL();
3034 PG_RETURN_TEXT_P(res);
3037 /* ---------------------
3038 * TO_TIMESTAMP()
3040 * Make Timestamp from date_str which is formatted at argument 'fmt'
3041 * ( to_timestamp is reverse to_char() )
3042 * ---------------------
3044 Datum
3045 to_timestamp(PG_FUNCTION_ARGS)
3047 text *date_txt = PG_GETARG_TEXT_P(0);
3048 text *fmt = PG_GETARG_TEXT_P(1);
3049 Timestamp result;
3050 int tz;
3051 struct pg_tm tm;
3052 fsec_t fsec;
3054 do_to_timestamp(date_txt, fmt, &tm, &fsec);
3056 tz = DetermineTimeZoneOffset(&tm, session_timezone);
3058 if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
3059 ereport(ERROR,
3060 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3061 errmsg("timestamp out of range")));
3063 PG_RETURN_TIMESTAMP(result);
3066 /* ----------
3067 * TO_DATE
3068 * Make Date from date_str which is formated at argument 'fmt'
3069 * ----------
3071 Datum
3072 to_date(PG_FUNCTION_ARGS)
3074 text *date_txt = PG_GETARG_TEXT_P(0);
3075 text *fmt = PG_GETARG_TEXT_P(1);
3076 DateADT result;
3077 struct pg_tm tm;
3078 fsec_t fsec;
3080 do_to_timestamp(date_txt, fmt, &tm, &fsec);
3082 result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
3084 PG_RETURN_DATEADT(result);
3088 * do_to_timestamp: shared code for to_timestamp and to_date
3090 * Parse the 'date_txt' according to 'fmt', return results as a struct pg_tm
3091 * and fractional seconds.
3093 * We parse 'fmt' into a list of FormatNodes, which is then passed to
3094 * DCH_from_char to populate a TmFromChar with the parsed contents of
3095 * 'date_txt'.
3097 * The TmFromChar is then analysed and converted into the final results in
3098 * struct 'tm' and 'fsec'.
3100 static void
3101 do_to_timestamp(text *date_txt, text *fmt,
3102 struct pg_tm * tm, fsec_t *fsec)
3104 FormatNode *format;
3105 TmFromChar tmfc;
3106 int fmt_len;
3108 ZERO_tmfc(&tmfc);
3109 ZERO_tm(tm);
3110 *fsec = 0;
3112 fmt_len = VARSIZE_ANY_EXHDR(fmt);
3114 if (fmt_len)
3116 char *fmt_str;
3117 char *date_str;
3118 bool incache;
3120 fmt_str = text_to_cstring(fmt);
3123 * Allocate new memory if format picture is bigger than static cache
3124 * and not use cache (call parser always)
3126 if (fmt_len > DCH_CACHE_SIZE)
3128 format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
3129 incache = FALSE;
3131 parse_format(format, fmt_str, DCH_keywords,
3132 DCH_suff, DCH_index, DCH_TYPE, NULL);
3134 (format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
3136 else
3139 * Use cache buffers
3141 DCHCacheEntry *ent;
3143 incache = TRUE;
3145 if ((ent = DCH_cache_search(fmt_str)) == NULL)
3147 ent = DCH_cache_getnew(fmt_str);
3150 * Not in the cache, must run parser and save a new
3151 * format-picture to the cache.
3153 parse_format(ent->format, fmt_str, DCH_keywords,
3154 DCH_suff, DCH_index, DCH_TYPE, NULL);
3156 (ent->format + fmt_len)->type = NODE_TYPE_END; /* Paranoia? */
3157 #ifdef DEBUG_TO_FROM_CHAR
3158 /* dump_node(ent->format, fmt_len); */
3159 /* dump_index(DCH_keywords, DCH_index); */
3160 #endif
3162 format = ent->format;
3165 #ifdef DEBUG_TO_FROM_CHAR
3166 /* dump_node(format, fmt_len); */
3167 #endif
3169 date_str = text_to_cstring(date_txt);
3171 DCH_from_char(format, date_str, &tmfc);
3173 pfree(date_str);
3174 pfree(fmt_str);
3175 if (!incache)
3176 pfree(format);
3179 DEBUG_TMFC(&tmfc);
3182 * Convert values that user define for FROM_CHAR (to_date/to_timestamp) to
3183 * standard 'tm'
3185 if (tmfc.ssss)
3187 int x = tmfc.ssss;
3189 tm->tm_hour = x / SECS_PER_HOUR;
3190 x %= SECS_PER_HOUR;
3191 tm->tm_min = x / SECS_PER_MINUTE;
3192 x %= SECS_PER_MINUTE;
3193 tm->tm_sec = x;
3196 if (tmfc.ss)
3197 tm->tm_sec = tmfc.ss;
3198 if (tmfc.mi)
3199 tm->tm_min = tmfc.mi;
3200 if (tmfc.hh)
3201 tm->tm_hour = tmfc.hh;
3203 if (tmfc.clock == CLOCK_12_HOUR)
3205 if (tm->tm_hour < 1 || tm->tm_hour > 12)
3206 ereport(ERROR,
3207 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3208 errmsg("hour \"%d\" is invalid for the 12-hour clock",
3209 tm->tm_hour),
3210 errhint("Use the 24-hour clock, or give an hour between 1 and 12.")));
3212 if (tmfc.pm && tm->tm_hour < 12)
3213 tm->tm_hour += 12;
3214 else if (!tmfc.pm && tm->tm_hour == 12)
3215 tm->tm_hour = 0;
3218 if (tmfc.year)
3221 * If CC and YY (or Y) are provided, use YY as 2 low-order digits for
3222 * the year in the given century. Keep in mind that the 21st century
3223 * runs from 2001-2100, not 2000-2099.
3225 * If a 4-digit year is provided, we use that and ignore CC.
3227 if (tmfc.cc && tmfc.yysz <= 2)
3229 tm->tm_year = tmfc.year % 100;
3230 if (tm->tm_year)
3231 tm->tm_year += (tmfc.cc - 1) * 100;
3232 else
3233 tm->tm_year = tmfc.cc * 100;
3235 else
3236 tm->tm_year = tmfc.year;
3238 else if (tmfc.cc) /* use first year of century */
3239 tm->tm_year = (tmfc.cc - 1) * 100 + 1;
3241 if (tmfc.bc)
3243 if (tm->tm_year > 0)
3244 tm->tm_year = -(tm->tm_year - 1);
3245 else
3246 ereport(ERROR,
3247 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3248 errmsg("inconsistent use of year %04d and \"BC\"",
3249 tm->tm_year)));
3252 if (tmfc.j)
3253 j2date(tmfc.j, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3255 if (tmfc.ww)
3257 if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
3260 * If tmfc.d is not set, then the date is left at the beginning of
3261 * the ISO week (Monday).
3263 if (tmfc.d)
3264 isoweekdate2date(tmfc.ww, tmfc.d, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3265 else
3266 isoweek2date(tmfc.ww, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3268 else
3269 tmfc.ddd = (tmfc.ww - 1) * 7 + 1;
3272 if (tmfc.w)
3273 tmfc.dd = (tmfc.w - 1) * 7 + 1;
3274 if (tmfc.d)
3275 tm->tm_wday = tmfc.d;
3276 if (tmfc.dd)
3277 tm->tm_mday = tmfc.dd;
3278 if (tmfc.ddd)
3279 tm->tm_yday = tmfc.ddd;
3280 if (tmfc.mm)
3281 tm->tm_mon = tmfc.mm;
3283 if (tmfc.ddd && (tm->tm_mon <= 1 || tm->tm_mday <= 1))
3286 * The month and day field have not been set, so we use the
3287 * day-of-year field to populate them. Depending on the date mode,
3288 * this field may be interpreted as a Gregorian day-of-year, or an ISO
3289 * week date day-of-year.
3292 if (!tm->tm_year && !tmfc.bc)
3293 ereport(ERROR,
3294 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3295 errmsg("cannot calculate day of year without year information")));
3297 if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
3299 int j0; /* zeroth day of the ISO year, in Julian */
3301 j0 = isoweek2j(tm->tm_year, 1) - 1;
3303 j2date(j0 + tmfc.ddd, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3305 else
3307 const int *y;
3308 int i;
3310 static const int ysum[2][13] = {
3311 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
3312 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
3314 y = ysum[isleap(tm->tm_year)];
3316 for (i = 1; i <= 12; i++)
3318 if (tmfc.ddd < y[i])
3319 break;
3321 if (tm->tm_mon <= 1)
3322 tm->tm_mon = i;
3324 if (tm->tm_mday <= 1)
3325 tm->tm_mday = tmfc.ddd - y[i - 1];
3329 #ifdef HAVE_INT64_TIMESTAMP
3330 if (tmfc.ms)
3331 *fsec += tmfc.ms * 1000;
3332 if (tmfc.us)
3333 *fsec += tmfc.us;
3334 #else
3335 if (tmfc.ms)
3336 *fsec += (double) tmfc.ms / 1000;
3337 if (tmfc.us)
3338 *fsec += (double) tmfc.us / 1000000;
3339 #endif
3341 DEBUG_TM(tm);
3345 /**********************************************************************
3346 * the NUMBER version part
3347 *********************************************************************/
3350 static char *
3351 fill_str(char *str, int c, int max)
3353 memset(str, c, max);
3354 *(str + max) = '\0';
3355 return str;
3358 #define zeroize_NUM(_n) \
3359 do { \
3360 (_n)->flag = 0; \
3361 (_n)->lsign = 0; \
3362 (_n)->pre = 0; \
3363 (_n)->post = 0; \
3364 (_n)->pre_lsign_num = 0; \
3365 (_n)->need_locale = 0; \
3366 (_n)->multi = 0; \
3367 (_n)->zero_start = 0; \
3368 (_n)->zero_end = 0; \
3369 } while(0)
3371 static NUMCacheEntry *
3372 NUM_cache_getnew(char *str)
3374 NUMCacheEntry *ent;
3376 /* counter overflow check - paranoia? */
3377 if (NUMCounter >= (INT_MAX - NUM_CACHE_FIELDS - 1))
3379 NUMCounter = 0;
3381 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3382 ent->age = (++NUMCounter);
3386 * If cache is full, remove oldest entry
3388 if (n_NUMCache > NUM_CACHE_FIELDS)
3390 NUMCacheEntry *old = NUMCache + 0;
3392 #ifdef DEBUG_TO_FROM_CHAR
3393 elog(DEBUG_elog_output, "Cache is full (%d)", n_NUMCache);
3394 #endif
3395 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3398 * entry removed via NUM_cache_remove() can be used here, which is
3399 * why it's worth scanning first entry again
3401 if (ent->str[0] == '\0')
3403 old = ent;
3404 break;
3406 if (ent->age < old->age)
3407 old = ent;
3409 #ifdef DEBUG_TO_FROM_CHAR
3410 elog(DEBUG_elog_output, "OLD: \"%s\" AGE: %d", old->str, old->age);
3411 #endif
3412 StrNCpy(old->str, str, NUM_CACHE_SIZE + 1);
3413 /* old->format fill parser */
3414 old->age = (++NUMCounter);
3415 ent = old;
3417 else
3419 #ifdef DEBUG_TO_FROM_CHAR
3420 elog(DEBUG_elog_output, "NEW (%d)", n_NUMCache);
3421 #endif
3422 ent = NUMCache + n_NUMCache;
3423 StrNCpy(ent->str, str, NUM_CACHE_SIZE + 1);
3424 /* ent->format fill parser */
3425 ent->age = (++NUMCounter);
3426 ++n_NUMCache;
3429 zeroize_NUM(&ent->Num);
3431 last_NUMCacheEntry = ent;
3432 return ent;
3435 static NUMCacheEntry *
3436 NUM_cache_search(char *str)
3438 int i;
3439 NUMCacheEntry *ent;
3441 /* counter overflow check - paranoia? */
3442 if (NUMCounter >= (INT_MAX - NUM_CACHE_FIELDS - 1))
3444 NUMCounter = 0;
3446 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
3447 ent->age = (++NUMCounter);
3450 for (i = 0, ent = NUMCache; i < n_NUMCache; i++, ent++)
3452 if (strcmp(ent->str, str) == 0)
3454 ent->age = (++NUMCounter);
3455 last_NUMCacheEntry = ent;
3456 return ent;
3460 return NULL;
3463 static void
3464 NUM_cache_remove(NUMCacheEntry *ent)
3466 #ifdef DEBUG_TO_FROM_CHAR
3467 elog(DEBUG_elog_output, "REMOVING ENTRY (%s)", ent->str);
3468 #endif
3469 ent->str[0] = '\0';
3470 ent->age = 0;
3473 /* ----------
3474 * Cache routine for NUM to_char version
3475 * ----------
3477 static FormatNode *
3478 NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree)
3480 FormatNode *format = NULL;
3481 char *str;
3483 str = text_to_cstring(pars_str);
3486 * Allocate new memory if format picture is bigger than static cache and
3487 * not use cache (call parser always). This branches sets shouldFree to
3488 * true, accordingly.
3490 if (len > NUM_CACHE_SIZE)
3492 format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
3494 *shouldFree = true;
3496 zeroize_NUM(Num);
3498 parse_format(format, str, NUM_keywords,
3499 NULL, NUM_index, NUM_TYPE, Num);
3501 (format + len)->type = NODE_TYPE_END; /* Paranoia? */
3503 else
3506 * Use cache buffers
3508 NUMCacheEntry *ent;
3510 *shouldFree = false;
3512 if ((ent = NUM_cache_search(str)) == NULL)
3514 ent = NUM_cache_getnew(str);
3517 * Not in the cache, must run parser and save a new format-picture
3518 * to the cache.
3520 parse_format(ent->format, str, NUM_keywords,
3521 NULL, NUM_index, NUM_TYPE, &ent->Num);
3523 (ent->format + len)->type = NODE_TYPE_END; /* Paranoia? */
3526 format = ent->format;
3529 * Copy cache to used struct
3531 Num->flag = ent->Num.flag;
3532 Num->lsign = ent->Num.lsign;
3533 Num->pre = ent->Num.pre;
3534 Num->post = ent->Num.post;
3535 Num->pre_lsign_num = ent->Num.pre_lsign_num;
3536 Num->need_locale = ent->Num.need_locale;
3537 Num->multi = ent->Num.multi;
3538 Num->zero_start = ent->Num.zero_start;
3539 Num->zero_end = ent->Num.zero_end;
3542 #ifdef DEBUG_TO_FROM_CHAR
3543 /* dump_node(format, len); */
3544 dump_index(NUM_keywords, NUM_index);
3545 #endif
3547 pfree(str);
3548 return format;
3552 static char *
3553 int_to_roman(int number)
3555 int len = 0,
3556 num = 0;
3557 char *p = NULL,
3558 *result,
3559 numstr[5];
3561 result = (char *) palloc(16);
3562 *result = '\0';
3564 if (number > 3999 || number < 1)
3566 fill_str(result, '#', 15);
3567 return result;
3569 len = snprintf(numstr, sizeof(numstr), "%d", number);
3571 for (p = numstr; *p != '\0'; p++, --len)
3573 num = *p - 49; /* 48 ascii + 1 */
3574 if (num < 0)
3575 continue;
3577 if (len > 3)
3579 while (num-- != -1)
3580 strcat(result, "M");
3582 else
3584 if (len == 3)
3585 strcat(result, rm100[num]);
3586 else if (len == 2)
3587 strcat(result, rm10[num]);
3588 else if (len == 1)
3589 strcat(result, rm1[num]);
3592 return result;
3597 /* ----------
3598 * Locale
3599 * ----------
3601 static void
3602 NUM_prepare_locale(NUMProc *Np)
3604 if (Np->Num->need_locale)
3606 struct lconv *lconv;
3609 * Get locales
3611 lconv = PGLC_localeconv();
3614 * Positive / Negative number sign
3616 if (lconv->negative_sign && *lconv->negative_sign)
3617 Np->L_negative_sign = lconv->negative_sign;
3618 else
3619 Np->L_negative_sign = "-";
3621 if (lconv->positive_sign && *lconv->positive_sign)
3622 Np->L_positive_sign = lconv->positive_sign;
3623 else
3624 Np->L_positive_sign = "+";
3627 * Number decimal point
3629 if (lconv->decimal_point && *lconv->decimal_point)
3630 Np->decimal = lconv->decimal_point;
3632 else
3633 Np->decimal = ".";
3635 if (!IS_LDECIMAL(Np->Num))
3636 Np->decimal = ".";
3639 * Number thousands separator
3641 * Some locales (e.g. broken glibc pt_BR), have a comma for decimal,
3642 * but "" for thousands_sep, so we set the thousands_sep too.
3643 * http://archives.postgresql.org/pgsql-hackers/2007-11/msg00772.php
3645 if (lconv->thousands_sep && *lconv->thousands_sep)
3646 Np->L_thousands_sep = lconv->thousands_sep;
3647 /* Make sure thousands separator doesn't match decimal point symbol. */
3648 else if (strcmp(Np->decimal, ",") !=0)
3649 Np->L_thousands_sep = ",";
3650 else
3651 Np->L_thousands_sep = ".";
3654 * Currency symbol
3656 if (lconv->currency_symbol && *lconv->currency_symbol)
3657 Np->L_currency_symbol = lconv->currency_symbol;
3658 else
3659 Np->L_currency_symbol = " ";
3661 else
3664 * Default values
3666 Np->L_negative_sign = "-";
3667 Np->L_positive_sign = "+";
3668 Np->decimal = ".";
3670 Np->L_thousands_sep = ",";
3671 Np->L_currency_symbol = " ";
3675 /* ----------
3676 * Return pointer of last relevant number after decimal point
3677 * 12.0500 --> last relevant is '5'
3678 * ----------
3680 static char *
3681 get_last_relevant_decnum(char *num)
3683 char *result,
3684 *p = strchr(num, '.');
3686 #ifdef DEBUG_TO_FROM_CHAR
3687 elog(DEBUG_elog_output, "get_last_relevant_decnum()");
3688 #endif
3690 if (!p)
3691 p = num;
3692 result = p;
3694 while (*(++p))
3696 if (*p != '0')
3697 result = p;
3700 return result;
3703 /* ----------
3704 * Number extraction for TO_NUMBER()
3705 * ----------
3707 static void
3708 NUM_numpart_from_char(NUMProc *Np, int id, int plen)
3710 bool isread = FALSE;
3712 #ifdef DEBUG_TO_FROM_CHAR
3713 elog(DEBUG_elog_output, " --- scan start --- id=%s",
3714 (id == NUM_0 || id == NUM_9) ? "NUM_0/9" : id == NUM_DEC ? "NUM_DEC" : "???");
3715 #endif
3717 if (*Np->inout_p == ' ')
3718 Np->inout_p++;
3720 #define OVERLOAD_TEST (Np->inout_p >= Np->inout + plen)
3721 #define AMOUNT_TEST(_s) (plen-(Np->inout_p-Np->inout) >= _s)
3723 if (*Np->inout_p == ' ')
3724 Np->inout_p++;
3726 if (OVERLOAD_TEST)
3727 return;
3730 * read sign before number
3732 if (*Np->number == ' ' && (id == NUM_0 || id == NUM_9) &&
3733 (Np->read_pre + Np->read_post) == 0)
3735 #ifdef DEBUG_TO_FROM_CHAR
3736 elog(DEBUG_elog_output, "Try read sign (%c), locale positive: %s, negative: %s",
3737 *Np->inout_p, Np->L_positive_sign, Np->L_negative_sign);
3738 #endif
3741 * locale sign
3743 if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_PRE)
3745 int x = 0;
3747 #ifdef DEBUG_TO_FROM_CHAR
3748 elog(DEBUG_elog_output, "Try read locale pre-sign (%c)", *Np->inout_p);
3749 #endif
3750 if ((x = strlen(Np->L_negative_sign)) &&
3751 AMOUNT_TEST(x) &&
3752 strncmp(Np->inout_p, Np->L_negative_sign, x) == 0)
3754 Np->inout_p += x;
3755 *Np->number = '-';
3757 else if ((x = strlen(Np->L_positive_sign)) &&
3758 AMOUNT_TEST(x) &&
3759 strncmp(Np->inout_p, Np->L_positive_sign, x) == 0)
3761 Np->inout_p += x;
3762 *Np->number = '+';
3765 else
3767 #ifdef DEBUG_TO_FROM_CHAR
3768 elog(DEBUG_elog_output, "Try read simple sign (%c)", *Np->inout_p);
3769 #endif
3772 * simple + - < >
3774 if (*Np->inout_p == '-' || (IS_BRACKET(Np->Num) &&
3775 *Np->inout_p == '<'))
3777 *Np->number = '-'; /* set - */
3778 Np->inout_p++;
3780 else if (*Np->inout_p == '+')
3782 *Np->number = '+'; /* set + */
3783 Np->inout_p++;
3788 if (OVERLOAD_TEST)
3789 return;
3791 #ifdef DEBUG_TO_FROM_CHAR
3792 elog(DEBUG_elog_output, "Scan for numbers (%c), current number: '%s'", *Np->inout_p, Np->number);
3793 #endif
3796 * read digit
3798 if (isdigit((unsigned char) *Np->inout_p))
3800 if (Np->read_dec && Np->read_post == Np->Num->post)
3801 return;
3803 *Np->number_p = *Np->inout_p;
3804 Np->number_p++;
3806 if (Np->read_dec)
3807 Np->read_post++;
3808 else
3809 Np->read_pre++;
3811 isread = TRUE;
3813 #ifdef DEBUG_TO_FROM_CHAR
3814 elog(DEBUG_elog_output, "Read digit (%c)", *Np->inout_p);
3815 #endif
3818 * read decimal point
3821 else if (IS_DECIMAL(Np->Num) && Np->read_dec == FALSE)
3823 #ifdef DEBUG_TO_FROM_CHAR
3824 elog(DEBUG_elog_output, "Try read decimal point (%c)", *Np->inout_p);
3825 #endif
3826 if (*Np->inout_p == '.')
3828 *Np->number_p = '.';
3829 Np->number_p++;
3830 Np->read_dec = TRUE;
3831 isread = TRUE;
3833 else
3835 int x = strlen(Np->decimal);
3837 #ifdef DEBUG_TO_FROM_CHAR
3838 elog(DEBUG_elog_output, "Try read locale point (%c)",
3839 *Np->inout_p);
3840 #endif
3841 if (x && AMOUNT_TEST(x) && strncmp(Np->inout_p, Np->decimal, x) == 0)
3843 Np->inout_p += x - 1;
3844 *Np->number_p = '.';
3845 Np->number_p++;
3846 Np->read_dec = TRUE;
3847 isread = TRUE;
3852 if (OVERLOAD_TEST)
3853 return;
3856 * Read sign behind "last" number
3858 * We need sign detection because determine exact position of post-sign is
3859 * difficult:
3861 * FM9999.9999999S -> 123.001- 9.9S -> .5- FM9.999999MI ->
3862 * 5.01-
3864 if (*Np->number == ' ' && Np->read_pre + Np->read_post > 0)
3867 * locale sign (NUM_S) is always anchored behind a last number, if: -
3868 * locale sign expected - last read char was NUM_0/9 or NUM_DEC - and
3869 * next char is not digit
3871 if (IS_LSIGN(Np->Num) && isread &&
3872 (Np->inout_p + 1) <= Np->inout + plen &&
3873 !isdigit((unsigned char) *(Np->inout_p + 1)))
3875 int x;
3876 char *tmp = Np->inout_p++;
3878 #ifdef DEBUG_TO_FROM_CHAR
3879 elog(DEBUG_elog_output, "Try read locale post-sign (%c)", *Np->inout_p);
3880 #endif
3881 if ((x = strlen(Np->L_negative_sign)) &&
3882 AMOUNT_TEST(x) &&
3883 strncmp(Np->inout_p, Np->L_negative_sign, x) == 0)
3885 Np->inout_p += x - 1; /* -1 .. NUM_processor() do inout_p++ */
3886 *Np->number = '-';
3888 else if ((x = strlen(Np->L_positive_sign)) &&
3889 AMOUNT_TEST(x) &&
3890 strncmp(Np->inout_p, Np->L_positive_sign, x) == 0)
3892 Np->inout_p += x - 1; /* -1 .. NUM_processor() do inout_p++ */
3893 *Np->number = '+';
3895 if (*Np->number == ' ')
3896 /* no sign read */
3897 Np->inout_p = tmp;
3901 * try read non-locale sign, it's happen only if format is not exact
3902 * and we cannot determine sign position of MI/PL/SG, an example:
3904 * FM9.999999MI -> 5.01-
3906 * if (.... && IS_LSIGN(Np->Num)==FALSE) prevents read wrong formats
3907 * like to_number('1 -', '9S') where sign is not anchored to last
3908 * number.
3910 else if (isread == FALSE && IS_LSIGN(Np->Num) == FALSE &&
3911 (IS_PLUS(Np->Num) || IS_MINUS(Np->Num)))
3913 #ifdef DEBUG_TO_FROM_CHAR
3914 elog(DEBUG_elog_output, "Try read simple post-sign (%c)", *Np->inout_p);
3915 #endif
3918 * simple + -
3920 if (*Np->inout_p == '-' || *Np->inout_p == '+')
3921 /* NUM_processor() do inout_p++ */
3922 *Np->number = *Np->inout_p;
3927 #define IS_PREDEC_SPACE(_n) \
3928 (IS_ZERO((_n)->Num)==FALSE && \
3929 (_n)->number == (_n)->number_p && \
3930 *(_n)->number == '0' && \
3931 (_n)->Num->post != 0)
3933 /* ----------
3934 * Add digit or sign to number-string
3935 * ----------
3937 static void
3938 NUM_numpart_to_char(NUMProc *Np, int id)
3940 int end;
3942 if (IS_ROMAN(Np->Num))
3943 return;
3945 /* Note: in this elog() output not set '\0' in 'inout' */
3947 #ifdef DEBUG_TO_FROM_CHAR
3950 * Np->num_curr is number of current item in format-picture, it is not
3951 * current position in inout!
3953 elog(DEBUG_elog_output,
3954 "SIGN_WROTE: %d, CURRENT: %d, NUMBER_P: \"%s\", INOUT: \"%s\"",
3955 Np->sign_wrote,
3956 Np->num_curr,
3957 Np->number_p,
3958 Np->inout);
3959 #endif
3960 Np->num_in = FALSE;
3963 * Write sign if real number will write to output Note: IS_PREDEC_SPACE()
3964 * handle "9.9" --> " .1"
3966 if (Np->sign_wrote == FALSE &&
3967 (Np->num_curr >= Np->num_pre || (IS_ZERO(Np->Num) && Np->Num->zero_start == Np->num_curr)) &&
3968 (IS_PREDEC_SPACE(Np) == FALSE || (Np->last_relevant && *Np->last_relevant == '.')))
3970 if (IS_LSIGN(Np->Num))
3972 if (Np->Num->lsign == NUM_LSIGN_PRE)
3974 if (Np->sign == '-')
3975 strcpy(Np->inout_p, Np->L_negative_sign);
3976 else
3977 strcpy(Np->inout_p, Np->L_positive_sign);
3978 Np->inout_p += strlen(Np->inout_p);
3979 Np->sign_wrote = TRUE;
3982 else if (IS_BRACKET(Np->Num))
3984 *Np->inout_p = Np->sign == '+' ? ' ' : '<';
3985 ++Np->inout_p;
3986 Np->sign_wrote = TRUE;
3988 else if (Np->sign == '+')
3990 if (!IS_FILLMODE(Np->Num))
3992 *Np->inout_p = ' '; /* Write + */
3993 ++Np->inout_p;
3995 Np->sign_wrote = TRUE;
3997 else if (Np->sign == '-')
3998 { /* Write - */
3999 *Np->inout_p = '-';
4000 ++Np->inout_p;
4001 Np->sign_wrote = TRUE;
4007 * digits / FM / Zero / Dec. point
4009 if (id == NUM_9 || id == NUM_0 || id == NUM_D || id == NUM_DEC)
4011 if (Np->num_curr < Np->num_pre &&
4012 (Np->Num->zero_start > Np->num_curr || !IS_ZERO(Np->Num)))
4015 * Write blank space
4017 if (!IS_FILLMODE(Np->Num))
4019 *Np->inout_p = ' '; /* Write ' ' */
4020 ++Np->inout_p;
4023 else if (IS_ZERO(Np->Num) &&
4024 Np->num_curr < Np->num_pre &&
4025 Np->Num->zero_start <= Np->num_curr)
4028 * Write ZERO
4030 *Np->inout_p = '0'; /* Write '0' */
4031 ++Np->inout_p;
4032 Np->num_in = TRUE;
4034 else
4037 * Write Decimal point
4039 if (*Np->number_p == '.')
4041 if (!Np->last_relevant || *Np->last_relevant != '.')
4043 strcpy(Np->inout_p, Np->decimal); /* Write DEC/D */
4044 Np->inout_p += strlen(Np->inout_p);
4048 * Ora 'n' -- FM9.9 --> 'n.'
4050 else if (IS_FILLMODE(Np->Num) &&
4051 Np->last_relevant && *Np->last_relevant == '.')
4053 strcpy(Np->inout_p, Np->decimal); /* Write DEC/D */
4054 Np->inout_p += strlen(Np->inout_p);
4057 else
4060 * Write Digits
4062 if (Np->last_relevant && Np->number_p > Np->last_relevant &&
4063 id != NUM_0)
4067 * '0.1' -- 9.9 --> ' .1'
4069 else if (IS_PREDEC_SPACE(Np))
4071 if (!IS_FILLMODE(Np->Num))
4073 *Np->inout_p = ' ';
4074 ++Np->inout_p;
4078 * '0' -- FM9.9 --> '0.'
4080 else if (Np->last_relevant && *Np->last_relevant == '.')
4082 *Np->inout_p = '0';
4083 ++Np->inout_p;
4086 else
4088 *Np->inout_p = *Np->number_p; /* Write DIGIT */
4089 ++Np->inout_p;
4090 Np->num_in = TRUE;
4093 ++Np->number_p;
4096 end = Np->num_count + (Np->num_pre ? 1 : 0) + (IS_DECIMAL(Np->Num) ? 1 : 0);
4098 if (Np->last_relevant && Np->last_relevant == Np->number_p)
4099 end = Np->num_curr;
4101 if (Np->num_curr + 1 == end)
4103 if (Np->sign_wrote == TRUE && IS_BRACKET(Np->Num))
4105 *Np->inout_p = Np->sign == '+' ? ' ' : '>';
4106 ++Np->inout_p;
4108 else if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_POST)
4110 if (Np->sign == '-')
4111 strcpy(Np->inout_p, Np->L_negative_sign);
4112 else
4113 strcpy(Np->inout_p, Np->L_positive_sign);
4114 Np->inout_p += strlen(Np->inout_p);
4119 ++Np->num_curr;
4123 * Note: 'plen' is used in FROM_CHAR conversion and it's length of
4124 * input (inout). In TO_CHAR conversion it's space before first number.
4126 static char *
4127 NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
4128 int plen, int sign, bool is_to_char)
4130 FormatNode *n;
4131 NUMProc _Np,
4132 *Np = &_Np;
4134 MemSet(Np, 0, sizeof(NUMProc));
4136 Np->Num = Num;
4137 Np->is_to_char = is_to_char;
4138 Np->number = number;
4139 Np->inout = inout;
4140 Np->last_relevant = NULL;
4141 Np->read_post = 0;
4142 Np->read_pre = 0;
4143 Np->read_dec = FALSE;
4145 if (Np->Num->zero_start)
4146 --Np->Num->zero_start;
4149 * Roman correction
4151 if (IS_ROMAN(Np->Num))
4153 if (!Np->is_to_char)
4154 ereport(ERROR,
4155 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4156 errmsg("\"RN\" not supported")));
4158 Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->post =
4159 Np->Num->pre = Np->num_pre = Np->sign = 0;
4161 if (IS_FILLMODE(Np->Num))
4163 Np->Num->flag = 0;
4164 Np->Num->flag |= NUM_F_FILLMODE;
4166 else
4167 Np->Num->flag = 0;
4168 Np->Num->flag |= NUM_F_ROMAN;
4172 * Sign
4174 if (is_to_char)
4176 Np->sign = sign;
4178 /* MI/PL/SG - write sign itself and not in number */
4179 if (IS_PLUS(Np->Num) || IS_MINUS(Np->Num))
4181 if (IS_PLUS(Np->Num) && IS_MINUS(Np->Num) == FALSE)
4182 Np->sign_wrote = FALSE; /* need sign */
4183 else
4184 Np->sign_wrote = TRUE; /* needn't sign */
4186 else
4188 if (Np->sign != '-')
4190 if (IS_BRACKET(Np->Num) && IS_FILLMODE(Np->Num))
4191 Np->Num->flag &= ~NUM_F_BRACKET;
4192 if (IS_MINUS(Np->Num))
4193 Np->Num->flag &= ~NUM_F_MINUS;
4195 else if (Np->sign != '+' && IS_PLUS(Np->Num))
4196 Np->Num->flag &= ~NUM_F_PLUS;
4198 if (Np->sign == '+' && IS_FILLMODE(Np->Num) && IS_LSIGN(Np->Num) == FALSE)
4199 Np->sign_wrote = TRUE; /* needn't sign */
4200 else
4201 Np->sign_wrote = FALSE; /* need sign */
4203 if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num)
4204 Np->Num->lsign = NUM_LSIGN_POST;
4207 else
4208 Np->sign = FALSE;
4211 * Count
4213 Np->num_count = Np->Num->post + Np->Num->pre - 1;
4215 if (is_to_char)
4217 Np->num_pre = plen;
4219 if (IS_FILLMODE(Np->Num))
4221 if (IS_DECIMAL(Np->Num))
4222 Np->last_relevant = get_last_relevant_decnum(
4223 Np->number +
4224 ((Np->Num->zero_end - Np->num_pre > 0) ?
4225 Np->Num->zero_end - Np->num_pre : 0));
4228 if (Np->sign_wrote == FALSE && Np->num_pre == 0)
4229 ++Np->num_count;
4231 else
4233 Np->num_pre = 0;
4234 *Np->number = ' '; /* sign space */
4235 *(Np->number + 1) = '\0';
4238 Np->num_in = 0;
4239 Np->num_curr = 0;
4241 #ifdef DEBUG_TO_FROM_CHAR
4242 elog(DEBUG_elog_output,
4243 "\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",
4244 Np->sign,
4245 Np->number,
4246 Np->Num->pre,
4247 Np->Num->post,
4248 Np->num_count,
4249 Np->num_pre,
4250 Np->sign_wrote ? "Yes" : "No",
4251 IS_ZERO(Np->Num) ? "Yes" : "No",
4252 Np->Num->zero_start,
4253 Np->Num->zero_end,
4254 Np->last_relevant ? Np->last_relevant : "<not set>",
4255 IS_BRACKET(Np->Num) ? "Yes" : "No",
4256 IS_PLUS(Np->Num) ? "Yes" : "No",
4257 IS_MINUS(Np->Num) ? "Yes" : "No",
4258 IS_FILLMODE(Np->Num) ? "Yes" : "No",
4259 IS_ROMAN(Np->Num) ? "Yes" : "No"
4261 #endif
4264 * Locale
4266 NUM_prepare_locale(Np);
4269 * Processor direct cycle
4271 if (Np->is_to_char)
4272 Np->number_p = Np->number;
4273 else
4274 Np->number_p = Np->number + 1; /* first char is space for sign */
4276 for (n = node, Np->inout_p = Np->inout; n->type != NODE_TYPE_END; n++)
4278 if (!Np->is_to_char)
4281 * Check non-string inout end
4283 if (Np->inout_p >= Np->inout + plen)
4284 break;
4288 * Format pictures actions
4290 if (n->type == NODE_TYPE_ACTION)
4293 * Create/reading digit/zero/blank/sing
4295 * 'NUM_S' note: The locale sign is anchored to number and we
4296 * read/write it when we work with first or last number
4297 * (NUM_0/NUM_9). This is reason why NUM_S missing in follow
4298 * switch().
4300 switch (n->key->id)
4302 case NUM_9:
4303 case NUM_0:
4304 case NUM_DEC:
4305 case NUM_D:
4306 if (Np->is_to_char)
4308 NUM_numpart_to_char(Np, n->key->id);
4309 continue; /* for() */
4311 else
4313 NUM_numpart_from_char(Np, n->key->id, plen);
4314 break; /* switch() case: */
4317 case NUM_COMMA:
4318 if (Np->is_to_char)
4320 if (!Np->num_in)
4322 if (IS_FILLMODE(Np->Num))
4323 continue;
4324 else
4325 *Np->inout_p = ' ';
4327 else
4328 *Np->inout_p = ',';
4330 else
4332 if (!Np->num_in)
4334 if (IS_FILLMODE(Np->Num))
4335 continue;
4338 break;
4340 case NUM_G:
4341 if (Np->is_to_char)
4343 if (!Np->num_in)
4345 if (IS_FILLMODE(Np->Num))
4346 continue;
4347 else
4349 int x = strlen(Np->L_thousands_sep);
4351 memset(Np->inout_p, ' ', x);
4352 Np->inout_p += x - 1;
4355 else
4357 strcpy(Np->inout_p, Np->L_thousands_sep);
4358 Np->inout_p += strlen(Np->inout_p) - 1;
4361 else
4363 if (!Np->num_in)
4365 if (IS_FILLMODE(Np->Num))
4366 continue;
4368 Np->inout_p += strlen(Np->L_thousands_sep) - 1;
4370 break;
4372 case NUM_L:
4373 if (Np->is_to_char)
4375 strcpy(Np->inout_p, Np->L_currency_symbol);
4376 Np->inout_p += strlen(Np->inout_p) - 1;
4378 else
4379 Np->inout_p += strlen(Np->L_currency_symbol) - 1;
4380 break;
4382 case NUM_RN:
4383 if (IS_FILLMODE(Np->Num))
4385 strcpy(Np->inout_p, Np->number_p);
4386 Np->inout_p += strlen(Np->inout_p) - 1;
4388 else
4390 sprintf(Np->inout_p, "%15s", Np->number_p);
4391 Np->inout_p += strlen(Np->inout_p) - 1;
4393 break;
4395 case NUM_rn:
4396 if (IS_FILLMODE(Np->Num))
4398 strcpy(Np->inout_p, str_tolower_z(Np->number_p));
4399 Np->inout_p += strlen(Np->inout_p) - 1;
4401 else
4403 sprintf(Np->inout_p, "%15s", str_tolower_z(Np->number_p));
4404 Np->inout_p += strlen(Np->inout_p) - 1;
4406 break;
4408 case NUM_th:
4409 if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
4410 Np->sign == '-' || IS_DECIMAL(Np->Num))
4411 continue;
4413 if (Np->is_to_char)
4414 strcpy(Np->inout_p, get_th(Np->number, TH_LOWER));
4415 Np->inout_p += 1;
4416 break;
4418 case NUM_TH:
4419 if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
4420 Np->sign == '-' || IS_DECIMAL(Np->Num))
4421 continue;
4423 if (Np->is_to_char)
4424 strcpy(Np->inout_p, get_th(Np->number, TH_UPPER));
4425 Np->inout_p += 1;
4426 break;
4428 case NUM_MI:
4429 if (Np->is_to_char)
4431 if (Np->sign == '-')
4432 *Np->inout_p = '-';
4433 else if (IS_FILLMODE(Np->Num))
4434 continue;
4435 else
4436 *Np->inout_p = ' ';
4438 else
4440 if (*Np->inout_p == '-')
4441 *Np->number = '-';
4443 break;
4445 case NUM_PL:
4446 if (Np->is_to_char)
4448 if (Np->sign == '+')
4449 *Np->inout_p = '+';
4450 else if (IS_FILLMODE(Np->Num))
4451 continue;
4452 else
4453 *Np->inout_p = ' ';
4455 else
4457 if (*Np->inout_p == '+')
4458 *Np->number = '+';
4460 break;
4462 case NUM_SG:
4463 if (Np->is_to_char)
4464 *Np->inout_p = Np->sign;
4466 else
4468 if (*Np->inout_p == '-')
4469 *Np->number = '-';
4470 else if (*Np->inout_p == '+')
4471 *Np->number = '+';
4473 break;
4476 default:
4477 continue;
4478 break;
4481 else
4484 * Remove to output char from input in TO_CHAR
4486 if (Np->is_to_char)
4487 *Np->inout_p = n->character;
4489 Np->inout_p++;
4492 if (Np->is_to_char)
4494 *Np->inout_p = '\0';
4495 return Np->inout;
4497 else
4499 if (*(Np->number_p - 1) == '.')
4500 *(Np->number_p - 1) = '\0';
4501 else
4502 *Np->number_p = '\0';
4505 * Correction - precision of dec. number
4507 Np->Num->post = Np->read_post;
4509 #ifdef DEBUG_TO_FROM_CHAR
4510 elog(DEBUG_elog_output, "TO_NUMBER (number): '%s'", Np->number);
4511 #endif
4512 return Np->number;
4516 /* ----------
4517 * MACRO: Start part of NUM - for all NUM's to_char variants
4518 * (sorry, but I hate copy same code - macro is better..)
4519 * ----------
4521 #define NUM_TOCHAR_prepare \
4522 do { \
4523 len = VARSIZE_ANY_EXHDR(fmt); \
4524 if (len <= 0 || len >= (INT_MAX-VARHDRSZ)/NUM_MAX_ITEM_SIZ) \
4525 PG_RETURN_TEXT_P(cstring_to_text("")); \
4526 result = (text *) palloc0((len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ); \
4527 format = NUM_cache(len, &Num, fmt, &shouldFree); \
4528 } while (0)
4530 /* ----------
4531 * MACRO: Finish part of NUM
4532 * ----------
4534 #define NUM_TOCHAR_finish \
4535 do { \
4536 NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true); \
4538 if (shouldFree) \
4539 pfree(format); \
4541 /* \
4542 * Convert null-terminated representation of result to standard text. \
4543 * The result is usually much bigger than it needs to be, but there \
4544 * seems little point in realloc'ing it smaller. \
4545 */ \
4546 len = strlen(VARDATA(result)); \
4547 SET_VARSIZE(result, len + VARHDRSZ); \
4548 } while (0)
4550 /* -------------------
4551 * NUMERIC to_number() (convert string to numeric)
4552 * -------------------
4554 Datum
4555 numeric_to_number(PG_FUNCTION_ARGS)
4557 text *value = PG_GETARG_TEXT_P(0);
4558 text *fmt = PG_GETARG_TEXT_P(1);
4559 NUMDesc Num;
4560 Datum result;
4561 FormatNode *format;
4562 char *numstr;
4563 bool shouldFree;
4564 int len = 0;
4565 int scale,
4566 precision;
4568 len = VARSIZE(fmt) - VARHDRSZ;
4570 if (len <= 0 || len >= INT_MAX / NUM_MAX_ITEM_SIZ)
4571 PG_RETURN_NULL();
4573 format = NUM_cache(len, &Num, fmt, &shouldFree);
4575 numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1);
4577 NUM_processor(format, &Num, VARDATA(value), numstr,
4578 VARSIZE(value) - VARHDRSZ, 0, false);
4580 scale = Num.post;
4581 precision = Max(0, Num.pre) + scale;
4583 if (shouldFree)
4584 pfree(format);
4586 result = DirectFunctionCall3(numeric_in,
4587 CStringGetDatum(numstr),
4588 ObjectIdGetDatum(InvalidOid),
4589 Int32GetDatum(((precision << 16) | scale) + VARHDRSZ));
4590 pfree(numstr);
4591 return result;
4594 /* ------------------
4595 * NUMERIC to_char()
4596 * ------------------
4598 Datum
4599 numeric_to_char(PG_FUNCTION_ARGS)
4601 Numeric value = PG_GETARG_NUMERIC(0);
4602 text *fmt = PG_GETARG_TEXT_P(1);
4603 NUMDesc Num;
4604 FormatNode *format;
4605 text *result;
4606 bool shouldFree;
4607 int len = 0,
4608 plen = 0,
4609 sign = 0;
4610 char *numstr,
4611 *orgnum,
4613 Numeric x;
4615 NUM_TOCHAR_prepare;
4618 * On DateType depend part (numeric)
4620 if (IS_ROMAN(&Num))
4622 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
4623 NumericGetDatum(value),
4624 Int32GetDatum(0)));
4625 numstr = orgnum =
4626 int_to_roman(DatumGetInt32(DirectFunctionCall1(numeric_int4,
4627 NumericGetDatum(x))));
4629 else
4631 Numeric val = value;
4633 if (IS_MULTI(&Num))
4635 Numeric a = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
4636 Int32GetDatum(10)));
4637 Numeric b = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
4638 Int32GetDatum(Num.multi)));
4640 x = DatumGetNumeric(DirectFunctionCall2(numeric_power,
4641 NumericGetDatum(a),
4642 NumericGetDatum(b)));
4643 val = DatumGetNumeric(DirectFunctionCall2(numeric_mul,
4644 NumericGetDatum(value),
4645 NumericGetDatum(x)));
4646 Num.pre += Num.multi;
4649 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
4650 NumericGetDatum(val),
4651 Int32GetDatum(Num.post)));
4652 orgnum = DatumGetCString(DirectFunctionCall1(numeric_out,
4653 NumericGetDatum(x)));
4655 if (*orgnum == '-')
4657 sign = '-';
4658 numstr = orgnum + 1;
4660 else
4662 sign = '+';
4663 numstr = orgnum;
4665 if ((p = strchr(numstr, '.')))
4666 len = p - numstr;
4667 else
4668 len = strlen(numstr);
4670 if (Num.pre > len)
4671 plen = Num.pre - len;
4672 else if (len > Num.pre)
4674 numstr = (char *) palloc(Num.pre + Num.post + 2);
4675 fill_str(numstr, '#', Num.pre + Num.post + 1);
4676 *(numstr + Num.pre) = '.';
4680 NUM_TOCHAR_finish;
4681 PG_RETURN_TEXT_P(result);
4684 /* ---------------
4685 * INT4 to_char()
4686 * ---------------
4688 Datum
4689 int4_to_char(PG_FUNCTION_ARGS)
4691 int32 value = PG_GETARG_INT32(0);
4692 text *fmt = PG_GETARG_TEXT_P(1);
4693 NUMDesc Num;
4694 FormatNode *format;
4695 text *result;
4696 bool shouldFree;
4697 int len = 0,
4698 plen = 0,
4699 sign = 0;
4700 char *numstr,
4701 *orgnum;
4703 NUM_TOCHAR_prepare;
4706 * On DateType depend part (int32)
4708 if (IS_ROMAN(&Num))
4709 numstr = orgnum = int_to_roman(value);
4710 else
4712 if (IS_MULTI(&Num))
4714 orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4715 Int32GetDatum(value * ((int32) pow((double) 10, (double) Num.multi)))));
4716 Num.pre += Num.multi;
4718 else
4720 orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4721 Int32GetDatum(value)));
4724 if (*orgnum == '-')
4726 sign = '-';
4727 orgnum++;
4729 else
4730 sign = '+';
4731 len = strlen(orgnum);
4733 if (Num.post)
4735 numstr = (char *) palloc(len + Num.post + 2);
4736 strcpy(numstr, orgnum);
4737 *(numstr + len) = '.';
4738 memset(numstr + len + 1, '0', Num.post);
4739 *(numstr + len + Num.post + 1) = '\0';
4741 else
4742 numstr = orgnum;
4744 if (Num.pre > len)
4745 plen = Num.pre - len;
4746 else if (len > Num.pre)
4748 numstr = (char *) palloc(Num.pre + Num.post + 2);
4749 fill_str(numstr, '#', Num.pre + Num.post + 1);
4750 *(numstr + Num.pre) = '.';
4754 NUM_TOCHAR_finish;
4755 PG_RETURN_TEXT_P(result);
4758 /* ---------------
4759 * INT8 to_char()
4760 * ---------------
4762 Datum
4763 int8_to_char(PG_FUNCTION_ARGS)
4765 int64 value = PG_GETARG_INT64(0);
4766 text *fmt = PG_GETARG_TEXT_P(1);
4767 NUMDesc Num;
4768 FormatNode *format;
4769 text *result;
4770 bool shouldFree;
4771 int len = 0,
4772 plen = 0,
4773 sign = 0;
4774 char *numstr,
4775 *orgnum;
4777 NUM_TOCHAR_prepare;
4780 * On DateType depend part (int32)
4782 if (IS_ROMAN(&Num))
4784 /* Currently don't support int8 conversion to roman... */
4785 numstr = orgnum = int_to_roman(DatumGetInt32(
4786 DirectFunctionCall1(int84, Int64GetDatum(value))));
4788 else
4790 if (IS_MULTI(&Num))
4792 double multi = pow((double) 10, (double) Num.multi);
4794 value = DatumGetInt64(DirectFunctionCall2(int8mul,
4795 Int64GetDatum(value),
4796 DirectFunctionCall1(dtoi8,
4797 Float8GetDatum(multi))));
4798 Num.pre += Num.multi;
4801 orgnum = DatumGetCString(DirectFunctionCall1(int8out,
4802 Int64GetDatum(value)));
4804 if (*orgnum == '-')
4806 sign = '-';
4807 orgnum++;
4809 else
4810 sign = '+';
4811 len = strlen(orgnum);
4813 if (Num.post)
4815 numstr = (char *) palloc(len + Num.post + 2);
4816 strcpy(numstr, orgnum);
4817 *(numstr + len) = '.';
4818 memset(numstr + len + 1, '0', Num.post);
4819 *(numstr + len + Num.post + 1) = '\0';
4821 else
4822 numstr = orgnum;
4824 if (Num.pre > len)
4825 plen = Num.pre - len;
4826 else if (len > Num.pre)
4828 numstr = (char *) palloc(Num.pre + Num.post + 2);
4829 fill_str(numstr, '#', Num.pre + Num.post + 1);
4830 *(numstr + Num.pre) = '.';
4834 NUM_TOCHAR_finish;
4835 PG_RETURN_TEXT_P(result);
4838 /* -----------------
4839 * FLOAT4 to_char()
4840 * -----------------
4842 Datum
4843 float4_to_char(PG_FUNCTION_ARGS)
4845 float4 value = PG_GETARG_FLOAT4(0);
4846 text *fmt = PG_GETARG_TEXT_P(1);
4847 NUMDesc Num;
4848 FormatNode *format;
4849 text *result;
4850 bool shouldFree;
4851 int len = 0,
4852 plen = 0,
4853 sign = 0;
4854 char *numstr,
4855 *orgnum,
4858 NUM_TOCHAR_prepare;
4860 if (IS_ROMAN(&Num))
4861 numstr = orgnum = int_to_roman((int) rint(value));
4862 else
4864 float4 val = value;
4866 if (IS_MULTI(&Num))
4868 float multi = pow((double) 10, (double) Num.multi);
4870 val = value * multi;
4871 Num.pre += Num.multi;
4874 orgnum = (char *) palloc(MAXFLOATWIDTH + 1);
4875 snprintf(orgnum, MAXFLOATWIDTH + 1, "%.0f", fabs(val));
4876 len = strlen(orgnum);
4877 if (Num.pre > len)
4878 plen = Num.pre - len;
4879 if (len >= FLT_DIG)
4880 Num.post = 0;
4881 else if (Num.post + len > FLT_DIG)
4882 Num.post = FLT_DIG - len;
4883 snprintf(orgnum, MAXFLOATWIDTH + 1, "%.*f", Num.post, val);
4885 if (*orgnum == '-')
4886 { /* < 0 */
4887 sign = '-';
4888 numstr = orgnum + 1;
4890 else
4892 sign = '+';
4893 numstr = orgnum;
4895 if ((p = strchr(numstr, '.')))
4896 len = p - numstr;
4897 else
4898 len = strlen(numstr);
4900 if (Num.pre > len)
4901 plen = Num.pre - len;
4902 else if (len > Num.pre)
4904 numstr = (char *) palloc(Num.pre + Num.post + 2);
4905 fill_str(numstr, '#', Num.pre + Num.post + 1);
4906 *(numstr + Num.pre) = '.';
4910 NUM_TOCHAR_finish;
4911 PG_RETURN_TEXT_P(result);
4914 /* -----------------
4915 * FLOAT8 to_char()
4916 * -----------------
4918 Datum
4919 float8_to_char(PG_FUNCTION_ARGS)
4921 float8 value = PG_GETARG_FLOAT8(0);
4922 text *fmt = PG_GETARG_TEXT_P(1);
4923 NUMDesc Num;
4924 FormatNode *format;
4925 text *result;
4926 bool shouldFree;
4927 int len = 0,
4928 plen = 0,
4929 sign = 0;
4930 char *numstr,
4931 *orgnum,
4934 NUM_TOCHAR_prepare;
4936 if (IS_ROMAN(&Num))
4937 numstr = orgnum = int_to_roman((int) rint(value));
4938 else
4940 float8 val = value;
4942 if (IS_MULTI(&Num))
4944 double multi = pow((double) 10, (double) Num.multi);
4946 val = value * multi;
4947 Num.pre += Num.multi;
4949 orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
4950 len = snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.0f", fabs(val));
4951 if (Num.pre > len)
4952 plen = Num.pre - len;
4953 if (len >= DBL_DIG)
4954 Num.post = 0;
4955 else if (Num.post + len > DBL_DIG)
4956 Num.post = DBL_DIG - len;
4957 snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.*f", Num.post, val);
4959 if (*orgnum == '-')
4960 { /* < 0 */
4961 sign = '-';
4962 numstr = orgnum + 1;
4964 else
4966 sign = '+';
4967 numstr = orgnum;
4969 if ((p = strchr(numstr, '.')))
4970 len = p - numstr;
4971 else
4972 len = strlen(numstr);
4974 if (Num.pre > len)
4975 plen = Num.pre - len;
4976 else if (len > Num.pre)
4978 numstr = (char *) palloc(Num.pre + Num.post + 2);
4979 fill_str(numstr, '#', Num.pre + Num.post + 1);
4980 *(numstr + Num.pre) = '.';
4984 NUM_TOCHAR_finish;
4985 PG_RETURN_TEXT_P(result);