## Function Calculating Timespan in Years, Months, and Days

Post your working scripts, libraries and tools
jackdunning
Posts: 46
Joined: 01 Aug 2016, 18:17

### Function Calculating Timespan in Years, Months, and Days

HowLongYearsMonthsDays.ahk offers a rewrite of the age calculating function found in the GrandKids.ahk script.

After a Web search of the AutoHotkey sites for routines to determine timespans in years, months, and days, I found very little of help. A general search did find 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.

Rather than attempting to calculate the difference between the two dates by brute force (pretty much my original route), I decided to incrementally close in on the answer by comparing the two (start date and end date) 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 that case, you can directly figure the difference between each time increment:

Code: Select all

``````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. The above calculation yields 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.

The Year, Month, Day Calculator
Acting as a demonstration app, two DateTime GUI controls appear in the pop-up window.

Select a Start Date and Stop Date, then hit the Calculate button. AutoHotkey feeds the two dates into the HowLong(StartDate,StopDate) function, then 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 for 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 as 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:

1. The timespan function trims both of the DateTime stamps removing the time portion. This makes any date comparisons less susceptible to error.
2. Test for leap year to set February to 29 days. [Removed as unneeded in the current version, although remains intact, yet inactive, in the commented version at the free scripts page].
3. The calculation for the previous month uses the AutoHotkey Format() function rather than my original SubStr() function kludge to left pad single digit months with a zero.
4. The #If directive converts the mouse scroll wheel to Up/Down arrows whenever hovering 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 get the AutoHotkey code for HowLongYearsMonthsDays.ahk at the ComputorEdge AutoHotkey Free Scripts page.

For a detailed explanation of the steps used in the HowLong() function, see "Calculating Timespans in Years, Months, Days in AutoHotkey (Understanding the HowLong() Function)" at my AutoHotkey blog.

August 29, 2018—I've made some of the changes I mention below.

October 9, 2018—Changed function to swap the two dates when the first date occurs after the second date. Calculation remains the same.

Here is the code for testing without all of the verbose comments found in the version at the site:

Code: Select all

``````Gui, SpanCalc:Add, Text, , Enter Date One:
Gui, SpanCalc:Add, Text, , Enter Date Two:
Gui,SpanCalc:Show, , Calculate How Long

Return

SpanCalcButtonCalculate:
Gui, Submit, NoHide
HowLong(Date1,Date2)
MsgBox, , TimeSpan Calculation, Years %Years%`rMonths %Months%`rDays %Days% %Past%
Return

HowLong(FromDay,ToDay)
{
Global Years,Months,Days,Past
Past := ""

FromDay := SubStr(FromDay,1,8)
ToDay := SubStr(ToDay,1,8)

; If two dates identical

If (ToDay = FromDay)
{
Years := 0, Months := 0, Days := 0
Return
}

; Added to swap dates if in reverse order (looking back). The calculation remains the same.

If (ToDay < FromDay)
{
Temp := Today
ToDay := FromDay
FromDay := Temp
Past := "Ago"
}

Years  := % SubStr(ToDay,5,4) - SubStr(FromDay,5,4) < 0 ? SubStr(ToDay,1,4)-SubStr(FromDay,1,4)-1
: SubStr(ToDay,1,4)-SubStr(FromDay,1,4)

FromYears := Substr(FromDay,1,4)+years . SubStr(FromDay,5,4)

If (Substr(FromYears,5,2) <= Substr(ToDay,5,2)) and (Substr(FromYears,7,2) <= Substr(ToDay,7,2))
Months := Substr(ToDay,5,2) - Substr(FromYears,5,2)
Else If (Substr(FromYears,5,2) < Substr(ToDay,5,2)) and (Substr(FromYears,7,2) > Substr(ToDay,7,2))
Months := Substr(ToDay,5,2) - Substr(FromYears,5,2) - 1
Else If (Substr(FromYears,5,2) > Substr(ToDay,5,2)) and (Substr(FromYears,7,2) <= Substr(ToDay,7,2))
Months := Substr(ToDay,5,2) - Substr(FromYears,5,2) +12
Else If (Substr(FromYears,5,2) >= Substr(ToDay,5,2)) and (Substr(FromYears,7,2) > Substr(ToDay,7,2))
Months := Substr(ToDay,5,2) - Substr(FromYears,5,2) +11

If (Substr(FromYears,7,2) <= Substr(ToDay,7,2))
FromMonth := Substr(ToDay,1,4) . SubStr(ToDay,5,2) . Substr(FromDay,7,2)
Else If Substr(ToDay,5,2) = "01"
FromMonth := Substr(ToDay,1,4)-1 . "12" . Substr(FromDay,7,2)
Else
FromMonth := Substr(ToDay,1,4) . Format("{:02}", SubStr(ToDay,5,2)-1) . Substr(FromDay,7,2)

Date1 := Substr(FromMonth,1,6) . "01"
Date2 := Substr(ToDay,1,6) . "01"
Date2 -= Date1, Days
If (Date2 < Substr(FromDay,7,2)) and (Date2 != 0)
FromMonth := Substr(FromMonth,1,6) . Date2

ToDay -= %FromMonth% , d
Days := ToDay
}

