[AccessD] Date Difference As Completed Years-Months-Days

A.D. Tejpal adtp at airtelmail.in
Wed Mar 31 09:26:53 CDT 2010


Gustav,

    Your idea of using the built in DateAdd() function is absolutely great. It ensures consistent results without needing conditional checks for various date combinations, including month ends and leap years. Thanks for coming up with this outstanding solution.

    Amongst the host of similar functions that have been attempted from time to time, yours appears to be the optimum one. 

    Another variation of YMD function, using your approach, is placed below. Results over sample tests at my end appear to be ok.

    Kevin - You could try out Gustav's function and might consider adopting it in preference to that by Chip Pearson.

Best wishes,
A.D. Tejpal
------------

' Sample function for date difference as YMD
' (It uses DateAdd approach evolved by Gustav, 
' as demonstrated in his function YearsMonthsDays())
'===================================
Public Function Fn_GetDateDiffAsYMD( _
            DtFrom As Date, _
            Optional DtUpto As Variant) As String
    ' Returns date difference as a string of
    ' Years, Months, Days
    
    ' If second argument is not supplied, it defaults
    ' to today's date - the function then serves as
    ' age calculator, with first argument being
    ' date of birth.
    
    ' If DtFrom is greater than DtUpto, the result
    ' gets prefixed with "(minus) "
    Dim Dt1 As Date, Dt2 As Date, Dt As Date
    Dim Yr As Long, Mn As Long, Dy As Long
    Dim Prefix As String
    
    If IsMissing(DtUpto) Then
        Dt = Date
    Else
        Dt = IIf(IsDate(DtUpto), DtUpto, Date)
    End If
    
    If Dt = DtFrom Then
        Fn_GetDateDiffAsYMD = "NIL"
        Exit Function
    End If
    
    ' Set Dt2 as the greater one
    If Dt > DtFrom Then
        Dt2 = Dt
        Dt1 = DtFrom
        Prefix = ""
    Else
        Dt2 = DtFrom
        Dt1 = Dt
        Prefix = "(Minus) "
    End If
    
    ' Get number of completed months
    ' Subtract 1 if Dt2 is not the last day of
    ' month and Day(Dt2) < Day(Dt1)
    Mn = DateDiff("m", Dt1, Dt2) - _
            IIf(Day(Dt2 + 1) > 1 And _
            Day(Dt2) < Day(Dt1), 1, 0)
    
    ' Get the difference of days between Dt2
    ' and projected date in last completed month
    ' (obtained by adding Mn months to Dt1)
    Dt = DateAdd("m", Mn, Dt1)
    Dy = DateDiff("d", Dt, Dt2)
    
    Yr = Mn \ 12
    Mn = Mn Mod 12
    
    Fn_GetDateDiffAsYMD = Prefix & _
                IIf(Yr > 0, Yr & _
                IIf(Yr > 1, " Years", " Year"), "") & _
                IIf(Yr > 0 And Mn > 0, ", ", "") & _
                IIf(Mn > 0, Mn & _
                IIf(Mn > 1, " Months", " Month"), "") & _
                IIf((Yr > 0 Or Mn > 0) And Dy > 0, _
                ", ", "") & IIf(Dy > 0, Dy & _
                IIf(Dy > 1, " Days", " Day"), "")
End Function
'==================================

  ----- Original Message ----- 
  From: Gustav Brock 
  To: accessd at databaseadvisors.com 
  Sent: Tuesday, March 30, 2010 23:10
  Subject: Re: [AccessD] Date Difference As Completed Years-Months-Days


  Hi A.D.

  I can see I missed your point in my first posting ...

  However, to handle the 30/31 days problem (not to say the 28/29 days of February) when counting months and days, I have redesigned my old Months function to work the same way as the Years function:

  <code>
  Public Function Months( _
    ByVal datDate1 As Date, _
    ByVal datDate2 As Date, _
    Optional ByVal booLinear As Boolean) _
    As Integer

  ' Returns the difference in full months between datDate1 and datDate2.

  ' <<Snipped>>
    
  End Function
  </code>

  Further, by counting the total months this can easily be converted to full years and remaining months
  Now, all that is left is to find the count of remaining days. This is not easy as it will be zero for many date combinations where both dates are at ultimo of a month, for example for both of these intervals:

    From 2015-03-30 to 2016-04-30
    From 2015-03-31 to 2016-04-30

  But I've created a function that does this:

  <code>
  Public Function YearsMonthsDays( _
    ByVal datDate1 As Date, _
    ByVal datDate2 As Date, _
    Optional ByRef lngYears As Long, _
    Optional ByRef lngMonths As Long, _
    Optional ByRef lngDays As Long) _
    As String
    
  ' Returns the difference in years, months, and days between datDate1 and datDate2.
  '
  ' Calculates correctly for:
  '   negative differences
  '   leap years
  '   dates of 29. February
  '   date/time values with embedded time values
  '   negative date/time values (prior to 1899-12-29)
  '
  ' Gustav Brock, Cactus Data ApS.
  ' 2010-03-30.

    ' Count of months in a calendar year.
    Const cintMonths  As Integer = 12
    
    Dim datDateMonth  As Date
    Dim intDays       As Integer
    
    ' No special error handling.
    On Error Resume Next
    
    lngMonths = Months(datDate1, datDate2)
    
    datDateMonth = DateAdd("m", lngMonths, datDate1)
    lngDays = DateDiff("d", datDateMonth, datDate2)
    intDays = Sgn(lngDays)
    If intDays <> 0 Then
      If intDays <> Sgn(DateDiff("d", datDate1, datDate2)) Then
        lngDays = 0
      End If
    End If
    
    lngYears = lngMonths \ cintMonths
    lngMonths = lngMonths Mod cintMonths
    
    YearsMonthsDays = CStr(lngYears) & " year(s), " & CStr(lngMonths) & " month(s), " & CStr(lngDays) & " day(s)"

  End Function
  </code>

  It will even count correctly for negative periods - where date1 is later than date2.
  To allow for general use, it also returns ByRef the count of years, months, and days while the normal return value is a formatted string like:

    12 year(s), 7 month(s), 3 day(s)

  I think this should fulfil your requirements.

  /gustav


More information about the AccessD mailing list