diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 9b89250..fe548e9 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -573,6 +573,14 @@ function template_preprocess_datetime_wrapper(&$variables) {
     $variables['title'] = $element['#title'];
   }
 
+  // Pass elements #type and #name to template.
+  if (!empty($element['#type'])) {
+    $variables['type'] = $element['#type'];
+  }
+  if (!empty($element['#name'])) {
+    $variables['name'] = $element['#name'];
+  }
+
   // Suppress error messages.
   $variables['errors'] = NULL;
 
@@ -580,6 +588,13 @@ function template_preprocess_datetime_wrapper(&$variables) {
     $variables['description'] = $element['#description'];
   }
 
+  // For disabled datetime fields, the 'disabled' attribute should not be
+  // applied to the wrapper, but the 'form-disabled' class should be.
+  $variables['disabled'] = !empty($element['#attributes']['disabled']) ? $element['#attributes']['disabled'] : NULL;
+  if (isset($variables['attributes']['disabled'])) {
+    unset($variables['attributes']['disabled']);
+  }
+
   $variables['required'] = FALSE;
   // For required datetime fields 'form-required' & 'js-form-required' classes
   // are appended to the label attributes.
diff --git a/core/lib/Drupal/Core/Datetime/Element/Datetime.php b/core/lib/Drupal/Core/Datetime/Element/Datetime.php
index 7637ad1..a544fcb 100644
--- a/core/lib/Drupal/Core/Datetime/Element/Datetime.php
+++ b/core/lib/Drupal/Core/Datetime/Element/Datetime.php
@@ -15,6 +15,8 @@
 class Datetime extends DateElementBase {
 
   /**
+   * An example date.
+   *
    * @var \DateTimeInterface
    */
   protected static $dateExample;
@@ -28,11 +30,11 @@ public function getInfo() {
     // Date formats cannot be loaded during install or update.
     if (!defined('MAINTENANCE_MODE')) {
       if ($date_format_entity = DateFormat::load('html_date')) {
-        /** @var $date_format_entity \Drupal\Core\Datetime\DateFormatInterface */
+        /* @var $date_format_entity \Drupal\Core\Datetime\DateFormatInterface */
         $date_format = $date_format_entity->getPattern();
       }
       if ($time_format_entity = DateFormat::load('html_time')) {
-        /** @var $time_format_entity \Drupal\Core\Datetime\DateFormatInterface */
+        /* @var $time_format_entity \Drupal\Core\Datetime\DateFormatInterface */
         $time_format = $time_format_entity->getPattern();
       }
     }
@@ -95,7 +97,7 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
       );
     }
     else {
-      $date = $element['#default_value'];
+      $date = isset($element['#default_value']) ? $element['#default_value'] : NULL;
       if ($date instanceof DrupalDateTime && !$date->hasErrors()) {
         $input = array(
           'date'   => $date->format($element['#date_date_format']),
@@ -137,17 +139,17 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
    *
    * Required settings:
    *   - #default_value: A DrupalDateTime object, adjusted to the proper local
-   *     timezone. Converting a date stored in the database from UTC to the local
-   *     zone and converting it back to UTC before storing it is not handled here.
-   *     This element accepts a date as the default value, and then converts the
-   *     user input strings back into a new date object on submission. No timezone
-   *     adjustment is performed.
+   *     timezone. Converting a date stored in the database from UTC to the
+   *     local zone and converting it back to UTC before storing it is not
+   *     handled here. This element accepts a date as the default value, and
+   *     then converts the user input strings back into a new date object on
+   *     submission. No timezone adjustment is performed.
    * Optional properties include:
    *   - #date_date_format: A date format string that describes the format that
    *     should be displayed to the end user for the date. When using HTML5
    *     elements the format MUST use the appropriate HTML5 format for that
-   *     element, no other format will work. See the format_date() function for a
-   *     list of the possible formats and HTML5 standards for the HTML5
+   *     element, no other format will work. See the format_date() function for
+   *     a list of the possible formats and HTML5 standards for the HTML5
    *     requirements. Defaults to the right HTML5 format for the chosen element
    *     if a HTML5 element is used, otherwise defaults to
    *     entity_load('date_format', 'html_date')->getPattern().
@@ -172,23 +174,24 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
    *     if a HTML5 element is used, otherwise defaults to
    *     entity_load('date_format', 'html_time')->getPattern().
    *   - #date_time_callbacks: An array of optional callbacks for the time
-   *     element. Can be used to add a jQuery timepicker or an 'All day' checkbox.
+   *     element. Can be used to add a jQuery timepicker or an 'All day'
+   *     checkbox.
    *   - #date_year_range: A description of the range of years to allow, like
    *     '1900:2050', '-3:+3' or '2000:+3', where the first value describes the
    *     earliest year and the second the latest year in the range. A year
    *     in either position means that specific year. A +/- value describes a
    *     dynamic value that is that many years earlier or later than the current
-   *     year at the time the form is displayed. Used in jQueryUI datepicker year
-   *     range and HTML5 min/max date settings. Defaults to '1900:2050'.
+   *     year at the time the form is displayed. Used in jQueryUI datepicker
+   *     year range and HTML5 min/max date settings. Defaults to '1900:2050'.
    *   - #date_increment: The increment to use for minutes and seconds, i.e.
-   *    '15' would show only :00, :15, :30 and :45. Used for HTML5 step values and
-   *     jQueryUI datepicker settings. Defaults to 1 to show every minute.
-   *   - #date_timezone: The local timezone to use when creating dates. Generally
-   *     this should be left empty and it will be set correctly for the user using
-   *     the form. Useful if the default value is empty to designate a desired
-   *     timezone for dates created in form processing. If a default date is
-   *     provided, this value will be ignored, the timezone in the default date
-   *     takes precedence. Defaults to the value returned by
+   *    '15' would show only :00, :15, :30 and :45. Used for HTML5 step values
+   *     and jQueryUI datepicker settings. Defaults to 1 to show every minute.
+   *   - #date_timezone: The local timezone to use when creating dates.
+   *     Generally this should be left empty and it will be set correctly for
+   *     the user using the form. Useful if the default value is empty to
+   *     designate a desired timezone for dates created in form processing. If a
+   *     default date is provided, this value will be ignored, the timezone in
+   *     the default date takes precedence. Defaults to the value returned by
    *     drupal_get_user_timezone().
    *
    * Example usage:
@@ -227,6 +230,19 @@ public static function processDatetime(&$element, FormStateInterface $form_state
 
     $element['#tree'] = TRUE;
 
+    // Visibility states need to be applied to the element as a whole, but the
+    // other states apply to the individual parts.
+    if (isset($element['#states']) && is_array($element['#states'])) {
+      $wrapper_states = array_filter($element['#states'], function ($key) {
+        return in_array($key, ['visible', 'invisible']);
+      }, ARRAY_FILTER_USE_KEY);
+      $children_states = array_diff_key($element['#states'], $wrapper_states);
+    }
+    else {
+      $wrapper_states = $children_states = [];
+    }
+    $element['#states'] = $wrapper_states;
+
     if ($element['#date_date_element'] != 'none') {
 
       $date_format = $element['#date_date_element'] != 'none' ? static::getHtml5DateFormat($element) : '';
@@ -264,6 +280,7 @@ public static function processDatetime(&$element, FormStateInterface $form_state
         '#size' => max(12, strlen($element['#value']['date'])),
         '#error_no_message' => TRUE,
         '#date_date_format' => $element['#date_date_format'],
+        '#states' => $children_states,
       );
 
       // Allows custom callbacks to alter the element.
@@ -296,6 +313,7 @@ public static function processDatetime(&$element, FormStateInterface $form_state
         '#required' => $element['#required'],
         '#size' => 12,
         '#error_no_message' => TRUE,
+        '#states' => $children_states,
       );
 
       // Allows custom callbacks to alter the element.
@@ -366,8 +384,10 @@ public static function validateDatetime(&$element, FormStateInterface $form_stat
    * This is centralized for a consistent method of creating these examples.
    *
    * @param string $format
+   *   A format acceptable by PHP's date() function.
    *
    * @return string
+   *   A formatted example date.
    */
   public static function formatExample($format) {
     if (!static::$dateExample) {
diff --git a/core/misc/states.js b/core/misc/states.js
index 24374b6..2e18316 100644
--- a/core/misc/states.js
+++ b/core/misc/states.js
@@ -610,6 +610,9 @@
         .prop('disabled', e.value)
         .closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value)
         .find('select, input, textarea').prop('disabled', e.value);
+      // A complex form element, like 'datetime' (one made up of multiple
+      // sub-elements) may have a label for the element as a whole.
+      $(e.target).closest('.js-complex-form-item').toggleClass('form-disabled', e.value);
 
       // Note: WebKit nightlies don't reflect that change correctly.
       // See https://bugs.webkit.org/show_bug.cgi?id=23789
@@ -621,13 +624,20 @@
       if (e.value) {
         var label = 'label' + (e.target.id ? '[for=' + e.target.id + ']' : '');
         var $label = $(e.target).attr({'required': 'required', 'aria-required': 'aria-required'}).closest('.js-form-item, .js-form-wrapper').find(label);
+        // A complex form element, like 'datetime' (one made up of multiple
+        // sub-elements) may have a label for the element as a whole.
+        var $complexLabel = $(e.target).closest('.js-complex-form-item').children('label');
         // Avoids duplicate required markers on initialization.
         if (!$label.hasClass('js-form-required').length) {
           $label.addClass('js-form-required form-required');
         }
+        if (!$complexLabel.hasClass('js-form-required').length) {
+          $complexLabel.addClass('js-form-required form-required');
+        }
       }
       else {
         $(e.target).removeAttr('required aria-required').closest('.js-form-item, .js-form-wrapper').find('label.js-form-required').removeClass('js-form-required form-required');
+        $(e.target).closest('.js-complex-form-item').children('label').removeClass('js-form-required form-required');
       }
     }
   });
diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
index df23426..0b28ccf 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
@@ -22,9 +22,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     // identifying this item in error messages. We do not want to display this
     // title because the actual title display is handled at a higher level by
     // the Field module.
-
     $element['#theme_wrappers'][] = 'datetime_wrapper';
-    $element['#attributes']['class'][] = 'container-inline';
 
     $element['value'] = array(
       '#type' => 'datetime',
diff --git a/core/modules/datetime/src/Tests/DateTimeFieldTest.php b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
index 3ed9df9..ba32dc0 100644
--- a/core/modules/datetime/src/Tests/DateTimeFieldTest.php
+++ b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
@@ -118,7 +118,7 @@ function testDateField() {
     // Display creation form.
     $this->drupalGet('entity_test/add');
     $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Date element found.');
-    $this->assertFieldByXPath('//*[@id="edit-' . $field_name . '-wrapper"]/h4[contains(@class, "js-form-required")]', TRUE, 'Required markup found');
+    $this->assertFieldByXPath('//*[@id="edit-' . $field_name . '-wrapper"]/div/label[contains(@class, "js-form-required")]', TRUE, 'Required markup found');
     $this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Time element not found.');
 
     // Build up a date in the UTC timezone.
diff --git a/core/modules/datetime/tests/modules/datetime_states/datetime_states.info.yml b/core/modules/datetime/tests/modules/datetime_states/datetime_states.info.yml
new file mode 100644
index 0000000..75e139f
--- /dev/null
+++ b/core/modules/datetime/tests/modules/datetime_states/datetime_states.info.yml
@@ -0,0 +1,8 @@
+name: 'Datetime #states test'
+type: module
+description: 'Provides an example demonstrating how form #states affect datetime elements.'
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+  - datetime
diff --git a/core/modules/datetime/tests/modules/datetime_states/datetime_states.routing.yml b/core/modules/datetime/tests/modules/datetime_states/datetime_states.routing.yml
new file mode 100644
index 0000000..f5e44fe
--- /dev/null
+++ b/core/modules/datetime/tests/modules/datetime_states/datetime_states.routing.yml
@@ -0,0 +1,6 @@
+datetime_states.example:
+  path: '/datetime/states-example'
+  defaults:
+    _form: '\Drupal\datetime_states\Form\StateForm'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/datetime/tests/modules/datetime_states/src/Form/StateForm.php b/core/modules/datetime/tests/modules/datetime_states/src/Form/StateForm.php
new file mode 100644
index 0000000..9b00a6d
--- /dev/null
+++ b/core/modules/datetime/tests/modules/datetime_states/src/Form/StateForm.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\datetime_states\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Defines a settings page for the File Hosting module.
+ */
+class StateForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return "datetime_states_form";
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['toggle_invisible'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Invisible'),
+    );
+    $form['toggle_disabled'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Disabled'),
+    );
+    $form['toggle_required'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Required'),
+    );
+
+
+    $form['date'] = array(
+      '#type' => 'datetime',
+      '#title' => $this->t('Datetime'),
+      '#description' => $this->t('A datetime form element.'),
+      '#states' => [
+        'invisible' => [
+          ':input[name="toggle_invisible"]' => ['checked' => TRUE],
+        ],
+        'disabled' => [
+          ':input[name="toggle_disabled"]' => ['checked' => TRUE],
+        ],
+        'required' => [
+          ':input[name="toggle_required"]' => ['checked' => TRUE],
+        ],
+      ],
+    );
+
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Save'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+
+  }
+
+}
diff --git a/core/modules/system/templates/datetime-wrapper.html.twig b/core/modules/system/templates/datetime-wrapper.html.twig
index 8430baa..3b25687 100644
--- a/core/modules/system/templates/datetime-wrapper.html.twig
+++ b/core/modules/system/templates/datetime-wrapper.html.twig
@@ -16,18 +16,25 @@
  */
 #}
 {%
+  set container_classes = [
+    'js-complex-form-item',
+  ]
+%}
+{%
   set title_classes = [
     required ? 'js-form-required',
     required ? 'form-required',
   ]
 %}
-{% if title %}
-  <h4{{ title_attributes.addClass(title_classes) }}>{{ title }}</h4>
-{% endif %}
-{{ content }}
-{% if errors %}
-  <div>
-    {{ errors }}
-  </div>
-{% endif %}
-{{ description }}
+<div{{ attributes.addClass(container_classes) }}>
+  {% if title %}
+    <h4{{ title_attributes.addClass(title_classes) }}>{{ title }}</h4>
+  {% endif %}
+  {{ content }}
+  {% if errors %}
+    <div>
+      {{ errors }}
+    </div>
+  {% endif %}
+  {{ description }}
+</div>
\ No newline at end of file
diff --git a/core/themes/classy/templates/form/datetime-wrapper.html.twig b/core/themes/classy/templates/form/datetime-wrapper.html.twig
index 3f6aa59..069f918 100644
--- a/core/themes/classy/templates/form/datetime-wrapper.html.twig
+++ b/core/themes/classy/templates/form/datetime-wrapper.html.twig
@@ -14,21 +14,34 @@
  */
 #}
 {%
+  set container_classes = [
+    'js-form-item',
+    'form-item',
+    'js-form-type-' ~ type|clean_class,
+    'form-type-' ~ type|clean_class,
+    'js-form-item-' ~ name|clean_class,
+    'form-item-' ~ name|clean_class,
+    'js-complex-form-item',
+    disabled == 'disabled' ? 'form-disabled',
+  ]
+%}
+{%
   set title_classes = [
-    'label',
     required ? 'js-form-required',
     required ? 'form-required',
   ]
 %}
-{% if title %}
-  <h4{{ title_attributes.addClass(title_classes) }}>{{ title }}</h4>
-{% endif %}
-{{ content }}
-{% if errors %}
-  <div class="form-item--error-message">
-    <strong>{{ errors }}</strong>
-  </div>
-{% endif %}
-{% if description %}
-  <div class="description">{{ description }}</div>
-{% endif %}
+<div{{ attributes.addClass(container_classes) }}>
+  {% if title %}
+    <label{{ title_attributes.addClass(title_classes) }}>{{ title }}</label>
+  {% endif %}
+  {{ content }}
+  {% if errors %}
+    <div class="form-item--error-message">
+      <strong>{{ errors }}</strong>
+    </div>
+  {% endif %}
+  {% if description %}
+    <div class="description">{{ description }}</div>
+  {% endif %}
+</div>
diff --git a/core/themes/seven/css/components/form.css b/core/themes/seven/css/components/form.css
index f42d59b..162a579 100644
--- a/core/themes/seven/css/components/form.css
+++ b/core/themes/seven/css/components/form.css
@@ -63,6 +63,8 @@ label[for] {
 .form-disabled input.form-number,
 .form-disabled input.form-color,
 .form-disabled input.form-file,
+.form-disabled input.form-date,
+.form-disabled input.form-time,
 .form-disabled textarea.form-textarea,
 .form-disabled select.form-select {
   border-color: #d4d4d4;
diff --git a/core/themes/stable/templates/form/datetime-wrapper.html.twig b/core/themes/stable/templates/form/datetime-wrapper.html.twig
index 3c37ffd..afdd5e2 100644
--- a/core/themes/stable/templates/form/datetime-wrapper.html.twig
+++ b/core/themes/stable/templates/form/datetime-wrapper.html.twig
@@ -14,18 +14,25 @@
  */
 #}
 {%
+  set container_classes = [
+    'js-complex-form-item',
+  ]
+%}
+{%
   set title_classes = [
     required ? 'js-form-required',
     required ? 'form-required',
   ]
 %}
-{% if title %}
-  <h4{{ title_attributes.addClass(title_classes) }}>{{ title }}</h4>
-{% endif %}
-{{ content }}
-{% if errors %}
-  <div>
-    {{ errors }}
-  </div>
-{% endif %}
-{{ description }}
+<div{{ attributes.addClass(container_classes) }}>
+  {% if title %}
+    <h4{{ title_attributes.addClass(title_classes) }}>{{ title }}</h4>
+  {% endif %}
+  {{ content }}
+  {% if errors %}
+    <div>
+      {{ errors }}
+    </div>
+  {% endif %}
+  {{ description }}
+</div>
\ No newline at end of file
