diff --git a/core/lib/Drupal/Core/Datetime/Element/Datetime.php b/core/lib/Drupal/Core/Datetime/Element/Datetime.php
index 44a3949..51a2b08 100644
--- a/core/lib/Drupal/Core/Datetime/Element/Datetime.php
+++ b/core/lib/Drupal/Core/Datetime/Element/Datetime.php
@@ -61,6 +61,7 @@ public function getInfo() {
       '#date_year_range' => '1900:2050',
       '#date_increment' => 1,
       '#date_timezone' => '',
+      '#expose_timezone' => FALSE,
     );
   }
 
@@ -73,6 +74,11 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
       $time_input  = $element['#date_time_element'] != 'none' && !empty($input['time']) ? $input['time'] : '';
       $date_format = $element['#date_date_element'] != 'none' ? static::getHtml5DateFormat($element) : '';
       $time_format = $element['#date_time_element'] != 'none' ? static::getHtml5TimeFormat($element) : '';
+
+      // Timezone.
+      if ($element['#expose_timezone'] && $input['timezone']) {
+        $element['#date_timezone'] = $input['timezone'];
+      }
       $timezone = !empty($element['#date_timezone']) ? $element['#date_timezone'] : NULL;
 
       // Seconds will be omitted in a post in case there's no entry.
@@ -91,6 +97,7 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
       $input = array(
         'date'   => $date_input,
         'time'   => $time_input,
+        'timezone' => $timezone,
         'object' => $date,
       );
     }
@@ -100,6 +107,7 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
         $input = array(
           'date'   => $date->format($element['#date_date_format']),
           'time'   => $date->format($element['#date_time_format']),
+          'timezone' => $date->getTimezone()->getName(),
           'object' => $date,
         );
       }
@@ -107,6 +115,7 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
         $input = array(
           'date'   => '',
           'time'   => '',
+          'timezone' => '',
           'object' => NULL,
         );
       }
@@ -190,6 +199,8 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
    *     provided, this value will be ignored, the timezone in the default date
    *     takes precedence. Defaults to the value returned by
    *     drupal_get_user_timezone().
+   *   - #expose_timezone: a boolean that if set to TRUE, will expose a timezone
+   *     select list. Defaults to FALSE.
    *
    * Example usage:
    * @code
@@ -308,6 +319,17 @@ public static function processDatetime(&$element, FormStateInterface $form_state
       }
     }
 
+    // Expose a timezone selector.
+    if ($element['#expose_timezone']) {
+      $element['timezone'] = array(
+        '#type' => 'select',
+        '#options' => array_combine(\DateTimeZone::listIdentifiers(), \DateTimeZone::listIdentifiers()),
+        // Default to user's timezone.
+        '#default_value' => $element['#date_timezone'],
+        '#required' => $element['#required'],
+      );
+    }
+
     return $element;
   }
 
diff --git a/core/modules/datetime/config/schema/datetime.schema.yml b/core/modules/datetime/config/schema/datetime.schema.yml
index a009011..7e01885 100644
--- a/core/modules/datetime/config/schema/datetime.schema.yml
+++ b/core/modules/datetime/config/schema/datetime.schema.yml
@@ -7,6 +7,9 @@ field.storage_settings.datetime:
     datetime_type:
       type: string
       label: 'Date type'
+    timezone_handling:
+      type: string
+      label: 'Timezone handling'
 
 field.field_settings.datetime:
   type: mapping
@@ -26,6 +29,9 @@ field.value.datetime:
 field.formatter.settings.datetime_base:
   type: mapping
   mapping:
+    timezone_display:
+      type: string
+      label: 'Timezone display'
     timezone_override:
       type: string
       label: 'Time zone override'
