diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index 90813e5..cc1a57e 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: 'Time zone'
+
 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..ea99742 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,8 +88,66 @@ 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' => '@interval hence',
+      'past_format' => '@interval 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('Use <em>@interval</em> where you want the formatted interval text to appear.'),
+    );
+
+    $form['past_format'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Past format'),
+      '#default_value' => $this->getSetting('past_format'),
+      '#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
+    );
+
+    $elements['granularity'] = array(
+      '#type' => 'number',
+      '#title' => $this->t('Granularity'),
+      '#description' => $this->t('How many time interval 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[] = $this->t('Future date: %display', array('%display' => $this->formatTimestamp($future_date)));
+    $summary[] = $this->t('Past date: %display', array('%display' => $this->formatTimestamp($past_date)));
+
+    return $summary;
   }
 
   /**
@@ -87,7 +158,7 @@ public function viewElements(FieldItemListInterface $items) {
 
     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,25 @@ public function viewElements(FieldItemListInterface $items) {
     return $elements;
   }
 
+  /**
+   * Formats a timestamp.
+   *
+   * @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];
+
+    if ($this->request->server->get('REQUEST_TIME') - $timestamp > 0) {
+      return SafeMarkup::format($this->getSetting('past_format'), ['@interval' => $this->dateFormatter->formatTimeDiffSince($timestamp, $options)]);
+    }
+    else {
+      return SafeMarkup::format($this->getSetting('future_format'), ['@interval' => $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..855b880 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,137 @@
  *   }
  * )
  */
-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 format 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('Used if Date format is set to Custom. See <a href="http://us.php.net/manual/en/function.date.php" target="_blank">the documentation for PHP date formats</a>.'),
+      '#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'),
+      '#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 +167,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 +185,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..248c0bd
--- /dev/null
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeCustomFormatter.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\datetime\Plugin\Field\FieldFormatter\DateTimeCustomFormatter.
+ */
+
+namespace Drupal\datetime\Plugin\Field\FieldFormatter;
+
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Plugin implementation of the 'Custom' formatter for 'datetime' fields.
+ *
+ * @FieldFormatter(
+ *   id = "datetime_custom",
+ *   label = @Translation("Custom"),
+ *   field_types = {
+ *     "datetime"
+ *   }
+ *)
+ */
+class DateTimeCustomFormatter extends DateTimeFormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings() {
+    return array(
+      'date_format' => 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 = $this->formatDate($date);
+      }
+      $elements[$delta] = [
+        '#markup' => $output,
+        '#cache' => [
+          'contexts' => [
+            'timezone',
+          ],
+        ],
+      ];
+    }
+
+    return $elements;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function formatDate($date) {
+    $format = $this->getSetting('date_format');
+    $timezone = $this->getSetting('timezone_override');
+    return $this->dateFormatter->format($date->getTimestamp(), 'custom', $format, $timezone != '' ? $timezone : NULL);
+  }
+
+  /**
+   * {@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('See <a href="http://us.php.net/manual/en/function.date.php" target="_blank">the documentation for PHP date formats</a>.'),
+      '#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..588f69f 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
@@ -7,18 +7,12 @@
 
 namespace Drupal\datetime\Plugin\Field\FieldFormatter;
 
-use Drupal\Core\Datetime\DateFormatter;
 use Drupal\Core\Datetime\DrupalDateTime;
-use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\Core\Field\FieldDefinitionInterface;
 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 +22,7 @@
  *   }
  * )
  */
