lubridate icon indicating copy to clipboard operation
lubridate copied to clipboard

Round date for months is dependant on month length

Open rlh1994 opened this issue 6 years ago • 4 comments

I'm not sure if this is by design, but when rounding dates to the nearest e.g. 2 months (or using bimonth) when the date is the first of the second, longer month, then it has to be past midday for the round to go up, otherwise it goes down. I would have assumed rounding to the nearest n months wouldn't want to depend on the different length of months, only the length of the month that the date is in as otherwise this is a little counter-intuitive to what you might expect (especially if it involved February).

The below demonstrates this for the 1st December, and the more extreme case of the 30th January.

library(lubrdiate)
round_date(as.POSIXct('2018-12-01 07:00:00', format = '%Y-%m-%d %H:%M:%S'), unit= '2 months')
# "2018-11-01 GMT"
round_date(as.POSIXct('2018-12-01 12:00:01', format = '%Y-%m-%d %H:%M:%S'), unit= '2 months')
# "2019-01-01 GMT"

round_date(as.POSIXct('2018-01-30 07:00:00', format = '%Y-%m-%d %H:%M:%S'), unit= '2 months')
# "2018-01-01 GMT"
round_date(as.POSIXct('2018-01-30 12:00:01', format = '%Y-%m-%d %H:%M:%S'), unit= '2 months')
# "2018-03-01 GMT"

I'm using R version 3.5.2 and have tested it on lubrdiate v 1.7.4.9000

rlh1994 avatar Feb 24 '19 11:02 rlh1994

It depends on the definition of rounding. Currently rounding just chooses the side (ceiling or floor) based on the absolute time difference between the sides and the supplied time. This part is consistent for all units and I think this is what most of the uses of rounding would assume. Making a special case for months and years would complicate the code with little benefit. So I would rather not add this extra option.

I think round_date(x, "m") %>% round_date("2m") should do what you need, no?

vspinu avatar Feb 24 '19 14:02 vspinu

That's fair. I guess it is mostly a subjective thing, if you asked me to round 30th Jan to either 1st Jan or 1st March I would probably say 1st Jan for most use cases, same with 1st December, I'd likely round to 1st Jan rather than November, but that might just be me and it probably is better to keep a consistent case for all units.

That solution doesn't quite do it, it provides consistency within a day as the time is removed, but in the examples it leads to the december example being rounded down still, and the January example being rounded up (both of which based on above I don't want).

This solution seems to work but is a bit clumsy, flooring the date to the month then setting the month based on the month value rounded

x <- as.POSIXct('2018-12-01 07:00:01', format = '%Y-%m-%d %H:%M:%S')
x <- x %>% floor_date("m") %>% `month<-`(floor(month(x)/2)*2 +1)

I will have to keep this in mind in the future, thanks.

rlh1994 avatar Feb 24 '19 15:02 rlh1994

Thiis particular logic is valid for 2 months rounding. You basically want floor for odd months and ceiling for even months. It's not immediately clear what should we do with 3 months rounding.

A generic algorithm for this style of rounding would be to transform the date into a decimal month first, round and then get back to the date.

I will keep this open, but it's low priority. In any case this should be an extra option to the round_date function. I think, changing the default is a bit too late.

vspinu avatar Feb 26 '19 08:02 vspinu

I agree that makes the most sense, and that would also negate any impact of current scripts. Thanks for understanding :)

rlh1994 avatar Feb 26 '19 20:02 rlh1994