diff --git a/core/modules/datetime/datetime.install b/core/modules/datetime/datetime.install
new file mode 100644
index 0000000..463c42a
--- /dev/null
+++ b/core/modules/datetime/datetime.install
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Update hooks for the Datetime module.
+ */
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
+use Drupal\field\Entity\FieldStorageConfig;
+
+/**
+ * Add the 'timezone' field to all datetime field tables.
+ */
+function datetime_update_8001(array &$sandbox) {
+
+  if (!isset($sandbox['progress'])) {
+    $sandbox['progress'] = 0;
+    $sandbox['current_id'] = '';
+    $sandbox['processed'] = [];
+
+    // Find all required updates.
+    $change_list = [];
+    foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_id => $entity_type) {
+      $storage = \Drupal::entityTypeManager()->getStorage($entity_type_id);
+      if ($storage instanceof DynamicallyFieldableEntityStorageSchemaInterface) {
+        $field_changes = [];
+        $field_storage_definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type_id);
+        $original_storage_definitions = \Drupal::service('entity.last_installed_schema.repository')->getLastInstalledFieldStorageDefinitions($entity_type_id);
+        foreach (array_intersect_key($field_storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
+          if ($storage_definition->getType() === 'datetime' && $storage->requiresFieldStorageSchemaChanges($storage_definition, $original_storage_definitions[$field_name])) {
+            /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
+            $table_mapping = $storage->getTableMapping();
+            $field_changes[$field_name] = $table_mapping->getFieldTableName($field_name);
+          }
+        }
+
+        if (!empty($field_changes)) {
+
+
+          $change_list[$entity_type_id] = [
+            'field_storage_definitions' => $field_changes,
+            'revision_table' => $entity_type->getRevisionTable(),
+          ];
+        }
+      }
+    }
+
+    $sandbox['change_list'] = $change_list;
+    $sandbox['max'] = count($change_list);
+  }
+
+  $remaining = array_diff(array_keys($sandbox['change_list']), $sandbox['processed']);
+  if (!empty($remaining)) {
+    $entity_type_id = array_pop($remaining);
+    $field_changes = $sandbox['change_list'][$entity_type_id];
+
+    $field_spec = [
+      'description' => 'The date timezone.',
+      'type' => 'varchar',
+      'length' => 50,
+    ];
+    $schema = Database::getConnection()->schema();
+
+    foreach ($field_changes['field_storage_definitions'] as $field_name => $field_table) {
+      $timezone_field_name = $field_name . '_timezone';
+      $field_schema = [
+        'fields' => [
+          // Include the 'value' field in order to properly create the index.
+          $field_name . '_value' => [
+            'description' => 'The date value.',
+            'type' => 'varchar',
+            'length' => 20,
+          ],
+          $timezone_field_name => $field_spec,
+        ],
+        'indexes' => [
+          'value_timezone' => [$field_name . '_value', $timezone_field_name],
+        ],
+      ];
+      $schema->addField($field_table, $timezone_field_name, $field_spec);
+      $schema->addIndex($field_table, 'value_timezone', $field_schema['indexes']['value_timezone'], $field_schema);
+
+      if ($field_changes['revision_table']) {
+        $revision_table = $field_changes['revision_table'] . '__' . $field_name;
+        $schema->addField($revision_table, $timezone_field_name, $field_spec);
+        $schema->addIndex($revision_table, 'value_timezone', $field_schema['indexes']['value_timezone'], $field_schema);
+      }
+
+      // Set timezone handling to 'user', which is the behavior prior to the
+      // introduction of configurable timezone handling.
+      $field_storage = FieldStorageConfig::load($entity_type_id . '.' . $field_name);
+      $field_storage->setSetting('timezone_handling', DateTimeItem::TIMEZONE_USER);
+      $field_storage->save();
+      \Drupal::entityDefinitionUpdateManager()->updateFieldStorageDefinition($field_storage);
+    }
+    $sandbox['processed'][] = $entity_type_id;
+  }
+
+  $sandbox['#finished'] = empty($remaining);
+}
diff --git a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeCustomFormatter.php b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeCustomFormatter.php
index fe5fa75..4d25b57 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeCustomFormatter.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeCustomFormatter.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
 
 /**
  * Plugin implementation of the 'Custom' formatter for 'datetime' fields.
@@ -42,7 +43,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
         /** @var \Drupal\Core\Datetime\DrupalDateTime $date */
         $date = $item->date;
 
-        $elements[$delta] = $this->buildDate($date);
+        $elements[$delta] = $this->buildDate($date, $item->timezone);
       }
     }
 
@@ -54,8 +55,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
    */
   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);
