[RFC,BZ,10871] Month names in genitive case

Message ID 2092610127.1000800.1448578835402.JavaMail.open-xchange@ox4.netart.com.pl
State Superseded
Headers

Commit Message

Rafal Luzynski Nov. 26, 2015, 11 p.m. UTC
  Hello,

This is my first message to this list so I apologize for any possible
errors.

Please find attached a patch which is an attempt to implement the
missing feature: month names in genitive case.
You can find more info and discussion at:
https://sourceware.org/bugzilla/show_bug.cgi?id=10871

Short summary: some languages require an alternative (genitive)
form of the month name when it is used in a full date, at the same
time they also require a basic (nominative) form when used standalone.
The locale subsystem should provide both forms of month names for
these languages plus an algorithm choosing the proper form.
The solution should not be destructive for the languages which
do not need this feature.

I will appreciate your comments.

Best regards,

Rafal Luzynski
  

Comments

Rafal Luzynski Jan. 7, 2016, 11:40 p.m. UTC | #1
The previous patch which I have sent on Nov 27 [1] could be considered too
long and too difficult to review so here I split it into 7 parts which
can be reviewed and accepted or rejected individually. Please find the
patches in the following emails:

1. This patch just adds the alternative month names support to nl_langinfo(3)
(the ALTMON_... constants.) I think this change is not questionable except
the question whether the new constants should be considered public or private.
I suggest public.

2. Add tests for the alternative month names. The only reason why this
change has been split out into a separate patch is that the file
tst-langinfo.c belongs to the localedata directory which has its own
ChangeLog. Feel free to merge this patch with the previous one if you think
that splitting it is a bad idea.

3. Support also day_month_order in nl_langinfo(3). If we are going to
implement a smart algorithm which determines whether "%B" in strftime(3)
should generate a nominative or genitive form then we need also a new
parameter which would determine if month should be genitive if it appears
after a day, or before a day, or in both cases, or it does not matter. Feel
free to merge this patch with the previous ones if you think this idea is
OK. Feel free to reject this patch if you think that we should implement
the "%OB" format specifier which would select the genitive case explicitly.

4. Smart algorithm choosing the nominative/genitive month name.
This is the main part: the implementation of the smart algorithm in
strftime(3) which would select the correct form of the month name for the
"%B" format specifier. Note that this patch requires the previous ones,
also it requires some NLS data from the following patches.

5. Alternative month names NLS data (Polish). Feel free to use this patch
only for local tests and reject it from the public repository if you don't
trust me. However, please note that you need some NLS data with the
alternative month names, otherwise you will not see any effect of the
previous patches.

6. Alternative month names NLS data (Russian). The names are taken from
CLDR database [2]. Also I have changed the nominative month names to
lowercase as suggested in the original bug report [3]. Please note that you
don't have to trust my knowledge of Russian, feel free to take this patch
for your local tests only and reject from the public repository.

7. Alternative month names NLS data (Ukrainian). The alternative month names
had been already present so I have only changed the alt_digits label to
alt_mon and removed all remains of the alt_digits hack. However, please
note that you absolutely should not trust my knowledge of Ukrainian language.
Feel free to use this patch for local tests only and reject it from the
public repository.

I'll appreciate your reviews and comments.

Best regards,

Rafal Luzynski

[1] https://sourceware.org/ml/libc-alpha/2015-11/msg00594.html
[2] http://st.unicode.org/cldr-apps/v#/ru/Gregorian/677bb4a72253df63
[3] https://sourceware.org/bugzilla/show_bug.cgi?id=10871
  
Mike Frysinger Jan. 8, 2016, 12:47 a.m. UTC | #2
On 27 Nov 2015 00:00, Rafal Luzynski wrote:
> Some languages (Slavic, Baltic, etc.) require a genitive case of the
> month name when formatting a full date (with the day number) while
> they require a nominative case when referring to the month standalone.
> This requirement cannot be fulfilled without providing two forms for
> each month name.  This new feature is optional so the languages which
> do not need it or do not yet provide the updated locales simply do not
> use it and their behaviour is unchanged.
> 
> The main change is in the strftime() function which now analyses the
> context where the month names appears in the format string and tries to
> choose whether the basic (nominative) or alternative (genitive) month
> name should be used.

because of this, and the other issues outlined here:
https://sourceware.org/bugzilla/show_bug.cgi?id=10871#c7

i think it'd be useful to start a thread on the POSIX list.  %OB is not
covered by the spec currently:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/strftime.html

the route you went with here -- not adding any new symbols, and trying
to make the %B behavior automatic -- makes sense to me.  it means we
don't really break existing apps, and if we change %B back if/when the
POSIX standard includes %OB, then we continue to not really break.

> --- a/time/strptime_l.c
> +++ b/time/strptime_l.c
>
> +static char const alt_month_name[][10] =
> +  {
> +    "January", "February", "March", "April", "May", "June",
> +    "July", "August", "September", "October", "November", "December"
> +  };

why introduce a new array ?  one with the same size & values already
exists a few lines above this with the name "month_name".
-mike
  
Rafal Luzynski Jan. 8, 2016, 11:26 a.m. UTC | #3
Thank you for your comments, Mike.

> On 8 Jan 2016 01:47 Mike Frysinger <vapier@gentoo.org> wrote:
>
> [...]
> because of this, and the other issues outlined here:
> https://sourceware.org/bugzilla/show_bug.cgi?id=10871#c7
>
> i think it'd be useful to start a thread on the POSIX list. %OB is not
> covered by the spec currently:
> http://pubs.opengroup.org/onlinepubs/9699919799/functions/strftime.html

That's true. But on the other hand I don't know what is the current
status of this document: http://austingroupbugs.net/view.php?id=258

> the route you went with here -- not adding any new symbols, and trying
> to make the %B behavior automatic -- makes sense to me. it means we
> don't really break existing apps, and if we change %B back if/when the
> POSIX standard includes %OB, then we continue to not really break.

That's exactly what I mean.

> > --- a/time/strptime_l.c
> > +++ b/time/strptime_l.c
> >
> > +static char const alt_month_name[][10] =
> > + {
> > + "January", "February", "March", "April", "May", "June",
> > + "July", "August", "September", "October", "November", "December"
> > + };
>
> why introduce a new array ? one with the same size & values already
> exists a few lines above this with the name "month_name".
> -mike

I've sacrificed the memory for the simplicity of the algorithm which
now (at least sometimes) has to check both basic and nominative month
names. But you're right, this can be implemented without this new
array. So I accept as a thing for me to do: remove this array if
not necessary.

Thanks,

Rafal Luzynski
  

Patch

