diff --git a/config/install/core.entity_form_display.eventseries.eventseries.default.yml b/config/install/core.entity_form_display.eventseries.eventseries.default.yml
index a2a8bc86e35f0a2f9738e7cd51e53d3570e3aeef..adbba6f41169eb8504e78a44cf1f562dfd1f3cd8 100644
--- a/config/install/core.entity_form_display.eventseries.eventseries.default.yml
+++ b/config/install/core.entity_form_display.eventseries.eventseries.default.yml
@@ -12,41 +12,67 @@ mode: default
 content:
   body:
     type: text_textarea
-    weight: -4
+    weight: 1
     region: content
     settings:
       rows: 5
       placeholder: ''
     third_party_settings: {  }
+  consecutive_recurring_date:
+    type: consecutive_recurring_date
+    weight: 3
+    region: content
+    settings: {  }
+    third_party_settings: {  }
   custom_date:
     type: daterange_default
     label: above
+    weight: 7
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  daily_recurring_date:
+    type: daily_recurring_date
     weight: 4
     region: content
     settings: {  }
     third_party_settings: {  }
+  excluded_dates:
+    type: daterange_default
+    label: above
+    weight: 8
+    settings: {  }
+    region: content
+    third_party_settings: {  }
+  included_dates:
+    type: daterange_default
+    label: above
+    weight: 9
+    settings: {  }
+    region: content
+    third_party_settings: {  }
   monthly_recurring_date:
     type: monthly_recurring_date
-    weight: 3
+    weight: 6
     region: content
     settings: {  }
     third_party_settings: {  }
   recur_type:
     type: options_buttons
     settings: {  }
-    weight: 1
+    weight: 2
     region: content
     third_party_settings: {  }
   status:
     type: boolean_checkbox
     settings:
       display_label: true
-    weight: 120
+    weight: 12
     region: content
     third_party_settings: {  }
   title:
     type: string_textfield
-    weight: -6
+    weight: 0
     region: content
     settings:
       size: 60
@@ -54,7 +80,7 @@ content:
     third_party_settings: {  }
   uid:
     type: entity_reference_autocomplete
-    weight: 5
+    weight: 11
     settings:
       match_operator: CONTAINS
       size: 60
@@ -63,7 +89,7 @@ content:
     third_party_settings: {  }
   weekly_recurring_date:
     type: weekly_recurring_date
-    weight: 2
+    weight: 5
     region: content
     settings: {  }
     third_party_settings: {  }
diff --git a/config/install/recurring_events.eventseries.config.yml b/config/install/recurring_events.eventseries.config.yml
index df30fd43b13c828478bcd94d3ef986ebe9549106..d596d0db663fb14940491068c5ea160aacc92708 100644
--- a/config/install/recurring_events.eventseries.config.yml
+++ b/config/install/recurring_events.eventseries.config.yml
@@ -7,3 +7,8 @@ days: 'monday,tuesday,wednesday,thursday,friday,saturday,sunday'
 limit: 10
 excludes: 1
 includes: 1
+enabled_fields: 'consecutive_recurring_date,daily_recurring_date,weekly_recurring_date,monthly_recurring_date,custom'
+threshold_warning: 1
+threshold_count: 200
+threshold_message: 'Saving this series will create up to @total event instances. This could result in memory exhaustion or site instability.'
+threshold_prevent_save: 0
diff --git a/config/schema/recurring_events.schema.yml b/config/schema/recurring_events.schema.yml
index e726f21933f69932aff8fb7f7d53ef99e99409d2..036148ed0dc6f6a3fff11743c2b6545895f4d190 100644
--- a/config/schema/recurring_events.schema.yml
+++ b/config/schema/recurring_events.schema.yml
@@ -29,6 +29,21 @@ recurring_events.eventseries.config:
     includes:
       type: integer
       label: 'Enable eventseries level included dates'
+    enabled_fields:
+      type: string
+      label: 'Which recur type fields are available in the creation form'
+    threshold_warning:
+      type: integer
+      label: 'Show a warning when too many event instances are being created'
+    threshold_count:
+      type: integer
+      label: 'The number of event instances to trigger the warning'
+    threshold_message:
+      type: string
+      label: 'The warning to show when too many instances are being created'
+    threshold_prevent_save:
+      type: integer
+      label: 'Prevent saving a series if too many instances are being created'
 
 recurring_events.eventinstance.config:
   type: config_object
diff --git a/recurring_events.api.php b/recurring_events.api.php
index 289cff536f95495e4e15a9aac56f36f883e7db5c..798bf2a1608e05241ec96ac51d81f0c16535b6a1 100644
--- a/recurring_events.api.php
+++ b/recurring_events.api.php
@@ -27,6 +27,17 @@ function hook_recurring_events_durations_alter(array &$durations = []) {
   $durations[172800] = t('2 days');
 }
 
+/**
+ * Alter the unit options available when creating an event series entity.
+ *
+ * @param array $units
+ *   An array of units.
+ */
+function hook_recurring_events_units_alter(array &$units = []) {
+  // Events can recur ever millisecond (please do not do this).
+  $units['millisecond'] = t('Millisecond(s)');
+}
+
 /**
  * Alter the days options available when creating an event series entity.
  *
@@ -129,6 +140,17 @@ function hook_recurring_events_event_instances_pre_create_alter(&$event_instance
   }
 }
 
+/**
+ * Alter the array of recur type fields available when creating a series.
+ *
+ * @var array $fields
+ *   The array of recur type fields available.
+ */
+function hook_recurring_events_recur_field_types(&$fields) {
+  // Do not allow weekly events.
+  unset($fields['weekly_recurring_date']);
+}
+
 /**
  * Execute custom code before event instances are deleted.
  *
diff --git a/recurring_events.install b/recurring_events.install
index 7ccb574ca240a4f4d62b07af8ee0ca030d1f1b4a..cf479472ff87ad71fb49f229ecdb9c87c4b51310 100644
--- a/recurring_events.install
+++ b/recurring_events.install
@@ -60,3 +60,112 @@ function recurring_events_update_8001() {
 
   \Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition('included_dates', 'eventseries', 'eventseries', $included_dates);
 }
+
+/**
+ * Update existing series to use the new recur type values.
+ */
+function recurring_events_update_8002(&$sandbox) {
+  // This is a multipass update.
+  // Grab all the weekly or monthly eventseries.
+  if (empty($sandbox['events'])) {
+    $event_series = \Drupal::database()->query('
+      SELECT
+        eventseries_field_data.id
+      FROM
+        eventseries_field_data
+      WHERE
+        recur_type = \'weekly\'
+        OR recur_type = \'monthly\''
+    )->fetchCol(0);
+
+    $sandbox['events'] = $event_series;
+    $sandbox['max'] = count($event_series);
+    $sandbox['limit'] = 20;
+    $sandbox['progress'] = 0;
+  }
+
+  if (count($sandbox['events']) > 0) {
+    // Loop through chunks of 20 events at a time.
+    $events = array_splice($sandbox['events'], 0, $sandbox['limit'], []);
+    if (!empty($events)) {
+      foreach ($events as $event_id) {
+        // Fully load this event so we can update and save it.
+        $event = \Drupal::entityTypeManager()->getStorage('eventseries')->load($event_id);
+        if (!empty($event)) {
+          // If the event was weekly it needs to be weekly_recurring_date.
+          if ($event->getRecurType() == 'weekly') {
+            $event->recur_type = 'weekly_recurring_date';
+            $event->save();
+          }
+          // If the event was monthly it needs to be monthly_recurring_date.
+          elseif ($event->getRecurType() == 'monthly') {
+            $event->recur_type = 'monthly_recurring_date';
+            $event->save();
+          }
+        }
+        $sandbox['progress']++;
+      }
+      echo 'Updated: ' . count($events) . ' eventseries. Total: ' . $sandbox['progress'] . ' of ' . $sandbox['max'] . "\r\n";
+      $sandbox['#finished'] = ($sandbox['progress'] / $sandbox['max']);
+    }
+  }
+  else {
+    $sandbox['#finished'] = 1;
+  }
+}
+
+/**
+ * Configure the event series to have the new recur types and settings config.
+ */
+function recurring_events_update_8003() {
+  // Add the consecutive recurring date type.
+  $consecutive_recurring_date = BaseFieldDefinition::create('consecutive_recurring_date')
+    ->setLabel(t('Consecutive Event'))
+    ->setDescription('The consecutive recurring date configuration.')
+    ->setDisplayConfigurable('form', TRUE)
+    ->setDisplayConfigurable('view', TRUE)
+    ->setRevisionable(TRUE)
+    ->setTranslatable(FALSE)
+    ->setCardinality(1)
+    ->setRequired(FALSE)
+    ->setDisplayOptions('form', [
+      'type' => 'consecutive_recurring_date',
+      'weight' => 1,
+    ]);
+
+  \Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition('consecutive_recurring_date', 'eventseries', 'eventseries', $consecutive_recurring_date);
+
+  // Add the daily recurring date type.
+  $daily_recurring_date = BaseFieldDefinition::create('daily_recurring_date')
+    ->setLabel(t('Daily Event'))
+    ->setDescription('The daily recurring date configuration.')
+    ->setDisplayConfigurable('form', TRUE)
+    ->setDisplayConfigurable('view', TRUE)
+    ->setRevisionable(TRUE)
+    ->setTranslatable(FALSE)
+    ->setCardinality(1)
+    ->setRequired(FALSE)
+    ->setDisplayOptions('form', [
+      'type' => 'daily_recurring_date',
+      'weight' => 2,
+    ]);
+
+  \Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition('daily_recurring_date', 'eventseries', 'eventseries', $daily_recurring_date);
+
+  // Enable all the recur types.
+  $config = \Drupal::configFactory()->getEditable('recurring_events.eventseries.config');
+  $config->set('enabled_fields', 'consecutive_recurring_date,daily_recurring_date,weekly_recurring_date,monthly_recurring_date,custom');
+  $config->save(TRUE);
+}
+
+/**
+ * Install new threshold warning config.
+ */
+function recurring_events_update_8004() {
+  $config = \Drupal::configFactory()->getEditable('recurring_events.eventseries.config');
+  $config->set('threshold_warning', 1);
+  $config->set('threshold_count', 200);
+  $config->set('threshold_message', 'Saving this series will create up to @total event instances. This could result in memory exhaustion or site instability.');
+  $config->set('threshold_prevent_save', 0);
+  $config->save(TRUE);
+}
diff --git a/recurring_events.module b/recurring_events.module
index 16faea695623c9dea9f4e241efcf819bbac6e124..3145ca1a5df19dc215ff108fcf20aa87921a6bb6 100644
--- a/recurring_events.module
+++ b/recurring_events.module
@@ -14,6 +14,8 @@ use Drupal\Core\Form\FormStateInterface;
 use Drupal\recurring_events\Entity\EventSeries;
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
 
 /**
  * Implements hook_help().
@@ -410,3 +412,34 @@ function recurring_events_recurring_events_event_instances_pre_create_alter(arra
     }
   }
 }
+
+/**
+ * Implements callback_allowed_values_function().
+ */
+function recurring_events_allowed_values_function(FieldStorageDefinitionInterface $definition, FieldableEntityInterface $entity = NULL) {
+  $values = ['custom' => t('Custom Event')];
+  $fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions('eventseries');
+  foreach ($fields as $field_name => $field) {
+    $field_definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field->getType());
+    $class = new \ReflectionClass($field_definition['class']);
+    if ($class->implementsInterface('\Drupal\recurring_events\RecurringEventsFieldTypeInterface')) {
+      $values[$field->getName()] = $field->getLabel();
+    }
+  }
+  return $values;
+}
+
+/**
+ * Implements hook_recurring_events_recur_field_types_alter().
+ */
+function recurring_events_recurring_events_recur_field_types_alter(&$fields) {
+  // Enable/disable the recur field types based on the eventseries settings.
+  $config = \Drupal::config('recurring_events.eventseries.config');
+  $enabled_fields = explode(',', $config->get('enabled_fields'));
+
+  foreach ($fields as $field_name => $field_label) {
+    if (array_search($field_name, $enabled_fields) === FALSE) {
+      unset($fields[$field_name]);
+    }
+  }
+}
diff --git a/recurring_events.services.yml b/recurring_events.services.yml
index b0f5718fd5df7655d5008055a6b0d9a39f770cc6..7f36cbbac181ab108207793fa8504d46c7766e96 100644
--- a/recurring_events.services.yml
+++ b/recurring_events.services.yml
@@ -1,7 +1,7 @@
 services:
   recurring_events.event_creation_service:
     class: Drupal\recurring_events\EventCreationService
-    arguments: ['@string_translation', '@database', '@logger.factory', '@messenger', '@date.formatter']
+    arguments: ['@string_translation', '@database', '@logger.factory', '@messenger', '@plugin.manager.field.field_type', '@entity_field.manager']
   plugin.manager.field_inheritance:
     class: Drupal\recurring_events\FieldInheritancePluginManager
     parent: default_plugin_manager
diff --git a/recurring_events.views.inc b/recurring_events.views.inc
index d1b34940018b374690280bf99f1ce15918e762c1..f64c0b95dfa95f4c180c4470454a227c36da0ecc 100644
--- a/recurring_events.views.inc
+++ b/recurring_events.views.inc
@@ -55,20 +55,4 @@ function recurring_events_views_data_alter(array &$data) {
   if (!$set) {
     $data['eventinstance_field_data']['table']['base']['defaults']['field'] = 'id';
   }
-
-  // @todo Remove these declarations when
-  // https://www.drupal.org/project/drupal/issues/2489476 is resolved.
-  $data['eventinstance_field_data']['date__value']['filter']['id'] = 'datetime';
-  $data['eventinstance_field_data']['date__value']['filter']['field_name'] = 'date';
-  $data['eventinstance_field_data']['date__value']['sort']['id'] = 'datetime';
-  $data['eventinstance_field_data']['date__value']['sort']['field_name'] = 'date';
-  $data['eventinstance_field_data']['date__value']['argument']['id'] = 'datetime';
-  $data['eventinstance_field_data']['date__value']['argument']['field_name'] = 'date';
-
-  $data['eventinstance_field_data']['date__end_value']['filter']['id'] = 'datetime';
-  $data['eventinstance_field_data']['date__end_value']['filter']['field_name'] = 'date';
-  $data['eventinstance_field_data']['date__end_value']['sort']['id'] = 'datetime';
-  $data['eventinstance_field_data']['date__end_value']['sort']['field_name'] = 'date';
-  $data['eventinstance_field_data']['date__end_value']['argument']['id'] = 'datetime';
-  $data['eventinstance_field_data']['date__end_value']['argument']['field_name'] = 'date';
 }
