
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/
1383 lines
33 KiB
C
1383 lines
33 KiB
C
|
|
/******************************************************************
|
|
*
|
|
* 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;
|
|
}
|