diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index 90813e5..e4821c4 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -304,9 +304,33 @@ field.formatter.settings.uri_link:
type: mapping
label: 'URI as link display format settings'
+field.formatter.settings.timestamp:
+ type: mapping
+ label: 'Timestamp display format settings'
+ mapping:
+ date_format:
+ type: string
+ label: 'Date format'
+ custom_date_format:
+ type: string
+ label: 'Custom date format'
+ timezone:
+ type: string
+ label: 'Timezone'
+
field.formatter.settings.timestamp_ago:
type: mapping
label: 'Timestamp ago display format settings'
+ mapping:
+ future_format:
+ type: string
+ label: 'Future format'
+ past_format:
+ type: string
+ label: 'Past format'
+ granularity:
+ type: integer
+ label: 'Granularity'
field.formatter.settings.entity_reference_entity_view:
type: mapping
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
index 30dae80..556e4cb 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
@@ -7,12 +7,15 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
+use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
/**
* Plugin implementation of the 'timestamp' formatter as time ago.
@@ -37,6 +40,13 @@ class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPlu
protected $dateFormatter;
/**
+ * The current Request object.
+ *
+ * @var \Symfony\Component\HttpFoundation\Request
+ */
+ protected $request;
+
+ /**
* Constructs a TimestampAgoFormatter object.
*
* @param string $plugin_id
@@ -55,11 +65,14 @@ class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPlu
* Any third party settings.
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
* The date formatter service.
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The current request.
*/
- public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatter $date_formatter) {
+ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatter $date_formatter, Request $request) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->dateFormatter = $date_formatter;
+ $this->request = $request;
}
/**
@@ -75,19 +88,77 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
- $container->get('date.formatter')
+ $container->get('date.formatter'),
+ $container->get('request_stack')->getCurrentRequest()
);
}
/**
* {@inheritdoc}
*/
+ public static function defaultSettings() {
+ return array(
+ 'future_format' => '@time hence',
+ 'past_format' => '@time ago',
+ 'granularity' => 2,
+ ) + parent::defaultSettings();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ $elements = parent::settingsForm($form, $form_state);
+
+ $form['future_format'] = array(
+ '#type' => 'textfield',
+ '#title' => $this->t('Future format'),
+ '#default_value' => $this->getSetting('future_format'),
+ '#description' => $this->t('The format for timestamps in the future. Use @time where you want the time to appear.'),
+ );
+
+ $form['past_format'] = array(
+ '#type' => 'textfield',
+ '#title' => $this->t('Past format'),
+ '#default_value' => $this->getSetting('past_format'),
+ '#description' => $this->t('The format for timestamps in the past. Use @time where you want the time to appear.'),
+ );
+
+ $elements['granularity'] = array(
+ '#type' => 'number',
+ '#title' => $this->t('Granularity'),
+ '#description' => $this->t('How many time units should be shown in the formatted output.'),
+ '#default_value' => $this->getSetting('granularity') ?: 2,
+ '#min' => 1,
+ '#max' => 6,
+ );
+
+ return $elements;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsSummary() {
+ $summary = parent::settingsSummary();
+
+ $future_date = strtotime('1 year 1 month 1 week 1 day 1 hour 1 minute');
+ $past_date = strtotime('-1 year -1 month -1 week -1 day -1 hour -1 minute');
+ $summary[] = t('Future date: %display', array('%display' => $this->formatTimestamp($future_date)));
+ $summary[] = t('Past date: %display', array('%display' => $this->formatTimestamp($past_date)));
+
+ return $summary;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($items as $delta => $item) {
if ($item->value) {
- $updated = $this->t('@time ago', array('@time' => $this->dateFormatter->formatTimeDiffSince($item->value)));
+ $updated = $this->formatTimestamp($item->value);
}
else {
$updated = $this->t('never');
@@ -99,4 +170,26 @@ public function viewElements(FieldItemListInterface $items) {
return $elements;
}
+ /**
+ * Creates a formatted timestamp value as a string.
+ *
+ * @param int $timestamp
+ * A UNIX timestamp to format.
+ *
+ * @return string
+ * The formatted timestamp string using the past or future format setting.
+ */
+ protected function formatTimestamp($timestamp) {
+ $granularity = $this->getSetting('granularity');
+ $options = ['granularity' => $granularity];
+ $interval = $this->request->server->get('REQUEST_TIME') - $timestamp;
+
+ if ($interval > 0) {
+ return SafeMarkup::format($this->getSetting('past_format'), ['@time' => $this->dateFormatter->formatTimeDiffSince($timestamp, $options)]);
+ }
+ else {
+ return SafeMarkup::format($this->getSetting('future_format'), ['@time' => $this->dateFormatter->formatTimeDiffUntil($timestamp, $options)]);
+ }
+ }
+
}
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
index cb28315..bfd9f55 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
@@ -7,8 +7,14 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
-use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Datetime\DateFormatter;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'timestamp' formatter.
@@ -23,7 +29,138 @@
* }
* )
*/
-class TimestampFormatter extends FormatterBase {
+class TimestampFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
+
+ /**
+ * The date formatter service.
+ *
+ * @var \Drupal\Core\Datetime\DateFormatter
+ */
+ protected $dateFormatter;
+
+ /**
+ * The date format entity storage.
+ *
+ * @var \Drupal\Core\Entity\EntityStorageInterface
+ */
+ protected $dateFormatStorage;
+
+ /**
+ * Constructs a new TimestampFormatter.
+ *
+ * @param string $plugin_id
+ * The plugin_id for the formatter.
+ * @param mixed $plugin_definition
+ * The plugin implementation definition.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+ * The definition of the field to which the formatter is associated.
+ * @param array $settings
+ * The formatter settings.
+ * @param string $label
+ * The formatter label display setting.
+ * @param string $view_mode
+ * The view mode.
+ * @param array $third_party_settings
+ * Third party settings.
+ * @param \Drupal\Core\Datetime\DateFormatter $date_formatter
+ * The date formatter service.
+ * @param \Drupal\Core\Entity\EntityStorageInterface $date_format_storage
+ * The date storage.
+ */
+ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatter $date_formatter, EntityStorageInterface $date_format_storage) {
+ parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
+
+ $this->dateFormatter = $date_formatter;
+ $this->dateFormatStorage = $date_format_storage;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $plugin_id,
+ $plugin_definition,
+ $configuration['field_definition'],
+ $configuration['settings'],
+ $configuration['label'],
+ $configuration['view_mode'],
+ $configuration['third_party_settings'],
+ $container->get('date.formatter'),
+ $container->get('entity.manager')->getStorage('date_format')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function defaultSettings() {
+ return array(
+ 'date_format' => 'medium',
+ 'custom_date_format' => '',
+ 'timezone' => '',
+ ) + parent::defaultSettings();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ $elements = parent::settingsForm($form, $form_state);
+
+ $date_formats = array();
+
+ foreach ($this->dateFormatStorage->loadMultiple() as $machine_name => $value) {
+ $date_formats[$machine_name] = $this->t('@name format: @date', array('@name' => $value->label(), '@date' => $this->dateFormatter->format(REQUEST_TIME, $machine_name)));
+ }
+
+ $date_formats['custom'] = $this->t('Custom');
+
+ $elements['date_format'] = array(
+ '#type' => 'select',
+ '#title' => $this->t('Date format'),
+ '#options' => $date_formats,
+ '#default_value' => $this->getSetting('date_format') ?: 'medium',
+ );
+
+ $elements['custom_date_format'] = array(
+ '#type' => 'textfield',
+ '#title' => $this->t('Custom date format'),
+ '#description' => $this->t('If "Custom", see the PHP docs for date formats. Otherwise, enter the number of different time units to display, which defaults to 2.'),
+ '#default_value' => $this->getSetting('custom_date_format') ?: '',
+ );
+
+ $elements['custom_date_format']['#states']['visible'][] = array(
+ ':input[name="options[settings][date_format]"]' => array('value' => 'custom'),
+ );
+
+ $elements['timezone'] = array(
+ '#type' => 'select',
+ '#title' => $this->t('Time zone'),
+ '#description' => $this->t('Time zone to be used for date output.'),
+ '#options' => array('' => $this->t('- Default site/user time zone -')) + system_time_zones(FALSE),
+ '#default_value' => $this->getSetting('timezone'),
+ );
+
+ return $elements;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsSummary() {
+ $summary = parent::settingsSummary();
+
+ $summary[] = $this->t('Date format: @date_format', array('@date_format' => $this->getSetting('date_format')));
+ if ($custom_date_format = $this->getSetting('custom_date_format')) {
+ $summary[] = $this->t('Custom date format: @custom_date_format', array('@custom_date_format' => $custom_date_format));
+ }
+ if ($timezone = $this->getSetting('timezone')) {
+ $summary[] = $this->t('Time zone: @timezone', array('@timezone' => $timezone));
+ }
+
+ return $summary;
+ }
/**
* {@inheritdoc}
@@ -31,6 +168,17 @@ class TimestampFormatter extends FormatterBase {
public function viewElements(FieldItemListInterface $items) {
$elements = array();
+ $date_format = $this->getSetting('date_format');
+ $custom_date_format = '';
+ $timezone = $this->getSetting('timezone') ?: NULL;
+ $langcode = NULL;
+
+ // If an RFC2822 date format is requested, then the month and day have to
+ // be in English. @see http://www.faqs.org/rfcs/rfc2822.html
+ if ($date_format === 'custom' && ($custom_date_format = $this->getSetting('custom_date_format')) === 'r') {
+ $langcode = 'en';
+ }
+
foreach ($items as $delta => $item) {
$elements[$delta] = [
'#cache' => [
@@ -38,7 +186,7 @@ public function viewElements(FieldItemListInterface $items) {
'timezone',
],
],
- '#markup' => format_date($item->value)
+ '#markup' => $this->dateFormatter->format($item->value, $date_format, $custom_date_format, $timezone, $langcode),
];
}
diff --git a/core/modules/datetime/config/schema/datetime.schema.yml b/core/modules/datetime/config/schema/datetime.schema.yml
index 0718e04..406a2fd 100644
--- a/core/modules/datetime/config/schema/datetime.schema.yml
+++ b/core/modules/datetime/config/schema/datetime.schema.yml
@@ -23,8 +23,15 @@ field.value.datetime:
type: string
label: 'Default date value'
-field.formatter.settings.datetime_default:
+field.formatter.settings.datetime_base:
type: mapping
+ mapping:
+ timezone_override:
+ type: string
+ label: 'Time zone override'
+
+field.formatter.settings.datetime_default:
+ type: field.formatter.settings.datetime_base
label: 'Datetime default display format settings'
mapping:
format_type:
@@ -32,9 +39,33 @@ field.formatter.settings.datetime_default:
label: 'Date format'
field.formatter.settings.datetime_plain:
- type: mapping
+ type: field.formatter.settings.datetime_base
label: 'Datetime plain display format settings'
+field.formatter.settings.datetime_custom:
+ type: field.formatter.settings.datetime_base
+ label: 'Datetime custom display format settings'
+ mapping:
+ date_format:
+ type: string
+ label: 'Date/time format'
+ translatable: true
+ translation context: 'PHP date format'
+
+field.formatter.settings.datetime_time_ago:
+ type: mapping
+ label: 'Datetime time ago display format settings'
+ mapping:
+ future_format:
+ type: string
+ label: 'Future format'
+ past_format:
+ type: string
+ label: 'Past format'
+ granularity:
+ type: integer
+ label: 'Granularity'
+
field.widget.settings.datetime_datelist:
type: mapping
label: 'Datetime select list display format settings'
diff --git a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeCustomFormatter.php b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeCustomFormatter.php
new file mode 100644
index 0000000..4115c33
--- /dev/null
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeCustomFormatter.php
@@ -0,0 +1,98 @@
+ DATETIME_DATETIME_STORAGE_FORMAT,
+ ) + parent::defaultSettings();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function viewElements(FieldItemListInterface $items) {
+ $elements = array();
+
+ foreach ($items as $delta => $item) {
+ $output = '';
+ if (!empty($item->date)) {
+ /** @var \Drupal\Core\Datetime\DrupalDateTime $date */
+ $date = $item->date;
+
+ if ($this->getFieldSetting('datetime_type') == 'date') {
+ // A date without time will pick up the current time, use the default.
+ datetime_date_default_time($date);
+ }
+ $this->setTimeZone($date);
+
+ $output = $date->format($this->getSetting('date_format'), $this->getFormatSettings());
+ }
+ $elements[$delta] = [
+ '#markup' => $output,
+ '#cache' => [
+ 'contexts' => [
+ 'timezone',
+ ],
+ ],
+ ];
+ }
+
+ return $elements;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ $form = parent::settingsForm($form, $form_state);
+
+ $form['date_format'] = array(
+ '#type' => 'textfield',
+ '#title' => $this->t('Date/time format'),
+ '#description' => $this->t('A user-defined date/time format. See the PHP manual for available options.', array('@url' => 'http://php.net/manual/function.date.php')),
+ '#default_value' => $this->getSetting('date_format'),
+ );
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsSummary() {
+ $summary = parent::settingsSummary();
+
+ $date = new DrupalDateTime();
+ $this->setTimeZone($date);
+ $summary[] = $date->format($this->getSetting('date_format'), $this->getFormatSettings());
+
+ return $summary;
+ }
+
+}
diff --git a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
index 0f30d5c..8e85e88 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
@@ -14,11 +14,10 @@
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Field\FormatterBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
- * Plugin implementation of the 'datetime_default' formatter.
+ * Plugin implementation of the 'Default' formatter for 'datetime' fields.
*
* @FieldFormatter(
* id = "datetime_default",
@@ -28,7 +27,7 @@
* }
* )
*/
-class DateTimeDefaultFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
+class DateTimeDefaultFormatter extends DateTimeFormatterBase implements ContainerFactoryPluginInterface {
/**
* {@inheritdoc}
@@ -47,11 +46,11 @@ public static function defaultSettings() {
protected $dateFormatter;
/**
- * The date storage.
+ * The date format entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
- protected $dateStorage;
+ protected $dateFormatStorage;
/**
* Constructs a new DateTimeDefaultFormatter.
@@ -72,14 +71,14 @@ public static function defaultSettings() {
* Third party settings.
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
* The date formatter service.
- * @param \Drupal\Core\Entity\EntityStorageInterface $date_storage
- * The date storage.
+ * @param \Drupal\Core\Entity\EntityStorageInterface $date_format_storage
+ * The date format entity storage.
*/
- public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatter $date_formatter, EntityStorageInterface $date_storage) {
+ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatter $date_formatter, EntityStorageInterface $date_format_storage) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->dateFormatter = $date_formatter;
- $this->dateStorage = $date_storage;
+ $this->dateFormatStorage = $date_format_storage;
}
/**
@@ -103,26 +102,25 @@ public static function create(ContainerInterface $container, array $configuratio
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
-
$elements = array();
foreach ($items as $delta => $item) {
-
- $formatted_date = '';
+ $output = '';
$iso_date = '';
if ($item->date) {
+ /** @var \Drupal\Core\Datetime\DrupalDateTime $date */
$date = $item->date;
// Create the ISO date in Universal Time.
$iso_date = $date->format("Y-m-d\TH:i:s") . 'Z';
- // The formatted output will be in local time.
- $date->setTimeZone(timezone_open(drupal_get_user_timezone()));
if ($this->getFieldSetting('datetime_type') == 'date') {
// A date without time will pick up the current time, use the default.
datetime_date_default_time($date);
}
- $formatted_date = $this->dateFormat($date);
+ $this->setTimeZone($date);
+
+ $output = $this->formatDate($date, $this->getFormatSettings());
}
// Display the date using theme datetime.
@@ -133,7 +131,8 @@ public function viewElements(FieldItemListInterface $items) {
],
],
'#theme' => 'time',
- '#text' => $formatted_date,
+ '#text' => $output,
+ '#html' => FALSE,
'#attributes' => array(
'datetime' => $iso_date,
),
@@ -159,23 +158,27 @@ public function viewElements(FieldItemListInterface $items) {
* @return string
* A formatted date string using the chosen format.
*/
- function dateFormat($date) {
+ protected function formatDate($date) {
$format_type = $this->getSetting('format_type');
- return $this->dateFormatter->format($date->getTimestamp(), $format_type);
+ $timezone = $this->getSetting('timezone_override');
+ return $this->dateFormatter->format($date->getTimestamp(), $format_type, '', $timezone != '' ? $timezone : null);
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
+ $form = parent::settingsForm($form, $form_state);
+
$time = new DrupalDateTime();
$format_types = $this->dateStorage->loadMultiple();
+ $options = [];
foreach ($format_types as $type => $type_info) {
$format = $this->dateFormatter->format($time->format('U'), $type);
$options[$type] = $type_info->label() . ' (' . $format . ')';
}
- $elements['format_type'] = array(
+ $form['format_type'] = array(
'#type' => 'select',
'#title' => t('Date format'),
'#description' => t("Choose a format for displaying the date. Be sure to set a format appropriate for the field, i.e. omitting time for a field that only has a date."),
@@ -183,16 +186,18 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
'#default_value' => $this->getSetting('format_type'),
);
- return $elements;
+ return $form;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
- $summary = array();
+ $summary = parent::settingsSummary();
+
$date = new DrupalDateTime();
- $summary[] = t('Format: @display', array('@display' => $this->dateFormat($date, FALSE)));
+ $summary[] = t('Format: @display', array('@display' => $this->formatDate($date, $this->getFormatSettings())));
+
return $summary;
}
diff --git a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeFormatterBase.php b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeFormatterBase.php
new file mode 100644
index 0000000..fefd065
--- /dev/null
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeFormatterBase.php
@@ -0,0 +1,85 @@
+ '',
+ ) + parent::defaultSettings();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ $form = parent::settingsForm($form, $form_state);
+
+ $form['timezone_override'] = array(
+ '#type' => 'select',
+ '#title' => $this->t('Time zone override'),
+ '#description' => $this->t('The time zone selected here will always be used'),
+ '#options' => system_time_zones(TRUE),
+ '#default_value' => $this->getSetting('timezone_override'),
+ );
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsSummary() {
+ $summary = parent::settingsSummary();
+
+ if ($override = $this->getSetting('timezone_override')) {
+ $summary[] = $this->t('Time zone: @timezone', array('@timezone' => $override));
+ }
+
+ return $summary;
+ }
+
+ /**
+ * Sets the current user's or overridden time zone on a DrupalDateTime object.
+ *
+ * @param \Drupal\Core\Datetime\DrupalDateTime $date
+ * A DrupalDateTime object.
+ */
+ protected function setTimeZone(DrupalDateTime $date) {
+ $date->setTimeZone(timezone_open(drupal_get_user_timezone()));
+ }
+
+ /**
+ * Gets a settings array suitable for DrupalDateTime::format().
+ *
+ * @return array
+ * The settings array that can be passed to DrupalDateTime::format().
+ */
+ protected function getFormatSettings() {
+ $settings = [];
+
+ if ($this->getSetting('timezone_override') != '') {
+ $settings['timezone'] = $this->getSetting('timezone_override');
+ }
+
+ return $settings;
+ }
+
+}
diff --git a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimePlainFormatter.php b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimePlainFormatter.php
index 3f68a7f..79e3270 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimePlainFormatter.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimePlainFormatter.php
@@ -7,11 +7,10 @@
namespace Drupal\datetime\Plugin\Field\FieldFormatter;
-use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
/**
- * Plugin implementation of the 'datetime_plain' formatter.
+ * Plugin implementation of the 'Plain' formatter for 'datetime' fields.
*
* @FieldFormatter(
* id = "datetime_plain",
@@ -21,30 +20,31 @@
* }
*)
*/
-class DateTimePlainFormatter extends FormatterBase {
+class DateTimePlainFormatter extends DateTimeFormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
-
$elements = array();
foreach ($items as $delta => $item) {
-
$output = '';
if (!empty($item->date)) {
- // The date was created and verified during field_load(), so it is safe
- // to use without further inspection.
+ /** @var \Drupal\Core\Datetime\DrupalDateTime $date */
$date = $item->date;
- $date->setTimeZone(timezone_open(drupal_get_user_timezone()));
- $format = DATETIME_DATETIME_STORAGE_FORMAT;
+
if ($this->getFieldSetting('datetime_type') == 'date') {
// A date without time will pick up the current time, use the default.
datetime_date_default_time($date);
$format = DATETIME_DATE_STORAGE_FORMAT;
}
- $output = $date->format($format);
+ else {
+ $format = DATETIME_DATETIME_STORAGE_FORMAT;
+ }
+ $this->setTimeZone($date);
+
+ $output = $date->format($format, $this->getFormatSettings());
}
$elements[$delta] = [
'#cache' => [
diff --git a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeTimeAgoFormatter.php b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeTimeAgoFormatter.php
new file mode 100644
index 0000000..6bc2639
--- /dev/null
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeTimeAgoFormatter.php
@@ -0,0 +1,196 @@
+dateFormatter = $date_formatter;
+ $this->request = $request;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function defaultSettings() {
+ $settings = array(
+ 'future_format' => '@time hence',
+ 'past_format' => '@time ago',
+ 'granularity' => 2,
+ ) + parent::defaultSettings();
+
+ return $settings;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $plugin_id,
+ $plugin_definition,
+ $configuration['field_definition'],
+ $configuration['settings'],
+ $configuration['label'],
+ $configuration['view_mode'],
+ $configuration['third_party_settings'],
+ $container->get('date.formatter'),
+ $container->get('request_stack')->getCurrentRequest()
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function viewElements(FieldItemListInterface $items) {
+ $elements = array();
+
+ foreach ($items as $delta => $item) {
+ $date = $item->date;
+ $output = '';
+ if (!empty($item->date)) {
+ if ($this->getFieldSetting('datetime_type') == 'date') {
+ // A date without time will pick up the current time, use the default.
+ datetime_date_default_time($date);
+ }
+ $output = $this->formatDate($date);
+ }
+ $elements[$delta] = array('#markup' => $output);
+ }
+
+ return $elements;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ $form = parent::settingsForm($form, $form_state);
+
+ $form['future_format'] = array(
+ '#type' => 'textfield',
+ '#title' => $this->t('Future format'),
+ '#default_value' => $this->getSetting('future_format'),
+ '#description' => $this->t('The format for dates in the future. Use @time where you want the time to appear.'),
+ );
+
+ $form['past_format'] = array(
+ '#type' => 'textfield',
+ '#title' => $this->t('Past format'),
+ '#default_value' => $this->getSetting('past_format'),
+ '#description' => $this->t('The format for dates in the past. Use @time where you want the time to appear.'),
+ );
+
+ $form['granularity'] = array(
+ '#type' => 'number',
+ '#title' => 'Interval',
+ '#default_value' => $this->getSetting('granularity'),
+ '#description' => $this->t('How many time units should be shown in the formatted output.'),
+ );
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsSummary() {
+ $summary = parent::settingsSummary();
+
+ $future_date = new DrupalDateTime('1 year 1 month 1 week 1 day 1 hour 1 minute');
+ $past_date = new DrupalDateTime('-1 year -1 month -1 week -1 day -1 hour -1 minute');
+ $summary[] = t('Future date: %display', array('%display' => $this->formatDate($future_date)));
+ $summary[] = t('Past date: %display', array('%display' => $this->formatDate($past_date)));
+
+ return $summary;
+ }
+
+ /**
+ * Creates a formatted date value as a string.
+ *
+ * @param \Drupal\Core\Datetime\DrupalDateTime|object $date
+ * A date/time object.
+ *
+ * @return string
+ * The formatted date/time string using the past or future format setting.
+ */
+ protected function formatDate(DrupalDateTime $date) {
+ $granularity = $this->getSetting('granularity');
+ $timestamp = $date->getTimestamp();
+ $options = ['granularity' => $granularity];
+ $interval = $this->request->server->get('REQUEST_TIME') - $timestamp;
+
+ if ($interval > 0) {
+ return SafeMarkup::format($this->getSetting('past_format'), ['@time' => $this->dateFormatter->formatTimeDiffSince($timestamp, $options)]);
+ }
+ else {
+ return SafeMarkup::format($this->getSetting('future_format'), ['@time' => $this->dateFormatter->formatTimeDiffUntil($timestamp, $options)]);
+ }
+ }
+
+}
diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
index 32d3668..dfe750e 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
@@ -43,12 +43,12 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
$date = $items[$delta]->date;
// The date was created and verified during field_load(), so it is safe to
// use without further inspection.
- $date->setTimezone(new \DateTimeZone($element['value']['#date_timezone']));
if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) {
// A date without time will pick up the current time, use the default
// time.
datetime_date_default_time($date);
}
+ $date->setTimezone(new \DateTimeZone($element['value']['#date_timezone']));
$element['value']['#default_value'] = $date;
}
diff --git a/core/modules/datetime/src/Tests/DateTimeFieldTest.php b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
index f3fcae1..0cb226b 100644
--- a/core/modules/datetime/src/Tests/DateTimeFieldTest.php
+++ b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
@@ -7,6 +7,7 @@
namespace Drupal\datetime\Tests;
+use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Datetime\DrupalDateTime;
@@ -27,6 +28,11 @@ class DateTimeFieldTest extends WebTestBase {
public static $modules = array('node', 'entity_test', 'datetime', 'field_ui');
/**
+ * The default display settings to use for the formatters.
+ */
+ protected $defaultSettings;
+
+ /**
* An array of display options to pass to entity_get_display()
*
* @var array
@@ -47,9 +53,18 @@ class DateTimeFieldTest extends WebTestBase {
*/
protected $field;
+ /**
+ * {@inheritdoc}
+ */
protected function setUp() {
parent::setUp();
+ // Set an explicit site timezone, and disallow per-user timezones.
+ $this->config('system.date')
+ ->set('timezone.user.configurable', 0)
+ ->set('timezone.default', 'Asia/Tokyo')
+ ->save();
+
$web_user = $this->drupalCreateUser(array(
'access content',
'view test entity',
@@ -81,10 +96,14 @@ protected function setUp() {
))
->save();
+ $this->defaultSettings = array(
+ 'timezone_override' => '',
+ );
+
$this->displayOptions = array(
'type' => 'datetime_default',
'label' => 'hidden',
- 'settings' => array('format_type' => 'medium'),
+ 'settings' => array('format_type' => 'medium') + $this->defaultSettings,
);
entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
->setComponent($field_name, $this->displayOptions)
@@ -103,9 +122,17 @@ function testDateField() {
$this->assertFieldByXPath('//*[@id="edit-' . $field_name . '-wrapper"]/h4[contains(@class, "form-required")]', TRUE, 'Required markup found');
$this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Time element not found.');
- // Submit a valid date and ensure it is accepted.
+ // Build up a date in the UTC timezone.
$value = '2012-12-31 00:00:00';
- $date = new DrupalDateTime($value);
+ $date = new DrupalDateTime($value, 'UTC');
+
+ // The expected values will use the default time.
+ datetime_date_default_time($date);
+
+ // Update the timezone to the system default.
+ $date->setTimezone(timezone_open(drupal_get_user_timezone()));
+
+ // Submit a valid date and ensure it is accepted.
$date_format = entity_load('date_format', 'html_date')->getPattern();
$time_format = entity_load('date_format', 'html_time')->getPattern();
@@ -119,9 +146,6 @@ function testDateField() {
$this->assertRaw($date->format($date_format));
$this->assertNoRaw($date->format($time_format));
- // The expected values will use the default time.
- datetime_date_default_time($date);
-
// Verify that the date is output according to the formatter settings.
$options = array(
'format_type' => array('short', 'medium', 'long'),
@@ -129,7 +153,7 @@ function testDateField() {
foreach ($options as $setting => $values) {
foreach ($values as $new_value) {
// Update the entity display settings.
- $this->displayOptions['settings'] = array($setting => $new_value);
+ $this->displayOptions['settings'] = array($setting => $new_value) + $this->defaultSettings;
entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
->setComponent($field_name, $this->displayOptions)
->save();
@@ -140,7 +164,7 @@ function testDateField() {
// Verify that a date is displayed.
$expected = format_date($date->getTimestamp(), $new_value);
$this->renderTestEntity($id);
- $this->assertText($expected, format_string('Formatted date field using %value format displayed as %expected.', array('%value' => $new_value, '%expected' => $expected)));
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected.', array('%value' => $new_value, '%expected' => $expected)));
break;
}
}
@@ -148,13 +172,65 @@ function testDateField() {
// Verify that the plain formatter works.
$this->displayOptions['type'] = 'datetime_plain';
- $this->displayOptions['settings'] = array();
+ $this->displayOptions['settings'] = $this->defaultSettings;
entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
->setComponent($field_name, $this->displayOptions)
->save();
$expected = $date->format(DATETIME_DATE_STORAGE_FORMAT);
$this->renderTestEntity($id);
- $this->assertText($expected, format_string('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected)));
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected)));
+
+ // Verify that the 'datetime_custom' formatter works.
+ $this->displayOptions['type'] = 'datetime_custom';
+ $this->displayOptions['settings'] = array('date_format' => 'm/d/Y') + $this->defaultSettings;
+ entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+ ->setComponent($field_name, $this->displayOptions)
+ ->save();
+ $expected = $date->format($this->displayOptions['settings']['date_format']);
+ $this->renderTestEntity($id);
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_custom format displayed as %expected.', array('%expected' => $expected)));
+
+ // Verify that the 'datetime_time_ago' formatter works for intervals in the
+ // past. First update the test entity so that the date difference always
+ // has the same interval. Since the database always stores UTC, and the
+ // interval will use this, force the test date to use UTC and not the local
+ // or user timezome.
+ $entity = entity_load('entity_test', $id);
+ $field_name = $this->fieldStorage->getName();
+ $date = DrupalDateTime::createFromTimestamp(REQUEST_TIME - 87654321, 'UTC');
+ $entity->{$field_name}->value = $date->format($date_format);
+ $entity->save();
+
+ $this->displayOptions['type'] = 'datetime_time_ago';
+ $this->displayOptions['settings'] = array(
+ 'future_format' => '@time in the future',
+ 'past_format' => '@time in the past',
+ 'granularity' => 3,
+ );
+ entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+ ->setComponent($field_name, $this->displayOptions)
+ ->save();
+ $expected = '2 years 9 months 1 week in the past';
+ $this->renderTestEntity($id);
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_time_ago format displayed as %expected.', array('%expected' => $expected)));
+
+ // Verify that the 'datetime_time_ago' formatter works for intervals in the
+ // future. First update the test entity so that the date difference always
+ // has the same interval. Since the database always stores UTC, and the
+ // interval will use this, force the test date to use UTC and not the local
+ // or user timezome.
+ $entity = entity_load('entity_test', $id);
+ $field_name = $this->fieldStorage->getName();
+ $date = DrupalDateTime::createFromTimestamp(REQUEST_TIME + 87654321, 'UTC');
+ $entity->{$field_name}->value = $date->format($date_format);
+ $entity->save();
+
+ entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+ ->setComponent($field_name, $this->displayOptions)
+ ->save();
+ $expected = '2 years 9 months 1 week in the future';
+ $this->renderTestEntity($id);
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_time_ago format displayed as %expected.', array('%expected' => $expected)));
}
/**
@@ -171,9 +247,14 @@ function testDatetimeField() {
$this->assertFieldByName("{$field_name}[0][value][date]", '', 'Date element found.');
$this->assertFieldByName("{$field_name}[0][value][time]", '', 'Time element found.');
- // Submit a valid date and ensure it is accepted.
+ // Build up a date in the UTC timezone.
$value = '2012-12-31 00:00:00';
- $date = new DrupalDateTime($value);
+ $date = new DrupalDateTime($value, 'UTC');
+
+ // Update the timezone to the system default.
+ $date->setTimezone(timezone_open(drupal_get_user_timezone()));
+
+ // Submit a valid date and ensure it is accepted.
$date_format = entity_load('date_format', 'html_date')->getPattern();
$time_format = entity_load('date_format', 'html_time')->getPattern();
@@ -195,7 +276,7 @@ function testDatetimeField() {
foreach ($options as $setting => $values) {
foreach ($values as $new_value) {
// Update the entity display settings.
- $this->displayOptions['settings'] = array($setting => $new_value);
+ $this->displayOptions['settings'] = array($setting => $new_value) + $this->defaultSettings;
entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
->setComponent($field_name, $this->displayOptions)
->save();
@@ -206,7 +287,7 @@ function testDatetimeField() {
// Verify that a date is displayed.
$expected = format_date($date->getTimestamp(), $new_value);
$this->renderTestEntity($id);
- $this->assertText($expected, format_string('Formatted date field using %value format displayed as %expected.', array('%value' => $new_value, '%expected' => $expected)));
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected.', array('%value' => $new_value, '%expected' => $expected)));
break;
}
}
@@ -214,13 +295,75 @@ function testDatetimeField() {
// Verify that the plain formatter works.
$this->displayOptions['type'] = 'datetime_plain';
- $this->displayOptions['settings'] = array();
+ $this->displayOptions['settings'] = $this->defaultSettings;
entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
->setComponent($field_name, $this->displayOptions)
->save();
$expected = $date->format(DATETIME_DATETIME_STORAGE_FORMAT);
$this->renderTestEntity($id);
- $this->assertText($expected, format_string('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected)));
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected)));
+
+ // Verify that the 'datetime_custom' formatter works.
+ $this->displayOptions['type'] = 'datetime_custom';
+ $this->displayOptions['settings'] = array('date_format' => 'm/d/Y g:i:s A') + $this->defaultSettings;
+ entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+ ->setComponent($field_name, $this->displayOptions)
+ ->save();
+ $expected = $date->format($this->displayOptions['settings']['date_format']);
+ $this->renderTestEntity($id);
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_custom format displayed as %expected.', array('%expected' => $expected)));
+
+ // Verify that the 'timezone_override' setting works.
+ $this->displayOptions['type'] = 'datetime_custom';
+ $this->displayOptions['settings'] = array('date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York') + $this->defaultSettings;
+ entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+ ->setComponent($field_name, $this->displayOptions)
+ ->save();
+ $expected = $date->format($this->displayOptions['settings']['date_format'], array('timezone' => 'America/New_York'));
+ $this->renderTestEntity($id);
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_custom format displayed as %expected.', array('%expected' => $expected)));
+
+ // Verify that the 'datetime_time_ago' formatter works for intervals in the
+ // past. First update the test entity so that the date difference always
+ // has the same interval. Since the database always stores UTC, and the
+ // interval will use this, force the test date to use UTC and not the local
+ // or user timezome.
+ $entity = entity_load('entity_test', $id);
+ $field_name = $this->fieldStorage->getName();
+ $date = DrupalDateTime::createFromTimestamp(REQUEST_TIME - 87654321, 'UTC');
+ $entity->{$field_name}->value = $date->format(DATETIME_DATETIME_STORAGE_FORMAT);
+ $entity->save();
+
+ $this->displayOptions['type'] = 'datetime_time_ago';
+ $this->displayOptions['settings'] = array(
+ 'future_format' => '@time from now',
+ 'past_format' => '@time earlier',
+ 'granularity' => 3,
+ );
+ entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+ ->setComponent($field_name, $this->displayOptions)
+ ->save();
+ $expected = '2 years 9 months 1 week earlier';
+ $this->renderTestEntity($id);
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_time_ago format displayed as %expected.', array('%expected' => $expected)));
+
+ // Verify that the 'datetime_time_ago' formatter works for intervals in the
+ // future. First update the test entity so that the date difference always
+ // has the same interval. Since the database always stores UTC, and the
+ // interval will use this, force the test date to use UTC and not the local
+ // or user timezome.
+ $entity = entity_load('entity_test', $id);
+ $field_name = $this->fieldStorage->getName();
+ $date = DrupalDateTime::createFromTimestamp(REQUEST_TIME + 87654321, 'UTC');
+ $entity->{$field_name}->value = $date->format(DATETIME_DATETIME_STORAGE_FORMAT);
+ $entity->save();
+
+ entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+ ->setComponent($field_name, $this->displayOptions)
+ ->save();
+ $expected = '2 years 9 months 1 week from now';
+ $this->renderTestEntity($id);
+ $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_time_ago format displayed as %expected.', array('%expected' => $expected)));
}
/**
@@ -391,7 +534,6 @@ function testDefaultValue() {
* Test that invalid values are caught and marked as invalid.
*/
function testInvalidField() {
-
// Change the field to a datetime field.
$this->fieldStorage->setSetting('datetime_type', 'datetime');
$this->fieldStorage->save();
diff --git a/core/modules/field/src/Tests/Timestamp/TimestampFormatterTest.php b/core/modules/field/src/Tests/Timestamp/TimestampFormatterTest.php
new file mode 100644
index 0000000..6aa1588
--- /dev/null
+++ b/core/modules/field/src/Tests/Timestamp/TimestampFormatterTest.php
@@ -0,0 +1,194 @@
+installConfig(['system']);
+ $this->installConfig(['field']);
+ $this->installEntitySchema('entity_test');
+
+ $this->entityType = 'entity_test';
+ $this->bundle = $this->entityType;
+ $this->fieldName = Unicode::strtolower($this->randomMachineName());
+
+ $field_storage = FieldStorageConfig::create([
+ 'field_name' => $this->fieldName,
+ 'entity_type' => $this->entityType,
+ 'type' => 'timestamp',
+ ]);
+ $field_storage->save();
+
+ $instance = FieldConfig::create([
+ 'field_storage' => $field_storage,
+ 'bundle' => $this->bundle,
+ 'label' => $this->randomMachineName(),
+ ]);
+ $instance->save();
+
+ $this->display = entity_get_display($this->entityType, $this->bundle, 'default')
+ ->setComponent($this->fieldName, [
+ 'type' => 'boolean',
+ 'settings' => [],
+ ]);
+ $this->display->save();
+ }
+
+ /**
+ * Renders fields of a given entity with a given display.
+ *
+ * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
+ * The entity object with attached fields to render.
+ * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
+ * The display to render the fields in.
+ *
+ * @return string
+ * The rendered entity fields.
+ */
+ protected function renderEntityFields(FieldableEntityInterface $entity, EntityViewDisplayInterface $display) {
+ $content = $display->build($entity);
+ $content = $this->render($content);
+ return $content;
+ }
+
+ /**
+ * Tests TimestampFormatter.
+ */
+ protected function testTimestampFormatter() {
+ $data = [];
+
+ // Test standard formats.
+ $date_formats = array_keys(\Drupal::entityManager()->getStorage('date_format')->loadMultiple());
+
+ foreach ($date_formats as $date_format) {
+ $data[] = ['date_format' => $date_format, 'custom_date_format' => '', 'timezone' => ''];
+ }
+
+ $data[] = ['date_format' => 'custom', 'custom_date_format' => 'r', 'timezone' => ''];
+ $data[] = ['date_format' => 'custom', 'custom_date_format' => 'e', 'timezone' => 'Asia/Tokyo'];
+
+ foreach ($data as $settings) {
+ list($date_format, $custom_date_format, $timezone) = array_values($settings);
+ if (empty($timezone)) {
+ $timezone = NULL;
+ }
+
+ $value = REQUEST_TIME - 87654321;
+ $expected = \Drupal::service('date.formatter')->format($value, $date_format, $custom_date_format, $timezone);
+
+ $component = $this->display->getComponent($this->fieldName);
+ $component['type'] = 'timestamp';
+ $component['settings'] = $settings;
+ $this->display->setComponent($this->fieldName, $component);
+
+ $entity = EntityTest::create([]);
+ $entity->{$this->fieldName}->value = $value;
+
+ $this->renderEntityFields($entity, $this->display);
+ $this->assertRaw($expected);
+ }
+ }
+
+ /**
+ * Tests TimestampAgoFormatter.
+ */
+ protected function testTimestampAgoFormatter() {
+ $data = [];
+
+ foreach (array(1,2,3,4,5,6) as $granularity) {
+ $data[] = [
+ 'future_format' => '@time hence',
+ 'past_format' => '@time ago',
+ 'granularity' => $granularity,
+ ];
+ }
+
+ foreach ($data as $settings) {
+ $future_format = $settings['future_format'];
+ $past_format = $settings['past_format'];
+ $granularity = $settings['granularity'];
+ $request_time = \Drupal::requestStack()->getCurrentRequest()->server->get('REQUEST_TIME');
+
+ // Test a timestamp in the past
+ $value = $request_time - 87654321;
+ $expected = SafeMarkup::format($past_format, ['@time' => \Drupal::service('date.formatter')->formatTimeDiffSince($value, ['granularity' => $granularity])]);
+
+ $component = $this->display->getComponent($this->fieldName);
+ $component['type'] = 'timestamp_ago';
+ $component['settings'] = $settings;
+ $this->display->setComponent($this->fieldName, $component);
+
+ $entity = EntityTest::create([]);
+ $entity->{$this->fieldName}->value = $value;
+
+ $this->renderEntityFields($entity, $this->display);
+ $this->assertRaw($expected);
+
+ // Test a timestamp in the future
+ $value = $request_time + 87654321;
+ $expected = SafeMarkup::format($future_format, ['@time' => \Drupal::service('date.formatter')->formatTimeDiffUntil($value, ['granularity' => $granularity])]);
+
+ $component = $this->display->getComponent($this->fieldName);
+ $component['type'] = 'timestamp_ago';
+ $component['settings'] = $settings;
+ $this->display->setComponent($this->fieldName, $component);
+
+ $entity = EntityTest::create([]);
+ $entity->{$this->fieldName}->value = $value;
+
+ $this->renderEntityFields($entity, $this->display);
+ $this->assertRaw($expected);
+ }
+ }
+
+}
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_formatter_settings.yml b/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_formatter_settings.yml
index c5165b3..594ff4b 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_formatter_settings.yml
+++ b/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_formatter_settings.yml
@@ -231,14 +231,19 @@ process:
date:
default:
format_type: fallback
+ timezone_override: ''
format_interval:
format_type: fallback
+ timezone_override: ''
long:
format_type: long
+ timezone_override: ''
medium:
format_type: medium
+ timezone_override: ''
short:
format_type: short
+ timezone_override: ''
text:
trimmed:
trim_length: 600
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldFormatterSettingsTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldFormatterSettingsTest.php
index eb26f9c..7780b2c 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldFormatterSettingsTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldFormatterSettingsTest.php
@@ -196,9 +196,10 @@ public function testEntityDisplaySettings() {
$this->assertIdentical($expected, $component);
// Test date field.
+ $defaults = array('format_type' => 'fallback', 'timezone_override' => '',);
$expected['weight'] = 10;
$expected['type'] = 'datetime_default';
- $expected['settings'] = array('format_type' => 'fallback');
+ $expected['settings'] = array('format_type' => 'fallback') + $defaults;
$component = $display->getComponent('field_test_date');
$this->assertIdentical($expected, $component);
$display = entity_load('entity_view_display', 'node.story.default');
@@ -212,13 +213,13 @@ public function testEntityDisplaySettings() {
$component = $display->getComponent('field_test_datestamp');
$this->assertIdentical($expected, $component);
$display = entity_load('entity_view_display', 'node.story.teaser');
- $expected['settings'] = array('format_type' => 'medium');
+ $expected['settings'] = array('format_type' => 'medium') + $defaults;
$component = $display->getComponent('field_test_datestamp');
$this->assertIdentical($expected, $component);
// Test datetime field.
$expected['weight'] = 12;
- $expected['settings'] = array('format_type' => 'short');
+ $expected['settings'] = array('format_type' => 'short') + $defaults;
$component = $display->getComponent('field_test_datetime');
$this->assertIdentical($expected, $component);
$display = entity_load('entity_view_display', 'node.story.default');