diff --git a/date.field.inc b/date.field.inc
index 7aef97be..1a9958fd 100644
--- a/date.field.inc
+++ b/date.field.inc
@@ -332,15 +332,14 @@ function date_field_widget_info() {
  * Implements hook_field_load().
  */
 function date_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
-  $timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
   $db_format = date_type_format($field['type']);
   $process = date_process_values($field);
   foreach ($entities as $id => $entity) {
     foreach ($items[$id] as $delta => &$item) {
-      // If the file does not exist, mark the entire item as empty.
+      // If the field is not an array, mark the entire item as empty.
       if (is_array($item)) {
         $timezone = isset($item['timezone']) ? $item['timezone'] : '';
-        $item['timezone'] = date_get_timezone($field['settings']['tz_handling'], $timezone);
+        $timezone_db = date_get_timezone_db($field['settings']['tz_handling'], $timezone);
         $item['timezone_db'] = $timezone_db;
         $item['date_type'] = $field['type'];
         if (!empty($field['settings']['cache_enabled']) && ($delta < $field['settings']['cache_count'] || $field['settings']['cache_count'] == 0)) {
@@ -417,7 +416,7 @@ function date_field_presave($entity_type, $entity, $field, $instance, $langcode,
     $timezone = isset($item['timezone']) ? $item['timezone'] : '';
     if (is_array($item)) {
       $items[$delta]['timezone'] = date_get_timezone($field['settings']['tz_handling'], $timezone);
-      $items[$delta]['timezone_db'] = date_get_timezone_db($field['settings']['tz_handling']);
+      $items[$delta]['timezone_db'] = date_get_timezone_db($field['settings']['tz_handling'], $timezone);
       $items[$delta]['date_type'] = $field['type'];
     }
   }
diff --git a/date.install b/date.install
index 765b90cf..f9c5067f 100644
--- a/date.install
+++ b/date.install
@@ -212,3 +212,107 @@ function date_update_7005() {
   // @see https://www.drupal.org/node/1355256
   date_update_7004();
 }
+
+/**
+ * Convert date fields with setting "date's timezone" from UTC to the selected
+ * timezone.
+ */
+function date_update_7200(&$sandbox) {
+  if (!isset($sandbox['total'])) {
+    // Select date fields.
+    $query = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC));
+    $query->fields('fc');
+    $query->condition(db_or()->condition('fc.type', 'date')->condition('fc.type', 'datetime'));
+    $results = $query->execute();
+    $sandbox['total'] = 0;
+    $sandbox['fields'] = array();
+    $sandbox['local_location'] = 0;
+    $sandbox['current_index'] = 0;
+    $sandbox['global_location'] = 0;
+    $sandbox['field_count_map'] = array();
+    $sandbox['field_granularity'] = array();
+    $sandbox['field_date_format'] = array();
+    // Find the ones that have tz_handling = date.
+    foreach ($results as $record) {
+      $config = unserialize($record['data']);
+      if ($config['settings']['tz_handling'] == 'date') {
+        $field_name = $record['field_name'];
+        $date_format = '';
+        switch ($record['type']) {
+          case 'datetime':
+            $sandbox['field_date_format'][] = DATE_FORMAT_DATETIME;
+           break;
+          case 'date':
+            $sandbox['field_date_format'][] = DATE_FORMAT_ISO;
+            break;
+        }
+        $field_count = db_select('field_data_' . $field_name)
+          ->fields(NULL, array('entity_id'))
+          ->countQuery()
+          ->execute()
+          ->fetchField();
+        $sandbox['total'] += $field_count;
+        $sandbox['fields'][] = $field_name;
+        $sandbox['field_count_map'][] = $field_count;
+        $sandbox['field_granularity'][] = $config['settings']['granularity'];
+      }
+    }
+  }
+
+  // Fail early if there's nothing to do.
+  if (empty($sandbox['total'])) {
+    return t('No date fields needed to have their timezone data fixed.');
+  }
+
+  $records_per_iteration = 100;
+
+  if (!empty($sandbox['fields'])) {
+    $field_name = $sandbox['fields'][$sandbox['current_index']];
+    $query2 = db_select('field_data_' . $field_name, 'fd', array('fetch' => PDO::FETCH_ASSOC));
+    $query2->fields('fd');
+    $query2->range($sandbox['local_location'], $records_per_iteration);
+    $results2 = $query2->execute();
+    foreach ($results2 as $record2) {
+      $timezone = $record2[$field_name . '_timezone'];
+      $value = new DateObject($record2[$field_name . '_value'], 'UTC');
+      $value->limitGranularity($sandbox['field_granularity'][$sandbox['current_index']]);
+      date_timezone_set($value, timezone_open($timezone));
+      $fields = array(
+        $field_name . '_value' => $value->format($sandbox['field_date_format'][$sandbox['current_index']]),
+      );
+      if (isset($record2[$field_name . '_value2'])) {
+        $value2 = new DateObject($record2[$field_name . '_value2'], 'UTC');
+        $value->limitGranularity($sandbox['field_granularity'][$sandbox['current_index']]);
+        date_timezone_set($value2, timezone_open($timezone));
+        $fields[$field_name . '_value2'] = $value2->format($sandbox['field_date_format'][$sandbox['current_index']]);
+      }
+      db_update('field_data_' . $field_name)
+        ->fields($fields)
+        ->condition('entity_id', $record2['entity_id'])
+        ->condition('revision_id', $record2['revision_id'])
+        ->condition('delta', $record2['delta'])
+        ->execute();
+      db_update('field_revision_' . $field_name)
+        ->fields($fields)
+        ->condition('entity_id', $record2['entity_id'])
+        ->condition('revision_id', $record2['revision_id'])
+        ->condition('delta', $record2['delta'])
+        ->execute();
+      $sandbox['local_location']++;
+      $sandbox['global_location']++;
+    }
+    if ($sandbox['local_location'] == $sandbox['field_count_map'][$sandbox['current_index']]) {
+      $sandbox['current_index']++;
+      $sandbox['local_location'] = 0;
+    }
+    $sandbox['#finished'] = ($sandbox['global_location'] / $sandbox['total']);
+  }
+  else {
+    $sandbox['#finished'] = 1;
+  }
+
+  if ($sandbox['#finished'] === 1) {
+    field_cache_clear();
+    drupal_set_message(t('Processed @fields date field entries', array('@fields' => $sandbox['total'])));
+  }
+}
diff --git a/date.module b/date.module
index e667b5dd..ae1ee772 100644
--- a/date.module
+++ b/date.module
@@ -226,11 +226,9 @@ function date_formatter_process($formatter, $entity_type, $entity, $field, $inst
   $settings = $display['settings'];
   $field_name = $field['field_name'];
   $format = date_formatter_format($formatter, $settings, $granularity, $langcode);
-  if (!isset($field['settings']['tz_handling']) || $field['settings']['tz_handling'] !== 'utc') {
-    $timezone = isset($item['timezone']) ? $item['timezone'] : '';
-    $timezone = date_get_timezone($field['settings']['tz_handling'], $timezone);
-  }
-  $timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
+  $timezone = date_get_timezone($field['settings']['tz_handling']);
+  $timezone_db = isset($item['timezone_db']) ? $item['timezone_db'] : '';
+  $timezone_db = date_get_timezone_db($field['settings']['tz_handling'], $timezone_db);
   $db_format = date_type_format($field['type']);
   $process = date_process_values($field);
   foreach ($process as $processed) {
@@ -249,10 +247,12 @@ function date_formatter_process($formatter, $entity_type, $entity, $field, $inst
         $date = new DateObject($item[$processed], $timezone_db, $db_format);
         $date->limitGranularity($field['settings']['granularity']);
       }
-
       $dates[$processed]['db']['object'] = $date;
       $dates[$processed]['db']['datetime'] = date_format($date, DATE_FORMAT_DATETIME);
 
+      // Use a cloned object and set the timezone on the clone to avoid
+      // disturbing the "db" date object.
+      $date = clone $date;
       date_timezone_set($date, timezone_open($timezone));
       $dates[$processed]['local']['object'] = $date;
       $dates[$processed]['local']['datetime'] = date_format($date, DATE_FORMAT_DATETIME);
diff --git a/date_admin.inc b/date_admin.inc
index b800201e..5d6ba319 100644
--- a/date_admin.inc
+++ b/date_admin.inc
@@ -620,7 +620,7 @@ function _date_field_settings_validate(&$form, &$form_state) {
     form_set_value($form['timezone_db'], '', $form_state);
   }
   else {
-    form_set_value($form['timezone_db'], date_get_timezone_db($field['settings']['tz_handling']), $form_state);
+    form_set_value($form['timezone_db'], date_get_timezone_db($field['settings']['tz_handling'], $form['timezone_db']), $form_state);
   }
 
   if ($field['settings']['tz_handling'] != 'none' && !in_array('hour', array_filter($field['settings']['granularity']))) {
diff --git a/date_elements.inc b/date_elements.inc
index 656d4998..9f67f484 100644
--- a/date_elements.inc
+++ b/date_elements.inc
@@ -165,7 +165,7 @@ function date_local_date($item, $timezone, $field, $instance, $part = 'value') {
   */
   // @codingStandardsIgnoreEnd
 
-  $date = new DateObject($value, date_get_timezone_db($field['settings']['tz_handling']));
+  $date = new DateObject($value, date_get_timezone_db($field['settings']['tz_handling'], $timezone));
   $date->limitGranularity($field['settings']['granularity']);
   if (empty($date)) {
     return NULL;
@@ -553,7 +553,7 @@ function date_combo_validate($element, &$form_state) {
   }
   else {
     $timezone = !empty($item[$tz_field]) ? $item[$tz_field] : $element['#date_timezone'];
-    $timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
+    $timezone_db = date_get_timezone_db($field['settings']['tz_handling'], $timezone);
     $element[$from_field]['#date_timezone'] = $timezone;
     $from_date = date_input_date($field, $instance, $element[$from_field], $posted[$from_field]);
 
diff --git a/date_repeat_field/date_repeat_field.module b/date_repeat_field/date_repeat_field.module
index d4d4c902..e6103292 100644
--- a/date_repeat_field/date_repeat_field.module
+++ b/date_repeat_field/date_repeat_field.module
@@ -449,14 +449,14 @@ function date_repeat_build_dates($rrule = NULL, $rrule_values = NULL, $field, $i
   // adjusted back to UTC, but we want localtime dates to do
   // things like '+1 Tuesday', so adjust back to localtime.
   $timezone = date_get_timezone($field['settings']['tz_handling'], $item['timezone']);
-  $timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
+  $timezone_db = date_get_timezone_db($field['settings']['tz_handling'], $timezone);
   $start = new DateObject($item['value'], $timezone_db, date_type_format($field['type']));
   $start->limitGranularity($field['settings']['granularity']);
   if ($timezone != $timezone_db) {
     date_timezone_set($start, timezone_open($timezone));
   }
   if (!empty($item['value2']) && $item['value2'] != $item['value']) {
-    $end = new DateObject($item['value2'], date_get_timezone_db($field['settings']['tz_handling']), date_type_format($field['type']));
+    $end = new DateObject($item['value2'], date_get_timezone_db($field['settings']['tz_handling'], $timezone), date_type_format($field['type']));
     $end->limitGranularity($field['settings']['granularity']);
     date_timezone_set($end, timezone_open($timezone));
   }
diff --git a/tests/date_timezone.test b/tests/date_timezone.test
index 9f2905a2..0b68409f 100644
--- a/tests/date_timezone.test
+++ b/tests/date_timezone.test
@@ -103,51 +103,100 @@ public function dateMultiValueForm($field_name, $field_type, $max_granularity, $
     $should_be = array();
     $edit['title'] = $this->randomName(8);
     $timezones = array('America/Chicago', 'America/Los_Angeles', 'America/New_York');
+    switch ($tz_handling) {
+      // With 'date' timezone handling, event times should be translated to
+      // the display timezone when viewed.
+      case 'date':
+        variable_set('date_default_timezone', 'Europe/Berlin'); // UTC + 2.
+        switch ($max_granularity) {
+          case 'hour':
+          $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
+          $should_be[0] = 'Thu, 10/07/2010 - 17 CEST';
 
-    switch ($max_granularity) {
-      case 'hour':
-        $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
-        $edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
-        $should_be[0] = 'Thu, 10/07/2010 - 10 CDT';
-
-        $edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
-        $edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
-        $should_be[1] = 'Thu, 10/07/2010 - 10 PDT';
-
-        $edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
-        $edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
-        $should_be[2] = 'Thu, 10/07/2010 - 10 EDT';
-
-        break;
-      case 'minute':
-        $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
-        $edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
-        $should_be[0] = 'Thu, 10/07/2010 - 10:30 CDT';
-
-        $edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
-        $edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
-        $should_be[1] = 'Thu, 10/07/2010 - 10:30 PDT';
-
-        $edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
-        $edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
-        $should_be[2] = 'Thu, 10/07/2010 - 10:30 EDT';
-
-        break;
-      case 'second':
-        $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
-        $edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
-        $should_be[0] = 'Thu, 10/07/2010 - 10:30:30 CDT';
-
-        $edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
-        $edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
-        $should_be[1] = 'Thu, 10/07/2010 - 10:30:30 PDT';
-
-        $edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
-        $edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
-        $should_be[2] = 'Thu, 10/07/2010 - 10:30:30 EDT';
-        break;
+          $edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
+          $should_be[1] = 'Thu, 10/07/2010 - 19 CEST';
+
+          $edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
+          $should_be[2] = 'Thu, 10/07/2010 - 16 CEST';
+
+          break;
+        case 'minute':
+          $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
+          $should_be[0] = 'Thu, 10/07/2010 - 17:30 CEST';
+
+          $edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
+          $should_be[1] = 'Thu, 10/07/2010 - 19:30 CEST';
+
+          $edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
+          $should_be[2] = 'Thu, 10/07/2010 - 16:30 CEST';
+
+          break;
+        case 'second':
+          $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
+          $should_be[0] = 'Thu, 10/07/2010 - 17:30:30 CEST';
+
+          $edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
+          $should_be[1] = 'Thu, 10/07/2010 - 19:30:30 CEST';
+
+          $edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
+          $should_be[2] = 'Thu, 10/07/2010 - 16:30:30 CEST';
+          break;
+      }
+      break;
+      default:
+        switch ($max_granularity) {
+          case 'hour':
+          $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
+          $should_be[0] = 'Thu, 10/07/2010 - 10 CDT';
+
+          $edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
+          $should_be[1] = 'Thu, 10/07/2010 - 10 PDT';
+
+          $edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
+          $should_be[2] = 'Thu, 10/07/2010 - 10 EDT';
+
+          break;
+        case 'minute':
+          $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
+          $should_be[0] = 'Thu, 10/07/2010 - 10:30 CDT';
+
+          $edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
+          $should_be[1] = 'Thu, 10/07/2010 - 10:30 PDT';
+
+          $edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
+          $should_be[2] = 'Thu, 10/07/2010 - 10:30 EDT';
+
+          break;
+        case 'second':
+          $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
+          $should_be[0] = 'Thu, 10/07/2010 - 10:30:30 CDT';
+
+          $edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
+          $should_be[1] = 'Thu, 10/07/2010 - 10:30:30 PDT';
+
+          $edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
+          $edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
+          $should_be[2] = 'Thu, 10/07/2010 - 10:30:30 EDT';
+          break;
+      }
     }
-
     $this->drupalPost('node/add/story', $edit, t('Save'));
     $this->assertText($edit['title'], "Node has been created");
 
@@ -169,6 +218,7 @@ public function dateMultiValueForm($field_name, $field_type, $max_granularity, $
    * @todo.
    */
   public function dateForm($field_name, $field_type, $max_granularity, $tz_handling) {
+    global $user;
     variable_set('date_format_long', 'D, m/d/Y - H:i:s');
     $edit = array();
     $edit['title'] = $this->randomName(8);
@@ -202,7 +252,18 @@ public function dateForm($field_name, $field_type, $max_granularity, $tz_handlin
       case 'minute':
         $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
         $edit[$field_name . '[und][0][value2][date]'] = '10/07/2010 - 11:30';
-        if ($tz_handling == 'utc') {
+        // Makes sure the timezone conversion is handled properly with date
+        // fields.
+        // @see https://www.drupal.org/node/998076
+        if ($tz_handling == 'date') {
+          // UTC + 8.
+          $edit[$field_name . '[und][0][timezone][timezone]'] = 'Asia/Hong_Kong';
+          // UTC + 2.
+          variable_set('date_default_timezone', 'Europe/Berlin');
+          $user->timezone = 'Europe/Berlin';
+          $should_be = 'Thu, 10/07/2010 - 04:30 to 05:30';
+        }
+        elseif ($tz_handling == 'utc') {
           $should_be = 'Thu, 10/07/2010 - 21:30 to 22:30';
         }
         else {
