diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml index 8a15a4c..e4821c4 100644 --- a/core/config/schema/core.entity.schema.yml +++ b/core/config/schema/core.entity.schema.yml @@ -322,9 +322,12 @@ field.formatter.settings.timestamp_ago: type: mapping label: 'Timestamp ago display format settings' mapping: - date_format: + future_format: type: string - label: 'Date format' + label: 'Future format' + past_format: + type: string + label: 'Past format' granularity: type: integer label: 'Granularity' 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 34cf92c..70f6636 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php @@ -7,6 +7,7 @@ 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; @@ -14,6 +15,7 @@ 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. @@ -38,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 @@ -56,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; } /** @@ -76,7 +88,8 @@ 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() ); } @@ -85,7 +98,8 @@ public static function create(ContainerInterface $container, array $configuratio */ public static function defaultSettings() { return array( - 'date_format' => 'raw time ago', + 'future_format' => '@time hence', + 'past_format' => '@time ago', 'granularity' => 2, ) + parent::defaultSettings(); } @@ -96,25 +110,24 @@ public static function defaultSettings() { public function settingsForm(array $form, FormStateInterface $form_state) { $elements = parent::settingsForm($form, $form_state); - $elements['date_format'] = array( - '#type' => 'select', - '#title' => $this->t('Date format'), - '#options' => array( - 'raw time ago' => $this->t('Time ago'), - 'time ago' => $this->t('Time ago (with "ago" appended)'), - 'raw time hence' => $this->t('Time hence'), - 'time hence' => $this->t('Time hence (with "hence" appended)'), - 'raw time span' => $this->t('Time span (future dates have "-" prepended)'), - 'inverse time span' => $this->t('Time span (past dates have "-" prepended)'), - 'time span' => $this->t('Time span (with "ago/hence" appended)'), - ), - '#default_value' => $this->getSetting('date_format') ?: 'raw time ago', + $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 "time ago" string.'), + '#description' => $this->t('How many time units should be shown in the formatted output.'), '#default_value' => $this->getSetting('granularity') ?: 2, '#min' => 1, '#max' => 7, @@ -129,8 +142,10 @@ public function settingsForm(array $form, FormStateInterface $form_state) { public function settingsSummary() { $summary = parent::settingsSummary(); - $summary[] = $this->t('Date format: @date_format', array('@date_format' => $this->getSetting('date_format'))); - $summary[] = $this->t('Granularity: @granularity', array('@granularity' => $this->getSetting('granularity'))); + $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; } @@ -143,34 +158,7 @@ public function viewElements(FieldItemListInterface $items) { foreach ($items as $delta => $item) { if ($item->value) { - $format = $this->getSetting('date_format'); - $granularity = $this->getSetting('granularity'); - // Will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence). - $time_diff = REQUEST_TIME - $item->value; - switch ($format) { - case 'time ago': - $updated = $this->t('%time ago', array('%time' => $this->dateFormatter->formatInterval($time_diff, $granularity))); - break; - case 'raw time hence': - $updated = $this->dateFormatter->formatInterval(-$time_diff, $granularity); - break; - case 'time hence': - $updated = $this->t('%time hence', array('%time' => $this->dateFormatter->formatInterval(-$time_diff, $granularity))); - break; - case 'raw time span': - $updated = ($time_diff < 0 ? '-' : '') . $this->dateFormatter->formatInterval(abs($time_diff), $granularity); - break; - case 'inverse time span': - $updated = ($time_diff > 0 ? '-' : '') . $this->dateFormatter->formatInterval(abs($time_diff), $granularity); - break; - case 'time span': - $updated = $this->t(($time_diff < 0 ? '%time hence' : '%time ago'), array('%time' => $this->dateFormatter->formatInterval(abs($time_diff), $granularity))); - break; - case 'raw time ago': - default: - $updated = $this->dateFormatter->formatInterval($time_diff, $granularity); - break; - } + $updated = $this->formatTimestamp($item->value); } else { $updated = $this->t('never'); @@ -182,4 +170,31 @@ public function viewElements(FieldItemListInterface $items) { return $elements; } + /** + * Creates a formatted timestamp value as a string. + * + * @todo Refactor when https://www.drupal.org/node/2456521 is committed. + * + * @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'); + // Will be positive for a timestamp in the past (ago), and negative for a datetime in the future (hence). + + $interval = $this->request->server->get('REQUEST_TIME') - $timestamp; + + if ($interval > 0) { + return SafeMarkup::format($this->getSetting('past_format'), ['@time' => $this->dateFormatter->formatInterval($interval, $granularity)]); + } + else { + // DateFormatter::formatInterval() assumes positive intervals, so negate + // it to force the calculation to work properly. + return SafeMarkup::format($this->getSetting('future_format'), ['@time' => $this->dateFormatter->formatInterval(-$interval, $granularity)]); + } + } + } 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 cffc141..f5515c0 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php @@ -39,14 +39,14 @@ class TimestampFormatter extends FormatterBase implements ContainerFactoryPlugin protected $dateFormatter; /** - * The date storage. + * The date format entity storage. * * @var \Drupal\Core\Entity\EntityStorageInterface */ - protected $dateStorage; + protected $dateFormatStorage; /** - * Constructs a new DateTimeDefaultFormatter. + * Constructs a new TimestampFormatter. * * @param string $plugin_id * The plugin_id for the formatter. @@ -64,14 +64,14 @@ class TimestampFormatter extends FormatterBase implements ContainerFactoryPlugin * Third party settings. * @param \Drupal\Core\Datetime\DateFormatter $date_formatter * The date formatter service. - * @param \Drupal\Core\Entity\EntityStorageInterface $date_storage + * @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_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; } /** @@ -110,7 +110,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) { $date_formats = array(); - foreach ($this->dateStorage->loadMultiple() as $machine_name => $value) { + 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))); } @@ -136,9 +136,9 @@ public function settingsForm(array $form, FormStateInterface $form_state) { $elements['timezone'] = array( '#type' => 'select', - '#title' => $this->t('Timezone'), - '#description' => $this->t('Timezone to be used for date output.'), - '#options' => array('' => $this->t('- Default site/user timezone -')) + system_time_zones(FALSE), + '#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'), ); @@ -153,7 +153,7 @@ public function settingsSummary() { $summary[] = $this->t('Date format: @date_format', array('@date_format' => $this->getSetting('date_format'))); $summary[] = $this->t('Custom date format: @custom_date_format', array('@custom_date_format' => $this->getSetting('custom_date_format'))); - $summary[] = $this->t('Timezone: @timezone', array('@timezone' => $this->getSetting('timezone'))); + $summary[] = $this->t('Time zone: @timezone', array('@timezone' => $this->getSetting('timezone'))); return $summary; }