diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 0bfdd0ec2d6fdf945b1328236147a3c158359a51..d8fbe0a30771862ccf4b57b89b4087df5cdc1203 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.185 2008/02/17 02:09:28 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.186 2008/02/25 23:21:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,7 +40,10 @@ static int DecodeTime(char *str, int fmask, int *tmask,
 		   struct pg_tm * tm, fsec_t *fsec);
 static int	DecodeTimezone(char *str, int *tzp);
 static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
-static int	DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm);
+static int	DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
+					   struct pg_tm * tm);
+static int	ValidateDate(int fmask, bool is2digits, bool bc,
+						 struct pg_tm * tm);
 static void TrimTrailingZeros(char *str);
 
 
@@ -805,7 +808,8 @@ DecodeDateTime(char **field, int *ftype, int nf,
 				}
 				else
 				{
-					dterr = DecodeDate(field[i], fmask, &tmask, tm);
+					dterr = DecodeDate(field[i], fmask,
+									   &tmask, &is2digits, tm);
 					if (dterr)
 						return dterr;
 				}
@@ -1000,7 +1004,8 @@ DecodeDateTime(char **field, int *ftype, int nf,
 					/* Embedded decimal and no date yet? */
 					if (cp != NULL && !(fmask & DTK_DATE_M))
 					{
-						dterr = DecodeDate(field[i], fmask, &tmask, tm);
+						dterr = DecodeDate(field[i], fmask,
+										   &tmask, &is2digits, tm);
 						if (dterr)
 							return dterr;
 					}
@@ -1234,51 +1239,14 @@ DecodeDateTime(char **field, int *ftype, int nf,
 		if (tmask & fmask)
 			return DTERR_BAD_FORMAT;
 		fmask |= tmask;
-	}
-
-	if (fmask & DTK_M(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;
-		}
-	}
-
-	/* 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 (fmask & DTK_M(MONTH))
-	{
-		if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
-			return DTERR_MD_FIELD_OVERFLOW;
-	}
+	}				/* end loop over fields */
 
-	/* minimal check for valid day */
-	if (fmask & DTK_M(DAY))
-	{
-		if (tm->tm_mday < 1 || tm->tm_mday > 31)
-			return DTERR_MD_FIELD_OVERFLOW;
-	}
+	/* do final checking/adjustment of Y/M/D fields */
+	dterr = ValidateDate(fmask, is2digits, bc, tm);
+	if (dterr)
+		return dterr;
 
+	/* handle AM/PM */
 	if (mer != HR24 && tm->tm_hour > 12)
 		return DTERR_FIELD_OVERFLOW;
 	if (mer == AM && tm->tm_hour == 12)
@@ -1296,14 +1264,6 @@ DecodeDateTime(char **field, int *ftype, int nf,
 			return DTERR_BAD_FORMAT;
 		}
 
-		/*
-		 * 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 DTERR_FIELD_OVERFLOW;
-
 		/*
 		 * If we had a full timezone spec, compute the offset (we could not do
 		 * it before, because we need the date to resolve DST status).
@@ -1486,6 +1446,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
 	int			val;
 	int			dterr;
 	bool		is2digits = FALSE;
+	bool		bc = FALSE;
 	int			mer = HR24;
 	pg_tz	   *namedTz = NULL;
 
@@ -1517,7 +1478,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
 				if (i == 0 && nf >= 2 &&
 					(ftype[nf - 1] == DTK_DATE || ftype[1] == DTK_TIME))
 				{
-					dterr = DecodeDate(field[i], fmask, &tmask, tm);
+					dterr = DecodeDate(field[i], fmask,
+									   &tmask, &is2digits, tm);
 					if (dterr)
 						return dterr;
 				}
@@ -1783,7 +1745,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
 						 */
 						if (i == 0 && nf >= 2 && ftype[nf - 1] == DTK_DATE)
 						{
-							dterr = DecodeDate(field[i], fmask, &tmask, tm);
+							dterr = DecodeDate(field[i], fmask,
+											   &tmask, &is2digits, tm);
 							if (dterr)
 								return dterr;
 						}
@@ -1912,6 +1875,10 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
 						mer = val;
 						break;
 
+					case ADBC:
+						bc = (val == BC);
+						break;
+
 					case UNITS:
 						tmask = 0;
 						ptype = val;
@@ -1960,8 +1927,14 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
 		if (tmask & fmask)
 			return DTERR_BAD_FORMAT;
 		fmask |= tmask;
-	}
+	}				/* end loop over fields */
 
+	/* do final checking/adjustment of Y/M/D fields */
+	dterr = ValidateDate(fmask, is2digits, bc, tm);
+	if (dterr)
+		return dterr;
+
+	/* handle AM/PM */
 	if (mer != HR24 && tm->tm_hour > 12)
 		return DTERR_FIELD_OVERFLOW;
 	if (mer == AM && tm->tm_hour == 12)
@@ -2047,10 +2020,15 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
  * Decode date string which includes delimiters.
  * Return 0 if okay, a DTERR code if not.
  *
- * Insist on a complete set of fields.
+ *	str: field to be parsed
+ *	fmask: bitmask for field types already seen
+ *	*tmask: receives bitmask for fields found here
+ *	*is2digits: set to TRUE if we find 2-digit year
+ *	*tm: field values are stored into appropriate members of this struct
  */
 static int
-DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
+DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
+		   struct pg_tm * tm)
 {
 	fsec_t		fsec;
 	int			nf = 0;
@@ -2058,13 +2036,13 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
 				len;
 	int			dterr;
 	bool		haveTextMonth = FALSE;
-	bool		bc = FALSE;
-	bool		is2digits = FALSE;
 	int			type,
 				val,
 				dmask = 0;
 	char	   *field[MAXDATEFIELDS];
 
+	*tmask = 0;
+
 	/* parse this string... */
 	while (*str != '\0' && nf < MAXDATEFIELDS)
 	{
@@ -2090,14 +2068,6 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
 		nf++;
 	}
 
-#if 0
-	/* don't allow too many fields */
-	if (nf > 3)
-		return DTERR_BAD_FORMAT;
-#endif
-
-	*tmask = 0;
-
 	/* look first for text fields, since that will be unambiguous month */
 	for (i = 0; i < nf; i++)
 	{
@@ -2115,10 +2085,6 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
 					haveTextMonth = TRUE;
 					break;
 
-				case ADBC:
-					bc = (val == BC);
-					break;
-
 				default:
 					return DTERR_BAD_FORMAT;
 			}
@@ -2144,7 +2110,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
 
 		dterr = DecodeNumber(len, field[i], haveTextMonth, fmask,
 							 &dmask, tm,
-							 &fsec, &is2digits);
+							 &fsec, is2digits);
 		if (dterr)
 			return dterr;
 
@@ -2158,23 +2124,38 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
 	if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
 		return DTERR_BAD_FORMAT;
 
-	/* 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)
+	/* validation of the field values must wait until ValidateDate() */
+
+	return 0;
+}
+
+/* ValidateDate()
+ * Check valid year/month/day values, handle BC and DOY cases
+ * Return 0 if okay, a DTERR code if not.
+ */
+static int
+ValidateDate(int fmask, bool is2digits, bool bc, struct pg_tm * tm)
+{
+	if (fmask & DTK_M(YEAR))
 	{
-		if (tm->tm_year < 70)
-			tm->tm_year += 2000;
-		else if (tm->tm_year < 100)
-			tm->tm_year += 1900;
+		/* 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;
+		}
 	}
 
 	/* now that we have correct year, decode DOY */
@@ -2185,16 +2166,29 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
 	}
 
 	/* check for valid month */
-	if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
-		return DTERR_MD_FIELD_OVERFLOW;
+	if (fmask & DTK_M(MONTH))
+	{
+		if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
+			return DTERR_MD_FIELD_OVERFLOW;
+	}
 
-	/* check for valid day */
-	if (tm->tm_mday < 1 || tm->tm_mday > 31)
-		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 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;
+	if ((fmask & DTK_DATE_M) == DTK_DATE_M)
+	{
+		/*
+		 * 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 DTERR_FIELD_OVERFLOW;
+	}
 
 	return 0;
 }