title: Efficient and accurate age calculation (in years, months, or weeks) in R given birth date and an arbitrary date
body: <p>I am facing the common task of calculating the age (in years, months, or weeks) given the date of birth and an arbitrary date. The thing is that quite often I have to do this over many many records (>300 millions), so performance is a key issue here.</p>
  
  <p>After a quick search in SO and Google I found 3 alternatives:</p>
  
  <ul>
  <li>A common arithmetic procedure (/365.25) (<a href="https://stackoverflow.com/questions/31048670/calculate-age-with-decimal-places-in-r">link</a>)</li>
  <li>Using functions <code>new_interval()</code> and <code>duration()</code> from package <code>lubridate</code> (<a href="http://cran.r-project.org/web/packages/lubridate/index.html" rel="noreferrer">link</a>)</li>
  <li>Function <code>age_calc()</code> from package <code>eeptools</code> (<a href="http://blog.jsonbecker.com/2013/06/calculating-age-in-r.html#fn:wrote" rel="noreferrer">link</a>, <a href="https://mcfromnz.wordpress.com/2013/06/12/updated-age-calculation-function/" rel="noreferrer">link</a>, <a href="https://github.com/jknowles/eeptools" rel="noreferrer">link</a>)</li>
  </ul>
  
  <p>So, here's my toy code:</p>
  
  <pre><code># Some toy birthdates
  birthdate &lt;- as.Date(c("1978-12-30", "1978-12-31", "1979-01-01", 
                         "1962-12-30", "1962-12-31", "1963-01-01", 
                         "2000-06-16", "2000-06-17", "2000-06-18", 
                         "2007-03-18", "2007-03-19", "2007-03-20", 
                         "1968-02-29", "1968-02-29", "1968-02-29"))
  
  # Given dates to calculate the age
  givendate &lt;- as.Date(c("2015-12-31", "2015-12-31", "2015-12-31", 
                         "2015-12-31", "2015-12-31", "2015-12-31", 
                         "2050-06-17", "2050-06-17", "2050-06-17",
                         "2008-03-19", "2008-03-19", "2008-03-19", 
                         "2015-02-28", "2015-03-01", "2015-03-02"))
  
  # Using a common arithmetic procedure ("Time differences in days"/365.25)
  (givendate-birthdate)/365.25
  
  # Use the package lubridate
  require(lubridate)
  new_interval(start = birthdate, end = givendate) / 
                       duration(num = 1, units = "years")
  
  # Use the package eeptools
  library(eeptools)
  age_calc(dob = birthdate, enddate = givendate, units = "years")
  </code></pre>
  
  <p>Let's talk later about accuracy and focus first on performance. Here's the code:</p>
  
  <pre><code># Now let's compare the performance of the alternatives using microbenchmark
  library(microbenchmark)
  mbm &lt;- microbenchmark(
      arithmetic = (givendate - birthdate) / 365.25,
      lubridate = new_interval(start = birthdate, end = givendate) /
                                       duration(num = 1, units = "years"),
      eeptools = age_calc(dob = birthdate, enddate = givendate, 
                          units = "years"),
      times = 1000
  )
  
  # And examine the results
  mbm
  autoplot(mbm)
  </code></pre>
  
  <p>Here the results:</p>
  
  <p><img src="https://i.stack.imgur.com/G2gNZ.png" alt="Microbenchmark results - table">
  <img src="https://i.stack.imgur.com/PYQ8g.png" alt="Microbenchmark results - plot"></p>
  
  <p>Bottom line: performance of <code>lubridate</code> and <code>eeptools</code> functions is much worse than the arithmetic method (/365.25 is at least 10 times faster). Unfortunately, the arithmetic method is not accurate enough and I cannot afford the few mistakes that this method will make.</p>
  
  <blockquote>
    <p>"because of the way the modern Gregorian calendar
    is constructed, there is no straightforward arithmetic
    method that produces a person’s age, stated according to
    common usage — common usage meaning that a person’s
    age should always be an integer that increases exactly on
    a birthday". (<a href="http://staff.washington.edu/billyk/TechTips_SC4Q98.pdf" rel="noreferrer">link</a>)</p>
  </blockquote>
  
  <p>As I read on some posts, <code>lubridate</code> and <code>eeptools</code> make no such mistakes (though, I haven't looked at the code/read more about those functions to know which method they use) and that's why I wanted to use them, but their performance does not work for my real application.</p>
  
  <p>Any ideas on an efficient and accurate method to calculate the age?</p>
  
  <h1>EDIT</h1>
  
  <p>Ops, it seems <code>lubridate</code> also makes mistakes. And apparently based on this toy example, it makes more mistakes than the arithmetic method (see lines 3, 6, 9, 12). (am I doing something wrong?)</p>
  
  <pre><code>toy_df &lt;- data.frame(
      birthdate = birthdate,
      givendate = givendate,
      arithmetic = as.numeric((givendate - birthdate) / 365.25),
      lubridate = new_interval(start = birthdate, end = givendate) /
          duration(num = 1, units = "years"),
      eeptools = age_calc(dob = birthdate, enddate = givendate,
                          units = "years")
  )
  toy_df[, 3:5] &lt;- floor(toy_df[, 3:5])
  toy_df
  
      birthdate  givendate arithmetic lubridate eeptools
  1  1978-12-30 2015-12-31         37        37       37
  2  1978-12-31 2015-12-31         36        37       37
  3  1979-01-01 2015-12-31         36        37       36
  4  1962-12-30 2015-12-31         53        53       53
  5  1962-12-31 2015-12-31         52        53       53
  6  1963-01-01 2015-12-31         52        53       52
  7  2000-06-16 2050-06-17         50        50       50
  8  2000-06-17 2050-06-17         49        50       50
  9  2000-06-18 2050-06-17         49        50       49
  10 2007-03-18 2008-03-19          1         1        1
  11 2007-03-19 2008-03-19          1         1        1
  12 2007-03-20 2008-03-19          0         1        0
  13 1968-02-29 2015-02-28         46        47       46
  14 1968-02-29 2015-03-01         47        47       47
  15 1968-02-29 2015-03-02         47        47       47
  </code></pre>