diff --git a/packages/devextreme/js/__internal/ui/calendar/m_calendar.range.selection.strategy.ts b/packages/devextreme/js/__internal/ui/calendar/m_calendar.range.selection.strategy.ts index b863141abbf8..123d2d7c90f1 100644 --- a/packages/devextreme/js/__internal/ui/calendar/m_calendar.range.selection.strategy.ts +++ b/packages/devextreme/js/__internal/ui/calendar/m_calendar.range.selection.strategy.ts @@ -117,8 +117,8 @@ class CalendarRangeSelectionStrategy extends CalendarSelectionStrategy { const { currentDate, viewsCount } = this.calendar.option(); const isAdditionalViewDate = this.calendar._isAdditionalViewDate(currentDate); - const firstDateInViews = dateUtils.getFirstMonthDate(dateUtils.addDateInterval(currentDate, 'month', isAdditionalViewDate ? -2 : -1)); - const lastDateInViews = dateUtils.getLastMonthDate(dateUtils.addDateInterval(currentDate, 'month', isAdditionalViewDate ? 1 : viewsCount)); + const firstDateInViews = dateUtils.getFirstMonthDate(currentDate, isAdditionalViewDate ? -2 : -1); + const lastDateInViews = dateUtils.getLastMonthDate(currentDate, isAdditionalViewDate ? 1 : viewsCount); // @ts-expect-error const rangeStartDate = new Date(Math.max(firstDateInViews, startDate)); diff --git a/packages/devextreme/js/core/utils/date.js b/packages/devextreme/js/core/utils/date.js index 7aa54270e76d..958cd93846dc 100644 --- a/packages/devextreme/js/core/utils/date.js +++ b/packages/devextreme/js/core/utils/date.js @@ -466,14 +466,24 @@ const getShortDateFormat = function() { return 'yyyy/MM/dd'; }; -const getFirstMonthDate = function(date) { +const getFirstMonthDate = function(date, offset = 0) { if(!isDefined(date)) return; - return createDateWithFullYear(date.getFullYear(), date.getMonth(), 1); + + const currentDate = new Date(date.getTime()); + const month = currentDate.getMonth() + offset; + currentDate.setMonth(month); + + return createDateWithFullYear(currentDate.getFullYear(), month, 1); }; -const getLastMonthDate = function(date) { +const getLastMonthDate = function(date, offset = 0) { if(!isDefined(date)) return; - return createDateWithFullYear(date.getFullYear(), date.getMonth() + 1, 0); + + const currentDate = new Date(date.getTime()); + const month = currentDate.getMonth() + offset; + currentDate.setMonth(month); + + return createDateWithFullYear(currentDate.getFullYear(), month + 1, 0); }; function getFirstWeekDate(date, firstDayOfWeek) { diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.date.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.date.tests.js index 7e96f2a3a6db..08c490186236 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.date.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.date.tests.js @@ -107,6 +107,106 @@ QUnit.test('getIntervalByString second', function(assert) { assert.deepEqual(this.getDateIntervalByString('second'), { seconds: 1 }); }); +QUnit.module('getFirstMonthDate', () => { + QUnit.test('should return same month first date when offset is not provided', function(assert) { + const newDate = dateUtils.getFirstMonthDate(new Date(2025, 11, 15)); + + assert.deepEqual(newDate, new Date(2025, 11, 1)); + }); + + QUnit.test('should return same month first date when offset is 0', function(assert) { + const newDate = dateUtils.getFirstMonthDate(new Date(2025, 11, 15), 0); + + assert.deepEqual(newDate, new Date(2025, 11, 1)); + }); + + QUnit.test('should decrease month correctly', function(assert) { + const newDate = dateUtils.getFirstMonthDate(new Date(2025, 11, 31), -1); + + assert.deepEqual(newDate, new Date(2025, 10, 1)); + }); + + QUnit.test('should increase month correctly', function(assert) { + const newDate = dateUtils.getFirstMonthDate(new Date(2025, 8, 30), 1); + + assert.deepEqual(newDate, new Date(2025, 9, 1)); + }); + + QUnit.test('should decrease month correctly when offset is less than -1', function(assert) { + const newDate = dateUtils.getFirstMonthDate(new Date(2025, 11, 15), -2); + + assert.deepEqual(newDate, new Date(2025, 9, 1)); + }); + + QUnit.test('should increase month correctly when offset is greater than 1', function(assert) { + const newDate = dateUtils.getFirstMonthDate(new Date(2025, 8, 15), 2); + + assert.deepEqual(newDate, new Date(2025, 10, 1)); + }); + + QUnit.test('should assign correct new date when current year is increased', function(assert) { + const newDate = dateUtils.getFirstMonthDate(new Date(2025, 11, 15), 1); + + assert.deepEqual(newDate, new Date(2026, 0, 1)); + }); + + QUnit.test('should assign correct new date when current year is decreased', function(assert) { + const newDate = dateUtils.getFirstMonthDate(new Date(2025, 0, 1), -1); + + assert.deepEqual(newDate, new Date(2024, 11, 1)); + }); +}); + +QUnit.module('getLastMonthDate', () => { + QUnit.test('should return same month last date when offset is not provided', function(assert) { + const newDate = dateUtils.getLastMonthDate(new Date(2025, 11, 15)); + + assert.deepEqual(newDate, new Date(2025, 11, 31)); + }); + + QUnit.test('should return same month last date when offset is 0', function(assert) { + const newDate = dateUtils.getLastMonthDate(new Date(2025, 11, 15), 0); + + assert.deepEqual(newDate, new Date(2025, 11, 31)); + }); + + QUnit.test('should decrease month correctly', function(assert) { + const newDate = dateUtils.getLastMonthDate(new Date(2025, 11, 31), -1); + + assert.deepEqual(newDate, new Date(2025, 10, 30)); + }); + + QUnit.test('should increase month correctly', function(assert) { + const newDate = dateUtils.getLastMonthDate(new Date(2025, 8, 30), 1); + + assert.deepEqual(newDate, new Date(2025, 9, 31)); + }); + + QUnit.test('should decrease month correctly when offset is less than -1', function(assert) { + const newDate = dateUtils.getLastMonthDate(new Date(2025, 11, 15), -2); + + assert.deepEqual(newDate, new Date(2025, 9, 31)); + }); + + QUnit.test('should increase month correctly when offset is greater than 1', function(assert) { + const newDate = dateUtils.getLastMonthDate(new Date(2025, 8, 15), 2); + + assert.deepEqual(newDate, new Date(2025, 10, 30)); + }); + + QUnit.test('should assign correct new date when current year is increased', function(assert) { + const newDate = dateUtils.getLastMonthDate(new Date(2025, 11, 15), 1); + + assert.deepEqual(newDate, new Date(2026, 0, 31)); + }); + + QUnit.test('should assign correct new date when current year is decreased', function(assert) { + const newDate = dateUtils.getLastMonthDate(new Date(2025, 0, 1), -1); + + assert.deepEqual(newDate, new Date(2024, 11, 31)); + }); +}); + QUnit.test('add negative Interval number', function(assert) { // arrange, act const newNumber = dateUtils.addInterval(11, 5, true); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js index 04426de106f2..16ca3bfe4f7d 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js @@ -2151,6 +2151,24 @@ QUnit.module('Options', { assert.ok($cell.hasClass(CALENDAR_CELL_IN_RANGE_CLASS)); }); + + QUnit.test(`Cells between startDate and endDate should have ${CALENDAR_CELL_IN_RANGE_CLASS} class even after currentDate runtime change (T1253076)`, function(assert) { + this.reinit({ + value: ['2025/01/01', '2025/12/31'], + selectionMode: 'range', + viewsCount: 2, + }); + + this.calendar.option('currentDate', new Date('2025-12-31')); + + const $prevButton = $(this.$element.find(toSelector(CALENDAR_NAVIGATOR_PREVIOUS_VIEW_CLASS))); + $prevButton.trigger('dxclick'); + + const $cell = $(getCurrentViewInstance(this.calendar).$element().find('*[data-value="2025/11/01"]')); + + assert.ok($cell.hasClass(CALENDAR_CELL_IN_RANGE_CLASS), 'cell is highlighted'); + }); + QUnit.test('Should reselect startDate and clear endDate on click when both value are defined', function(assert) { const expectedValue = [new Date('2023/01/11'), null]; const $cell = $(getCurrentViewInstance(this.calendar).$element().find('*[data-value="2023/01/11"]'));