DatePicker: Daylight Saving Time (#7599)

* date-table: fix daylight saving time highlight

* test: update date-picker range test
pull/7603/head
Jiewei Qian 2017-10-20 14:06:44 +11:00 committed by 杨奕
parent 8313016dae
commit af46f968ab
5 changed files with 121 additions and 95 deletions

View File

@ -30,7 +30,7 @@
</template> </template>
<script> <script>
import { getFirstDayOfMonth, getDayCountOfMonth, getWeekNumber, getStartDateOfMonth, DAY_DURATION, isDate } from '../util'; import { getFirstDayOfMonth, getDayCountOfMonth, getWeekNumber, getStartDateOfMonth, nextDate, isDate } from '../util';
import { hasClass } from 'element-ui/src/utils/dom'; import { hasClass } from 'element-ui/src/utils/dom';
import Locale from 'element-ui/src/mixins/locale'; import Locale from 'element-ui/src/mixins/locale';
@ -136,7 +136,7 @@
if (this.showWeekNumber) { if (this.showWeekNumber) {
if (!row[0]) { if (!row[0]) {
row[0] = { type: 'week', text: getWeekNumber(new Date(startDate.getTime() + DAY_DURATION * (i * 7 + 1))) }; row[0] = { type: 'week', text: getWeekNumber(nextDate(startDate, i * 7 + 1)) };
} }
} }
@ -149,7 +149,7 @@
cell.type = 'normal'; cell.type = 'normal';
const index = i * 7 + j; const index = i * 7 + j;
const time = startDate.getTime() + DAY_DURATION * (index - offset); const time = nextDate(startDate, index - offset).getTime();
cell.inRange = time >= clearHours(this.minDate) && time <= clearHours(this.maxDate); cell.inRange = time >= clearHours(this.minDate) && time <= clearHours(this.maxDate);
cell.start = this.minDate && time === clearHours(this.minDate); cell.start = this.minDate && time === clearHours(this.minDate);
cell.end = this.maxDate && time === clearHours(this.maxDate); cell.end = this.maxDate && time === clearHours(this.maxDate);
@ -289,22 +289,8 @@
}, },
getDateOfCell(row, column) { getDateOfCell(row, column) {
const startDate = this.startDate; const offsetFromStart = row * 7 + (column - (this.showWeekNumber ? 1 : 0)) - this.offsetDay;
return nextDate(this.startDate, offsetFromStart);
return new Date(startDate.getTime() + (row * 7 + (column - (this.showWeekNumber ? 1 : 0)) - this.offsetDay) * DAY_DURATION);
},
getCellByDate(date) {
const startDate = this.startDate;
const rows = this.rows;
const index = (date - startDate) / DAY_DURATION;
const row = rows[Math.floor(index / 7)];
if (this.showWeekNumber) {
return row[index % 7 + 1];
} else {
return row[index % 7];
}
}, },
isWeekActive(cell) { isWeekActive(cell) {
@ -343,7 +329,7 @@
const cell = row[j]; const cell = row[j];
const index = i * 7 + j + (this.showWeekNumber ? -1 : 0); const index = i * 7 + j + (this.showWeekNumber ? -1 : 0);
const time = startDate.getTime() + DAY_DURATION * (index - this.offsetDay); const time = nextDate(startDate, index - this.offsetDay).getTime();
cell.inRange = minDate && time >= clearHours(minDate) && time <= clearHours(maxDate); cell.inRange = minDate && time >= clearHours(minDate) && time <= clearHours(maxDate);
cell.start = minDate && time === clearHours(minDate.getTime()); cell.start = minDate && time === clearHours(minDate.getTime());

View File

@ -49,14 +49,13 @@
<script type="text/babel"> <script type="text/babel">
import Locale from 'element-ui/src/mixins/locale'; import Locale from 'element-ui/src/mixins/locale';
import { isDate, range, getDayCountOfMonth } from '../util'; import { isDate, range, getDayCountOfMonth, nextDate } from '../util';
import { hasClass } from 'element-ui/src/utils/dom'; import { hasClass } from 'element-ui/src/utils/dom';
const datesInMonth = (year, month) => { const datesInMonth = (year, month) => {
const numOfDays = getDayCountOfMonth(year, month); const numOfDays = getDayCountOfMonth(year, month);
const firstDay = new Date(year, month, 1); const firstDay = new Date(year, month, 1);
const ONE_DAY = 8.64e7; return range(numOfDays).map(n => nextDate(firstDay, n));
return range(numOfDays).map(n => new Date(firstDay.getTime() + n * ONE_DAY));
}; };
export default { export default {

View File

@ -45,15 +45,12 @@
<script type="text/babel"> <script type="text/babel">
import { hasClass } from 'element-ui/src/utils/dom'; import { hasClass } from 'element-ui/src/utils/dom';
import { isDate, range } from '../util'; import { isDate, range, nextDate, getDayCountOfYear } from '../util';
const isLeapYear = year => year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0);
const datesInYear = year => { const datesInYear = year => {
const numOfDays = isLeapYear(year) ? 366 : 365; const numOfDays = getDayCountOfYear(year);
const firstDay = new Date(year, 0, 1); const firstDay = new Date(year, 0, 1);
const ONE_DAY = 8.64e7; return range(numOfDays).map(n => nextDate(firstDay, n));
return range(numOfDays).map(n => new Date(firstDay.getTime() + ONE_DAY));
}; };
export default { export default {

View File

@ -61,25 +61,37 @@ export const getDayCountOfMonth = function(year, month) {
return 31; return 31;
}; };
export const getDayCountOfYear = function(year) {
const isLeapYear = year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0);
return isLeapYear ? 366 : 365;
};
export const getFirstDayOfMonth = function(date) { export const getFirstDayOfMonth = function(date) {
const temp = new Date(date.getTime()); const temp = new Date(date.getTime());
temp.setDate(1); temp.setDate(1);
return temp.getDay(); return temp.getDay();
}; };
export const DAY_DURATION = 86400000; // see: https://stackoverflow.com/questions/3674539/incrementing-a-date-in-javascript
// {prev, next} Date should work for Daylight Saving Time
// Adding 24 * 60 * 60 * 1000 does not work in the above scenario
export const prevDate = function(date, amount = 1) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate() - amount);
};
export const nextDate = function(date, amount = 1) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);
};
export const getStartDateOfMonth = function(year, month) { export const getStartDateOfMonth = function(year, month) {
const result = new Date(year, month, 1); const result = new Date(year, month, 1);
const day = result.getDay(); const day = result.getDay();
if (day === 0) { if (day === 0) {
result.setTime(result.getTime() - DAY_DURATION * 7); return prevDate(result, 7);
} else { } else {
result.setTime(result.getTime() - DAY_DURATION * day); return prevDate(result, day);
} }
return result;
}; };
export const getWeekNumber = function(src) { export const getWeekNumber = function(src) {
@ -90,6 +102,7 @@ export const getWeekNumber = function(src) {
// January 4 is always in week 1. // January 4 is always in week 1.
const week1 = new Date(date.getFullYear(), 0, 4); const week1 = new Date(date.getFullYear(), 0, 4);
// Adjust to Thursday in week 1 and count number of weeks from date to week 1. // Adjust to Thursday in week 1 and count number of weeks from date to week 1.
// Rounding should be fine for Daylight Saving Time. Its shift should never be more than 12 hours.
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7); return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
}; };
@ -208,13 +221,3 @@ export const nextYear = function(date, amount = 1) {
const monthDate = Math.min(date.getDate(), getDayCountOfMonth(year, month)); const monthDate = Math.min(date.getDate(), getDayCountOfMonth(year, month));
return modifyDate(date, year, month, monthDate); return modifyDate(date, year, month, monthDate);
}; };
// {prev, next} Date works for daylight saving time
// add / subtract one day's duration does not work
export const prevDate = function(date, amount = 1) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate() - amount);
};
export const nextDate = function(date, amount = 1) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);
};

