【问题标题】:Why TFDBatchMove raises exception ELocalTimeInvalid for date field with value "03/11/2019"?为什么 TFDBatchMove 为值为“03/11/2019”的日期字段引发异常 ELocalTimeInvalid?
【发布时间】:2019-04-29 23:39:18
【问题描述】:

我在 Windows 7 SP1 机器上使用 Delphi 10.3 Rio Update 1。

我的程序的目的是将TFDMemtable 转换为 JSON 格式。由于我无法理解的原因,当此 TFDMemTable 的日期字段具有值 '03/11/2019',使用 DisplayFormat "day/month/year" 时,它会引发异常:

Project ProjMemtabJSON.exe 引发异常类 ELocalTimeInvalid 并显示消息“给定的“2019 年 3 月 11 日”当地时间无效(位于 DST 之前的缺失时段内)。

"Nov, 3rd 2019" 不同的任何其他日期都可以正常工作。

我不知道这里发生了什么!

program ProjMemtabJSON;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Classes,
  System.JSON,
  FireDAC.Comp.DataSet,
  FireDAC.Comp.Client,
  FireDAC.Comp.BatchMove,
  FireDAC.Comp.BatchMove.DataSet,
  FireDAC.Comp.BatchMove.JSON,
  Data.DB;

Var
  Fmemtable   : TFDmemtable ;
  FJSONArray  : TJSONArray;
  FDBatchMoveJSONWriter1     : TFDBatchMoveJSONWriter;
  FDBatchMoveDataSetReader1  : TFDBatchMoveDataSetReader;
  FDBatchMove1               : TFDBatchMove;

procedure  CreateMemtable;
begin
  Fmemtable := TFDmemtable.Create(nil);
  FMemTable.FieldDefs.Add('ID', ftInteger, 0, False);
  FMemTable.FieldDefs.Add('Name', ftString, 20, False);
  FMemTable.FieldDefs.Add('Status', ftString, 20, False);
  FMemTable.FieldDefs.Add('Duedate', ftdatetime,0, False);
  FMemTable.CreateDataSet;
end;

procedure FillMemtable;
begin
  FMemtable.Append;
  FMemtable.Fields[0].Value := 10;                           // ID
  FMemtable.Fields[1].Value := 'John';                       // Name
  FMemtable.Fields[2].Value := 'Active';                    // Status
  { ==> HERE IS THE PROBLEM :  change this date to 03/11/2019  i.e.  03/Nov/2019 and an error will raise }
  FMemtable.Fields[3].Value := StrToDate('02/11/2019');     // Due date dd/mm/yyyy
end;

procedure PrintMemtable;
begin
  writeln('ID       : ' ,Fmemtable.Fields[0].AsString);
  writeln('Name     : ' ,Fmemtable.Fields[1].AsString);
  writeln('Status   : ' ,Fmemtable.Fields[2].AsString);
  writeln('Due Date : ' ,Fmemtable.Fields[3].AsString);
end;

function TableToJson : TJSONArray;
begin
  Result := TJSONArray.Create;
  try
    FDBatchMoveDataSetReader1 := TFDBatchMoveDataSetReader.Create(nil);
    FDBatchMoveJSONWriter1    := TFDBatchMoveJSONWriter.Create(nil);
    FDBatchMove1              := TFDBatchMove.Create(nil);

    FDBatchMove1.Reader       := FDBatchMoveDataSetReader1 ;
    FDBatchMove1.Writer       := FDBatchMoveJSONWriter1;

    try
      if not FMemtable.Active then
        FMemtable.Active := True;
      FDBatchMoveDataSetReader1.DataSet := FMemtable;
      FDBatchMoveJSONWriter1.JsonArray  := Result;
      FDBatchMove1.Execute;
    except
      on E: Exception do
        raise Exception.Create('Error Message: ' + E.Message);
    end;
  finally
    FDBatchMoveDataSetReader1.Free;
    FDBatchMoveJSONWriter1.Free;
    FDBatchMove1.Free;
  end;
end;

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }

    Creatememtable;
    FillMemtable;
    PrintMemtable;

    FJSONArray   := TableToJSON;

    readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

【问题讨论】:

  • 无论我如何在我的 Windows 10 机器上设置短日期格式(dd/mm/yyyymm/dd/yyyy)以及如何设置月份和日期( 03/11/201911/03/2019)。
  • 如果使用 EncodeDate 而不是 StrToDate 会发生什么?另外,为什么要使用 BatchMove 过程。 TFDMemDataset.SaveToFile 也可以保存 JSON 文件。
  • @Ken,谢谢。我忘了提到我使用的是 Windows 7 SP 1。
  • @JacalarRick,我正在使用 BatchMove,因为我需要将 TJSONArray 作为 Result 用作 TRESTRequest POST 方法调用的 Body !我会尝试 EncodeDate 然后让你知道结果。谢谢。
  • @JacalarRick,EncodeDate 也不行。

标签: json delphi


【解决方案1】:

有一个System.DateUtils 例程可以检查无效时间:

TTimeZone.IsInvalidTime(const ADateTime : TDateTime) : Boolean;

如果您将 DateUtils 添加到您的 Uses 子句中,则将您的 FillMemTable 更新为:

procedure FillMemtable;   
var  MyDate : tDateTime;
begin
  FMemtable.Append;
  FMemtable.Fields[0].Value := 10;                           // ID
  FMemtable.Fields[1].Value := 'John';                       // Name
  FMemtable.Fields[2].Value := 'Active';                    // Status
  { ==> HERE IS THE PROBLEM :  change this date to 03/11/2019  i.e.  03/Nov/2019 and     an error will raise }
  MyDate := StrToDate('03/11/2019');
  if TTimeZone.local.IsInvalidTime(MyDate) then MyDate := MyDate + 0.5; //noon won't be invalid
  FMemtable.Fields[3].Value := MyDate;     // Due date dd/mm/yyyy
end;

或者,如 cmets 中所述,如果您不希望 IF 语句的开销,只需强制所有日期为中午。

我从来没有意识到有时区在午夜切换到/从 DST。不幸的是,如何定义日期(没有时间)。

【讨论】:

  • 非常感谢!非常有用的代码。我按照您的建议更改了代码,现在可以使用。我希望通过应用操作系统补丁或升级来彻底解决这个问题。非常感谢!
  • @jrg,如果你能接受我的回答...当然,这只是一个潜在问题的解决方法。即使在某些时区中的某些日期午夜落在无效时间段内,我相信 datetime 值的时间部分的确切“0”不应该引发此异常:也许 InvalidTime 应该从时间 00 开始: 00:00.01 允许没有时间的日期。
猜你喜欢
  • 2020-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-14
  • 1970-01-01
  • 2020-10-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多