Published on System iNetwork (http://systeminetwork.com)
Calculate Years, Months, and Days Between Two Days
By cbushong
Created Apr 28 2008 - 18:48

By:
Bob Cozzi [1]

Date Duration the Way You Really Want It

Normally when you subtract two date variables, you receive the number of years, months, or days between those two dates. For example, if you were born the day Neil Armstrong and Buzz Aldrin landed on the moon (July 20, 1969), and you perform data arithmetic using SUBDUR or the %DIFF built-in function, you would get this:

Years = 38
Months = 465
Days = 14,157

These are accurate as of this writing anyway. But what if you really wanted to know that it has been 38 years, 9 months, and 5 days since man first landed on the moon -- how would you calculate that in RPG IV?

At first, I had an elaborate scheme to calculate these values using various algorithms I saw used in old C code years ago. But then it dawned on me, why do any work at all? Why not let RPG IV do it for me.

The solution came to me and is so simply that initially I didn't believe it would work, but it does. Here's the basic RPG IV code:

     D baseDate        S               D   Inz DatFmt(*ISO)
     D dur             S               D   Inz DatFmt(*ISO)
     D nDays           S             20I 0

      /free
          nDays = %diff(startDT : endDT : *DAYS);
          dur   = baseDate + %days(nDays);
      /end-free

Two lines of code? Yep, well sort of, but not really. Let me explain...

Let's look at each of these two lines and figure out why they do what we want them to do:

          nDays = %diff(startDT : endDT : *DAYS);

This line subtracts ENDDT (end date) from STARTDT (start date) and returns the number of days between those two dates. That's what the *DAYS value yields on the third parameter of the %DIFF built-in function.

Assuming STARTDT is today's date, and ENDDT is July 20, 1969, then we have calculated that 14,157 days figure previous mentioned. But how do we turn that into years, months, and days?

          dur   = baseDate + %days(nDays);

This line uses a date variable named BASEDATE and adds 14,157 days (via the nDays variable) to it. The result is stored in another date variable named DUR.

The secret to this technique is about to be revealed.

RPG TnT: 101 Dynamic Tips 'n Techniques with RPG IV by Bob Cozzi is available now.
[2] Cozzi wrote down every cool technique he's found over the years, updated Them, and consolidated them into this compact book that is your new desktop companion. The latest book from author Bob Cozzi is available today! This 300-page book contains 101 example RPG IV Tips and Techniques for everyday programming -- from date calculations, to regular express searches, to using APIs. These aren't excerpts, but full working source code examples printed right there. Order your copy today. [3]

If you noticed, the BASEDATE variable is a date variable that is initialed with the INZ keyword. Since no specific initial value is specified, only the INZ keyword is used -- the variable is initialized to the lowest date value, which is D'0001-01-01'.

By adding 14,157 days to January 1, 0001, you get D'0039-10-06' (October 6, 0039).

And there you have it, the number of years, months, and days between those two days. Uh, wait a minute, I wasn't very good at math, so maybe we should think about this for a moment.

This equation produces 39 years, 10 months, and 6 days. That certainly is close, but not a correct result. Everything seems to be one unit greater than it should be. That is curious!

Not really. If you realize that January 1, 0001, is already one year, month, and day greater than zero, you realize that you now have to subtract 1 from each unit. So to complete the equation, we need to add the following:

      /free
          nYears = %subdt(dur:*Years)-1;
          nMonths= %subdt(dur:*Months)-1;
          nDays  = %subdt(dur:*Days)-1;
      /end-free

If we inspect the nYears, nMonths, and nDays variable now, we see that their values are correct:

nYears = 38
nMonths = 9
nDays = 5

So we can say that it has been 38 years, 9 months, and 5 days since man first landed on the moon. And isn't that what we all wanted in the first place?

To tie this all together, I've included an example program with a nice little subprocedure that does the work for you.

     H OPTION(*SRCSTMT:*NODEBUGIO)
     H DFTACTGRP(*NO) ACTGRP(*NEW)

     D howOld          S               D   DatFmt(*ISO)
     D today           S               D   DatFmt(*ISO) Inz(*SYS)
     D birthday        S               D   DatFmt(*ISO) Inz(*SYS)

     D dateDur_T       DS                  Qualified Inz
     D  years                        10I 0
     D  months                       10I 0
     D  days                         10I 0

       // Prototype for the DATEDUR subprocedure
       // NOTE: Parm 3 is optional.
       //       Return value is date, but Years/Months/Days
       //       are +1, so need to -1 from returned value.

     D dateDur         PR              D   DatFmt(*ISO)
     D  startDT                        D   Const DatFmt(*ISO)
     D  endDT                          D   Const DatFmt(*ISO)
     D  theDur                             LikeDS(dateDur_T) OPTIONS(*NOPASS)

     D nYears          S             10I 0
     D nMonths         S             10I 0
     D nDays           S             10I 0

     D myDur           DS                  LikeDS(dateDur_T) Inz

     C                   MOVE      *ON           *INLR

      /free
          birthDay = D'1969-07-20';
          howOld = DateDur(Today:Birthday);

          nYears = %subdt(howOld:*Years)-1;
          nMonths= %subdt(howOld:*Months)-1;
          nDays  = %subdt(howOld:*Days)-1;

          callp  DateDur(today:birthday:myDur);

      /end-free

     P dateDur         B
     D dateDur         PI              D   DatFmt(*ISO)
     D  startDT                        D   Const DatFmt(*ISO)
     D  endDT                          D   Const DatFmt(*ISO)
     D  theDur                             LikeDS(dateDur_T) OPTIONS(*NOPASS)

     D bot             S               D   Inz DatFmt(*ISO)
     D dur             S               D   Inz DatFmt(*ISO)
     D nDays           S             20I 0

      /free
          nDays = %diff(startDT : endDT : *DAYS);
          dur   = bot + %days(nDays);

          if  (%Parms() >= 3);
              theDur.years = %subdt(dur:*years) - 1;
              theDur.months = %subdt(dur:*months) - 1;
              theDur.days = %subdt(dur:*days) - 1;
          endif;
          return  dur;
      /end-free
     P dateDur         E

Bob Cozzi, a black belt in Judo, started practicing Judo in 1974 and began to learn programming in 1975. Today, Bob runs RPG World [4], the most popular third-party, commercial RPG IV Training Event in the world. The next RPG World Conference [5] is open to all, and is coming up on May 4, 2008. If you miss RPG World, join Bob at RPG World Academy at select cities across the U.S. coming Summer 2008.

Copyright © Penton Media

Source URL: http://systeminetwork.com/article/calculate-years-months-and-days-between-two-days

Links:
[1] http://systeminetwork.com/author/bob-cozzi
[2] http://www.amazon.com/dp/1583041214?tag=rw0c-20&camp=14573&creative=327641&linkCode=as1&creativeASIN=1583041214&adid=0FDDKRAMASMW6SZB3462&
[3] http://www.amazon.com/dp/1583041214?tag=rw0c-20&camp=14573&creative=327641&linkCode=as1&creativeASIN=1583041214&adid=0FDDKRAMASMW6SZB3462&
[4] http://www.rpgworld.com
[5] http://www.rpgworld.com