diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index d73cc8dcfe8bc254764a46d812613c488c3ae5cb..4b805b61dfd9875abb77f24d9782658c6d6a8a16 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -188,12 +188,17 @@ struct tzEntry;
 #define DTK_DATE_M		(DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
 #define DTK_TIME_M		(DTK_M(HOUR) | DTK_M(MINUTE) | DTK_ALL_SECS_M)
 
-#define MAXDATELEN		63		/* maximum possible length of an input date
-								 * string (not counting tr. null) */
-#define MAXDATEFIELDS	25		/* maximum possible number of fields in a date
-								 * string */
-#define TOKMAXLEN		10		/* only this many chars are stored in
-								 * datetktbl */
+/*
+ * Working buffer size for input and output of interval, timestamp, etc.
+ * Inputs that need more working space will be rejected early.  Longer outputs
+ * will overrun buffers, so this must suffice for all possible output.  As of
+ * this writing, interval_out() needs the most space at ~90 bytes.
+ */
+#define MAXDATELEN		128
+/* maximum possible number of fields in a date string */
+#define MAXDATEFIELDS	25
+/* only this many chars are stored in datetktbl */
+#define TOKMAXLEN		10
 
 /* keep this struct small; it gets used a lot */
 typedef struct
diff --git a/src/interfaces/ecpg/pgtypeslib/datetime.c b/src/interfaces/ecpg/pgtypeslib/datetime.c
index 823626fcf56b5111b713812990535949802f9796..4adcd1e361ee60cf48bebf72a68d7a3d9486b5a2 100644
--- a/src/interfaces/ecpg/pgtypeslib/datetime.c
+++ b/src/interfaces/ecpg/pgtypeslib/datetime.c
@@ -61,14 +61,14 @@ PGTYPESdate_from_asc(char *str, char **endptr)
 	int			nf;
 	char	   *field[MAXDATEFIELDS];
 	int			ftype[MAXDATEFIELDS];
-	char		lowstr[MAXDATELEN + 1];
+	char		lowstr[MAXDATELEN + MAXDATEFIELDS];
 	char	   *realptr;
 	char	  **ptr = (endptr != NULL) ? endptr : &realptr;
 
 	bool		EuroDates = FALSE;
 
 	errno = 0;
