View Issue Details

IDProjectCategoryView StatusLast Update
0031354FPCPackagespublic2017-02-11 18:05
ReporterBart BroersmaAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Platformi386OSWindowsOS VersionWin7
Product VersionProduct Build 
Target Version3.2.0Fixed in Version3.1.1 
Summary0031354: Incorrect result of MontshBetween()
DescriptionMonthsBetween(1900-01-01, 1901-01-01) gives 11, where it should be 12.
Steps To Reproduceuses DateUtils;

var
  Months: Integer;
  D1, D2: TDateTime;

begin
  D1 := EncodeDate(1900, 01, 01);
  D2 := EncodeDate(1901, 01, 01);
  Months := Monthsbetween(D1, D2);
  writeln('Months = ',Months); //outputs 11, expected; 12
end.
Additional InformationThis is due to rounding errors by using division by ApproxDaysPerMonth.

Related issue: 0031233
TagsNo tags attached.
Fixed in Revision35422
FPCOldBugId
FPCTarget
Attached Files
  • monthsbetween.diff (757 bytes)
    Index: packages/rtl-objpas/src/inc/dateutil.inc
    ===================================================================
    --- packages/rtl-objpas/src/inc/dateutil.inc	(revision 35221)
    +++ packages/rtl-objpas/src/inc/dateutil.inc	(working copy)
    @@ -1288,8 +1288,17 @@
     
     
     Function MonthsBetween(const ANow, AThen: TDateTime): Integer;
    +var
    +  y, m, d: Word;
     begin
    -  Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)/ApproxDaysPerMonth);
    +  if (ANow >= -DateDelta) and (AThen >= -DateDelta) and
    +     (ANow <= MaxDateTime) and (AThen <= MaxDateTime) then
    +  begin
    +    PeriodBetween(ANow, AThen, y, m, d);
    +    Result := y*12 + m;
    +  end
    +  else
    +    Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+TDateTimeEpsilon)/ApproxDaysPerMonth);
     end;
     
     
    
    monthsbetween.diff (757 bytes)

Activities

Bart Broersma

2017-02-05 14:14

reporter  

monthsbetween.diff (757 bytes)
Index: packages/rtl-objpas/src/inc/dateutil.inc
===================================================================
--- packages/rtl-objpas/src/inc/dateutil.inc	(revision 35221)
+++ packages/rtl-objpas/src/inc/dateutil.inc	(working copy)
@@ -1288,8 +1288,17 @@
 
 
 Function MonthsBetween(const ANow, AThen: TDateTime): Integer;
+var
+  y, m, d: Word;
 begin
-  Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)/ApproxDaysPerMonth);
+  if (ANow >= -DateDelta) and (AThen >= -DateDelta) and
+     (ANow <= MaxDateTime) and (AThen <= MaxDateTime) then
+  begin
+    PeriodBetween(ANow, AThen, y, m, d);
+    Result := y*12 + m;
+  end
+  else
+    Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+TDateTimeEpsilon)/ApproxDaysPerMonth);
 end;
 
 
monthsbetween.diff (757 bytes)

Bart Broersma

2017-02-05 14:49

reporter   ~0097988

Where it says: ApproxDaysPerYear, it must be: ApproxDaysPerMonth.

Michael Van Canneyt

2017-02-11 16:15

administrator   ~0098105

I checked this. Delphi also outputs 11.

The fact that this is an approximation is Delphi compatible, and is in fact documented:
http://www.freepascal.org/docs-html/rtl/dateutils/monthsbetween.html

For Delphi compatibility reasons, I have added a AExact : Boolean = False parameter, which you can set to True, and then you'll get the exact number calculated using your (slower) approach.

Issue History

Date Modified Username Field Change
2017-02-05 14:14 Bart Broersma New Issue
2017-02-05 14:14 Bart Broersma File Added: monthsbetween.diff
2017-02-05 14:49 Bart Broersma Note Added: 0097988
2017-02-05 20:35 Michael Van Canneyt Assigned To => Michael Van Canneyt
2017-02-05 20:35 Michael Van Canneyt Status new => assigned
2017-02-11 16:15 Michael Van Canneyt Fixed in Revision => 35422
2017-02-11 16:15 Michael Van Canneyt Note Added: 0098105
2017-02-11 16:15 Michael Van Canneyt Status assigned => resolved
2017-02-11 16:15 Michael Van Canneyt Fixed in Version => 3.1.1
2017-02-11 16:15 Michael Van Canneyt Resolution open => fixed
2017-02-11 16:15 Michael Van Canneyt Target Version => 3.2.0
2017-02-11 18:05 Bart Broersma Status resolved => closed