aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/datetime.c
diff options
context:
space:
mode:
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