aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/datetime.c
diff options
context:
space:
mode:
authorThomas G. Lockhart <lockhart@fourpalms.org>2001-09-28 08:09:14 +0000
committerThomas G. Lockhart <lockhart@fourpalms.org>2001-09-28 08:09:14 +0000
commit6f58115dddfa8ca63004c4784f57ef660422861d (patch)
tree71816e03286e53113ec4b6de337f0b345028a314 /src/backend/utils/adt/datetime.c
parent1f075a32ee28004251f508f50a4325944801da10 (diff)
downloadpostgresql-6f58115dddfa8ca63004c4784f57ef660422861d.tar.gz
postgresql-6f58115dddfa8ca63004c4784f57ef660422861d.zip
Measure the current transaction time to milliseconds.
Define a new function, GetCurrentTransactionStartTimeUsec() to get the time to this precision. Allow now() and timestamp 'now' to use this higher precision result so we now have fractional seconds in this "constant". Add timestamp without time zone type. Move previous timestamp type to timestamp with time zone. Accept another ISO variant for date/time values: yyyy-mm-ddThh:mm:ss (note the "T" separating the day from hours information). Remove 'current' from date/time types; convert to 'now' in input. Separate time and timetz regression tests. Separate timestamp and timestamptz regression test.
Diffstat (limited to 'src/backend/utils/adt/datetime.c')
-rw-r--r--src/backend/utils/adt/datetime.c210
1 files changed, 168 insertions, 42 deletions
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 7095f24de73..28ca77b64ea 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.66 2001/07/10 01:41:47 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.67 2001/09/28 08:09:10 thomas Exp $
*
*-------------------------------------------------------------------------
*/
@@ -29,13 +29,13 @@
#define ROUND_ALL 1
static int DecodeNumber(int flen, char *field,
- int fmask, int *tmask,
- struct tm * tm, double *fsec, int *is2digits);
+ int fmask, int *tmask,
+ struct tm * tm, double *fsec, int *is2digits);
static int DecodeNumberField(int len, char *str,
- int fmask, int *tmask,
- struct tm * tm, double *fsec, int *is2digits);
+ int fmask, int *tmask,
+ struct tm * tm, double *fsec, int *is2digits);
static int DecodeTime(char *str, int fmask, int *tmask,
- struct tm * tm, double *fsec);
+ struct tm * tm, double *fsec);
static int DecodeTimezone(char *str, int *tzp);
static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm);
@@ -47,10 +47,10 @@ int day_tab[2][13] = {
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
-"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
-"Thursday", "Friday", "Saturday", NULL};
+ "Thursday", "Friday", "Saturday", NULL};
/*****************************************************************************
@@ -71,7 +71,7 @@ char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
* the text field is not guaranteed to be NULL-terminated.
*/
static datetkn datetktbl[] = {
-/* text token lexval */
+/* text, token, lexval */
{EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
{"acsst", DTZ, 63}, /* Cent. Australia */
{"acst", TZ, 57}, /* Cent. Australia */
@@ -104,6 +104,7 @@ static datetkn datetktbl[] = {
{"cetdst", DTZ, 12}, /* Central European Dayl.Time */
{"cst", TZ, NEG(36)}, /* Central Standard Time */
{DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
+ {"d", UNITS, DAY}, /* "day of month" for ISO input */
{"dec", MONTH, 12},
{"december", MONTH, 12},
{"dnt", TZ, 6}, /* Dansk Normal Tid */
@@ -124,6 +125,7 @@ static datetkn datetktbl[] = {
{"fwt", DTZ, 12}, /* French Winter Time */
{"gmt", TZ, 0}, /* Greenwish Mean Time */
{"gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */
+ {"h", UNITS, HOUR}, /* "hour" */
{"hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */
{"hmt", DTZ, 18}, /* Hellas ? ? */
{"hst", TZ, NEG(60)}, /* Hawaii Std Time */
@@ -134,16 +136,19 @@ static datetkn datetktbl[] = {
/* "invalid" reserved for invalid time */
{"ist", TZ, 12}, /* Israel */
{"it", TZ, 21}, /* Iran Time */
+ {"j", UNITS, JULIAN},
{"jan", MONTH, 1},
{"january", MONTH, 1},
+ {"jd", UNITS, JULIAN},
{"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */
{"jt", TZ, 45}, /* Java Time */
{"jul", MONTH, 7},
- {"july", MONTH, 7},
+ {"julian", UNITS, JULIAN},
{"jun", MONTH, 6},
{"june", MONTH, 6},
{"kst", TZ, 54}, /* Korea Standard Time */
{"ligt", TZ, 60}, /* From Melbourne, Australia */
+ {"m", UNITS, MONTH}, /* "month" for ISO input */
{"mar", MONTH, 3},
{"march", MONTH, 3},
{"may", MONTH, 5},
@@ -153,6 +158,7 @@ static datetkn datetktbl[] = {
{"metdst", DTZ, 12}, /* Middle Europe Daylight Time */
{"mewt", TZ, 6}, /* Middle Europe Winter Time */
{"mez", TZ, 6}, /* Middle Europe Zone */
+ {"mm", UNITS, MINUTE}, /* "minute" for ISO input */
{"mon", DOW, 1},
{"monday", DOW, 1},
{"mst", TZ, NEG(42)}, /* Mountain Standard Time */
@@ -174,6 +180,7 @@ static datetkn datetktbl[] = {
{"pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */
{"pm", AMPM, PM},
{"pst", TZ, NEG(48)}, /* Pacific Standard Time */
+ {"s", UNITS, SECOND}, /* "seconds" for ISO input */
{"sadt", DTZ, 63}, /* S. Australian Dayl. Time */
{"sast", TZ, 57}, /* South Australian Std Time */
{"sat", DOW, 6},
@@ -186,6 +193,7 @@ static datetkn datetktbl[] = {
{"sun", DOW, 0},
{"sunday", DOW, 0},
{"swt", TZ, 6}, /* Swedish Winter Time */
+ {"t", DTK_ISO_TIME, 0}, /* Filler for ISO time fields */
{"thu", DOW, 4},
{"thur", DOW, 4},
{"thurs", DOW, 4},
@@ -208,6 +216,7 @@ static datetkn datetktbl[] = {
{"wet", TZ, 0}, /* Western Europe */
{"wetdst", DTZ, 6}, /* Western Europe */
{"wst", TZ, 48}, /* West Australian Std Time */
+ {"y", UNITS, YEAR}, /* "year" for ISO input */
{"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */
{YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
{"yst", TZ, NEG(54)}, /* Yukon Standard Time */
@@ -222,7 +231,7 @@ static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
/* Used for SET australian_timezones to override North American ones */
static datetkn australian_datetktbl[] = {
- {"cst", TZ, 63}, /* Australia Eastern Std Time */
+ {"cst", TZ, 63}, /* Australia Central Std Time */
{"est", TZ, 60}, /* Australia Eastern Std Time */
{"sat", TZ, 57},
};
@@ -231,7 +240,7 @@ static unsigned int australian_szdatetktbl = sizeof australian_datetktbl /
sizeof australian_datetktbl[0];
static datetkn deltatktbl[] = {
-/* text token lexval */
+/* text, token, lexval */
{"@", IGNORE, 0}, /* postgres relative time prefix */
{DAGO, AGO, 0}, /* "ago" indicates negative time offset */
{"c", UNITS, DTK_CENTURY}, /* "century" relative time units */
@@ -329,7 +338,8 @@ datetkn *deltacache[MAXDATEFIELDS] = {NULL};
* Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
* now at Aerospace Corp. (hi, Henry!)
*
- * These routines will be used by other date/time packages - tgl 97/02/25
+ * These routines will be used by other date/time packages
+ * - thomas 97/02/25
*/
int
@@ -413,6 +423,7 @@ ParseDateTime(char *timestr, char *lowstr,
if (*cp == ':')
{
ftype[nf] = DTK_TIME;
+ *lp++ = *cp++;
while (isdigit((unsigned char) *cp) ||
(*cp == ':') || (*cp == '.'))
*lp++ = *cp++;
@@ -422,10 +433,20 @@ ParseDateTime(char *timestr, char *lowstr,
else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
{
ftype[nf] = DTK_DATE;
- while (isalnum((unsigned char) *cp) || (*cp == '-') ||
- (*cp == '/') || (*cp == '.'))
- *lp++ = tolower((unsigned char) *cp++);
-
+ *lp++ = *cp++;
+ /* second field is all digits? then no embedded text month */
+ if (isdigit((unsigned char) *cp))
+ {
+ while (isdigit((unsigned char) *cp) || (*cp == '-') ||
+ (*cp == '/') || (*cp == '.'))
+ *lp++ = *cp++;
+ }
+ else
+ {
+ while (isalnum((unsigned char) *cp) || (*cp == '-') ||
+ (*cp == '/') || (*cp == '.'))
+ *lp++ = tolower((unsigned char) *cp++);
+ }
}
/*
@@ -539,7 +560,7 @@ ParseDateTime(char *timestr, char *lowstr,
* 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 GMT time zone. - tgl 97/05/27
+ * then assume GMT time zone. - thomas 1997/05/27
*/
int
DecodeDateTime(char **field, int *ftype, int nf,
@@ -548,6 +569,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
int fmask = 0,
tmask,
type;
+ int ptype = 0; /* "prefix type" for ISO y2001m02d04 format */
int i;
int flen,
val;
@@ -556,13 +578,16 @@ DecodeDateTime(char **field, int *ftype, int nf,
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;
- tm->tm_isdst = -1; /* don't know daylight savings time status
- * apriori */
+ tm->tm_isdst = -1; /* don't know daylight savings time status apriori */
if (tzp != NULL)
*tzp = 0;
@@ -571,13 +596,32 @@ DecodeDateTime(char **field, int *ftype, int nf,
switch (ftype[i])
{
case DTK_DATE:
+ /* Previous field was a label for "julian date"?
+ * then this should be a julian date with fractional day...
+ */
+ if (ptype == JULIAN)
+ {
+ char *cp;
+ double dt, date, time;
- /*
- * Already have a date? Then this might be a POSIX time
- * zone with an embedded dash (e.g. "PST-3" == "EST") -
- * thomas 2000-03-15
+ dt = strtod(field[i], &cp);
+ if (*cp != '\0')
+ return -1;
+
+ time = dt * 86400;
+ TMODULO(time, date, 86400e0);
+ j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ dt2time(time, &tm->tm_hour, &tm->tm_min, fsec);
+
+ tmask = DTK_DATE_M | DTK_TIME_M;
+ *dtype = DTK_DATE;
+ }
+
+ /* Already have a date? Then this might be a POSIX time
+ * zone with an embedded dash (e.g. "PST-3" == "EST")
+ * - thomas 2000-03-15
*/
- if ((fmask & DTK_DATE_M) == DTK_DATE_M)
+ else if ((fmask & DTK_DATE_M) == DTK_DATE_M)
{
if ((tzp == NULL)
|| (DecodePosixTimezone(field[i], tzp) != 0))
@@ -587,15 +631,16 @@ DecodeDateTime(char **field, int *ftype, int nf,
tmask = DTK_M(TZ);
}
else if (DecodeDate(field[i], fmask, &tmask, tm) != 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
+ /* Check upper limit on hours; other limits checked in
* DecodeTime()
*/
if (tm->tm_hour > 23)
@@ -618,7 +663,8 @@ DecodeDateTime(char **field, int *ftype, int nf,
* PST)
*/
if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
- && (ftype[i - 1] == DTK_TZ) && (isalpha((unsigned char) *field[i - 1])))
+ && (ftype[i - 1] == DTK_TZ)
+ && (isalpha((unsigned char) *field[i - 1])))
{
*tzp -= tz;
tmask = 0;
@@ -634,21 +680,81 @@ DecodeDateTime(char **field, int *ftype, int nf,
case DTK_NUMBER:
flen = strlen(field[i]);
+ /* 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);
+ if (*cp != '\0')
+ return -1;
+
+ switch (ptype) {
+ case YEAR:
+ tm->tm_year = val;
+ tmask = DTK_M(ptype);
+ break;
+
+ case MONTH:
+ tm->tm_mon = val;
+ tmask = DTK_M(ptype);
+ break;
+
+ case DAY:
+ tm->tm_mday = val;
+ tmask = DTK_M(ptype);
+ break;
+
+ case HOUR:
+ tm->tm_hour = val;
+ tmask = DTK_M(ptype);
+ break;
+
+ case MINUTE:
+ tm->tm_min = val;
+ tmask = DTK_M(ptype);
+ break;
+
+ case SECOND:
+ tm->tm_sec = val;
+ tmask = DTK_M(ptype);
+ break;
+
+ case JULIAN:
+ /* previous field was a label for "julian date"?
+ * then this is a julian day with no fractional part
+ * (see DTK_DATE for cases involving fractional parts)
+ */
+ j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+
+ tmask = DTK_DATE_M;
+ break;
+
+ default:
+ return -1;
+ break;
+ }
+
+ ptype = 0;
+ *dtype = DTK_DATE;
+ }
/*
* long numeric string and either no date or no time read
* yet? then interpret as a concatenated date or time...
*/
- if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M)))
+ else if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M)))
{
if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
return -1;
}
/* otherwise it is a single date/time field... */
- else
+ else if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
{
- if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
- return -1;
+ return -1;
}
break;
@@ -664,10 +770,15 @@ DecodeDateTime(char **field, int *ftype, int nf,
case RESERV:
switch (val)
{
+ case DTK_CURRENT:
case DTK_NOW:
tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
*dtype = DTK_DATE;
+#if NOT_USED
GetCurrentTime(tm);
+#else
+ GetCurrentTimeUsec(tm, fsec);
+#endif
if (tzp != NULL)
*tzp = CTimeZone;
break;
@@ -786,6 +897,18 @@ DecodeDateTime(char **field, int *ftype, int nf,
tm->tm_wday = val;
break;
+ case UNITS:
+ ptype = val;
+ tmask = 0;
+ break;
+
+ case DTK_ISO_TIME:
+ if ((i < 1) || (i >= (nf-1))
+ || (ftype[i-1] != DTK_DATE)
+ || (ftype[i+1] != DTK_TIME))
+ return -1;
+ break;
+
default:
return -1;
}
@@ -1182,6 +1305,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
str++;
}
+ /* Just get rid of any non-digit, non-alpha characters... */
if (*str != '\0')
*str++ = '\0';
nf++;
@@ -1362,8 +1486,9 @@ DecodeNumber(int flen, char *str, int fmask,
/*
* 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
+ * 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)
@@ -1613,7 +1738,7 @@ DecodeSpecial(int field, char *lowtoken, int *val)
datecache[field] = tp;
if (tp == NULL)
{
- type = IGNORE;
+ type = UNKNOWN_FIELD;
*val = 0;
}
else
@@ -1747,10 +1872,11 @@ DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, do
case DTK_NUMBER:
val = strtol(field[i], &cp, 10);
+ if (type == IGNORE)
+ type = DTK_SECOND;
+
if (*cp == '.')
{
- if (type == IGNORE)
- type = DTK_SECOND;
fval = strtod(cp, &cp);
if (*cp != '\0')
return -1;
@@ -1928,7 +2054,7 @@ DecodeUnits(int field, char *lowtoken, int *val)
deltacache[field] = tp;
if (tp == NULL)
{
- type = IGNORE;
+ type = UNKNOWN_FIELD;
*val = 0;
}
else
@@ -1985,8 +2111,8 @@ EncodeDateOnly(struct tm * tm, int style, char *str)
switch (style)
{
- /* compatible with ISO date formats */
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);
@@ -1995,8 +2121,8 @@ EncodeDateOnly(struct tm * tm, int style, char *str)
-(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
break;
- /* compatible with Oracle/Ingres date formats */
case USE_SQL_DATES:
+ /* compatible with Oracle/Ingres date formats */
if (EuroDates)
sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
else
@@ -2007,8 +2133,8 @@ EncodeDateOnly(struct tm * tm, int style, char *str)
sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC");
break;
- /* German-style date format */
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);
@@ -2016,9 +2142,9 @@ EncodeDateOnly(struct tm * tm, int style, char *str)
sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC");
break;
- /* traditional date-only style for Postgres */
case USE_POSTGRES_DATES:
default:
+ /* traditional date-only style for Postgres */
if (EuroDates)
sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
else