Jump to content
Ian Branch

Variation of FormatDateTime(

Recommended Posts

Hi Team,

Currently "FormatDateTime('dd:hh:mm:ss', EndDateTime - StartDateTime)" only allows 0-31 for the days.

I am trying to subtract DateTimes that may be in the 100s to 200s of days apart and still want to represent the difference as just ddd:hh:mm:ss.  i.e.  123:12:34:56

How can I do this please?

 

Regards & TIA,

Ian

 

Share this post


Link to post

You can easily use a TTimeSpan:

program Project1;
{$APPTYPE CONSOLE}

uses System.SysUtils, System.DateUtils, System.TimeSpan;

var
	StartDateTime, EndDateTime: TDateTime;
	TimePassed: TTimeSpan;
begin
	StartDateTime := EncodeDateTime(2020, 01, 01, 00, 00, 00, 000);
	EndDateTime := Now();

	TimePassed := TTimeSpan.FromDays(EndDateTime - StartDateTime);
	WriteLn(TimePassed.ToString());
	ReadLn;
end.

 

which will output 266.07:29:15.1320000

 

If you want to have it differently, you can easily define your own format, like

function formatTimespan(const timeSpan: TTimeSpan): String;
begin
	Result := String.Format(
		'%d.%.2d:%.2d:%.2d', [
		timeSpan.Days,
		timeSpan.Hours,
		timeSpan.Minutes,
		timeSpan.Seconds
	]);
end;

 

which will then output 266.07:35:12

  • Like 3

Share this post


Link to post

FormatDateTime() and other related functions are meant for formatting a specific date/time, not formatting a duration between 2 date/times.  You are going to have to format a duration manually, like Der showed using TTimeSpan.

Share this post


Link to post

Hi Team,

Thank you for your inputs.  Appreciated.

I have implemented the following..

    FieldByName('Duration').AsString := (IntToStr(DaysBetween(FieldByName('FinishDateTime').AsDateTime, FieldByName('StartDateTime').AsDateTime))
      + ':' + FormatDateTime('hh:mm:ss', FieldByName('FinishDateTime').AsDateTime - FieldByName('StartDateTime').AsDateTime)).PadLeft(12, '0');

This works exactly as I need.

 

Regards,

Ian

Share this post


Link to post

Lars,

For some reason your post is not showing att.

The code is used once only when the User logs out so time isn't really an issue but I take your point.  If the code was used more often I would certainly optimise it as you suggest.

The User's Administrator has the ability to generate ad-hoc reports, but is somewhat illiterate with SQL.  By adding the Duration to the table he doesn't have to figure out how to create it.

 

Regards,

Ian

Share this post


Link to post

Side note 1: for my projects I try to not rely on implementation details of data types. That is, assumption that Datetime1 - Datetime2 is "days between"

Side note 2: usually it's more wise to store some meaningful value in DB and convert it to readable form on display (probably one day you'll want to calc some stats about durations - so you'll have to write 'ddd:hh:mm:ss' => Number convertor)

 

Btw, here's function I use in my project:

const
  NoneLbl = '(None)';
  // Patterns
  RightNowLbl = 'less than a minute';
  MinsPatt = '%d min';
  HoursMinsPatt = '%d hr %.2d min';
  DaysHoursMinsPatt = '%d d ' + HoursMinsPatt;

// Formats number of minutes elapsed from AFrom to ATo.
// If AFrom is 0 returns "None" (process wasn't started)
function FormatMinutesSince(AFrom, ATo: TDateTime): string;
var Mins: Integer;
begin
  if AFrom = 0 then Exit(NoneLbl);
  Mins := Abs(MinutesSince(AFrom, ATo));
  case Mins of
    0:
      Result := RightNowLbl;
    1..MinsPerHour-1:
      Result := Format(MinsPatt, [Mins]);
    MinsPerHour..MinsPerDay-1:
      Result := Format(HoursMinsPatt, [Mins div MinsPerHour, Mins mod MinsPerHour])
    else
      Result := Format(DaysHoursMinsPatt, [Mins div MinsPerDay, (Mins mod MinsPerDay) div MinsPerHour, Mins mod MinsPerHour]);
  end;
end;

 

  • Like 1

Share this post


Link to post
20 hours ago, Ian Branch said:

This works exactly as I need.

Actually, it doesn't, because as I said earlier, FormatDateTime() simply isn't intended to handle a duration between 2 date/time values like are you using it for.  If you don't want to (or cannot) use TTimeSpan, then you should calculate the necessary components manually, eg:

var
  // TS: TTimeSpan;
  StartDateTime, FinishDateTime: TDateTime;
  Days, Hours, Minutes, Seconds: Int64;
begin
  StartDateTime := FieldByName('StartDateTime').AsDateTime;
  FinishDateTime := FieldByName('FinishDateTime').AsDateTime;

  {
  TS := TTimeSpan.Subtract(FinishDateTime, StartDateTime);
  FieldByName('Duration').AsString := Format('%.3d:%.2d:%.2d:%.2d', [TS.Days, TS.Hours, TS.Minutes, TS.Seconds]);
  }

  Seconds := SecondsBetween(FinishDateTime, StartDateTime);

  Days := Seconds div SecsPerDay;
  Seconds := Seconds mod SecsPerDay;

  Hours := Seconds div SecsPerHour;
  Seconds := Seconds mod SecsPerHour;

  Minutes := Seconds div SecsPerMin;
  Seconds := Seconds mod SecsPerMin;

  FieldByName('Duration').AsString := Format('%.3d:%.2d:%.2d:%.2d', [Days, Hours, Minutes, Seconds]);
end;

 

Edited by Remy Lebeau

Share this post


Link to post

Hi Remy,

Intended or  not, my equation produces exactly the same as yours.

See attached screen shot.

The columns are - Start, Finish Yours, Mine.

I am quite willing to accept that my solution is more good luck than good management.  😉

Regards,

Ian

 

Screenshot_1.jpg

Share this post


Link to post
20 hours ago, Ian Branch said:

I am quite willing to accept that my solution is more good luck than good management.  😉

Which is exactly why you shouldn't rely on it.  It depends on internal implementation details that could change one day in the future.  Better to be explicit about the calculations so that if the implementation ever does change then the result will still work as expected.

Edited by Remy Lebeau

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×