Recent

Author Topic: Extracting time values from TDateTime value  (Read 5078 times)

asdf1337

  • Jr. Member
  • **
  • Posts: 56
Extracting time values from TDateTime value
« on: March 07, 2020, 12:33:31 pm »
Hey,
I need some advice on how to calculate the seconds/minutes/hours/days/weeks/months from a TDateTime value.

It's easy to announce the date+time with DateTimeToStr but how do I get the other infos extracted from a given TDateTime value?
Example:
Code: Pascal  [Select][+][-]
  1. 31-12-19 04:34:44 -> 2 months 5 hour 56 minutes 39 seconds

Thanks in advance.
« Last Edit: March 07, 2020, 12:53:31 pm by asdf1337 »

eljo

  • Sr. Member
  • ****
  • Posts: 468
Re: Extracting time values from TDateTime value
« Reply #1 on: March 07, 2020, 12:36:47 pm »
In the unit DateUtils there are functions like monthsbetween you can use as you see fit.

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Extracting time values from TDateTime value
« Reply #2 on: March 07, 2020, 12:40:15 pm »
One of the nice things about TDateTime is that you can simply subtract two datetime values and THEN convert to a string..... That's why the representation is like that.
You do not need dateutils, but it is comfortable.
E.g.:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. uses sysutils,dateutils;
  3. var
  4.   settings:TFormatSettings;
  5.   B:TDateTime;
  6. begin
  7.   settings := DefaultFormatsettings;
  8.   settings.DateSeparator :='/';
  9.   settings.ShortDateFormat:='yyyy/mm/dd';
  10.   B:= StrToDate('1958/02/21',settings);
  11.   // gives exact distance w/o dateutils
  12.   // simple subtraction
  13.   writeln(DateTimeToStr(Now-B));
  14.   writeln(FormatDateTime('yy',Now-B));
  15.  
  16.   // gives the years as above but needs dateutils.
  17.   writeln(YearsBetween(Now,B));
  18. end.

« Last Edit: March 07, 2020, 01:04:34 pm by Thaddy »
Specialize a type, not a var.

asdf1337

  • Jr. Member
  • **
  • Posts: 56
Re: Extracting time values from TDateTime value
« Reply #3 on: March 07, 2020, 01:02:38 pm »
Following code
Code: Pascal  [Select][+][-]
  1. Program datetimes;
  2.  
  3. Uses sysutils, dateutils;
  4.  
  5. var
  6.     start, start2, ende, diff: TDateTime;
  7.     YY,M,DD,HH,MM,SS,MS: Word;
  8.  
  9. Begin
  10.   start := EncodeDate(2019, 12, 31);
  11.   start2 := EncodeTime(4, 34, 44, 0);
  12.   start := start + start2;
  13.   Writeln ('start is : ',DateTimeToStr(start));
  14.  
  15.   ende := Now;
  16.   Writeln ('ende is : ',DateTimeToStr(ende));
  17.  
  18.   diff := ende - start;
  19.   Writeln ('diff is : ',DateTimeToStr(diff));
  20.  
  21.   writeln('-- output --');
  22.  
  23.   Writeln('secs : ',IntToStr(SecondsBetween(start, ende)));
  24.   Writeln('minutes : ',IntToStr(MinutesBetween(start, ende)));
  25.   Writeln('hours : ',IntToStr(HoursBetween(start, ende)));
  26.   Writeln('weeks : ',IntToStr(WeeksBetween(start, ende)));
  27.   Writeln('months : ',IntToStr(MonthsBetween(start, ende)));
  28.   Writeln('years : ',IntToStr(YearsBetween(start, ende)));
  29.  
  30.   DecodeDate(diff,YY,M,DD);
  31.   Writeln (Format ('Today is %d/%d/%d',[dd,m,yy]));
  32.   DecodeTime(diff,HH,MM,SS,MS);
  33.   Writeln (format('The time is %d:%d:%d',[hh,mm,ss]));
  34. End.
gives
Code: Pascal  [Select][+][-]
  1. start is : 31-12-19 04:34:44
  2. ende is : 7-3-20 13:01:01
  3. diff is : 7-3-00 08:26:17
  4. -- output --
  5. secs : 5819177
  6. minutes : 96986
  7. hours : 1616
  8. weeks : 9
  9. months : 2
  10. years : 0
  11. Today is 7/3/1900
  12. The time is 8:26:17

