# Math on interval objects #105

Closed
opened this Issue Jan 5, 2012 · 7 comments

None yet

### 4 participants

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?

Collaborator

Ken,

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

Garrett

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

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

http://stackoverflow.com/questions/8765621/length-of-lubridate-interval

Collaborator

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

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

@garrettgman is there dmonth?
I cannot find it

Collaborator

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.