diff --git a/src/Entity/EventInstance.php b/src/Entity/EventInstance.php
index 55559770f69e9110eb7e3f9b985a91489faf07fa..2b93988e92512df862d25fc3e586c6ac01714afa 100644
--- a/src/Entity/EventInstance.php
+++ b/src/Entity/EventInstance.php
@@ -285,14 +285,6 @@ class EventInstance extends EditorialContentEntityBase implements EventInterface
     return $this;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getTitle() {
-    // @todo Resolve this with field inheritance.
-    return $this->getEventSeries()->get('title')->value;
-  }
-
   /**
    * {@inheritdoc}
    *
diff --git a/src/Entity/EventSeries.php b/src/Entity/EventSeries.php
index 7af7f54274ebfe61d0190432e54af7974cff768d..c1573172de32d88dc3d1a637062ca20c9558cf32 100644
--- a/src/Entity/EventSeries.php
+++ b/src/Entity/EventSeries.php
@@ -373,29 +373,46 @@ class EventSeries extends EditorialContentEntityBase implements EventInterface {
       ->setTranslatable(FALSE)
       ->setRequired(TRUE)
       ->setCardinality(1)
-      ->setSetting('allowed_values', [
-        'weekly' => t('Weekly Event'),
-        'monthly' => t('Monthly Event'),
-        'custom' => t('Custom Event'),
-      ])
+      ->setSetting('allowed_values_function', 'recurring_events_allowed_values_function')
       ->setDisplayOptions('form', [
         'type' => 'options_buttons',
-        'settings' => [
-          'allowed_values' => [
-            'weekly' => t('Weekly Event'),
-            'monthly' => t('Monthly Event'),
-            'custom' => t('Custom Event'),
-          ],
-        ],
-        'weight' => 1,
+        'weight' => 0,
       ])
       ->setDisplayOptions('view', [
         'label' => 'above',
         'weight' => 10,
       ]);
 
+    $fields['consecutive_recurring_date'] = BaseFieldDefinition::create('consecutive_recurring_date')
+      ->setLabel(t('Consecutive Event'))
+      ->setDescription('The consecutive recurring date configuration.')
+      ->setDisplayConfigurable('form', TRUE)
+      ->setDisplayConfigurable('view', TRUE)
+      ->setRevisionable(TRUE)
+      ->setTranslatable(FALSE)
+      ->setCardinality(1)
+      ->setRequired(FALSE)
+      ->setDisplayOptions('form', [
+        'type' => 'consecutive_recurring_date',
+        'weight' => 1,
+      ]);
+
+    $fields['daily_recurring_date'] = BaseFieldDefinition::create('daily_recurring_date')
+      ->setLabel(t('Daily Event'))
+      ->setDescription('The daily recurring date configuration.')
+      ->setDisplayConfigurable('form', TRUE)
+      ->setDisplayConfigurable('view', TRUE)
+      ->setRevisionable(TRUE)
+      ->setTranslatable(FALSE)
+      ->setCardinality(1)
+      ->setRequired(FALSE)
+      ->setDisplayOptions('form', [
+        'type' => 'daily_recurring_date',
+        'weight' => 2,
+      ]);
+
     $fields['weekly_recurring_date'] = BaseFieldDefinition::create('weekly_recurring_date')
-      ->setLabel(t('Weekly Recurring Date'))
+      ->setLabel(t('Weekly Event'))
       ->setDescription('The weekly recurring date configuration.')
       ->setDisplayConfigurable('form', TRUE)
       ->setDisplayConfigurable('view', TRUE)
@@ -405,11 +422,11 @@ class EventSeries extends EditorialContentEntityBase implements EventInterface {
       ->setRequired(FALSE)
       ->setDisplayOptions('form', [
         'type' => 'weekly_recurring_date',
-        'weight' => 2,
+        'weight' => 3,
       ]);
 
     $fields['monthly_recurring_date'] = BaseFieldDefinition::create('monthly_recurring_date')
-      ->setLabel(t('Monthly Recurring Date'))
+      ->setLabel(t('Monthly Event'))
       ->setDescription('The monthly recurring date configuration.')
       ->setDisplayConfigurable('form', TRUE)
       ->setDisplayConfigurable('view', TRUE)
@@ -419,7 +436,7 @@ class EventSeries extends EditorialContentEntityBase implements EventInterface {
       ->setRequired(FALSE)
       ->setDisplayOptions('form', [
         'type' => 'monthly_recurring_date',
-        'weight' => 3,
+        'weight' => 4,
       ]);
 
     $fields['custom_date'] = BaseFieldDefinition::create('daterange')
@@ -434,7 +451,7 @@ class EventSeries extends EditorialContentEntityBase implements EventInterface {
       ->setDisplayOptions('form', [
         'type' => 'daterange_default',
         'label' => 'above',
-        'weight' => 4,
+        'weight' => 5,
       ]);
 
     $fields['excluded_dates'] = BaseFieldDefinition::create('daterange')
@@ -577,6 +594,130 @@ class EventSeries extends EditorialContentEntityBase implements EventInterface {
     return $this->get('recur_type')->value;
   }
 
+  /**
+   * Get consecutive recurring start date.
+   *
+   * @return Drupal\Core\Datetime\DrupalDateTime
+   *   The date object for the consecutive start date.
+   */
+  public function getConsecutiveStartDate() {
+    $user_timezone = new \DateTimeZone(drupal_get_user_timezone());
+    return $this->get('consecutive_recurring_date')->start_date->setTimezone($user_timezone)->setTime(0, 0, 0);
+  }
+
+  /**
+   * Get consecutive recurring end date.
+   *
+   * @return Drupal\Core\Datetime\DrupalDateTime
+   *   The date object for the consecutive end date.
+   */
+  public function getConsecutiveEndDate() {
+    $user_timezone = new \DateTimeZone(drupal_get_user_timezone());
+    return $this->get('consecutive_recurring_date')->end_date->setTimezone($user_timezone)->setTime(0, 0, 0);
+  }
+
+  /**
+   * Get consecutive recurring start time.
+   *
+   * @return string
+   *   The string for the consecutive start time.
+   */
+  public function getConsecutiveStartTime() {
+    return $this->get('consecutive_recurring_date')->time;
+  }
+
+  /**
+   * Get consecutive recurring end time.
+   *
+   * @return string
+   *   The string for the consecutive end time.
+   */
+  public function getConsecutiveEndTime() {
+    return $this->get('consecutive_recurring_date')->end_time;
+  }
+
+  /**
+   * Get consecutive recurring duration.
+   *
+   * @return int
+   *   The integer for the consecutive duration.
+   */
+  public function getConsecutiveDuration() {
+    return $this->get('consecutive_recurring_date')->duration;
+  }
+
+  /**
+   * Get consecutive recurring duration units.
+   *
+   * @return int
+   *   The value for the consecutive duration units.
+   */
+  public function getConsecutiveDurationUnits() {
+    return $this->get('consecutive_recurring_date')->duration_units;
+  }
+
+  /**
+   * Get consecutive recurring buffer.
+   *
+   * @return int
+   *   The integer for the consecutive buffer.
+   */
+  public function getConsecutiveBuffer() {
+    return $this->get('consecutive_recurring_date')->buffer;
+  }
+
+  /**
+   * Get consecutive recurring duration units.
+   *
+   * @return int
+   *   The value for the consecutive duration units.
+   */
+  public function getConsecutiveBufferUnits() {
+    return $this->get('consecutive_recurring_date')->buffer_units;
+  }
+
+  /**
+   * Get daily recurring start date.
+   *
+   * @return Drupal\Core\Datetime\DrupalDateTime
+   *   The date object for the daily start date.
+   */
+  public function getDailyStartDate() {
+    $user_timezone = new \DateTimeZone(drupal_get_user_timezone());
+    return $this->get('daily_recurring_date')->start_date->setTimezone($user_timezone)->setTime(0, 0, 0);
+  }
+
+  /**
+   * Get daily recurring end date.
+   *
+   * @return Drupal\Core\Datetime\DrupalDateTime
+   *   The date object for the daily end date.
+   */
+  public function getDailyEndDate() {
+    $user_timezone = new \DateTimeZone(drupal_get_user_timezone());
+    return $this->get('daily_recurring_date')->end_date->setTimezone($user_timezone)->setTime(0, 0, 0);
+  }
+
+  /**
+   * Get daily recurring start time.
+   *
+   * @return string
+   *   The string for the daily start time.
+   */
+  public function getDailyStartTime() {
+    return $this->get('daily_recurring_date')->time;
+  }
+
+  /**
+   * Get daily recurring duration.
+   *
+   * @return int
+   *   The integer for the daily duration.
+   */
+  public function getDailyDuration() {
+    return $this->get('daily_recurring_date')->duration;
+  }
+
   /**
    * Get weekly recurring start date.
    *
diff --git a/src/EventCreationService.php b/src/EventCreationService.php
index 4e93b3d81b52321e88741deae9188724935bcf64..9a74d6e27bb684a1ccca7e911deae4780995bdcb 100644
--- a/src/EventCreationService.php
+++ b/src/EventCreationService.php
@@ -10,6 +10,8 @@ use Drupal\recurring_events\Entity\EventSeries;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Messenger\Messenger;
 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
+use Drupal\Core\Entity\EntityFieldManager;
+use Drupal\Core\Field\FieldTypePluginManager;
 
 /**
  * EventCreationService class.
@@ -44,6 +46,20 @@ class EventCreationService {
    */
   protected $messenger;
 
+  /**
+   * The field type plugin manager.
+   *
+   * @var Drupal\Core\Field\FieldTypePluginManager
+   */
+  protected $fieldTypePluginManager;
+
+  /**
+   * The entity field manager.
+   *
+   * @var Drupal\Core\Entity\EntityFieldManager
+   */
+  protected $entityFieldManager;
+
   /**
    * Class constructor.
    *
@@ -55,12 +71,18 @@ class EventCreationService {
    *   The logger factory.
    * @param \Drupal\Core\Messenger\Messenger $messenger
    *   The messenger service.
+   * @param \Drupal\Core\Field\FieldTypePluginManager $field_type_plugin_manager
+   *   The field type plugin manager.
+   * @param \Drupal\Core\Entity\EntityFieldManager $entity_field_manager
+   *   The entity field manager.
    */
