1*db926724SAndreas Gohr<?php 2*db926724SAndreas Gohr 3*db926724SAndreas Gohrnamespace PHP81_BC\strftime; 4*db926724SAndreas Gohr 5*db926724SAndreas Gohruse DateTimeInterface; 6*db926724SAndreas Gohruse IntlDateFormatter; 7*db926724SAndreas Gohruse IntlGregorianCalendar; 8*db926724SAndreas Gohr 9*db926724SAndreas Gohr/** 10*db926724SAndreas Gohr * This formatter uses the IntlDateFormatter class to do proper, locale aware formatting 11*db926724SAndreas Gohr */ 12*db926724SAndreas Gohrclass IntlLocaleFormatter extends AbstractLocaleFormatter 13*db926724SAndreas Gohr{ 14*db926724SAndreas Gohr 15*db926724SAndreas Gohr /** @var string[] strftime to ICU placeholders */ 16*db926724SAndreas Gohr protected $formats = [ 17*db926724SAndreas Gohr '%a' => 'EEE', // An abbreviated textual representation of the day Sun through Sat 18*db926724SAndreas Gohr '%A' => 'EEEE', // A full textual representation of the day Sunday through Saturday 19*db926724SAndreas Gohr '%b' => 'MMM', // Abbreviated month name, based on the locale Jan through Dec 20*db926724SAndreas Gohr '%B' => 'MMMM', // Full month name, based on the locale January through December 21*db926724SAndreas Gohr '%h' => 'MMM', // Abbreviated month name, based on the locale (an alias of %b) Jan through Dec 22*db926724SAndreas Gohr ]; 23*db926724SAndreas Gohr 24*db926724SAndreas Gohr /** @inheritdoc */ 25*db926724SAndreas Gohr public function __invoke(DateTimeInterface $timestamp, string $format) 26*db926724SAndreas Gohr { 27*db926724SAndreas Gohr $tz = $timestamp->getTimezone(); 28*db926724SAndreas Gohr $date_type = IntlDateFormatter::FULL; 29*db926724SAndreas Gohr $time_type = IntlDateFormatter::FULL; 30*db926724SAndreas Gohr $pattern = ''; 31*db926724SAndreas Gohr 32*db926724SAndreas Gohr switch ($format) { 33*db926724SAndreas Gohr // %c = Preferred date and time stamp based on locale 34*db926724SAndreas Gohr // Example: Tue Feb 5 00:45:10 2009 for February 5, 2009 at 12:45:10 AM 35*db926724SAndreas Gohr case '%c': 36*db926724SAndreas Gohr $date_type = IntlDateFormatter::LONG; 37*db926724SAndreas Gohr $time_type = IntlDateFormatter::SHORT; 38*db926724SAndreas Gohr break; 39*db926724SAndreas Gohr 40*db926724SAndreas Gohr // %x = Preferred date representation based on locale, without the time 41*db926724SAndreas Gohr // Example: 02/05/09 for February 5, 2009 42*db926724SAndreas Gohr case '%x': 43*db926724SAndreas Gohr $date_type = IntlDateFormatter::SHORT; 44*db926724SAndreas Gohr $time_type = IntlDateFormatter::NONE; 45*db926724SAndreas Gohr break; 46*db926724SAndreas Gohr 47*db926724SAndreas Gohr // Localized time format 48*db926724SAndreas Gohr case '%X': 49*db926724SAndreas Gohr $date_type = IntlDateFormatter::NONE; 50*db926724SAndreas Gohr $time_type = IntlDateFormatter::MEDIUM; 51*db926724SAndreas Gohr break; 52*db926724SAndreas Gohr 53*db926724SAndreas Gohr default: 54*db926724SAndreas Gohr if (!isset($this->formats[$format])) { 55*db926724SAndreas Gohr throw new \RuntimeException("'$format' is not a supported locale placeholder"); 56*db926724SAndreas Gohr } 57*db926724SAndreas Gohr $pattern = $this->formats[$format]; 58*db926724SAndreas Gohr } 59*db926724SAndreas Gohr 60*db926724SAndreas Gohr // In October 1582, the Gregorian calendar replaced the Julian in much of Europe, and 61*db926724SAndreas Gohr // the 4th October was followed by the 15th October. 62*db926724SAndreas Gohr // ICU (including IntlDateFormattter) interprets and formats dates based on this cutover. 63*db926724SAndreas Gohr // Posix (including strftime) and timelib (including DateTimeImmutable) instead use 64*db926724SAndreas Gohr // a "proleptic Gregorian calendar" - they pretend the Gregorian calendar has existed forever. 65*db926724SAndreas Gohr // This leads to the same instants in time, as expressed in Unix time, having different representations 66*db926724SAndreas Gohr // in formatted strings. 67*db926724SAndreas Gohr // To adjust for this, a custom calendar can be supplied with a cutover date arbitrarily far in the past. 68*db926724SAndreas Gohr $calendar = IntlGregorianCalendar::createInstance(); 69*db926724SAndreas Gohr // NOTE: IntlGregorianCalendar::createInstance DOES NOT return an IntlGregorianCalendar instance when 70*db926724SAndreas Gohr // using a non-Gregorian locale (e.g. fa_IR)! In that case, setGregorianChange will not exist. 71*db926724SAndreas Gohr if (method_exists($calendar, 'setGregorianChange')) $calendar->setGregorianChange(PHP_INT_MIN); 72*db926724SAndreas Gohr 73*db926724SAndreas Gohr return (new IntlDateFormatter($this->locale, $date_type, $time_type, $tz, $calendar, $pattern))->format($timestamp); 74*db926724SAndreas Gohr } 75*db926724SAndreas Gohr 76*db926724SAndreas Gohr} 77