From 0a6506e03bbbba26c367b33711fb8befa4b86acf Mon Sep 17 00:00:00 2001
From: Rafal Luzynski <digitalfreak@lingonborough.com>
Date: Thu, 19 Nov 2015 01:40:14 +0100
Subject: [PATCH] Implement alternative (genitive case) month names (bug
 10871).

Some languages (Slavic, Baltic, etc.) require a genitive case of the
month name when formatting a full date (with the day number) while
they require a nominative case when referring to the month standalone.
This requirement cannot be fulfilled without providing two forms for
each month name.  This new feature is optional so the languages which
do not need it or do not yet provide the updated locales simply do not
use it and their behaviour is unchanged.

The main change is in the strftime() function which now analyses the
context where the month names appears in the format string and tries to
choose whether the basic (nominative) or alternative (genitive) month
name should be used.

This patch also defines the new constants ALTMON_1 .. ALTMON_12.

[BZ #10871]
* conform/data/langinfo.h-data: constants ALTMON_1 .. ALTMON_12 added.
* locale/categories.def: alt_mon, wide-alt_mon, and day_month_order added.
* locale/langinfo.h: ALTMON_1 .. ALTMON_12 and similar contants defined.
* locale/programs/ld-time.c: alternative month names support added.
* locale/programs/locfile-kw.gperf: alt_mon and day_month_order defined.
* locale/programs/locfile-kw.h: regenerated for alt_mon and day_month_order.
* locale/programs/locfile-token.h: alt_mon and day_month_order defined.
* localedata/locales/pl_PL: alternative (genitive) month names added.
* localedata/locales/ru_RU: alternative (genitive) month names added. Basic
(nominative) month names changed to lowercase.
* localedata/locales/uk_UA: alternative digits removed, alternative month
names used instead.  date_fmt definition changed not to use the alternative
digits hack.
* localedata/tst-langinfo.c: public constants ALTMON_1 .. ALTMON_12 added.
* time/strftime_l.c: alternative (genitive) month names supported, a smart
algorithm decides which form is correct in the current context.
* time/strptime_l.c: alternative (genitive) month names also recognized.
---
 conform/data/langinfo.h-data     |  12 +++
 locale/categories.def            |   3 +
 locale/langinfo.h                |  48 ++++++++++++
 locale/programs/ld-time.c        |  26 +++++++
 locale/programs/locfile-kw.gperf |   2 +
 locale/programs/locfile-kw.h     | 112 ++++++++++++++--------------
 locale/programs/locfile-token.h  |   2 +
 localedata/locales/pl_PL         |  13 ++++
 localedata/locales/ru_RU         |  37 +++++++---
 localedata/locales/uk_UA         |  22 +++---
 localedata/tst-langinfo.c        |  12 +++
 time/strftime_l.c                | 155 ++++++++++++++++++++++++++++++++++++++-
 time/strptime_l.c                |  25 +++++++
 13 files changed, 390 insertions(+), 79 deletions(-)

diff --git a/conform/data/langinfo.h-data b/conform/data/langinfo.h-data
index 51dd925..4778bc4 100644
--- a/conform/data/langinfo.h-data
+++ b/conform/data/langinfo.h-data
@@ -49,6 +49,18 @@  constant ERA_D_FMT
 constant ERA_D_T_FMT
 constant ERA_T_FMT
 constant ALT_DIGITS
+constant ALTMON_1
+constant ALTMON_2
+constant ALTMON_3
+constant ALTMON_4
+constant ALTMON_5
+constant ALTMON_6
+constant ALTMON_7
+constant ALTMON_8
+constant ALTMON_9
+constant ALTMON_10
+constant ALTMON_11
+constant ALTMON_12
 constant RADIXCHAR
 constant THOUSEP
 constant YESEXPR
diff --git a/locale/categories.def b/locale/categories.def
index a8dda53..865f2b9 100644
--- a/locale/categories.def
+++ b/locale/categories.def
@@ -249,6 +249,9 @@  DEFINE_CATEGORY
   DEFINE_ELEMENT (_DATE_FMT,                "date_fmt",            opt, string)
   DEFINE_ELEMENT (_NL_W_DATE_FMT,           "wide-date_fmt",       opt, wstring)
   DEFINE_ELEMENT (_NL_TIME_CODESET,	    "time-codeset",	   std, string)
+  DEFINE_ELEMENT (ALTMON_1,       "alt_mon",       opt, stringarray, 12, 12)
+  DEFINE_ELEMENT (_NL_WALTMON_1,  "wide-alt_mon",    opt, wstringarray, 12, 12)
+  DEFINE_ELEMENT (_NL_DAY_MONTH_ORDER, "day_month_order", opt, byte)
   ), NO_POSTLOAD)
 
 
diff --git a/locale/langinfo.h b/locale/langinfo.h
index a565d9d..497f801 100644
--- a/locale/langinfo.h
+++ b/locale/langinfo.h
@@ -231,6 +231,54 @@  enum
 
   _NL_TIME_CODESET,
 
+  /* Alternative (genitive, full date context) month names.  */
+  ALTMON_1,			/* of January */
+#define ALTMON_1		ALTMON_1
+  ALTMON_2,
+#define ALTMON_2		ALTMON_2
+  ALTMON_3,
+#define ALTMON_3		ALTMON_3
+  ALTMON_4,
+#define ALTMON_4		ALTMON_4
+  ALTMON_5,
+#define ALTMON_5		ALTMON_5
+  ALTMON_6,
+#define ALTMON_6		ALTMON_6
+  ALTMON_7,
+#define ALTMON_7		ALTMON_7
+  ALTMON_8,
+#define ALTMON_8		ALTMON_8
+  ALTMON_9,
+#define ALTMON_9		ALTMON_9
+  ALTMON_10,
+#define ALTMON_10		ALTMON_10
+  ALTMON_11,
+#define ALTMON_11		ALTMON_11
+  ALTMON_12,
+#define ALTMON_12		ALTMON_12
+
+  /* Alternative (genitive, full date context) month names.  */
+  _NL_WALTMON_1,			/* of January */
+  _NL_WALTMON_2,
+  _NL_WALTMON_3,
+  _NL_WALTMON_4,
+  _NL_WALTMON_5,
+  _NL_WALTMON_6,
+  _NL_WALTMON_7,
+  _NL_WALTMON_8,
+  _NL_WALTMON_9,
+  _NL_WALTMON_10,
+  _NL_WALTMON_11,
+  _NL_WALTMON_12,
+
+  _NL_DAY_MONTH_ORDER,	/* Day-month or month-day order in full date.      */
+			/* Value 1 means that day-month is valid and also  */
+			/* requires an alternative (genitive) month case.  */
+			/* 2 is default, means that both orders are valid  */
+			/* or it does not matter because the alternative   */
+			/* case does not exist. 3 means that month-day is  */
+			/* a valid order.  */
+
   _NL_NUM_LC_TIME,	/* Number of indices in LC_TIME category.  */
 
   /* LC_COLLATE category: text sorting.
diff --git a/locale/programs/ld-time.c b/locale/programs/ld-time.c
index db490c6..26b01d0 100644
--- a/locale/programs/ld-time.c
+++ b/locale/programs/ld-time.c
@@ -91,6 +91,10 @@  struct locale_time_t
   const char *date_fmt;
   const uint32_t *wdate_fmt;
   int alt_digits_defined;
+  const char *alt_mon[12];
+  const uint32_t *walt_mon[12];
+  int alt_mon_defined;
+  unsigned char day_month_order;
   unsigned char week_ndays;
   uint32_t week_1stday;
   unsigned char week_1stweek;
@@ -531,6 +535,14 @@  No definition for %s category found"), "LC_TIME"));
     time->date_fmt = "%a %b %e %H:%M:%S %Z %Y";
   if (time->wdate_fmt == NULL)
     time->wdate_fmt = (const uint32_t *) L"%a %b %e %H:%M:%S %Z %Y";
+
+  if (time->day_month_order == '\0')
+    /* The definition does not specify this so the default is used.  */
+    time->day_month_order = 2;
+  else if (time->day_month_order > 3)
+    WITH_CUR_LOCALE (error (0, 0, _("\
+%s: values for field `%s' must not be larger than %d"),
+			    "LC_TIME", "day_month_order", 3));
 }
 
 