But it's not exactly what I need as it calculates all the seconds, minutes etc.
The second version gives the correct time difference but the year/month/day is fucked up?

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Extracting time values from TDateTime value
« Reply #4 on: March 07, 2020, 01:06:11 pm »
The second version gives the correct time difference but the year/month/day is fucked up?
No, you do not allow for formatsettings. See my example, since that explains in detail what you asked.
Specialize a type, not a var.

MoCityMM

  • Jr. Member
  • **
  • Posts: 72
Re: Extracting time values from TDateTime value
« Reply #5 on: March 07, 2020, 04:41:34 pm »
Today is a TDateTime question day for Thaddy...  %)

asdf1337

  • Jr. Member
  • **
  • Posts: 56
Re: Extracting time values from TDateTime value
« Reply #6 on: April 04, 2020, 02:38:02 am »
I've tried to implement it

Code: Pascal  [Select][+][-]
  1. program DateTimes;
  2.  
  3. {$mode Delphi}
  4.  
  5. uses SysUtils, DateUtils;
  6.  
  7. function DateTimeToReadableString(const TimeThen, TimeNow: TDateTime; Padding: boolean = False): String;
  8. var
  9.   fDifference: TDateTime;
  10.   fYears, fMonths, fDays: Word;
  11.   fHours, fMinutes, fSeconds, fMilliSeconds: Word;
  12.   fYearsStr, fMonthsStr, fDaysStr: String;
  13.   fHourStr, fMinutesStr, fSecondsStr: String;
  14. begin
  15.   Result := '';
  16.   fDifference := TimeNow - TimeThen;
  17.  
  18.   DecodeDate(fDifference, fYears, fMonths, fDays);
  19.   // the values from DecodeDate are not correct
  20.   fYears := YearsBetween(TimeNow, TimeThen);
  21.   fMonths := MonthsBetween(TimeNow, TimeThen);
  22.  
  23.   DecodeTime(fDifference, fHours, fMinutes, fSeconds, fMilliSeconds);
  24.  
  25.   if fYears > 0 then
  26.   begin
  27.     if Padding then
  28.       fYearsStr := Format('%2d', [fYears])
  29.     else
  30.       fYearsStr := Format('%d', [fYears]);
  31.  
  32.     if fYears > 1 then
  33.       fYearsStr := fYearsStr + ' years'
  34.     else
  35.       fYearsStr := fYearsStr + ' year';
  36.   end;
  37.  
  38.   if fMonths > 0 then
  39.   begin
  40.     if Padding then
  41.       fMonthsStr := Format('%2d', [fMonths])
  42.     else
  43.       fMonthsStr := Format('%d', [fMonths]);
  44.  
  45.     if fMonths > 1 then
  46.       fMonthsStr := fMonthsStr + ' months'
  47.     else
  48.       fMonthsStr := fMonthsStr + ' month';
  49.   end;
  50.  
  51.   if fDays > 0 then
  52.   begin
  53.     if Padding then
  54.       fDaysStr := Format('%2d', [fDays])
  55.     else
  56.       fDaysStr := Format('%d', [fDays]);
  57.  
  58.     if fDays > 1 then
  59.       fDaysStr := fDaysStr + ' days'
  60.     else
  61.       fDaysStr := fDaysStr + ' day';
  62.   end;
  63.  
  64.   if fHours > 0 then
  65.   begin
  66.     if Padding then
  67.       fHourStr := Format('%2d', [fHours])
  68.     else
  69.       fHourStr := Format('%d', [fHours]);
  70.  
  71.     if fHours > 1 then
  72.       fHourStr := fHourStr + ' hours'
  73.     else
  74.       fHourStr := fHourStr + ' hour';
  75.   end;
  76.  
  77.   if fMinutes > 0 then
  78.   begin
  79.     if Padding then
  80.       fMinutesStr := Format('%2d', [fMinutes])
  81.     else
  82.       fMinutesStr := Format('%d', [fMinutes]);
  83.  
  84.     if fMinutes > 1 then
  85.       fMinutesStr := fMinutesStr + ' minutes'
  86.     else
  87.       fMinutesStr := fMinutesStr + ' minute';
  88.   end;
  89.  
  90.   if fSeconds > 0 then
  91.   begin
  92.     if Padding then
  93.       fSecondsStr := Format('%2d', [fSeconds])
  94.     else
  95.       fSecondsStr := Format('%d', [fSeconds]);
  96.  
  97.     if fSeconds > 1 then
  98.       fSecondsStr := fSecondsStr + ' seconds'
  99.     else
  100.       fSecondsStr := fSecondsStr + ' second';
  101.   end;
  102.  
  103.  
  104.   if fYearsStr <> '' then
  105.     Result := fYearsStr;
  106.  
  107.   if fMonthsStr <> '' then
  108.     Result := Result + ' ' + fMonthsStr;
  109.  
  110.   if fDaysStr <> '' then
  111.     Result := Result + ' ' + fDaysStr;
  112.  
  113.   if fHourStr <> '' then
  114.     Result := Result + ' ' + fHourStr;
  115.  
  116.   if fMinutesStr <> '' then
  117.     Result := Result + ' ' + fMinutesStr;
  118.  
  119.   if fSecondsStr <> '' then
  120.     Result := Result + ' ' + fSecondsStr;
  121.  
  122.   if not Padding then
  123.     Result := Trim(Result);
  124. end;
  125.  
  126. var
  127.   x: TDateTime;
  128.  
  129. begin
  130.     writeln('Hello, world!');
  131.     x := Now;
  132.     Sleep(2000);
  133.     writeln(DateTimeToReadableString(x, Now));
  134.     Sleep(6000);
  135.     writeln(DateTimeToReadableString(x, Now));
  136.     writeln('Bye, world!');
  137. end.

