diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index f180cbd..d187423 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -576,8 +576,14 @@ function template_preprocess_datetime_wrapper(&$variables) {
   // Suppress error messages.
   $variables['errors'] = NULL;
 
+  $variables['description'] = NULL;
   if (!empty($element['#description'])) {
-    $variables['description'] = $element['#description'];
+    $description_attributes = [];
+    if (!empty($element['#id'])) {
+      $description_attributes['id'] = $element['#id'] . '--description';
+    }
+    $variables['description']['attributes'] = new Attribute($description_attributes);
+    $variables['description']['content'] = $element['#description'];
   }
 
   $variables['required'] = FALSE;
diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDatelistWidget.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDatelistWidget.php
index 8cdabf1..06ced05 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDatelistWidget.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDatelistWidget.php
@@ -35,6 +35,9 @@ public static function defaultSettings() {
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
     $element = parent::formElement($items, $delta, $element, $form, $form_state);
 
+    // Wrap all of the select elements with a fieldset.
+    $element['#theme_wrappers'][] = 'fieldset';
+
     $date_order = $this->getSetting('date_order');
 
     if ($this->getFieldSetting('datetime_type') == 'datetime') {
diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php
index 89381ab..6d03ffd 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php
@@ -59,6 +59,16 @@ public static function create(ContainerInterface $container, array $configuratio
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
     $element = parent::formElement($items, $delta, $element, $form, $form_state);
 
+    // If the field is date-only, make sure the title is displayed. Otherwise,
+    // wrap everything in a fieldset, and the title will be shown in the legend.
+    if ($this->getFieldSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
+      $element['value']['#title'] = $this->fieldDefinition->getLabel();
+      $element['value']['#description'] = $this->fieldDefinition->getDescription();
+    }
+    else {
+      $element['#theme_wrappers'][] = 'fieldset';
+    }
+
     // Identify the type of date and time elements to use.
     switch ($this->getFieldSetting('datetime_type')) {
       case DateTimeItem::DATETIME_TYPE_DATE:
diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
index aa6175a..b363eed 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeWidgetBase.php
@@ -17,15 +17,6 @@ class DateTimeWidgetBase extends WidgetBase {
    * {@inheritdoc}
    */
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
-    // We are nesting some sub-elements inside the parent, so we need a wrapper.
-    // We also need to add another #title attribute at the top level for ease in
-    // 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',
       '#default_value' => NULL,
diff --git a/core/modules/datetime/src/Tests/DateTimeFieldTest.php b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
index f7aeae8..9c20e43 100644
--- a/core/modules/datetime/src/Tests/DateTimeFieldTest.php
+++ b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
@@ -47,7 +47,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"]//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. Note that using this will also
diff --git a/core/modules/datetime_range/src/Plugin/Field/FieldWidget/DateRangeWidgetBase.php b/core/modules/datetime_range/src/Plugin/Field/FieldWidget/DateRangeWidgetBase.php
index 87cecbb..a4f8350 100644
--- a/core/modules/datetime_range/src/Plugin/Field/FieldWidget/DateRangeWidgetBase.php
+++ b/core/modules/datetime_range/src/Plugin/Field/FieldWidget/DateRangeWidgetBase.php
@@ -19,6 +19,10 @@ class DateRangeWidgetBase extends DateTimeWidgetBase {
    */
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
     $element = parent::formElement($items, $delta, $element, $form, $form_state);
+
+    // Wrap all of the select elements with a fieldset.
+    $element['#theme_wrappers'][] = 'fieldset';
+
     $element['#element_validate'][] = [$this, 'validateStartEnd'];
     $element['value']['#title'] = $this->t('Start date');
 
diff --git a/core/modules/datetime_range/src/Tests/DateRangeFieldTest.php b/core/modules/datetime_range/src/Tests/DateRangeFieldTest.php
index 759352e..a76e75b 100644
--- a/core/modules/datetime_range/src/Tests/DateRangeFieldTest.php
+++ b/core/modules/datetime_range/src/Tests/DateRangeFieldTest.php
@@ -61,7 +61,7 @@ public function testDateRangeField() {
       $this->drupalGet('entity_test/add');
       $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.');
       $this->assertFieldByName("{$field_name}[0][end_value][date]", '', 'End 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"]//label[contains(@class, "js-form-required")]', TRUE, 'Required markup found');
       $this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Start time element not found.');
       $this->assertNoFieldByName("{$field_name}[0][end_value][time]", '', 'End time element not found.');
 
@@ -415,7 +415,7 @@ public function testAlldayRangeField() {
     $this->drupalGet('entity_test/add');
     $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.');
     $this->assertFieldByName("{$field_name}[0][end_value][date]", '', 'End 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"]//label[contains(@class, "js-form-required")]', TRUE, 'Required markup found');
     $this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Start time element not found.');
     $this->assertNoFieldByName("{$field_name}[0][end_value][time]", '', 'End time element not found.');
 
diff --git a/core/modules/system/templates/datetime-wrapper.html.twig b/core/modules/system/templates/datetime-wrapper.html.twig
index 8430baa..d39df2f 100644
--- a/core/modules/system/templates/datetime-wrapper.html.twig
+++ b/core/modules/system/templates/datetime-wrapper.html.twig
@@ -30,4 +30,6 @@
     {{ errors }}
   </div>
 {% endif %}
-{{ description }}
+{% if description.content %}
+  {{ description.content }}
+{% endif %}
diff --git a/core/themes/classy/templates/form/datetime-wrapper.html.twig b/core/themes/classy/templates/form/datetime-wrapper.html.twig
index 3f6aa59..a11c1e7 100644
--- a/core/themes/classy/templates/form/datetime-wrapper.html.twig
+++ b/core/themes/classy/templates/form/datetime-wrapper.html.twig
@@ -29,6 +29,8 @@
     <strong>{{ errors }}</strong>
   </div>
 {% endif %}
-{% if description %}
-  <div class="description">{{ description }}</div>
+{% if description.content %}
+  <div{{ description.attributes.addClass('description') }}>
+    {{ description.content }}
+  </div>
 {% endif %}
diff --git a/core/themes/stable/templates/form/datetime-wrapper.html.twig b/core/themes/stable/templates/form/datetime-wrapper.html.twig
index 3c37ffd..a89bd8d 100644
--- a/core/themes/stable/templates/form/datetime-wrapper.html.twig
+++ b/core/themes/stable/templates/form/datetime-wrapper.html.twig
@@ -28,4 +28,6 @@
     {{ errors }}
   </div>
 {% endif %}
-{{ description }}
+{% if description.content %}
+  {{ description.content }}
+{% endif %}
