Math on interval objects #105

Closed
kenahoo opened this Issue Jan 5, 2012 · 7 comments

Comments

Projects
None yet
4 participants
@kenahoo

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?

@garrettgman

This comment has been minimized.

Show comment
Hide comment
@garrettgman

garrettgman Feb 29, 2012

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

Collaborator

garrettgman commented Feb 29, 2012

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

@kenahoo

This comment has been minimized.

Show comment
Hide comment
@kenahoo

kenahoo 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

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

This comment has been minimized.

Show comment
Hide comment
@kenahoo

kenahoo Feb 29, 2012

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

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:

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

@garrettgman

This comment has been minimized.

Show comment
Hide comment
@garrettgman

garrettgman Feb 29, 2012

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
int_end(ival) - answer = int_start(ival).

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

Collaborator

garrettgman commented Feb 29, 2012

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.

@wolkym

This comment has been minimized.

Show comment
Hide comment
@wolkym

wolkym Dec 10, 2015

@garrettgman is there dmonth?
I cannot find it

wolkym commented Dec 10, 2015

@garrettgman is there dmonth?
I cannot find it

@vspinu

This comment has been minimized.

Show comment
Hide comment
@vspinu

vspinu Dec 11, 2015

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.

Collaborator

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.

@wolkym

This comment has been minimized.

Show comment
Hide comment
@wolkym

wolkym Dec 14, 2015

@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.

wolkym commented Dec 14, 2015

@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