Hi

so 15. 8. 2026 v 14:03 odesílatel Bernd Reiß <[email protected]> napsal:

> Hi again,
>
> thanks for the updated patch.
> > TAMMON is not implemented, because glibc doesn't provide an
> > alternative form for abbreviated month names.
> > It is a question if it is better to raise an error, return a non
> > alternative name or just ignore this flag. I have not strong
> > opinion about this. Inside DCH_to_char the prefix TM is ignored when
> > it is not used. So I did the same.
> The Locale standard actually mentions abbreviated alternative month
> names as
> "ab_alt_mon" (see [1]). I tested this by setting your TAMMONTH strftime
> call to '%Ob'.
> If we set the locale to Russian and call the function for May this
> actually returns
> an abbreviated version of the month name:
>
> Breakpoint 1, cache_locale_time () at pg_locale.c:772
> 772           if (strftime_l(bufptr, MAX_L10N_DATA, "%Ob", timeinfo,
> locale) <= 0)
> (gdb) n
> 774                     bufptr += MAX_L10N_DATA;
> (gdb) print bufptr
> $4 = 0x7ffde4554630 "май"
>
> Compared to the TMMON form of May in Russian this actually makes a
> difference:
>
> postgres=# set lc_time='ru_RU.UTF8';
> SET
> postgres=# select to_char('2026-05-01'::date, 'TMMON');
>   to_char
> ---------
>   МАЯ
> (1 row)
>
> postgres=# select to_char('2026-05-01'::date, 'TAMMONTH');
>   to_char
> ---------
>   МАЙ
> (1 row)
>
> Again, TAMMONTH uses %Ob here. So I would argue for implementing the
> abbreviated
> forms too.
>

I implemented it - please check


