]> git.immae.eu Git - github/shaarli/Shaarli.git/commitdiff
Improve datetime display
authorArthurHoaro <arthur@hoa.ro>
Sat, 7 Jan 2017 13:30:42 +0000 (14:30 +0100)
committerArthurHoaro <arthur@hoa.ro>
Mon, 6 Mar 2017 20:11:12 +0000 (21:11 +0100)
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)

application/Utils.php
tests/UtilsTest.php
tests/languages/bootstrap.php [new file with mode: 0644]
tests/languages/de/UtilsDeTest.php [new file with mode: 0644]
tests/languages/en/UtilsEnTest.php [new file with mode: 0644]
tests/languages/fr/UtilsFrTest.php [new file with mode: 0644]
tpl/default/linklist.html
tpl/vintage/linklist.html

index 19fb7116f0535402119250855a6fe74f09b4a5c2..a936b09fa87710ef86df5ffd9f28a8584b9ac09c 100644 (file)
@@ -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);
+}
index b8f608b9ee409409eadbc4f8a27efa2231349435..e70cc1aef10197d66718149a0ded03f85cbec072 100644 (file)
@@ -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));
+    }
 }
diff --git a/tests/languages/bootstrap.php b/tests/languages/bootstrap.php
new file mode 100644 (file)
index 0000000..9560921
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+if (! empty('UT_LOCALE')) {
+    setlocale(LC_ALL, getenv('UT_LOCALE'));
+}
+
+require_once 'vendor/autoload.php';
+
diff --git a/tests/languages/de/UtilsDeTest.php b/tests/languages/de/UtilsDeTest.php
new file mode 100644 (file)
index 0000000..8a74038
--- /dev/null
@@ -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));
+    }
+}
diff --git a/tests/languages/en/UtilsEnTest.php b/tests/languages/en/UtilsEnTest.php
new file mode 100644 (file)
index 0000000..60bcb65
--- /dev/null
@@ -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));
+    }
+}
diff --git a/tests/languages/fr/UtilsFrTest.php b/tests/languages/fr/UtilsFrTest.php
new file mode 100644 (file)
index 0000000..890308d
--- /dev/null
@@ -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));
+    }
+}
index a9712704b387ba762f6850cb71e2395bb113d506..9bc3ba1a3c51e28d55d6ec74f8447f3fa40e8a70 100644 (file)
               <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}
index 5accc92fa4eed0367a187c3509d27e943ffd50fd..fc116667aac83dd12bb4d45bbfad8d5744cbb560 100644 (file)
                 <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