aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/interfaces/ecpg/ChangeLog4
-rw-r--r--src/interfaces/ecpg/ecpglib/data.c50
-rw-r--r--src/interfaces/ecpg/ecpglib/execute.c90
-rw-r--r--src/interfaces/ecpg/ecpglib/typename.c6
-rw-r--r--src/interfaces/ecpg/include/decimal.h10
-rw-r--r--src/interfaces/ecpg/include/ecpgtype.h4
-rw-r--r--src/interfaces/ecpg/include/pgtypes_date.h12
-rw-r--r--src/interfaces/ecpg/include/pgtypes_error.h4
-rw-r--r--src/interfaces/ecpg/include/pgtypes_timestamp.h16
-rw-r--r--src/interfaces/ecpg/pgtypeslib/Makefile4
-rw-r--r--src/interfaces/ecpg/pgtypeslib/common.c29
-rw-r--r--src/interfaces/ecpg/pgtypeslib/datetime.c105
-rw-r--r--src/interfaces/ecpg/pgtypeslib/dt.h303
-rw-r--r--src/interfaces/ecpg/pgtypeslib/dt_common.c2558
-rw-r--r--src/interfaces/ecpg/pgtypeslib/extern.h4
-rw-r--r--src/interfaces/ecpg/pgtypeslib/numeric.c17
-rw-r--r--src/interfaces/ecpg/pgtypeslib/timestamp.c356
-rw-r--r--src/interfaces/ecpg/preproc/preproc.y67
-rw-r--r--src/interfaces/ecpg/preproc/type.c22
-rw-r--r--src/interfaces/ecpg/test/Makefile4
-rw-r--r--src/interfaces/ecpg/test/dt_test.pgc51
-rw-r--r--src/interfaces/ecpg/test/num_test.pgc8
22 files changed, 3674 insertions, 50 deletions
diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog
index e4c3548dfc4..bfe62f1fda5 100644
--- a/src/interfaces/ecpg/ChangeLog
+++ b/src/interfaces/ecpg/ChangeLog
@@ -1360,6 +1360,10 @@ Sun Mar 16 11:28:01 CET 2003
- Started with a pgtypes library.
- Renamed lib directory to ecpglib.
- Added numerical functions to library and preprocessor.
+
+Don Mar 20 16:53:40 CET 2003
+
+ - Added date/timestamp to library and preprocessor.
- Set ecpg version to 2.12.0.
- Set ecpg library to 3.4.2.
- Set pgtypes library to 1.0.0
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index 85d5e30a1ba..81f9d62e691 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.2 2003/03/20 15:56:50 meskes Exp $ */
#include "postgres_fe.h"
@@ -11,6 +11,8 @@
#include "extern.h"
#include "sqlca.h"
#include "pgtypes_numeric.h"
+#include "pgtypes_date.h"
+#include "pgtypes_timestamp.h"
bool
ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
@@ -99,6 +101,8 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
double dres;
char *scan_length;
NumericVar *nres;
+ Date ddres;
+ Timestamp tres;
case ECPGt_short:
case ECPGt_int:
@@ -397,7 +401,51 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
PGTYPESnumeric_copy(nres, (NumericVar *)(var + offset * act_tuple));
break;
+
+ case ECPGt_date:
+ if (pval)
+ {
+ if (isarray && *pval == '"')
+ ddres = PGTYPESdate_atod(pval + 1, &scan_length);
+ else
+ ddres = PGTYPESdate_atod(pval, &scan_length);
+
+ if (isarray && *scan_length == '"')
+ scan_length++;
+
+ if ((isarray && *scan_length != ',' && *scan_length != '}')
+ || (!isarray && *scan_length != '\0')) /* Garbage left */
+ {
+ ECPGraise(lineno, ECPG_FLOAT_FORMAT, pval);
+ return (false);
+ }
+
+ *((Date *)(var + offset * act_tuple)) = ddres;
+ }
+ break;
+ case ECPGt_timestamp:
+ if (pval)
+ {
+ if (isarray && *pval == '"')
+ tres = PGTYPEStimestamp_atot(pval + 1, &scan_length);
+ else
+ tres = PGTYPEStimestamp_atot(pval, &scan_length);
+
+ if (isarray && *scan_length == '"')
+ scan_length++;
+
+ if ((isarray && *scan_length != ',' && *scan_length != '}')
+ || (!isarray && *scan_length != '\0')) /* Garbage left */
+ {
+ ECPGraise(lineno, ECPG_FLOAT_FORMAT, pval);
+ return (false);
+ }
+
+ *((Timestamp *)(var + offset * act_tuple)) = tres;
+ }
+ break;
+
default:
ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(type));
return (false);
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 8cb3fa36a67..0fdd925a09c 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.3 2003/03/19 16:05:41 petere Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.4 2003/03/20 15:56:50 meskes Exp $ */
/*
* The aim is to get a simpler inteface to the database routines.
@@ -27,6 +27,8 @@
#include "sqlca.h"
#include "sql3types.h"
#include "pgtypes_numeric.h"
+#include "pgtypes_date.h"
+#include "pgtypes_timestamp.h"
/* variables visible to the programs */
struct sqlca sqlca =
@@ -59,8 +61,7 @@ struct sqlca sqlca =
/* This function returns a newly malloced string that has the \
in the argument quoted with \ and the ' quoted with ' as SQL92 says.
*/
-static
-char *
+static char *
quote_postgres(char *arg, int lineno)
{
char *res = (char *) ECPGalloc(2 * strlen(arg) + 3, lineno);
@@ -876,6 +877,89 @@ ECPGstore_input(const struct statement * stmt, const struct variable * var,
free(str);
}
break;
+
+ case ECPGt_date:
+ {
+ char *str = NULL;
+ int slen;
+
+ if (var->arrsize > 1)
+ {
+ for (element = 0; element < var->arrsize; element++)
+ {
+ str = PGTYPESdate_dtoa(*(Date *)((var + var->offset * element)->value));
+ slen = strlen (str);
+
+ if (!(mallocedval = ECPGrealloc(mallocedval, strlen(mallocedval) + slen + 5, stmt->lineno)))
+ return false;
+
+ if (!element)
+ strcpy(mallocedval, "'{");
+
+ strncpy(mallocedval + strlen(mallocedval), str , slen + 1);
+ strcpy(mallocedval + strlen(mallocedval), ",");
+ }
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+ }
+ else
+ {
+ str = PGTYPESdate_dtoa(*(Date *)(var->value));
+ slen = strlen (str);
+
+ if (!(mallocedval = ECPGalloc(slen + 1, stmt->lineno)))
+ return false;
+
+ strncpy(mallocedval, str , slen);
+ mallocedval[slen] = '\0';
+ }
+
+ *tobeinserted_p = mallocedval;
+ *malloced_p = true;
+ free(str);
+ }
+ break;
+
+ case ECPGt_timestamp:
+ {
+ char *str = NULL;
+ int slen;
+
+ if (var->arrsize > 1)
+ {
+ for (element = 0; element < var->arrsize; element++)
+ {
+ str = PGTYPEStimestamp_ttoa(*(Timestamp *)((var + var->offset * element)->value));
+ slen = strlen (str);
+
+ if (!(mallocedval = ECPGrealloc(mallocedval, strlen(mallocedval) + slen + 5, stmt->lineno)))
+ return false;
+
+ if (!element)
+ strcpy(mallocedval, "'{");
+
+ strncpy(mallocedval + strlen(mallocedval), str , slen + 1);
+ strcpy(mallocedval + strlen(mallocedval), ",");
+ }
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+ }
+ else
+ {
+ str = PGTYPEStimestamp_ttoa(*(Timestamp *)(var->value));
+ slen = strlen (str);
+
+ if (!(mallocedval = ECPGalloc(slen + 1, stmt->lineno)))
+ return false;
+
+ strncpy(mallocedval, str , slen);
+ mallocedval[slen] = '\0';
+ }
+
+ *tobeinserted_p = mallocedval;
+ *malloced_p = true;
+ free(str);
+ }
+ break;
+
default:
/* Not implemented yet */
ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, (char *) ECPGtype_name(var->type));
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
index c970f767242..6ae846c3812 100644
--- a/src/interfaces/ecpg/ecpglib/typename.c
+++ b/src/interfaces/ecpg/ecpglib/typename.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/typename.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/typename.c,v 1.2 2003/03/20 15:56:50 meskes Exp $ */
#include "postgres_fe.h"
@@ -49,6 +49,10 @@ ECPGtype_name(enum ECPGttype typ)
return "char";
case ECPGt_numeric:
return "numeric";
+ case ECPGt_date:
+ return "date";
+ case ECPGt_timestamp:
+ return "timestamp";
default:
abort();
}
diff --git a/src/interfaces/ecpg/include/decimal.h b/src/interfaces/ecpg/include/decimal.h
index f307207adce..2c13a33c708 100644
--- a/src/interfaces/ecpg/include/decimal.h
+++ b/src/interfaces/ecpg/include/decimal.h
@@ -2,4 +2,14 @@
#ifndef dec_t
#define dec_t NumericVar
+
+#define CSHORTTYPE 0
+#define CMONEYTYPE 0
+#define CCHARTYPE 0
+#define CDECIMALTYPE 0
+#define CINTTYPE 0
+#define CDATETYPE 0
+#define CDOUBLETYPE 0
+#define CLONGTYPE 0
+
#endif /* dec_t */
diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h
index 6ed5f5d3e4f..31738d421f1 100644
--- a/src/interfaces/ecpg/include/ecpgtype.h
+++ b/src/interfaces/ecpg/include/ecpgtype.h
@@ -52,7 +52,9 @@ enum ECPGttype
ECPGt_NO_INDICATOR, /* no indicator */
ECPGt_long_long, ECPGt_unsigned_long_long,
ECPGt_descriptor, /* sql descriptor, no C variable */
- ECPGt_numeric
+ ECPGt_numeric,
+ ECPGt_date,
+ ECPGt_timestamp
};
/* descriptor items */
diff --git a/src/interfaces/ecpg/include/pgtypes_date.h b/src/interfaces/ecpg/include/pgtypes_date.h
new file mode 100644
index 00000000000..882ddab82df
--- /dev/null
+++ b/src/interfaces/ecpg/include/pgtypes_date.h
@@ -0,0 +1,12 @@
+#ifndef PGTYPES_DATETIME
+#define PGTYPES_DATETIME
+
+#define Date long
+
+extern Date PGTYPESdate_atod(char *, char **);
+extern char *PGTYPESdate_dtoa(Date);
+extern int PGTYPESdate_julmdy(Date, int*);
+extern int PGTYPESdate_mdyjul(int*, Date *);
+extern int PGTYPESdate_day(Date);
+
+#endif /* PGTYPES_DATETIME */
diff --git a/src/interfaces/ecpg/include/pgtypes_error.h b/src/interfaces/ecpg/include/pgtypes_error.h
index e997b03ae01..7cab1971b98 100644
--- a/src/interfaces/ecpg/include/pgtypes_error.h
+++ b/src/interfaces/ecpg/include/pgtypes_error.h
@@ -2,5 +2,7 @@
#define PGTYPES_BAD_NUMERIC 202
#define PGTYPES_DIVIDE_ZERO 203
-#define PGTYPES_BAD_DATE 300
+#define PGTYPES_BAD_DATE 210
+
+#define PGTYPES_BAD_TIMESTAMP 220
diff --git a/src/interfaces/ecpg/include/pgtypes_timestamp.h b/src/interfaces/ecpg/include/pgtypes_timestamp.h
new file mode 100644
index 00000000000..48a54b1381f
--- /dev/null
+++ b/src/interfaces/ecpg/include/pgtypes_timestamp.h
@@ -0,0 +1,16 @@
+#ifndef PGTYPES_TIMESTAMP
+#define PGTYPES_TIMESTAMP
+
+#ifdef HAVE_INT64_TIMESTAMP
+typedef int64 Timestamp;
+typedef int64 TimestampTz;
+
+#else
+typedef double Timestamp;
+typedef double TimestampTz;
+#endif
+
+extern Timestamp PGTYPEStimestamp_atot(char *, char **);
+extern char *PGTYPEStimestamp_ttoa(Timestamp);
+
+#endif /* PGTYPES_TIMESTAMP */
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 73d174e3d23..ae691c7d21d 100644
--- a/src/interfaces/ecpg/pgtypeslib/Makefile
+++ b/src/interfaces/ecpg/pgtypeslib/Makefile
@@ -4,7 +4,7 @@
#
# Copyright (c) 1994, Regents of the University of California
#
-# $Header: /cvsroot/pgsql/src/interfaces/ecpg/pgtypeslib/Makefile,v 1.1 2003/03/16 10:42:54 meskes Exp $
+# $Header: /cvsroot/pgsql/src/interfaces/ecpg/pgtypeslib/Makefile,v 1.2 2003/03/20 15:56:50 meskes Exp $
#
#-------------------------------------------------------------------------
@@ -18,7 +18,7 @@ SO_MINOR_VERSION= 0.0
override CPPFLAGS := -g -I$(top_srcdir)/src/interfaces/ecpg/include -I$(top_srcdir)/src/include/utils $(CPPFLAGS)
-OBJS= numeric.o
+OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o
all: all-lib
diff --git a/src/interfaces/ecpg/pgtypeslib/common.c b/src/interfaces/ecpg/pgtypeslib/common.c
new file mode 100644
index 00000000000..b91bedcd581
--- /dev/null
+++ b/src/interfaces/ecpg/pgtypeslib/common.c
@@ -0,0 +1,29 @@
+#include <errno.h>
+
+#include "extern.h"
+
+char *
+pgtypes_alloc(long size)
+{
+ char *new = (char *) calloc(1L, size);
+
+ if (!new)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ memset(new, '\0', size);
+ return (new);
+}
+
+char *
+pgtypes_strdup(char *str)
+{
+ char *new = (char *) strdup(str);
+
+ if (!new)
+ errno = ENOMEM;
+ return (new);
+}
+
diff --git a/src/interfaces/ecpg/pgtypeslib/datetime.c b/src/interfaces/ecpg/pgtypeslib/datetime.c
new file mode 100644
index 00000000000..46fd1ab96f9
--- /dev/null
+++ b/src/interfaces/ecpg/pgtypeslib/datetime.c
@@ -0,0 +1,105 @@
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <float.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "dt.h"
+#include "extern.h"
+#include "pgtypes_error.h"
+#include "pgtypes_date.h"
+
+Date
+PGTYPESdate_atod(char *str, char **endptr)
+{
+
+ 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;
+
+ if (strlen(str) >= sizeof(lowstr))
+ {
+ errno = PGTYPES_BAD_DATE;
+ return -1;
+ }
+
+ if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0)
+ || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp, EuroDates) != 0))
+ {
+ errno = PGTYPES_BAD_DATE;
+ return -1;
+ }
+
+ switch (dtype)
+ {
+ case DTK_DATE:
+ break;
+
+ case DTK_EPOCH:
+ GetEpochTime(tm);
+ break;
+
+ default:
+ errno = PGTYPES_BAD_DATE;
+ return -1;
+ }
+
+ dDate = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
+
+ return dDate;
+}
+
+char *
+PGTYPESdate_dtoa(Date dDate)
+{
+ 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);
+}
+
+int
+PGTYPESdate_julmdy(Date jd, int* mdy)
+{
+ printf("day: %d\n", mdy[0]);
+ printf("month: %d\n", mdy[1]);
+ printf("year: %d\n", mdy[2]);
+ j2date((int) jd, mdy+2, mdy+1, mdy+0);
+ return 0;
+}
+
+int
+PGTYPESdate_mdyjul(int* mdy, Date *jdate)
+{
+ /* month is mdy[0] */
+ /* day is mdy[1] */
+ /* year is mdy[2] */
+ printf("day: %d\n", mdy[1]);
+ printf("month: %d\n", mdy[0]);
+ printf("year: %d\n", mdy[2]);
+ *jdate = (Date) date2j(mdy[2], mdy[0], mdy[1]);
+ return 0;
+}
+
+int
+PGTYPESdate_day(Date dDate)
+{
+ return j2day(dDate);
+}
+
diff --git a/src/interfaces/ecpg/pgtypeslib/dt.h b/src/interfaces/ecpg/pgtypeslib/dt.h
new file mode 100644
index 00000000000..d20c0cd819d
--- /dev/null
+++ b/src/interfaces/ecpg/pgtypeslib/dt.h
@@ -0,0 +1,303 @@
+#ifndef DT_H
+#define DT_H
+
+#define MAXTZLEN 10
+
+#ifdef HAVE_INT64_TIMESTAMP
+
+typedef int32 fsec_t;
+
+#else
+
+typedef double fsec_t;
+
+#define TIME_PREC_INV 1000000.0
+#define JROUND(j) (rint(((double) (j))*TIME_PREC_INV)/TIME_PREC_INV)
+#endif
+
+#ifndef bool
+#define bool char
+#endif /* ndef bool */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* FALSE */
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* TRUE */
+
+#define USE_POSTGRES_DATES 0
+#define USE_ISO_DATES 1
+#define USE_SQL_DATES 2
+#define USE_GERMAN_DATES 3
+
+#define DAGO "ago"
+#define EPOCH "epoch"
+#define INVALID "invalid"
+#define EARLY "-infinity"
+#define LATE "infinity"
+#define NOW "now"
+#define TODAY "today"
+#define TOMORROW "tomorrow"
+#define YESTERDAY "yesterday"
+#define ZULU "zulu"
+
+#define DMICROSEC "usecond"
+#define DMILLISEC "msecond"
+#define DSECOND "second"
+#define DMINUTE "minute"
+#define DHOUR "hour"
+#define DDAY "day"
+#define DWEEK "week"
+#define DMONTH "month"
+#define DQUARTER "quarter"
+#define DYEAR "year"
+#define DDECADE "decade"
+#define DCENTURY "century"
+#define DMILLENNIUM "millennium"
+#define DA_D "ad"
+#define DB_C "bc"
+#define DTIMEZONE "timezone"
+#define DCURRENT "current"
+
+/*
+ * Fundamental time field definitions for parsing.
+ *
+ * Meridian: am, pm, or 24-hour style.
+ * Millennium: ad, bc
+ */
+
+#define AM 0
+#define PM 1
+#define HR24 2
+
+#define AD 0
+#define BC 1
+
+/*
+ * Fields for time decoding.
+ *
+ * Can't have more of these than there are bits in an unsigned int
+ * since these are turned into bit masks during parsing and decoding.
+ *
+ * Furthermore, the values for YEAR, MONTH, DAY, HOUR, MINUTE, SECOND
+ * must be in the range 0..14 so that the associated bitmasks can fit
+ * into the left half of an INTERVAL's typmod value.
+ */
+
+#define RESERV 0
+#define MONTH 1
+#define YEAR 2
+#define DAY 3
+#define JULIAN 4
+#define TZ 5
+#define DTZ 6
+#define DTZMOD 7
+#define IGNORE_DTF 8
+#define AMPM 9
+#define HOUR 10
+#define MINUTE 11
+#define SECOND 12
+#define DOY 13
+#define DOW 14
+#define UNITS 15
+#define ADBC 16
+/* these are only for relative dates */
+#define AGO 17
+#define ABS_BEFORE 18
+#define ABS_AFTER 19
+/* generic fields to help with parsing */
+#define ISODATE 20
+#define ISOTIME 21
+/* reserved for unrecognized string values */
+#define UNKNOWN_FIELD 31
+
+/*
+ * Token field definitions for time parsing and decoding.
+ * These need to fit into the datetkn table type.
+ * At the moment, that means keep them within [-127,127].
+ * These are also used for bit masks in DecodeDateDelta()
+ * so actually restrict them to within [0,31] for now.
+ * - thomas 97/06/19
+ * Not all of these fields are used for masks in DecodeDateDelta
+ * so allow some larger than 31. - thomas 1997-11-17
+ */
+
+#define DTK_NUMBER 0
+#define DTK_STRING 1
+
+#define DTK_DATE 2
+#define DTK_TIME 3
+#define DTK_TZ 4
+#define DTK_AGO 5
+
+#define DTK_SPECIAL 6
+#define DTK_INVALID 7
+#define DTK_CURRENT 8
+#define DTK_EARLY 9
+#define DTK_LATE 10
+#define DTK_EPOCH 11
+#define DTK_NOW 12
+#define DTK_YESTERDAY 13
+#define DTK_TODAY 14
+#define DTK_TOMORROW 15
+#define DTK_ZULU 16
+
+#define DTK_DELTA 17
+#define DTK_SECOND 18
+#define DTK_MINUTE 19
+#define DTK_HOUR 20
+#define DTK_DAY 21
+#define DTK_WEEK 22
+#define DTK_MONTH 23
+#define DTK_QUARTER 24
+#define DTK_YEAR 25
+#define DTK_DECADE 26
+#define DTK_CENTURY 27
+#define DTK_MILLENNIUM 28
+#define DTK_MILLISEC 29
+#define DTK_MICROSEC 30
+#define DTK_JULIAN 31
+
+#define DTK_DOW 32
+#define DTK_DOY 33
+#define DTK_TZ_HOUR 34
+#define DTK_TZ_MINUTE 35
+
+
+/*
+ * Bit mask definitions for time parsing.
+ */
+
+#define DTK_M(t) (0x01 << (t))
+
+#define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
+#define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_M(SECOND))
+
+#define MAXDATELEN 51 /* maximum possible length of an input
+ * date string (not counting tr. null) */
+#define MAXDATEFIELDS 25 /* maximum possible number of fields in a
+ * date string */
+#define TOKMAXLEN 10 /* only this many chars are stored in
+ * datetktbl */
+
+/* keep this struct small; it gets used a lot */
+typedef struct
+{
+#if defined(_AIX)
+ char *token;
+#else
+ char token[TOKMAXLEN];
+#endif /* _AIX */
+ char type;
+ char value; /* this may be unsigned, alas */
+} datetkn;
+
+
+/* TMODULO()
+ * Macro to replace modf(), which is broken on some platforms.
+ * t = input and remainder
+ * q = integer part
+ * u = divisor
+ */
+#ifdef HAVE_INT64_TIMESTAMP
+#define TMODULO(t,q,u) \
+do { \
+ q = (t / u); \
+ if (q != 0) t -= (q * u); \
+} while(0)
+#else
+#define TMODULO(t,q,u) \
+do { \
+ q = ((t < 0)? ceil(t / u): floor(t / u)); \
+ if (q != 0) t -= rint(q * u); \
+} while(0)
+#endif
+
+/* Global variable holding time zone information. */
+#if defined(__CYGWIN__) || defined(N_PLAT_NLM)
+#define TIMEZONE_GLOBAL _timezone
+#else
+#define TIMEZONE_GLOBAL timezone
+#endif
+
+/*
+ * Date/time validation
+ * Include check for leap year.
+ */
+
+extern int day_tab[2][13];
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/* Julian date support for date2j() and j2date()
+ * Set the minimum year to one greater than the year of the first valid day
+ * to avoid having to check year and day both. - tgl 97/05/08
+ */
+
+#define JULIAN_MINYEAR (-4713)
+#define JULIAN_MINMONTH (11)
+#define JULIAN_MINDAY (24)
+
+#define IS_VALID_JULIAN(y,m,d) (((y) > JULIAN_MINYEAR) \
+ || (((y) == JULIAN_MINYEAR) && (((m) > JULIAN_MINMONTH) \
+ || (((m) == JULIAN_MINMONTH) && ((d) >= JULIAN_MINDAY)))))
+
+#define UTIME_MINYEAR (1901)
+#define UTIME_MINMONTH (12)
+#define UTIME_MINDAY (14)
+#define UTIME_MAXYEAR (2038)
+#define UTIME_MAXMONTH (01)
+#define UTIME_MAXDAY (18)
+
+#define IS_VALID_UTIME(y,m,d) ((((y) > UTIME_MINYEAR) \
+ || (((y) == UTIME_MINYEAR) && (((m) > UTIME_MINMONTH) \
+ || (((m) == UTIME_MINMONTH) && ((d) >= UTIME_MINDAY))))) \
+ && (((y) < UTIME_MAXYEAR) \
+ || (((y) == UTIME_MAXYEAR) && (((m) < UTIME_MAXMONTH) \
+ || (((m) == UTIME_MAXMONTH) && ((d) <= UTIME_MAXDAY))))))
+
+#ifdef HUGE_VAL
+#define DT_NOBEGIN (-HUGE_VAL)
+#define DT_NOEND (HUGE_VAL)
+#else
+#define DT_NOBEGIN (-DBL_MAX)
+#define DT_NOEND (DBL_MAX)
+#endif
+
+#define TIMESTAMP_NOBEGIN(j) do {j = DT_NOBEGIN;} while (0)
+#define TIMESTAMP_NOEND(j) do {j = DT_NOEND;} while (0)
+#define TIMESTAMP_IS_NOBEGIN(j) ((j) == DT_NOBEGIN)
+#define TIMESTAMP_IS_NOEND(j) ((j) == DT_NOEND)
+#define TIMESTAMP_NOT_FINITE(j) (TIMESTAMP_IS_NOBEGIN(j) || TIMESTAMP_IS_NOEND(j))
+
+extern int DecodeTimeOnly(char **field, int *ftype,
+ int nf, int *dtype,
+ struct tm * tm, fsec_t *fsec, int *tzp);
+
+extern int DecodeInterval(char **field, int *ftype,
+ int nf, int *dtype,
+ struct tm * tm, fsec_t *fsec);
+
+extern int EncodeTimeOnly(struct tm * tm, fsec_t fsec, int *tzp, int style, char *str);
+extern int EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, char *str, bool);
+extern int EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str);
+
+extern int DecodeUnits(int field, char *lowtoken, int *val);
+extern bool ClearDateCache(bool, bool, bool);
+
+extern int j2day(int jd);
+
+extern bool CheckDateTokenTables(void);
+
+extern int EncodeDateOnly(struct tm *, int, char *, bool);
+extern void GetEpochTime(struct tm *);
+extern int ParseDateTime(char *, char *, char **, int *, int, int *, char **);
+extern int DecodeDateTime(char **, int *, int, int *, struct tm *, fsec_t *, int *, bool);
+extern void j2date(int, int *, int *, int *);
+extern int date2j(int, int, int);
+extern double rint(double x);
+
+#endif /* DT_H */
+
diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c
new file mode 100644
index 00000000000..3b85f7df152
--- /dev/null
+++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -0,0 +1,2558 @@
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <float.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "dt.h"
+#include "extern.h"
+
+static int day_tab[2][13] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
+
+typedef long AbsoluteTime;
+
+#define ABS_SIGNBIT ((char) 0200)
+#define POS(n) (n)
+#define NEG(n) ((n)|ABS_SIGNBIT)
+#define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 15) /* uncompress */
+#define VALMASK ((char) 0177)
+#define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
+
+static datetkn datetktbl[] = {
+/* text, token, lexval */
+ {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
+ {"abstime", IGNORE_DTF, 0}, /* for pre-v6.1 "Invalid Abstime" */
+ {"acsst", DTZ, POS(42)}, /* Cent. Australia */
+ {"acst", DTZ, NEG(16)}, /* Atlantic/Porto Acre */
+ {"act", TZ, NEG(20)}, /* Atlantic/Porto Acre */
+ {DA_D, ADBC, AD}, /* "ad" for years >= 0 */
+ {"adt", DTZ, NEG(12)}, /* Atlantic Daylight Time */
+ {"aesst", DTZ, POS(44)}, /* E. Australia */
+ {"aest", TZ, POS(40)}, /* Australia Eastern Std Time */
+ {"aft", TZ, POS(18)}, /* Kabul */
+ {"ahst", TZ, NEG(40)}, /* Alaska-Hawaii Std Time */
+ {"akdt", DTZ, NEG(32)}, /* Alaska Daylight Time */
+ {"akst", DTZ, NEG(36)}, /* Alaska Standard Time */
+ {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
+ {"almst", TZ, POS(28)}, /* Almaty Savings Time */
+ {"almt", TZ, POS(24)}, /* Almaty Time */
+ {"am", AMPM, AM},
+ {"amst", DTZ, POS(20)}, /* Armenia Summer Time (Yerevan) */
+#if 0
+ {"amst", DTZ, NEG(12)}, /* Porto Velho */
+#endif
+ {"amt", TZ, POS(16)}, /* Armenia Time (Yerevan) */
+ {"anast", DTZ, POS(52)}, /* Anadyr Summer Time (Russia) */
+ {"anat", TZ, POS(48)}, /* Anadyr Time (Russia) */
+ {"apr", MONTH, 4},
+ {"april", MONTH, 4},
+#if 0
+ aqtst
+ aqtt
+ arst
+#endif
+ {"art", TZ, NEG(12)}, /* Argentina Time */
+#if 0
+ ashst
+ ast /* Atlantic Standard Time, Arabia Standard
+ * Time, Acre Standard Time */
+#endif
+ {"ast", TZ, NEG(16)}, /* Atlantic Std Time (Canada) */
+ {"at", IGNORE_DTF, 0}, /* "at" (throwaway) */
+ {"aug", MONTH, 8},
+ {"august", MONTH, 8},
+ {"awsst", DTZ, POS(36)}, /* W. Australia */
+ {"awst", TZ, POS(32)}, /* W. Australia */
+ {"awt", DTZ, NEG(12)},
+ {"azost", DTZ, POS(0)}, /* Azores Summer Time */
+ {"azot", TZ, NEG(4)}, /* Azores Time */
+ {"azst", DTZ, POS(20)}, /* Azerbaijan Summer Time */
+ {"azt", TZ, POS(16)}, /* Azerbaijan Time */
+ {DB_C, ADBC, BC}, /* "bc" for years < 0 */
+ {"bdst", TZ, POS(8)}, /* British Double Summer Time */
+ {"bdt", TZ, POS(24)}, /* Dacca */
+ {"bnt", TZ, POS(32)}, /* Brunei Darussalam Time */
+ {"bort", TZ, POS(32)}, /* Borneo Time (Indonesia) */
+#if 0
+ bortst
+ bost
+#endif
+ {"bot", TZ, NEG(16)}, /* Bolivia Time */
+ {"bra", TZ, NEG(12)}, /* Brazil Time */
+#if 0
+ brst
+ brt
+#endif
+ {"bst", DTZ, POS(4)}, /* British Summer Time */
+#if 0
+ {"bst", TZ, NEG(12)}, /* Brazil Standard Time */
+ {"bst", DTZ, NEG(44)}, /* Bering Summer Time */
+#endif
+ {"bt", TZ, POS(12)}, /* Baghdad Time */
+ {"btt", TZ, POS(24)}, /* Bhutan Time */
+ {"cadt", DTZ, POS(42)}, /* Central Australian DST */
+ {"cast", TZ, POS(38)}, /* Central Australian ST */
+ {"cat", TZ, NEG(40)}, /* Central Alaska Time */
+ {"cct", TZ, POS(32)}, /* China Coast Time */
+#if 0
+ {"cct", TZ, POS(26)}, /* Indian Cocos (Island) Time */
+#endif
+ {"cdt", DTZ, NEG(20)}, /* Central Daylight Time */
+ {"cest", DTZ, POS(8)}, /* Central European Dayl.Time */
+ {"cet", TZ, POS(4)}, /* Central European Time */
+ {"cetdst", DTZ, POS(8)}, /* Central European Dayl.Time */
+ {"chadt", DTZ, POS(55)}, /* Chatham Island Daylight Time (13:45) */
+ {"chast", TZ, POS(51)}, /* Chatham Island Time (12:45) */
+#if 0
+ ckhst
+#endif
+ {"ckt", TZ, POS(48)}, /* Cook Islands Time */
+ {"clst", DTZ, NEG(12)}, /* Chile Summer Time */
+ {"clt", TZ, NEG(16)}, /* Chile Time */
+#if 0
+ cost
+#endif
+ {"cot", TZ, NEG(20)}, /* Columbia Time */
+ {"cst", TZ, NEG(24)}, /* Central Standard Time */
+ {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
+#if 0
+ cvst
+#endif
+ {"cvt", TZ, POS(28)}, /* Christmas Island Time (Indian Ocean) */
+ {"cxt", TZ, POS(28)}, /* Christmas Island Time (Indian Ocean) */
+ {"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
+ {"davt", TZ, POS(28)}, /* Davis Time (Antarctica) */
+ {"ddut", TZ, POS(40)}, /* Dumont-d'Urville Time (Antarctica) */
+ {"dec", MONTH, 12},
+ {"december", MONTH, 12},
+ {"dnt", TZ, POS(4)}, /* Dansk Normal Tid */
+ {"dow", RESERV, DTK_DOW}, /* day of week */
+ {"doy", RESERV, DTK_DOY}, /* day of year */
+ {"dst", DTZMOD, 6},
+#if 0
+ {"dusst", DTZ, POS(24)}, /* Dushanbe Summer Time */
+#endif
+ {"easst", DTZ, NEG(20)}, /* Easter Island Summer Time */
+ {"east", TZ, NEG(24)}, /* Easter Island Time */
+ {"eat", TZ, POS(12)}, /* East Africa Time */
+#if 0
+ {"east", DTZ, POS(16)}, /* Indian Antananarivo Savings Time */
+ {"eat", TZ, POS(12)}, /* Indian Antananarivo Time */
+ {"ect", TZ, NEG(16)}, /* Eastern Caribbean Time */
+ {"ect", TZ, NEG(20)}, /* Ecuador Time */
+#endif
+ {"edt", DTZ, NEG(16)}, /* Eastern Daylight Time */
+ {"eest", DTZ, POS(12)}, /* Eastern Europe Summer Time */
+ {"eet", TZ, POS(8)}, /* East. Europe, USSR Zone 1 */
+ {"eetdst", DTZ, POS(12)}, /* Eastern Europe Daylight Time */
+ {"egst", DTZ, POS(0)}, /* East Greenland Summer Time */
+ {"egt", TZ, NEG(4)}, /* East Greenland Time */
+#if 0
+ ehdt
+#endif
+ {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
+ {"est", TZ, NEG(20)}, /* Eastern Standard Time */
+ {"feb", MONTH, 2},
+ {"february", MONTH, 2},
+ {"fjst", DTZ, NEG(52)}, /* Fiji Summer Time (13 hour offset!) */
+ {"fjt", TZ, NEG(48)}, /* Fiji Time */
+ {"fkst", DTZ, NEG(12)}, /* Falkland Islands Summer Time */
+ {"fkt", TZ, NEG(8)}, /* Falkland Islands Time */
+#if 0
+ fnst
+ fnt
+#endif
+ {"fri", DOW, 5},
+ {"friday", DOW, 5},
+ {"fst", TZ, POS(4)}, /* French Summer Time */
+ {"fwt", DTZ, POS(8)}, /* French Winter Time */
+ {"galt", TZ, NEG(24)}, /* Galapagos Time */
+ {"gamt", TZ, NEG(36)}, /* Gambier Time */
+ {"gest", DTZ, POS(20)}, /* Georgia Summer Time */
+ {"get", TZ, POS(16)}, /* Georgia Time */
+ {"gft", TZ, NEG(12)}, /* French Guiana Time */
+#if 0
+ ghst
+#endif
+ {"gilt", TZ, POS(48)}, /* Gilbert Islands Time */
+ {"gmt", TZ, POS(0)}, /* Greenwish Mean Time */
+ {"gst", TZ, POS(40)}, /* Guam Std Time, USSR Zone 9 */
+ {"gyt", TZ, NEG(16)}, /* Guyana Time */
+ {"h", UNITS, DTK_HOUR}, /* "hour" */
+#if 0
+ hadt
+ hast
+#endif
+ {"hdt", DTZ, NEG(36)}, /* Hawaii/Alaska Daylight Time */
+#if 0
+ hkst
+#endif
+ {"hkt", TZ, POS(32)}, /* Hong Kong Time */
+#if 0
+ {"hmt", TZ, POS(12)}, /* Hellas ? ? */
+ hovst
+ hovt
+#endif
+ {"hst", TZ, NEG(40)}, /* Hawaii Std Time */
+#if 0
+ hwt
+#endif
+ {"ict", TZ, POS(28)}, /* Indochina Time */
+ {"idle", TZ, POS(48)}, /* Intl. Date Line, East */
+ {"idlw", TZ, NEG(48)}, /* Intl. Date Line, West */
+#if 0
+ idt /* Israeli, Iran, Indian Daylight Time */
+#endif
+ {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
+ {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for bad time */
+ {"iot", TZ, POS(20)}, /* Indian Chagos Time */
+ {"irkst", DTZ, POS(36)}, /* Irkutsk Summer Time */
+ {"irkt", TZ, POS(32)}, /* Irkutsk Time */
+ {"irt", TZ, POS(14)}, /* Iran Time */
+#if 0
+ isst
+#endif
+ {"ist", TZ, POS(8)}, /* Israel */
+ {"it", TZ, POS(14)}, /* Iran Time */
+ {"j", UNITS, DTK_JULIAN},
+ {"jan", MONTH, 1},
+ {"january", MONTH, 1},
+ {"javt", TZ, POS(28)}, /* Java Time (07:00? see JT) */
+ {"jayt", TZ, POS(36)}, /* Jayapura Time (Indonesia) */
+ {"jd", UNITS, DTK_JULIAN},
+ {"jst", TZ, POS(36)}, /* Japan Std Time,USSR Zone 8 */
+ {"jt", TZ, POS(30)}, /* Java Time (07:30? see JAVT) */
+ {"jul", MONTH, 7},
+ {"julian", UNITS, DTK_JULIAN},
+ {"july", MONTH, 7},
+ {"jun", MONTH, 6},
+ {"june", MONTH, 6},
+ {"kdt", DTZ, POS(40)}, /* Korea Daylight Time */
+ {"kgst", DTZ, POS(24)}, /* Kyrgyzstan Summer Time */
+ {"kgt", TZ, POS(20)}, /* Kyrgyzstan Time */
+ {"kost", TZ, POS(48)}, /* Kosrae Time */
+ {"krast", DTZ, POS(28)}, /* Krasnoyarsk Summer Time */
+ {"krat", TZ, POS(32)}, /* Krasnoyarsk Standard Time */
+ {"kst", TZ, POS(36)}, /* Korea Standard Time */
+ {"lhdt", DTZ, POS(44)}, /* Lord Howe Daylight Time, Australia */
+ {"lhst", TZ, POS(42)}, /* Lord Howe Standard Time, Australia */
+ {"ligt", TZ, POS(40)}, /* From Melbourne, Australia */
+ {"lint", TZ, POS(56)}, /* Line Islands Time (Kiribati; +14
+ * hours!) */
+ {"lkt", TZ, POS(24)}, /* Lanka Time */
+ {"m", UNITS, DTK_MONTH}, /* "month" for ISO input */
+ {"magst", DTZ, POS(48)}, /* Magadan Summer Time */
+ {"magt", TZ, POS(44)}, /* Magadan Time */
+ {"mar", MONTH, 3},
+ {"march", MONTH, 3},
+ {"mart", TZ, NEG(38)}, /* Marquesas Time */
+ {"mawt", TZ, POS(24)}, /* Mawson, Antarctica */
+ {"may", MONTH, 5},
+ {"mdt", DTZ, NEG(24)}, /* Mountain Daylight Time */
+ {"mest", DTZ, POS(8)}, /* Middle Europe Summer Time */
+ {"met", TZ, POS(4)}, /* Middle Europe Time */
+ {"metdst", DTZ, POS(8)}, /* Middle Europe Daylight Time */
+ {"mewt", TZ, POS(4)}, /* Middle Europe Winter Time */
+ {"mez", TZ, POS(4)}, /* Middle Europe Zone */
+ {"mht", TZ, POS(48)}, /* Kwajalein */
+ {"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
+ {"mmt", TZ, POS(26)}, /* Myannar Time */
+ {"mon", DOW, 1},
+ {"monday", DOW, 1},
+#if 0
+ most
+#endif
+ {"mpt", TZ, POS(40)}, /* North Mariana Islands Time */
+ {"msd", DTZ, POS(16)}, /* Moscow Summer Time */
+ {"msk", TZ, POS(12)}, /* Moscow Time */
+ {"mst", TZ, NEG(28)}, /* Mountain Standard Time */
+ {"mt", TZ, POS(34)}, /* Moluccas Time */
+ {"mut", TZ, POS(16)}, /* Mauritius Island Time */
+ {"mvt", TZ, POS(20)}, /* Maldives Island Time */
+ {"myt", TZ, POS(32)}, /* Malaysia Time */
+#if 0
+ ncst
+#endif
+ {"nct", TZ, POS(44)}, /* New Caledonia Time */
+ {"ndt", DTZ, NEG(10)}, /* Nfld. Daylight Time */
+ {"nft", TZ, NEG(14)}, /* Newfoundland Standard Time */
+ {"nor", TZ, POS(4)}, /* Norway Standard Time */
+ {"nov", MONTH, 11},
+ {"november", MONTH, 11},
+ {"novst", DTZ, POS(28)}, /* Novosibirsk Summer Time */
+ {"novt", TZ, POS(24)}, /* Novosibirsk Standard Time */
+ {NOW, RESERV, DTK_NOW}, /* current transaction time */
+ {"npt", TZ, POS(23)}, /* Nepal Standard Time (GMT-5:45) */
+ {"nst", TZ, NEG(14)}, /* Nfld. Standard Time */
+ {"nt", TZ, NEG(44)}, /* Nome Time */
+ {"nut", TZ, NEG(44)}, /* Niue Time */
+ {"nzdt", DTZ, POS(52)}, /* New Zealand Daylight Time */
+ {"nzst", TZ, POS(48)}, /* New Zealand Standard Time */
+ {"nzt", TZ, POS(48)}, /* New Zealand Time */
+ {"oct", MONTH, 10},
+ {"october", MONTH, 10},
+ {"omsst", DTZ, POS(28)}, /* Omsk Summer Time */
+ {"omst", TZ, POS(24)}, /* Omsk Time */
+ {"on", IGNORE_DTF, 0}, /* "on" (throwaway) */
+ {"pdt", DTZ, NEG(28)}, /* Pacific Daylight Time */
+#if 0
+ pest
+#endif
+ {"pet", TZ, NEG(20)}, /* Peru Time */
+ {"petst", DTZ, POS(52)}, /* Petropavlovsk-Kamchatski Summer Time */
+ {"pett", TZ, POS(48)}, /* Petropavlovsk-Kamchatski Time */
+ {"pgt", TZ, POS(40)}, /* Papua New Guinea Time */
+ {"phot", TZ, POS(52)}, /* Phoenix Islands (Kiribati) Time */
+#if 0
+ phst
+#endif
+ {"pht", TZ, POS(32)}, /* Phillipine Time */
+ {"pkt", TZ, POS(20)}, /* Pakistan Time */
+ {"pm", AMPM, PM},
+ {"pmdt", DTZ, NEG(8)}, /* Pierre & Miquelon Daylight Time */
+#if 0
+ pmst
+#endif
+ {"pont", TZ, POS(44)}, /* Ponape Time (Micronesia) */
+ {"pst", TZ, NEG(32)}, /* Pacific Standard Time */
+ {"pwt", TZ, POS(36)}, /* Palau Time */
+ {"pyst", DTZ, NEG(12)}, /* Paraguay Summer Time */
+ {"pyt", TZ, NEG(16)}, /* Paraguay Time */
+ {"ret", DTZ, POS(16)}, /* Reunion Island Time */
+ {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
+ {"sadt", DTZ, POS(42)}, /* S. Australian Dayl. Time */
+#if 0
+ samst
+ samt
+#endif
+ {"sast", TZ, POS(38)}, /* South Australian Std Time */
+ {"sat", DOW, 6},
+ {"saturday", DOW, 6},
+#if 0
+ sbt
+#endif
+ {"sct", DTZ, POS(16)}, /* Mahe Island Time */
+ {"sep", MONTH, 9},
+ {"sept", MONTH, 9},
+ {"september", MONTH, 9},
+ {"set", TZ, NEG(4)}, /* Seychelles Time ?? */
+#if 0
+ sgt
+#endif
+ {"sst", DTZ, POS(8)}, /* Swedish Summer Time */
+ {"sun", DOW, 0},
+ {"sunday", DOW, 0},
+ {"swt", TZ, POS(4)}, /* Swedish Winter Time */
+#if 0
+ syot
+#endif
+ {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
+ {"tft", TZ, POS(20)}, /* Kerguelen Time */
+ {"that", TZ, NEG(40)}, /* Tahiti Time */
+ {"thu", DOW, 4},
+ {"thur", DOW, 4},
+ {"thurs", DOW, 4},
+ {"thursday", DOW, 4},
+ {"tjt", TZ, POS(20)}, /* Tajikistan Time */
+ {"tkt", TZ, NEG(40)}, /* Tokelau Time */
+ {"tmt", TZ, POS(20)}, /* Turkmenistan Time */
+ {TODAY, RESERV, DTK_TODAY}, /* midnight */
+ {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
+#if 0
+ tost
+#endif
+ {"tot", TZ, POS(52)}, /* Tonga Time */
+#if 0
+ tpt
+#endif
+ {"truk", TZ, POS(40)}, /* Truk Time */
+ {"tue", DOW, 2},
+ {"tues", DOW, 2},
+ {"tuesday", DOW, 2},
+ {"tvt", TZ, POS(48)}, /* Tuvalu Time */
+#if 0
+ uct
+#endif
+ {"ulast", DTZ, POS(36)}, /* Ulan Bator Summer Time */
+ {"ulat", TZ, POS(32)}, /* Ulan Bator Time */
+ {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
+ {"ut", TZ, POS(0)},
+ {"utc", TZ, POS(0)},
+ {"uyst", DTZ, NEG(8)}, /* Uruguay Summer Time */
+ {"uyt", TZ, NEG(12)}, /* Uruguay Time */
+ {"uzst", DTZ, POS(24)}, /* Uzbekistan Summer Time */
+ {"uzt", TZ, POS(20)}, /* Uzbekistan Time */
+ {"vet", TZ, NEG(16)}, /* Venezuela Time */
+ {"vlast", DTZ, POS(44)}, /* Vladivostok Summer Time */
+ {"vlat", TZ, POS(40)}, /* Vladivostok Time */
+#if 0
+ vust
+#endif
+ {"vut", TZ, POS(44)}, /* Vanuata Time */
+ {"wadt", DTZ, POS(32)}, /* West Australian DST */
+ {"wakt", TZ, POS(48)}, /* Wake Time */
+#if 0
+ warst
+#endif
+ {"wast", TZ, POS(28)}, /* West Australian Std Time */
+ {"wat", TZ, NEG(4)}, /* West Africa Time */
+ {"wdt", DTZ, POS(36)}, /* West Australian DST */
+ {"wed", DOW, 3},
+ {"wednesday", DOW, 3},
+ {"weds", DOW, 3},
+ {"west", DTZ, POS(4)}, /* Western Europe Summer Time */
+ {"wet", TZ, POS(0)}, /* Western Europe */
+ {"wetdst", DTZ, POS(4)}, /* Western Europe Daylight Savings Time */
+ {"wft", TZ, POS(48)}, /* Wallis and Futuna Time */
+ {"wgst", DTZ, NEG(8)}, /* West Greenland Summer Time */
+ {"wgt", TZ, NEG(12)}, /* West Greenland Time */
+ {"wst", TZ, POS(32)}, /* West Australian Standard Time */
+ {"y", UNITS, DTK_YEAR}, /* "year" for ISO input */
+ {"yakst", DTZ, POS(40)}, /* Yakutsk Summer Time */
+ {"yakt", TZ, POS(36)}, /* Yakutsk Time */
+ {"yapt", TZ, POS(40)}, /* Yap Time (Micronesia) */
+ {"ydt", DTZ, NEG(32)}, /* Yukon Daylight Time */
+ {"yekst", DTZ, POS(24)}, /* Yekaterinburg Summer Time */
+ {"yekt", TZ, POS(20)}, /* Yekaterinburg Time */
+ {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
+ {"yst", TZ, NEG(36)}, /* Yukon Standard Time */
+ {"z", TZ, POS(0)}, /* time zone tag per ISO-8601 */
+ {"zp4", TZ, NEG(16)}, /* UTC +4 hours. */
+ {"zp5", TZ, NEG(20)}, /* UTC +5 hours. */
+ {"zp6", TZ, NEG(24)}, /* UTC +6 hours. */
+ {ZULU, TZ, POS(0)}, /* UTC */
+};
+
+static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
+
+static datetkn *datecache[MAXDATEFIELDS] = {NULL};
+
+char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
+
+char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", NULL};
+
+#ifndef HAVE_RINT
+
+/* @(#)s_rint.c 5.1 93/09/24 */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+/*
+ * rint(x)
+ * Return x rounded to integral value according to the prevailing
+ * rounding mode.
+ * Method:
+ * Using floating addition.
+ * Exception:
+ * Inexact flag raised if x not equal to rint(x).
+ */
+
+static const double one = 1.0,
+ TWO52[2] = {
+ 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */
+ -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */
+};
+
+double
+rint(double x)
+{
+ int i0,
+ n0,
+ j0,
+ sx;
+ unsigned i,
+ i1;
+ double w,
+ t;
+
+ n0 = (*((int *) &one) >> 29) ^ 1;
+ i0 = *(n0 + (int *) &x);
+ sx = (i0 >> 31) & 1;
+ i1 = *(1 - n0 + (int *) &x);
+ j0 = ((i0 >> 20) & 0x7ff) - 0x3ff;
+ if (j0 < 20)
+ {
+ if (j0 < 0)
+ {
+ if (((i0 & 0x7fffffff) | i1) == 0)
+ return x;
+ i1 |= (i0 & 0x0fffff);
+ i0 &= 0xfffe0000;
+ i0 |= ((i1 | -i1) >> 12) & 0x80000;
+ *(n0 + (int *) &x) = i0;
+ w = TWO52[sx] + x;
+ t = w - TWO52[sx];
+ i0 = *(n0 + (int *) &t);
+ *(n0 + (int *) &t) = (i0 & 0x7fffffff) | (sx << 31);
+ return t;
+ }
+ else
+ {
+ i = (0x000fffff) >> j0;
+ if (((i0 & i) | i1) == 0)
+ return x; /* x is integral */
+ i >>= 1;
+ if (((i0 & i) | i1) != 0)
+ {
+ if (j0 == 19)
+ i1 = 0x40000000;
+ else
+ i0 = (i0 & (~i)) | ((0x20000) >> j0);
+ }
+ }
+ }
+ else if (j0 > 51)
+ {
+ if (j0 == 0x400)
+ return x + x; /* inf or NaN */
+ else
+ return x; /* x is integral */
+ }
+ else
+ {
+ i = ((unsigned) (0xffffffff)) >> (j0 - 20);
+ if ((i1 & i) == 0)
+ return x; /* x is integral */
+ i >>= 1;
+ if ((i1 & i) != 0)
+ i1 = (i1 & (~i)) | ((0x40000000) >> (j0 - 20));
+ }
+ *(n0 + (int *) &x) = i0;
+ *(1 - n0 + (int *) &x) = i1;
+ w = TWO52[sx] + x;
+ return w - TWO52[sx];
+}
+
+#endif /* !HAVE_RINT */
+
+static datetkn *
+datebsearch(char *key, datetkn *base, unsigned int nel)
+{
+ datetkn *last = base + nel - 1,
+ *position;
+ int result;
+
+ while (last >= base)
+ {
+ position = base + ((last - base) >> 1);
+ result = key[0] - position->token[0];
+ if (result == 0)
+ {
+ result = strncmp(key, position->token, TOKMAXLEN);
+ if (result == 0)
+ return position;
+ }
+ if (result < 0)
+ last = position - 1;
+ else
+ base = position + 1;
+ }
+ return NULL;
+}
+
+/*
+ * Calendar time to Julian date conversions.
+ * Julian date is commonly used in astronomical applications,
+ * since it is numerically accurate and computationally simple.
+ * The algorithms here will accurately convert between Julian day
+ * and calendar date for all non-negative Julian days
+ * (i.e. from Nov 24, -4713 on).
+ *
+ * These routines will be used by other date/time packages
+ * - thomas 97/02/25
+ *
+ * Rewritten to eliminate overflow problems. This now allows the
+ * routines to work correctly for all Julian day counts from
+ * 0 to 2147483647 (Nov 24, -4713 to Jun 3, 5874898) assuming
+ * a 32-bit integer. Longer types should also work to the limits
+ * of their precision.
+ */
+
+int
+date2j(int y, int m, int d)
+{
+ int julian;
+ int century;
+
+ if (m > 2) {
+ m += 1;
+ y += 4800;
+ } else {
+ m += 13;
+ y += 4799;
+ }
+
+ century = y/100;
+ julian = y*365 - 32167;
+ julian += y/4 - century + century/4;
+ julian += 7834*m/256 + d;
+
+ return julian;
+} /* date2j() */
+
+void
+j2date(int jd, int *year, int *month, int *day)
+{
+ unsigned int julian;
+ unsigned int quad;
+ unsigned int extra;
+ int y;
+
+ julian = jd;
+ julian += 32044;
+ quad = julian/146097;
+ extra = (julian - quad*146097)*4 + 3;
+ julian += 60 + quad*3 + extra/146097;
+ quad = julian/1461;
+ julian -= quad*1461;
+ y = julian * 4 / 1461;
+ julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
+ + 123;
+ y += quad*4;
+ *year = y - 4800;
+ quad = julian * 2141 / 65536;
+ *day = julian - 7834*quad/256;
+ *month = (quad + 10) % 12 + 1;
+
+ return;
+} /* j2date() */
+
+int
+j2day(int date)
+{
+ unsigned int day;
+
+ day = date;
+ day += 1;
+ day %= 7;
+ return (int) day;
+} /*j2day() */
+
+/* DecodeSpecial()
+ * Decode text string using lookup table.
+ * Implement a cache lookup since it is likely that dates
+ * will be related in format.
+ */
+static int
+DecodeSpecial(int field, char *lowtoken, int *val)
+{
+ int type;
+ datetkn *tp;
+
+ if ((datecache[field] != NULL)
+ && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
+ tp = datecache[field];
+ else
+ {
+ tp = NULL;
+ if (!tp)
+ tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
+ }
+ datecache[field] = tp;
+ if (tp == NULL)
+ {
+ type = UNKNOWN_FIELD;
+ *val = 0;
+ }
+ else
+ {
+ type = tp->type;
+ switch (type)
+ {
+ case TZ:
+ case DTZ:
+ case DTZMOD:
+ *val = FROMVAL(tp);
+ break;
+
+ default:
+ *val = tp->value;
+ break;
+ }
+ }
+
+ return type;
+} /* DecodeSpecial() */
+
+/* EncodeDateOnly()
+ * Encode date as local time.
+ */
+int
+EncodeDateOnly(struct tm * tm, int style, char *str, bool EuroDates)
+{
+ if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
+ return -1;
+
+ switch (style)
+ {
+ case USE_ISO_DATES:
+ /* compatible with ISO date formats */
+ if (tm->tm_year > 0)
+ sprintf(str, "%04d-%02d-%02d",
+ tm->tm_year, tm->tm_mon, tm->tm_mday);
+ else
+ sprintf(str, "%04d-%02d-%02d %s",
+ -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
+ break;
+
+ case USE_SQL_DATES:
+ /* compatible with Oracle/Ingres date formats */
+ if (EuroDates)
+ sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
+ else
+ sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
+ if (tm->tm_year > 0)
+ sprintf((str + 5), "/%04d", tm->tm_year);
+ else
+ sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC");
+ break;
+
+ case USE_GERMAN_DATES:
+ /* German-style date format */
+ sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
+ if (tm->tm_year > 0)
+ sprintf((str + 5), ".%04d", tm->tm_year);
+ else
+ sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC");
+ break;
+
+ case USE_POSTGRES_DATES:
+ default:
+ /* traditional date-only style for Postgres */
+ if (EuroDates)
+ sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
+ else
+ sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
+ if (tm->tm_year > 0)
+ sprintf((str + 5), "-%04d", tm->tm_year);
+ else
+ sprintf((str + 5), "-%04d %s", -(tm->tm_year - 1), "BC");
+ break;
+ }
+
+ return TRUE;
+} /* EncodeDateOnly() */
+
+static void
+TrimTrailingZeros(char *str)
+{
+ int len = strlen(str);
+
+ /* chop off trailing zeros... but leave at least 2 fractional digits */
+ while ((*(str + len - 1) == '0') && (*(str + len - 3) != '.'))
+ {
+ len--;
+ *(str + len) = '\0';
+ }
+}
+
+/* EncodeDateTime()
+ * Encode date and time interpreted as local time.
+ * Support several date styles:
+ * Postgres - day mon hh:mm:ss yyyy tz
+ * SQL - mm/dd/yyyy hh:mm:ss.ss tz
+ * ISO - yyyy-mm-dd hh:mm:ss+/-tz
+ * German - dd.mm.yyyy hh:mm:ss tz
+ * Variants (affects order of month and day for Postgres and SQL styles):
+ * US - mm/dd/yyyy
+ * European - dd/mm/yyyy
+ */
+int
+EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, char *str, bool EuroDates)
+{
+ int day,
+ hour,
+ min;
+
+ switch (style)
+ {
+ case USE_ISO_DATES:
+ /* Compatible with ISO-8601 date formats */
+
+ sprintf(str, "%04d-%02d-%02d %02d:%02d",
+ ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
+ tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
+
+ /*
+ * Print fractional seconds if any. The field widths here should
+ * be at least equal to MAX_TIMESTAMP_PRECISION.
+ *
+ * In float mode, don't print fractional seconds before 1 AD,
+ * since it's unlikely there's any precision left ...
+ */
+#ifdef HAVE_INT64_TIMESTAMP
+ if (fsec != 0)
+ {
+ sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
+#else
+ if ((fsec != 0) && (tm->tm_year > 0))
+ {
+ sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
+#endif
+ TrimTrailingZeros(str);
+ }
+ else
+ sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
+
+ if (tm->tm_year <= 0)
+ sprintf((str + strlen(str)), " BC");
+
+ /*
+ * tzp == NULL indicates that we don't want *any* time zone
+ * info in the output string. *tzn != NULL indicates that we
+ * have alpha time zone info available. tm_isdst != -1
+ * indicates that we have a valid time zone translation.
+ */
+ if ((tzp != NULL) && (tm->tm_isdst >= 0))
+ {
+ hour = -(*tzp / 3600);
+ min = ((abs(*tzp) / 60) % 60);
+ sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
+ }
+ break;
+
+ case USE_SQL_DATES:
+ /* Compatible with Oracle/Ingres date formats */
+
+ if (EuroDates)
+ sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
+ else
+ sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
+
+ sprintf((str + 5), "/%04d %02d:%02d",
+ ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
+ tm->tm_hour, tm->tm_min);
+
+ /*
+ * Print fractional seconds if any. The field widths here should
+ * be at least equal to MAX_TIMESTAMP_PRECISION.
+ *
+ * In float mode, don't print fractional seconds before 1 AD,
+ * since it's unlikely there's any precision left ...
+ */
+#ifdef HAVE_INT64_TIMESTAMP
+ if (fsec != 0)
+ {
+ sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
+#else
+ if ((fsec != 0) && (tm->tm_year > 0))
+ {
+ sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
+#endif
+ TrimTrailingZeros(str);
+ }
+ else
+ sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
+
+ if (tm->tm_year <= 0)
+ sprintf((str + strlen(str)), " BC");
+
+ if ((tzp != NULL) && (tm->tm_isdst >= 0))
+ {
+ if (*tzn != NULL)
+ sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
+ else
+ {
+ hour = -(*tzp / 3600);
+ min = ((abs(*tzp) / 60) % 60);
+ sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
+ }
+ }
+ break;
+
+ case USE_GERMAN_DATES:
+ /* German variant on European style */
+
+ sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
+
+ sprintf((str + 5), ".%04d %02d:%02d",
+ ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)),
+ tm->tm_hour, tm->tm_min);
+
+ /*
+ * Print fractional seconds if any. The field widths here should
+ * be at least equal to MAX_TIMESTAMP_PRECISION.
+ *
+ * In float mode, don't print fractional seconds before 1 AD,
+ * since it's unlikely there's any precision left ...
+ */
+#ifdef HAVE_INT64_TIMESTAMP
+ if (fsec != 0)
+ {
+ sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
+#else
+ if ((fsec != 0) && (tm->tm_year > 0))
+ {
+ sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
+#endif
+ TrimTrailingZeros(str);
+ }
+ else
+ sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
+
+ if (tm->tm_year <= 0)
+ sprintf((str + strlen(str)), " BC");
+
+ if ((tzp != NULL) && (tm->tm_isdst >= 0))
+ {
+ if (*tzn != NULL)
+ sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
+ else
+ {
+ hour = -(*tzp / 3600);
+ min = ((abs(*tzp) / 60) % 60);
+ sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
+ }
+ }
+ break;
+
+ case USE_POSTGRES_DATES:
+ default:
+ /* Backward-compatible with traditional Postgres abstime dates */
+
+ day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
+ tm->tm_wday = j2day(day);
+
+ strncpy(str, days[tm->tm_wday], 3);
+ strcpy((str + 3), " ");
+
+ if (EuroDates)
+ sprintf((str + 4), "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
+ else
+ sprintf((str + 4), "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
+
+ sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min);
+
+ /*
+ * Print fractional seconds if any. The field widths here should
+ * be at least equal to MAX_TIMESTAMP_PRECISION.
+ *
+ * In float mode, don't print fractional seconds before 1 AD,
+ * since it's unlikely there's any precision left ...
+ */
+#ifdef HAVE_INT64_TIMESTAMP
+ if (fsec != 0)
+ {
+ sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
+#else
+ if ((fsec != 0) && (tm->tm_year > 0))
+ {
+ sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
+#endif
+ TrimTrailingZeros(str);
+ }
+ else
+ sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
+
+ sprintf((str + strlen(str)), " %04d",
+ ((tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)));
+ if (tm->tm_year <= 0)
+ sprintf((str + strlen(str)), " BC");
+
+ if ((tzp != NULL) && (tm->tm_isdst >= 0))
+ {
+ if (*tzn != NULL)
+ sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
+ else
+ {
+ /*
+ * We have a time zone, but no string version. Use the
+ * numeric form, but be sure to include a leading
+ * space to avoid formatting something which would be
+ * rejected by the date/time parser later. - thomas
+ * 2001-10-19
+ */
+ hour = -(*tzp / 3600);
+ min = ((abs(*tzp) / 60) % 60);
+ sprintf((str + strlen(str)), ((min != 0) ? " %+03d:%02d" : " %+03d"), hour, min);
+ }
+ }
+ break;
+ }
+
+ return TRUE;
+} /* EncodeDateTime() */
+
+void
+GetEpochTime(struct tm * tm)
+{
+ struct tm *t0;
+ time_t epoch = 0;
+
+ t0 = gmtime(&epoch);
+
+ tm->tm_year = t0->tm_year;
+ tm->tm_mon = t0->tm_mon;
+ tm->tm_mday = t0->tm_mday;
+ tm->tm_hour = t0->tm_hour;
+ tm->tm_min = t0->tm_min;
+ tm->tm_sec = t0->tm_sec;
+
+ if (tm->tm_year < 1900)
+ tm->tm_year += 1900;
+ tm->tm_mon++;
+
+ return;
+} /* GetEpochTime() */
+
+static void
+abstime2tm(AbsoluteTime _time, int *tzp, struct tm * tm, char **tzn)
+{
+ time_t time = (time_t) _time;
+ struct tm *tx;
+
+ if (tzp != NULL)
+ tx = localtime((time_t *) &time);
+ else
+ tx = gmtime((time_t *) &time);
+
+ tm->tm_year = tx->tm_year + 1900;
+ tm->tm_mon = tx->tm_mon + 1;
+ tm->tm_mday = tx->tm_mday;
+ tm->tm_hour = tx->tm_hour;
+ tm->tm_min = tx->tm_min;
+ tm->tm_sec = tx->tm_sec;
+ tm->tm_isdst = tx->tm_isdst;
+
+#if defined(HAVE_TM_ZONE)
+ tm->tm_gmtoff = tx->tm_gmtoff;
+ tm->tm_zone = tx->tm_zone;
+
+ if (tzp != NULL)
+ {
+ /*
+ * We have a brute force time zone per SQL99? Then use it without
+ * change since we have already rotated to the time zone.
+ */
+ *tzp = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */
+ /*
+ * XXX FreeBSD man pages indicate that this should work - tgl
+ * 97/04/23
+ */
+ if (tzn != NULL)
+ {
+ /*
+ * Copy no more than MAXTZLEN bytes of timezone to tzn, in
+ * case it contains an error message, which doesn't fit in
+ * the buffer
+ */
+ StrNCpy(*tzn, tm->tm_zone, MAXTZLEN + 1);
+ if (strlen(tm->tm_zone) > MAXTZLEN)
+ elog(WARNING, "Invalid timezone \'%s\'",
+ tm->tm_zone);
+ }
+ }
+ else
+ tm->tm_isdst = -1;
+#elif defined(HAVE_INT_TIMEZONE)
+ if (tzp != NULL)
+ {
+ *tzp = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
+
+ if (tzn != NULL)
+ {
+ /*
+ * Copy no more than MAXTZLEN bytes of timezone to tzn, in
+ * case it contains an error message, which doesn't fit in
+ * the buffer
+ */
+ StrNCpy(*tzn, tzname[tm->tm_isdst], MAXTZLEN + 1);
+ if (strlen(tzname[tm->tm_isdst]) > MAXTZLEN)
+ elog(WARNING, "Invalid timezone \'%s\'",
+ tzname[tm->tm_isdst]);
+ }
+ }
+ else
+ tm->tm_isdst = -1;
+#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
+ if (tzp != NULL)
+ {
+ /* default to UTC */
+ *tzp = 0;
+ if (tzn != NULL)
+ *tzn = NULL;
+ }
+ else
+ tm->tm_isdst = -1;
+#endif
+}
+
+static void
+GetCurrentDateTime(struct tm * tm)
+{
+ int tz;
+
+ abstime2tm(time(NULL), &tz, tm, NULL);
+}
+
+/* DetermineLocalTimeZone()
+ *
+ * Given a struct tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
+ * tm_sec fields are set, attempt to determine the applicable local zone
+ * (ie, regular or daylight-savings time) at that time. Set the struct tm's
+ * tm_isdst field accordingly, and return the actual timezone offset.
+ *
+ * This subroutine exists to centralize uses of mktime() and defend against
+ * mktime() bugs/restrictions on various platforms. This should be
+ * the *only* call of mktime() in the backend.
+ */
+static int
+DetermineLocalTimeZone(struct tm * tm)
+{
+ int tz;
+
+ if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
+ {
+#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
+
+ /*
+ * Some buggy mktime() implementations may change the
+ * year/month/day when given a time right at a DST boundary. To
+ * prevent corruption of the caller's data, give mktime() a
+ * copy...
+ */
+ struct tm tt,
+ *tmp = &tt;
+
+ *tmp = *tm;
+ /* change to Unix conventions for year/month */
+ tmp->tm_year -= 1900;
+ tmp->tm_mon -= 1;
+
+ /* indicate timezone unknown */
+ tmp->tm_isdst = -1;
+
+ if (mktime(tmp) != ((time_t) -1) &&
+ tmp->tm_isdst >= 0)
+ {
+ /* mktime() succeeded, trust its result */
+ tm->tm_isdst = tmp->tm_isdst;
+
+#if defined(HAVE_TM_ZONE)
+ /* tm_gmtoff is Sun/DEC-ism */
+ tz = -(tmp->tm_gmtoff);
+#elif defined(HAVE_INT_TIMEZONE)
+ tz = ((tmp->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
+#endif /* HAVE_INT_TIMEZONE */
+ }
+ else
+ {
+ /*
+ * We have a buggy (not to say deliberately brain damaged)
+ * mktime(). Work around it by using localtime() instead.
+ *
+ * First, generate the time_t value corresponding to the given
+ * y/m/d/h/m/s taken as GMT time. This will not overflow (at
+ * least not for time_t taken as signed) because of the range
+ * check we did above.
+ */
+ long day,
+ mysec,
+ locsec,
+ delta1,
+ delta2;
+ time_t mytime;
+
+ day = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) -
+ date2j(1970, 1, 1));
+ mysec = tm->tm_sec + (tm->tm_min + (day * 24 + tm->tm_hour) * 60) * 60;
+ mytime = (time_t) mysec;
+
+ /*
+ * Use localtime to convert that time_t to broken-down time,
+ * and reassemble to get a representation of local time.
+ */
+ tmp = localtime(&mytime);
+ day = (date2j(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday) -
+ date2j(1970, 1, 1));
+ locsec = tmp->tm_sec + (tmp->tm_min + (day * 24 + tmp->tm_hour) * 60) * 60;
+
+ /*
+ * The local time offset corresponding to that GMT time is now
+ * computable as mysec - locsec.
+ */
+ delta1 = mysec - locsec;
+
+ /*
+ * However, if that GMT time and the local time we are
+ * actually interested in are on opposite sides of a
+ * daylight-savings-time transition, then this is not the time
+ * offset we want. So, adjust the time_t to be what we think
+ * the GMT time corresponding to our target local time is, and
+ * repeat the localtime() call and delta calculation. We may
+ * have to do it twice before we have a trustworthy delta.
+ *
+ * Note: think not to put a loop here, since if we've been given
+ * an "impossible" local time (in the gap during a
+ * spring-forward transition) we'd never get out of the loop.
+ * Twice is enough to give the behavior we want, which is that
+ * "impossible" times are taken as standard time, while at a
+ * fall-back boundary ambiguous times are also taken as
+ * standard.
+ */
+ mysec += delta1;
+ mytime = (time_t) mysec;
+ tmp = localtime(&mytime);
+ day = (date2j(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday) -
+ date2j(1970, 1, 1));
+ locsec = tmp->tm_sec + (tmp->tm_min + (day * 24 + tmp->tm_hour) * 60) * 60;
+ delta2 = mysec - locsec;
+ if (delta2 != delta1)
+ {
+ mysec += (delta2 - delta1);
+ mytime = (time_t) mysec;
+ tmp = localtime(&mytime);
+ day = (date2j(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday) -
+ date2j(1970, 1, 1));
+ locsec = tmp->tm_sec + (tmp->tm_min + (day * 24 + tmp->tm_hour) * 60) * 60;
+ delta2 = mysec - locsec;
+ }
+ tm->tm_isdst = tmp->tm_isdst;
+ tz = (int) delta2;
+ }
+#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
+ /* Assume UTC if no system timezone info available */
+ tm->tm_isdst = 0;
+ tz = 0;
+#endif
+ }
+ else
+ {
+ /* Given date is out of range, so assume UTC */
+ tm->tm_isdst = 0;
+ tz = 0;
+ }
+
+ return tz;
+}
+
+static void
+dt2time(double jd, int *hour, int *min, int *sec, fsec_t *fsec)
+{
+#ifdef HAVE_INT64_TIMESTAMP
+ int64 time;
+
+#else
+ double time;
+#endif
+
+ time = jd;
+#ifdef HAVE_INT64_TIMESTAMP
+ *hour = (time / INT64CONST(3600000000));
+ time -= ((*hour) * INT64CONST(3600000000));
+ *min = (time / INT64CONST(60000000));
+ time -= ((*min) * INT64CONST(60000000));
+ *sec = (time / INT64CONST(1000000));
+ *fsec = (time - (*sec * INT64CONST(1000000)));
+#else
+ *hour = (time / 3600);
+ time -= ((*hour) * 3600);
+ *min = (time / 60);
+ time -= ((*min) * 60);
+ *sec = time;
+ *fsec = JROUND(time - *sec);
+#endif
+ return;
+} /* dt2time() */
+
+
+
+/* DecodeNumberField()
+ * Interpret numeric string as a concatenated date or time field.
+ * Use the context of previously decoded fields to help with
+ * the interpretation.
+ */
+static int
+DecodeNumberField(int len, char *str, int fmask,
+ int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits, bool EuroDates)
+{
+ char *cp;
+
+ /*
+ * Have a decimal point? Then this is a date or something with a
+ * seconds field...
+ */
+ if ((cp = strchr(str, '.')) != NULL)
+ {
+#ifdef HAVE_INT64_TIMESTAMP
+ char fstr[MAXDATELEN + 1];
+
+ /*
+ * OK, we have at most six digits to care about. Let's construct a
+ * string and then do the conversion to an integer.
+ */
+ strcpy(fstr, (cp + 1));
+ strcpy((fstr + strlen(fstr)), "000000");
+ *(fstr + 6) = '\0';
+ *fsec = strtol(fstr, NULL, 10);
+#else
+ *fsec = strtod(cp, NULL);
+#endif
+ *cp = '\0';
+ len = strlen(str);
+ }
+ /* No decimal point and no complete date yet? */
+ else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
+ {
+ /* yyyymmdd? */
+ if (len == 8)
+ {
+ *tmask = DTK_DATE_M;
+
+ tm->tm_mday = atoi(str + 6);
+ *(str + 6) = '\0';
+ tm->tm_mon = atoi(str + 4);
+ *(str + 4) = '\0';
+ tm->tm_year = atoi(str + 0);
+
+ return DTK_DATE;
+ }
+ /* yymmdd? */
+ else if (len == 6)
+ {
+ *tmask = DTK_DATE_M;
+ tm->tm_mday = atoi(str + 4);
+ *(str + 4) = '\0';
+ tm->tm_mon = atoi(str + 2);
+ *(str + 2) = '\0';
+ tm->tm_year = atoi(str + 0);
+ *is2digits = TRUE;
+
+ return DTK_DATE;
+ }
+ /* yyddd? */
+ else if (len == 5)
+ {
+ *tmask = DTK_DATE_M;
+ tm->tm_mday = atoi(str + 2);
+ *(str + 2) = '\0';
+ tm->tm_mon = 1;
+ tm->tm_year = atoi(str + 0);
+ *is2digits = TRUE;
+
+ return DTK_DATE;
+ }
+ }
+
+ /* not all time fields are specified? */
+ if ((fmask & DTK_TIME_M) != DTK_TIME_M)
+ {
+ /* hhmmss */
+ if (len == 6)
+ {
+ *tmask = DTK_TIME_M;
+ tm->tm_sec = atoi(str + 4);
+ *(str + 4) = '\0';
+ tm->tm_min = atoi(str + 2);
+ *(str + 2) = '\0';
+ tm->tm_hour = atoi(str + 0);
+
+ return DTK_TIME;
+ }
+ /* hhmm? */
+ else if (len == 4)
+ {
+ *tmask = DTK_TIME_M;
+ tm->tm_sec = 0;
+ tm->tm_min = atoi(str + 2);
+ *(str + 2) = '\0';
+ tm->tm_hour = atoi(str + 0);
+
+ return DTK_TIME;
+ }
+ }
+
+ return -1;
+} /* DecodeNumberField() */
+
+
+/* DecodeNumber()
+ * Interpret plain numeric field as a date value in context.
+ */
+static int
+DecodeNumber(int flen, char *str, int fmask,
+ int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits, bool EuroDates)
+{
+ int val;
+ char *cp;
+
+ *tmask = 0;
+
+ val = strtol(str, &cp, 10);
+ if (cp == str)
+ return -1;
+
+ if (*cp == '.')
+ {
+ /*
+ * More than two digits? Then could be a date or a run-together
+ * time: 2001.360 20011225 040506.789
+ */
+ if ((cp - str) > 2)
+ return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
+ tmask, tm, fsec, is2digits, EuroDates);
+
+ *fsec = strtod(cp, &cp);
+ if (*cp != '\0')
+ return -1;
+ }
+ else if (*cp != '\0')
+ return -1;
+
+ /* Special case day of year? */
+ if ((flen == 3) && (fmask & DTK_M(YEAR))
+ && ((val >= 1) && (val <= 366)))
+ {
+ *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
+ tm->tm_yday = val;
+ j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
+ &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ }
+
+ /***
+ * Enough digits to be unequivocal year? Used to test for 4 digits or
+ * more, but we now test first for a three-digit doy so anything
+ * bigger than two digits had better be an explicit year.
+ * - thomas 1999-01-09
+ * Back to requiring a 4 digit year. We accept a two digit
+ * year farther down. - thomas 2000-03-28
+ ***/
+ else if (flen >= 4)
+ {
+ *tmask = DTK_M(YEAR);
+
+ /* already have a year? then see if we can substitute... */
+ if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
+ && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
+ {
+ tm->tm_mday = tm->tm_year;
+ *tmask = DTK_M(DAY);
+ }
+
+ tm->tm_year = val;
+ }
+
+ /* already have year? then could be month */
+ else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
+ && ((val >= 1) && (val <= 12)))
+ {
+ *tmask = DTK_M(MONTH);
+ tm->tm_mon = val;
+ }
+ /* no year and EuroDates enabled? then could be day */
+ else if ((EuroDates || (fmask & DTK_M(MONTH)))
+ && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
+ && ((val >= 1) && (val <= 31)))
+ {
+ *tmask = DTK_M(DAY);
+ tm->tm_mday = val;
+ }
+ else if ((!(fmask & DTK_M(MONTH)))
+ && ((val >= 1) && (val <= 12)))
+ {
+ *tmask = DTK_M(MONTH);
+ tm->tm_mon = val;
+ }
+ else if ((!(fmask & DTK_M(DAY)))
+ && ((val >= 1) && (val <= 31)))
+ {
+ *tmask = DTK_M(DAY);
+ tm->tm_mday = val;
+ }
+
+ /*
+ * Check for 2 or 4 or more digits, but currently we reach here only
+ * if two digits. - thomas 2000-03-28
+ */
+ else if (!(fmask & DTK_M(YEAR))
+ && ((flen >= 4) || (flen == 2)))
+ {
+ *tmask = DTK_M(YEAR);
+ tm->tm_year = val;
+
+ /* adjust ONLY if exactly two digits... */
+ *is2digits = (flen == 2);
+ }
+ else
+ return -1;
+
+ return 0;
+} /* DecodeNumber() */
+
+/* DecodeDate()
+ * Decode date string which includes delimiters.
+ * Insist on a complete set of fields.
+ */
+static int
+DecodeDate(char *str, int fmask, int *tmask, struct tm * tm, bool EuroDates)
+{
+ fsec_t fsec;
+
+ int nf = 0;
+ int i,
+ len;
+ int bc = FALSE;
+ int is2digits = FALSE;
+ int type,
+ val,
+ dmask = 0;
+ char *field[MAXDATEFIELDS];
+
+ /* parse this string... */
+ while ((*str != '\0') && (nf < MAXDATEFIELDS))
+ {
+ /* skip field separators */
+ while (!isalnum((unsigned char) *str))
+ str++;
+
+ field[nf] = str;
+ if (isdigit((unsigned char) *str))
+ {
+ while (isdigit((unsigned char) *str))
+ str++;
+ }
+ else if (isalpha((unsigned char) *str))
+ {
+ while (isalpha((unsigned char) *str))
+ str++;
+ }
+
+ /* Just get rid of any non-digit, non-alpha characters... */
+ if (*str != '\0')
+ *str++ = '\0';
+ nf++;
+ }
+
+#if 0
+ /* don't allow too many fields */
+ if (nf > 3)
+ return -1;
+#endif
+
+ *tmask = 0;
+
+ /* look first for text fields, since that will be unambiguous month */
+ for (i = 0; i < nf; i++)
+ {
+ if (isalpha((unsigned char) *field[i]))
+ {
+ type = DecodeSpecial(i, field[i], &val);
+ if (type == IGNORE_DTF)
+ continue;
+
+ dmask = DTK_M(type);
+ switch (type)
+ {
+ case MONTH:
+ tm->tm_mon = val;
+ break;
+
+ case ADBC:
+ bc = (val == BC);
+ break;
+
+ default:
+ return -1;
+ }
+ if (fmask & dmask)
+ return -1;
+
+ fmask |= dmask;
+ *tmask |= dmask;
+
+ /* mark this field as being completed */
+ field[i] = NULL;
+ }
+ }
+
+ /* now pick up remaining numeric fields */
+ for (i = 0; i < nf; i++)
+ {
+ if (field[i] == NULL)
+ continue;
+
+ if ((len = strlen(field[i])) <= 0)
+ return -1;
+
+ if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits, EuroDates) != 0)
+ return -1;
+
+ if (fmask & dmask)
+ return -1;
+
+ fmask |= dmask;
+ *tmask |= dmask;
+ }
+
+ if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
+ return -1;
+
+ /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
+ if (bc)
+ {
+ if (tm->tm_year > 0)
+ tm->tm_year = -(tm->tm_year - 1);
+ else
+ return -1;
+ }
+ else if (is2digits)
+ {
+ if (tm->tm_year < 70)
+ tm->tm_year += 2000;
+ else if (tm->tm_year < 100)
+ tm->tm_year += 1900;
+ }
+
+ return 0;
+} /* DecodeDate() */
+
+
+/* DecodeTime()
+ * Decode time string which includes delimiters.
+ * Only check the lower limit on hours, since this same code
+ * can be used to represent time spans.
+ */
+static int
+DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
+{
+ char *cp;
+
+ *tmask = DTK_TIME_M;
+
+ tm->tm_hour = strtol(str, &cp, 10);
+ if (*cp != ':')
+ return -1;
+ str = cp + 1;
+ tm->tm_min = strtol(str, &cp, 10);
+ if (*cp == '\0')
+ {
+ tm->tm_sec = 0;
+ *fsec = 0;
+ }
+ else if (*cp != ':')
+ return -1;
+ else
+ {
+ str = cp + 1;
+ tm->tm_sec = strtol(str, &cp, 10);
+ if (*cp == '\0')
+ *fsec = 0;
+ else if (*cp == '.')
+ {
+#ifdef HAVE_INT64_TIMESTAMP
+ char fstr[MAXDATELEN + 1];
+
+ /*
+ * OK, we have at most six digits to work with. Let's
+ * construct a string and then do the conversion to an
+ * integer.
+ */
+ strncpy(fstr, (cp + 1), 7);
+ strcpy((fstr + strlen(fstr)), "000000");
+ *(fstr + 6) = '\0';
+ *fsec = strtol(fstr, &cp, 10);
+#else
+ str = cp;
+ *fsec = strtod(str, &cp);
+#endif
+ if (*cp != '\0')
+ return -1;
+ }
+ else
+ return -1;
+ }
+
+ /* do a sanity check */
+#ifdef HAVE_INT64_TIMESTAMP
+ if ((tm->tm_hour < 0)
+ || (tm->tm_min < 0) || (tm->tm_min > 59)
+ || (tm->tm_sec < 0) || (tm->tm_sec > 59)
+ || (*fsec >= INT64CONST(1000000)))
+ return -1;
+#else
+ if ((tm->tm_hour < 0)
+ || (tm->tm_min < 0) || (tm->tm_min > 59)
+ || (tm->tm_sec < 0) || (tm->tm_sec > 59)
+ || (*fsec >= 1))
+ return -1;
+#endif
+
+ return 0;
+} /* DecodeTime() */
+
+/* DecodeTimezone()
+ * Interpret string as a numeric timezone.
+ *
+ * Note: we allow timezone offsets up to 13:59. There are places that
+ * use +1300 summer time.
+ */
+static int
+DecodeTimezone(char *str, int *tzp)
+{
+ int tz;
+ int hr,
+ min;
+ char *cp;
+ int len;
+
+ /* assume leading character is "+" or "-" */
+ hr = strtol((str + 1), &cp, 10);
+
+ /* explicit delimiter? */
+ if (*cp == ':')
+ min = strtol((cp + 1), &cp, 10);
+ /* otherwise, might have run things together... */
+ else if ((*cp == '\0') && ((len = strlen(str)) > 3))
+ {
+ min = strtol((str + len - 2), &cp, 10);
+ if ((min < 0) || (min >= 60))
+ return -1;
+
+ *(str + len - 2) = '\0';
+ hr = strtol((str + 1), &cp, 10);
+ if ((hr < 0) || (hr > 13))
+ return -1;
+ }
+ else
+ min = 0;
+
+ tz = (hr * 60 + min) * 60;
+ if (*str == '-')
+ tz = -tz;
+
+ *tzp = -tz;
+ return *cp != '\0';
+} /* DecodeTimezone() */
+
+
+/* DecodePosixTimezone()
+ * Interpret string as a POSIX-compatible timezone:
+ * PST-hh:mm
+ * PST+h
+ * - thomas 2000-03-15
+ */
+static int
+DecodePosixTimezone(char *str, int *tzp)
+{
+ int val,
+ tz;
+ int type;
+ char *cp;
+ char delim;
+
+ cp = str;
+ while ((*cp != '\0') && isalpha((unsigned char) *cp))
+ cp++;
+
+ if (DecodeTimezone(cp, &tz) != 0)
+ return -1;
+
+ delim = *cp;
+ *cp = '\0';
+ type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
+ *cp = delim;
+
+ switch (type)
+ {
+ case DTZ:
+ case TZ:
+ *tzp = (val * 60) - tz;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+} /* DecodePosixTimezone() */
+
+/* ParseDateTime()
+ * Break string into tokens based on a date/time context.
+ * Several field types are assigned:
+ * DTK_NUMBER - digits and (possibly) a decimal point
+ * DTK_DATE - digits and two delimiters, or digits and text
+ * DTK_TIME - digits, colon delimiters, and possibly a decimal point
+ * DTK_STRING - text (no digits)
+ * DTK_SPECIAL - leading "+" or "-" followed by text
+ * DTK_TZ - leading "+" or "-" followed by digits
+ * Note that some field types can hold unexpected items:
+ * DTK_NUMBER can hold date fields (yy.ddd)
+ * DTK_STRING can hold months (January) and time zones (PST)
+ * DTK_DATE can hold Posix time zones (GMT-8)
+ */
+int
+ParseDateTime(char *timestr, char *lowstr,
+ char **field, int *ftype, int maxfields, int *numfields, char **endstr)
+{
+ int nf = 0;
+ char *lp = lowstr;
+
+ *endstr = timestr;
+ /* outer loop through fields */
+ while (*(*endstr) != '\0')
+ {
+ field[nf] = lp;
+
+ /* leading digit? then date or time */
+ if (isdigit((unsigned char) *(*endstr)))
+ {
+ *lp++ = *(*endstr)++;
+ while (isdigit((unsigned char) *(*endstr)))
+ *lp++ = *(*endstr)++;
+
+ /* time field? */
+ if (*(*endstr) == ':')
+ {
+ ftype[nf] = DTK_TIME;
+ *lp++ = *(*endstr)++;
+ while (isdigit((unsigned char) *(*endstr)) ||
+ (*(*endstr) == ':') || (*(*endstr) == '.'))
+ *lp++ = *(*endstr)++;
+ }
+ /* date field? allow embedded text month */
+ else if ((*(*endstr) == '-') || (*(*endstr) == '/') || (*(*endstr) == '.'))
+ {
+ /* save delimiting character to use later */
+ char *dp = (*endstr);
+
+ *lp++ = *(*endstr)++;
+ /* second field is all digits? then no embedded text month */
+ if (isdigit((unsigned char) *(*endstr)))
+ {
+ ftype[nf] = ((*dp == '.') ? DTK_NUMBER : DTK_DATE);
+ while (isdigit((unsigned char) *(*endstr)))
+ *lp++ = *(*endstr)++;
+
+ /*
+ * insist that the delimiters match to get a
+ * three-field date.
+ */
+ if (*(*endstr) == *dp)
+ {
+ ftype[nf] = DTK_DATE;
+ *lp++ = *(*endstr)++;
+ while (isdigit((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
+ *lp++ = *(*endstr)++;
+ }
+ }
+ else
+ {
+ ftype[nf] = DTK_DATE;
+ while (isalnum((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
+ *lp++ = tolower((unsigned char) *(*endstr)++);
+ }
+ }
+
+ /*
+ * otherwise, number only and will determine year, month, day,
+ * or concatenated fields later...
+ */
+ else
+ ftype[nf] = DTK_NUMBER;
+ }
+ /* Leading decimal point? Then fractional seconds... */
+ else if (*(*endstr) == '.')
+ {
+ *lp++ = *(*endstr)++;
+ while (isdigit((unsigned char) *(*endstr)))
+ *lp++ = *(*endstr)++;
+
+ ftype[nf] = DTK_NUMBER;
+ }
+
+ /*
+ * text? then date string, month, day of week, special, or
+ * timezone
+ */
+ else if (isalpha((unsigned char) *(*endstr)))
+ {
+ ftype[nf] = DTK_STRING;
+ *lp++ = tolower((unsigned char) *(*endstr)++);
+ while (isalpha((unsigned char) *(*endstr)))
+ *lp++ = tolower((unsigned char) *(*endstr)++);
+
+ /*
+ * Full date string with leading text month? Could also be a
+ * POSIX time zone...
+ */
+ if ((*(*endstr) == '-') || (*(*endstr) == '/') || (*(*endstr) == '.'))
+ {
+ char *dp = (*endstr);
+
+ ftype[nf] = DTK_DATE;
+ *lp++ = *(*endstr)++;
+ while (isdigit((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
+ *lp++ = *(*endstr)++;
+ }
+ }
+ /* skip leading spaces */
+ else if (isspace((unsigned char) *(*endstr)))
+ {
+ (*endstr)++;
+ continue;
+ }
+ /* sign? then special or numeric timezone */
+ else if ((*(*endstr) == '+') || (*(*endstr) == '-'))
+ {
+ *lp++ = *(*endstr)++;
+ /* soak up leading whitespace */
+ while (isspace((unsigned char) *(*endstr)))
+ (*endstr)++;
+ /* numeric timezone? */
+ if (isdigit((unsigned char) *(*endstr)))
+ {
+ ftype[nf] = DTK_TZ;
+ *lp++ = *(*endstr)++;
+ while (isdigit((unsigned char) *(*endstr)) ||
+ (*(*endstr) == ':') || (*(*endstr) == '.'))
+ *lp++ = *(*endstr)++;
+ }
+ /* special? */
+ else if (isalpha((unsigned char) *(*endstr)))
+ {
+ ftype[nf] = DTK_SPECIAL;
+ *lp++ = tolower((unsigned char) *(*endstr)++);
+ while (isalpha((unsigned char) *(*endstr)))
+ *lp++ = tolower((unsigned char) *(*endstr)++);
+ }
+ /* otherwise something wrong... */
+ else
+ return -1;
+ }
+ /* ignore punctuation but use as delimiter */
+ else if (ispunct((unsigned char) *(*endstr)))
+ {
+ (*endstr)++;
+ continue;
+
+ }
+ /* otherwise, something is not right... */
+ else
+ return -1;
+
+ /* force in a delimiter after each field */
+ *lp++ = '\0';
+ nf++;
+ if (nf > MAXDATEFIELDS)
+ return -1;
+ }
+
+ *numfields = nf;
+
+ return 0;
+} /* ParseDateTime() */
+
+
+/* DecodeDateTime()
+ * Interpret previously parsed fields for general date and time.
+ * Return 0 if full date, 1 if only time, and -1 if problems.
+ * External format(s):
+ * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
+ * "Fri Feb-7-1997 15:23:27"
+ * "Feb-7-1997 15:23:27"
+ * "2-7-1997 15:23:27"
+ * "1997-2-7 15:23:27"
+ * "1997.038 15:23:27" (day of year 1-366)
+ * Also supports input in compact time:
+ * "970207 152327"
+ * "97038 152327"
+ * "20011225T040506.789-07"
+ *
+ * Use the system-provided functions to get the current time zone
+ * if not specified in the input string.
+ * If the date is outside the time_t system-supported time range,
+ * then assume UTC time zone. - thomas 1997-05-27
+ */
+int
+DecodeDateTime(char **field, int *ftype, int nf,
+ int *dtype, struct tm * tm, fsec_t *fsec, int *tzp, bool EuroDates)
+{
+ int fmask = 0,
+ tmask,
+ type;
+ int ptype = 0; /* "prefix type" for ISO y2001m02d04
+ * format */
+ int i;
+ int val;
+ int mer = HR24;
+ int haveTextMonth = FALSE;
+ int is2digits = FALSE;
+ int bc = FALSE;
+
+ /***
+ * We'll insist on at least all of the date fields, but initialize the
+ * remaining fields in case they are not set later...
+ ***/
+ *dtype = DTK_DATE;
+ tm->tm_hour = 0;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ *fsec = 0;
+ /* don't know daylight savings time status apriori */
+ tm->tm_isdst = -1;
+ if (tzp != NULL)
+ *tzp = 0;
+
+ for (i = 0; i < nf; i++)
+ {
+ switch (ftype[i])
+ {
+ case DTK_DATE:
+ /***
+ * Integral julian day with attached time zone?
+ * All other forms with JD will be separated into
+ * distinct fields, so we handle just this case here.
+ ***/
+ if (ptype == DTK_JULIAN)
+ {
+ char *cp;
+ int val;
+
+ if (tzp == NULL)
+ return -1;
+
+ val = strtol(field[i], &cp, 10);
+ if (*cp != '-')
+ return -1;
+
+ j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ /* Get the time zone from the end of the string */
+ if (DecodeTimezone(cp, tzp) != 0)
+ return -1;
+
+ tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
+ ptype = 0;
+ break;
+ }
+ /***
+ * Already have a date? Then this might be a POSIX time
+ * zone with an embedded dash (e.g. "PST-3" == "EST") or
+ * a run-together time with trailing time zone (e.g. hhmmss-zz).
+ * - thomas 2001-12-25
+ ***/
+ else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
+ || (ptype != 0))
+ {
+ /* No time zone accepted? Then quit... */
+ if (tzp == NULL)
+ return -1;
+
+ if (isdigit((unsigned char) *field[i]) || ptype != 0)
+ {
+ char *cp;
+
+ if (ptype != 0)
+ {
+ /* Sanity check; should not fail this test */
+ if (ptype != DTK_TIME)
+ return -1;
+ ptype = 0;
+ }
+
+ /*
+ * Starts with a digit but we already have a time
+ * field? Then we are in trouble with a date and
+ * time already...
+ */
+ if ((fmask & DTK_TIME_M) == DTK_TIME_M)
+ return -1;
+
+ if ((cp = strchr(field[i], '-')) == NULL)
+ return -1;
+
+ /* Get the time zone from the end of the string */
+ if (DecodeTimezone(cp, tzp) != 0)
+ return -1;
+ *cp = '\0';
+
+ /*
+ * Then read the rest of the field as a
+ * concatenated time
+ */
+ if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
+ &tmask, tm, fsec, &is2digits, EuroDates)) < 0)
+ return -1;
+
+ /*
+ * modify tmask after returning from
+ * DecodeNumberField()
+ */
+ tmask |= DTK_M(TZ);
+ }
+ else
+ {
+ if (DecodePosixTimezone(field[i], tzp) != 0)
+ return -1;
+
+ ftype[i] = DTK_TZ;
+ tmask = DTK_M(TZ);
+ }
+ }
+ else if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
+ return -1;
+ break;
+
+ case DTK_TIME:
+ if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
+ return -1;
+
+ /*
+ * Check upper limit on hours; other limits checked in
+ * DecodeTime()
+ */
+ if (tm->tm_hour > 23)
+ return -1;
+ break;
+
+ case DTK_TZ:
+ {
+ int tz;
+
+ if (tzp == NULL)
+ return -1;
+
+ if (DecodeTimezone(field[i], &tz) != 0)
+ return -1;
+
+ /*
+ * Already have a time zone? Then maybe this is the
+ * second field of a POSIX time: EST+3 (equivalent to
+ * PST)
+ */
+ if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
+ && (ftype[i - 1] == DTK_TZ)
+ && (isalpha((unsigned char) *field[i - 1])))
+ {
+ *tzp -= tz;
+ tmask = 0;
+ }
+ else
+ {
+ *tzp = tz;
+ tmask = DTK_M(TZ);
+ }
+ }
+ break;
+
+ case DTK_NUMBER:
+
+ /*
+ * Was this an "ISO date" with embedded field labels? An
+ * example is "y2001m02d04" - thomas 2001-02-04
+ */
+ if (ptype != 0)
+ {
+ char *cp;
+ int val;
+
+ val = strtol(field[i], &cp, 10);
+
+ /*
+ * only a few kinds are allowed to have an embedded
+ * decimal
+ */
+ if (*cp == '.')
+ switch (ptype)
+ {
+ case DTK_JULIAN:
+ case DTK_TIME:
+ case DTK_SECOND:
+ break;
+ default:
+ return 1;
+ break;
+ }
+ else if (*cp != '\0')
+ return -1;
+
+ switch (ptype)
+ {
+ case DTK_YEAR:
+ tm->tm_year = val;
+ tmask = DTK_M(YEAR);
+ break;
+
+ case DTK_MONTH:
+
+ /*
+ * already have a month and hour? then assume
+ * minutes
+ */
+ if (((fmask & DTK_M(MONTH)) != 0)
+ && ((fmask & DTK_M(HOUR)) != 0))
+ {
+ tm->tm_min = val;
+ tmask = DTK_M(MINUTE);
+ }
+ else
+ {
+ tm->tm_mon = val;
+ tmask = DTK_M(MONTH);
+ }
+ break;
+
+ case DTK_DAY:
+ tm->tm_mday = val;
+ tmask = DTK_M(DAY);
+ break;
+
+ case DTK_HOUR:
+ tm->tm_hour = val;
+ tmask = DTK_M(HOUR);
+ break;
+
+ case DTK_MINUTE:
+ tm->tm_min = val;
+ tmask = DTK_M(MINUTE);
+ break;
+
+ case DTK_SECOND:
+ tm->tm_sec = val;
+ tmask = DTK_M(SECOND);
+ if (*cp == '.')
+ {
+ double frac;
+
+ frac = strtod(cp, &cp);
+ if (*cp != '\0')
+ return -1;
+#ifdef HAVE_INT64_TIMESTAMP
+ *fsec = frac * 1000000;
+#else
+ *fsec = frac;
+#endif
+ }
+ break;
+
+ case DTK_TZ:
+ tmask = DTK_M(TZ);
+ if (DecodeTimezone(field[i], tzp) != 0)
+ return -1;
+ break;
+
+ case DTK_JULIAN:
+ /***
+ * previous field was a label for "julian date"?
+ ***/
+ tmask = DTK_DATE_M;
+ j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ /* fractional Julian Day? */
+ if (*cp == '.')
+ {
+ double time;
+
+ time = strtod(cp, &cp);
+ if (*cp != '\0')
+ return -1;
+
+ tmask |= DTK_TIME_M;
+#ifdef HAVE_INT64_TIMESTAMP
+ dt2time((time * 86400000000), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
+#else
+ dt2time((time * 86400), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
+#endif
+ }
+ break;
+
+ case DTK_TIME:
+ /* previous field was "t" for ISO time */
+ if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
+ &tmask, tm, fsec, &is2digits, EuroDates)) < 0)
+ return -1;
+
+ if (tmask != DTK_TIME_M)
+ return -1;
+ break;
+
+ default:
+ return -1;
+ break;
+ }
+
+ ptype = 0;
+ *dtype = DTK_DATE;
+ }
+ else
+ {
+ char *cp;
+ int flen;
+
+ flen = strlen(field[i]);
+ cp = strchr(field[i], '.');
+
+ /* Embedded decimal and no date yet? */
+ if ((cp != NULL) && !(fmask & DTK_DATE_M))
+ {
+ if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
+ return -1;
+ }
+ /* embedded decimal and several digits before? */
+ else if ((cp != NULL) && ((flen - strlen(cp)) > 2))
+ {
+ /*
+ * Interpret as a concatenated date or time Set
+ * the type field to allow decoding other fields
+ * later. Example: 20011223 or 040506
+ */
+ if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
+ &tmask, tm, fsec, &is2digits, EuroDates)) < 0)
+ return -1;
+ }
+ else if (flen > 4)
+ {
+ if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
+ &tmask, tm, fsec, &is2digits, EuroDates)) < 0)
+ return -1;
+ }
+ /* otherwise it is a single date/time field... */
+ else if (DecodeNumber(flen, field[i], fmask,
+ &tmask, tm, fsec, &is2digits, EuroDates) != 0)
+ return -1;
+ }
+ break;
+
+ case DTK_STRING:
+ case DTK_SPECIAL:
+ type = DecodeSpecial(i, field[i], &val);
+ if (type == IGNORE_DTF)
+ continue;
+
+ tmask = DTK_M(type);
+ switch (type)
+ {
+ case RESERV:
+ switch (val)
+ {
+ case DTK_NOW:
+ tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
+ *dtype = DTK_DATE;
+ GetCurrentDateTime(tm);
+ break;
+
+ case DTK_YESTERDAY:
+ tmask = DTK_DATE_M;
+ *dtype = DTK_DATE;
+ GetCurrentDateTime(tm);
+ j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1),
+ &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ tm->tm_hour = 0;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ break;
+
+ case DTK_TODAY:
+ tmask = DTK_DATE_M;
+ *dtype = DTK_DATE;
+ GetCurrentDateTime(tm);
+ tm->tm_hour = 0;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ break;
+
+ case DTK_TOMORROW:
+ tmask = DTK_DATE_M;
+ *dtype = DTK_DATE;
+ GetCurrentDateTime(tm);
+ j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1),
+ &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ tm->tm_hour = 0;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ break;
+
+ case DTK_ZULU:
+ tmask = (DTK_TIME_M | DTK_M(TZ));
+ *dtype = DTK_DATE;
+ tm->tm_hour = 0;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ if (tzp != NULL)
+ *tzp = 0;
+ break;
+
+ default:
+ *dtype = val;
+ }
+
+ break;
+
+ case MONTH:
+
+ /*
+ * already have a (numeric) month? then see if we
+ * can substitute...
+ */
+ if ((fmask & DTK_M(MONTH)) && (!haveTextMonth)
+ && (!(fmask & DTK_M(DAY)))
+ && ((tm->tm_mon >= 1) && (tm->tm_mon <= 31)))
+ {
+ tm->tm_mday = tm->tm_mon;
+ tmask = DTK_M(DAY);
+ }
+ haveTextMonth = TRUE;
+ tm->tm_mon = val;
+ break;
+
+ case DTZMOD:
+
+ /*
+ * daylight savings time modifier (solves "MET
+ * DST" syntax)
+ */
+ tmask |= DTK_M(DTZ);
+ tm->tm_isdst = 1;
+ if (tzp == NULL)
+ return -1;
+ *tzp += val * 60;
+ break;
+
+ case DTZ:
+
+ /*
+ * set mask for TZ here _or_ check for DTZ later
+ * when getting default timezone
+ */
+ tmask |= DTK_M(TZ);
+ tm->tm_isdst = 1;
+ if (tzp == NULL)
+ return -1;
+ *tzp = val * 60;
+ ftype[i] = DTK_TZ;
+ break;
+
+ case TZ:
+ tm->tm_isdst = 0;
+ if (tzp == NULL)
+ return -1;
+ *tzp = val * 60;
+ ftype[i] = DTK_TZ;
+ break;
+
+ case IGNORE_DTF:
+ break;
+
+ case AMPM:
+ mer = val;
+ break;
+
+ case ADBC:
+ bc = (val == BC);
+ break;
+
+ case DOW:
+ tm->tm_wday = val;
+ break;
+
+ case UNITS:
+ tmask = 0;
+ ptype = val;
+ break;
+
+ case ISOTIME:
+
+ /*
+ * This is a filler field "t" indicating that the
+ * next field is time. Try to verify that this is
+ * sensible.
+ */
+ tmask = 0;
+
+ /* No preceeding date? Then quit... */
+ if ((fmask & DTK_DATE_M) != DTK_DATE_M)
+ return -1;
+
+ /***
+ * We will need one of the following fields:
+ * DTK_NUMBER should be hhmmss.fff
+ * DTK_TIME should be hh:mm:ss.fff
+ * DTK_DATE should be hhmmss-zz
+ ***/
+ if ((i >= (nf - 1))
+ || ((ftype[i + 1] != DTK_NUMBER)
+ && (ftype[i + 1] != DTK_TIME)
+ && (ftype[i + 1] != DTK_DATE)))
+ return -1;
+
+ ptype = val;
+ break;
+
+ default:
+ return -1;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ if (tmask & fmask)
+ return -1;
+ fmask |= tmask;
+ }
+
+ /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
+ if (bc)
+ {
+ if (tm->tm_year > 0)
+ tm->tm_year = -(tm->tm_year - 1);
+ else
+ return -1;
+ }
+ else if (is2digits)
+ {
+ if (tm->tm_year < 70)
+ tm->tm_year += 2000;
+ else if (tm->tm_year < 100)
+ tm->tm_year += 1900;
+ }
+
+ if ((mer != HR24) && (tm->tm_hour > 12))
+ return -1;
+ if ((mer == AM) && (tm->tm_hour == 12))
+ tm->tm_hour = 0;
+ else if ((mer == PM) && (tm->tm_hour != 12))
+ tm->tm_hour += 12;
+
+ /* do additional checking for full date specs... */
+ if (*dtype == DTK_DATE)
+ {
+ if ((fmask & DTK_DATE_M) != DTK_DATE_M)
+ return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
+
+ /*
+ * check for valid day of month, now that we know for sure the
+ * month and year...
+ */
+ if ((tm->tm_mday < 1)
+ || (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]))
+ return -1;
+
+ /* timezone not specified? then find local timezone if possible */
+ if (((fmask & DTK_DATE_M) == DTK_DATE_M)
+ && (tzp != NULL) && (!(fmask & DTK_M(TZ))))
+ {
+ /*
+ * daylight savings time modifier but no standard timezone?
+ * then error
+ */
+ if (fmask & DTK_M(DTZMOD))
+ return -1;
+
+ *tzp = DetermineLocalTimeZone(tm);
+ }
+ }
+
+ return 0;
+} /* DecodeDateTime() */
+
diff --git a/src/interfaces/ecpg/pgtypeslib/extern.h b/src/interfaces/ecpg/pgtypeslib/extern.h
new file mode 100644
index 00000000000..b843107f653
--- /dev/null
+++ b/src/interfaces/ecpg/pgtypeslib/extern.h
@@ -0,0 +1,4 @@
+#include <string.h>
+
+extern char *pgtypes_alloc(long);
+extern char *pgtypes_strdup(char *);
diff --git a/src/interfaces/ecpg/pgtypeslib/numeric.c b/src/interfaces/ecpg/pgtypeslib/numeric.c
index 03fa42089ae..d164579f5a5 100644
--- a/src/interfaces/ecpg/pgtypeslib/numeric.c
+++ b/src/interfaces/ecpg/pgtypeslib/numeric.c
@@ -5,9 +5,9 @@
#include <math.h>
#include <errno.h>
#include <stdlib.h>
-#include <string.h>
#include "c.h"
+#include "extern.h"
#include "numeric.h"
#include "pgtypes_error.h"
@@ -25,21 +25,6 @@
#include "pgtypes_numeric.h"
-static char *
-pgtypes_alloc(long size)
-{
- char *new = (char *) calloc(1L, size);
-
- if (!new)
- {
- errno = ENOMEM;
- return NULL;
- }
-
- memset(new, '\0', size);
- return (new);
-}
-
#if 0
/* ----------
* apply_typmod() -
diff --git a/src/interfaces/ecpg/pgtypeslib/timestamp.c b/src/interfaces/ecpg/pgtypeslib/timestamp.c
new file mode 100644
index 00000000000..2bf3557f760
--- /dev/null
+++ b/src/interfaces/ecpg/pgtypeslib/timestamp.c
@@ -0,0 +1,356 @@
+#include <math.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <float.h>
+#include <stdio.h>
+
+#ifdef __FAST_MATH__
+#error -ffast-math is known to break this code
+#endif
+
+#include "dt.h"
+#include "extern.h"
+#include "pgtypes_error.h"
+#include "pgtypes_timestamp.h"
+
+#ifdef HAVE_INT64_TIMESTAMP
+static int64
+time2t(const int hour, const int min, const int sec, const fsec_t fsec)
+{
+ return ((((((hour * 60) + min) * 60) + sec) * INT64CONST(1000000)) + fsec);
+} /* time2t() */
+
+#else
+static double
+time2t(const int hour, const int min, const int sec, const fsec_t fsec)
+{
+ return ((((hour * 60) + min) * 60) + sec + fsec);
+} /* time2t() */
+#endif
+
+static Timestamp
+dt2local(Timestamp dt, int tz)
+{
+#ifdef HAVE_INT64_TIMESTAMP
+ dt -= (tz * INT64CONST(1000000));
+#else
+ dt -= tz;
+ dt = JROUND(dt);
+#endif
+ return dt;
+} /* dt2local() */
+
+/* tm2timestamp()
+ * Convert a tm structure to a timestamp data type.
+ * Note that year is _not_ 1900-based, but is an explicit full value.
+ * Also, month is one-based, _not_ zero-based.
+ */
+static int
+tm2timestamp(struct tm * tm, fsec_t fsec, int *tzp, Timestamp *result)
+{
+#ifdef HAVE_INT64_TIMESTAMP
+ int date;
+ int64 time;
+
+#else
+ double date,
+ time;
+#endif
+
+ /* Julian day routines are not correct for negative Julian days */
+ if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
+ return -1;
+
+ date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
+ time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
+#ifdef HAVE_INT64_TIMESTAMP
+ *result = ((date * INT64CONST(86400000000)) + time);
+ if ((*result < 0 && date >= 0) || (*result >= 0 && date < 0))
+ elog(ERROR, "TIMESTAMP out of range '%04d-%02d-%02d'",
+ tm->tm_year, tm->tm_mon, tm->tm_mday);
+#else
+ *result = ((date * 86400) + time);
+#endif
+ if (tzp != NULL)
+ *result = dt2local(*result, -(*tzp));
+
+ return 0;
+} /* tm2timestamp() */
+
+static Timestamp
+SetEpochTimestamp(void)
+{
+ Timestamp dt;
+ struct tm tt, *tm = &tt;
+
+ GetEpochTime(tm);
+ tm2timestamp(tm, 0, NULL, &dt);
+ return dt;
+} /* SetEpochTimestamp() */
+
+static void
+dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
+{
+#ifdef HAVE_INT64_TIMESTAMP
+ int64 time;
+#else
+ double time;
+#endif
+
+ time = jd;
+
+#ifdef HAVE_INT64_TIMESTAMP
+ *hour = (time / INT64CONST(3600000000));
+ time -= ((*hour) * INT64CONST(3600000000));
+ *min = (time / INT64CONST(60000000));
+ time -= ((*min) * INT64CONST(60000000));
+ *sec = (time / INT64CONST(1000000));
+ *fsec = (time - (*sec * INT64CONST(1000000)));
+ *sec = (time / INT64CONST(1000000));
+ *fsec = (time - (*sec * INT64CONST(1000000)));
+#else
+ *hour = (time / 3600);
+ time -= ((*hour) * 3600);
+ *min = (time / 60);
+ time -= ((*min) * 60);
+ *sec = time;
+ *fsec = JROUND(time - *sec);
+#endif
+ return;
+} /* dt2time() */
+
+/* timestamp2tm()
+ * Convert timestamp data type to POSIX time structure.
+ * Note that year is _not_ 1900-based, but is an explicit full value.
+ * Also, month is one-based, _not_ zero-based.
+ * Returns:
+ * 0 on success
+ * -1 on out of range
+ *
+ * For dates within the system-supported time_t range, convert to the
+ * local time zone. If out of this range, leave as GMT. - tgl 97/05/27
+ */
+static int
+timestamp2tm(Timestamp dt, int *tzp, struct tm * tm, fsec_t *fsec, char **tzn)
+{
+#ifdef HAVE_INT64_TIMESTAMP
+ int date,
+ date0;
+ int64 time;
+
+#else
+ double date,
+ date0;
+ double time;
+#endif
+ time_t utime;
+
+#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
+ struct tm *tx;
+#endif
+
+ date0 = date2j(2000, 1, 1);
+
+ time = dt;
+#ifdef HAVE_INT64_TIMESTAMP
+ TMODULO(time, date, INT64CONST(86400000000));
+
+ if (time < INT64CONST(0))
+ {
+ time += INT64CONST(86400000000);
+ date -= 1;
+ }
+#else
+ TMODULO(time, date, 86400e0);
+
+ if (time < 0)
+ {
+ time += 86400;
+ date -= 1;
+ }
+#endif
+
+ /* Julian day routine does not work for negative Julian days */
+ if (date < -date0)
+ return -1;
+
+ /* add offset to go from J2000 back to standard Julian date */
+ date += date0;
+
+ j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
+
+ if (tzp != NULL)
+ {
+ /*
+ * Does this fall within the capabilities of the localtime()
+ * interface? Then use this to rotate to the local time zone.
+ */
+ if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
+ {
+#ifdef HAVE_INT64_TIMESTAMP
+ utime = ((dt / INT64CONST(1000000))
+ + ((date0 - date2j(1970, 1, 1)) * INT64CONST(86400)));
+#else
+ utime = (dt + ((date0 - date2j(1970, 1, 1)) * 86400));
+#endif
+
+#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
+ tx = localtime(&utime);
+ tm->tm_year = tx->tm_year + 1900;
+ tm->tm_mon = tx->tm_mon + 1;
+ tm->tm_mday = tx->tm_mday;
+ tm->tm_hour = tx->tm_hour;
+ tm->tm_min = tx->tm_min;
+ tm->tm_isdst = tx->tm_isdst;
+
+#if defined(HAVE_TM_ZONE)
+ tm->tm_gmtoff = tx->tm_gmtoff;
+ tm->tm_zone = tx->tm_zone;
+
+ *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
+ if (tzn != NULL)
+ *tzn = (char *) tm->tm_zone;
+#elif defined(HAVE_INT_TIMEZONE)
+ *tzp = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
+ if (tzn != NULL)
+ *tzn = tzname[(tm->tm_isdst > 0)];
+#endif
+
+#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
+ *tzp = 0;
+ /* Mark this as *no* time zone available */
+ tm->tm_isdst = -1;
+ if (tzn != NULL)
+ *tzn = NULL;
+#endif
+
+ dt = dt2local(dt, *tzp);
+ }
+ else
+ {
+ *tzp = 0;
+ /* Mark this as *no* time zone available */
+ tm->tm_isdst = -1;
+ if (tzn != NULL)
+ *tzn = NULL;
+ }
+ }
+ else
+ {
+ tm->tm_isdst = -1;
+ if (tzn != NULL)
+ *tzn = NULL;
+ }
+
+ return 0;
+} /* timestamp2tm() */
+
+/* EncodeSpecialTimestamp()
+ * * Convert reserved timestamp data type to string.
+ * */
+static int
+EncodeSpecialTimestamp(Timestamp dt, char *str)
+{
+ if (TIMESTAMP_IS_NOBEGIN(dt))
+ strcpy(str, EARLY);
+ else if (TIMESTAMP_IS_NOEND(dt))
+ strcpy(str, LATE);
+ else
+ return FALSE;
+
+ return TRUE;
+} /* EncodeSpecialTimestamp() */
+
+Timestamp
+PGTYPEStimestamp_atot(char *str, char **endptr)
+{
+ Timestamp result;
+#ifdef HAVE_INT64_TIMESTAMP
+ int64 noresult = 0;
+#else
+ double noresult = 0.0;
+#endif
+ fsec_t fsec;
+ struct tm tt, *tm = &tt;
+ int tz;
+ int dtype;
+ int nf;
+ char *field[MAXDATEFIELDS];
+ int ftype[MAXDATEFIELDS];
+ char lowstr[MAXDATELEN + MAXDATEFIELDS];
+ char *realptr;
+ char **ptr = (endptr != NULL) ? endptr : &realptr;
+
+ errno = 0;
+ if (strlen(str) >= sizeof(lowstr))
+ {
+ errno = PGTYPES_BAD_TIMESTAMP;
+ return (noresult);
+ }
+
+ if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0)
+ || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz, 0) != 0))
+ {
+ errno = PGTYPES_BAD_TIMESTAMP;
+ return (noresult);
+ }
+
+ switch (dtype)
+ {
+ case DTK_DATE:
+ if (tm2timestamp(tm, fsec, NULL, &result) != 0)
+ {
+ errno = PGTYPES_BAD_TIMESTAMP;
+ return (noresult);;
+ }
+ break;
+
+ case DTK_EPOCH:
+ result = SetEpochTimestamp();
+ break;
+
+ case DTK_LATE:
+ TIMESTAMP_NOEND(result);
+ break;
+
+ case DTK_EARLY:
+ TIMESTAMP_NOBEGIN(result);
+ break;
+
+ case DTK_INVALID:
+ errno = PGTYPES_BAD_TIMESTAMP;
+ return (noresult);
+
+ default:
+ errno = PGTYPES_BAD_TIMESTAMP;
+ return (noresult);
+ }
+
+ /* AdjustTimestampForTypmod(&result, typmod); */
+
+ return result;
+}
+
+char *
+PGTYPEStimestamp_ttoa(Timestamp tstamp)
+{
+ struct tm tt, *tm = &tt;
+ char buf[MAXDATELEN + 1];
+ char *tzn = NULL;
+ fsec_t fsec;
+ int DateStyle = 0;
+
+ if (TIMESTAMP_NOT_FINITE(tstamp))
+ EncodeSpecialTimestamp(tstamp, buf);
+ else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0)
+ EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf, 0);
+ else
+ {
+ errno = PGTYPES_BAD_TIMESTAMP;
+ return NULL;
+ }
+ return pgtypes_strdup(buf);
+}
+
diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y
index 42c8799ea77..d618da6d8f9 100644
--- a/src/interfaces/ecpg/preproc/preproc.y
+++ b/src/interfaces/ecpg/preproc/preproc.y
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.212 2003/03/16 10:42:54 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.213 2003/03/20 15:56:50 meskes Exp $ */
/* Copyright comment */
%{
@@ -4223,6 +4223,22 @@ single_vt_type: common_type
$$.type_index = -1;
$$.type_sizeof = NULL;
}
+ else if (strcmp($1, "date") == 0)
+ {
+ $$.type_enum = ECPGt_date;
+ $$.type_str = make_str("Date");
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ $$.type_sizeof = NULL;
+ }
+ else if (strcmp($1, "timestamp") == 0)
+ {
+ $$.type_enum = ECPGt_timestamp;
+ $$.type_str = make_str("Timestamp");
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ $$.type_sizeof = NULL;
+ }
else
{
/* this is for typedef'ed types */
@@ -4236,17 +4252,6 @@ single_vt_type: common_type
struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
}
}
- | ECPGColLabelCommon '(' precision opt_scale ')'
- {
- if (strcmp($1, "numeric") != 0 && strcmp($1, "decimal") != 0)
- mmerror(PARSE_ERROR, ET_ERROR, "Only numeric/decimal have precision/scale argument");
-
- $$.type_enum = ECPGt_numeric;
- $$.type_str = EMPTY;
- $$.type_dimension = -1;
- $$.type_index = -1;
- $$.type_sizeof = NULL;
- }
;
/*
@@ -4415,6 +4420,17 @@ common_type: simple_type
$$.type_index = -1;
$$.type_sizeof = NULL;
}
+ | ECPGColLabelCommon '(' precision opt_scale ')'
+ {
+ if (strcmp($1, "numeric") != 0 && strcmp($1, "decimal") != 0)
+ mmerror(PARSE_ERROR, ET_ERROR, "Only numeric/decimal have precision/scale argument");
+
+ $$.type_enum = ECPGt_numeric;
+ $$.type_str = EMPTY;
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ $$.type_sizeof = NULL;
+ }
;
var_type: common_type
@@ -4464,6 +4480,22 @@ var_type: common_type
$$.type_index = -1;
$$.type_sizeof = NULL;
}
+ else if (strcmp($1, "date") == 0)
+ {
+ $$.type_enum = ECPGt_date;
+ $$.type_str = make_str("Date");
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ $$.type_sizeof = NULL;
+ }
+ else if (strcmp($1, "timestamp") == 0)
+ {
+ $$.type_enum = ECPGt_timestamp;
+ $$.type_str = make_str("Timestamp");
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ $$.type_sizeof = NULL;
+ }
else
{
/* this is for typedef'ed types */
@@ -4477,17 +4509,6 @@ var_type: common_type
struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
}
}
- | ECPGColLabelCommon '(' precision opt_scale ')'
- {
- if (strcmp($1, "numeric") != 0 && strcmp($1, "decimal") != 0)
- mmerror(PARSE_ERROR, ET_ERROR, "Only numeric/decimal have precision/scale argument");
-
- $$.type_enum = ECPGt_numeric;
- $$.type_str = EMPTY;
- $$.type_dimension = -1;
- $$.type_index = -1;
- $$.type_sizeof = NULL;
- }
;
enum_type: SQL_ENUM opt_symbol enum_definition
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index ec916a1e623..c55244fb1c4 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -175,6 +175,12 @@ get_type(enum ECPGttype type)
case ECPGt_descriptor:
return ("ECPGt_descriptor");
break;
+ case ECPGt_date:
+ return ("ECPGt_date");
+ break;
+ case ECPGt_timestamp:
+ return ("ECPGt_timestamp");
+ break;
default:
sprintf(errortext, "illegal variable type %d\n", type);
yyerror(errortext);
@@ -330,6 +336,22 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
sprintf(variable, "&(%s%s)", prefix ? prefix : "", name);
sprintf(offset, "sizeof(struct NumericVar)");
break;
+ case ECPGt_date:
+
+ /*
+ * we have to use a pointer and translate the variable type
+ */
+ sprintf(variable, "&(%s%s)", prefix ? prefix : "", name);
+ sprintf(offset, "sizeof(Date)");
+ break;
+ case ECPGt_timestamp:
+
+ /*
+ * we have to use a pointer and translate the variable type
+ */
+ sprintf(variable, "&(%s%s)", prefix ? prefix : "", name);
+ sprintf(offset, "sizeof(Date)");
+ break;
default:
/*
diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile
index 7c9c0d1edc5..c6424910ab5 100644
--- a/src/interfaces/ecpg/test/Makefile
+++ b/src/interfaces/ecpg/test/Makefile
@@ -1,4 +1,4 @@
-# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.34 2003/03/16 10:42:54 meskes Exp $
+# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.35 2003/03/20 15:56:50 meskes Exp $
subdir = src/interfaces/ecpg/test
top_builddir = ../../../..
@@ -8,7 +8,7 @@ override CPPFLAGS := -I$(srcdir)/../include $(CPPFLAGS) -g
ECPG = ../preproc/ecpg -I$(srcdir)/../include
-TESTS = test1 test2 test3 test4 perftest dyntest dyntest2 test_notice test_code100 test_init testdynalloc num_test
+TESTS = test1 test2 test3 test4 perftest dyntest dyntest2 test_notice test_code100 test_init testdynalloc num_test dt_test
all: $(TESTS)
diff --git a/src/interfaces/ecpg/test/dt_test.pgc b/src/interfaces/ecpg/test/dt_test.pgc
new file mode 100644
index 00000000000..34f520f891b
--- /dev/null
+++ b/src/interfaces/ecpg/test/dt_test.pgc
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <pgtypes_date.h>
+#include <pgtypes_timestamp.h>
+
+int
+main()
+{
+ exec sql begin declare section;
+ date date1;
+ timestamp ts1;
+ char *text;
+ exec sql end declare section;
+#if 0
+ Date date2;
+ short int mdy[3] = { 4, 19, 1998 };
+#endif
+ FILE *dbgs;
+
+ if ((dbgs = fopen("log", "w")) != NULL)
+ ECPGdebug(1, dbgs);
+ exec sql whenever sqlerror do sqlprint();
+ exec sql connect to mm;
+ exec sql create table date_test (d date, ts timestamp);
+
+ exec sql insert into date_test(d, ts) values ('Mon Jan 17 1966', '2000-7-12 17:34:29');
+
+ exec sql select * into :date1, :ts1 from date_test;
+
+ text = PGTYPESdate_dtoa(date1);
+ printf ("Date: %s\n", text);
+ ts1 = PGTYPEStimestamp_atot("2000-7-12 17:34:29", NULL);
+ text = PGTYPEStimestamp_ttoa(ts1);
+ printf ("timestamp: %s\n", text);
+#if 0
+ PGTYPESdate_mdyjul(mdy, &date2);
+ printf("m: %d, d: %d, y: %d\n", mdy[0], mdy[1], mdy[2]);
+ /* reset */
+ mdy[0] = mdy[1] = mdy[2] = 0;
+
+ PGTYPESdate_julmdy(date2, mdy);
+ printf("m: %d, d: %d, y: %d\n", mdy[0], mdy[1], mdy[2]);
+#endif
+ exec sql rollback;
+ exec sql disconnect;
+
+ if (dbgs != NULL)
+ fclose(dbgs);
+
+ return (0);
+}
+
diff --git a/src/interfaces/ecpg/test/num_test.pgc b/src/interfaces/ecpg/test/num_test.pgc
index f81b81b915c..f534df5da25 100644
--- a/src/interfaces/ecpg/test/num_test.pgc
+++ b/src/interfaces/ecpg/test/num_test.pgc
@@ -8,6 +8,7 @@ main()
NumericVar *value1, *value2, *res;
exec sql begin declare section;
decimal(14,7) des = {0, 0, 0, 0, 0, NULL, NULL} ;
+ numeric num;
exec sql end declare section;
double d;
FILE *dbgs;
@@ -52,6 +53,13 @@ main()
text = PGTYPESnumeric_ntoa(res);
PGTYPESnumeric_ntod(res, &d);
printf("div = %s %e\n", text, d);
+
+ exec sql rollback;
+ exec sql disconnect;
+
+ if (dbgs != NULL)
+ fclose(dbgs);
+
return (0);
}