From 213b37077ca5c2ede38859e86f33bd66ec689cb2 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 9 Oct 2024 21:31:36 +0800 Subject: [PATCH] MDL-83412 core: Use LLL/LLLL for standalone Month names We currently use MMM and MMMM which mean 'month in year', however in some locales this is context-specific and should only be used in conjunction with a day, for example "10 October 2024". LLL and LLLL are for "stand-alone on the in year" and should be used when a month is used without a day. --- lib/classes/date.php | 24 ++++++++--- lib/tests/date_test.php | 91 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 5 deletions(-) diff --git a/lib/classes/date.php b/lib/classes/date.php index 5338f023492..453faa133ae 100644 --- a/lib/classes/date.php +++ b/lib/classes/date.php @@ -741,13 +741,14 @@ class core_date { $intl_formats = [ '%a' => 'EEE', // An abbreviated textual representation of the day Sun through Sat '%A' => 'EEEE', // A full textual representation of the day Sunday through Saturday - '%b' => 'MMM', // Abbreviated month name, based on the locale Jan through Dec - '%B' => 'MMMM', // Full month name, based on the locale January through December - '%h' => 'MMM', // Abbreviated month name, based on the locale (an alias of %b) Jan through Dec ]; - $intl_formatter = function (DateTimeInterface $timestamp, string $format) use ($intl_formats, $locale) { - + $originalformat = $format; + $intl_formatter = function (DateTimeInterface $timestamp, string $format) use ( + $intl_formats, + $locale, + $originalformat, + ) { // Map IANA timezone DB names (used by PHP) to those used internally by the "intl" extension. The extension uses its // own data based on ICU timezones, which may not necessarily be in-sync with IANA depending on the version installed // on the local system. See: https://unicode-org.github.io/icu/userguide/datetime/timezone/#updating-the-time-zone-data @@ -789,6 +790,19 @@ class core_date { $time_type = IntlDateFormatter::MEDIUM; break; + case "%B": + case "%b": + case "%h": + // Check for any day (%d, or %e) in the string. + if (preg_match('/%[de]/', $originalformat)) { + // The day is present so use the standard format. + $pattern = $format === '%B' ? 'MMMM' : 'MMM'; + } else { + // The day is not present so use the stand-alone format. + $pattern = $format === '%B' ? 'LLLL' : 'LLL'; + } + break; + default: $pattern = $intl_formats[$format]; } diff --git a/lib/tests/date_test.php b/lib/tests/date_test.php index cc1fbb5193d..b46aebab8df 100644 --- a/lib/tests/date_test.php +++ b/lib/tests/date_test.php @@ -627,6 +627,36 @@ class date_test extends advanced_testcase { "%c", "20 February 2024 at 1:09 pm", ], + 'Month Year only' => [ + "1708405742", + "%B %Y", + "February 2024", + ], + 'Abbreviated Month Year only' => [ + "1708405742", + "%b %Y", + "Feb 2024", + ], + 'DD Month Year' => [ + "1708405742", + "%d %B %Y", + "20 February 2024", + ], + 'D Month Year' => [ + "1708405742", + "%e %B %Y", + "20 February 2024", + ], + 'Abbreviated DD Month Year' => [ + "1708405742", + "%d %b %Y", + "20 Feb 2024", + ], + 'Abbreviated D Month Year' => [ + "1708405742", + "%e %b %Y", + "20 Feb 2024", + ], 'numeric_c' => [ 1708405742, "%c", @@ -666,4 +696,65 @@ class date_test extends advanced_testcase { public function test_strftime(mixed $input, string $format, string $expected): void { $this->assertEqualsIgnoringWhitespace($expected, core_date::strftime($format, $input)); } + + public static function get_strftime_locale_provider(): array { + return [ + 'Month Year only' => [ + "1728487000", + 'ru_RU.UTF-8', + "%B %Y", + "октябрь 2024", + ], + 'DD Month Year' => [ + "1728487000", + 'ru_RU.UTF-8', + "%d %B %Y", + "09 октября 2024", + ], + 'D Month Year' => [ + "1728487000", + 'ru_RU.UTF-8', + "%e %B %Y", + " 9 октября 2024", + ], + 'Abbreviated Month Year only' => [ + "1728487000", + 'ru_RU.UTF-8', + "%b %Y", + "окт. 2024", + ], + 'Abbreviated DD Month Year' => [ + "1728487000", + 'ru_RU.UTF-8', + "%d %b %Y", + "09 окт. 2024", + ], + 'Abbreviated D Month Year' => [ + "1728487000", + 'ru_RU.UTF-8', + "%e %b %Y", + " 9 окт. 2024", + ], + ]; + } + + /** + * Test \core_date::strftime function with alternate languages. + * + * @dataProvider get_strftime_locale_provider + * @param mixed $input Input passed to strftime + * @param string $locale The locale + * @param string $format The date format to pass to strftime, falls back to '%c' if null + * @param string $expected The output generated by strftime + */ + public function test_strftime_locale( + mixed $input, + string $locale, + string $format, + string $expected, + ): void { + $this->assertEqualsIgnoringWhitespace( + $expected, + core_date::strftime($format, $input, $locale)); + } }