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

feat(filtering): date adjustment for timezone conversions #14722

Open
wants to merge 15 commits into
base: 18.2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,25 @@ export abstract class DateTimeUtil {
return formattedDate;
}

/** Adjusts the given date to or from the specified timezone. */
public static adjustDate(timezone: string, value: Date, isDateOnly: boolean, reverse: boolean = false): any {
const reverseValue = reverse ? -1 : 1;
const inputTimezoneOffset = value.getTimezoneOffset();
const columnTimezoneOffset = this.timezoneToOffset(timezone, inputTimezoneOffset);
const offsetDifference = reverseValue * (columnTimezoneOffset - inputTimezoneOffset);

if (isDateOnly) {
// When column timezone is left of the locale timezone (date is shifted one day before), add 1 day
if (columnTimezoneOffset - inputTimezoneOffset > 0) {
return new Date(value.setDate(value.getDate() + 1))
}

return value;
}

return this.addDateMinutes(value, offsetDifference);
}

/**
* Returns the date format based on a provided locale.
* Supports Angular's DatePipe format options such as `shortDate`, `longDate`.
Expand Down Expand Up @@ -692,4 +711,13 @@ export abstract class DateTimeUtil {
}
}
}

private static timezoneToOffset(timezone: string, fallback: number): number {
const requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
}

private static addDateMinutes(date: Date, minutes: number): Date {
return new Date(date.getTime() + minutes * 60000);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
OnDestroy
} from '@angular/core';
import { GridColumnDataType, DataUtil } from '../../../data-operations/data-util';
import { DateTimeUtil } from '../../../date-common/util/date-time.util';
import { IgxDropDownComponent } from '../../../drop-down/drop-down.component';
import { IFilteringOperation } from '../../../data-operations/filtering-condition';
import { FilteringLogic, IFilteringExpression } from '../../../data-operations/filtering-expression.interface';
Expand Down Expand Up @@ -107,7 +108,9 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy {
}

this._value = val;
this.expression.searchVal = DataUtil.parseValue(this.column.dataType, val);
const isDateOnly = this.column.dataType === GridColumnDataType.Date;
const isDate = isDateOnly || this.column.dataType === GridColumnDataType.DateTime;
this.expression.searchVal = isDate ? DateTimeUtil.adjustDate(this.column.pipeArgs.timezone, val, isDateOnly) : DataUtil.parseValue(this.column.dataType, val);
if (this.expressionsList.find(item => item.expression === this.expression) === undefined) {
this.addExpression(true);
}
Expand Down Expand Up @@ -228,7 +231,10 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy {
const selectedItem = this.expressionsList.find(expr => expr.isSelected === true);
if (selectedItem) {
this.expression = selectedItem.expression;
this._value = this.expression.searchVal;
const isDateOnly = this.column.dataType === GridColumnDataType.Date;
const isDate = isDateOnly || this.column.dataType === GridColumnDataType.DateTime;
const adjustedSearchVal = isDate ? DateTimeUtil.adjustDate(this.column.pipeArgs.timezone, this.expression.searchVal, isDateOnly, true) : this.expression.searchVal
this._value = adjustedSearchVal;
}

this.filteringService.grid.localeChange
Expand Down Expand Up @@ -626,7 +632,10 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy {
item.isSelected = !item.isSelected;
if (item.isSelected) {
this.expression = item.expression;
this._value = this.expression.searchVal;
const isDateOnly = this.column.dataType === GridColumnDataType.Date;
const isDate = isDateOnly || this.column.dataType === GridColumnDataType.DateTime;
const adjustedSearchVal = isDate ? DateTimeUtil.adjustDate(this.column.pipeArgs.timezone, this.expression.searchVal, isDateOnly, true) : this.expression.searchVal
this._value = adjustedSearchVal;
this.focusEditElement();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ export class IgxFilteringService implements OnDestroy {
return formatter(expression.searchVal, undefined);
}
const pipeArgs = column.pipeArgs;
return formatDate(expression.searchVal, pipeArgs.format, this.grid.locale);
return formatDate(expression.searchVal, pipeArgs.format, this.grid.locale, pipeArgs.timezone);
} else {
return expression.searchVal;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,93 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => {
expect(grid.rowList.length).toEqual(1);
}));

it('should correctly filter dateTime column by \'equals\' filtering condition, with applied timezone', fakeAsync(() => {
const column = grid.getColumnByName('ReleaseDate');

column.pipeArgs = {
timezone: "GMT-12"
};

GridFunctions.clickFilterCellChip(fix, 'ReleaseDate');
fix.detectChanges();

const filteringRow = fix.debugElement.query(By.directive(IgxGridFilteringRowComponent));
const reset = filteringRow.queryAll(By.css('button'))[0];
const inputDebugElement = filteringRow.query(By.directive(IgxInputDirective));
const input = inputDebugElement.nativeElement;
input.click();
tick(100);
fix.detectChanges();

let outlet = document.getElementsByClassName('igx-grid__outlet')[0];
let calendar = outlet.getElementsByClassName('igx-calendar')[0];
let todayDayItem: HTMLElement = calendar.querySelector('.igx-days-view__date--current');
todayDayItem.firstChild.dispatchEvent(new Event('mousedown'));
grid.filteringRow.onInputGroupFocusout();
tick(100);
fix.detectChanges();

// Clicking today's date should not return any results because GMT-7 would shift today's date in the previous one
expect(grid.rowList.length).toEqual(0);

// Reset filtering and open the datePicker again
reset.nativeElement.click();
flush();
fix.detectChanges();

input.click();
tick(100);
fix.detectChanges();

outlet = document.getElementsByClassName('igx-grid__outlet')[0];
calendar = outlet.getElementsByClassName('igx-calendar')[0];
todayDayItem = calendar.querySelector('.igx-days-view__date--current');

// From today's date, try to select the previous date
const dateRow: HTMLElement = todayDayItem.closest('.igx-days-view__row');
const daysInRow = Array.from(dateRow.getElementsByClassName('igx-days-view__date'));
const currentIndex = daysInRow.indexOf(todayDayItem);
let previousDay: Element;

// If previous day in the same week, select it
if (currentIndex > 0) {
previousDay = daysInRow[currentIndex - 1];
} else {
// If previous day is in last week, select the previous week row and the last date
const previousRow = dateRow.previousElementSibling;
if (previousRow) {
const daysInPreviousRow = Array.from(previousRow.getElementsByClassName('igx-days-view__date'));
previousDay = daysInPreviousRow[daysInPreviousRow.length - 1];
} else {
// If previous day is in last month, switch the month and select the last active date
const previousMonth = fix.debugElement.query(By.css('.igx-calendar-picker__prev'));
UIInteractions.simulateMouseDownEvent(previousMonth.nativeElement);
tick(100);
fix.detectChanges();

const weekRows = Array.from(calendar.querySelectorAll('.igx-days-view__row'));

// If all days are inactive (from next month), select the previous week and check it instead.
for (let i = weekRows.length - 1; i >= 0; i--) {
const daysInWeek = Array.from(weekRows[i].getElementsByClassName('igx-days-view__date'));
const activeDays = daysInWeek.filter(day => !day.classList.contains('igx-days-view__date--inactive'));

if (activeDays.length > 0) {
previousDay = activeDays[activeDays.length - 1];
break;
}
}
}
}

previousDay.firstChild.dispatchEvent(new Event('mousedown'));
grid.filteringRow.onInputGroupFocusout();
tick(100);
fix.detectChanges();

expect(grid.rowList.length).toEqual(1);
}));

it('Should correctly select month from month view datepicker/calendar component', fakeAsync(() => {
pending('This should be tested in the e2e test');
const filteringCells = fix.debugElement.queryAll(By.css(FILTER_UI_CELL));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1902,7 +1902,7 @@ export class SampleTestData {
Downloads: 254,
ID: 1,
ProductName: 'Ignite UI for JavaScript',
ReleaseDate: SampleTestData.timeGenerator.timedelta(SampleTestData.today, 'day', 1),
ReleaseDate: SampleTestData.timeGenerator.timedelta(SampleTestData.today, 'day', 2),
ReleaseDateTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'hour', 1),
ReleaseTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'hour', 1),
Released: false,
Expand Down Expand Up @@ -1957,7 +1957,7 @@ export class SampleTestData {
Downloads: 702,
ID: 6,
ProductName: 'Some other item with Script',
ReleaseDate: SampleTestData.timeGenerator.timedelta(SampleTestData.today, 'day', 1),
ReleaseDate: SampleTestData.timeGenerator.timedelta(SampleTestData.today, 'day', 2),
ReleaseDateTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'second', 20),
ReleaseTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'second', 20),
Released: null,
Expand Down
Loading