2003-03-20 16:56:50 +01:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <float.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
2003-04-01 16:37:25 +02:00
|
|
|
#include <string.h>
|
2003-03-20 16:56:50 +01:00
|
|
|
|
|
|
|
#include "extern.h"
|
2003-06-25 19:55:50 +02:00
|
|
|
#include "dt.h"
|
2003-03-20 16:56:50 +01:00
|
|
|
#include "pgtypes_error.h"
|
|
|
|
#include "pgtypes_date.h"
|
2003-03-30 13:48:19 +02:00
|
|
|
|
|
|
|
Date
|
2003-04-01 16:37:25 +02:00
|
|
|
PGTYPESdate_from_timestamp(Timestamp dt)
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
Date dDate;
|
|
|
|
|
|
|
|
dDate = 0; /* suppress compiler warning */
|
|
|
|
|
|
|
|
if (TIMESTAMP_NOT_FINITE(dt))
|
|
|
|
return
|
|
|
|
|
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
|
|
|
/* Microseconds to days */
|
|
|
|
dDate = (dt / INT64CONST(86400000000));
|
|
|
|
#else
|
|
|
|
/* Seconds to days */
|
|
|
|
dDate = (dt / 86400.0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return dDate;
|
|
|
|
}
|
2003-03-20 16:56:50 +01: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
|
|
|
{
|
|
|
|
|
|
|
|
Date dDate;
|
|
|
|
fsec_t fsec;
|
|
|
|
struct tm tt,
|
|
|
|
*tm = &tt;
|
|
|
|
int tzp;
|
|
|
|
int dtype;
|
|
|
|
int nf;
|
|
|
|
char *field[MAXDATEFIELDS];
|
|
|
|
int ftype[MAXDATEFIELDS];
|
|
|
|
char lowstr[MAXDATELEN + 1];
|
|
|
|
char *realptr;
|
|
|
|
char **ptr = (endptr != NULL) ? endptr : &realptr;
|
|
|
|
|
|
|
|
bool EuroDates = FALSE;
|
|
|
|
|
2003-04-08 14:34:25 +02:00
|
|
|
errno = 0;
|
2003-03-20 16:56:50 +01:00
|
|
|
if (strlen(str) >= sizeof(lowstr))
|
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_DATE_BAD_DATE;
|
2003-04-08 14:34:25 +02:00
|
|
|
return 0;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0)
|
|
|
|
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp, EuroDates) != 0))
|
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_DATE_BAD_DATE;
|
2003-04-08 14:34:25 +02:00
|
|
|
return 0;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (dtype)
|
|
|
|
{
|
|
|
|
case DTK_DATE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DTK_EPOCH:
|
|
|
|
GetEpochTime(tm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_DATE_BAD_DATE;
|
2003-03-20 16:56:50 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
dDate = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
|
|
|
|
|
|
|
|
return dDate;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
2003-04-01 16:37:25 +02:00
|
|
|
PGTYPESdate_to_asc(Date dDate)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
|
|
|
struct tm tt, *tm = &tt;
|
|
|
|
char buf[MAXDATELEN + 1];
|
|
|
|
int DateStyle=0;
|
|
|
|
bool EuroDates = FALSE;
|
|
|
|
|
|
|
|
j2date((dDate + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
|
|
|
|
EncodeDateOnly(tm, DateStyle, buf, EuroDates);
|
|
|
|
return pgtypes_strdup(buf);
|
|
|
|
}
|
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
void
|
2003-03-20 16:56:50 +01:00
|
|
|
PGTYPESdate_julmdy(Date jd, int* mdy)
|
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
int y, m, d;
|
|
|
|
|
|
|
|
j2date((int) jd, &y, &m, &d);
|
|
|
|
mdy[0] = (short int) m;
|
|
|
|
mdy[1] = (short int) d;
|
|
|
|
mdy[2] = (short int) y;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
void
|
2003-03-20 16:56:50 +01:00
|
|
|
PGTYPESdate_mdyjul(int* mdy, Date *jdate)
|
|
|
|
{
|
|
|
|
/* month is mdy[0] */
|
|
|
|
/* day is mdy[1] */
|
|
|
|
/* year is mdy[2] */
|
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
*jdate = (Date) date2j(mdy[2], mdy[0], mdy[1]);
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
2003-03-21 15:17:47 +01:00
|
|
|
int
|
2003-03-30 13:48:19 +02:00
|
|
|
PGTYPESdate_dayofweek(Date dDate)
|
2003-03-21 15:17:47 +01:00
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
/*
|
|
|
|
Sunday: 0
|
|
|
|
Monday: 1
|
|
|
|
Tuesday: 2
|
|
|
|
Wednesday: 3
|
|
|
|
Thursday: 4
|
|
|
|
Friday: 5
|
|
|
|
Saturday: 6
|
|
|
|
*/
|
|
|
|
return 6-j2day(dDate+3);
|
2003-03-21 15:17:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2003-03-30 13:48:19 +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);
|
|
|
|
*d = date2j(ts.tm_year, ts.tm_mon, ts.tm_mday) - date2j(2000, 1, 1);
|
2003-03-21 15:17:47 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
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
|
2003-04-01 16:37:25 +02:00
|
|
|
PGTYPESdate_fmt_asc(Date dDate, char* fmtstring, char* outbuf) {
|
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 }
|
|
|
|
};
|
|
|
|
|
|
|
|
union {
|
|
|
|
char* replace_str;
|
|
|
|
unsigned int replace_uint;
|
|
|
|
} replace_val;
|
|
|
|
int replace_type;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int dow;
|
|
|
|
char* start_pattern;
|
|
|
|
struct tm tm;
|
|
|
|
|
|
|
|
/* XXX error handling ? */
|
|
|
|
/* copy the string over */
|
|
|
|
strcpy(outbuf, fmtstring);
|
|
|
|
|
|
|
|
/* get the date */
|
|
|
|
j2date((dDate + date2j(2000, 1, 1)), &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
|
|
|
|
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:
|
|
|
|
replace_val.replace_str = pgtypes_date_weekdays_short[dow];
|
2003-04-01 16:37:25 +02:00
|
|
|
replace_type = PGTYPES_REPLACE_STRING_CONSTANT;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_DAY_DIGITS_LZ:
|
|
|
|
replace_val.replace_uint = tm.tm_mday;
|
2003-04-01 16:37:25 +02:00
|
|
|
replace_type = PGTYPES_REPLACE_UINT_2_LZ;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_MONTH_LITERAL_SHORT:
|
2003-04-01 16:37:25 +02:00
|
|
|
replace_val.replace_str = months[tm.tm_mon-1];
|
|
|
|
replace_type = PGTYPES_REPLACE_STRING_CONSTANT;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_MONTH_DIGITS_LZ:
|
|
|
|
replace_val.replace_uint = tm.tm_mon;
|
2003-04-01 16:37:25 +02:00
|
|
|
replace_type = PGTYPES_REPLACE_UINT_2_LZ;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_YEAR_DIGITS_LONG:
|
|
|
|
replace_val.replace_uint = tm.tm_year;
|
2003-04-01 16:37:25 +02:00
|
|
|
replace_type = PGTYPES_REPLACE_UINT_4_LZ;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
case PGTYPES_FMTDATE_YEAR_DIGITS_SHORT:
|
|
|
|
replace_val.replace_uint = tm.tm_year % 1000;
|
2003-04-01 16:37:25 +02:00
|
|
|
replace_type = PGTYPES_REPLACE_UINT_2_LZ;
|
2003-03-30 13:48:19 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* should not happen, set something
|
|
|
|
* anyway */
|
|
|
|
replace_val.replace_str = " ";
|
2003-04-01 16:37:25 +02:00
|
|
|
replace_type = PGTYPES_REPLACE_STRING_CONSTANT;
|
2003-03-30 13:48:19 +02:00
|
|
|
}
|
|
|
|
switch(replace_type) {
|
2003-04-01 16:37:25 +02:00
|
|
|
case PGTYPES_REPLACE_STRING_MALLOCED:
|
|
|
|
case PGTYPES_REPLACE_STRING_CONSTANT:
|
2003-03-30 13:48:19 +02:00
|
|
|
strncpy(start_pattern, replace_val.replace_str,
|
|
|
|
strlen(replace_val.replace_str));
|
2003-04-01 16:37:25 +02:00
|
|
|
if (replace_type == PGTYPES_REPLACE_STRING_MALLOCED) {
|
2003-03-30 13:48:19 +02:00
|
|
|
free(replace_val.replace_str);
|
|
|
|
}
|
|
|
|
break;
|
2003-04-01 16:37:25 +02:00
|
|
|
case PGTYPES_REPLACE_UINT:
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
char* t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
|
|
|
|
if (!t) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
|
|
|
|
"%u", replace_val.replace_uint);
|
|
|
|
strncpy(start_pattern, t, strlen(t));
|
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
break;
|
2003-04-01 16:37:25 +02:00
|
|
|
case PGTYPES_REPLACE_UINT_2_LZ:
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
char* t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
|
|
|
|
if (!t) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
|
|
|
|
"%02u", replace_val.replace_uint);
|
|
|
|
strncpy(start_pattern, t, strlen(t));
|
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
break;
|
2003-04-01 16:37:25 +02:00
|
|
|
case PGTYPES_REPLACE_UINT_4_LZ:
|
2003-03-30 13:48:19 +02:00
|
|
|
{
|
|
|
|
char* t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
|
|
|
|
if (!t) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
|
|
|
|
"%04u", replace_val.replace_uint);
|
|
|
|
strncpy(start_pattern, t, strlen(t));
|
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* doesn't happen (we set
|
|
|
|
* replace_type to
|
2003-04-01 16:37:25 +02:00
|
|
|
* PGTYPES_REPLACE_STRING_CONSTANT
|
2003-03-30 13:48:19 +02:00
|
|
|
* in case of an error above) */
|
|
|
|
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:
|
|
|
|
* - first we analyze the paramters
|
|
|
|
* - if this is a special case with no delimiters, add delimters
|
|
|
|
* - 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
|
2003-04-01 16:37:25 +02:00
|
|
|
PGTYPESdate_defmt_asc(Date *d, char *fmt, 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;
|
|
|
|
int i;
|
|
|
|
int reading_digit;
|
|
|
|
int token_count;
|
|
|
|
char* str_copy;
|
|
|
|
struct tm tm;
|
|
|
|
|
|
|
|
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++) {
|
|
|
|
if (!isdigit(str[i])) {
|
|
|
|
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 for the delimiters between the 3 fiedls
|
|
|
|
* */
|
|
|
|
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;
|
|
|
|
/* 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;
|
|
|
|
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++) {
|
|
|
|
str_copy[i] = (char) tolower(str_copy[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* look for numerical tokens */
|
|
|
|
reading_digit = 0;
|
|
|
|
token_count = 0;
|
|
|
|
for (i = 0; i < strlen(str_copy); i++) {
|
|
|
|
if (!isdigit(str_copy[i]) && reading_digit) {
|
|
|
|
/* the token is finished */
|
|
|
|
token[token_count][1] = i-1;
|
|
|
|
reading_digit = 0;
|
|
|
|
token_count++;
|
|
|
|
} else if (isdigit(str_copy[i]) && !reading_digit) {
|
|
|
|
/* we have found a token */
|
|
|
|
token[token_count][0] = i;
|
|
|
|
reading_digit = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* 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);
|
|
|
|
errno = PGTYPES_DATE_ERR_ENOTDMY;
|
|
|
|
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;
|
|
|
|
|
|
|
|
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++) {
|
|
|
|
month_lower_tmp[j] = (char) tolower(list[i][j]);
|
|
|
|
if (!month_lower_tmp[j]) {
|
|
|
|
/* properly terminated */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((start_pos = strstr(str_copy, month_lower_tmp))) {
|
|
|
|
offset = start_pos - str_copy;
|
|
|
|
/* 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;
|
|
|
|
} else {
|
|
|
|
token_count = 2;
|
|
|
|
}
|
|
|
|
token[token_count][0] = offset;
|
|
|
|
token[token_count][1] = offset + strlen(month_lower_tmp) - 1;
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
/* evil[tm] hack:
|
|
|
|
* if we read the pgtypes_date_months and haven't
|
|
|
|
* found a match, reset list to point to
|
|
|
|
* pgtypes_date_months_short 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tm.tm_mon < 1 || tm.tm_mon > 12)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-04-01 16:37:25 +02:00
|
|
|
/* XXX: DBCENTURY ? */
|
|
|
|
|
2003-03-30 13:48:19 +02:00
|
|
|
*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;
|
|
|
|
}
|