Table.Filter by date and time
Posted: 16.10.2008, 21:13
TZTable and TZQuery Filter property dos not works correctly with datetime fields. So first of all I wrote 2 tests:
\test\component\ZTestDataSetGeneric.pas
Add to published interface
procedure TestTimeFilterExpression;
procedure TestDateTimeFilterExpression;
---
{**
Runs a test for time filter expressions.
}
procedure TZGenericTestDbcResultSet.TestTimeFilterExpression;
var
Query: TZQuery;
begin
Query := TZQuery.Create(nil);
try
Query.Connection := Connection;
Query.SQL.Text := 'SELECT * FROM people';
Query.Filter := 'p_begin_work >= "'+TimeToStr(EncodeTime(8,30,0,50))+'"';
Query.Filtered := True;
Query.Open;
CheckEquals(4, Query.RecordCount);
Query.Last;
CheckEquals(EncodeTime(8,30,0,0), Query.FieldByName('p_begin_work').AsDateTime);
Query.Close;
Query.Filter := '(p_begin_work > "'+TimeToStr(EncodeTime(8,0,0,0))+ '") AND (p_end_work < "'+TimeToStr(EncodeTime(18,0,0,0))+'")';
Query.Open;
CheckEquals(2, Query.RecordCount);
Query.Last;
CheckEquals(EncodeTime(8,30,0,0), Query.FieldByName('p_begin_work').AsDateTime);
CheckEquals(EncodeTime(17,30,0,0), Query.FieldByName('p_end_work').AsDateTime);
Query.Close;
finally
Query.Free;
end;
end;
{**
Runs a test for Datetime filter expressions.
}
procedure TZGenericTestDbcResultSet.TestDateTimeFilterExpression;
var
Query: TZQuery;
Date_came,Date_out : TDateTime;
begin
Query := TZQuery.Create(nil);
try
Query.Connection := Connection;
Query.SQL.Text := 'SELECT * FROM cargo';
Date_came := EncodeDateTime(2002,12,19,18,30,0,0);
Query.Filter := 'c_date_came >= "'+DateTimeToStr(Date_came)+'"';
Query.Filtered := True;
Query.Open;
CheckEquals(3, Query.RecordCount);
Query.Last;
Date_came := EncodeDateTime(2002,12,21,10,20,0,0);
CheckEquals(Date_Came, Query.FieldByName('c_date_came').AsDateTime);
Query.Close;
Date_came := EncodeDateTime(2002,12,19,14,30,0,0);
Date_out := EncodeDateTime(2002,12,23,2,0,0,0);
Query.Filter := '(c_date_came > "'+DateTimeToStr(Date_came)+ '") AND (c_date_out < "'+DateTimeToStr(Date_out)+'")';
Query.Open;
CheckEquals(2, Query.RecordCount);
Query.First;
Date_came := EncodeDateTime(2002,12,20,2,0,0,0);
Date_out := EncodeDateTime(2002,12,20,2,0,0,0);
CheckEquals(Date_came, Query.FieldByName('c_date_came').AsDateTime);
CheckEquals(Date_out, Query.FieldByName('c_date_out').AsDateTime);
Query.Close;
finally
Query.Free;
end;
end;
On \src\core\ZTokenizer.pas
TZTokenType = (ttUnknown, ttEOF, ttFloat, ttInteger, ttHexDecimal,
ttNumber, ttSymbol, ttQuoted, ttQuotedIdentifier, ttWord, ttKeyword, ttWhitespace,
ttComment, ttSpecial,ttTime,ttDate,ttDateTime);
On \src\core\ZExprParser.pas , procedure TokenizeExpression
...
ttSymbol:
begin
Temp := Tokens[TokenIndex];
for I := Low(OperatorTokens) to High(OperatorTokens) do
begin
if Temp = OperatorTokens then
begin
TokenType := OperatorCodes;
Break;
end;
end;
end;
ttTime,ttDate,ttDateTime:
begin
TokenType := ttConstant;
DefVarManager.SetAsDateTime(TokenValue, StrToDateTime(Tokens[TokenIndex]));
end;
end;
if TokenType = ttUnknown then
raise TZParseError.Create(Format(SUnknownSymbol, [Tokens[TokenIndex]]));
...
On \src\parsesql\ZGenericSQLToken.pas
function TZGenericSQLQuoteState.NextToken(Stream: TStream;
FirstChar: Char; Tokenizer: TZTokenizer): TZToken;
var
ReadChar: Char;
LastChar: Char;
CountDoublePoint,CountSlash : integer;
isDateTime : TDateTime;
begin
Result.Value := FirstChar;
LastChar := #0;
CountDoublePoint := 0;
CountSlash := 0;
while Stream.Read(ReadChar, 1) > 0 do
begin
if (LastChar = FirstChar) and (ReadChar <> FirstChar) then
begin
Stream.Seek(-1, soFromCurrent);
Break;
end;
if ReadChar = TimeSeparator then
inc(CountDoublePoint);
if ReadChar = DateSeparator then
inc(CountSlash);
Result.Value := Result.Value + ReadChar;
if (LastChar = FirstChar) and (ReadChar = FirstChar) then
LastChar := #0
else LastChar := ReadChar;
end;
if FirstChar = '"' then
Result.TokenType := ttWord
else Result.TokenType := ttQuoted;
// Time constant
if (CountDoublePoint = 2) and (CountSlash = 0) then
begin
try
isDateTime := StrToTime(DecodeString(Result.Value,'"'));
Result.Value := DecodeString(Result.Value,'"');
Result.TokenType := ttTime;
except
end;
end;
// Date constant
if (CountDoublePoint = 0) and (CountSlash = 2) then
begin
try
isDateTime := StrToDate(DecodeString(Result.Value,'"'));
Result.Value := DecodeString(Result.Value,'"');
Result.TokenType := ttDate;
except
end;
end;
// DateTime constant
if (CountDoublePoint = 2) and (CountSlash = 2) then
begin
try
isDateTime := StrToDateTime(DecodeString(Result.Value,'"'));
Result.Value := DecodeString(Result.Value,'"');
Result.TokenType := ttDateTime;
except
end;
end;
end;
Moreover can you please add on \src\plain\ZPlainFirebirdInterbaseConstants.pas
isc_info_db_SQL_dialect = 62;
isc_info_db_read_only = 63;
isc_info_db_size_in_pages = 64;
isc_info_db_SQL_dialect = 62;
isc_info_db_read_only = 63;
isc_info_db_size_in_pages = 64;
frb_info_att_charset = 101;
isc_info_db_class = 102;
isc_info_firebird_version = 103;
isc_info_oldest_transaction = 104;
isc_info_oldest_active = 105;
isc_info_oldest_snapshot = 106;
isc_info_next_transaction = 107;
isc_info_db_provider = 108;
isc_info_active_transactions = 109;
isc_info_active_tran_count = 110;
isc_info_creation_date = 111;
isc_info_db_file_size = 112;
All other tests works correctly to me
\test\component\ZTestDataSetGeneric.pas
Add to published interface
procedure TestTimeFilterExpression;
procedure TestDateTimeFilterExpression;
---
{**
Runs a test for time filter expressions.
}
procedure TZGenericTestDbcResultSet.TestTimeFilterExpression;
var
Query: TZQuery;
begin
Query := TZQuery.Create(nil);
try
Query.Connection := Connection;
Query.SQL.Text := 'SELECT * FROM people';
Query.Filter := 'p_begin_work >= "'+TimeToStr(EncodeTime(8,30,0,50))+'"';
Query.Filtered := True;
Query.Open;
CheckEquals(4, Query.RecordCount);
Query.Last;
CheckEquals(EncodeTime(8,30,0,0), Query.FieldByName('p_begin_work').AsDateTime);
Query.Close;
Query.Filter := '(p_begin_work > "'+TimeToStr(EncodeTime(8,0,0,0))+ '") AND (p_end_work < "'+TimeToStr(EncodeTime(18,0,0,0))+'")';
Query.Open;
CheckEquals(2, Query.RecordCount);
Query.Last;
CheckEquals(EncodeTime(8,30,0,0), Query.FieldByName('p_begin_work').AsDateTime);
CheckEquals(EncodeTime(17,30,0,0), Query.FieldByName('p_end_work').AsDateTime);
Query.Close;
finally
Query.Free;
end;
end;
{**
Runs a test for Datetime filter expressions.
}
procedure TZGenericTestDbcResultSet.TestDateTimeFilterExpression;
var
Query: TZQuery;
Date_came,Date_out : TDateTime;
begin
Query := TZQuery.Create(nil);
try
Query.Connection := Connection;
Query.SQL.Text := 'SELECT * FROM cargo';
Date_came := EncodeDateTime(2002,12,19,18,30,0,0);
Query.Filter := 'c_date_came >= "'+DateTimeToStr(Date_came)+'"';
Query.Filtered := True;
Query.Open;
CheckEquals(3, Query.RecordCount);
Query.Last;
Date_came := EncodeDateTime(2002,12,21,10,20,0,0);
CheckEquals(Date_Came, Query.FieldByName('c_date_came').AsDateTime);
Query.Close;
Date_came := EncodeDateTime(2002,12,19,14,30,0,0);
Date_out := EncodeDateTime(2002,12,23,2,0,0,0);
Query.Filter := '(c_date_came > "'+DateTimeToStr(Date_came)+ '") AND (c_date_out < "'+DateTimeToStr(Date_out)+'")';
Query.Open;
CheckEquals(2, Query.RecordCount);
Query.First;
Date_came := EncodeDateTime(2002,12,20,2,0,0,0);
Date_out := EncodeDateTime(2002,12,20,2,0,0,0);
CheckEquals(Date_came, Query.FieldByName('c_date_came').AsDateTime);
CheckEquals(Date_out, Query.FieldByName('c_date_out').AsDateTime);
Query.Close;
finally
Query.Free;
end;
end;
On \src\core\ZTokenizer.pas
TZTokenType = (ttUnknown, ttEOF, ttFloat, ttInteger, ttHexDecimal,
ttNumber, ttSymbol, ttQuoted, ttQuotedIdentifier, ttWord, ttKeyword, ttWhitespace,
ttComment, ttSpecial,ttTime,ttDate,ttDateTime);
On \src\core\ZExprParser.pas , procedure TokenizeExpression
...
ttSymbol:
begin
Temp := Tokens[TokenIndex];
for I := Low(OperatorTokens) to High(OperatorTokens) do
begin
if Temp = OperatorTokens then
begin
TokenType := OperatorCodes;
Break;
end;
end;
end;
ttTime,ttDate,ttDateTime:
begin
TokenType := ttConstant;
DefVarManager.SetAsDateTime(TokenValue, StrToDateTime(Tokens[TokenIndex]));
end;
end;
if TokenType = ttUnknown then
raise TZParseError.Create(Format(SUnknownSymbol, [Tokens[TokenIndex]]));
...
On \src\parsesql\ZGenericSQLToken.pas
function TZGenericSQLQuoteState.NextToken(Stream: TStream;
FirstChar: Char; Tokenizer: TZTokenizer): TZToken;
var
ReadChar: Char;
LastChar: Char;
CountDoublePoint,CountSlash : integer;
isDateTime : TDateTime;
begin
Result.Value := FirstChar;
LastChar := #0;
CountDoublePoint := 0;
CountSlash := 0;
while Stream.Read(ReadChar, 1) > 0 do
begin
if (LastChar = FirstChar) and (ReadChar <> FirstChar) then
begin
Stream.Seek(-1, soFromCurrent);
Break;
end;
if ReadChar = TimeSeparator then
inc(CountDoublePoint);
if ReadChar = DateSeparator then
inc(CountSlash);
Result.Value := Result.Value + ReadChar;
if (LastChar = FirstChar) and (ReadChar = FirstChar) then
LastChar := #0
else LastChar := ReadChar;
end;
if FirstChar = '"' then
Result.TokenType := ttWord
else Result.TokenType := ttQuoted;
// Time constant
if (CountDoublePoint = 2) and (CountSlash = 0) then
begin
try
isDateTime := StrToTime(DecodeString(Result.Value,'"'));
Result.Value := DecodeString(Result.Value,'"');
Result.TokenType := ttTime;
except
end;
end;
// Date constant
if (CountDoublePoint = 0) and (CountSlash = 2) then
begin
try
isDateTime := StrToDate(DecodeString(Result.Value,'"'));
Result.Value := DecodeString(Result.Value,'"');
Result.TokenType := ttDate;
except
end;
end;
// DateTime constant
if (CountDoublePoint = 2) and (CountSlash = 2) then
begin
try
isDateTime := StrToDateTime(DecodeString(Result.Value,'"'));
Result.Value := DecodeString(Result.Value,'"');
Result.TokenType := ttDateTime;
except
end;
end;
end;
Moreover can you please add on \src\plain\ZPlainFirebirdInterbaseConstants.pas
isc_info_db_SQL_dialect = 62;
isc_info_db_read_only = 63;
isc_info_db_size_in_pages = 64;
isc_info_db_SQL_dialect = 62;
isc_info_db_read_only = 63;
isc_info_db_size_in_pages = 64;
frb_info_att_charset = 101;
isc_info_db_class = 102;
isc_info_firebird_version = 103;
isc_info_oldest_transaction = 104;
isc_info_oldest_active = 105;
isc_info_oldest_snapshot = 106;
isc_info_next_transaction = 107;
isc_info_db_provider = 108;
isc_info_active_transactions = 109;
isc_info_active_tran_count = 110;
isc_info_creation_date = 111;
isc_info_db_file_size = 112;
All other tests works correctly to me