-class DateTimeDefaultFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
+class DateTimeDefaultFormatter extends DateTimeFormatterBase {
 
   /**
    * {@inheritdoc}
@@ -40,89 +34,28 @@ public static function defaultSettings() {
   }
 
   /**
-   * The date formatter service.
-   *
-   * @var \Drupal\Core\Datetime\DateFormatter
-   */
-  protected $dateFormatter;
-
-  /**
-   * The date storage.
-   *
-   * @var \Drupal\Core\Entity\EntityStorageInterface
-   */
-  protected $dateStorage;
-
-  /**
-   * Constructs a new DateTimeDefaultFormatter.
-   *
-   * @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_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) {
-    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
-
-    $this->dateFormatter = $date_formatter;
-    $this->dateStorage = $date_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 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);
       }
 
       // Display the date using theme datetime.
@@ -133,7 +66,8 @@ public function viewElements(FieldItemListInterface $items) {
           ],
         ],
         '#theme' => 'time',
-        '#text' => $formatted_date,
+        '#text' => $output,
+        '#html' => FALSE,
         '#attributes' => array(
           'datetime' => $iso_date,
         ),
@@ -151,31 +85,29 @@ public function viewElements(FieldItemListInterface $items) {
   }
 
   /**
-   * Creates a formatted date value as a string.
-   *
-   * @param object $date
-   *   A date object.
-   *
-   * @return string
-   *   A formatted date string using the chosen format.
+   * {@inheritdoc}
    */
-  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 +115,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..aad8c16
--- /dev/null
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeFormatterBase.php
@@ -0,0 +1,167 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\datetime\Plugin\Field\FieldFormatter\DateTimeFormatterBase.
+ */
+
+namespace Drupal\datetime\Plugin\Field\FieldFormatter;
+
+use Drupal\Core\Datetime\DateFormatter;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+
+/**
+ * Base class for 'DateTime Field formatter' plugin implementations.
+ */
+abstract class DateTimeFormatterBase 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 DateTimeDefaultFormatter.
+   *
+   * @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 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_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(
+      'timezone_override' => '',
+    ) + 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;
+  }
+
+  /**
+   * Creates a formatted date value as a string.
+   *
+   * @param object $date
+   *   A date object.
+   *
+   * @return string
+   *   A formatted date string using the chosen format.
+   */
+  abstract protected function formatDate($date);
+
+  /**
+   * Sets the proper time zone on a DrupalDateTime object for the current user.
+   *
+   * A DrupalDateTime object loaded from the database will have the UTC time
+   * zone applied to it.  This method will apply the time zone for the current
+   * user, based on system and user settings.
+   *
+   * @see drupal_get_user_timezone()
+   *
+   * @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..b92c07c 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,29 @@
  *   }
  *)
  */
