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.c226
1 files changed, 129 insertions, 97 deletions
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 1b9b4fcc821..f053c9ebb2c 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.107 2003/07/27 04:53:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.108 2003/07/29 00:03:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1425,23 +1425,47 @@ DecodeDateTime(char **field, int *ftype, int nf,
fmask |= tmask;
}
- /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
- if (bc)
+ if (fmask & DTK_M(YEAR))
{
- if (tm->tm_year > 0)
- tm->tm_year = -(tm->tm_year - 1);
- else
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("inconsistent use of year %04d and \"BC\"",
- tm->tm_year)));
+ /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
+ if (bc)
+ {
+ if (tm->tm_year > 0)
+ tm->tm_year = -(tm->tm_year - 1);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("inconsistent use of year %04d and \"BC\"",
+ tm->tm_year)));
+ }
+ else if (is2digits)
+ {
+ if (tm->tm_year < 70)
+ tm->tm_year += 2000;
+ else if (tm->tm_year < 100)
+ tm->tm_year += 1900;
+ }
}
- else if (is2digits)
+
+ /* now that we have correct year, decode DOY */
+ if (fmask & DTK_M(DOY))
{
- if (tm->tm_year < 70)
- tm->tm_year += 2000;
- else if (tm->tm_year < 100)
- tm->tm_year += 1900;
+ j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
+ &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ }
+
+ /* check for valid month */
+ if (fmask & DTK_M(MONTH))
+ {
+ if (tm->tm_mon < 1 || tm->tm_mon > 12)
+ return -1;
+ }
+
+ /* minimal check for valid day */
+ if (fmask & DTK_M(DAY))
+ {
+ if (tm->tm_mday < 1 || tm->tm_mday > 31)
+ return -1;
}
if ((mer != HR24) && (tm->tm_hour > 12))
@@ -1461,13 +1485,11 @@ DecodeDateTime(char **field, int *ftype, int nf,
* check for valid day of month, now that we know for sure the
* month and year...
*/
- if ((tm->tm_mday < 1)
- || (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]))
+ if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
return -1;
/* timezone not specified? then find local timezone if possible */
- if (((fmask & DTK_DATE_M) == DTK_DATE_M)
- && (tzp != NULL) && (!(fmask & DTK_M(TZ))))
+ if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
{
/*
* daylight savings time modifier but no standard timezone?
@@ -2259,6 +2281,22 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
tm->tm_year += 1900;
}
+ /* now that we have correct year, decode DOY */
+ if (fmask & DTK_M(DOY))
+ {
+ j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
+ &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ }
+
+ /* check for valid month */
+ if (tm->tm_mon < 1 || tm->tm_mon > 12)
+ return -1;
+
+ /* check for valid day */
+ if (tm->tm_mday < 1 ||
+ tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
+ return -1;
+
return 0;
} /* DecodeDate() */
@@ -2356,8 +2394,8 @@ DecodeNumber(int flen, char *str, int fmask,
if (*cp == '.')
{
/*
- * More than two digits? Then could be a date or a run-together
- * time: 2001.360 20011225 040506.789
+ * More than two digits before decimal point? 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),
@@ -2370,85 +2408,91 @@ DecodeNumber(int flen, char *str, int fmask,
else if (*cp != '\0')
return -1;
- /* Special case day of year? */
- if ((flen == 3) && (fmask & DTK_M(YEAR))
- && ((val >= 1) && (val <= 366)))
+ /* Special case for day of year */
+ if ((flen == 3) &&
+ ((fmask & DTK_DATE_M) == DTK_M(YEAR)) &&
+ ((val >= 1) && (val <= 366)))
{
*tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
tm->tm_yday = val;
- j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
- &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ /* tm_mon and tm_mday can't actually be set yet ... */
+ return 0;
}
- /***
- * Enough digits to be unequivocal year? Used to test for 4 digits or
- * more, but we now test first for a three-digit doy so anything
- * bigger than two digits had better be an explicit year.
- * - thomas 1999-01-09
- * Back to requiring a 4 digit year. We accept a two digit
- * year farther down. - thomas 2000-03-28
- ***/
- else if (flen >= 4)
+ /* Switch based on what we have so far */
+ switch (fmask & DTK_DATE_M)
{
- *tmask = DTK_M(YEAR);
+ case 0:
+ /*
+ * Nothing so far; make a decision about what we think the
+ * input is. There used to be lots of heuristics here, but
+ * the consensus now is to be paranoid. It *must* be either
+ * YYYY-MM-DD (with a more-than-two-digit year field), or the
+ * field order defined by DateOrder.
+ */
+ if (flen >= 3 || DateOrder == DATEORDER_YMD)
+ {
+ *tmask = DTK_M(YEAR);
+ tm->tm_year = val;
+ }
+ else if (DateOrder == DATEORDER_DMY)
+ {
+ *tmask = DTK_M(DAY);
+ tm->tm_mday = val;
+ }
+ else
+ {
+ *tmask = DTK_M(MONTH);
+ tm->tm_mon = val;
+ }
+ break;
- /* already have a year? then see if we can substitute... */
- if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
- && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
- {
- tm->tm_mday = tm->tm_year;
+ case (DTK_M(YEAR)):
+ /* Must be at second field of YY-MM-DD */
+ *tmask = DTK_M(MONTH);
+ tm->tm_mon = val;
+ break;
+
+ case (DTK_M(YEAR) | DTK_M(MONTH)):
+ /* Must be at third field of YY-MM-DD */
*tmask = DTK_M(DAY);
- }
+ tm->tm_mday = val;
+ break;
- tm->tm_year = val;
- }
+ case (DTK_M(DAY)):
+ /* Must be at second field of DD-MM-YY */
+ *tmask = DTK_M(MONTH);
+ tm->tm_mon = val;
+ break;
- /* already have year? then could be month */
- else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
- && ((val >= 1) && (val <= 12)))
- {
- *tmask = DTK_M(MONTH);
- tm->tm_mon = val;
- }
- /* no year and EuroDates enabled? then could be day */
- else if ((EuroDates || (fmask & DTK_M(MONTH)))
- && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
- && ((val >= 1) && (val <= 31)))
- {
- *tmask = DTK_M(DAY);
- tm->tm_mday = val;
- }
- else if ((!(fmask & DTK_M(MONTH)))
- && ((val >= 1) && (val <= 12)))
- {
- *tmask = DTK_M(MONTH);
- tm->tm_mon = val;
- }
- else if ((!(fmask & DTK_M(DAY)))
- && ((val >= 1) && (val <= 31)))
- {
- *tmask = DTK_M(DAY);
- tm->tm_mday = val;
+ case (DTK_M(MONTH) | DTK_M(DAY)):
+ /* Must be at third field of DD-MM-YY or MM-DD-YY */
+ *tmask = DTK_M(YEAR);
+ tm->tm_year = val;
+ break;
+
+ case (DTK_M(MONTH)):
+ /* Must be at second field of MM-DD-YY */
+ *tmask = DTK_M(DAY);
+ tm->tm_mday = val;
+ break;
+
+ default:
+ /* Anything else is bogus input */
+ return -1;
}
/*
- * Check for 2 or 4 or more digits, but currently we reach here only
- * if two digits. - thomas 2000-03-28
+ * When processing a year field, mark it for adjustment if it's
+ * exactly two digits.
*/
- else if (!(fmask & DTK_M(YEAR))
- && ((flen >= 4) || (flen == 2)))
+ if (*tmask == DTK_M(YEAR))
{
- *tmask = DTK_M(YEAR);
- tm->tm_year = val;
-
- /* adjust ONLY if exactly two digits... */
*is2digits = (flen == 2);
}
- else
- return -1;
return 0;
-} /* DecodeNumber() */
+}
/* DecodeNumberField()
@@ -2514,18 +2558,6 @@ DecodeNumberField(int len, char *str, int fmask,
return DTK_DATE;
}
- /* yyddd? */
- else if (len == 5)
- {
- *tmask = DTK_DATE_M;
- tm->tm_mday = atoi(str + 2);
- *(str + 2) = '\0';
- tm->tm_mon = 1;
- tm->tm_year = atoi(str + 0);
- *is2digits = TRUE;
-
- return DTK_DATE;
- }
}
/* not all time fields are specified? */
@@ -3152,7 +3184,7 @@ EncodeDateOnly(struct tm * tm, int style, char *str)
case USE_SQL_DATES:
/* compatible with Oracle/Ingres date formats */
- if (EuroDates)
+ if (DateOrder == DATEORDER_DMY)
sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
else
sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
@@ -3174,7 +3206,7 @@ EncodeDateOnly(struct tm * tm, int style, char *str)
case USE_POSTGRES_DATES:
default:
/* traditional date-only style for Postgres */
- if (EuroDates)
+ if (DateOrder == DATEORDER_DMY)
sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
else
sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
@@ -3308,7 +3340,7 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
case USE_SQL_DATES:
/* Compatible with Oracle/Ingres date formats */
- if (EuroDates)
+ if (DateOrder == DATEORDER_DMY)
sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
else
sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
@@ -3410,7 +3442,7 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
strncpy(str, days[tm->tm_wday], 3);
strcpy((str + 3), " ");
- if (EuroDates)
+ if (DateOrder == DATEORDER_DMY)
sprintf((str + 4), "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
else
sprintf((str + 4), "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);