Get date difference in years (floating point)

RDate

R Problem Overview


I want to correct source activity based on the difference between reference and measurement date and source half life (measured in years). Say I have

ref_date <- as.Date('06/01/08',format='%d/%m/%y')

and a column in my data.frame with the same date format, e.g.,

today <- as.Date(Sys.Date(), format='%d/%m/%y')

I can find the number of years between these dates using the lubridate package

year(today)-year(ref_date)
[1] 5

Is there a function I can use to get a floating point answer today - ref_date = 5.2y, for example?

R Solutions


Solution 1 - R

Yes, of course, use difftime() with an as numeric:

R> as.numeric(difftime(as.Date("2003-04-05"), as.Date("2001-01-01"), 
+                      unit="weeks"))/52.25
[1] 2.2529
R> 

Note that we do have to switch to weeks scaled by 52.25 as there is a bit of ambiguity there in terms of counting years---a February 29 comes around every 4 years but not every 100th etc.

So you have to define that. difftime() handles all time units up to weeks. Months cannot be done for the same reason of the non-constant 'numerator'.

Solution 2 - R

The lubridate package contains a built-in function, time_length, which can help perform this task.

time_length(difftime(as.Date("2003-04-05"), as.Date("2001-01-01")), "years")
[1] 2.257534

time_length(difftime(as.Date("2017-03-01"), as.Date("2012-03-01")),"years")
[1] 5.00274

Documentation for the lubridate package can be found here.

Solution 3 - R

Inspired by Bryan F, time_length() would work better if using interval object

time_length(interval(as.Date("2003-04-05"), as.Date("2001-01-01")), "years")
[1] -2.257534
time_length(difftime(as.Date("2017-03-01"), as.Date("2012-03-01")),"years")
[1] 5.00274
time_length(interval(as.Date("2017-03-01"), as.Date("2012-03-01")),"years")
[1] -5

You can see if you use interval() to get the time difference and then pass it to time_length(), time_length() would take into account the fact that not all months and years have the same number of days, e.g., the leap year.

Solution 4 - R

Not an exact answer to your question, but the answer from Dirk Eddelbuettel in some situations can produce small errors.

Please, consider the following example:

as.numeric(difftime(as.Date("2012-03-01"), as.Date("2017-03-01"), unit="weeks"))/52.25
[1] -4.992481

The correct answer here should be at least 5 years.

The following function (using lubridate package) will calculate a number of full years between two dates:

# Function to calculate an exact full number of years between two dates
year.diff <- function(firstDate, secondDate) {
  yearsdiff <- year(secondDate) - year(firstDate)
  monthsdiff <- month(secondDate) - month(firstDate)
  daysdiff <- day(secondDate) - day(firstDate)
  
  if ((monthsdiff < 0) | (monthsdiff == 0 & daysdiff < 0)) {
    yearsdiff <- yearsdiff - 1
  }
  
  yearsdiff
}

You can modify it to calculate a fractional part depending on how you define the number of days in the last (not finished) year.

Solution 5 - R

You can use the function AnnivDates() of the package BondValuation:

R> library('BondValuation')
R> DateIndexes <- unlist(
+   suppressWarnings(
+     AnnivDates("2001-01-01", "2003-04-05", CpY=1)$DateVectors[2]
+     )
+   )
R> names(DateIndexes) <- NULL
R> DateIndexes[length(DateIndexes)] - DateIndexes[1]
[1] 2.257534

Click here for documentation of the package BondValuation.

Solution 6 - R

To get the date difference in years (floating point) you can convert the dates to decimal numbers of Year and calculate then their difference.

#Example Dates
x <- as.Date(c("2001-01-01", "2003-04-05"))

#Convert Date to decimal year:
date2DYear <- function(x) {
  as.numeric(format(x,"%Y")) + #Get Year an add
    (as.numeric(format(x,"%j")) - 0.5) / #Day of the year divided by
    as.numeric(format(as.Date(paste0(format(x,"%Y"), "-12-31")),"%j")) #days of the year
}
diff(date2DYear(x)) #Get the difference in years
#[1] 2.257534

I subtract 0.5 from the day of the year as it is not known if you are at the beginning or the end of the day and %j starts with 1.

I think the difference between 2012-03-01 and 2017-03-01 need not to be 5 Years, as 2012 has 366 days and 2017 365 and 2012-03-01 is on the 61 day of the year and 2017-03-01 on the 60.

x <- as.Date(c("2012-03-01", "2017-03-01"))
diff(date2DYear(x))
#[1] 4.997713

Note that using time_length and interval from lubridate need not come to the same result when you make a cumulative time difference.

library(lubridate)

x <- as.Date(c("2012-01-01", "2012-03-01", "2012-12-31"))
time_length(interval(x[1], x[3]), "years")
#[1] 0.9972678
time_length(interval(x[1], x[2]), "years") +
 time_length(interval(x[2], x[3]), "years")
#[1] 0.9995509 #!
diff(date2DYear(x[c(1,3)]))
#[1] 0.9972678
diff(date2DYear(x[c(1,2)])) + diff(date2DYear(x[c(2,3)]))
#[1] 0.9972678

x <- as.Date(c("2013-01-01", "2013-03-01", "2013-12-31"))
time_length(interval(x[1], x[3]), "years")
#[1] 0.9972603
time_length(interval(x[1], x[2]), "years") +
 time_length(interval(x[2], x[3]), "years")
#[1] 0.9972603
diff(date2DYear(x[c(1,3)]))
#[1] 0.9972603
diff(date2DYear(x[c(1,2)])) + diff(date2DYear(x[c(2,3)]))
#[1] 0.9972603

Solution 7 - R

Since you are already using lubridate package, you can obtain number of years in floating point using a simple trick:

find number of seconds in one year:

seconds_in_a_year <- as.integer((seconds(ymd("2010-01-01")) - seconds(ymd("2009-01-01"))))

now obtain number of seconds between the 2 dates you desire

seconds_between_dates <- as.integer(seconds(date1) - seconds(date2))

your final answer for number of years in floating points will be

years_between_dates <- seconds_between_dates / seconds_in_a_year 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmoadeepView Question on Stackoverflow
Solution 1 - RDirk EddelbuettelView Answer on Stackoverflow
Solution 2 - RBryan FView Answer on Stackoverflow
Solution 3 - RRen MaView Answer on Stackoverflow
Solution 4 - RRoman ShevtsivView Answer on Stackoverflow
Solution 5 - R01wadjatView Answer on Stackoverflow
Solution 6 - RGKiView Answer on Stackoverflow
Solution 7 - Ruser3774594View Answer on Stackoverflow