-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 {
+        }
+        $this->setTimeZone($date);
+
+        $output = $this->formatDate($date);
       }
       $elements[$delta] = [
         '#cache' => [
@@ -59,4 +57,13 @@ public function viewElements(FieldItemListInterface $items) {
     return $elements;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function formatDate($date) {
+    $format = $this->getFieldSetting('datetime_type') == 'date' ? DATETIME_DATE_STORAGE_FORMAT : DATETIME_DATETIME_STORAGE_FORMAT;
+    $timezone = $this->getSetting('timezone_override');
+    return $this->dateFormatter->format($date->getTimestamp(), 'custom', $format, $timezone != '' ? $timezone : NULL);
+  }
+
 }
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..6db7cf8
--- /dev/null
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeTimeAgoFormatter.php
@@ -0,0 +1,195 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\datetime\Plugin\Field\FieldFormatter\DateTimeTimeAgoFormatter.
+ */
+
+namespace Drupal\datetime\Plugin\Field\FieldFormatter;
+
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Datetime\DateFormatter;
+use Drupal\Core\Datetime\DrupalDateTime;
+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\HttpFoundation\Request;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Plugin implementation of the 'Time ago' formatter for 'datetime' fields.
+ *
+ * @FieldFormatter(
+ *   id = "datetime_time_ago",
+ *   label = @Translation("Time ago"),
+ *   field_types = {
+ *     "datetime"
+ *   }
+ * )
+ */
+class DateTimeTimeAgoFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The date formatter service.
+   *
+   * @var \Drupal\Core\Datetime\DateFormatter
+   */
+  protected $dateFormatter;
+
+  /**
+   * The current Request object.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * Constructs a DateTimeTimeAgoFormatter object.
+   *
+   * @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 \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, Request $request) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
+
+    $this->dateFormatter = $date_formatter;
+    $this->request = $request;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings() {
+    $settings = array(
+      'future_format' => '@interval hence',
+      'past_format' => '@interval 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('Use <em>@interval</em> where you want the formatted interval text to appear.'),
+    );
+
+    $form['past_format'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Past format'),
+      '#default_value' => $this->getSetting('past_format'),
+      '#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
+    );
+
+    $form['granularity'] = array(
+      '#type' => 'number',
+      '#title' => $this->t('Granularity'),
+      '#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;
+  }
+
+  /**
+   * Formats a date/time as a time interval.
+   *
+   * @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];
+
+    if ($this->request->server->get('REQUEST_TIME') - $timestamp > 0) {
+      return SafeMarkup::format($this->getSetting('past_format'), ['@interval' => $this->dateFormatter->formatTimeDiffSince($timestamp, $options)]);
+    }
+    else {
+      return SafeMarkup::format($this->getSetting('future_format'), ['@interval' => $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..bcf40b6 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,71 @@ 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.
+    $timestamp = REQUEST_TIME - 87654321;
+    $entity = entity_load('entity_test', $id);
+    $field_name = $this->fieldStorage->getName();
+    $date = DrupalDateTime::createFromTimestamp($timestamp, 'UTC');
+    $entity->{$field_name}->value = $date->format($date_format);
+    $entity->save();
+
+    $this->displayOptions['type'] = 'datetime_time_ago';
+    $this->displayOptions['settings'] = array(
+      'future_format' => '@interval in the future',
+      'past_format' => '@interval in the past',
+      'granularity' => 3,
+    );
+    entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+      ->setComponent($field_name, $this->displayOptions)
+      ->save();
+    $expected = SafeMarkup::format($this->displayOptions['settings']['past_format'], [
+      '@interval' => \Drupal::service('date.formatter')->formatTimeDiffSince($timestamp, ['granularity' => $this->displayOptions['settings']['granularity']])
+    ]);
+    $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.
+    $timestamp = REQUEST_TIME + 87654321;
+    $entity = entity_load('entity_test', $id);
+    $field_name = $this->fieldStorage->getName();
+    $date = DrupalDateTime::createFromTimestamp($timestamp, '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 = SafeMarkup::format($this->displayOptions['settings']['future_format'], [
+      '@interval' => \Drupal::service('date.formatter')->formatTimeDiffUntil($timestamp, ['granularity' => $this->displayOptions['settings']['granularity']])
+    ]);
+    $this->renderTestEntity($id);
+    $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_time_ago format displayed as %expected.', array('%expected' => $expected)));
   }
 
   /**
@@ -171,9 +253,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 +282,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 +293,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 +301,81 @@ 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.
+    $timestamp = REQUEST_TIME - 87654321;
+    $entity = entity_load('entity_test', $id);
+    $field_name = $this->fieldStorage->getName();
+    $date = DrupalDateTime::createFromTimestamp($timestamp, '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' => '@interval from now',
+      'past_format' => '@interval earlier',
+      'granularity' => 3,
+    );
+    entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+      ->setComponent($field_name, $this->displayOptions)
+      ->save();
+    $expected = SafeMarkup::format($this->displayOptions['settings']['past_format'], [
+      '@interval' => \Drupal::service('date.formatter')->formatTimeDiffSince($timestamp, ['granularity' => $this->displayOptions['settings']['granularity']])
+    ]);
+    $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.
+    $timestamp = REQUEST_TIME + 87654321;
+    $entity = entity_load('entity_test', $id);
+    $field_name = $this->fieldStorage->getName();
+    $date = DrupalDateTime::createFromTimestamp($timestamp, '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 = SafeMarkup::format($this->displayOptions['settings']['future_format'], [
+      '@interval' => \Drupal::service('date.formatter')->formatTimeDiffUntil($timestamp, ['granularity' => $this->displayOptions['settings']['granularity']])
+    ]);
+    $this->renderTestEntity($id);
+    $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_time_ago format displayed as %expected.', array('%expected' => $expected)));
   }
 
   /**
@@ -391,7 +546,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..8ef98d6
--- /dev/null
+++ b/core/modules/field/src/Tests/Timestamp/TimestampFormatterTest.php
@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field\Tests\Timestamp\TimestampFormatterTest.
+ */
+
+namespace Drupal\field\Tests\Timestamp;
+
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\simpletest\KernelTestBase;
+
+/**
+ * Tests the timestamp formatters.
+ *
+ * @group field
+ */
+class TimestampFormatterTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'field', 'text', 'entity_test', 'user'];
+
+  /**
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * @var string
+   */
+  protected $bundle;
+
+  /**
+   * @var string
+   */
+  protected $fieldName;
+
+  /**
+   * @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
+   */
+  protected $display;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->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' => '@interval hence',
+        'past_format' => '@interval 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, ['@interval' => \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, ['@interval' => \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');
