Calculating the Years, Months, and Days Between Two Points in Time Takes More Than Simple Mathematics
Years ago, I wrote an AutoHotkey timespan calculation function for keeping track of my grandkids ages. (I wrote about it in my Digging Deeper Into AutoHotkey e-book and you can find the original function in the GrandKids.ahk scripts.) Developing the function was a bit of a mindbender. As I remember, I just plowed through the project finding my way by trial-and-error. When I recently reviewed the script, I had a heck of a time figuring out what I had done. I know that I explained the steps in the book, but the script (even with the few comments) remained a mystery to me.
As I thought about it, I soon realized that I might write a better function if I changed how I viewed the problem.
The Problem with Calculating Years, Months, and Days
After a Web search of AutoHotkey sites for routines which determine the timespan between two dates in years, months, and days, I found very little of help. A general search did uncover plenty of Web sites which offer Year, Month, Day calculators, but I wanted a function to build into any AutoHotkey app. I found a couple of examples which used pure math to calculate the variables, but, while those techniques get you close, the number of remaining days tends to go astray.
My original age function—which I wrote years ago—works well, but I wanted something a little cleaner and easier to understand. (If I ever want to convert it into AutoHotkey V2.0 code, then I need to know how it works!) I took a new approach to solving the problem.
Rather than attempting to calculate the difference between the two dates by brute force (pretty much my original tack), I decided to incrementally close in on the answer by comparing the two (start date and end date) in standard computer date formats (YYYYMMDD). First, I would calculate the years and set them aside. Next, I would pull out the number of months leaving me with only a days calculation—one of the parameters available in the AutoHotkey EnvSub command (Var -= Value , TimeUnits) for calculating time differences.
As it turns out, this works great as long as the target day of the month is a higher number than the starting day of the month, and the target month of the year is later than the starting month of the year. In this situation, you can directly figure the difference between each time increment:
Years := SubStr(EndDate,1,4) - SubStr(StartDate,1,4) Months := SubStr(EndDate,5,2) - SubStr(StartDate,5,2) Days := SubStr(EndDate,7,2) - SubStr(StartDate,7,2)
If only the entire problem was that simple.
Issues arise when the starting day of the month is a higher number than the target day of the month and/or the starting month of the year is later than the target month of the year. Two of the above calculations yield negative numbers. Plus, the fact that every four years we experience a February 29th, a number of months (five) contain less than 31 days, and every 12 months we start over with “01” for January only increases the confusion. When either of these negative calculation conditions occurs, we must do our arithmetic with either the previous year and/or previous month.
In spite of all that complication, I now have it working—I think.
The HowLongYearsMonthsDays.ahk Script
I know that the AutoHotkey script name HowLongYearsMonthsDays.ahk extends beyond the range of reasonableness, but I didn’t want to cause any confusion by calling it HowLong.ahk (two miles?). As a demonstration app, I put two DateTime GUI controls into a pop-up window.
To get the Years, Months, and Days, select a Start Date and Stop Date, then hit the Calculate button. AutoHotkey feeds the two dates into the HowLong(StartDate,StopDate) function and a pop-up MsgBox displays the exact number of years, months, and days from the StartDate to the StopDate.
You can use this function to calculate ages by providing the birthday in YYYYMMDD format and today’s date (A_Now) as function parameters (HowLong(Birthday,A_Now). Maybe you want to use it as a countdown to a major event? Or, possibly, you want to keep track of how much time has elapsed since a major turning point in your life (HowLong(CriticalDate,A_Now).
As it turns out, I used much of the same code in the original age calculating function—just in a different order. It uses a couple more techniques to make the app a little more robust and user-friendly:
- The function trims both DateTime stamps removing the time portion. This makes any date comparisons less susceptible to error.
- Test for leap years to set February to 29 days. [Removed this feature as unneeded in the current version, although remains intact, yet inactive, in the commented version at the free scripts page. You can also use the new code to check for leap years.]
- The calculation for the previous month uses the AutoHotkey Format() function rather than the my original SubStr() function kludge to left pad single digit months with a zero.
- The #If directive converts the mouse scroll wheel to Up/Down arrows whenever over an AutoHotkey GUI pop-up window. This facilitates using the mouse wheel to increment the fields in the DateTime GUI controls without resorting to the cursor arrows
Plus, I’ve added more detailed comments to the script which hopefully make it easier to understand the logic.
You can find the HowLongYearsMonthsDays.ahk script at the ComputorEdge AutoHotkey Free Scripts page. If you want the latest code without all my extraneous comments (and deprecated routines), I’ve posted it at the AutoHotkey Forum.
Next time, I’ll dig into the inner workings and hidden mechanisms of the HowLong() function.
This post was proofread by Grammarly
(Any other mistakes are all mine.)
(Full disclosure: If you sign up for a free Grammarly account, I get 20¢. I use the spelling/grammar checking service all the time, but, then again, I write a lot more than most people. I recommend Grammarly because it works and it’s free.)tu