1010
1111/**
1212 * Timezone library
13+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1314 */
1415class Timezone implements TimezoneInterface
1516{
@@ -110,11 +111,21 @@ public function getConfigTimezone($scopeType = null, $scopeCode = null)
110111 */
111112 public function getDateFormat ($ type = \IntlDateFormatter::SHORT )
112113 {
113- return (new \IntlDateFormatter (
114+ $ pattern = (new \IntlDateFormatter (
114115 $ this ->_localeResolver ->getLocale (),
115116 $ type ,
116117 \IntlDateFormatter::NONE
117118 ))->getPattern ();
119+
120+ /**
121+ * This replacement is a workaround to prevent bugs in some third party libraries,
122+ * that works incorrectly with 'yyyy' value.
123+ * According to official doc of the ICU library
124+ * internally used in \Intl, 'yyyy' and 'y' formats are the same
125+ * @see http://userguide.icu-project.org/formatparse/datetime
126+ */
127+ $ pattern = str_replace ('yyyy ' , 'y ' , $ pattern );
128+ return $ pattern ;
118129 }
119130
120131 /**
@@ -151,9 +162,8 @@ public function getDateTimeFormat($type)
151162
152163 /**
153164 * {@inheritdoc}
154- * @SuppressWarnings(PHPMD.NPathComplexity)
155165 */
156- public function date ($ date = null , $ locale = null , $ useTimezone = true )
166+ public function date ($ date = null , $ locale = null , $ useTimezone = true , $ includeTime = true )
157167 {
158168 $ locale = $ locale ?: $ this ->_localeResolver ->getLocale ();
159169 $ timezone = $ useTimezone
@@ -164,17 +174,52 @@ public function date($date = null, $locale = null, $useTimezone = true)
164174 return new \DateTime ('now ' , new \DateTimeZone ($ timezone ));
165175 } elseif ($ date instanceof \DateTime) {
166176 return $ date ->setTimezone (new \DateTimeZone ($ timezone ));
177+ } elseif ($ date instanceof \DateTimeImmutable) {
178+ return new \DateTime ($ date ->format ('Y-m-d H:i:s ' ), $ date ->getTimezone ());
167179 } elseif (!is_numeric ($ date )) {
168- $ formatter = new \IntlDateFormatter (
169- $ locale ,
170- \IntlDateFormatter::SHORT ,
171- \IntlDateFormatter::NONE
172- );
173- $ date = $ formatter ->parse ($ date ) ?: (new \DateTime ($ date ))->getTimestamp ();
180+ $ date = $ this ->prepareDate ($ date , $ locale , $ timezone , $ includeTime );
174181 }
182+
175183 return (new \DateTime (null , new \DateTimeZone ($ timezone )))->setTimestamp ($ date );
176184 }
177185
186+ /**
187+ * Convert string date according to locale format
188+ *
189+ * @param string $date
190+ * @param string $locale
191+ * @param string $timezone
192+ * @param bool $includeTime
193+ * @return string
194+ */
195+ private function prepareDate ($ date , $ locale , $ timezone , $ includeTime )
196+ {
197+ $ timeType = $ includeTime ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE ;
198+ $ formatter = new \IntlDateFormatter (
199+ $ locale ,
200+ \IntlDateFormatter::SHORT ,
201+ $ timeType ,
202+ new \DateTimeZone ($ timezone )
203+ );
204+
205+ /**
206+ * IntlDateFormatter does not parse correctly date formats per some locales
207+ * It depends on ICU lib version used by intl extension
208+ * For locales like fr_FR, ar_KW parse date with hyphen as separator
209+ */
210+ if ($ includeTime ) {
211+ $ date = $ this ->appendTimeIfNeeded ($ date );
212+ }
213+ try {
214+ $ date = $ formatter ->parse ($ date ) ?: (new \DateTime ($ date ))->getTimestamp ();
215+ } catch (\Exception $ e ) {
216+ $ date = str_replace ('/ ' , '- ' , $ date );
217+ $ date = $ formatter ->parse ($ date ) ?: (new \DateTime ($ date ))->getTimestamp ();
218+ }
219+
220+ return $ date ;
221+ }
222+
178223 /**
179224 * {@inheritdoc}
180225 */
@@ -195,7 +240,7 @@ public function formatDate($date = null, $format = \IntlDateFormatter::SHORT, $s
195240 {
196241 $ formatTime = $ showTime ? $ format : \IntlDateFormatter::NONE ;
197242
198- if (!($ date instanceof \DateTime )) {
243+ if (!($ date instanceof \DateTimeInterface )) {
199244 $ date = new \DateTime ($ date );
200245 }
201246
@@ -232,12 +277,8 @@ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null)
232277 $ toTimeStamp += 86400 ;
233278 }
234279
235- $ result = false ;
236- if (!$ this ->_dateTime ->isEmptyDate ($ dateFrom ) && $ scopeTimeStamp < $ fromTimeStamp ) {
237- } elseif (!$ this ->_dateTime ->isEmptyDate ($ dateTo ) && $ scopeTimeStamp > $ toTimeStamp ) {
238- } else {
239- $ result = true ;
240- }
280+ $ result = !(!$ this ->_dateTime ->isEmptyDate ($ dateFrom ) && $ scopeTimeStamp < $ fromTimeStamp )
281+ && !(!$ this ->_dateTime ->isEmptyDate ($ dateTo ) && $ scopeTimeStamp > $ toTimeStamp );
241282 return $ result ;
242283 }
243284
@@ -258,7 +299,7 @@ public function formatDateTime(
258299 $ timezone = null ,
259300 $ pattern = null
260301 ) {
261- if (!($ date instanceof \DateTime )) {
302+ if (!($ date instanceof \DateTimeInterface )) {
262303 $ date = new \DateTime ($ date );
263304 }
264305
@@ -294,8 +335,12 @@ public function formatDateTime(
294335 */
295336 public function convertConfigTimeToUtc ($ date , $ format = 'Y-m-d H:i:s ' )
296337 {
297- if (!($ date instanceof \DateTime)) {
298- $ date = new \DateTime ($ date , new \DateTimeZone ($ this ->getConfigTimezone ()));
338+ if (!($ date instanceof \DateTimeInterface)) {
339+ if ($ date instanceof \DateTimeImmutable) {
340+ $ date = new \DateTime ($ date ->format ('Y-m-d H:i:s ' ), new \DateTimeZone ($ this ->getConfigTimezone ()));
341+ } else {
342+ $ date = new \DateTime ($ date , new \DateTimeZone ($ this ->getConfigTimezone ()));
343+ }
299344 } else {
300345 if ($ date ->getTimezone ()->getName () !== $ this ->getConfigTimezone ()) {
301346 throw new LocalizedException (
@@ -308,4 +353,18 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s')
308353
309354 return $ date ->format ($ format );
310355 }
356+
357+ /**
358+ * Add time in case if no time provided but required
359+ *
360+ * @param string $date
361+ * @return string
362+ */
363+ private function appendTimeIfNeeded ($ date )
364+ {
365+ if (!preg_match ('/\d{1,2}:\d{2}/ ' , $ date )) {
366+ $ date .= " 00:00 " ;
367+ }
368+ return $ date ;
369+ }
311370}
0 commit comments