Fixing the “Start and end date excluded” Problem in Drupal 9 views filters

If you’ve ever worked with exposed filters in Drupal Views, particularly date filters, you may have run into an annoying issue: the end date you select gets excluded from the results. This happens even when your filter is set to “is less than or equal to”.

This is a known behavior stemming from how date filters handle time — the end date often defaults to midnight (00:00:00), so anything happening later on that day is not included in the results. If you want to understand the root cause, check out this issue on drupal.org.

There are several ways to deal with this:

But sometimes, all you need is a quick and clean solution. Here’s how I resolved this without patching core or introducing new modules.

My Solution Using hook_form_views_exposed_form_alter()

Instead of altering Views queries or building custom plugins, I used the hook_form_views_exposed_form_alter() hook to programmatically adjust the exposed filter values after submission. This means modifying the form values before Views gets a chance to run the query.

Here’s the complete code:

 function mymodule_form_views_exposed_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  // Add a custom submit handler to adjust the submitted values of 'start' and 'end'.
  if (!empty($form['start']) || !empty($form['end'])) {
    array_unshift($form['#submit'], function ($form, \Drupal\Core\Form\FormStateInterface $form_state) {
      // Adjust the 'start' value by subtracting 1 day.
      if (!empty($form_state->getValue('start'))) {
        $start_value = $form_state->getValue('start');
        $start_value = date('Y-m-d\T23:59:00', strtotime($start_value . ' -1 day'));
        $form_state->setValue('start', $start_value);
        $form['start']['#value'] = $start_value;
      }

      // Adjust the 'end' value by adding 1 day.
      if (!empty($form_state->getValue('end'))) {
        $end_value = $form_state->getValue('end');
        $end_value = date('Y-m-d', strtotime($end_value . ' +1 day'));
        $form_state->setValue('end', $end_value);
        $form['end']['#value'] = $end_value;
      }
    });
  }
} 

This approach ensures that the start date includes the entire day before, and the end date includes the full selected day — effectively working around the default behavior that trims the time to 00:00:00.

Why This Works

Views doesn’t offer a simple way to manipulate form values after submission, but the exposed filters form is still a Drupal form — and that means we can use standard form API techniques. By adding our own submit handler, we intercept the input just in time to reshape the values before the View executes the query.

Conclusion

This is a lightweight, module-free workaround that fixes the inclusive end date problem without additional overhead. It's ideal if you want to avoid patches, custom filters, or additional contrib modules.

But if you need more advanced filtering options and a user-friendly date picker, don’t hesitate to try out the Views Date Filter module — it’s built for exactly this purpose.

 

Saved you some valuable time?

Buy me a drink 🍺 to keep me motivated to create free content like this!