BUG Fix internal date formatting inheriting default locale

Fixes #8097
This commit is contained in:
Damian Mooyman 2018-06-13 13:31:04 +12:00
parent c2123f772f
commit 02ae2e7ed0
No known key found for this signature in database
GPG Key ID: 78B823A10DE27D1A
4 changed files with 100 additions and 22 deletions

View File

@ -173,9 +173,9 @@ class DateField extends TextField
*/ */
public function getDateFormat() public function getDateFormat()
{ {
// Browsers expect ISO 8601 dates, localisation is handled on the client
if ($this->getHTML5()) { if ($this->getHTML5()) {
// Browsers expect ISO 8601 dates, localisation is handled on the client return DBDate::ISO_DATE;
$this->setDateFormat(DBDate::ISO_DATE);
} }
if ($this->dateFormat) { if ($this->dateFormat) {
@ -220,9 +220,7 @@ class DateField extends TextField
); );
} }
if ($this->getHTML5() && $this->locale && $this->locale !== DBDate::ISO_LOCALE) {
if ($this->getHTML5() && $this->locale) {
throw new \LogicException( throw new \LogicException(
'Please opt-out of HTML5 processing of ISO 8601 dates via setHTML5(false) if using setLocale()' 'Please opt-out of HTML5 processing of ISO 8601 dates via setHTML5(false) if using setLocale()'
); );
@ -254,9 +252,8 @@ class DateField extends TextField
*/ */
protected function getInternalFormatter() protected function getInternalFormatter()
{ {
$locale = i18n::config()->uninherited('default_locale');
$formatter = IntlDateFormatter::create( $formatter = IntlDateFormatter::create(
i18n::config()->uninherited('default_locale'), DBDate::ISO_LOCALE,
IntlDateFormatter::MEDIUM, IntlDateFormatter::MEDIUM,
IntlDateFormatter::NONE IntlDateFormatter::NONE
); );
@ -448,6 +445,10 @@ class DateField extends TextField
*/ */
public function getLocale() public function getLocale()
{ {
// Use iso locale for html5
if ($this->getHTML5()) {
return DBDate::ISO_LOCALE;
}
return $this->locale ?: i18n::get_locale(); return $this->locale ?: i18n::get_locale();
} }

View File

@ -5,6 +5,7 @@ namespace SilverStripe\Forms;
use IntlDateFormatter; use IntlDateFormatter;
use InvalidArgumentException; use InvalidArgumentException;
use SilverStripe\i18n\i18n; use SilverStripe\i18n\i18n;
use SilverStripe\ORM\FieldType\DBDate;
use SilverStripe\ORM\FieldType\DBDatetime; use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\ValidationResult; use SilverStripe\ORM\ValidationResult;
@ -297,7 +298,7 @@ class DatetimeField extends TextField
} }
$formatter = IntlDateFormatter::create( $formatter = IntlDateFormatter::create(
i18n::config()->uninherited('default_locale'), DBDate::ISO_LOCALE,
IntlDateFormatter::MEDIUM, IntlDateFormatter::MEDIUM,
IntlDateFormatter::MEDIUM, IntlDateFormatter::MEDIUM,
$timezone $timezone
@ -337,10 +338,10 @@ class DatetimeField extends TextField
$internalFormatter = $this->getInternalFormatter(); $internalFormatter = $this->getInternalFormatter();
$timestamp = $internalFormatter->parse($value); $timestamp = $internalFormatter->parse($value);
// Retry without "T" separator // Retry with "T" separator
if (!$timestamp) { if (!$timestamp) {
$fallbackFormatter = $this->getInternalFormatter(); $fallbackFormatter = $this->getInternalFormatter();
$fallbackFormatter->setPattern(DBDatetime::ISO_DATETIME); $fallbackFormatter->setPattern(DBDatetime::ISO_DATETIME_NORMALISED);
$timestamp = $fallbackFormatter->parse($value); $timestamp = $fallbackFormatter->parse($value);
} }

View File

@ -34,6 +34,12 @@ class DBDate extends DBField
*/ */
const ISO_DATE = 'y-MM-dd'; const ISO_DATE = 'y-MM-dd';
/**
* Fixed locale to use for ISO date formatting. This is necessary to prevent
* locale-specific numeric localisation breaking internal date strings.
*/
const ISO_LOCALE = 'en_NZ';
public function setValue($value, $record = null, $markChanged = true) public function setValue($value, $record = null, $markChanged = true)
{ {
$value = $this->parseDate($value); $value = $this->parseDate($value);
@ -77,8 +83,7 @@ class DBDate extends DBField
} }
// Format as iso8601 // Format as iso8601
$formatter = $this->getFormatter(); $formatter = $this->getInternalFormatter();
$formatter->setPattern($this->getISOFormat());
return $formatter->format($source); return $formatter->format($source);
} }
@ -203,7 +208,43 @@ class DBDate extends DBField
*/ */
public function getFormatter($dateLength = IntlDateFormatter::MEDIUM, $timeLength = IntlDateFormatter::NONE) public function getFormatter($dateLength = IntlDateFormatter::MEDIUM, $timeLength = IntlDateFormatter::NONE)
{ {
return new IntlDateFormatter(i18n::get_locale(), $dateLength, $timeLength); return $this->getCustomFormatter(null, $dateLength, $timeLength);
}
/**
* Return formatter in a given locale. Useful if localising in a format other than the current locale.
*
* @param string|null $locale The current locale, or null to use default
* @param string|null $pattern Custom pattern to use for this, if required
* @param int $dateLength
* @param int $timeLength
* @return IntlDateFormatter
*/
public function getCustomFormatter(
$locale = null,
$pattern = null,
$dateLength = IntlDateFormatter::MEDIUM,
$timeLength = IntlDateFormatter::NONE
) {
$locale = $locale ?: i18n::get_locale();
$formatter = IntlDateFormatter::create($locale, $dateLength, $timeLength);
if ($pattern) {
$formatter->setPattern($pattern);
}
return $formatter;
}
/**
* Formatter used internally
*
* @internal
* @return IntlDateFormatter
*/
protected function getInternalFormatter()
{
$formatter = $this->getCustomFormatter(DBDate::ISO_LOCALE, DBDate::ISO_DATE);
$formatter->setLenient(false);
return $formatter;
} }
/** /**
@ -271,7 +312,8 @@ class DBDate extends DBField
// Get user format // Get user format
$format = $member->getDateFormat(); $format = $member->getDateFormat();
return $this->Format($format); $formatter = $this->getCustomFormatter($format, $member->getLocale());
return $formatter->format($this->getTimestamp());
} }
/** /**
@ -319,7 +361,9 @@ class DBDate extends DBField
*/ */
public function Rfc2822() public function Rfc2822()
{ {
return $this->Format('y-MM-dd HH:mm:ss'); $formatter = $this->getInternalFormatter();
$formatter->setPattern('y-MM-dd HH:mm:ss');
return $formatter->format($this->getTimestamp());
} }
/** /**
@ -420,7 +464,7 @@ class DBDate extends DBField
); );
case "minutes": case "minutes":
$span = round($ago/60); $span = round($ago / 60);
return _t( return _t(
__CLASS__ . '.MINUTES_SHORT_PLURALS', __CLASS__ . '.MINUTES_SHORT_PLURALS',
'{count} min|{count} mins', '{count} min|{count} mins',
@ -428,7 +472,7 @@ class DBDate extends DBField
); );
case "hours": case "hours":
$span = round($ago/3600); $span = round($ago / 3600);
return _t( return _t(
__CLASS__ . '.HOURS_SHORT_PLURALS', __CLASS__ . '.HOURS_SHORT_PLURALS',
'{count} hour|{count} hours', '{count} hour|{count} hours',
@ -436,7 +480,7 @@ class DBDate extends DBField
); );
case "days": case "days":
$span = round($ago/86400); $span = round($ago / 86400);
return _t( return _t(
__CLASS__ . '.DAYS_SHORT_PLURALS', __CLASS__ . '.DAYS_SHORT_PLURALS',
'{count} day|{count} days', '{count} day|{count} days',
@ -444,7 +488,7 @@ class DBDate extends DBField
); );
case "months": case "months":
$span = round($ago/86400/30); $span = round($ago / 86400 / 30);
return _t( return _t(
__CLASS__ . '.MONTHS_SHORT_PLURALS', __CLASS__ . '.MONTHS_SHORT_PLURALS',
'{count} month|{count} months', '{count} month|{count} months',
@ -452,7 +496,7 @@ class DBDate extends DBField
); );
case "years": case "years":
$span = round($ago/86400/365); $span = round($ago / 86400 / 365);
return _t( return _t(
__CLASS__ . '.YEARS_SHORT_PLURALS', __CLASS__ . '.YEARS_SHORT_PLURALS',
'{count} year|{count} years', '{count} year|{count} years',
@ -466,8 +510,8 @@ class DBDate extends DBField
public function requireField() public function requireField()
{ {
$parts=array('datatype'=>'date', 'arrayValue'=>$this->arrayValue); $parts = array('datatype' => 'date', 'arrayValue' => $this->arrayValue);
$values=array('type'=>'date', 'parts'=>$parts); $values = array('type' => 'date', 'parts' => $parts);
DB::require_field($this->tableName, $this->name, $values); DB::require_field($this->tableName, $this->name, $values);
} }

View File

@ -228,6 +228,38 @@ class DBDatetime extends DBDate implements TemplateGlobalProvider
return new IntlDateFormatter(i18n::get_locale(), $dateLength, $timeLength); return new IntlDateFormatter(i18n::get_locale(), $dateLength, $timeLength);
} }
/**
* Return formatter in a given locale. Useful if localising in a format other than the current locale.
*
* @param string|null $locale The current locale, or null to use default
* @param string|null $pattern Custom pattern to use for this, if required
* @param int $dateLength
* @param int $timeLength
* @return IntlDateFormatter
*/
public function getCustomFormatter(
$locale = null,
$pattern = null,
$dateLength = IntlDateFormatter::MEDIUM,
$timeLength = IntlDateFormatter::MEDIUM
) {
return parent::getCustomFormatter($locale, $pattern, $dateLength, $timeLength);
}
/**
* Formatter used internally
*
* @internal
* @return IntlDateFormatter
*/
protected function getInternalFormatter()
{
$formatter = $this->getCustomFormatter(DBDate::ISO_LOCALE, DBDatetime::ISO_DATETIME);
$formatter->setLenient(false);
return $formatter;
}
/** /**
* Get standard ISO date format string * Get standard ISO date format string
* *