; Enable mousewheel in AutoHotkey GUIs

#If MouseIsOver("ahk_class AutoHotkeyGUI")
WheelUp::Send {Up}
WheelDown::Send {Down}
#If

MouseIsOver(WinTitle)
{
MouseGetPos,,, Win
Return WinExist(WinTitle . " ahk_id " . Win)
}``````
Last edited by jackdunning on 10 Oct 2018, 12:42, edited 11 times in total.
Jack Dunning

I have a passion for helping new users grow maniacal about AutoHotkey Windows scripting.

Jack's AutoHotkey Blog
Free AutoHotkey Scripts and Apps for Learning Script Writing and Generating Ideas
AutoHotkey E-Books
r2997790
Posts: 40
Joined: 02 Feb 2017, 02:46

### Re: Function Calculating Timespan in Years, Months, and Days

Jack,

This is gold. I've been looking for something like this for ages. Thank you so much for sharing it with us. It's a simple thing that is more difficult than it first appears I know.

For my needs I'm going to need to modify to use drop-downs for each of DD MM YYYY as I need to show the month like this... 06 JUNE, 07 JULY etc.

Will see if there's a clever way to detect leap year and month lengths so I can master updating the drop-down lists.

Thanks again for sharing this.

jackdunning
Posts: 46
Joined: 01 Aug 2016, 18:17

### Re: Function Calculating Timespan in Years, Months, and Days

I merely used a set of conditionals for shorter month lengths, but you could calculate it with the EnvSub command:

Code: Select all

``````Date1 := 20200201
Date2 := 20200301
Date2 -= Date1, Days
MsgBox, % Date2    ; Returns 29``````

This code also checks for leap years (as shown) if you use February (0201) and March (0301) for any given year. I might put this in a function to replace the current leap year test. However, if I use this approach, I no longer need to test for leap years and eliminate a number of lines of code. Just calculate the month length. Humm. [August 29, 2018—Done! See code in the code in the first post.]
Jack Dunning

I have a passion for helping new users grow maniacal about AutoHotkey Windows scripting.

Jack's AutoHotkey Blog
Free AutoHotkey Scripts and Apps for Learning Script Writing and Generating Ideas
AutoHotkey E-Books
jackdunning
Posts: 46
Joined: 01 Aug 2016, 18:17

### Re: Function Calculating Timespan in Years, Months, and Days

After discussions, I realized that I had no reason to insist upon the order of the dates. If they appear in the wrong order, I merely swap the two and add the word "Ago" to the Past variable.
Jack Dunning

I have a passion for helping new users grow maniacal about AutoHotkey Windows scripting.

Jack's AutoHotkey Blog
Free AutoHotkey Scripts and Apps for Learning Script Writing and Generating Ideas
AutoHotkey E-Books
SKAN
Posts: 360
Joined: 29 Sep 2013, 16:58

### Re: Function Calculating Timespan in Years, Months, and Days

Very nice!

There is typo ( dot instead of comma ) in following line

Code: Select all

