Log On
Embarcadero Home
Watch, Follow, &
Connect with Us
Share This
QualityCentral
Communities
Articles
Blogs
Resources
Downloads
Help
QualityCentral
Delphi-BCB
RTL
Delphi
Arithmetic
ConvUtils
Date - Time
DateUtils
File Management
Format + Float
Input/Output
Math Unit
Memory, Pointer, Address
Null-terminated strings
Other Classes
Other RTL
Pascal Strings
Regular Expressions
RTL Exceptions
Text Files
Thread support
Typed/Untyped Files
WinAPI
You are not logged in.
Help
Print
Public Report
Report From:
Delphi-BCB/RTL/Delphi/Date - Time
[ Add a report in this area ]
Report #:
3820
Status:
Closed
HoursBetween fails to return the good value
Project:
Delphi
Build #:
6.240
Version:
9.0
Submitted By:
Iman Crawford
Report Type:
Basic functionality failure
Date Reported:
3/16/2003 8:58:06 PM
Severity:
Infrequently encountered problem
Last Updated:
3/20/2012 2:24:39 AM
Platform:
All platforms
Internal Tracking #:
161567
Resolution:
Fixed
(Resolution Comments)
Resolved in Build:
:
15.0.3729.28755
Duplicate of:
None
Voting and Rating
Overall Rating:
(3 Total Ratings)
4.67 out of 5
Total Votes:
5
Description
This has also been reported for BCB/6.0, report #3526. I just copied the same report here.
HoursBetween does not return the correct value of HoursBetween in some cases.
By analysing the behaviour of the functions involved in returning the number of hours, i found that the problem seems to be localised on the Trunc function (called in HoursBetween) returning 999 as the truncated value of 1000.000008, calculated by the Hourspan function.
Steps to Reproduce:
Compile and run the attached program.
See the following output.
FAIL #2
FAIL
FAIL #2 indicates that the expected value of 1000 was not seen from HoursBetween.
program Test;
{$APPTYPE CONSOLE}
uses
SysUtils, DateUtils;
var
datebeg, dateEnd: TDateTime;
RetVal: Integer;
Counter: Integer = 0;
begin
datebeg := EncodeDateTime(2003,1,1,0,0,0,0);
dateend := EncodeDateTime(2003,2,11,15,0,0,0);
RetVal := HoursBetween(datebeg,dateend);
if RetVal = 999 then
Inc(Counter)
else
WriteLn('FAIL #1');
datebeg := EncodeDateTime(2003,1,1,0,0,0,0);
dateend := EncodeDateTime(2003,2,11,16,0,0,0);
RetVal := HoursBetween(datebeg,dateend);
if RetVal = 1000 then
Inc(Counter)
else
WriteLn('FAIL #2');
datebeg := EncodeDateTime(2003,1,1,0,0,0,0);
dateend := EncodeDateTime(2003,2,11,17,0,0,0);
RetVal := HoursBetween(datebeg,dateend);
if RetVal = 1001 then
Inc(Counter)
else
WriteLn('FAIL #3');
if Counter = 3 then
WriteLn('PASS')
else
WriteLn('FAIL');
end.
Workarounds
None
Attachment
None
Comments
Iman Crawford at 3/16/2003 9:28:59 PM
-
Correction, the number being truncated is 999.999999999942 instead of 1000.000008. So the trunc function is working correctly, but HoursBetween returns the wrong number of hours. One may look at using SimpleRoundTo with enough significant digits so milleseconds are not affected. I assumed 9 in the following workaround, not sure how many are needed to leave milleseconds unaffected for TDateTime calculations.
function HoursBetween(const ANow, AThen: TDateTime): Int64;
begin
Result := Trunc(SimpleRoundTo(HourSpan(ANow, AThen), -9));
end;
Iman
David Marcus at 3/20/2003 6:35:36 PM
-
Delphi 7 does the same.
John Herbster at 1/25/2006 5:59:17 PM
-
Given that the FormatDateTime, and DateTimeToStr and related functions do a good job of converting the internal TDateTime values into display values, when these internal values are generated as days and true fractions of a day. The complimentary Str-to-DateTime values also work well.
So our methods of doing date-time difference arithmetic must be compatible with the above functions.
After some experimentation, I found that the date-time to string functions work by first doing a *round to the nearest millisecond-of-a-day*. Then they divide and remainder to get first the milliseconds, then seconds, then minutes, then hours, and then days.
The half millisecond in the following calc will duplicate the effect of the rounding in the date-time to string functions and make the results compatible with what the human user might expect from hand calculations on date and time variables.
So here is what I recommend:
function MinutesBetween_JH(const ANow, AThen: TDateTime): LongInt;
const HalfMillisecond = 0.5/(24*60*60*1000);
var M1, M2: integer;
begin
M1 := trunc((AThen + HalfMillisecond)*(24*60));
M2 := trunc((ANow + HalfMillisecond)*(24*60));
Result := M2 - M1;
end;
The other differences for days, hours, seconds, and milliseconds can be done in a very similar way. Only the multiplier (24*60) must be changed to 1, 24, (24*60*60), and (24*60*60*1000), respectively. Further, for the seconds and milliseconds differences, the type of M1, M2, and Result should be changed to type Int64.
I have not investigated the requirements for values of TDateTime variables less than zero.
--JohnH
John Herbster at 12/11/2006 11:16:54 AM
-
Suggested fixes for the DateUtils Unit
Note that the SysUtils.DateTimeToTimeStamp function is the common function used as the basis for DateToStr, TimeToStr, DateTimeToStr, and FormatDateTime.
This, DateTimeToTimeStamp, function decodes any TDateTime variable into the TTimeStamp record's integer Time {Number of milliseconds since midnight} and Date {One plus number of days since 1/1/0001}.
I suggest that the subject problem of this QC report and the multitude of other problems in the DateUtils module could be fixed by replacing every use of the trunc function on TDateTime variables in DateUtils with the use of the DateTimeToTimeStamp function.
At the same time, the in-code documention of all of the DateUtils functions be revised to clarify the action of these functions so that the programmer could know without testing if MinutesBetween(<01:02:03.004>,<01:03:02.004>) would return 0 or 1; if MinutesBetween(<01:02:03.004>,<01:03:03.003>) would return 0 or 1; and what sign is given to the result.
Possibly DateUtils problems:
3240 (C) EndOfADay long format returns value 1 month out
3820 (O) HoursBetween fails to return the good value
4430 (O) Errors using MinutesBetween function
5953 (O) Some functions that use TFormatSettings are not independant of SysLocale & thread's locale
9184 (D) Incorrect Value returned by MinutesBetween function in the DateUtils Unit
27191 (O) DateTime is loosing precision when accessed as Double from DateTime utils functions
36551 (C) TryEncodeDateTime
37174 (O) CompareDate function in DateUtils.pas use Trunc and can wrong compare
--JohnH, 2006-12-11
John Herbster at 1/15/2008 5:04:08 AM
-
After a lot thought and testing, I filed the QC report #56957, titled "A Fix for DateUtils Date-Time Compare Functions", with sources for a program which tests suggested changes to the DateUtils module. For the details, see that report. --JohnH
View Your Reports
Search
Server Response from: ETNACODE01
Developer Tools
Blackfish SQL
C++Builder
Delphi
FireMonkey
Prism
InterBase
JBuilder
J Optimizer
HTML5 Builder
3rdRail & TurboRuby
Database Tools
Change Manager
DBArtisan
DB Optimizer
ER/Studio
Performance Center
Rapid SQL
Technical Articles
Tutorials
White Papers
Press Releases
Newsletters
Add Content (GetPublished)
Audio
Audio & Video
Video
Bugs & Suggestions (QualityCentral)
Discussion Forums
Examples (CodeCentral)
Tags
Technology Partners
Downloads
Free Trials
Registered User Downloads
Beta Programs
Add Content (GetPublished)
Articles
Blogs
Bugs & Suggestions (QualityCentral)
Discussion Forums
Examples (CodeCentral)
Member Services
About
Connect with Us