happyNoob
happyNoob

Reputation: 65

How to convert a formatted date/time string without separators to a TDateTime?

I have a stringified date/time in an odd format - yyyymmddhhnnss - and while I know how to convert a TDateTime to this format with FormatDateTime(), I have troubles converting it back. I tried using StrToDateTime() with the TFormatOptions parameter, but you obviously cannot set it to have no separator (you either give some separator character, or the default system setting is used).

What are some ways to handle this? I know I could parse the string manually, I can do that, but are there some more efficient ways that I'm missing?

Upvotes: 1

Views: 180

Answers (2)

Leonard
Leonard

Reputation: 446

Another alternative to the excellent (and the most recommended) answer of Andreas is to force format the string to be interpreted correctly by the StrToDateTime() function:

function MyDateTimeFromString(MyString: string): TDateTime;
var
  Fs: TFormatSettings;
begin
  Insert('-', MyString, 5);
  Insert('-', MyString, 8);
  Insert(' ', MyString, 11);
  Insert(':', MyString, 14);
  Insert(':', MyString, 17);
  Fs := TFormatSettings.Create;
  Fs.DateSeparator := '-';
  Fs.TimeSeparator := ':';
  Fs.ShortDateFormat := 'yyyy-mm-dd';
  Fs.ShortTimeFormat := 'hh:nn:ss';
  Result := StrToDateTime(MyString, Fs);
end;

Upvotes: 0

Andreas Rejbrand
Andreas Rejbrand

Reputation: 109003

Every time you have a particular datetime string format you want to parse, best practice is (still) to write your own parser.

Then you know it will be 100% correct and bug free (yes, a simple task like this is very much possible to get 100% bug free), today and in every future Delphi RTL version, and on every system and user locale.

Best practice also requires you to write not only the MyStringToDateTime function, but also the TryMyStringToDateTime and MyStringToDateTimeDef functions.

Here's how I'd do it (uses Math, DateUtils):

function TryMyStringToDateTime(const S: string; out ADateTime: TDateTime): Boolean;
begin

  if S.Length <> 14 then
    Exit(False);

  var LYearStr := Copy(S, 1, 4);
  var LMonthStr := Copy(S, 5, 2);
  var LDayStr := Copy(S, 7, 2);
  var LHourStr := Copy(S, 9, 2);
  var LMinuteStr := Copy(S, 11, 2);
  var LSecondStr := Copy(S, 13, 2);

  var LYear, LMonth, LDay, LHour, LMinute, LSecond: Integer;
  if
    not
      (
        TryStrToInt(LYearStr, LYear)
          and
        TryStrToInt(LMonthStr, LMonth)
          and
        TryStrToInt(LDayStr, LDay)
          and
        TryStrToInt(LHourStr, LHour)
          and
        TryStrToInt(LMinuteStr, LMinute)
          and
        TryStrToInt(LSecondStr, LSecond)
      )
  then
    Exit(False);

  if not InRange(LYear, 1, 9999) then
    Exit(False);

  if not InRange(LMonth, 1, 12) then
    Exit(False);

  if not InRange(LDay, 1, DaysInAMonth(LYear, LMonth)) then
    Exit(False);

  if not InRange(LHour, 0, 23) then
    Exit(False);

  if not InRange(LMinute, 0, 59) then
    Exit(False);

  if not InRange(LSecond, 0, 59) then
    Exit(False);

  ADateTime := EncodeDateTime(LYear, LMonth, LDay, LHour, LMinute, LSecond, 0);
  Result := True;

end;

function MyStringToDateTime(const S: string): TDateTime;
begin
  if not TryMyStringToDateTime(S, Result) then
    raise EConvertError.CreateFmt('Invalid datetime string: "%s"', [S]);
end;

function MyStringToDateTimeDef(const S: string; const ADefault: TDateTime): TDateTime;
begin
  if not TryMyStringToDateTime(S, Result) then
    Result := ADefault;
end;

Upvotes: 3

Related Questions