Skip to content

Date Filter

Use the Pro date filter when a value represents a calendar day: due dates, business dates, holidays, or reporting periods. Use the Datetime Filter when values represent exact instants such as audit timestamps or scheduled executions.

Source code
TypeScriptts
grid.plugins = [AdvanceFilterPlugin];
grid.columns = [
  { prop: 'dueDate', name: 'Due date', filter: [FILTER_DATE] },
];
grid.filter = true;

return [
  { dueDate: '2026-08-25' },
  { dueDate: '2026-08-26' },
  { dueDate: '2026-08-27' },
  { dueDate: '2026-09-01' },
];
import { AdvanceFilterPlugin, FILTER_DATE } from '@revolist/revogrid-pro';
import '@revolist/revogrid-pro/dist/revogrid-pro.css';
grid.plugins = [AdvanceFilterPlugin];
grid.columns = [{ prop: 'dueDate', name: 'Due date', filter: [FILTER_DATE] }];
grid.filter = true;
grid.source = [
{ dueDate: '2026-08-25' },
{ dueDate: '2026-08-26' },
{ dueDate: null },
];

A source YYYY-MM-DD keeps its written calendar date. A Date, epoch value, or ISO datetime with Z or an explicit offset is projected into the resolved timezone before the calendar day is compared.

Source code
TypeScriptts
import { defineCustomElements } from '@revolist/revogrid/loader';
import { AdvanceFilterPlugin, ColumnStretchPlugin, FilterHeaderPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
import {
  createFilterDateColumns,
  createFilterDateConfig,
  createFilterDateRows,
  type FilterDateRow,
} from './FilterDate.shared';

defineCustomElements();

const { isDark } = currentTheme();

export function load(parentSelector: string, rows?: FilterDateRow[]) {
  const grid = document.createElement('revo-grid');
  grid.className = 'grow h-full cell-border';
  grid.style.minHeight = '400px';
  grid.columns = createFilterDateColumns();
  grid.plugins = [AdvanceFilterPlugin, FilterHeaderPlugin, ColumnStretchPlugin];
  grid.filter = createFilterDateConfig();
  grid.stretch = 'all';
  grid.theme = isDark() ? 'darkMaterial' : 'material';
  grid.hideAttribution = true;
  grid.readonly = true;
  document.querySelector(parentSelector)?.appendChild(grid);
  grid.source = Array.isArray(rows) ? rows : createFilterDateRows();
  return () => grid.remove();
}
Vuevue
<template>
  <VGrid class="grow h-full cell-border" :theme="isDark ? 'darkMaterial' : 'material'"
    :columns="columns" :source="rows" :plugins="plugins" :filter="filter" stretch="all"
    style="min-height: 400px" hide-attribution readonly />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { VGrid } from '@revolist/vue3-datagrid';
import { AdvanceFilterPlugin, ColumnStretchPlugin, FilterHeaderPlugin } from '@revolist/revogrid-pro';
import { currentThemeVue } from '../composables/useRandomData';
import {
  createFilterDateColumns,
  createFilterDateConfig,
  createFilterDateRows,
} from './FilterDate.shared';

const { isDark } = currentThemeVue();
const rows = ref(createFilterDateRows());
const columns = createFilterDateColumns();
const plugins = [AdvanceFilterPlugin, FilterHeaderPlugin, ColumnStretchPlugin];
const filter = createFilterDateConfig();
</script>
Reacttsx
import React from 'react';
import { RevoGrid, type ColumnRegular } from '@revolist/react-datagrid';
import { AdvanceFilterPlugin, ColumnStretchPlugin, FilterHeaderPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
import {
  createFilterDateColumns,
  createFilterDateConfig,
  createFilterDateRows,
  type FilterDateRow,
} from './FilterDate.shared';

const { isDark } = currentTheme();
const sampleRows = createFilterDateRows();

export default function FilterDateExample({ rows = sampleRows }: { rows?: FilterDateRow[] }) {
  const columns = React.useMemo<ColumnRegular[]>(createFilterDateColumns, []);
  const plugins = React.useMemo(
    () => [AdvanceFilterPlugin, FilterHeaderPlugin, ColumnStretchPlugin],
    [],
  );
  const additionalData = React.useMemo(() => ({ stretch: 'all' as const }), []);
  const filter = React.useMemo(createFilterDateConfig, []);

  return <RevoGrid className="grow h-full cell-border" theme={isDark() ? 'darkMaterial' : 'material'}
    columns={columns} source={rows} plugins={plugins} additionalData={additionalData} filter={filter}
    style={{ minHeight: '400px' }} hideAttribution readonly />;
}
Angularts
import { Component, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { AdvanceFilterPlugin, ColumnStretchPlugin, FilterHeaderPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
import {
  createFilterDateColumns,
  createFilterDateConfig,
  createFilterDateRows,
} from './FilterDate.shared';

@Component({
  selector: 'filter-date-grid',
  standalone: true,
  imports: [RevoGrid],
  encapsulation: ViewEncapsulation.None,
  template: `<revo-grid class="grow h-full cell-border" [theme]="isDark ? 'darkMaterial' : 'material'" [columns]="columns"
    [source]="source" [plugins]="plugins" [stretch]="stretch" [filter]="filter"
    style="min-height: 400px" [hideAttribution]="true" [readonly]="true"></revo-grid>`,
})
export class FilterDateGridComponent {
  columns = createFilterDateColumns();
  plugins = [AdvanceFilterPlugin, FilterHeaderPlugin, ColumnStretchPlugin];
  source = createFilterDateRows();
  filter = createFilterDateConfig();
  stretch = 'all' as const;
  isDark = currentTheme().isDark();
}
Shared configurationts
import type { ColumnFilterConfig, ColumnRegular } from '@revolist/revogrid';
import {
  FILTER_CALENDAR_RANGE,
  FILTER_RELATIVE_WINDOW,
  FILTER_TIME_MATRIX,
  FILTER_TIMELINE_BRUSH,
  createCalendarRangeStructuredFilterType,
  createRelativeWindowStructuredFilterType,
  createTimeMatrixStructuredFilterType,
  createTimelineBrushStructuredFilterType,
} from '@revolist/revogrid-pro';

export interface FilterDateRow {
  event: string;
  calendarDate: string;
  orderDate: string;
  renewalDate: string;
  createdAt: string;
  activityAt: string;
  warsawInstant: string;
  organizationInstant: string;
  exactInstant: string;
}

function dateRow(
  event: string,
  calendarDate: string,
  instant: string,
  renewalDate: string,
): FilterDateRow {
  return {
    event,
    calendarDate,
    orderDate: calendarDate,
    renewalDate,
    createdAt: instant,
    activityAt: instant,
    warsawInstant: instant,
    organizationInstant: instant,
    exactInstant: instant,
  };
}

export function createFilterDateRows(): FilterDateRow[] {
  const today = new Date();
  const renewalDate = (offset: number) => {
    const date = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate() + offset));
    return date.toISOString().slice(0, 10);
  };
  return [
    dateRow('Fiscal year start', '2026-02-01', '2026-02-01T09:15:00Z', renewalDate(-17)),
    dateRow('New York DST — before', '2026-03-08', '2026-03-08T06:59:59Z', renewalDate(-10)),
    dateRow('New York DST — after', '2026-03-08', '2026-03-08T07:00:00Z', renewalDate(-6)),
    dateRow('Early morning activity', '2026-04-27', '2026-04-27T05:30:00Z', renewalDate(-1)),
    dateRow('Business-hours activity', '2026-05-05', '2026-05-05T10:30:00Z', renewalDate(0)),
    dateRow('Late shift activity', '2026-05-24', '2026-05-24T22:15:00Z', renewalDate(1)),
    dateRow('Quarter boundary', '2026-06-30', '2026-06-30T16:45:00Z', renewalDate(5)),
    dateRow('Weekend activity', '2026-07-04', '2026-07-04T14:00:00Z', renewalDate(10)),
    dateRow('Warsaw / New York split', '2026-08-05', '2026-08-05T00:30:00Z', renewalDate(14)),
    dateRow('UTC instant', '2026-08-12', '2026-08-12T12:00:00Z', renewalDate(30)),
    dateRow('Range final day', '2026-08-30', '2026-08-30T23:59:59Z', renewalDate(60)),
    dateRow('Inclusive month end', '2026-09-30', '2026-09-30T23:59:59Z', renewalDate(90)),
  ];
}

export function createFilterDateColumns(): ColumnRegular[] {
  return [
    { name: 'Scenario', prop: 'event', filter: false, size: 220 },
    { name: 'Calendar date', prop: 'calendarDate', filter: 'date', size: 160 },
    { name: 'Order date', prop: 'orderDate', filter: [FILTER_CALENDAR_RANGE], size: 160 },
    { name: 'Renewal date', prop: 'renewalDate', filter: [FILTER_RELATIVE_WINDOW], size: 170 },
    { name: 'Created at', prop: 'createdAt', filter: [FILTER_TIMELINE_BRUSH], size: 190 },
    { name: 'Activity time', prop: 'activityAt', filter: [FILTER_TIME_MATRIX], size: 190 },
    { name: 'Warsaw day', prop: 'warsawInstant', filter: 'date', size: 160 },
    { name: 'New York org day', prop: 'organizationInstant', filter: 'date', size: 180 },
    { name: 'Exact UTC datetime', prop: 'exactInstant', filter: 'datetime', size: 210 },
  ];
}

export function createFilterDateConfig(): ColumnFilterConfig {
  return {
    expressions: true,
    structuredFilterTypes: [
      createCalendarRangeStructuredFilterType({
        locale: 'en-US',
        weekStartsOn: 1,
        operators: [
          'calendarRangeBetween',
          'calendarRangeEquals',
          'calendarRangeBefore',
          'calendarRangeAfter',
        ],
        showOutsideDays: true,
        allowOutsideMonthSelection: true,
        showTodayButton: true,
        rangeValidation: { minDays: 1, maxDays: 60 },
      }),
      createRelativeWindowStructuredFilterType({
        locale: 'en-US',
        presets: [
          'last7Days',
          'today',
          'thisMonth',
          'thisQuarter',
          'yearToDate',
          'overdue',
          {
            id: 'next14Days',
            label: 'Next 14 days',
            expression: {
              version: 1,
              mode: 'custom',
              direction: 'next',
              amount: 14,
              unit: 'day',
              rolling: true,
            },
          },
        ],
        customWindow: {
          directions: ['last', 'next'],
          units: ['day', 'week', 'month', 'quarter'],
          showRolling: true,
        },
      }),
      createTimelineBrushStructuredFilterType({
        granularity: 'day',
        binCap: 32,
        locale: 'en-US',
        format: { month: 'short', day: 'numeric' },
        chart: { type: 'area', showPoints: true },
      }),
      createTimeMatrixStructuredFilterType({
        locale: 'en-US',
        weekStartsOn: 1,
        useAmPm: true,
        workingHours: { startHour: 9, endHour: 18 },
        weekendDays: [0, 6],
        presets: {
          businessHours: {
            days: [1, 2, 3, 4, 5],
            startHour: 9,
            endHour: 18,
            label: 'Office hours',
          },
          weekends: { days: [0, 6] },
          nights: { startHour: 22, endHour: 6 },
        },
      }),
    ],
    date: {
      timezoneMode: 'user',
      userTimeZone: 'America/New_York',
      weekStartsOn: 1,
      fiscalYearStart: { month: 2, day: 1 },
      operators: [
        'equals',
        'before',
        'after',
        'between',
        'isEmpty',
        'isNotEmpty',
        'today',
        'thisMonth',
        'thisQuarter',
        'thisFiscalQuarter',
        'nextFiscalQuarter',
        'previousFiscalQuarter',
        'thisFiscalYear',
        'nextFiscalYear',
        'previousFiscalYear',
      ],
      columns: {
        calendarDate: { timezoneMode: 'utc' },
        orderDate: { timezoneMode: 'utc' },
        renewalDate: { timezoneMode: 'utc' },
        createdAt: { timezoneMode: 'utc' },
        activityAt: { timezoneMode: 'utc' },
        warsawInstant: { timezoneMode: 'explicit', timeZone: 'Europe/Warsaw' },
        organizationInstant: {
          timezoneMode: 'organization',
          organizationTimeZone: 'America/New_York',
        },
        exactInstant: { timezoneMode: 'utc' },
      },
    },
  };
}

Start with browser-local behavior. Add a timezone, week-start, or fiscal-calendar policy only when the application has a defined business requirement.

Date equality includes the complete civil day. A between range includes both selected days; internally it is evaluated as a half-open range whose end is the start of the following day.

grid.filter = {
multiFilterItems: {
dueDate: [
{
id: 1,
type: 'between',
value: {
fromDate: '2026-08-01',
toDate: '2026-08-31',
},
relation: 'and',
},
],
},
};

Use ISO calendar strings in persisted state so the intended day remains explicit.

grid.filter = {
date: {
timezoneMode: 'organization',
organizationTimeZone: 'America/New_York',
weekStartsOn: 1, // Monday; Sunday is 0
fiscalYearStart: { month: 2, day: 1 },
// Ordered allowlist used by every date and datetime menu.
operators: ['equals', 'before', 'after', 'between', 'today', 'thisFiscalYear'],
columns: {
accountingDate: {
timezoneMode: 'utc',
// A column entry replaces the global operator list for this field.
operators: ['between', 'thisFiscalYear'],
},
warsawDate: {
timezoneMode: 'explicit',
timeZone: 'Europe/Warsaw',
},
},
},
};

Settings resolve field by field: defaults, grid policy, then the partial entry in date.columns[prop]. The operators array is an ordered allowlist, so it also controls menu order. A column-level array replaces the grid-level array for that field. The same date IDs configure datetime menus; the plugin maps them to the corresponding datetime operators internally.

ModeResolved timezone
useruserTimeZone, otherwise the browser’s resolved IANA timezone
organizationRequired organizationTimeZone
utcUTC
explicitRequired timeZone

With no date configuration, behavior remains browser-local and the week starts on Monday. Fiscal operators are hidden until an effective fiscalYearStart is configured globally or for that column. Once configured, all fiscal quarter and fiscal year operators are available by default; use operators to show only the fiscal choices appropriate for the application. Date calculations still use January 1 internally when evaluating existing persisted fiscal conditions without an authored fiscal policy. Initialization rejects unknown operator IDs, invalid IANA names, week indexes outside 0…6, missing organization or explicit zones, February 29, and other fiscal dates that do not recur every year.

The same instant can fall on different calendar days in Warsaw and New York. Calendar strings do not shift: 2026-08-05 remains August 5 under every timezone policy.

The date filter provides:

  • Comparisons: Equals, Before, After, On or before, On or after, Between, Not equal
  • Blank policy: Is blank, Is not blank
  • Rolling days: Today, Yesterday, Last 7 days, Next 30 days
  • Weeks: This week, Last week, Next week
  • Months: This month, Last month
  • Calendar periods: This, Previous, and Next quarter or year
  • Fiscal periods (when fiscalYearStart is set): This, Previous, and Next fiscal quarter or fiscal year

last7Days includes today plus the preceding six civil days. next30Days includes today plus the following 29 days. Calendar quarters remain January/April/July/October quarters; only fiscal operators use fiscalYearStart.

The expression editor uses the same evaluator and captured clock. Examples include today, this week, next 30 days, this fiscal quarter, and between "2026-08-01" and "2026-08-31". Expressions still reference only their current column.

isEmpty and isNotEmpty use Core’s configured blank policy. Invalid nonblank temporal values fail comparisons but are not automatically blank. false and NaN therefore do not become blank dates.

Read Blank Values for policy precedence and missing-property behavior.

Each filtering run captures one reference instant. beforefilterapply exposes temporalContext, and pagination or infinity-scroll loadData receives it as the optional seventh argument; the first six arguments are unchanged.

loadData: async (
skip,
take,
order,
singleConditionFilters,
multiConditionFilters,
quickFilter,
temporalContext,
) =>
fetch('/api/rows', {
method: 'POST',
body: JSON.stringify({
skip,
take,
order,
multiConditionFilters,
quickFilter,
temporalContext,
}),
}).then((response) => response.json());

The JSON-safe envelope contains referenceInstant, each filtered temporal column’s resolved timezone, and condition ranges keyed by <column>:<filter id>. UTC boundaries use start and endExclusive; an unbounded side is null.

  • Using date for timestamps that must retain time-of-day precision; use Datetime Filter.
  • Assuming browser-local time matches the organization’s reporting timezone.
  • Treating the end of a date range as exclusive; the selected final day is included.
  • Sending a relative operator to a server without the captured temporalContext.
  • Treating invalid nonblank values as blanks.