Skip to content
Permalink
Browse files

feat(i18n): output date in locale string

Support formatting a date representation in locale string (eg. Dutch)

fixes #5077 #9858 #12245
  • Loading branch information
jeabakker committed Feb 7, 2019
1 parent db760e2 commit c2ca5da28e6d9e2af70bb82f812d96db128c55ab
@@ -0,0 +1,105 @@
<?php
namespace Elgg\I18n;
use \DateTime as PHPDateTime;
/**
* Extension of the DateTime class to support formating a date using the locale
*
* @since 3.0
*/
class DateTime extends PHPDateTime {
/**
* Convert a date format to a strftime format
*
* Timezone conversion is done for unix. Windows users must exchange %z and %Z.
*
* Unsupported date formats : n, t, L, B, u, e, I, P, Z, c, r
* Unsupported strftime formats : %U, %W, %C, %g, %r, %R, %T, %X, %c, %D, %F, %x
*
* @param string $dateFormat a date format
* @return false|string
* @see https://secure.php.net/manual/en/function.strftime.php#96424
*/
protected function dateFormatToStrftime(string $dateFormat) {
if (preg_match('/(?<!\\|%)[ntLBueIPZcr]/', $dateFormat)) {
// unsupported characters found
return false;
}
$caracs = [
// Day - no strf eq : S
'd' => '%d', 'D' => '%a', 'j' => '%e', 'l' => '%A', 'N' => '%u', 'w' => '%w', 'z' => '%j',
// Week - no date eq : %U, %W
'W' => '%V',
// Month - no strf eq : n, t
'F' => '%B', 'm' => '%m', 'M' => '%b',
// Year - no strf eq : L; no date eq : %C, %g
'o' => '%G', 'Y' => '%Y', 'y' => '%y',
// Time - no strf eq : B, G, u; no date eq : %r, %R, %T, %X
'a' => '%P', 'A' => '%p', 'g' => '%l', 'h' => '%I', 'H' => '%H', 'i' => '%M', 's' => '%S',
// Timezone - no strf eq : e, I, P, Z
'O' => '%z', 'T' => '%Z',
// Full Date / Time - no strf eq : c, r; no date eq : %c, %D, %F, %x
'U' => '%s',
// less supported replacements
// Day
'S' => '',
// Time
'G' => '%k',
];
return strtr((string) $dateFormat, $caracs);
}
/**
* Format the date using strftime() which supports locale output
*
* @param string $format output format, supports date() formatting
* @param string $locale the output locale, defaults to current language
*
* @return string
*/
public function formatLocale($format, $locale = null) {
// convert date() format to strftime() format
$correct_format = $this->dateFormatToStrftime($format);
if ($correct_format === false) {
elgg_log("Unable to convert date format: '{$format}', using non-locale version", 'INFO');
return $this->format($format);
}
if (isset($locale)) {
try {
$new_locale = Locale::parse($locale);
$locale = (string) $new_locale;
} catch (InvalidLocaleException $e) {
$locale = null;
}
}
if (!isset($locale)) {
$locale = get_current_language();
}
// switch locale
$current_locale = setlocale(LC_TIME, 0);
@setlocale(LC_TIME, $locale);
$result = strftime($correct_format, $this->getTimestamp());
// restore locale
setlocale(LC_TIME, $current_locale);
if ($result === false) {
elgg_log("Unable to generate locale representation for format: '{$correct_format}', using non-locale version", 'INFO');
return $this->format($format);
}
return $result;
}
}
@@ -2,8 +2,9 @@
namespace Elgg;
use Elgg\I18n\DateTime as ElggDateTime;
use DataFormatException;
use DateTime;
use DateTime as PHPDateTime;
use DateTimeZone;
use Exception;
@@ -56,7 +57,7 @@ public static function getArray() {
/**
* Returns timestamp value of the time representation
*
* @param DateTime|string|int $time Time
* @param \DateTime|\Elgg\I18n\DateTime|string|int $time Time
*
* @return int
* @throws DataFormatException
@@ -68,20 +69,22 @@ public static function normalizeTimestamp($time) {
/**
* Returns DateTime object based on time representation
*
* @param DateTime|string|int $time Time
* @param \DateTime|\Elgg\I18n\DateTime|string|int $time Time
*
* @return DateTime
* @return \Elgg\I18n\DateTime
* @throws DataFormatException
*/
public static function normalizeTime($time) {
try {
if ($time instanceof DateTime) {
if ($time instanceof ElggDateTime) {
$dt = $time;
} elseif ($time instanceof PHPDateTime) {
$dt = new ElggDateTime($time->format(PHPDateTime::RFC3339_EXTENDED));
} else if (is_numeric($time)) {
$dt = new DateTime(null, new DateTimeZone('UTC'));
$dt = new ElggDateTime(null, new DateTimeZone('UTC'));
$dt->setTimestamp((int) $time);
} else {
$dt = new DateTime($time);
$dt = new ElggDateTime($time);
}
} catch (Exception $e) {
throw new DataFormatException($e->getMessage());
@@ -0,0 +1,59 @@
<?php
namespace Elgg\I18n;
use Elgg\UnitTestCase;
/**
* @group UnitTests
*/
class DateTimeUnitTest extends UnitTestCase {
/**
* {@inheritDoc}
* @see \Elgg\BaseTestCase::up()
*/
public function up() {
}
/**
* {@inheritDoc}
* @see \Elgg\BaseTestCase::down()
*/
public function down() {
}
/**
* @dataProvider formatLocaleProvider
*/
public function testFormatLocale($time, $date_format, $strftime_format, $locale) {
$date = new DateTime($time);
// make expected output
$current_locale = setlocale(LC_TIME, 0);
setlocale(LC_TIME, $locale);
$expected = strftime($strftime_format, $date->getTimestamp());
setlocale(LC_TIME, $current_locale);
$this->assertEquals($expected, $date->formatLocale($date_format, $locale));
}
public function formatLocaleProvider() {
return [
['midnight', 'l d, F Y H:i:s', '%A %d, %B %Y %H:%M:%S', 'en'],
['+2 days', 'l d, F Y H:i:s', '%A %d, %B %Y %H:%M:%S', 'nl'],
['tomorrow', 'l', '%A', 'nl'],
['January 9, 2018 12:00', 'l d, F Y H:i:s', '%A %d, %B %Y %H:%M:%S', 'en'],
['January 9, 2018 12:00', 'l d, F Y H:i:s', '%A %d, %B %Y %H:%M:%S', 'nl'],
['January 9, 2018 12:00', 'F', '%B', 'nl'],
[1515496794, 'l d, F Y H:i:s', '%A %d, %B %Y %H:%M:%S', 'en'],
[1515496794, 'l d, F Y H:i:s', '%A %d, %B %Y %H:%M:%S', 'nl'],
[1515496794, 'l, F', '%A, %B', 'nl'],
];
}
}
@@ -2,7 +2,8 @@
namespace Elgg;
use DateTime;
use DateTime as PHPDateTime;
use Elgg\I18n\DateTime as ElggDateTime;
/**
* @group Values
@@ -24,7 +25,7 @@ public function testCanNormalizeTime($time) {
$dt = Values::normalizeTime($time);
$this->assertInstanceOf(\DateTime::class, $dt);
$this->assertInstanceOf(ElggDateTime::class, $dt);
$this->assertEquals($dt->getTimestamp(), Values::normalizeTimestamp($time));
}
@@ -33,7 +34,8 @@ public function timeProvider() {
return [
['January 9, 2018 12:00'],
[1515496794],
[new DateTime('+2 days')],
[new PHPDateTime('+2 days')],
[new ElggDateTime('-2 days')],
];
}
@@ -56,4 +58,4 @@ public function emptyProvider() {
[new \stdClass(), false],
];
}
}
}
@@ -2,6 +2,8 @@
namespace Elgg\Views;
use Elgg\I18n\DateTime;
/**
* @group ViewRendering
* @group ViewsService
@@ -12,7 +14,7 @@ class DateOutputTest extends ViewRenderingTestCase {
public function up() {
parent::up();
$this->date = new \DateTime();
$this->date = new DateTime();
$this->format = 'Y-m-d H:i';
}
@@ -37,7 +39,7 @@ public function testCanRenderDate() {
$output = elgg_format_element('time', [
'datetime' => $this->date->format('c'),
], $this->date->format($this->format));
], $this->date->formatLocale($this->format));
$this->assertViewOutput($output, 'output/date', [
'value' => $this->date,
@@ -46,18 +48,15 @@ public function testCanRenderDate() {
}
public function testCanRenderTime() {
$format = 'g:ia';
$output = elgg_format_element('time', [
'datetime' => $this->date->format('c'),
], $this->date->format($format));
], $this->date->formatLocale($format));
$this->assertViewOutput($output, 'output/time', [
'value' => $this->date,
'format' => $format,
]);
}
}
@@ -17,7 +17,10 @@
$ts = $cron_service->getLog('completion', $period);
if ($ts) {
$row[] = elgg_format_element('td', [], elgg_view_friendly_time($ts));
$row[] = elgg_format_element('td', [], date('r', $ts));
$row[] = elgg_format_element('td', [], elgg_view('output/date', [
'value' => $ts,
'format' => DATE_RFC2822,
]));
} else {
$row[] = elgg_format_element('td', [], elgg_echo('never'));
$row[] = elgg_format_element('td', [], '&nbsp;');
@@ -16,8 +16,14 @@
$label_member_since = elgg_echo('usersettings:statistics:label:membersince');
$label_last_login = elgg_echo('usersettings:statistics:label:lastlogin');
$time_created = date("r", $user->time_created);
$last_login = date("r", $user->last_login);
$time_created = elgg_view('output/date', [
'value' => $user->time_created,
'format' => DATE_RFC2822,
]);
$last_login = elgg_view('output/date', [
'value' => $user->last_login,
'format' => DATE_RFC2822,
]);
$title = elgg_echo('usersettings:statistics:yourdetails');
@@ -20,6 +20,6 @@
'datetime' => $dt->format('c'),
];
echo elgg_format_element('time', $attributes, $dt->format($format));
echo elgg_format_element('time', $attributes, $dt->formatLocale($format));
} catch (DataFormatException $ex) {
}
@@ -7,20 +7,28 @@
* @uses int $vars['number_of_days'] (optional) number of days before friendly time switches to a date format
*/
use Elgg\Values;
$timestamp = elgg_extract('time', $vars);
try {
$date = Values::normalizeTime($timestamp);
} catch (DataFormatException $e) {
return;
}
$default_friendly_time_number_of_days = elgg_get_config('friendly_time_number_of_days', 30);
$friendly_time_number_of_days = (int) elgg_extract('number_of_days', $vars, $default_friendly_time_number_of_days);
if (strtotime("-{$friendly_time_number_of_days}days") < $timestamp) {
$output = elgg_get_friendly_time($timestamp);
} else {
$output = date(elgg_echo('friendlytime:date_format:short'), $timestamp);
$output = $date->formatLocale(elgg_echo('friendlytime:date_format:short'));
}
$attributes = [
'title' => date(elgg_echo('friendlytime:date_format'), $timestamp),
'datetime' => date('c', $timestamp),
'title' => $date->formatLocale(elgg_echo('friendlytime:date_format')),
'datetime' => $date->format('c'),
];
echo elgg_format_element('time', $attributes, $output);
@@ -13,7 +13,7 @@
return;
}
$format = elgg_extract('format', $vars, 'M d, Y H:i');
$format = elgg_extract('format', $vars, DATE_RFC2822);
if ($format === 'friendly') {
echo elgg_view('output/friendlytime', [
@@ -22,4 +22,7 @@
return;
}
echo date($format, $entity->time_created);
echo elgg_view('output/date', [
'value' => $entity->time_created,
'format' => $format,
]);

0 comments on commit c2ca5da

Please sign in to comment.
You can’t perform that action at this time.