I need something more flexible, encompassing various formats, so I implemented TTimeDiff
as:
uses SysUtils, DateUtils, StrUtils, Math; type TTimeDiff = record type TTimeDiffFormat = (tdfFull, tdfSignificant, tdfAllNonZeros, tdfXNonZeros); procedure Init(const ANow, AThen: TDateTime); class function TimeDiff(const ANow, AThen: TDateTime): TTimeDiff; static; function ToString(const TimeDiffFormat: TTimeDiffFormat; const Delimiter: string = ', '; const NonZerosCount: Byte = 1): string; case Integer of 0: (Years, Months, Days, Houres, Minutes, Seconds: Word); 1: (Values: array[0..5] of Word); end; { TTimeDiff } class function TTimeDiff.TimeDiff(const ANow, AThen: TDateTime): TTimeDiff; begin Result.Init(ANow, AThen); end; procedure TTimeDiff.Init(const ANow, AThen: TDateTime); begin Years := YearsBetween(ANow, AThen); Months := MonthsBetween(ANow, AThen) mod 12; Days := DaysBetween(IncMonth(Min(ANow, AThen), Years * 12 + Months), Max(ANow, AThen)); Houres := HoursBetween(ANow, AThen) mod 24; Minutes := MinutesBetween(ANow, AThen) mod 60; Seconds := SecondsBetween(ANow, AThen) mod 60; end; function TTimeDiff.ToString(const TimeDiffFormat: TTimeDiffFormat; const Delimiter: string = ', '; const NonZerosCount: Byte = 1): string; const Captions: array [0..5] of string = ('year', 'month', 'day', 'hour', 'minute', 'second'); var I: Integer; VisitedNonZeros: Byte; begin Result := ''; VisitedNonZeros := 0; for I := 0 to 5 do begin if Values[I] > 0 then Inc(VisitedNonZeros); if (TimeDiffFormat = tdfFull) or ((TimeDiffFormat = tdfSignificant) and (VisitedNonZeros > 0)) or ((TimeDiffFormat in [tdfAllNonZeros, tdfXNonZeros]) and (Values[I] > 0)) then begin Result := Result + Format('%d %s%s%s', [Values[I], Captions[I], IfThen(Values[I] = 1, '', 's'), Delimiter]); if (TimeDiffFormat = tdfXNonZeros) and (VisitedNonZeros = NonZerosCount) then Break; end; end; Result := Copy(Result, 1, Length(Result) - Length(Delimiter)); end;
TTimeDiffFormat
explanation:
tdfFull
: includes all parts regardless of their values โโ(years, months, days, hours, minutes and seconds, respectively).
tdfSignificant
: excludes tdfSignificant
parts with a null value
tdfAllNonZeros
: excludes ALL parts with a null value
tdfXNonZeros
: includes only the first X non-zero parts, where X is set to 1 by default
How to use:
var ANow, AThen: TDateTime; Diff: TTimeDiff; begin try ANow := DateUtils.EncodeDateTime(1993, 11, 3, 21, 22, 18, 0); AThen := DateUtils.EncodeDateTime(1993, 9, 21, 6, 21, 34, 0); Writeln('Difference between '); Writeln(FormatDateTime('YYYY/MM/DD HH:NN:SS', ANow), ' and'); Writeln(FormatDateTime('YYYY/MM/DD HH:NN:SS', AThen), ' is:'); Writeln(''); Diff.Init(ANow, AThen); with Diff do begin Writeln(ToString(tdfFull)); Writeln(ToString(tdfSignificant, ' and ')); Writeln(TTimeDiff.TimeDiff(Athen, ANow).ToString(tdfSignificant), ' (inverted)'); Writeln(ToString(tdfAllNonZeros)); Writeln(ToString(tdfXNonZeros, ', ', 2)); Writeln(ToString(tdfXNonZeros)); readln; end; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.
Results:
Difference between 1993/11/03 21:22:18 and 1993/09/21 06:21:34 is: 0 years, 1 month, 13 days, 15 hours, 0 minutes, 43 seconds 1 month and 13 days and 15 hours and 0 minutes and 43 seconds 1 month, 13 days, 15 hours, 0 minutes, 43 seconds (inverted) 1 month, 13 days, 15 hours, 43 seconds 1 month, 13 days 1 month