+    return $this->dateFormatter->format($date->getTimestamp(), 'custom', $format, $date->getTimezone()->getName());
   }
 
   /**
@@ -82,7 +82,7 @@ public function settingsSummary() {
 
     $date = new DrupalDateTime();
     $this->setTimeZone($date);
-    $summary[] = $date->format($this->getSetting('date_format'), $this->getFormatSettings());
+    $summary[] = $date->format($this->getSetting('date_format'));
 
     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 8f703fb..6386630 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
 
 /**
  * Plugin implementation of the 'Default' formatter for 'datetime' fields.
@@ -32,8 +33,7 @@ public static function defaultSettings() {
    */
   protected function formatDate($date) {
     $format_type = $this->getSetting('format_type');
-    $timezone = $this->getSetting('timezone_override') ?: $date->getTimezone()->getName();
-    return $this->dateFormatter->format($date->getTimestamp(), $format_type, '', $timezone != '' ? $timezone : NULL);
+    return $this->dateFormatter->format($date->getTimestamp(), $format_type, '', $date->getTimezone()->getName());
   }
 
   /**
@@ -68,7 +68,8 @@ public function settingsSummary() {
     $summary = parent::settingsSummary();
 
     $date = new DrupalDateTime();
-    $summary[] = t('Format: @display', array('@display' => $this->formatDate($date, $this->getFormatSettings())));
+    $this->setTimeZone($date);
+    $summary[] = t('Format: @display', array('@display' => $this->formatDate($date)));
 
     return $summary;
   }
diff --git a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeFormatterBase.php b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeFormatterBase.php
index 22e5aa8..3a1df22 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeFormatterBase.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeFormatterBase.php
@@ -84,6 +84,7 @@ public static function create(ContainerInterface $container, array $configuratio
    */
   public static function defaultSettings() {
     return array(
+      'timezone_display' => DateTimeItem::TIMEZONE_USER,
       'timezone_override' => '',
     ) + parent::defaultSettings();
   }
@@ -94,13 +95,38 @@ public static function defaultSettings() {
   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'),
-    );
+    // Timezone display is only applicable to datetime items.
+    if ($this->fieldDefinition->getFieldStorageDefinition()->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATETIME) {
+      $form['timezone_display'] = array(
+        '#type' => 'select',
+        '#title' => $this->t('Timezone display'),
+        '#description' => $this->t('The timezone to use when displaying this date.'),
+        '#options' => array(
+          DateTimeItem::TIMEZONE_USER => $this->t("The user's timezone"),
+          DateTimeItem::TIMEZONE_NONE => $this->t("Timezone override"),
+        ),
+        '#default_value' => $this->getSetting('timezone_display'),
+      );
+
+      // If this field is using per-date timezone storage, add that as an
+      // option.
+      if ($this->fieldDefinition->getFieldStorageDefinition()->getSetting('timezone_handling') === DateTimeItem::TIMEZONE_DATE) {
+        $form['timezone_display']['#options'][DateTimeItem::TIMEZONE_DATE] = $this->t("The date's timezone");
+      }
+
+      $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'),
+        '#states' => array(
+          'visible' => array(
+            ':input[name="fields[' . $this->fieldDefinition->getName() . '][settings_edit_form][settings][timezone_display]"]' => ['value' => DateTimeItem::TIMEZONE_NONE],
+          ),
+        ),
+      );
+    }
 
     return $form;
   }
@@ -111,8 +137,16 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
   public function settingsSummary() {
     $summary = parent::settingsSummary();
 
-    if ($override = $this->getSetting('timezone_override')) {
-      $summary[] = $this->t('Time zone: @timezone', array('@timezone' => $override));
+    if ($this->fieldDefinition->getFieldStorageDefinition()->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATETIME) {
+      $timezone_display = $this->getSetting('timezone_display');
+      $timezone_override = $this->getSetting('timezone_override');
+      if ($timezone_display === DateTimeItem::TIMEZONE_NONE && $timezone_override) {
+        $summary[] = $this->t('Time zone: @timezone', array('@timezone' => $timezone_override));
+      }
+      else {
+        // @todo Make human-readable.
+        $summary[] = $this->t('Time zone display: @timezone', array('@timezone' => $timezone_display));
+      }
     }
 
     return $summary;
@@ -128,7 +162,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
       if ($item->date) {
         /** @var \Drupal\Core\Datetime\DrupalDateTime $date */
         $date = $item->date;
-        $elements[$delta] = $this->buildDateWithIsoAttribute($date);
+        $elements[$delta] = $this->buildDateWithIsoAttribute($date, $item->timezone);
 
         if (!empty($item->_attributes)) {
           $elements[$delta]['#attributes'] += $item->_attributes;
@@ -154,59 +188,57 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
   abstract protected function formatDate($date);
 
   /**
-   * Sets the proper time zone on a DrupalDateTime object for the current user.
+   * Sets the proper time zone on a DrupalDateTime object.
    *
    * 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.
+   * zone applied to it. This method applies the proper timezone based on
+   * the formatter configuration.
    *
    * @see drupal_get_user_timezone()
    *
    * @param \Drupal\Core\Datetime\DrupalDateTime $date
    *   A DrupalDateTime object.
+   * @param string $date_instance_timezone
+   *   (optional) The timezone associated with the specific date field instance.
    */
-  protected function setTimeZone(DrupalDateTime $date) {
+  protected function setTimeZone(DrupalDateTime $date, $date_instance_timezone = NULL) {
     if ($this->getFieldSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
       // A date without time has no timezone conversion.
       $timezone = DATETIME_STORAGE_TIMEZONE;
     }
     else {
-      $timezone = drupal_get_user_timezone();
+      $timezone_display = $this->getSetting('timezone_display');
+      $timezone_override = $this->getSetting('timezone_override');
+      if ($timezone_display === DateTimeItem::TIMEZONE_DATE && !empty($date_instance_timezone)) {
+        $timezone = $date_instance_timezone;
+      }
+      elseif ($timezone_display === DateTimeItem::TIMEZONE_NONE && $timezone_override) {
+        $timezone = $timezone_override;
+      }
+      else {
+        $timezone = drupal_get_user_timezone();
+      }
     }
     $date->setTimeZone(timezone_open($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;
-  }
-
-  /**
    * Creates a render array from a date object.
    *
    * @param \Drupal\Core\Datetime\DrupalDateTime $date
    *   A date object.
+   * @param string $timezone
+   *   (optional) A timezone to explicitly set the date to.
    *
    * @return array
    *   A render array.
    */
-  protected function buildDate(DrupalDateTime $date) {
+  protected function buildDate(DrupalDateTime $date, $timezone = NULL) {
     if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) {
       // A date without time will pick up the current time, use the default.
       datetime_date_default_time($date);
     }
-    $this->setTimeZone($date);
+    $this->setTimeZone($date, $timezone);
 
     $build = [
       '#markup' => $this->formatDate($date),
@@ -225,11 +257,13 @@ protected function buildDate(DrupalDateTime $date) {
    *
    * @param \Drupal\Core\Datetime\DrupalDateTime $date
    *   A date object.
+   * @param string $timezone
+   *   (optional) A timezone to explicitly set the date to.
    *
    * @return array
    *   A render array.
    */
-  protected function buildDateWithIsoAttribute(DrupalDateTime $date) {
+  protected function buildDateWithIsoAttribute(DrupalDateTime $date, $timezone = NULL) {
     if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) {
       // A date without time will pick up the current time, use the default.
       datetime_date_default_time($date);
@@ -238,7 +272,7 @@ protected function buildDateWithIsoAttribute(DrupalDateTime $date) {
     // Create the ISO date in Universal Time.
     $iso_date = $date->format("Y-m-d\TH:i:s") . 'Z';
 
-    $this->setTimeZone($date);
+    $this->setTimeZone($date, $timezone);
 
     $build = [
       '#theme' => 'time',
diff --git a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimePlainFormatter.php b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimePlainFormatter.php
index 5bb6766..04b007d 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimePlainFormatter.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimePlainFormatter.php
@@ -29,7 +29,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
         /** @var \Drupal\Core\Datetime\DrupalDateTime $date */
         $date = $item->date;
 
-        $elements[$delta] = $this->buildDate($date);
+        $elements[$delta] = $this->buildDate($date, $item->timezone);
       }
     }
 
@@ -41,8 +41,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
    */
   protected function formatDate($date) {
     $format = $this->getFieldSetting('datetime_type') == DateTimeItem::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);
+    return $this->dateFormatter->format($date->getTimestamp(), 'custom', $format, $date->getTimezone()->getName());
   }
 
 }
diff --git a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
index f07d564..5fa252b 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
@@ -28,6 +28,7 @@ class DateTimeItem extends FieldItemBase {
   public static function defaultStorageSettings() {
     return array(
       'datetime_type' => 'datetime',
+      'timezone_handling' => static::TIMEZONE_USER,
     ) + parent::defaultStorageSettings();
   }
 
@@ -42,6 +43,28 @@ public static function defaultStorageSettings() {
   const DATETIME_TYPE_DATETIME = 'datetime';
 
   /**
+   * Timezone uses the site's timezone, regardless of the user's timezone.
+   */
+  const TIMEZONE_SITE = 'site';
+
+  /**
+   * Timezone uses the user's timezone.
+   *
+   * @see drupal_get_user_timezone()
+   */
+  const TIMEZONE_USER = 'user';
+
+  /**
+   * Timezone is set per-date.
+   */
+  const TIMEZONE_DATE = 'date';
+
+  /**
+   * No timezone conversion is performed.
+   */
+  const TIMEZONE_NONE = 'none';
+
+  /**
    * {@inheritdoc}
    */
   public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
@@ -56,6 +79,9 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
       ->setClass('\Drupal\datetime\DateTimeComputed')
       ->setSetting('date source', 'value');
 
+    $properties['timezone'] = DataDefinition::create('string')
+      ->setLabel(t('Timezone'));
+
     return $properties;
   }
 
@@ -63,18 +89,26 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
    * {@inheritdoc}
    */
   public static function schema(FieldStorageDefinitionInterface $field_definition) {
-    return array(
+    $schema = array(
       'columns' => array(
         'value' => array(
           'description' => 'The date value.',
           'type' => 'varchar',
           'length' => 20,
         ),
+        'timezone' => array(
+          'description' => 'The date timezone',
+          'type' => 'varchar',
+          'length' => 50,
+        ),
       ),
       'indexes' => array(
         'value' => array('value'),
+        'value_timezone' => array('value', 'timezone'),
       ),
     );
+
+    return $schema;
   }
 
   /**
@@ -95,6 +129,29 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state
       '#disabled' => $has_data,
     );
 
+    $element['timezone_handling'] = array(
+      '#type' => 'select',
+      '#title' => t('Timezone handling'),
+      '#options' => array(
+        static::TIMEZONE_SITE => $this->t("Site's timezone"),
+        static::TIMEZONE_DATE => $this->t("Date's timezone"),
+        static::TIMEZONE_USER => $this->t("User's timezone"),
+        static::TIMEZONE_NONE => $this->t('No timezone conversion'),
+      ),
+      '#default_value' => $this->getSetting('timezone_handling'),
+      '#required' => TRUE,
+      '#states' => array(
+        // Hide the field if this is a date-only field.
+        'visible' => array(
+          ':input[name="settings[datetime_type]"]' => ['value' => static::DATETIME_TYPE_DATETIME],
+        ),
+        'disabled' => array(
+          ':input[name="settings[datetime_type]"]' => ['value' => static::DATETIME_TYPE_DATE],
+        ),
+      ),
+      '#disabled' => $has_data,
+    );
+
     return $element;
   }
 
diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
index aa6175a..65326f2 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
@@ -30,7 +30,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       '#type' => 'datetime',
       '#default_value' => NULL,
       '#date_increment' => 1,
-      '#date_timezone' => drupal_get_user_timezone(),
+      '#date_timezone' => $items[$delta]->timezone ?: drupal_get_user_timezone(),
       '#required' => $element['#required'],
     );
 
@@ -40,7 +40,12 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       $element['value']['#date_timezone'] = DATETIME_STORAGE_TIMEZONE;
     }
 
+    if ($this->getFieldSetting('timezone_handling') === DateTimeItem::TIMEZONE_DATE) {
+      $element['value']['#expose_timezone'] = TRUE;
+    }
+
     if ($items[$delta]->date) {
+      /** @var \Drupal\Core\Datetime\DrupalDateTime $date */
       $date = $items[$delta]->date;
       // The date was created and verified during field_load(), so it is safe to
       // use without further inspection.
@@ -78,8 +83,20 @@ public function massageFormValues(array $values, array $form, FormStateInterface
             $format = DATETIME_DATETIME_STORAGE_FORMAT;
             break;
         }
-        // Adjust the date for storage.
-        $date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE));
+
+        // Store the timezone if set.
+        if ($this->getFieldSetting('timezone_handling') === DateTimeItem::TIMEZONE_DATE) {
+          $item['timezone'] = $date->getTimezone()->getName();
+        }
+        else {
+          $item['timezone'] = '';
+        }
+
+        // Adjust the date for storage once validation is complete.
+        if ($form_state->isValidationComplete()) {
+          $date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE));
+        }
+
         $item['value'] = $date->format($format);
       }
     }
diff --git a/core/modules/datetime/src/Tests/DateTimeFieldTest.php b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
index 0f2f4ff..f0212cd 100644
--- a/core/modules/datetime/src/Tests/DateTimeFieldTest.php
+++ b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
@@ -7,6 +7,7 @@
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Datetime\Entity\DateFormat;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
@@ -24,7 +25,10 @@ class DateTimeFieldTest extends DateTestBase {
    *
    * @var array
    */
-  protected $defaultSettings = ['timezone_override' => ''];
+  protected $defaultSettings = [
+    'timezone_display' => DateTimeItem::TIMEZONE_USER,
+    'timezone_override' => '',
+  ];
 
   /**
    * {@inheritdoc}
@@ -213,6 +217,7 @@ function testDatetimeField() {
     $this->drupalGet('entity_test/add');
     $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Date element found.');
     $this->assertFieldByName("{$field_name}[0][value][time]", '', 'Time element found.');
+    $this->assertNoFieldByName("{$field_name}[0][timezone]", '', 'No timezone field appears for dates that do not collect timezone information.');
 
     // Build up a date in the UTC timezone.
     $value = '2012-12-31 00:00:00';
@@ -283,7 +288,7 @@ function testDatetimeField() {
 
     // 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;
+    $this->displayOptions['settings'] = array('date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York', 'timezone_display' => DateTimeItem::TIMEZONE_NONE) + $this->defaultSettings;
     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
       ->setComponent($field_name, $this->displayOptions)
       ->save();
@@ -338,6 +343,32 @@ function testDatetimeField() {
     ]);
     $this->renderTestEntity($id);
     $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_time_ago format displayed as %expected.', array('%expected' => $expected)));
+
+    // Verify timezone display settings.
+    $this->fieldStorage->setSetting('timezone_handling', DateTimeItem::TIMEZONE_DATE);
+    $this->fieldStorage->save();
+
+    $edit = array(
+      "{$field_name}[0][value][date]" => $date->format($date_format, ['timezone' => 'America/New_York']),
+      "{$field_name}[0][value][time]" => $date->format($time_format, ['timezone' => 'America/New_York']),
+      "{$field_name}[0][value][timezone]" => 'America/New_York',
+    );
+    $this->drupalPostForm('entity_test/add', $edit, t('Save'));
+    preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
+    $id = $match[1];
+    $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
+
+    $this->displayOptions['type'] = 'datetime_custom';
+    $this->displayOptions['settings'] = [
+        'date_format' => 'm/d/Y g:i:s A e',
+        'timezone_display' => DateTimeItem::TIMEZONE_DATE,
+      ] + $this->defaultSettings;
+    entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
+      ->setComponent($field_name, $this->displayOptions)
+      ->save();
+    $this->renderTestEntity($id);
+    $expected = $date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']);
+    $this->assertText($expected);
   }
 
   /**
diff --git a/core/modules/datetime/tests/src/Kernel/DateTimeItemTest.php b/core/modules/datetime/tests/src/Kernel/DateTimeItemTest.php
index 424cbd9..05616bf 100644
--- a/core/modules/datetime/tests/src/Kernel/DateTimeItemTest.php
+++ b/core/modules/datetime/tests/src/Kernel/DateTimeItemTest.php
@@ -46,7 +46,10 @@ protected function setUp() {
       'field_name' => 'field_datetime',
       'type' => 'datetime',
       'entity_type' => 'entity_test',
-      'settings' => array('datetime_type' => DateTimeItem::DATETIME_TYPE_DATETIME),
+      'settings' => array(
+        'datetime_type' => DateTimeItem::DATETIME_TYPE_DATETIME,
+        'timezone_handling' => DateTimeItem::TIMEZONE_SITE,
+      ),
     ));
     $this->fieldStorage->save();
     $this->field = FieldConfig::create([
@@ -221,4 +224,28 @@ public function testSetValueProperty() {
     $this->assertEqual($entity->field_datetime[0]->value, $value, '"Value" property can be set directly.');
   }
 
+  /**
+   * Tests DateTimeItem with per-date timezone handling.
+   */
+  public function testTimezoneDate() {
+    /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
+    $field_storage = FieldStorageConfig::load('entity_test.field_datetime');
+    $field_storage->setSetting('timezone_handling', 'date');
+    $field_storage->save();
+
+    // Use a non-UTC timezone.
+    $timezone = 'America/Yellowknife';
+
+    $entity = EntityTest::create();
+    $value = '2014-01-01T20:00:00Z';
+
+    $entity->set('field_datetime', ['value' => $value, 'timezone' => $timezone]);
+    $entity->save();
+
+    // Load the entity.
+    $id = $entity->id();
+    $entity = EntityTest::load($id);
+    $this->assertEqual($timezone, $entity->field_datetime[0]->timezone, '"timezone" property can be set.');
+  }
+
 }
diff --git a/core/modules/datetime_range/src/Plugin/Field/FieldType/DateRangeItem.php b/core/modules/datetime_range/src/Plugin/Field/FieldType/DateRangeItem.php
index 49c4dce..0d2b92e 100644
--- a/core/modules/datetime_range/src/Plugin/Field/FieldType/DateRangeItem.php
+++ b/core/modules/datetime_range/src/Plugin/Field/FieldType/DateRangeItem.php
@@ -54,6 +54,9 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
       ->setClass(DateTimeComputed::class)
       ->setSetting('date source', 'end_value');
 
+    $properties['timezone'] = DataDefinition::create('string')
+      ->setLabel(t('Timezone'));
+
     return $properties;
   }
 
diff --git a/core/modules/datetime_range/src/Tests/DateRangeFieldTest.php b/core/modules/datetime_range/src/Tests/DateRangeFieldTest.php
index 74b8f6a..51ff48c 100644
--- a/core/modules/datetime_range/src/Tests/DateRangeFieldTest.php
+++ b/core/modules/datetime_range/src/Tests/DateRangeFieldTest.php
@@ -6,6 +6,7 @@
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Datetime\Entity\DateFormat;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
 use Drupal\datetime\Tests\DateTestBase;
 use Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem;
 use Drupal\entity_test\Entity\EntityTest;
@@ -336,7 +337,7 @@ public function testDatetimeRangeField() {
 
     // Verify that the 'timezone_override' setting works.
     $this->displayOptions['type'] = 'daterange_custom';
-    $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York'] + $this->defaultSettings;
+    $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York', 'timezone_display' => DateTimeItem::TIMEZONE_NONE] + $this->defaultSettings;
     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
       ->setComponent($field_name, $this->displayOptions)
       ->save();
@@ -497,7 +498,7 @@ public function testAlldayRangeField() {
 
     // Verify that the 'timezone_override' setting works.
     $this->displayOptions['type'] = 'daterange_custom';
-    $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York'] + $this->defaultSettings;
+    $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York', 'timezone_display' => DateTimeItem::TIMEZONE_NONE] + $this->defaultSettings;
     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
       ->setComponent($field_name, $this->displayOptions)
       ->save();
diff --git a/core/modules/datetime_range/tests/src/Kernel/DateRangeItemTest.php b/core/modules/datetime_range/tests/src/Kernel/DateRangeItemTest.php
index 67e1b9d..a44623b 100644
--- a/core/modules/datetime_range/tests/src/Kernel/DateRangeItemTest.php
+++ b/core/modules/datetime_range/tests/src/Kernel/DateRangeItemTest.php
@@ -50,7 +50,10 @@ protected function setUp() {
       'field_name' => Unicode::strtolower($this->randomMachineName()),
       'entity_type' => 'entity_test',
       'type' => 'daterange',
-      'settings' => ['datetime_type' => DateRangeItem::DATETIME_TYPE_DATE],
+      'settings' => [
+        'datetime_type' => DateRangeItem::DATETIME_TYPE_DATE,
+        'timezone_handling' => DateRangeItem::TIMEZONE_SITE,
+      ],
     ]);
     $this->fieldStorage->save();
 
diff --git a/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldFormatterSettingsTest.php b/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldFormatterSettingsTest.php
index 2ee3c77..e1eb59b 100644
--- a/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldFormatterSettingsTest.php
+++ b/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldFormatterSettingsTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\field\Kernel\Migrate\d6;
 
 use Drupal\Core\Entity\Entity\EntityViewDisplay;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
 use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
 
 /**
@@ -138,7 +139,7 @@ public function testEntityDisplaySettings() {
     $this->assertIdentical($expected, $component);
 
     // Test date field.
-    $defaults = array('format_type' => 'fallback', 'timezone_override' => '',);
+    $defaults = array('format_type' => 'fallback', 'timezone_override' => '', 'timezone_display' => DateTimeItem::TIMEZONE_USER);
     $expected['weight'] = 10;
     $expected['type'] = 'datetime_default';
     $expected['settings'] = array('format_type' => 'fallback') + $defaults;
