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
