commit eb75659624d76144ae6c6b304c9c2715e7ce722a
Author: Alexander Korotkov <akorotkov@postgresql.org>
Date:   Wed Sep 19 12:21:31 2018 +0300

    Fix handling of format string text characters in to_timestamp()/to_date()
    
    cf984672 introduced improvement of handling of spaces and separators in
    to_timestamp()/to_date() functions.  In particular, now we're skipping spaces
    both before and after fields.  That may cause format string text character to
    consume part of field in the situations, when it didn't happen before cf984672.
    This commit cause format string text character consume input string characters
    only when since previous field (or string beginning) number of skipped input
    string characters is not greater than number of format string characters
    (that is we didn't skip any extra characters in input string).

diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 2ed8ca675bd..5001af32f60 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -3061,9 +3061,24 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
 			 * Text character, so consume one character from input string.
 			 * Notice we don't insist that the consumed character match the
 			 * format's character.
-			 * Text field ignores FX mode.
 			 */
-			s += pg_mblen(s);
+			if (!fx_mode)
+			{
+				/*
+				 * In non FX mode we might have skipped some extra characters
+				 * (more than specified in format string) before.  In this case
+				 * we don't skip input string character, because it might be
+				 * part of field.
+				 */
+				if (extra_skip > 0)
+					extra_skip--;
+				else
+					s += pg_mblen(s);
+			}
+			else
+			{
+				s += pg_mblen(s);
+			}
 			continue;
 		}
 
diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out
index 7d11f251584..b00ab0f142c 100644
--- a/src/test/regress/expected/horology.out
+++ b/src/test/regress/expected/horology.out
@@ -3128,6 +3128,21 @@ SELECT to_date('2011   12 18', 'YYYY  MM DD');
  12-18-2011
 (1 row)
 
+SELECT to_date('2011 12 18', 'YYYYxMMxDD');
+  to_date   
+------------
+ 12-18-2011
+(1 row)
+
+SELECT to_date('2011x 12x 18', 'YYYYxMMxDD');
+  to_date   
+------------
+ 12-18-2011
+(1 row)
+
+SELECT to_date('2011 x12 x18', 'YYYYxMMxDD');
+ERROR:  invalid value "x1" for "MM"
+DETAIL:  Value must be an integer.
 --
 -- Check errors for some incorrect usages of to_timestamp() and to_date()
 --
diff --git a/src/test/regress/sql/horology.sql b/src/test/regress/sql/horology.sql
index 807037be76e..ca34e8753de 100644
--- a/src/test/regress/sql/horology.sql
+++ b/src/test/regress/sql/horology.sql
@@ -495,6 +495,10 @@ SELECT to_date('2011 12 18', 'YYYY  MM DD');
 SELECT to_date('2011  12 18', 'YYYY  MM DD');
 SELECT to_date('2011   12 18', 'YYYY  MM DD');
 
+SELECT to_date('2011 12 18', 'YYYYxMMxDD');
+SELECT to_date('2011x 12x 18', 'YYYYxMMxDD');
+SELECT to_date('2011 x12 x18', 'YYYYxMMxDD');
+
 --
 -- Check errors for some incorrect usages of to_timestamp() and to_date()
 --
