mirror of https://github.com/ElemeFE/element
				
				
				
			DatePicker: Daylight Saving Time (#7599)
* date-table: fix daylight saving time highlight * test: update date-picker range testpull/7603/head
							parent
							
								
									8313016dae
								
							
						
					
					
						commit
						af46f968ab
					
				| 
						 | 
				
			
			@ -30,7 +30,7 @@
 | 
			
		|||
</template>
 | 
			
		||||
 | 
			
		||||
<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 Locale from 'element-ui/src/mixins/locale';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +136,7 @@
 | 
			
		|||
 | 
			
		||||
          if (this.showWeekNumber) {
 | 
			
		||||
            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';
 | 
			
		||||
 | 
			
		||||
            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.start = this.minDate && time === clearHours(this.minDate);
 | 
			
		||||
            cell.end = this.maxDate && time === clearHours(this.maxDate);
 | 
			
		||||
| 
						 | 
				
			
			@ -289,22 +289,8 @@
 | 
			
		|||
      },
 | 
			
		||||
 | 
			
		||||
      getDateOfCell(row, column) {
 | 
			
		||||
        const startDate = this.startDate;
 | 
			
		||||
 | 
			
		||||
        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];
 | 
			
		||||
        }
 | 
			
		||||
        const offsetFromStart = row * 7 + (column - (this.showWeekNumber ? 1 : 0)) - this.offsetDay;
 | 
			
		||||
        return nextDate(this.startDate, offsetFromStart);
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      isWeekActive(cell) {
 | 
			
		||||
| 
						 | 
				
			
			@ -343,7 +329,7 @@
 | 
			
		|||
 | 
			
		||||
            const cell = row[j];
 | 
			
		||||
            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.start = minDate && time === clearHours(minDate.getTime());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,14 +49,13 @@
 | 
			
		|||
 | 
			
		||||
<script type="text/babel">
 | 
			
		||||
  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';
 | 
			
		||||
 | 
			
		||||
  const datesInMonth = (year, month) => {
 | 
			
		||||
    const numOfDays = getDayCountOfMonth(year, month);
 | 
			
		||||
    const firstDay = new Date(year, month, 1);
 | 
			
		||||
    const ONE_DAY = 8.64e7;
 | 
			
		||||
    return range(numOfDays).map(n => new Date(firstDay.getTime() + n * ONE_DAY));
 | 
			
		||||
    return range(numOfDays).map(n => nextDate(firstDay, n));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,15 +45,12 @@
 | 
			
		|||
 | 
			
		||||
<script type="text/babel">
 | 
			
		||||
  import { hasClass } from 'element-ui/src/utils/dom';
 | 
			
		||||
  import { isDate, range } from '../util';
 | 
			
		||||
 | 
			
		||||
  const isLeapYear = year => year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0);
 | 
			
		||||
  import { isDate, range, nextDate, getDayCountOfYear } from '../util';
 | 
			
		||||
 | 
			
		||||
  const datesInYear = year => {
 | 
			
		||||
    const numOfDays = isLeapYear(year) ? 366 : 365;
 | 
			
		||||
    const numOfDays = getDayCountOfYear(year);
 | 
			
		||||
    const firstDay = new Date(year, 0, 1);
 | 
			
		||||
    const ONE_DAY = 8.64e7;
 | 
			
		||||
    return range(numOfDays).map(n => new Date(firstDay.getTime() + ONE_DAY));
 | 
			
		||||
    return range(numOfDays).map(n => nextDate(firstDay, n));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,25 +61,37 @@ export const getDayCountOfMonth = function(year, month) {
 | 
			
		|||
  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) {
 | 
			
		||||
  const temp = new Date(date.getTime());
 | 
			
		||||
  temp.setDate(1);
 | 
			
		||||
  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) {
 | 
			
		||||
  const result = new Date(year, month, 1);
 | 
			
		||||
  const day = result.getDay();
 | 
			
		||||
 | 
			
		||||
  if (day === 0) {
 | 
			
		||||
    result.setTime(result.getTime() - DAY_DURATION * 7);
 | 
			
		||||
    return prevDate(result, 7);
 | 
			
		||||
  } else {
 | 
			
		||||
    result.setTime(result.getTime() - DAY_DURATION * day);
 | 
			
		||||
    return prevDate(result, day);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getWeekNumber = function(src) {
 | 
			
		||||
| 
						 | 
				
			
			@ -90,6 +102,7 @@ export const getWeekNumber = function(src) {
 | 
			
		|||
  // January 4 is always in week 1.
 | 
			
		||||
  const week1 = new Date(date.getFullYear(), 0, 4);
 | 
			
		||||
  // 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);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -208,13 +221,3 @@ export const nextYear = function(date, amount = 1) {
 | 
			
		|||
  const monthDate = Math.min(date.getDate(), getDayCountOfMonth(year, month));
 | 
			
		||||
  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);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1076,64 +1076,105 @@ describe('DatePicker', () => {
 | 
			
		|||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('type:daterange', done => {
 | 
			
		||||
    vm = createTest(DatePicker, {
 | 
			
		||||
      type: 'daterange'
 | 
			
		||||
    }, true);
 | 
			
		||||
    const input = vm.$el.querySelector('input');
 | 
			
		||||
  describe('type:daterange', () => {
 | 
			
		||||
    it('works', done => {
 | 
			
		||||
      vm = createVue({
 | 
			
		||||
        template: '<el-date-picker type="daterange" v-model="value" ref="compo" />',
 | 
			
		||||
        data() {
 | 
			
		||||
          return {
 | 
			
		||||
            value: ''
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
      }, true);
 | 
			
		||||
 | 
			
		||||
    input.click();
 | 
			
		||||
 | 
			
		||||
    setTimeout(_ => {
 | 
			
		||||
      const panels = vm.picker.$el.querySelectorAll('.el-date-range-picker__content');
 | 
			
		||||
 | 
			
		||||
      expect(Array.prototype.slice.call(panels)).to.length(2);
 | 
			
		||||
 | 
			
		||||
      panels[0].querySelector('td.available').click();
 | 
			
		||||
      setTimeout(_ => {
 | 
			
		||||
        panels[1].querySelector('td.available').click();
 | 
			
		||||
 | 
			
		||||
        const {
 | 
			
		||||
          minDate,
 | 
			
		||||
          maxDate
 | 
			
		||||
        } = vm.picker;
 | 
			
		||||
        expect(minDate).to.exist;
 | 
			
		||||
        expect(maxDate).to.exist;
 | 
			
		||||
        expect(maxDate > minDate).to.true;
 | 
			
		||||
        done();
 | 
			
		||||
      }, DELAY);
 | 
			
		||||
    }, DELAY);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('type:daterange with unlink-panels', done => {
 | 
			
		||||
    vm = createTest(DatePicker, {
 | 
			
		||||
      type: 'daterange',
 | 
			
		||||
      unlinkPanels: true
 | 
			
		||||
    }, true);
 | 
			
		||||
    const input = vm.$el.querySelector('input');
 | 
			
		||||
 | 
			
		||||
    input.click();
 | 
			
		||||
 | 
			
		||||
    setTimeout(_ => {
 | 
			
		||||
      const panels = vm.picker.$el.querySelectorAll('.el-date-range-picker__content');
 | 
			
		||||
 | 
			
		||||
      expect(Array.prototype.slice.call(panels)).to.length(2);
 | 
			
		||||
 | 
			
		||||
      panels[1].querySelector('.el-icon-d-arrow-right').click();
 | 
			
		||||
      panels[1].querySelector('.el-icon-arrow-right').click();
 | 
			
		||||
      const rangePicker = vm.$refs.compo;
 | 
			
		||||
      const inputs = rangePicker.$el.querySelectorAll('input');
 | 
			
		||||
      inputs[0].focus();
 | 
			
		||||
 | 
			
		||||
      setTimeout(_ => {
 | 
			
		||||
        const left = panels[0].querySelector('.el-date-range-picker__header');
 | 
			
		||||
        const right = panels[1].querySelector('.is-right .el-date-range-picker__header');
 | 
			
		||||
        const leftText = left.textContent.match(/\d+/g);
 | 
			
		||||
        const rightText = right.textContent.match(/\d+/g);
 | 
			
		||||
        const panels = rangePicker.picker.$el.querySelectorAll('.el-date-range-picker__content');
 | 
			
		||||
        expect(Array.prototype.slice.call(panels)).to.length(2);
 | 
			
		||||
        panels[0].querySelector('td.available').click();
 | 
			
		||||
        setTimeout(_ => {
 | 
			
		||||
          panels[1].querySelector('td.available').click();
 | 
			
		||||
          setTimeout(_ => {
 | 
			
		||||
            inputs[0].focus();
 | 
			
		||||
            setTimeout(_ => {
 | 
			
		||||
              // correct highlight
 | 
			
		||||
              const startDate = rangePicker.picker.$el.querySelectorAll('.start-date');
 | 
			
		||||
              const endDate = rangePicker.picker.$el.querySelectorAll('.end-date');
 | 
			
		||||
              const inRangeDate = rangePicker.picker.$el.querySelectorAll('.in-range');
 | 
			
		||||
              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();
 | 
			
		||||
            }, DELAY);
 | 
			
		||||
          }, DELAY);
 | 
			
		||||
        }, DELAY);
 | 
			
		||||
      }, DELAY);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
        expect(rightText[0] - leftText[0]).to.equal(1);
 | 
			
		||||
        expect((rightText[1] <= 2 ? rightText[1] + 12 : rightText[1]) - leftText[1]).to.equal(2);
 | 
			
		||||
    it('unlink panels', done => {
 | 
			
		||||
      vm = createTest(DatePicker, {
 | 
			
		||||
        type: 'daterange',
 | 
			
		||||
        unlinkPanels: true
 | 
			
		||||
      }, true);
 | 
			
		||||
      const input = vm.$el.querySelector('input');
 | 
			
		||||
 | 
			
		||||
      input.click();
 | 
			
		||||
 | 
			
		||||
      setTimeout(_ => {
 | 
			
		||||
        const panels = vm.picker.$el.querySelectorAll('.el-date-range-picker__content');
 | 
			
		||||
 | 
			
		||||
        expect(Array.prototype.slice.call(panels)).to.length(2);
 | 
			
		||||
 | 
			
		||||
        panels[1].querySelector('.el-icon-d-arrow-right').click();
 | 
			
		||||
        panels[1].querySelector('.el-icon-arrow-right').click();
 | 
			
		||||
 | 
			
		||||
        setTimeout(_ => {
 | 
			
		||||
          const left = panels[0].querySelector('.el-date-range-picker__header');
 | 
			
		||||
          const right = panels[1].querySelector('.is-right .el-date-range-picker__header');
 | 
			
		||||
          const leftText = left.textContent.match(/\d+/g);
 | 
			
		||||
          const rightText = right.textContent.match(/\d+/g);
 | 
			
		||||
 | 
			
		||||
          expect(rightText[0] - leftText[0]).to.equal(1);
 | 
			
		||||
          expect((rightText[1] <= 2 ? rightText[1] + 12 : rightText[1]) - leftText[1]).to.equal(2);
 | 
			
		||||
 | 
			
		||||
          done();
 | 
			
		||||
        }, 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);
 | 
			
		||||
    }, DELAY);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('type:datetimerange', () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue