* fixed rounding error in DateTimeToUnix (mantis #12894 and #12936)

* fixed errors in Inc*() routines regarding skipping the black
    home in the data-time continuum around 0, and the same for the
    *Between() functions
  + tests, test...

git-svn-id: trunk@12958 -
This commit is contained in:
Jonas Maebe 2009-03-22 18:26:34 +00:00
parent 1c2d2ae481
commit 6a813b8e7b
3 changed files with 133 additions and 20 deletions

1
.gitattributes vendored
View File

@ -8784,6 +8784,7 @@ tests/webtbs/tw1279.pp svneol=native#text/plain
tests/webtbs/tw1283.pp svneol=native#text/plain
tests/webtbs/tw1284.pp svneol=native#text/plain
tests/webtbs/tw1286.pp svneol=native#text/plain
tests/webtbs/tw12894.pp svneol=native#text/plain
tests/webtbs/tw12942.pp svneol=native#text/plain
tests/webtbs/tw1295.pp svneol=native#text/plain
tests/webtbs/tw12985.pp svneol=native#text/plain

View File

@ -1288,54 +1288,66 @@ end;
{
These functions are declared as approximate by Borland.
A bit strange, since it can be calculated exactly ?
-- No, because you need rounding or truncating (JM)
}
Function DateTimeDiff(const ANow, AThen: TDateTime): TDateTime;
begin
Result:= ANow - AThen;
if (ANow>0) and (AThen<0) then
Result:=Result-0.5
else if (ANow<-1.0) and (AThen>-1.0) then
Result:=Result+0.5;
end;
Function YearsBetween(const ANow, AThen: TDateTime): Integer;
begin
Result:=Trunc(Abs(ANow-AThen)/ApproxDaysPerYear);
Result:=Round(Abs(DateTimeDiff(ANow,AThen))/ApproxDaysPerYear);
end;
Function MonthsBetween(const ANow, AThen: TDateTime): Integer;
begin
Result:=Trunc(Abs(ANow-Athen)/ApproxDaysPerMonth);
Result:=Round(Abs(DateTimeDiff(ANow,AThen))/ApproxDaysPerMonth);
end;
Function WeeksBetween(const ANow, AThen: TDateTime): Integer;
begin
Result:=Trunc(Abs(ANow-AThen)) div 7;
Result:=Round(Abs(DateTimeDiff(ANow,AThen))) div 7;
end;
Function DaysBetween(const ANow, AThen: TDateTime): Integer;
begin
Result:=Trunc(Abs(ANow-AThen));
Result:=Round(Abs(DateTimeDiff(ANow,AThen)));
end;
Function HoursBetween(const ANow, AThen: TDateTime): Int64;
begin
Result:=Trunc(Abs(ANow-AThen)*HoursPerDay);
Result:=Round(Abs(DateTimeDiff(ANow,AThen))*HoursPerDay);
end;
Function MinutesBetween(const ANow, AThen: TDateTime): Int64;
begin
Result:=Trunc(Abs(ANow-AThen)*MinsPerDay);
Result:=Round(Abs(DateTimeDiff(ANow,AThen))*MinsPerDay);
end;
Function SecondsBetween(const ANow, AThen: TDateTime): Int64;
begin
Result:=Trunc(Abs(ANow-AThen)*SecsPerDay);
Result:=Round(Abs(DateTimeDiff(ANow,AThen))*SecsPerDay);
end;
Function MilliSecondsBetween(const ANow, AThen: TDateTime): Int64;
begin
Result:=Trunc(Abs(ANow-AThen)*MSecsPerDay);
Result:=Round(Abs(DateTimeDiff(ANow,AThen))*MSecsPerDay);
end;
@ -1345,49 +1357,49 @@ end;
Function YearSpan(const ANow, AThen: TDateTime): Double;
begin
Result:=Abs(Anow-Athen)/ApproxDaysPerYear;
Result:=Abs(DateTimeDiff(ANow,AThen))/ApproxDaysPerYear;
end;
Function MonthSpan(const ANow, AThen: TDateTime): Double;
begin
Result:=Abs(ANow-AThen)/ApproxDaysPerMonth;
Result:=Abs(DateTimeDiff(ANow,AThen))/ApproxDaysPerMonth;
end;
Function WeekSpan(const ANow, AThen: TDateTime): Double;
begin
Result:=Abs(ANow-AThen) / 7
Result:=Abs(DateTimeDiff(ANow,AThen)) / 7
end;
Function DaySpan(const ANow, AThen: TDateTime): Double;
begin
Result:=Abs(ANow-AThen);
Result:=Abs(DateTimeDiff(ANow,AThen));
end;
Function HourSpan(const ANow, AThen: TDateTime): Double;
begin
Result:=Abs(ANow-AThen)*HoursPerDay;
Result:=Abs(DateTimeDiff(ANow,AThen))*HoursPerDay;
end;
Function MinuteSpan(const ANow, AThen: TDateTime): Double;
begin
Result:=Abs(ANow-AThen)*MinsPerDay;
Result:=Abs(DateTimeDiff(ANow,AThen))*MinsPerDay;
end;
Function SecondSpan(const ANow, AThen: TDateTime): Double;
begin
Result:=Abs(ANow-AThen)*SecsPerDay;
Result:=Abs(DateTimeDiff(ANow,AThen))*SecsPerDay;
end;
Function MilliSecondSpan(const ANow, AThen: TDateTime): Double;
begin
Result:=Abs(ANow-AThen)*MSecsPerDay;
Result:=Abs(DateTimeDiff(ANow,AThen))*MSecsPerDay;
end;
@ -1395,13 +1407,19 @@ end;
Increment/decrement functions.
---------------------------------------------------------------------}
Procedure MaybeSkipTimeWarp(OldDate: TDateTime; var NewDate: TDateTime);
begin
if (OldDate>0) and (NewDate<0) then
NewDate:=NewDate-0.5
else if (OldDate<-1.0) and (NewDate>-1.0) then
NewDate:=NewDate+0.5;
end;
Function IncYear(const AValue: TDateTime; const ANumberOfYears: Integer ): TDateTime;
Var
Y,M,D,H,N,S,MS : Word;
begin
DecodeDateTime(AValue,Y,M,D,H,N,S,MS);
Y:=Y+ANumberOfYears;
@ -1420,6 +1438,7 @@ end;
Function IncWeek(const AValue: TDateTime; const ANumberOfWeeks: Integer): TDateTime;
begin
Result:=AValue+ANumberOfWeeks*7;
MaybeSkipTimeWarp(AValue,Result);
end;
@ -1432,6 +1451,7 @@ end;
Function IncDay(const AValue: TDateTime; const ANumberOfDays: Integer): TDateTime;
begin
Result:=AValue+ANumberOfDays;
MaybeSkipTimeWarp(AValue,Result);
end;
@ -1444,6 +1464,7 @@ end;
Function IncHour(const AValue: TDateTime; const ANumberOfHours: Int64): TDateTime;
begin
Result:=AValue+ANumberOfHours/HoursPerDay;
MaybeSkipTimeWarp(AValue,Result);
end;
@ -1456,6 +1477,7 @@ end;
Function IncMinute(const AValue: TDateTime; const ANumberOfMinutes: Int64): TDateTime;
begin
Result:=AValue+ANumberOfMinutes / MinsPerDay;
MaybeSkipTimeWarp(AValue,Result);
end;
@ -1468,6 +1490,7 @@ end;
Function IncSecond(const AValue: TDateTime; const ANumberOfSeconds: Int64): TDateTime;
begin
Result:=AValue+ANumberOfSeconds / SecsPerDay;
MaybeSkipTimeWarp(AValue,Result);
end;
@ -1480,6 +1503,7 @@ end;
Function IncMilliSecond(const AValue: TDateTime; const ANumberOfMilliSeconds: Int64): TDateTime;
begin
Result:=AValue+ANumberOfMilliSeconds/MSecsPerDay;
MaybeSkipTimeWarp(AValue,Result);
end;
@ -1799,7 +1823,7 @@ Var
Y,M,D,H,N,S,MS : Word;
begin
DecodeDateTime(AValue,Y,M,D,H,N,S,MS);
DecodeDateTime(AValue,Y,M,D,H,N,S,MS);
FV(Y,AYear);
FV(M,AMonth);
FV(D,ADay);
@ -2050,7 +2074,7 @@ end;
Function DateTimeToUnix(const AValue: TDateTime): Int64;
begin
Result:=SecondsBetween(UnixEpoch, AValue);
Result:=Round(DateTimeDiff(AValue,UnixEpoch)*SecsPerDay);
end;

