StrToDate 无法转换从 DateToStr 获得的值

StrToDate cannot convert value obtained from DateToStr

我有一个旧应用程序。它的数据中存储了一个本地化格式的日期。该字符串仅用于显示,因此以本地化形式存在是可以接受的。 现在我们需要将其重新用作 TDateTime。看起来很简单:因为我们从 DateToStr 获得了字符串,所以我们将使用 StrToDate 将其转换回来。所以我写了一个小控制台程序来验证它:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;
var
  S:String;
  D: TDateTime;
begin
  S := DateToStr(Now);
  Writeln(S);
  D := StrToDate(S); //! throws an EConvertError
  Readln;
end.

它抛出一个 EConvertError: 项目 Project1.exe 引发异常 class EConvertError,消息为“28。 9. 2017'不是有效日期'。 这是不正确的,异常提到的日期是有效的!它是刚才通过 DateToStr 生成的。 这对我来说完全没有意义。 这可能是 Windows 10 中的错误吗?

如果我在 OS 的区域设置中输入 dd. M.yyyy 作为短日期格式,我可以重复你的问题。或者 dd. M. yyyy 要获得您在问题中引用的确切错误消息,请注意格式字符串中的 spaces。

StrToDate失败的原因是"sysutils.pas"中的ScanDate函数失败,returns错误。 ScanDate 失败的原因是默认格式设置记录中的日期分隔符不正确。

RTL 使用以下代码检索日期分隔符,其中 LOCALE_SDATE 传递给 LocaleType 参数,“/”传递给 Default 参数。

...
var
  Buffer: array[0..1] of Char;
begin
  if GetLocaleInfo(Locale, LocaleType, Buffer, 2) > 0 then
    Result := Buffer[0] else
  Result := Default;

对于您的日期格式,所需的缓冲区是 3 个字符,因为 RTL 仅提供 2 个字符,API 失败并显示 ERROR_INSUFFICIENT_BUFFER 和上述函数,与失败原因无关, returns 默认日期分隔符“/”。所以这是 Delphi RTL 中的一个错误。

如果您有奇数格式字符串的有效用例,请使用接收格式设置参数的 StrToDate 重载。如果不是,只需在区域设置中更正您的日期字符串即可。

不幸的是,您不能为第一个备选方案应用通用解决方案。也就是说,您可以检索正确的日期分隔符,例如:

function GetDateSeparator: string;
var
  NumChars: Integer;
begin
  NumChars := GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, nil, 0);
  Win32Check(NumChars <> 0);
  SetLength(Result, NumChars);
  Win32Check(GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, PChar(Result), NumChars) <> 0);
  SetLength(Result, Length(Result) - 1);
end;

但是您不能将返回的 '. ' 分配给 TFormatSettingsDateSeparator,因为它需要一个 Char。你必须假装你知道分隔符是 '.' 并使用它,这只有效,因为第二个字符是 space ,它在 ScanDate.

中被剥离