From 76c78a51f335b4583b1b2d278f7e95c96995efee Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii <5622446+sjbur@users.noreply.github.com> Date: Wed, 19 Aug 2026 16:42:14 +0200 Subject: [PATCH 1/5] fix: fix normalizing hours for allDay appointments --- .../js/__internal/scheduler/scheduler.ts | 5 +- .../generate_grid_view_model.ts | 9 ++- .../steps/expand_all_day.test.ts | 58 ++++++++++++++++++- .../steps/expand_all_day.ts | 43 ++++++++++++-- 4 files changed, 106 insertions(+), 9 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/scheduler.ts b/packages/devextreme/js/__internal/scheduler/scheduler.ts index bc3810c7abf9..faab89515299 100644 --- a/packages/devextreme/js/__internal/scheduler/scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/scheduler.ts @@ -2089,8 +2089,11 @@ class Scheduler extends SchedulerOptionsBaseWidget { if ( appointment.allDay - && !this._workSpace.supportAllDayRow() && !this._workSpace.keepOriginalHours() + && ( + !this._workSpace.supportAllDayRow() + || this.option('allDayPanelMode') === 'hidden' + ) ) { const dateCopy = new Date(resultedStartDate); dateCopy.setHours(0); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts index b3d23b419944..d31997c40ab5 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts @@ -25,8 +25,9 @@ export const sortAppointments = ( hasAllDayPanel, snapToCellsMode, viewOffset, - compareOptions: { endDayHour }, + compareOptions: { endDayHour, startDayHour }, } = optionManager.options; + const ignoreAllDayHours = !hasAllDayPanel && !isMonthView; const step2 = maybeSplit(items, hasAllDayPanel, (entities, panelName) => { const byGroup = groupByGroupIndex(entities); @@ -35,7 +36,11 @@ export const sortAppointments = ( sortByStartDate(group); const innerStep0 = isMonthView || panelName === 'allDayPanel' ? expandAllDayAllDayPanel(group, endDayHour, viewOffset) - : expandAllDayRegularPanel(group); + : expandAllDayRegularPanel(group, { + startDayHour, + viewOffsetMs: viewOffset, + ignoreAllDayHours, + }); const innerStep1 = splitByParts(innerStep0, optionManager.getSplitIntervals(panelName)); sortByDuration(innerStep1); sortByStartDate(innerStep1); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts index 789c5eec5e6a..58742748a62d 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts @@ -2,6 +2,12 @@ import { describe, expect, it } from '@jest/globals'; import { expandAllDayAllDayPanel, expandAllDayRegularPanel } from './expand_all_day'; +const defaultRegularPanelOptions = { + startDayHour: 0, + viewOffsetMs: 0, + ignoreAllDayHours: false, +}; + describe('expandAllDay', () => { describe('expandAllDayAllDayPanel', () => { it('should not expand regular appointment', () => { @@ -89,7 +95,7 @@ describe('expandAllDay', () => { allDay: false, startDateUTC: Date.UTC(2020, 0, 10, 0), endDateUTC: Date.UTC(2020, 0, 10, 1), - }])).toEqual([ + }], defaultRegularPanelOptions)).toEqual([ { allDay: false, startDateUTC: Date.UTC(2020, 0, 10, 0), @@ -113,7 +119,7 @@ describe('expandAllDay', () => { startDateUTC: Date.UTC(2020, 0, 11, 23), endDateUTC: Date.UTC(2020, 0, 12), }, - ])).toEqual([ + ], defaultRegularPanelOptions)).toEqual([ { allDay: true, startDateUTC: Date.UTC(2020, 0, 10), @@ -129,5 +135,53 @@ describe('expandAllDay', () => { }, ]); }); + + it('should ignore hours in all day appointment dates when ignoreAllDayHours is true', () => { + expect(expandAllDayRegularPanel([ + { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 5), + endDateUTC: Date.UTC(2020, 0, 10, 5), + }, { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 4), + endDateUTC: Date.UTC(2020, 0, 11, 5), + }, + ], { + startDayHour: 8, + viewOffsetMs: 0, + ignoreAllDayHours: true, + })).toEqual([ + { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 8), + endDateUTC: Date.UTC(2020, 0, 11, 8), + }, { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 8), + endDateUTC: Date.UTC(2020, 0, 12, 8), + }, + ]); + }); + + it('should ignore hours for timeline all day appointment the same way as for week view', () => { + expect(expandAllDayRegularPanel([ + { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 5), + endDateUTC: Date.UTC(2020, 0, 10, 17), + }, + ], { + startDayHour: 0, + viewOffsetMs: 0, + ignoreAllDayHours: true, + })).toEqual([ + { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 0), + endDateUTC: Date.UTC(2020, 0, 11, 0), + }, + ]); + }); }); }); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts index 99c414167557..37092bcd63b3 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts @@ -72,13 +72,43 @@ export const expandAllDayAllDayPanel = >( - entities: T[], -): T[] => entities.map((entity) => { +interface ExpandAllDayRegularPanelOptions { + startDayHour: number; + viewOffsetMs: number; + ignoreAllDayHours: boolean; +} + +const expandAllDayRegularPanelEntity = >( + entity: T, + { startDayHour, viewOffsetMs, ignoreAllDayHours }: ExpandAllDayRegularPanelOptions, +): T => { if (!entity.allDay) { return entity; } + if (ignoreAllDayHours) { + if (viewOffsetMs === 0) { + const normalizedStart = new Date(entity.startDateUTC) + .setUTCHours(startDayHour, 0, 0, 0); + const endDate = new Date(entity.endDateUTC); + endDate.setUTCDate(endDate.getUTCDate() + 1); + + return { + ...entity, + startDateUTC: normalizedStart, + endDateUTC: endDate.setUTCHours(startDayHour, 0, 0, 0), + }; + } + + return { + ...entity, + startDateUTC: getShiftedStartDate(entity.startDateUTC, viewOffsetMs) + + startDayHour * toMs('hour'), + endDateUTC: getShiftedEndDate(entity.endDateUTC + DAY_MS, viewOffsetMs) + + startDayHour * toMs('hour'), + }; + } + const startDate = new Date(entity.startDateUTC); const endDate = new Date(entity.endDateUTC); endDate.setDate(endDate.getDate() + 1); @@ -93,4 +123,9 @@ export const expandAllDayRegularPanel = >( + entities: T[], + options: ExpandAllDayRegularPanelOptions, +): T[] => entities.map((entity) => expandAllDayRegularPanelEntity(entity, options)); From 65bfc84bb0009caf27d320f0cd4b4f9a436a269b Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii <5622446+sjbur@users.noreply.github.com> Date: Thu, 20 Aug 2026 12:10:42 +0200 Subject: [PATCH 2/5] fix: undo changes for viewOffset != 0 --- .../generate_grid_view_model.ts | 2 +- .../steps/expand_all_day.test.ts | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts index d31997c40ab5..a8efc972cba4 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts @@ -27,7 +27,7 @@ export const sortAppointments = ( viewOffset, compareOptions: { endDayHour, startDayHour }, } = optionManager.options; - const ignoreAllDayHours = !hasAllDayPanel && !isMonthView; + const ignoreAllDayHours = !hasAllDayPanel && !isMonthView && viewOffset === 0; const step2 = maybeSplit(items, hasAllDayPanel, (entities, panelName) => { const byGroup = groupByGroupIndex(entities); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts index 58742748a62d..f46297eb394c 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts @@ -183,5 +183,25 @@ describe('expandAllDay', () => { }, ]); }); + + it('should preserve hours in all day appointment dates when view offset is used', () => { + expect(expandAllDayRegularPanel([ + { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 20, 30), + endDateUTC: Date.UTC(2020, 0, 10, 23, 30), + }, + ], { + startDayHour: 0, + viewOffsetMs: -735 * 60_000, + ignoreAllDayHours: false, + })).toEqual([ + { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 20, 30), + endDateUTC: Date.UTC(2020, 0, 11, 20, 30), + }, + ]); + }); }); }); From aaaf4b9b6be8b389e5b82dbb66aa1fd166771602 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii <5622446+sjbur@users.noreply.github.com> Date: Thu, 20 Aug 2026 12:25:29 +0200 Subject: [PATCH 3/5] fix: undo changes for viewOffset != 0 --- .../generate_grid_view_model.ts | 2 +- .../steps/expand_all_day.test.ts | 16 ++++++++++++---- .../steps/expand_all_day.ts | 18 +++++++++--------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts index a8efc972cba4..d31997c40ab5 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/generate_grid_view_model.ts @@ -27,7 +27,7 @@ export const sortAppointments = ( viewOffset, compareOptions: { endDayHour, startDayHour }, } = optionManager.options; - const ignoreAllDayHours = !hasAllDayPanel && !isMonthView && viewOffset === 0; + const ignoreAllDayHours = !hasAllDayPanel && !isMonthView; const step2 = maybeSplit(items, hasAllDayPanel, (entities, panelName) => { const byGroup = groupByGroupIndex(entities); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts index f46297eb394c..ee0a7f7eb4ab 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts @@ -184,22 +184,30 @@ describe('expandAllDay', () => { ]); }); - it('should preserve hours in all day appointment dates when view offset is used', () => { + it('should map all day appointment to its calendar offset-day, ignoring hours', () => { expect(expandAllDayRegularPanel([ { allDay: true, startDateUTC: Date.UTC(2020, 0, 10, 20, 30), endDateUTC: Date.UTC(2020, 0, 10, 23, 30), + }, { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 2), + endDateUTC: Date.UTC(2020, 0, 11, 2), }, ], { startDayHour: 0, viewOffsetMs: -735 * 60_000, - ignoreAllDayHours: false, + ignoreAllDayHours: true, })).toEqual([ { allDay: true, - startDateUTC: Date.UTC(2020, 0, 10, 20, 30), - endDateUTC: Date.UTC(2020, 0, 11, 20, 30), + startDateUTC: Date.UTC(2020, 0, 9, 11, 45), + endDateUTC: Date.UTC(2020, 0, 10, 11, 44), + }, { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 9, 11, 45), + endDateUTC: Date.UTC(2020, 0, 11, 11, 44), }, ]); }); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts index 37092bcd63b3..bde4fe198e13 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts @@ -87,25 +87,25 @@ const expandAllDayRegularPanelEntity = Date: Thu, 20 Aug 2026 12:52:12 +0200 Subject: [PATCH 4/5] fix: undo changes for viewOffset != 0 --- .../steps/expand_all_day.test.ts | 24 +++++++++++++++++-- .../steps/expand_all_day.ts | 17 +++---------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts index ee0a7f7eb4ab..69652c08d333 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts @@ -203,11 +203,31 @@ describe('expandAllDay', () => { { allDay: true, startDateUTC: Date.UTC(2020, 0, 9, 11, 45), - endDateUTC: Date.UTC(2020, 0, 10, 11, 44), + endDateUTC: Date.UTC(2020, 0, 10, 11, 45), }, { allDay: true, startDateUTC: Date.UTC(2020, 0, 9, 11, 45), - endDateUTC: Date.UTC(2020, 0, 11, 11, 44), + endDateUTC: Date.UTC(2020, 0, 11, 11, 45), + }, + ]); + }); + + it('should keep all day appointment on its calendar day when offset is a full day', () => { + expect(expandAllDayRegularPanel([ + { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 10, 20, 30), + endDateUTC: Date.UTC(2020, 0, 10, 23, 30), + }, + ], { + startDayHour: 0, + viewOffsetMs: -1440 * 60_000, + ignoreAllDayHours: true, + })).toEqual([ + { + allDay: true, + startDateUTC: Date.UTC(2020, 0, 9, 0), + endDateUTC: Date.UTC(2020, 0, 10, 0), }, ]); }); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts index bde4fe198e13..b7b4ba073578 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts @@ -89,23 +89,12 @@ const expandAllDayRegularPanelEntity = Date: Thu, 20 Aug 2026 13:00:21 +0200 Subject: [PATCH 5/5] fix: undo changes for viewOffset != 0 --- .../steps/expand_all_day.test.ts | 16 ++++++++-------- .../generate_view_model/steps/expand_all_day.ts | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts index 69652c08d333..31e942a35d7c 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.test.ts @@ -184,7 +184,7 @@ describe('expandAllDay', () => { ]); }); - it('should map all day appointment to its calendar offset-day, ignoring hours', () => { + it('should ignore hours and keep calendar dates so view offset can shift them like timed appointments', () => { expect(expandAllDayRegularPanel([ { allDay: true, @@ -202,17 +202,17 @@ describe('expandAllDay', () => { })).toEqual([ { allDay: true, - startDateUTC: Date.UTC(2020, 0, 9, 11, 45), - endDateUTC: Date.UTC(2020, 0, 10, 11, 45), + startDateUTC: Date.UTC(2020, 0, 10, 0), + endDateUTC: Date.UTC(2020, 0, 11, 0), }, { allDay: true, - startDateUTC: Date.UTC(2020, 0, 9, 11, 45), - endDateUTC: Date.UTC(2020, 0, 11, 11, 45), + startDateUTC: Date.UTC(2020, 0, 10, 0), + endDateUTC: Date.UTC(2020, 0, 12, 0), }, ]); }); - it('should keep all day appointment on its calendar day when offset is a full day', () => { + it('should not compensate a full-day view offset on all day appointments', () => { expect(expandAllDayRegularPanel([ { allDay: true, @@ -226,8 +226,8 @@ describe('expandAllDay', () => { })).toEqual([ { allDay: true, - startDateUTC: Date.UTC(2020, 0, 9, 0), - endDateUTC: Date.UTC(2020, 0, 10, 0), + startDateUTC: Date.UTC(2020, 0, 10, 0), + endDateUTC: Date.UTC(2020, 0, 11, 0), }, ]); }); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts index b7b4ba073578..cf76bc3b07b7 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/generate_view_model/steps/expand_all_day.ts @@ -80,7 +80,7 @@ interface ExpandAllDayRegularPanelOptions { const expandAllDayRegularPanelEntity = >( entity: T, - { startDayHour, viewOffsetMs, ignoreAllDayHours }: ExpandAllDayRegularPanelOptions, + { startDayHour, ignoreAllDayHours }: ExpandAllDayRegularPanelOptions, ): T => { if (!entity.allDay) { return entity; @@ -93,8 +93,8 @@ const expandAllDayRegularPanelEntity =