diff options
author | Bruce Momjian <bruce@momjian.us> | 2000-01-25 23:53:56 +0000 |
---|---|---|
committer | Bruce Momjian <bruce@momjian.us> | 2000-01-25 23:53:56 +0000 |
commit | b866d2e2d79416f8497e4dffa7e800298d018f6c (patch) | |
tree | b633b1415e4b916aaff71d86dc8cee4dba0258e9 | |
parent | 90aaad06cf25753e10717ac7eb727cd65d8d5726 (diff) | |
download | postgresql-b866d2e2d79416f8497e4dffa7e800298d018f6c.tar.gz |
as attache of this mail is patch (to the main tree) with to_char's
family functions. Contain:
conversion from a datetype to formatted text:
to_char( datetime, text)
to_char( timestamp, text)
to_char( int4, text)
to_char( int8, text)
to_char( float4, text)
to_char( float8, text)
to_char( numeric, text)
vice versa:
to_date ( text, text)
to_datetime ( text, text)
to_timestamp ( text, text)
to_number ( text, text) (convert to numeric)
PostgreSQL to_char is very compatible with Oracle's to_char(), but not
total exactly (now). Small differentions are in number formating. It will
fix in next to_char() version.
! If will this patch aplly to the main tree, must be delete the current
to_char version in contrib (directory "dateformat" and note in contrib's
README), this patch not erase it (sorry Bruce).
The patch patching files:
doc/src/sgml/func.sgml
^^^^^^^^
Hmm, I'm not sure if my English... :( Check it anyone (volunteer)?
Thomas, it is right? SGML is not my primary lang and compile
the current PG docs tree is very happy job (hard variables setting in
docs/sgml/Makefile --> HSTYLE= /home/users/t/thomas/.... :-)
What add any definition to global configure.in and set Makefiles in docs
tree via ./configure?
src/backend/utils/adt/Makefile
src/backend/utils/adt/formatting.c
src/include/catalog/pg_proc.h
src/include/utils/formatting.h
Karel Zak <zakkr@zf.jcu.cz> http://home.zf.jcu.cz/~zakkr/
-rw-r--r-- | contrib/README | 4 | ||||
-rw-r--r-- | contrib/dateformat/Makefile | 71 | ||||
-rw-r--r-- | contrib/dateformat/test/Makefile | 25 | ||||
-rw-r--r-- | contrib/dateformat/test/README | 33 | ||||
-rw-r--r-- | contrib/dateformat/test/rand_datetime.c | 71 | ||||
-rw-r--r-- | contrib/dateformat/test/regress.sql | 58 | ||||
-rw-r--r-- | contrib/dateformat/to-from_char.c | 1382 | ||||
-rw-r--r-- | contrib/dateformat/to-from_char.doc | 183 | ||||
-rw-r--r-- | contrib/dateformat/to-from_char.h | 18 | ||||
-rw-r--r-- | contrib/dateformat/to-from_char.sql.in | 29 | ||||
-rw-r--r-- | doc/src/sgml/func.sgml | 541 | ||||
-rw-r--r-- | src/backend/utils/adt/Makefile | 4 | ||||
-rw-r--r-- | src/backend/utils/adt/formatting.c | 3157 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.h | 26 | ||||
-rw-r--r-- | src/include/utils/formatting.h | 31 |
15 files changed, 3756 insertions, 1877 deletions
diff --git a/contrib/README b/contrib/README index 5b1a4600f7..0fcaa468f2 100644 --- a/contrib/README +++ b/contrib/README @@ -14,10 +14,6 @@ bit - Bit type by Adriaan Joubert <a.joubert@albourne.com> -dateformat - - Date Formatting to/from character strings - by Karel Zak - Zakkr <zakkr@zf.jcu.cz> - datetime - Date & time functions by Massimo Dal Zotto <dz@cs.unitn.it> diff --git a/contrib/dateformat/Makefile b/contrib/dateformat/Makefile deleted file mode 100644 index 849d7a777e..0000000000 --- a/contrib/dateformat/Makefile +++ /dev/null @@ -1,71 +0,0 @@ -#------------------------------------------------------------------------- -# -# Makefile -- -# -# Makefile for TO-FROM_CHAR module. -# -#------------------------------------------------------------------------- - -PGDIR = ../.. -SRCDIR = $(PGDIR)/src - -include $(SRCDIR)/Makefile.global - -INCLUDE_OPT = -I ./ \ - -I $(SRCDIR)/ \ - -I $(SRCDIR)/include \ - -I $(SRCDIR)/port/$(PORTNAME) - -CFLAGS += $(INCLUDE_OPT) $(CFLAGS_SL) - -MODNAME = to-from_char - -SQLDEFS = $(MODNAME).sql - -MODULE = $(MODNAME)$(DLSUFFIX) - -MODDIR = $(LIBDIR)/modules - -SQLDIR = $(LIBDIR)/sql - -all: module sql - -module: $(MODULE) - -sql: $(SQLDEFS) - -install: $(MODULE) $(SQLDEFS) $(MODDIR) $(SQLDIR) - cp -p $(MODULE) $(MODDIR)/ - strip $(MODDIR)/$(MODULE) - cp -p $(SQLDEFS) $(SQLDIR)/ - -install-doc: - if [ -d "$(DOCDIR)" ]; then \ - cp -p *.doc $(DOCDIR); \ - else \ - cp -p *.doc $(SQLDIR); \ - fi - -$(MODDIR): - mkdir -p $@ - -$(SQLDIR): - mkdir -p $@ - -%.sql: %.sql.in - sed "s|MODULE_PATHNAME|$(MODDIR)/$(MODULE)|" < $< > $@ - -.SUFFIXES: $(DLSUFFIX) - -%$(DLSUFFIX): %.c - $(CC) $(CFLAGS) -shared -o $@ $< - -depend dep: - $(CC) -MM $(INCLUDE_OPT) *.c >depend - -clean: - rm -f *~ $(MODULE) $(MODNAME).sql - -ifeq (depend,$(wildcard depend)) -include depend -endif diff --git a/contrib/dateformat/test/Makefile b/contrib/dateformat/test/Makefile deleted file mode 100644 index 6eb539c3aa..0000000000 --- a/contrib/dateformat/test/Makefile +++ /dev/null @@ -1,25 +0,0 @@ - -PROGRAM = rand_datetime - -OBJECTS = rand_datetime.o - -CFLAGS = -Wall -fpic -O3 -CC = gcc -RM = rm -f -LIBS = -INCLUDE = - -COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDE) -LINK = $(CC) $(CFLAGS) -o $@ $(LIBS) - - -all: $(PROGRAM) - -$(PROGRAM): $(OBJECTS) - $(LINK) $(OBJECTS) - -.c.o: $< - $(COMPILE) -c $< - -clean: - $(RM) -f *~ $(OBJECTS) $(PROGRAM) diff --git a/contrib/dateformat/test/README b/contrib/dateformat/test/README deleted file mode 100644 index 63177f2ee5..0000000000 --- a/contrib/dateformat/test/README +++ /dev/null @@ -1,33 +0,0 @@ - - TO/FROM CHAR tests - ~~~~~~~~~~~~~~~~~~ - - * rand_datetime - - The program 'rand_datetime' output a random datetime strings - (with yaer range 0..9999), you can use this for datetime testing. - - You can usage this (example) for table filling. - - Usage: - - ./rand_datetime <randfile> <num> <prefix> <postfix> - - Example: - - ./rand_datetime /dev/urandom 2 "INSERT INTO tab VALUES('" "'::datetime);" - - INSERT INTO tab VALUES('Sat 27 Jul 13:08:57 19618'::datetime); - INSERT INTO tab VALUES('Wed 25 Aug 20:31:50 27450'::datetime); - - * regress - - psql < regress.sql (all answers, must be TRUE, for Posgres - datestyle) - - - --> TO_DATE() is simular as FROM_CHAR(), but convert full datetime - to date ==> needn't test (?). - - - diff --git a/contrib/dateformat/test/rand_datetime.c b/contrib/dateformat/test/rand_datetime.c deleted file mode 100644 index 6a96776b9b..0000000000 --- a/contrib/dateformat/test/rand_datetime.c +++ /dev/null @@ -1,71 +0,0 @@ - -#include <stdio.h> -#include <errno.h> -#include <ctype.h> -#include <stdlib.h> - - -char *month[] = { - "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",NULL -}; - -char *day[] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat", NULL }; - -int num(FILE *f, int min, int max) -{ - int x, y, one; - - one = x = fgetc(f); - - - if (x < min) - x = min; - else if (x > max) { - while(x > max) - x /= 2; - return x; - } - - do { - y = fgetc(f); - if ((x+y) > max) - return x; - x += y; - } while(--one > 0); - - return x; -} - -int main(int argc, char **argv) -{ - FILE *f; - int count; - - if (argc < 5) { - printf("\nUsage: %s <randfile> <num> <prefix> <postfix>\n", argv[0]); - printf("\n(C) Karel Zak - Zakkr 1999\n\n"); - exit(1); - } - - if ((f = fopen(argv[1], "r")) == NULL) { - perror(argv[1]); - exit(1); - } - - count = atoi(argv[2]); - - for(; count > 0; --count) { - fprintf(stdout, "%s%s %02d %s %02d:%02d:%02d %d%s\n", - argv[3], - day[ num(f, 0, 6) ], - num(f, 1, 28), - month[ num(f, 0, 11) ], - num(f, 0, 23), - num(f, 0, 59), - num(f, 0, 59), - num(f, 0, 9999), - argv[4] - ); - } - exit(0); -}
\ No newline at end of file diff --git a/contrib/dateformat/test/regress.sql b/contrib/dateformat/test/regress.sql deleted file mode 100644 index f3c2815fab..0000000000 --- a/contrib/dateformat/test/regress.sql +++ /dev/null @@ -1,58 +0,0 @@ - ---- ---- Postgres DateStyle needs all tests which parsing 'now'::datetime string ---- -SET DATESTYLE TO 'Postgres'; - - -SELECT 'now'::datetime = - TO_CHAR('now'::datetime, 'Dy Mon DD HH24:MI:SS YYYY')::datetime -as "Now vs. to_char"; - - -SELECT 'now'::datetime = - FROM_CHAR('now'::datetime, 'Dy Mon DD HH24:MI:SS YYYY') -as "Now vs. from_char"; - - -SELECT FROM_CHAR('now'::datetime, 'Dy Mon DD HH24:MI:SS YYYY') = - TO_CHAR('now'::datetime, 'Dy Mon DD HH24:MI:SS YYYY')::datetime -as "From_char vs. To_char"; - - -SELECT 'now'::datetime = - FROM_CHAR( - TO_CHAR('now'::datetime, '"Time: "HH24-MI-SS" Date: "Dy DD Mon YYYY'), - '"Time: "HH24-MI-SS" Date: "Dy DD Mon YYYY' - ) -as "High from/to char test"; - - -SELECT TO_CHAR('now'::datetime, 'SSSS')::int = - TO_CHAR('now'::datetime, 'HH24')::int * 3600 + - TO_CHAR('now'::datetime, 'MI')::int * 60 + - TO_CHAR('now'::datetime, 'SS')::int -as "SSSS test"; - - -SELECT TO_CHAR('now'::datetime, 'WW')::int = - (TO_CHAR('now'::datetime, 'DDD')::int - - TO_CHAR('now'::datetime, 'D')::int + 7) / 7 -as "Week test"; - - -SELECT TO_CHAR('now'::datetime, 'Q')::int = - TO_CHAR('now'::datetime, 'MM')::int / 3 + 1 -as "Quartal test"; - - -SELECT TO_CHAR('now'::datetime, 'DDD')::int = - (TO_CHAR('now'::datetime, 'WW')::int * 7) - - (7 - TO_CHAR('now'::datetime, 'D')::int) + - (7 - TO_CHAR(('01-Jan-'|| - TO_CHAR('now'::datetime,'YYYY'))::datetime,'D')::int) - +1 -as "Week and day test"; - - - diff --git a/contrib/dateformat/to-from_char.c b/contrib/dateformat/to-from_char.c deleted file mode 100644 index eebd7a8c64..0000000000 --- a/contrib/dateformat/to-from_char.c +++ /dev/null @@ -1,1382 +0,0 @@ - -/****************************************************************** - * - * The PostgreSQL modul for DateTime formating, inspire with - * Oracle TO_CHAR() / TO_DATE() routines. - * - * Copyright (c) 1999, Karel Zak "Zakkr" <zakkr@zf.jcu.cz> - * - * This file is distributed under the GNU General Public License - * either version 2, or (at your option) any later version. - * - * - * NOTE: - * In this modul is _not_ used POSIX 'struct tm' type, but - * PgSQL type, which has tm_mon based on one (_non_ zero) and - * year not based on 1900, but is used full year number. - * Modul support AC / BC years. - * - ******************************************************************/ - -/* -#define DEBUG_TO_FROM_CHAR -#define DEBUG_elog_output NOTICE -*/ - -#include <stdio.h> -#include <string.h> -#include <ctype.h> -#include <sys/time.h> -#include <unistd.h> - -#include "postgres.h" -#include "utils/builtins.h" - -#include "to-from_char.h" - -#define MAX_NODE_SIZ 16 /* maximal length of one node */ - -#ifdef DEBUG_TO_FROM_CHAR - #define NOTICE_TM {\ - elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\ - tm->tm_sec, tm->tm_year,\ - tm->tm_min, tm->tm_wday, tm->tm_hour, tm->tm_yday,\ - tm->tm_mday, tm->tm_isdst,tm->tm_mon);\ - } -#endif - -/*------ - * (External) defined in PgSQL dt.c (datetime utils) - *------ - */ -extern char *months[], /* month abbreviation */ - *days[]; /* full days */ - -/*------ - * Private definitions - *------ - */ -static struct tm _tm, *tm = &_tm; - -static char *months_full[] = { - "January", "February", "March", "April", "May", "June", "July", - "August", "September", "October", "November", "December", NULL -}; - -/*------ - * AC / DC - *------ - */ -#define YEAR_ABS(_y) (_y < 0 ? -(_y -1) : _y) -#define BC_STR " BC" - -/*------ - * Months in roman-numeral - * (Must be conversely for seq_search (in FROM_CHAR), because - * 'VIII' must be over 'V') - *------ - */ -static char *rm_months[] = { - "XII", "XI", "X", "IX", "VIII", "VII", - "VI", "V", "IV", "III", "II", "I", NULL -}; - -/*------ - * Ordinal postfixes - *------ - */ -static char *numTH[] = { "ST", "ND", "RD", "TH", NULL }; -static char *numth[] = { "st", "nd", "rd", "th", NULL }; - -/*------ - * Flags: - *------ - */ -#define TO_CHAR 1 -#define FROM_CHAR 2 - -#define ONE_UPPER 1 /* Name */ -#define ALL_UPPER 2 /* NAME */ -#define ALL_LOWER 3 /* name */ - -#define FULL_SIZ 0 - -#define MAX_MON_LEN 3 -#define MAX_DY_LEN 3 - -#define TH_UPPER 1 -#define TH_LOWER 2 - -/**************************************************************************** - * Structs for format parsing - ****************************************************************************/ - -/*------ - * Format parser structs - *------ - */ -typedef struct { - char *name; /* suffix string */ - int len, /* suffix length */ - id, /* used in node->suffix */ - type; /* prefix / postfix */ -} KeySuffix; - -typedef struct { - char *name; /* keyword */ - /* action for keyword */ - int len, /* keyword length */ - (*action)(), - id; /* keyword id */ -} KeyWord; - -typedef struct { - int type; /* node type */ - KeyWord *key; /* if node type is KEYWORD */ - int character, /* if node type is CHAR */ - suffix; /* keyword suffix */ -} FormatNode; - -#define NODE_TYPE_END 0 -#define NODE_TYPE_ACTION 1 -#define NODE_TYPE_CHAR 2 -#define NODE_LAST 3 /* internal option */ - -#define SUFFTYPE_PREFIX 1 -#define SUFFTYPE_POSTFIX 2 - - -/***************************************************************************** - * KeyWords definition & action - *****************************************************************************/ - -static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node); -static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node); - -/*------ - * Suffixes: - *------ - */ -#define DCH_S_FM 0x01 -#define DCH_S_TH 0x02 -#define DCH_S_th 0x04 -#define DCH_S_SP 0x08 - -/*------ - * Suffix tests - *------ - */ -#define S_THth(_s) (((_s & DCH_S_TH) || (_s & DCH_S_th)) ? 1 : 0) -#define S_TH(_s) ((_s & DCH_S_TH) ? 1 : 0) -#define S_th(_s) ((_s & DCH_S_th) ? 1 : 0) -#define S_TH_TYPE(_s) ((_s & DCH_S_TH) ? TH_UPPER : TH_LOWER) - -#define S_FM(_s) ((_s & DCH_S_FM) ? 1 : 0) -#define S_SP(_s) ((_s & DCH_S_SP) ? 1 : 0) - -/*------ - * Suffixes definition for TO / FROM CHAR - *------ - */ -static KeySuffix suff[] = { - { "FM", 2, DCH_S_FM, SUFFTYPE_PREFIX }, - { "TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX }, - { "th", 2, DCH_S_th, SUFFTYPE_POSTFIX }, - { "SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX }, - /* last */ - { NULL, 0, 0, 0 } -}; - -/*------ - * - * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted - * complicated -to-> easy: - * - * (example: "DDD","DD","Day","D" ) - * - * (this specific sort needs the algorithm for sequential search for strings, - * which not has exact end; - How keyword is in "HH12blabla" ? - "HH" - * or "HH12"? You must first try "HH12", because "HH" is in string, but - * it is not good:-) - * - * (!) Position for the keyword is simular as position in the enum I_poz (!) - * - * For fast search is used the KWindex[256], in this index is DCH_ enums for - * each ASCII position or -1 if char is not used in the KeyWord. Search example - * for string "MM": - * 1) see KWindex to KWindex[77] ('M'), - * 2) take keywords position from KWindex[77] - * 3) run sequential search in keywords[] from position - * - *------ - */ - -typedef enum { - DCH_CC, - DCH_DAY, - DCH_DDD, - DCH_DD, - DCH_DY, - DCH_Day, - DCH_Dy, - DCH_D, - DCH_HH24, - DCH_HH12, - DCH_HH, - DCH_J, - DCH_MI, - DCH_MM, - DCH_MONTH, - DCH_MON, - DCH_Month, - DCH_Mon, - DCH_Q, - DCH_RM, - DCH_SSSS, - DCH_SS, - DCH_WW, - DCH_W, - DCH_Y_YYY, - DCH_YYYY, - DCH_YYY, - DCH_YY, - DCH_Y, - DCH_day, - DCH_dy, - DCH_month, - DCH_mon, - /* last */ - _DCH_last_ -} I_poz; - -static KeyWord keywords[] = { -/* keyword, len, func. I_poz is in KWindex */ - -{ "CC", 2, dch_date, DCH_CC }, /*C*/ -{ "DAY", 3, dch_date, DCH_DAY }, /*D*/ -{ "DDD", 3, dch_date, DCH_DDD }, -{ "DD", 2, dch_date, DCH_DD }, -{ "DY", 2, dch_date, DCH_DY }, -{ "Day", 3, dch_date, DCH_Day }, -{ "Dy", 2, dch_date, DCH_Dy }, -{ "D", 1, dch_date, DCH_D }, -{ "HH24", 4, dch_time, DCH_HH24 }, /*H*/ -{ "HH12", 4, dch_time, DCH_HH12 }, -{ "HH", 2, dch_time, DCH_HH }, -{ "J", 1, dch_date, DCH_J }, /*J*/ -{ "MI", 2, dch_time, DCH_MI }, -{ "MM", 2, dch_date, DCH_MM }, -{ "MONTH", 5, dch_date, DCH_MONTH }, -{ "MON", 3, dch_date, DCH_MON }, -{ "Month", 5, dch_date, DCH_Month }, -{ "Mon", 3, dch_date, DCH_Mon }, -{ "Q", 1, dch_date, DCH_Q }, /*Q*/ -{ "RM", 2, dch_date, DCH_RM }, /*R*/ -{ "SSSS", 4, dch_time, DCH_SSSS }, /*S*/ -{ "SS", 2, dch_time, DCH_SS }, -{ "WW", 2, dch_date, DCH_WW }, /*W*/ -{ "W", 1, dch_date, DCH_W }, -{ "Y,YYY", 5, dch_date, DCH_Y_YYY }, /*Y*/ -{ "YYYY", 4, dch_date, DCH_YYYY }, -{ "YYY", 3, dch_date, DCH_YYY }, -{ "YY", 2, dch_date, DCH_YY }, -{ "Y", 1, dch_date, DCH_Y }, -{ "day", 3, dch_date, DCH_day }, /*d*/ -{ "dy", 2, dch_date, DCH_dy }, -{ "month", 5, dch_date, DCH_month }, /*m*/ -{ "mon", 3, dch_date, DCH_mon }, -/* last */ -{ NULL, 0, NULL, 0 }}; - - -static int KWindex[256] = { -/* -0 1 2 3 4 5 6 7 8 9 -*/ --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, DCH_CC, DCH_DAY,-1, --1, -1, DCH_HH24,-1, DCH_J, -1, -1, DCH_MI, -1, -1, --1, DCH_Q, DCH_RM, DCH_SSSS,-1, -1, -1, DCH_WW, -1, DCH_Y_YYY, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -DCH_day,-1, -1, -1, -1, -1, -1, -1, -1, DCH_month, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1, -1, -1, -1, -1, --1, -1, -1, -1, -1, -1 -}; - - -/*------ - * Fast sequential search, use index for selection data which - * go to seq. cycle (it is very fast for non-wanted strings) - * (can't be used binary search in format parsing) - *------ - */ -static KeyWord *index_seq_search(char *str, KeyWord *kw, int *index) -{ - int poz; - - if ( (poz = *(index + *str)) > -1) { - - KeyWord *k = kw+poz; - - do { - if (! strncmp(str, k->name, k->len)) - return k; - k++; - if (!k->name) - return (KeyWord *) NULL; - } while(*str == *k->name); - } - return (KeyWord *) NULL; -} - -static KeySuffix *suff_search(char *str, KeySuffix *suf, int type) -{ - KeySuffix *s; - - for(s=suf; s->name != NULL; s++) { - if (s->type != type) - continue; - - if (!strncmp(str, s->name, s->len)) - return s; - } - return (KeySuffix *) NULL; -} - -/*------ - * Format parser, search small keywords and keyword's suffixes, and make - * format-node tree. - *------ - */ -#undef FUNC_NAME -#define FUNC_NAME "parse_format" - -static void parse_format(FormatNode *node, char *str, KeyWord *kw, - KeySuffix *suf, int *index) -{ - KeySuffix *s; - FormatNode *n; - int node_set=0, - suffix, - last=0; - n = node; - - while(*str) { - suffix=0; - - /* prefix */ - if ((s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL) { - suffix |= s->id; - if (s->len) - str += s->len; - } - - /* keyword */ - if (*str && (n->key = index_seq_search(str, kw, index)) != NULL) { - n->type = NODE_TYPE_ACTION; - n->suffix = 0; - node_set= 1; - if (n->key->len) - str += n->key->len; - - /* postfix */ - if (*str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL) { - suffix |= s->id; - if (s->len) - str += s->len; - } - - } else if (*str) { - /* special characters '\' and '"' */ - - if (*str == '"' && last != '\\') { - while(*(++str)) { - if (*str == '"') { - str++; - break; - } - n->type = NODE_TYPE_CHAR; - n->character = *str; - n->key = (KeyWord *) NULL; - n->suffix = 0; - ++n; - } - node_set = 0; - suffix = 0; - last = 0; - - } else if (*str && *str == '\\' && last!='\\' && *(str+1) =='"') { - last = *str; - str++; - - } else if (*str) { - n->type = NODE_TYPE_CHAR; - n->character = *str; - n->key = (KeyWord *) NULL; - node_set = 1; - last = 0; - str++; - } - } - - /* end */ - if (node_set) { - if (n->type == NODE_TYPE_ACTION) - n->suffix = suffix; - ++n; - n->suffix = 0; - node_set = 0; - } - } - - n->type = NODE_TYPE_END; - n->suffix = 0; - return; -} - -/*------ - * Call keyword's function for each of (action) node in format-node tree - *------ - */ -static char *node_action(FormatNode *node, char *inout, int flag) -{ - FormatNode *n; - char *s; - - for(n=node, s=inout; n->type != NODE_TYPE_END; n++, s++) { - if (n->type == NODE_TYPE_ACTION) { - - int len; - - /* - * Call node action function - */ - len = n->key->action(n->key->id, s, n->suffix, flag, n); - if (len > 0) - s += len; - - } else { - - /* - * Remove to output char from input in TO_CHAR - */ - if (flag == TO_CHAR) - *s = n->character; - - else { - /* - * Skip blank space in FROM_CHAR's input - */ - if (isspace(n->character)) { - while(*s != '\0' && isspace(*(s+1))) - ++s; - } - } - } - } - - if (flag == TO_CHAR) - *s = '\0'; - return inout; -} - -/***************************************************************************** - * Private utils - *****************************************************************************/ - -/*------ - * Return ST/ND/RD/TH for simple (1..9) numbers - * type --> 0 upper, 1 lower - *------ - */ -static char *get_th(int num, int type) -{ - switch(num) { - case 1: - if (type==TH_UPPER) return numTH[0]; - return numth[0]; - case 2: - if (type==TH_UPPER) return numTH[1]; - return numth[1]; - case 3: - if (type==TH_UPPER) return numTH[2]; - return numth[2]; - } - if (type==TH_UPPER) return numTH[3]; - return numth[3]; -} - -/*------ - * Convert string-number to ordinal string-number - * type --> 0 upper, 1 lower - *------ - */ -#undef FUNC_NAME -#define FUNC_NAME "str_numth" - -static char *str_numth(char *dest, char *src, int type) -{ - int len = strlen(src), - num=0, f_num=0; - - num = *(src+(len-1)); - if (num < 48 || num > 57) - elog(ERROR, "%s: in '%s' is not number.", FUNC_NAME, src); - - num -= 48; - if (num==1 || num==2) { /* 11 || 12 */ - f_num = atoi(src); - if (abs(f_num)==11 || abs(f_num)==12) - num=0; - } - sprintf(dest, "%s%s", src, get_th(num, type)); - return dest; -} - -/*------ - * Return length of integer writed in string - *------- - */ -static int int4len(int4 num) -{ - char b[16]; - - sprintf(b, "%d", num); - return strlen(b); -} - -/*------ - * Convert string to upper-string - *------ - */ -static char *str_toupper(char *buff) -{ - char *p_buff=buff; - - while (*p_buff) { - *p_buff = toupper((unsigned char) *p_buff); - ++p_buff; - } - return buff; -} - -/*------ - * Check if in string is AC or BC (return: 0==none; -1==BC; 1==AC) - *------ - */ -static int is_acdc(char *str, int *len) -{ - char *p; - - for(p=str; *p != '\0'; p++) { - if (isspace(*p)) - continue; - - if (*(p+1)) { - if (toupper(*p)=='B' && toupper(*(++p))=='C') { - *len += (p - str) +1; - return -1; - } else if (toupper(*p)=='A' && toupper(*(++p))=='C') { - *len += (p - str) +1; - return 1; - } - } - return 0; - } - return 0; -} - - -/*------ - * Sequential search with to upper/lower conversion - *------ - */ -static int seq_search(char *name, char **array, int type, int max, int *len) -{ - char *p, *n, **a; - int last, i; - - *len = 0; - - if (!*name) - return -1; - - /* set first char */ - if (type == ONE_UPPER || ALL_UPPER) - *name = toupper((unsigned char) *name); - else if (type == ALL_LOWER) - *name = tolower((unsigned char) *name); - - for(last=0, a=array; *a != NULL; a++) { - - /* comperate first chars */ - if (*name != **a) - continue; - - for(i=1, p=*a+1, n=name+1; ; n++, p++, i++) { - - /* search fragment (max) only */ - if (max && i == max) { - *len = i; - return a - array; - } - /* full size */ - if (*p=='\0') { - *len = i; - return a - array; - } - /* Not found in array 'a' */ - if (*n=='\0') - break; - - /* - * Convert (but convert new chars only) - */ - if (i > last) { - if (type == ONE_UPPER || type == ALL_LOWER) - *n = tolower((unsigned char) *n); - else if (type == ALL_UPPER) - *n = toupper((unsigned char) *n); - last=i; - } - -#ifdef DEBUG_TO_FROM_CHAR - elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)", *n, *p, *a, name); -#endif - - if (*n != *p) - break; - } - } - - return -1; -} - - -#ifdef DEBUG_TO_FROM_CHAR -/*------- - * Call for debug and for KWindex checking; (Show ASCII char and defined - * keyword for each used position - *------- - */ -static void dump_KWindex() -{ - int i; - - for(i=0; i<255; i++) { - if (KWindex[i] != -1) - elog(NOTICE, "%c: %s, ", i, keywords[ KWindex[i] ].name); - } -} -#endif - -/***************************************************************************** - * Master routines - *****************************************************************************/ - -/* - * Spip TM / th in FROM_CHAR - */ -#define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0) - -/*------ - * Master of TIME for TO_CHAR - write (inout) formated string - * FROM_CHAR - scan (inout) string by course of FormatNode - *------ - */ -#undef FUNC_NAME -#define FUNC_NAME "dch_time" - -static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node) -{ - char *p_inout = inout; - - switch(arg) { - case DCH_HH: - case DCH_HH12: - if (flag == TO_CHAR) { - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, - tm->tm_hour==0 ? 12 : - tm->tm_hour <13 ? tm->tm_hour : tm->tm_hour-12); - if (S_THth(suf)) - str_numth(p_inout, inout, 0); - if (S_FM(suf) || S_THth(suf)) return strlen(p_inout)-1; - else return 1; - } else if (flag == FROM_CHAR) { - if (S_FM(suf)) { - sscanf(inout, "%d", &tm->tm_hour); - return int4len((int4) tm->tm_hour)-1 + SKIP_THth(suf); - } else { - sscanf(inout, "%02d", &tm->tm_hour); - return 1 + SKIP_THth(suf); - } - - } - case DCH_HH24: - if (flag == TO_CHAR) { - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_hour); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_FM(suf) || S_THth(suf)) return strlen(p_inout)-1; - else return 1; - } else if (flag == FROM_CHAR) { - if (S_FM(suf)) { - sscanf(inout, "%d", &tm->tm_hour); - return int4len((int4) tm->tm_hour)-1 + SKIP_THth(suf); - } else { - sscanf(inout, "%02d", &tm->tm_hour); - return 1 + SKIP_THth(suf); - } - } - case DCH_MI: - if (flag == TO_CHAR) { - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_min); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_FM(suf) || S_THth(suf)) return strlen(p_inout)-1; - else return 1; - } else if (flag == FROM_CHAR) { - if (S_FM(suf)) { - sscanf(inout, "%d", &tm->tm_min); - return int4len((int4) tm->tm_min)-1 + SKIP_THth(suf); - } else { - sscanf(inout, "%02d", &tm->tm_min); - return 1 + SKIP_THth(suf); - } - } - case DCH_SS: - if (flag == TO_CHAR) { - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_sec); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_FM(suf) || S_THth(suf)) return strlen(p_inout)-1; - else return 1; - } else if (flag == FROM_CHAR) { - if (S_FM(suf)) { - sscanf(inout, "%d", &tm->tm_sec); - return int4len((int4) tm->tm_sec)-1 + SKIP_THth(suf); - } else { - sscanf(inout, "%02d", &tm->tm_sec); - return 1 + SKIP_THth(suf); - } - } - case DCH_SSSS: - if (flag == TO_CHAR) { - sprintf(inout, "%d", tm->tm_hour * 3600 + - tm->tm_min * 60 + - tm->tm_sec); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - return strlen(p_inout)-1; - } else if (flag == FROM_CHAR) - elog(ERROR, "%s: SSSS is not supported", FUNC_NAME); - } - return 0; -} - -#define CHECK_SEQ_SEARCH(_l, _s) { \ - if (_l <= 0) { \ - elog(ERROR, "%s: bad value for %s", FUNC_NAME, _s); \ - } \ -} - -/*------ - * Master of DATE for TO_CHAR - write (inout) formated string - * FROM_CHAR - scan (inout) string by course of FormatNode - *------ - */ -#undef FUNC_NAME -#define FUNC_NAME "dch_date" - -static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) -{ - char buff[MAX_NODE_SIZ], - *p_inout; - int i, len; - - p_inout = inout; - - /*------ - * In the FROM-char is not difference between "January" or "JANUARY" - * or "january", all is before search convert to one-upper. - * This convention is used for MONTH, MON, DAY, DY - *------ - */ - if (flag == FROM_CHAR) { - if (arg == DCH_MONTH || arg == DCH_Month || arg == DCH_month) { - - tm->tm_mon = seq_search(inout, months_full, ONE_UPPER, FULL_SIZ, &len); - CHECK_SEQ_SEARCH(len, "MONTH/Month/month"); - ++tm->tm_mon; - if (S_FM(suf)) return len-1; - else return 8; - - } else if (arg == DCH_MON || arg == DCH_Mon || arg == DCH_mon) { - - tm->tm_mon = seq_search(inout, months, ONE_UPPER, MAX_MON_LEN, &len); - CHECK_SEQ_SEARCH(len, "MON/Mon/mon"); - ++tm->tm_mon; - return 2; - - } else if (arg == DCH_DAY || arg == DCH_Day || arg == DCH_day) { - - tm->tm_wday = seq_search(inout, days, ONE_UPPER, FULL_SIZ, &len); - CHECK_SEQ_SEARCH(len, "DAY/Day/day"); - if (S_FM(suf)) return len-1; - else return 8; - - } else if (arg == DCH_DY || arg == DCH_Dy || arg == DCH_dy) { - - tm->tm_wday = seq_search(inout, days, ONE_UPPER, MAX_DY_LEN, &len); - CHECK_SEQ_SEARCH(len, "DY/Dy/dy"); - return 2; - - } - } - - switch(arg) { - case DCH_MONTH: - strcpy(inout, months_full[ tm->tm_mon - 1]); - sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout)); - if (S_FM(suf)) return strlen(p_inout)-1; - else return 8; - case DCH_Month: - sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[ tm->tm_mon -1 ]); - if (S_FM(suf)) return strlen(p_inout)-1; - else return 8; - case DCH_month: - sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[ tm->tm_mon -1 ]); - *inout = tolower(*inout); - if (S_FM(suf)) return strlen(p_inout)-1; - else return 8; - case DCH_MON: - strcpy(inout, months[ tm->tm_mon -1 ]); - inout = str_toupper(inout); - return 2; - case DCH_Mon: - strcpy(inout, months[ tm->tm_mon -1 ]); - return 2; - case DCH_mon: - strcpy(inout, months[ tm->tm_mon -1 ]); - *inout = tolower(*inout); - return 2; - case DCH_MM: - if (flag == TO_CHAR) { - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mon ); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_FM(suf) || S_THth(suf)) - return strlen(p_inout)-1; - else return 1; - } else if (flag == FROM_CHAR) { - if (S_FM(suf)) { - sscanf(inout, "%d", &tm->tm_mon); - return int4len((int4) tm->tm_mon)-1 + SKIP_THth(suf); - } else { - sscanf(inout, "%02d", &tm->tm_mon); - return 1 + SKIP_THth(suf); - } - } - case DCH_DAY: - strcpy(inout, days[ tm->tm_wday ]); - sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout)); - if (S_FM(suf)) return strlen(p_inout)-1; - else return 8; - case DCH_Day: - sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[ tm->tm_wday]); - if (S_FM(suf)) return strlen(p_inout)-1; - else return 8; - case DCH_day: - sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[ tm->tm_wday]); - *inout = tolower(*inout); - if (S_FM(suf)) return strlen(p_inout)-1; - else return 8; - case DCH_DY: - strcpy(inout, days[ tm->tm_wday]); - inout = str_toupper(inout); - return 2; - case DCH_Dy: - strcpy(inout, days[ tm->tm_wday]); - return 2; - case DCH_dy: - strcpy(inout, days[ tm->tm_wday]); - *inout = tolower(*inout); - return 2; - case DCH_DDD: - if (flag == TO_CHAR) { - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 3, tm->tm_yday); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_FM(suf) || S_THth(suf)) - return strlen(p_inout)-1; - else return 2; - } else if (flag == FROM_CHAR) { - if (S_FM(suf)) { - sscanf(inout, "%d", &tm->tm_yday); - return int4len((int4) tm->tm_yday)-1 + SKIP_THth(suf); - } else { - sscanf(inout, "%03d", &tm->tm_yday); - return 2 + SKIP_THth(suf); - } - } - case DCH_DD: - if (flag == TO_CHAR) { - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mday); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_FM(suf) || S_THth(suf)) - return strlen(p_inout)-1; - else return 1; - } else if (flag == FROM_CHAR) { - if (S_FM(suf)) { - sscanf(inout, "%d", &tm->tm_mday); - return int4len((int4) tm->tm_mday)-1 + SKIP_THth(suf); - } else { - sscanf(inout, "%02d", &tm->tm_mday); - return 1 + SKIP_THth(suf); - } - } - case DCH_D: - if (flag == TO_CHAR) { - sprintf(inout, "%d", tm->tm_wday+1); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_THth(suf)) - return 2; - return 0; - } else if (flag == FROM_CHAR) { - sscanf(inout, "%1d", &tm->tm_wday); - if(tm->tm_wday) --tm->tm_wday; - return 0 + SKIP_THth(suf); - } - case DCH_WW: - if (flag == TO_CHAR) { - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, - (tm->tm_yday - tm->tm_wday + 7) / 7); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_FM(suf) || S_THth(suf)) - return strlen(p_inout)-1; - else return 1; - } else if (flag == FROM_CHAR) - elog(ERROR, "%s: WW is not supported", FUNC_NAME); - case DCH_Q: - if (flag == TO_CHAR) { - sprintf(inout, "%d", (tm->tm_mon-1)/3+1); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_THth(suf)) - return 2; - return 0; - } else if (flag == FROM_CHAR) - elog(ERROR, "%s: Q is not supported", FUNC_NAME); - case DCH_CC: - if (flag == TO_CHAR) { - i = tm->tm_year/100 +1; - if (i <= 99 && i >= -99) - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, i); - else - sprintf(inout, "%d", i); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - return strlen(p_inout)-1; - } else if (flag == FROM_CHAR) - elog(ERROR, "%s: CC is not supported", FUNC_NAME); - case DCH_Y_YYY: - if (flag == TO_CHAR) { - i= YEAR_ABS(tm->tm_year) / 1000; - sprintf(inout, "%d,%03d", i, YEAR_ABS(tm->tm_year) -(i*1000)); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (tm->tm_year < 0) - strcat(inout, BC_STR); - return strlen(p_inout)-1; - } else if (flag == FROM_CHAR) { - int cc, yy; - sscanf(inout, "%d,%03d", &cc, &yy); - tm->tm_year = (cc * 1000) + yy; - - if (!S_FM(suf) && tm->tm_year <= 9999 && tm->tm_year >= -9999) - len = 5; - else - len = int4len((int4) tm->tm_year)+1; - len += SKIP_THth(suf); - /* AC/BC */ - if (is_acdc(inout+len, &len) < 0 && tm->tm_year > 0) - tm->tm_year = -(tm->tm_year); - if (tm->tm_year < 0) - tm->tm_year = tm->tm_year+1; - return len-1; - } - case DCH_YYYY: - if (flag == TO_CHAR) { - if (tm->tm_year <= 9999 && tm->tm_year >= -9998) - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 4, YEAR_ABS(tm->tm_year)); - else - sprintf(inout, "%d", YEAR_ABS(tm->tm_year)); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (tm->tm_year < 0) - strcat(inout, BC_STR); - return strlen(p_inout)-1; - } else if (flag == FROM_CHAR) { - sscanf(inout, "%d", &tm->tm_year); - if (!S_FM(suf) && tm->tm_year <= 9999 && tm->tm_year >= -9999) - len = 4; - else - len = int4len((int4) tm->tm_year); - len += SKIP_THth(suf); - /* AC/BC */ - if (is_acdc(inout+len, &len) < 0 && tm->tm_year > 0) - tm->tm_year = -(tm->tm_year); - if (tm->tm_year < 0) - tm->tm_year = tm->tm_year+1; - return len-1; - } - case DCH_YYY: - if (flag == TO_CHAR) { - sprintf(buff, "%03d", YEAR_ABS(tm->tm_year)); - i=strlen(buff); - strcpy(inout, buff+(i-3)); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_THth(suf)) return 4; - return 2; - } else if (flag == FROM_CHAR) { - int yy; - sscanf(inout, "%03d", &yy); - tm->tm_year = (tm->tm_year/1000)*1000 +yy; - return 2 + SKIP_THth(suf); - } - case DCH_YY: - if (flag == TO_CHAR) { - sprintf(buff, "%02d", YEAR_ABS(tm->tm_year)); - i=strlen(buff); - strcpy(inout, buff+(i-2)); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_THth(suf)) return 3; - return 1; - } else if (flag == FROM_CHAR) { - int yy; - sscanf(inout, "%02d", &yy); - tm->tm_year = (tm->tm_year/100)*100 +yy; - return 1 + SKIP_THth(suf); - } - case DCH_Y: - if (flag == TO_CHAR) { - sprintf(buff, "%1d", YEAR_ABS(tm->tm_year)); - i=strlen(buff); - strcpy(inout, buff+(i-1)); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_THth(suf)) return 2; - return 0; - } else if (flag == FROM_CHAR) { - int yy; - sscanf(inout, "%1d", &yy); - tm->tm_year = (tm->tm_year/10)*10 +yy; - return 0 + SKIP_THth(suf); - } - case DCH_RM: - if (flag == TO_CHAR) { - sprintf(inout, "%*s", S_FM(suf) ? 0 : -4, - rm_months[ 12 - tm->tm_mon ]); - if (S_FM(suf)) return strlen(p_inout)-1; - else return 3; - } else if (flag == FROM_CHAR) { - tm->tm_mon = 11-seq_search(inout, rm_months, ALL_UPPER, FULL_SIZ, &len); - CHECK_SEQ_SEARCH(len, "RM"); - ++tm->tm_mon; - if (S_FM(suf)) return len-1; - else return 3; - } - case DCH_W: - if (flag == TO_CHAR) { - sprintf(inout, "%d", (tm->tm_mday - tm->tm_wday +7) / 7 ); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - if (S_THth(suf)) return 2; - return 0; - } else if (flag == FROM_CHAR) - elog(ERROR, "%s: W is not supported", FUNC_NAME); - case DCH_J: - if (flag == TO_CHAR) { - sprintf(inout, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); - if (S_THth(suf)) - str_numth(p_inout, inout, S_TH_TYPE(suf)); - return strlen(p_inout)-1; - } else if (flag == FROM_CHAR) - elog(ERROR, "%s: J is not supported", FUNC_NAME); - } - return 0; -} - -/**************************************************************************** - * Public routines - ***************************************************************************/ - - -/********************************************************************* - * - * to_char - * - * Syntax: - * - * text *to_char(DateTime *dt, text *fmt) - * - * Purpose: - * - * Returns string, with date and/or time, formated at - * argument 'fmt' - * - *********************************************************************/ - -#undef FUNC_NAME -#define FUNC_NAME "to_char" - -text -*to_char(DateTime *dt, text *fmt) -{ - FormatNode *tree; - text *result; - char *pars_str; - double fsec; - char *tzn; - int len=0, tz; - - if ((!PointerIsValid(dt)) || (!PointerIsValid(fmt))) - return NULL; - - len = VARSIZE(fmt) - VARHDRSZ; - - if (!len) - return textin(""); - - tm->tm_sec =0; tm->tm_year =0; - tm->tm_min =0; tm->tm_wday =0; - tm->tm_hour =0; tm->tm_yday =0; - tm->tm_mday =1; tm->tm_isdst =0; - tm->tm_mon =1; - - if (DATETIME_IS_EPOCH(*dt)) - { - datetime2tm(SetDateTime(*dt), NULL, tm, &fsec, NULL); - } else if (DATETIME_IS_CURRENT(*dt)) { - datetime2tm(SetDateTime(*dt), &tz, tm, &fsec, &tzn); - } else { - if (datetime2tm(*dt, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "s%: Unable to convert datetime to tm", FUNC_NAME); - } - - /* In dt.c is j2day as static :-((( - tm->tm_wday = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); - must j2day convert itself... - */ - - tm->tm_wday = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1) % 7; - tm->tm_yday = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(tm->tm_year, 1,1) +1; - - tree = (FormatNode *) palloc(len * sizeof(FormatNode) +1); - result = (text *) palloc( len * MAX_NODE_SIZ + VARHDRSZ); - (tree + len)->type = NODE_LAST; - - pars_str = VARDATA(fmt); - pars_str[ len ] = '\0'; - - parse_format( tree, pars_str, keywords, suff, KWindex); - - node_action(tree, VARDATA(result), TO_CHAR); - VARSIZE(result) = strlen(VARDATA(result)) + VARHDRSZ; - - return result; -} - - -/********************************************************************* - * - * from_char - * - * Syntax: - * - * DateTime *from_char(text *date_str, text *fmt) - * - * Purpose: - * - * Make DateTime from date_str which is formated at argument 'fmt' - * ( from_char is reverse to_char() ) - * - *********************************************************************/ - -#undef FUNC_NAME -#define FUNC_NAME "from_char" - -DateTime -*from_char(text *date_str, text *fmt) -{ - FormatNode *tree; - DateTime *result; - char *pars_str; - int len=0, - fsec=0, - tz=0; - - if ((!PointerIsValid(date_str)) || (!PointerIsValid(fmt))) - return NULL; - - tm->tm_sec =0; tm->tm_year =0; - tm->tm_min =0; tm->tm_wday =0; - tm->tm_hour =0; tm->tm_yday =0; - tm->tm_mday =1; tm->tm_isdst =0; - tm->tm_mon =1; - - result = palloc(sizeof(DateTime)); - - len = VARSIZE(fmt) - VARHDRSZ; - - if (len) { - tree = (FormatNode *) palloc((len+1) * sizeof(FormatNode)); - (tree + len)->type = NODE_LAST; - - pars_str = VARDATA(fmt); - pars_str[ len ] = '\0'; - parse_format( tree, pars_str, keywords, suff, KWindex); - VARDATA(date_str)[ VARSIZE(date_str) - VARHDRSZ ] = '\0'; - node_action(tree, VARDATA(date_str), FROM_CHAR); - } - -#ifdef DEBUG_TO_FROM_CHAR - NOTICE_TM; -#endif - if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) { - -#ifdef USE_POSIX_TIME - tm->tm_isdst = -1; - tm->tm_year -= 1900; - tm->tm_mon -= 1; - -#ifdef DEBUG_TO_FROM_CHAR - elog(NOTICE, "TO-FROM_CHAR: Call mktime()"); - NOTICE_TM; -#endif - mktime(tm); - tm->tm_year += 1900; - tm->tm_mon += 1; - -#if defined(HAVE_TM_ZONE) - tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ -#elif defined(HAVE_INT_TIMEZONE) - -#ifdef __CYGWIN__ - tz = (tm->tm_isdst ? (_timezone - 3600) : _timezone); -#else - tz = (tm->tm_isdst ? (timezone - 3600) : timezone); -#endif - -#else -#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined -#endif - -#else /* !USE_POSIX_TIME */ - tz = CTimeZone; -#endif - } else { - tm->tm_isdst = 0; - tz = 0; - } -#ifdef DEBUG_TO_FROM_CHAR - NOTICE_TM; -#endif - if (tm2datetime(tm, fsec, &tz, result) != 0) - elog(ERROR, "%s: can't convert 'tm' to datetime.", FUNC_NAME); - - return result; -} - -/********************************************************************* - * - * to_date - * - * Syntax: - * - * DateADT *to_date(text *date_str, text *fmt) - * - * Purpose: - * - * Make Date from date_str which is formated at argument 'fmt' - * - *********************************************************************/ - -#undef FUNC_NAME -#define FUNC_NAME "to_date" - -DateADT -to_date(text *date_str, text *fmt) -{ - return datetime_date( from_char(date_str, fmt) ); -} - - -/******************************************************************** - * - * ordinal - * - * Syntax: - * - * text *ordinal(int4 num, text type) - * - * Purpose: - * - * Add to number 'th' suffix and return this as text. - * - ********************************************************************/ - -#undef FUNC_NAME -#define FUNC_NAME "ordinal" - -text -*ordinal(int4 num, text *typ) -{ - text *result; - int ttt=0; - - if (!PointerIsValid(typ)) - return NULL; - - VARDATA(typ)[ VARSIZE(typ) - VARHDRSZ ] = '\0'; - - if (!strcmp("TH", VARDATA(typ))) - ttt = TH_UPPER; - else if (!strcmp("th", VARDATA(typ))) - ttt = TH_LOWER; - else - elog(ERROR, "%s: bad type '%s' (allowed: 'TH' or 'th')", - FUNC_NAME, VARDATA(typ)); - - result = (text *) palloc(16); /* ! not int8 ! */ - - sprintf(VARDATA(result), "%d", (int) num); - str_numth(VARDATA(result), VARDATA(result), ttt); - - VARSIZE(result) = strlen(VARDATA(result)) + VARHDRSZ; - - return result; -} diff --git a/contrib/dateformat/to-from_char.doc b/contrib/dateformat/to-from_char.doc deleted file mode 100644 index 564de88189..0000000000 --- a/contrib/dateformat/to-from_char.doc +++ /dev/null @@ -1,183 +0,0 @@ - - - TO_CHAR(datetime, text) - ----------------------- - (returns text) - - TO_CHAR - the DateTime function for formating date and time outputs. - This routine is inspire with the Oracle to_char(). - - SELECT TO_CHAR('now'::datetime, 'HH:MI:SS YYYY'); - ------------- - 11:57:11 1999 - - - FROM_CHAR(text, text) - --------------------- - (returns DateTime) - - FROM_CHAR - the PostgreSQL extension routine which read non-datetime - string and convert it to DateTime. This func. is inspire with the - Oracle to_date() routine, but in Oracle this func. return date only - and not support all keywords (format pictures). - - SELECT FROM_CHAR('11:57:11 1999', 'HH:MI:SS YYYY'); - ---------------------------- - Fri 01 Jan 11:57:11 1999 CET - - - TO_DATE(text, text) - ------------------- - (returns Date) - - TO_DATE - the Date function which read non-datetime (non-date) string - and convert it to date. All for thos func. is just as from_char(). - This func. is inspire with the Oracle to_date() routine. - - SELECT TO_DATE('11:57:11 1999', 'HH:MI:SS YYYY'); - ---------- - 01-01-1999 - - - - ---------------------------------- - String format-KeyWords and options: - ---------------------------------- - - * TO_CHAR (..., 'format picture') - * FROM_CHAR (..., 'format picture') - * TO_DATE (..., 'format picture') - - (Note: In Oracle manual is format-keyword called 'format pictures'.) - - All keywords has suffixes (prefix or postfix), example for 2 hours: - keyword: HH (hour) 'HH' --> '02' - prefix: FM (fill mode) 'FMHH' --> '2' - postfix: TH (ordinal number) 'HHth' --> '02nd' - 'FMHHth' --> '2nd' - - Suffixes: - -------- - FM - fill mode - 02 --> FMHH --> 2 - January , --> FMMonth --> January, - - TH - upper ordinal number - 02 --> HHTH --> 02ND - - th - lower ordinal number - 02 --> HHth --> 02th - - - KeyWords (format pictures): - -------------------------- - - HH - hour of day (01-12) - HH12 - -- // -- - HH24 - hour (00-24) - MI - minute (00-59) - SS - socond (00-59) - SSSS - seconds past midnight (0-86399) - Y,YYY - year with comma (full PgSQL datetime range) digits) - YYYY - year (4 and more (full PgSQL datetime range) digits) - YYY - last 3 digits of year - YY - last 2 digits of year - Y - last digit of year - MONTH - full month name (upper) (9-letters) - Month - full month name - first character is upper (9-letters) - month - full month name - all characters is upper (9-letters) - MON - abbreviated month name (3-letters) - Mon - abbreviated month name (3-letters) - first character is upper - mon - abbreviated month name (3-letters) - all characters is upper - MM - month (01-12) - DAY - full day name (upper) (9-letters) - Day - full day name - first character is upper (9-letters) - day - full day name - all characters is upper (9-letters) - DY - abbreviated day name (3-letters) (upper) - Dy - abbreviated day name (3-letters) - first character is upper - Dy - abbreviated day name (3-letters) - all character is upper - DDD - day of year (001-366) - DD - day of month (01-31) - D - day of week (1-7; SUN=1) - WW - week number of year - CC - century (2-digits) - Q - quarter - RM - roman numeral month (I=JAN; I-XII) - W - week of month - J - julian day (days since January 1, 4712 BC) - - - AC / BC: - ------- - - TO-FROM CHAR routines support BC and AC postfix for years. - You can combine BC and AC with TH. - - OTHER: - ----- - '\' - must be use as double \\ - - '\\HH\\MI\\SS' --> 11\45\56 - - '"' - string berween a quotation marks is skipen and not - is parsed. If you wand write '"' to output you must - use \\" - - '"Month: "Month' --> Month: November - '\\"YYYY Month\\"' --> "1999 November " - - text - the PostgreSQL TO-FROM CHAR support text without '"', - but " text " is fastly and you have guarantee, - that this text not will interprete as keyword. - - WARNING: - ------- - - You DON'T OMIT differention between fill mode (FM prefix) - and standard input in FROM_CHAR (TO_DATE), because this - routines can't scan your input string and conver it to - Datetime. See: - - WRONG: FROM_CHAR('August 1999', 'Month YYYY'); - - RIGHT: FROM_CHAR('August 1999', 'Month YYYY'); - or FROM_CHAR('August 1999', 'FMMonth YYYY'); - - (! Month is 9-letters string if you not set fill-mode !) - - ---------------------------- -TODO / Now is not supported: ---------------------------- - - - spelled-out SP suffix ( 22 --> Twenty-two ) - - AM/PM - - - not supported number to character converting - - TO_CHAR(number, 'format') - - - -------------------------------------------------------------------------------- -- secondary products :-) ------------------------------------------------------ -------------------------------------------------------------------------------- - - -ORDINAL(int4, text) -------------------- - - * Translate number to ordinal number and return this as text - - -* Examples: - -template1=> select ordinal(21212, 'TH'); -ordinal -------- -21212ND - -template1=> select ordinal(21212, 'th'); -ordinal -------- -21212nd diff --git a/contrib/dateformat/to-from_char.h b/contrib/dateformat/to-from_char.h deleted file mode 100644 index e96e0a3797..0000000000 --- a/contrib/dateformat/to-from_char.h +++ /dev/null @@ -1,18 +0,0 @@ - -#ifndef TO_FROM_CHAR_H -#define TO_FROM_CHAR_H - -/*------ - * For postgres - *------ - */ -extern text *to_char(DateTime *dt, text *format); -extern DateTime *from_char(text *date_str, text *format); -extern DateADT to_date(text *date_str, text *format); - -extern text *ordinal(int4 num, text *type); - -extern char *months_full[]; /* full months name */ -extern char *rm_months[]; /* roman numeral of months */ - -#endif
\ No newline at end of file diff --git a/contrib/dateformat/to-from_char.sql.in b/contrib/dateformat/to-from_char.sql.in deleted file mode 100644 index 102a24ff46..0000000000 --- a/contrib/dateformat/to-from_char.sql.in +++ /dev/null @@ -1,29 +0,0 @@ --- to-from_char.sql datetime routines -- --- --- Copyright (c) 1999, Karel Zak "Zakkr" <zakkr@zf.jcu.cz> --- --- This file is distributed under the GNU General Public License --- either version 2, or (at your option) any later version. - - --- Define the new functions --- - -create function to_char(datetime, text) returns text - as 'MODULE_PATHNAME' - language 'c'; - -create function from_char(text, text) returns datetime - as 'MODULE_PATHNAME' - language 'c'; - -create function to_date(text, text) returns date - as 'MODULE_PATHNAME' - language 'c'; - -create function ordinal(int, text) returns text - as 'MODULE_PATHNAME' - language 'c'; - - --- end of file
\ No newline at end of file diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 2bfec9272e..55cadab445 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -420,6 +420,547 @@ </para> </sect1> + + <sect1> + + <title id="formatting-funcs"> Formatting Functions </title> + + <note> + <title>Author</title> + <para> + Written by + <ulink url="mailto:zakkr@zf.jcu.cz">Karel Zak</ulink> + on 2000-01-24. + </para> + </note> + + <para> + Formatting functions provide a powerful set of tools for converting + various datetypes (date/time, int, float, numeric) to formatted strings + and reverse convert from formatted strings to original datetypes. + </para> + + <para> + <table tocentry="1"> + <title>Formatting Functions</title> + <tgroup cols="4"> + <thead> + <row> + <entry>Function</entry> + <entry>Returns</entry> + <entry>Description</entry> + <entry>Example</entry> + </row> + </thead> + <tbody> + <row> + <entry> to_char(datetime, text) </entry> + <entry> text </entry> + <entry> convert datetime to string </entry> + <entry> to_char('now'::datetime, 'HH12:MI:SS') </entry> + </row> + <row> + <entry> to_char(timestamp, text) </entry> + <entry> text </entry> + <entry> convert timestamp to string </entry> + <entry> to_char( now(), 'HH12:MI:SS') </entry> + </row> + <row> + <entry> to_char(int, text) </entry> + <entry> text </entry> + <entry> convert int4/int8 to string </entry> + <entry> to_char(125, '999') </entry> + </row> + <row> + <entry> to_char(float, text) </entry> + <entry> text </entry> + <entry> convert float4/float8 to string </entry> + <entry> to_char(125.8, '999D9') </entry> + </row> + <row> + <entry> to_char(numeric, text) </entry> + <entry> text </entry> + <entry> convert numeric to string </entry> + <entry> to_char(-125.8, '999D99S') </entry> + </row> + <row> + <entry> to_datetime(text, text) </entry> + <entry> datetime </entry> + <entry> convert string to datetime </entry> + <entry> to_datetime('05 Dec 2000 13', 'DD Mon YYYY HH') </entry> + </row> + <row> + <entry> to_date(text, text) </entry> + <entry> date </entry> + <entry> convert string to date </entry> + <entry> to_date('05 Dec 2000', 'DD Mon YYYY') </entry> + </row> + <row> + <entry> to_timestamp(text, text) </entry> + <entry> date </entry> + <entry> convert string to timestamp </entry> + <entry> to_timestamp('05 Dec 2000', 'DD Mon YYYY') </entry> + </row> + <row> + <entry> to_number(text, text) </entry> + <entry> numeric </entry> + <entry> convert string to numeric </entry> + <entry> to_number('12,454.8-', '99G999D9S') </entry> + </row> + </tbody> + </tgroup> + </table> + </para> + + <para> + For all formatting functions is second argument format-picture. + </para> + + <para> + <table tocentry="1"> + <title>Format-pictures for datetime to_char() version.</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Format-picture</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry> HH </entry> + <entry> hour of day (01-12) </entry> + </row> + <row> + <entry> HH12 </entry> + <entry> hour of day (01-12) </entry> + </row> + <row> + <entry> MI </entry> + <entry> minute (00-59) </entry> + </row> + <row> + <entry> SS </entry> + <entry> socond (00-59) </entry> + </row> + <row> + <entry> SSSS </entry> + <entry> seconds past midnight (0-86399) </entry> + </row> + <row> + <entry> Y,YYY </entry> + <entry> year (4 and more digits) with comma </entry> + </row> + <row> + <entry> YYYY </entry> + <entry> year (4 and more digits) </entry> + </row> + <row> + <entry> YYY </entry> + <entry> last 3 digits of year </entry> + </row> + <row> + <entry> YY </entry> + <entry> last 2 digits of year </entry> + </row> + <row> + <entry> Y </entry> + <entry> last digit of year </entry> + </row> + <row> + <entry> MONTH </entry> + <entry> full month name (9-letters) - all characters is upper </entry> + </row> + <row> + <entry> Month </entry> + <entry> full month name (9-letters) - first character is upper </entry> + </row> + <row> + <entry> month </entry> + <entry> full month name (9-letters) - all characters is lower </entry> + </row> + <row> + <entry> MON </entry> + <entry> abbreviated month name (3-letters) - all characters is upper </entry> + </row> + <row> + <entry> Mon </entry> + <entry> abbreviated month name (3-letters) - first character is upper </entry> + </row> + <row> + <entry> mon </entry> + <entry> abbreviated month name (3-letters) - all characters is lower </entry> + </row> + <row> + <entry> MM </entry> + <entry> month (01-12) </entry> + </row> + <row> + <entry> DAY </entry> + <entry> full day name (9-letters) - all characters is upper </entry> + </row> + <row> + <entry> Day </entry> + <entry> full day name (9-letters) - first character is upper </entry> + </row> + <row> + <entry> day </entry> + <entry> full day name (9-letters) - all characters is lower </entry> + </row> + <row> + <entry> DY </entry> + <entry> abbreviated day name (3-letters) - all characters is upper </entry> + </row> + <row> + <entry> Dy </entry> + <entry> abbreviated day name (3-letters) - first character is upper </entry> + </row> + <row> + <entry> dy </entry> + <entry> abbreviated day name (3-letters) - all characters is upper </entry> + </row> + <row> + <entry> DDD </entry> + <entry> day of year (001-366) </entry> + </row> + <row> + <entry> DD </entry> + <entry> day of month (01-31) </entry> + </row> + <row> + <entry> D </entry> + <entry> day of week (1-7; SUN=1) </entry> + </row> + <row> + <entry> W </entry> + <entry> week of month </entry> + </row> + <row> + <entry> WW </entry> + <entry> week number of year </entry> + </row> + <row> + <entry> CC </entry> + <entry> century (2-digits) </entry> + </row> + <row> + <entry> J </entry> + <entry> julian day (days since January 1, 4712 BC) </entry> + </row> + <row> + <entry> Q </entry> + <entry> quarter </entry> + </row> + <row> + <entry> RM </entry> + <entry> month in roman numeral (I-XII; I=JAN) </entry> + </row> + </tbody> + </tgroup> + </table> + </para> + + <para> + All format-pictures allow use suffixes (postfix / prefix). The suffix is + always valid for near format-picture. The 'FX' is global prefix only. + </para> + + <para> + <table tocentry="1"> + <title>Suffixes for format-pictures for datetime to_char() version.</title> + <tgroup cols="3"> + <thead> + <row> + <entry>Suffix</entry> + <entry>Description</entry> + <entry>Example</entry> + </row> + </thead> + <tbody> + <row> + <entry> FM </entry> + <entry> fill mode - prefix </entry> + <entry> FMMonth </entry> + </row> + <row> + <entry> TH </entry> + <entry> upper ordinal number - postfix </entry> + <entry> DDTH </entry> + </row> + <row> + <entry> th </entry> + <entry> lower ordinal number - postfix </entry> + <entry> DDTH </entry> + </row> + <row> + <entry> FX </entry> + <entry> FX - (Fixed format) global format-picture switch. + the TO_DATETIME / TO_DATA skip blank space if this option is + not use. Must by used as first item in formt-picture. </entry> + <entry> FX Month DD Day </entry> + </row> + <row> + <entry> SP </entry> + <entry> spell mode (not implement now)</entry> + <entry> DDSP </entry> + </row> + </tbody> + </tgroup> + </table> + </para> + + <para> + '\' - must be use as double \\, example '\\HH\\MI\\SS' + </para> + <para> + '"' - string between a quotation marks is skipen and not is parsed. + If you want write '"' to output you must use \\", exapmle '\\"YYYY Month\\"'. + </para> + <para> + text - the PostgreSQL's to_char() support text without '"', but string + between a quotation marks is fastly and you have guarantee, that a text + not will interpreted as a keyword (format-picture), exapmle '"Hello Year: "YYYY'. + </para> + + <para> + <table tocentry="1"> + <title>Format-pictures for number (int/float/numeric) to_char() version.</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Format-picture</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry> 9 </entry> + <entry> return value with the specified number of digits, and if digit is + not available use blank space </entry> + </row> + <row> + <entry> 0 </entry> + <entry> as 9, but instead blank space use zero </entry> + </row> + <row> + <entry> . (period) </entry> + <entry> decimal point </entry> + </row> + <row> + <entry> , (comma) </entry> + <entry> group (thousand) separator </entry> + </row> + <row> + <entry> PR </entry> + <entry> return negative value in angle brackets </entry> + </row> + <row> + <entry> S </entry> + <entry> return negatice value with minus sign (use locales) </entry> + </row> + <row> + <entry> L </entry> + <entry> currency symbol (use locales) </entry> + </row> + <row> + <entry> D </entry> + <entry> decimal point (use locales) </entry> + </row> + <row> + <entry> G </entry> + <entry> group separator (use locales) </entry> + </row> + <row> + <entry> MI </entry> + <entry> return minus sign on specified position (if number < 0) </entry> + </row> + <row> + <entry> PL </entry> + <entry> return plus sign on specified position (if number > 0) </entry> + </row> + <row> + <entry> RN </entry> + <entry> return number as roman number (number must be between 1 and 3999) </entry> + </row> + <row> + <entry> TH or th </entry> + <entry> convert number to ordinal number (not convert numbers under zero and decimal numbers) </entry> + </row> + <row> + <entry> V </entry> + <entry> arg1 * (10 ^ n); - return a value multiplied by 10^n (where 'n' is number of '9's after the 'V'). + The to_char() not support use 'V' and decimal poin together, example "99.9V99". </entry> + </row> + <row> + <entry> EEEE </entry> + <entry> science numbers. Now not supported. </entry> + </row> + </tbody> + </tgroup> + </table> + </para> + + <para> + The PostgreSQL to_char() not support absurd to_char(0.1, '99.99') + --> <ProgramListing> ' .10' </ProgramListing> format. + </para> + + <para> + <table tocentry="1"> + <title> The to_char() examples. </title> + <tgroup cols="2"> + <thead> + <row> + <entry>Input</entry> + <entry>Output</entry> + </row> + </thead> + <tbody> + <row> + <entry> to_char(now(), 'Day, HH12:MI:SS') </entry> + <entry><ProgramListing> 'Tuesday , 05:39:18' </ProgramListing></entry> + </row> + <row> + <entry> to_char(now(), 'FMDay, HH12:MI:SS') </entry> + <entry><ProgramListing> 'Tuesday, 05:39:18' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 0.1, '99.99') </entry> + <entry><ProgramListing> ' 0.10' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 0.1, '0.9') </entry> + <entry><ProgramListing> ' 0.1' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 0.1, '090.9') </entry> + <entry><ProgramListing> ' 000.1' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, '999') </entry> + <entry><ProgramListing> ' 485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( -485, '999') </entry> + <entry><ProgramListing> '-485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, '09999') </entry> + <entry><ProgramListing> ' 00485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, 'FM09999') </entry> + <entry><ProgramListing> '00485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, 'FM999') </entry> + <entry><ProgramListing> '485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, '9 9 9') </entry> + <entry><ProgramListing> ' 4 8 5' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 1485, '9,999') </entry> + <entry><ProgramListing> ' 1,485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 1485, '9G999') </entry> + <entry><ProgramListing> ' 1 485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 148.5, '999.999') </entry> + <entry><ProgramListing> ' 148.500' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 148.5, '999D999') </entry> + <entry><ProgramListing> ' 148,500' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 3148.5,'9G999D999') </entry> + <entry><ProgramListing> ' 3 148,500' </ProgramListing></entry> + </row> + <row> + <entry> to_char( -485, '999S') </entry> + <entry><ProgramListing> '485-' </ProgramListing></entry> + </row> + <row> + <entry> to_char( -485, '999MI') </entry> + <entry><ProgramListing> '485-' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, '999MI') </entry> + <entry><ProgramListing> '485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, 'PL999') </entry> + <entry><ProgramListing> '+485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, 'SG999') </entry> + <entry><ProgramListing> '+485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( -485, 'SG999') </entry> + <entry><ProgramListing> '-485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( -485, '9SG99') </entry> + <entry><ProgramListing> '4-85' </ProgramListing></entry> + </row> + <row> + <entry> to_char( -485, '999PR') </entry> + <entry><ProgramListing> '<485>' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, 'L999') </entry> + <entry><ProgramListing> 'DM 485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, 'RN') </entry> + <entry><ProgramListing> ' CDLXXXV' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, 'FMRN') </entry> + <entry><ProgramListing> 'CDLXXXV' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 5.2, 'FMRN') </entry> + <entry><ProgramListing> 'V' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 482, '999th') </entry> + <entry><ProgramListing> ' 482nd' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485, '"Good number:"999') </entry> + <entry><ProgramListing> 'Good number: 485' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 485.8, '"Pre-decimal:"999" Post-decimal:" .999') </entry> + <entry><ProgramListing> 'Pre-decimal: 485 Post-decimal: .800' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 12, '99V999') </entry> + <entry><ProgramListing> ' 12000' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 12.4, '99V999') </entry> + <entry><ProgramListing> ' 12400' </ProgramListing></entry> + </row> + <row> + <entry> to_char( 12.45, '99V9') </entry> + <entry><ProgramListing> ' 125' </ProgramListing></entry> + </row> + </tbody> + </tgroup> + </table> + </para> + + </sect1> + + + <sect1> <title>Geometric Functions</title> diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index ce132c719b..192928db09 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -4,7 +4,7 @@ # Makefile for utils/adt # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.32 2000/01/19 02:58:56 petere Exp $ +# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.33 2000/01/25 23:53:51 momjian Exp $ # #------------------------------------------------------------------------- @@ -31,7 +31,7 @@ OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o chunk.o \ regexp.o regproc.o ruleutils.o selfuncs.o sets.o \ tid.o timestamp.o varchar.o varlena.o version.o \ network.o mac.o inet_net_ntop.o inet_net_pton.o \ - ri_triggers.o pg_lzcompress.o pg_locale.o + ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o all: SUBSYS.o diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c new file mode 100644 index 0000000000..ca03b13375 --- /dev/null +++ b/src/backend/utils/adt/formatting.c @@ -0,0 +1,3157 @@ + +/* ----------------------------------------------------------------------- + * formatting.c + * + * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.1 2000/01/25 23:53:51 momjian Exp $ + * + * TO_CHAR(); TO_DATETIME(); TO_DATE(); TO_NUMBER(); + * + * The PostgreSQL routines for a DateTime/int/float/numeric formatting, + * inspire with Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines. + * + * 1999 Karel Zak "Zakkr" + * + * + * Cache & Memory: + * Routines use (itself) internal cache for format pictures. If + * new format arg is same as a last format string, routines not + * call the format-parser. + * + * The cache use static buffer and is persistent across transactions. If + * format-picture is bigger than cache buffer, parser is called always. + * + * NOTE for Number version: + * All in this version is implemented as keywords ( => not used + * suffixes), because a format picture is for *one* item (number) + * only. It not is as a datetime version, where each keyword (can) + * has suffix. + * + * NOTE for DateTime version: + * In this modul is *not* used POSIX 'struct tm' type, but + * PgSQL type, which has tm_mon based on one (*non* zero) and + * year *not* based on 1900, but is used full year number. + * Modul support AC / BC years. + * + * Supported types for to_char(): + * + * DateTime, Numeric, int4, int8, float4, float8 + * + * Supported types for reverse conversion: + * + * Datetime - to_datetime() + * Date - to_date() + * Numeric - to_number() + * + * ----------------------------------------------------------------------- + */ + +/* ---------- + * UnComment me for DEBUG + * ---------- + */ +/*** +#define DEBUG_TO_FROM_CHAR +#define DEBUG_elog_output NOTICE +***/ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <sys/time.h> +#include <unistd.h> +#include <locale.h> +#include <math.h> + +#include "postgres.h" +#include "utils/builtins.h" +#include "utils/pg_locale.h" +#include "utils/formatting.h" + +/* ---------- + * Routines type + * ---------- + */ +#define DCH_TYPE 1 /* DATE-TIME version */ +#define NUM_TYPE 2 /* NUMBER version */ + +/* ---------- + * KeyWord Index (ascii from position 32 (' ') to 126 (~)) + * ---------- + */ +#define KeyWord_INDEX_SIZE ('~' - ' ' + 1) +#define KeyWord_INDEX_FILTER(_c) ((_c) < ' ' || (_c) > '~' ? 0 : 1) + +/* ---------- + * Maximal length of one node + * ---------- + */ +#define DCH_MAX_ITEM_SIZ 9 /* some month name ? */ +#define NUM_MAX_ITEM_SIZ 16 /* roman number */ + +/* ---------- + * Format picture cache limits + * ---------- + */ +#define NUM_CACHE_SIZE 64 +#define DCH_CACHE_SIZE 128 + +/* ---------- + * More in float.c + * ---------- + */ +#define MAXFLOATWIDTH 64 +#define MAXDOUBLEWIDTH 128 + +/* ---------- + * External (defined in PgSQL dt.c (datetime utils)) + * ---------- + */ +extern char *months[], /* month abbreviation */ + *days[]; /* full days */ + +/* ---------- + * Format parser structs + * ---------- + */ +typedef struct { + char *name; /* suffix string */ + int len, /* suffix length */ + id, /* used in node->suffix */ + type; /* prefix / postfix */ +} KeySuffix; + +typedef struct { + char *name; /* keyword */ + /* action for keyword */ + int len, /* keyword length */ + (*action)(), + id; /* keyword id */ +} KeyWord; + +typedef struct { + int type; /* node type */ + KeyWord *key; /* if node type is KEYWORD */ + int character, /* if node type is CHAR */ + suffix; /* keyword suffix */ +} FormatNode; + +#define NODE_TYPE_END 1 +#define NODE_TYPE_ACTION 2 +#define NODE_TYPE_CHAR 3 + +#define SUFFTYPE_PREFIX 1 +#define SUFFTYPE_POSTFIX 2 + + +/* ---------- + * Full months + * ---------- + */ +static char *months_full[] = { + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December", NULL +}; + +/* ---------- + * AC / DC + * ---------- + */ +#define YEAR_ABS(_y) (_y < 0 ? -(_y -1) : _y) +#define BC_STR " BC" + +/* ---------- + * Months in roman-numeral + * (Must be conversely for seq_search (in FROM_CHAR), because + * 'VIII' must be over 'V') + * ---------- + */ +static char *rm_months[] = { + "XII", "XI", "X", "IX", "VIII", "VII", + "VI", "V", "IV", "III", "II", "I", NULL +}; + +/* ---------- + * Roman numbers + * ---------- + */ +static char *rm1[] = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL }; +static char *rm10[] = { "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL }; +static char *rm100[] = { "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL }; + +/* ---------- + * Ordinal postfixes + * ---------- + */ +static char *numTH[] = { "ST", "ND", "RD", "TH", NULL }; +static char *numth[] = { "st", "nd", "rd", "th", NULL }; + +/* ---------- + * Flags & Options: + * ---------- + */ +#define TO_CHAR 1 +#define FROM_CHAR 2 + +#define ONE_UPPER 1 /* Name */ +#define ALL_UPPER 2 /* NAME */ +#define ALL_LOWER 3 /* name */ + +#define FULL_SIZ 0 + +#define MAX_MON_LEN 3 +#define MAX_DY_LEN 3 + +#define TH_UPPER 1 +#define TH_LOWER 2 + + +#ifdef DEBUG_TO_FROM_CHAR + #define NOTICE_TM {\ + elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\ + tm->tm_sec, tm->tm_year,\ + tm->tm_min, tm->tm_wday, tm->tm_hour, tm->tm_yday,\ + tm->tm_mday, tm->tm_isdst,tm->tm_mon);\ + } +#endif + +/* ---------- + * Flags for DCH version + * ---------- + */ +static int DCH_global_flag = 0; + +#define DCH_F_FX 0x01 + +#define IS_FX (DCH_global_flag & DCH_F_FX) + + +/* ---------- + * Number description struct + * ---------- + */ +typedef struct { + int pre, /* (count) numbers before decimal */ + pos, /* (count) numbers after decimal */ + lsign, /* want locales sign */ + flag, /* number parametrs */ + pre_lsign_num, /* tmp value for lsign */ + multi, /* multiplier for 'V' */ + need_locale; /* needs it locale */ +} NUMDesc; + +/* ---------- + * Flags for NUMBER version + * ---------- + */ +#define NUM_F_DECIMAL 0x01 +#define NUM_F_LDECIMAL 0x02 +#define NUM_F_ZERO 0x04 +#define NUM_F_BLANK 0x08 +#define NUM_F_FILLMODE 0x10 +#define NUM_F_LSIGN 0x20 +#define NUM_F_BRACKET 0x40 +#define NUM_F_MINUS 0x80 +#define NUM_F_PLUS 0x100 +#define NUM_F_ROMAN 0x200 +#define NUM_F_MULTI 0x400 + +#define NUM_LSIGN_PRE -1 +#define NUM_LSIGN_POST 1 +#define NUM_LSIGN_NONE 0 + +/* ---------- + * Tests + * ---------- + */ +#define IS_DECIMAL(_f) ((_f)->flag & NUM_F_DECIMAL) +#define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL) +#define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO) +#define IS_BLANK(_f) ((_f)->flag & NUM_F_BLANK) +#define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE) +#define IS_BRACKET(_f) ((_f)->flag & NUM_F_BRACKET) +#define IS_MINUS(_f) ((_f)->flag & NUM_F_MINUS) +#define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS) +#define IS_ROMAN(_f) ((_f)->flag & NUM_F_ROMAN) +#define IS_MULTI(_f) ((_f)->flag & NUM_F_MULTI) + +/* ---------- + * Private global-modul definitions + * ---------- + */ +static struct tm _tm, *tm = &_tm; + +/* ---------- + * Utils + * ---------- + */ +#ifndef MIN +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) +#endif + +/***************************************************************************** + * KeyWords definition & action + *****************************************************************************/ + +static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node); +static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node); +static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node); + +/* ---------- + * Suffixes: + * ---------- + */ +#define DCH_S_FM 0x01 +#define DCH_S_TH 0x02 +#define DCH_S_th 0x04 +#define DCH_S_SP 0x08 + +/* ---------- + * Suffix tests + * ---------- + */ +#define S_THth(_s) (((_s & DCH_S_TH) || (_s & DCH_S_th)) ? 1 : 0) +#define S_TH(_s) ((_s & DCH_S_TH) ? 1 : 0) +#define S_th(_s) ((_s & DCH_S_th) ? 1 : 0) +#define S_TH_TYPE(_s) ((_s & DCH_S_TH) ? TH_UPPER : TH_LOWER) + +#define S_FM(_s) ((_s & DCH_S_FM) ? 1 : 0) +#define S_SP(_s) ((_s & DCH_S_SP) ? 1 : 0) + +/* ---------- + * Suffixes definition for DATE-TIME TO/FROM CHAR + * ---------- + */ +static KeySuffix DCH_suff[] = { + { "FM", 2, DCH_S_FM, SUFFTYPE_PREFIX }, + { "TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX }, + { "th", 2, DCH_S_th, SUFFTYPE_POSTFIX }, + { "SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX }, + /* last */ + { NULL, 0, 0, 0 } +}; + +/* ---------- + * Format-pictures (KeyWord). + * + * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted + * complicated -to-> easy: + * + * (example: "DDD","DD","Day","D" ) + * + * (this specific sort needs the algorithm for sequential search for strings, + * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH" + * or "HH12"? You must first try "HH12", because "HH" is in string, but + * it is not good. + * + * (!) + * Position for the keyword is simular as position in the enum DCH/NUM_poz + * (!) + * + * For fast search is used the 'int index[256-32]' (first 32 ascii chars is + * skiped), in this index is DCH_ enums for each ASCII position or -1 if + * char is not used in the KeyWord. Search example for string "MM": + * 1) see in index to index[77-32] (77 = 'M'), + * 2) take keywords position from index[77-32] + * 3) run sequential search in keywords[] from position + * + * ---------- + */ + +typedef enum { + DCH_CC, + DCH_DAY, + DCH_DDD, + DCH_DD, + DCH_DY, + DCH_Day, + DCH_Dy, + DCH_D, + DCH_FX, /* global suffix */ + DCH_HH24, + DCH_HH12, + DCH_HH, + DCH_J, + DCH_MI, + DCH_MM, + DCH_MONTH, + DCH_MON, + DCH_Month, + DCH_Mon, + DCH_Q, + DCH_RM, + DCH_SSSS, + DCH_SS, + DCH_WW, + DCH_W, + DCH_Y_YYY, + DCH_YYYY, + DCH_YYY, + DCH_YY, + DCH_Y, + DCH_day, + DCH_dy, + DCH_month, + DCH_mon, + /* last */ + _DCH_last_ +} DCH_poz; + +typedef enum { + NUM_COMMA, + NUM_DEC, + NUM_0, + NUM_9, + NUM_B, + NUM_C, + NUM_D, + NUM_E, + NUM_FM, + NUM_G, + NUM_L, + NUM_MI, + NUM_PL, + NUM_PR, + NUM_RN, + NUM_SG, + NUM_SP, + NUM_S, + NUM_TH, + NUM_V, + NUM_rn, + NUM_th, + /* last */ + _NUM_last_ +} NUM_poz; + +/* ---------- + * KeyWords for DATE-TIME version + * ---------- + */ +static KeyWord DCH_keywords[] = { +/* keyword, len, func. type is in Index */ + +{ "CC", 2, dch_date, DCH_CC }, /*C*/ +{ "DAY", 3, dch_date, DCH_DAY }, /*D*/ +{ "DDD", 3, dch_date, DCH_DDD }, +{ "DD", 2, dch_date, DCH_DD }, +{ "DY", 2, dch_date, DCH_DY }, +{ "Day", 3, dch_date, DCH_Day }, +{ "Dy", 2, dch_date, DCH_Dy }, +{ "D", 1, dch_date, DCH_D }, +{ "FX", 2, dch_global, DCH_FX }, /*F*/ +{ "HH24", 4, dch_time, DCH_HH24 }, /*H*/ +{ "HH12", 4, dch_time, DCH_HH12 }, +{ "HH", 2, dch_time, DCH_HH }, +{ "J", 1, dch_date, DCH_J }, /*J*/ +{ "MI", 2, dch_time, DCH_MI }, +{ "MM", 2, dch_date, DCH_MM }, +{ "MONTH", 5, dch_date, DCH_MONTH }, +{ "MON", 3, dch_date, DCH_MON }, +{ "Month", 5, dch_date, DCH_Month }, +{ "Mon", 3, dch_date, DCH_Mon }, +{ "Q", 1, dch_date, DCH_Q }, /*Q*/ +{ "RM", 2, dch_date, DCH_RM }, /*R*/ +{ "SSSS", 4, dch_time, DCH_SSSS }, /*S*/ +{ "SS", 2, dch_time, DCH_SS }, +{ "WW", 2, dch_date, DCH_WW }, /*W*/ +{ "W", 1, dch_date, DCH_W }, +{ "Y,YYY", 5, dch_date, DCH_Y_YYY }, /*Y*/ +{ "YYYY", 4, dch_date, DCH_YYYY }, +{ "YYY", 3, dch_date, DCH_YYY }, +{ "YY", 2, dch_date, DCH_YY }, +{ "Y", 1, dch_date, DCH_Y }, +{ "day", 3, dch_date, DCH_day }, /*d*/ +{ "dy", 2, dch_date, DCH_dy }, +{ "month", 5, dch_date, DCH_month }, /*m*/ +{ "mon", 3, dch_date, DCH_mon }, + +/* last */ +{ NULL, 0, NULL, 0 }}; + +/* ---------- + * KeyWords for NUMBER version + * ---------- + */ +static KeyWord NUM_keywords[] = { +/* keyword, len, func. type is in Index */ +{ ",", 1, NULL, NUM_COMMA }, /*,*/ +{ ".", 1, NULL, NUM_DEC }, /*.*/ +{ "0", 1, NULL, NUM_0 }, /*0*/ +{ "9", 1, NULL, NUM_9 }, /*9*/ +{ "B", 1, NULL, NUM_B }, /*B*/ +{ "C", 1, NULL, NUM_C }, /*C*/ +{ "D", 1, NULL, NUM_D }, /*D*/ +{ "E", 1, NULL, NUM_E }, /*E*/ +{ "FM", 2, NULL, NUM_FM }, /*F*/ +{ "G", 1, NULL, NUM_G }, /*G*/ +{ "L", 1, NULL, NUM_L }, /*L*/ +{ "MI", 2, NULL, NUM_MI }, /*M*/ +{ "PL", 2, NULL, NUM_PL }, /*P*/ +{ "PR", 2, NULL, NUM_PR }, +{ "RN", 2, NULL, NUM_RN }, /*R*/ +{ "SG", 2, NULL, NUM_SG }, /*S*/ +{ "SP", 2, NULL, NUM_SP }, +{ "S", 1, NULL, NUM_S }, +{ "TH", 2, NULL, NUM_TH }, /*T*/ +{ "V", 1, NULL, NUM_V }, /*V*/ +{ "rn", 2, NULL, NUM_rn }, /*r*/ +{ "th", 2, NULL, NUM_th }, /*t*/ + +/* last */ +{ NULL, 0, NULL, 0 }}; + + +/* ---------- + * KeyWords index for DATE-TIME version + * ---------- + */ +static int DCH_index[256 - 32] = { +/* +0 1 2 3 4 5 6 7 8 9 +*/ + /*---- first 0..31 chars is skiped ----*/ + + -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, -1, -1, -1, DCH_CC, DCH_DAY,-1, +DCH_FX, -1, DCH_HH24,-1, DCH_J, -1, -1, DCH_MI, -1, -1, +-1, DCH_Q, DCH_RM, DCH_SSSS,-1, -1, -1, DCH_WW, -1, DCH_Y_YYY, +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +DCH_day,-1, -1, -1, -1, -1, -1, -1, -1, DCH_month, +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, -1, -1 + + /*---- chars over 126 are skiped ----*/ +}; + +/* ---------- + * KeyWords index for NUMBER version + * ---------- + */ +static int NUM_index[256 - 32] = { +/* +0 1 2 3 4 5 6 7 8 9 +*/ + /*---- first 0..31 chars are skiped ----*/ + + -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, NUM_COMMA,-1, NUM_DEC,-1, NUM_0, -1, +-1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1, +-1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E, +NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1, +NUM_PL,-1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1, +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, NUM_rn, -1, NUM_th, -1, -1, -1, +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, -1, -1 + + /*---- chars over 126 are skiped ----*/ +}; + +/* ---------- + * Number processor struct + * ---------- + */ +typedef struct NUMProc +{ + int type; /* FROM_CHAR (TO_NUMBER) or TO_CHAR */ + + NUMDesc *Num; /* number description */ + + int sign, /* '-' or '+' */ + sign_wrote, /* is sign wrote */ + count_num, /* number of non-write digits */ + in_number, /* is inside number */ + pre_number; /* space before number */ + + char *number, /* string with number */ + *number_p, /* pointer to current number pozition */ + *inout, /* in / out buffer */ + *inout_p, /* pointer to current inout pozition */ + + *L_negative_sign, /* Locale */ + *L_positive_sign, + *decimal, + *L_thousands_sep, + *L_currency_symbol; +} NUMProc; + + +/* ---------- + * Functions + * ---------- + */ +static KeyWord *index_seq_search(char *str, KeyWord *kw, int *index); +static KeySuffix *suff_search(char *str, KeySuffix *suf, int type); +static void NUMDesc_prepare(NUMDesc *num, FormatNode *n); +static void parse_format(FormatNode *node, char *str, KeyWord *kw, + KeySuffix *suf, int *index, int ver, NUMDesc *Num); +static char *DCH_action(FormatNode *node, char *inout, int flag); + +#ifdef DEBUG_TO_FROM_CHAR + static void dump_index(KeyWord *k, int *index); + static void dump_node(FormatNode *node, int max); +#endif + +static char *get_th(char *num, int type); +static char *str_numth(char *dest, char *num, int type); +static int int4len(int4 num); +static char *str_toupper(char *buff); +static char *str_tolower(char *buff); +static int is_acdc(char *str, int *len); +static int seq_search(char *name, char **array, int type, int max, int *len); +static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node); +static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node); +static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node); +static char *fill_str(char *str, int c, int max); +static FormatNode *NUM_cache( int len, char *CacheStr, FormatNode *CacheFormat, + NUMDesc *CacheNum, NUMDesc *Num, char *pars_str, int *flag); +static char *int_to_roman(int number); +static void NUM_prepare_locale(NUMProc *Np); +static int NUM_numpart(NUMProc *Np, int id); +static char *NUM_processor (FormatNode *node, NUMDesc *Num, char *inout, char *number, + int plen, int sign, int type); + + +/* ---------- + * Fast sequential search, use index for data selection which + * go to seq. cycle (it is very fast for non-wanted strings) + * (can't be used binary search in format parsing) + * ---------- + */ +static KeyWord * +index_seq_search(char *str, KeyWord *kw, int *index) +{ + int poz; + + if (! KeyWord_INDEX_FILTER(*str)) + return (KeyWord *) NULL; + + if ( (poz = *(index + (*str - ' '))) > -1) { + + KeyWord *k = kw+poz; + + do { + if (! strncmp(str, k->name, k->len)) + return k; + k++; + if (!k->name) + return (KeyWord *) NULL; + } while(*str == *k->name); + } + return (KeyWord *) NULL; +} + +static KeySuffix * +suff_search(char *str, KeySuffix *suf, int type) +{ + KeySuffix *s; + + for(s=suf; s->name != NULL; s++) { + if (s->type != type) + continue; + + if (!strncmp(str, s->name, s->len)) + return s; + } + return (KeySuffix *) NULL; +} + +/* ---------- + * Prepare NUMDesc (number description struct) via FormatNode struct + * ---------- + */ +static void +NUMDesc_prepare(NUMDesc *num, FormatNode *n) +{ + + if (n->type != NODE_TYPE_ACTION) + return; + + switch(n->key->id) { + + case NUM_9: + if (IS_MULTI(num)) { + ++num->multi; + break; + } + if (IS_DECIMAL(num)) + ++num->pos; + else + ++num->pre; + break; + + case NUM_0: + if (num->pre == 0) + num->flag |= NUM_F_ZERO; + if (! IS_DECIMAL(num)) + ++num->pre; + else + ++num->pos; + break; + + case NUM_B: + if (num->pre == 0 && num->pos == 0 && (! IS_ZERO(num))) + num->flag |= NUM_F_BLANK; + break; + + case NUM_D: + num->flag |= NUM_F_LDECIMAL; + num->need_locale = TRUE; + case NUM_DEC: + if (IS_DECIMAL(num)) + elog(ERROR, "to_char/number(): not unique decimal poit."); + if (IS_MULTI(num)) + elog(ERROR, "to_char/number(): can't use 'V' and decimal poin together."); + num->flag |= NUM_F_DECIMAL; + break; + + case NUM_FM: + num->flag |= NUM_F_FILLMODE; + break; + + case NUM_S: + if (! IS_DECIMAL(num)) { + num->lsign = NUM_LSIGN_PRE; + num->pre_lsign_num = num->pre; + num->need_locale = TRUE; + + } else if (num->lsign == NUM_LSIGN_NONE) { + num->lsign = NUM_LSIGN_POST; + num->need_locale = TRUE; + } + break; + + case NUM_MI: + num->flag |= NUM_F_MINUS; + break; + + case NUM_PL: + num->flag |= NUM_F_PLUS; + break; + + case NUM_SG: + num->flag |= NUM_F_MINUS; + num->flag |= NUM_F_PLUS; + break; + + case NUM_PR: + num->flag |= NUM_F_BRACKET; + break; + case NUM_rn: + case NUM_RN: + num->flag |= NUM_F_ROMAN; + break; + + case NUM_L: + case NUM_G: + num->need_locale = TRUE; + break; + + case NUM_V: + if (IS_DECIMAL(num)) + elog(ERROR, "to_char/number(): can't use 'V' and decimal poin together."); + num->flag |= NUM_F_MULTI; + break; + } + + return; +} + +/* ---------- + * Format parser, search small keywords and keyword's suffixes, and make + * format-node tree. + * + * for DATE-TIME & NUMBER version + * ---------- + */ +static void +parse_format(FormatNode *node, char *str, KeyWord *kw, + KeySuffix *suf, int *index, int ver, NUMDesc *Num) +{ + KeySuffix *s; + FormatNode *n; + int node_set=0, + suffix, + last=0; + +#ifdef DEBUG_TO_FROM_CHAR + elog(DEBUG_elog_output, "to-from_char(): run parser."); +#endif + + n = node; + + while(*str) { + suffix=0; + + /* ---------- + * Prefix + * ---------- + */ + if (ver==DCH_TYPE && (s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL) { + suffix |= s->id; + if (s->len) + str += s->len; + } + + /* ---------- + * Keyword + * ---------- + */ + if (*str && (n->key = index_seq_search(str, kw, index)) != NULL) { + + n->type = NODE_TYPE_ACTION; + n->suffix = 0; + node_set= 1; + if (n->key->len) + str += n->key->len; + + /* ---------- + * NUM version: Prepare global NUMDesc struct + * ---------- + */ + if (ver==NUM_TYPE) + NUMDesc_prepare(Num, n); + + /* ---------- + * Postfix + * ---------- + */ + if (ver==DCH_TYPE && *str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL) { + suffix |= s->id; + if (s->len) + str += s->len; + } + + } else if (*str) { + + /* ---------- + * Special characters '\' and '"' + * ---------- + */ + if (*str == '"' && last != '\\') { + + int x = 0; + + while(*(++str)) { + if (*str == '"' && x != '\\') { + str++; + break; + } else if (*str == '\\' && x != '\\') { + x = '\\'; + continue; + } + n->type = NODE_TYPE_CHAR; + n->character = *str; + n->key = (KeyWord *) NULL; + n->suffix = 0; + ++n; + x = *str; + } + node_set = 0; + suffix = 0; + last = 0; + + } else if (*str && *str == '\\' && last!='\\' && *(str+1) =='"') { + last = *str; + str++; + + } else if (*str) { + n->type = NODE_TYPE_CHAR; + n->character = *str; + n->key = (KeyWord *) NULL; + node_set = 1; + last = 0; + str++; + } + + } + + /* end */ + if (node_set) { + if (n->type == NODE_TYPE_ACTION) + n->suffix = suffix; + ++n; + + n->suffix = 0; + node_set = 0; + } + + } + + n->type = NODE_TYPE_END; + n->suffix = 0; + return; +} + +/* ---------- + * Call keyword's function for each of (action) node in format-node tree + * ---------- + */ +static char * +DCH_action(FormatNode *node, char *inout, int flag) +{ + FormatNode *n; + char *s; + + + /* ---------- + * Zeroing global flags + * ---------- + */ + DCH_global_flag = 0; + + for(n=node, s=inout; n->type != NODE_TYPE_END; n++) { + if (n->type == NODE_TYPE_ACTION) { + + int len; + + /* ---------- + * Call node action function + * ---------- + */ + len = n->key->action(n->key->id, s, n->suffix, flag, n); + if (len > 0) + s += len; + else + continue; + + } else { + + /* ---------- + * Remove to output char from input in TO_CHAR + * ---------- + */ + if (flag == TO_CHAR) + *s = n->character; + + else { + /* ---------- + * Skip blank space in FROM_CHAR's input + * ---------- + */ + if (isspace(n->character) && IS_FX == 0) { + while(*s != '\0' && isspace(*(s+1))) + ++s; + } + } + } + + ++s; /* ! */ + + } + + if (flag == TO_CHAR) + *s = '\0'; + return inout; +} + + +/* ---------- + * DEBUG: Dump the FormatNode Tree (debug) + * ---------- + */ +#ifdef DEBUG_TO_FROM_CHAR + +#define DUMP_THth(_suf) (S_TH(_suf) ? "TH" : (S_th(_suf) ? "th" : " ")) +#define DUMP_FM(_suf) (S_FM(_suf) ? "FM" : " ") + +static void +dump_node(FormatNode *node, int max) +{ + FormatNode *n; + int a; + + elog(DEBUG_elog_output, "to_from-char(): DUMP FORMAT"); + + for(a=0, n=node; a<=max; n++, a++) { + if (n->type == NODE_TYPE_ACTION) + elog(DEBUG_elog_output, "%d:\t NODE_TYPE_ACTION\t(%s,%s)", + a, DUMP_THth(n->suffix), DUMP_FM(n->suffix)); + else if (n->type == NODE_TYPE_CHAR) + elog(DEBUG_elog_output, "%d:\t NODE_TYPE_CHAR '%c'", a, n->character); + else if (n->type == NODE_TYPE_END) { + elog(DEBUG_elog_output, "%d:\t NODE_TYPE_END", a); + return; + } else + elog(DEBUG_elog_output, "%d:\t UnKnown NODE !!!", a); + + } +} +#endif + +/***************************************************************************** + * Private utils + *****************************************************************************/ + +/* ---------- + * Return ST/ND/RD/TH for simple (1..9) numbers + * type --> 0 upper, 1 lower + * ---------- + */ +static char * +get_th(char *num, int type) +{ + int len = strlen(num), + last; + + last = *(num + (len-1)); + if (!isdigit((unsigned char) last)) + elog(ERROR, "get_th: '%s' is not number.", num); + + /* 11 || 12 */ + if (len == 2 && (last=='1' || last=='2') && *num == '1') + last=0; + + switch(last) { + case '1': + if (type==TH_UPPER) return numTH[0]; + return numth[0]; + case '2': + if (type==TH_UPPER) return numTH[1]; + return numth[1]; + case '3': + if (type==TH_UPPER) return numTH[2]; + return numth[2]; + default: + if (type==TH_UPPER) return numTH[3]; + return numth[3]; + } + return NULL; +} + +/* ---------- + * Convert string-number to ordinal string-number + * type --> 0 upper, 1 lower + * ---------- + */ +static char * +str_numth(char *dest, char *num, int type) +{ + sprintf(dest, "%s%s", num, get_th(num, type)); + return dest; +} + +/* ---------- + * Return length of integer writed in string + * ---------- + */ +static int +int4len(int4 num) +{ + char b[16]; + return sprintf(b, "%d", num); +} + +/* ---------- + * Convert string to upper-string + * ---------- + */ +static char * +str_toupper(char *buff) +{ + char *p_buff=buff; + + while (*p_buff) { + *p_buff = toupper((unsigned char) *p_buff); + ++p_buff; + } + return buff; +} + +/* ---------- + * Convert string to lower-string + * ---------- + */ +static char * +str_tolower(char *buff) +{ + char *p_buff=buff; + + while (*p_buff) { + *p_buff = tolower((unsigned char) *p_buff); + ++p_buff; + } + return buff; +} + +/* ---------- + * Check if in string is AC or BC (return: 0==none; -1==BC; 1==AC) + * ---------- + */ +static int +is_acdc(char *str, int *len) +{ + char *p; + + for(p=str; *p != '\0'; p++) { + if (isspace(*p)) + continue; + + if (*(p+1)) { + if (toupper(*p)=='B' && toupper(*(++p))=='C') { + *len += (p - str) +1; + return -1; + } else if (toupper(*p)=='A' && toupper(*(++p))=='C') { + *len += (p - str) +1; + return 1; + } + } + return 0; + } + return 0; +} + +/* ---------- + * Sequential search with to upper/lower conversion + * ---------- + */ +static int +seq_search(char *name, char **array, int type, int max, int *len) +{ + char *p, *n, **a; + int last, i; + + *len = 0; + + if (!*name) + return -1; + + /* set first char */ + if (type == ONE_UPPER || ALL_UPPER) + *name = toupper((unsigned char) *name); + else if (type == ALL_LOWER) + *name = tolower((unsigned char) *name); + + for(last=0, a=array; *a != NULL; a++) { + + /* comperate first chars */ + if (*name != **a) + continue; + + for(i=1, p=*a+1, n=name+1; ; n++, p++, i++) { + + /* search fragment (max) only */ + if (max && i == max) { + *len = i; + return a - array; + } + /* full size */ + if (*p=='\0') { + *len = i; + return a - array; + } + /* Not found in array 'a' */ + if (*n=='\0') + break; + + /* + * Convert (but convert new chars only) + */ + if (i > last) { + if (type == ONE_UPPER || type == ALL_LOWER) + *n = tolower((unsigned char) *n); + else if (type == ALL_UPPER) + *n = toupper((unsigned char) *n); + last=i; + } + +#ifdef DEBUG_TO_FROM_CHAR + /* elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)", *n, *p, *a, name);*/ +#endif + if (*n != *p) + break; + } + } + + return -1; +} + + +#ifdef DEBUG_TO_FROM_CHAR +/* ----------- + * DEBUG: Call for debug and for index checking; (Show ASCII char + * and defined keyword for each used position + * ---------- + */ +static void +dump_index(KeyWord *k, int *index) +{ + int i, count=0, free_i=0; + + elog(DEBUG_elog_output, "TO-FROM_CHAR: Dump KeyWord Index:"); + + for(i=0; i < KeyWord_INDEX_SIZE; i++) { + if (index[i] != -1) { + elog(DEBUG_elog_output, "\t%c: %s, ", i+32, k[ index[i] ].name); + count++; + } else { + free_i++; + elog(DEBUG_elog_output, "\t(%d) %c %d", i, i+32, index[i]); + } + } + elog(DEBUG_elog_output, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d", + count, free_i); +} +#endif + +/* ---------- + * Skip TM / th in FROM_CHAR + * ---------- + */ +#define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0) + + +/* ---------- + * Global format opton for DCH version + * ---------- + */ +static int +dch_global(int arg, char *inout, int suf, int flag, FormatNode *node) +{ + switch(arg) { + + case DCH_FX: + DCH_global_flag |= DCH_F_FX; + break; + } + return 0; +} + +/* ---------- + * Master function of TIME for: + * TO_CHAR - write (inout) formated string + * FROM_CHAR - scan (inout) string by course of FormatNode + * ---------- + */ +static int +dch_time(int arg, char *inout, int suf, int flag, FormatNode *node) +{ + char *p_inout = inout; + + switch(arg) { + + case DCH_HH: + case DCH_HH12: + if (flag == TO_CHAR) { + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, + tm->tm_hour==0 ? 12 : + tm->tm_hour <13 ? tm->tm_hour : tm->tm_hour-12); + if (S_THth(suf)) + str_numth(p_inout, inout, 0); + if (S_FM(suf) || S_THth(suf)) return strlen(p_inout)-1; + else return 1; + } else if (flag == FROM_CHAR) { + if (S_FM(suf)) { + sscanf(inout, "%d", &tm->tm_hour); + return int4len((int4) tm->tm_hour)-1 + SKIP_THth(suf); + } else { + sscanf(inout, "%02d", &tm->tm_hour); + return 1 + SKIP_THth(suf); + } + + } + case DCH_HH24: + if (flag == TO_CHAR) { + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_hour); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_FM(suf) || S_THth(suf)) return strlen(p_inout)-1; + else return 1; + } else if (flag == FROM_CHAR) { + if (S_FM(suf)) { + sscanf(inout, "%d", &tm->tm_hour); + return int4len((int4) tm->tm_hour)-1 + SKIP_THth(suf); + } else { + sscanf(inout, "%02d", &tm->tm_hour); + return 1 + SKIP_THth(suf); + } + } + case DCH_MI: + if (flag == TO_CHAR) { + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_min); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_FM(suf) || S_THth(suf)) return strlen(p_inout)-1; + else return 1; + } else if (flag == FROM_CHAR) { + if (S_FM(suf)) { + sscanf(inout, "%d", &tm->tm_min); + return int4len((int4) tm->tm_min)-1 + SKIP_THth(suf); + } else { + sscanf(inout, "%02d", &tm->tm_min); + return 1 + SKIP_THth(suf); + } + } + case DCH_SS: + if (flag == TO_CHAR) { + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_sec); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_FM(suf) || S_THth(suf)) return strlen(p_inout)-1; + else return 1; + } else if (flag == FROM_CHAR) { + if (S_FM(suf)) { + sscanf(inout, "%d", &tm->tm_sec); + return int4len((int4) tm->tm_sec)-1 + SKIP_THth(suf); + } else { + sscanf(inout, "%02d", &tm->tm_sec); + return 1 + SKIP_THth(suf); + } + } + case DCH_SSSS: + if (flag == TO_CHAR) { + sprintf(inout, "%d", tm->tm_hour * 3600 + + tm->tm_min * 60 + + tm->tm_sec); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + return strlen(p_inout)-1; + } else if (flag == FROM_CHAR) + elog(ERROR, "to_datatime: SSSS is not supported"); + } + return 0; +} + +#define CHECK_SEQ_SEARCH(_l, _s) { \ + if (_l <= 0) { \ + elog(ERROR, "to_datatime: bad value for %s", _s); \ + } \ +} + +/* ---------- + * Master of DATE for: + * TO_CHAR - write (inout) formated string + * FROM_CHAR - scan (inout) string by course of FormatNode + * ---------- + */ +static int +dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) +{ + char buff[ DCH_CACHE_SIZE ], + *p_inout; + int i, len; + + p_inout = inout; + + /* ---------- + * In the FROM-char is not difference between "January" or "JANUARY" + * or "january", all is before search convert to "first-upper". + * This convention is used for MONTH, MON, DAY, DY + * ---------- + */ + if (flag == FROM_CHAR) { + if (arg == DCH_MONTH || arg == DCH_Month || arg == DCH_month) { + + tm->tm_mon = seq_search(inout, months_full, ONE_UPPER, FULL_SIZ, &len); + CHECK_SEQ_SEARCH(len, "MONTH/Month/month"); + ++tm->tm_mon; + if (S_FM(suf)) return len-1; + else return 8; + + } else if (arg == DCH_MON || arg == DCH_Mon || arg == DCH_mon) { + + tm->tm_mon = seq_search(inout, months, ONE_UPPER, MAX_MON_LEN, &len); + CHECK_SEQ_SEARCH(len, "MON/Mon/mon"); + ++tm->tm_mon; + return 2; + + } else if (arg == DCH_DAY || arg == DCH_Day || arg == DCH_day) { + + tm->tm_wday = seq_search(inout, days, ONE_UPPER, FULL_SIZ, &len); + CHECK_SEQ_SEARCH(len, "DAY/Day/day"); + if (S_FM(suf)) return len-1; + else return 8; + + } else if (arg == DCH_DY || arg == DCH_Dy || arg == DCH_dy) { + + tm->tm_wday = seq_search(inout, days, ONE_UPPER, MAX_DY_LEN, &len); + CHECK_SEQ_SEARCH(len, "DY/Dy/dy"); + return 2; + + } + } + + switch(arg) { + case DCH_MONTH: + strcpy(inout, months_full[ tm->tm_mon - 1]); + sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout)); + if (S_FM(suf)) return strlen(p_inout)-1; + else return 8; + case DCH_Month: + sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[ tm->tm_mon -1 ]); + if (S_FM(suf)) return strlen(p_inout)-1; + else return 8; + case DCH_month: + sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[ tm->tm_mon -1 ]); + *inout = tolower(*inout); + if (S_FM(suf)) return strlen(p_inout)-1; + else return 8; + case DCH_MON: + strcpy(inout, months[ tm->tm_mon -1 ]); + inout = str_toupper(inout); + return 2; + case DCH_Mon: + strcpy(inout, months[ tm->tm_mon -1 ]); + return 2; + case DCH_mon: + strcpy(inout, months[ tm->tm_mon -1 ]); + *inout = tolower(*inout); + return 2; + case DCH_MM: + if (flag == TO_CHAR) { + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mon ); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_FM(suf) || S_THth(suf)) + return strlen(p_inout)-1; + else return 1; + } else if (flag == FROM_CHAR) { + if (S_FM(suf)) { + sscanf(inout, "%d", &tm->tm_mon); + return int4len((int4) tm->tm_mon)-1 + SKIP_THth(suf); + } else { + sscanf(inout, "%02d", &tm->tm_mon); + return 1 + SKIP_THth(suf); + } + } + case DCH_DAY: + strcpy(inout, days[ tm->tm_wday ]); + sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout)); + if (S_FM(suf)) return strlen(p_inout)-1; + else return 8; + case DCH_Day: + sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[ tm->tm_wday]); + if (S_FM(suf)) return strlen(p_inout)-1; + else return 8; + case DCH_day: + sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[ tm->tm_wday]); + *inout = tolower(*inout); + if (S_FM(suf)) return strlen(p_inout)-1; + else return 8; + case DCH_DY: + strcpy(inout, days[ tm->tm_wday]); + inout = str_toupper(inout); + return 2; + case DCH_Dy: + strcpy(inout, days[ tm->tm_wday]); + return 2; + case DCH_dy: + strcpy(inout, days[ tm->tm_wday]); + *inout = tolower(*inout); + return 2; + case DCH_DDD: + if (flag == TO_CHAR) { + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 3, tm->tm_yday); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_FM(suf) || S_THth(suf)) + return strlen(p_inout)-1; + else return 2; + } else if (flag == FROM_CHAR) { + if (S_FM(suf)) { + sscanf(inout, "%d", &tm->tm_yday); + return int4len((int4) tm->tm_yday)-1 + SKIP_THth(suf); + } else { + sscanf(inout, "%03d", &tm->tm_yday); + return 2 + SKIP_THth(suf); + } + } + case DCH_DD: + if (flag == TO_CHAR) { + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mday); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_FM(suf) || S_THth(suf)) + return strlen(p_inout)-1; + else return 1; + } else if (flag == FROM_CHAR) { + if (S_FM(suf)) { + sscanf(inout, "%d", &tm->tm_mday); + return int4len((int4) tm->tm_mday)-1 + SKIP_THth(suf); + } else { + sscanf(inout, "%02d", &tm->tm_mday); + return 1 + SKIP_THth(suf); + } + } + case DCH_D: + if (flag == TO_CHAR) { + sprintf(inout, "%d", tm->tm_wday+1); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_THth(suf)) + return 2; + return 0; + } else if (flag == FROM_CHAR) { + sscanf(inout, "%1d", &tm->tm_wday); + if(tm->tm_wday) --tm->tm_wday; + return 0 + SKIP_THth(suf); + } + case DCH_WW: + if (flag == TO_CHAR) { + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, + (tm->tm_yday - tm->tm_wday + 7) / 7); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_FM(suf) || S_THth(suf)) + return strlen(p_inout)-1; + else return 1; + } else if (flag == FROM_CHAR) + elog(ERROR, "to_datatime: WW is not supported"); + case DCH_Q: + if (flag == TO_CHAR) { + sprintf(inout, "%d", (tm->tm_mon-1)/3+1); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_THth(suf)) + return 2; + return 0; + } else if (flag == FROM_CHAR) + elog(ERROR, "to_datatime: Q is not supported"); + case DCH_CC: + if (flag == TO_CHAR) { + i = tm->tm_year/100 +1; + if (i <= 99 && i >= -99) + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, i); + else + sprintf(inout, "%d", i); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + return strlen(p_inout)-1; + } else if (flag == FROM_CHAR) + elog(ERROR, "to_datatime: CC is not supported"); + case DCH_Y_YYY: + if (flag == TO_CHAR) { + i= YEAR_ABS(tm->tm_year) / 1000; + sprintf(inout, "%d,%03d", i, YEAR_ABS(tm->tm_year) -(i*1000)); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (tm->tm_year < 0) + strcat(inout, BC_STR); + return strlen(p_inout)-1; + } else if (flag == FROM_CHAR) { + int cc, yy; + sscanf(inout, "%d,%03d", &cc, &yy); + tm->tm_year = (cc * 1000) + yy; + + if (!S_FM(suf) && tm->tm_year <= 9999 && tm->tm_year >= -9999) + len = 5; + else + len = int4len((int4) tm->tm_year)+1; + len += SKIP_THth(suf); + /* AC/BC */ + if (is_acdc(inout+len, &len) < 0 && tm->tm_year > 0) + tm->tm_year = -(tm->tm_year); + if (tm->tm_year < 0) + tm->tm_year = tm->tm_year+1; + return len-1; + } + case DCH_YYYY: + if (flag == TO_CHAR) { + if (tm->tm_year <= 9999 && tm->tm_year >= -9998) + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 4, YEAR_ABS(tm->tm_year)); + else + sprintf(inout, "%d", YEAR_ABS(tm->tm_year)); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (tm->tm_year < 0) + strcat(inout, BC_STR); + return strlen(p_inout)-1; + } else if (flag == FROM_CHAR) { + sscanf(inout, "%d", &tm->tm_year); + if (!S_FM(suf) && tm->tm_year <= 9999 && tm->tm_year >= -9999) + len = 4; + else + len = int4len((int4) tm->tm_year); + len += SKIP_THth(suf); + /* AC/BC */ + if (is_acdc(inout+len, &len) < 0 && tm->tm_year > 0) + tm->tm_year = -(tm->tm_year); + if (tm->tm_year < 0) + tm->tm_year = tm->tm_year+1; + return len-1; + } + case DCH_YYY: + if (flag == TO_CHAR) { + sprintf(buff, "%03d", YEAR_ABS(tm->tm_year)); + i=strlen(buff); + strcpy(inout, buff+(i-3)); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_THth(suf)) return 4; + return 2; + } else if (flag == FROM_CHAR) { + int yy; + sscanf(inout, "%03d", &yy); + tm->tm_year = (tm->tm_year/1000)*1000 +yy; + return 2 + SKIP_THth(suf); + } + case DCH_YY: + if (flag == TO_CHAR) { + sprintf(buff, "%02d", YEAR_ABS(tm->tm_year)); + i=strlen(buff); + strcpy(inout, buff+(i-2)); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_THth(suf)) return 3; + return 1; + } else if (flag == FROM_CHAR) { + int yy; + sscanf(inout, "%02d", &yy); + tm->tm_year = (tm->tm_year/100)*100 +yy; + return 1 + SKIP_THth(suf); + } + case DCH_Y: + if (flag == TO_CHAR) { + sprintf(buff, "%1d", YEAR_ABS(tm->tm_year)); + i=strlen(buff); + strcpy(inout, buff+(i-1)); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_THth(suf)) return 2; + return 0; + } else if (flag == FROM_CHAR) { + int yy; + sscanf(inout, "%1d", &yy); + tm->tm_year = (tm->tm_year/10)*10 +yy; + return 0 + SKIP_THth(suf); + } + case DCH_RM: + if (flag == TO_CHAR) { + sprintf(inout, "%*s", S_FM(suf) ? 0 : -4, + rm_months[ 12 - tm->tm_mon ]); + if (S_FM(suf)) return strlen(p_inout)-1; + else return 3; + } else if (flag == FROM_CHAR) { + tm->tm_mon = 11-seq_search(inout, rm_months, ALL_UPPER, FULL_SIZ, &len); + CHECK_SEQ_SEARCH(len, "RM"); + ++tm->tm_mon; + if (S_FM(suf)) return len-1; + else return 3; + } + case DCH_W: + if (flag == TO_CHAR) { + sprintf(inout, "%d", (tm->tm_mday - tm->tm_wday +7) / 7 ); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + if (S_THth(suf)) return 2; + return 0; + } else if (flag == FROM_CHAR) + elog(ERROR, "to_datatime: W is not supported"); + case DCH_J: + if (flag == TO_CHAR) { + sprintf(inout, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); + if (S_THth(suf)) + str_numth(p_inout, inout, S_TH_TYPE(suf)); + return strlen(p_inout)-1; + } else if (flag == FROM_CHAR) + elog(ERROR, "to_datatime: J is not supported"); + } + return 0; +} + +/**************************************************************************** + * Public routines + ***************************************************************************/ + +/* ------------------- + * DATETIME to_char() + * ------------------- + */ +text * +datetime_to_char(DateTime *dt, text *fmt) +{ + static FormatNode CacheFormat[ DCH_CACHE_SIZE +1]; + static char CacheStr[ DCH_CACHE_SIZE +1]; + + text *result; + FormatNode *format; + int flag=0; + char *str; + double fsec; + char *tzn; + int len=0, tz; + + if ((!PointerIsValid(dt)) || (!PointerIsValid(fmt))) + return NULL; + + len = VARSIZE(fmt) - VARHDRSZ; + + if (!len) + return textin(""); + + tm->tm_sec =0; tm->tm_year =0; + tm->tm_min =0; tm->tm_wday =0; + tm->tm_hour =0; tm->tm_yday =0; + tm->tm_mday =1; tm->tm_isdst =0; + tm->tm_mon =1; + + if (DATETIME_IS_EPOCH(*dt)) + { + datetime2tm(SetDateTime(*dt), NULL, tm, &fsec, NULL); + } else if (DATETIME_IS_CURRENT(*dt)) { + datetime2tm(SetDateTime(*dt), &tz, tm, &fsec, &tzn); + } else { + if (datetime2tm(*dt, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "to_char: Unable to convert datetime to tm"); + } + + tm->tm_wday = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1) % 7; + tm->tm_yday = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(tm->tm_year, 1,1) +1; + + /* ---------- + * Convert VARDATA() to string + * ---------- + */ + str = (char *) palloc(len + 1); + memcpy(str, VARDATA(fmt), len); + *(str + len) = '\0'; + + /* ---------- + * Allocate result + * ---------- + */ + result = (text *) palloc( (len * DCH_MAX_ITEM_SIZ) + 1 + VARHDRSZ); + + /* ---------- + * Allocate new memory if format picture is bigger than static cache + * and not use cache (call parser always) - flag=1 show this variant + * ---------- + */ + if ( len > DCH_CACHE_SIZE ) { + + format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode)); + flag = 1; + + parse_format(format, str, DCH_keywords, + DCH_suff, DCH_index, DCH_TYPE, NULL); + + (format + len)->type = NODE_TYPE_END; /* Paranoa? */ + + } else { + + /* ---------- + * Use cache buffers + * ---------- + */ +#ifdef DEBUG_TO_FROM_CHAR + elog(DEBUG_elog_output, "DCH_TO_CHAR() Len: %d", len); + elog(DEBUG_elog_output, "DCH_TO_CHAR() Cache - str: >%s<", CacheStr); + elog(DEBUG_elog_output, "DCH_TO_CHAR() Arg.str: >%s<", str); +#endif + flag = 0; + + if (strcmp(CacheStr, str) != 0) { + + /* ---------- + * Can't use the cache, must run parser and save a original + * format-picture string to the cache. + * ---------- + */ + strncpy(CacheStr, str, DCH_CACHE_SIZE); + +#ifdef DEBUG_TO_FROM_CHAR + /* dump_node(CacheFormat, len); */ + /* dump_index(DCH_keywords, DCH_index); */ +#endif + parse_format(CacheFormat, str, DCH_keywords, + DCH_suff, DCH_index, DCH_TYPE, NULL); + + (CacheFormat + len)->type = NODE_TYPE_END; /* Paranoa? */ + } + + format = CacheFormat; + } + + DCH_action(format, VARDATA(result), TO_CHAR); + + if (flag) + pfree(format); + + pfree(str); + VARSIZE(result) = strlen(VARDATA(result)) + VARHDRSZ; + return result; +} + + +/* ------------------- + * TIMESTAMP to_char() + * ------------------- + */ +text * +timestamp_to_char(time_t dt, text *fmt) +{ + return datetime_to_char( timestamp_datetime(dt), fmt); +} + +/* --------------------- + * TO_DATETIME() + * + * Make DateTime from date_str which is formated at argument 'fmt' + * ( to_datetime is reverse to_char() ) + * --------------------- + */ +DateTime * +to_datetime(text *date_str, text *fmt) +{ + static FormatNode CacheFormat[ DCH_CACHE_SIZE +1]; + static char CacheStr[ DCH_CACHE_SIZE +1]; + + FormatNode *format; + int flag=0; + DateTime *result; + char *str; + int len=0, + fsec=0, + tz=0; + + if ((!PointerIsValid(date_str)) || (!PointerIsValid(fmt))) + return NULL; + + tm->tm_sec =0; tm->tm_year =0; + tm->tm_min =0; tm->tm_wday =0; + tm->tm_hour =0; tm->tm_yday =0; + tm->tm_mday =1; tm->tm_isdst =0; + tm->tm_mon =1; + + result = palloc(sizeof(DateTime)); + + len = VARSIZE(fmt) - VARHDRSZ; + + if (len) { + + /* ---------- + * Convert VARDATA() to string + * ---------- + */ + str = (char *) palloc(len + 1); + memcpy(str, VARDATA(fmt), len); + *(str + len) = '\0'; + + /* ---------- + * Allocate new memory if format picture is bigger than static cache + * and not use cache (call parser always) - flag=1 show this variant + * ---------- + */ + if ( len > DCH_CACHE_SIZE ) { + + format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode)); + flag = 1; + + parse_format(format, str, DCH_keywords, + DCH_suff, DCH_index, DCH_TYPE, NULL); + + (format + len)->type = NODE_TYPE_END; /* Paranoa? */ + + } else { + + /* ---------- + * Use cache buffers + * ---------- + */ +#ifdef DEBUG_TO_FROM_CHAR + elog(DEBUG_elog_output, "DCH_TO_CHAR() Len: %d", len); + elog(DEBUG_elog_output, "DCH_TO_CHAR() Cache - str: >%s<", CacheStr); + elog(DEBUG_elog_output, "DCH_TO_CHAR() Arg.str: >%s<", str); +#endif + flag = 0; + + if (strcmp(CacheStr, str) != 0) { + + /* ---------- + * Can't use the cache, must run parser and save a original + * format-picture string to the cache. + * ---------- + */ + strncpy(CacheStr, str, DCH_CACHE_SIZE); + + parse_format(CacheFormat, str, DCH_keywords, + DCH_suff, DCH_index, DCH_TYPE, NULL); + + (CacheFormat + len)->type = NODE_TYPE_END; /* Paranoa? */ + } + + format = CacheFormat; + } + + /* ---------- + * Call action for each node in FormatNode tree + * ---------- + */ +#ifdef DEBUG_TO_FROM_CHAR + /* dump_node(format, len); */ +#endif + VARDATA(date_str)[ VARSIZE(date_str) - VARHDRSZ ] = '\0'; + DCH_action(format, VARDATA(date_str), FROM_CHAR); + + pfree(str); + + if (flag) + pfree(format); + } + +#ifdef DEBUG_TO_FROM_CHAR + NOTICE_TM; +#endif + if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) { + +#ifdef USE_POSIX_TIME + tm->tm_isdst = -1; + tm->tm_year -= 1900; + tm->tm_mon -= 1; + +#ifdef DEBUG_TO_FROM_CHAR + elog(DEBUG_elog_output, "TO-FROM_CHAR: Call mktime()"); + NOTICE_TM; +#endif + mktime(tm); + tm->tm_year += 1900; + tm->tm_mon += 1; + +#if defined(HAVE_TM_ZONE) + tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ +#elif defined(HAVE_INT_TIMEZONE) + +#ifdef __CYGWIN__ + tz = (tm->tm_isdst ? (_timezone - 3600) : _timezone); +#else + tz = (tm->tm_isdst ? (timezone - 3600) : timezone); +#endif + +#else +#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined +#endif + +#else /* !USE_POSIX_TIME */ + tz = CTimeZone; +#endif + } else { + tm->tm_isdst = 0; + tz = 0; + } +#ifdef DEBUG_TO_FROM_CHAR + NOTICE_TM; +#endif + if (tm2datetime(tm, fsec, &tz, result) != 0) + elog(ERROR, "to_datatime: can't convert 'tm' to datetime."); + + return result; +} + +/* ---------- + * TO_DATE + * Make Date from date_str which is formated at argument 'fmt' + * ---------- + */ +DateADT +to_date(text *date_str, text *fmt) +{ + return datetime_date( to_datetime(date_str, fmt) ); +} + +/* ---------- + * TO_TIMESTAMP + * Make timestamp from date_str which is formated at argument 'fmt' + * ---------- + */ +time_t +to_timestamp(text *date_str, text *fmt) +{ + return datetime_timestamp( to_datetime(date_str, fmt) ); +} + +/********************************************************************** + * the NUMBER version part + *********************************************************************/ + + +static char * +fill_str(char *str, int c, int max) +{ + memset(str, c, max); + *(str+max+1) = '\0'; + return str; +} + + +/* ---------- + * Cache routine for NUM to_char version + * ---------- + */ +static FormatNode * +NUM_cache( int len, char *CacheStr, FormatNode *CacheFormat, + NUMDesc *CacheNum, NUMDesc *Num, char *pars_str, int *flag) +{ + FormatNode *format; + char *str; + + /* ---------- + * Convert VARDATA() to string + * ---------- + */ + str = (char *) palloc(len + 1); + memcpy(str, pars_str, len); + *(str + len) = '\0'; + + /* ---------- + * Allocate new memory if format picture is bigger than static cache + * and not use cache (call parser always) - flag=1 show this variant + * ---------- + */ + if ( len > NUM_CACHE_SIZE ) { + + format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode)); + *flag = 1; + + Num->flag = 0; + Num->lsign = 0; + Num->pre = 0; + Num->pos = 0; + Num->pre_lsign_num = 0; + + parse_format(format, str, NUM_keywords, + NULL, NUM_index, NUM_TYPE, Num); + + (format + len)->type = NODE_TYPE_END; /* Paranoa? */ + pfree(str); + return format; + + } else { + + /* ---------- + * Use cache buffer + * ---------- + */ +#ifdef DEBUG_TO_FROM_CHAR + elog(DEBUG_elog_output, "NUM_TO_CHAR() Len: %d", len); + elog(DEBUG_elog_output, "NUM_TO_CHAR() Cache - str: >%s<", CacheStr); + elog(DEBUG_elog_output, "NUM_TO_CHAR() Arg.str: >%s<", str); +#endif + *flag = 0; + + if (strcmp(CacheStr, str) != 0) { + + /* ---------- + * Can't use the cache, must run parser and save a original + * format-picture string to the cache. + * ---------- + */ + strncpy(CacheStr, str, NUM_CACHE_SIZE); + + /* ---------- + * Set zeros to CacheNum struct + * ---------- + */ + CacheNum->flag = 0; + CacheNum->lsign = 0; + CacheNum->pre = 0; + CacheNum->pos = 0; + CacheNum->pre_lsign_num = 0; + CacheNum->need_locale = 0; + CacheNum->multi = 0; + +#ifdef DEBUG_TO_FROM_CHAR + /* dump_node(CacheFormat, len); */ + /* dump_index(NUM_keywords, NUM_index); */ +#endif + parse_format(CacheFormat, str, NUM_keywords, + NULL, NUM_index, NUM_TYPE, CacheNum); + + (CacheFormat + len)->type = NODE_TYPE_END; /* Paranoa? */ + } + /* ---------- + * Copy cache to used struct + * ---------- + */ + Num->flag = CacheNum->flag; + Num->lsign = CacheNum->lsign; + Num->pre = CacheNum->pre; + Num->pos = CacheNum->pos; + Num->pre_lsign_num = CacheNum->pre_lsign_num; + Num->need_locale = CacheNum->need_locale; + Num->multi = CacheNum->multi; + + pfree(str); + return CacheFormat; + } +} + + +static char * +int_to_roman(int number) +{ + int len = 0, + num = 0, + set = 0; + char *p = NULL, + *result, + numstr[5]; + + result = (char *) palloc( 16 ); + *result = '\0'; + + if (number > 3999 || number < 1) { + fill_str(result, '#', 15); + return result; + } + len = sprintf(numstr, "%d", number); + + for(p=numstr; *p!='\0'; p++, --len) { + num = *p - 49; /* 48 ascii + 1 */ + if (num < 0) + continue; + if (num == -1 && set==0) + continue; + set = 1; + + if (len > 3) { + while(num-- != -1) + strcat(result, "M"); + } else { + if (len==3) + strcat(result, rm100[num]); + else if (len==2) + strcat(result, rm10[num]); + else if (len==1) + strcat(result, rm1[num]); + } + } + return result; +} + + + +/* ---------- + * Locale + * ---------- + */ +static void +NUM_prepare_locale(NUMProc *Np) +{ + +#ifdef USE_LOCALE + + if (Np->Num->need_locale) { + + struct lconv *lconv; + + /* ---------- + * Get locales + * ---------- + */ + lconv = PGLC_localeconv(); + + /* ---------- + * Positive / Negative number sign + * ---------- + */ + if (lconv->negative_sign && *lconv->negative_sign) + Np->L_negative_sign = lconv->negative_sign; + else + Np->L_negative_sign = "-"; + + if (lconv->positive_sign && *lconv->positive_sign) + Np->L_positive_sign = lconv->positive_sign; + else + Np->L_positive_sign = "+"; + + /* ---------- + * Number thousands separator + * ---------- + */ + if (lconv->thousands_sep && *lconv->thousands_sep) + Np->L_thousands_sep = lconv->thousands_sep; + else + Np->L_thousands_sep = ","; + + /* ---------- + * Number decimal point + * ---------- + */ + if (lconv->decimal_point && *lconv->decimal_point) + Np->decimal = lconv->decimal_point; + else + Np->decimal = "."; + + /* ---------- + * Currency symbol + * ---------- + */ + if (lconv->currency_symbol && *lconv->currency_symbol) + Np->L_currency_symbol = lconv->currency_symbol; + else + Np->L_currency_symbol = " "; + + + if (!IS_LDECIMAL(Np->Num)) + Np->decimal = "."; + } else { + +#endif + /* ---------- + * Default values + * ---------- + */ + Np->L_negative_sign = "-"; + Np->L_positive_sign = "+"; + Np->decimal = "."; + Np->L_thousands_sep = ","; + Np->L_currency_symbol = " "; + +#ifdef USE_LOCALE + } +#endif +} + +/* ---------- + * SET SIGN macro - set 'S' or 'PR' befor number + * ---------- + */ +#define SET_SIGN(_np) { \ + if ((_np)->sign_wrote==0) { \ + if (IS_BRACKET((_np)->Num)) { \ + \ + *(_np)->inout_p = '<'; \ + ++(_np)->inout_p; \ + (_np)->sign_wrote = 1; \ + \ + } else if ((_np)->Num->lsign==NUM_LSIGN_PRE && (! IS_BRACKET((_np)->Num)) && \ + (! IS_MINUS((_np)->Num)) && (! IS_PLUS((_np)->Num))) { \ + \ + if ((_np)->sign=='-') \ + strcpy((_np)->inout_p, (_np)->L_negative_sign); \ + else \ + strcpy((_np)->inout_p, (_np)->L_positive_sign); \ + (_np)->inout_p += strlen((_np)->inout_p); \ + (_np)->sign_wrote = 1; \ + \ + } else { \ + if ((_np)->sign=='-' && (_np)->Num->lsign==NUM_LSIGN_NONE \ + && (! IS_BRACKET((_np)->Num)) && \ + (! IS_MINUS((_np)->Num)) && (! IS_PLUS((_np)->Num))) { \ + \ + *(_np)->inout_p = '-'; \ + ++(_np)->inout_p; \ + (_np)->sign_wrote=1; \ + \ + } else if ((! IS_FILLMODE((_np)->Num)) && (_np)->Num->lsign==NUM_LSIGN_NONE && \ + (! IS_BRACKET((_np)->Num)) && (! IS_MINUS((_np)->Num)) && (! IS_PLUS((_np)->Num))) { \ + \ + *(_np)->inout_p = ' '; \ + ++(_np)->inout_p; \ + (_np)->sign_wrote=1; \ + } \ + } \ + } \ +} + +/* ---------- + * Create number + * return FALSE if any work over current NUM_[0|9|D|DEC] must be skip + * in NUM_processor() + * ---------- + */ +static int +NUM_numpart(NUMProc *Np, int id) +{ + if (IS_ROMAN(Np->Num)) + return FALSE; + + /* Note: in this elog() output not set '\0' in inout + elog(DEBUG_elog_output, "sign_w: %d, count: %d, plen %d, sn: %s, inout: '%s'", + Np->sign_wrote, Np->count_num, Np->pre_number, Np->number_p, Np->inout); + */ + + /* ---------- + * Number extraction for TO_NUMBER() + * ---------- + */ + if (Np->type == FROM_CHAR) { + if (id == NUM_9 || id == NUM_0) { + if (!*(Np->number+1)) { + if (*Np->inout_p == '-' || + (IS_BRACKET(Np->Num) && + *Np->inout_p == '<' )) { + + *Np->number = '-'; + Np->inout_p++; + + } else if (*Np->inout_p == '+') { + + *Np->number = '+'; + Np->inout_p++; + + } else if (*Np->inout_p == ' ' && (! IS_FILLMODE(Np->Num))) { + Np->inout_p++; + } + } + if (isdigit((unsigned char) *Np->inout_p)) { + *Np->number_p = *Np->inout_p; + Np->number_p++; + } + } else { + if (id == NUM_DEC) { + *Np->number_p = '.'; + Np->number_p++; + + } else if (id == NUM_D) { + + int x = strlen(Np->decimal); + + if (!strncmp(Np->inout_p, Np->decimal, x)) { + Np->inout_p += x-1; + *Np->number_p = '.'; + Np->number_p++; + } + } + } + return TRUE; + } + + /* ---------- + * Add blank space (pre number) + * ---------- + */ + if ((! IS_ZERO(Np->Num)) && (! IS_FILLMODE(Np->Num)) && Np->pre_number > 0) { + *Np->inout_p = ' '; + --Np->pre_number; + --Np->count_num; + return TRUE; + } + + /* ---------- + * Add zero (pre number) + * ---------- + */ + if (IS_ZERO(Np->Num) && Np->pre_number > 0) { + + SET_SIGN(Np); + *Np->inout_p='0'; + --Np->pre_number; + --Np->count_num; + return TRUE; + } + + if (IS_FILLMODE(Np->Num) && Np->pre_number > 0) { + --Np->pre_number; + return FALSE; + } + + /* ---------- + * Add sign or '<' or ' ' + * ---------- + */ + if (Np->number_p==Np->number && Np->sign_wrote==0) { + SET_SIGN(Np); + } + + /* ---------- + * Add decimal point + * ---------- + */ + if (*Np->number_p=='.') { + strcpy(Np->inout_p, Np->decimal); + Np->inout_p += strlen(Np->inout_p); + if (*Np->number_p) + ++Np->number_p; + return FALSE; + } + + if (*Np->number_p) { + + /* ---------- + * Copy number string to inout string + * ---------- + */ + *Np->inout_p = *Np->number_p; + ++Np->number_p; + + Np->in_number = 1; + + /* ---------- + * Number end (set post 'S' or '>' + * ---------- + */ + if ((--Np->count_num)==0) { + + if (IS_BRACKET(Np->Num)) { + ++Np->inout_p; + *Np->inout_p = '>'; + Np->sign_wrote =1; + } + + if (Np->Num->lsign==NUM_LSIGN_POST && Np->sign_wrote==0) { + ++Np->inout_p; + if (Np->sign=='-') + strcpy(Np->inout_p, Np->L_negative_sign); + else + strcpy(Np->inout_p, Np->L_positive_sign); + Np->inout_p += strlen(Np->inout_p)-1; + Np->sign_wrote = 1; + } + } + } else + return FALSE; + return TRUE; +} + +/* ---------- + * Master function for NUMBER version. + * type (variant): + * TO_CHAR + * -> create formatted string by course of FormatNode + * + * FROM_CHAR (TO_NUMBER) + * -> extract number string from formatted string (reverse TO_CHAR) + * + * - ! this version use non-string 'inout' VARDATA(), + * and 'plen' is used for VARSIZE() + * - value 'sign' is not used + * - number is creating in Np->number and first position (*Np->number) + * in this buffer is reserved for +/- sign + * ---------- + */ +static char * +NUM_processor (FormatNode *node, NUMDesc *Num, char *inout, char *number, + int plen, int sign, int type) +{ + FormatNode *n; + NUMProc _Np, *Np = &_Np; + + Np->Num = Num; + Np->type = type; + Np->number = number; + + if (type == TO_CHAR) { + Np->pre_number = plen; + Np->sign = sign; + + } else if (type == FROM_CHAR) { + Np->pre_number = 0; + *Np->number = ' '; /* sign space */ + *(Np->number+1) = '\0'; + Np->sign = 0; + } + + Np->inout = inout; + + Np->sign_wrote = Np->count_num = Np->in_number = 0; + + /* ---------- + * Roman corection + * ---------- + */ + if (IS_ROMAN(Np->Num)) { + if (Np->type==FROM_CHAR) + elog(ERROR, "to_number: RN is not supported"); + + Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->pos = + Np->Num->pre = Np->pre_number = Np->sign = 0; + + if (IS_FILLMODE(Np->Num)){ + Np->Num->flag = 0; + Np->Num->flag |= NUM_F_FILLMODE; + } else { + Np->Num->flag = 0; + } + Np->Num->flag |= NUM_F_ROMAN; + } + + /* ---------- + * Check Num struct + * ---------- + */ + if (Np->sign=='+') + Np->Num->flag &= ~NUM_F_BRACKET; + + if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num) + Np->Num->lsign = NUM_LSIGN_POST; + + /* set counter (number of all digits) */ + Np->count_num = Np->Num->pos + Np->Num->pre; + + if (Np->type==TO_CHAR && IS_FILLMODE(Np->Num) && (! IS_ZERO(Np->Num))) + Np->count_num -= Np->pre_number; + +#ifdef DEBUG_TO_FROM_CHAR + elog(DEBUG_elog_output, "NUM: '%s', PRE: %d, POS: %d, PLEN: %d, LSIGN: %d, DECIMAL: %d, MULTI: %d", + Np->number, Np->Num->pre, Np->Num->pos, + Np->pre_number, Np->Num->lsign,IS_DECIMAL(Np->Num), + Np->Num->multi); +#endif + /* ---------- + * Locale + * ---------- + */ + NUM_prepare_locale(Np); + + /* need for DEBUG: memset(Np->inout, '\0', Np->count_num );*/ + + /* ---------- + * Processor direct cycle + * ---------- + */ + if (Np->type == FROM_CHAR) + Np->number_p=Np->number+1; /* first char is space for sign */ + else if (Np->type == TO_CHAR) + Np->number_p=Np->number; + + for(n=node, Np->inout_p=Np->inout; n->type != NODE_TYPE_END; n++) { + + if (Np->type == FROM_CHAR) { + /* ---------- + * Check non-string inout end + * ---------- + */ + if (Np->inout_p == Np->inout + plen) + break; + } + + /* ---------- + * Format pictures actions + * ---------- + */ + if (n->type == NODE_TYPE_ACTION) { + + /* ---------- + * Create/reading digit/zero/blank/sing + * ---------- + */ + switch( n->key->id ) { + + case NUM_9: + case NUM_0: + case NUM_DEC: + case NUM_D: + + if (NUM_numpart(Np, n->key->id)) + break; + else + continue; + + case NUM_COMMA: + if (Np->type == TO_CHAR) { + if (!Np->in_number) { + if (IS_FILLMODE(Np->Num)) + continue; + else + *Np->inout_p= ' '; + } else + *Np->inout_p = ','; + + } else if (Np->type == FROM_CHAR) { + if (!Np->in_number) { + if (IS_FILLMODE(Np->Num)) + continue; + } + } + break; + + case NUM_G: + if (Np->type == TO_CHAR) { + if (!Np->in_number) { + if (IS_FILLMODE(Np->Num)) + continue; + else { + int x = strlen(Np->L_thousands_sep); + memset(Np->inout_p, ' ', x); + Np->inout_p += x-1; + } + } else { + strcpy(Np->inout_p, Np->L_thousands_sep); + Np->inout_p += strlen(Np->inout_p)-1; + } + + } else if (Np->type == FROM_CHAR) { + if (!Np->in_number) { + if (IS_FILLMODE(Np->Num)) + continue; + } + Np->inout_p += strlen(Np->L_thousands_sep)-1; + } + break; + + case NUM_L: + if (Np->type == TO_CHAR) { + strcpy(Np->inout_p, Np->L_currency_symbol); + Np->inout_p += strlen(Np->inout_p)-1; + + } else if (Np->type == FROM_CHAR) { + Np->inout_p += strlen(Np->L_currency_symbol)-1; + } + break; + + case NUM_MI: + if (Np->sign_wrote==1 || IS_BRACKET(Np->Num)) { + if (Np->type == TO_CHAR) + continue; + else + break; + } + if (Np->type == TO_CHAR) { + if (Np->sign=='-') + *Np->inout_p = '-'; + else + *Np->inout_p = ' '; + + } else if (Np->type == FROM_CHAR) { + if (*Np->inout_p == '-') + *Np->number = '-'; + } + Np->sign_wrote=1; + break; + + case NUM_PL: + if (Np->sign_wrote==1 || IS_BRACKET(Np->Num)) { + if (Np->type == TO_CHAR) + continue; + else + break; + } + if (Np->type == TO_CHAR) { + if (Np->sign=='+') + *Np->inout_p = '+'; + else + *Np->inout_p = ' '; + + } else if (Np->type == FROM_CHAR) { + if (*Np->inout_p == '+') + *Np->number = '+'; + } + Np->sign_wrote=1; + break; + + case NUM_SG: + if (Np->sign_wrote==1 || IS_BRACKET(Np->Num)) { + if (Np->type == TO_CHAR) + continue; + else + break; + } + if (Np->type == TO_CHAR) + *Np->inout_p = Np->sign; + + else if (Np->type == FROM_CHAR) { + if (*Np->inout_p == '-') + *Np->number = '-'; + else if (*Np->inout_p == '+') + *Np->number = '+'; + } + Np->sign_wrote=1; + break; + + case NUM_RN: + if (Np->type == FROM_CHAR) + elog(ERROR, "TO_NUMBER: internal error #1"); + + if (IS_FILLMODE(Np->Num)) { + strcpy(Np->inout_p, Np->number_p); + Np->inout_p += strlen(Np->inout_p) - 1; + } else + Np->inout_p += sprintf(Np->inout_p, "%15s", Np->number_p) -1; + break; + + case NUM_rn: + if (Np->type == FROM_CHAR) + elog(ERROR, "TO_NUMBER: internal error #2"); + + if (IS_FILLMODE(Np->Num)) { + strcpy(Np->inout_p, str_tolower(Np->number_p)); + Np->inout_p += strlen(Np->inout_p) - 1; + } else + Np->inout_p += sprintf(Np->inout_p, "%15s", str_tolower(Np->number_p)) -1; + break; + + case NUM_th: + if (IS_ROMAN(Np->Num) || *Np->number=='#' || + Np->sign=='-' || IS_DECIMAL(Np->Num)) + continue; + + if (Np->type == TO_CHAR) + strcpy(Np->inout_p, get_th(Np->number, TH_LOWER)); + Np->inout_p += 1; + break; + + case NUM_TH: + if (IS_ROMAN(Np->Num) || *Np->number=='#' || + Np->sign=='-' || IS_DECIMAL(Np->Num)) + continue; + + if (Np->type == TO_CHAR) + strcpy(Np->inout_p, get_th(Np->number, TH_UPPER)); + Np->inout_p += 1; + break; + + case NUM_S: + /* ---------- + * 'S' for TO_CHAR is in NUM_numpart() + * ---------- + */ + if (Np->type == FROM_CHAR && Np->sign_wrote==FALSE) { + + int x = strlen(Np->L_negative_sign); + if (!strncmp(Np->inout_p, Np->L_negative_sign, x)) { + Np->inout_p += x-1; + *Np->number = '-'; + break; + } + + x = strlen(Np->L_positive_sign); + if (!strncmp(Np->inout_p, Np->L_positive_sign, x)) { + Np->inout_p += x-1; + *Np->number = '+'; + break; + } + } else + continue; + break; + default: + continue; + break; + } + + } else { + /* ---------- + * Remove to output char from input in TO_CHAR + * ---------- + */ + if (Np->type == TO_CHAR) + *Np->inout_p = n->character; + } + Np->inout_p++; + } + + if (Np->type == TO_CHAR) { + *Np->inout_p = '\0'; + return Np->inout; + + } else if (Np->type == FROM_CHAR) { + *Np->number_p = '\0'; +#ifdef DEBUG_TO_FROM_CHAR + elog(DEBUG_elog_output, "TO_NUMBER (number): '%s'", Np->number); +#endif + return Np->number; + } else + return NULL; +} + +/* ---------- + * MACRO: Start part of NUM - for all NUM's to_char variants + * (sorry, but I hate copy same code - macro is better..) + * ---------- + */ +#define NUM_TOCHAR_prepare { \ + if (!PointerIsValid(fmt)) \ + return NULL; \ + \ + len = VARSIZE(fmt) - VARHDRSZ; \ + \ + if (!len) \ + return textin(""); \ + \ + result = (text *) palloc( (len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ); \ + format = NUM_cache(len, CacheStr, CacheFormat, &CacheNum, &Num, \ + VARDATA(fmt), &flag); \ +} + +/* ---------- + * MACRO: Finish part of NUM + * ---------- + */ +#define NUM_TOCHAR_finish { \ + \ + NUM_processor(format, &Num, VARDATA(result), \ + numstr, plen, sign, TO_CHAR); \ + pfree(orgnum); \ + \ + if (flag) \ + pfree(format); \ + \ + VARSIZE(result) = strlen(VARDATA(result)) + VARHDRSZ; \ +} + +/* ------------------- + * NUMERIC to_number() + * ------------------- + */ +Numeric +numeric_to_number(text *value, text *fmt) +{ + static FormatNode CacheFormat[ NUM_CACHE_SIZE +1]; + static char CacheStr[ NUM_CACHE_SIZE +1]; + static NUMDesc CacheNum; + + NUMDesc Num; + FormatNode *format; + char *numstr; + int flag=0; + int len=0; + + int scale, precision; + + if ((!PointerIsValid(value)) || (!PointerIsValid(fmt))) + return NULL; + + len = VARSIZE(fmt) - VARHDRSZ; + + if (!len) + return numeric_in(NULL, 0, 0); + + format = NUM_cache(len, CacheStr, CacheFormat, &CacheNum, &Num, + VARDATA(fmt), &flag); + + numstr = (char *) palloc( (len * NUM_MAX_ITEM_SIZ) + 1); + + NUM_processor(format, &Num, VARDATA(value), numstr, + VARSIZE(value) - VARHDRSZ, 0, FROM_CHAR); + + scale = Num.pos; + precision = MAX(0, Num.pre) + scale; + + return numeric_in(numstr, 0, ((precision << 16) | scale) + VARHDRSZ); +} + +/* ------------------ + * NUMERIC to_char() + * ------------------ + */ +text * +numeric_to_char(Numeric value, text *fmt) +{ + static FormatNode CacheFormat[ NUM_CACHE_SIZE +1]; + static char CacheStr[ NUM_CACHE_SIZE +1]; + static NUMDesc CacheNum; + + NUMDesc Num; + FormatNode *format; + text *result; + int flag=0; + int len=0, plen=0, sign=0; + char *numstr, *orgnum, *p; + + NUM_TOCHAR_prepare; + + /* ---------- + * On DateType depend part (numeric) + * ---------- + */ + if (IS_ROMAN(&Num)) { + numstr = orgnum = int_to_roman( numeric_int4( numeric_round(value, 0))); + + } else { + Numeric val = value; + + if (IS_MULTI(&Num)) { + val = numeric_mul(value, + numeric_power(int4_numeric(10), int4_numeric(Num.multi))); + Num.pre += Num.multi; + } + + orgnum = numeric_out( numeric_round(val, Num.pos) ); + if (*orgnum == '-') { /* < 0 */ + sign = '-'; + numstr = orgnum+1; + } else { + sign = '+'; + numstr = orgnum; + } + if ((p = strchr(numstr, '.'))) + len = p - numstr; + else + len = strlen(numstr); + + if (Num.pre > len) + plen = Num.pre - len; + + else if (len > Num.pre) { + fill_str(numstr, '#', Num.pre); + *(numstr + Num.pre) = '.'; + fill_str(numstr + 1 + Num.pre, '#', Num.pos); + } + } + /*dump_node(format, VARSIZE(fmt) - VARHDRSZ);*/ + NUM_TOCHAR_finish; + return result; +} + +/* --------------- + * INT4 to_char() + * --------------- + */ +text * +int4_to_char(int32 value, text *fmt) +{ + static FormatNode CacheFormat[ NUM_CACHE_SIZE +1]; + static char CacheStr[ NUM_CACHE_SIZE +1]; + static NUMDesc CacheNum; + + NUMDesc Num; + FormatNode *format; + text *result; + int flag=0; + int len=0, plen=0, sign=0; + char *numstr, *orgnum; + + NUM_TOCHAR_prepare; + + /* ---------- + * On DateType depend part (int32) + * ---------- + */ + if (IS_ROMAN(&Num)) { + numstr = orgnum = int_to_roman( value ); + + } else { + if (IS_MULTI(&Num)) { + orgnum = int4out(int4mul(value, (int32) pow( (double)10, (double) Num.multi))); + Num.pre += Num.multi; + } else + orgnum = int4out(value); + len = strlen(orgnum); + + if (*orgnum == '-') { /* < 0 */ + sign = '-'; + --len; + } else + sign = '+'; + + if (Num.pos) { + int i; + + numstr = palloc( len + 1 + Num.pos ); + strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0)); + *(numstr + len) = '.'; + + for(i=len+1; i<=Num.pos+len+1; i++) + *(numstr+i) = '0'; + *(numstr + Num.pos + len + 1) = '\0'; + pfree(orgnum); + orgnum = numstr; + } else + numstr = orgnum + (*orgnum == '-' ? 1 : 0); + + if (Num.pre > len) + plen = Num.pre - len; + else if (len > Num.pre) { + fill_str(numstr, '#', Num.pre); + *(numstr + Num.pre) = '.'; + fill_str(numstr + 1 + Num.pre, '#', Num.pos); + } + } + + /*dump_node(format, len);*/ + + NUM_TOCHAR_finish; + return result; +} + +/* --------------- + * INT8 to_char() + * --------------- + */ +text * +int8_to_char(int64 *value, text *fmt) +{ + static FormatNode CacheFormat[ NUM_CACHE_SIZE +1]; + static char CacheStr[ NUM_CACHE_SIZE +1]; + static NUMDesc CacheNum; + + NUMDesc Num; + FormatNode *format; + text *result; + int flag=0; + int len=0, plen=0, sign=0; + char *numstr, *orgnum; + + NUM_TOCHAR_prepare; + + /* ---------- + * On DateType depend part (int32) + * ---------- + */ + if (IS_ROMAN(&Num)) { + numstr = orgnum = int_to_roman( int84( value )); + + } else { + if (IS_MULTI(&Num)) { + double multi = pow( (double)10, (double) Num.multi); + orgnum = int8out( int8mul(value, dtoi8( (float64) &multi ))); + Num.pre += Num.multi; + } else + orgnum = int8out(value); + len = strlen(orgnum); + + if (*orgnum == '-') { /* < 0 */ + sign = '-'; + --len; + } else + sign = '+'; + + if (Num.pos) { + int i; + + numstr = palloc( len + 1 + Num.pos ); + strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0)); + *(numstr + len) = '.'; + + for(i=len+1; i<=Num.pos+len+1; i++) + *(numstr+i) = '0'; + *(numstr + Num.pos + len + 1) = '\0'; + pfree(orgnum); + orgnum = numstr; + } else + numstr = orgnum + (*orgnum == '-' ? 1 : 0); + + if (Num.pre > len) + plen = Num.pre - len; + else if (len > Num.pre) { + fill_str(numstr, '#', Num.pre); + *(numstr + Num.pre) = '.'; + fill_str(numstr + 1 + Num.pre, '#', Num.pos); + } + } + + NUM_TOCHAR_finish; + return result; +} + +/* ----------------- + * FLOAT4 to_char() + * ----------------- + */ +text * +float4_to_char(float32 value, text *fmt) +{ + static FormatNode CacheFormat[ NUM_CACHE_SIZE +1]; + static char CacheStr[ NUM_CACHE_SIZE +1]; + static NUMDesc CacheNum; + + NUMDesc Num; + FormatNode *format; + text *result; + int flag=0; + int len=0, plen=0, sign=0; + char *numstr, *orgnum, *p; + + NUM_TOCHAR_prepare; + + if (IS_ROMAN(&Num)) { + numstr = orgnum = int_to_roman( (int) rint( *value )); + + } else { + float32 val = value; + + if (IS_MULTI(&Num)) { + float multi = pow( (double) 10, (double) Num.multi); + val = float4mul(value, (float32) &multi); + Num.pre += Num.multi; + } + + orgnum = (char *) palloc(MAXFLOATWIDTH + 1); + len = sprintf(orgnum, "%.0f", fabs(*val)); + if (Num.pre > len) + plen = Num.pre - len; + if (len >= FLT_DIG) + Num.pos = 0; + else if (Num.pos + len > FLT_DIG) + Num.pos = FLT_DIG - len; + sprintf(orgnum, "%.*f", Num.pos, *val); + + if (*orgnum == '-') { /* < 0 */ + sign = '-'; + numstr = orgnum+1; + } else { + sign = '+'; + numstr = orgnum; + } + if ((p = strchr(numstr, '.'))) + len = p - numstr; + else + len = strlen(numstr); + + if (Num.pre > len) + plen = Num.pre - len; + + else if (len > Num.pre) { + fill_str(numstr, '#', Num.pre); + *(numstr + Num.pre) = '.'; + fill_str(numstr + 1 + Num.pre, '#', Num.pos); + } + } + + NUM_TOCHAR_finish; + return result; +} + +/* ----------------- + * FLOAT8 to_char() + * ----------------- + */ +text * +float8_to_char(float64 value, text *fmt) +{ + static FormatNode CacheFormat[ NUM_CACHE_SIZE +1]; + static char CacheStr[ NUM_CACHE_SIZE +1]; + static NUMDesc CacheNum; + + NUMDesc Num; + FormatNode *format; + text *result; + int flag=0; + int len=0, plen=0, sign=0; + char *numstr, *orgnum, *p; + + NUM_TOCHAR_prepare; + + if (IS_ROMAN(&Num)) { + numstr = orgnum = int_to_roman( (int) rint( *value )); + + } else { + float64 val = value; + + if (IS_MULTI(&Num)) { + double multi = pow( (double) 10, (double) Num.multi); + val = float8mul(value, (float64) &multi); + Num.pre += Num.multi; + } + orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1); + len = sprintf(orgnum, "%.0f", fabs(*val)); + if (Num.pre > len) + plen = Num.pre - len; + if (len >= DBL_DIG) + Num.pos = 0; + else if (Num.pos + len > DBL_DIG) + Num.pos = DBL_DIG - len; + sprintf(orgnum, "%.*f", Num.pos, *val); + + if (*orgnum == '-') { /* < 0 */ + sign = '-'; + numstr = orgnum+1; + } else { + sign = '+'; + numstr = orgnum; + } + if ((p = strchr(numstr, '.'))) + len = p - numstr; + else + len = strlen(numstr); + + if (Num.pre > len) + plen = Num.pre - len; + + else if (len > Num.pre) { + fill_str(numstr, '#', Num.pre); + *(numstr + Num.pre) = '.'; + fill_str(numstr + 1 + Num.pre, '#', Num.pos); + } + } + + NUM_TOCHAR_finish; + return result; +} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index d731d939c5..872057b4ce 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.116 2000/01/24 07:16:52 tgl Exp $ + * $Id: pg_proc.h,v 1.117 2000/01/25 23:53:52 momjian Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2343,6 +2343,30 @@ DESCR("larger of two numbers"); DATA(insert OID = 1769 ( numeric_cmp PGUID 11 f t t 2 f 23 "1700 1700" 100 0 0 100 numeric_cmp - )); DESCR("compare two numbers"); +/* formatting */ +DATA(insert OID = 1770 ( to_char PGUID 11 f t f 2 f 25 "1184 25" 100 0 0 100 datetime_to_char - )); +DESCR("convert / formatting datetime to text"); +DATA(insert OID = 1771 ( to_char PGUID 11 f t f 2 f 25 "1296 25" 100 0 0 100 timestamp_to_char - )); +DESCR("convert / formatting timestamp to text"); +DATA(insert OID = 1772 ( to_char PGUID 11 f t f 2 f 25 "1700 25" 100 0 0 100 numeric_to_char - )); +DESCR("convert / formatting numeric to text"); +DATA(insert OID = 1773 ( to_char PGUID 11 f t f 2 f 25 "23 25" 100 0 0 100 int4_to_char - )); +DESCR("convert / formatting int4 to text"); +DATA(insert OID = 1774 ( to_char PGUID 11 f t f 2 f 25 "20 25" 100 0 0 100 int8_to_char - )); +DESCR("convert / formatting int8 to text"); +DATA(insert OID = 1775 ( to_char PGUID 11 f t f 2 f 25 "700 25" 100 0 0 100 float4_to_char - )); +DESCR("convert / formatting float4 to text"); +DATA(insert OID = 1776 ( to_char PGUID 11 f t f 2 f 25 "701 25" 100 0 0 100 float8_to_char - )); +DESCR("convert / formatting float8 to text"); +DATA(insert OID = 1777 ( to_number PGUID 11 f t f 2 f 1700 "25 25" 100 0 0 100 numeric_to_number - )); +DESCR("convert text to numeric"); +DATA(insert OID = 1778 ( to_datetime PGUID 11 f t f 2 f 1184 "25 25" 100 0 0 100 to_datetime - )); +DESCR("convert text to datetime"); +DATA(insert OID = 1779 ( to_timestamp PGUID 11 f t f 2 f 1296 "25 25" 100 0 0 100 to_timestamp - )); +DESCR("convert text to datetime"); +DATA(insert OID = 1780 ( to_date PGUID 11 f t f 2 f 1082 "25 25" 100 0 0 100 to_date - )); +DESCR("convert text to date"); + /* * prototypes for functions pg_proc.c diff --git a/src/include/utils/formatting.h b/src/include/utils/formatting.h new file mode 100644 index 0000000000..b5b1a5ff1f --- /dev/null +++ b/src/include/utils/formatting.h @@ -0,0 +1,31 @@ + +/* ----------------------------------------------------------------------- + * formatting.h + * + * $Id: formatting.h,v 1.1 2000/01/25 23:53:56 momjian Exp $ + * + * + * The PostgreSQL routines for a DateTime/int/float/numeric formatting, + * inspire with Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines. + * + * 1999 Karel Zak "Zakkr" + * + * ----------------------------------------------------------------------- + */ + +#ifndef _FORMATTING_H_ +#define _FORMATTING_H_ + +extern text *datetime_to_char(DateTime *dt, text *fmt); +extern text *timestamp_to_char(time_t dt, text *fmt); +extern DateTime *to_datetime(text *date_str, text *fmt); +extern time_t to_timestamp(text *date_str, text *fmt); +extern DateADT to_date(text *date_str, text *fmt); +extern Numeric numeric_to_number(text *value, text *fmt); +extern text *numeric_to_char(Numeric value, text *fmt); +extern text *int4_to_char(int32 value, text *fmt); +extern text *int8_to_char(int64 *value, text *fmt); +extern text *float4_to_char(float32 value, text *fmt); +extern text *float8_to_char(float64 value, text *fmt); + +#endif |