更改 Sysem.Variants.VarToWideStr 的区域设置格式

Change the regional settings format for Sysem.Variants.VarToWideStr

我的应用程序中的第三方组件 (FastReports) 广泛使用 System.Variants.VarToWideStr 函数,这很好,只是它忽略了我需要该应用程序使用的区域设置。

示例:

FormatSettings.ShortDateFormat := 'dd/mm/yyyy';
ShowMessage(VarToWideStr(Date));
FormatSettings.ShortDateFormat := 'yyyy/mm/dd';
ShowMessage(VarToWideStr(Date));

此代码始终returns相同的字符串,忽略了我指示要使用的应用程序的区域设置。

您是否知道另一种更改应用程序(具体为 VarToWideStr)将要使用的区域设置的方法?

我认为您被 Variants 单元和 FastReport 糟糕的设计搞砸了。 VarToWideStr 调用 VarToWideStrDef,后者从单元 System.VarUtils.

调用隐式 _VarToWStr,然后是 DateToWStrViaOS,最后是 VarBStrFromDate

实际上VarBStrFromDate是对函数的引用,它的实现依赖于编译器。在 Windows 32/64 这只是对 VarBstrFromDate from oleaut32.dll. Non-Windows compilers fall back to converting the value via DateTimeToStr (single argument invariant) which uses global format settings and 'C' format specifier 的引用以格式化值。

没有好的解决办法,因为所有这些例程都太依赖于全局状态。幸运的是 (??) 您可以将 VarBStrFromDate 指向您自己的实现。您可以从单元 System.VarUtils 的函数 BackupVarBStrFromDate 中看到的非 Windows 平台的默认实现中获得启发。然后你可以这样做:

uses
  System.SysUtils, System.Variants, System.VarUtils, System.DateUtils;

function MyVarBstrFromDate(dateIn: TDateTime; LCID: Integer; dwFlags: Integer;
  out bstrOut: WideString): HRESULT; stdcall;
begin
  if LCID = VAR_LOCALE_USER_DEFAULT then
  begin
    bstrOut := DateTimeToStr(dateIn);
    Result := VAR_OK;
  end
  else
    Result := VAR_NOTIMPL;
end;

{ ... }

System.VarUtils.VarBstrFromDate := MyVarBstrFromDate;
FormatSettings.ShortDateFormat := 'yyyy-mm-dd';
FormatSettings.LongTimeFormat := 'hh:nn:ss';
Writeln(VarToWideStr(EncodeDate(2019, 11, 29)));
Writeln(VarToWideStr(EncodeDateTime(2019, 11, 29, 10, 30, 50, 700)));

得到结果:

2019-11-29
2019-11-29 10:30:50

您需要注意,这会改变整个应用程序中依赖 VarBstrFromDate 的所有例程的行为。

VarToStr也是如此。另见 here,其中作者建议在转换为字符串之前从变体中提取日期值。