Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(scheduler-core): process DST correctly #3136

Merged
merged 12 commits into from
Oct 22, 2020
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,14 @@
"build:site:watch": "npx nodemon --exec \"yarn build:site:docs\" --watch \"packages/dx-react-grid-demos/dist/\" --watch \"packages/dx-react-chart-demos/dist/\" --watch \"packages/dx-react-scheduler-demos/dist/\" --watch \"packages/*/docs/\" --watch \"packages/*/demos/\" --watch \"gulpfile.js\" --ext md,js",
"lint": "lerna run lint --ignore @devexpress/dx-vue-*",
"lint:ci": "lerna run lint:ci",
"test": "lerna run test --ignore @devexpress/dx-vue-* && yarn test:bs4",
"test:ci": "yarn update-api:ci && lerna run test --ignore @devexpress/dx-vue-* -- -- --maxWorkers=1 --max-old-space-size=4096 && yarn test:bs4",
"test": "lerna run test --ignore @devexpress/dx-vue-* && yarn test:bs4 && yarn test:scheduler:pacific",
"test:ci": "yarn update-api:ci && lerna run test --ignore @devexpress/dx-vue-* -- -- --maxWorkers=1 --max-old-space-size=4096 && yarn test:bs4 && yarn test:scheduler:pacific",
"testcafe:ci": "start-server-and-test start-demo-servers \"3002|3004|3005\" testcafe:ci:start",
"testcafe:ci:start": "testcafe -q",
"testcafe:local": "start-server-and-test start-demo-servers \"3002|3004|3005\" testcafe:local:start",
"testcafe:local:start": "testcafe chrome ./**/*.testcafe.js --skip-js-errors",
"test:bs4": "jest packages/dx-react-bootstrap4 --ci --silent --runInBand --bail",
"test:scheduler:pacific": "TZ=US/Pacific jest packages/dx-scheduler-core --ci --silent --runInBand --bail",
"test:watch": "jest --watch",
"publish:prepare": "yarn && node ./scripts/prepare-commit.js",
"publish:npm": "yarn && node ./scripts/publish-npm.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import { GRID_TREE_NODE_TYPE } from './constants';
import {
customTreeRowsWithMeta,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { changeColumnFilter } from './reducers';
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';

describe('FilteringState reducers', () => {
describe('#changeColumnFilter', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import {
changeColumnGrouping,
draftColumnGrouping,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import {
groupRowChecker,
groupRowLevelKeyGetter,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import { sortedRows } from './computeds';
import { Sorting } from '../../types';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import { ReadonlyObject } from '@devexpress/dx-core';
import { changeColumnSorting } from './reducers';
import { ColumnSortingState, ChangeSortingPayload } from '../../types';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import {
TABLE_BAND_TYPE, BAND_GROUP_CELL, BAND_HEADER_CELL, BAND_EMPTY_CELL, BAND_DUPLICATE_RENDER,
BAND_FILL_LEVEL_CELL,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import { TABLE_DATA_TYPE } from '../table/constants';
import { TABLE_REORDERING_TYPE } from './constants';
import {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import { changeColumnOrder } from './reducers';

describe('TableColumnReordering reducers', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import {
getColumnSizes,
isValidValue,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import {
changeTableColumnWidth,
draftTableColumnWidth,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import { TABLE_ADDED_TYPE, TABLE_EDIT_TYPE } from './constants';
import { TABLE_DATA_TYPE } from '../table/constants';
import { tableRowsWithEditing } from './computeds';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import { TABLE_DATA_TYPE } from '../table/constants';
import { FIXED_COLUMN_LEFT_SIDE, FIXED_COLUMN_RIGHT_SIDE, TABLE_FIXED_TYPE } from './constants';
import { getFixedColumnKeys, isFixedTableRow, calculateFixedColumnProps } from './helpers';
Expand Down
3 changes: 2 additions & 1 deletion packages/dx-grid-core/src/utils/merge-sort.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import mergeSort from './merge-sort';

describe('margeSort', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/dx-react-grid/src/components/table-layout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import * as React from 'react';
import { findDOMNode } from 'react-dom';
import { shallow, mount } from 'enzyme';
import * as Immutable from 'seamless-immutable';
// tslint:disable-next-line: import-name
import Immutable from 'seamless-immutable';
import {
getAnimations,
filterActiveAnimations,
Expand Down
1 change: 1 addition & 0 deletions packages/dx-scheduler-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
],
"scripts": {
"test": "jest",
"test:pacific": "TZ=US/Pacific jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"build": "rollup -c rollup.config.js",
Expand Down
48 changes: 48 additions & 0 deletions packages/dx-scheduler-core/src/plugins/common/computeds.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,54 @@ describe('#timeScale', () => {
expect(units[2].end.getHours()).toBe(23);
expect(units[2].end.getMinutes()).toBe(59);
});

it('should work correctly if current day contains DST change', () => {
const dateWithDSTChange = new Date('2020-11-01T00:00:00');
const units = timeScale(dateWithDSTChange, 0, 0, 4, 30);

expect(units.length).toBe(8);

expect(units[0].start.getHours()).toBe(0);
expect(units[0].start.getMinutes()).toBe(0);
expect(units[0].end.getHours()).toBe(0);
expect(units[0].end.getMinutes()).toBe(30);

expect(units[1].start.getHours()).toBe(0);
expect(units[1].start.getMinutes()).toBe(30);
expect(units[1].end.getHours()).toBe(1);
expect(units[1].end.getMinutes()).toBe(0);

expect(units[2].start.getHours()).toBe(1);
expect(units[2].start.getMinutes()).toBe(0);
expect(units[2].end.getHours()).toBe(1);
expect(units[2].end.getMinutes()).toBe(30);

expect(units[3].start.getHours()).toBe(1);
expect(units[3].start.getMinutes()).toBe(30);
expect(units[3].end.getHours()).toBe(2);
expect(units[3].end.getMinutes()).toBe(0);

expect(units[4].start.getHours()).toBe(2);
expect(units[4].start.getMinutes()).toBe(0);
expect(units[4].end.getHours()).toBe(2);
expect(units[4].end.getMinutes()).toBe(30);

expect(units[5].start.getHours()).toBe(2);
expect(units[5].start.getMinutes()).toBe(30);
expect(units[5].end.getHours()).toBe(3);
expect(units[5].end.getMinutes()).toBe(0);

expect(units[6].start.getHours()).toBe(3);
expect(units[6].start.getMinutes()).toBe(0);
expect(units[6].end.getHours()).toBe(3);
expect(units[6].end.getMinutes()).toBe(30);

expect(units[7].start.getHours()).toBe(3);
expect(units[7].start.getMinutes()).toBe(30);
expect(units[7].end.getHours()).toBe(4);
expect(units[7].end.getMinutes()).toBe(0);

});
});

describe('#availableViews', () => {
Expand Down
23 changes: 20 additions & 3 deletions packages/dx-scheduler-core/src/plugins/common/computeds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ export const dayScale: DayScaleFn = (
return result;
};

const containsDSTChange = (date: SchedulerDateTime) => {
const momentDate = moment(date);
momentDate.startOf('day');
const isStartDST = momentDate.isDST();

momentDate.endOf('day');
const isEndDst = momentDate.isDST();

return (isStartDST && !isEndDst) || (!isStartDST && isEndDst);
};

export const timeScale: TimeScaleFn = (
currentDate,
firstDayOfWeek,
Expand All @@ -43,10 +54,17 @@ export const timeScale: TimeScaleFn = (
const startDateOfView = firstDayOfWeek !== undefined
? calculateFirstDateOfWeek(currentDate, firstDayOfWeek, excludedDays)
: currentDate;
const left = moment(startDateOfView as Date)

const isDSTChange = containsDSTChange(startDateOfView as Date);
const validDate = moment(startDateOfView as Date);
if (isDSTChange) {
validDate.subtract(1, 'day');
}

const left = moment(validDate)
.startOf('day')
.add(startDayHour, 'hour');
const right = moment(startDateOfView as Date)
const right = moment(validDate)
.startOf('day')
.add(endDayHour, 'hour');

Expand Down Expand Up @@ -86,7 +104,6 @@ export const viewCellsData: ViewCellsDataFn = (
currentDate, firstDayOfWeek!, startDayHour, endDayHour, cellDuration, excludedDays,
);
const currentTime = moment(currTime as SchedulerDateTime);

return times.reduce((cellsAcc, time) => {
const start = moment(time.start);
const end = moment(time.end);
Expand Down
47 changes: 26 additions & 21 deletions packages/dx-scheduler-core/src/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
} from './utils';
import { addDateToKey } from '.';

const PACIFIC_TIMEZONE_OFFSET = 480;

describe('Utils', () => {
describe('#viewPredicate', () => {
it('should filter outside appointments', () => {
Expand Down Expand Up @@ -395,28 +397,31 @@ describe('Utils', () => {
expect(result[1].end.toString())
.toBe(moment(new Date('2019-04-26T23:00:00+0300')).toString());
});
it('should work with recurrence appointment with UNTIL set', () => {
const monthlyLeftBound = new Date('2019-04-1 00:00');
const monthlyRightBound = new Date('2019-05-30 00:00');
const appointment = {
start: moment(new Date('2019-04-9 00:00')),
end: moment(new Date('2019-04-9 23:59')),
rRule: 'FREQ=DAILY;UNTIL=20190410T000000Z',
};
const result = filterByViewBoundaries(appointment, monthlyLeftBound, monthlyRightBound);

expect(result).toHaveLength(2);

expect(result[0].start.toString())
.toBe(moment(new Date('2019-04-09 0:00')).toString());
expect(result[0].end.toString())
.toBe(moment(new Date('2019-04-09 23:59')).toString());

expect(result[1].start.toString())
.toBe(moment(new Date('2019-04-10 0:00')).toString());
expect(result[1].end.toString())
.toBe(moment(new Date('2019-04-10 23:59')).toString());
});
if ((new Date(2020, 2, 7)).getTimezoneOffset() !== PACIFIC_TIMEZONE_OFFSET) {
it('should work with recurrence appointment with UNTIL set', () => {
const monthlyLeftBound = new Date('2019-04-1 00:00');
const monthlyRightBound = new Date('2019-05-30 00:00');
const appointment = {
start: moment(new Date('2019-04-9 00:00')),
end: moment(new Date('2019-04-9 23:59')),
rRule: 'FREQ=DAILY;UNTIL=20190410T000000Z',
};
const result = filterByViewBoundaries(appointment, monthlyLeftBound, monthlyRightBound);

expect(result).toHaveLength(2);

expect(result[0].start.toString())
.toBe(moment(new Date('2019-04-09 0:00')).toString());
expect(result[0].end.toString())
.toBe(moment(new Date('2019-04-09 23:59')).toString());

expect(result[1].start.toString())
.toBe(moment(new Date('2019-04-10 0:00')).toString());
expect(result[1].end.toString())
.toBe(moment(new Date('2019-04-10 23:59')).toString());
});
}
});
describe('#getRRuleSetWithExDates', () => {
it('should create RRuleSet', () => {
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@devexpress/*":["packages/*/src"],
"*": ["node_modules", "packages"]
},
"esModuleInterop": true
},
"exclude": [
"node_modules",
Expand Down