If I use the values from DecodeDate directly the output is:

Code: [Select]
Hello, world!
1899 years 12 months 30 days 2 seconds
1899 years 12 months 30 days 8 seconds
Bye, world!
otherwise it is (with code posted above)
Code: [Select]
Hello, world!
30 days 2 seconds
30 days 8 seconds
Bye, world!

Is the function DecodeDate broken?

trev

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2020
  • Former Delphi 1-7, 10.2 user
Re: Extracting time values from TDateTime value
« Reply #7 on: April 04, 2020, 02:59:35 am »
This post may help if I understand what you're trying to do correctly.
« Last Edit: April 04, 2020, 03:01:32 am by trev »

jamie

  • Hero Member
  • *****
  • Posts: 6077
Re: Extracting time values from TDateTime value
« Reply #8 on: April 04, 2020, 03:09:04 am »
The DecodeDate is not broken....

The problem is that The TDateTime type when = 0 starts at the year 1899.. This type can not be used to represent dates before that, well directly anyways  ;)

 So if this does not worry you, then simply subtract 1899 from the results you get which will give you days between.
 
 In this case, it would be 0.

 Its that simple!.. He my EASY BUtton!
The only true wisdom is knowing you know nothing

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Extracting time values from TDateTime value
« Reply #9 on: April 04, 2020, 03:31:50 am »
Hi!

Dont subtract 1899 but 1900.
TDateTime starts on 31.12.1899

Example:

Code: Pascal  [Select][+][-]
  1. const LE = LineEnding;
  2. var
  3. T, delta : TDateTime;
  4. s : string;
  5. y,m,d,h,min,sec,junk: word;
  6. begin
  7. T := EncodeDate(1954,3,1)+EncodeTime(18,45,0,0);
  8. delta := now-t;
  9. showMessage (DateTimeToStr(delta));
  10. DecodeDate(delta,y,m,d);
  11. y := y -1900; // BUG in decodeDate =============================
  12. DecodeTime(delta,h,min,sec,junk);
  13.  
  14. s := IntToStr(y)+' years'+LE+IntTostr(m)+' month'+LE + IntToStr(d) +' days'+LE +
  15.      IntToStr(h)+' hours'+LE+ IntToStr(min)+' minutes'+LE+IntToStr(sec)+' seconds';
  16. showMessage
  17.  ('Time since the great thermonuclear Castle Bravo bomb in Bikini Atoll is '+LE+s);  
  18.  

Every 2-digit-year is assumed to be a year from 1900 + year.
If you get out of the range of TDateTime it starts to get tricky.

