From 878088d5a1a5c28d78f8d1fb4f90a916648950e7 Mon Sep 17 00:00:00 2001 From: Vladimir Bushmanov Date: Mon, 13 Oct 2025 04:02:52 -0500 Subject: [PATCH 1/3] Scheduler: fix recurrence appointment display date according DST (T1305659) --- .../__snapshots__/recurrence.DST.test.ts.snap | 46 ++ .../__tests__/recurrence.DST.test.ts | 97 +++++ ...appointment_recurrence_occurrences.test.ts | 408 ++++++++++-------- .../get_appointment_recurrence_occurrences.ts | 30 +- 4 files changed, 380 insertions(+), 201 deletions(-) create mode 100644 packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap create mode 100644 packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap b/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap new file mode 100644 index 000000000000..30f24cb98c7d --- /dev/null +++ b/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Recurrence appointments should change dates according to DST in target (Belgrade) and appointment timezones (T1305659) 1`] = ` +[ + "2:00 PM - 3:00 PM", + "1:00 PM - 2:00 PM", + "1:00 PM - 2:00 PM", + "1:00 PM - 2:00 PM", + "2:00 PM - 3:00 PM", + "2:00 PM - 3:00 PM", + "2:00 PM - 3:00 PM", + "1:00 PM - 2:00 PM", + "1:00 PM - 2:00 PM", + "1:00 PM - 2:00 PM", + "2:00 PM - 3:00 PM", + "2:00 PM - 3:00 PM", +] +`; + +exports[`Recurrence appointments should change dates according to DST in target (Chicago) and appointment timezones (T1305659) 1`] = ` +[ + "7:00 AM - 8:00 AM", + "7:00 AM - 8:00 AM", + "7:00 AM - 8:00 AM", + "7:00 AM - 8:00 AM", + "7:00 AM - 8:00 AM", + "7:00 AM - 8:00 AM", +] +`; + +exports[`Recurrence appointments should change dates according to DST in target (Sydney) and appointment timezones (T1305659) 1`] = ` +[ + "12:00 AM - 1:00 AM", + "12:00 AM - 1:00 AM", + "11:00 PM - 12:00 AM", + "11:00 PM - 12:00 AM", + "10:00 PM - 11:00 PM", + "10:00 PM - 11:00 PM", + "10:00 PM - 11:00 PM", + "10:00 PM - 11:00 PM", + "11:00 PM - 12:00 AM", + "11:00 PM - 12:00 AM", + "11:00 PM - 12:00 AM", + "12:00 AM - 1:00 AM", +] +`; diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts b/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts new file mode 100644 index 000000000000..c3d0a42a1cec --- /dev/null +++ b/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts @@ -0,0 +1,97 @@ +import { + describe, expect, it, +} from '@jest/globals'; + +import { createScheduler } from './__mock__/create_scheduler'; +import { setupSchedulerTestEnvironment } from './__mock__/m_mock_scheduler'; + +const ChicagoDST = [new Date(2025, 2, 8), new Date(2025, 10, 1)]; // +1, -1 +const SydneyDST = [new Date(2025, 3, 7), new Date(2025, 9, 4)]; // -1, +1 +const BelgradeDST = [new Date(2025, 2, 29), new Date(2025, 9, 25)]; // +1, -1 +const dailyAppointment = { + startDate: new Date(2025, 0, 7, 7), + endDate: new Date(2025, 0, 7, 8), + startDateTimeZone: 'America/Chicago', + endDateTimeZone: 'America/Chicago', + recurrenceRule: 'FREQ=DAILY', +}; +const views = [{ type: 'day', intervalCount: 3 }]; + +/* + * NOTE: + * display date = source date - source time zone offset + target time zone offset + * + * Chicago: UTC-6h, UTC-5h, UTC-6h + * Sydney: UTC+11h, UTC+10h, UTC+11h + * Belgrade: UTC+1h, UTC+2h, UTC+1h + * + * Chicago to Chicago: should keep the same display date + * Chicago to Sydney: +17h, +16h, +15h, +16h, +17h + * Chicago to Belgrade: +7h, +6h, +7h, +6h, +7h + */ +describe('Recurrence appointments', () => { + it('should change dates according to DST in target (Chicago) and appointment timezones (T1305659)', async () => { + setupSchedulerTestEnvironment(); + const { POM, scheduler } = await createScheduler({ + timeZone: 'America/Chicago', + dataSource: [dailyAppointment], + views, + currentView: 'day', + currentDate: ChicagoDST[0], + }); + + const getDates = () => POM.getAppointments().map((appointment) => appointment.getDisplayDate()); + + const dates = getDates(); + scheduler.option('currentDate', ChicagoDST[1]); + dates.push(...getDates()); + + expect(dates).toMatchSnapshot(); + }); + + it('should change dates according to DST in target (Sydney) and appointment timezones (T1305659)', async () => { + setupSchedulerTestEnvironment(); + const { POM, scheduler } = await createScheduler({ + timeZone: 'Australia/Sydney', + dataSource: [dailyAppointment], + views, + currentView: 'day', + currentDate: ChicagoDST[0], + }); + + const getDates = () => POM.getAppointments().map((appointment) => appointment.getDisplayDate()); + + const dates = getDates(); + scheduler.option('currentDate', SydneyDST[0]); + dates.push(...getDates()); + scheduler.option('currentDate', SydneyDST[1]); + dates.push(...getDates()); + scheduler.option('currentDate', ChicagoDST[1]); + dates.push(...getDates()); + + expect(dates).toMatchSnapshot(); + }); + + it('should change dates according to DST in target (Belgrade) and appointment timezones (T1305659)', async () => { + setupSchedulerTestEnvironment(); + const { POM, scheduler } = await createScheduler({ + timeZone: 'Europe/Belgrade', + dataSource: [dailyAppointment], + views, + currentView: 'day', + currentDate: ChicagoDST[0], + }); + + const getDates = () => POM.getAppointments().map((appointment) => appointment.getDisplayDate()); + + const dates = getDates(); + scheduler.option('currentDate', BelgradeDST[0]); + dates.push(...getDates()); + scheduler.option('currentDate', BelgradeDST[1]); + dates.push(...getDates()); + scheduler.option('currentDate', ChicagoDST[1]); + dates.push(...getDates()); + + expect(dates).toMatchSnapshot(); + }); +}); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/filtration/utils/split_by_recurrence/get_appointment_recurrence_occurrences.test.ts b/packages/devextreme/js/__internal/scheduler/view_model/filtration/utils/split_by_recurrence/get_appointment_recurrence_occurrences.test.ts index 525c567fd505..a2bb98cd56c9 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/filtration/utils/split_by_recurrence/get_appointment_recurrence_occurrences.test.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/filtration/utils/split_by_recurrence/get_appointment_recurrence_occurrences.test.ts @@ -90,205 +90,255 @@ describe('getAppointmentRecurrenceOccurrences', () => { }); }); - it('should return appointment occurrences for appointment starts before view interval', () => { - const appointment: any = { - source: { - startDate: Date.UTC(2000, 0, 9, 10), - endDate: Date.UTC(2000, 0, 9, 11), - }, - recurrenceRule: 'FREQ=DAILY', - hasRecurrenceRule: true, - }; - expect(getAppointmentRecurrenceOccurrences( - appointment, - options, - )).toEqual([ - { - ...appointment, + describe('with recurrence rule', () => { + it('should return the same source in any timezone if appointment timezone set', () => { + const appointment: any = { source: { - startDate: Date.UTC(2000, 0, 10, 10), - endDate: Date.UTC(2000, 0, 10, 11), + startDate: Date.UTC(2025, 0, 7, 1), + endDate: Date.UTC(2025, 0, 7, 2), }, - startDateUTC: Date.UTC(2000, 0, 10, 10) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 10, 11) - HOUR_MS * 8, - }, - { - ...appointment, + startDateTimeZone: 'America/Chicago', + endDateTimeZone: 'America/Chicago', + recurrenceRule: 'FREQ=DAILY', + hasRecurrenceRule: true, + }; + const getSources = (date: number, timeZone: string) => { + const dateCopy = new Date(date); + return getAppointmentRecurrenceOccurrences( + appointment, + { + interval: { + min: dateCopy.setDate(dateCopy.getDate() - 2), + max: dateCopy.setDate(dateCopy.getDate() + 2), + }, + timeZone, + }, + ).map((item) => ({ + startDate: new Date(item.source.startDate).toUTCString(), + endDate: new Date(item.source.endDate).toUTCString(), + })); + }; + + const sourcesChicago = [ + ...getSources(Date.UTC(2025, 1), 'America/Chicago'), + ...getSources(Date.UTC(2025, 6), 'America/Chicago'), + ...getSources(Date.UTC(2025, 11), 'America/Chicago'), + ]; + const sourcesSydney = [ + ...getSources(Date.UTC(2025, 1) + 24 * HOUR_MS, 'Australia/Sydney'), + ...getSources(Date.UTC(2025, 6) + 24 * HOUR_MS, 'Australia/Sydney'), + ...getSources(Date.UTC(2025, 11) + 24 * HOUR_MS, 'Australia/Sydney'), + ]; + const sourcesBelgrade = [ + ...getSources(Date.UTC(2025, 1) + 24 * HOUR_MS, 'Europe/Belgrade'), + ...getSources(Date.UTC(2025, 6) + 24 * HOUR_MS, 'Europe/Belgrade'), + ...getSources(Date.UTC(2025, 11) + 24 * HOUR_MS, 'Europe/Belgrade'), + ]; + + expect(sourcesChicago).toEqual(sourcesSydney); + expect(sourcesChicago).toEqual(sourcesBelgrade); + }); + + it('should return appointment occurrences for appointment starts before view interval', () => { + const appointment: any = { source: { - startDate: Date.UTC(2000, 0, 11, 10), - endDate: Date.UTC(2000, 0, 11, 11), + startDate: Date.UTC(2000, 0, 9, 10), + endDate: Date.UTC(2000, 0, 9, 11), }, - startDateUTC: Date.UTC(2000, 0, 11, 10) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 11, 11) - HOUR_MS * 8, - }, - { - ...appointment, - source: { - startDate: Date.UTC(2000, 0, 12, 10), - endDate: Date.UTC(2000, 0, 12, 11), + recurrenceRule: 'FREQ=DAILY', + hasRecurrenceRule: true, + }; + expect(getAppointmentRecurrenceOccurrences( + appointment, + options, + )).toEqual([ + { + ...appointment, + source: { + startDate: Date.UTC(2000, 0, 10, 10), + endDate: Date.UTC(2000, 0, 10, 11), + }, + startDateUTC: Date.UTC(2000, 0, 10, 10) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 10, 11) - HOUR_MS * 8, }, - startDateUTC: Date.UTC(2000, 0, 12, 10) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 12, 11) - HOUR_MS * 8, - }, - { - ...appointment, + { + ...appointment, + source: { + startDate: Date.UTC(2000, 0, 11, 10), + endDate: Date.UTC(2000, 0, 11, 11), + }, + startDateUTC: Date.UTC(2000, 0, 11, 10) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 11, 11) - HOUR_MS * 8, + }, + { + ...appointment, + source: { + startDate: Date.UTC(2000, 0, 12, 10), + endDate: Date.UTC(2000, 0, 12, 11), + }, + startDateUTC: Date.UTC(2000, 0, 12, 10) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 12, 11) - HOUR_MS * 8, + }, + { + ...appointment, + source: { + startDate: Date.UTC(2000, 0, 13, 10), + endDate: Date.UTC(2000, 0, 13, 11), + }, + startDateUTC: Date.UTC(2000, 0, 13, 10) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 13, 11) - HOUR_MS * 8, + }, + { + ...appointment, + source: { + startDate: Date.UTC(2000, 0, 14, 10), + endDate: Date.UTC(2000, 0, 14, 11), + }, + startDateUTC: Date.UTC(2000, 0, 14, 10) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 14, 11) - HOUR_MS * 8, + }, + ]); + }); + + it('should return appointment occurrences for appointment starts inside view interval', () => { + const appointment: any = { source: { startDate: Date.UTC(2000, 0, 13, 10), endDate: Date.UTC(2000, 0, 13, 11), }, - startDateUTC: Date.UTC(2000, 0, 13, 10) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 13, 11) - HOUR_MS * 8, - }, - { - ...appointment, + recurrenceRule: 'FREQ=DAILY', + hasRecurrenceRule: true, + }; + expect(getAppointmentRecurrenceOccurrences( + appointment, + options, + )).toEqual([ + { + ...appointment, + startDateUTC: Date.UTC(2000, 0, 13, 10) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 13, 11) - HOUR_MS * 8, + }, + { + ...appointment, + source: { + startDate: Date.UTC(2000, 0, 14, 10), + endDate: Date.UTC(2000, 0, 14, 11), + }, + startDateUTC: Date.UTC(2000, 0, 14, 10) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 14, 11) - HOUR_MS * 8, + }, + ]); + }); + + it('should return appointment occurrences for appointment starts after view interval', () => { + const appointment: any = { source: { - startDate: Date.UTC(2000, 0, 14, 10), - endDate: Date.UTC(2000, 0, 14, 11), + startDate: Date.UTC(2000, 0, 20, 10), + endDate: Date.UTC(2000, 0, 13, 11), }, - startDateUTC: Date.UTC(2000, 0, 14, 10) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 14, 11) - HOUR_MS * 8, - }, - ]); - }); + recurrenceRule: 'FREQ=DAILY', + hasRecurrenceRule: true, + }; + expect(getAppointmentRecurrenceOccurrences( + appointment, + options, + )).toEqual([]); + }); - it('should return appointment occurrences for appointment starts inside view interval', () => { - const appointment: any = { - source: { - startDate: Date.UTC(2000, 0, 13, 10), - endDate: Date.UTC(2000, 0, 13, 11), - }, - recurrenceRule: 'FREQ=DAILY', - hasRecurrenceRule: true, - }; - expect(getAppointmentRecurrenceOccurrences( - appointment, - options, - )).toEqual([ - { - ...appointment, - startDateUTC: Date.UTC(2000, 0, 13, 10) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 13, 11) - HOUR_MS * 8, - }, - { + it.each([ + { title: 'appointment', delta: 0 }, + { title: 'appointment occurrence', delta: -20 }, + ])('should return $title is hagging view interval', ({ delta }) => { + const appointment: any = { + source: { + startDate: Date.UTC(2000, 0, 9 + delta), + endDate: Date.UTC(2000, 0, 16 + delta), + }, + recurrenceRule: 'FREQ=DAILY;INTERVAL=20', + hasRecurrenceRule: true, + }; + expect(getAppointmentRecurrenceOccurrences( + appointment, + options, + )).toEqual([{ ...appointment, source: { - startDate: Date.UTC(2000, 0, 14, 10), - endDate: Date.UTC(2000, 0, 14, 11), + startDate: Date.UTC(2000, 0, 9), + endDate: Date.UTC(2000, 0, 16), }, - startDateUTC: Date.UTC(2000, 0, 14, 10) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 14, 11) - HOUR_MS * 8, - }, - ]); - }); - - it('should return appointment occurrences for appointment starts after view interval', () => { - const appointment: any = { - source: { - startDate: Date.UTC(2000, 0, 20, 10), - endDate: Date.UTC(2000, 0, 13, 11), - }, - recurrenceRule: 'FREQ=DAILY', - hasRecurrenceRule: true, - }; - expect(getAppointmentRecurrenceOccurrences( - appointment, - options, - )).toEqual([]); - }); - - it.each([ - { title: 'appointment', delta: 0 }, - { title: 'appointment occurrence', delta: -20 }, - ])('should return $title is hagging view interval', ({ delta }) => { - const appointment: any = { - source: { - startDate: Date.UTC(2000, 0, 9 + delta), - endDate: Date.UTC(2000, 0, 16 + delta), - }, - recurrenceRule: 'FREQ=DAILY;INTERVAL=20', - hasRecurrenceRule: true, - }; - expect(getAppointmentRecurrenceOccurrences( - appointment, - options, - )).toEqual([{ - ...appointment, - source: { - startDate: Date.UTC(2000, 0, 9), - endDate: Date.UTC(2000, 0, 16), - }, - startDateUTC: Date.UTC(2000, 0, 9) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 16) - HOUR_MS * 8, - }]); - }); - - it.each([ - { title: 'appointment', delta: 0 }, - { title: 'appointment occurrence', delta: -10 }, - ])('should return $title starts before view interval', ({ delta }) => { - const appointment: any = { - source: { - startDate: Date.UTC(2000, 0, 9 + delta, 20), - endDate: Date.UTC(2000, 0, 10 + delta, 10), - }, - recurrenceRule: 'FREQ=DAILY;INTERVAL=10', - hasRecurrenceRule: true, - }; - expect(getAppointmentRecurrenceOccurrences( - appointment, - options, - )).toEqual([{ - ...appointment, - source: { - startDate: Date.UTC(2000, 0, 9, 20), - endDate: Date.UTC(2000, 0, 10, 10), - }, - startDateUTC: Date.UTC(2000, 0, 9, 20) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 10, 10) - HOUR_MS * 8, - }]); - }); + startDateUTC: Date.UTC(2000, 0, 9) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 16) - HOUR_MS * 8, + }]); + }); - it('should return appointment occurrences for appointment with exceptions', () => { - const exception1 = getAsciiStringByDate( - new Date(Date.UTC(2000, 0, 11, 10)), - ); - const exception2 = getAsciiStringByDate( - new Date(Date.UTC(2000, 0, 12, 10)), - ); - const exception3 = getAsciiStringByDate( - new Date(Date.UTC(2000, 0, 13, 10)), - ); - const appointment: any = { - source: { - startDate: Date.UTC(2000, 0, 9, 10), - endDate: Date.UTC(2000, 0, 9, 11), - }, - recurrenceException: `${exception1},${exception2},${exception3}`, - recurrenceRule: 'FREQ=DAILY', - hasRecurrenceRule: true, - }; - expect(getAppointmentRecurrenceOccurrences( - appointment, - options, - )).toEqual([ - { - ...appointment, + it.each([ + { title: 'appointment', delta: 0 }, + { title: 'appointment occurrence', delta: -10 }, + ])('should return $title starts before view interval', ({ delta }) => { + const appointment: any = { source: { - startDate: Date.UTC(2000, 0, 10, 10), - endDate: Date.UTC(2000, 0, 10, 11), + startDate: Date.UTC(2000, 0, 9 + delta, 20), + endDate: Date.UTC(2000, 0, 10 + delta, 10), }, - startDateUTC: Date.UTC(2000, 0, 10, 10) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 10, 11) - HOUR_MS * 8, - }, - { + recurrenceRule: 'FREQ=DAILY;INTERVAL=10', + hasRecurrenceRule: true, + }; + expect(getAppointmentRecurrenceOccurrences( + appointment, + options, + )).toEqual([{ ...appointment, source: { - startDate: Date.UTC(2000, 0, 14, 10), - endDate: Date.UTC(2000, 0, 14, 11), + startDate: Date.UTC(2000, 0, 9, 20), + endDate: Date.UTC(2000, 0, 10, 10), + }, + startDateUTC: Date.UTC(2000, 0, 9, 20) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 10, 10) - HOUR_MS * 8, + }]); + }); + + it('should return appointment occurrences for appointment with exceptions', () => { + const exception1 = getAsciiStringByDate( + new Date(Date.UTC(2000, 0, 11, 10)), + ); + const exception2 = getAsciiStringByDate( + new Date(Date.UTC(2000, 0, 12, 10)), + ); + const exception3 = getAsciiStringByDate( + new Date(Date.UTC(2000, 0, 13, 10)), + ); + const appointment: any = { + source: { + startDate: Date.UTC(2000, 0, 9, 10), + endDate: Date.UTC(2000, 0, 9, 11), + }, + recurrenceException: `${exception1},${exception2},${exception3}`, + recurrenceRule: 'FREQ=DAILY', + hasRecurrenceRule: true, + }; + expect(getAppointmentRecurrenceOccurrences( + appointment, + options, + )).toEqual([ + { + ...appointment, + source: { + startDate: Date.UTC(2000, 0, 10, 10), + endDate: Date.UTC(2000, 0, 10, 11), + }, + startDateUTC: Date.UTC(2000, 0, 10, 10) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 10, 11) - HOUR_MS * 8, }, - startDateUTC: Date.UTC(2000, 0, 14, 10) - HOUR_MS * 8, - endDateUTC: Date.UTC(2000, 0, 14, 11) - HOUR_MS * 8, - }, - ]); + { + ...appointment, + source: { + startDate: Date.UTC(2000, 0, 14, 10), + endDate: Date.UTC(2000, 0, 14, 11), + }, + startDateUTC: Date.UTC(2000, 0, 14, 10) - HOUR_MS * 8, + endDateUTC: Date.UTC(2000, 0, 14, 11) - HOUR_MS * 8, + }, + ]); + }); }); }); diff --git a/packages/devextreme/js/__internal/scheduler/view_model/filtration/utils/split_by_recurrence/get_appointment_recurrence_occurrences.ts b/packages/devextreme/js/__internal/scheduler/view_model/filtration/utils/split_by_recurrence/get_appointment_recurrence_occurrences.ts index cda62fa7dba0..b69fdd86720a 100644 --- a/packages/devextreme/js/__internal/scheduler/view_model/filtration/utils/split_by_recurrence/get_appointment_recurrence_occurrences.ts +++ b/packages/devextreme/js/__internal/scheduler/view_model/filtration/utils/split_by_recurrence/get_appointment_recurrence_occurrences.ts @@ -44,20 +44,6 @@ const getUnreachableShift = ( } }; -const getDSTChanges = ( - targetTimeZoneChange: number, - appointmentTimeZoneChange: number, -): number => { - if (targetTimeZoneChange < 0 && appointmentTimeZoneChange < 0) { - return Math.min(targetTimeZoneChange, appointmentTimeZoneChange); - } - if (targetTimeZoneChange > 0 && appointmentTimeZoneChange > 0) { - return Math.max(targetTimeZoneChange, appointmentTimeZoneChange); - } - - return targetTimeZoneChange + appointmentTimeZoneChange; -}; - export const getAppointmentRecurrenceOccurrences = ( appointment: T, { @@ -109,17 +95,17 @@ export const getAppointmentRecurrenceOccurrences = Date: Mon, 13 Oct 2025 11:33:46 +0200 Subject: [PATCH 2/3] fix --- .../__snapshots__/recurrence.DST.test.ts.snap | 19 --------- .../__tests__/recurrence.DST.test.ts | 42 ++++++++++++------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap b/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap index 30f24cb98c7d..d2e4095b5203 100644 --- a/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap +++ b/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap @@ -4,15 +4,8 @@ exports[`Recurrence appointments should change dates according to DST in target [ "2:00 PM - 3:00 PM", "1:00 PM - 2:00 PM", - "1:00 PM - 2:00 PM", - "1:00 PM - 2:00 PM", - "2:00 PM - 3:00 PM", "2:00 PM - 3:00 PM", - "2:00 PM - 3:00 PM", - "1:00 PM - 2:00 PM", "1:00 PM - 2:00 PM", - "1:00 PM - 2:00 PM", - "2:00 PM - 3:00 PM", "2:00 PM - 3:00 PM", ] `; @@ -20,26 +13,14 @@ exports[`Recurrence appointments should change dates according to DST in target exports[`Recurrence appointments should change dates according to DST in target (Chicago) and appointment timezones (T1305659) 1`] = ` [ "7:00 AM - 8:00 AM", - "7:00 AM - 8:00 AM", - "7:00 AM - 8:00 AM", - "7:00 AM - 8:00 AM", - "7:00 AM - 8:00 AM", - "7:00 AM - 8:00 AM", ] `; exports[`Recurrence appointments should change dates according to DST in target (Sydney) and appointment timezones (T1305659) 1`] = ` [ - "12:00 AM - 1:00 AM", "12:00 AM - 1:00 AM", "11:00 PM - 12:00 AM", - "11:00 PM - 12:00 AM", - "10:00 PM - 11:00 PM", "10:00 PM - 11:00 PM", - "10:00 PM - 11:00 PM", - "10:00 PM - 11:00 PM", - "11:00 PM - 12:00 AM", - "11:00 PM - 12:00 AM", "11:00 PM - 12:00 AM", "12:00 AM - 1:00 AM", ] diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts b/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts index c3d0a42a1cec..2aeb08af8037 100644 --- a/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts +++ b/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts @@ -4,18 +4,30 @@ import { import { createScheduler } from './__mock__/create_scheduler'; import { setupSchedulerTestEnvironment } from './__mock__/m_mock_scheduler'; +import type { AppointmentModel } from './__mock__/model/appointment'; -const ChicagoDST = [new Date(2025, 2, 8), new Date(2025, 10, 1)]; // +1, -1 -const SydneyDST = [new Date(2025, 3, 7), new Date(2025, 9, 4)]; // -1, +1 -const BelgradeDST = [new Date(2025, 2, 29), new Date(2025, 9, 25)]; // +1, -1 +const ChicagoDST = [new Date('2025-03-08T00:00:00.000Z'), new Date('2025-11-01T00:00:00.000Z')]; // +1, -1 +const SydneyDST = [new Date('2025-04-07T00:00:00.000Z'), new Date('2025-10-04T00:00:00.000Z')]; // -1, +1 +const BelgradeDST = [new Date('2025-03-29T00:00:00.000Z'), new Date('2025-10-25T00:00:00.000Z')]; // +1, -1 const dailyAppointment = { - startDate: new Date(2025, 0, 7, 7), - endDate: new Date(2025, 0, 7, 8), + startDate: new Date('2025-01-07T13:00:00.000Z'), + endDate: new Date('2025-01-07T14:00:00.000Z'), startDateTimeZone: 'America/Chicago', endDateTimeZone: 'America/Chicago', recurrenceRule: 'FREQ=DAILY', }; -const views = [{ type: 'day', intervalCount: 3 }]; +const views = [{ type: 'week', intervalCount: 2 }]; + +const getDisplayDates = (appointments: AppointmentModel[]): string[] => appointments + .map((appointment) => appointment.getDisplayDate()); +const reduceDates = (texts: string[]): string[] => texts + .reduce((result, time) => { + if (result.at(-1) !== time) { + result.push(time); + } + + return result; + }, []); /* * NOTE: @@ -36,17 +48,17 @@ describe('Recurrence appointments', () => { timeZone: 'America/Chicago', dataSource: [dailyAppointment], views, - currentView: 'day', + currentView: 'week', currentDate: ChicagoDST[0], }); - const getDates = () => POM.getAppointments().map((appointment) => appointment.getDisplayDate()); + const getDates = () => getDisplayDates(POM.getAppointments()); const dates = getDates(); scheduler.option('currentDate', ChicagoDST[1]); dates.push(...getDates()); - expect(dates).toMatchSnapshot(); + expect(reduceDates(dates)).toMatchSnapshot(); }); it('should change dates according to DST in target (Sydney) and appointment timezones (T1305659)', async () => { @@ -55,11 +67,11 @@ describe('Recurrence appointments', () => { timeZone: 'Australia/Sydney', dataSource: [dailyAppointment], views, - currentView: 'day', + currentView: 'week', currentDate: ChicagoDST[0], }); - const getDates = () => POM.getAppointments().map((appointment) => appointment.getDisplayDate()); + const getDates = () => getDisplayDates(POM.getAppointments()); const dates = getDates(); scheduler.option('currentDate', SydneyDST[0]); @@ -69,7 +81,7 @@ describe('Recurrence appointments', () => { scheduler.option('currentDate', ChicagoDST[1]); dates.push(...getDates()); - expect(dates).toMatchSnapshot(); + expect(reduceDates(dates)).toMatchSnapshot(); }); it('should change dates according to DST in target (Belgrade) and appointment timezones (T1305659)', async () => { @@ -78,11 +90,11 @@ describe('Recurrence appointments', () => { timeZone: 'Europe/Belgrade', dataSource: [dailyAppointment], views, - currentView: 'day', + currentView: 'week', currentDate: ChicagoDST[0], }); - const getDates = () => POM.getAppointments().map((appointment) => appointment.getDisplayDate()); + const getDates = () => getDisplayDates(POM.getAppointments()); const dates = getDates(); scheduler.option('currentDate', BelgradeDST[0]); @@ -92,6 +104,6 @@ describe('Recurrence appointments', () => { scheduler.option('currentDate', ChicagoDST[1]); dates.push(...getDates()); - expect(dates).toMatchSnapshot(); + expect(reduceDates(dates)).toMatchSnapshot(); }); }); From ba007c53bddbf6707fe9e19abce598c5419bb1ef Mon Sep 17 00:00:00 2001 From: Vladimir Bushmanov Date: Mon, 13 Oct 2025 06:40:31 -0500 Subject: [PATCH 3/3] simplify --- .../__snapshots__/recurrence.DST.test.ts.snap | 27 ------------------- .../__tests__/recurrence.DST.test.ts | 20 +++++++++++--- 2 files changed, 17 insertions(+), 30 deletions(-) delete mode 100644 packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap b/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap deleted file mode 100644 index d2e4095b5203..000000000000 --- a/packages/devextreme/js/__internal/scheduler/__tests__/__snapshots__/recurrence.DST.test.ts.snap +++ /dev/null @@ -1,27 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Recurrence appointments should change dates according to DST in target (Belgrade) and appointment timezones (T1305659) 1`] = ` -[ - "2:00 PM - 3:00 PM", - "1:00 PM - 2:00 PM", - "2:00 PM - 3:00 PM", - "1:00 PM - 2:00 PM", - "2:00 PM - 3:00 PM", -] -`; - -exports[`Recurrence appointments should change dates according to DST in target (Chicago) and appointment timezones (T1305659) 1`] = ` -[ - "7:00 AM - 8:00 AM", -] -`; - -exports[`Recurrence appointments should change dates according to DST in target (Sydney) and appointment timezones (T1305659) 1`] = ` -[ - "12:00 AM - 1:00 AM", - "11:00 PM - 12:00 AM", - "10:00 PM - 11:00 PM", - "11:00 PM - 12:00 AM", - "12:00 AM - 1:00 AM", -] -`; diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts b/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts index 2aeb08af8037..a63657d82be1 100644 --- a/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts +++ b/packages/devextreme/js/__internal/scheduler/__tests__/recurrence.DST.test.ts @@ -58,7 +58,9 @@ describe('Recurrence appointments', () => { scheduler.option('currentDate', ChicagoDST[1]); dates.push(...getDates()); - expect(reduceDates(dates)).toMatchSnapshot(); + expect(reduceDates(dates)).toEqual([ + '7:00 AM - 8:00 AM', + ]); }); it('should change dates according to DST in target (Sydney) and appointment timezones (T1305659)', async () => { @@ -81,7 +83,13 @@ describe('Recurrence appointments', () => { scheduler.option('currentDate', ChicagoDST[1]); dates.push(...getDates()); - expect(reduceDates(dates)).toMatchSnapshot(); + expect(reduceDates(dates)).toEqual([ + '12:00 AM - 1:00 AM', + '11:00 PM - 12:00 AM', + '10:00 PM - 11:00 PM', + '11:00 PM - 12:00 AM', + '12:00 AM - 1:00 AM', + ]); }); it('should change dates according to DST in target (Belgrade) and appointment timezones (T1305659)', async () => { @@ -104,6 +112,12 @@ describe('Recurrence appointments', () => { scheduler.option('currentDate', ChicagoDST[1]); dates.push(...getDates()); - expect(reduceDates(dates)).toMatchSnapshot(); + expect(reduceDates(dates)).toEqual([ + '2:00 PM - 3:00 PM', + '1:00 PM - 2:00 PM', + '2:00 PM - 3:00 PM', + '1:00 PM - 2:00 PM', + '2:00 PM - 3:00 PM', + ]); }); });