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
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' },
];Quick start
Section titled “Quick start”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.
Advanced example
Section titled “Advanced example”Source code
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();
}
<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>
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 />;
}
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();
}
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.
Filter a day or range
Section titled “Filter a day or range”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.
Configure timezone and calendar policy
Section titled “Configure timezone and calendar policy”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.
| Mode | Resolved timezone |
|---|---|
user | userTimeZone, otherwise the browser’s resolved IANA timezone |
organization | Required organizationTimeZone |
utc | UTC |
explicit | Required 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.
Operators
Section titled “Operators”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
fiscalYearStartis 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.
Blank and invalid values
Section titled “Blank and invalid values”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.
Remote filtering
Section titled “Remote filtering”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.
Common mistakes
Section titled “Common mistakes”- Using
datefor 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.