Explorar el Código

Improve datetime display

Use php-intl extension to display datetimes a bit more nicely, depending on the locale.

What changes:

  * the day is no longer displayed
  * day number and month are ordered according to the locale
  * the timezone is more readable (UTC+1 instead of CET)
ArthurHoaro hace 7 años
padre
commit
52b503105d

+ 52 - 20
application/Utils.php

@@ -225,44 +225,46 @@ function autoLocale($headerLocale)
             $encodings = ['utf8', 'UTF-8'];
             if (!empty($matches[2])) {
                 $second = [strtoupper($matches[2]), strtolower($matches[2])];
-                $attempts = arrays_combination([$first, $separators, $second, ['.'], $encodings]);
+                $attempts = cartesian_product_generator([$first, $separators, $second, ['.'], $encodings]);
             } else {
-                $attempts = arrays_combination([$first, $separators, $first, ['.'], $encodings]);
+                $attempts = cartesian_product_generator([$first, $separators, $first, ['.'], $encodings]);
             }
         }
     }
-    setlocale(LC_ALL, $attempts);
+    setlocale(LC_ALL, implode('implode', iterator_to_array($attempts)));
 }
 
 /**
- * Combine multiple arrays of string to get all possible strings.
- * The order is important because this doesn't shuffle the entries.
+ * Build a Generator object representing the cartesian product from given $items.
  *
  * Example:
  *   [['a'], ['b', 'c']]
  * will generate:
- *   - ab
- *   - ac
- *
- * TODO PHP 5.6: use the `...` operator instead of an array of array.
+ *   [
+ *      ['a', 'b'],
+ *      ['a', 'c'],
+ *   ]
  *
  * @param array $items array of array of string
  *
- * @return array Combined string from the input array.
+ * @return Generator representing the cartesian product of given array.
+ *
+ * @see https://en.wikipedia.org/wiki/Cartesian_product
  */