88
tests/webtbs/tw12894.pp Normal file
View File

@ -0,0 +1,88 @@
program Project1;
uses
Classes, SysUtils, DateUtils;
var
utime : longword;
sec : word;
currentDt, convertedDt : TDateTime;
times: longint;
s1, s2: ansistring;
begin
for sec := 0 to 59 do
begin
currentDt := EncodeDateTime(1989, 9, 16, 12, 0, sec, 0);
utime := DateTimeToUnix(currentDt);
convertedDt := UnixToDateTime(utime);
s1:=FormatDateTime('mm/dd/yyyy HH:nn:ss', currentDt);
s2:=FormatDateTime('mm/dd/yyyy HH:nn:ss', convertedDt);
writeln(s1 + ' = ' + IntToStr(utime) + ' = ' + s2);
if (s1<>s2) then
halt(1);
end;
for times:=-10000 to 10000 do
if times<>datetimetounix(unixtodatetime(times)) then
begin
writeln('error for ',times,', becomes ',datetimetounix(unixtodatetime(times)));
halt(2);
end;
// check some borderline cases
currentDt := EncodeDateTime(1899, 12, 29, 6, 0, 0, 0);
convertedDt := EncodeDateTime(1899, 12, 30, 6, 0, 0, 0);
writeln(currentDt:0:4,' - ',convertedDt:0:4);
s1:=FormatDateTime('mm/dd/yyyy HH:nn:ss', currentDt);
s2:=FormatDateTime('mm/dd/yyyy HH:nn:ss', convertedDt);
writeln(s1);
writeln(s2);
if (currentDt<>-1.25) or
(convertedDt<>0.25) or
(s1<>'12-29-1899 06:00:00') or
(s2<>'12-30-1899 06:00:00') or
(DaysBetween(currentDt,convertedDt)<>1) or
(HoursBetween(currentDt,convertedDt)<>24) or
(MinutesBetween(currentDt,convertedDt)<>24*60) or
(SecondsBetween(currentDt,convertedDt)<>24*60*60) or
(MilliSecondsBetween(currentDt,convertedDt)<>24*60*60*1000) then
begin
writeln('between ',s1,' and ',s2);
writeln(DaysBetween(currentDt,convertedDt));
writeln(HoursBetween(currentDt,convertedDt));
writeln(MinutesBetween(currentDt,convertedDt));
writeln(SecondsBetween(currentDt,convertedDt));
writeln(MilliSecondsBetween(currentDt,convertedDt));
halt(3);
end;
currentDt := EncodeDateTime(1899, 12, 30, 6, 0, 0, 0);
convertedDt := EncodeDateTime(1899, 12, 29, 6, 0, 0, 0);
if (DaysBetween(currentDt,convertedDt)<>1) or
(HoursBetween(currentDt,convertedDt)<>24) or
(MinutesBetween(currentDt,convertedDt)<>24*60) or
(SecondsBetween(currentDt,convertedDt)<>24*60*60) or
(MilliSecondsBetween(currentDt,convertedDt)<>24*60*60*1000) then
halt(4);
currentDt := EncodeDateTime(1898, 12, 30, 6, 0, 0, 0);
convertedDt := EncodeDateTime(1899, 12, 30, 6, 0, 0, 0);
if (YearsBetween(currentDt,convertedDt)<>1) or
(MonthsBetween(currentDt,convertedDt)<>12) or
(DaysBetween(currentDt,convertedDt)<>365) or
(HoursBetween(currentDt,convertedDt)<>365*24) or
(MinutesBetween(currentDt,convertedDt)<>365*24*60) or
(SecondsBetween(currentDt,convertedDt)<>365*24*60*60) or
(MilliSecondsBetween(currentDt,convertedDt)<>365*24*60*60*1000) then
halt(5);
currentDt := EncodeDateTime(1898, 12, 29, 6, 0, 0, 0);
convertedDt := EncodeDateTime(1899, 12, 30, 6, 0, 0, 0);
if (YearsBetween(currentDt,convertedDt)<>1) or
(MonthsBetween(currentDt,convertedDt)<>12) or
(DaysBetween(currentDt,convertedDt)<>366) or
(HoursBetween(currentDt,convertedDt)<>366*24) or
(MinutesBetween(currentDt,convertedDt)<>366*24*60) or
(SecondsBetween(currentDt,convertedDt)<>366*24*60*60) or
(MilliSecondsBetween(currentDt,convertedDt)<>366*24*60*60*1000) then
halt(6);
// convertedDt:=incseconds(currentDt,
end.