``````HowLong(FromDay,ToDay)
{
Global Years,Months,Days.Past``````
I tried this 11 years ago and wasn't successful in creating something fast enough.
I did have a accurate slow version though: Age()

Seemingly your current version is off by a year: ( 8398 11 30 )
The difference between max permissible dates should be:

9998 12 31 23:59:59
1601 01 01 00:00:00
--------------------
8397 11 30 23:59:59

I will update my Age() function and post it in current forum.
Thanks
Guest

### Re: Function Calculating Timespan in Years, Months, and Days

Related but very useful:

Time() - Count Days, hours, minutes, seconds between dates by HotKeyIt
https://autohotkey.com/board/topic/4266 ... een-dates/
jackdunning
Posts: 46
Joined: 01 Aug 2016, 18:17

### Re: Function Calculating Timespan in Years, Months, and Days

Thanks, SKAN,

I fixed the error in the code.
Seemingly your current version is off by a year: ( 8398 11 30 )
I think I see what you're saying, SKAN. I never tested it at the limits. However, it seems to be accurate in other tests.

Just tested again and it returned:

Years 8397
Months 11
Days 30

Seems to work.
Jack Dunning

I have a passion for helping new users grow maniacal about AutoHotkey Windows scripting.

Jack's AutoHotkey Blog
Free AutoHotkey Scripts and Apps for Learning Script Writing and Generating Ideas
AutoHotkey E-Books
jackdunning
Posts: 46
Joined: 01 Aug 2016, 18:17

### Re: Function Calculating Timespan in Years, Months, and Days

I've written a DateConvert() function for use in conjunction with this function.

Jack Dunning

I have a passion for helping new users grow maniacal about AutoHotkey Windows scripting.

Jack's AutoHotkey Blog
Free AutoHotkey Scripts and Apps for Learning Script Writing and Generating Ideas
AutoHotkey E-Books
AlphaBravo
Posts: 460
Joined: 29 Sep 2013, 22:59

### Re: Function Calculating Timespan in Years, Months, and Days

Jack,
from the last post, I would have to say I disagreed with the results, from Jan 24,2019 to March 18, 2021 in my mind is 2 years, 1 month and 25 days.
from Jan 24 to Jan 31 (end of month) is 7 days + 18 days in March = 25 Days.

from Jan 24 to Feb 24 is 1 month and from Feb 24 to Feb 28 (end of month) is 4 Days + 18 Days in March = 22 Days

so I checked few online Age Calculators, some said 25 days others said 22 days.
www.calculator.net
www.calculatorsoup.com
http://www.calculconversion.com/age-calculator.html
www.timeanddate.com
www.calculator.net wrote:In some situations, the months and days result of this age calculator may be confusing, especially when the starting date is the end of a month. For example, we all count Feb. 20 to March 20 to be one month. However, there are two ways to calculate the age from Feb. 28, 2015 to Mar. 31, 2015. If thinking Feb. 28 to Mar. 28 as one month, then the result is one month and 3 days. If thinking both Feb. 28 and Mar. 31 as the end of the month, then the result is one month. Both calculation results are reasonable. Similar situations exist for dates like Apr. 30 to May 31, May 30 to June 30, etc. The confusion comes from the uneven number of days in different months. In our calculation, we used the former method.

I'll post this function here as an alternative way of calculation, also taking in consideration Leap Years.

Code: Select all

``````HowLong(Date1,Date2){
year1 := SubStr(Date1, 1, 4), 	month1 := SubStr(Date1, 5, 2), 	day1 := SubStr(Date1, 7, 2)
year2 := SubStr(Date2, 1, 4), 	month2 := SubStr(Date2, 5, 2), 	day2 := SubStr(Date2, 7, 2)
month := leapyear(year1) ? [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if (day1 > day2)
day2 := day2 + month[month1], month2 := month2 - 1
if (month1 > month2)
year2 := year2 - 1, month2 := month2 + 12
D:= day2 - day1, 	M:= month2 - month1, 	Y := year2 - year1
;~ MsgBox % Y " Years`n" M " Months`n" D " Days"
return Y " Years`n" M " Months`n" D " Days"
}
leapyear(year){
if (Mod(year, 100) = 0)
return (Mod(year, 400) = 0)
return (Mod(year, 4) = 0)
}``````
jackdunning
Posts: 46
Joined: 01 Aug 2016, 18:17

### Re: Function Calculating Timespan in Years, Months, and Days

For example, we all count Feb. 20 to March 20 to be one month.
AlphaBravo, as you stated, from Jan 24, 2021 to Feb 24, 2021 is one month. Since we can now bank that full month, we can ignore the number of days in January. From Feb 24, 2021 to March 18, 2021 is 22 days.

Also, my function does account for Leap Years.

The function you included generates the answer, 25 days, but I think that reasonable people can disagree on which solution is more appropriate. I prefer counting days from the day of the month that marks the end of the last complete month (e.g. Feb 24).
Jack Dunning

I have a passion for helping new users grow maniacal about AutoHotkey Windows scripting.

Jack's AutoHotkey Blog
Free AutoHotkey Scripts and Apps for Learning Script Writing and Generating Ideas
AutoHotkey E-Books
carno
Posts: 176
Joined: 20 Jun 2014, 16:48

### Re: Function Calculating Timespan in Years, Months, and Days

Excellent function. Added to my personal library including Date Counter below:

Code: Select all

``````#SingleInstance Force ; Ensures that only the last executed instance of script is running
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases

Gui, Add, Edit, vPr ym ; The ym option starts a new column of controls
Gui, Add, Button, default, OK ; The label ButtonOK (if it exists) will run when the button is pressed
Gui, Show, w350, Enter Date in Format: YYYYMMDD
Return ; End of auto-execute section. The script is idle pending user's action

GuiClose:
ButtonOK:
Gui, Submit ; Save input from the user to each control's associated variable
EnvSub, po, %pr%, days

years := po // 365
remain_days := mod(po, 365)

months := remain_days // 30 ; Month taken as 30 days
remain_days := mod(remain_days, 30)

fortnights := remain_days // 15 ; Fortnight taken as 15 days
remain_days := mod(remain_days, 15)

weeks := remain_days // 7
days := mod(remain_days, 7 )

MsgBox % "Total days in between: " . po . "`n`n" . "Years: " . years . "`n" . "Months: " . months . "`n" . "Fortnights: " . fortnights . "`n" . "Weeks: " . weeks . "`n" . "Days: " . days

Clipboard := po

ExitApp

Esc::ExitApp``````
jeeswg
Posts: 5750
Joined: 19 Dec 2016, 01:58
Location: UK

### Re: Function Calculating Timespan in Years, Months, and Days

Hello, I just shared a similar function here. Thanks.
DateAdd/DateDiff with friendly format - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=59825
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA