*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.115 2003/08/25 23:30:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.116 2003/08/27 23:29:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* ParseDateTime()
* Break string into tokens based on a date/time context.
- * Returns 0 if successful, -1 if bogus input detected.
+ * Returns 0 if successful, DTERR code if bogus input detected.
*
* timestr - the input string
* lowstr - workspace for field string storage (must be large enough for
/* Record start of current field */
if (nf >= maxfields)
- return -1;
+ return DTERR_BAD_FORMAT;
field[nf] = lp;
/* leading digit? then date or time */
}
/* otherwise something wrong... */
else
- return -1;
+ return DTERR_BAD_FORMAT;
}
/* ignore other punctuation but use as delimiter */
else if (ispunct((unsigned char) *cp))
}
/* otherwise, something is not right... */
else
- return -1;
+ return DTERR_BAD_FORMAT;
/* force in a delimiter after each field */
*lp++ = '\0';
/* DecodeDateTime()
* Interpret previously parsed fields for general date and time.
- * Return 0 if full date, 1 if only time, and -1 if problems.
+ * Return 0 if full date, 1 if only time, and negative DTERR code if problems.
+ * (Currently, all callers treat 1 as an error return too.)
+ *
* External format(s):
* "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
* "Fri Feb-7-1997 15:23:27"
* format */
int i;
int val;
+ int dterr;
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;
int val;
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
val = strtol(field[i], &cp, 10);
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;
+ dterr = DecodeTimezone(cp, tzp);
+ if (dterr)
+ return dterr;
tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
ptype = 0;
{
/* No time zone accepted? Then quit... */
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
if (isdigit((unsigned char) *field[i]) || ptype != 0)
{
{
/* Sanity check; should not fail this test */
if (ptype != DTK_TIME)
- return -1;
+ return DTERR_BAD_FORMAT;
ptype = 0;
}
* time already...
*/
if ((fmask & DTK_TIME_M) == DTK_TIME_M)
- return -1;
+ return DTERR_BAD_FORMAT;
if ((cp = strchr(field[i], '-')) == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
/* Get the time zone from the end of the string */
- if (DecodeTimezone(cp, tzp) != 0)
- return -1;
+ dterr = DecodeTimezone(cp, tzp);
+ if (dterr)
+ return dterr;
*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;
+ dterr = DecodeNumberField(strlen(field[i]), field[i],
+ fmask,
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr < 0)
+ return dterr;
+ ftype[i] = dterr;
/*
* modify tmask after returning from
}
else
{
- if (DecodePosixTimezone(field[i], tzp) != 0)
- return -1;
+ dterr = DecodePosixTimezone(field[i], tzp);
+ if (dterr)
+ return dterr;
ftype[i] = DTK_TZ;
tmask = DTK_M(TZ);
}
}
- else if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
- return -1;
+ else
+ {
+ dterr = DecodeDate(field[i], fmask, &tmask, tm);
+ if (dterr)
+ return dterr;
+ }
break;
case DTK_TIME:
- if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
- return -1;
+ dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec);
+ if (dterr)
+ return dterr;
/*
* Check upper limit on hours; other limits checked in
* DecodeTime()
*/
if (tm->tm_hour > 23)
- return -1;
+ return DTERR_FIELD_OVERFLOW;
break;
case DTK_TZ:
int tz;
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
- if (DecodeTimezone(field[i], &tz) != 0)
- return -1;
+ dterr = DecodeTimezone(field[i], &tz);
+ if (dterr)
+ return dterr;
/*
* Already have a time zone? Then maybe this is the
case DTK_SECOND:
break;
default:
- return 1;
+ return DTERR_BAD_FORMAT;
break;
}
else if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
switch (ptype)
{
frac = strtod(cp, &cp);
if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
#else
case DTK_TZ:
tmask = DTK_M(TZ);
- if (DecodeTimezone(field[i], tzp) != 0)
- return -1;
+ dterr = DecodeTimezone(field[i], tzp);
+ if (dterr)
+ return dterr;
break;
case DTK_JULIAN:
time = strtod(cp, &cp);
if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
tmask |= DTK_TIME_M;
#ifdef HAVE_INT64_TIMESTAMP
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;
+ dterr = DecodeNumberField(strlen(field[i]), field[i],
+ (fmask | DTK_DATE_M),
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr < 0)
+ return dterr;
+ ftype[i] = dterr;
if (tmask != DTK_TIME_M)
- return -1;
+ return DTERR_BAD_FORMAT;
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
break;
}
/* Embedded decimal and no date yet? */
if ((cp != NULL) && !(fmask & DTK_DATE_M))
{
- if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
- return -1;
+ dterr = DecodeDate(field[i], fmask, &tmask, tm);
+ if (dterr)
+ return dterr;
}
/* embedded decimal and several digits before? */
else if ((cp != NULL) && ((flen - strlen(cp)) > 2))
* 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;
+ dterr = DecodeNumberField(flen, field[i], fmask,
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr < 0)
+ return dterr;
+ ftype[i] = dterr;
}
else if (flen > 4)
{
- if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
- &tmask, tm, fsec, &is2digits)) < 0)
- return -1;
+ dterr = DecodeNumberField(flen, field[i], fmask,
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr < 0)
+ return dterr;
+ ftype[i] = dterr;
}
/* otherwise it is a single date/time field... */
- else if (DecodeNumber(flen, field[i], fmask,
- &tmask, tm, fsec, &is2digits) != 0)
- return -1;
+ else
+ {
+ dterr = DecodeNumber(flen, field[i], fmask,
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr)
+ return dterr;
+ }
}
break;
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("\"current\" is no longer supported")));
- return -1;
+ return DTERR_BAD_FORMAT;
break;
case DTK_NOW:
tmask |= DTK_M(DTZ);
tm->tm_isdst = 1;
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
*tzp += val * 60;
break;
tmask |= DTK_M(TZ);
tm->tm_isdst = 1;
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
*tzp = val * 60;
ftype[i] = DTK_TZ;
break;
case TZ:
tm->tm_isdst = 0;
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
*tzp = val * 60;
ftype[i] = DTK_TZ;
break;
*/
tmask = 0;
- /* No preceeding date? Then quit... */
+ /* No preceding date? Then quit... */
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
- return -1;
+ return DTERR_BAD_FORMAT;
/***
* We will need one of the following fields:
|| ((ftype[i + 1] != DTK_NUMBER)
&& (ftype[i + 1] != DTK_TIME)
&& (ftype[i + 1] != DTK_DATE)))
- return -1;
+ return DTERR_BAD_FORMAT;
ptype = val;
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
if (tmask & fmask)
- return -1;
+ return DTERR_BAD_FORMAT;
fmask |= tmask;
}
if (fmask & DTK_M(MONTH))
{
if (tm->tm_mon < 1 || tm->tm_mon > 12)
- return -1;
+ return DTERR_MD_FIELD_OVERFLOW;
}
/* minimal check for valid day */
if (fmask & DTK_M(DAY))
{
if (tm->tm_mday < 1 || tm->tm_mday > 31)
- return -1;
+ return DTERR_MD_FIELD_OVERFLOW;
}
if ((mer != HR24) && (tm->tm_hour > 12))
- return -1;
+ return DTERR_FIELD_OVERFLOW;
if ((mer == AM) && (tm->tm_hour == 12))
tm->tm_hour = 0;
else if ((mer == PM) && (tm->tm_hour != 12))
if (*dtype == DTK_DATE)
{
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
- return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
+ {
+ if ((fmask & DTK_TIME_M) == DTK_TIME_M)
+ return 1;
+ return DTERR_BAD_FORMAT;
+ }
/*
- * check for valid day of month, now that we know for sure the
- * month and year...
+ * Check for valid day of month, now that we know for sure the
+ * month and year. Note we don't use MD_FIELD_OVERFLOW here,
+ * since it seems unlikely that "Feb 29" is a YMD-order error.
*/
if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
- return -1;
+ return DTERR_FIELD_OVERFLOW;
/* timezone not specified? then find local timezone if possible */
if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
* then error
*/
if (fmask & DTK_M(DTZMOD))
- return -1;
+ return DTERR_BAD_FORMAT;
*tzp = DetermineLocalTimeZone(tm);
}
}
return 0;
-} /* DecodeDateTime() */
+}
/* DetermineLocalTimeZone()
/* DecodeTimeOnly()
* Interpret parsed string as time fields only.
+ * Returns 0 if successful, DTERR code if bogus input detected.
+ *
* Note that support for time zone is here for
* SQL92 TIME WITH TIME ZONE, but it reveals
* bogosity with SQL92 date/time standards, since
int ptype = 0; /* "prefix type" for ISO h04mm05s06 format */
int i;
int val;
+ int dterr;
int is2digits = FALSE;
int mer = HR24;
* time zones no matter what else!
*/
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
/* Under limited circumstances, we will accept a date... */
if ((i == 0) && (nf >= 2)
&& ((ftype[nf - 1] == DTK_DATE)
|| (ftype[1] == DTK_TIME)))
{
- if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
- return -1;
+ dterr = DecodeDate(field[i], fmask, &tmask, tm);
+ if (dterr)
+ return dterr;
}
/* otherwise, this is a time and/or time zone */
else
* already...
*/
if ((fmask & DTK_TIME_M) == DTK_TIME_M)
- return -1;
+ return DTERR_BAD_FORMAT;
/*
* Should not get here and fail. Sanity check
* only...
*/
if ((cp = strchr(field[i], '-')) == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
/* Get the time zone from the end of the string */
- if (DecodeTimezone(cp, tzp) != 0)
- return -1;
+ dterr = DecodeTimezone(cp, tzp);
+ if (dterr)
+ return dterr;
*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;
+ dterr = DecodeNumberField(strlen(field[i]), field[i],
+ (fmask | DTK_DATE_M),
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr < 0)
+ return dterr;
+ ftype[i] = dterr;
tmask |= DTK_M(TZ);
}
else
{
- if (DecodePosixTimezone(field[i], tzp) != 0)
- return -1;
+ dterr = DecodePosixTimezone(field[i], tzp);
+ if (dterr)
+ return dterr;
ftype[i] = DTK_TZ;
tmask = DTK_M(TZ);
break;
case DTK_TIME:
- if (DecodeTime(field[i], (fmask | DTK_DATE_M), &tmask, tm, fsec) != 0)
- return -1;
+ dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
+ &tmask, tm, fsec);
+ if (dterr)
+ return dterr;
break;
case DTK_TZ:
- if (tzp == NULL)
- return -1;
-
{
int tz;
- if (DecodeTimezone(field[i], &tz) != 0)
- return -1;
+ if (tzp == NULL)
+ return DTERR_BAD_FORMAT;
+
+ dterr = DecodeTimezone(field[i], &tz);
+ if (dterr)
+ return dterr;
/*
* Already have a time zone? Then maybe this is the
case DTK_MONTH:
case DTK_DAY:
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
default:
break;
}
case DTK_SECOND:
break;
default:
- return 1;
+ return DTERR_BAD_FORMAT;
break;
}
else if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
switch (ptype)
{
frac = strtod(cp, &cp);
if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
#else
case DTK_TZ:
tmask = DTK_M(TZ);
- if (DecodeTimezone(field[i], tzp) != 0)
- return -1;
+ dterr = DecodeTimezone(field[i], tzp);
+ if (dterr)
+ return dterr;
break;
case DTK_JULIAN:
time = strtod(cp, &cp);
if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
tmask |= DTK_TIME_M;
#ifdef HAVE_INT64_TIMESTAMP
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;
+ dterr = DecodeNumberField(strlen(field[i]), field[i],
+ (fmask | DTK_DATE_M),
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr < 0)
+ return dterr;
+ ftype[i] = dterr;
if (tmask != DTK_TIME_M)
- return -1;
+ return DTERR_BAD_FORMAT;
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
break;
}
*/
if ((i == 0) && ((nf >= 2) && (ftype[nf - 1] == DTK_DATE)))
{
- if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
- return -1;
+ dterr = DecodeDate(field[i], fmask, &tmask, tm);
+ if (dterr)
+ return dterr;
}
/* embedded decimal and several digits before? */
else if ((flen - strlen(cp)) > 2)
* Set the type field to allow decoding other
* fields later. Example: 20011223 or 040506
*/
- if ((ftype[i] = DecodeNumberField(flen, field[i],
- (fmask | DTK_DATE_M),
- &tmask, tm, fsec, &is2digits)) < 0)
- return -1;
+ dterr = DecodeNumberField(flen, field[i],
+ (fmask | DTK_DATE_M),
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr < 0)
+ return dterr;
+ ftype[i] = dterr;
}
else
- return -1;
+ return DTERR_BAD_FORMAT;
}
else if (flen > 4)
{
- if ((ftype[i] = DecodeNumberField(flen, field[i],
- (fmask | DTK_DATE_M),
- &tmask, tm, fsec, &is2digits)) < 0)
- return -1;
+ dterr = DecodeNumberField(flen, field[i],
+ (fmask | DTK_DATE_M),
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr < 0)
+ return dterr;
+ ftype[i] = dterr;
}
/* otherwise it is a single date/time field... */
- else if (DecodeNumber(flen, field[i],
- (fmask | DTK_DATE_M),
- &tmask, tm, fsec, &is2digits) != 0)
- return -1;
+ else
+ {
+ dterr = DecodeNumber(flen, field[i],
+ (fmask | DTK_DATE_M),
+ &tmask, tm,
+ fsec, &is2digits);
+ if (dterr)
+ return dterr;
+ }
}
break;
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("\"current\" is no longer supported")));
- return -1;
+ return DTERR_BAD_FORMAT;
break;
case DTK_NOW:
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
break;
tmask |= DTK_M(DTZ);
tm->tm_isdst = 1;
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
*tzp += val * 60;
break;
tmask |= DTK_M(TZ);
tm->tm_isdst = 1;
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
*tzp = val * 60;
ftype[i] = DTK_TZ;
break;
case TZ:
tm->tm_isdst = 0;
if (tzp == NULL)
- return -1;
+ return DTERR_BAD_FORMAT;
*tzp = val * 60;
ftype[i] = DTK_TZ;
break;
|| ((ftype[i + 1] != DTK_NUMBER)
&& (ftype[i + 1] != DTK_TIME)
&& (ftype[i + 1] != DTK_DATE)))
- return -1;
+ return DTERR_BAD_FORMAT;
ptype = val;
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
if (tmask & fmask)
- return -1;
+ return DTERR_BAD_FORMAT;
fmask |= tmask;
}
if ((mer != HR24) && (tm->tm_hour > 12))
- return -1;
+ return DTERR_FIELD_OVERFLOW;
if ((mer == AM) && (tm->tm_hour == 12))
tm->tm_hour = 0;
else if ((mer == PM) && (tm->tm_hour != 12))
|| (tm->tm_min < 0) || (tm->tm_min > 59)
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
|| (*fsec < INT64CONST(0)) || (*fsec >= INT64CONST(1000000)))
- return -1;
+ return DTERR_FIELD_OVERFLOW;
#else
if ((tm->tm_hour < 0) || (tm->tm_hour > 23)
|| (tm->tm_min < 0) || (tm->tm_min > 59)
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
|| (*fsec < 0) || (*fsec >= 1))
- return -1;
+ return DTERR_FIELD_OVERFLOW;
#endif
if ((fmask & DTK_TIME_M) != DTK_TIME_M)
- return -1;
+ return DTERR_BAD_FORMAT;
/* timezone not specified? then find local timezone if possible */
if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
* error
*/
if (fmask & DTK_M(DTZMOD))
- return -1;
+ return DTERR_BAD_FORMAT;
if ((fmask & DTK_DATE_M) == 0)
GetCurrentDateTime(tmp);
}
return 0;
-} /* DecodeTimeOnly() */
+}
/* DecodeDate()
* Decode date string which includes delimiters.
+ * Return 0 if okay, a DTERR code if not.
+ *
* Insist on a complete set of fields.
*/
static int
DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
{
fsec_t fsec;
-
int nf = 0;
int i,
len;
+ int dterr;
int bc = FALSE;
int is2digits = FALSE;
int type,
#if 0
/* don't allow too many fields */
if (nf > 3)
- return -1;
+ return DTERR_BAD_FORMAT;
#endif
*tmask = 0;
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
if (fmask & dmask)
- return -1;
+ return DTERR_BAD_FORMAT;
fmask |= dmask;
*tmask |= dmask;
continue;
if ((len = strlen(field[i])) <= 0)
- return -1;
+ return DTERR_BAD_FORMAT;
- if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits) != 0)
- return -1;
+ dterr = DecodeNumber(len, field[i], fmask,
+ &dmask, tm,
+ &fsec, &is2digits);
+ if (dterr)
+ return dterr;
if (fmask & dmask)
- return -1;
+ return DTERR_BAD_FORMAT;
fmask |= dmask;
*tmask |= dmask;
}
if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
- return -1;
+ return DTERR_BAD_FORMAT;
/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
if (bc)
/* check for valid month */
if (tm->tm_mon < 1 || tm->tm_mon > 12)
- return -1;
+ return DTERR_MD_FIELD_OVERFLOW;
/* check for valid day */
- if (tm->tm_mday < 1 ||
- tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
- return -1;
+ if (tm->tm_mday < 1 || tm->tm_mday > 31)
+ return DTERR_MD_FIELD_OVERFLOW;
+
+ /* We don't want to hint about DateStyle for Feb 29 */
+ if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
+ return DTERR_FIELD_OVERFLOW;
return 0;
-} /* DecodeDate() */
+}
/* DecodeTime()
* Decode time string which includes delimiters.
+ * Return 0 if okay, a DTERR code if not.
+ *
* Only check the lower limit on hours, since this same code
* can be used to represent time spans.
*/
tm->tm_hour = strtol(str, &cp, 10);
if (*cp != ':')
- return -1;
+ return DTERR_BAD_FORMAT;
str = cp + 1;
tm->tm_min = strtol(str, &cp, 10);
if (*cp == '\0')
*fsec = 0;
}
else if (*cp != ':')
- return -1;
+ return DTERR_BAD_FORMAT;
else
{
str = cp + 1;
str = cp;
frac = strtod(str, &cp);
if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
#else
#endif
}
else
- return -1;
+ return DTERR_BAD_FORMAT;
}
/* do a sanity check */
|| (tm->tm_min < 0) || (tm->tm_min > 59)
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
|| (*fsec < INT64CONST(0)) || (*fsec >= INT64CONST(1000000)))
- return -1;
+ return DTERR_FIELD_OVERFLOW;
#else
if ((tm->tm_hour < 0)
|| (tm->tm_min < 0) || (tm->tm_min > 59)
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
|| (*fsec < 0) || (*fsec >= 1))
- return -1;
+ return DTERR_FIELD_OVERFLOW;
#endif
return 0;
-} /* DecodeTime() */
+}
/* DecodeNumber()
* Interpret plain numeric field as a date value in context.
+ * Return 0 if okay, a DTERR code if not.
*/
static int
DecodeNumber(int flen, char *str, int fmask,
{
int val;
char *cp;
+ int dterr;
*tmask = 0;
val = strtol(str, &cp, 10);
if (cp == str)
- return -1;
+ return DTERR_BAD_FORMAT;
if (*cp == '.')
{
*/
if ((cp - str) > 2)
{
- if (DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
- tmask, tm, fsec, is2digits) < 0)
- return -1;
+ dterr = DecodeNumberField(flen, str,
+ (fmask | DTK_DATE_M),
+ tmask, tm,
+ fsec, is2digits);
+ if (dterr < 0)
+ return dterr;
return 0;
}
frac = strtod(cp, &cp);
if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
#else
#endif
}
else if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
/* Special case for day of year */
if ((flen == 3) &&
case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):
/* we have all the date, so it must be a time field */
- if (DecodeNumberField(flen, str, fmask,
- tmask, tm, fsec, is2digits) < 0)
- return -1;
+ dterr = DecodeNumberField(flen, str, fmask,
+ tmask, tm,
+ fsec, is2digits);
+ if (dterr < 0)
+ return dterr;
return 0;
default:
/* Anything else is bogus input */
- return -1;
+ return DTERR_BAD_FORMAT;
}
/*
/* DecodeNumberField()
* Interpret numeric string as a concatenated date or time field.
+ * Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
+ *
* 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)
+ int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits)
{
char *cp;
}
}
- return -1;
-} /* DecodeNumberField() */
+ return DTERR_BAD_FORMAT;
+}
/* DecodeTimezone()
* Interpret string as a numeric timezone.
*
- * Return 0 if okay (and set *tzp), nonzero if not okay.
+ * Return 0 if okay (and set *tzp), a DTERR code if not okay.
*
* NB: this must *not* ereport on failure; see commands/variable.c.
*
int hr,
min;
char *cp;
- int len;
/* leading character must be "+" or "-" */
if (*str != '+' && *str != '-')
- return -1;
+ return DTERR_BAD_FORMAT;
hr = strtol((str + 1), &cp, 10);
if (*cp == ':')
min = strtol((cp + 1), &cp, 10);
/* otherwise, might have run things together... */
- else if ((*cp == '\0') && ((len = strlen(str)) > 3))
+ else if ((*cp == '\0') && (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;
+ min = hr % 100;
+ hr = hr / 100;
}
else
min = 0;
- tz = (hr * 60 + min) * 60;
+ if ((hr < 0) || (hr > 13))
+ return DTERR_TZDISP_OVERFLOW;
+ if ((min < 0) || (min >= 60))
+ return DTERR_TZDISP_OVERFLOW;
+ tz = (hr * 60 + min) * 60;
if (*str == '-')
tz = -tz;
*tzp = -tz;
- return *cp != '\0';
-} /* DecodeTimezone() */
+
+ if (*cp != '\0')
+ return DTERR_BAD_FORMAT;
+
+ return 0;
+}
/* DecodePosixTimezone()
* PST
* - thomas 2000-03-15
*
- * Return 0 if okay (and set *tzp), nonzero if not okay.
+ * Return 0 if okay (and set *tzp), a DTERR code if not okay.
*
* NB: this must *not* ereport on failure; see commands/variable.c.
*/
int val,
tz;
int type;
+ int dterr;
char *cp;
char delim;
/* decode offset, if present */
if (*cp)
{
- if (DecodeTimezone(cp, &tz) != 0)
- return -1;
+ dterr = DecodeTimezone(cp, &tz);
+ if (dterr)
+ return dterr;
}
else
tz = 0;
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
return 0;
-} /* DecodePosixTimezone() */
+}
/* DecodeSpecial()
}
return type;
-} /* DecodeSpecial() */
+}
/* DecodeInterval()
* Interpret previously parsed fields for general time interval.
- * Return 0 if decoded and -1 if problems.
+ * Returns 0 if successful, DTERR code if bogus input detected.
*
* Allow "date" field DTK_DATE since this could be just
* an unsigned floating point number. - thomas 1997-11-16
DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec)
{
int is_before = FALSE;
-
char *cp;
int fmask = 0,
tmask,
type;
int i;
+ int dterr;
int val;
double fval;
switch (ftype[i])
{
case DTK_TIME:
- if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
- return -1;
+ dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec);
+ if (dterr)
+ return dterr;
type = DTK_DAY;
break;
cp = field[i] + 1;
while ((*cp != '\0') && (*cp != ':') && (*cp != '.'))
cp++;
- if ((*cp == ':')
- && (DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0))
+ if ((*cp == ':') &&
+ (DecodeTime(field[i] + 1, fmask, &tmask, tm, fsec) == 0))
{
if (*field[i] == '-')
{
{
fval = strtod(cp, &cp);
if (*cp != '\0')
- return -1;
+ return DTERR_BAD_FORMAT;
if (val < 0)
fval = -(fval);
else if (*cp == '\0')
fval = 0;
else
- return -1;
+ return DTERR_BAD_FORMAT;
tmask = 0; /* DTK_M(type); */
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
break;
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
if (tmask & fmask)
- return -1;
+ return DTERR_BAD_FORMAT;
fmask |= tmask;
}
}
/* ensure that at least one time field has been found */
- return (fmask != 0) ? 0 : -1;
-} /* DecodeInterval() */
+ if (fmask == 0)
+ return DTERR_BAD_FORMAT;
+
+ return 0;
+}
/* DecodeUnits()
return type;
} /* DecodeUnits() */
+/*
+ * Report an error detected by one of the datetime input processing routines.
+ *
+ * dterr is the error code, str is the original input string, datatype is
+ * the name of the datatype we were trying to accept.
+ *
+ * Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
+ * DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
+ * separate SQLSTATE codes, so ...
+ */
+void
+DateTimeParseError(int dterr, const char *str, const char *datatype)
+{
+ switch (dterr)
+ {
+ case DTERR_FIELD_OVERFLOW:
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
+ errmsg("date/time field value out of range: \"%s\"",
+ str)));
+ break;
+ case DTERR_MD_FIELD_OVERFLOW:
+ /* <nanny>same as above, but add hint about DateStyle</nanny> */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
+ errmsg("date/time field value out of range: \"%s\"",
+ str),
+ errhint("Perhaps you need a different DateStyle setting.")));
+ break;
+ case DTERR_INTERVAL_OVERFLOW:
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
+ errmsg("interval field value out of range: \"%s\"",
+ str)));
+ break;
+ case DTERR_TZDISP_OVERFLOW:
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
+ errmsg("time zone displacement out of range: \"%s\"",
+ str)));
+ break;
+ case DTERR_BAD_FORMAT:
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ /* translator: first %s is datatype name */
+ errmsg("invalid input syntax for %s: \"%s\"",
+ datatype, str)));
+ break;
+ }
+}
/* datebsearch()
* Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this