-function arrays_combination($items)
+function cartesian_product_generator($items)
 {
-    $out = [''];
-    foreach ($items as $item) {
-        $add = [];
-        foreach ($item as $element) {
-            foreach ($out as $key => $existingEntry) {
-                $add[] = $existingEntry . $element;
-            }
+    if (empty($items)) {
+        yield [];
+    }
+    $subArray = array_pop($items);
+    if (empty($subArray)) {
+        return;
+    }
+    foreach (cartesian_product_generator($items) as $item) {
+        foreach ($subArray as $value) {
+            yield $item + [count($item) => $value];
         }
-        $out = $add;
     }
-    return $out;
 }
 
 /**
@@ -303,3 +305,33 @@ function normalize_spaces($string)
 {
     return preg_replace('/\s{2,}/', ' ', trim($string));
 }
+
+/**
+ * Format the date according to the locale.
+ *
+ * Requires php-intl to display international datetimes,
+ * otherwise default format '%c' will be returned.
+ *
+ * @param DateTime $date to format.
+ * @param bool     $intl Use international format if true.
+ *
+ * @return bool|string Formatted date, or false if the input is invalid.
+ */
+function format_date($date, $intl = true)
+{
+    if (! $date instanceof DateTime) {
+        return false;
+    }
+
+    if (! $intl || ! class_exists('IntlDateFormatter')) {
+        return strftime('%c', $date->getTimestamp());
+    }
+
+    $formatter = new IntlDateFormatter(
+        setlocale(LC_TIME, 0),
+        IntlDateFormatter::LONG,
+        IntlDateFormatter::LONG
+    );
+
+    return $formatter->format($date);
+}

+ 35 - 11
tests/UtilsTest.php

@@ -23,7 +23,12 @@ class UtilsTest extends PHPUnit_Framework_TestCase
 
     // Expected log date format
     protected static $dateFormat = 'Y/m/d H:i:s';
-    
+
+    /**
+     * @var string Save the current timezone.
+     */
+    protected static $defaultTimeZone;
+
 
     /**
      * Assign reference data
@@ -31,6 +36,17 @@ class UtilsTest extends PHPUnit_Framework_TestCase
     public static function setUpBeforeClass()
     {
         self::$sidHashes = ReferenceSessionIdHashes::getHashes();
+        self::$defaultTimeZone = date_default_timezone_get();
+        // Timezone without DST for test consistency
+        date_default_timezone_set('Africa/Nairobi');
+    }
+
+    /**
+     * Reset the timezone
+     */
+    public static function tearDownAfterClass()
+    {
+        date_default_timezone_set(self::$defaultTimeZone);
     }
 
     /**
@@ -286,20 +302,28 @@ class UtilsTest extends PHPUnit_Framework_TestCase
     /**
      * Test arrays_combine
      */
-    public function testArraysCombination()
+    public function testCartesianProductGenerator()
     {
         $arr = [['ab', 'cd'], ['ef', 'gh'], ['ij', 'kl'], ['m']];
         $expected = [
-            'abefijm',
-            'cdefijm',
-            'abghijm',
-            'cdghijm',
-            'abefklm',
-            'cdefklm',
-            'abghklm',
-            'cdghklm',
+            ['ab', 'ef', 'ij', 'm'],
+            ['ab', 'ef', 'kl', 'm'],
+            ['ab', 'gh', 'ij', 'm'],
+            ['ab', 'gh', 'kl', 'm'],
+            ['cd', 'ef', 'ij', 'm'],
+            ['cd', 'ef', 'kl', 'm'],
+            ['cd', 'gh', 'ij', 'm'],
+            ['cd', 'gh', 'kl', 'm'],
         ];
-        $this->assertEquals($expected, arrays_combination($arr));
+        $this->assertEquals($expected, iterator_to_array(cartesian_product_generator($arr)));
     }
 
+    /**
+     * Test date_format() with invalid parameter.
+     */
+    public function testDateFormatInvalid()
+    {
+        $this->assertFalse(format_date([]));
+        $this->assertFalse(format_date(null));
+    }
 }

+ 7 - 0
tests/languages/bootstrap.php

@@ -0,0 +1,7 @@
+<?php
+if (! empty('UT_LOCALE')) {
+    setlocale(LC_ALL, getenv('UT_LOCALE'));
+}
+
+require_once 'vendor/autoload.php';
+

+ 25 - 0
tests/languages/de/UtilsDeTest.php

@@ -0,0 +1,25 @@
+<?php
+
+require_once 'tests/UtilsTest.php';
+
+
+class UtilsDeTest extends UtilsTest
+{
+    /**
+     * Test date_format().
+     */
+    public function testDateFormat()
+    {
+        $date = DateTime::createFromFormat('Ymd_His', '20170101_101112');
+        $this->assertRegExp('/1. Januar 2017 (um )?10:11:12 GMT\+0?3(:00)?/', format_date($date, true));
+    }
+
+    /**
+     * Test date_format() using builtin PHP function strftime.
+     */
+    public function testDateFormatDefault()
+    {
+        $date = DateTime::createFromFormat('Ymd_His', '20170101_101112');
+        $this->assertEquals('So 01 Jan 2017 10:11:12 EAT', format_date($date, false));
+    }
+}

+ 25 - 0
tests/languages/en/UtilsEnTest.php

@@ -0,0 +1,25 @@
+<?php
+
+require_once 'tests/UtilsTest.php';
+
+
+class UtilsEnTest extends UtilsTest
+{
+    /**
+     * Test date_format().
+     */
+    public function testDateFormat()
+    {
+        $date = DateTime::createFromFormat('Ymd_His', '20170101_101112');
+        $this->assertRegExp('/January 1, 2017 (at )?10:11:12 AM GMT\+0?3(:00)?/', format_date($date, true));
+    }
+
+    /**
+     * Test date_format() using builtin PHP function strftime.
+     */
+    public function testDateFormatDefault()
+    {
+        $date = DateTime::createFromFormat('Ymd_His', '20170101_101112');
+        $this->assertEquals('Sun 01 Jan 2017 10:11:12 AM EAT', format_date($date, false));
+    }
+}

+ 25 - 0
tests/languages/fr/UtilsFrTest.php

@@ -0,0 +1,25 @@
+<?php
+
+require_once 'tests/UtilsTest.php';
+
+
+class UtilsFrTest extends UtilsTest
+{
+    /**
+     * Test date_format().
+     */
+    public function testDateFormat()
+    {
+        $date = DateTime::createFromFormat('Ymd_His', '20170101_101112');
+        $this->assertRegExp('/1 janvier 2017 (à )?10:11:12 UTC\+0?3(:00)?/', format_date($date));
+    }
+
+    /**
+     * Test date_format() using builtin PHP function strftime.
+     */
+    public function testDateFormatDefault()
+    {
+        $date = DateTime::createFromFormat('Ymd_His', '20170101_101112');
+        $this->assertEquals('dim. 01 janv. 2017 10:11:12 EAT', format_date($date, false));
+    }
+}

+ 3 - 2
tpl/default/linklist.html

@@ -171,10 +171,11 @@
               <div class="linklist-item-infos-dateblock pure-u-lg-3-8 pure-u-1">
                 <a href="?{$value.shorturl}" title="{'Permalink'|t}">
                   {if="!$hide_timestamps || isLoggedIn()"}
-                    {$updated=$value.updated_timestamp ? 'Edited: '. strftime('%c', $value.updated_timestamp) : 'Permalink'}
+                    {$updated=$value.updated_timestamp ? 'Edited: '. format_date($value.updated) : 'Permalink'}
                     <span class="linkdate" title="{$updated}">
                       <i class="fa fa-clock-o"></i>
-                      {function="strftime('%c', $value.timestamp)"}{if="$value.updated_timestamp"}*{/if}
+                      {$value.created|format_date}
+                      {if="$value.updated_timestamp"}*{/if}
                       &middot;
                     </span>
                   {/if}

+ 2 - 2
tpl/vintage/linklist.html

@@ -99,11 +99,11 @@
                 <br>
                 {if="$value.description"}<div class="linkdescription">{$value.description}</div>{/if}
                 {if="!$hide_timestamps || isLoggedIn()"}
-                    {$updated=$value.updated_timestamp ? 'Edited: '. strftime('%c', $value.updated_timestamp) : 'Permalink'}
+                    {$updated=$value.updated_timestamp ? 'Edited: '. format_date($value.updated) : 'Permalink'}
                     <span class="linkdate" title="Permalink">
                         <a href="?{$value.shorturl}">
                             <span title="{$updated}">
-                                {function="strftime('%c', $value.timestamp)"}
+                                {$value.created|format_date}
                                 {if="$value.updated_timestamp"}*{/if}
                             </span>
                             - permalink