@@ -648,6 +660,18 @@  time_output (struct localedef_t *locale, const struct charmap_t *charmap,
   add_locale_string (&file, time->date_fmt);
   add_locale_wstring (&file, time->wdate_fmt);
   add_locale_string (&file, charmap->code_set_name);
+
+  /* The alt'mons.  */
+  for (n = 0; n < 12; ++n)
+    add_locale_string (&file, time->alt_mon[n] ?: "");
+
+  /* The wide character alt'mons.  */
+  for (n = 0; n < 12; ++n)
+    add_locale_wstring (&file, time->walt_mon[n] ?: empty_wstr);
+
+  /* The preferred day-month order in full date format.  */
+  add_locale_char (&file, time->day_month_order);
+
   write_locale_data (output_path, LC_TIME, "LC_TIME", &file);
 }
 
@@ -791,6 +815,7 @@  time_read (struct linereader *ldfile, struct localedef_t *result,
 	  STRARR_ELEM (mon, 12, 12);
 	  STRARR_ELEM (am_pm, 2, 2);
 	  STRARR_ELEM (alt_digits, 0, 100);
+	  STRARR_ELEM (alt_mon, 12, 12);
 
 	case tok_era:
 	  /* Ignore the rest of the line if we don't need the input of
@@ -896,6 +921,7 @@  time_read (struct linereader *ldfile, struct localedef_t *result,
 	  INT_ELEM (first_weekday);
 	  INT_ELEM (first_workday);
 	  INT_ELEM (cal_direction);
+	  INT_ELEM (day_month_order);
 
 	case tok_week:
 	  /* Ignore the rest of the line if we don't need the input of
diff --git a/locale/programs/locfile-kw.gperf b/locale/programs/locfile-kw.gperf
index ab40f28..edf35b8 100644
--- a/locale/programs/locfile-kw.gperf
+++ b/locale/programs/locfile-kw.gperf
@@ -148,6 +148,8 @@  first_workday,          tok_first_workday,          0
 cal_direction,          tok_cal_direction,          0
 timezone,               tok_timezone,               0
 date_fmt,               tok_date_fmt,               0
+alt_mon,                tok_alt_mon,                0
+day_month_order,        tok_day_month_order,        0
 LC_MESSAGES,            tok_lc_messages,            0
 yesexpr,                tok_yesexpr,                0
 noexpr,                 tok_noexpr,                 0
diff --git a/locale/programs/locfile-kw.h b/locale/programs/locfile-kw.h
index 0978861..670ccfc 100644
--- a/locale/programs/locfile-kw.h
+++ b/locale/programs/locfile-kw.h
@@ -54,7 +54,7 @@ 
 #line 24 "locfile-kw.gperf"
 struct keyword_t ;
 
-#define TOTAL_KEYWORDS 176
+#define TOTAL_KEYWORDS 178
 #define MIN_WORD_LENGTH 3
 #define MAX_WORD_LENGTH 22
 #define MIN_HASH_VALUE 3
@@ -147,22 +147,22 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 30 "locfile-kw.gperf"
       {"LC_CTYPE",               tok_lc_ctype,               0},
       {""},
-#line 166 "locfile-kw.gperf"
+#line 168 "locfile-kw.gperf"
       {"LC_ADDRESS",             tok_lc_address,             0},
-#line 151 "locfile-kw.gperf"
+#line 153 "locfile-kw.gperf"
       {"LC_MESSAGES",            tok_lc_messages,            0},
-#line 159 "locfile-kw.gperf"
+#line 161 "locfile-kw.gperf"
       {"LC_NAME",                tok_lc_name,                0},
-#line 156 "locfile-kw.gperf"
+#line 158 "locfile-kw.gperf"
       {"LC_PAPER",               tok_lc_paper,               0},
-#line 184 "locfile-kw.gperf"
+#line 186 "locfile-kw.gperf"
       {"LC_MEASUREMENT",         tok_lc_measurement,         0},
 #line 56 "locfile-kw.gperf"
       {"LC_COLLATE",             tok_lc_collate,             0},
       {""},
-#line 186 "locfile-kw.gperf"
+#line 188 "locfile-kw.gperf"
       {"LC_IDENTIFICATION",      tok_lc_identification,      0},
-#line 199 "locfile-kw.gperf"
+#line 201 "locfile-kw.gperf"
       {"revision",               tok_revision,               0},
 #line 69 "locfile-kw.gperf"
       {"UNDEFINED",              tok_undefined,              0},
@@ -170,19 +170,19 @@  locfile_hash (register const char *str, register unsigned int len)
       {"LC_NUMERIC",             tok_lc_numeric,             0},
 #line 82 "locfile-kw.gperf"
       {"LC_MONETARY",            tok_lc_monetary,            0},
-#line 179 "locfile-kw.gperf"
+#line 181 "locfile-kw.gperf"
       {"LC_TELEPHONE",           tok_lc_telephone,           0},
       {""}, {""}, {""},
 #line 75 "locfile-kw.gperf"
       {"define",                 tok_define,                 0},
-#line 152 "locfile-kw.gperf"
+#line 154 "locfile-kw.gperf"
       {"yesexpr",                tok_yesexpr,                0},
 #line 141 "locfile-kw.gperf"
       {"era_year",               tok_era_year,               0},
       {""},
 #line 54 "locfile-kw.gperf"
       {"translit_ignore",        tok_translit_ignore,        0},
-#line 154 "locfile-kw.gperf"
+#line 156 "locfile-kw.gperf"
       {"yesstr",                 tok_yesstr,                 0},
       {""},
 #line 89 "locfile-kw.gperf"
@@ -190,7 +190,7 @@  locfile_hash (register const char *str, register unsigned int len)
       {""},
 #line 137 "locfile-kw.gperf"
       {"t_fmt",                  tok_t_fmt,                  0},
-#line 157 "locfile-kw.gperf"
+#line 159 "locfile-kw.gperf"
       {"height",                 tok_height,                 0},
       {""}, {""},
 #line 52 "locfile-kw.gperf"
@@ -213,7 +213,7 @@  locfile_hash (register const char *str, register unsigned int len)
       {""},
 #line 142 "locfile-kw.gperf"
       {"era_d_fmt",              tok_era_d_fmt,              0},
-#line 187 "locfile-kw.gperf"
+#line 189 "locfile-kw.gperf"
       {"title",                  tok_title,                  0},
       {""}, {""},
 #line 149 "locfile-kw.gperf"
@@ -243,7 +243,7 @@  locfile_hash (register const char *str, register unsigned int len)
       {"duo_n_cs_precedes",      tok_duo_n_cs_precedes,      0},
 #line 127 "locfile-kw.gperf"
       {"thousands_sep",          tok_thousands_sep,          0},
-#line 195 "locfile-kw.gperf"
+#line 197 "locfile-kw.gperf"
       {"territory",              tok_territory,              0},
 #line 36 "locfile-kw.gperf"
       {"digit",                  tok_digit,                  0},
@@ -258,7 +258,7 @@  locfile_hash (register const char *str, register unsigned int len)
       {""},
 #line 78 "locfile-kw.gperf"
       {"else",                   tok_else,                   0},
-#line 182 "locfile-kw.gperf"
+#line 184 "locfile-kw.gperf"
       {"int_select",             tok_int_select,             0},
       {""}, {""}, {""},
 #line 132 "locfile-kw.gperf"
@@ -266,11 +266,11 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 33 "locfile-kw.gperf"
       {"upper",                  tok_upper,                  0},
       {""}, {""},
-#line 192 "locfile-kw.gperf"
+#line 194 "locfile-kw.gperf"
       {"tel",                    tok_tel,                    0},
 #line 93 "locfile-kw.gperf"
       {"p_sep_by_space",         tok_p_sep_by_space,         0},
-#line 158 "locfile-kw.gperf"
+#line 160 "locfile-kw.gperf"
       {"width",                  tok_width,                  0},
       {""},
 #line 98 "locfile-kw.gperf"
@@ -301,7 +301,7 @@  locfile_hash (register const char *str, register unsigned int len)
       {""}, {""}, {""}, {""}, {""},
 #line 58 "locfile-kw.gperf"
       {"section-symbol",         tok_section_symbol,         0},
-#line 183 "locfile-kw.gperf"
+#line 185 "locfile-kw.gperf"
       {"int_prefix",             tok_int_prefix,             0},
       {""}, {""}, {""}, {""},
 #line 42 "locfile-kw.gperf"
@@ -318,7 +318,7 @@  locfile_hash (register const char *str, register unsigned int len)
       {"duo_p_sep_by_space",     tok_duo_p_sep_by_space,     0},
 #line 118 "locfile-kw.gperf"
       {"duo_int_p_sign_posn",    tok_duo_int_p_sign_posn,    0},
-#line 155 "locfile-kw.gperf"
+#line 157 "locfile-kw.gperf"
       {"nostr",                  tok_nostr,                  0},
       {""}, {""},
 #line 140 "locfile-kw.gperf"
@@ -327,26 +327,26 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 84 "locfile-kw.gperf"
       {"currency_symbol",        tok_currency_symbol,        0},
       {""},
-#line 165 "locfile-kw.gperf"
+#line 167 "locfile-kw.gperf"
       {"name_ms",                tok_name_ms,                0},
-#line 163 "locfile-kw.gperf"
+#line 165 "locfile-kw.gperf"
       {"name_mrs",               tok_name_mrs,               0},
-#line 164 "locfile-kw.gperf"
+#line 166 "locfile-kw.gperf"
       {"name_miss",              tok_name_miss,              0},
 #line 83 "locfile-kw.gperf"
       {"int_curr_symbol",        tok_int_curr_symbol,        0},
-#line 188 "locfile-kw.gperf"
+#line 190 "locfile-kw.gperf"
       {"source",                 tok_source,                 0},
-#line 162 "locfile-kw.gperf"
+#line 164 "locfile-kw.gperf"
       {"name_mr",                tok_name_mr,                0},
-#line 161 "locfile-kw.gperf"
+#line 163 "locfile-kw.gperf"
       {"name_gen",               tok_name_gen,               0},
-#line 200 "locfile-kw.gperf"
+#line 202 "locfile-kw.gperf"
       {"date",                   tok_date,                   0},
       {""}, {""},
-#line 189 "locfile-kw.gperf"
+#line 191 "locfile-kw.gperf"
       {"address",                tok_address,                0},
-#line 160 "locfile-kw.gperf"
+#line 162 "locfile-kw.gperf"
       {"name_fmt",               tok_name_fmt,               0},
 #line 32 "locfile-kw.gperf"
       {"copy",                   tok_copy,                   0},
@@ -365,16 +365,16 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 117 "locfile-kw.gperf"
       {"duo_n_sign_posn",        tok_duo_n_sign_posn,        0},
       {""},
-#line 168 "locfile-kw.gperf"
+#line 170 "locfile-kw.gperf"
       {"country_name",           tok_country_name,           0},
 #line 71 "locfile-kw.gperf"
       {"reorder-after",          tok_reorder_after,          0},
       {""}, {""},
-#line 153 "locfile-kw.gperf"
+#line 155 "locfile-kw.gperf"
       {"noexpr",                 tok_noexpr,                 0},
 #line 50 "locfile-kw.gperf"
       {"tolower",                tok_tolower,                0},
-#line 196 "locfile-kw.gperf"
+#line 198 "locfile-kw.gperf"
       {"audience",               tok_audience,               0},
       {""}, {""}, {""},
 #line 49 "locfile-kw.gperf"
@@ -395,7 +395,7 @@  locfile_hash (register const char *str, register unsigned int len)
       {""},
 #line 102 "locfile-kw.gperf"
       {"int_p_sign_posn",        tok_int_p_sign_posn,        0},
-#line 173 "locfile-kw.gperf"
+#line 175 "locfile-kw.gperf"
       {"country_car",            tok_country_car,            0},
       {""}, {""},
 #line 104 "locfile-kw.gperf"
@@ -406,9 +406,9 @@  locfile_hash (register const char *str, register unsigned int len)
       {""}, {""},
 #line 116 "locfile-kw.gperf"
       {"duo_p_sign_posn",        tok_duo_p_sign_posn,        0},
-#line 185 "locfile-kw.gperf"
+#line 187 "locfile-kw.gperf"
       {"measurement",            tok_measurement,            0},
-#line 174 "locfile-kw.gperf"
+#line 176 "locfile-kw.gperf"
       {"country_isbn",           tok_country_isbn,           0},
 #line 37 "locfile-kw.gperf"
       {"outdigit",               tok_outdigit,               0},
@@ -418,9 +418,9 @@  locfile_hash (register const char *str, register unsigned int len)
       {""}, {""}, {""},
 #line 34 "locfile-kw.gperf"
       {"lower",                  tok_lower,                  0},
-#line 181 "locfile-kw.gperf"
+#line 183 "locfile-kw.gperf"
       {"tel_dom_fmt",            tok_tel_dom_fmt,            0},
-#line 169 "locfile-kw.gperf"
+#line 171 "locfile-kw.gperf"
       {"country_post",           tok_country_post,           0},
 #line 148 "locfile-kw.gperf"
       {"cal_direction",          tok_cal_direction,          0},
@@ -430,7 +430,7 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 91 "locfile-kw.gperf"
       {"frac_digits",            tok_frac_digits,            0},
       {""}, {""},
-#line 175 "locfile-kw.gperf"
+#line 177 "locfile-kw.gperf"
       {"lang_name",              tok_lang_name,              0},
 #line 90 "locfile-kw.gperf"
       {"int_frac_digits",        tok_int_frac_digits,        0},
@@ -445,7 +445,7 @@  locfile_hash (register const char *str, register unsigned int len)
       {""}, {""}, {""}, {""},
 #line 107 "locfile-kw.gperf"
       {"duo_frac_digits",        tok_duo_frac_digits,        0},
-#line 180 "locfile-kw.gperf"
+#line 182 "locfile-kw.gperf"
       {"tel_int_fmt",            tok_tel_int_fmt,            0},
 #line 123 "locfile-kw.gperf"
       {"duo_valid_to",           tok_duo_valid_to,           0},
@@ -455,7 +455,7 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 130 "locfile-kw.gperf"
       {"abday",                  tok_abday,                  0},
       {""},
-#line 198 "locfile-kw.gperf"
+#line 200 "locfile-kw.gperf"
       {"abbreviation",           tok_abbreviation,           0},
 #line 147 "locfile-kw.gperf"
       {"first_workday",          tok_first_workday,          0},
@@ -472,12 +472,12 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 45 "locfile-kw.gperf"
       {"blank",                  tok_blank,                  0},
       {""}, {""},
-#line 194 "locfile-kw.gperf"
+#line 196 "locfile-kw.gperf"
       {"language",               tok_language,               0},
 #line 120 "locfile-kw.gperf"
       {"uno_valid_from",         tok_uno_valid_from,         0},
       {""},
-#line 197 "locfile-kw.gperf"
+#line 199 "locfile-kw.gperf"
       {"application",            tok_application,            0},
       {""},
 #line 80 "locfile-kw.gperf"
@@ -498,7 +498,7 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 96 "locfile-kw.gperf"
       {"p_sign_posn",            tok_p_sign_posn,            0},
       {""},
-#line 201 "locfile-kw.gperf"
+#line 203 "locfile-kw.gperf"
       {"category",               tok_category,               0},
       {""}, {""}, {""}, {""},
 #line 134 "locfile-kw.gperf"
@@ -510,36 +510,38 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 63 "locfile-kw.gperf"
       {"order_start",            tok_order_start,            0},
       {""}, {""}, {""}, {""}, {""},
-#line 176 "locfile-kw.gperf"
-      {"lang_ab",                tok_lang_ab,                0},
 #line 178 "locfile-kw.gperf"
+      {"lang_ab",                tok_lang_ab,                0},
+#line 180 "locfile-kw.gperf"
       {"lang_lib",               tok_lang_lib,               0},
       {""}, {""}, {""},
-#line 190 "locfile-kw.gperf"
+#line 192 "locfile-kw.gperf"
       {"contact",                tok_contact,                0},
       {""}, {""}, {""},
-#line 171 "locfile-kw.gperf"
+#line 173 "locfile-kw.gperf"
       {"country_ab3",            tok_country_ab3,            0},
       {""}, {""}, {""},
-#line 191 "locfile-kw.gperf"
+#line 193 "locfile-kw.gperf"
       {"email",                  tok_email,                  0},
-#line 170 "locfile-kw.gperf"
+#line 172 "locfile-kw.gperf"
       {"country_ab2",            tok_country_ab2,            0},
       {""}, {""}, {""},
 #line 55 "locfile-kw.gperf"
       {"default_missing",        tok_default_missing,        0},
       {""}, {""},
-#line 193 "locfile-kw.gperf"
+#line 195 "locfile-kw.gperf"
       {"fax",                    tok_fax,                    0},
       {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 172 "locfile-kw.gperf"
+#line 174 "locfile-kw.gperf"
       {"country_num",            tok_country_num,            0},
       {""}, {""}, {""}, {""}, {""}, {""},
 #line 51 "locfile-kw.gperf"
       {"map",                    tok_map,                    0},
 #line 65 "locfile-kw.gperf"
       {"from",                   tok_from,                   0},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 152 "locfile-kw.gperf"
+      {"day_month_order",        tok_day_month_order,        0},
+      {""}, {""}, {""}, {""}, {""}, {""},
 #line 86 "locfile-kw.gperf"
       {"mon_thousands_sep",      tok_mon_thousands_sep,      0},
       {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
@@ -548,7 +550,9 @@  locfile_hash (register const char *str, register unsigned int len)
       {"endif",                  tok_endif,                  0},
       {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
       {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 151 "locfile-kw.gperf"
+      {"alt_mon",                tok_alt_mon,                0},
       {""}, {""}, {""}, {""}, {""}, {""}, {""},
 #line 76 "locfile-kw.gperf"
       {"undef",                  tok_undef,                  0},
@@ -569,7 +573,7 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 85 "locfile-kw.gperf"
       {"mon_decimal_point",      tok_mon_decimal_point,      0},
       {""}, {""},
-#line 167 "locfile-kw.gperf"
+#line 169 "locfile-kw.gperf"
       {"postal_fmt",             tok_postal_fmt,             0},
       {""}, {""}, {""}, {""}, {""},
 #line 60 "locfile-kw.gperf"
@@ -588,7 +592,7 @@  locfile_hash (register const char *str, register unsigned int len)
 #line 87 "locfile-kw.gperf"
       {"mon_grouping",           tok_mon_grouping,           0},
       {""},
-#line 177 "locfile-kw.gperf"
+#line 179 "locfile-kw.gperf"
       {"lang_term",              tok_lang_term,              0},
       {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
       {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
diff --git a/locale/programs/locfile-token.h b/locale/programs/locfile-token.h
index 10c9907..f701d8f 100644
--- a/locale/programs/locfile-token.h
+++ b/locale/programs/locfile-token.h
@@ -186,6 +186,8 @@  enum token_t
   tok_cal_direction,
   tok_timezone,
   tok_date_fmt,
+  tok_alt_mon,
+  tok_day_month_order,
   tok_lc_messages,
   tok_yesexpr,
   tok_noexpr,
diff --git a/localedata/locales/pl_PL b/localedata/locales/pl_PL
index e829330..23adbb6 100644
--- a/localedata/locales/pl_PL
+++ b/localedata/locales/pl_PL
@@ -2198,6 +2198,19 @@  date_fmt "<U0025><U0061><U002C><U0020>/
 week	7;19971130;4
 first_weekday	2
 first_workday	2
+alt_mon "<U0073><U0074><U0079><U0063><U007A><U006E><U0069><U0061>";/
+        "<U006C><U0075><U0074><U0065><U0067><U006F>";/
+        "<U006D><U0061><U0072><U0063><U0061>";/
+        "<U006B><U0077><U0069><U0065><U0074><U006E><U0069><U0061>";/
+        "<U006D><U0061><U006A><U0061>";/
+        "<U0063><U007A><U0065><U0072><U0077><U0063><U0061>";/
+        "<U006C><U0069><U0070><U0063><U0061>";/
+        "<U0073><U0069><U0065><U0072><U0070><U006E><U0069><U0061>";/
+        "<U0077><U0072><U007A><U0065><U015B><U006E><U0069><U0061>";/
+        "<U0070><U0061><U017A><U0064><U007A><U0069><U0065><U0072><U006E><U0069><U006B><U0061>";/
+        "<U006C><U0069><U0073><U0074><U006F><U0070><U0061><U0064><U0061>";/
+        "<U0067><U0072><U0075><U0064><U006E><U0069><U0061>"
+day_month_order	1
 END LC_TIME
 
 LC_PAPER
diff --git a/localedata/locales/ru_RU b/localedata/locales/ru_RU
index 2e51a4d..0552ac6 100644
--- a/localedata/locales/ru_RU
+++ b/localedata/locales/ru_RU
@@ -116,18 +116,18 @@  abday   "<U0412><U0441>";/
         "<U0427><U0442>";/
         "<U041F><U0442>";/
         "<U0421><U0431>"
-mon     "<U042F><U043D><U0432><U0430><U0440><U044C>";/
-        "<U0424><U0435><U0432><U0440><U0430><U043B><U044C>";/
-        "<U041C><U0430><U0440><U0442>";/
-        "<U0410><U043F><U0440><U0435><U043B><U044C>";/
-        "<U041C><U0430><U0439>";/
-        "<U0418><U044E><U043D><U044C>";/
-        "<U0418><U044E><U043B><U044C>";/
-        "<U0410><U0432><U0433><U0443><U0441><U0442>";/
-        "<U0421><U0435><U043D><U0442><U044F><U0431><U0440><U044C>";/
-        "<U041E><U043A><U0442><U044F><U0431><U0440><U044C>";/
-        "<U041D><U043E><U044F><U0431><U0440><U044C>";/
-        "<U0414><U0435><U043A><U0430><U0431><U0440><U044C>"
+mon     "<U044F><U043D><U0432><U0430><U0440><U044C>";/
+        "<U0444><U0435><U0432><U0440><U0430><U043B><U044C>";/
+        "<U043C><U0430><U0440><U0442>";/
+        "<U0430><U043F><U0440><U0435><U043B><U044C>";/
+        "<U043C><U0430><U0439>";/
+        "<U0438><U044E><U043D><U044C>";/
+        "<U0438><U044E><U043B><U044C>";/
+        "<U0430><U0432><U0433><U0443><U0441><U0442>";/
+        "<U0441><U0435><U043D><U0442><U044F><U0431><U0440><U044C>";/
+        "<U043E><U043A><U0442><U044F><U0431><U0440><U044C>";/
+        "<U043D><U043E><U044F><U0431><U0440><U044C>";/
+        "<U0434><U0435><U043A><U0430><U0431><U0440><U044C>"
 abmon   "<U044F><U043D><U0432>";/
         "<U0444><U0435><U0432>";/
         "<U043C><U0430><U0440>";/
@@ -151,6 +151,19 @@  date_fmt       "<U0025><U0061><U0020><U0025><U0062><U0020><U0025><U0065>/
 <U0025><U005A><U0020><U0025><U0059>"
 first_weekday 2
 first_workday 2
+alt_mon "<U044F><U043D><U0432><U0430><U0440><U044F>";/
+        "<U0444><U0435><U0432><U0440><U0430><U043B><U044F>";/
+        "<U043C><U0430><U0440><U0442><U0430>";/
+        "<U0430><U043F><U0440><U0435><U043B><U044F>";/
+        "<U043C><U0430><U044F>";/
+        "<U0438><U044E><U043D><U044F>";/
+        "<U0438><U044E><U043B><U044F>";/
+        "<U0430><U0432><U0433><U0443><U0441><U0442><U0430>";/
+        "<U0441><U0435><U043D><U0442><U044F><U0431><U0440><U044F>";/
+        "<U043E><U043A><U0442><U044F><U0431><U0440><U044F>";/
+        "<U043D><U043E><U044F><U0431><U0440><U044F>";/
+        "<U0434><U0435><U043A><U0430><U0431><U0440><U044F>"
+day_month_order 1
 END LC_TIME
 
 LC_PAPER
diff --git a/localedata/locales/uk_UA b/localedata/locales/uk_UA
index 5e58043..4d3b977 100644
--- a/localedata/locales/uk_UA
+++ b/localedata/locales/uk_UA
@@ -898,7 +898,7 @@  abmon /
         "<U043B><U0438><U0441>"; %lys  /
         "<U0433><U0440><U0443>"  %hru
 
-% A list of month names in proper form for calendar, see alt_digits. (%B)
+% A list of month names in proper form for calendar, see alt_mon. (%B)
 mon /
         "<U0441><U0456><U0447><U0435><U043D><U044C>";   %sichen`  /
         "<U043B><U044E><U0442><U0438><U0439>";    %lyutyj   /
@@ -913,16 +913,8 @@  mon /
         "<U043B><U0438><U0441><U0442><U043E><U043F><U0430><U0434>"; %lystopad /
         "<U0433><U0440><U0443><U0434><U0435><U043D><U044C>"   %hruden`
 
-% Initially alt_digits was supposed to hold alternative symbols for _digits_,
-% corresponding to %O modified conversion specification.
-% Although in Ukrainian language alternate _names_ are used instead of digits.
-% We'll use this keyword to present a list of month names in proper form for
-% date, see mon.  (%Om)
-%
-% This hack is dedicated for months it won't work for other %O* modifiers
-% (weeks, days etc).
-%
-alt_digits "<U0030>";	     % digits are starting from zero /
+% A list of month names in genitive form, for full date format, with day. (%B)
+alt_mon /
         "<U0441><U0456><U0447><U043D><U044F>";     % sichnya   /
         "<U043B><U044E><U0442><U043E><U0433><U043E>";    % lyutoho   /
         "<U0431><U0435><U0440><U0435><U0437><U043D><U044F>";   % bereznya  /
@@ -936,8 +928,14 @@  alt_digits "<U0030>";	     % digits are starting from zero /
         "<U043B><U0438><U0441><U0442><U043E><U043F><U0430><U0434><U0430>"; % lystopada /
         "<U0433><U0440><U0443><U0434><U043D><U044F>"     % hrudnya
 
+% Proper (preferred) day-month order which also requires a genitive case
+% for the month name. 1 means "day-month" is a valid order, 2 means that
+% both orders are correct (or does not matter), 3 means that "month-day"
+% is a valid order.
+day_month_order 1
+
 % Appropriate date representation for date(1).
-date_fmt       "<U0025><U0041><U002C><U0020><U0025><U002D><U0064><U0020><U0025><U004F><U006D><U0020><U0025><U0059><U0020><U0025><U0058><U0020><U0025><U007A>"
+date_fmt       "<U0025><U0041><U002C><U0020><U0025><U002D><U0064><U0020><U0025><U0042><U0020><U0025><U0059><U0020><U0025><U0058><U0020><U0025><U007A>"
 
 % The appropriate date and time format. (%c)
 d_t_fmt  "<U0025><U0061><U002C><U0020><U0025><U0064><U002D><U0025><U0062><U002D><U0025><U0059><U0020><U0025><U0058><U0020><U0025><U007A>"
diff --git a/localedata/tst-langinfo.c b/localedata/tst-langinfo.c
index 49807f4..d0f3666 100644
--- a/localedata/tst-langinfo.c
+++ b/localedata/tst-langinfo.c
@@ -50,6 +50,18 @@  struct map
   VAL (ABMON_8),
   VAL (ABMON_9),
   VAL (ALT_DIGITS),
+  VAL (ALTMON_1),
+  VAL (ALTMON_10),
+  VAL (ALTMON_11),
+  VAL (ALTMON_12),
+  VAL (ALTMON_2),
+  VAL (ALTMON_3),
+  VAL (ALTMON_4),
+  VAL (ALTMON_5),
+  VAL (ALTMON_6),
+  VAL (ALTMON_7),
+  VAL (ALTMON_8),
+  VAL (ALTMON_9),
   VAL (AM_STR),
   VAL (CRNCYSTR),
   VAL (CURRENCY_SYMBOL),
diff --git a/time/strftime_l.c b/time/strftime_l.c
index 098ade5..e6805bd 100644
--- a/time/strftime_l.c
+++ b/time/strftime_l.c
@@ -72,6 +72,7 @@  extern char *tzname[];
 #  define mbstate_t int
 #  define mbrlen(s, n, ps) mblen (s, n)
 #  define mbsinit(ps) (*(ps) == 0)
+#  define mbrtowc(pwc, s, n, ps) mbtowc (pwc, s, n)
 # endif
   static const mbstate_t mbstate_zero;
 #endif
@@ -432,6 +433,125 @@  static CHAR_T const month_name[][10] =
 # define ut 0
 #endif
 
+/* Check if the characters pointed to by S are a valid suffix: if they
+   start with a valid letter (in the current locale) or a single
+   punctuation character (a dash, an apostrophe, etc.) followed by
+   a letter, and they are not a format specifier. If a suffix is appended
+   at the end of a month name then probably a caller already tries to
+   workaround the problem of missing genitive forms so we should not
+   interfere with that.  */
+static bool is_suffix (const CHAR_T *s)
+{
+#if defined COMPILE_WIDE || DO_MULTIBYTE
+# define ISALPHA iswalpha
+# define ISPUNCT iswpunct
+#else
+# define ISALPHA isalpha
+# define ISPUNCT ispunct
+#endif
+
+#if DO_MULTIBYTE && !defined COMPILE_WIDE
+  wchar_t c, next_char;
+  size_t mb_char_len, mb_next_char_len;
+  mbstate_t mbstate = mbstate_zero;
+
+  mb_char_len = mbrtowc (&c, s, strlen (s), &mbstate);
+  if ((ssize_t) mb_char_len <= 0)
+    c = L_('\0');
+
+  if (c == L_('\0'))
+    {
+      next_char = L_('\0');
+      mb_next_char_len = 0;
+    }
+  else
+    {
+      mbsinit (&mbstate);
+      mb_next_char_len = mbrtowc (&next_char, s + mb_char_len,
+				  strlen (s + mb_char_len), &mbstate);
+      if ((ssize_t) mb_next_char_len <= 0)
+        next_char = L_('\0');
+    }
+# define NEXT_CHAR_OFFSET mb_char_len
+# define NEXT_NEXT_CHAR_OFFSET (mb_char_len + mb_next_char_len)
+#else
+  const CHAR_T c = s[0];
+  const CHAR_T next_char = c == L_('\0') ? L_('\0') : s[1];
+# define NEXT_CHAR_OFFSET 1
+# define NEXT_NEXT_CHAR_OFFSET 2
+#endif
+
+  if (ISALPHA (c))
+    return true;
+
+  if (c == L_('%'))
+    {
+      if (next_char == L_('%'))
+	return is_suffix (s + NEXT_NEXT_CHAR_OFFSET);
+      else
+	return false;
+    }
+
+  if (ISPUNCT (c))
+    return is_suffix (s + NEXT_CHAR_OFFSET);
+
+  return false;
+}
+
+/* Checks if the nearest format specifier in the format string pointed
+   to by Sis a day of the month specifier: "%e" or "%d". The format
+   specifier may be preceded with any number of other characters and may
+   contain modifiers. This function does not check if the modifiers are
+   valid and actually produce the day number so the result may be sometimes
+   wrong but in these cases the output will be wrong anyway because the
+   format is wrong.  */
+static bool next_is_day (const CHAR_T *s)
+{
+  /* Skip until the percent sign is found.  */
+  while (1)
+    {
+      switch (*s++)
+	{
+	case L_('\0'):
+	    return false;
+	case L_('%'):
+	    if (*s == L_('%'))
+	      {
+		++s;
+		continue;
+	      }
+	    break;
+	default:
+	    continue;
+	}
+      break;
+    }
+
+  while (1)
+    {
+      switch (*s++)
+	{
+	/* Skip the modifiers. Here we don't check if they are valid.  */
+	case L_('_'):
+	case L_('-'):
+	case L_('^'):
+	case L_('#'):
+	case L_('E'): case L_('O'):
+	case L_('0'): case L_('1'): case L_('2'): case L_('3'): case L_('4'):
+	case L_('5'): case L_('6'): case L_('7'): case L_('8'): case L_('9'):
+	    continue;
+
+	/* Day found.  */
+	case L_('d'): case L_('e'):
+	    return true;
+
+	/* Something else found, including the terminating zero.  */
+	default:
+	    return false;
+	}
+    }
+}
+
 static size_t __strftime_internal (CHAR_T *, size_t, const CHAR_T *,
 				   const struct tm *, bool *
 				   ut_argument_spec
@@ -492,6 +612,9 @@  __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
 # define f_month \
   ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11			     \
 		     ? "?" : _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon)))
