mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Fix date filter for DateTime columns.
Summary: Date filter was not taking timezone correclty into account, which was causing to wrong-inclusion and wrong-exclusion of dates near the bounds. Diff fixes that, it also bring little refactoring that hopefully clarifies things a little. Test Plan: Includes brand new test for `app/common/ColumnFilterFunc`. Reviewers: jarek Reviewed By: jarek Differential Revision: https://phab.getgrist.com/D3763
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
import {CellValue} from "app/common/DocActions";
|
||||
import {
|
||||
FilterState, IRangeBoundType, isRangeFilter, isRelativeBound, makeFilterState
|
||||
} from "app/common/FilterState";
|
||||
import {FilterState, IRangeBoundType, isRangeFilter, makeFilterState} from "app/common/FilterState";
|
||||
import {decodeObject} from "app/plugin/objtypes";
|
||||
import moment from "moment-timezone";
|
||||
import {isDateLikeType, isList, isListType, isNumberType} from "./gristTypes";
|
||||
import {toUnixTimestamp} from "app/common/RelativeDates";
|
||||
import moment, { Moment } from "moment-timezone";
|
||||
import {extractInfoFromColType, isDateLikeType, isList, isListType, isNumberType} from "app/common/gristTypes";
|
||||
import {isRelativeBound, relativeDateToUnixTimestamp} from "app/common/RelativeDates";
|
||||
import {noop} from "lodash";
|
||||
|
||||
export type ColumnFilterFunc = (value: CellValue) => boolean;
|
||||
|
||||
@@ -18,8 +17,12 @@ export function makeFilterFunc(state: FilterState,
|
||||
let {min, max} = state;
|
||||
if (isNumberType(columnType) || isDateLikeType(columnType)) {
|
||||
|
||||
min = getBoundsValue(state, 'min');
|
||||
max = getBoundsValue(state, 'max');
|
||||
if (isDateLikeType(columnType)) {
|
||||
const info = extractInfoFromColType(columnType);
|
||||
const timezone = (info.type === 'DateTime' && info.timezone) || 'utc';
|
||||
min = changeTimezone(min, timezone, m => m.startOf('day'));
|
||||
max = changeTimezone(max, timezone, m => m.endOf('day'));
|
||||
}
|
||||
|
||||
return (val) => {
|
||||
if (typeof val !== 'number') { return false; }
|
||||
@@ -59,18 +62,14 @@ export function buildColFilter(filterJson: string | undefined,
|
||||
return filterJson ? makeFilterFunc(makeFilterState(filterJson), columnType) : null;
|
||||
}
|
||||
|
||||
|
||||
function getBoundsValue(state: {min?: IRangeBoundType, max?: IRangeBoundType}, minMax: 'min'|'max') {
|
||||
const value = state[minMax];
|
||||
if (isRelativeBound(value)) {
|
||||
const val = toUnixTimestamp(value);
|
||||
const m = moment.utc(val * 1000);
|
||||
if (minMax === 'min') {
|
||||
m.startOf('day');
|
||||
} else {
|
||||
m.endOf('day');
|
||||
}
|
||||
return Math.floor(m.valueOf() / 1000);
|
||||
}
|
||||
return value;
|
||||
// Returns the unix timestamp for date in timezone. Function support relative date. Also support
|
||||
// optional mod argument that let you modify date as a moment instance.
|
||||
function changeTimezone(date: IRangeBoundType,
|
||||
timezone: string,
|
||||
mod: (m: Moment) => void = noop): number|undefined {
|
||||
if (date === undefined) { return undefined; }
|
||||
const val = isRelativeBound(date) ? relativeDateToUnixTimestamp(date) : date;
|
||||
const m = moment.tz(val * 1000, timezone);
|
||||
mod(m);
|
||||
return Math.floor(m.valueOf() / 1000);
|
||||
}
|
||||
|
||||
@@ -32,31 +32,25 @@ export function isRelativeBound(bound?: number|IRelativeDateSpec): bound is IRel
|
||||
|
||||
// Returns the number of seconds between 1 January 1970 00:00:00 UTC and the given bound, may it be
|
||||
// a relative date.
|
||||
export function toUnixTimestamp(bound: IRelativeDateSpec|number): number {
|
||||
export function relativeDateToUnixTimestamp(bound: IRelativeDateSpec): number {
|
||||
const localDate = getCurrentTime().startOf('day');
|
||||
const date = moment.utc(localDate.toObject());
|
||||
const periods = Array.isArray(bound) ? bound : [bound];
|
||||
|
||||
if (isRelativeBound(bound)) {
|
||||
const localDate = getCurrentTime().startOf('day');
|
||||
const date = moment.utc(localDate.toObject());
|
||||
const periods = Array.isArray(bound) ? bound : [bound];
|
||||
for (const period of periods) {
|
||||
const {quantity, unit, endOf} = period;
|
||||
|
||||
for (const period of periods) {
|
||||
const {quantity, unit, endOf} = period;
|
||||
date.add(quantity, unit);
|
||||
if (endOf) {
|
||||
date.endOf(unit);
|
||||
|
||||
date.add(quantity, unit);
|
||||
if (endOf) {
|
||||
date.endOf(unit);
|
||||
|
||||
// date must have "hh:mm:ss" set to "00:00:00"
|
||||
date.startOf('day');
|
||||
} else {
|
||||
date.startOf(unit);
|
||||
}
|
||||
// date must have "hh:mm:ss" set to "00:00:00"
|
||||
date.startOf('day');
|
||||
} else {
|
||||
date.startOf(unit);
|
||||
}
|
||||
|
||||
return Math.floor(date.valueOf() / 1000);
|
||||
} else {
|
||||
return bound;
|
||||
}
|
||||
return Math.floor(date.valueOf() / 1000);
|
||||
}
|
||||
|
||||
// Format a relative date.
|
||||
|
||||
Reference in New Issue
Block a user