Commit 1c5aec60 authored by Bruce Momjian's avatar Bruce Momjian

I finish devel. of Oracle compatible DateTime routines TO_CHAR(),

TO_DATE()
and PgSQL extension FROM_CHAR().

TO_CHAR() routine allow formating text output with a datetime values:

        SELECT TO_CHAR('now'::datetime, '"Now is: "HH24:MI:SS');
        to_char
        ----------------
        Now is: 21:04:10

FROM_CHAR() routine allow convert text to a datetime:

        SELECT FROM_CHAR('September 1999 10:20:30', 'FMMonth YYYY
HH:MI:SS');
        from_char
        -----------------------------
        Wed Sep 01 10:20:30 1999 CEST

TO_DATE() is equal with FROM_CHAR(), but output a Date only:

        SELECT TO_DATE('September 1999 10:20:30', 'FMMonth YYYY
HH:MI:SS');
        to_date
        ----------
        09-01-1999


In attache is compressed dir for the contrib. All is prepared, but I'am
not
sure if Makefile is good (probably yes).

Comments & suggestions ?


Thomas, thank you for your good advices.

                                                        Karel


------------------------------------------------------------------------------

Karel Zak <zakkr@zf.jcu.cz>
http://home.zf.jcu.cz/~zakkr/
parent 1f747c67
......@@ -10,6 +10,14 @@ array -
Array iterator functions
by Massimo Dal Zotto <dz@cs.unitn.it>
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>
......
#-------------------------------------------------------------------------
#
# 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
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)
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 (?).
#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
---
--- 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";
/******************************************************************
*
* 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;
}
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
#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
-- 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment