diff --git a/core/modules/datetime/datetime.module b/core/modules/datetime/datetime.module
index d0114e2..c30bb07 100644
--- a/core/modules/datetime/datetime.module
+++ b/core/modules/datetime/datetime.module
@@ -112,82 +112,6 @@ function datetime_theme() {
 }
 
 /**
- * Validation callback for the datetime widget element.
- *
- * The date has already been validated by the datetime form type validator and
- * transformed to an date object. We just need to convert the date back to a the
- * storage timezone and format.
- *
- * @param array $element
- *   The form element whose value is being validated.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- *   The current state of the form.
- */
-function datetime_datetime_widget_validate(&$element, FormStateInterface $form_state) {
-  if (!form_get_errors($form_state)) {
-    $input_exists = FALSE;
-    $input = NestedArray::getValue($form_state->getValues(), $element['#parents'], $input_exists);
-    if ($input_exists) {
-      // The date should have been returned to a date object at this point by
-      // datetime_validate(), which runs before this.
-      if (!empty($input['value'])) {
-        $date = $input['value'];
-        if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
-
-          // If this is a date-only field, set it to the default time so the
-          // timezone conversion can be reversed.
-          if ($element['value']['#date_time_element'] == 'none') {
-            datetime_date_default_time($date);
-          }
-          // Adjust the date for storage.
-          $date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE));
-          $value = $date->format($element['value']['#date_storage_format']);
-          form_set_value($element['value'], $value, $form_state);
-        }
-      }
-    }
-  }
-}
-
-/**
- * Validation callback for the datelist widget element.
- *
- * The date has already been validated by the datetime form type validator and
- * transformed to an date object. We just need to convert the date back to a the
- * storage timezone and format.
- *
- * @param array $element
- *   The form element whose value is being validated.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- *   The current state of the form.
- */
-function datetime_datelist_widget_validate(&$element, FormStateInterface $form_state) {
-  if (!form_get_errors($form_state)) {
-    $input_exists = FALSE;
-    $input = NestedArray::getValue($form_state->getValues(), $element['#parents'], $input_exists);
-    if ($input_exists) {
-      // The date should have been returned to a date object at this point by
-      // datetime_validate(), which runs before this.
-      if (!empty($input['value'])) {
-        $date = $input['value'];
-        if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
-
-          // If this is a date-only field, set it to the default time so the
-          // timezone conversion can be reversed.
-          if (!in_array('hour', $element['value']['#date_part_order'])) {
-            datetime_date_default_time($date);
-          }
-          // Adjust the date for storage.
-          $date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE));
-          $value = $date->format($element['value']['#date_storage_format']);
-          form_set_value($element['value'], $value, $form_state);
-        }
-      }
-    }
-  }
-}
-
-/**
  * Sets a consistent time on a date without time.
  *
  * The default time for a date without time can be anything, so long as it is
diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDatelistWidget.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDatelistWidget.php
index 6d05a06..187de5b 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDatelistWidget.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDatelistWidget.php
@@ -6,6 +6,7 @@
 
 namespace Drupal\datetime\Plugin\Field\FieldWidget;
 
+use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormStateInterface;
@@ -52,18 +53,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
 
     $element['#theme_wrappers'][] = 'datetime_wrapper';
     $element['#attributes']['class'][] = 'container-inline';
-    $element['#element_validate'][] = 'datetime_datelist_widget_validate';
-
-    // Identify the type of date and time elements to use.
-    switch ($this->getFieldSetting('datetime_type')) {
-      case DateTimeItem::DATETIME_TYPE_DATE:
-        $storage_format = DATETIME_DATE_STORAGE_FORMAT;
-        break;
-
-      default:
-        $storage_format = DATETIME_DATETIME_STORAGE_FORMAT;
-        break;
-    }
 
     // Set up the date part order array.
     switch ($date_order) {
@@ -101,10 +90,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       '#required' => $element['#required'],
     );
 
-    // Set the storage and widget options so the validation can use them. The
-    // validator will not have access to the field definition.
-    $element['value']['#date_storage_format'] = $storage_format;
-
     if ($items[$delta]->date) {
       $date = $items[$delta]->date;
       // The date was created and verified during field_load(), so it is safe to
@@ -123,6 +108,38 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
   /**
    * {@inheritdoc}
    */
+  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+    // The 'datelist' form element type has transformed the value to a
+    // DrupalDateTime object at this point. We need to convert it back to the
+    // storage timezone and format.
+    foreach ($values as &$item) {
+      if (!empty($item['value'])) {
+        $date = $item['value'];
+        if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
+          switch ($this->getFieldSetting('datetime_type')) {
+            case DateTimeItem::DATETIME_TYPE_DATE:
+              // If this is a date-only field, set it to the default time so the
+              // timezone conversion can be reversed.
+              datetime_date_default_time($date);
+              $format = DATETIME_DATE_STORAGE_FORMAT;
+              break;
+
+            default:
+              $format = DATETIME_DATETIME_STORAGE_FORMAT;
+              break;
+          }
+          // Adjust the date for storage.
+          $date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE));
+          $item['value'] = $date->format($format);
+        }
+      }
+    }
+    return $values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   function settingsForm(array $form, FormStateInterface $form_state) {
     $element = parent::settingsForm($form, $form_state);
 
diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php
index a411eb4..0003203 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php
@@ -6,6 +6,7 @@
 
 namespace Drupal\datetime\Plugin\Field\FieldWidget;
 
+use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
@@ -70,7 +71,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
 
     $element['#theme_wrappers'][] = 'datetime_wrapper';
     $element['#attributes']['class'][] = 'container-inline';
-    $element['#element_validate'][] = 'datetime_datetime_widget_validate';
 
     // Identify the type of date and time elements to use.
     switch ($this->getFieldSetting('datetime_type')) {
@@ -79,8 +79,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
         $time_type = 'none';
         $date_format = $this->dateStorage->load('html_date')->getPattern();
         $time_format = '';
-        $element_format = $date_format;
-        $storage_format = DATETIME_DATE_STORAGE_FORMAT;
         break;
 
       default:
@@ -88,8 +86,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
         $time_type = 'time';
         $date_format = $this->dateStorage->load('html_date')->getPattern();
         $time_format = $this->dateStorage->load('html_time')->getPattern();
-        $element_format = $date_format . ' ' . $time_format;
-        $storage_format = DATETIME_DATETIME_STORAGE_FORMAT;
         break;
     }
 
@@ -107,10 +103,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       '#required' => $element['#required'],
     );
 
-    // Set the storage and widget options so the validation can use them. The
-    // validator will not have access to the field definition.
-    $element['value']['#date_element_format'] = $element_format;
-    $element['value']['#date_storage_format'] = $storage_format;
     if ($items[$delta]->date) {
       $date = $items[$delta]->date;
       // The date was created and verified during field_load(), so it is safe to
@@ -127,4 +119,36 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     return $element;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+    // The 'datetime' form element type has transformed the value to a
+    // DrupalDateTime object at this point. We need to convert it back to the
+    // storage timezone and format.
+    foreach ($values as &$item) {
+      if (!empty($item['value'])) {
+        $date = $item['value'];
+        if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
+          switch ($this->getFieldSetting('datetime_type')) {
+            case DateTimeItem::DATETIME_TYPE_DATE:
+              // If this is a date-only field, set it to the default time so the
+              // timezone conversion can be reversed.
+              datetime_date_default_time($date);
+              $format = DATETIME_DATE_STORAGE_FORMAT;
+              break;
+
+            default:
+              $format = DATETIME_DATETIME_STORAGE_FORMAT;
+              break;
+          }
+          // Adjust the date for storage.
+          $date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE));
+          $item['value'] = $date->format($format);
+        }
+      }
+    }
+    return $values;
+  }
+
 }
