From d5ec3594cc3cf1d39218181c4e2327dd4973bf58 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Tue, 9 May 2023 11:23:32 +0100 Subject: [PATCH] MDL-78151 core: account for timezone differences in intl extension. Recent additions to timezones from the IANA TZDB are not necessarily reflected in those used by the intl extension and/or ICU zones: * America/Ciudad_Juarez * America/Nuuk * Europe/Kyiv * Pacific/Kanton Create mapping to allow these zones to be used without errors. --- lib/classes/date.php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/classes/date.php b/lib/classes/date.php index 747a098faaa..01b8ac0b664 100644 --- a/lib/classes/date.php +++ b/lib/classes/date.php @@ -608,6 +608,7 @@ class core_date { 'America/Cordoba' => 'America/Argentina/Cordoba', 'America/Ensenada' => 'America/Tijuana', 'America/Fort_Wayne' => 'America/Indiana/Indianapolis', + 'America/Godthab' => 'America/Nuuk', 'America/Indianapolis' => 'America/Indiana/Indianapolis', 'America/Jujuy' => 'America/Argentina/Jujuy', 'America/Knox_IN' => 'America/Indiana/Knox', @@ -641,8 +642,10 @@ class core_date { 'Australia/Canberra' => 'Australia/Sydney', 'Australia/Yancowinna' => 'Australia/Broken_Hill', 'Europe/Belfast' => 'Europe/London', + 'Europe/Kiev' => 'Europe/Kyiv', 'Europe/Nicosia' => 'Asia/Nicosia', 'Europe/Tiraspol' => 'Europe/Chisinau', + 'Pacific/Enderbury' => 'Pacific/Kanton', 'Pacific/Johnston' => 'Pacific/Honolulu', 'Pacific/Ponape' => 'Pacific/Pohnpei', 'Pacific/Samoa' => 'Pacific/Pago_Pago', @@ -744,7 +747,23 @@ class core_date { ]; $intl_formatter = function (DateTimeInterface $timestamp, string $format) use ($intl_formats, $locale) { + + // 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 $tz = $timestamp->getTimezone(); + $intltz = IntlTimeZone::fromDateTimeZone($tz); + if ($intltz === null) { + // Where intl doesn't know about a recent timezone, map it to an equivalent existing zone. + $intltzname = strtr($tz->getName(), [ + 'America/Ciudad_Juarez' => 'America/Denver', + 'America/Nuuk' => 'America/Godthab', + 'Europe/Kyiv' => 'Europe/Kiev', + 'Pacific/Kanton' => 'Pacific/Enderbury', + ]); + $intltz = IntlTimeZone::createTimeZone($intltzname); + } + $date_type = IntlDateFormatter::FULL; $time_type = IntlDateFormatter::FULL; $pattern = ''; @@ -782,10 +801,10 @@ class core_date { // This leads to the same instants in time, as expressed in Unix time, having different representations // in formatted strings. // To adjust for this, a custom calendar can be supplied with a cutover date arbitrarily far in the past. - $calendar = IntlGregorianCalendar::createInstance(); + $calendar = IntlGregorianCalendar::createInstance($intltz); $calendar->setGregorianChange(PHP_INT_MIN); - return (new IntlDateFormatter($locale, $date_type, $time_type, $tz, $calendar, $pattern))->format($timestamp); + return (new IntlDateFormatter($locale, $date_type, $time_type, $intltz, $calendar, $pattern))->format($timestamp); }; // Same order as https://www.php.net/manual/en/function.strftime.php -- 2.43.0