0

Using Lubridate package, how can I determine the numeric age between two times, accounting for leap years.

I need a function where the user can specify wither the age is to be in all the various units, such as 'milliseconds','seconds','minutes','weeks','months' and 'years'.

Here is an illustration of what I have got so far:

age <- function(from, to = today(), units = "years", floor = FALSE) {
  calc = interval(from,to) / duration(num = 1, units = units)
  if (floor) calc = as.integer(floor(calc))
  calc
}

The year 2016 is a leap year, lets determine the age between the start and end times in the month of June. It should be 30 days.

require(lubridate)
#Consider month of June 2016, known to have 30 days
from       = as.POSIXct("2016-6-1")
to         = from + months(1)

daysInYear = 365 + leap_year(to)
age(from,to,units='days')             #30.00, CORRECT
age(from,to,units='years')*daysInYear #30.08, INCORRECT
age(from,to,units='years')*365        #30.00, CORRECT ANSWER, WRONG DAYS IN YEAR

If I calculate the same interval in 'years', it returns: 0.08219178, which is incorrect, because the duration divisor in the age function, is not accounting for 2016 being a leap year, I need it to calculate 0.08196721, which is the same value multiplied by (365/366).

Is there an easier way to determine the exact numerical age between two dates, accounting for leap years, and permitting the full specification of interval units?

Hack-R
  • 22,422
  • 14
  • 75
  • 131
Nicholas Hamilton
  • 10,044
  • 6
  • 57
  • 88

1 Answers1

0
is.leapyear=function(year){
  #http://en.wikipedia.org/wiki/Leap_year
  return(((year %% 4 == 0) & (year %% 100 != 0)) | (year %% 400 == 0))
}

age.hackr <- 
            function(from, to = today(), units = "years", floor = FALSE) {
              if(is.leapyear(year(to))){
                calc = interval(from,to) / duration(num = 1, units = units)
                if (floor) calc = as.integer(floor(calc))
                calc <- calc - 0.00022457
                calc
              } else{
              calc = interval(from,to) / duration(num = 1, units = units)
              if (floor) calc = as.integer(floor(calc))
              calc
              }
            }

age.hackr(from, to, units = "years") * 366

[1] 30

Hack-R
  • 22,422
  • 14
  • 75
  • 131
  • 1
    Since we're using `lubridate` anyway, you can use `lubridate::leap_year()`. It's identical to yours but with some input checking. – Gregor Thomas Jul 31 '16 at 06:15