Math on interval objects #105

kenahoo opened this Issue Jan 5, 2012 · 7 comments


None yet

4 participants

kenahoo commented Jan 5, 2012

If I'm given an interval object, I'd like to know how many days/hours/whatevers it represents between its start & stop. The only way I can see to do it currently is to use difftime() in a pretty awkward way:

> ival
[1] 2011-01-01 03:00:46 -- 2011-10-21 18:33:44
> class(ival)
[1] "interval" "numeric"

> difftime(attr(ival, "start") + as.numeric(ival), attr(ival, "start"), 'days')
Time difference of 293.6479 days

An as.difftime.interval(x, units) method would be helpful, interested in a patch? Or is there another way I'm not thinking of?



Our intention was that you could do this sort of thing with division:

ival <- ymd_hms("2011-01-01 03:00:46") %--% ymd_hms("2011-10-21 18:33:44")
ival # 2011-01-01 03:00:46 UTC--2011-10-21 18:33:44 UTC
ival / ddays(1) # 293.6479
ival / dyears(1) # 0.8045148


kenahoo commented Feb 29, 2012

That almost works, but since it's a duration, those 2 examples are really just checking how many 86400-second periods and 31536000-second periods can fit in the given interval. I was looking for something that knows about actual date math, so that (for example) the following 2 calculations have the same answer:

> (ymd("2011-02-01") - ymd("2012-02-01")) / dyears(1)
[1] 1
> (ymd("2012-02-01") - ymd("2013-02-01")) / dyears(1)
[1] 1.00274

For another example - "lubridate knows" that 2012-02-01 plus 1 year is 2013-02-01, but it doesn't know that 2013-02-01 minus 2012-02-01 is also 1 year:

> x <- ymd("2012-02-01"); ((x + years(1)) - x) / dyears(1)
[1] 1.00274

To solve that last case, as a user, it's tempting to use years(1), but that's just rounding:

> x <- ymd("2012-02-01"); ((x + years(1)) - x) / years(1)
estimate only: convert periods to intervals for accuracy
[1] 1
> x <- ymd("2012-02-01"); ((x + years(1) + days(1)) - x) / years(1)
estimate only: convert periods to intervals for accuracy
[1] 1
kenahoo commented Feb 29, 2012

Oh, I should clarify - after I wrote the initial ticket, I discovered that difftime() is also not what I wanted:


Yes, difftimes and durations are similar in that respect. You could just convert the interval to a period

as.period(ival) # 9 months, 20 days, 15 hours, 32 minutes and 58 seconds 
as.period(ymd("2012-02-01") %--% ymd("2013-02-01")) # 1 year

You can also do integer division or modulo division

ival %/% months(1) # 9
ival %% months(1) # 2011-10-01 03:00:46 UTC--2011-10-21 18:33:44 UTC

(I apologize because I realize these are all from the new version which isn't on cran yet. I'm sending it to cran today so it should be up in a couple of days).

I don't think its possible to do any better than an estimate for periods. Even if I could calculate that ival was 9.3 months long , this would require some implicit estimation/judgement. How many seconds does it take to be 0.3 months? Where do I assume the remainder takes place at? A month at the end of the interval may be 31 days, but a month at the beginning may be 28 days. What if the interval is negative (new version)? I wouldn't be able to have both of the following work

int_start(ival) + answer = int_end(ival), and
int_end(ival) - answer = int_start(ival).

I'd rather be upfront about the estimation that occurs.


@garrettgman is there dmonth?
I cannot find it

vspinu commented Dec 11, 2015

No there is not. Durations are recorded in seconds, as there is no standard duration of months that function is not there.

Unfortunately same issue is with years but there is a dyears function. Lubridate also uses average month duration internally in a couple of places. I guess these might be enough reasons to add the dmonth function as well.


@vspinu nevertheless, when we posses start and end points of the interval, we can easily measure months (or any other duration). E.g. [01-01-2015; 05-06-2015] => {5 months 4 days}. If we have at least two of these three [start = 01-01-2015, end = 05-06-2015, duration = {5 months 4 days}], we can calculate any duration. This implies that duration should be always a projection of an interval and either store preimage(start or end point of interval) within object attributes or expect it as a parameter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment