TTimePicker adds a 1899/12/30 date when changed

128 Views Asked by At

I am busy writing a program in Delphi 11.3 that works with times. Specifically, a start and end time, and it's calculating the time spent between the two, as well as if the one is bigger than the other.

Things are going haywire despite using MinutesBetween() or MinuteSpan(). My start time may be 08:00 and my end time 09:30. If I use if start < end, I'll get False yet it is True. Calculating the time spent equates to over 100 years. Odd stuff like this.

As a test, I wrote a small program with the following code:

mmo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz',tmpckr1.Time)+'   '
+FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz',tmpckr2.Time)+'   '
+IntToStr(MinutesBetween(tmpckr1.Time,tmpckr2.Time))+'   '
+FloatToStr(MinuteSpan(tmpckr1.Time,tmpckr2.Time)));

This is running in a TTimer. The output is as expected:

2023/09/18 20:46:29.571   2023/09/18 20:46:29.571   0   0

I'll click on tmpckr1 and then OK tick at the bottom. Note I don't change anything on the time and this is the result.

1899/12/30 20:46:29.571   2023/09/18 20:46:29.571   65069280   65069280

I repeat and this time with tmpckr2:

2023/09/18 20:46:29.571   1899/12/30 20:46:29.571   65069280   65069280

Is there a way to prevent this from happening, or will I have to do some magic using EncodeDate(), EncodeTime(), ReplaceDate() and ReplaceTime() so that the TTime value of the TTimePickers actually match, or am I over-complicating things?

If I'm just dealing with TTime, why is the date even a factor? Why is the date changing in the TTimePicker once it has an OnChange event?

2

There are 2 best solutions below

1
Alirio Gavidia On

Well. TDateTime is a double data type, Where int part is for store date and fractional part for time. So 1232.25 implies 1232 for date (1232 days from 1899/12/30) and 0.25 (a quarter of day, 24/4 ergo 6 am). Good for date-time arithmetic.

If you want to ignore the Date Part, change

MinutesBetween(tmpckr1.Time,tmpckr2.Time)

for

MinutesBetween(frac(tmpckr1.Time),frac(tmpckr2.Time))

If you want to ignore the time Part, change

MinutesBetween(trunc(tmpckr1.Time),trunc(tmpckr2.Time))

now, why is tmpckr2 returning TDateTime or Time, i'm not sure.

1
Remy Lebeau On

You are asking FormatDateTime() to output a date for a TTime value that does not have a date component defined.

TDate, TTime, and TDateTime are all just aliases for Double, where the "date component" represents 1899-12-30 when the integral portion of the Double is 0.

TTimePicker.Time is supposed to return a TTime value with only a time component present, not a date component. So, the fact that your output includes a non-zero date at all is clearly a bug, but that doesn't matter because you should be ignoring the date to begin with since it has no defined meaning in this situation.

You need to fix your code to ignore the date when formatting the TTimePicket.Time value. I also find it best to use the RTL's DateOf() and TimeOf() functions when dealing with TDate/TTime/TDateTime values containing undefined components, especially values returned by UI controls like TDateTimePicker, TMonthCalendar, TTimePicker, etc for exactly this kind of situation.

Try this:

FormatDateTime('hh:nn:ss.zzz', TimeOf(tmpckr1.Time))
FormatDateTime('hh:nn:ss.zzz', TimeOf(tmpckr2.Time))

// just to demonstrate that a zero date is being returned properly...
FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz', TimeOf(tmpckr1.Time))
FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz', TimeOf(tmpckr2.Time))

MinutesBetween(TimeOf(tmpckr1.Time), TimeOf(tmpckr2.Time)))
MinuteSpan(TimeOf(tmpckr1.Time), TimeOf(tmpckr2.Time)))
start := TimeOf(tmpckr1.Time);
end := TimeOf(tmpckr2.Time);
if start < end then ...