0

Delphi has this function:

function VarToDateAsString(const V: TVarData): TDateTime;
....
LResult := VarDateFromStr(S, VAR_LOCALE_USER_DEFAULT, 0, Result);

doing this with today's date:

   VarDateFromStr('07;12;18', VAR_LOCALE_USER_DEFAULT, 0, Result);

then Result will be equal

Result = '07;18;12'

Is any fix possible ?

CODE EXAMPLE

procedure TForm1.Button1Click(Sender: TObject);
var
  LDateTimeVar: Variant;
  LDateTime: TDateTime;
begin
  // Current date separator in OS settings is ';'
  LDateTimeVar := '07;12;18';
  LDateTime := VarToDateTime(LDateTimeVar);
  // Expected LDateTime = '07;12;18', 
  // but will be LDateTime = '07;18;12'  
  ShowMessage(DateToStr(LDateTime));
end;
RBA
  • 12,337
  • 16
  • 79
  • 126
YoungMaster
  • 395
  • 3
  • 17
  • What is the date format? `MM;yy;dd`? What's your locale? Is your sample date the 7th December, 1918? – Andreas Rejbrand Jul 18 '12 at 16:37
  • @Andreas - The format is 'MM;yy;dd' and the example date is today's date. – Sertac Akyuz Jul 18 '12 at 17:55
  • @Sertac: Oh, I see. I need to pay attention to the current date! – Andreas Rejbrand Jul 18 '12 at 17:58
  • 1
    I have tested your code in Delphi XE2, and the result is an EVariantTypeCastError error. Which delphi version do you have? – RBA Jul 18 '12 at 18:19
  • 1
    Due to documentation - VarToDateTime converts the given variant to a TDateTime value. VarToDateTime raises an exception if the conversion fails. I don't understand your result. – RBA Jul 18 '12 at 18:21
  • I don't understand why you keep trying to make up your own date formats instead of using one of the standardized ones for your locale. You're making your (programming) life so much more complicated, and making your code very non-portable to other regions or locales. You really should stop trying to fight against Windows and it's support for various locales and start working with it instead. (And if you insist on inventing your own, write your own functions to convert to and from that format instead of trying to use the built-in ones.) – Ken White Jul 18 '12 at 19:07

1 Answers1

2

VarToDateAsString calls 'oleaut32's VarDateFromStr to perform the conversion, which simply fails if you've got the year in the middle.

variants.VarToDateAsString:

function VarToDateAsString(const V: TVarData): TDateTime;
var
  ...
begin
  _VarToWStr(S, V);
  LResult := VarDateFromStr(S, VAR_LOCALE_USER_DEFAULT, 0, Result);
  ...

Put a breakpoint on the second line and you'll see the Result parameter will reverse the day and the year. You can carry out tests yourself by calling activex.VarDateFromStr.

You can bypass variants.VarToDateAsString calling VarDateFromStr and implement your own function, but don't think using something from sysutils because it doesn't support a year-in the middle format too. sysutils.TryStrToDateTime calls ScanDate which in turn calls GetDateOrder. The below is the entire function:

function GetDateOrder(const DateFormat: string): TDateOrder;
var
  I: Integer;
begin
  Result := doMDY;
  I := 1;
  while I <= Length(DateFormat) do
  begin
    case Chr(Ord(DateFormat[I]) and $DF) of
      'E': Result := doYMD;
      'Y': Result := doYMD;
      'M': Result := doMDY;
      'D': Result := doDMY;
    else
      Inc(I);
      Continue;
    end;
    Exit;
  end;
end;

As you can see there's no outcome ordering with year in the middle.


Looks like you have to parse the string yourself:

uses
  varutils;

function MyVarDateFromStr(const strIn: WideString; LCID: DWORD; dwFlags: Longint;
  out dateOut: TDateTime): HRESULT; stdcall;
begin
  // write your parser here and return a valid 'dateOut'
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  LDateTimeVar: Variant;
  LDateTime: TDateTime;
begin
  varutils.VarDateFromStr := MyVarDateFromStr;   // replace the broken function

  // Current date separator in OS settings is ';'
  LDateTimeVar := '07;12;18';
  LDateTime := VarToDateTime(LDateTimeVar);
  // Expected LDateTime = '07;12;18',
  // but will be LDateTime = '07;18;12'
  ShowMessage(DateToStr(LDateTime));
end;
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169