-  public function __construct(TranslationInterface $translation, Connection $database, LoggerChannelFactoryInterface $logger, Messenger $messenger) {
+  public function __construct(TranslationInterface $translation, Connection $database, LoggerChannelFactoryInterface $logger, Messenger $messenger, FieldTypePluginManager $field_type_plugin_manager, EntityFieldManager $entity_field_manager) {
     $this->translation = $translation;
     $this->database = $database;
     $this->loggerFactory = $logger->get('recurring_events');
     $this->messenger = $messenger;
+    $this->fieldTypePluginManager = $field_type_plugin_manager;
+    $this->entityFieldManager = $entity_field_manager;
   }
 
   /**
@@ -71,7 +93,9 @@ class EventCreationService {
       $container->get('string_translation'),
       $container->get('database'),
       $container->get('logger.factory'),
-      $container->get('messenger')
+      $container->get('messenger'),
+      $container->get('plugin.manager.field.field_type'),
+      $container->get('entity_field.manager')
     );
   }
 
@@ -124,37 +148,13 @@ class EventCreationService {
     $config['excluded_dates'] = $event->getExcludedDates();
     $config['included_dates'] = $event->getIncludedDates();
 
-    switch ($event->getRecurType()) {
-      case 'weekly':
-        $config['start_date'] = $event->getWeeklyStartDate();
-        $config['end_date'] = $event->getWeeklyEndDate();
-        $config['time'] = $event->getWeeklyStartTime();
-        $config['duration'] = $event->getWeeklyDuration();
-        $config['days'] = $event->getWeeklyDays();
-        break;
-
-      case 'monthly':
-        $config['start_date'] = $event->getMonthlyStartDate();
-        $config['end_date'] = $event->getMonthlyEndDate();
-        $config['time'] = $event->getMonthlyStartTime();
-        $config['duration'] = $event->getMonthlyDuration();
-        $config['monthly_type'] = $event->getMonthlyType();
-
-        switch ($event->getMonthlyType()) {
-          case 'weekday':
-            $config['day_occurrence'] = $event->getMonthlyDayOccurrences();
-            $config['days'] = $event->getMonthlyDays();
-            break;
-
-          case 'monthday':
-            $config['day_of_month'] = $event->getMonthlyDayOfMonth();
-            break;
-        }
-        break;
-
-      case 'custom':
-        $config['custom_dates'] = $event->getCustomDates();
-        break;
+    if ($config['type'] === 'custom') {
+      $config['custom_dates'] = $event->getCustomDates();
+    }
+    else {
+      $field_definition = $this->fieldTypePluginManager->getDefinition($config['type']);
+      $field_class = $field_definition['class'];
+      $config += $field_class::convertEntityConfigToArray($event);
     }
 
     \Drupal::moduleHandler()->alter('recurring_events_entity_config_array', $config);
@@ -183,95 +183,45 @@ class EventCreationService {
     $config['excluded_dates'] = $this->getDatesFromForm($user_input['excluded_dates']);
     $config['included_dates'] = $this->getDatesFromForm($user_input['included_dates']);
 
-    switch ($config['type']) {
-      case 'weekly':
-        $time = $user_input['weekly_recurring_date'][0]['time'];
-        $time_parts = $this->convertTimeTo24hourFormat($time);
-        $timestamp = implode(':', $time_parts) . ':00';
-
-        $start_timestamp = $user_input['weekly_recurring_date'][0]['value']['date'] . 'T' . $timestamp;
-        $start_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start_timestamp, $user_timezone);
-        $start_date->setTime(0, 0, 0);
-
-        $end_timestamp = $user_input['weekly_recurring_date'][0]['end_value']['date'] . 'T' . $timestamp;
-        $end_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $end_timestamp, $user_timezone);
-        $end_date->setTime(0, 0, 0);
-
-        $config['start_date'] = $start_date;
-        $config['end_date'] = $end_date;
-
-        $config['time'] = $time;
-        $config['duration'] = $user_input['weekly_recurring_date'][0]['duration'];
-        $config['days'] = array_filter(array_values($user_input['weekly_recurring_date'][0]['days']));
-        break;
-
-      case 'monthly':
-        $time = $user_input['monthly_recurring_date'][0]['time'];
-        $time_parts = $this->convertTimeTo24hourFormat($time);
-        $timestamp = implode(':', $time_parts) . ':00';
-
-        $start_timestamp = $user_input['monthly_recurring_date'][0]['value']['date'] . 'T' . $timestamp;
-        $start_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start_timestamp, $user_timezone);
-        $start_date->setTime(0, 0, 0);
-
-        $end_timestamp = $user_input['monthly_recurring_date'][0]['end_value']['date'] . 'T' . $timestamp;
-        $end_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $end_timestamp, $user_timezone);
-        $end_date->setTime(0, 0, 0);
-
-        $config['start_date'] = $start_date;
-        $config['end_date'] = $end_date;
-
-        $config['time'] = $time;
-        $config['duration'] = $user_input['monthly_recurring_date'][0]['duration'];
-        $config['monthly_type'] = $user_input['monthly_recurring_date'][0]['type'];
-
-        switch ($config['monthly_type']) {
-          case 'weekday':
-            $config['day_occurrence'] = array_filter(array_values($user_input['monthly_recurring_date'][0]['day_occurrence']));
-            $config['days'] = array_filter(array_values($user_input['monthly_recurring_date'][0]['days']));
-            break;
-
-          case 'monthday':
-            $config['day_of_month'] = array_filter(array_values($user_input['monthly_recurring_date'][0]['day_of_month']));
-            break;
-        }
-        break;
+    if ($config['type'] === 'custom') {
+      foreach ($user_input['custom_date'] as $custom_date) {
+        $start_date = $end_date = NULL;
 
-      case 'custom':
-        foreach ($user_input['custom_date'] as $custom_date) {
-          $start_date = $end_date = NULL;
+        if (!empty($custom_date['value']['date'])
+          && !empty($custom_date['value']['time'])
+          && !empty($custom_date['end_value']['date'])
+          && !empty($custom_date['end_value']['time'])) {
 
-          if (!empty($custom_date['value']['date'])
-            && !empty($custom_date['value']['time'])
-            && !empty($custom_date['end_value']['date'])
-            && !empty($custom_date['end_value']['time'])) {
-
-            // For some reason, sometimes we do not receive seconds from the
-            // date range picker.
-            if (strlen($custom_date['value']['time']) == 5) {
-              $custom_date['value']['time'] .= ':00';
-            }
-            if (strlen($custom_date['end_value']['time']) == 5) {
-              $custom_date['end_value']['time'] .= ':00';
-            }
+          // For some reason, sometimes we do not receive seconds from the
+          // date range picker.
+          if (strlen($custom_date['value']['time']) == 5) {
+            $custom_date['value']['time'] .= ':00';
+          }
+          if (strlen($custom_date['end_value']['time']) == 5) {
+            $custom_date['end_value']['time'] .= ':00';
+          }
 
-            $start_timestamp = implode('T', $custom_date['value']);
-            $start_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start_timestamp, $user_timezone);
-            // Convert the DateTime object back to UTC timezone.
-            $start_date->setTimezone($utc_timezone);
+          $start_timestamp = implode('T', $custom_date['value']);
+          $start_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start_timestamp, $user_timezone);
+          // Convert the DateTime object back to UTC timezone.
+          $start_date->setTimezone($utc_timezone);
 
-            $end_timestamp = implode('T', $custom_date['end_value']);
-            $end_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $end_timestamp, $user_timezone);
-            // Convert the DateTime object back to UTC timezone.
-            $end_date->setTimezone($utc_timezone);
+          $end_timestamp = implode('T', $custom_date['end_value']);
+          $end_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $end_timestamp, $user_timezone);
+          // Convert the DateTime object back to UTC timezone.
+          $end_date->setTimezone($utc_timezone);
 
-            $config['custom_dates'][] = [
-              'start_date' => $start_date,
-              'end_date' => $end_date,
-            ];
-          }
+          $config['custom_dates'][] = [
+            'start_date' => $start_date,
+            'end_date' => $end_date,
+          ];
         }
-        break;
+      }
+    }
+    else {
+      $field_definition = $this->fieldTypePluginManager->getDefinition($config['type']);
+      $field_class = $field_definition['class'];
+      $config += $field_class::convertFormConfigToArray($form_state);
     }
 
     \Drupal::moduleHandler()->alter('recurring_events_form_config_array', $config);
@@ -335,108 +285,34 @@ class EventCreationService {
           'override' => $config_dates,
         ];
       }
-      switch ($entity_config['type']) {
-        case 'weekly':
-        case 'monthly':
-          if ($entity_config['start_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT) !== $form_config['start_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT)) {
-            $diff['start_date'] = [
-              'label' => $this->translation->translate('Start Date'),
-              'stored' => $entity_config['start_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
-              'override' => $form_config['start_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
-            ];
-          }
-          if ($entity_config['end_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT) !== $form_config['end_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT)) {
-            $diff['end_date'] = [
-              'label' => $this->translation->translate('End Date'),
-              'stored' => $entity_config['end_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
-              'override' => $form_config['end_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
-            ];
-          }
-          if ($entity_config['time'] !== $form_config['time']) {
-            $diff['time'] = [
-              'label' => $this->translation->translate('Time'),
-              'stored' => $entity_config['time'],
-              'override' => $form_config['time'],
-            ];
-          }
-          if ($entity_config['duration'] !== $form_config['duration']) {
-            $diff['duration'] = [
-              'label' => $this->translation->translate('Duration'),
-              'stored' => $entity_config['duration'],
-              'override' => $form_config['duration'],
-            ];
-          }
 
-          if ($entity_config['type'] === 'weekly') {
-            if ($entity_config['days'] !== $form_config['days']) {
-              $diff['days'] = [
-                'label' => $this->translation->translate('Days'),
-                'stored' => implode(',', $entity_config['days']),
-                'override' => implode(',', $form_config['days']),
-              ];
-            }
-          }
+      if ($entity_config['type'] === 'custom') {
+        if ($entity_config['custom_dates'] !== $form_config['custom_dates']) {
+          $stored_start_ends = $overridden_start_ends = [];
 
-          if ($entity_config['type'] === 'monthly') {
-            if ($entity_config['monthly_type'] !== $form_config['monthly_type']) {
-              $diff['monthly_type'] = [
-                'label' => $this->translation->translate('Monthly Type'),
-                'stored' => $entity_config['monthly_type'],
-                'override' => $form_config['monthly_type'],
-              ];
-            }
-            if ($entity_config['monthly_type'] === 'weekday') {
-              if ($entity_config['day_occurrence'] !== $form_config['day_occurrence']) {
-                $diff['day_occurrence'] = [
-                  'label' => $this->translation->translate('Day Occurrence'),
-                  'stored' => implode(',', $entity_config['day_occurrence']),
-                  'override' => implode(',', $form_config['day_occurrence']),
-                ];
-              }
-              if ($entity_config['days'] !== $form_config['days']) {
-                $diff['days'] = [
-                  'label' => $this->translation->translate('Days'),
-                  'stored' => implode(',', $entity_config['days']),
-                  'override' => implode(',', $form_config['days']),
-                ];
-              }
-            }
-            else {
-              if ($entity_config['day_of_month'] !== $form_config['day_of_month']) {
-                $diff['day_of_month'] = [
-                  'label' => $this->translation->translate('Day of the Month'),
-                  'stored' => implode(',', $entity_config['day_of_month']),
-                  'override' => implode(',', $form_config['day_of_month']),
-                ];
-              }
+          foreach ($entity_config['custom_dates'] as $date) {
+            if (!empty($date['start_date']) && !empty($date['end_date'])) {
+              $stored_start_ends[] = $date['start_date']->format('Y-m-d h:ia') . ' - ' . $date['end_date']->format('Y-m-d h:ia');
             }
           }
 
-          break;
-
-        case 'custom':
-          if ($entity_config['custom_dates'] !== $form_config['custom_dates']) {
-            $stored_start_ends = $overridden_start_ends = [];
-
-            foreach ($entity_config['custom_dates'] as $date) {
-              if (!empty($date['start_date']) && !empty($date['end_date'])) {
-                $stored_start_ends[] = $date['start_date']->format('Y-m-d h:ia') . ' - ' . $date['end_date']->format('Y-m-d h:ia');
-              }
-            }
-
-            foreach ($form_config['custom_dates'] as $dates) {
-              if (!empty($date['start_date']) && !empty($date['end_date'])) {
-                $overridden_start_ends[] = $date['start_date']->format('Y-m-d h:ia') . ' - ' . $date['end_date']->format('Y-m-d h:ia');
-              }
+          foreach ($form_config['custom_dates'] as $dates) {
+            if (!empty($date['start_date']) && !empty($date['end_date'])) {
+              $overridden_start_ends[] = $date['start_date']->format('Y-m-d h:ia') . ' - ' . $date['end_date']->format('Y-m-d h:ia');
             }
-
-            $diff['custom_dates'] = [
-              'label' => $this->translation->translate('Custom Dates'),
-              'stored' => implode(', ', $stored_start_ends),
-              'override' => implode(', ', $overridden_start_ends),
-            ];
           }
-          break;
+
+          $diff['custom_dates'] = [
+            'label' => $this->translation->translate('Custom Dates'),
+            'stored' => implode(', ', $stored_start_ends),
+            'override' => implode(', ', $overridden_start_ends),
+          ];
+        }
+      }
+      else {
+        $field_definition = $this->fieldTypePluginManager->getDefinition($entity_config['type']);
+        $field_class = $field_definition['class'];
+        $diff += $field_class::buildDiffArray($entity_config, $form_config);
       }
     }
 
@@ -509,137 +385,42 @@ class EventCreationService {
     $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
 
     if (!empty($form_data['type'])) {
-      switch ($form_data['type']) {
-        case 'custom':
-          if (!empty($form_data['custom_dates'])) {
-            $events_to_create = [];
-            foreach ($form_data['custom_dates'] as $date_range) {
-              // Set this event to be created.
-              $events_to_create[$date_range['start_date']->format('r')] = [
-                'start_date' => $date_range['start_date'],
-                'end_date' => $date_range['end_date'],
-              ];
-            }
-
-            // Allow modules to alter the array of event instances before they
-            // get created.
-            \Drupal::moduleHandler()->alter('recurring_events_event_instances_pre_create', $events_to_create, $event);
-
-            if (!empty($events_to_create)) {
-              foreach ($events_to_create as $custom_event) {
-                $event_instances[] = $this->createEventInstance($event, $custom_event['start_date'], $custom_event['end_date']);
-              }
-            }
+      if ($form_data['type'] === 'custom') {
+        if (!empty($form_data['custom_dates'])) {
+          $events_to_create = [];
+          foreach ($form_data['custom_dates'] as $date_range) {
+            // Set this event to be created.
+            $events_to_create[$date_range['start_date']->format('r')] = [
+              'start_date' => $date_range['start_date'],
+              'end_date' => $date_range['end_date'],
+            ];
           }
-          break;
 
-        case 'weekly':
-          if (!empty($form_data['days'])) {
-            $dates = [];
+          // Allow modules to alter the array of event instances before they
+          // get created.
+          \Drupal::moduleHandler()->alter('recurring_events_event_instances_pre_create', $events_to_create, $event);
 
-            // Loop through each weekday and find occurrences of it in the
-            // date range provided.
-            foreach ($form_data['days'] as $weekday) {
-              $weekday_dates = $this->findWeekdaysBetweenDates($weekday, $form_data['start_date'], $form_data['end_date']);
-              $dates = array_merge($dates, $weekday_dates);
-            }
-            $time_parts = $this->convertTimeTo24hourFormat($form_data['time']);
-
-            if (!empty($dates)) {
-              $events_to_create = [];
-              foreach ($dates as $weekly_date) {
-                // Set the time of the start date to be the hours and minutes.
-                $weekly_date->setTime($time_parts[0], $time_parts[1]);
-                // Configure the right timezone.
-                $weekly_date->setTimezone($utc_timezone);
-                // Create a clone of this date.
-                $weekly_date_end = clone $weekly_date;
-                // Add the number of seconds specified in the duration field.
-                $weekly_date_end->modify('+' . $form_data['duration'] . ' seconds');
-                // Set this event to be created.
-                $events_to_create[$weekly_date->format('r')] = [
-                  'start_date' => $weekly_date,
-                  'end_date' => $weekly_date_end,
-                ];
-              }
-
-              // Allow modules to alter the array of event instances before they
-              // get created.
-              \Drupal::moduleHandler()->alter('recurring_events_event_instances_pre_create', $events_to_create, $event);
-
-              if (!empty($events_to_create)) {
-                foreach ($events_to_create as $weekly_event) {
-                  $event_instances[] = $this->createEventInstance($event, $weekly_event['start_date'], $weekly_event['end_date']);
-                }
-              }
+          if (!empty($events_to_create)) {
+            foreach ($events_to_create as $custom_event) {
+              $event_instances[] = $this->createEventInstance($event, $custom_event['start_date'], $custom_event['end_date']);
             }
           }
-          break;
-
-        case 'monthly':
-          $dates = [];
-          $time_parts = $this->convertTimeTo24hourFormat($form_data['time']);
-
-          if (!empty($form_data['monthly_type'])) {
-            $dates = [];
-            switch ($form_data['monthly_type']) {
-              case 'weekday':
-                // Loop through each weekday occurrence and weekday.
-                if (!empty($form_data['day_occurrence']) && !empty($form_data['days'])) {
-                  foreach ($form_data['day_occurrence'] as $occurrence) {
-                    foreach ($form_data['days'] as $weekday) {
-                      // Find the occurrence of the specific weekdays within
-                      // each month.
-                      $day_occurrences = $this->findWeekdayOccurrencesBetweenDates($occurrence, $weekday, $form_data['start_date'], $form_data['end_date']);
-                      $dates = array_merge($dates, $day_occurrences);
-                    }
-                  }
-                }
-                break;
-
-              case 'monthday':
-                foreach ($form_data['day_of_month'] as $day_of_month) {
-                  $days_of_month = $this->findMonthDaysBetweenDates($day_of_month, $form_data['start_date'], $form_data['end_date']);
-                  $dates = array_merge($dates, $days_of_month);
-                }
-                break;
+        }
+      }
+      else {
+        $field_definition = $this->fieldTypePluginManager->getDefinition($form_data['type']);
+        $field_class = $field_definition['class'];
+        $events_to_create = $field_class::calculateInstances($form_data);
 
-            }
+        // Allow modules to alter the array of event instances before they
+        // get created.
+        \Drupal::moduleHandler()->alter('recurring_events_event_instances_pre_create', $events_to_create, $event);
 
-            // If valid recurring dates were found.
-            if (!empty($dates)) {
-              $events_to_create = [];
-              foreach ($dates as $monthly_date) {
-                // Set the time of the start date to be the hours and
-                // minutes.
-                $monthly_date->setTime($time_parts[0], $time_parts[1]);
-                // Configure the timezone.
-                $monthly_date->setTimezone($utc_timezone);
-                // Create a clone of this date.
-                $monthly_date_end = clone $monthly_date;
-                // Add the number of seconds specified in the duration
-                // field.
-                $monthly_date_end->modify('+' . $form_data['duration'] . ' seconds');
-                // Set this event to be created.
-                $events_to_create[$monthly_date->format('r')] = [
-                  'start_date' => $monthly_date,
-                  'end_date' => $monthly_date_end,
-                ];
-              }
-
-              // Allow modules to alter the array of event instances before they
-              // get created.
-              \Drupal::moduleHandler()->alter('recurring_events_event_instances_pre_create', $events_to_create, $event);
-
-              if (!empty($events_to_create)) {
-                foreach ($events_to_create as $monthly_event) {
-                  $event_instances[] = $this->createEventInstance($event, $monthly_event['start_date'], $monthly_event['end_date']);
-                }
-              }
-            }
+        if (!empty($events_to_create)) {
+          foreach ($events_to_create as $event_to_create) {
+            $event_instances[] = $this->createEventInstance($event, $event_to_create['start_date'], $event_to_create['end_date']);
           }
-          break;
-
+        }
       }
     }
 
@@ -650,239 +431,6 @@ class EventCreationService {
     $event->set('event_instances', $event_instances);
   }
 
-  /**
-   * Convert a time from 12 hour format to 24 hour format.
-   *
-   * @var string $time
-   *   The time to convert to 24 hour format.
-   *
-   * @return array
-   *   An array of time parts.
-   */
-  public function convertTimeTo24hourFormat($time) {
-    $time_parts = [];
-
-    // Split the start time up to separate out hours and minutes.
-    $time_parts = explode(':', $time);
-    // If this is PM then add 12 hours to the hours, unless the time was
-    // set as noon.
-    if (strpos($time_parts[1], 'pm') !== FALSE && $time_parts[0] != '12') {
-      $time_parts[0] += 12;
-    }
-    // If this is AM and the time was midnight, set hours to 00.
-    elseif (strpos($time_parts[1], 'am') !== FALSE && $time_parts[0] == '12') {
-      $time_parts[0] = '00';
-    }
-    // Strip out AM or PM from the time.
-    $time_parts[1] = substr($time_parts[1], 0, -3);
-
-    return $time_parts;
-  }
-
-  /**
-   * Find all the weekday occurrences between two dates.
-   *
-   * @param string $weekday
-   *   The name of the day of the week.
-   * @param Drupal\Core\Datetime\DrupalDateTime $start_date
-   *   The start date.
-   * @param Drupal\Core\Datetime\DrupalDateTime $end_date
-   *   The end date.
-   *
-   * @return array
-   *   An array of matching dates.
-   */
-  public static function findWeekdaysBetweenDates($weekday, DrupalDateTime $start_date, DrupalDateTime $end_date) {
-    $dates = [];
-
-    // Clone the date as we do not want to make changes to the original object.
-    $start = clone $start_date;
-    $end = clone $end_date;
-
-    // We want to create events up to and including the last day, so modify the
-    // end date to be midnight of the next day.
-    $end->modify('midnight next day');
-
-    // If the start date is after the end date then we have an invalid range so
-    // just return nothing.
-    if ($start->getTimestamp() > $end->getTimestamp()) {
-      return $dates;
-    }
-
-    // If the start date is not the weekday we are seeking, jump to the next
-    // instance of that weekday.
-    if ($start->format('l') != ucwords($weekday)) {
-      $start->modify('next ' . $weekday);
-    }
-
-    // Loop through a week at a time, storing the date in the array to return
-    // until the end date is surpassed.
-    while ($start->getTimestamp() <= $end->getTimestamp()) {
-      // If we do not clone here we end up modifying the value of start in
-      // the array and get some funky dates returned.
-      $dates[] = clone $start;
-      $start->modify('+1 week');
-    }
-
-    return $dates;
-  }
-
-  /**
-   * Find all the day-of-month occurrences between two dates.
-   *
-   * @param int $day_of_month
-   *   The day of the month.
-   * @param Drupal\Core\Datetime\DrupalDateTime $start_date
-   *   The start date.
-   * @param Drupal\Core\Datetime\DrupalDateTime $end_date
-   *   The end date.
-   *
-   * @return array
-   *   An array of matching dates.
-   */
-  public function findMonthDaysBetweenDates($day_of_month, DrupalDateTime $start_date, DrupalDateTime $end_date) {
-    $dates = [];
-
-    // Clone the date as we do not want to make changes to the original object.
-    $start = clone $start_date;
-    $end = clone $end_date;
-
-    // We want to create events up to and including the last day, so modify the
-    // end date to be midnight of the next day.
-    $end->modify('midnight next day');
-
-    // If the start date is after the end date then we have an invalid range so
-    // just return nothing.
-    if ($start->getTimestamp() > $end->getTimestamp()) {
-      return $dates;
-    }
-
-    $day_to_check = $day_of_month;
-
-    // If day of month is set to -1 that is the last day of the month, we need
-    // to calculate how many days a month has.
-    if ($day_of_month === '-1') {
-      $day_to_check = $start->format('t');
-    }
-
-    // If the day of the month is after the start date.
-    if ($start->format('d') < $day_to_check) {
-      $new_date = clone $start;
-      $curr_month = $new_date->format('m');
-      $curr_year = $new_date->format('Y');
-
-      // Check to see if that date is a valid date.
-      if (!checkdate($curr_month, $day_to_check, $curr_year)) {
-        // If not, go find the next valid date.
-        $start = $this->findNextMonthDay($day_of_month, $start);
-      }
-      else {
-        // This is a valid date, so let us start there.
-        $start->setDate($curr_year, $curr_month, $day_to_check);
-      }
-    }
-    // If the day of the month is in the past.
-    elseif ($start->format('d') > $day_to_check) {
-      // Find the next valid start date.
-      $start = $this->findNextMonthDay($day_of_month, $start);
-    }
-
-    // Loop through each month checking to see if the day of the month is a
-    // valid day, until the end date has been surpassed.
-    while ($start->getTimestamp() <= $end->getTimestamp()) {
-      // If we do not clone here we end up modifying the value of start in
-      // the array and get some funky dates returned.
-      $dates[] = clone $start;
-      // Find the next valid event date.
-      $start = $this->findNextMonthDay($day_of_month, $start);
-    }
-
-    return $dates;
-  }
-
-  /**
-   * Find the next day-of-month occurrence.
-   *
-   * @param int $day_of_month
-   *   The day of the month.
-   * @param Drupal\Core\Datetime\DrupalDateTime $date
-   *   The start date.
-   *
-   * @return Drupal\Core\Datetime\DrupalDateTime
-   *   The next occurrence of a specific day of the month.
-   */
-  public function findNextMonthDay($day_of_month, DrupalDateTime $date) {
-    $new_date = clone $date;
-
-    $curr_month = $new_date->format('m');
-    $curr_year = $new_date->format('Y');
-    $next_month = $curr_month;
-    $next_year = $curr_year;
-
-    do {
-      $next_month = ($next_month + 1) % 12 ?: 12;
-      $next_year = $next_month == 1 ? $next_year + 1 : $next_year;
-
-      // If the desired day of the month is the last day, calculate what that
-      // day is.
-      if ($day_of_month === '-1') {
-        $new_date->setDate($next_year, $next_month, '1');
-        $day_of_month = $new_date->format('t');
-      }
-    } while (checkdate($next_month, $day_of_month, $next_year) === FALSE);
-
-    $new_date->setDate($next_year, $next_month, $day_of_month);
-    return $new_date;
-  }
-
-  /**
-   * Find all the monthly occurrences of a specific weekday between two dates.
-   *
-   * @param string $occurrence
-   *   Which occurrence of the weekday to find.
-   * @param string $weekday
-   *   The name of the day of the week.
-   * @param Drupal\Core\Datetime\DrupalDateTime $start_date
-   *   The start date.
-   * @param Drupal\Core\Datetime\DrupalDateTime $end_date
-   *   The end date.
-   *
-   * @return array
-   *   An array of matching dates.
-   */
-  public function findWeekdayOccurrencesBetweenDates($occurrence, $weekday, DrupalDateTime $start_date, DrupalDateTime $end_date) {
-    $dates = [];
-
-    // Clone the date as we do not want to make changes to the original object.
-    $start = clone $start_date;
-
-    // If the start date is after the end date then we have an invalid range so
-    // just return nothing.
-    if ($start->getTimestamp() > $end_date->getTimestamp()) {
-      return $dates;
-    }
-
-    // Grab the occurrence of the weekday we want for this current month.
-    $start->modify($occurrence . ' ' . $weekday . ' of this month');
-
-    // Make sure we didn't just go back in time.
-    if ($start < $start_date) {
-      // Go straight to next month.
-      $start->modify($occurrence . ' ' . $weekday . ' of next month');
-    }
-
-    // Loop through a week at a time, storing the date in the array to return
-    // until the end date is surpassed.
-    while ($start->getTimestamp() <= $end_date->getTimestamp()) {
-      // If we do not clone here we end up modifying the value of start in
-      // the array and get some funky dates returned.
-      $dates[] = clone $start;
-      $start->modify($occurrence . ' ' . $weekday . ' of next month');
-    }
-
-    return $dates;
-  }
-
   /**
    * Create an event instance from an event series.
    *
@@ -968,4 +516,35 @@ class EventCreationService {
     return $string;
   }
 
+  /**
+   * Retrieve the recur field types.
+   *
+   * @param bool $allow_alter
+   *   Allow altering of the field types.
+   *
+   * @return array
+   *   An array of field types.
+   */
+  public function getRecurFieldTypes($allow_alter = TRUE) {
+    // Build an array of recur type field options based on FieldTypes that
+    // implement the Drupal\recurring_events\RecurringEventsFieldTypeInterface
+    // interface. Allow for other modules to customize this list with an alter
+    // hook.
+    $recur_fields = [];
+    $fields = $this->entityFieldManager->getBaseFieldDefinitions('eventseries');
+    foreach ($fields as $field_name => $field) {
+      $field_definition = $this->fieldTypePluginManager->getDefinition($field->getType());
+      $class = new \ReflectionClass($field_definition['class']);
+      if ($class->implementsInterface('\Drupal\recurring_events\RecurringEventsFieldTypeInterface')) {
+        $recur_fields[$field->getName()] = $field->getLabel();
+      }
+    }
+
+    $recur_fields['custom'] = t('Custom Event');
+    if ($allow_alter) {
+      \Drupal::moduleHandler()->alter('recurring_events_recur_field_types', $recur_fields);
+    }
+    return $recur_fields;
+  }
+
 }
