From b866d2e2d79416f8497e4dffa7e800298d018f6c Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Tue, 25 Jan 2000 23:53:56 +0000
Subject: [PATCH]  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/
---
 contrib/README                          |    4 -
 contrib/dateformat/Makefile             |   71 -
 contrib/dateformat/test/Makefile        |   25 -
 contrib/dateformat/test/README          |   33 -
 contrib/dateformat/test/rand_datetime.c |   71 -
 contrib/dateformat/test/regress.sql     |   58 -
 contrib/dateformat/to-from_char.c       | 1382 ----------
 contrib/dateformat/to-from_char.doc     |  183 --
 contrib/dateformat/to-from_char.h       |   18 -
 contrib/dateformat/to-from_char.sql.in  |   29 -
 doc/src/sgml/func.sgml                  |  541 ++++
 src/backend/utils/adt/Makefile          |    4 +-
 src/backend/utils/adt/formatting.c      | 3157 +++++++++++++++++++++++
 src/include/catalog/pg_proc.h           |   26 +-
 src/include/utils/formatting.h          |   31 +
 15 files changed, 3756 insertions(+), 1877 deletions(-)
 delete mode 100644 contrib/dateformat/Makefile
 delete mode 100644 contrib/dateformat/test/Makefile
 delete mode 100644 contrib/dateformat/test/README
 delete mode 100644 contrib/dateformat/test/rand_datetime.c
 delete mode 100644 contrib/dateformat/test/regress.sql
 delete mode 100644 contrib/dateformat/to-from_char.c
 delete mode 100644 contrib/dateformat/to-from_char.doc
 delete mode 100644 contrib/dateformat/to-from_char.h
 delete mode 100644 contrib/dateformat/to-from_char.sql.in
 create mode 100644 src/backend/utils/adt/formatting.c
 create mode 100644 src/include/utils/formatting.h

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
-- 
2.40.0