diff --git a/office_hours.module b/office_hours.module
index fde2afc9ca7bae95c2504d4a71874247c29fb650..ba1fab84aa2bf3ffd427f147928507a5e4407422 100644
--- a/office_hours.module
+++ b/office_hours.module
@@ -97,6 +97,13 @@ function office_hours_views_query_substitutions(ViewExecutable $view) {
   return OfficeHoursStatusFilter::viewsQuerySubstitutions($view);
 }
 
+/**
+ * Implements hook_views_pre_execute().
+ */
+function office_hours_views_pre_execute(ViewExecutable $view) {
+  return OfficeHoursStatusFilter::viewsPreExecute($view);
+}
+
 /**
  * Implements hook_views_post_execute().
  */
diff --git a/src/Plugin/Field/FieldType/OfficeHoursItem.php b/src/Plugin/Field/FieldType/OfficeHoursItem.php
index b8fa0d091d5f0b60c6def81d6cf946cabfddc16d..f28d687160168df02291cf213990adf8fc667a23 100644
--- a/src/Plugin/Field/FieldType/OfficeHoursItem.php
+++ b/src/Plugin/Field/FieldType/OfficeHoursItem.php
@@ -183,7 +183,7 @@ class OfficeHoursItem extends OfficeHoursItemBase {
    * @return int
    *   a predefined constant with the status.
    */
-  public function getStatus($time) {
+  public function getStatus($time = 0): int {
     $status = static::UNDEFINED;
 
     $now_weekday = OfficeHoursDateHelper::getWeekday($time);
diff --git a/src/Plugin/Field/FieldType/OfficeHoursItemBase.php b/src/Plugin/Field/FieldType/OfficeHoursItemBase.php
index ff06e1f49e60b20b3f19edacf3b7382acaefab1e..554ce71dce12e34e45721de3d09c12ec37085df5 100644
--- a/src/Plugin/Field/FieldType/OfficeHoursItemBase.php
+++ b/src/Plugin/Field/FieldType/OfficeHoursItemBase.php
@@ -71,6 +71,15 @@ class OfficeHoursItemBase extends FieldItemBase {
       ->addConstraint('Length', ['max' => 255])
       ->setDescription("Stores the comment.");
 
+    // @todo #3501772 Convert to complex datatype, for usingkey/value formatter
+    //$properties['status'] = DataDefinition::create('field_item:list_string')
+    //$properties['status'] = DataDefinition::create('map')
+    $properties['status'] = DataDefinition::create('integer')
+      ->setLabel(t('Status'))
+      ->setDescription(t('Is the entity currently open, currently closed or never open.'))
+      ->setComputed(TRUE)
+      ->setClass('\Drupal\office_hours\Plugin\Field\FieldType\OfficeHoursStatus');
+
     return $properties;
   }
 
diff --git a/src/Plugin/Field/FieldType/OfficeHoursItemList.php b/src/Plugin/Field/FieldType/OfficeHoursItemList.php
index e44cd09555a4ccf3f7ee95a03b7c600b31947393..7b231b2bba7c80a0b98a783e14d5b69124b49e41 100644
--- a/src/Plugin/Field/FieldType/OfficeHoursItemList.php
+++ b/src/Plugin/Field/FieldType/OfficeHoursItemList.php
@@ -269,6 +269,25 @@ class OfficeHoursItemList extends FieldItemList implements OfficeHoursItemListIn
     return count($exception_days);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getStatus($time = 0): int {
+    $items = $this;
+
+    switch (TRUE) {
+      case is_null($items):
+      case $items->isEmpty():
+        $status = OfficeHoursStatus::NEVER;
+        break;
+
+      default:
+        $status = $items->isOpen($time);
+        break;
+    }
+    return $status;
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/src/Plugin/Field/FieldType/OfficeHoursItemListInterface.php b/src/Plugin/Field/FieldType/OfficeHoursItemListInterface.php
index 137ca17e255c580e927b0b4d683eb3778ac960ce..e2cccdd6af178adea3de17a8e309463210ebbdbf 100644
--- a/src/Plugin/Field/FieldType/OfficeHoursItemListInterface.php
+++ b/src/Plugin/Field/FieldType/OfficeHoursItemListInterface.php
@@ -122,6 +122,17 @@ interface OfficeHoursItemListInterface extends FieldItemListInterface {
    */
   public function countExceptionDays();
 
+   /**
+   * Returns if an entity currently open, currently closed or never open.
+   *
+   * @param int $time
+   *   A timestamp. Might be adapted for User Timezone.
+   *
+   * @return int
+   *   a predefined constant with the status.
+   */
+  public function getStatus($time = 0): int;
+
   /**
    * Determines if the Entity is Open or Closed.
    *
diff --git a/src/Plugin/Field/FieldType/OfficeHoursStatus.php b/src/Plugin/Field/FieldType/OfficeHoursStatus.php
new file mode 100644
index 0000000000000000000000000000000000000000..a28f49106bbc5df8fcfa0a541cb5bcaf9589db79
--- /dev/null
+++ b/src/Plugin/Field/FieldType/OfficeHoursStatus.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\office_hours\Plugin\Field\FieldType;
+
+use Drupal\Core\TypedData\TypedData;
+
+/**
+ * A computed property for displaying the open/closed status of a field.
+ *
+ * Plugin implementation of the 'list_string' field type.
+ * @see OfficeHoursItemBase~propertyDefinitions()
+ */
+class OfficeHoursStatus extends TypedData {
+  // @todo #3501772 Convert to complex datatype, for usingkey/value formatter
+  // Mapitem, ListStringItem, Map, TypedData {
+
+  public const string ANY = 'all';
+  public const bool CLOSED = FALSE;
+  public const bool OPEN = TRUE;
+  public const int NEVER = 2;
+
+  /**
+   * Cached open/closed status.
+   *
+   * @var int
+   */
+  protected $value = FALSE;
+
+  /**
+   * Implements \Drupal\Core\TypedData\TypedDataInterface::getValue().
+   */
+  public function getValue() {
+    $items = $this->getParent()->getParent();
+    $status = $items->getStatus();
+    $this->setValue((int) $status);
+    return $status;
+  }
+
+}
diff --git a/src/Plugin/views/field/FieldBase.php b/src/Plugin/views/field/FieldBase.php
index bbf77059e7bd5db73496c32a34ed30c04babc417..ab837a7a170b61c31f9b6a56d33b1132f0565157 100644
--- a/src/Plugin/views/field/FieldBase.php
+++ b/src/Plugin/views/field/FieldBase.php
@@ -26,6 +26,7 @@ class FieldBase extends FieldPluginBase {
     $columns = [
       'season' => 'office_hours_season',
       'timeslot' => 'office_hours_timeslot',
+      'status' => 'office_hours_status',
     ];
 
     foreach ($data as $table_name => $table_data) {
@@ -91,6 +92,30 @@ class FieldBase extends FieldPluginBase {
         unset($field_data['filter']);
         unset($field_data['sort']);
 
+        // Extend 'Status'.
+        $column = 'status';
+        $label = $field_label . ' - Status';
+        $real_field = 'delta';
+        $title = t('@label (@name:@column)',
+          ['@label' => $label, '@name' => $field_name, '@column' => $column]
+        );
+        $title_short = t('@label:@column',
+          ['@label' => $label, '@column' => $column]);
+
+        // @todo Do not take over all field attributes.
+        $field_data = &$data[$table_name][$field_name . "_$column"];
+        // Use ?? to avoid TypeError: Unsupported operand types: array + null.
+        // This may have side effects, since data should exist. @see #3421574.
+        $field_data += $data[$table_name][$field_name] ?? [];
+        $field_data['field'] += $data[$table_name][$real_field]['field'] ?? [];
+        $field_data['field']['real field'] = $real_field;
+        $field_data['field']['property'] = $real_field;
+        $field_data['title'] = $title;
+        $field_data['title short'] = $title_short;
+        unset($field_data['argument']);
+        // Filter is set in \views\filter\OfficeHoursStatusFilter.
+        // unset($field_data['filter']);
+        unset($field_data['sort']);
       }
     }
 
@@ -157,7 +182,7 @@ class FieldBase extends FieldPluginBase {
 
     $entity = $this->getEntity($values);
     // Entities with no / empty office_hours will have delta = NULL.
-    $delta = $values->{$table . '_delta'} ?? NULL;
+    $delta = $values->{"{$table}_delta"} ?? NULL;
     // So, no need to check for $entity->hasField($field_name).
     if (!is_null($delta)) {
       $items = $entity->get($field_name);
diff --git a/src/Plugin/views/field/Status.php b/src/Plugin/views/field/Status.php
new file mode 100644
index 0000000000000000000000000000000000000000..658c1dfeba23970a20486dfb8de37520415bc81a
--- /dev/null
+++ b/src/Plugin/views/field/Status.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\office_hours\Plugin\views\field;
+
+use Drupal\office_hours\Plugin\Field\FieldType\OfficeHoursStatus;
+use Drupal\views\ResultRow;
+
+/**
+ * Computed field to display the open/closed status.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @ViewsField("office_hours_status")
+ *
+ * @see https://www.drupal.org/docs/drupal-apis/entity-api/dynamicvirtual-field-values-using-computed-field-property-classes
+ */
+class Status extends FieldBase {
+
+  /**
+   * Called to add the field to a query.
+   */
+  public function query() {
+    // Do not add the computed subfield to the query.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render(ResultRow $values) {
+    $entity = $values->_entity;
+    $field_name = $this->configuration['field_name'];
+    $property_name = 'status';
+
+    $result = $entity->{$field_name}->{$property_name} ?? OfficeHoursStatus::NEVER;
+    return $result; // $this->sanitizeValue($result);
+  }
+
+}
diff --git a/src/Plugin/views/filter/OfficeHoursStatusFilter.php b/src/Plugin/views/filter/OfficeHoursStatusFilter.php
index de117154185a94a0f253246b25a0d87f633d8eb8..a9fcf69ddfb24ef5d424f134042d4c87961e9885 100644
--- a/src/Plugin/views/filter/OfficeHoursStatusFilter.php
+++ b/src/Plugin/views/filter/OfficeHoursStatusFilter.php
@@ -3,12 +3,13 @@
 namespace Drupal\office_hours\Plugin\views\filter;
 
 use Drupal\field\FieldStorageConfigInterface;
+use Drupal\office_hours\Plugin\Field\FieldType\OfficeHoursStatus;
 use Drupal\views\Plugin\views\cache\CachePluginBase;
 use Drupal\views\Plugin\views\filter\ManyToOne;
 use Drupal\views\ViewExecutable;
 
 /**
- * Filter by open/closed status.
+ * Views Filter by open/closed status.
  *
  * @ingroup views_filter_handlers
  *
@@ -20,17 +21,14 @@ use Drupal\views\ViewExecutable;
  * @see https://www.webomelette.com/creating-custom-views-filter-drupal-8
  * @see https://www.drupal.org/docs/drupal-apis/entity-api/dynamicvirtual-field-values-using-computed-field-property-classes
  * @see https://drupal.stackexchange.com/questions/249963/how-to-add-a-custom-views-filter-handler-for-a-specific-field
+ * @see https://drupal.stackexchange.com/questions/291236/creating-a-custom-field-with-dynamic-virtual-computed-property-value
  */
 class OfficeHoursStatusFilter extends ManyToOne {
 
   /*
    * Duplicate of the @ViewsFilter annotation.
    */
-  const VIEWS_FILTER_ID = "office_hours_is_open";
-  const ANY = 'all';
-  const CLOSED = FALSE;
-  const OPEN = TRUE;
-  const NEVER = 2;
+  public const string VIEWS_FILTER_ID = "office_hours_is_open";
 
   /**
    * Implements hook_field_views_data().
@@ -65,10 +63,10 @@ class OfficeHoursStatusFilter extends ManyToOne {
    */
   public function getValueOptions() {
     $this->valueOptions = [
-      static::ANY => $this->t('Select all'),
-      static::OPEN => $this->t('Open now'),
-      static::CLOSED => $this->t('Temporarily closed'),
-      static::NEVER => $this->t('Permanently closed'),
+      OfficeHoursStatus::ANY => $this->t('Select all'),
+      OfficeHoursStatus::OPEN => $this->t('Open now'),
+      OfficeHoursStatus::CLOSED => $this->t('Temporarily closed'),
+      OfficeHoursStatus::NEVER => $this->t('Permanently closed'),
     ];
 
     return $this->valueOptions;
@@ -106,60 +104,43 @@ class OfficeHoursStatusFilter extends ManyToOne {
       return;
     }
 
-    $filterValue = $filter->value;
-    $filterValue = array_filter($filterValue, function ($statusValue) {
-      return $statusValue !== 0;
-    });
-
-    if (in_array(static::ANY, $filterValue)) {
-      return;
-    }
-
-    $fieldName = $filter->realField;
+    // Remove duplicate rows from the view.
     $previous_id = -1;
-    /** @var \Drupal\views\ResultRow $value */
     foreach ($view->result as $key => $value) {
-      // Remove duplicate rows from the view.
       $id = $value->_entity->id();
       if ($previous_id === $id) {
         unset($view->result[$key]);
-        continue;
       }
       $previous_id = $id;
+    }
 
-      // Remove filtered rows from the view.
-      // Since this is a calculated field, it cannot be done via query().
-      /** @var \Drupal\office_hours\Plugin\Field\FieldType\OfficeHoursItemList $items */
-      $items = $value->_entity->$fieldName;
-      if (is_null($items) || $items->isEmpty()) {
-        if (!in_array(static::NEVER, $filterValue)) {
-          unset($view->result[$key]);
-        }
-        continue;
-      }
+    $filterValue = $filter->value;
+    if (empty($filterValue)) {
+      return;
+    }
+    if (in_array(OfficeHoursStatus::ANY, $filterValue)) {
+      return;
+    }
 
-      $is_open = $items->isOpen();
-      if ($is_open && in_array((int) static::OPEN, $filterValue)) {
-        continue;
-      }
-      if ((!$is_open) && in_array((int) static::CLOSED, $filterValue)) {
-        continue;
+    // Remove filtered rows from the view.
+    // Since this is a calculated field, it cannot be done via query().
+    $fieldName = $filter->realField;
+    foreach ($view->result as $key => $value) {
+      $id = $value->_entity->id();
+      $status = $value->_entity->$fieldName->getStatus($time = 0);
+      if (!in_array($status, $filterValue)) {
+        unset($view->result[$key]);
       }
-      unset($view->result[$key]);
     }
-
   }
 
   /**
    * {@inheritdoc}
    */
   public function query() {
+    // Do not add query details for this computed field. No SQL is possible.
     // The views.inc file is not always loaded. Lazy load here.
     \Drupal::moduleHandler()->loadInclude('office_hours', 'inc', 'office_hours.views');
-
-    // Do not add query details, since this is a computed field,
-    // and no SQL is possible.
-    // parent::query();
   }
 
   /**
@@ -172,29 +153,42 @@ class OfficeHoursStatusFilter extends ManyToOne {
     return ['***OFFICE_HOURS_REQUEST_TIME***' => \Drupal::time()->getRequestTime()];
   }
 
+  /**
+   * Implements hook_views_pre_execute().
+   */
+  public static function viewsPreExecute(ViewExecutable $view) {
+    // Nothing to do here.
+    // if (static::getFilter($view)) {
+    // self::filter($view);
+    // }
+  }
+
   /**
    * Implements hook_views_post_execute().
    */
   public static function viewsPostExecute(ViewExecutable $view) {
     // Nothing to do here.
+    if (static::getFilter($view)) {
+      self::filter($view);
+    }
   }
 
   /**
    * Implements hook_field_views_pre_render().
    */
   public static function viewsPreRender(ViewExecutable $view) {
-    if (static::getFilter($view)) {
-      self::filter($view);
-    }
+    // if (static::getFilter($view)) {
+    // self::filter($view);
+    // }
   }
 
   /**
    * Implements hook_views_post_render().
    */
   public static function viewsPostRender(ViewExecutable $view, array &$output, CachePluginBase $cache) {
-    if (!static::getFilter($view)) {
-      return;
-    }
+    // if (static::getFilter($view)) {
+    // return;
+    // }
 
     // @todo Improve time-based caching (is_open/closed status),
     // setting $output['#cache']['max-age'] from $items->getCacheMaxAge().