From 6a813b8e7b85dfe08afc395207736d0c8f3794f4 Mon Sep 17 00:00:00 2001 From: Jonas Maebe Date: Sun, 22 Mar 2009 18:26:34 +0000 Subject: [PATCH] * 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 - --- .gitattributes | 1 + rtl/objpas/dateutil.inc | 64 ++++++++++++++++++++---------- tests/webtbs/tw12894.pp | 88 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 tests/webtbs/tw12894.pp diff --git a/.gitattributes b/.gitattributes index efbc97b0c6..7c09d6112c 100644 --- a/.gitattributes +++ b/.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 diff --git a/rtl/objpas/dateutil.inc b/rtl/objpas/dateutil.inc index 4e78dde4f6..64c6b7bfdc 100644 --- a/rtl/objpas/dateutil.inc +++ b/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; diff --git a/tests/webtbs/tw12894.pp b/tests/webtbs/tw12894.pp new file mode 100644 index 0000000000..b1388a3e88 --- /dev/null +++ b/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.