> >
> >     Code Review
> >     ===========
> >     I am not sure if this condition can be ever true:
> >
> >     + if (IS_SUFFIX_TM(n->suffix) && IS_SUFFIX_TAM(n->suffix))
> >
> >     In case this is not possible the subsequent error message will
> >     never be
> >     thrown and the whole block is dead code.
> >
> >     However, if there is a reason for this check, it would be nice to
> >     have a comment mention it.
> >
> >
> > Yes, I badly expected that prefixes could be mixed. I checked the
> > code, and this is not possible, so I removed this check
>
> I think you missed one occurrence in the current patch:
>
> [bernd@paco patches]$ cat
> v2-0001-introduce-tam-modifier-for-date-timestamp-formatting.patch |
> grep -B4 together
> +
> + if (IS_SUFFIX_TM(n->suffix) && IS_SUFFIX_TAM(n->suffix))
> +         ereturn(escontext,,
> +                         (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
> +                          errmsg("TM and TAM prefixes cannot be used
> together")));
>

removed


>
> >
> >     One thing I am not entirely sure about is this line:
> >
> >     + if (strftime_l(bufptr, MAX_L10N_DATA, "%OB", timeinfo, locale)
> > <= 0)
> >     +     strftimefail = true;
> >
> >     As far as I checked %OB is not supported on Windows:
> >
> >
> https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/strftime-wcsftime-strftime-l-wcsftime-l?view=msvc-170
> >
> >
> >     I was, however, not able to confirm this on a Windows machine
> >     right now.
> >     I might be able to do so on the weekend.
> >
> >     However, the same problem exists with older glibc versions not
> >     supporting
> >     %OB. As far as I understand strftime_l this will lead to it
> >     returning 0 and
> >     therefore setting strftimefail to true always. Since this if
> >     statement
> >     is evaluated
> >     unconditionally I expect this to also influence the behaviour of TM.
> >
> >
> > I am afraid about this case too, and I expect maybe some fallback mode
> > there. But because
> > I have not a Window machine or some machine with older glibc I decided
> > to don't touch it
> > for this moment.
> >
> > glibc older than 2.28 doesn't support %OB. On the second hand the
> > result of %B is probably
> > equal to expected result %B.
>
> Turns out I was wrong about this one. For one, although %OB is not
> supported, Windows
> actually does fall back to %B when used:
>
> postgres=# set lc_time='cs-CZ.UTF8';
> SET
> postgres=# SELECT to_char('2026-08-01'::date, 'TMMONTH');
>   to_char
> ---------
>   SRPEN
> (1 row)
>
> postgres=# SELECT to_char('2026-08-01'::date, 'TAMMONTH');
>   to_char
> ---------
>   SRPEN
> (1 row)
>
> The documentation for wcsftime even mentions this behaviour (see [2]).
>
> Furthermore, strftime_l only returns 0 when the result string does not
> fit the buffer (or the result is in fact empty, see [3]). The return
> value of
> a function call with unrecognized format is not defined. glibc seems to
> default to returning the format string itself:
>
> PS C:\Users\bernd> docker run --rm -it debian:8 bash -c 'LC_TIME=C date
> "+%B"'
> August
> PS C:\Users\bernd> docker run --rm -it debian:8 bash -c 'LC_TIME=C date
> "+%OB"'
> %OB
>
> In my opinion this function call is very unlikely to ever return 0
> because %OB is not
> defined. So I don't see an issue with that code segment after all, as
> this patch does
> not interfere with TM and the current documentation of this patch
> mentions the
> platform dependency of this feature. However, one could argue for
> checking whether
> %OB/%Ob is literally returned and manually falling back to %B/%b to
> cover all bases
> and make behaviour more consistent.
>
> >
> >     Nitpicking & Conclusion
> >     =======================
> >     I feel like the documentation could explicitly mention the difference
> >     between TMMONTH and TAMMONTH (i.e., mentioning genitive and
> > nominative
> >     like the Locale docs you mentioned do). I also think it would be
> >     beneficial to
> >     mention that this linguistic detail is specific to certain languages.
> >
> >
> > please, if you can write this part of the doc. My English is not good
> > enough to write well about these linguistic details.
> > It is a problem primarily for slavic languages - like Czech or Russian
> > - but maybe it can be a wide problem.
>
> I attached a revised version of your patch to this message explaining the
> difference from a linguistic standpoint. I also fixed the second
> listitem not
> mentioning the suppression of trailing white space by TAM.
>
> As English is also not my first language, I would be very glad if any
> native
> speaker reading this could have a look over it. Any feedback is
> appreciated!
>

I merged your documentation patch, thank you


>
> > Thank you very much for this immediate review
> You are most welcome 🙂
>

:-)

Please check updated patch

Nice evening

Pavel


> Best
> Bernd
>
> [1]
> https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html
> [2] https://en.cppreference.com/cpp/chrono/c/wcsftime
> [3] https://man7.org/linux/man-pages/man3/strftime.3.html
>
From f471aba088bd438a731c598060f11871c7fd1264 Mon Sep 17 00:00:00 2001
From: "[email protected]" <[email protected]>
Date: Wed, 12 Aug 2026 07:03:28 +0200
Subject: [PATCH] introduce 'tam' modifier for date/timestamp formatting
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

glibc returns localized month names in the genitive case, but it also
provides alternative month names in the nominative case. The TAM prefix
allows you to retrieve this alternative name.

example:

SELECT to_char(date '2026-08-01', 'TMMONTH');
┌─────────┐
│ to_char │
╞═════════╡
│ SRPNA   │
└─────────┘
(1 row)

SELECT to_char(date '2026-08-01', 'TAMMONTH');
┌─────────┐
│ to_char │
╞═════════╡
│ SRPEN   │
└─────────┘
(1 row)
---
 doc/src/sgml/func/func-formatting.sgml        |  31 +++-
 src/backend/utils/adt/formatting.c            | 170 +++++++++++++++---
 src/backend/utils/adt/pg_locale.c             |  16 +-
 src/include/utils/pg_locale.h                 |   2 +
 .../regress/expected/collate.linux.utf8.out   |  40 +++++
 src/test/regress/sql/collate.linux.utf8.sql   |  14 ++
 6 files changed, 241 insertions(+), 32 deletions(-)

diff --git a/doc/src/sgml/func/func-formatting.sgml b/doc/src/sgml/func/func-formatting.sgml
index e4edaf4f42c..563587dc617 100644
--- a/doc/src/sgml/func/func-formatting.sgml
+++ b/doc/src/sgml/func/func-formatting.sgml
@@ -477,6 +477,12 @@
          <xref linkend="guc-lc-time"/>)</entry>
         <entry><literal>TMMonth</literal></entry>
        </row>
+       <row>
+        <entry><literal>TAM</literal> prefix</entry>
+        <entry>translation alternative mode (use localized alternative month names based on
+         <xref linkend="guc-lc-time"/>; see usage notes)</entry>
+        <entry><literal>TAMMonth</literal></entry>
+       </row>
        <row>
         <entry><literal>SP</literal> suffix</entry>
         <entry>spell mode (not implemented)</entry>
@@ -504,8 +510,29 @@
 
      <listitem>
       <para>
-       <literal>TM</literal> suppresses trailing blanks whether or
-       not <literal>FM</literal> is specified.
+       <literal>TM</literal> and <literal>TAM</literal> suppress trailing
+       blanks whether or not <literal>FM</literal> is specified.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       <literal>TM</literal> and <literal>TAM</literal> both produce
+       localized month names according to <xref linkend="guc-lc-time"/>,
+       but they can differ for certain languages that inflect month names
+       (e.g., Slavic languages).  In such languages <literal>TM</literal>
+       produces the form used together with a day number, which is often the
+       genitive case, while <literal>TAM</literal> produces the standalone
+       (nominative) form.  For example, with <literal>lc_time</literal> set to
+       a Czech locale, <literal>to_char('2026-08-01'::date, 'TMMONTH')</literal>
+       returns <literal>SRPNA</literal> (genitive), whereas
+       <literal>to_char('2026-08-01'::date, 'TAMMONTH')</literal> returns
+       <literal>SRPEN</literal> (nominative).  For languages that do not draw
+       this distinction, and for the <literal>C</literal> locale, the two
+       modifiers produce the same result.  This distinction relies on
+       alternative month names being available from the underlying operating
+       system's locale support, so <literal>TAM</literal> may not be effective
+       on all platforms.
       </para>
      </listitem>
 
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index effad4c37dd..f5376d0a0a7 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -553,6 +553,7 @@ do { \
 #define DCH_SUFFIX_th	0x04
 #define DCH_SUFFIX_SP	0x08
 #define DCH_SUFFIX_TM	0x10
+#define DCH_SUFFIX_TAM	0x20
 
 /*
  * Suffix tests
@@ -594,16 +595,25 @@ IS_SUFFIX_TM(uint8 _s)
 	return (_s & DCH_SUFFIX_TM);
 }
 
+static inline bool
+IS_SUFFIX_TAM(uint8 _s)
+{
+	return (_s & DCH_SUFFIX_TAM);
+}
+
 /*
  * Suffixes definition for DATE-TIME TO/FROM CHAR
  */
 #define TM_SUFFIX_LEN	2
+#define TAM_SUFFIX_LEN	3
 
 static const KeySuffix DCH_suff[] = {
 	{"FM", 2, DCH_SUFFIX_FM, SUFFTYPE_PREFIX},
 	{"fm", 2, DCH_SUFFIX_FM, SUFFTYPE_PREFIX},
 	{"TM", TM_SUFFIX_LEN, DCH_SUFFIX_TM, SUFFTYPE_PREFIX},
+	{"TAM", TAM_SUFFIX_LEN, DCH_SUFFIX_TAM, SUFFTYPE_PREFIX},
 	{"tm", 2, DCH_SUFFIX_TM, SUFFTYPE_PREFIX},
+	{"tam", 3, DCH_SUFFIX_TAM, SUFFTYPE_PREFIX},
 	{"TH", 2, DCH_SUFFIX_TH, SUFFTYPE_POSTFIX},
 	{"th", 2, DCH_SUFFIX_th, SUFFTYPE_POSTFIX},
 	{"SP", 2, DCH_SUFFIX_SP, SUFFTYPE_POSTFIX},
@@ -2573,6 +2583,50 @@ from_char_seq_search(int *dest, const char **src, const char *const *array,
 	return true;
 }
 
+/*
+ * Allow to choose localized names or alternative localized names by
+ * usage prefix TM or TAM. Simple code, just reduction of repeated code.
+ */
+static char **
+get_localized_abbrev_months(uint8 suffix, int *suffix_len)
+{
+	if (IS_SUFFIX_TM(suffix))
+	{
+		*suffix_len = 2;
+		return localized_abbrev_months;
+	}
+	else if (IS_SUFFIX_TAM(suffix))
+	{
+		*suffix_len = 3;
+		return localized_alt_abbrev_months;
+	}
+	else
+	{
+		*suffix_len = 0;
+		return NULL;
+	}
+}
+
+static char **
+get_localized_full_months(uint8 suffix, int *suffix_len)
+{
+	if (IS_SUFFIX_TM(suffix))
+	{
+		*suffix_len = 2;
+		return localized_full_months;
+	}
+	else if (IS_SUFFIX_TAM(suffix))
+	{
+		*suffix_len = 3;
+		return localized_alt_full_months;
+	}
+	else
+	{
+		*suffix_len = 0;
+		return NULL;
+	}
+}
+
 /*
  * Process a TmToChar struct as denoted by a list of FormatNodes.
  * The formatted data is written to the string pointed to by 'out'.
@@ -2786,11 +2840,18 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
 				INVALID_FOR_INTERVAL;
 				if (!tm->tm_mon)
 					break;
-				if (IS_SUFFIX_TM(n->suffix))
+				if (IS_SUFFIX_TM(n->suffix) || IS_SUFFIX_TAM(n->suffix))
 				{
-					char	   *str = str_toupper_z(localized_full_months[tm->tm_mon - 1], collid);
+					char	   *str;
+					char	   **localized_months;
+					int			suffix_len;
 
-					if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
+					localized_months = get_localized_full_months(n->suffix,
+																 &suffix_len);
+
+					str = str_toupper_z(localized_months[tm->tm_mon - 1], collid);
+
+					if (strlen(str) <= (n->key->len + suffix_len) * DCH_MAX_ITEM_SIZ)
 						strcpy(s, str);
 					else
 						ereport(ERROR,
@@ -2806,11 +2867,18 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
 				INVALID_FOR_INTERVAL;
 				if (!tm->tm_mon)
 					break;
-				if (IS_SUFFIX_TM(n->suffix))
+				if (IS_SUFFIX_TM(n->suffix) || IS_SUFFIX_TAM(n->suffix))
 				{
-					char	   *str = str_initcap_z(localized_full_months[tm->tm_mon - 1], collid);
+					char	   *str;
+					char	   **localized_months;
+					int			suffix_len;
 
-					if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
+					localized_months = get_localized_full_months(n->suffix,
+																 &suffix_len);
+
+					str = str_initcap_z(localized_months[tm->tm_mon - 1], collid);
+
+					if (strlen(str) <= (n->key->len + suffix_len) * DCH_MAX_ITEM_SIZ)
 						strcpy(s, str);
 					else
 						ereport(ERROR,
@@ -2826,11 +2894,18 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
 				INVALID_FOR_INTERVAL;
 				if (!tm->tm_mon)
 					break;
-				if (IS_SUFFIX_TM(n->suffix))
+				if (IS_SUFFIX_TM(n->suffix) || IS_SUFFIX_TAM(n->suffix))
 				{
-					char	   *str = str_tolower_z(localized_full_months[tm->tm_mon - 1], collid);
+					char	   *str;
+					char	   **localized_months;
+					int			suffix_len;
 
-					if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
+					localized_months = get_localized_full_months(n->suffix,
+																 &suffix_len);
+
+					str = str_tolower_z(localized_months[tm->tm_mon - 1], collid);
+
+					if (strlen(str) <= (n->key->len + suffix_len) * DCH_MAX_ITEM_SIZ)
 						strcpy(s, str);
 					else
 						ereport(ERROR,
@@ -2846,9 +2921,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
 				INVALID_FOR_INTERVAL;
 				if (!tm->tm_mon)
 					break;
-				if (IS_SUFFIX_TM(n->suffix))
+				if (IS_SUFFIX_TM(n->suffix) || IS_SUFFIX_TAM(n->suffix))
 				{
-					char	   *str = str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid);
+					char	   *str;
+					char	   **localized_months;
+					int			suffix_len;
+
+					localized_months = get_localized_abbrev_months(n->suffix,
+																 &suffix_len);
+
+					str = str_toupper_z(localized_months[tm->tm_mon - 1], collid);
 
 					if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
 						strcpy(s, str);
@@ -2865,9 +2947,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
 				INVALID_FOR_INTERVAL;
 				if (!tm->tm_mon)
 					break;
-				if (IS_SUFFIX_TM(n->suffix))
+				if (IS_SUFFIX_TM(n->suffix) || IS_SUFFIX_TAM(n->suffix))
 				{
-					char	   *str = str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid);
+					char	   *str;
+					char	   **localized_months;
+					int			suffix_len;
+
+					localized_months = get_localized_abbrev_months(n->suffix,
+																 &suffix_len);
+
+					str = str_initcap_z(localized_months[tm->tm_mon - 1], collid);
 
 					if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
 						strcpy(s, str);
@@ -2884,9 +2973,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
 				INVALID_FOR_INTERVAL;
 				if (!tm->tm_mon)
 					break;
-				if (IS_SUFFIX_TM(n->suffix))
+				if (IS_SUFFIX_TM(n->suffix) || IS_SUFFIX_TAM(n->suffix))
 				{
-					char	   *str = str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid);
+					char	   *str;
+					char	   **localized_months;
+					int			suffix_len;
+
+					localized_months = get_localized_abbrev_months(n->suffix,
+																 &suffix_len);
+
+					str = str_tolower_z(localized_months[tm->tm_mon - 1], collid);
 
 					if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
 						strcpy(s, str);
@@ -3565,24 +3661,40 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
 			case DCH_MONTH:
 			case DCH_Month:
 			case DCH_month:
-				if (!from_char_seq_search(&value, &s, months_full,
-										  IS_SUFFIX_TM(n->suffix) ? localized_full_months : NULL,
-										  collid,
-										  n, escontext))
-					return;
-				if (!from_char_set_int(&out->mm, value + 1, n, escontext))
-					return;
+				{
+					char	  **localized_months;
+					int			suffix_len;
+
+					localized_months = get_localized_full_months(n->suffix,
+																 &suffix_len);
+
+					if (!from_char_seq_search(&value, &s, months_full,
+											  localized_months,
+											  collid,
+											  n, escontext))
+						return;
+					if (!from_char_set_int(&out->mm, value + 1, n, escontext))
+						return;
+				}
 				break;
 			case DCH_MON:
 			case DCH_Mon:
 			case DCH_mon:
-				if (!from_char_seq_search(&value, &s, months,
-										  IS_SUFFIX_TM(n->suffix) ? localized_abbrev_months : NULL,
-										  collid,
-										  n, escontext))
-					return;
-				if (!from_char_set_int(&out->mm, value + 1, n, escontext))
-					return;
+				{
+					char	   **localized_months;
+					int			suffix_len;
+
+					localized_months = get_localized_abbrev_months(n->suffix,
+																 &suffix_len);
+
+					if (!from_char_seq_search(&value, &s, months,
+											  localized_months,
+											  collid,
+											  n, escontext))
+						return;
+					if (!from_char_set_int(&out->mm, value + 1, n, escontext))
+						return;
+				}
 				break;
 			case DCH_MM:
 				if (from_char_parse_int(&out->mm, &s, n, escontext) < 0)
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 9eb99487e57..773a563867e 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -101,7 +101,9 @@ int			icu_validation_level = WARNING;
 char	   *localized_abbrev_days[7 + 1];
 char	   *localized_full_days[7 + 1];
 char	   *localized_abbrev_months[12 + 1];
+char	   *localized_alt_abbrev_months[12 + 1];
 char	   *localized_full_months[12 + 1];
+char	   *localized_alt_full_months[12 + 1];
 
 static pg_locale_t default_locale = NULL;
 
@@ -701,7 +703,7 @@ cache_single_string(char **dst, const char *src, int encoding)
 void
 cache_locale_time(void)
 {
-	char		buf[(2 * 7 + 2 * 12) * MAX_L10N_DATA];
+	char		buf[(2 * 7 + 4 * 12) * MAX_L10N_DATA];
 	char	   *bufptr;
 	time_t		timenow;
 	struct tm  *timeinfo;
@@ -765,9 +767,15 @@ cache_locale_time(void)
 		if (strftime_l(bufptr, MAX_L10N_DATA, "%b", timeinfo, locale) <= 0)
 			strftimefail = true;
 		bufptr += MAX_L10N_DATA;
+		if (strftime_l(bufptr, MAX_L10N_DATA, "%Ob", timeinfo, locale) <= 0)
+			strftimefail = true;
+		bufptr += MAX_L10N_DATA;
 		if (strftime_l(bufptr, MAX_L10N_DATA, "%B", timeinfo, locale) <= 0)
 			strftimefail = true;
 		bufptr += MAX_L10N_DATA;
+		if (strftime_l(bufptr, MAX_L10N_DATA, "%OB", timeinfo, locale) <= 0)
+			strftimefail = true;
+		bufptr += MAX_L10N_DATA;
 	}
 
 #ifdef WIN32
@@ -822,11 +830,17 @@ cache_locale_time(void)
 	{
 		cache_single_string(&localized_abbrev_months[i], bufptr, encoding);
 		bufptr += MAX_L10N_DATA;
+		cache_single_string(&localized_alt_abbrev_months[i], bufptr, encoding);
+		bufptr += MAX_L10N_DATA;
 		cache_single_string(&localized_full_months[i], bufptr, encoding);
 		bufptr += MAX_L10N_DATA;
+		cache_single_string(&localized_alt_full_months[i], bufptr, encoding);
+		bufptr += MAX_L10N_DATA;
 	}
 	localized_abbrev_months[12] = NULL;
+	localized_alt_abbrev_months[12] = NULL;
 	localized_full_months[12] = NULL;
+	localized_alt_full_months[12] = NULL;
 
 	CurrentLCTimeValid = true;
 }
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index b74821fdfa9..a23876b5f43 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -42,7 +42,9 @@ extern PGDLLIMPORT int icu_validation_level;
 extern PGDLLIMPORT char *localized_abbrev_days[];
 extern PGDLLIMPORT char *localized_full_days[];
 extern PGDLLIMPORT char *localized_abbrev_months[];
+extern PGDLLIMPORT char *localized_alt_abbrev_months[];
 extern PGDLLIMPORT char *localized_full_months[];
+extern PGDLLIMPORT char *localized_alt_full_months[];
 
 extern bool check_locale(int category, const char *locale, char **canonname);
 extern char *pg_perm_setlocale(int category, const char *locale);
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index e0a39e4c300..f4b0d60ea4c 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -463,7 +463,28 @@ SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr_TR");
  01 NİS 2010
 (1 row)
 
+-- to_char
+SET lc_time TO 'cs_CZ';
+SELECT to_char(date '2010-02-01', 'DD TMMONTH');
+ to_char  
+----------
+ 01 ÚNORA
+(1 row)
+
+SELECT to_char(date '2010-02-01', 'TAMMONTH');
+ to_char 
+---------
+ ÚNOR
+(1 row)
+
+SELECT to_char(date '2010-02-01', 'TAMMON');
+ to_char 
+---------
+ ÚNO
+(1 row)
+
 -- to_date
+SET lc_time TO 'tr_TR';
 SELECT to_date('01 ŞUB 2010', 'DD TMMON YYYY');
   to_date   
 ------------
@@ -497,6 +518,25 @@ SELECT to_date('2010 01 araLık', 'YYYY DD TMMONTH');
  12-01-2010
 (1 row)
 
+SET lc_time TO 'cs_CZ';
+SELECT to_date('1 srpna 2010', 'DD TMMONTH YYYY');
+  to_date   
+------------
+ 08-01-2010
+(1 row)
+
+SELECT to_date('1 srp 2010', 'DD TAMMON YYYY');
+  to_date   
+------------
+ 08-01-2010
+(1 row)
+
+SELECT to_date('1 srpen 2010', 'DD TAMMONTH YYYY');
+  to_date   
+------------
+ 08-01-2010
+(1 row)
+
 -- backwards parsing
 CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc';
 CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C";
diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql
index 6d726ee9c99..544679fede5 100644
--- a/src/test/regress/sql/collate.linux.utf8.sql
+++ b/src/test/regress/sql/collate.linux.utf8.sql
@@ -182,7 +182,15 @@ SELECT to_char(date '2010-02-01', 'DD TMMON YYYY' COLLATE "tr_TR");
 SELECT to_char(date '2010-04-01', 'DD TMMON YYYY');
 SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr_TR");
 
+-- to_char
+SET lc_time TO 'cs_CZ';
+
+SELECT to_char(date '2010-02-01', 'DD TMMONTH');
+SELECT to_char(date '2010-02-01', 'TAMMONTH');
+SELECT to_char(date '2010-02-01', 'TAMMON');
+
 -- to_date
+SET lc_time TO 'tr_TR';
 
 SELECT to_date('01 ŞUB 2010', 'DD TMMON YYYY');
 SELECT to_date('01 Şub 2010', 'DD TMMON YYYY');
@@ -192,6 +200,12 @@ SELECT to_date('01 Aralık 2010', 'DD TMMONTH YYYY');
 SELECT to_date('01 aralık 2010', 'DD TMMONTH YYYY');
 SELECT to_date('2010 01 araLık', 'YYYY DD TMMONTH');
 
+SET lc_time TO 'cs_CZ';
+
+SELECT to_date('1 srpna 2010', 'DD TMMONTH YYYY');
+SELECT to_date('1 srp 2010', 'DD TAMMON YYYY');
+SELECT to_date('1 srpen 2010', 'DD TAMMONTH YYYY');
+
 -- backwards parsing
 
 CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc';
-- 
2.55.0

Reply via email to