View File

@ -1076,36 +1076,50 @@ describe('DatePicker', () => {
}); });
}); });
it('type:daterange', done => { describe('type:daterange', () => {
vm = createTest(DatePicker, { it('works', done => {
type: 'daterange' vm = createVue({
template: '<el-date-picker type="daterange" v-model="value" ref="compo" />',
data() {
return {
value: ''
};
}
}, true); }, true);
const input = vm.$el.querySelector('input');
input.click(); const rangePicker = vm.$refs.compo;
const inputs = rangePicker.$el.querySelectorAll('input');
inputs[0].focus();
setTimeout(_ => { setTimeout(_ => {
const panels = vm.picker.$el.querySelectorAll('.el-date-range-picker__content'); const panels = rangePicker.picker.$el.querySelectorAll('.el-date-range-picker__content');
expect(Array.prototype.slice.call(panels)).to.length(2); expect(Array.prototype.slice.call(panels)).to.length(2);
panels[0].querySelector('td.available').click(); panels[0].querySelector('td.available').click();
setTimeout(_ => { setTimeout(_ => {
panels[1].querySelector('td.available').click(); panels[1].querySelector('td.available').click();
setTimeout(_ => {
const { inputs[0].focus();
minDate, setTimeout(_ => {
maxDate // correct highlight
} = vm.picker; const startDate = rangePicker.picker.$el.querySelectorAll('.start-date');
expect(minDate).to.exist; const endDate = rangePicker.picker.$el.querySelectorAll('.end-date');
expect(maxDate).to.exist; const inRangeDate = rangePicker.picker.$el.querySelectorAll('.in-range');
expect(maxDate > minDate).to.true; expect(startDate.length).to.equal(1);
expect(endDate.length).to.equal(1);
expect(inRangeDate.length).to.above(0);
// value is array
expect(vm.value).to.be.an.instanceof(Array);
// input text is something like date string
expect(inputs[0].value.length).to.equal(10);
expect(inputs[1].value.length).to.equal(10);
done(); done();
}, DELAY); }, DELAY);
}, DELAY); }, DELAY);
}, DELAY);
}, DELAY);
}); });
it('type:daterange with unlink-panels', done => { it('unlink panels', done => {
vm = createTest(DatePicker, { vm = createTest(DatePicker, {
type: 'daterange', type: 'daterange',
unlinkPanels: true unlinkPanels: true
@ -1136,6 +1150,33 @@ describe('DatePicker', () => {
}, DELAY); }, DELAY);
}); });
it('daylight saving time highlight', done => {
// Run test with environment variable TZ=Australia/Sydney
// The following test uses Australian Eastern Daylight Time (AEDT)
// AEST -> AEDT shift happened on 2016-10-02 02:00:00
vm = createVue({
template: '<el-date-picker type="daterange" v-model="value" ref="compo" />',
data() {
return {
value: [new Date(2016, 9, 1), new Date(2016, 9, 3)]
};
}
}, true);
const rangePicker = vm.$refs.compo;
const inputs = rangePicker.$el.querySelectorAll('input');
inputs[0].focus();
setTimeout(_ => {
const startDate = rangePicker.picker.$el.querySelectorAll('.start-date');
const endDate = rangePicker.picker.$el.querySelectorAll('.end-date');
expect(startDate.length).to.equal(1);
expect(endDate.length).to.equal(1);
done();
}, DELAY);
});
});
describe('type:datetimerange', () => { describe('type:datetimerange', () => {
let vm; let vm;
beforeEach(done => { beforeEach(done => {