diff --git a/CHANGES b/CHANGES index c00d1a594..e494f9c0a 100644 --- a/CHANGES +++ b/CHANGES @@ -28,6 +28,7 @@ Bugs fixed * #4490: autodoc: type annotation is broken with python 3.7.0a4+ * utils package is installed * #3952: apidoc: module header is too escaped +* #4275: Formats accepted by sphinx.util.i18n.format_date are limited Testing -------- diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py index 75a8506fa..d52cc0aab 100644 --- a/sphinx/util/i18n.py +++ b/sphinx/util/i18n.py @@ -152,34 +152,45 @@ def find_catalog_source_files(locale_dirs, locale, domains=None, gettext_compact # date_format mappings: ustrftime() to bable.dates.format_datetime() date_format_mappings = { - '%a': 'EEE', # Weekday as locale’s abbreviated name. - '%A': 'EEEE', # Weekday as locale’s full name. - '%b': 'MMM', # Month as locale’s abbreviated name. - '%B': 'MMMM', # Month as locale’s full name. - '%c': 'medium', # Locale’s appropriate date and time representation. - '%d': 'dd', # Day of the month as a zero-padded decimal number. - '%H': 'HH', # Hour (24-hour clock) as a decimal number [00,23]. - '%I': 'hh', # Hour (12-hour clock) as a decimal number [01,12]. - '%j': 'DDD', # Day of the year as a zero-padded decimal number. - '%m': 'MM', # Month as a zero-padded decimal number. - '%M': 'mm', # Minute as a decimal number [00,59]. - '%p': 'a', # Locale’s equivalent of either AM or PM. - '%S': 'ss', # Second as a decimal number. - '%U': 'WW', # Week number of the year (Sunday as the first day of the week) - # as a zero padded decimal number. All days in a new year preceding - # the first Sunday are considered to be in week 0. - '%w': 'e', # Weekday as a decimal number, where 0 is Sunday and 6 is Saturday. - '%W': 'WW', # Week number of the year (Monday as the first day of the week) - # as a decimal number. All days in a new year preceding the first - # Monday are considered to be in week 0. - '%x': 'medium', # Locale’s appropriate date representation. - '%X': 'medium', # Locale’s appropriate time representation. - '%y': 'YY', # Year without century as a zero-padded decimal number. - '%Y': 'YYYY', # Year with century as a decimal number. - '%Z': 'zzzz', # Time zone name (no characters if no time zone exists). - '%%': '%', + '%a': 'EEE', # Weekday as locale’s abbreviated name. + '%A': 'EEEE', # Weekday as locale’s full name. + '%b': 'MMM', # Month as locale’s abbreviated name. + '%B': 'MMMM', # Month as locale’s full name. + '%c': 'medium', # Locale’s appropriate date and time representation. + '%-d': 'd', # Day of the month as a decimal number. + '%d': 'dd', # Day of the month as a zero-padded decimal number. + '%-H': 'H', # Hour (24-hour clock) as a decimal number [0,23]. + '%H': 'HH', # Hour (24-hour clock) as a zero-padded decimal number [00,23]. + '%-I': 'h', # Hour (12-hour clock) as a decimal number [1,12]. + '%I': 'hh', # Hour (12-hour clock) as a zero-padded decimal number [01,12]. + '%-j': 'D', # Day of the year as a decimal number. + '%j': 'DDD', # Day of the year as a zero-padded decimal number. + '%-m': 'M', # Month as a decimal number. + '%m': 'MM', # Month as a zero-padded decimal number. + '%-M': 'm', # Minute as a decimal number [0,59]. + '%M': 'mm', # Minute as a zero-padded decimal number [00,59]. + '%p': 'a', # Locale’s equivalent of either AM or PM. + '%-S': 's', # Second as a decimal number. + '%S': 'ss', # Second as a zero-padded decimal number. + '%U': 'WW', # Week number of the year (Sunday as the first day of the week) + # as a zero padded decimal number. All days in a new year preceding + # the first Sunday are considered to be in week 0. + '%w': 'e', # Weekday as a decimal number, where 0 is Sunday and 6 is Saturday. + '%-W': 'W', # Week number of the year (Monday as the first day of the week) + # as a decimal number. All days in a new year preceding the first + # Monday are considered to be in week 0. + '%W': 'WW', # Week number of the year (Monday as the first day of the week) + # as a zero-padded decimal number. + '%x': 'medium', # Locale’s appropriate date representation. + '%X': 'medium', # Locale’s appropriate time representation. + '%y': 'YY', # Year without century as a zero-padded decimal number. + '%Y': 'YYYY', # Year with century as a decimal number. + '%Z': 'zzzz', # Time zone name (no characters if no time zone exists). + '%%': '%', } +date_format_re = re.compile('(%s)' % '|'.join(date_format_mappings)) + def babel_format_date(date, format, locale, formatter=babel.dates.format_date): # type: (datetime, unicode, unicode, Callable) -> unicode @@ -214,7 +225,7 @@ def format_date(format, date=None, language=None): date = datetime.now() result = [] - tokens = re.split('(%.)', format) + tokens = date_format_re.split(format) for token in tokens: if token in date_format_mappings: babel_format = date_format_mappings.get(token, '') diff --git a/tests/test_util_i18n.py b/tests/test_util_i18n.py index bec4e91e9..14a7aae6f 100644 --- a/tests/test_util_i18n.py +++ b/tests/test_util_i18n.py @@ -176,6 +176,8 @@ def test_format_date(): datet = datetime.datetime(2016, 2, 7, 5, 11, 17, 0) assert i18n.format_date(format, date=datet) == 'February 07, 2016, 05:11:17 05 AM' + format = '%B %-d, %Y, %-H:%-M:%-S %-I %p' + assert i18n.format_date(format, date=datet) == 'February 7, 2016, 5:11:17 5 AM' format = '%x' assert i18n.format_date(format, date=datet) == 'Feb 7, 2016' format = '%X'