I need to calculate days and months between dates, ensuring accuracy when crossing month boundaries.
I assume Wolfram Alpha results are correct so I want the same results from my VBA code.
For example:
The difference between "31/03/1955" and "26/05/2024" should be:
The difference between "30/03/1955" and "26/05/2024" should be:
My code in this last case, starting date "30/03/1955" calculates:
Option Explicit
Public Function DiffDate44(ByVal data1 As Variant, ByVal data2 As Variant, dato_richiesto As String, valori_assoluti As Boolean) As Variant
Dim anni As Integer, mesi As Integer, giorni As Integer
Dim giorni_totali As Long
Dim dummy As Date, tempDate As Date
Dim anniStr As String, mesiStr As String, giorniStr As String, giorni_totaliStr As String, spaziatura As String
On Error GoTo ErrorHandler
' Convert inputs to Date type
data1 = CDate(data1)
data2 = CDate(data2)
' Handle absolute values if required
If valori_assoluti = True Then
If data1 > data2 Then
dummy = data1
data1 = data2
data2 = dummy
End If
Else
If data1 > data2 Then
MsgBox "ERRORE: Data1 > Data2!"
DiffDate = "ERRORE DiffDate"
Exit Function
End If
End If
' Calculate total days difference
giorni_totali = Abs(DateDiff("d", data1, data2))
' Calculate years
anni = DateDiff("yyyy", data1, data2)
If DateSerial(Year(data2), Month(data1), Day(data1)) > data2 Then
anni = anni - 1
End If
' Calculate months
tempDate = DateAdd("yyyy", anni, data1)
mesi = DateDiff("m", tempDate, data2)
If DateAdd("m", mesi, tempDate) > data2 Then
mesi = mesi - 1
End If
' Calculate days
giorni = DateDiff("d", DateAdd("m", mesi, DateAdd("yyyy", anni, data1)), data2)
If giorni < 0 Then
mesi = mesi - 1
tempDate = DateAdd("m", mesi, DateAdd("yyyy", anni, data1))
giorni = DateDiff("d", tempDate, data2)
End If
' Adjust for cases where the months calculation might be incorrect
If mesi < 0 Then
mesi = mesi + 12
anni = anni - 1
End If
' Construct output strings
If anni <> 1 Then
anniStr = " anni"
Else
anniStr = " anno"
End If
If mesi <> 1 Then
mesiStr = " mesi"
Else
mesiStr = " mese"
End If
If giorni <> 1 Then
giorniStr = " giorni"
Else
giorniStr = " giorno"
End If
If anni = 0 And mesi = 0 And giorni = giorni_totali Then
giorni_totaliStr = ""
Else
giorni_totaliStr = " (" & CStr(Format(giorni_totali, "#,###")) & " giorni totali)"
End If
' Return requested output
Select Case dato_richiesto
Case "anni"
DiffDate = anni
Case "mesi"
DiffDate = mesi
Case "giorni"
DiffDate = giorni
Case "giorni_totali"
DiffDate = giorni_totali
Case "stringa1"
DiffDate = mesi & mesiStr & ", " & giorni & giorniStr
Case "stringa2"
DiffDate = mesi & mesiStr & ", " & giorni & giorniStr & giorni_totaliStr
Case "nascondi_valori_a_zero"
If anni > 1 Then
anniStr = CStr(anni) & " anni"
If mesi > 1 Or giorni > 1 Then
spaziatura = ", "
End If
ElseIf anni = 1 Then
anniStr = CStr(anni) & " anno"
If mesi >= 1 Or giorni >= 1 Then
spaziatura = ", "
End If
Else
anniStr = ""
spaziatura = ""
End If
anniStr = anniStr & spaziatura
If mesi > 1 Then
mesiStr = CStr(mesi) & " mesi"
If giorni >= 1 Then
spaziatura = ", "
End If
ElseIf mesi = 1 Then
mesiStr = CStr(mesi) & " mese"
If giorni >= 1 Then
spaziatura = ", "
End If
Else
mesiStr = ""
spaziatura = ""
End If
mesiStr = mesiStr & spaziatura
If giorni > 1 Then
giorniStr = CStr(giorni) & " giorni"
ElseIf giorni = 1 Then
giorniStr = CStr(giorni) & " giorno"
Else
giorniStr = ""
End If
DiffDate = TrueTrim(CStr(anniStr & mesiStr & giorniStr & giorni_totaliStr))
If DiffDate = "" Then DiffDate = "nessuna"
Case "stringa3"
DiffDate = anni & anniStr & ", " & mesi & mesiStr & ", " & giorni & giorniStr
Case "stringa4"
DiffDate = anni & anniStr & ", " & mesi & mesiStr & ", " & giorni & giorniStr & giorni_totaliStr
Case "prossimo_compleanno"
DiffDate = data2 & " (" & Giorno_Settimana(data2) & ")"
End Select
Exit Function
ErrorHandler:
MsgBox "Invalid date format. Please enter valid dates."
DiffDate = "ERRORE Invalid Date"
End Function
Adding my own code because other answers are incorrect and want you to believe that duplicates and gaps in results are perfectly acceptable.
Updated: added CountDateOnLastDayAsFullMonth
boolean flag to treat end dates that fall on the last day of the month to count as full months (this will create dup results for some date ranges)
Public Function DateDiffToText(ByVal StartDate As Date, ByVal EndDate As Date, Optional ByVal CountDateOnLastDayAsFullMonth As Boolean = False)
' expresses a date range difference in years, months and days
' useful for countdown timers or age calcs
'
' Set CountDateOnLastDayAsFullMonth = true if you want end dates that fall on the last day of the month to count as full months
' - this will create dup results for some date ranges
'
' year calc - counts any complete calendar years between dates + one year if start day/month is < end day/month
' Month calc - similiar to year calc - counts complete months (regardless of days) between dates
' on the remainder after subtracting complete years + one month if start day of month (DOM) < end DOM
'
' Day calc - based on the DOM - if start DOM <= end DOM then just the days remaining from start to end DOM,
' else we calc days remaining in start month and add days in end month
'
' By: @DJ. - David Johnston - Burnaby, Canada
Dim Years As Integer
Dim Months As Integer
Dim Days As Integer
Dim StartYear As Integer
Dim StartMonth As Integer
Dim StartDay As Integer
Dim StartMonthDay As Integer
Dim EndYear As Integer
Dim EndMonth As Integer
Dim EndDay As Integer
Dim EndMonthDay As Integer
Dim TempMonth As Integer
Dim TempDate As Date
Dim TempDate2 As Date
' split up date parts
StartYear = Year(StartDate)
StartMonth = Month(StartDate)
StartDay = Day(StartDate)
StartMonthDay = (StartMonth * 100) + StartDay
EndYear = Year(EndDate)
EndMonth = Month(EndDate)
EndDay = Day(EndDate)
EndMonthDay = (EndMonth * 100) + EndDay
'Calculate Years
Years = EndYear - StartYear - 1
If EndMonthDay >= StartMonthDay Then Years = Years + 1
'Calculate Months
TempMonth = EndMonth
If EndMonthDay < StartMonthDay Then TempMonth = TempMonth + 12
Months = TempMonth - StartMonth - 1
If EndDay >= StartDay Then Months = Months + 1
' Calculate Days
If StartDay <= EndDay Then
Days = EndDay - StartDay
Else
'get number of days remaing in month by getting last date of the month
TempDate = DateAdd("m", 1, StartDate)
TempDate = DateSerial(Year(TempDate), Month(TempDate), 1)
TempDate = DateAdd("d", -1, TempDate)
If CountDateOnLastDayAsFullMonth = True Then
TempDate2 = DateAdd("m", 1, EndDate)
TempDate2 = DateSerial(Year(TempDate2), Month(TempDate2), 1)
TempDate2 = DateAdd("d", -1, TempDate2)
If EndDate = TempDate2 Then
'special case - end date is last day of month - count it as a full month
Days = Day(TempDate) - StartDay
Months = Months + 1
If Months = 12 Then
Months = 0
Years = Years + 1
End If
Else
Days = Day(TempDate) - StartDay + EndDay
End If
Else
Days = Day(TempDate) - StartDay + EndDay
End If
End If
DateDiffToText = CStr(Years) & " year(s), " & CStr(Months) & " month(s), " & CStr(Days) & " day(s)"
End Function
Results:
Updated: Results with CountDateOnLastDayAsFullMonth = True