Recent

Author Topic: [SOLVED]MonthsBetween() function behave differently with (03, 04, 05,07)  (Read 2041 times)

kito

  • New Member
  • *
  • Posts: 37
Hello,
 I made a little vacancy project I tested the function on random dates everything was perfect. unfortunately, I never thought  it will calculate differently  with those 04 months
(March, April, May, July) 


please notice that date formatting: YYYY/mm/dd


MonthsBetween(2021/01/01,2021/12/31)  ------------>  11
MonthsBetween(2021/02/01,2021/12/31)  ------------>  10
MonthsBetween(2021/03/01,2021/12/31)  ------------>  10  expected to be 09
MonthsBetween(2021/04/01,2021/12/31)  ------------>  09  expected to be 08
MonthsBetween(2021/05/01,2021/12/31)  ------------>  08  expected to be 07
MonthsBetween(2021/06/01,2021/12/31)  ------------>  06
MonthsBetween(2021/07/01,2021/12/31)  ------------> 06  expected to be 05
MonthsBetween(2021/08/01,2021/12/31)  ------------>  04 
MonthsBetween(2021/09/01,2021/12/31)  ------------>  03
MonthsBetween(2021/10/01,2021/12/31)  ------------>  02
MonthsBetween(2021/11/01,2021/12/31)  ------------>  01
MonthsBetween(2021/12/01,2021/12/31)  ------------>  00

I  already printed all the Holiday statements for the employees  I made a huge mess my boss is so angry  :-[  unlike some of my coworkers  :D

So  what's wrong, is that normal and exactly how it suppose to work ?
« Last Edit: August 06, 2021, 02:12:59 am by kito »

wp

  • Hero Member
  • *****
  • Posts: 8747
Look at the source code (CTRL+click on the identifier "MonthsBetween" in your code to open its source file):
Code: Pascal  [Select][+][-]
  1. Function MonthsBetween(const ANow, AThen: TDateTime; AExact : Boolean = False): Integer;
  2.  
  3. var
  4.   y, m, d: Word;
  5.  
  6. begin
  7.   if AExact and (ANow >= -DateDelta) and (AThen >= -DateDelta) and
  8.      (ANow <= MaxDateTime) and (AThen <= MaxDateTime) then
  9.     begin
  10.     PeriodBetween(ANow, AThen, y, m, d);
  11.     Result := y*12 + m;
  12.     end
  13.   else
  14.     Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)/ApproxDaysPerMonth);
  15. end;
  16.  
You see that there is a "hidden" argument "Exact" which is false by default. In this case, the function calculates the DateTimeDiff between the two dates in TDataeTime units (days), and in order to get months it divides that by the average days per month which you can find in the top of the unit to have the value 30.4375. But since the exact length of a month depends on the month itself (and the year in case of February) the result will not be "exact". This behaviour is documented in the help file of MonthsBetween: "MonthsBetween returns the number of whole months between ANow and AThen. This number is an approximation, based on an average number of days of 30.4375 per month (average over 4 years). This means the fractional part of a month is dropped."

However, when you explicitely call the function with the "Exact" parameter set to true then the function calculates the difference between the two dates as years, months, and days. Now it neglects the days and takes the difference in month values (plus the 12-fold difference in year values). Now the results are as expected.

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. uses
  3.   SysUtils, DateUtils;
  4. var
  5.   d1, d2: TDateTime;
  6.   m, mBetween: Integer;
  7. begin
  8.   d2 := EncodeDate(2021, 12, 31);
  9.   for m := 1 to 12 do
  10.   begin
  11.     d1 := EncodeDate(2021, m, 1);
  12.     mBetween := MonthsBetween(d2, d1, true);
  13.     WriteLn(DateToStr(d1), '...', DateToStr(d2), ' = ', mBetween, ' months');
  14.   end;
  15.   ReadLn;
  16. end.
« Last Edit: August 06, 2021, 12:25:30 am by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

engkin

  • Hero Member
  • *****
  • Posts: 2924
Lol,  :D

How come DaysBetween function doesn't have this exact extra feature?

kito

  • New Member
  • *
  • Posts: 37
  thank you for this generous clarification and detailed examples, indeed I must always check the source code, and the documentation. like it said  never build decisions by guessings or assuming
thanks again I really appreciate all your efforts in this forum.
 

 

lucamar

  • Hero Member
  • *****
  • Posts: 4219
How come DaysBetween function doesn't have this exact extra feature?

Because it calculates the exact value always: a simple substraction ... with a small milliseconds adjustment to cope with floating-point weirdness.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

 

TinyPortal © 2005-2018