Browse Source

Fix localized output for datetime

mscherer 3 years ago
parent
commit
92ef27a06b
4 changed files with 77 additions and 36 deletions
  1. 1 1
      config/bootstrap.php
  2. 59 20
      src/Utility/FrozenTime.php
  3. 1 1
      src/Utility/Mime.php
  4. 16 14
      tests/TestCase/Utility/FrozenTimeTest.php

+ 1 - 1
config/bootstrap.php

@@ -30,7 +30,7 @@ if (!defined('FORMAT_NICE_YMDHMS')) {
 	define('FORMAT_NICE_HM', 'H:i');
 	define('FORMAT_NICE_HM', 'H:i');
 	define('FORMAT_NICE_HMS', 'H:i:s');
 	define('FORMAT_NICE_HMS', 'H:i:s');
 
 
-	// localDate strings
+	// localDate strings DEPRECATED PHP 8.1+
 	define('FORMAT_LOCAL_WA_YMDHMS', '%a, %d.%m.%Y, %H:%M:%S');
 	define('FORMAT_LOCAL_WA_YMDHMS', '%a, %d.%m.%Y, %H:%M:%S');
 	define('FORMAT_LOCAL_WF_YMDHMS', '%A, %d.%m.%Y, %H:%M:%S');
 	define('FORMAT_LOCAL_WF_YMDHMS', '%A, %d.%m.%Y, %H:%M:%S');
 	define('FORMAT_LOCAL_WA_YMDHM', '%a, %d.%m.%Y, %H:%M');
 	define('FORMAT_LOCAL_WA_YMDHM', '%a, %d.%m.%Y, %H:%M');

+ 59 - 20
src/Utility/FrozenTime.php

@@ -10,6 +10,8 @@ use Cake\I18n\FrozenTime as CakeFrozenTime;
 use DateInterval;
 use DateInterval;
 use DateTime;
 use DateTime;
 use DateTimeInterface;
 use DateTimeInterface;
+use DateTimeZone;
+use IntlDateFormatter;
 
 
 /**
 /**
  * Extend CakeTime with a few important improvements:
  * Extend CakeTime with a few important improvements:
@@ -411,8 +413,13 @@ class FrozenTime extends CakeFrozenTime {
 	 * @param array<string, mixed> $options
 	 * @param array<string, mixed> $options
 	 * @return string
 	 * @return string
 	 */
 	 */
-	public static function localDate($dateString, $format = null, array $options = []) {
-		$defaults = ['default' => '-----', 'timezone' => null];
+	public static function localDate(?string $dateString, ?string $format = null, array $options = []) {
+		$defaults = [
+			'default' => '-----',
+			'timezone' => null,
+			'language' => 'en',
+			'oclock' => null,
+		];
 		$options += $defaults;
 		$options += $defaults;
 
 
 		if ($options['timezone'] === null && strlen($dateString) === 10) {
 		if ($options['timezone'] === null && strlen($dateString) === 10) {
@@ -425,31 +432,19 @@ class FrozenTime extends CakeFrozenTime {
 			$options['timezone'] = static::safeCreateDateTimeZone($options['timezone']);
 			$options['timezone'] = static::safeCreateDateTimeZone($options['timezone']);
 		}
 		}
 		$date = new CakeFrozenTime($dateString, $options['timezone']);
 		$date = new CakeFrozenTime($dateString, $options['timezone']);
-		$date = $date->format('U');
-
-		if ($date <= 0) {
-			return $options['default'];
-		}
-
 		if ($format === null) {
 		if ($format === null) {
 			if (is_int($dateString) || strpos($dateString, ' ') !== false) {
 			if (is_int($dateString) || strpos($dateString, ' ') !== false) {
-				$format = FORMAT_LOCAL_YMDHM;
+				$format = 'd.m.Y, H:i';
 			} else {
 			} else {
-				$format = FORMAT_LOCAL_YMD;
+				$format = 'd.m.Y';
 			}
 			}
 		}
 		}
 
 
-		$date = static::_strftime($format, (int)$date);
-
-		if (!empty($options['oclock'])) {
-			switch ($format) {
-				case FORMAT_LOCAL_YMDHM:
-				case FORMAT_LOCAL_YMDHMS:
-				case FORMAT_LOCAL_HM:
-				case FORMAT_LOCAL_HMS:
-					$date .= ' ' . __d('tools', 'o\'clock');
+		$date = static::formatLocalized($date, $format, $options['language']);
 
 
-					break;
+		if ($options['oclock']) {
+			if (strpos($format, 'H:i') !== false) {
+				$date .= ' ' . __d('tools', 'o\'clock');
 			}
 			}
 		}
 		}
 
 
@@ -457,6 +452,50 @@ class FrozenTime extends CakeFrozenTime {
 	}
 	}
 
 
 	/**
 	/**
+	 * @param \DateTimeInterface $dt
+	 * @param string $format
+	 * @param string $language
+	 *
+	 * @return string
+	 */
+	public static function formatLocalized(DateTimeInterface $dt, string $format, string $language = 'en'): string {
+		$curTz = $dt->getTimezone();
+		if ($curTz->getName() === 'Z') {
+			// INTL don't know Z
+			$curTz = new DateTimeZone('UTC');
+		}
+
+		$formatPattern = strtr($format, [
+			'D' => '{#1}',
+			'l' => '{#2}',
+			'M' => '{#3}',
+			'F' => '{#4}',
+		]);
+		$strDate = $dt->format($formatPattern);
+		$regEx = '~\{#\d\}~';
+		while (preg_match($regEx, $strDate, $match)) {
+			$IntlFormat = strtr($match[0], [
+				'{#1}' => 'E',
+				'{#2}' => 'EEEE',
+				'{#3}' => 'MMM',
+				'{#4}' => 'MMMM',
+			]);
+			$fmt = datefmt_create(
+				$language,
+				IntlDateFormatter::FULL,
+				IntlDateFormatter::FULL,
+				$curTz,
+				IntlDateFormatter::GREGORIAN,
+				$IntlFormat,
+			);
+			$replace = $fmt ? datefmt_format($fmt, $dt) : '???';
+			$strDate = str_replace($match[0], $replace, $strDate);
+		}
+
+		return $strDate;
+	}
+
+	/**
 	 * Multibyte wrapper for strftime.
 	 * Multibyte wrapper for strftime.
 	 *
 	 *
 	 * Handles utf8_encoding the result of strftime when necessary.
 	 * Handles utf8_encoding the result of strftime when necessary.

+ 1 - 1
src/Utility/Mime.php

@@ -804,7 +804,7 @@ class Mime extends Response {
 			// @codingStandardsIgnoreStart
 			// @codingStandardsIgnoreStart
 			$headers = @get_headers($file);
 			$headers = @get_headers($file);
 			// @codingStandardsIgnoreEnd
 			// @codingStandardsIgnoreEnd
-			if (!preg_match("|\b200\b|", $headers[0])) {
+			if (!$headers || !preg_match("|\b200\b|", $headers[0])) {
 				return '';
 				return '';
 			}
 			}
 			foreach ($headers as $header) {
 			foreach ($headers as $header) {

+ 16 - 14
tests/TestCase/Utility/FrozenTimeTest.php

@@ -3,7 +3,6 @@
 namespace Tools\Test\TestCase\Utility;
 namespace Tools\Test\TestCase\Utility;
 
 
 use Cake\Core\Configure;
 use Cake\Core\Configure;
-use Cake\Error\Debugger;
 use DateTime;
 use DateTime;
 use Shim\TestSuite\TestCase;
 use Shim\TestSuite\TestCase;
 use Tools\Utility\FrozenTime;
 use Tools\Utility\FrozenTime;
@@ -162,8 +161,13 @@ class FrozenTimeTest extends TestCase {
 
 
 		$ret = $this->Time->niceDate('2009-12-01');
 		$ret = $this->Time->niceDate('2009-12-01');
 		$this->assertEquals('01.12.2009', $ret);
 		$this->assertEquals('01.12.2009', $ret);
+	}
 
 
-		$ret = $this->Time->localDate('2009-12-01');
+	/**
+	 * @return void
+	 */
+	public function testFormatLocalized() {
+		$ret = $this->Time->formatLocalized(new FrozenTime('2009-12-01'), 'd.m.Y');
 		$this->assertEquals('01.12.2009', $ret);
 		$this->assertEquals('01.12.2009', $ret);
 	}
 	}
 
 
@@ -209,30 +213,28 @@ class FrozenTimeTest extends TestCase {
 	 * @return void
 	 * @return void
 	 */
 	 */
 	public function testLocalDate() {
 	public function testLocalDate() {
-		$this->skipIf(true, '//Doesnt work on GithubActions CI');
-
-		$res = setlocale(LC_TIME, ['de_DE.UTF-8', 'deu_deu']);
-		$this->assertTrue(!empty($res), 'Result: ' . Debugger::exportVar($res, true));
+		//$this->skipIf(true, '//Doesnt work on GithubActions CI');
+		//$res = setlocale(LC_TIME, ['de_DE.UTF-8', 'deu_deu']);
+		//$this->assertTrue(!empty($res), 'Result: ' . Debugger::exportVar($res, true));
 
 
 		$values = [
 		$values = [
-			['2009-12-01 00:00:00', FORMAT_LOCAL_YMD, '01.12.2009'],
-			['2009-12-01 00:00:00', FORMAT_LOCAL_M_FULL, 'Dezember'],
+			['2009-12-01 00:00:00', 'd.m.Y', '01.12.2009'],
+			['2009-12-01 00:00:00', 'M', 'Dez.'],
 		];
 		];
 		foreach ($values as $v) {
 		foreach ($values as $v) {
-			$ret = $this->Time->localDate($v[0], $v[1]);
-			//$this->debug($ret);
+			$ret = $this->Time->localDate($v[0], $v[1], ['language' => 'de']);
 			$this->assertEquals($v[2], $ret);
 			$this->assertEquals($v[2], $ret);
 		}
 		}
 
 
 		$date = '2009-12-01 00:00:00';
 		$date = '2009-12-01 00:00:00';
-		$format = FORMAT_LOCAL_YMD;
-		$result = $this->Time->localDate($date, $format, ['oclock' => true]);
+		$format = 'd.m.Y';
+		$result = $this->Time->localDate($date, $format, ['language' => 'de', 'oclock' => true]);
 		$expected = '01.12.2009';
 		$expected = '01.12.2009';
 		$this->assertEquals($expected, $result);
 		$this->assertEquals($expected, $result);
 
 
 		$date = '2009-12-01 00:00:00';
 		$date = '2009-12-01 00:00:00';
-		$format = FORMAT_LOCAL_YMDHM;
-		$result = $this->Time->localDate($date, $format, ['oclock' => true]);
+		$format = 'd.m.Y, H:i';
+		$result = $this->Time->localDate($date, $format, ['language' => 'de', 'oclock' => true]);
 		$expected = '01.12.2009, 00:00 ' . __d('tools', 'o\'clock');
 		$expected = '01.12.2009, 00:00 ' . __d('tools', 'o\'clock');
 		$this->assertEquals($expected, $result);
 		$this->assertEquals($expected, $result);
 	}
 	}