+# define f_altmonth \
+  ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11			     \
+		     ? "?" : _NL_CURRENT (LC_TIME, NLW(ALTMON_1) + tp->tm_mon)))
 # define ampm \
   ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11		      \
 				 ? NLW(PM_STR) : NLW(AM_STR)))
@@ -499,6 +622,8 @@  __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
 # define aw_len STRLEN (a_wkday)
 # define am_len STRLEN (a_month)
 # define ap_len STRLEN (ampm)
+# define day_month_order \
+  (current->values[_NL_ITEM_INDEX (_NL_DAY_MONTH_ORDER)].string[0])
 #else
 # if !HAVE_STRFTIME
 #  define f_wkday (tp->tm_wday < 0 || tp->tm_wday > 6	\
@@ -507,13 +632,16 @@  __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
 		   ? "?" : month_name[tp->tm_mon])
 #  define a_wkday f_wkday
 #  define a_month f_month
+#  define f_altmonth (L_(""))
 #  define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11))
 
   size_t aw_len = 3;
   size_t am_len = 3;
   size_t ap_len = 2;
+# define day_month_order 2
 # endif
 #endif
+  int last_was_day = 0;
   const char *zone;
   size_t i = 0;
   CHAR_T *p = s;
@@ -783,7 +911,20 @@  __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
 	      to_lowcase = 0;
 	    }
 #if defined _NL_CURRENT || !HAVE_STRFTIME
