Commit b5e23db4 authored by Thomas G. Lockhart's avatar Thomas G. Lockhart

Rework the date/time parsing to tighten up some cases and to enable other

 cases which should have worked but did not.
Now supports julian day (J2452271), ISO time labels (T040506) and various
 combinations of spaces and run-togethers of dates, times, and time zones.
All regression tests pass, and I have more tests to add after the 7.2
 release (don't want to require changes to the ancillary horology result
 files until after then).
parent 3e87bfc1
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.82 2001/12/21 06:03:27 thomas Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.83 2001/12/29 18:31:29 thomas Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,13 +43,13 @@ void TrimTrailingZeros(char *str); ...@@ -43,13 +43,13 @@ void TrimTrailingZeros(char *str);
int day_tab[2][13] = { int day_tab[2][13] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}, {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}}; {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 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", char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", NULL}; "Thursday", "Friday", "Saturday", NULL};
/***************************************************************************** /*****************************************************************************
...@@ -65,9 +65,16 @@ char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", ...@@ -65,9 +65,16 @@ char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
#define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10)) #define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
/* /*
* to keep this table reasonably small, we divide the lexval for TZ and DTZ * datetktbl holds date/time keywords. Note that this table must be strictly
* ordered to allow an O(ln(N)) search algorithm.
*
* To keep this table reasonably small, we divide the lexval for TZ and DTZ
* entries by 10 and truncate the text field at MAXTOKLEN characters. * entries by 10 and truncate the text field at MAXTOKLEN characters.
* the text field is not guaranteed to be NULL-terminated. * the text field is not guaranteed to be NULL-terminated.
*
* Let's include all strings from my current zinc time zone database.
* Not all of them are unique, or even very understandable, so we will
* leave some commented out for now.
*/ */
static datetkn datetktbl[] = { static datetkn datetktbl[] = {
/* text, token, lexval */ /* text, token, lexval */
...@@ -76,8 +83,7 @@ static datetkn datetktbl[] = { ...@@ -76,8 +83,7 @@ static datetkn datetktbl[] = {
{"acst", DTZ, NEG(24)}, /* Atlantic/Porto Acre */ {"acst", DTZ, NEG(24)}, /* Atlantic/Porto Acre */
{"act", TZ, NEG(30)}, /* Atlantic/Porto Acre */ {"act", TZ, NEG(30)}, /* Atlantic/Porto Acre */
{DA_D, ADBC, AD}, /* "ad" for years >= 0 */ {DA_D, ADBC, AD}, /* "ad" for years >= 0 */
{"abstime", IGNORE, 0}, /* "abstime" for pre-v6.1 "Invalid {"abstime", IGNORE, 0}, /* for pre-v6.1 "Invalid Abstime" */
* Abstime" */
{"adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */ {"adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */
{"aesst", DTZ, 66}, /* E. Australia */ {"aesst", DTZ, 66}, /* E. Australia */
{"aest", TZ, 60}, /* Australia Eastern Std Time */ {"aest", TZ, 60}, /* Australia Eastern Std Time */
...@@ -161,7 +167,7 @@ cvt ...@@ -161,7 +167,7 @@ cvt
#endif #endif
{"cxt", TZ, 42}, /* Indian Christmas (Island) Time */ {"cxt", TZ, 42}, /* Indian Christmas (Island) Time */
{DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */ {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
{"d", UNITS, DAY}, /* "day of month" for ISO input */ {"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
#if 0 #if 0
davt davt
ddut ddut
...@@ -223,7 +229,7 @@ gilt ...@@ -223,7 +229,7 @@ gilt
#if 0 #if 0
gyt gyt
#endif #endif
{"h", UNITS, HOUR}, /* "hour" */ {"h", UNITS, DTK_HOUR}, /* "hour" */
#if 0 #if 0
hadt hadt
hast hast
...@@ -259,18 +265,18 @@ isst ...@@ -259,18 +265,18 @@ isst
#endif #endif
{"ist", TZ, 12}, /* Israel */ {"ist", TZ, 12}, /* Israel */
{"it", TZ, 21}, /* Iran Time */ {"it", TZ, 21}, /* Iran Time */
{"j", UNITS, JULIAN}, {"j", UNITS, DTK_JULIAN},
{"jan", MONTH, 1}, {"jan", MONTH, 1},
{"january", MONTH, 1}, {"january", MONTH, 1},
#if 0 #if 0
javt javt
jayt jayt
#endif #endif
{"jd", UNITS, JULIAN}, {"jd", UNITS, DTK_JULIAN},
{"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */ {"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */
{"jt", TZ, 45}, /* Java Time */ {"jt", TZ, 45}, /* Java Time */
{"jul", MONTH, 7}, {"jul", MONTH, 7},
{"julian", UNITS, JULIAN}, {"julian", UNITS, DTK_JULIAN},
{"july", MONTH, 7}, {"july", MONTH, 7},
{"jun", MONTH, 6}, {"jun", MONTH, 6},
{"june", MONTH, 6}, {"june", MONTH, 6},
...@@ -291,7 +297,7 @@ lhst ...@@ -291,7 +297,7 @@ lhst
lint lint
lkt lkt
#endif #endif
{"m", UNITS, MONTH}, /* "month" for ISO input */ {"m", UNITS, DTK_MONTH}, /* "month" for ISO input */
#if 0 #if 0
magst magst
magt magt
...@@ -310,7 +316,7 @@ mart ...@@ -310,7 +316,7 @@ mart
{"mewt", TZ, 6}, /* Middle Europe Winter Time */ {"mewt", TZ, 6}, /* Middle Europe Winter Time */
{"mez", TZ, 6}, /* Middle Europe Zone */ {"mez", TZ, 6}, /* Middle Europe Zone */
{"mht", TZ, 72}, /* Kwajalein */ {"mht", TZ, 72}, /* Kwajalein */
{"mm", UNITS, MINUTE}, /* "minute" for ISO input */ {"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
#if 0 #if 0
mmt mmt
#endif #endif
...@@ -381,7 +387,7 @@ pyst ...@@ -381,7 +387,7 @@ pyst
pyt pyt
#endif #endif
{"ret", DTZ, 24}, /* Reunion Island Time */ {"ret", DTZ, 24}, /* Reunion Island Time */
{"s", UNITS, SECOND}, /* "seconds" for ISO input */ {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
{"sadt", DTZ, 63}, /* S. Australian Dayl. Time */ {"sadt", DTZ, 63}, /* S. Australian Dayl. Time */
#if 0 #if 0
samst samst
...@@ -408,7 +414,7 @@ sgt ...@@ -408,7 +414,7 @@ sgt
#if 0 #if 0
syot syot
#endif #endif
{"t", DTK_ISO_TIME, 0}, /* Filler for ISO time fields */ {"t", TIME, DTK_TIME}, /* Filler for ISO time fields */
#if 0 #if 0
taht taht
#endif #endif
...@@ -475,7 +481,7 @@ wgst ...@@ -475,7 +481,7 @@ wgst
wgt wgt
#endif #endif
{"wst", TZ, 48}, /* West Australian Std Time */ {"wst", TZ, 48}, /* West Australian Std Time */
{"y", UNITS, YEAR}, /* "year" for ISO input */ {"y", UNITS, DTK_YEAR}, /* "year" for ISO input */
#if 0 #if 0
yakst yakst
yakt yakt
...@@ -488,11 +494,11 @@ yekt ...@@ -488,11 +494,11 @@ yekt
#endif #endif
{YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */ {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
{"yst", TZ, NEG(54)}, /* Yukon Standard Time */ {"yst", TZ, NEG(54)}, /* Yukon Standard Time */
{"z", RESERV, DTK_ZULU}, /* 00:00:00 */ {"z", TZ, 0}, /* time zone tag per ISO-8601 */
{"zp4", TZ, NEG(24)}, /* GMT +4 hours. */ {"zp4", TZ, NEG(24)}, /* UTC +4 hours. */
{"zp5", TZ, NEG(30)}, /* GMT +5 hours. */ {"zp5", TZ, NEG(30)}, /* UTC +5 hours. */
{"zp6", TZ, NEG(36)}, /* GMT +6 hours. */ {"zp6", TZ, NEG(36)}, /* UTC +6 hours. */
{ZULU, RESERV, DTK_ZULU}, /* 00:00:00 */ {ZULU, TZ, 0}, /* UTC */
}; };
static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0]; static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
...@@ -685,6 +691,17 @@ TrimTrailingZeros(char *str) ...@@ -685,6 +691,17 @@ TrimTrailingZeros(char *str)
/* ParseDateTime() /* ParseDateTime()
* Break string into tokens based on a date/time context. * 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 int
ParseDateTime(char *timestr, char *lowstr, ParseDateTime(char *timestr, char *lowstr,
...@@ -700,11 +717,12 @@ ParseDateTime(char *timestr, char *lowstr, ...@@ -700,11 +717,12 @@ ParseDateTime(char *timestr, char *lowstr,
field[nf] = lp; field[nf] = lp;
/* leading digit? then date or time */ /* leading digit? then date or time */
if (isdigit((unsigned char) *cp) || (*cp == '.')) if (isdigit((unsigned char) *cp))
{ {
*lp++ = *cp++; *lp++ = *cp++;
while (isdigit((unsigned char) *cp)) while (isdigit((unsigned char) *cp))
*lp++ = *cp++; *lp++ = *cp++;
/* time field? */ /* time field? */
if (*cp == ':') if (*cp == ':')
{ {
...@@ -713,37 +731,54 @@ ParseDateTime(char *timestr, char *lowstr, ...@@ -713,37 +731,54 @@ ParseDateTime(char *timestr, char *lowstr,
while (isdigit((unsigned char) *cp) || while (isdigit((unsigned char) *cp) ||
(*cp == ':') || (*cp == '.')) (*cp == ':') || (*cp == '.'))
*lp++ = *cp++; *lp++ = *cp++;
} }
/* date field? allow embedded text month */ /* date field? allow embedded text month */
else if ((*cp == '-') || (*cp == '/') || (*cp == '.')) else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
{ {
ftype[nf] = DTK_DATE; /* save delimiting character to use later */
char *dp = cp;
*lp++ = *cp++; *lp++ = *cp++;
/* second field is all digits? then no embedded text month */ /* second field is all digits? then no embedded text month */
if (isdigit((unsigned char) *cp)) if (isdigit((unsigned char) *cp))
{ {
while (isdigit((unsigned char) *cp) || (*cp == '-') || ftype[nf] = ((*dp == '.')? DTK_NUMBER: DTK_DATE);
(*cp == '/') || (*cp == '.')) while (isdigit((unsigned char) *cp))
*lp++ = *cp++; *lp++ = *cp++;
/* insist that the delimiters match to get a three-field date. */
if (*cp == *dp)
{
ftype[nf] = DTK_DATE;
*lp++ = *cp++;
while (isdigit((unsigned char) *cp) || (*cp == *dp))
*lp++ = *cp++;
}
} }
else else
{ {
while (isalnum((unsigned char) *cp) || (*cp == '-') || ftype[nf] = DTK_DATE;
(*cp == '/') || (*cp == '.')) while (isalnum((unsigned char) *cp) || (*cp == *dp))
*lp++ = tolower((unsigned char) *cp++); *lp++ = tolower((unsigned char) *cp++);
} }
} }
/* otherwise, number only and will determine
/* * year, month, day, or concatenated fields later...
* otherwise, number only and will determine year, month, or
* day later
*/ */
else else
{
ftype[nf] = DTK_NUMBER; ftype[nf] = DTK_NUMBER;
} }
}
/* Leading decimal point? Then fractional seconds... */
else if (*cp == '.')
{
*lp++ = *cp++;
while (isdigit((unsigned char) *cp))
*lp++ = *cp++;
ftype[nf] = DTK_NUMBER;
}
/* /*
* text? then date string, month, day of week, special, or * text? then date string, month, day of week, special, or
* timezone * timezone
...@@ -761,21 +796,21 @@ ParseDateTime(char *timestr, char *lowstr, ...@@ -761,21 +796,21 @@ ParseDateTime(char *timestr, char *lowstr,
*/ */
if ((*cp == '-') || (*cp == '/') || (*cp == '.')) if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
{ {
char *dp = cp;
ftype[nf] = DTK_DATE; ftype[nf] = DTK_DATE;
while (isdigit((unsigned char) *cp) || *lp++ = *cp++;
(*cp == '-') || (*cp == '/') || (*cp == '.')) while (isdigit((unsigned char) *cp) || (*cp == *dp))
*lp++ = tolower((unsigned char) *cp++); *lp++ = *cp++;
} }
/* skip leading spaces */
} }
/* skip leading spaces */
else if (isspace((unsigned char) *cp)) else if (isspace((unsigned char) *cp))
{ {
cp++; cp++;
continue; continue;
/* sign? then special or numeric timezone */
} }
/* sign? then special or numeric timezone */
else if ((*cp == '+') || (*cp == '-')) else if ((*cp == '+') || (*cp == '-'))
{ {
*lp++ = *cp++; *lp++ = *cp++;
...@@ -790,33 +825,35 @@ ParseDateTime(char *timestr, char *lowstr, ...@@ -790,33 +825,35 @@ ParseDateTime(char *timestr, char *lowstr,
while (isdigit((unsigned char) *cp) || while (isdigit((unsigned char) *cp) ||
(*cp == ':') || (*cp == '.')) (*cp == ':') || (*cp == '.'))
*lp++ = *cp++; *lp++ = *cp++;
/* special? */
} }
/* special? */
else if (isalpha((unsigned char) *cp)) else if (isalpha((unsigned char) *cp))
{ {
ftype[nf] = DTK_SPECIAL; ftype[nf] = DTK_SPECIAL;
*lp++ = tolower((unsigned char) *cp++); *lp++ = tolower((unsigned char) *cp++);
while (isalpha((unsigned char) *cp)) while (isalpha((unsigned char) *cp))
*lp++ = tolower((unsigned char) *cp++); *lp++ = tolower((unsigned char) *cp++);
/* otherwise something wrong... */
} }
/* otherwise something wrong... */
else else
{
return -1; return -1;
/* ignore punctuation but use as delimiter */
} }
}
/* ignore punctuation but use as delimiter */
else if (ispunct((unsigned char) *cp)) else if (ispunct((unsigned char) *cp))
{ {
cp++; cp++;
continue; continue;
} }
/* otherwise, something is not right... */
else else
{
return -1; return -1;
}
/* force in a delimiter */ /* force in a delimiter after each field */
*lp++ = '\0'; *lp++ = '\0';
nf++; nf++;
if (nf > MAXDATEFIELDS) if (nf > MAXDATEFIELDS)
...@@ -842,11 +879,12 @@ ParseDateTime(char *timestr, char *lowstr, ...@@ -842,11 +879,12 @@ ParseDateTime(char *timestr, char *lowstr,
* Also supports input in compact time: * Also supports input in compact time:
* "970207 152327" * "970207 152327"
* "97038 152327" * "97038 152327"
* "20011225T040506.789-07"
* *
* Use the system-provided functions to get the current time zone * Use the system-provided functions to get the current time zone
* if not specified in the input string. * if not specified in the input string.
* If the date is outside the time_t system-supported time range, * If the date is outside the time_t system-supported time range,
* then assume GMT time zone. - thomas 1997/05/27 * then assume UTC time zone. - thomas 1997-05-27
*/ */
int int
DecodeDateTime(char **field, int *ftype, int nf, DecodeDateTime(char **field, int *ftype, int nf,
...@@ -855,27 +893,25 @@ DecodeDateTime(char **field, int *ftype, int nf, ...@@ -855,27 +893,25 @@ DecodeDateTime(char **field, int *ftype, int nf,
int fmask = 0, int fmask = 0,
tmask, tmask,
type; type;
int ptype = 0; /* "prefix type" for ISO y2001m02d04 int ptype = 0; /* "prefix type" for ISO y2001m02d04 format */
* format */
int i; int i;
int flen, int val;
val;
int mer = HR24; int mer = HR24;
int haveTextMonth = FALSE; int haveTextMonth = FALSE;
int is2digits = FALSE; int is2digits = FALSE;
int bc = FALSE; int bc = FALSE;
/* /***
* We'll insist on at least all of the date fields, but initialize the * We'll insist on at least all of the date fields, but initialize the
* remaining fields in case they are not set later... * remaining fields in case they are not set later...
*/ ***/
*dtype = DTK_DATE; *dtype = DTK_DATE;
tm->tm_hour = 0; tm->tm_hour = 0;
tm->tm_min = 0; tm->tm_min = 0;
tm->tm_sec = 0; tm->tm_sec = 0;
*fsec = 0; *fsec = 0;
tm->tm_isdst = -1; /* don't know daylight savings time status /* don't know daylight savings time status apriori */
* apriori */ tm->tm_isdst = -1;
if (tzp != NULL) if (tzp != NULL)
*tzp = 0; *tzp = 0;
...@@ -884,47 +920,92 @@ DecodeDateTime(char **field, int *ftype, int nf, ...@@ -884,47 +920,92 @@ DecodeDateTime(char **field, int *ftype, int nf,
switch (ftype[i]) switch (ftype[i])
{ {
case DTK_DATE: case DTK_DATE:
/***
/* * Integral julian day with attached time zone?
* Previous field was a label for "julian date"? then this * All other forms with JD will be separated into
* should be a julian date with fractional day... * distinct fields, so we handle just this case here.
*/ ***/
if (ptype == JULIAN) if (ptype == DTK_JULIAN)
{ {
char *cp; char *cp;
double dt, int val;
date,
time;
dt = strtod(field[i], &cp); if (tzp == NULL)
if (*cp != '\0')
return -1; return -1;
time = dt * 86400; val = strtol(field[i], &cp, 10);
TMODULO(time, date, 86400e0); if (*cp != '-')
j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); return -1;
dt2time(time, &tm->tm_hour, &tm->tm_min, fsec);
tmask = DTK_DATE_M | DTK_TIME_M; j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
*dtype = DTK_DATE; /* 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 * Already have a date? Then this might be a POSIX time
* zone with an embedded dash (e.g. "PST-3" == "EST") - * zone with an embedded dash (e.g. "PST-3" == "EST") or
* thomas 2000-03-15 * 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 (ptype != 0)
{
/* Sanity check; should not fail this test */
if (ptype != DTK_TIME)
return -1;
ptype = 0;
}
if (isdigit(*field[i]))
{
char *cp;
/* Starts with a digit but we already have a time field?
* Then we are in trouble with a date and time already...
*/ */
else if ((fmask & DTK_DATE_M) == DTK_DATE_M) 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)) < 0)
return -1;
/* modify tmask after returning from DecodeNumberField() */
tmask |= DTK_M(TZ);
}
else
{ {
if ((tzp == NULL) if (DecodePosixTimezone(field[i], tzp) != 0)
|| (DecodePosixTimezone(field[i], tzp) != 0))
return -1; return -1;
ftype[i] = DTK_TZ; ftype[i] = DTK_TZ;
tmask = DTK_M(TZ); tmask = DTK_M(TZ);
} }
}
else if (DecodeDate(field[i], fmask, &tmask, tm) != 0) else if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
{
return -1; return -1;
}
break; break;
case DTK_TIME: case DTK_TIME:
...@@ -940,12 +1021,12 @@ DecodeDateTime(char **field, int *ftype, int nf, ...@@ -940,12 +1021,12 @@ DecodeDateTime(char **field, int *ftype, int nf,
break; break;
case DTK_TZ: case DTK_TZ:
if (tzp == NULL)
return -1;
{ {
int tz; int tz;
if (tzp == NULL)
return -1;
if (DecodeTimezone(field[i], &tz) != 0) if (DecodeTimezone(field[i], &tz) != 0)
return -1; return -1;
...@@ -970,8 +1051,6 @@ DecodeDateTime(char **field, int *ftype, int nf, ...@@ -970,8 +1051,6 @@ DecodeDateTime(char **field, int *ftype, int nf,
break; break;
case DTK_NUMBER: case DTK_NUMBER:
flen = strlen(field[i]);
/* /*
* Was this an "ISO date" with embedded field labels? An * Was this an "ISO date" with embedded field labels? An
* example is "y2001m02d04" - thomas 2001-02-04 * example is "y2001m02d04" - thomas 2001-02-04
...@@ -982,52 +1061,104 @@ DecodeDateTime(char **field, int *ftype, int nf, ...@@ -982,52 +1061,104 @@ DecodeDateTime(char **field, int *ftype, int nf,
int val; int val;
val = strtol(field[i], &cp, 10); val = strtol(field[i], &cp, 10);
if (*cp != '\0') /* 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; return -1;
switch (ptype) switch (ptype)
{ {
case YEAR: case DTK_YEAR:
tm->tm_year = val; tm->tm_year = val;
tmask = DTK_M(ptype); tmask = DTK_M(YEAR);
break; break;
case MONTH: 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; tm->tm_mon = val;
tmask = DTK_M(ptype); tmask = DTK_M(MONTH);
}
break; break;
case DAY: case DTK_DAY:
tm->tm_mday = val; tm->tm_mday = val;
tmask = DTK_M(ptype); tmask = DTK_M(DAY);
break; break;
case HOUR: case DTK_HOUR:
tm->tm_hour = val; tm->tm_hour = val;
tmask = DTK_M(ptype); tmask = DTK_M(HOUR);
break; break;
case MINUTE: case DTK_MINUTE:
tm->tm_min = val; tm->tm_min = val;
tmask = DTK_M(ptype); tmask = DTK_M(MINUTE);
break; break;
case SECOND: case DTK_SECOND:
tm->tm_sec = val; tm->tm_sec = val;
tmask = DTK_M(ptype); tmask = DTK_M(SECOND);
if (*cp == '.')
{
*fsec = strtod(cp, &cp);
if (*cp != '\0')
return -1;
}
break; break;
case JULIAN: 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"? then this is a julian day with no * previous field was a label for "julian date"?
* fractional part (see DTK_DATE for cases ***/
* involving fractional parts) tmask = DTK_DATE_M;
*/
j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
if (*cp == '.')
{
double time;
tmask = DTK_DATE_M; time = strtod(cp, &cp);
if (*cp != '\0')
return -1;
tmask |= DTK_TIME_M;
dt2time((time*86400), &tm->tm_hour, &tm->tm_min, fsec);
tm->tm_sec = *fsec;
*fsec -= tm->tm_sec;
}
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)) < 0)
return -1;
if (tmask != DTK_TIME_M)
return -1;
break; break;
default: default:
...@@ -1038,20 +1169,44 @@ DecodeDateTime(char **field, int *ftype, int nf, ...@@ -1038,20 +1169,44 @@ DecodeDateTime(char **field, int *ftype, int nf,
ptype = 0; ptype = 0;
*dtype = DTK_DATE; *dtype = DTK_DATE;
} }
else
{
char *cp;
int flen;
/* flen = strlen(field[i]);
* long numeric string and either no date or no time read cp = strchr(field[i], '.');
* yet? then interpret as a concatenated date or time...
/* Embedded decimal and no date yet? */
if ((cp != NULL) && !(fmask & DTK_DATE_M))
{
if (DecodeDate(field[i], fmask, &tmask, tm) != 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
*/ */
else if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M))) if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
}
else if (flen > 4)
{ {
if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
&tmask, tm, fsec, &is2digits)) < 0)
return -1; return -1;
} }
/* otherwise it is a single date/time field... */ /* otherwise it is a single date/time field... */
else if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) else if (DecodeNumber(flen, field[i], fmask,
&tmask, tm, fsec, &is2digits) != 0)
{
return -1; return -1;
}
}
break; break;
case DTK_STRING: case DTK_STRING:
...@@ -1198,12 +1353,30 @@ DecodeDateTime(char **field, int *ftype, int nf, ...@@ -1198,12 +1353,30 @@ DecodeDateTime(char **field, int *ftype, int nf,
ptype = val; ptype = val;
break; break;
case DTK_ISO_TIME: case TIME:
/* This is a filler field "t"
* indicating that the next field is time.
* Try to verify that this is sensible.
*/
tmask = 0; tmask = 0;
if ((i < 1) || (i >= (nf - 1))
|| (ftype[i - 1] != DTK_DATE) /* No preceeding date? Then quit... */
|| (ftype[i + 1] != DTK_TIME)) 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; return -1;
ptype = val;
break; break;
default: default:
...@@ -1322,7 +1495,7 @@ DetermineLocalTimeZone(struct tm * tm) ...@@ -1322,7 +1495,7 @@ DetermineLocalTimeZone(struct tm * tm)
if (tmp->tm_isdst >= 0) if (tmp->tm_isdst >= 0)
tz = -(tmp->tm_gmtoff); tz = -(tmp->tm_gmtoff);
else else
tz = 0; /* assume GMT if mktime failed */ tz = 0; /* assume UTC if mktime failed */
#elif defined(HAVE_INT_TIMEZONE) #elif defined(HAVE_INT_TIMEZONE)
tz = ((tmp->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL); tz = ((tmp->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
#endif /* HAVE_INT_TIMEZONE */ #endif /* HAVE_INT_TIMEZONE */
...@@ -1334,7 +1507,7 @@ DetermineLocalTimeZone(struct tm * tm) ...@@ -1334,7 +1507,7 @@ DetermineLocalTimeZone(struct tm * tm)
} }
else else
{ {
/* Given date is out of range, so assume GMT */ /* Given date is out of range, so assume UTC */
tm->tm_isdst = 0; tm->tm_isdst = 0;
tz = 0; tz = 0;
} }
...@@ -1350,17 +1523,19 @@ DetermineLocalTimeZone(struct tm * tm) ...@@ -1350,17 +1523,19 @@ DetermineLocalTimeZone(struct tm * tm)
* bogosity with SQL92 date/time standards, since * bogosity with SQL92 date/time standards, since
* we must infer a time zone from current time. * we must infer a time zone from current time.
* - thomas 2000-03-10 * - thomas 2000-03-10
* Allow specifying date to get a better time zone,
* if time zones are allowed. - thomas 2001-12-26
*/ */
int int
DecodeTimeOnly(char **field, int *ftype, int nf, DecodeTimeOnly(char **field, int *ftype, int nf,
int *dtype, struct tm * tm, double *fsec, int *tzp) int *dtype, struct tm * tm, double *fsec, int *tzp)
{ {
int fmask, int fmask = 0,
tmask, tmask,
type; type;
int ptype = 0; /* "prefix type" for ISO h04mm05s06 format */
int i; int i;
int flen, int val;
val;
int is2digits = FALSE; int is2digits = FALSE;
int mer = HR24; int mer = HR24;
...@@ -1369,33 +1544,74 @@ DecodeTimeOnly(char **field, int *ftype, int nf, ...@@ -1369,33 +1544,74 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
tm->tm_min = 0; tm->tm_min = 0;
tm->tm_sec = 0; tm->tm_sec = 0;
*fsec = 0; *fsec = 0;
tm->tm_isdst = -1; /* don't know daylight savings time status /* don't know daylight savings time status apriori */
* apriori */ tm->tm_isdst = -1;
if (tzp != NULL) if (tzp != NULL)
*tzp = 0; *tzp = 0;
fmask = DTK_DATE_M;
for (i = 0; i < nf; i++) for (i = 0; i < nf; i++)
{ {
switch (ftype[i]) switch (ftype[i])
{ {
case DTK_DATE: case DTK_DATE:
/* Time zone not allowed?
* Then should not accept dates or time zones
* no matter what else!
*/
if (tzp == NULL)
return -1;
/* /* Under limited circumstances, we will accept a date... */
* This might be a POSIX time zone with an embedded dash if ((i == 0) && (nf >= 2)
* (e.g. "PST-3" == "EST") - thomas 2000-03-15 && ((ftype[nf-1] == DTK_DATE)
|| (ftype[1] == DTK_TIME)))
{
if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
return -1;
}
/* otherwise, this is a time and/or time zone */
else
{
if (isdigit(*field[i]))
{
char *cp;
/* Starts with a digit but we already have a time field?
* Then we are in trouble with time already...
*/ */
if ((tzp == NULL) if ((fmask & DTK_TIME_M) == DTK_TIME_M)
|| (DecodePosixTimezone(field[i], tzp) != 0)) return -1;
/* Should not get here and fail. Sanity check only... */
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 | DTK_DATE_M),
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
tmask |= DTK_M(TZ);
}
else
{
if (DecodePosixTimezone(field[i], tzp) != 0)
return -1; return -1;
ftype[i] = DTK_TZ; ftype[i] = DTK_TZ;
tmask = DTK_M(TZ); tmask = DTK_M(TZ);
}
}
break; break;
case DTK_TIME: case DTK_TIME:
if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) if (DecodeTime(field[i], (fmask | DTK_DATE_M), &tmask, tm, fsec) != 0)
return -1; return -1;
break; break;
...@@ -1429,10 +1645,183 @@ DecodeTimeOnly(char **field, int *ftype, int nf, ...@@ -1429,10 +1645,183 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
break; break;
case DTK_NUMBER: case DTK_NUMBER:
/*
* Was this an "ISO time" with embedded field labels? An
* example is "h04m05s06" - thomas 2001-02-04
*/
if (ptype != 0)
{
char *cp;
int val;
/* Only accept a date under limited circumstances */
switch (ptype)
{
case DTK_JULIAN:
case DTK_YEAR:
case DTK_MONTH:
case DTK_DAY:
if (tzp == NULL)
return -1;
default:
break;
}
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 == '.')
{
*fsec = strtod(cp, &cp);
if (*cp != '\0')
return -1;
}
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);
if (*cp == '.')
{
double time;
time = strtod(cp, &cp);
if (*cp != '\0')
return -1;
tmask |= DTK_TIME_M;
dt2time((time*86400), &tm->tm_hour, &tm->tm_min, fsec);
tm->tm_sec = *fsec;
*fsec -= tm->tm_sec;
}
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)) < 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]); flen = strlen(field[i]);
cp = strchr(field[i], '.');
if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0) /* Embedded decimal? */
if (cp != NULL)
{
/* Under limited circumstances, we will accept a date... */
if ((i == 0) && ((nf >= 2) && (ftype[nf-1] == DTK_DATE)))
{
if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
return -1;
}
/* embedded decimal and several digits before? */
else if ((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)) < 0)
return -1; return -1;
}
else
{
return -1;
}
}
else if (flen > 4)
{
if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
}
/* otherwise it is a single date/time field... */
else if (DecodeNumber(flen, field[i], fmask,
&tmask, tm, fsec, &is2digits) != 0)
{
return -1;
}
}
break; break;
case DTK_STRING: case DTK_STRING:
...@@ -1515,6 +1904,29 @@ DecodeTimeOnly(char **field, int *ftype, int nf, ...@@ -1515,6 +1904,29 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
mer = val; mer = val;
break; break;
case UNITS:
tmask = 0;
ptype = val;
break;
case TIME:
tmask = 0;
/***
* 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: default:
return -1; return -1;
} }
...@@ -1557,11 +1969,19 @@ DecodeTimeOnly(char **field, int *ftype, int nf, ...@@ -1557,11 +1969,19 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
if (fmask & DTK_M(DTZMOD)) if (fmask & DTK_M(DTZMOD))
return -1; return -1;
if ((fmask & DTK_DATE_M) == 0)
{
GetCurrentTime(tmp); GetCurrentTime(tmp);
}
else
{
tmp->tm_year = tm->tm_year;
tmp->tm_mon = tm->tm_mon;
tmp->tm_mday = tm->tm_mday;
}
tmp->tm_hour = tm->tm_hour; tmp->tm_hour = tm->tm_hour;
tmp->tm_min = tm->tm_min; tmp->tm_min = tm->tm_min;
tmp->tm_sec = tm->tm_sec; tmp->tm_sec = tm->tm_sec;
*tzp = DetermineLocalTimeZone(tmp); *tzp = DetermineLocalTimeZone(tmp);
tm->tm_isdst = tmp->tm_isdst; tm->tm_isdst = tmp->tm_isdst;
} }
...@@ -1753,7 +2173,7 @@ DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec) ...@@ -1753,7 +2173,7 @@ DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
/* DecodeNumber() /* DecodeNumber()
* Interpret numeric field as a date value in context. * Interpret plain numeric field as a date value in context.
*/ */
static int static int
DecodeNumber(int flen, char *str, int fmask, DecodeNumber(int flen, char *str, int fmask,
...@@ -1767,12 +2187,26 @@ DecodeNumber(int flen, char *str, int fmask, ...@@ -1767,12 +2187,26 @@ DecodeNumber(int flen, char *str, int fmask,
val = strtol(str, &cp, 10); val = strtol(str, &cp, 10);
if (cp == str) if (cp == str)
return -1; return -1;
if (*cp == '.') 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);
*fsec = strtod(cp, &cp); *fsec = strtod(cp, &cp);
if (*cp != '\0') if (*cp != '\0')
return -1; return -1;
} }
else if (*cp != '\0')
{
return -1;
}
/* Special case day of year? */ /* Special case day of year? */
if ((flen == 3) && (fmask & DTK_M(YEAR)) if ((flen == 3) && (fmask & DTK_M(YEAR))
...@@ -1785,13 +2219,14 @@ DecodeNumber(int flen, char *str, int fmask, ...@@ -1785,13 +2219,14 @@ DecodeNumber(int flen, char *str, int fmask,
} }
/* /***
* Enough digits to be unequivocal year? Used to test for 4 digits or * 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 * more, but we now test first for a three-digit doy so anything
* bigger than two digits had better be an explicit year. - thomas * bigger than two digits had better be an explicit year.
* 1999-01-09 Back to requiring a 4 digit year. We accept a two digit * - thomas 1999-01-09
* Back to requiring a 4 digit year. We accept a two digit
* year farther down. - thomas 2000-03-28 * year farther down. - thomas 2000-03-28
*/ ***/
else if (flen >= 4) else if (flen >= 4)
{ {
*tmask = DTK_M(YEAR); *tmask = DTK_M(YEAR);
...@@ -1857,7 +2292,9 @@ DecodeNumber(int flen, char *str, int fmask, ...@@ -1857,7 +2292,9 @@ DecodeNumber(int flen, char *str, int fmask,
/* DecodeNumberField() /* DecodeNumberField()
* Interpret numeric string as a concatenated date field. * Interpret numeric string as a concatenated date or time field.
* Use the context of previously decoded fields to help with
* the interpretation.
*/ */
static int static int
DecodeNumberField(int len, char *str, int fmask, DecodeNumberField(int len, char *str, int fmask,
...@@ -1865,6 +2302,18 @@ DecodeNumberField(int len, char *str, int fmask, ...@@ -1865,6 +2302,18 @@ DecodeNumberField(int len, char *str, int fmask,
{ {
char *cp; char *cp;
/* Have a decimal point?
* Then this is a date or something with a seconds field...
*/
if ((cp = strchr(str, '.')) != NULL)
{
*fsec = strtod(cp, NULL);
*cp = '\0';
len = strlen(str);
}
/* No decimal point and no complete date yet? */
else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
{
/* yyyymmdd? */ /* yyyymmdd? */
if (len == 8) if (len == 8)
{ {
...@@ -1875,20 +2324,11 @@ DecodeNumberField(int len, char *str, int fmask, ...@@ -1875,20 +2324,11 @@ DecodeNumberField(int len, char *str, int fmask,
tm->tm_mon = atoi(str + 4); tm->tm_mon = atoi(str + 4);
*(str + 4) = '\0'; *(str + 4) = '\0';
tm->tm_year = atoi(str + 0); tm->tm_year = atoi(str + 0);
/* yymmdd or hhmmss? */
return DTK_DATE;
} }
/* yymmdd? */
else if (len == 6) else if (len == 6)
{
if (fmask & DTK_DATE_M)
{
*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);
}
else
{ {
*tmask = DTK_DATE_M; *tmask = DTK_DATE_M;
tm->tm_mday = atoi(str + 4); tm->tm_mday = atoi(str + 4);
...@@ -1897,10 +2337,11 @@ DecodeNumberField(int len, char *str, int fmask, ...@@ -1897,10 +2337,11 @@ DecodeNumberField(int len, char *str, int fmask,
*(str + 2) = '\0'; *(str + 2) = '\0';
tm->tm_year = atoi(str + 0); tm->tm_year = atoi(str + 0);
*is2digits = TRUE; *is2digits = TRUE;
}
return DTK_DATE;
} }
else if ((len == 5) && !(fmask & DTK_DATE_M)) /* yyddd? */
else if (len == 5)
{ {
*tmask = DTK_DATE_M; *tmask = DTK_DATE_M;
tm->tm_mday = atoi(str + 2); tm->tm_mday = atoi(str + 2);
...@@ -1908,25 +2349,40 @@ DecodeNumberField(int len, char *str, int fmask, ...@@ -1908,25 +2349,40 @@ DecodeNumberField(int len, char *str, int fmask,
tm->tm_mon = 1; tm->tm_mon = 1;
tm->tm_year = atoi(str + 0); tm->tm_year = atoi(str + 0);
*is2digits = TRUE; *is2digits = TRUE;
return DTK_DATE;
} }
else if (strchr(str, '.') != NULL) }
/* not all time fields are specified? */
if ((fmask & DTK_TIME_M) != DTK_TIME_M)
{
/* hhmmss */
if (len == 6)
{ {
*tmask = DTK_TIME_M; *tmask = DTK_TIME_M;
tm->tm_sec = strtod((str + 4), &cp); tm->tm_sec = atoi(str + 4);
if (cp == (str + 4))
return -1;
if (*cp == '.')
*fsec = strtod(cp, NULL);
*(str + 4) = '\0'; *(str + 4) = '\0';
tm->tm_min = strtod((str + 2), &cp); tm->tm_min = atoi(str + 2);
*(str + 2) = '\0'; *(str + 2) = '\0';
tm->tm_hour = strtod((str + 0), &cp); tm->tm_hour = atoi(str + 0);
return DTK_TIME;
} }
else /* hhmm? */
return -1; 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 0; return DTK_TIME;
}
}
return -1;
} /* DecodeNumberField() */ } /* DecodeNumberField() */
...@@ -1949,18 +2405,23 @@ DecodeTimezone(char *str, int *tzp) ...@@ -1949,18 +2405,23 @@ DecodeTimezone(char *str, int *tzp)
if (*cp == ':') if (*cp == ':')
{ {
min = strtol((cp + 1), &cp, 10); min = strtol((cp + 1), &cp, 10);
/* otherwise, might have run things together... */
} }
/* otherwise, might have run things together... */
else if ((*cp == '\0') && ((len = strlen(str)) > 3)) else if ((*cp == '\0') && ((len = strlen(str)) > 3))
{ {
min = strtol((str + len - 2), &cp, 10); min = strtol((str + len - 2), &cp, 10);
if ((min < 0) || (min >= 60))
return -1;
*(str + len - 2) = '\0'; *(str + len - 2) = '\0';
hr = strtol((str + 1), &cp, 10); hr = strtol((str + 1), &cp, 10);
if ((hr < 0) || (hr > 12))
return -1;
} }
else else
{
min = 0; min = 0;
}
tz = (hr * 60 + min) * 60; tz = (hr * 60 + min) * 60;
if (*str == '-') if (*str == '-')
...@@ -2510,7 +2971,7 @@ EncodeTimeOnly(struct tm * tm, double fsec, int *tzp, int style, char *str) ...@@ -2510,7 +2971,7 @@ EncodeTimeOnly(struct tm * tm, double fsec, int *tzp, int style, char *str)
* Postgres - day mon hh:mm:ss yyyy tz * Postgres - day mon hh:mm:ss yyyy tz
* SQL - mm/dd/yyyy hh:mm:ss.ss tz * SQL - mm/dd/yyyy hh:mm:ss.ss tz
* ISO - yyyy-mm-dd hh:mm:ss+/-tz * ISO - yyyy-mm-dd hh:mm:ss+/-tz
* German - dd.mm/yyyy hh:mm:ss tz * German - dd.mm.yyyy hh:mm:ss tz
* Variants (affects order of month and day for Postgres and SQL styles): * Variants (affects order of month and day for Postgres and SQL styles):
* US - mm/dd/yyyy * US - mm/dd/yyyy
* European - dd/mm/yyyy * European - dd/mm/yyyy
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.60 2001/11/21 18:29:48 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.61 2001/12/29 18:31:31 thomas Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2461,6 +2461,11 @@ timestamp_part(PG_FUNCTION_ARGS) ...@@ -2461,6 +2461,11 @@ timestamp_part(PG_FUNCTION_ARGS)
result = (tm->tm_year / 1000); result = (tm->tm_year / 1000);
break; break;
case DTK_JULIAN:
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
result += (((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec) / 86400e0);
break;
case DTK_TZ: case DTK_TZ:
case DTK_TZ_MINUTE: case DTK_TZ_MINUTE:
case DTK_TZ_HOUR: case DTK_TZ_HOUR:
...@@ -2549,7 +2554,8 @@ timestamptz_part(PG_FUNCTION_ARGS) ...@@ -2549,7 +2554,8 @@ timestamptz_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result); PG_RETURN_FLOAT8(result);
} }
if ((type == UNITS) && (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0)) if ((type == UNITS)
&& (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0))
{ {
switch (val) switch (val)
{ {
...@@ -2619,6 +2625,11 @@ timestamptz_part(PG_FUNCTION_ARGS) ...@@ -2619,6 +2625,11 @@ timestamptz_part(PG_FUNCTION_ARGS)
result = (tm->tm_year / 1000); result = (tm->tm_year / 1000);
break; break;
case DTK_JULIAN:
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
result += (((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec) / 86400e0);
break;
default: default:
elog(ERROR, "TIMESTAMP WITH TIME ZONE units '%s' not supported", lowunits); elog(ERROR, "TIMESTAMP WITH TIME ZONE units '%s' not supported", lowunits);
result = 0; result = 0;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: datetime.h,v 1.26 2001/11/05 17:46:36 momjian Exp $ * $Id: datetime.h,v 1.27 2001/12/29 18:31:48 thomas Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -108,6 +108,9 @@ ...@@ -108,6 +108,9 @@
#define AGO 17 #define AGO 17
#define ABS_BEFORE 18 #define ABS_BEFORE 18
#define ABS_AFTER 19 #define ABS_AFTER 19
/* generic fields to help with parsing */
#define DATE 20
#define TIME 21
/* reserved for unrecognized string values */ /* reserved for unrecognized string values */
#define UNKNOWN_FIELD 31 #define UNKNOWN_FIELD 31
...@@ -163,9 +166,6 @@ ...@@ -163,9 +166,6 @@
#define DTK_TZ_HOUR 34 #define DTK_TZ_HOUR 34
#define DTK_TZ_MINUTE 35 #define DTK_TZ_MINUTE 35
#define DTK_ISO_DATE 36
#define DTK_ISO_TIME 37
/* /*
* Bit mask definitions for time parsing. * Bit mask definitions for time parsing.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment