Allow DATE_TIME IS operand to filter on a whole day (#17529)

Fixes:  https://github.com/twentyhq/core-team-issues/issues/2027

We've replaced the DATE_TIME picker with DATE picker, and changed the
logic to filter for complete day period.



https://github.com/user-attachments/assets/ba7e1078-bab3-4c62-a803-d6a851f14b7d

---------

Co-authored-by: Arun kumar <arunkumar@Aruns-MacBook-Air.local>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Arun
2026-02-18 19:14:05 +05:30
committed by GitHub
parent 88146c2170
commit 8e6b267ff3
15 changed files with 245 additions and 52 deletions
@@ -1480,6 +1480,7 @@ export enum FeatureFlagKey {
IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED',
IS_CORE_PICTURE_MIGRATED = 'IS_CORE_PICTURE_MIGRATED',
IS_DASHBOARD_V2_ENABLED = 'IS_DASHBOARD_V2_ENABLED',
IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED = 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED',
IS_DRAFT_EMAIL_ENABLED = 'IS_DRAFT_EMAIL_ENABLED',
IS_EMAILING_DOMAIN_ENABLED = 'IS_EMAILING_DOMAIN_ENABLED',
IS_FILES_FIELD_MIGRATED = 'IS_FILES_FIELD_MIGRATED',
@@ -19,6 +19,7 @@ import {
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
import { stringifyRelativeDateFilter } from '@/views/view-filter-value/utils/stringifyRelativeDateFilter';
import { WORKFLOW_TIMEZONE } from '@/workflow/constants/WorkflowTimeZone';
import { isObject, isString } from '@sniptt/guards';
@@ -62,6 +63,10 @@ export const AdvancedFilterCommandMenuValueFormInput = ({
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const featureFlags = useFeatureFlagsMap();
const isWholeDayFilterEnabled =
featureFlags.IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED ?? false;
const handleChange = (newValue: JsonValue) => {
if (isString(newValue)) {
applyObjectFilterDropdownFilterValue(newValue);
@@ -178,7 +183,12 @@ export const AdvancedFilterCommandMenuValueFormInput = ({
}
const field = {
type: recordFilter.type as FieldMetadataType,
type:
isWholeDayFilterEnabled === true &&
recordFilter.type === FieldMetadataType.DATE_TIME &&
recordFilter.operand === RecordFilterOperand.IS
? FieldMetadataType.DATE
: (recordFilter.type as FieldMetadataType),
label: '',
metadata: fieldDefinition?.metadata as FieldMetadata,
};
@@ -38,7 +38,12 @@ export const ObjectFilterDropdownDateInput = () => {
useApplyObjectFilterDropdownFilterValue();
const handleAbsoluteDateChange = (newPlainDate: string | null) => {
const newFilterValue = newPlainDate ?? '';
if (!isDefined(newPlainDate)) {
applyObjectFilterDropdownFilterValue('', '');
return;
}
const newFilterValue = newPlainDate;
// TODO: remove this and use getDisplayValue instead
const formattedDate = formatDateString({
@@ -91,6 +96,7 @@ export const ObjectFilterDropdownDateInput = () => {
? handleRelativeDateChange(null)
: handleAbsoluteDateChange(null);
};
const resolvedValue = objectFilterDropdownCurrentRecordFilter
? resolveDateFilter(objectFilterDropdownCurrentRecordFilter)
: null;
@@ -100,7 +106,7 @@ export const ObjectFilterDropdownDateInput = () => {
? resolvedValue
: undefined;
const plainDateValue =
const safePlainDateValue: string | undefined =
resolvedValue && typeof resolvedValue === 'string'
? resolvedValue
: undefined;
@@ -110,7 +116,7 @@ export const ObjectFilterDropdownDateInput = () => {
instanceId={`object-filter-dropdown-date-input`}
relativeDate={relativeDate}
isRelative={isRelativeOperand}
plainDateString={plainDateValue ?? null}
plainDateString={safePlainDateValue ?? null}
onChange={handleAbsoluteDateChange}
onRelativeDateChange={handleRelativeDateChange}
onClear={handleClear}
@@ -5,6 +5,7 @@ import { ObjectFilterDropdownRatingInput } from '@/object-record/object-filter-d
import { ObjectFilterDropdownRecordSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect';
import { ObjectFilterDropdownSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
import { ViewFilterOperand } from 'twenty-shared/types';
@@ -28,6 +29,10 @@ export const ObjectFilterDropdownFilterInput = ({
filterDropdownId,
recordFilterId,
}: ObjectFilterDropdownFilterInputProps) => {
const featureFlags = useFeatureFlagsMap();
const isWholeDayFilterEnabled =
featureFlags.IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED ?? false;
const fieldMetadataItemUsedInDropdown = useRecoilComponentValue(
fieldMetadataItemUsedInDropdownComponentSelector,
);
@@ -76,6 +81,18 @@ export const ObjectFilterDropdownFilterInput = ({
</>
);
} else if (filterType === 'DATE_TIME') {
if (
isWholeDayFilterEnabled &&
selectedOperandInDropdown === ViewFilterOperand.IS
) {
return (
<>
<ObjectFilterDropdownInnerSelectOperandDropdown />
<DropdownMenuSeparator />
<ObjectFilterDropdownDateInput />
</>
);
}
return (
<>
<ObjectFilterDropdownInnerSelectOperandDropdown />
@@ -1,3 +1,6 @@
import { isNonEmptyString } from '@sniptt/guards';
import { Temporal } from 'temporal-polyfill';
import { useUpsertObjectFilterDropdownCurrentFilter } from '@/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
@@ -7,14 +10,12 @@ import { useGetRelativeDateFilterWithUserTimezone } from '@/object-record/record
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { stringifyRelativeDateFilter } from '@/views/view-filter-value/utils/stringifyRelativeDateFilter';
import { isNonEmptyString } from '@sniptt/guards';
import { Temporal } from 'temporal-polyfill';
import { DEFAULT_RELATIVE_DATE_FILTER_VALUE } from 'twenty-shared/constants';
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
import { DEFAULT_RELATIVE_DATE_FILTER_VALUE } from 'twenty-shared/constants';
import {
isDefined,
relativeDateFilterStringifiedSchema,
@@ -47,6 +48,10 @@ export const useApplyObjectFilterDropdownOperand = () => {
const { getRelativeDateFilterWithUserTimezone } =
useGetRelativeDateFilterWithUserTimezone();
const featureFlags = useFeatureFlagsMap();
const isWholeDayFilterEnabled =
featureFlags.IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED ?? false;
const applyObjectFilterDropdownOperand = (
newOperand: RecordFilterOperand,
) => {
@@ -106,7 +111,24 @@ export const useApplyObjectFilterDropdownOperand = () => {
recordFilterToUpsert.value,
);
if (filterValueIsEmpty || isStillRelativeFilterValue.success) {
const previousOperand =
objectFilterDropdownCurrentRecordFilter?.operand;
const isDateTimeOperandFormatChange =
recordFilterToUpsert.type === 'DATE_TIME' &&
!filterValueIsEmpty &&
!isStillRelativeFilterValue.success &&
(previousOperand === RecordFilterOperand.IS ||
newOperand === RecordFilterOperand.IS);
if (isDateTimeOperandFormatChange) {
recordFilterToUpsert.value = convertDateTimeFilterValue(
recordFilterToUpsert.value,
newOperand,
userTimezone,
isWholeDayFilterEnabled,
);
} else if (filterValueIsEmpty || isStillRelativeFilterValue.success) {
const zonedDateToUse = Temporal.Now.zonedDateTimeISO(userTimezone);
if (recordFilterToUpsert.type === 'DATE') {
@@ -116,11 +138,18 @@ export const useApplyObjectFilterDropdownOperand = () => {
recordFilterToUpsert.value = initialNowDateFilterValue;
} else {
const initialNowDateTimeFilterValue = zonedDateToUse
.toInstant()
.toString();
recordFilterToUpsert.value = initialNowDateTimeFilterValue;
if (
newOperand === RecordFilterOperand.IS &&
isWholeDayFilterEnabled
) {
recordFilterToUpsert.value = zonedDateToUse
.toPlainDate()
.toString();
} else {
recordFilterToUpsert.value = zonedDateToUse
.toInstant()
.toString();
}
}
}
}
@@ -137,3 +166,40 @@ export const useApplyObjectFilterDropdownOperand = () => {
applyObjectFilterDropdownOperand,
};
};
const convertDateTimeFilterValue = (
currentValue: string,
targetOperand: RecordFilterOperand,
userTimezone: string,
isWholeDayFilterEnabled = false,
): string => {
const zonedDateToUse = Temporal.Now.zonedDateTimeISO(userTimezone);
if (targetOperand === RecordFilterOperand.IS) {
try {
const existingZoned = currentValue.includes('T')
? Temporal.Instant.from(currentValue).toZonedDateTimeISO(userTimezone)
: Temporal.PlainDate.from(currentValue).toZonedDateTime(userTimezone);
if (isWholeDayFilterEnabled) {
return existingZoned.toPlainDate().toString();
} else {
return existingZoned.toInstant().toString();
}
} catch {
return zonedDateToUse.toPlainDate().toString();
}
} else {
try {
const existingPlainDate = Temporal.PlainDate.from(currentValue);
const currentTime = zonedDateToUse.toPlainTime();
const zonedFromPlain = existingPlainDate.toZonedDateTime({
timeZone: userTimezone,
plainTime: currentTime,
});
return zonedFromPlain.toInstant().toString();
} catch {
return zonedDateToUse.toInstant().toString();
}
}
};
@@ -1,9 +1,12 @@
import { Temporal } from 'temporal-polyfill';
import { useGetDateFilterDisplayValue } from '@/object-record/object-filter-dropdown/hooks/useGetDateFilterDisplayValue';
import { useGetDateTimeFilterDisplayValue } from '@/object-record/object-filter-dropdown/hooks/useGetDateTimeFilterDisplayValue';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone';
import { Temporal } from 'temporal-polyfill';
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
import { type FilterableAndTSVectorFieldType } from 'twenty-shared/types';
const activeDatePickerOperands = [
@@ -16,6 +19,9 @@ export const useGetInitialFilterValue = () => {
const { userTimezone } = useUserTimezone();
const { getDateFilterDisplayValue } = useGetDateFilterDisplayValue();
const { getDateTimeFilterDisplayValue } = useGetDateTimeFilterDisplayValue();
const featureFlags = useFeatureFlagsMap();
const isWholeDayFilterEnabled =
featureFlags.IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED ?? false;
const getInitialFilterValue = (
newType: FilterableAndTSVectorFieldType,
@@ -44,6 +50,17 @@ export const useGetInitialFilterValue = () => {
alreadyExistingZonedDateTime ??
Temporal.Now.zonedDateTimeISO(userTimezone);
if (
isWholeDayFilterEnabled === true &&
newOperand === RecordFilterOperand.IS
) {
const value = referenceDate.toPlainDate().toString();
const { displayValue } = getDateFilterDisplayValue(referenceDate);
return { value, displayValue };
}
const value = referenceDate.toInstant().toString();
const { displayValue } = getDateTimeFilterDisplayValue(referenceDate);
@@ -1,3 +1,6 @@
import { isNonEmptyString } from '@sniptt/guards';
import { Temporal } from 'temporal-polyfill';
import { useGetFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
import { useGetDateFilterDisplayValue } from '@/object-record/object-filter-dropdown/hooks/useGetDateFilterDisplayValue';
import { useGetDateTimeFilterDisplayValue } from '@/object-record/object-filter-dropdown/hooks/useGetDateTimeFilterDisplayValue';
@@ -7,8 +10,7 @@ import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordF
import { isRecordFilterConsideredEmpty } from '@/object-record/record-filter/utils/isRecordFilterConsideredEmpty';
import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone';
import { getTimezoneAbbreviationForZonedDateTime } from '@/ui/input/components/internal/date/utils/getTimeZoneAbbreviationForZonedDateTime';
import { isNonEmptyString } from '@sniptt/guards';
import { Temporal } from 'temporal-polyfill';
import { type Nullable } from 'twenty-shared/types';
import {
isDefined,
@@ -87,7 +89,23 @@ export const useGetRecordFilterDisplayValue = () => {
}
} else if (recordFilter.type === 'DATE_TIME') {
switch (recordFilter.operand) {
case RecordFilterOperand.IS:
case RecordFilterOperand.IS: {
if (!isNonEmptyString(recordFilter.value)) {
return '';
}
const zonedDateTime = recordFilter.value.includes('T')
? Temporal.Instant.from(recordFilter.value).toZonedDateTimeISO(
userTimezone,
)
: Temporal.PlainDate.from(recordFilter.value).toZonedDateTime(
userTimezone,
);
const { displayValue } = getDateFilterDisplayValue(zonedDateTime);
return `${displayValue}`;
}
case RecordFilterOperand.IS_AFTER:
case RecordFilterOperand.IS_BEFORE: {
if (!isNonEmptyString(recordFilter.value)) {
@@ -1024,9 +1024,9 @@ describe('should work as expected for the different field types', () => {
const dateFilterIs: RecordFilter = {
id: 'company-date-filter-is',
value: '2024-09-17T20:46:58.922Z',
value: '2024-09-17',
fieldMetadataId: companyMockDateFieldMetadataId?.id,
displayValue: '2024-09-17T20:46:58.922Z',
displayValue: '2024-09-17',
operand: ViewFilterOperand.IS,
label: 'Created At',
type: FieldMetadataType.DATE_TIME,
@@ -1081,12 +1081,12 @@ describe('should work as expected for the different field types', () => {
and: [
{
createdAt: {
lt: '2024-09-17T20:47:00Z',
gte: '2024-09-16T22:00:00Z',
},
},
{
createdAt: {
gte: '2024-09-17T20:46:00Z',
lt: '2024-09-17T22:00:00Z',
},
},
],
@@ -20,6 +20,7 @@ export enum FeatureFlagKey {
IS_SSE_DB_EVENTS_ENABLED = 'IS_SSE_DB_EVENTS_ENABLED',
IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED',
IS_NAVIGATION_MENU_ITEM_ENABLED = 'IS_NAVIGATION_MENU_ITEM_ENABLED',
IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED = 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED',
IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED = 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED',
IS_DRAFT_EMAIL_ENABLED = 'IS_DRAFT_EMAIL_ENABLED',
}
@@ -0,0 +1 @@
export const DEFAULT_TIMEZONE = 'UTC';
@@ -12,6 +12,7 @@ import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadat
import { ViewSortDirection } from 'src/engine/metadata-modules/view-sort/enums/view-sort-direction';
import { ViewQueryParamsService } from 'src/engine/metadata-modules/view/services/view-query-params.service';
import { ViewService } from 'src/engine/metadata-modules/view/services/view.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
describe('ViewQueryParamsService', () => {
let viewQueryParamsService: ViewQueryParamsService;
@@ -73,6 +74,12 @@ describe('ViewQueryParamsService', () => {
getOrRecomputeManyOrAllFlatEntityMaps: jest.fn(),
},
},
{
provide: GlobalWorkspaceOrmManager,
useValue: {
getRepository: jest.fn(),
},
},
],
}).compile();
@@ -20,7 +20,10 @@ import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadat
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { ViewSortDirection } from 'src/engine/metadata-modules/view-sort/enums/view-sort-direction';
import { DEFAULT_TIMEZONE } from 'src/engine/metadata-modules/view/constants/default-timezone.constant';
import { ViewService } from 'src/engine/metadata-modules/view/services/view.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { type WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
export type ViewQueryParams = {
objectNameSingular: string;
@@ -35,6 +38,7 @@ export class ViewQueryParamsService {
constructor(
private readonly viewService: ViewService,
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
) {}
async resolveViewToQueryParams(
@@ -61,6 +65,11 @@ export class ViewQueryParamsService {
flatEntityMaps: flatObjectMetadataMaps,
});
const timeZone = await this.getWorkspaceMemberTimezoneIfAvailable(
workspaceId,
currentWorkspaceMemberId,
);
const recordFilters: RecordFilter[] = (view.viewFilters ?? [])
.map((viewFilter) => {
const field = findFlatEntityByIdInFlatEntityMaps({
@@ -122,7 +131,7 @@ export class ViewQueryParamsService {
fields,
recordFilters,
recordFilterGroups,
filterValueDependencies: { currentWorkspaceMemberId, timeZone: 'UTC' }, // TODO: check if we need to put workspace member timezone here
filterValueDependencies: { currentWorkspaceMemberId, timeZone },
});
const orderBy: ObjectRecordOrderBy = (view.viewSorts ?? [])
@@ -151,4 +160,29 @@ export class ViewQueryParamsService {
viewType: view.type,
};
}
private async getWorkspaceMemberTimezoneIfAvailable(
workspaceId: string,
currentWorkspaceMemberId?: string,
): Promise<string> {
if (!isDefined(currentWorkspaceMemberId)) {
return DEFAULT_TIMEZONE;
}
try {
const workspaceMemberRepository =
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
workspaceId,
'workspaceMember',
);
const workspaceMember = await workspaceMemberRepository.findOne({
where: { id: currentWorkspaceMemberId },
});
return workspaceMember?.timeZone ?? DEFAULT_TIMEZONE;
} catch {
return DEFAULT_TIMEZONE;
}
}
}
@@ -243,6 +243,7 @@ describe('WorkspaceEntityManager', () => {
IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED: false,
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: false,
IS_MARKETPLACE_ENABLED: false,
IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED: false,
IS_FILES_FIELD_MIGRATED: false,
IS_DRAFT_EMAIL_ENABLED: false,
IS_CORE_PICTURE_MIGRATED: false,
@@ -101,6 +101,11 @@ export const seedFeatureFlags = async ({
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
workspaceId: workspaceId,
@@ -1,4 +1,5 @@
import { isNonEmptyString } from '@sniptt/guards';
import { Temporal } from 'temporal-polyfill';
import {
FieldMetadataType,
@@ -52,7 +53,6 @@ import {
import { arrayOfStringsOrVariablesSchema } from '@/utils/filter/utils/validation-schemas/arrayOfStringsOrVariablesSchema';
import { arrayOfUuidOrVariableSchema } from '@/utils/filter/utils/validation-schemas/arrayOfUuidsOrVariablesSchema';
import { jsonRelationFilterValueSchema } from '@/utils/filter/utils/validation-schemas/jsonRelationFilterValueSchema';
import { Temporal } from 'temporal-polyfill';
type FieldShared = {
id: string;
@@ -390,6 +390,43 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
throw new Error(`Date filter is empty`);
}
if (recordFilter.operand === RecordFilterOperand.IS) {
const timeZone = filterValueDependencies.timeZone ?? 'UTC';
let parsedPlainDate = null;
try {
parsedPlainDate = recordFilter.value.includes('T')
? Temporal.Instant.from(recordFilter.value)
.toZonedDateTimeISO(timeZone)
.toPlainDate()
: Temporal.PlainDate.from(recordFilter.value);
} catch {
throw new Error(
`Cannot parse "${recordFilter.value}" for ${filterType} filter`,
);
}
const zonedDateTime = parsedPlainDate.toZonedDateTime(timeZone);
const start = zonedDateTime.toInstant();
const end = zonedDateTime.add({ days: 1 }).toInstant();
return {
and: [
{
[correspondingFieldMetadataItem.name]: {
gte: start.toString(),
} as DateTimeFilter,
},
{
[correspondingFieldMetadataItem.name]: {
lt: end.toString(),
} as DateTimeFilter,
},
],
};
}
const resolvedDateTime = Temporal.Instant.from(recordFilter.value);
switch (recordFilter.operand) {
@@ -407,34 +444,6 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
} as DateTimeFilter,
};
}
case RecordFilterOperand.IS: {
const start = resolvedDateTime
.toZonedDateTimeISO('UTC')
.with({
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
})
.toInstant();
const end = start.add({ minutes: 1 });
return {
and: [
{
[correspondingFieldMetadataItem.name]: {
lt: end.toString(),
} as DateTimeFilter,
},
{
[correspondingFieldMetadataItem.name]: {
gte: start.toString(),
} as DateTimeFilter,
},
],
};
}
}
}