2010-09-20 22:08:53 +02:00
|
|
|
/* src/interfaces/ecpg/pgtypeslib/datetime.c */
|
2006-03-11 05:38:42 +01:00
|
|
|
|
2003-07-02 09:57:36 +02:00
|
|
|
#include "postgres_fe.h"
|
|
|
|
|
2003-03-20 16:56:50 +01:00
|
|
|
#include <time.h>
|
2003-07-02 09:57:36 +02:00
|
|
|
#include <ctype.h>
|
2003-10-07 10:07:56 +02:00
|
|
|
#include <limits.h>
|
2003-03-20 16:56:50 +01:00
|
|
|
|
2003-06-25 19:55:50 +02:00
|
|
|
#include "dt.h"
|
2003-03-20 16:56:50 +01:00
|
|
|
#include "pgtypes_date.h"
|
2019-10-23 06:08:53 +02:00
|
|
|
#include "pgtypes_error.h"
|
|
|
|
#include "pgtypeslib_extern.h"
|
2003-03-30 13:48:19 +02:00
|
|
|
|
2006-09-14 10:02:38 +02:00
|
|
|
date *
|
|
|
|
PGTYPESdate_new(void)
|
|
|
|
{
|
|
|
|
date *result;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-09-14 10:02:38 +02:00
|
|
|
result = (date *) pgtypes_alloc(sizeof(date));
|
|
|
|
/* result can be NULL if we run out of memory */
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PGTYPESdate_free(date * d)
|
|
|
|
{
|
|
|
|
free(d);
|
|
|
|
}
|
|
|
|
|
2003-09-09 12:46:42 +02:00
|
|
|
date
|
|
|
|
PGTYPESdate_from_timestamp(timestamp dt)
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
2003-09-09 12:46:42 +02:00
|
|
|
date dDate;
|
2003-03-30 13:48:19 +02:00
|
|
|
|
|
|
|
dDate = 0; /* suppress compiler warning */
|
|
|
|
|
2006-05-31 10:12:48 +02:00
|
|
|
if (!TIMESTAMP_NOT_FINITE(dt))
|
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
/* Microseconds to days */
|
2006-05-31 10:12:48 +02:00
|
|
|
dDate = (dt / USECS_PER_DAY);
|
|
|
|
}
|
2003-03-30 13:48:19 +02:00
|
|
|
|
|
|
|
return dDate;
|
|
|
|
}
|
2003-03-20 16:56:50 +01:00
|
|
|
|
2003-09-09 12:46:42 +02:00
|
|
|
date
|
2003-04-01 16:37:25 +02:00
|
|
|
PGTYPESdate_from_asc(char *str, char **endptr)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
2003-09-09 12:46:42 +02:00
|
|
|
date dDate;
|
2003-03-20 16:56:50 +01:00
|
|
|
fsec_t fsec;
|
|
|
|
struct tm tt,
|
|
|
|
*tm = &tt;
|
|
|
|
int dtype;
|
|
|
|
int nf;
|
|
|
|
char *field[MAXDATEFIELDS];
|
|
|
|
int ftype[MAXDATEFIELDS];
|
2014-02-17 15:33:31 +01:00
|
|
|
char lowstr[MAXDATELEN + MAXDATEFIELDS];
|
2003-03-20 16:56:50 +01:00
|
|
|
char *realptr;
|
|
|
|
char **ptr = (endptr != NULL) ? endptr : &realptr;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2017-08-16 06:22:32 +02:00
|
|
|
bool EuroDates = false;
|
2003-03-20 16:56:50 +01:00
|
|
|
|
2003-04-08 14:34:25 +02:00
|
|
|
errno = 0;
|
2014-02-17 15:33:31 +01:00
|
|
|
if (strlen(str) > MAXDATELEN)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_DATE_BAD_DATE;
|
2003-10-07 07:46:56 +02:00
|
|
|
return INT_MIN;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
2009-05-20 18:13:18 +02:00
|
|
|
if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
|
2007-08-14 12:01:54 +02:00
|
|
|
DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, EuroDates) != 0)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_DATE_BAD_DATE;
|
2003-10-07 07:46:56 +02:00
|
|
|
return INT_MIN;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (dtype)
|
|
|
|
{
|
|
|
|
case DTK_DATE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DTK_EPOCH:
|
2009-02-04 09:51:10 +01:00
|
|
|
if (GetEpochTime(tm) < 0)
|
|
|
|
{
|
|
|
|
errno = PGTYPES_DATE_BAD_DATE;
|
|
|
|
return INT_MIN;
|
|
|
|
}
|
2003-03-20 16:56:50 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_DATE_BAD_DATE;
|
2003-10-07 07:46:56 +02:00
|
|
|
return INT_MIN;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dDate = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
|
|
|
|
|
|
|
|
return dDate;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
2003-09-09 12:46:42 +02:00
|
|
|
PGTYPESdate_to_asc(date dDate)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
|
|
|
struct tm tt,
|
|
|
|
*tm = &tt;
|
|
|
|
char buf[MAXDATELEN + 1];
|
2003-07-04 14:00:52 +02:00
|
|
|
int DateStyle = 1;
|
2017-08-16 06:22:32 +02:00
|
|
|
bool EuroDates = false;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2005-05-24 04:09:45 +02:00
|
|
|
j2date(dDate + date2j(2000, 1, 1), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
|
2003-03-20 16:56:50 +01:00
|
|
|
EncodeDateOnly(tm, DateStyle, buf, EuroDates);
|
|
|
|
return pgtypes_strdup(buf);
|
|
|
|
}
|
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
void
|
2003-09-09 12:46:42 +02:00
|
|
|
PGTYPESdate_julmdy(date jd, int *mdy)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
int y,
|
|
|
|
m,
|
|
|
|
d;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-07-24 10:41:07 +02:00
|
|
|
j2date((int) (jd + date2j(2000, 1, 1)), &y, &m, &d);
|
2003-07-17 09:54:29 +02:00
|
|
|
mdy[0] = m;
|
|
|
|
mdy[1] = d;
|
|
|
|
mdy[2] = y;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
void
|
2003-09-09 12:46:42 +02:00
|
|
|
PGTYPESdate_mdyjul(int *mdy, date * jdate)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
|
|
|
/* month is mdy[0] */
|
|
|
|
/* day is mdy[1] */
|
|
|
|
/* year is mdy[2] */
|
|
|
|
|
2003-09-09 12:46:42 +02:00
|
|
|
*jdate = (date) (date2j(mdy[2], mdy[0], mdy[1]) - date2j(2000, 1, 1));
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
2003-03-21 15:17:47 +01:00
|
|
|
int
|
2003-09-09 12:46:42 +02:00
|
|
|
PGTYPESdate_dayofweek(date dDate)
|
2003-03-21 15:17:47 +01:00
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
2003-10-05 13:12:00 +02:00
|
|
|
* Sunday: 0 Monday: 1 Tuesday: 2 Wednesday: 3 Thursday: 4
|
2003-03-30 13:48:19 +02:00
|
|
|
* Friday: 5 Saturday: 6
|
|
|
|
*/
|
2003-10-05 13:12:00 +02:00
|
|
|
return (int) (dDate + date2j(2000, 1, 1) + 1) % 7;
|
2003-03-21 15:17:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2003-09-09 12:46:42 +02:00
|
|
|
PGTYPESdate_today(date * d)
|
2003-03-21 15:17:47 +01:00
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
struct tm ts;
|
|
|
|
|
|
|
|
GetCurrentDateTime(&ts);
|
2009-02-04 09:51:10 +01:00
|
|
|
if (errno == 0)
|
|
|
|
*d = date2j(ts.tm_year, ts.tm_mon, ts.tm_mday) - date2j(2000, 1, 1);
|
2003-03-21 15:17:47 +01:00
|
|
|
}
|
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
#define PGTYPES_DATE_NUM_MAX_DIGITS 20 /* should suffice for most
|
|
|
|
* years... */
|
2003-03-21 15:17:47 +01:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
#define PGTYPES_FMTDATE_DAY_DIGITS_LZ 1 /* LZ means "leading zeroes" */
|
|
|
|
#define PGTYPES_FMTDATE_DOW_LITERAL_SHORT 2
|
|
|
|
#define PGTYPES_FMTDATE_MONTH_DIGITS_LZ 3
|
|
|
|
#define PGTYPES_FMTDATE_MONTH_LITERAL_SHORT 4
|
|
|
|
#define PGTYPES_FMTDATE_YEAR_DIGITS_SHORT 5
|
|
|
|
#define PGTYPES_FMTDATE_YEAR_DIGITS_LONG 6
|
2003-03-21 15:17:47 +01:00
|
|
|
|
|
|
|
int
|
2011-12-18 19:04:19 +01:00
|
|
|
PGTYPESdate_fmt_asc(date dDate, const char *fmtstring, char *outbuf)
|
2003-04-01 16:37:25 +02:00
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
static struct
|
|
|
|
{
|
|
|
|
char *format;
|
|
|
|
int component;
|
|
|
|
} mapping[] =
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* format items have to be sorted according to their length, since the
|
|
|
|
* first pattern that matches gets replaced by its value
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
"ddd", PGTYPES_FMTDATE_DOW_LITERAL_SHORT
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"dd", PGTYPES_FMTDATE_DAY_DIGITS_LZ
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"mmm", PGTYPES_FMTDATE_MONTH_LITERAL_SHORT
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"mm", PGTYPES_FMTDATE_MONTH_DIGITS_LZ
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"yyyy", PGTYPES_FMTDATE_YEAR_DIGITS_LONG
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"yy", PGTYPES_FMTDATE_YEAR_DIGITS_SHORT
|
|
|
|
},
|
|
|
|
{
|
|
|
|
NULL, 0
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2003-08-01 10:21:04 +02:00
|
|
|
union un_fmt_comb replace_val;
|
2003-03-30 13:48:19 +02:00
|
|
|
int replace_type;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int dow;
|
|
|
|
char *start_pattern;
|
|
|
|
struct tm tm;
|
|
|
|
|
|
|
|
/* copy the string over */
|
|
|
|
strcpy(outbuf, fmtstring);
|
|
|
|
|
|
|
|
/* get the date */
|
2005-05-24 04:09:45 +02:00
|
|
|
j2date(dDate + date2j(2000, 1, 1), &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
|
2003-03-30 13:48:19 +02:00
|
|
|
dow = PGTYPESdate_dayofweek(dDate);
|
|
|
|
|
|
|
|
for (i = 0; mapping[i].format != NULL; i++)
|
|
|
|
{
|
|
|
|
while ((start_pattern = strstr(outbuf, mapping[i].format)) != NULL)
|
|
|
|
{
|
|
|
|
switch (mapping[i].component)
|
|
|
|
{
|
|
|
|
case PGTYPES_FMTDATE_DOW_LITERAL_SHORT:
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = pgtypes_date_weekdays_short[dow];
|
|
|
|
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_DAY_DIGITS_LZ:
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm.tm_mday;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_MONTH_LITERAL_SHORT:
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = months[tm.tm_mon - 1];
|
|
|
|
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_MONTH_DIGITS_LZ:
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm.tm_mon;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_YEAR_DIGITS_LONG:
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm.tm_year;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_4_LZ;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_YEAR_DIGITS_SHORT:
|
2006-08-15 08:40:20 +02:00
|
|
|
replace_val.uint_val = tm.tm_year % 100;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
default:
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
|
|
|
* should not happen, set something anyway
|
|
|
|
*/
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = " ";
|
|
|
|
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
|
2003-03-30 13:48:19 +02:00
|
|
|
}
|
|
|
|
switch (replace_type)
|
|
|
|
{
|
2003-08-01 10:21:04 +02:00
|
|
|
case PGTYPES_TYPE_STRING_MALLOCED:
|
|
|
|
case PGTYPES_TYPE_STRING_CONSTANT:
|
Replace a bunch more uses of strncpy() with safer coding.
strncpy() has a well-deserved reputation for being unsafe, so make an
effort to get rid of nearly all occurrences in HEAD.
A large fraction of the remaining uses were passing length less than or
equal to the known strlen() of the source, in which case no null-padding
can occur and the behavior is equivalent to memcpy(), though doubtless
slower and certainly harder to reason about. So just use memcpy() in
these cases.
In other cases, use either StrNCpy() or strlcpy() as appropriate (depending
on whether padding to the full length of the destination buffer seems
useful).
I left a few strncpy() calls alone in the src/timezone/ code, to keep it
in sync with upstream (the IANA tzcode distribution). There are also a
few such calls in ecpg that could possibly do with more analysis.
AFAICT, none of these changes are more than cosmetic, except for the four
occurrences in fe-secure-openssl.c, which are in fact buggy: an overlength
source leads to a non-null-terminated destination buffer and ensuing
misbehavior. These don't seem like security issues, first because no stack
clobber is possible and second because if your values of sslcert etc are
coming from untrusted sources then you've got problems way worse than this.
Still, it's undesirable to have unpredictable behavior for overlength
inputs, so back-patch those four changes to all active branches.
2015-01-24 19:05:42 +01:00
|
|
|
memcpy(start_pattern, replace_val.str_val,
|
|
|
|
strlen(replace_val.str_val));
|
2003-08-01 10:21:04 +02:00
|
|
|
if (replace_type == PGTYPES_TYPE_STRING_MALLOCED)
|
|
|
|
free(replace_val.str_val);
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
2003-08-01 10:21:04 +02:00
|
|
|
case PGTYPES_TYPE_UINT:
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
if (!t)
|
|
|
|
return -1;
|
|
|
|
snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
|
2003-08-01 10:21:04 +02:00
|
|
|
"%u", replace_val.uint_val);
|
Replace a bunch more uses of strncpy() with safer coding.
strncpy() has a well-deserved reputation for being unsafe, so make an
effort to get rid of nearly all occurrences in HEAD.
A large fraction of the remaining uses were passing length less than or
equal to the known strlen() of the source, in which case no null-padding
can occur and the behavior is equivalent to memcpy(), though doubtless
slower and certainly harder to reason about. So just use memcpy() in
these cases.
In other cases, use either StrNCpy() or strlcpy() as appropriate (depending
on whether padding to the full length of the destination buffer seems
useful).
I left a few strncpy() calls alone in the src/timezone/ code, to keep it
in sync with upstream (the IANA tzcode distribution). There are also a
few such calls in ecpg that could possibly do with more analysis.
AFAICT, none of these changes are more than cosmetic, except for the four
occurrences in fe-secure-openssl.c, which are in fact buggy: an overlength
source leads to a non-null-terminated destination buffer and ensuing
misbehavior. These don't seem like security issues, first because no stack
clobber is possible and second because if your values of sslcert etc are
coming from untrusted sources then you've got problems way worse than this.
Still, it's undesirable to have unpredictable behavior for overlength
inputs, so back-patch those four changes to all active branches.
2015-01-24 19:05:42 +01:00
|
|
|
memcpy(start_pattern, t, strlen(t));
|
2003-03-30 13:48:19 +02:00
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
break;
|
2003-08-01 10:21:04 +02:00
|
|
|
case PGTYPES_TYPE_UINT_2_LZ:
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
if (!t)
|
|
|
|
return -1;
|
|
|
|
snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
|
2003-08-01 10:21:04 +02:00
|
|
|
"%02u", replace_val.uint_val);
|
Replace a bunch more uses of strncpy() with safer coding.
strncpy() has a well-deserved reputation for being unsafe, so make an
effort to get rid of nearly all occurrences in HEAD.
A large fraction of the remaining uses were passing length less than or
equal to the known strlen() of the source, in which case no null-padding
can occur and the behavior is equivalent to memcpy(), though doubtless
slower and certainly harder to reason about. So just use memcpy() in
these cases.
In other cases, use either StrNCpy() or strlcpy() as appropriate (depending
on whether padding to the full length of the destination buffer seems
useful).
I left a few strncpy() calls alone in the src/timezone/ code, to keep it
in sync with upstream (the IANA tzcode distribution). There are also a
few such calls in ecpg that could possibly do with more analysis.
AFAICT, none of these changes are more than cosmetic, except for the four
occurrences in fe-secure-openssl.c, which are in fact buggy: an overlength
source leads to a non-null-terminated destination buffer and ensuing
misbehavior. These don't seem like security issues, first because no stack
clobber is possible and second because if your values of sslcert etc are
coming from untrusted sources then you've got problems way worse than this.
Still, it's undesirable to have unpredictable behavior for overlength
inputs, so back-patch those four changes to all active branches.
2015-01-24 19:05:42 +01:00
|
|
|
memcpy(start_pattern, t, strlen(t));
|
2003-03-30 13:48:19 +02:00
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
break;
|
2003-08-01 10:21:04 +02:00
|
|
|
case PGTYPES_TYPE_UINT_4_LZ:
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
if (!t)
|
|
|
|
return -1;
|
|
|
|
snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
|
2003-08-01 10:21:04 +02:00
|
|
|
"%04u", replace_val.uint_val);
|
Replace a bunch more uses of strncpy() with safer coding.
strncpy() has a well-deserved reputation for being unsafe, so make an
effort to get rid of nearly all occurrences in HEAD.
A large fraction of the remaining uses were passing length less than or
equal to the known strlen() of the source, in which case no null-padding
can occur and the behavior is equivalent to memcpy(), though doubtless
slower and certainly harder to reason about. So just use memcpy() in
these cases.
In other cases, use either StrNCpy() or strlcpy() as appropriate (depending
on whether padding to the full length of the destination buffer seems
useful).
I left a few strncpy() calls alone in the src/timezone/ code, to keep it
in sync with upstream (the IANA tzcode distribution). There are also a
few such calls in ecpg that could possibly do with more analysis.
AFAICT, none of these changes are more than cosmetic, except for the four
occurrences in fe-secure-openssl.c, which are in fact buggy: an overlength
source leads to a non-null-terminated destination buffer and ensuing
misbehavior. These don't seem like security issues, first because no stack
clobber is possible and second because if your values of sslcert etc are
coming from untrusted sources then you've got problems way worse than this.
Still, it's undesirable to have unpredictable behavior for overlength
inputs, so back-patch those four changes to all active branches.
2015-01-24 19:05:42 +01:00
|
|
|
memcpy(start_pattern, t, strlen(t));
|
2003-03-30 13:48:19 +02:00
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
|
|
|
* doesn't happen (we set replace_type to
|
2003-08-01 10:21:04 +02:00
|
|
|
* PGTYPES_TYPE_STRING_CONSTANT in case of an error above)
|
2003-03-30 13:48:19 +02:00
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-03-21 15:17:47 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
|
|
|
|
/*
|
2003-04-01 16:37:25 +02:00
|
|
|
* PGTYPESdate_defmt_asc
|
2003-03-30 13:48:19 +02:00
|
|
|
*
|
|
|
|
* function works as follows:
|
2015-04-26 18:42:31 +02:00
|
|
|
* - first we analyze the parameters
|
2017-02-06 10:33:58 +01:00
|
|
|
* - if this is a special case with no delimiters, add delimiters
|
2003-03-30 13:48:19 +02:00
|
|
|
* - find the tokens. First we look for numerical values. If we have found
|
|
|
|
* less than 3 tokens, we check for the months' names and thereafter for
|
|
|
|
* the abbreviations of the months' names.
|
|
|
|
* - then we see which parameter should be the date, the month and the
|
|
|
|
* year and from these values we calculate the date
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define PGTYPES_DATE_MONTH_MAXLENGTH 20 /* probably even less :-) */
|
2003-03-21 15:17:47 +01:00
|
|
|
int
|
2017-10-31 15:34:31 +01:00
|
|
|
PGTYPESdate_defmt_asc(date * d, const char *fmt, const char *str)
|
2003-03-21 15:17:47 +01:00
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
|
|
|
* token[2] = { 4,6 } means that token 2 starts at position 4 and ends at
|
|
|
|
* (including) position 6
|
|
|
|
*/
|
|
|
|
int token[3][2];
|
|
|
|
int token_values[3] = {-1, -1, -1};
|
|
|
|
char *fmt_token_order;
|
|
|
|
char *fmt_ystart,
|
|
|
|
*fmt_mstart,
|
|
|
|
*fmt_dstart;
|
2009-05-20 18:49:23 +02:00
|
|
|
unsigned int i;
|
2003-03-30 13:48:19 +02:00
|
|
|
int reading_digit;
|
|
|
|
int token_count;
|
|
|
|
char *str_copy;
|
|
|
|
struct tm tm;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2005-09-25 00:54:44 +02:00
|
|
|
tm.tm_year = tm.tm_mon = tm.tm_mday = 0; /* keep compiler quiet */
|
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
if (!d || !str || !fmt)
|
|
|
|
{
|
|
|
|
errno = PGTYPES_DATE_ERR_EARGS;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* analyze the fmt string */
|
|
|
|
fmt_ystart = strstr(fmt, "yy");
|
|
|
|
fmt_mstart = strstr(fmt, "mm");
|
|
|
|
fmt_dstart = strstr(fmt, "dd");
|
|
|
|
|
|
|
|
if (!fmt_ystart || !fmt_mstart || !fmt_dstart)
|
|
|
|
{
|
|
|
|
errno = PGTYPES_DATE_ERR_EARGS;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmt_ystart < fmt_mstart)
|
|
|
|
{
|
|
|
|
/* y m */
|
|
|
|
if (fmt_dstart < fmt_ystart)
|
|
|
|
{
|
|
|
|
/* d y m */
|
|
|
|
fmt_token_order = "dym";
|
|
|
|
}
|
|
|
|
else if (fmt_dstart > fmt_mstart)
|
|
|
|
{
|
|
|
|
/* y m d */
|
|
|
|
fmt_token_order = "ymd";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* y d m */
|
|
|
|
fmt_token_order = "ydm";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* fmt_ystart > fmt_mstart */
|
|
|
|
/* m y */
|
|
|
|
if (fmt_dstart < fmt_mstart)
|
|
|
|
{
|
|
|
|
/* d m y */
|
|
|
|
fmt_token_order = "dmy";
|
|
|
|
}
|
|
|
|
else if (fmt_dstart > fmt_ystart)
|
|
|
|
{
|
|
|
|
/* m y d */
|
|
|
|
fmt_token_order = "myd";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* m d y */
|
|
|
|
fmt_token_order = "mdy";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* handle the special cases where there is no delimiter between the
|
|
|
|
* digits. If we see this:
|
|
|
|
*
|
|
|
|
* only digits, 6 or 8 bytes then it might be ddmmyy and ddmmyyyy (or
|
|
|
|
* similar)
|
|
|
|
*
|
|
|
|
* we reduce it to a string with delimiters and continue processing
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* check if we have only digits */
|
|
|
|
reading_digit = 1;
|
|
|
|
for (i = 0; str[i]; i++)
|
|
|
|
{
|
2004-01-04 05:17:01 +01:00
|
|
|
if (!isdigit((unsigned char) str[i]))
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
reading_digit = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (reading_digit)
|
|
|
|
{
|
|
|
|
int frag_length[3];
|
|
|
|
int target_pos;
|
|
|
|
|
|
|
|
i = strlen(str);
|
|
|
|
if (i != 8 && i != 6)
|
|
|
|
{
|
|
|
|
errno = PGTYPES_DATE_ERR_ENOSHORTDATE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* okay, this really is the special case */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* as long as the string, one additional byte for the terminator and 2
|
2017-06-15 15:45:13 +02:00
|
|
|
* for the delimiters between the 3 fields
|
2003-03-30 13:48:19 +02:00
|
|
|
*/
|
|
|
|
str_copy = pgtypes_alloc(strlen(str) + 1 + 2);
|
|
|
|
if (!str_copy)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* determine length of the fragments */
|
|
|
|
if (i == 6)
|
|
|
|
{
|
|
|
|
frag_length[0] = 2;
|
|
|
|
frag_length[1] = 2;
|
|
|
|
frag_length[2] = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (fmt_token_order[0] == 'y')
|
|
|
|
{
|
|
|
|
frag_length[0] = 4;
|
|
|
|
frag_length[1] = 2;
|
|
|
|
frag_length[2] = 2;
|
|
|
|
}
|
|
|
|
else if (fmt_token_order[1] == 'y')
|
|
|
|
{
|
|
|
|
frag_length[0] = 2;
|
|
|
|
frag_length[1] = 4;
|
|
|
|
frag_length[2] = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
frag_length[0] = 2;
|
|
|
|
frag_length[1] = 2;
|
|
|
|
frag_length[2] = 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
target_pos = 0;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
|
|
|
* XXX: Here we could calculate the positions of the tokens and save
|
|
|
|
* the for loop down there where we again check with isdigit() for
|
|
|
|
* digits.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
int start_pos = 0;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
if (i >= 1)
|
|
|
|
start_pos += frag_length[0];
|
|
|
|
if (i == 2)
|
|
|
|
start_pos += frag_length[1];
|
|
|
|
|
|
|
|
strncpy(str_copy + target_pos, str + start_pos,
|
|
|
|
frag_length[i]);
|
|
|
|
target_pos += frag_length[i];
|
|
|
|
if (i != 2)
|
|
|
|
{
|
|
|
|
str_copy[target_pos] = ' ';
|
|
|
|
target_pos++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
str_copy[target_pos] = '\0';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
str_copy = pgtypes_strdup(str);
|
|
|
|
if (!str_copy)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* convert the whole string to lower case */
|
|
|
|
for (i = 0; str_copy[i]; i++)
|
2004-05-07 02:24:59 +02:00
|
|
|
str_copy[i] = (char) pg_tolower((unsigned char) str_copy[i]);
|
2003-03-30 13:48:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* look for numerical tokens */
|
|
|
|
reading_digit = 0;
|
|
|
|
token_count = 0;
|
|
|
|
for (i = 0; i < strlen(str_copy); i++)
|
|
|
|
{
|
2004-01-04 05:17:01 +01:00
|
|
|
if (!isdigit((unsigned char) str_copy[i]) && reading_digit)
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
/* the token is finished */
|
|
|
|
token[token_count][1] = i - 1;
|
|
|
|
reading_digit = 0;
|
|
|
|
token_count++;
|
|
|
|
}
|
2004-01-04 05:17:01 +01:00
|
|
|
else if (isdigit((unsigned char) str_copy[i]) && !reading_digit)
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
/* we have found a token */
|
|
|
|
token[token_count][0] = i;
|
|
|
|
reading_digit = 1;
|
|
|
|
}
|
|
|
|
}
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
|
|
|
* we're at the end of the input string, but maybe we are still reading a
|
|
|
|
* number...
|
|
|
|
*/
|
|
|
|
if (reading_digit)
|
|
|
|
{
|
|
|
|
token[token_count][1] = i - 1;
|
|
|
|
token_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (token_count < 2)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* not all tokens found, no way to find 2 missing tokens with string
|
|
|
|
* matches
|
|
|
|
*/
|
|
|
|
free(str_copy);
|
2006-08-15 08:40:20 +02:00
|
|
|
errno = PGTYPES_DATE_ERR_ENOSHORTDATE;
|
2003-03-30 13:48:19 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (token_count != 3)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* not all tokens found but we may find another one with string
|
|
|
|
* matches by testing for the months names and months abbreviations
|
|
|
|
*/
|
|
|
|
char *month_lower_tmp = pgtypes_alloc(PGTYPES_DATE_MONTH_MAXLENGTH);
|
|
|
|
char *start_pos;
|
|
|
|
int j;
|
|
|
|
int offset;
|
|
|
|
int found = 0;
|
|
|
|
char **list;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
if (!month_lower_tmp)
|
|
|
|
{
|
|
|
|
/* free variables we alloc'ed before */
|
|
|
|
free(str_copy);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
list = pgtypes_date_months;
|
|
|
|
for (i = 0; list[i]; i++)
|
|
|
|
{
|
|
|
|
for (j = 0; j < PGTYPES_DATE_MONTH_MAXLENGTH; j++)
|
|
|
|
{
|
2004-05-07 02:24:59 +02:00
|
|
|
month_lower_tmp[j] = (char) pg_tolower((unsigned char) list[i][j]);
|
2003-03-30 13:48:19 +02:00
|
|
|
if (!month_lower_tmp[j])
|
|
|
|
{
|
|
|
|
/* properly terminated */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((start_pos = strstr(str_copy, month_lower_tmp)))
|
|
|
|
{
|
|
|
|
offset = start_pos - str_copy;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
|
|
|
* sort the new token into the numeric tokens, shift them if
|
|
|
|
* necessary
|
|
|
|
*/
|
|
|
|
if (offset < token[0][0])
|
|
|
|
{
|
|
|
|
token[2][0] = token[1][0];
|
|
|
|
token[2][1] = token[1][1];
|
|
|
|
token[1][0] = token[0][0];
|
|
|
|
token[1][1] = token[0][1];
|
|
|
|
token_count = 0;
|
|
|
|
}
|
|
|
|
else if (offset < token[1][0])
|
|
|
|
{
|
|
|
|
token[2][0] = token[1][0];
|
|
|
|
token[2][1] = token[1][1];
|
|
|
|
token_count = 1;
|
|
|
|
}
|
2003-08-04 02:43:34 +02:00
|
|
|
else
|
2003-03-30 13:48:19 +02:00
|
|
|
token_count = 2;
|
|
|
|
token[token_count][0] = offset;
|
|
|
|
token[token_count][1] = offset + strlen(month_lower_tmp) - 1;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
|
|
|
* the value is the index of the month in the array of months
|
|
|
|
* + 1 (January is month 0)
|
|
|
|
*/
|
|
|
|
token_values[token_count] = i + 1;
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
|
|
|
* evil[tm] hack: if we read the pgtypes_date_months and haven't
|
2019-08-05 05:14:58 +02:00
|
|
|
* found a match, reset list to point to months (abbreviations)
|
2003-03-30 13:48:19 +02:00
|
|
|
* and reset the counter variable i
|
|
|
|
*/
|
|
|
|
if (list == pgtypes_date_months)
|
|
|
|
{
|
|
|
|
if (list[i + 1] == NULL)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
list = months;
|
2003-03-30 13:48:19 +02:00
|
|
|
i = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
free(month_lower_tmp);
|
|
|
|
free(str_copy);
|
|
|
|
errno = PGTYPES_DATE_ERR_ENOTDMY;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* here we found a month. token[token_count] and
|
|
|
|
* token_values[token_count] reflect the month's details.
|
|
|
|
*
|
|
|
|
* only the month can be specified with a literal. Here we can do a
|
|
|
|
* quick check if the month is at the right position according to the
|
|
|
|
* format string because we can check if the token that we expect to
|
|
|
|
* be the month is at the position of the only token that already has
|
|
|
|
* a value. If we wouldn't check here we could say "December 4 1990"
|
|
|
|
* with a fmt string of "dd mm yy" for 12 April 1990.
|
|
|
|
*/
|
|
|
|
if (fmt_token_order[token_count] != 'm')
|
|
|
|
{
|
|
|
|
/* deal with the error later on */
|
|
|
|
token_values[token_count] = -1;
|
|
|
|
}
|
|
|
|
free(month_lower_tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* terminate the tokens with ASCII-0 and get their values */
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
*(str_copy + token[i][1] + 1) = '\0';
|
|
|
|
/* A month already has a value set, check for token_value == -1 */
|
|
|
|
if (token_values[i] == -1)
|
|
|
|
{
|
|
|
|
errno = 0;
|
|
|
|
token_values[i] = strtol(str_copy + token[i][0], (char **) NULL, 10);
|
|
|
|
/* strtol sets errno in case of an error */
|
|
|
|
if (errno)
|
|
|
|
token_values[i] = -1;
|
|
|
|
}
|
|
|
|
if (fmt_token_order[i] == 'd')
|
|
|
|
tm.tm_mday = token_values[i];
|
|
|
|
else if (fmt_token_order[i] == 'm')
|
|
|
|
tm.tm_mon = token_values[i];
|
|
|
|
else if (fmt_token_order[i] == 'y')
|
|
|
|
tm.tm_year = token_values[i];
|
|
|
|
}
|
|
|
|
free(str_copy);
|
|
|
|
|
|
|
|
if (tm.tm_mday < 1 || tm.tm_mday > 31)
|
|
|
|
{
|
|
|
|
errno = PGTYPES_DATE_BAD_DAY;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-07-21 05:56:25 +02:00
|
|
|
if (tm.tm_mon < 1 || tm.tm_mon > MONTHS_PER_YEAR)
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
errno = PGTYPES_DATE_BAD_MONTH;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tm.tm_mday == 31 && (tm.tm_mon == 4 || tm.tm_mon == 6 || tm.tm_mon == 9 || tm.tm_mon == 11))
|
|
|
|
{
|
|
|
|
errno = PGTYPES_DATE_BAD_DAY;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tm.tm_mon == 2 && tm.tm_mday > 29)
|
|
|
|
{
|
|
|
|
errno = PGTYPES_DATE_BAD_DAY;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*d = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - date2j(2000, 1, 1);
|
|
|
|
|
2003-03-21 15:17:47 +01:00
|
|
|
return 0;
|
|
|
|
}
|