Skip to content

Commit

Permalink
fix(scheduler-core): process DST correctly (#3136)
Browse files Browse the repository at this point in the history
  • Loading branch information
AryamnovEugeniy committed Oct 22, 2020
1 parent c414927 commit e0e3b99
Show file tree
Hide file tree
Showing 21 changed files with 129 additions and 41 deletions.
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

0 comments on commit e0e3b99

Please sign in to comment.