diff --git a/src/Form/EventInstanceSettingsForm.php b/src/Form/EventInstanceSettingsForm.php
index db8c0ae934c6f358c9b446dce7514841bb969047..b4572ca3c9a1b5a400abc3f6c4aa42d437b738da 100644
--- a/src/Form/EventInstanceSettingsForm.php
+++ b/src/Form/EventInstanceSettingsForm.php
@@ -60,7 +60,7 @@ class EventInstanceSettingsForm extends ConfigFormBase {
    *   Form definition array.
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
-    $config = $this->config('recurring_events.eventinstance.config');
+    $config = $this->config('recurring_events.eventseries.config');
 
     $php_date_url = Url::fromUri('https://secure.php.net/manual/en/function.date.php');
     $php_date_link = Link::fromTextAndUrl($this->t('PHP date/time format'), $php_date_url);
diff --git a/src/Form/EventSeriesForm.php b/src/Form/EventSeriesForm.php
index b979d24fe95bbbce2fbd4f60a2ee95661c69c63b..6e4767b08f8a1f615e0299aa3f651cb2c9e45290 100644
--- a/src/Form/EventSeriesForm.php
+++ b/src/Form/EventSeriesForm.php
@@ -10,6 +10,9 @@ use Drupal\recurring_events\EventCreationService;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Messenger\Messenger;
 use Drupal\Core\Datetime\DateFormatter;
+use Drupal\Core\Entity\EntityFieldManager;
+use Drupal\Core\Field\FieldTypePluginManager;
+use Drupal\recurring_events\Plugin\Field\FieldWidget\ConsecutiveRecurringDateWidget;
 
 /**
  * Form controller for the eventseries entity create form.
@@ -53,6 +56,20 @@ class EventSeriesForm extends ContentEntityForm {
    */
   protected $dateFormatter;
 
+  /**
+   * The entity field manager.
+   *
+   * @var Drupal\Core\Entity\EntityFieldManager
+   */
+  protected $entityFieldManager;
+
+  /**
+   * The field type plugin manager.
+   *
+   * @var Drupal\Core\Field\FieldTypePluginManager
+   */
+  protected $fieldTypePluginManager;
+
   /**
    * {@inheritdoc}
    */
@@ -62,7 +79,9 @@ class EventSeriesForm extends ContentEntityForm {
       $container->get('entity_type.manager')->getStorage('eventseries'),
       $container->get('entity.manager'),
       $container->get('messenger'),
-      $container->get('date.formatter')
+      $container->get('date.formatter'),
+      $container->get('entity_field.manager'),
+      $container->get('plugin.manager.field.field_type')
     );
   }
 
