Delphi 如果还有日期时间字段,10.4 无法在 DBGrids 上显示十进制 (5,2) BCD 字段

Delphi 10.4 fails to show decimal(5,2) BCD fields on DBGrids if there also is a datetime field

当我有一个带有日期时间字段和小数 (5,2) 字段的 ClientDataset 时,Delphi 10.4 无法在 TDBGrid 上显示它们,它会引发转换异常。

我已经准备了一个小测试项目来显示这个错误(我的真实数据来自 SQL 服务器,尽管我可以手动填充客户端数据集得到相同的错误)。

procedure TForm1.FormCreate(Sender: TObject);
var ClientDataset: TClientDataset;
    Datasource: TDatasource;
    DBGrid: TDBGrid;
begin
  ClientDataset := TClientDataset.Create(Self);
  ClientDataset.FieldDefs.Add('Id', ftInteger);
  ClientDataset.FieldDefs.Add('Date', ftDateTime);
  ClientDataset.FieldDefs.Add('Decimal', ftBCD, 2);
  ClientDataset.FieldDefs.Find('Decimal').Precision := 5;
  ClientDataset.CreateDataSet;
  Datasource := TDatasource.Create(Self);
  Datasource.Dataset := ClientDataset;

  ClientDataset.Insert;
  ClientDataset.FieldValues['id'] := 1;
  ClientDataset.FieldValues['Date'] := Now;
  ClientDataset.FieldValues['Decimal'] := 7.55;
  ClientDataset.Post;
  ClientDataset.Insert;
  ClientDataset.FieldValues['id'] := 2;
  ClientDataset.FieldValues['Date'] := Now;
  ClientDataset.FieldValues['Decimal'] := 8.2;
  ClientDataset.Post;

  DBGrid := TDBGrid.Create(Self);
  DBGrid.Parent := Self;
  DBGrid.Align := alClient;
  DBGrid.Datasource := Datasource;
end;

它引发了这个异常:'8200@' is not a valid integer value

decimal(18,2) 没有这个问题,如果我注释行 ClientDataset.FieldDefs.Find('Decimal').Precision := 5; 则不会引发错误。

此外,如果没有日期时间字段,则也不会引发错误。这运行良好:

procedure TForm1.FormCreate(Sender: TObject);
var ClientDataset: TClientDataset;
    Datasource: TDatasource;
    DBGrid: TDBGrid;
begin
  ClientDataset := TClientDataset.Create(Self);
  ClientDataset.FieldDefs.Add('Id', ftInteger);
  ClientDataset.FieldDefs.Add('Decimal', ftBCD, 2);
  ClientDataset.FieldDefs.Find('Decimal').Precision := 5;
  ClientDataset.CreateDataSet;
  Datasource := TDatasource.Create(Self);
  Datasource.Dataset := ClientDataset;

  ClientDataset.Insert;
  ClientDataset.FieldValues['id'] := 1;
  ClientDataset.FieldValues['Decimal'] := 7.55;
  ClientDataset.Post;
  ClientDataset.Insert;
  ClientDataset.FieldValues['id'] := 2;
  ClientDataset.FieldValues['Decimal'] := 8.2;
  ClientDataset.Post;

  DBGrid := TDBGrid.Create(Self);
  DBGrid.Parent := Self;
  DBGrid.Align := alClient;
  DBGrid.Datasource := Datasource;
end;

您认为无需将我的所有小数 (5,2) 字段替换为小数 (18,2) 字段即可解决此问题吗?

更新: 这是 ClientDataset 特有的问题。如果我在 TADOQuery 或 TFDMemTable 上打开相同的数据(具有完全相同的日期时间和十进制(5,2)BCD 字段),Delphi 10.4 显示 DBGrid 没有任何问题。

问题也是 DBGrid 特有的。在 DBEdits 上显示这些字段没有问题。

单元 Data.FmtBcd 的版本 10.4 中已对函数 BCDToCurr 和 BCDToCurrency 进行了更改。

Delphi10.3

function BCDToCurr(const BCD: TBcd; var Curr: Currency): Boolean;
  Curr := StrToCurr(string(Bcd));
  Result := True;
end;

function BCDToCurrency(const BCD: TBcd): Currency;
begin
  Result := StrToCurr(string(Bcd)); 
end;

Delphi10.4

function BCDToCurr(const BCD: TBcd; var Curr: Currency): Boolean;
var
  B: TBcd;
  S: string;
const
  DecimalSeparator = '.';
begin
  // B := BCD * 10000;
  B := BCD;
  if BcdScale(B) >= 4 then
    Dec(B.SignSpecialPlaces, 4)
  else if BcdPrecision(B) <= (MaxFMTBcdFractionSize - 4) then
    Inc(B.Precision, 4)
  else
    OverflowError(SBcdOverflow);

  S := BcdToStr(B, DecimalSeparator);
  Round(S, DecimalSeparator, 0);
  // The real format of Currency type is Int64.
  PInt64(@Curr)^ := StrToInt64(S);
  Result := True;
end;

function BCDToCurrency(const BCD: TBcd): Currency;
begin
  BCDToCurr(BCD, Result);
end;

因此,在 BCDToCurr 函数中精度为:=5:

否则如果 BcdPrecision(B) <= (MaxFMTBcdFractionSize - 4) 那么 公司(B.Precision, 4)

和 B.Precision 递增到 9。然后变量 Curr 取了一个不正确的值。

我报告了这个错误,现在已在最近的 Delphi 10.4 更新 1 中修复。