-	if (strlen(str) >= sizeof(lowstr))
+	if (strlen(str) > MAXDATELEN)
 	{
 		errno = PGTYPES_DATE_BAD_DATE;
 		return INT_MIN;
diff --git a/src/interfaces/ecpg/pgtypeslib/dt.h b/src/interfaces/ecpg/pgtypeslib/dt.h
index dfe6f9e687276fdacc6d70f68b2fc7d5c0eb4224..2780593c5e515b14cf3a978071394bcc950e2011 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt.h
+++ b/src/interfaces/ecpg/pgtypeslib/dt.h
@@ -192,12 +192,17 @@ typedef double fsec_t;
 #define DTK_DATE_M		(DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
 #define DTK_TIME_M		(DTK_M(HOUR) | DTK_M(MINUTE) | DTK_M(SECOND))
 
-#define MAXDATELEN		63		/* maximum possible length of an input date
-								 * string (not counting tr. null) */
-#define MAXDATEFIELDS	25		/* maximum possible number of fields in a date
-								 * string */
-#define TOKMAXLEN		10		/* only this many chars are stored in
-								 * datetktbl */
+/*
+ * Working buffer size for input and output of interval, timestamp, etc.
+ * Inputs that need more working space will be rejected early.  Longer outputs
+ * will overrun buffers, so this must suffice for all possible output.  As of
+ * this writing, PGTYPESinterval_to_asc() needs the most space at ~90 bytes.
+ */
+#define MAXDATELEN		128
+/* maximum possible number of fields in a date string */
+#define MAXDATEFIELDS	25
+/* only this many chars are stored in datetktbl */
+#define TOKMAXLEN		10
 
 /* keep this struct small; it gets used a lot */
 typedef struct
diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c
index 6b89e4a67eb6b9611cba62c13e6d583ac178bbe2..18178dd34b821941017a6b155dff8d04c5e2e396 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt_common.c
+++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -1171,15 +1171,22 @@ DecodeNumberField(int len, char *str, int fmask,
 	if ((cp = strchr(str, '.')) != NULL)
 	{
 #ifdef HAVE_INT64_TIMESTAMP
-		char		fstr[MAXDATELEN + 1];
+		char		fstr[7];
+		int			i;
+
+		cp++;
 
 		/*
 		 * OK, we have at most six digits to care about. Let's construct a
-		 * string and then do the conversion to an integer.
+		 * string with those digits, zero-padded on the right, and then do
+		 * the conversion to an integer.
+		 *
+		 * XXX This truncates the seventh digit, unlike rounding it as do
+		 * the backend and the !HAVE_INT64_TIMESTAMP case.
 		 */
-		strcpy(fstr, (cp + 1));
-		strcpy(fstr + strlen(fstr), "000000");
-		*(fstr + 6) = '\0';
+		for (i = 0; i < 6; i++)
+			fstr[i] = *cp != '\0' ? *cp++ : '0';
+		fstr[i] = '\0';
 		*fsec = strtol(fstr, NULL, 10);
 #else
 		*fsec = strtod(cp, NULL);
@@ -1531,15 +1538,22 @@ DecodeTime(char *str, int *tmask, struct tm * tm, fsec_t *fsec)
 		else if (*cp == '.')
 		{
 #ifdef HAVE_INT64_TIMESTAMP
-			char		fstr[MAXDATELEN + 1];
+			char		fstr[7];
+			int			i;
+
+			cp++;
 
 			/*
-			 * OK, we have at most six digits to work with. Let's construct a
-			 * string and then do the conversion to an integer.
+			 * OK, we have at most six digits to care about. Let's construct a
+			 * string with those digits, zero-padded on the right, and then do
+			 * the conversion to an integer.
+			 *
+			 * XXX This truncates the seventh digit, unlike rounding it as do
+			 * the backend and the !HAVE_INT64_TIMESTAMP case.
 			 */
-			strncpy(fstr, (cp + 1), 7);
-			strcpy(fstr + strlen(fstr), "000000");
-			*(fstr + 6) = '\0';
+			for (i = 0; i < 6; i++)
+				fstr[i] = *cp != '\0' ? *cp++ : '0';
+			fstr[i] = '\0';
 			*fsec = strtol(fstr, &cp, 10);
 #else
 			str = cp;
@@ -1665,6 +1679,9 @@ DecodePosixTimezone(char *str, int *tzp)
  *	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)
+ *
+ * The "lowstr" work buffer must have at least strlen(timestr) + MAXDATEFIELDS
+ * bytes of space.  On output, field[] entries will point into it.
  */
 int
 ParseDateTime(char *timestr, char *lowstr,
@@ -1677,7 +1694,10 @@ ParseDateTime(char *timestr, char *lowstr,
 	/* outer loop through fields */
 	while (*(*endstr) != '\0')
 	{
+		/* Record start of current field */
 		field[nf] = lp;
+		if (nf >= MAXDATEFIELDS)
+			return -1;
 
 		/* leading digit? then date or time */
 		if (isdigit((unsigned char) *(*endstr)))
@@ -1818,8 +1838,6 @@ ParseDateTime(char *timestr, char *lowstr,
 		/* force in a delimiter after each field */
 		*lp++ = '\0';
 		nf++;
-		if (nf > MAXDATEFIELDS)
-			return -1;
 	}
 
 	*numfields = nf;
diff --git a/src/interfaces/ecpg/pgtypeslib/interval.c b/src/interfaces/ecpg/pgtypeslib/interval.c
index bcc10eeafd116f47c757debbe36667add6809123..fdd8f49ac8310f843e4a896ed60a798d932d0bea 100644
--- a/src/interfaces/ecpg/pgtypeslib/interval.c
+++ b/src/interfaces/ecpg/pgtypeslib/interval.c
@@ -1092,7 +1092,7 @@ PGTYPESinterval_from_asc(char *str, char **endptr)
 	tm->tm_sec = 0;
 	fsec = 0;
 
-	if (strlen(str) >= sizeof(lowstr))
+	if (strlen(str) > MAXDATELEN)
 	{
 		errno = PGTYPES_INTVL_BAD_INTERVAL;
 		return NULL;
diff --git a/src/interfaces/ecpg/pgtypeslib/timestamp.c b/src/interfaces/ecpg/pgtypeslib/timestamp.c
index 7d3f7c89f922cf5212952d4c8af9a67c34dd0835..4f91e63257f167e9f1f9e1f801e8ab12b6d3ca96 100644
--- a/src/interfaces/ecpg/pgtypeslib/timestamp.c
+++ b/src/interfaces/ecpg/pgtypeslib/timestamp.c
@@ -297,7 +297,7 @@ PGTYPEStimestamp_from_asc(char *str, char **endptr)
 	char	   *realptr;
 	char	  **ptr = (endptr != NULL) ? endptr : &realptr;
 
-	if (strlen(str) >= sizeof(lowstr))
+	if (strlen(str) > MAXDATELEN)
 	{
 		errno = PGTYPES_TS_BAD_TIMESTAMP;
 		return (noresult);
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c
index d3ebb0e106c35f6f353f4e17feb539b4f776022b..0ba1936f1db1a9aba2ea4880542fba2440cbc5dd 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c
@@ -45,6 +45,15 @@ char *dates[] = { "19990108foobar",
 				  "1999.008",
 				  "J2451187",
 				  "January 8, 99 BC",
+				  /*
+				   * Maximize space usage in ParseDateTime() with 25
+				   * (MAXDATEFIELDS) fields and 128 (MAXDATELEN) total length.
+				   */
+				  "........................Xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+				  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+				  /* 26 fields */
+				  ".........................aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+				  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
 				  NULL };
 
 /* do not conflict with libc "times" symbol */
@@ -52,6 +61,7 @@ static char *times[] = { "0:04",
 				  "1:59 PDT",
 				  "13:24:40 -8:00",
 				  "13:24:40.495+3",
+				  "13:24:40.123456789+3",
 				  NULL };
 
 char *intervals[] = { "1 minute",
@@ -73,22 +83,22 @@ main(void)
 		 
 		 
 	
-#line 52 "dt_test2.pgc"
+#line 62 "dt_test2.pgc"
  date date1 ;
  
-#line 53 "dt_test2.pgc"
+#line 63 "dt_test2.pgc"
  timestamp ts1 , ts2 ;
  
-#line 54 "dt_test2.pgc"
+#line 64 "dt_test2.pgc"
  char * text ;
  
-#line 55 "dt_test2.pgc"
+#line 65 "dt_test2.pgc"
  interval * i1 ;
  
-#line 56 "dt_test2.pgc"
+#line 66 "dt_test2.pgc"
  date * dc ;
 /* exec sql end declare section */
-#line 57 "dt_test2.pgc"
+#line 67 "dt_test2.pgc"
 
 
 	int i, j;
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout
index 24e9d26dfeb7744e88e52cf1508b12df3e734828..9a4587b498e5a9692813fe6838abbac1e711981d 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout
@@ -8,85 +8,104 @@ TS[3,0]: 1999-01-08 00:04:00
 TS[3,1]: 1999-01-08 01:59:00
 TS[3,2]: 1999-01-08 13:24:40
 TS[3,3]: 1999-01-08 13:24:40.495
+TS[3,4]: 1999-01-08 13:24:40.123456
 Date[4]: 1999-01-08 (N - F)
 TS[4,0]: 1999-01-08 00:04:00
 TS[4,1]: 1999-01-08 01:59:00
 TS[4,2]: 1999-01-08 13:24:40
 TS[4,3]: 1999-01-08 13:24:40.495
+TS[4,4]: 1999-01-08 13:24:40.123456
 Date[5]: 1999-01-08 (N - F)
 TS[5,0]: 1999-01-08 00:04:00
 TS[5,1]: 1999-01-08 01:59:00
 TS[5,2]: 1999-01-08 13:24:40
 TS[5,3]: 1999-01-08 13:24:40.495
+TS[5,4]: 1999-01-08 13:24:40.123456
 Date[6]: 1999-01-18 (N - F)
 TS[6,0]: 1999-01-18 00:04:00
 TS[6,1]: 1999-01-18 01:59:00
 TS[6,2]: 1999-01-18 13:24:40
 TS[6,3]: 1999-01-18 13:24:40.495
+TS[6,4]: 1999-01-18 13:24:40.123456
 Date[7]: 2003-01-02 (N - F)
 TS[7,0]: 2003-01-02 00:04:00
 TS[7,1]: 2003-01-02 01:59:00
 TS[7,2]: 2003-01-02 13:24:40
 TS[7,3]: 2003-01-02 13:24:40.495
+TS[7,4]: 2003-01-02 13:24:40.123456
 Date[8]: 1999-01-08 (N - F)
 TS[8,0]: 1999-01-08 00:04:00
 TS[8,1]: 1999-01-08 01:59:00
 TS[8,2]: 1999-01-08 13:24:40
 TS[8,3]: 1999-01-08 13:24:40.495
+TS[8,4]: 1999-01-08 13:24:40.123456
 Date[9]: 1999-01-08 (N - F)
 TS[9,0]: 1999-01-08 00:04:00
 TS[9,1]: 1999-01-08 01:59:00
 TS[9,2]: 1999-01-08 13:24:40
 TS[9,3]: 1999-01-08 13:24:40.495
+TS[9,4]: 1999-01-08 13:24:40.123456
 Date[10]: 1999-01-08 (N - F)
 TS[10,0]: 1999-01-08 00:04:00
 TS[10,1]: 1999-01-08 01:59:00
 TS[10,2]: 1999-01-08 13:24:40
 TS[10,3]: 1999-01-08 13:24:40.495
+TS[10,4]: 1999-01-08 13:24:40.123456
 Date[11]: 1999-01-08 (N - F)
 TS[11,0]: 1999-01-08 00:04:00
 TS[11,1]: 1999-01-08 01:59:00
 TS[11,2]: 1999-01-08 13:24:40
 TS[11,3]: 1999-01-08 13:24:40.495
+TS[11,4]: 1999-01-08 13:24:40.123456
 Date[12]: 1999-01-08 (N - F)
 TS[12,0]: 1999-01-08 00:04:00
 TS[12,1]: 1999-01-08 01:59:00
 TS[12,2]: 1999-01-08 13:24:40
 TS[12,3]: 1999-01-08 13:24:40.495
+TS[12,4]: 1999-01-08 13:24:40.123456
 Date[13]: 2006-01-08 (N - F)
 TS[13,0]: 2006-01-08 00:04:00
 TS[13,1]: 2006-01-08 01:59:00
 TS[13,2]: 2006-01-08 13:24:40
 TS[13,3]: 2006-01-08 13:24:40.495
+TS[13,4]: 2006-01-08 13:24:40.123456
 Date[14]: 1999-01-08 (N - F)
 TS[14,0]: 1999-01-08 00:04:00
 TS[14,1]: 1999-01-08 01:59:00
 TS[14,2]: 1999-01-08 13:24:40
 TS[14,3]: 1999-01-08 13:24:40.495
+TS[14,4]: 1999-01-08 13:24:40.123456
 Date[15]: 1999-01-08 (N - F)
 TS[15,0]: 1999-01-08 00:04:00
 TS[15,1]: 1999-01-08 01:59:00
 TS[15,2]: 1999-01-08 13:24:40
 TS[15,3]: 1999-01-08 13:24:40.495
+TS[15,4]: 1999-01-08 13:24:40.123456
 Date[16]: 1999-01-08 (N - F)
 TS[16,0]: 1999-01-08 00:04:00
 TS[16,1]: 1999-01-08 01:59:00
 TS[16,2]: 1999-01-08 13:24:40
 TS[16,3]: 1999-01-08 13:24:40.495
+TS[16,4]: 1999-01-08 13:24:40.123456
 Date[17]: 1999-01-08 (N - F)
 TS[17,0]: 1999-01-08 00:04:00
 TS[17,1]: 1999-01-08 01:59:00
 TS[17,2]: 1999-01-08 13:24:40
 TS[17,3]: 1999-01-08 13:24:40.495
+TS[17,4]: 1999-01-08 13:24:40.123456
 Date[18]: 1999-01-08 (N - F)
 TS[18,0]: 1999-01-08 00:04:00
 TS[18,1]: 1999-01-08 01:59:00
 TS[18,2]: 1999-01-08 13:24:40
 TS[18,3]: 1999-01-08 13:24:40.495
+TS[18,4]: 1999-01-08 13:24:40.123456
 Date[19]: 0099-01-08 BC (N - F)
 TS[19,0]: 0099-01-08 00:04:00 BC
 TS[19,1]: 0099-01-08 01:59:00 BC
 TS[19,2]: 0099-01-08 13:24:40 BC
+TS[19,4]: 0099-01-08 13:24:40.123456 BC
+Date[20]: - (N - T)
+Date[21]: - (N - T)
 interval[0]: @ 1 min
 interval_copy[0]: @ 1 min
 interval[1]: @ 1 day 12 hours 59 mins 10 secs
diff --git a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc
index 0edf012fd11253da11ddbae50f8b08bd7d5fa81b..a127dd93a66cee499fd99195f355e2c5ad476a8a 100644
--- a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc
+++ b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc
@@ -27,6 +27,15 @@ char *dates[] = { "19990108foobar",
 				  "1999.008",
 				  "J2451187",
 				  "January 8, 99 BC",
+				  /*
+				   * Maximize space usage in ParseDateTime() with 25
+				   * (MAXDATEFIELDS) fields and 128 (MAXDATELEN) total length.
+				   */
+				  "........................Xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+				  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+				  /* 26 fields */
+				  ".........................aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+				  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
 				  NULL };
 
 /* do not conflict with libc "times" symbol */
@@ -34,6 +43,7 @@ static char *times[] = { "0:04",
 				  "1:59 PDT",
 				  "13:24:40 -8:00",
 				  "13:24:40.495+3",
+				  "13:24:40.123456789+3",
 				  NULL };
 
 char *intervals[] = { "1 minute",
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index 3bf221187bc49d75ed4c10e42e8313456a6b94d7..99fd0ca4900c8208f66fff236ec77ef1ed3d26a7 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -306,6 +306,13 @@ select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31
  @ 4541 years 4 mons 4 days 17 mins 31 secs
 (1 row)
 
+-- test long interval output
+select '100000000y 10mon -1000000000d -1000000000h -10min -10.000001s ago'::interval;
+                                         interval                                          
+-------------------------------------------------------------------------------------------
+ @ 100000000 years 10 mons -1000000000 days -1000000000 hours -10 mins -10.000001 secs ago
+(1 row)
+
 -- test justify_hours() and justify_days()
 SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds";
  6 mons 5 days 4 hours 3 mins 2 seconds 
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index f1da4c2911775212300bf394a0d8b9992264af39..7cee2864def15f0619319ce8de5e30b81f093e85 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -108,6 +108,8 @@ select avg(f1) from interval_tbl;
 -- test long interval input
 select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31 seconds'::interval;
 
+-- test long interval output
+select '100000000y 10mon -1000000000d -1000000000h -10min -10.000001s ago'::interval;
 
 -- test justify_hours() and justify_days()