@@ -79,12 +98,18 @@ class EventSeriesForm extends ContentEntityForm {
    *   The messenger service.
    * @param \Drupal\Core\Datetime\DateFormatter $date_formatter
    *   The date formatter service.
+   * @param \Drupal\Core\Entity\EntityFieldManager $entity_field_manager
+   *   The entity field manager.
+   * @param \Drupal\Core\Field\FieldTypePluginManager $field_type_plugin_manager
+   *   The field type plugin manager.
    */
-  public function __construct(EventCreationService $creation_service, EntityStorageInterface $storage, EntityManagerInterface $entity_manager, Messenger $messenger, DateFormatter $date_formatter) {
+  public function __construct(EventCreationService $creation_service, EntityStorageInterface $storage, EntityManagerInterface $entity_manager, Messenger $messenger, DateFormatter $date_formatter, EntityFieldManager $entity_field_manager, FieldTypePluginManager $field_type_plugin_manager) {
     $this->creationService = $creation_service;
     $this->storage = $storage;
     $this->messenger = $messenger;
     $this->dateFormatter = $date_formatter;
+    $this->entityFieldManager = $entity_field_manager;
+    $this->fieldTypePluginManager = $field_type_plugin_manager;
     parent::__construct($entity_manager);
   }
 
@@ -94,6 +119,8 @@ class EventSeriesForm extends ContentEntityForm {
   public function buildForm(array $form, FormStateInterface $form_state) {
     $form = parent::buildForm($form, $form_state);
 
+    $config = \Drupal::config('recurring_events.eventseries.config');
+
     $editing = ($form_state->getBuildInfo()['form_id'] == 'eventseries_edit_form');
 
     /* @var $entity \Drupal\recurring_events\Entity\EventSeries */
@@ -105,6 +132,23 @@ class EventSeriesForm extends ContentEntityForm {
       ],
     ];
 
+    // Get all the available recur type fields. Suppress altering so that we can
+    // get a list of all the fields, so that after we alter, we can remove the
+    // necessary fields from the entity form.
+    $recur_fields = $this->creationService->getRecurFieldTypes(FALSE);
+    $all_recur_fields = $recur_fields;
+    \Drupal::moduleHandler()->alter('recurring_events_recur_field_types', $recur_fields);
+
+    $form['recur_type']['widget']['#options'] = $recur_fields;
+
+    // Loop through all the recurring date configuration fields and if any were
+    // suppressed then also suppress the fields associated with that.
+    foreach ($all_recur_fields as $field_name => $field_label) {
+      if (!isset($recur_fields[$field_name])) {
+        unset($form[$field_name]);
+      }
+    }
+
     if ($editing) {
       $original = $this->storage->loadUnchanged($entity->id());
       if ($this->step === 1) {
@@ -141,6 +185,20 @@ class EventSeriesForm extends ContentEntityForm {
             '#rows' => $diff_array,
           ];
 
+          if ($config->get('threshold_warning')) {
+            $total = ConsecutiveRecurringDateWidget::checkDuration($form_state);
+            if ($total > $config->get('threshold_count')) {
+              $message = $config->get('threshold_message');
+              $message = str_replace('@total', $total, $message);
+              $form['diff']['count_warning'] = [
+                '#type' => 'markup',
+                '#prefix' => '<p class="form-item--error-message">',
+                '#markup' => $message,
+                '#suffix' => '</p>',
+              ];
+            }
+          }
+
           $form['diff']['confirm'] = [
             '#type' => 'submit',
             '#value' => $this->t('Confirm Date Changes'),
@@ -200,7 +258,12 @@ class EventSeriesForm extends ContentEntityForm {
     $editing = ($form_state->getBuildInfo()['form_id'] == 'eventseries_edit_form');
     $trigger = $form_state->getTriggeringElement();
 
-    if ($trigger['#id'] !== 'edit-confirm' && $editing) {
+    $ignored_triggers = [
+      'consecutive_recurring_date[0][duration]',
+      'consecutive_recurring_date[0][duration_units]',
+    ];
+
+    if ($trigger['#id'] !== 'edit-confirm' && array_search($trigger['#name'], $ignored_triggers) === FALSE && $editing) {
       $original = $this->storage->loadUnchanged($entity->id());
       if ($this->creationService->checkForFormRecurConfigChanges($original, $form_state)) {
         $this->step = 1;
diff --git a/src/Form/EventSeriesSettingsForm.php b/src/Form/EventSeriesSettingsForm.php
index 450273651011d44b53b6340b8939db9e762f15fb..4e7762ac4546b2dc87d82e6875b64297d3001468 100644
--- a/src/Form/EventSeriesSettingsForm.php
+++ b/src/Form/EventSeriesSettingsForm.php
@@ -50,8 +50,11 @@ class EventSeriesSettingsForm extends ConfigFormBase {
       ->set('limit', $form_state->getValue('limit'))
       ->set('excludes', $form_state->getValue('excludes'))
       ->set('includes', $form_state->getValue('includes'))
-      ->set('excluded_dates', implode(',', array_filter($form_state->getValue('excluded_dates'))))
-      ->set('included_dates', implode(',', array_filter($form_state->getValue('included_dates'))))
+      ->set('enabled_fields', implode(',', array_filter($form_state->getValue('enabled_fields'))))
+      ->set('threshold_warning', $form_state->getValue('threshold_warning'))
+      ->set('threshold_count', $form_state->getValue('threshold_count'))
+      ->set('threshold_message', $form_state->getValue('threshold_message'))
+      ->set('threshold_prevent_save', $form_state->getValue('threshold_prevent_save'))
       ->save();
 
     parent::submitForm($form, $form_state);
@@ -161,6 +164,60 @@ class EventSeriesSettingsForm extends ConfigFormBase {
       '#default_value' => $config->get('includes'),
     ];
 
+    $fields = \Drupal::service('recurring_events.event_creation_service')->getRecurFieldTypes(FALSE);
+
+    $form['creation']['enabled_fields'] = [
+      '#type' => 'checkboxes',
+      '#title' => $this->t('Enabled Recur Field Types'),
+      '#required' => TRUE,
+      '#options' => $fields,
+      '#description' => $this->t('Select the recur field types to enable.'),
+      '#default_value' => explode(',', $config->get('enabled_fields')),
+    ];
+
+    $form['creation']['threshold_warning'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Show Event Instance Threshold Warning?'),
+      '#description' => $this->t('Display a warning when too many event instances may be created?'),
+      '#default_value' => $config->get('threshold_warning'),
+    ];
+
+    $form['creation']['threshold_count'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Event Instance Threshold Count'),
+      '#description' => $this->t('The number of event instances to trigger the warning'),
+      '#default_value' => $config->get('threshold_count'),
+      '#states' => [
+        'visible' => [
+          'input[name="threshold_warning"]' => ['checked' => TRUE],
+        ],
+      ],
+    ];
+
+    $form['creation']['threshold_message'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Event Instance Threshold Message'),
+      '#description' => $this->t('Enter the message to be displayed. Use @total as a placeholder for the amount of instances being created.'),
+      '#default_value' => $config->get('threshold_message'),
+      '#states' => [
+        'visible' => [
+          'input[name="threshold_warning"]' => ['checked' => TRUE],
+        ],
+      ],
+    ];
+
+    $form['creation']['threshold_prevent_save'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Event Instance Threshold Prevent Save'),
+      '#description' => $this->t('Prevent saving a series if the threshold is exceeded?'),
+      '#default_value' => $config->get('threshold_prevent_save'),
+      '#states' => [
+        'visible' => [
+          'input[name="threshold_warning"]' => ['checked' => TRUE],
+        ],
+      ],
+    ];
+
     $form['display'] = [
       '#type' => 'details',
       '#title' => $this->t('Event Display'),
diff --git a/src/Plugin/Field/FieldType/ConsecutiveRecurringDate.php b/src/Plugin/Field/FieldType/ConsecutiveRecurringDate.php
new file mode 100644
index 0000000000000000000000000000000000000000..95bf5ef37fef4352d9115822a365a989eb680595
--- /dev/null
+++ b/src/Plugin/Field/FieldType/ConsecutiveRecurringDate.php
@@ -0,0 +1,373 @@
+<?php
+
+namespace Drupal\recurring_events\Plugin\Field\FieldType;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem;
+use Drupal\recurring_events\RecurringEventsFieldTypeInterface;
+use Drupal\recurring_events\Entity\EventSeries;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
+use Drupal\recurring_events\Plugin\RecurringEventsFieldTrait;
+
+/**
+ * Plugin implementation of the 'consecutive_recurring_date' field type.
+ *
+ * @FieldType (
+ *   id = "consecutive_recurring_date",
+ *   label = @Translation("Consecutive Event"),
+ *   description = @Translation("Stores a consecutive recurring date configuration"),
+ *   default_widget = "consecutive_recurring_date",
+ *   default_formatter = "consecutive_recurring_date"
+ * )
+ */
+class ConsecutiveRecurringDate extends DateRangeItem implements RecurringEventsFieldTypeInterface {
+
+  use RecurringEventsFieldTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(FieldStorageDefinitionInterface $field_definition) {
+    $schema = parent::schema($field_definition);
+
+    $schema['columns']['time'] = [
+      'type' => 'varchar',
+      'length' => 20,
+    ];
+
+    $schema['columns']['end_time'] = [
+      'type' => 'varchar',
+      'length' => 20,
+    ];
+
+    $schema['columns']['duration'] = [
+      'type' => 'int',
+      'unsigned' => TRUE,
+    ];
+
+    $schema['columns']['duration_units'] = [
+      'type' => 'varchar',
+      'length' => 20,
+    ];
+
+    $schema['columns']['buffer'] = [
+      'type' => 'int',
+      'unsigned' => TRUE,
+    ];
+
+    $schema['columns']['buffer_units'] = [
+      'type' => 'varchar',
+      'length' => 20,
+    ];
+
+    return $schema;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    $time = $this->get('time')->getValue();
+    $end_time = $this->get('end_time')->getValue();
+    $duration = $this->get('duration')->getValue();
+    $duration_units = $this->get('duration_units')->getValue();
+    $buffer = $this->get('buffer')->getValue();
+    $buffer_units = $this->get('buffer_units')->getValue();
+    return parent::isEmpty() && empty($time) && empty($end_time)
+    && empty($duration) && empty($duration_units)
+    && empty($buffer) && empty($buffer_units);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
+    $properties = parent::propertyDefinitions($field_definition);
+
+    // Add our properties.
+    $properties['time'] = DataDefinition::create('string')
+      ->setLabel(t('Time'))
+      ->setDescription(t('The time the events begin each day'));
+
+    $properties['end_time'] = DataDefinition::create('string')
+      ->setLabel(t('End Time'))
+      ->setDescription(t('The time the events end each day'));
+
+    $properties['duration'] = DataDefinition::create('integer')
+      ->setLabel(t('Duration'))
+      ->setDescription(t('The duration of the events'));
+
+    $properties['duration_units'] = DataDefinition::create('string')
+      ->setLabel(t('Duration Units'))
+      ->setDescription(t('The duration unites of the events'));
+
+    $properties['buffer'] = DataDefinition::create('integer')
+      ->setLabel(t('Buffer'))
+      ->setDescription(t('The time between consecutive events'));
+
+    $properties['buffer_units'] = DataDefinition::create('string')
+      ->setLabel(t('Buffer Units'))
+      ->setDescription(t('The units used for the time between consecutive events'));
+
+    return $properties;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function convertEntityConfigToArray(EventSeries $event) {
+    $config = [];
+    $config['start_date'] = $event->getConsecutiveStartDate();
+    $config['end_date'] = $event->getConsecutiveEndDate();
+    $config['time'] = $event->getConsecutiveStartTime();
+    $config['end_time'] = $event->getConsecutiveEndTime();
+    $config['duration'] = $event->getConsecutiveDuration();
+    $config['duration_units'] = $event->getConsecutiveDurationUnits();
+    $config['buffer'] = $event->getConsecutiveBuffer();
+    $config['buffer_units'] = $event->getConsecutiveBufferUnits();
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function convertFormConfigToArray(FormStateInterface $form_state) {
+    $config = [];
+
+    $user_timezone = new \DateTimeZone(drupal_get_user_timezone());
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+    $user_input = $form_state->getUserInput();
+
+    if (!empty($user_input['consecutive_recurring_date'][0]['value']['date'])
+    && !empty($user_input['consecutive_recurring_date'][0]['end_value']['date'])
+    && !empty($user_input['consecutive_recurring_date'][0]['time'])) {
+      $time = $user_input['consecutive_recurring_date'][0]['time'];
+      $time_parts = static::convertTimeTo24hourFormat($time);
+      $timestamp = implode(':', $time_parts) . ':00';
+
+      $start_timestamp = $user_input['consecutive_recurring_date'][0]['value']['date'] . 'T' . $timestamp;
+      $start_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start_timestamp, $user_timezone);
+      $start_date->setTime(0, 0, 0);
+
+      $end_timestamp = $user_input['consecutive_recurring_date'][0]['end_value']['date'] . 'T' . $timestamp;
+      $end_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $end_timestamp, $user_timezone);
+      $end_date->setTime(0, 0, 0);
+
+      $config['start_date'] = $start_date;
+      $config['end_date'] = $end_date;
+
+      $config['time'] = $time;
+      $config['end_time'] = $user_input['consecutive_recurring_date'][0]['end_time'];
+      $config['duration'] = $user_input['consecutive_recurring_date'][0]['duration'];
+      $config['duration_units'] = $user_input['consecutive_recurring_date'][0]['duration_units'];
+      $config['buffer'] = $user_input['consecutive_recurring_date'][0]['buffer'];
+      $config['buffer_units'] = $user_input['consecutive_recurring_date'][0]['buffer_units'];
+    }
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function buildDiffArray(array $entity_config, array $form_config) {
+    $diff = [];
+
+    if ($entity_config['start_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT) !== $form_config['start_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT)) {
+      $diff['start_date'] = [
+        'label' => t('Start Date'),
+        'stored' => $entity_config['start_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+        'override' => $form_config['start_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+      ];
+    }
+    if ($entity_config['end_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT) !== $form_config['end_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT)) {
+      $diff['end_date'] = [
+        'label' => t('End Date'),
+        'stored' => $entity_config['end_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+        'override' => $form_config['end_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+      ];
+    }
+    if ($entity_config['time'] !== $form_config['time']) {
+      $diff['time'] = [
+        'label' => t('Time'),
+        'stored' => $entity_config['time'],
+        'override' => $form_config['time'],
+      ];
+    }
+    if ($entity_config['end_time'] !== $form_config['end_time']) {
+      $diff['end_time'] = [
+        'label' => t('End Time'),
+        'stored' => $entity_config['end_time'],
+        'override' => $form_config['end_time'],
+      ];
+    }
+    if ($entity_config['duration'] !== $form_config['duration']) {
+      $diff['duration'] = [
+        'label' => t('Duration'),
+        'stored' => $entity_config['duration'],
+        'override' => $form_config['duration'],
+      ];
+    }
+    if ($entity_config['duration_units'] !== $form_config['duration_units']) {
+      $diff['duration_units'] = [
+        'label' => t('Duration Units'),
+        'stored' => $entity_config['duration_units'],
+        'override' => $form_config['duration_units'],
+      ];
+    }
+    if ($entity_config['buffer'] !== $form_config['buffer']) {
+      $diff['buffer'] = [
+        'label' => t('Buffer'),
+        'stored' => $entity_config['buffer'],
+        'override' => $form_config['buffer'],
+      ];
+    }
+    if ($entity_config['buffer_units'] !== $form_config['buffer_units']) {
+      $diff['buffer_units'] = [
+        'label' => t('Buffer Units'),
+        'stored' => $entity_config['buffer_units'],
+        'override' => $form_config['buffer_units'],
+      ];
+    }
+
+    return $diff;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function calculateInstances(array $form_data) {
+    $dates = $events_to_create = [];
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+
+    $daily_dates = static::findDailyDatesBetweenDates($form_data['start_date'], $form_data['end_date']);
+    $time_parts = static::convertTimeTo24hourFormat($form_data['time']);
+
+    if (!empty($daily_dates)) {
+      foreach ($daily_dates as $daily_date) {
+        // Set the time of the start date to be the hours and minutes.
+        $daily_date->setTime($time_parts[0], $time_parts[1]);
+        // Configure the right timezone.
+        $daily_date->setTimezone($utc_timezone);
+        $day_times = static::findSlotsBetweenTimes($daily_date, $form_data);
+
+        if (!empty($day_times)) {
+          foreach ($day_times as $day_time) {
+            // Create a clone of this date.
+            $daily_date_end = clone $day_time;
+            // Add the number of seconds specified in the duration field.
+            $daily_date_end->modify('+' . $form_data['duration'] . ' ' . $form_data['duration_units']);
+            // Set this event to be created.
+            $events_to_create[$day_time->format('r')] = [
+              'start_date' => $day_time,
+              'end_date' => $daily_date_end,
+            ];
+          }
+        }
+      }
+    }
+
+    return $events_to_create;
+  }
+
+  /**
+   * Find all the daily date occurrences between two dates.
+   *
+   * @param Drupal\Core\Datetime\DrupalDateTime $start_date
+   *   The start date.
+   * @param Drupal\Core\Datetime\DrupalDateTime $end_date
+   *   The end date.
+   * @param bool $count_only
+   *   Whether to only return a count.
+   *
+   * @return array|int
+   *   An array of matching dates, or a count.
+   */
+  public static function findDailyDatesBetweenDates(DrupalDateTime $start_date, DrupalDateTime $end_date, $count_only = FALSE) {
+    $dates = [];
+    $count = 0;
+
+    // Clone the date as we do not want to make changes to the original object.
+    $start = clone $start_date;
+    $end = clone $end_date;
+
+    // We want to create events up to and including the last day, so modify the
+    // end date to be midnight of the next day.
+    $end->modify('midnight next day');
+
+    // If the start date is after the end date then we have an invalid range so
+    // just return nothing.
+    if ($start->getTimestamp() > $end->getTimestamp()) {
+      if ($count_only) {
+        return $count;
+      }
+      return $dates;
+    }
+
+    // Loop through a week at a time, storing the date in the array to return
+    // until the end date is surpassed.
+    while ($start->getTimestamp() < $end->getTimestamp()) {
+      // If we do not clone here we end up modifying the value of start in
+      // the array and get some funky dates returned.
+      if (!$count_only) {
+        $dates[] = clone $start;
+      }
+      $start->modify('+1 day');
+      $count++;
+    }
+
+    if ($count_only) {
+      return $count;
+    }
+    return $dates;
+  }
+
+  /**
+   * Find all the time slots between two times of a specific day.
+   *
+   * @param Drupal\Core\Datetime\DrupalDateTime $date
+   *   The date.
+   * @param array $form_data
+   *   The form data used to find the time slots.
+   * @param bool $count_only
+   *   Whether to only return a count.
+   *
+   * @return array|int
+   *   An array of time slots, or a count.
+   */
+  public static function findSlotsBetweenTimes(DrupalDateTime $date, array $form_data, $count_only = FALSE) {
+    $slots = [];
+    $count = 0;
+
+    $max_time = clone $date;
+    $user_timezone = new \DateTimeZone(drupal_get_user_timezone());
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+
+    $time_parts = static::convertTimeTo24hourFormat($form_data['end_time']);
+    $max_time->setTimezone($user_timezone);
+    $max_time->setTime($time_parts[0], $time_parts[1]);
+    $max_time->setTimezone($utc_timezone);
+
+    while ($date->getTimestamp() <= $max_time->getTimestamp()) {
+      $duration = $form_data['duration'];
+      $duration_units = $form_data['duration_units'];
+      $buffer = $form_data['buffer'];
+      $buffer_units = $form_data['buffer_units'];
+
+      if (!$count_only) {
+        $slots[] = clone $date;
+      }
+      $date->modify('+ ' . $duration . ' ' . $duration_units);
+      $date->modify('+ ' . $buffer . ' ' . $buffer_units);
+      $count++;
+    }
+
+    if ($count_only) {
+      return $count;
+    }
+    return $slots;
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/DailyRecurringDate.php b/src/Plugin/Field/FieldType/DailyRecurringDate.php
new file mode 100644
index 0000000000000000000000000000000000000000..d59c68d26e3578063eb9b0c5523ef7cbb5ad2f49
--- /dev/null
+++ b/src/Plugin/Field/FieldType/DailyRecurringDate.php
@@ -0,0 +1,227 @@
+<?php
+
+namespace Drupal\recurring_events\Plugin\Field\FieldType;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem;
+use Drupal\recurring_events\RecurringEventsFieldTypeInterface;
+use Drupal\recurring_events\Entity\EventSeries;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
+use Drupal\recurring_events\Plugin\RecurringEventsFieldTrait;
+
+/**
+ * Plugin implementation of the 'daily_recurring_date' field type.
+ *
+ * @FieldType (
+ *   id = "daily_recurring_date",
+ *   label = @Translation("Daily Event"),
+ *   description = @Translation("Stores a daily recurring date configuration"),
+ *   default_widget = "daily_recurring_date",
+ *   default_formatter = "daily_recurring_date"
+ * )
+ */
+class DailyRecurringDate extends DateRangeItem implements RecurringEventsFieldTypeInterface {
+
+  use RecurringEventsFieldTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(FieldStorageDefinitionInterface $field_definition) {
+    $schema = parent::schema($field_definition);
+
+    $schema['columns']['time'] = [
+      'type' => 'varchar',
+      'length' => 20,
+    ];
+
+    $schema['columns']['duration'] = [
+      'type' => 'int',
+      'unsigned' => TRUE,
+    ];
+
+    return $schema;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    $time = $this->get('time')->getValue();
+    $duration = $this->get('duration')->getValue();
+    return parent::isEmpty() && empty($time) && empty($duration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
+    $properties = parent::propertyDefinitions($field_definition);
+
+    // Add our properties.
+    $properties['time'] = DataDefinition::create('string')
+      ->setLabel(t('Time'))
+      ->setDescription(t('The time the event begins'));
+
+    $properties['duration'] = DataDefinition::create('integer')
+      ->setLabel(t('Duration'))
+      ->setDescription(t('The duration of the event in minutes'));
+
+    return $properties;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function convertEntityConfigToArray(EventSeries $event) {
+    $config = [];
+    $config['start_date'] = $event->getDailyStartDate();
+    $config['end_date'] = $event->getDailyEndDate();
+    $config['time'] = $event->getDailyStartTime();
+    $config['duration'] = $event->getDailyDuration();
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function convertFormConfigToArray(FormStateInterface $form_state) {
+    $config = [];
+
+    $user_timezone = new \DateTimeZone(drupal_get_user_timezone());
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+    $user_input = $form_state->getUserInput();
+
+    $time = $user_input['daily_recurring_date'][0]['time'];
+    $time_parts = static::convertTimeTo24hourFormat($time);
+    $timestamp = implode(':', $time_parts) . ':00';
+
+    $start_timestamp = $user_input['daily_recurring_date'][0]['value']['date'] . 'T' . $timestamp;
+    $start_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start_timestamp, $user_timezone);
+    $start_date->setTime(0, 0, 0);
+
+    $end_timestamp = $user_input['daily_recurring_date'][0]['end_value']['date'] . 'T' . $timestamp;
+    $end_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $end_timestamp, $user_timezone);
+    $end_date->setTime(0, 0, 0);
+
+    $config['start_date'] = $start_date;
+    $config['end_date'] = $end_date;
+
+    $config['time'] = $time;
+    $config['duration'] = $user_input['daily_recurring_date'][0]['duration'];
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function buildDiffArray(array $entity_config, array $form_config) {
+    $diff = [];
+
+    if ($entity_config['start_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT) !== $form_config['start_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT)) {
+      $diff['start_date'] = [
+        'label' => t('Start Date'),
+        'stored' => $entity_config['start_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+        'override' => $form_config['start_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+      ];
+    }
+    if ($entity_config['end_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT) !== $form_config['end_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT)) {
+      $diff['end_date'] = [
+        'label' => t('End Date'),
+        'stored' => $entity_config['end_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+        'override' => $form_config['end_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+      ];
+    }
+    if ($entity_config['time'] !== $form_config['time']) {
+      $diff['time'] = [
+        'label' => t('Time'),
+        'stored' => $entity_config['time'],
+        'override' => $form_config['time'],
+      ];
+    }
+    if ($entity_config['duration'] !== $form_config['duration']) {
+      $diff['duration'] = [
+        'label' => t('Duration'),
+        'stored' => $entity_config['duration'],
+        'override' => $form_config['duration'],
+      ];
+    }
+
+    return $diff;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function calculateInstances(array $form_data) {
+    $dates = $events_to_create = [];
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+
+    $daily_dates = static::findDailyDatesBetweenDates($form_data['start_date'], $form_data['end_date']);
+    $time_parts = static::convertTimeTo24hourFormat($form_data['time']);
+
+    if (!empty($daily_dates)) {
+      foreach ($daily_dates as $daily_date) {
+        // Set the time of the start date to be the hours and minutes.
+        $daily_date->setTime($time_parts[0], $time_parts[1]);
+        // Configure the right timezone.
+        $daily_date->setTimezone($utc_timezone);
+        // Create a clone of this date.
+        $daily_date_end = clone $daily_date;
+        // Add the number of seconds specified in the duration field.
+        $daily_date_end->modify('+' . $form_data['duration'] . ' seconds');
+        // Set this event to be created.
+        $events_to_create[$daily_date->format('r')] = [
+          'start_date' => $daily_date,
+          'end_date' => $daily_date_end,
+        ];
+      }
+    }
+
+    return $events_to_create;
+  }
+
+  /**
+   * Find all the daily date occurrences between two dates.
+   *
+   * @param Drupal\Core\Datetime\DrupalDateTime $start_date
+   *   The start date.
+   * @param Drupal\Core\Datetime\DrupalDateTime $end_date
+   *   The end date.
+   *
+   * @return array
+   *   An array of matching dates.
+   */
+  public static function findDailyDatesBetweenDates(DrupalDateTime $start_date, DrupalDateTime $end_date) {
+    $dates = [];
+
+    // Clone the date as we do not want to make changes to the original object.
+    $start = clone $start_date;
+    $end = clone $end_date;
+
+    // We want to create events up to and including the last day, so modify the
+    // end date to be midnight of the next day.
+    $end->modify('midnight next day');
+
+    // If the start date is after the end date then we have an invalid range so
+    // just return nothing.
+    if ($start->getTimestamp() > $end->getTimestamp()) {
+      return $dates;
+    }
+
+    // Loop through a week at a time, storing the date in the array to return
+    // until the end date is surpassed.
+    while ($start->getTimestamp() < $end->getTimestamp()) {
+      // If we do not clone here we end up modifying the value of start in
+      // the array and get some funky dates returned.
+      $dates[] = clone $start;
+      $start->modify('+1 day');
+    }
+
+    return $dates;
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/MonthlyRecurringDate.php b/src/Plugin/Field/FieldType/MonthlyRecurringDate.php
index 1f27708658ca28890b84d4474d3f49c4986014fe..21154441d854ea4004f803fec47f6d7f3363105e 100644
--- a/src/Plugin/Field/FieldType/MonthlyRecurringDate.php
+++ b/src/Plugin/Field/FieldType/MonthlyRecurringDate.php
@@ -4,19 +4,27 @@ namespace Drupal\recurring_events\Plugin\Field\FieldType;
 
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\TypedData\DataDefinition;
+use Drupal\recurring_events\RecurringEventsFieldTypeInterface;
+use Drupal\recurring_events\Entity\EventSeries;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
+use Drupal\recurring_events\Plugin\RecurringEventsFieldTrait;
 
 /**
  * Plugin implementation of the 'monthly_recurring_date' field type.
  *
  * @FieldType (
  *   id = "monthly_recurring_date",
- *   label = @Translation("Monthly Recurring Date"),
+ *   label = @Translation("Monthly Event"),
  *   description = @Translation("Stores a monthly recurring date configuration"),
  *   default_widget = "monthly_recurring_date",
  *   default_formatter = "monthly_recurring_date"
  * )
  */
-class MonthlyRecurringDate extends WeeklyRecurringDate {
+class MonthlyRecurringDate extends WeeklyRecurringDate implements RecurringEventsFieldTypeInterface {
+
+  use RecurringEventsFieldTrait;
 
   /**
    * {@inheritdoc}
@@ -77,4 +85,329 @@ class MonthlyRecurringDate extends WeeklyRecurringDate {
     return $properties;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function convertEntityConfigToArray(EventSeries $event) {
+    $config = [];
+    $config['start_date'] = $event->getMonthlyStartDate();
+    $config['end_date'] = $event->getMonthlyEndDate();
+    $config['time'] = $event->getMonthlyStartTime();
+    $config['duration'] = $event->getMonthlyDuration();
+    $config['monthly_type'] = $event->getMonthlyType();
+
+    switch ($event->getMonthlyType()) {
+      case 'weekday':
+        $config['day_occurrence'] = $event->getMonthlyDayOccurrences();
+        $config['days'] = $event->getMonthlyDays();
+        break;
+
+      case 'monthday':
+        $config['day_of_month'] = $event->getMonthlyDayOfMonth();
+        break;
+    }
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function convertFormConfigToArray(FormStateInterface $form_state) {
+    $config = [];
+
+    $user_timezone = new \DateTimeZone(drupal_get_user_timezone());
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+    $user_input = $form_state->getUserInput();
+
+    $time = $user_input['monthly_recurring_date'][0]['time'];
+    $time_parts = static::convertTimeTo24hourFormat($time);
+    $timestamp = implode(':', $time_parts) . ':00';
+
+    $start_timestamp = $user_input['monthly_recurring_date'][0]['value']['date'] . 'T' . $timestamp;
+    $start_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start_timestamp, $user_timezone);
+    $start_date->setTime(0, 0, 0);
+
+    $end_timestamp = $user_input['monthly_recurring_date'][0]['end_value']['date'] . 'T' . $timestamp;
+    $end_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $end_timestamp, $user_timezone);
+    $end_date->setTime(0, 0, 0);
+
+    $config['start_date'] = $start_date;
+    $config['end_date'] = $end_date;
+
+    $config['time'] = $time;
+    $config['duration'] = $user_input['monthly_recurring_date'][0]['duration'];
+    $config['monthly_type'] = $user_input['monthly_recurring_date'][0]['type'];
+
+    switch ($config['monthly_type']) {
+      case 'weekday':
+        $config['day_occurrence'] = array_filter(array_values($user_input['monthly_recurring_date'][0]['day_occurrence']));
+        $config['days'] = array_filter(array_values($user_input['monthly_recurring_date'][0]['days']));
+        break;
+
+      case 'monthday':
+        $config['day_of_month'] = array_filter(array_values($user_input['monthly_recurring_date'][0]['day_of_month']));
+        break;
+    }
+
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function buildDiffArray(array $entity_config, array $form_config) {
+    $diff = parent::buildDiffArray($entity_config, $form_config);
+
+    if ($entity_config['type'] === 'monthly') {
+      if ($entity_config['monthly_type'] !== $form_config['monthly_type']) {
+        $diff['monthly_type'] = [
+          'label' => t('Monthly Type'),
+          'stored' => $entity_config['monthly_type'],
+          'override' => $form_config['monthly_type'],
+        ];
+      }
+      if ($entity_config['monthly_type'] === 'weekday') {
+        if ($entity_config['day_occurrence'] !== $form_config['day_occurrence']) {
+          $diff['day_occurrence'] = [
+            'label' => t('Day Occurrence'),
+            'stored' => implode(',', $entity_config['day_occurrence']),
+            'override' => implode(',', $form_config['day_occurrence']),
+          ];
+        }
+        if ($entity_config['days'] !== $form_config['days']) {
+          $diff['days'] = [
+            'label' => t('Days'),
+            'stored' => implode(',', $entity_config['days']),
+            'override' => implode(',', $form_config['days']),
+          ];
+        }
+      }
+      else {
+        if ($entity_config['day_of_month'] !== $form_config['day_of_month']) {
+          $diff['day_of_month'] = [
+            'label' => t('Day of the Month'),
+            'stored' => implode(',', $entity_config['day_of_month']),
+            'override' => implode(',', $form_config['day_of_month']),
+          ];
+        }
+      }
+    }
+
+    return $diff;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function calculateInstances(array $form_data) {
+    $dates = $events_to_create = [];
+    $time_parts = static::convertTimeTo24hourFormat($form_data['time']);
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+
+    if (!empty($form_data['monthly_type'])) {
+      switch ($form_data['monthly_type']) {
+        case 'weekday':
+          // Loop through each weekday occurrence and weekday.
+          if (!empty($form_data['day_occurrence']) && !empty($form_data['days'])) {
+            foreach ($form_data['day_occurrence'] as $occurrence) {
+              foreach ($form_data['days'] as $weekday) {
+                // Find the occurrence of the specific weekdays within
+                // each month.
+                $day_occurrences = static::findWeekdayOccurrencesBetweenDates($occurrence, $weekday, $form_data['start_date'], $form_data['end_date']);
+                $dates = array_merge($dates, $day_occurrences);
+              }
+            }
+          }
+          break;
+
+        case 'monthday':
+          foreach ($form_data['day_of_month'] as $day_of_month) {
+            $days_of_month = static::findMonthDaysBetweenDates($day_of_month, $form_data['start_date'], $form_data['end_date']);
+            $dates = array_merge($dates, $days_of_month);
+          }
+          break;
+
+      }
+
+      // If valid recurring dates were found.
+      if (!empty($dates)) {
+        foreach ($dates as $monthly_date) {
+          // Set the time of the start date to be the hours and
+          // minutes.
+          $monthly_date->setTime($time_parts[0], $time_parts[1]);
+          // Configure the timezone.
+          $monthly_date->setTimezone($utc_timezone);
+          // Create a clone of this date.
+          $monthly_date_end = clone $monthly_date;
+          // Add the number of seconds specified in the duration
+          // field.
+          $monthly_date_end->modify('+' . $form_data['duration'] . ' seconds');
+          // Set this event to be created.
+          $events_to_create[$monthly_date->format('r')] = [
+            'start_date' => $monthly_date,
+            'end_date' => $monthly_date_end,
+          ];
+        }
+      }
+    }
+
+    return $events_to_create;
+  }
+
+  /**
+   * Find all the day-of-month occurrences between two dates.
+   *
+   * @param int $day_of_month
+   *   The day of the month.
+   * @param Drupal\Core\Datetime\DrupalDateTime $start_date
+   *   The start date.
+   * @param Drupal\Core\Datetime\DrupalDateTime $end_date
+   *   The end date.
+   *
+   * @return array
+   *   An array of matching dates.
+   */
+  public static function findMonthDaysBetweenDates($day_of_month, DrupalDateTime $start_date, DrupalDateTime $end_date) {
+    $dates = [];
+
+    // Clone the date as we do not want to make changes to the original object.
+    $start = clone $start_date;
+    $end = clone $end_date;
+
+    // We want to create events up to and including the last day, so modify the
+    // end date to be midnight of the next day.
+    $end->modify('midnight next day');
+
+    // If the start date is after the end date then we have an invalid range so
+    // just return nothing.
+    if ($start->getTimestamp() > $end->getTimestamp()) {
+      return $dates;
+    }
+
+    $day_to_check = $day_of_month;
+
+    // If day of month is set to -1 that is the last day of the month, we need
+    // to calculate how many days a month has.
+    if ($day_of_month === '-1') {
+      $day_to_check = $start->format('t');
+    }
+
+    // If the day of the month is after the start date.
+    if ($start->format('d') < $day_to_check) {
+      $new_date = clone $start;
+      $curr_month = $new_date->format('m');
+      $curr_year = $new_date->format('Y');
+
+      // Check to see if that date is a valid date.
+      if (!checkdate($curr_month, $day_to_check, $curr_year)) {
+        // If not, go find the next valid date.
+        $start = static::findNextMonthDay($day_of_month, $start);
+      }
+      else {
+        // This is a valid date, so let us start there.
+        $start->setDate($curr_year, $curr_month, $day_to_check);
+      }
+    }
+    // If the day of the month is in the past.
+    elseif ($start->format('d') > $day_to_check) {
+      // Find the next valid start date.
+      $start = static::findNextMonthDay($day_of_month, $start);
+    }
+
+    // Loop through each month checking to see if the day of the month is a
+    // valid day, until the end date has been surpassed.
+    while ($start->getTimestamp() <= $end->getTimestamp()) {
+      // If we do not clone here we end up modifying the value of start in
+      // the array and get some funky dates returned.
+      $dates[] = clone $start;
+      // Find the next valid event date.
+      $start = static::findNextMonthDay($day_of_month, $start);
+    }
+
+    return $dates;
+  }
+
+  /**
+   * Find the next day-of-month occurrence.
+   *
+   * @param int $day_of_month
+   *   The day of the month.
+   * @param Drupal\Core\Datetime\DrupalDateTime $date
+   *   The start date.
+   *
+   * @return Drupal\Core\Datetime\DrupalDateTime
+   *   The next occurrence of a specific day of the month.
+   */
+  public static function findNextMonthDay($day_of_month, DrupalDateTime $date) {
+    $new_date = clone $date;
+
+    $curr_month = $new_date->format('m');
+    $curr_year = $new_date->format('Y');
+    $next_month = $curr_month;
+    $next_year = $curr_year;
+
+    do {
+      $next_month = ($next_month + 1) % 12 ?: 12;
+      $next_year = $next_month == 1 ? $next_year + 1 : $next_year;
+
+      // If the desired day of the month is the last day, calculate what that
+      // day is.
+      if ($day_of_month === '-1') {
+        $new_date->setDate($next_year, $next_month, '1');
+        $day_of_month = $new_date->format('t');
+      }
+    } while (checkdate($next_month, $day_of_month, $next_year) === FALSE);
+
+    $new_date->setDate($next_year, $next_month, $day_of_month);
+    return $new_date;
+  }
+
+  /**
+   * Find all the monthly occurrences of a specific weekday between two dates.
+   *
+   * @param string $occurrence
+   *   Which occurrence of the weekday to find.
+   * @param string $weekday
+   *   The name of the day of the week.
+   * @param Drupal\Core\Datetime\DrupalDateTime $start_date
+   *   The start date.
+   * @param Drupal\Core\Datetime\DrupalDateTime $end_date
+   *   The end date.
+   *
+   * @return array
+   *   An array of matching dates.
+   */
+  public static function findWeekdayOccurrencesBetweenDates($occurrence, $weekday, DrupalDateTime $start_date, DrupalDateTime $end_date) {
+    $dates = [];
+
+    // Clone the date as we do not want to make changes to the original object.
+    $start = clone $start_date;
+
+    // If the start date is after the end date then we have an invalid range so
+    // just return nothing.
+    if ($start->getTimestamp() > $end_date->getTimestamp()) {
+      return $dates;
+    }
+
+    // Grab the occurrence of the weekday we want for this current month.
+    $start->modify($occurrence . ' ' . $weekday . ' of this month');
+
+    // Make sure we didn't just go back in time.
+    if ($start < $start_date) {
+      // Go straight to next month.
+      $start->modify($occurrence . ' ' . $weekday . ' of next month');
+    }
+
+    // Loop through a week at a time, storing the date in the array to return
+    // until the end date is surpassed.
+    while ($start->getTimestamp() <= $end_date->getTimestamp()) {
+      // If we do not clone here we end up modifying the value of start in
+      // the array and get some funky dates returned.
+      $dates[] = clone $start;
+      $start->modify($occurrence . ' ' . $weekday . ' of next month');
+    }
+
+    return $dates;
+  }
+
 }
diff --git a/src/Plugin/Field/FieldType/WeeklyRecurringDate.php b/src/Plugin/Field/FieldType/WeeklyRecurringDate.php
index 02d4ec3fc0342ed97a93c756e3dd8ac9303ec6d1..28cc10494a484e40adadc078d957eb98f82df67d 100644
--- a/src/Plugin/Field/FieldType/WeeklyRecurringDate.php
+++ b/src/Plugin/Field/FieldType/WeeklyRecurringDate.php
@@ -4,20 +4,27 @@ namespace Drupal\recurring_events\Plugin\Field\FieldType;
 
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem;
+use Drupal\recurring_events\RecurringEventsFieldTypeInterface;
+use Drupal\recurring_events\Entity\EventSeries;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
+use Drupal\recurring_events\Plugin\RecurringEventsFieldTrait;
 
 /**
  * Plugin implementation of the 'weekly_recurring_date' field type.
  *
  * @FieldType (
  *   id = "weekly_recurring_date",
- *   label = @Translation("Weekly Recurring Date"),
+ *   label = @Translation("Weekly Event"),
  *   description = @Translation("Stores a weekly recurring date configuration"),
  *   default_widget = "weekly_recurring_date",
  *   default_formatter = "weekly_recurring_date"
  * )
  */
-class WeeklyRecurringDate extends DateRangeItem {
+class WeeklyRecurringDate extends DailyRecurringDate implements RecurringEventsFieldTypeInterface {
+
+  use RecurringEventsFieldTrait;
 
   /**
    * {@inheritdoc}
@@ -25,16 +32,6 @@ class WeeklyRecurringDate extends DateRangeItem {
   public static function schema(FieldStorageDefinitionInterface $field_definition) {
     $schema = parent::schema($field_definition);
 
-    $schema['columns']['time'] = [
-      'type' => 'varchar',
-      'length' => 20,
-    ];
-
-    $schema['columns']['duration'] = [
-      'type' => 'int',
-      'unsigned' => TRUE,
-    ];
-
     $schema['columns']['days'] = [
       'type' => 'varchar',
       'length' => 255,
@@ -47,10 +44,8 @@ class WeeklyRecurringDate extends DateRangeItem {
    * {@inheritdoc}
    */
   public function isEmpty() {
-    $time = $this->get('time')->getValue();
-    $duration = $this->get('duration')->getValue();
     $days = $this->get('days')->getValue();
-    return parent::isEmpty() && empty($time) && empty($duration) && empty($days);
+    return parent::isEmpty() && empty($days);
   }
 
   /**
@@ -59,15 +54,6 @@ class WeeklyRecurringDate extends DateRangeItem {
   public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
     $properties = parent::propertyDefinitions($field_definition);
 
-    // Add our properties.
-    $properties['time'] = DataDefinition::create('string')
-      ->setLabel(t('Time'))
-      ->setDescription(t('The time the event begins'));
-
-    $properties['duration'] = DataDefinition::create('integer')
-      ->setLabel(t('Duration'))
-      ->setDescription(t('The duration of the event in minutes'));
-
     $properties['days'] = DataDefinition::create('string')
       ->setLabel(t('Days'))
       ->setDescription(t('The days of the week on which this event occurs'));
@@ -75,4 +61,178 @@ class WeeklyRecurringDate extends DateRangeItem {
     return $properties;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function convertEntityConfigToArray(EventSeries $event) {
+    $config = [];
+    $config['start_date'] = $event->getWeeklyStartDate();
+    $config['end_date'] = $event->getWeeklyEndDate();
+    $config['time'] = $event->getWeeklyStartTime();
+    $config['duration'] = $event->getWeeklyDuration();
+    $config['days'] = $event->getWeeklyDays();
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function convertFormConfigToArray(FormStateInterface $form_state) {
+    $config = [];
+
+    $user_timezone = new \DateTimeZone(drupal_get_user_timezone());
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+    $user_input = $form_state->getUserInput();
+
+    $time = $user_input['weekly_recurring_date'][0]['time'];
+    $time_parts = static::convertTimeTo24hourFormat($time);
+    $timestamp = implode(':', $time_parts) . ':00';
+
+    $start_timestamp = $user_input['weekly_recurring_date'][0]['value']['date'] . 'T' . $timestamp;
+    $start_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start_timestamp, $user_timezone);
+    $start_date->setTime(0, 0, 0);
+
+    $end_timestamp = $user_input['weekly_recurring_date'][0]['end_value']['date'] . 'T' . $timestamp;
+    $end_date = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $end_timestamp, $user_timezone);
+    $end_date->setTime(0, 0, 0);
+
+    $config['start_date'] = $start_date;
+    $config['end_date'] = $end_date;
+
+    $config['time'] = $time;
+    $config['duration'] = $user_input['weekly_recurring_date'][0]['duration'];
+    $config['days'] = array_filter(array_values($user_input['weekly_recurring_date'][0]['days']));
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function buildDiffArray(array $entity_config, array $form_config) {
+    $diff = [];
+
+    if ($entity_config['start_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT) !== $form_config['start_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT)) {
+      $diff['start_date'] = [
+        'label' => t('Start Date'),
+        'stored' => $entity_config['start_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+        'override' => $form_config['start_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+      ];
+    }
+    if ($entity_config['end_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT) !== $form_config['end_date']->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT)) {
+      $diff['end_date'] = [
+        'label' => t('End Date'),
+        'stored' => $entity_config['end_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+        'override' => $form_config['end_date']->format(DateTimeItemInterface::DATE_STORAGE_FORMAT),
+      ];
+    }
+    if ($entity_config['time'] !== $form_config['time']) {
+      $diff['time'] = [
+        'label' => t('Time'),
+        'stored' => $entity_config['time'],
+        'override' => $form_config['time'],
+      ];
+    }
+    if ($entity_config['duration'] !== $form_config['duration']) {
+      $diff['duration'] = [
+        'label' => t('Duration'),
+        'stored' => $entity_config['duration'],
+        'override' => $form_config['duration'],
+      ];
+    }
+
+    if ($entity_config['days'] !== $form_config['days']) {
+      $diff['days'] = [
+        'label' => t('Days'),
+        'stored' => implode(',', $entity_config['days']),
+        'override' => implode(',', $form_config['days']),
+      ];
+    }
+
+    return $diff;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function calculateInstances(array $form_data) {
+    $dates = $events_to_create = [];
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+
+    // Loop through each weekday and find occurrences of it in the
+    // date range provided.
+    foreach ($form_data['days'] as $weekday) {
+      $weekday_dates = static::findWeekdaysBetweenDates($weekday, $form_data['start_date'], $form_data['end_date']);
+      $dates = array_merge($dates, $weekday_dates);
+    }
+    $time_parts = static::convertTimeTo24hourFormat($form_data['time']);
+
+    if (!empty($dates)) {
+      foreach ($dates as $weekly_date) {
+        // Set the time of the start date to be the hours and minutes.
+        $weekly_date->setTime($time_parts[0], $time_parts[1]);
+        // Configure the right timezone.
+        $weekly_date->setTimezone($utc_timezone);
+        // Create a clone of this date.
+        $weekly_date_end = clone $weekly_date;
+        // Add the number of seconds specified in the duration field.
+        $weekly_date_end->modify('+' . $form_data['duration'] . ' seconds');
+        // Set this event to be created.
+        $events_to_create[$weekly_date->format('r')] = [
+          'start_date' => $weekly_date,
+          'end_date' => $weekly_date_end,
+        ];
+      }
+    }
+
+    return $events_to_create;
+  }
+
+  /**
+   * Find all the weekday occurrences between two dates.
+   *
+   * @param string $weekday
+   *   The name of the day of the week.
+   * @param Drupal\Core\Datetime\DrupalDateTime $start_date
+   *   The start date.
+   * @param Drupal\Core\Datetime\DrupalDateTime $end_date
+   *   The end date.
+   *
+   * @return array
+   *   An array of matching dates.
+   */
+  public static function findWeekdaysBetweenDates($weekday, DrupalDateTime $start_date, DrupalDateTime $end_date) {
+    $dates = [];
+
+    // Clone the date as we do not want to make changes to the original object.
+    $start = clone $start_date;
+    $end = clone $end_date;
+
+    // We want to create events up to and including the last day, so modify the
+    // end date to be midnight of the next day.
+    $end->modify('midnight next day');
+
+    // If the start date is after the end date then we have an invalid range so
+    // just return nothing.
+    if ($start->getTimestamp() > $end->getTimestamp()) {
+      return $dates;
+    }
+
+    // If the start date is not the weekday we are seeking, jump to the next
+    // instance of that weekday.
+    if ($start->format('l') != ucwords($weekday)) {
+      $start->modify('next ' . $weekday);
+    }
+
+    // Loop through a week at a time, storing the date in the array to return
+    // until the end date is surpassed.
+    while ($start->getTimestamp() < $end->getTimestamp()) {
+      // If we do not clone here we end up modifying the value of start in
+      // the array and get some funky dates returned.
+      $dates[] = clone $start;
+      $start->modify('+1 week');
+    }
+
+    return $dates;
+  }
+
 }
diff --git a/src/Plugin/Field/FieldWidget/ConsecutiveRecurringDateWidget.php b/src/Plugin/Field/FieldWidget/ConsecutiveRecurringDateWidget.php
new file mode 100644
index 0000000000000000000000000000000000000000..e139a1731f54b5bf373df6d1e107c2878f274c61
--- /dev/null
+++ b/src/Plugin/Field/FieldWidget/ConsecutiveRecurringDateWidget.php
@@ -0,0 +1,251 @@
+<?php
+
+namespace Drupal\recurring_events\Plugin\Field\FieldWidget;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\datetime_range\Plugin\Field\FieldWidget\DateRangeDefaultWidget;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
+use Drupal\recurring_events\Plugin\RecurringEventsFieldTrait;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\HtmlCommand;
+use Drupal\recurring_events\Plugin\Field\FieldType\ConsecutiveRecurringDate;
+
+/**
+ * Plugin implementation of the 'consecutive recurring date' widget.
+ *
+ * @FieldWidget (
+ *   id = "consecutive_recurring_date",
+ *   label = @Translation("Consecutive recurring date widget"),
+ *   field_types = {
+ *     "consecutive_recurring_date"
+ *   }
+ * )
+ */
+class ConsecutiveRecurringDateWidget extends DateRangeDefaultWidget {
+
+  use RecurringEventsFieldTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
+    $element = parent::formElement($items, $delta, $element, $form, $form_state);
+
+    $config = \Drupal::config('recurring_events.eventseries.config');
+
+    $element['#type'] = 'container';
+    $element['#states'] = [
+      'visible' => [
+        ':input[name="recur_type"]' => ['value' => 'consecutive_recurring_date'],
+      ],
+    ];
+    $element['#element_validate'][] = [$this, 'validateThreshold'];
+
+    $element['value']['#title'] = t('Create Events Between');
+    $element['value']['#weight'] = 1;
+    $element['value']['#date_date_format'] = DateTimeItemInterface::DATE_STORAGE_FORMAT;
+    $element['value']['#date_date_element'] = 'date';
+    $element['value']['#date_time_format'] = '';
+    $element['value']['#date_time_element'] = 'none';
+    $element['value']['#ajax'] = [
+      'callback' => [$this, 'changeDuration'],
+      'event' => 'change',
+      'wrapper' => 'eventseries-edit-form',
+    ];
+
+    $element['end_value']['#title'] = t('And');
+    $element['end_value']['#weight'] = 2;
+    $element['end_value']['#date_date_format'] = DateTimeItemInterface::DATE_STORAGE_FORMAT;
+    $element['end_value']['#date_date_element'] = 'date';
+    $element['end_value']['#date_time_format'] = '';
+    $element['end_value']['#date_time_element'] = 'none';
+    $element['end_value']['#ajax'] = [
+      'callback' => [$this, 'changeDuration'],
+      'event' => 'change',
+      'wrapper' => 'eventseries-edit-form',
+    ];
+
+    $times = $this->getTimeOptions();
+    $element['time'] = [
+      '#type' => 'select',
+      '#title' => t('First Event Starts At'),
+      '#options' => $times,
+      '#default_value' => $items[$delta]->time ?: reset(array_keys($times)),
+      '#weight' => 3,
+      '#ajax' => [
+        'callback' => [$this, 'changeDuration'],
+        'event' => 'change',
+        'wrapper' => 'eventseries-edit-form',
+      ],
+    ];
+
+    $element['end_time'] = [
+      '#type' => 'select',
+      '#title' => t('Final Event Starts At'),
+      '#options' => $times,
+      '#default_value' => $items[$delta]->end_time ?: end(array_keys($times)),
+      '#weight' => 4,
+      '#ajax' => [
+        'callback' => [$this, 'changeDuration'],
+        'event' => 'change',
+        'wrapper' => 'eventseries-edit-form',
+      ],
+    ];
+
+    $units = $this->getUnitOptions();
+
+    $element['duration'] = [
+      '#type' => 'number',
+      '#title' => t('Event Duration'),
+      '#default_value' => $items[$delta]->duration ?: 5,
+      '#weight' => 5,
+      '#ajax' => [
+        'callback' => [$this, 'changeDuration'],
+        'event' => 'change',
+        'wrapper' => 'eventseries-edit-form',
+      ],
+    ];
+
+    $element['duration_units'] = [
+      '#type' => 'select',
+      '#title' => t('Event Duration'),
+      '#options' => $units,
+      '#default_value' => $items[$delta]->duration_units ?: 'minute',
+      '#weight' => 6,
+      '#ajax' => [
+        'callback' => [$this, 'changeDuration'],
+        'event' => 'change',
+        'wrapper' => 'eventseries-edit-form',
+      ],
+    ];
+
+    // If the form was reloaded due to a trigger, then check the duration
+    // configuration to ensure we're not going to be creating too many
+    // instances.
+    if ($config->get('threshold_warning')) {
+      $trigger = $form_state->getTriggeringElement();
+      if (!empty($trigger)) {
+        $total = self::checkDuration($form_state);
+        if ($total > $config->get('threshold_count')) {
+          $message = $config->get('threshold_message');
+          $message = str_replace('@total', $total, $message);
+          $element['count_warning'] = [
+            '#type' => 'markup',
+            '#prefix' => '<span class="form-item--error-message">',
+            '#markup' => $message,
+            '#suffix' => '</span>',
+            '#weight' => 6,
+          ];
+        }
+      }
+    }
+
+    $element['buffer'] = [
+      '#type' => 'number',
+      '#title' => t('Event Buffer'),
+      '#default_value' => $items[$delta]->buffer ?: 0,
+      '#weight' => 7,
+    ];
+
+    $element['buffer_units'] = [
+      '#type' => 'select',
+      '#title' => t('Event Buffer'),
+      '#options' => $units,
+      '#default_value' => $items[$delta]->buffer_units ?: 'minute',
+      '#weight' => 8,
+    ];
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+    foreach ($values as &$item) {
+      if (empty($item['value'])) {
+        $item['value'] = '';
+      }
+      elseif (!$item['value'] instanceof DrupalDateTime) {
+        $item['value'] = substr($item['value'], 0, 10);
+      }
+      else {
+        $item['value'];
+      }
+      if (empty($item['end_value'])) {
+        $item['end_value'] = '';
+      }
+      elseif (!$item['end_value'] instanceof DrupalDateTime) {
+        $item['end_value'] = substr($item['end_value'], 0, 10);
+      }
+      else {
+        $item['end_value'];
+      }
+
+    }
+    $values = parent::massageFormValues($values, $form, $form_state);
+    return $values;
+  }
+
+  /**
+   * Perform an AJAX reload of the form.
+   */
+  public function changeDuration(array $form, FormStateInterface $form_state) {
+    $response = new AjaxResponse();
+    $form_id = $form_state->getBuildInfo()['form_id'] == 'eventseries_edit_form' ? 'eventseries-edit-form' : 'eventseries-add-form';
+    $response->addCommand(new HtmlCommand('#' . $form_id, $form));
+    return $response;
+  }
+
+  /**
+   * Check for unreasonable duration and duration unit values.
+   */
+  public static function checkDuration(FormStateInterface $form_state) {
+    $day_count = $time_count = $total = 0;
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+    $form_data = ConsecutiveRecurringDate::convertFormConfigToArray($form_state);
+    if (!empty($form_data['start_date']) && !empty($form_data['end_date'])) {
+      $day_count = ConsecutiveRecurringDate::findDailyDatesBetweenDates($form_data['start_date'], $form_data['end_date'], TRUE);
+      $time_parts = static::convertTimeTo24hourFormat($form_data['time']);
+      if (!empty($time_parts)) {
+        $form_data['start_date']->setTime($time_parts[0], $time_parts[1]);
+        // Configure the right timezone.
+        $form_data['start_date']->setTimezone($utc_timezone);
+        $time_count = ConsecutiveRecurringDate::findSlotsBetweenTimes($form_data['start_date'], $form_data, TRUE);
+      }
+    }
+
+    if (!empty($day_count) && !empty($time_count)) {
+      $total = $day_count * $time_count;
+    }
+
+    return $total;
+  }
+
+  /**
+   * Element validate callback to ensure that the threshold is not exceeded.
+   *
+   * @param array $element
+   *   An associative array containing the properties and children of the
+   *   generic form element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param array $complete_form
+   *   The complete form structure.
+   */
+  public function validateThreshold(array &$element, FormStateInterface $form_state, array &$complete_form) {
+    $config = \Drupal::config('recurring_events.eventseries.config');
+
+    if ($config->get('threshold_prevent_save')) {
+      $total = $this->checkDuration($form_state);
+      if ($total > $config->get('threshold_count')) {
+        $message = $config->get('threshold_message');
+        $message = str_replace('@total', $total, $message);
+        $form_state->setError($element, $message);
+      }
+    }
+  }
+
+}
diff --git a/src/Plugin/Field/FieldWidget/DailyRecurringDateWidget.php b/src/Plugin/Field/FieldWidget/DailyRecurringDateWidget.php
new file mode 100644
index 0000000000000000000000000000000000000000..69a9c0270f97afdfe38ea2acfd1d81bf29960ce5
--- /dev/null
+++ b/src/Plugin/Field/FieldWidget/DailyRecurringDateWidget.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace Drupal\recurring_events\Plugin\Field\FieldWidget;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\datetime_range\Plugin\Field\FieldWidget\DateRangeDefaultWidget;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
+use Drupal\recurring_events\Plugin\RecurringEventsFieldTrait;
+
+/**
+ * Plugin implementation of the 'daily recurring date' widget.
+ *
+ * @FieldWidget (
+ *   id = "daily_recurring_date",
+ *   label = @Translation("Daily recurring date widget"),
+ *   field_types = {
+ *     "daily_recurring_date"
+ *   }
+ * )
+ */
+class DailyRecurringDateWidget extends DateRangeDefaultWidget {
+
+  use RecurringEventsFieldTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
+    $element = parent::formElement($items, $delta, $element, $form, $form_state);
+
+    $element['#type'] = 'container';
+    $element['#states'] = [
+      'visible' => [
+        ':input[name="recur_type"]' => ['value' => 'daily_recurring_date'],
+      ],
+    ];
+
+    $element['value']['#title'] = t('Create Events Between');
+    $element['value']['#weight'] = 1;
+    $element['value']['#date_date_format'] = DateTimeItemInterface::DATE_STORAGE_FORMAT;
+    $element['value']['#date_date_element'] = 'date';
+    $element['value']['#date_time_format'] = '';
+    $element['value']['#date_time_element'] = 'none';
+
+    $element['end_value']['#title'] = t('And');
+    $element['end_value']['#weight'] = 2;
+    $element['end_value']['#date_date_format'] = DateTimeItemInterface::DATE_STORAGE_FORMAT;
+    $element['end_value']['#date_date_element'] = 'date';
+    $element['end_value']['#date_time_format'] = '';
+    $element['end_value']['#date_time_element'] = 'none';
+
+    $times = $this->getTimeOptions();
+    $element['time'] = [
+      '#type' => 'select',
+      '#title' => t('Event Start Time'),
+      '#options' => $times,
+      '#default_value' => $items[$delta]->time ?: '',
+      '#weight' => 3,
+    ];
+
+    $durations = $this->getDurationOptions();
+    $element['duration'] = [
+      '#type' => 'select',
+      '#title' => t('Event Duration'),
+      '#options' => $durations,
+      '#default_value' => $items[$delta]->duration ?: '',
+      '#weight' => 4,
+    ];
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+    foreach ($values as &$item) {
+      if (empty($item['value'])) {
+        $item['value'] = '';
+      }
+      elseif (!$item['value'] instanceof DrupalDateTime) {
+        $item['value'] = substr($item['value'], 0, 10);
+      }
+      else {
+        $item['value'];
+      }
+      if (empty($item['end_value'])) {
+        $item['end_value'] = '';
+      }
+      elseif (!$item['end_value'] instanceof DrupalDateTime) {
+        $item['end_value'] = substr($item['end_value'], 0, 10);
+      }
+      else {
+        $item['end_value'];
+      }
+
+    }
+    $values = parent::massageFormValues($values, $form, $form_state);
+    return $values;
+  }
+
+}
diff --git a/src/Plugin/Field/FieldWidget/MonthlyRecurringDateWidget.php b/src/Plugin/Field/FieldWidget/MonthlyRecurringDateWidget.php
index 9091e1255f78e7f8647b6e463bf4a3fcccb8c57b..22bef8aa73e7c47f8f400e2a0d2f62812a36bb6c 100644
--- a/src/Plugin/Field/FieldWidget/MonthlyRecurringDateWidget.php
+++ b/src/Plugin/Field/FieldWidget/MonthlyRecurringDateWidget.php
@@ -28,7 +28,7 @@ class MonthlyRecurringDateWidget extends WeeklyRecurringDateWidget {
     $element['#type'] = 'container';
     $element['#states'] = [
       'visible' => [
-        ':input[name="recur_type"]' => ['value' => 'monthly'],
+        ':input[name="recur_type"]' => ['value' => 'monthly_recurring_date'],
       ],
     ];
 
diff --git a/src/Plugin/Field/FieldWidget/WeeklyRecurringDateWidget.php b/src/Plugin/Field/FieldWidget/WeeklyRecurringDateWidget.php
index 4bc34e359f1f3b26a63dda7f1a53fddc03b860ab..23dd15ffaee1a911bff6bf8d18785dfeb715f35a 100644
--- a/src/Plugin/Field/FieldWidget/WeeklyRecurringDateWidget.php
+++ b/src/Plugin/Field/FieldWidget/WeeklyRecurringDateWidget.php
@@ -3,10 +3,8 @@
 namespace Drupal\recurring_events\Plugin\Field\FieldWidget;
 
 use Drupal\Core\Field\FieldItemListInterface;
-use Drupal\datetime_range\Plugin\Field\FieldWidget\DateRangeDefaultWidget;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Datetime\DrupalDateTime;
-use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
 
 /**
  * Plugin implementation of the 'weekly recurring date' widget.
@@ -19,7 +17,7 @@ use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
  *   }
  * )
  */
-class WeeklyRecurringDateWidget extends DateRangeDefaultWidget {
+class WeeklyRecurringDateWidget extends DailyRecurringDateWidget {
 
   /**
    * {@inheritdoc}
@@ -30,42 +28,10 @@ class WeeklyRecurringDateWidget extends DateRangeDefaultWidget {
     $element['#type'] = 'container';
     $element['#states'] = [
       'visible' => [
-        ':input[name="recur_type"]' => ['value' => 'weekly'],
+        ':input[name="recur_type"]' => ['value' => 'weekly_recurring_date'],
       ],
     ];
 
-    $element['value']['#title'] = t('Create Events Between');
-    $element['value']['#weight'] = 1;
-    $element['value']['#date_date_format'] = DateTimeItemInterface::DATE_STORAGE_FORMAT;
-    $element['value']['#date_date_element'] = 'date';
-    $element['value']['#date_time_format'] = '';
-    $element['value']['#date_time_element'] = 'none';
-
-    $element['end_value']['#title'] = t('And');
-    $element['end_value']['#weight'] = 2;
-    $element['end_value']['#date_date_format'] = DateTimeItemInterface::DATE_STORAGE_FORMAT;
-    $element['end_value']['#date_date_element'] = 'date';
-    $element['end_value']['#date_time_format'] = '';
-    $element['end_value']['#date_time_element'] = 'none';
-
-    $times = $this->getTimeOptions();
-    $element['time'] = [
-      '#type' => 'select',
-      '#title' => t('Event Start Time'),
-      '#options' => $times,
-      '#default_value' => $items[$delta]->time ?: '',
-      '#weight' => 3,
-    ];
-
-    $durations = $this->getDurationOptions();
-    $element['duration'] = [
-      '#type' => 'select',
-      '#title' => t('Event Duration'),
-      '#options' => $durations,
-      '#default_value' => $items[$delta]->duration ?: '',
-      '#weight' => 4,
-    ];
-
     $days = $this->getDayOptions();
     $element['days'] = [
       '#type' => 'checkboxes',
@@ -115,95 +81,6 @@ class WeeklyRecurringDateWidget extends DateRangeDefaultWidget {
     return $values;
   }
 
-  /**
-   * Generate times based on specific intervals and min/max times.
-   *
-   * @return array
-   *   An array of times suitable for a select list.
-   */
-  protected function getTimeOptions() {
-    $times = [];
-
-    $config = \Drupal::config('recurring_events.eventseries.config');
-    // Take interval in minutes, and multiply it by 60 to convert to seconds.
-    $interval = $config->get('interval') * 60;
-    $min_time = $config->get('min_time');
-    $max_time = $config->get('max_time');
-    $format = $config->get('time_format');
-
-    $min_time = DrupalDateTime::createFromFormat('h:ia', $min_time);
-    $max_time = DrupalDateTime::createFromFormat('h:ia', $max_time);
-
-    // Convert the mininum time to a number of seconds after midnight.
-    $lower_hour = $min_time->format('H') * 60 * 60;
-    $lower_minute = $min_time->format('i') * 60;
-    $lower = $lower_hour + $lower_minute;
-
-    // Convert the maximum time to a number of seconds after midnight.
-    $upper_hour = $max_time->format('H') * 60 * 60;
-    $upper_minute = $max_time->format('i') * 60;
-    $upper = $upper_hour + $upper_minute;
-
-    $range = range($lower, $upper, $interval);
-    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
-
-    foreach ($range as $time) {
-      $time_option = DrupalDateTime::createFromTimestamp($time, $utc_timezone);
-      $times[$time_option->format('h:i a')] = $time_option->format($format);
-    }
-
-    \Drupal::moduleHandler()->alter('recurring_events_times', $times);
-
-    return $times;
-  }
-
-  /**
-   * Return durations for events.
-   *
-   * @return array
-   *   An array of durations suitable for a select list.
-   */
-  protected function getDurationOptions() {
-    $durations = [
-      '900' => t('15 minutes'),
-      '1800' => t('30 minutes'),
-      '2700' => t('45 minutes'),
-      '3600' => t('1 hour'),
-      '5400' => t('1.5 hours'),
-      '7200' => t('2 hours'),
-      '9000' => t('2.5 hours'),
-      '10800' => t('3 hours'),
-      '12600' => t('3.5 hours'),
-      '14400' => t('4 hours'),
-      '16200' => t('4.5 hours'),
-      '18000' => t('5 hours'),
-      '19800' => t('5.5 hours'),
-      '21600' => t('6 hours'),
-      '25200' => t('7 hours'),
-      '28800' => t('8 hours'),
-      '32400' => t('9 hours'),
-      '36000' => t('10 hours'),
-      '39600' => t('11 hours'),
-      '43200' => t('12 hours'),
-      '46800' => t('13 hours'),
-      '50400' => t('14 hours'),
-      '54000' => t('15 hours'),
-      '57600' => t('16 hours'),
-      '61200' => t('17 hours'),
-      '64800' => t('18 hours'),
-      '68400' => t('19 hours'),
-      '72000' => t('20 hours'),
-      '75600' => t('21 hours'),
-      '79200' => t('22 hours'),
-      '82800' => t('23 hours'),
-      '86400' => t('24 hours'),
-    ];
-
-    \Drupal::moduleHandler()->alter('recurring_events_durations', $durations);
-
-    return $durations;
-  }
-
   /**
    * Return day options for events.
    *
diff --git a/src/Plugin/RecurringEventsFieldTrait.php b/src/Plugin/RecurringEventsFieldTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..eabeba9131e9a0d29e03fb7957d4f643eccf9566
--- /dev/null
+++ b/src/Plugin/RecurringEventsFieldTrait.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace Drupal\recurring_events\Plugin;
+
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
+
+/**
+ * A trait to provide some time related reusable static methods.
+ */
+trait RecurringEventsFieldTrait {
+
+  /**
+   * Generate times based on specific intervals and min/max times.
+   *
+   * @return array
+   *   An array of times suitable for a select list.
+   */
+  protected function getTimeOptions() {
+    $times = [];
+
+    $config = \Drupal::config('recurring_events.eventseries.config');
+    // Take interval in minutes, and multiply it by 60 to convert to seconds.
+    $interval = $config->get('interval') * 60;
+    $min_time = $config->get('min_time');
+    $max_time = $config->get('max_time');
+    $format = $config->get('time_format');
+
+    $min_time = DrupalDateTime::createFromFormat('h:ia', $min_time);
+    $max_time = DrupalDateTime::createFromFormat('h:ia', $max_time);
+
+    // Convert the mininum time to a number of seconds after midnight.
+    $lower_hour = $min_time->format('H') * 60 * 60;
+    $lower_minute = $min_time->format('i') * 60;
+    $lower = $lower_hour + $lower_minute;
+
+    // Convert the maximum time to a number of seconds after midnight.
+    $upper_hour = $max_time->format('H') * 60 * 60;
+    $upper_minute = $max_time->format('i') * 60;
+    $upper = $upper_hour + $upper_minute;
+
+    $range = range($lower, $upper, $interval);
+    $utc_timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
+
+    foreach ($range as $time) {
+      $time_option = DrupalDateTime::createFromTimestamp($time, $utc_timezone);
+      $times[$time_option->format('h:i a')] = $time_option->format($format);
+    }
+
+    \Drupal::moduleHandler()->alter('recurring_events_times', $times);
+
+    return $times;
+  }
+
+  /**
+   * Return durations for events.
+   *
+   * @return array
+   *   An array of durations suitable for a select list.
+   */
+  protected function getDurationOptions() {
+    $durations = [
+      '900' => t('15 minutes'),
+      '1800' => t('30 minutes'),
+      '2700' => t('45 minutes'),
+      '3600' => t('1 hour'),
+      '5400' => t('1.5 hours'),
+      '7200' => t('2 hours'),
+      '9000' => t('2.5 hours'),
+      '10800' => t('3 hours'),
+      '12600' => t('3.5 hours'),
+      '14400' => t('4 hours'),
+      '16200' => t('4.5 hours'),
+      '18000' => t('5 hours'),
+      '19800' => t('5.5 hours'),
+      '21600' => t('6 hours'),
+      '25200' => t('7 hours'),
+      '28800' => t('8 hours'),
+      '32400' => t('9 hours'),
+      '36000' => t('10 hours'),
+      '39600' => t('11 hours'),
+      '43200' => t('12 hours'),
+      '46800' => t('13 hours'),
+      '50400' => t('14 hours'),
+      '54000' => t('15 hours'),
+      '57600' => t('16 hours'),
+      '61200' => t('17 hours'),
+      '64800' => t('18 hours'),
+      '68400' => t('19 hours'),
+      '72000' => t('20 hours'),
+      '75600' => t('21 hours'),
+      '79200' => t('22 hours'),
+      '82800' => t('23 hours'),
+      '86400' => t('24 hours'),
+    ];
+
+    \Drupal::moduleHandler()->alter('recurring_events_durations', $durations);
+
+    return $durations;
+  }
+
+  /**
+   * Convert a time from 12 hour format to 24 hour format.
+   *
+   * @var string $time
+   *   The time to convert to 24 hour format.
+   *
+   * @return array
+   *   An array of time parts.
+   */
+  protected static function convertTimeTo24hourFormat($time) {
+    $time_parts = [];
+
+    // Split the start time up to separate out hours and minutes.
+    $time_parts = explode(':', $time);
+    // If this is PM then add 12 hours to the hours, unless the time was
+    // set as noon.
+    if (strpos($time_parts[1], 'pm') !== FALSE && $time_parts[0] != '12') {
+      $time_parts[0] += 12;
+    }
+    // If this is AM and the time was midnight, set hours to 00.
+    elseif (strpos($time_parts[1], 'am') !== FALSE && $time_parts[0] == '12') {
+      $time_parts[0] = '00';
+    }
+    // Strip out AM or PM from the time.
+    $time_parts[1] = substr($time_parts[1], 0, -3);
+
+    return $time_parts;
+  }
+
+  /**
+   * Return unit options for events.
+   *
+   * @return array
+   *   An array of unit options suitable for a select list.
+   */
+  protected function getUnitOptions() {
+    $units = [
+      'second' => t('Second(s)'),
+      'minute' => t('Minute(s)'),
+      'hour' => t('Hour(s)'),
+    ];
+
+    \Drupal::moduleHandler()->alter('recurring_events_units', $units);
+
+    return $units;
+  }
+
+}
diff --git a/src/RecurringEventsFieldTypeInterface.php b/src/RecurringEventsFieldTypeInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..775c34291fdbd608444714fa4b2eb16c193e1e29
--- /dev/null
+++ b/src/RecurringEventsFieldTypeInterface.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Drupal\recurring_events;
+
+use Drupal\recurring_events\Entity\EventSeries;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * RecurringEventsFieldTypeInterface interface.
+ */
+interface RecurringEventsFieldTypeInterface {
+
+  /**
+   * Converts an EventSeries entity's recurring configuration to an array.
+   *
+   * @param Drupal\recurring_events\Entity\EventSeries $event
+   *   The stored event series entity.
+   *
+   * @return array
+   *   The recurring configuration as an array.
+   */
+  public static function convertEntityConfigToArray(EventSeries $event);
+
+  /**
+   * Converts a form state object's recurring configuration to an array.
+   *
+   * @param Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state of an updated event series entity.
+   *
+   * @return array
+   *   The recurring configuration as an array.
+   */
+  public static function convertFormConfigToArray(FormStateInterface $form_state);
+
+  /**
+   * Calculate the event instances to create for a series.
+   *
+   * @param array $form_data
+   *   The updated event series form data.
+   */
+  public static function calculateInstances(array $form_data);
+
+  /**
+   * Build diff array between stored entity and form state.
+   *
+   * @param array $entity_config
+   *   The stored event series configuration.
+   * @param array $form_config
+   *   The form state, or original, event series configuration.
+   *
+   * @return array
+   *   An array of differences.
+   */
+  public static function buildDiffArray(array $entity_config, array $form_config);
+
+}