Browse Source

* 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 -

Jonas Maebe 16 năm trước cách đây
mục cha
commit
6a813b8e7b
3 tập tin đã thay đổi với 133 bổ sung20 xóa
  1. 1 0
      .gitattributes
  2. 44 20
      rtl/objpas/dateutil.inc
  3. 88 0
      tests/webtbs/tw12894.pp

+ 1 - 0
.gitattributes

@@ -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

+ 44 - 20
rtl/objpas/dateutil.inc

@@ -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 - 0
tests/webtbs/tw12894.pp

@@ -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.