Winni

PS: That bug is Delphi compatible
« Last Edit: April 04, 2020, 03:34:01 am by winni »

asdf1337

  • Jr. Member
  • **
  • Posts: 56
Re: Extracting time values from TDateTime value
« Reply #10 on: April 04, 2020, 11:25:44 pm »
The DecodeDate is not broken....

The problem is that The TDateTime type when = 0 starts at the year 1899.. This type can not be used to represent dates before that, well directly anyways  ;)
Well,  DecodeDate is made for TDateTime. It is stated directly as that in the docs!!

PS: That bug is Delphi compatible
Then the exact behaviour should be explained in the docs?!

Can I always rely on values from DecodeTime or are there also Hours/Minutes/SecondsBetween functions which should be preferred (as for the years, months, days)?

eljo

  • Sr. Member
  • ****
  • Posts: 468
Re: Extracting time values from TDateTime value
« Reply #11 on: April 04, 2020, 11:52:51 pm »
The DecodeDate is not broken....

The problem is that The TDateTime type when = 0 starts at the year 1899.. This type can not be used to represent dates before that, well directly anyways  ;)
Well,  DecodeDate is made for TDateTime. It is stated directly as that in the docs!!
true, it is not stated that it supports date difference only dates. so decodedata will break a date in to year,month, day of the date represented it does not understand differences between two dates.
PS: That bug is Delphi compatible
Then the exact behaviour should be explained in the docs?!

Can I always rely on values from DecodeTime or are there also Hours/Minutes/SecondsBetween functions which should be preferred (as for the years, months, days)?
As long as you do not expect the difference between two times yes you can always depend the decodetime to interpret as time passed from the 12 at night of the previous date and for 24 hours only more than that is not supported. Use the diff functions provided.

asdf1337

  • Jr. Member
  • **
  • Posts: 56
Re: Extracting time values from TDateTime value
« Reply #12 on: April 05, 2020, 12:21:42 am »
Okay but is there any function which automatically takes care of the next unit?
Like SecondsBetween will return e.g. 68s, then MinutesBetween returns 1min but for outputting it I'd need 1min and 8seconds :-[

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Extracting time values from TDateTime value
« Reply #13 on: April 05, 2020, 12:29:58 am »
Hi!

Dont use DecodeDate for years before 1900.
Or write your own version.

Astonishing enough: DateTimeToStr does not have this bug.

So you can get away from the decode bug if you get the result with a string from
TDateTimeToStr and then copy year,month,day,hour,minute,second in Strings and convert
them in integers.

Awful solution but you can rely on.

Winni

eljo

  • Sr. Member
  • ****
  • Posts: 468
Re: Extracting time values from TDateTime value
« Reply #14 on: April 05, 2020, 12:43:44 am »
Okay but is there any function which automatically takes care of the next unit?
Like SecondsBetween will return e.g. 68s, then MinutesBetween returns 1min but for outputting it I'd need 1min and 8seconds :-[
there is the procedure Periodbetween in the dateutils for the date part and following is a fast and dirty procedure that calculates the timeBetween.
Code: Pascal  [Select][+][-]
  1. procedure TimeBetween(const aTime:TDateTime; var Hours, Min, Secs:Integer);
  2. const
  3.   SecsPerHour = MinsPerHour*SecsPerMin;
  4. var
  5.   TotalSeconds:UInt64;
  6. begin
  7.   TotalSeconds := Abs(DateTimeDiff(ANow,AThen))+TDateTimeEpsilon)*SecsPerDay;
  8.   //TotalSeconds := TotalSeconds - ((TotalSecods div SecsPerDay)*SecsPerDay); //uncomment this to remove the days.
  9.   Hours := TotalSeconds div SecsPerHour;
  10.   TotalSeconds := TotalSeconds - (Hours * SecsPerHour);
  11.   Min := TotalSeconds div SecsPerMin;
  12.   Secs := TotalSeconds - (Min * SecsPerMin);
  13. end;
  14.  
Keep in mind that the above code was typed directly in the browser and never compiled so some attention is mandatory.

Edit:sorry, sorry. Not mod but div  :-[
« Last Edit: April 05, 2020, 01:05:51 am by eljo »

 

TinyPortal © 2005-2018