@@ -30,6 +30,11 @@ class Timezone implements TimezoneInterface
3030 \IntlDateFormatter::SHORT ,
3131 ];
3232
33+ /**
34+ * @var array
35+ */
36+ private $ dateFormatterCache = [];
37+
3338 /**
3439 * @var string
3540 */
@@ -174,16 +179,9 @@ public function date($date = null, $locale = null, $useTimezone = true, $include
174179 case ($ date instanceof \DateTimeImmutable):
175180 return new \DateTime ($ date ->format ('Y-m-d H:i:s ' ), $ date ->getTimezone ());
176181 case (!is_numeric ($ date )):
177- $ timeType = $ includeTime ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE ;
178- $ formatter = new \IntlDateFormatter (
179- $ locale ,
180- \IntlDateFormatter::MEDIUM ,
181- $ timeType ,
182- new \DateTimeZone ($ timezone )
183- );
184-
185182 $ date = $ this ->appendTimeIfNeeded ($ date , $ includeTime , $ timezone , $ locale );
186- $ date = $ formatter ->parse ($ date ) ?: (new \DateTime ($ date ))->getTimestamp ();
183+ $ date = $ this ->parseLocaleDate ($ date , $ locale , $ timezone , $ includeTime )
184+ ?: (new \DateTime ($ date ))->getTimestamp ();
187185 break ;
188186 }
189187
@@ -343,33 +341,77 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s')
343341 private function appendTimeIfNeeded ($ date , $ includeTime , $ timezone , $ locale )
344342 {
345343 if ($ includeTime && !preg_match ('/\d{1}:\d{2}/ ' , $ date )) {
346-
347- $ formatterWithoutHour = new \IntlDateFormatter (
348- $ locale ,
349- \IntlDateFormatter::MEDIUM ,
350- \IntlDateFormatter::NONE ,
351- new \DateTimeZone ($ timezone )
352- );
353- $ convertedDate = $ formatterWithoutHour ->parse ($ date );
354-
344+ $ convertedDate = $ this ->parseLocaleDate ($ date , $ locale , $ timezone , false );
355345 if (!$ convertedDate ) {
356346 throw new LocalizedException (
357347 new Phrase (
358348 'Could not append time to DateTime '
359349 )
360350 );
361-
362351 }
363352
364- $ formatterWithHour = new \ IntlDateFormatter (
353+ $ formatterWithHour = $ this -> getDateFormatter (
365354 $ locale ,
355+ $ timezone ,
366356 \IntlDateFormatter::MEDIUM ,
367- \IntlDateFormatter::SHORT ,
368- new \DateTimeZone ($ timezone )
357+ \IntlDateFormatter::SHORT
369358 );
370-
371359 $ date = $ formatterWithHour ->format ($ convertedDate );
372360 }
373361 return $ date ;
374362 }
363+
364+ /**
365+ * Parse date by locale format through IntlDateFormatter
366+ *
367+ * @param string $date
368+ * @param string $locale
369+ * @param string $timeZone
370+ * @param bool $includeTime
371+ * @return int|null Timestamp of date
372+ */
373+ private function parseLocaleDate (string $ date , string $ locale , string $ timeZone , bool $ includeTime ): ?int
374+ {
375+ $ allowedStyles = [\IntlDateFormatter::MEDIUM , \IntlDateFormatter::SHORT ];
376+ $ timeStyle = $ includeTime ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE ;
377+
378+ /**
379+ * Try to parse date with different styles
380+ */
381+ foreach ($ allowedStyles as $ style ) {
382+ $ formatter = $ this ->getDateFormatter ($ locale , $ timeZone , $ style , $ timeStyle );
383+ $ timeStamp = $ formatter ->parse ($ date );
384+ if ($ timeStamp ) {
385+ return $ timeStamp ;
386+ }
387+ }
388+
389+ return null ;
390+ }
391+
392+ /**
393+ * Get date formatter for locale
394+ *
395+ * @param string $locale
396+ * @param string $timeZone
397+ * @param int $style
398+ * @param int $timeStyle
399+ * @return \IntlDateFormatter
400+ */
401+ private function getDateFormatter (string $ locale , string $ timeZone , int $ style , int $ timeStyle ): \IntlDateFormatter
402+ {
403+ $ cacheKey = "{$ locale }_ {$ timeZone }_ {$ style }_ {$ timeStyle }" ;
404+ if (isset ($ this ->dateFormatterCache [$ cacheKey ])) {
405+ return $ this ->dateFormatterCache [$ cacheKey ];
406+ }
407+
408+ $ this ->dateFormatterCache [$ cacheKey ] = new \IntlDateFormatter (
409+ $ locale ,
410+ $ style ,
411+ $ timeStyle ,
412+ new \DateTimeZone ($ timeZone )
413+ );
414+
415+ return $ this ->dateFormatterCache [$ cacheKey ];
416+ }
375417}
0 commit comments