Commit 949e7fe6 authored by catch's avatar catch
Browse files

Issue #2276183 by alexpott: Fixed Date intl support is broken, remove it.

parent a71080e6
......@@ -68,7 +68,7 @@ text:
# PHP Date format string that is translatable.
date_format:
type: string
label: 'PHP date format'
label: 'Date format'
translatable: true
# HTML color value.
......
......@@ -708,30 +708,6 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
return $cache[$langcode][$code][$string];
}
/**
* Retrieves the correct datetime format type for this system.
*
* This value is sometimes required when the format type needs to be determined
* before a date can be created.
*
* @return string
* A string as defined in \DrupalComponent\Datetime\DateTimePlus.php: either
* 'intl' or 'php', depending on whether IntlDateFormatter is available.
*/
function datetime_default_format_type() {
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast['format_type'] = &drupal_static(__FUNCTION__);
}
$format_type = &$drupal_static_fast['format_type'];
if (!isset($format_type)) {
$date = new DrupalDateTime();
$format_type = $date->canUseIntl() ? DrupalDateTime::INTL : DrupalDateTime::PHP;
}
return $format_type;
}
/**
* @} End of "defgroup format".
*/
......
......@@ -15,16 +15,6 @@
* format, or an array of date parts. It also adds an errors array
* and a __toString() method to the date object.
*
* In addition, it swaps the IntlDateFormatter into the format() method,
* if it is available. The format() method is also extended with a settings
* array to provide settings needed by the IntlDateFormatter. It will
* will only be used if the class is available, a langcode, country, and
* calendar have been set, and the format is in the right pattern, otherwise
* the parent format() method is used in the usual way. These values can
* either be set globally in the object and reused over and over as the date
* is repeatedly formatted, or set specifically in the format() method
* for the requested format.
*
* This class is less lenient than the parent DateTime class. It changes
* the default behavior for handling date values like '2011-00-00'.
* The parent class would convert that value to '2010-11-30' and report
......@@ -39,9 +29,6 @@
class DateTimePlus extends \DateTime {
const FORMAT = 'Y-m-d H:i:s';
const CALENDAR = 'gregorian';
const PHP = 'php';
const INTL = 'intl';
/**
* An array of possible date parts.
......@@ -90,26 +77,11 @@ class DateTimePlus extends \DateTime {
*/
protected $langcode = NULL;
/**
* The value of the country code passed to the constructor.
*/
protected $country = NULL;
/**
* The value of the calendar setting passed to the constructor.
*/
protected $calendar = NULL;
/**
* An array of errors encountered when creating this date.
*/
protected $errors = array();
/**
* A boolean to store whether or not the intl php extension is available.
*/
static $intlExtentionExists = NULL;
/**
* Creates a date object from an input date object.
*
......@@ -205,38 +177,7 @@ public static function createFromFormat($format, $time, $timezone = NULL, $setti
// invalid it doesn't return an exception.
$datetimeplus = new static('', $timezone, $settings);
$format_string_type = isset($settings['format_string_type']) ? $settings['format_string_type'] : static::PHP;
if ($datetimeplus->canUseIntl() && $format_string_type == static::INTL) {
// Construct the $locale variable needed by the IntlDateFormatter.
$locale = $datetimeplus->langcode . '_' . $datetimeplus->country;
// If we have information about a calendar, add it.
if (!empty($datetimeplus->calendar) && $datetimeplus->calendar != static::CALENDAR) {
$locale .= '@calendar=' . $datetimeplus->calendar;
}
// If we're working with a non-gregorian calendar, indicate that.
$calendar_type = \IntlDateFormatter::GREGORIAN;
if ($datetimeplus->calendar != static::CALENDAR) {
$calendar_type = \IntlDateFormatter::TRADITIONAL;
}
$date_type = !empty($settings['date_type']) ? $settings['date_type'] : \IntlDateFormatter::FULL;
$time_type = !empty($settings['time_type']) ? $settings['time_type'] : \IntlDateFormatter::FULL;
$timezone = !empty($settings['timezone']) ? $settings['timezone'] : $datetimeplus->getTimezone()->getName();
$formatter = new \IntlDateFormatter($locale, $date_type, $time_type, $timezone, $calendar_type, $format);
$timestamp = $formatter->parse($time);
if ($timestamp) {
$date = $datetimeplus->createFromTimestamp($timestamp, $timezone, $settings);
}
else {
$date = NULL;
}
}
else {
$date = \DateTime::createFromFormat($format, $time, $datetimeplus->getTimezone());
}
$date = \DateTime::createFromFormat($format, $time, $datetimeplus->getTimezone());
if (!$date instanceOf \DateTime) {
throw new \Exception('The date cannot be created from a format.');
}
......@@ -271,16 +212,8 @@ public static function createFromFormat($format, $time, $timezone = NULL, $setti
* PHP DateTimeZone object, string or NULL allowed.
* Defaults to NULL.
* @param array $settings
* - langcode: (optional) String two letter language code to construct
* the locale string by the intlDateFormatter class. Used to control
* the result of the format() method if that class is available.
* Defaults to NULL.
* - country: (optional) String two letter country code to construct
* the locale string by the intlDateFormatter class. Used to control
* the result of the format() method if that class is available.
* Defaults to NULL.
* - calendar: (optional) String calendar name to use for the date.
* Defaults to DateTimePlus::CALENDAR.
* - langcode: (optional) String two letter language code used to control
* the result of the format(). Defaults to NULL.
* - debug: (optional) Boolean choice to leave debug values in the
* date object for debugging purposes. Defaults to FALSE.
*/
......@@ -288,8 +221,6 @@ public function __construct($time = 'now', $timezone = NULL, $settings = array()
// Unpack settings.
$this->langcode = !empty($settings['langcode']) ? $settings['langcode'] : NULL;
$this->country = !empty($settings['country']) ? $settings['country'] : NULL;
$this->calendar = !empty($settings['calendar']) ? $settings['calendar'] : static::CALENDAR;
// Massage the input values as necessary.
$prepared_time = $this->prepareTime($time);
......@@ -571,71 +502,16 @@ public static function datePad($value, $size = 2) {
return sprintf("%0" . $size . "d", $value);
}
/**
* Tests whether the IntlDateFormatter can be used.
*
* @param string $calendar
* (optional) String calendar name to use for the date. Defaults to NULL.
* @param string $langcode
* (optional) String two letter language code to construct the locale string
* by the intlDateFormatter class. Defaults to NULL.
* @param string $country
* (optional) String two letter country code to construct the locale string
* by the intlDateFormatter class. Defaults to NULL.
*
* @return bool
* TRUE if IntlDateFormatter can be used.
*/
public function canUseIntl($calendar = NULL, $langcode = NULL, $country = NULL) {
$langcode = !empty($langcode) ? $langcode : $this->langcode;
$country = !empty($country) ? $country : $this->country;
$calendar = !empty($calendar) ? $calendar : $this->calendar;
return $this->intlDateFormatterExists() && !empty($calendar) && !empty($langcode) && !empty($country);
}
public static function intlDateFormatterExists() {
if (static::$intlExtentionExists === NULL) {
static::$intlExtentionExists = class_exists('IntlDateFormatter');
}
return static::$intlExtentionExists;
}
/**
* Formats the date for display.
*
* Uses the IntlDateFormatter to display the format, if possible.
* Adds an optional array of settings that provides the information
* the IntlDateFormatter will need.
*
* @param string $format
* A format string using either PHP's date() or the
* IntlDateFormatter() format.
* A format string using either PHP's date().
* @param array $settings
* - format_string_type: (optional) DateTimePlus::PHP or
* DateTimePlus::INTL. Identifies the pattern used by the format
* string. When using the Intl formatter, the format string must
* use the Intl pattern, which is different from the pattern used
* by the DateTime format function. Defaults to DateTimePlus::PHP.
* - langcode: (optional) String two letter language code used to control
* the result of the format(). Defaults to NULL.
* - timezone: (optional) String timezone name. Defaults to the timezone
* of the date object.
* - langcode: (optional) String two letter language code to construct the
* locale string by the intlDateFormatter class. Used to control the
* result of the format() method if that class is available. Defaults
* to NULL.
* - country: (optional) String two letter country code to construct the
* locale string by the intlDateFormatter class. Used to control the
* result of the format() method if that class is available. Defaults
* to NULL.
* - calendar: (optional) String calendar name to use for the date,
* Defaults to DateTimePlus::CALENDAR.
* - date_type: (optional) Integer date type to use in the formatter,
* defaults to IntlDateFormatter::FULL.
* - time_type: (optional) Integer date type to use in the formatter,
* defaults to IntlDateFormatter::FULL.
* - lenient: (optional) Boolean choice of whether or not to use lenient
* processing in the intl formatter. Defaults to FALSE;
*
* @return string
* The formatted value of the date.
......@@ -647,45 +523,9 @@ public function format($format, $settings = array()) {
return;
}
$format_string_type = isset($settings['format_string_type']) ? $settings['format_string_type'] : static::PHP;
$langcode = !empty($settings['langcode']) ? $settings['langcode'] : $this->langcode;
$country = !empty($settings['country']) ? $settings['country'] : $this->country;
$calendar = !empty($settings['calendar']) ? $settings['calendar'] : $this->calendar;
// Format the date and catch errors.
try {
// If we have what we need to use the IntlDateFormatter, do so.
if ($this->canUseIntl($calendar, $langcode, $country) && $format_string_type == static::INTL) {
// Construct the $locale variable needed by the IntlDateFormatter.
$locale = $langcode . '_' . $country;
// If we have information about a calendar, add it.
if (!empty($calendar) && $calendar != static::CALENDAR) {
$locale .= '@calendar=' . $calendar;
}
// If we're working with a non-gregorian calendar, indicate that.
$calendar_type = \IntlDateFormatter::GREGORIAN;
if ($calendar != self::CALENDAR) {
$calendar_type = \IntlDateFormatter::TRADITIONAL;
}
$date_type = !empty($settings['date_type']) ? $settings['date_type'] : \IntlDateFormatter::FULL;
$time_type = !empty($settings['time_type']) ? $settings['time_type'] : \IntlDateFormatter::FULL;
$timezone = !empty($settings['timezone']) ? $settings['timezone'] : $this->getTimezone()->getName();
$formatter = new \IntlDateFormatter($locale, $date_type, $time_type, $timezone, $calendar_type, $format);
$lenient = !empty($settings['lenient']) ? $settings['lenient'] : FALSE;
$formatter->setLenient($lenient);
$value = $formatter->format($this);
}
// Otherwise, use the parent method.
else {
$value = parent::format($format);
}
$value = parent::format($format);
}
catch (\Exception $e) {
$this->errors[] = $e->getMessage();
......
......@@ -142,23 +142,19 @@ public function format($timestamp, $type = 'medium', $format = '', $timezone = N
);
$date = DrupalDateTime::createFromTimestamp($timestamp, $this->timezones[$timezone], $create_settings);
// Find the appropriate format type.
$key = $date->canUseIntl() ? DrupalDateTime::INTL : DrupalDateTime::PHP;
// If we have a non-custom date format use the provided date format pattern.
if ($date_format = $this->dateFormat($type, $langcode)) {
$format = $date_format->getPattern($key);
$format = $date_format->getPattern();
}
// Fall back to medium if a format was not found.
if (empty($format)) {
$format = $this->dateFormat('fallback', $langcode)->getPattern($key);
$format = $this->dateFormat('fallback', $langcode)->getPattern();
}
// Call $date->format().
$settings = array(
'langcode' => $langcode,
'format_string_type' => $key,
);
return Xss::filter($date->format($format, $settings));
}
......
......@@ -37,29 +37,16 @@ class DrupalDateTime extends DateTimePlus {
* possible to a validation step to confirm that the date created
* from a format string exactly matches the input. This option
* indicates the format can be used for validation. Defaults to TRUE.
* - langcode: (optional) String two letter language code to construct
* the locale string by the intlDateFormatter class. Used to control
* the result of the format() method if that class is available.
* - langcode: (optional) Used to control the result of the format() method.
* Defaults to NULL.
* - country: (optional) String two letter country code to construct
* the locale string by the intlDateFormatter class. Used to control
* the result of the format() method if that class is available.
* Defaults to NULL.
* - calendar: (optional) String calendar name to use for the date.
* Defaults to DateTimePlus::CALENDAR.
* - debug: (optional) Boolean choice to leave debug values in the
* date object for debugging purposes. Defaults to FALSE.
*/
public function __construct($time = 'now', $timezone = NULL, $settings = array()) {
// We can set the langcode and country using Drupal values.
if (!isset($settings['langcode'])) {
$settings['langcode'] = \Drupal::languageManager()->getCurrentLanguage()->id;
}
if (!isset($settings['country'])) {
$settings['country'] = \Drupal::config('system.date')->get('country.default');
}
// Instantiate the parent class.
parent::__construct($time, $timezone, $settings);
......@@ -82,75 +69,37 @@ protected function prepareTimezone($timezone) {
/**
* Overrides format().
*
* Uses the IntlDateFormatter to display the format, if possible.
* Adds an optional array of settings that provides the information
* the IntlDateFormatter will need.
*
* @param string $format
* A format string using either PHP's date() or the
* IntlDateFormatter() format.
* A format string using either PHP's date().
* @param array $settings
* - format_string_type: (optional) DateTimePlus::PHP or
* DateTimePlus::INTL. Identifies the pattern used by the format
* string. When using the Intl formatter, the format string must
* use the Intl pattern, which is different from the pattern used
* by the DateTime format function. Defaults to DateTimePlus::PHP.
* - timezone: (optional) String timezone name. Defaults to the timezone
* of the date object.
* - langcode: (optional) String two letter language code to construct the
* locale string by the intlDateFormatter class. Used to control the
* result of the format() method if that class is available. Defaults
* to NULL.
* - country: (optional) String two letter country code to construct the
* locale string by the intlDateFormatter class. Used to control the
* result of the format() method if that class is available. Defaults
* to NULL.
* - calendar: (optional) String calendar name to use for the date,
* Defaults to DateTimePlus::CALENDAR.
* - date_type: (optional) Integer date type to use in the formatter,
* defaults to IntlDateFormatter::FULL.
* - time_type: (optional) Integer date type to use in the formatter,
* defaults to IntlDateFormatter::FULL.
* - lenient: (optional) Boolean choice of whether or not to use lenient
* processing in the intl formatter. Defaults to FALSE;
* - langcode: (optional) String two letter language code used to control
* the result of the format() method. Defaults to NULL.
*
* @return string
* The formatted value of the date.
*/
public function format($format, $settings = array()) {
$settings['format_string_type'] = isset($settings['format_string_type']) ? $settings['format_string_type'] : static::PHP;
$settings['calendar'] = !empty($settings['calendar']) ? $settings['calendar'] : $this->calendar;
$settings['langcode'] = !empty($settings['langcode']) ? $settings['langcode'] : $this->langcode;
$settings['country'] = !empty($settings['country']) ? $settings['country'] : $this->country;
// Format the date and catch errors.
try {
// If we have what we need to use the IntlDateFormatter, do so.
if ($this->canUseIntl($settings['calendar'], $settings['langcode'], $settings['country']) && $settings['format_string_type'] == parent::INTL) {
$value = parent::format($format, $settings);
}
// Otherwise, use the default Drupal method.
else {
// Encode markers that should be translated. 'A' becomes
// '\xEF\AA\xFF'. xEF and xFF are invalid UTF-8 sequences,
// and we assume they are not in the input string.
// Paired backslashes are isolated to prevent errors in
// read-ahead evaluation. The read-ahead expression ensures that
// A matches, but not \A.
$format = preg_replace(array('/\\\\\\\\/', '/(?<!\\\\)([AaeDlMTF])/'), array("\xEF\\\\\\\\\xFF", "\xEF\\\\\$1\$1\xFF"), $format);
// Call date_format().
$format = parent::format($format);
// Pass the langcode to _format_date_callback().
_format_date_callback(NULL, $settings['langcode']);
// Translate the marked sequences.
$value = preg_replace_callback('/\xEF([AaeDlMTF]?)(.*?)\xFF/', '_format_date_callback', $format);
}
// Encode markers that should be translated. 'A' becomes
// '\xEF\AA\xFF'. xEF and xFF are invalid UTF-8 sequences,
// and we assume they are not in the input string.
// Paired backslashes are isolated to prevent errors in
// read-ahead evaluation. The read-ahead expression ensures that
// A matches, but not \A.
$format = preg_replace(array('/\\\\\\\\/', '/(?<!\\\\)([AaeDlMTF])/'), array("\xEF\\\\\\\\\xFF", "\xEF\\\\\$1\$1\xFF"), $format);
// Call date_format().
$format = parent::format($format);
// Pass the langcode to _format_date_callback().
_format_date_callback(NULL, $settings['langcode']);
// Translate the marked sequences.
$value = preg_replace_callback('/\xEF([AaeDlMTF]?)(.*?)\xFF/', '_format_date_callback', $format);
}
catch (\Exception $e) {
$this->errors[] = $e->getMessage();
......
......@@ -23,12 +23,7 @@ class DateFormat implements ElementInterface {
* {@inheritdoc}
*/
public function getFormElement(array $definition, Language $language, $value) {
if (class_exists('intlDateFormatter')) {
$description = $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://userguide.icu-project.org/formatparse/datetime'));
}
else {
$description = $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php'));
}
$description = $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php'));
$format = $this->t('Displayed as %date_format', array('%date_format' => \Drupal::service('date')->format(REQUEST_TIME, 'custom', $value)));
return array(
'#type' => 'textfield',
......
......@@ -403,7 +403,7 @@ public function testDateFormatTranslation() {
// Update translatable fields.
$edit = array(
'config_names[system.date_format.' . $id . '][label][translation]' => $id . ' - FR',
'config_names[system.date_format.' . $id . '][pattern][pattern.php][translation]' => 'D',
'config_names[system.date_format.' . $id . '][pattern][translation]' => 'D',
);
// Save language specific version of form.
......@@ -413,7 +413,7 @@ public function testDateFormatTranslation() {
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.date_format.' . $id);
$expected = array(
'label' => $id . ' - FR',
'pattern' => array('php' => 'D'),
'pattern' => 'D',
);
$this->assertEqual($expected, $override->get());
......
......@@ -30,17 +30,15 @@
* Implements hook_element_info().
*/
function datetime_element_info() {
$format_type = datetime_default_format_type();
$date_format = '';
$time_format = '';
// Date formats cannot be loaded during install or update.
if (!defined('MAINTENANCE_MODE')) {
if ($date_format_entity = entity_load('date_format', 'html_date')) {
$date_format = $date_format_entity->getPattern($format_type);
$date_format = $date_format_entity->getPattern();
}
if ($time_format_entity = entity_load('date_format', 'html_time')) {
$time_format = $time_format_entity->getPattern($format_type);
$time_format = $time_format_entity->getPattern();
}
}
$types['datetime'] = array(
......@@ -51,7 +49,6 @@ function datetime_element_info() {
'#theme' => 'datetime_form',
'#theme_wrappers' => array('datetime_wrapper'),
'#date_date_format' => $date_format,
'#date_format_string_type' => $format_type,
'#date_date_element' => 'date',
'#date_date_callbacks' => array(),
'#date_time_format' => $time_format,
......@@ -341,7 +338,7 @@ function template_preprocess_datetime_wrapper(&$variables) {
* The form element whose value has been processed.
*/
function datetime_datetime_form_process($element, &$form_state) {
$format_settings = array('format_string_type' => $element['#date_format_string_type']);
$format_settings = array();
// The value callback has populated the #value array.
$date = !empty($element['#value']['object']) ? $element['#value']['object'] : NULL;
......@@ -468,8 +465,7 @@ function form_type_datetime_value($element, $input = FALSE) {
try {
$date_time_format = trim($date_format . ' ' . $time_format);
$date_time_input = trim($date_input . ' ' . $time_input);
$date_time_settings = array('format_string_type' => $element['#date_format_string_type']);
$date = DrupalDateTime::createFromFormat($date_time_format, $date_time_input, $timezone, $date_time_settings);
$date = DrupalDateTime::createFromFormat($date_time_format, $date_time_input, $timezone);
}
catch (\Exception $e) {
$date = NULL;
......@@ -484,8 +480,8 @@ function form_type_datetime_value($element, $input = FALSE) {
$date = $element['#default_value'];
if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
$input = array(
'date' => $date->format($element['#date_date_format'], array('format_string_type' => $element['#date_format_string_type'])),
'time' => $date->format($element['#date_time_format'], array('format_string_type' => $element['#date_format_string_type'])),
'date' => $date->format($element['#date_date_format']),
'time' => $date->format($element['#date_time_format']),
'object' => $date,
);
}
......@@ -564,16 +560,15 @@ function datetime_datetime_validate($element, &$form_state) {
* if this is not a HTML5 element.
*/
function datetime_html5_format($part, $element) {
$format_type = datetime_default_format_type();
switch ($part) {
case 'date':
switch ($element['#date_date_element']) {
case 'date':
return entity_load('date_format', 'html_date')->getPattern($format_type);
return entity_load('date_format', 'html_date')->getPattern();
case 'datetime':
case 'datetime-local':
return entity_load('date_format', 'html_datetime')->getPattern($format_type);
return entity_load('date_format', 'html_datetime')->getPattern();
default:
return $element['#date_date_format'];
......@@ -583,7 +578,7 @@ function datetime_html5_format($part, $element) {
case 'time':
switch ($element['#date_time_element']) {
case 'time':
return entity_load('date_format', 'html_time')->getPattern($format_type);
return entity_load('date_format', 'html_time')->getPattern();
default:
return $element['#date_time_format'];
......@@ -604,12 +599,11 @@ function datetime_html5_format($part, $element) {
*
*/
function datetime_format_example($format) {
$format_type = datetime_default_format_type();
$date = &drupal_static(__FUNCTION__);
if (empty($date)) {
$date = new DrupalDateTime();
}
return $date->format($format, array('format_string_type' => $format_type));
return $date->format($format);
}
/**
......@@ -987,12 +981,10 @@ function datetime_range_years($string, $date = NULL) {
* Implements hook_form_BASE_FORM_ID_alter() for node forms.
*/
function datetime_form_node_form_alter(&$form, &$form_state, $form_id) {
$format_type = datetime_default_format_type();
// Alter the 'Authored on' date to use datetime.
$form['created']['#type'] = 'datetime';
$date_format = entity_load('date_format', 'html_date')->getPattern($format_type);
$time_format = entity_load('date_format', 'html_time')->getPattern($format_type);
$date_format = entity_load('date_format', 'html_date')->getPattern();
$time_format = entity_load('date_format', 'html_time')->getPattern();
$form['created']['#description'] = t('Format: %format. Leave blank to use the time of form submission.', array('%format' => datetime_format_example($date_format . ' ' . $time_format)));
unset($form['created']['#maxlength']);
}
......
......@@ -45,8 +45,6 @@ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInter
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) {
$format_type = datetime_default_format_type();
// We are nesting some sub-elements inside the parent, so we need a wrapper.
// We also need to add another #title attribute at the top level for ease in
// identifying this item in error messages. We do not want to display this
......@@ -62,7 +60,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
case DateTimeItem::DATETIME_TYPE_DATE:
$date_type = 'date';
$time_type = 'none';