-	  cpy (STRLEN (f_month), f_month);
+	  /* Use f_altmonth only if f_altmonth is provided.  */
+	  if (f_altmonth[0]
+	  /* Don't use f_altmonth if there is a suffix after %B because
+	     it means that a caller already provides its own workaround.  */
+	      && !is_suffix (f + 1)
+	  /* Use f_altmonth if the day-month order is valid and the last
+	     format specifier was a day specifier.  */
+	      && ( (day_month_order <= 2 && last_was_day)
+	  /* Use f_altmonth if the month-day order is valid and the next
+	     format specifier will be a day specifier.  */
+		  || (day_month_order >= 2 && next_is_day (f + 1)) ))
+	    cpy (STRLEN (f_altmonth), f_altmonth);
+	  else
+	    cpy (STRLEN (f_month), f_month);
 	  break;
 #else
 	  goto underlying_strftime;
@@ -1400,6 +1541,18 @@  __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
 	  }
 	  break;
 	}
+
+      /* Update the previous_was_day flag. */
+      switch (format_char)
+	{
+	case L_('d'):
+	case L_('e'):
+	  last_was_day = 1;
+	  break;
+	default:
+	  last_was_day = 0;
+	  break;
+	}
     }
 
   if (p && maxsize != 0)
diff --git a/time/strptime_l.c b/time/strptime_l.c
index cc8164d..6602c3b 100644
--- a/time/strptime_l.c
+++ b/time/strptime_l.c
@@ -124,6 +124,8 @@  extern const struct __locale_data _nl_C_LC_TIME attribute_hidden;
   (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
+# define alt_month_name \
+  (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ALTMON_1)].string)
 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
@@ -153,6 +155,11 @@  static char const ab_month_name[][4] =
     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
   };
+static char const alt_month_name[][10] =
+  {
+    "January", "February", "March", "April", "May", "June",
+    "July", "August", "September", "October", "November", "December"
+  };
 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
 # define HERE_D_FMT "%m/%d/%y"
 # define HERE_AM_STR "AM"
@@ -403,6 +410,20 @@  __strptime_internal (const char *rp, const char *fmt, struct tm *tmp,
 	      if (s.decided !=raw)
 		{
 		  trp = rp;
+		  /* First check if the alt month is provided.  */
+		  if (_NL_CURRENT (LC_TIME, ALTMON_1 + cnt)
+		      && * (_NL_CURRENT (LC_TIME, ALTMON_1 + cnt))
+		      && match_string (_NL_CURRENT (LC_TIME, ALTMON_1 + cnt), trp)
+		      && trp > rp_longest)
+		    {
+		      rp_longest = trp;
+		      cnt_longest = cnt;
+		      if (s.decided == not
+			  && strcmp (_NL_CURRENT (LC_TIME, ALTMON_1 + cnt),
+				     alt_month_name[cnt]))
+			decided_longest = loc;
+		    }
+		  trp = rp;
 		  if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
 		      && trp > rp_longest)
 		    {
@@ -429,6 +450,10 @@  __strptime_internal (const char *rp, const char *fmt, struct tm *tmp,
 	      if (s.decided != loc
 		  && (((trp = rp, match_string (month_name[cnt], trp))
 		       && trp > rp_longest)
+		      || ((trp = rp, alt_month_name[cnt]
+			  && alt_month_name[cnt][0]
+			  && match_string (alt_month_name[cnt], trp))
+			  && trp > rp_longest)
 		      || ((trp = rp, match_string (ab_month_name[cnt], trp))
 			  && trp > rp_longest)))
 		{
-- 
2.5.0